Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[bugfix] #58 allow inserts with primary key nulled to be rolled back #57

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

jtopenpetition
Copy link

@jtopenpetition jtopenpetition commented Jun 14, 2023

fixes #58

when the row to be inserted contains null for its primary key column, it's most likely, that it's an auto-increment or otherwise generated primary key, so the $row value is not reliable.

in that particular case, prefer the last insert id over the row value

remark: I wanted to add tests, but I couldn't get the docker container to run for a while, and when I did, I couldn't get the tests to run successfully, so I gave up. Theoretically, the groups table has an auto increment primary key, with which this could be tested, but i didn't make it that far to actually be able to write a test.

this bug was introduced in #44 to fix another bug

when the row to be inserted contains `null` for its primary key column, it's most likely, that it's an auto-increment or otherwise generated primary key, so the row value is not reliable.

in that particular case, prefer the last insert id over the row value
@jtopenpetition jtopenpetition changed the title allow inserts with primary key nulled to be rolled back [bugfix] #58 allow inserts with primary key nulled to be rolled back Jun 14, 2023
@JesusTheHun
Copy link
Contributor

The thing is that you can have a nullable PK. So we cannot assume that the provided value should be ignored.
However this is a breaking change indeed, so maybe we should check if the PK is nullable, if not, then use the last inserted id during teardown.
Can you update your PR in that direction ?

@jtopenpetition
Copy link
Author

The thing is that you can have a nullable PK.

Actually, as far as I know, only sqlite allows this, and only due to a "historic oversight". SQL standard (at least since SQL-92 section 4.10.2) explicitly forbids nullable columns in primary keys in general (!!!). That is both single-column and multi-column primary keys.

So we cannot assume that the provided value should be ignored.

Well ... in general, we should, except for sqlite and some exceedingly non-standard databases perhaps.

However this is a breaking change indeed, so maybe we should check if the PK is nullable, if not, then use the last inserted id during teardown. Can you update your PR in that direction ?

To be frank, I would much prefer to just roll back #44, since it was the breaking change to begin with. It was the reason why we didn't upgrade to module-db 2.x.

I would rather have a flag (or a check for the sqlite driver being used) to allow this non-standard behavior, than adding an essentially unnecessary nullable-check for every insert (because schemas may change between inserts, caching is hard, etc.) - also, checks for column definitions/properties isn't implemented in module-db, so this would be from scratch.

side note: Relying on a nullable primary key is - to be honest - madness. I mean, sqlite even allows it to have multiple rows with null as primary key:

sqlite> .nullvalue NULL
sqlite> create table null_madness (id int primary key);
sqlite> insert into null_madness (id) values (null), (null);
sqlite> select * from null_madness;
NULL
NULL
sqlite> select * from null_madness where id = null;
sqlite> select * from null_madness where id is null;
NULL
NULL

why would anyone want this? Why wouldn't you have a nullable unique key instead? Because it's technically not a primary key, since you can't use it to identify every single row, if two rows have the same pk value. flip table

(side side note: interestingly enough ... int is supposed to be an alias for integer, however, the same code produces different results with integer instead of int, since "integer primary key" is meaning autoincrement and is also strict on the datatype and doesn't allow null)

@@ -808,7 +808,9 @@ private function addInsertedRow(string $table, array $row, $id): void
{
$primaryKey = $this->_getDriver()->getPrimaryKey($table);
$primary = [];
if ($primaryKey !== []) {
if (count($primaryKey) == 1 && is_null($row[$primaryKey[0]])) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not check for "all pk values are null" instead of checking the size is 1 and the single value is null ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tbh, I'm actually not certain if auto-increment is even possible in a composite primary key. this might be database specific, again. I would shy away from setting anything, if the primary key had more than one (nulled) column. Someone with broader knowledge than me might know this ...

only size 1 and only if it's null seemed a very safe bet ;o)

addendum: this was also the behaviour before, but ... inverted, if there was an $id, it would be set on the primary key, which - as you pointed out - is actually dangerous, since the last insert id can return a non-primary auto-increment value and would override a proper primary key value.

@JesusTheHun
Copy link
Contributor

JesusTheHun commented Jun 16, 2023

Actually, as far as I know, only sqlite allows this

Alright, then we should fix this !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

inserting null on an autoincrement primary key prevents cleanup
2 participants