-
-
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
PostgreSQL 10 support #2893
PostgreSQL 10 support #2893
Conversation
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.
Can you also add new tests that covers that for PGSQL 10?
@@ -289,7 +289,13 @@ protected function _getPortableSequenceDefinition($sequence) | |||
$sequenceName = $sequence['relname']; | |||
} | |||
|
|||
$data = $this->_conn->fetchAll('SELECT min_value, increment_by FROM ' . $this->_platform->quoteIdentifier($sequenceName)); | |||
$version = (float) $this->_conn->getWrappedConnection()->getServerVersion(); |
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 is breaking the test suite, can you please check that?
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 Postgres 10 yet is not supported in Travis, so there's no way to 100% guarantee the compatibility at this moment.
Duplicate of #2868. Waiting for Travis, see travis-ci/travis-ci#8537. |
@lcobucci It's done. I believe the test was not working properly before (correct me if I'm wrong) |
There was a case when null was returned for $data and this case was ignored. https://github.com/doctrine/dbal/pull/2893/files#diff-e4bd68e20fcb55598e13e0831ba281abR63 I can add null case it needed. |
$data = $this->_conn->fetchAll('SELECT min_value, increment_by FROM ' . $this->_platform->quoteIdentifier($sequenceName)); | ||
$version = (float) $this->_conn->getServerVersion(); | ||
|
||
if ($version >= 10) { |
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 think this should rather be based on Platform check, there will sure be separate platform for Postgres 10. Anyway I think it should all be done in a single PR, not fixing random pieces randomly... :/
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 agree, also exposing Connection#getServerVersion()
a no-go IMO
Tried to refactor. I see this project's internals for the first time tho but IMO it's good start for PSQL10. I can either continue improving this or we can close. |
Any news about this PR? |
@PillowPillow Travis still working to implement Postgresql 10. Until that time, we have to wait. |
Just ran functional tests on Postgres10 localy and all good. 👍 |
@SCIF it's really good to hear that let's just hope that travis adds support to it soon |
I got tired of infinitely waiting for Travis, so here is externally installed Postgres 10: #2946. Feel free to pick that commit and build a working compat PR on top. 😎 |
@@ -3570,4 +3570,9 @@ public function getStringLiteralQuoteCharacter() | |||
{ | |||
return "'"; | |||
} | |||
|
|||
public function getSequenceDataSQL($sequenceName, $schemaName) |
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.
DBAL requires PHP 7.1, please add parameter and return types where applicable.
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.
It should probably be renamed to getSequenceMetadataSQL
, data
is not explicit.
@@ -0,0 +1,37 @@ | |||
<?php | |||
/* |
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.
No license headers please, we've already removed them in develop.
@@ -67,7 +67,7 @@ protected function getDatabasePlatformsForVersions() | |||
array('9.4', 'Doctrine\DBAL\Platforms\PostgreSQL94Platform'), | |||
array('9.4.0', 'Doctrine\DBAL\Platforms\PostgreSQL94Platform'), | |||
array('9.4.1', 'Doctrine\DBAL\Platforms\PostgreSQL94Platform'), | |||
array('10', 'Doctrine\DBAL\Platforms\PostgreSQL94Platform'), | |||
array('10', 'Doctrine\DBAL\Platforms\PostgreSQL100Platform'), |
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 ::class.
public function filtersSequencesDataProvider() | ||
{ | ||
return [ | ||
['Doctrine\DBAL\Platforms\PostgreSqlPlatform'], |
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 ::class.
Here's a diff of keywords in 9.6 and 10: https://www.diffchecker.com/fJ0YkHgw |
They were not reserved before tho. |
return PostgreSQL100Keywords::class; | ||
} | ||
|
||
public function getSequenceMetadataSQL(string $rawSequenceName, string $schemaName) : string |
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.
There shouldn't be a semantic difference between this method and its parents from AbstractPlatform
and PostgreSqlPlatform
. Otherwise, the caller which depends on the abstract interface will not know whether it should pass raw or non-raw sequence name.
@@ -289,7 +289,7 @@ protected function _getPortableSequenceDefinition($sequence) | |||
$sequenceName = $sequence['relname']; | |||
} | |||
|
|||
$data = $this->_conn->fetchAll('SELECT min_value, increment_by FROM ' . $this->_platform->quoteIdentifier($sequenceName)); | |||
$data = $this->_conn->fetchAll($this->_platform->getSequenceMetadataSQL($sequenceName, $sequence['schemaname'])); |
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 it really necessary to fetchAll()
and then use only $data[0]
? Doing fetch()
and using the row directly would make more sense to me.
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.
There's no method fetch()
in connection. In my opinion, this is ok as it fetches one row
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.
There are more fetch*() methods though, fetchArray() is the one you need.
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.
{ | ||
$sequenceName = $this->cleanSequenceNameFromSchemaName($rawSequenceName, $schemaName); | ||
|
||
return 'SELECT min_value, increment_by FROM pg_sequences WHERE schemaname = '.$this->quoteStringLiteral($schemaName).' AND sequencename = ' . $this->quoteStringLiteral($sequenceName); |
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.
Given that in PostgreSQL 10, the sequence metadata is available via a view, would it make sense to rework getListSequencesSQL()
instead? With this approach, the client will not have to execute the SQL from getSequenceMetadataSQL()
for each sequence, and the method itself will not need to be introduced on the abstract platform level.
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.
Not sure it's a good idea. getListSequencesSQL()
lists sequences. It would need to add also increment_by
and min_value
columns. That would change the method's functionality. This method is called before in listSequences()
already.
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.
Look at the OraclePlatform
. Its getListSequencesSQL()
returns list and metadata. The only reason it doesn't do the same for PostgreSQL is that prior to version 10 there was no view to fetch the metadata from.
@simPod are you going to fix/apply or need help ? We're waiting to deploy new product to production for 2 months :P I can help if you wish. |
@iquad you can already use this fork so you don't have to wait :) |
@simPod yeah, but need doctrine to merge them, server tests, 30+ development machine etc. Can't handle them one by one :/ |
|
||
$sequence1Name = 'sequence_1'; | ||
$sequence2Name = 'sequence_2'; | ||
$sequence1 = new Sequence($sequence1Name); |
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.
@simPod please make sure all sequence attributes are covered by the test, not only the name.
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.
Can you review now please? Thanks
$actualSequences[$sequence->getName()] = $sequence; | ||
} | ||
|
||
$isOracle = $this->_sm->getDatabasePlatform()->getName() === OraclePlatform::NAME; |
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.
Oracle is not the only supported platform which returns upper-cased column names. DB2 is the other one but it looks DBAL doesn't support sequences in DB2. In any event, checking the platform name is not the best approach.
I can think of two solutions:
- Figure out why the rest of the tests in this class don't fail on Oracle without using the portability layer. For instance,
testListTableColumns()
expects column names to be lower-cased, however, they are upper-cased in Oracle. Must be implemented in:
$name = strtolower($column->getQuotedName($this->_platform)); - Use
Portability\Connection
withPORTABILITY_FIX_CASE
andfetch_case = CASE_LOWER
when instantiating the schema manager. This is hor a real production code would deal with the case discrepancy.
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.
testListTableColumns
also converts table name to lowercase self::assertEquals('test', strtolower($columns['test']->getname()));
Can do it this way then. In
$name = strtolower($column->getQuotedName($this->_platform)); |
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.
It would be a breaking change. Doing strtolower()
in a test would mean that the production code should do the same by design which it not true, it's a bug.
Let's skip the test for Oracle for now and fix the schema manager in 3.0.
$sequence1InitialValue = 1; | ||
$sequence2Name = 'sequence_2'; | ||
$sequence2AllocationSize = 2; | ||
$sequence2InitialValue = 2; |
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.
It's better to use different values for different parameters to make sure they are not confused during deployment or introspection.
|
||
$this->assertSame($sequence2Name, $actualSequence2->getName()); | ||
$this->assertEquals($sequence2AllocationSize, $actualSequence2->getAllocationSize()); | ||
$this->assertEquals($sequence2InitialValue, $actualSequence2->getInitialValue()); |
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.
Not sure the assertions are intentionally this verbose, but the same could be checked in a more compact way:
$sequence1 = new Sequence('sequence_1', 1, 2);
$sequence2 = new Sequence('sequence_2', 3, 4);
// create the sequences
self::assertArraySubset([
$sequence1,
$sequence2,
], $this->_sm->listSequences());
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'm fan of verbosity but can remake if you think it better suits code style of this project. Should I proceed 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.
Depending on what's the purpose of this verbosity. The approach I suggested is more state-based. It only tests that the collection returned by listSequences()
contains the objects which were previously created. Isn't it what it's supposed to do?
The current approach also tests the public API of the Sequence
class. For instance, if there was some logic in getInitialValue
and getAllocationSize()
, the test would have to take this logic into account as well which would make it less stable.
There are no strict coding guidelines in this regard, so it's up to you how you want to structure the test.
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.
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.
It's failing on Psql 9.5. I'd have to either sort it by name or revert to the previous verbose version.
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.
Eh… I didn't know assertArraySubset()
is strict about numeric keys. The last approach could fail on any DB because the order of sequences is not enforced.
The problem is not verbosity itself, the problem is also that having the logic like array_map()
or array_filter()
outside of assertions which makes the test less readable and less clear about which exactly case it covers.
How about this:
$list = $sm->listSequences();
self::assertContains($sequence1, $list);
self::assertContains($sequence2, $list);
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.
That wouldn't work as those are different objects.
I guess in the end this is the simplier solution:
$this->assertSame($sequence1Name, $actualSequence1->getName());
$this->assertEquals($sequence1AllocationSize, $actualSequence1->getAllocationSize());
$this->assertEquals($sequence1InitialValue, $actualSequence1->getInitialValue());
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, let's leave it like this.
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.
Kk, it's there
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.
Just last few nitpicks, otherwise LGTM. 👍
$data = $this->_conn->fetchAll('SELECT min_value, increment_by FROM ' . $this->_platform->quoteIdentifier($sequenceName)); | ||
if ( ! isset($sequence['increment_by'], $sequence['min_value'])) { | ||
$data = $this->_conn->fetchAssoc('SELECT min_value, increment_by FROM ' . $this->_platform->quoteIdentifier($sequenceName)); | ||
$sequence = array_merge($sequence, $data); |
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 array_merge needed here? Wouldn't +
just work since it's assoc?
$actualSequence1 = $actualSequences[$sequence1Name]; | ||
$actualSequence2 = $actualSequences[$sequence2Name]; | ||
|
||
$this->assertSame($sequence1Name, $actualSequence1->getName()); |
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 call all asserts statically.
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.
Isn't that deprecated?
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.
What do you mean? All assert methods are declared static: https://github.com/sebastianbergmann/phpunit/blob/master/src/Framework/Assert.php
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.
Ah, not deprecated but it's there because of something old sebastianbergmann/phpunit#1914 (comment).
Both can be used tho. I'm used to call it via non-static way.
I guess it needs to be reworked to static then as the whole project uses static calls. Shall I?
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.
And bad design in PHPUnit apparently, since non-static TestCase extends static Assert class. :/
Since it's declared as static, it was decided to use static calls in tests for assert methods, otherwise it's different context (and personally I think PHP should emit a warning, just like static->instance call).
tests/Doctrine/Tests/DBAL/Driver/AbstractPostgreSQLDriverTest.php
Outdated
Show resolved
Hide resolved
@morozov thanks for help! |
@lcobucci is there anything else? |
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.
LGTM 🚢
OMG! |
thank you so much, all of you! |
Is there any date for 2.7 so I could use postgres 10? Or is there a way to patch 2.6 with this commit? |
No date yet. You can already use 2.6 with PostgreSQL 10, but the schema tools won't be working correctly yet. |
Use this before 2.6 branch will be patched doctrine#2893
Use this before 2.6 branch will be patched doctrine#2893
Still have this issue with |
I think this issue can easily be solved using proper doctrine connection string specifying the version of postgres like: DATABASE_URL=postgres://user@127.0.0.1:5432/db_name?charset=utf8&serverVersion=10 the serverVersion part helped me solving all issues with Doctrine & Postgres. Try it for free ;) |
@exebece Yeah, my bad, updated the doctrine configuration from |
@exebece Thanks! Spent two hours and your comment save me from another time wasting. |
When using Postgres 10, DBAL throws
This fixes it as PSQL10 reads sequences definitions from
pg_sequences
. https://www.postgresql.org/docs/10/static/view-pg-sequences.html