setIntegrityCheck in Zend Selects with joins - zend-framework

I was looking at some questions that ask how to do joins in Zend Framework queries, but the answer is always something like "just do setIntegrityCheck(FALSE)".
My question is: why do I need to do this?
It seems to me disabling "integrity checks" is not the proper way of making this work. In my particular case, I'm using a MySQL database with some InnoDB tables with foreign keys, so for example:
CREATE TABLE IF NOT EXISTS `tableA`
(
`id` CHAR(6),
`name` VARCHAR(255),
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `tableB`
(
`tableA_id` CHAR(6),
`somefield` VARCHAR(255),
PRIMARY KEY (`tableA_id`)
) ENGINE=InnoDB;
ALTER TABLE `tableB` ADD FOREIGN KEY fk1 (`tableA_id`) REFERENCES `tableA` (`id`);
(this is a very simplified version of my DB)
And, my query code looks like this:
$table = new Zend_Db_Table('tableB');
$select = $table->select(TRUE)
->join(array('a' => 'tableA'), 'tableB.tableA_id = a.id');
$result = $table->fetchAll($select);
This is giving me the "Select query cannot join with another table" exception unless I add the setIntegrity(FALSE) to my $select.

Calling setIntegrityCheck(false) is the proper way to do a join; if you are using Zend_Db_Table and Zend_Db_Table_Select, you can't join unless you disable the integrity check.
The integrity check is simply in place to make sure the query DOES NOT use multiple tables, and when in place, ensures that the Zend_Db_Table_Row objects can be deleted or modified and then saved because the row data is exclusive to a single table, and is not a mix of data from different tables.
To indicate that you WANT to use multiple tables, then specify setIntegrityCheck(false) to let Zend Framework know that it is intentional. The result is that you get a locked row which cannot call save() or delete() on.
Here is a quote from the reference guide on Zend_Db_Table - Advanced Usage (skip to example 27.
The Zend_Db_Table_Select is primarily used to constrain and validate
so that it may enforce the criteria for a legal SELECT query. However
there may be certain cases where you require the flexibility of the
Zend_Db_Table_Row component and do not require a writable or deletable
row. for this specific user case, it is possible to retrieve a row or
rowset by passing a FALSE value to setIntegrityCheck(). The resulting
row or rowset will be returned as a 'locked' row (meaning the save(),
delete() and any field-setting methods will throw an exception).
See also: One-to-Many Joins with Zend_Db_Table_Select

Ok, I did some research, and it isn't quite true that you have to call setIntegrityCheck(FALSE) in order to do joins.
The relevant code in the Zend_Db_Select class (i.e. the only place to find the very last word to this argument), contains this code:
if ($this->_integrityCheck !== false) {
foreach ($fields as $columnEntry) {
list($table, $column) = $columnEntry;
// Check each column to ensure it only references the primary table
if ($column) {
if (!isset($from[$table]) || $from[$table]['tableName'] != $primary) {
require_once 'Zend/Db/Table/Select/Exception.php';
throw new Zend_Db_Table_Select_Exception('Select query cannot join with another table');
}
}
}
}
So, actually, it checks to see if all the selected fields in the query belong to the "primary table". A query does not necessarily have to return all the fields in the involved tables.
Coming back to the example in my question, it turns out this does work:
$table = new Zend_Db_Table('tableB');
$select = $table->select(TRUE)
->join(array('a' => 'tableA'), 'tableB.tableA_id = a.id', NULL); // <-- notice the third parameter here
$result = $table->fetchAll($select);
This new query only returns the fields from tableB, but you can add where conditions on any of the tables, as you would normally do with SQL, with no problem.

Related

PostgreSQL: Return auto-generated ids from COPY FROM insertion

I have a non-empty PostgreSQL table with a GENERATED ALWAYS AS IDENTITY column id. I do a bulk insert with the C++ binding pqxx::stream_to, which I'm assuming uses COPY FROM. My problem is that I want to know the ids of the newly created rows, but COPY FROM has no RETURNING clause. I see several possible solutions, but I'm not sure if any of them is good, or which one is the least bad:
Provide the ids manually through COPY FROM, taking care to give the values which the identity sequence would have provided, then afterwards synchronize the sequence with setval(...).
First stream the data to a temp-table with a custom index column for ordering. Then do something likeINSERT INTO foo (col1, col2)
SELECT ttFoo.col1, ttFoo.col2 FROM ttFoo
ORDER BY ttFoo.idx RETURNING foo.id
and depend on the fact that the identity sequence produces ascending numbers to correlate them with ttFoo.idx (I cannot do RETURNING ttFoo.idx too because only the inserted row is available for that which doesn't contain idx)
Query the current value of the identity sequence prior to insertion, then check afterwards which rows are new.
I would assume that this is a common situation, yet I don't see an obviously correct solution. What do you recommend?
You can find out which rows have been affected by your current transaction using the system columns. The xmin column contains the ID of the inserting transaction, so to return the id values you just copied, you could:
BEGIN;
COPY foo(col1,col2) FROM STDIN;
SELECT id FROM foo
WHERE xmin::text = (txid_current() % (2^32)::bigint)::text
ORDER BY id;
COMMIT;
The WHERE clause comes from this answer, which explains the reasoning behind it.
I don't think there's any way to optimise this with an index, so it might be too slow on a large table. If so, I think your second option would be the way to go, i.e. stream into a temp table and INSERT ... RETURNING.
I think you can create id with type is uuid.
The first step, you should random your ids after that bulk insert them, by this way your will not need to return ids from database.

Teaching Postgres how to handle an update on a view's column which is a result of a `case` operation

I have a situation where I create a view based on a single table from a different schema (for the sake of brevity, I've shortened the example):
create view b.users as
select
uid as id
case
when name = '' then null
else name
end as username
from public.users;
Since the username column in the view is not a simple map I can't just update the column:
update b.users set username = 'foo' where id = 1234;
The query above results in an error.
Is there any way I can tell Postgres how to handle an update on the username column? Ideally, it would just insert the raw data (pseudo code):
create trigger handle_username_update before update
on users for each row
b.users.username = NEW.username where b.users.id = NEW.id
Edit
I will elaborate a little more to be more specific about my question.
Just as a_horse_with_no_name and many googling around answers suggested, I could just write a trigger to "hijack" the update operation and handle the update/insert myself. while it's possible, I would like to avoid this solution since in my case, I'm talking about potentially tens or hundreds of columns which most of them are updatable by default with a few of them that are formatted using case operations. With that solution, I would need to override the default behavior for each column. Also, each time I add a new column I will need to remember to update the trigger or else some of my applications will just stop working.
I was wondering if there any other solution for that problem.

How can I avoid duplicate rows from near-simultaneous SQL adds?

My Razor 3 web app is creating multiple rows for the same foreign key Id, when multiple input comes in for the same Id, and I would like help on how to avoid this.
The SQL Server table stores data about records in another table (it's ratings users have given about certain things, where there is also a table of users and a table of rate-able things, so the ratings table has a foreign key id for user, a foreign key id for the thing rated, and a value for the rating). When no rating has been given, there is no row for that user id & thing id.
When a user rates a thing, the code calls the server, which checks to see if that user has rated that thing before, and if so, it updates the row, but if not, it creates a new row:
// Get the member's rating for the thing, or create it.
Member_Thing_Rating memPref = (from mip in _myEntities.Member_Thing_Rating
where mip.thingID == thingId
where mip.MemberID == memberId
select mip).FirstOrDefault();
if (memPref == null)
{
memPref = new Member_Thing_Rating();
memPref.MemberID = memberId;
memPref.thingID = thingId;
_myEntities.Member_Thing_Rating.AddObject(memPref);
}
Which works fine EXCEPT when the user sends two ratings for the same thing very quickly (which happens rather often), which results in the server creating two rows, because apparently it is multi-threaded and neither thread sees an existing row, so they both create a new one.
So... how can I avoid this?
I assume I can somehow (?) tell SQL Server to make a constraint that combos of memberID and thingID should be unique in this table, and then it would be SQL Server's job to auto-magically resolve the insertions and hopefully use the latest value.
or
I imagine I could somehow (?) tell this routine to lock the DB or become single-threaded so that one completes adding a row before the next call to the same routine is allowed to execute.
I just don't know the syntax or UI/SQL steps to do either, despite a bit of looking. I think I prefer the thread lock solution, because I am more programmer than DB person, so I prefer my complexity in the code.
Thanks for any help!
You could easily add a unique constraint to your SQL Server table to make sure you never get duplicates on your (memberID, thingID) columns:
ALTER TABLE dbo.YourTableNameHere
ADD CONSTRAINT UQ_MemberID_ThingID UNIQUE(MemberID, ThingID)
Now, if your second connection tries to insert a row with values for (MemberID, ThingID) that are already in the table, the INSERT will fail, you'll get an exception which you can handle and e.g. get new values for one or both of those ID's.

EF 6 Migration: How to execute sql SELECT?

In our project we have necessity of adding some predefined data to DB. I think the best way and concept is using for that EF Migrations (not Seed method).
But we have a big troubles with adding related data to DB:
For Example:
Suppose we have 2 tables:
Users:
Id (PK auto increment)
Name
RoleId
Roles:
Id (PK auto increment)
Name
Let's suppose that we need to add User(Name = 'John', RoleId = (Id of role that name is 'Admin')).
How can we do it? It would be great if we find a solution that allows us to execute pure SQL SELECT script which not uses Entities of Code First because they can be modified or removed.
For DELETE, INSERT, UPDATE can be used Sql(...) method but what about SELECT?
You cannot have a context into the migration.
Logically first are ran the migrations to Update the DB Schema, then you can have a context to work with the data via it. If your DB does not match the model, or even the table is still not there, you cannot use it in EF.
I had to look into the EF code (and also because was curious). Practically the Sql() method in the DbMigration class in several levels below just adds the SQL string into a list of queries that should be executed into the transaction and moves on. It does not executes it when it is called. So in short EF just fills in a list of codes lines that should be executed in the end at once. And it seems correct if you try to walk in all paths of what you can do with the C# code in the migration code.
The question is quite good actually, unfortunately still I didn't found any better solution rather than using pure ADO.
Another option is to generate more custom SQL queries, and use T-SQL more widely.
For your case as you want to insert the user and set the groupId looking by the name, it can be used with inner select:
INSERT INTO Users (Name, GroupId)
VALUES ('John', RoleId = (SELECT Id FROM Roles WHERE Name = 'Admin')).
For my issue, I had to a bit do more sophisticated execution - the following does the same as the AddOrUpdate method of the DbSet, using the IF statement:
IF EXISTS (SELECT * FROM Table1 WHERE Column1='SomeValue')
UPDATE Table1 SET (...) WHERE Column1='SomeValue'
ELSE
INSERT INTO Table1 VALUES (...)
I found it here: http://blogs.msdn.com/b/miah/archive/2008/02/17/sql-if-exists-update-else-insert.aspx
I'm using good old LINQ for this:
public override void Up()
{
using (var dc = new DbContext("your connection string or name"))
{
var ids = dc.Database.SqlQuery<int>("SELECT id FROM sometable WHERE somefield={0}", 42).ToArray();
...
}
}
Using LINQ is better, even for usual migrations, because, there is a bug in DbMigration.Sql method, it ignores arguments: How to pass parameters to DbMigration.Sql() Method

How does one call a function from a postgresql rule that has access to NEW and OLD?

I'm new to postgresql (and therefore rules) and I've looked around, but can't really find an example calling a 'global' function.
I am using a normalized database where rows will be flagged as deleted rather than deleted. However, I would like to retain the DELETE FROM... functionality for the end user, by using an instead of delete rule to update the table's deleted_time column. Each table should, therefore, be able to use a common function, but I am not sure how this would be called in this context, or how it would have access to NEW and OLD?
CREATE OR REPLACE RULE rule_tablename_delete AS ON DELETE
TO tablename DO INSTEAD (
/// call function here to update the table's delete_time column
);
Is this even the correct approach? (I note that INSTEAD OF triggers are restricted to views only)
Just use an UPDATE statement:
create rule rule_tablename_delete as
on delete to tablename
do instead
update tablename
set delete_time = current_timestamp
where id = old.id
and delete_time is null;
Assuming that the id column is the primary key of that table.
Some more examples are in the manual: http://www.postgresql.org/docs/9.1/static/rules-update.html