-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Don't Set a Default Value on PostgreSQL SERIAL Fields #2907
Conversation
The scrutenizer failure here seems unrelated, just picked up because I changed a file with a pre-existing issue: https://scrutinizer-ci.com/g/doctrine/dbal/inspections/6801d978-162b-436c-a2e2-c1315ea461c5/issues/files/tests/Doctrine/Tests/DBAL/Functional/Schema/PostgreSqlSchemaManagerTest.php?status=new&orderField=path&order=asc&honorSelectedPaths=0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice job, great commit organisation too.
I got some suggestions, questions, and nitpicking - obviously.
Can you please make sure that the code you're adding follows our standards? phpcs
is already configured - with phpcbf
- and can be helpful (I've tried to make it mandatory and simplify the lives of new contributors on #2849 but @Ocramius said NOPE 😂).
*/ | ||
public function getDefaultValueDeclarationSQL($field) | ||
{ | ||
if (self::isSerialField($field) && empty($field['notnull'])) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This condition will result to true
when $field['notnull']
doesn't exist, is this the expected behaviour? I'd suggest to be more explicit about that, like $field['notnull'] ?? false === false
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know that we use empty()
on the parent method, but we didn't have these features back then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any reason to leave the empty($field['notnull'])
out of the method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also do we have unit tests covering all the paths?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any reason to leave the empty($field['notnull']) out of the method?
Yep. nonnull
doesn't have anything to do with being a SERIAL
field. This is perfectly okay: whatever SERIAL NOT NULL
. So if someone wants to make sure the field is notnull
they are free to do so. so it make more sense to me to check for a serial field as well as check notnull
in one condition here. Could move it all into a isSerialFieldAndNotNull
method, though.
This condition will result to true when $field['notnull'] doesn't exist, is this the expected behaviour?
$field['notnull']
missing is the same as $field['notnull'] = false
yeah? I think you're right though, should be more explicit.
Also do we have unit tests covering all the paths?
I need to add some for whatever SERIAL NOT NULL
now that I think about it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, coming back to this notnull
thing. I'm not actually it sure it should get checked at all. Any default value for a serial field is invalid -- they already have defaults built in. Maybe getDefaultValueDeclarationSQL
should just return its empty string for any SERIAL
field?
return parent::getDefaultValueDeclarationSQL($field); | ||
} | ||
|
||
private static function isSerialField($field) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing type declarations (parameters and return)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, any reason to make this static?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing type declarations (parameters and return)
Left out the parameter type declaration because the method from which this is called doesn't have one either (getDefaultValueDeclarationSQL
). $field
theoretically should be array
? Happy to add the type declaration for $field
, but that was the reason I didn't.
Definitely should have the return type!
Also, any reason to make this static?
Habit of my, sorry. I tend to make "pure" utility functions static -- since nothing from the platform's instance state is required, I made it static. Want me to make it non-static?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got your point. I don't see much difference in this case, since the caller is already in the scope of the instance. I'd keep it non-static here but was just curious 😄
|
||
private static function isSerialField($field) | ||
{ | ||
return !empty($field['autoincrement']) && isset($field['type']) && ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comments about being explicit here (with empty()
)
|
||
/** | ||
* @dataProvider serialTypes | ||
* @group https://github.com/doctrine/dbal/issues/2906 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use only 2906
as group
|
||
/** | ||
* @dataProvider serialTypes | ||
* @group https://github.com/doctrine/dbal/issues/2906 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use only 2906
as group
* @dataProvider serialTypes | ||
* @group https://github.com/doctrine/dbal/issues/2906 | ||
*/ | ||
public function testAutoIncrementCreatesSerialDataTypesWithoutADefaultValueWheNotNullIsFalse($type) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public function testAutoIncrementCreatesSerialDataTypesWithoutADefaultValueWheNotNullIsFalse(string $type) : void
|
||
$columns = $this->_sm->listTableColumns($tableName); | ||
|
||
$this->assertNull($columns['id']->getDefault()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use self::assert*
instead
@@ -144,6 +144,30 @@ public function testGenerateTableWithAutoincrement() | |||
self::assertEquals(array('CREATE TABLE autoinc_table (id SERIAL NOT NULL)'), $this->_platform->getCreateTableSQL($table)); | |||
} | |||
|
|||
public static function serialTypes() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add : array
as return type
* @dataProvider serialTypes | ||
* @group https://github.com/doctrine/dbal/issues/2906 | ||
*/ | ||
public function testGenerateTableWithAutoincrementAndNotNullFalseDoesNotSetADefaultValueOnSerialFields($type, $definition) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add type declarations here
|
||
$sql = $this->_platform->getCreateTableSQL($table); | ||
|
||
self::assertEquals(array("CREATE TABLE autoinc_table_notnull (id $definition)"), $sql); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use short-array notation
I did the best I could with this. Found some violations in the tests with aligned equals signs. Running either tool on the whole codebase produces a lot of violations, even running it on the files I edited were 40 or 50 fixes per file.
I did make the change so |
Time for a shameless self-promotion:
@chrisguitarguy, see morozov/diff-sniffer-pull-request and morozov/diff-sniffer-pre-commit. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great job @chrisguitarguy, just one more thing (apart from what @morozov said).
return parent::getDefaultValueDeclarationSQL($field); | ||
} | ||
|
||
private static function isSerialField($field) : bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding the type, we just don't have the type declaration on getDefaultValueDeclarationSQL
for historical reasons. Let's add it here too
Postgres has a `SERIAL` data type to allow shorthand for `nextval('sequence_name')`, but DDL like this: CREATE TABLE example (id SERIAL) Cannot be generated in the DBAL schema APIs.
Serial fields already have a default value. By opting out of `notnull` serial fields will just set the next value in the sequence.
0a19f37
to
15c2aee
Compare
@chrisguitarguy merged and backported to 2.6, thanks for your contribution! |
My pleasure, thanks for the support @lcobucci! |
Specifically when
'notnull' => false
is in the column options.Right now DBAL adds a
DEFAULT NULL
when that happens.[BIG]SERIAL
fields already have a default value and adding theDEFAULT NULL
produces an error.Fixes #2906
Not sure if the functional tests here are necessary, but it seemed like a good idea.