On 9/1/10 2:01 PM, lyle fairfield wrote:
The bug is somewhat specific - we will see it if Access is forced to
fall back to its last resort but I'm getting ahead of myself.
In general, whenever we insert a new record, Access will send a request
to the backend to locate the newly inserted record. With SQL Server,
it'll send a "SELECT @@identity;". With other ODBC backend, it depends
on whether the ODBC driver will help Access out.
But when we use a SQL Server view or an ODBC backend that does not help
Access out, Access notes that it can't be certain that SELECT @@identity
will succeed so it turns to the last resort of doing a:
"SELECT pk FROM a_table WHERE a_col = a_value AND b_col = b_value ...
n_col = n_column;"
where the WHERE criteria is built based on how the user dirtied the
fields. If the user never dirty a field, then it's not a part of the
criteria. This will work as long there is exactly one row returned. If
Access get zero or more than one, Access gives up and mark the newly
inserted row as #Deleted even though in fact the insertion was successful.
If you've sticked with me so far, now here's how null comes in the
picture. If an user dirty the field, move out, and changes the mind and
clear out the content... _before_ saving, then proceeds to save the
field, Access will then build the WHERE criteria and notice that the
field was dirty and makes it so:
"SELECT pk FROM table_a WHERE ... col_x = NULL..."
which is of course impossible and we get nothing. The result is a
#Deleted. Note that this will not occur if Access sends in a @@identity
instead of comparing every field. Access will basically send in
@@identity only when it knows that a certain column on linked table is
in fact the primary key. That key icon won't show up in table design if
we link a SQL Server link or any other ODBC backend.
I did post a workaround by basically collecting all new values from
dirtied fields, then undoing then re-apply the dirtied fields back and
proceed to save the field. This way, any dirty nulls will not impact
this problem (though we are still stuck with the not so great last
resort of comparing every field which will certainly fail should a table
contain two rows with different pk but otherwise identical content for
non-pk columns.
Tangent:
I did have a looky-see in PostgreSQL ODBC driver source code and was
intrigued to find the following:
They added a special flag to basically lie to Access that PostgreSQL is
SQL Server in order to coax the Access to send in more reliable "SELECT
@@identity;" and go out of their way to convert the @@identity into a
currval(). Furthermore, I did not notice that until long after the OP
posted last post in the June but PostgreSQL did have a setting to
recongize "col = NULL" and convert it to "col IS NULL" thus "fixing" the
bug and the comment suggested that they added this for Access so they
had to have knew about that bug long before the OP posted this. Whether
their fix is a good is questionable but at least it's off by default and
isn't easy to turn on, but well, there you go.
So in conclusion, Access team did do one thing right - they didn't try
to assume that any ODBC backend would use the same @@identity trick to
find the key and because ODBC does not supply that information, they had
to do that "compare-every-fields" trick to locate the new record to add
to the form after the user has submitted the content. But geez, they
stop short of giving a one simple property that could have been added to
linked tables that would allow the developer to define how to find the
key. Bam! All that quirky #Deleted problems would go away peasy-easy.
But they felt that it'd be too "programmatic" and not something an
Access user would be ever interested in setting. Oh well.
Hope that made for a enjoyable bedtime reading.
