-
-
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
DBAL-2925: Added functional test checking ability to change PK for po… #2929
Conversation
7d1bdee
to
6333a15
Compare
@@ -3570,4 +3575,14 @@ public function getStringLiteralQuoteCharacter() | |||
{ | |||
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.
I think the phpdoc is redundant
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.
Hi @simPod ,
Yeah, you are correct, it's totally useless and I will drop it soon.
86ea186
to
6147eb5
Compare
@simPod Does this PR is ready to be merged ? I've got the same problem for PK under postgresql. |
@PapsOu Unfortunately nobody has simply got to review it yet. |
@@ -752,6 +752,11 @@ public function getDropForeignKeySQL($foreignKey, $table) | |||
return $this->getDropConstraintSQL($foreignKey, $table); | |||
} | |||
|
|||
protected function getDropPrimaryKeySQL(string $table) : string | |||
{ | |||
return $this->getDropConstraintSQL("{$table}_pkey", $table); |
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 string concatenation here + '
instead of variable in 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.
|
||
$c = new Comparator(); | ||
$diff = $c->diffTable($tableFrom, $tableTo); | ||
self::assertInstanceOf(TableDiff::class, $diff, "There should be a difference and not false being returned from the table comparison"); |
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.
Constant strings should be enclosed in '
.
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 should be a difference and not false
It's a very vague assertion. I'd omit it together with the previous one about the autoincrement and only focus on the end result where the new primary key is (id)
.
/** | ||
* @group 2925 | ||
*/ | ||
public function testAlterTableChangePrimaryKey() : void |
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 move this test to SchemaManagerFunctionalTestCase
. It should be checked and work the same for all platforms, not only for PostgreSQL.
|
||
$c = new Comparator(); | ||
$diff = $c->diffTable($tableFrom, $tableTo); | ||
self::assertInstanceOf(TableDiff::class, $diff, "There should be a difference and not false being returned from the table comparison"); |
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 should be a difference and not false
It's a very vague assertion. I'd omit it together with the previous one about the autoincrement and only focus on the end result where the new primary key is (id)
.
@@ -2065,6 +2065,11 @@ protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) | |||
$sql[] = $this->getDropIndexSQL($index, $tableName); | |||
} | |||
foreach ($diff->changedIndexes as $index) { | |||
if ($index->isPrimary()) { | |||
$sql[] = $this->getDropPrimaryKeySQL($tableName); |
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.
@Majkl578 is there a way to enforce a new line before flow control operators like return
, continue
, etc on the code sniffer level? It would be very nice to have.
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 sniff I'm aware of, but shouldn't be hard to implement. Just needs to be compatible i.e. with single statements:
if (...) {
continue;
}
or comments:
foo();
// comment
return 123;
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.
Yeah. More generically, the first case is if the previous line has flow control statement which is a block opener, there shouldn't be an empty line. Like:
function x()
{
foreach (...) {
foreach (...) {
}
}
foreach (...) {
}
}
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've added new line before continue
.
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.
@carusogabriel do you want to file this as a requirement for coding standards (I see we have disabled issues for the repository). It'd be nice to have a reference point somewhere. Otherwise, I constantly forget where the discussion started.
/cc @deeky666
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.
@morozov Require new lines before control structions you mean? :)
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.
Yes. Before and after.
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.
@morozov , Totally agree with this proposal, but code marked as still required your review :) Mark it properly please.
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.
@SCIF it's not because of the formatting issue. I want to see if the behavior of dropping a non-existing PK on SQL Server is consistent with other platforms.
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 thread is regarding cs only, the PK ones here
@@ -752,6 +752,11 @@ public function getDropForeignKeySQL($foreignKey, $table) | |||
return $this->getDropConstraintSQL($foreignKey, $table); | |||
} | |||
|
|||
protected function getDropPrimaryKeySQL(string $table) : string | |||
{ | |||
return $this->getDropConstraintSQL("{$table}_pkey", $table); |
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.
6147eb5
to
1a0614b
Compare
$this->_sm->dropAndCreateTable($tableFrom); | ||
|
||
$tableFrom = $this->_sm->listTableDetails('primary_key_userid'); | ||
self::assertTrue($tableFrom->getColumn('user_id')->getAutoincrement()); |
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 can see that this test is failed on Oracle. Should I skip this assert for Oracle or does whole test make sense for Oracle?
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.
@SCIF if the test is about being able to change PK, why does it use/verify autoincrement? Please try to not use it and use getPrimaryKey()
instead.
842c6ac
to
5e886df
Compare
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.
@SCIF thanks for the cross-platform test. As you can see, it's already bringing value :-)
Please see my comments and address the code style issues.
|
||
protected function getDropPrimaryKeySQL(string $table) : string | ||
{ | ||
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.
Could you use heredoc syntax instead?
SELECT @sql = 'ALTER TABLE ' + @table + ' DROP CONSTRAINT ' + name + ';' | ||
FROM sys.key_constraints | ||
WHERE [type] = 'PK' | ||
AND [parent_object_id] = OBJECT_ID(@table); |
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.
Would it make sense to move the object ID outside of the @sql
so that the function is not invoked for every single PK in sys.key_constraints
?
How will this query behave if the table doesn't have a PK? I'd expect sp_executeSQL
be invoked with an empty argument and throwing some sort of exception.
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.
Emm. If you will try to drop PK for any other platforms on table not containing it, exception will be thrown as well, so I don't see any inconsistency here.
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 the same type of exception?
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.
Hmm, different one, you are right. Just syntax error
vs can't drop unexisted PK
. What's your proposal? I don't have windows pc and that is my very first mssql experience in life :)
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.
You don't need a Windows PC. The SQL Server extension can be compiled for PHP on Linux, and the server itself can be running in a Docker container (microsoft/mssql-server-linux).
I don't have a proposal currently. My only concern is this code may execute knowingly syntactically invalid SQL. Instead of doing the in the dynamic SQL way, can we just rely on some conventional PK name in SQL Server (like DROP CONSTRAINT PK_$table;
from here)?
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.
Hold on. getDropPrimaryKeySQL()
is not even part of the abstract platform API. Why are we using it in the abstract platform code? From what I see, most of the platforms are capable of identifying PK by name including SQL Server. Why should we take special care about the PK on SQL Server if it can be just dropped by name same as other constraints?
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.
We can't rely on any name conventions/best practices. As far as I investigated, the name builds according to schema PK_{$tableName}_{$someHash}
and can't be predicted.
@@ -2078,6 +2078,12 @@ protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) | |||
$sql[] = $this->getDropIndexSQL($index, $tableName); | |||
} | |||
foreach ($diff->changedIndexes as $index) { | |||
if ($index->isPrimary()) { |
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.
The Scrutinizer failure reveals this as a decent candidate for refactoring in the future. Instead of the platform knowing how to drop a given index based on its properties (primary, non-primary and so on), the index should tell the platform how to drop itself (sort of a Visitor pattern):
interface Index
{
function drop(Platform $platform);
}
interface Platform
{
function dropIndex(string $name);
function dropPrimaryKey(string $tableName);
}
This is outside of scope, just noting down.
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 was thinking about it but that will lead to creation of platform specific indices. Quite heavy refactoring will be required, I worry. I'm afraid I'm not able to do it in reasonable time.
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 really fan of the suggested visitor approach. Index would then need to know how to represent itself on all existing platforms and even those not provided by DBAL out-of-box.
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.
@SCIF no need to worry about it right now. It's just for the record.
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 really fan of the suggested visitor approach. Index would then need to know how to represent itself on all existing platforms and even those not provided by DBAL out-of-box.
Not exactly. They will need to be able to identify themselves as a type while the platform will know how to handle certain types. Same as we do with column types.
|
||
protected function getDropPrimaryKeySQL(string $table) : string | ||
{ | ||
return 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY'; |
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 see, getDropPrimaryKeySQL()
is being introduced here for an abstract platform but it's only needed for MySQL and has MySQL-specific syntax. This is a no-go.
I believe, the original issue (#2925) is filed for MySQL where DROP INDEX PRIMARY ON $table
is not supported. Instead of introducing this method, MySqlPlatform::getDropIndexSQL()
should be updated to produce correct SQL for the PK.
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.
The issue originally discovered during work with Postgres. Initially I tried to modify getDropIndexSQL()
method but it can't be done for postgres at least, so separate method for primary keys only is the best solution.
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.
PK is not just an index, but it's a constraint in some RDBMS. Handling could be done by changing getDropIndexSQL()
signature but I believe it's not the best option.
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.
so separate method for primary keys only is the best solution
It's fine to have it for Postgres and MySQL which seem unable to drop the PK by name but it shouldn't be part of the abstract platform API. Those platforms which can drop PK by its name shouldn't have any changes.
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 really don't understand what you propose. Do you mean define getDropPrimaryKeySQL
as:
protected function getDropPrimaryKeySQL(string $table) : string
{
$this->getDropIndexSQL('primary', $table);
}
?
I can't get rid of getDropPrimaryKeySQL
in favour getDropIndexSQL()
because $index
defined as Index|string
. So, BC break required, if you would like to use only getDropIndexSQL()
.
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.
Then, in the case when $index
is a string, you get its object representation and see if it's primary or not.
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.
You also can fix it only for your use case when the $index
comes as an object from the schema manager. If there's another case when dropping the PK by name is required, it can be solved separately.
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.
Then, in the case when $index is a string, you get its object representation and see if it's primary or not.
How I can do that? I don't know a way to do it. If you mean to find a workaround for particular getAlterTableSQL()
usage — yes, I can fix it in another way, but it's bloody stupid idea to find a big issue and solve only one of use-cases rather than fix it in principal. I've spent a lot of time on supporting this PR and would like to solve a root of evil, rather than just superficial solution of the problem.
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.
How I can do that? I don't know a way to do it.
Neither do I. That's why I'm saying it's fine if you solve only the case where the passed index is an object.
I've spent a lot of time on supporting this PR and would like to solve a root of evil, rather than just superficial solution of the problem.
The root can could be only solved by reworking the entire index-related API (I recall there was a discussion or at least mention of that /cc @deeky666). This API would account for all differences between indices, constraints, primary keys, foreign keys, etc. in different platforms and would provide some unified higher level API.
It's impossible to solve it correctly in terms of the current API without introducing more controversial methods (I'm talking about getDropPrimaryKeySQL
()). You can either solve your case in 2.x or postpone it until 3.x and solve properly.
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.
Right now, Index
class contain primary property so, really not necessary to be revised because reflect it's purpose pretty fine. The problem is that index can be supplied by name, which would be my proposal to drop in 3.x.
@morozov , thanks for your input. I will refactor this PR just to fix comparing issue soon.
Travis fails on getting https://dev.mysql.com/get/mysql-apt-config_0.8.9-1_all.deb which can be downloaded for me. |
Travis issue reported in travis-ci/travis-ci#9421. |
…stgres platform