From 10b115ccc85cd01cad0024256041ce3cac41db33 Mon Sep 17 00:00:00 2001 From: Sasha Alex Romanenko Date: Sat, 3 Sep 2016 00:54:09 -0500 Subject: [PATCH 01/31] Add PostgreSQL platform. --- src/Sql/Platform/Platform.php | 25 +++++++++++-------- src/Sql/Platform/Postgresql/Postgresql.php | 20 +++++++++++++++ test/Sql/Platform/PlatformTest.php | 23 +++++++++++++++++ .../Platform/Postgresql/PostgresqlTest.php | 24 ++++++++++++++++++ test/TestAsset/TrustingPostgresqlPlatform.php | 20 +++++++++++++++ 5 files changed, 101 insertions(+), 11 deletions(-) create mode 100644 src/Sql/Platform/Postgresql/Postgresql.php create mode 100644 test/Sql/Platform/Postgresql/PostgresqlTest.php create mode 100644 test/TestAsset/TrustingPostgresqlPlatform.php diff --git a/src/Sql/Platform/Platform.php b/src/Sql/Platform/Platform.php index ad375d5d71..5a8a43324a 100644 --- a/src/Sql/Platform/Platform.php +++ b/src/Sql/Platform/Platform.php @@ -13,6 +13,7 @@ use Zend\Db\Adapter\Platform\PlatformInterface; use Zend\Db\Adapter\StatementContainerInterface; use Zend\Db\Sql\Exception; +use Zend\Db\Sql\Platform\Postgresql; use Zend\Db\Sql\PreparableSqlInterface; use Zend\Db\Sql\SqlInterface; @@ -32,17 +33,19 @@ public function __construct(AdapterInterface $adapter) { $this->defaultPlatform = $adapter->getPlatform(); - $mySqlPlatform = new Mysql\Mysql(); - $sqlServerPlatform = new SqlServer\SqlServer(); - $oraclePlatform = new Oracle\Oracle(); - $ibmDb2Platform = new IbmDb2\IbmDb2(); - $sqlitePlatform = new Sqlite\Sqlite(); - - $this->decorators['mysql'] = $mySqlPlatform->getDecorators(); - $this->decorators['sqlserver'] = $sqlServerPlatform->getDecorators(); - $this->decorators['oracle'] = $oraclePlatform->getDecorators(); - $this->decorators['ibmdb2'] = $ibmDb2Platform->getDecorators(); - $this->decorators['sqlite'] = $sqlitePlatform->getDecorators(); + $mySqlPlatform = new Mysql\Mysql(); + $sqlServerPlatform = new SqlServer\SqlServer(); + $oraclePlatform = new Oracle\Oracle(); + $ibmDb2Platform = new IbmDb2\IbmDb2(); + $sqlitePlatform = new Sqlite\Sqlite(); + $postgresqlPlatform = new Postgresql\Postgresql(); + + $this->decorators['mysql'] = $mySqlPlatform->getDecorators(); + $this->decorators['sqlserver'] = $sqlServerPlatform->getDecorators(); + $this->decorators['oracle'] = $oraclePlatform->getDecorators(); + $this->decorators['ibmdb2'] = $ibmDb2Platform->getDecorators(); + $this->decorators['sqlite'] = $sqlitePlatform->getDecorators(); + $this->decorators['postgresql'] = $postgresqlPlatform->getDecorators(); } /** diff --git a/src/Sql/Platform/Postgresql/Postgresql.php b/src/Sql/Platform/Postgresql/Postgresql.php new file mode 100644 index 0000000000..7eb530ed59 --- /dev/null +++ b/src/Sql/Platform/Postgresql/Postgresql.php @@ -0,0 +1,20 @@ +assertEquals('sqlserver', $reflectionMethod->invoke($platform, new TestAsset\TrustingSqlServerPlatform())); $this->assertEquals('oracle', $reflectionMethod->invoke($platform, new TestAsset\TrustingOraclePlatform())); $this->assertEquals('sql92', $reflectionMethod->invoke($platform, new TestAsset\TrustingSql92Platform())); + $this->assertEquals('postgresql', $reflectionMethod->invoke($platform, new TestAsset\TrustingPostgresqlPlatform())); } /** @@ -83,6 +85,17 @@ public function testAbstractPlatformCrashesGracefullyOnMissingDefaultPlatformWit $platform->getDecorators(); } + /** + * @dataProvider availablePlatformDecorators + */ + public function testDecoratorsRegistered($adapter, $decorators) + { + $platform = new Platform($adapter); + $registeredDecorators = $platform->getDecorators(); + + $this->assertEquals($registeredDecorators, $decorators); + } + /** * @param string $platformName * @@ -105,6 +118,8 @@ protected function resolveAdapter($platformName) case 'SqlServer' : $platform = new TestAsset\TrustingSqlServerPlatform(); break; + case 'PostgreSQL' : + $platform = new TestAsset\TrustingPostgresqlPlatform(); } /* @var $mockDriver \Zend\Db\Adapter\Driver\DriverInterface|\PHPUnit_Framework_MockObject_MockObject */ @@ -117,4 +132,12 @@ protected function resolveAdapter($platformName) return new Adapter($mockDriver, $platform); } + + public function availablePlatformDecorators() + { + return [ + //@TODO add all supported platforms + [$this->resolveAdapter('PostgreSQL'), (new Postgresql())->getDecorators()], + ]; + } } diff --git a/test/Sql/Platform/Postgresql/PostgresqlTest.php b/test/Sql/Platform/Postgresql/PostgresqlTest.php new file mode 100644 index 0000000000..5264177276 --- /dev/null +++ b/test/Sql/Platform/Postgresql/PostgresqlTest.php @@ -0,0 +1,24 @@ +quoteTrustedValue($value); + } +} \ No newline at end of file From 7a2b9bb45540e757f778f65c0378ad80b4d9b0a1 Mon Sep 17 00:00:00 2001 From: Sasha Alex Romanenko Date: Sat, 3 Sep 2016 10:12:28 -0500 Subject: [PATCH 02/31] Add CreateTableDecorator. Change ends of commands with ';' --- .../Postgresql/Ddl/CreateTableDecorator.php | 37 ++++++++++++ src/Sql/Platform/Postgresql/Postgresql.php | 2 +- .../Ddl/CreateTableDecoratorTest.php | 57 +++++++++++++++++++ .../Platform/Postgresql/PostgresqlTest.php | 6 +- 4 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php create mode 100644 test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php diff --git a/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php b/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php new file mode 100644 index 0000000000..bf7fcaa10c --- /dev/null +++ b/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php @@ -0,0 +1,37 @@ +subject = $subject; + } + + protected function processStatementEnd(PlatformInterface $adapterPlatform = null) + { + return ["\n);"]; + } + +} \ No newline at end of file diff --git a/src/Sql/Platform/Postgresql/Postgresql.php b/src/Sql/Platform/Postgresql/Postgresql.php index 7eb530ed59..0bf52ae4f0 100644 --- a/src/Sql/Platform/Postgresql/Postgresql.php +++ b/src/Sql/Platform/Postgresql/Postgresql.php @@ -15,6 +15,6 @@ class Postgresql extends AbstractPlatform { public function __construct() { - + $this->setTypeDecorator('Zend\Db\Sql\Ddl\CreateTable', new Ddl\CreateTableDecorator()); } } \ No newline at end of file diff --git a/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php b/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php new file mode 100644 index 0000000000..03e83e99ea --- /dev/null +++ b/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php @@ -0,0 +1,57 @@ +setSubject($createTable); + + $createTableSql = $createTableDecorator->getSqlString(new Postgresql()); + $this->assertEquals($expectedSql, $createTableSql); + } + + public function tableDefinitionsProvider() + { + $id = new Ddl\Column\Integer('id', false, null); + $name = new Ddl\Column\Varchar('username', false, null); + $name->setLength(1024); + + + $columnsOnly = new CreateTable('columns_only'); + $columnsOnly->addColumn($id); + $columnsOnly->addColumn($name); + + $expectedColumnsOnly = 'CREATE TABLE "columns_only" ( '."\n". + ' "id" INTEGER NOT NULL,'."\n". + ' "username" VARCHAR(1024) NOT NULL '."\n". + ');'; + + return [ + [$columnsOnly, $expectedColumnsOnly] + ]; + } + +} diff --git a/test/Sql/Platform/Postgresql/PostgresqlTest.php b/test/Sql/Platform/Postgresql/PostgresqlTest.php index 5264177276..ac12f1c887 100644 --- a/test/Sql/Platform/Postgresql/PostgresqlTest.php +++ b/test/Sql/Platform/Postgresql/PostgresqlTest.php @@ -14,11 +14,15 @@ class PostgresqlTest extends \PHPUnit_Framework_TestCase { /* - * @testdox unit test / object test: + * @testdox unit test / object test: Has CreateTable proxy * @covers Zend\Db\Sql\Platform\Postgresql\Postgresql::__construct */ public function testConstruct() { + $postgresql = new Postgresql(); + $decorators = $postgresql->getDecorators(); + $this->assertArrayHasKey('Zend\Db\Sql\Ddl\CreateTable', $decorators); + $this->assertInstanceOf(Ddl\CreateTableDecorator::class, $decorators['Zend\Db\Sql\Ddl\CreateTable']); } } From 0c5845d478d65c10a64abafa9098281742ed7f2c Mon Sep 17 00:00:00 2001 From: Sasha Alex Romanenko Date: Sat, 3 Sep 2016 11:06:36 -0500 Subject: [PATCH 03/31] Index constructor signature does not follow docs and the overridden Constraint construct. It also makes decorator creation at Platform level impossible. To avoid missing parameter and causing invalid syntaxes, will propose adding exceptions in all DB components instead of getting cryptic SQL errors in logs. --- src/Sql/Ddl/Index/Index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Sql/Ddl/Index/Index.php b/src/Sql/Ddl/Index/Index.php index a0239089a4..704fab0a14 100644 --- a/src/Sql/Ddl/Index/Index.php +++ b/src/Sql/Ddl/Index/Index.php @@ -26,7 +26,7 @@ class Index extends AbstractIndex * @param null|string $name * @param array $lengths */ - public function __construct($columns, $name = null, array $lengths = []) + public function __construct($columns = null, $name = null, array $lengths = []) { $this->setColumns($columns); From 030fbebe9f58582cd895c898be836ed3642a6732 Mon Sep 17 00:00:00 2001 From: Sasha Alex Romanenko Date: Sat, 3 Sep 2016 11:30:08 -0500 Subject: [PATCH 04/31] Add Postgres Index Decorator. Modifies specification to eventually construct CREATE INDEX index_name ON table_name (columns...) Intended to be used within CreateTableDecorator, but can also add it to platform if needed, as long as table name is supplied somehow. --- .../Postgresql/Ddl/Index/IndexDecorator.php | 61 +++++++++++++++++++ .../Ddl/CreateTableDecoratorTest.php | 9 +-- .../Ddl/Index/IndexDecoratorTest.php | 52 ++++++++++++++++ 3 files changed, 118 insertions(+), 4 deletions(-) create mode 100644 src/Sql/Platform/Postgresql/Ddl/Index/IndexDecorator.php create mode 100644 test/Sql/Platform/Postgresql/Ddl/Index/IndexDecoratorTest.php diff --git a/src/Sql/Platform/Postgresql/Ddl/Index/IndexDecorator.php b/src/Sql/Platform/Postgresql/Ddl/Index/IndexDecorator.php new file mode 100644 index 0000000000..ec0e0428aa --- /dev/null +++ b/src/Sql/Platform/Postgresql/Ddl/Index/IndexDecorator.php @@ -0,0 +1,61 @@ +subject = $subject; + $this->subject->specification = $this->specification; + } + + public function setTable($table) + { + $this->table = $table; + } + + /** + * @inheritDoc + */ + public function getExpressionData() + { + if (!$this->table) { + throw new InvalidQueryException('PostgreSQL Index needs table name specified.'); + } + + $expressionData = $this->subject->getExpressionData(); + + // [0] => specification + // [1] => values + // [2] => types + array_splice($expressionData[0][1], 1, 0, $this->table); + array_splice($expressionData[0][2], 1, 0, self::TYPE_IDENTIFIER); + + return $expressionData; + } +} diff --git a/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php b/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php index 03e83e99ea..c309aa4405 100644 --- a/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php +++ b/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php @@ -44,10 +44,11 @@ public function tableDefinitionsProvider() $columnsOnly->addColumn($id); $columnsOnly->addColumn($name); - $expectedColumnsOnly = 'CREATE TABLE "columns_only" ( '."\n". - ' "id" INTEGER NOT NULL,'."\n". - ' "username" VARCHAR(1024) NOT NULL '."\n". - ');'; + $expectedColumnsOnly = + 'CREATE TABLE "columns_only" ( '."\n". + ' "id" INTEGER NOT NULL,'."\n". + ' "username" VARCHAR(1024) NOT NULL '."\n". + ');'; return [ [$columnsOnly, $expectedColumnsOnly] diff --git a/test/Sql/Platform/Postgresql/Ddl/Index/IndexDecoratorTest.php b/test/Sql/Platform/Postgresql/Ddl/Index/IndexDecoratorTest.php new file mode 100644 index 0000000000..fc156298f1 --- /dev/null +++ b/test/Sql/Platform/Postgresql/Ddl/Index/IndexDecoratorTest.php @@ -0,0 +1,52 @@ +setName('test_index'); + $index->setColumns(['test_column_one', 'test_column_two']); + + $postgresIndex = new IndexDecorator(); + $postgresIndex->setSubject($index); + $postgresIndex->setTable('test_table'); // PostgreSQL must have table name to operate on, unlike other engines + + $expressionData = $postgresIndex->getExpressionData()[0]; + + // [0] => specification + // [1] => values + // [2] => types + $this->assertEquals('CREATE INDEX %s ON %s(%s, %s)', $expressionData[0]); + $this->assertEquals(['test_index', 'test_table', 'test_column_one', 'test_column_two'], $expressionData[1]); + $this->assertEquals( + [Index::TYPE_IDENTIFIER, Index::TYPE_IDENTIFIER, Index::TYPE_IDENTIFIER, Index::TYPE_IDENTIFIER], + $expressionData[2] + ); + } + + public function testExceptionThrownIfNoTableSpecified() + { + $postgresIndex = new IndexDecorator(); + + $this->setExpectedException(InvalidQueryException::class); + $postgresIndex->getExpressionData(); + } +} From 5706b0cdefd73b8323b43b383d5b0dda4ef8b32f Mon Sep 17 00:00:00 2001 From: Sasha Alex Romanenko Date: Sat, 3 Sep 2016 15:11:23 -0500 Subject: [PATCH 05/31] Process indexes separately from constraints, after CreateTable specification. --- .../Postgresql/Ddl/CreateTableDecorator.php | 86 ++++++++++++++++++- .../Ddl/CreateTableDecoratorTest.php | 46 ++++++++-- 2 files changed, 123 insertions(+), 9 deletions(-) diff --git a/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php b/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php index bf7fcaa10c..05f83461f1 100644 --- a/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php +++ b/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php @@ -9,26 +9,108 @@ namespace Zend\Db\Sql\Platform\Postgresql\Ddl; +use Zend\Db\Adapter\Driver\DriverInterface; +use Zend\Db\Adapter\ParameterContainer; use Zend\Db\Sql\Ddl\CreateTable; +use Zend\Db\Sql\Ddl\Index\Index; use Zend\Db\Sql\Platform\PlatformDecoratorInterface; use Zend\Db\Adapter\Platform\PlatformInterface; +use Zend\Db\Sql\Platform\Postgresql\Ddl\Index\IndexDecorator; class CreateTableDecorator extends CreateTable implements PlatformDecoratorInterface { + const INDEXES = 'indexes'; /** * @var CreateTable */ protected $subject; + /** + * @var string[] + */ + protected $indexes = []; + /** * @inheritDoc */ - public function setSubject($subject) - { + protected $indexSpecification = [ + self::INDEXES => [ + "\n%1\$s" => [ + [1 => '%1$s;', 'combinedby' => "\n"] + ] + ] + ]; + + /** + * @param $subject + * @return mixed + */ + public function setSubject($subject) { $this->subject = $subject; + + $this->specifications = array_merge($this->specifications, $this->indexSpecification); + $this->subject->specifications = $this->specifications; + + return $this; + } + + /** + * @inheritDoc + */ + protected function buildSqlString( + PlatformInterface $platform, + DriverInterface $driver = null, + ParameterContainer $parameterContainer = null + ) { + $this->separateIndexesFromConstraints(); + + return parent::buildSqlString($platform, $driver, $parameterContainer); } + private function separateIndexesFromConstraints() + { + // take advantage of PHP's ability to access protected properties of different instances created from same class + $this->indexes = array_filter($this->subject->constraints, function($constraint) { + return $constraint instanceof Index; + }); + + $filteredConstraints = array_filter($this->subject->constraints, function($constraint) { + return !($constraint instanceof Index); + }); + + $this->subject->constraints = $filteredConstraints; + + array_walk($this->indexes, function (&$index, $key) { + $indexDecorator = new IndexDecorator(); + $indexDecorator->setSubject($index); + $indexDecorator->setTable($this->subject->table); + $index = $indexDecorator; + }); + } + + /** + * @param PlatformInterface|null $adapterPlatform + * @return array|void + */ + protected function processIndexes(PlatformInterface $adapterPlatform = null) { + if (!$this->indexes) { + return; + } + + $sqls = []; + + foreach ($this->indexes as $index) { + $sqls[] = $this->processExpression($index, $adapterPlatform); + } + + return [$sqls]; + } + + /** + * @param PlatformInterface|null $adapterPlatform + * @return array|void + */ protected function processStatementEnd(PlatformInterface $adapterPlatform = null) { return ["\n);"]; diff --git a/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php b/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php index c309aa4405..6a15e61c47 100644 --- a/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php +++ b/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php @@ -19,7 +19,7 @@ class CreateTableDecoratorTest extends \PHPUnit_Framework_TestCase /** * @testdox integration test: Testing CreateTableDecorator will use CreateTable as an internal state to adjust Index creation to be a separate statement * @covers Zend\Db\Sql\Platform\Postgresql\Ddl\CreateTableDecorator::setSubject - * @covers Zend\Db\Sql\Platform\Postgresql\Ddl\CreateTableDecorator::processConstraints + * @covers Zend\Db\Sql\Platform\Postgresql\Ddl\CreateTableDecorator::buildSqlString * @covers Zend\Db\Sql\Platform\Postgresql\Ddl\CreateTableDecorator::processIndexes * @covers Zend\Db\Sql\Platform\Postgresql\Ddl\CreateTableDecorator::processStatementEnd * @dataProvider tableDefinitionsProvider @@ -38,20 +38,52 @@ public function tableDefinitionsProvider() $id = new Ddl\Column\Integer('id', false, null); $name = new Ddl\Column\Varchar('username', false, null); $name->setLength(1024); + $nameUnique = new Ddl\Constraint\UniqueKey('username'); + + $idIndex = new Ddl\Index\Index('id', 'id_idx'); + $nameIndex = new Ddl\Index\Index('username', 'username_index'); $columnsOnly = new CreateTable('columns_only'); $columnsOnly->addColumn($id); $columnsOnly->addColumn($name); - $expectedColumnsOnly = - 'CREATE TABLE "columns_only" ( '."\n". - ' "id" INTEGER NOT NULL,'."\n". - ' "username" VARCHAR(1024) NOT NULL '."\n". - ');'; + $withSingleIndex = new CreateTable('with_single_index'); + $withSingleIndex->addColumn($id); + $withSingleIndex->addColumn($name); + $withSingleIndex->addConstraint($idIndex); + + $mixed = new CreateTable('mixed'); + $mixed->addColumn($id); + $mixed->addColumn($name); + $mixed->addConstraint($nameUnique); + $mixed->addConstraint($idIndex); + $mixed->addConstraint($nameIndex); + + + $expectedColumnsOnly = 'CREATE TABLE "columns_only" ( ' . "\n" + . ' "id" INTEGER NOT NULL,' . "\n" + . ' "username" VARCHAR(1024) NOT NULL ' . "\n" + . ');'; + + $expectedWithSingleInstance = 'CREATE TABLE "with_single_index" ( ' . "\n" + . ' "id" INTEGER NOT NULL,' . "\n" + . ' "username" VARCHAR(1024) NOT NULL ' . "\n" + . '); ' . "\n" + . 'CREATE INDEX "id_idx" ON "with_single_index"("id");'; + + $expectedMixed = 'CREATE TABLE "mixed" ( ' . "\n" + . ' "id" INTEGER NOT NULL,' . "\n" + . ' "username" VARCHAR(1024) NOT NULL , ' . "\n" + . ' UNIQUE ("username") ' . "\n" + . '); ' . "\n" + . 'CREATE INDEX "id_idx" ON "mixed"("id");' . "\n" + . 'CREATE INDEX "username_index" ON "mixed"("username");'; return [ - [$columnsOnly, $expectedColumnsOnly] + [$columnsOnly, $expectedColumnsOnly], + [$withSingleIndex, $expectedWithSingleInstance], + [$mixed, $expectedMixed] ]; } From 4a3c918b4dfa7fd76785b5bae842fc6e1f450910 Mon Sep 17 00:00:00 2001 From: Sasha Alex Romanenko Date: Sat, 3 Sep 2016 16:17:17 -0500 Subject: [PATCH 06/31] Add AlterTableDecorator to PostgreSQL platform. --- .../Postgresql/Ddl/AlterTableDecorator.php | 22 +++++++++++++++++++ src/Sql/Platform/Postgresql/Postgresql.php | 1 + .../Platform/Postgresql/PostgresqlTest.php | 2 ++ 3 files changed, 25 insertions(+) create mode 100644 src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php diff --git a/src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php b/src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php new file mode 100644 index 0000000000..c96dac0e9a --- /dev/null +++ b/src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php @@ -0,0 +1,22 @@ +subject = $subject; + } + +} diff --git a/src/Sql/Platform/Postgresql/Postgresql.php b/src/Sql/Platform/Postgresql/Postgresql.php index 0bf52ae4f0..f54760d9c4 100644 --- a/src/Sql/Platform/Postgresql/Postgresql.php +++ b/src/Sql/Platform/Postgresql/Postgresql.php @@ -16,5 +16,6 @@ class Postgresql extends AbstractPlatform public function __construct() { $this->setTypeDecorator('Zend\Db\Sql\Ddl\CreateTable', new Ddl\CreateTableDecorator()); + $this->setTypeDecorator('Zend\Db\Sql\Ddl\AlterTable', new Ddl\AlterTableDecorator()); } } \ No newline at end of file diff --git a/test/Sql/Platform/Postgresql/PostgresqlTest.php b/test/Sql/Platform/Postgresql/PostgresqlTest.php index ac12f1c887..a368fa1c78 100644 --- a/test/Sql/Platform/Postgresql/PostgresqlTest.php +++ b/test/Sql/Platform/Postgresql/PostgresqlTest.php @@ -24,5 +24,7 @@ public function testConstruct() $this->assertArrayHasKey('Zend\Db\Sql\Ddl\CreateTable', $decorators); $this->assertInstanceOf(Ddl\CreateTableDecorator::class, $decorators['Zend\Db\Sql\Ddl\CreateTable']); + $this->assertArrayHasKey('Zend\Db\Sql\Ddl\AlterTable', $decorators); + $this->assertInstanceOf(Ddl\AlterTableDecorator::class, $decorators['Zend\Db\Sql\Ddl\AlterTable']); } } From 7a0bd3bfd61b4c749bedea80a3b8665d6787aea6 Mon Sep 17 00:00:00 2001 From: Sasha Alex Romanenko Date: Sat, 3 Sep 2016 18:09:37 -0500 Subject: [PATCH 07/31] styles --- src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php | 2 +- test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php b/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php index 05f83461f1..7266c1048e 100644 --- a/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php +++ b/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php @@ -27,7 +27,7 @@ class CreateTableDecorator extends CreateTable implements PlatformDecoratorInter protected $subject; /** - * @var string[] + * @var IndexDecorator[] */ protected $indexes = []; diff --git a/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php b/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php index 6a15e61c47..b9cedbf151 100644 --- a/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php +++ b/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php @@ -83,7 +83,7 @@ public function tableDefinitionsProvider() return [ [$columnsOnly, $expectedColumnsOnly], [$withSingleIndex, $expectedWithSingleInstance], - [$mixed, $expectedMixed] + [$mixed, $expectedMixed], ]; } From 334685ef689ac66db3357706a2b8e33bf3c065a2 Mon Sep 17 00:00:00 2001 From: Sasha Alex Romanenko Date: Mon, 5 Sep 2016 00:12:41 -0500 Subject: [PATCH 08/31] Add support for index creation in PostgreSQL AlterTableDecorator --- .../Postgresql/Ddl/AlterTableDecorator.php | 118 +++++++++++++++++- .../Ddl/AlterTableDecoratorTest.php | 65 ++++++++++ 2 files changed, 182 insertions(+), 1 deletion(-) create mode 100644 test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php diff --git a/src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php b/src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php index c96dac0e9a..b7d07c51e1 100644 --- a/src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php +++ b/src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php @@ -1,22 +1,138 @@ '%1$s', + self::ADD_INDEXES => [ + "%1\$s" => [ + [1 => '%1$s;', 'combinedby' => "\n"] + ] + ], + ]; /** * @inheritDoc */ public function setSubject($subject) { $this->subject = $subject; + + return $this; } + /** + * @inheritDoc + */ + protected function buildSqlString( + PlatformInterface $platform, + DriverInterface $driver = null, + ParameterContainer $parameterContainer = null + ) { + $this->separateIndexesFromConstraints(); + $this->deleteUnneededSpecification(); + + $alterTable = ''; + if ($this->hasCreateTable) { + $alterTable = parent::buildSqlString($platform, $driver, $parameterContainer); + $this->subject->specifications = $this->specifications = $this->indexSpecification; + } + + $indexes = parent::buildSqlString($platform, $driver, $parameterContainer); + return $alterTable.$indexes; + } + + private function separateIndexesFromConstraints() + { + // take advantage of PHP's ability to access protected properties of different instances created from same class + $this->addIndexes = array_filter($this->subject->addConstraints, function($constraint) { + return $constraint instanceof Index; + }); + + $filteredConstraints = array_filter($this->subject->addConstraints, function($constraint) { + return !($constraint instanceof Index); + }); + + $this->subject->addConstraints = $filteredConstraints; + + array_walk($this->addIndexes, function (&$index, $key) { + $indexDecorator = new IndexDecorator(); + $indexDecorator->setSubject($index); + $indexDecorator->setTable($this->subject->table); + $index = $indexDecorator; + }); + } + + /** + * @param PlatformInterface|null $adapterPlatform + * @return array|void + */ + protected function processAddIndexes(PlatformInterface $adapterPlatform = null) { + if (!$this->addIndexes) { + return; + } + + $sqls = []; + + foreach ($this->addIndexes as $index) { + $sqls[] = $this->processExpression($index, $adapterPlatform); + } + + return [$sqls]; + } + + /** + * @param PlatformInterface|null $adapterPlatform + * @return array|void + */ + protected function processStatementEnd(PlatformInterface $adapterPlatform = null) + { + return [";\n"]; + } + + private function deleteUnneededSpecification() + { + $subject = $this->subject; + if (!($subject->addColumns || $subject->changeColumns || $subject->dropColumns + || $subject->addConstraints || $subject->dropConstraints)) { + + $this->hasCreateTable = false; + + unset($this->indexSpecification['statementEnd']); + $this->subject->specifications = $this->specifications = $this->indexSpecification; + } + } } diff --git a/test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php b/test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php new file mode 100644 index 0000000000..f38d610bed --- /dev/null +++ b/test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php @@ -0,0 +1,65 @@ +setSubject($createTable); + + $adapterPlatform = new Postgresql(); + $createTableSql = $alterTableDecorator->getSqlString($adapterPlatform); + $this->assertEquals($expectedSql, $createTableSql); + } + + public function tableAlterationsProvider() + { + $newIdx = new Ddl\Index\Index('field_1', 'new_idx'); + $newField_2 = new Ddl\Column\Varchar('field_2'); + $newField_2->setLength(1024); + + // AlterTable on its own + $noIndex = new Ddl\AlterTable('no_index'); + $noIndex->addColumn($newField_2); + + // AlterTable on its own + $onlyIndex = new Ddl\AlterTable('only_index'); + $onlyIndex->addConstraint($newIdx); + + // AlterTable with Create Index + $mixedAddIndex = new Ddl\AlterTable('mixed_index'); + $mixedAddIndex->addColumn($newField_2); + $mixedAddIndex->addConstraint($newIdx); + + $expectedNewFieldNoIndex = 'ALTER TABLE "no_index"' . "\n" + . ' ADD COLUMN "field_2" VARCHAR(1024) NOT NULL;'; + + $expectedOnlyIndex = 'CREATE INDEX "new_idx" ON "only_index"("field_1");'; + + $expectedMixedAddIndex = 'ALTER TABLE "mixed_index"' . "\n" + . ' ADD COLUMN "field_2" VARCHAR(1024) NOT NULL;' . "\n" + . ' CREATE INDEX "new_idx" ON "mixed_index"("field_1");'; + + return [ + [$noIndex, $expectedNewFieldNoIndex], + [$onlyIndex, $expectedOnlyIndex], + [$mixedAddIndex, $expectedMixedAddIndex], + ]; + } +} From ccaa6bf9dd2dc218b576576799796ee0ebde996b Mon Sep 17 00:00:00 2001 From: Sasha Alex Romanenko Date: Mon, 5 Sep 2016 00:17:56 -0500 Subject: [PATCH 09/31] Keep expected strings together with test API stubs. --- .../Ddl/AlterTableDecoratorTest.php | 10 +++---- .../Ddl/CreateTableDecoratorTest.php | 28 ++++++++++--------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php b/test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php index f38d610bed..9efeb71b4c 100644 --- a/test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php +++ b/test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php @@ -38,20 +38,20 @@ public function tableAlterationsProvider() $noIndex = new Ddl\AlterTable('no_index'); $noIndex->addColumn($newField_2); + $expectedNewFieldNoIndex = 'ALTER TABLE "no_index"' . "\n" + . ' ADD COLUMN "field_2" VARCHAR(1024) NOT NULL;'; + // AlterTable on its own $onlyIndex = new Ddl\AlterTable('only_index'); $onlyIndex->addConstraint($newIdx); + $expectedOnlyIndex = 'CREATE INDEX "new_idx" ON "only_index"("field_1");'; + // AlterTable with Create Index $mixedAddIndex = new Ddl\AlterTable('mixed_index'); $mixedAddIndex->addColumn($newField_2); $mixedAddIndex->addConstraint($newIdx); - $expectedNewFieldNoIndex = 'ALTER TABLE "no_index"' . "\n" - . ' ADD COLUMN "field_2" VARCHAR(1024) NOT NULL;'; - - $expectedOnlyIndex = 'CREATE INDEX "new_idx" ON "only_index"("field_1");'; - $expectedMixedAddIndex = 'ALTER TABLE "mixed_index"' . "\n" . ' ADD COLUMN "field_2" VARCHAR(1024) NOT NULL;' . "\n" . ' CREATE INDEX "new_idx" ON "mixed_index"("field_1");'; diff --git a/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php b/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php index b9cedbf151..2884ceb27e 100644 --- a/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php +++ b/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php @@ -43,16 +43,30 @@ public function tableDefinitionsProvider() $idIndex = new Ddl\Index\Index('id', 'id_idx'); $nameIndex = new Ddl\Index\Index('username', 'username_index'); - + // CREATE TABLE only. $columnsOnly = new CreateTable('columns_only'); $columnsOnly->addColumn($id); $columnsOnly->addColumn($name); + $expectedColumnsOnly = 'CREATE TABLE "columns_only" ( ' . "\n" + . ' "id" INTEGER NOT NULL,' . "\n" + . ' "username" VARCHAR(1024) NOT NULL ' . "\n" + . ');'; + + // CreateTable with Create Index $withSingleIndex = new CreateTable('with_single_index'); $withSingleIndex->addColumn($id); $withSingleIndex->addColumn($name); $withSingleIndex->addConstraint($idIndex); + $expectedWithSingleInstance = 'CREATE TABLE "with_single_index" ( ' . "\n" + . ' "id" INTEGER NOT NULL,' . "\n" + . ' "username" VARCHAR(1024) NOT NULL ' . "\n" + . '); ' . "\n" + . 'CREATE INDEX "id_idx" ON "with_single_index"("id");'; + + + // Mixed handling of index separation from constraints. $mixed = new CreateTable('mixed'); $mixed->addColumn($id); $mixed->addColumn($name); @@ -60,18 +74,6 @@ public function tableDefinitionsProvider() $mixed->addConstraint($idIndex); $mixed->addConstraint($nameIndex); - - $expectedColumnsOnly = 'CREATE TABLE "columns_only" ( ' . "\n" - . ' "id" INTEGER NOT NULL,' . "\n" - . ' "username" VARCHAR(1024) NOT NULL ' . "\n" - . ');'; - - $expectedWithSingleInstance = 'CREATE TABLE "with_single_index" ( ' . "\n" - . ' "id" INTEGER NOT NULL,' . "\n" - . ' "username" VARCHAR(1024) NOT NULL ' . "\n" - . '); ' . "\n" - . 'CREATE INDEX "id_idx" ON "with_single_index"("id");'; - $expectedMixed = 'CREATE TABLE "mixed" ( ' . "\n" . ' "id" INTEGER NOT NULL,' . "\n" . ' "username" VARCHAR(1024) NOT NULL , ' . "\n" From 2803f4ed5fbc78d05c422855a649a33dfec2729f Mon Sep 17 00:00:00 2001 From: Sasha Alex Romanenko Date: Mon, 5 Sep 2016 12:36:35 -0500 Subject: [PATCH 10/31] dropConstraint() now supports index-related constraints. Because AlterTable interface signature currently only takes in string, not AbstractConstraint, not able to inspect the types to determine best query to run. Modify specification to have IF EXISTS and perform operation both in ALTER TABLE and DROP INDEX DDL to account for possibility of either. Get a notice in database logs, but better than overhead and extra dependency on performing a meta query. --- .../Postgresql/Ddl/AlterTableDecorator.php | 55 +++++++++++++++++++ .../Ddl/AlterTableDecoratorTest.php | 23 ++++++-- 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php b/src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php index b7d07c51e1..0853a89e4c 100644 --- a/src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php +++ b/src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php @@ -20,6 +20,7 @@ class AlterTableDecorator extends AlterTable implements PlatformDecoratorInterface { const ADD_INDEXES = 'addIndexes'; + const DROP_INDEXES = 'dropIndexes'; /** * @var AlterTable @@ -31,11 +32,28 @@ class AlterTableDecorator extends AlterTable implements PlatformDecoratorInterfa */ protected $addIndexes = []; + /** + * @var string[] + */ + protected $dropIndexes = []; + /** * @var bool */ private $hasCreateTable = true; + /** + * Compensate for dropConstraint() interface not distinguishing between string and index object. + * Add IF EXISTS for safety to handle either until/if new signature is approved. + * + * @var array + */ + protected $dropConstraintSpecification = [ + "%1\$s" => [ + [1 => "DROP CONSTRAINT IF EXISTS %1\$s,\n", 'combinedby' => ""], + ] + ]; + protected $indexSpecification = [ 'statementEnd' => '%1$s', self::ADD_INDEXES => [ @@ -43,6 +61,11 @@ class AlterTableDecorator extends AlterTable implements PlatformDecoratorInterfa [1 => '%1$s;', 'combinedby' => "\n"] ] ], + self::DROP_INDEXES => [ + "%1\$s" => [ + [1 => 'DROP INDEX IF EXISTS %1$s;', 'combinedby' => "\n"] + ] + ] ]; /** @@ -51,6 +74,9 @@ class AlterTableDecorator extends AlterTable implements PlatformDecoratorInterfa public function setSubject($subject) { $this->subject = $subject; + $this->specifications[self::DROP_CONSTRAINTS] = $this->dropConstraintSpecification; + $this->subject->specifications = $this->specifications; + return $this; } @@ -63,8 +89,13 @@ protected function buildSqlString( ParameterContainer $parameterContainer = null ) { $this->separateIndexesFromConstraints(); + $this->duplicateDropConstraintToDropIndex(); $this->deleteUnneededSpecification(); + // unlike CreateTableDecorator where CREATE TABLE is always present for new tables, regardless of Incex creation + // PostgreSQL does not use ALTER TABLE to add/drop indexes to existing tables. + // Therefore, if the only change is index related, DDL would have dangling ALTER TABLE. + // Consequently, table alterations outside of ALTER TABLE syntax get processed as whole different specification chunk $alterTable = ''; if ($this->hasCreateTable) { $alterTable = parent::buildSqlString($platform, $driver, $parameterContainer); @@ -96,6 +127,16 @@ private function separateIndexesFromConstraints() }); } + /** + * DROP CONSTRAINT always with DROP INDEX to compensate for dropConstraint() interface + * only accepting strings, not inspectable objects. + * @TODO if new signature removeConstraint(string|AbstractConstraint) gets approved, delete this method + */ + private function duplicateDropConstraintToDropIndex() + { + $this->dropIndexes = $this->subject->dropConstraints; + } + /** * @param PlatformInterface|null $adapterPlatform * @return array|void @@ -114,6 +155,20 @@ protected function processAddIndexes(PlatformInterface $adapterPlatform = null) return [$sqls]; } + protected function processDropIndexes(PlatformInterface $adapterPlatform = null) { + if (!$this->dropIndexes) { + return; + } + + $sqls = []; + + foreach ($this->dropIndexes as $index) { + $sqls[] = $adapterPlatform->quoteIdentifier($index); + } + + return [$sqls]; + } + /** * @param PlatformInterface|null $adapterPlatform * @return array|void diff --git a/test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php b/test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php index 9efeb71b4c..be6155dac2 100644 --- a/test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php +++ b/test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php @@ -24,7 +24,7 @@ public function testGetSqlString(AlterTable $createTable, $expectedSql) $alterTableDecorator->setSubject($createTable); $adapterPlatform = new Postgresql(); - $createTableSql = $alterTableDecorator->getSqlString($adapterPlatform); + $createTableSql = $this->trimExtraIndents($alterTableDecorator->getSqlString($adapterPlatform)); $this->assertEquals($expectedSql, $createTableSql); } @@ -39,7 +39,7 @@ public function tableAlterationsProvider() $noIndex->addColumn($newField_2); $expectedNewFieldNoIndex = 'ALTER TABLE "no_index"' . "\n" - . ' ADD COLUMN "field_2" VARCHAR(1024) NOT NULL;'; + . 'ADD COLUMN "field_2" VARCHAR(1024) NOT NULL;'; // AlterTable on its own $onlyIndex = new Ddl\AlterTable('only_index'); @@ -53,13 +53,28 @@ public function tableAlterationsProvider() $mixedAddIndex->addConstraint($newIdx); $expectedMixedAddIndex = 'ALTER TABLE "mixed_index"' . "\n" - . ' ADD COLUMN "field_2" VARCHAR(1024) NOT NULL;' . "\n" - . ' CREATE INDEX "new_idx" ON "mixed_index"("field_1");'; + . 'ADD COLUMN "field_2" VARCHAR(1024) NOT NULL;' . "\n" + . 'CREATE INDEX "new_idx" ON "mixed_index"("field_1");'; + + // Drop Index + // DROP CONSTRAINT always with DROP INDEX to compensate for dropConstraint() + // interface only accepting strings, not inspectable object. + $dropIndex = new Ddl\AlterTable('drop_index'); + $dropIndex->dropConstraint('new_idx'); + + $expectedOnlyDropIndex = 'ALTER TABLE "drop_index"' . "\n" + . 'DROP CONSTRAINT IF EXISTS "new_idx";' . "\n" + . 'DROP INDEX IF EXISTS "new_idx";'; return [ [$noIndex, $expectedNewFieldNoIndex], [$onlyIndex, $expectedOnlyIndex], [$mixedAddIndex, $expectedMixedAddIndex], + [$dropIndex, $expectedOnlyDropIndex] ]; } + + private function trimExtraIndents($sqlString) { + return join("\n", array_map("trim", explode("\n", $sqlString))); + } } From 8b3e1d405363d44319902802086b7378a6524d58 Mon Sep 17 00:00:00 2001 From: Sasha Alex Romanenko Date: Tue, 6 Sep 2016 12:13:02 -0500 Subject: [PATCH 11/31] code sniffer rules --- src/Sql/Platform/Platform.php | 1 - .../Postgresql/Ddl/AlterTableDecorator.php | 14 ++++++++------ .../Postgresql/Ddl/CreateTableDecorator.php | 11 ++++++----- src/Sql/Platform/Postgresql/Postgresql.php | 2 +- .../Postgresql/Ddl/AlterTableDecoratorTest.php | 9 +++++---- .../Postgresql/Ddl/CreateTableDecoratorTest.php | 2 -- test/Sql/Platform/Postgresql/PostgresqlTest.php | 3 ++- 7 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/Sql/Platform/Platform.php b/src/Sql/Platform/Platform.php index 5a8a43324a..452eaa6091 100644 --- a/src/Sql/Platform/Platform.php +++ b/src/Sql/Platform/Platform.php @@ -13,7 +13,6 @@ use Zend\Db\Adapter\Platform\PlatformInterface; use Zend\Db\Adapter\StatementContainerInterface; use Zend\Db\Sql\Exception; -use Zend\Db\Sql\Platform\Postgresql; use Zend\Db\Sql\PreparableSqlInterface; use Zend\Db\Sql\SqlInterface; diff --git a/src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php b/src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php index 0853a89e4c..c02a2c1967 100644 --- a/src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php +++ b/src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php @@ -71,7 +71,8 @@ class AlterTableDecorator extends AlterTable implements PlatformDecoratorInterfa /** * @inheritDoc */ - public function setSubject($subject) { + public function setSubject($subject) + { $this->subject = $subject; $this->specifications[self::DROP_CONSTRAINTS] = $this->dropConstraintSpecification; @@ -109,11 +110,11 @@ protected function buildSqlString( private function separateIndexesFromConstraints() { // take advantage of PHP's ability to access protected properties of different instances created from same class - $this->addIndexes = array_filter($this->subject->addConstraints, function($constraint) { + $this->addIndexes = array_filter($this->subject->addConstraints, function ($constraint) { return $constraint instanceof Index; }); - $filteredConstraints = array_filter($this->subject->addConstraints, function($constraint) { + $filteredConstraints = array_filter($this->subject->addConstraints, function ($constraint) { return !($constraint instanceof Index); }); @@ -141,7 +142,8 @@ private function duplicateDropConstraintToDropIndex() * @param PlatformInterface|null $adapterPlatform * @return array|void */ - protected function processAddIndexes(PlatformInterface $adapterPlatform = null) { + protected function processAddIndexes(PlatformInterface $adapterPlatform = null) + { if (!$this->addIndexes) { return; } @@ -155,7 +157,8 @@ protected function processAddIndexes(PlatformInterface $adapterPlatform = null) return [$sqls]; } - protected function processDropIndexes(PlatformInterface $adapterPlatform = null) { + protected function processDropIndexes(PlatformInterface $adapterPlatform = null) + { if (!$this->dropIndexes) { return; } @@ -183,7 +186,6 @@ private function deleteUnneededSpecification() $subject = $this->subject; if (!($subject->addColumns || $subject->changeColumns || $subject->dropColumns || $subject->addConstraints || $subject->dropConstraints)) { - $this->hasCreateTable = false; unset($this->indexSpecification['statementEnd']); diff --git a/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php b/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php index 7266c1048e..50ca7e1716 100644 --- a/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php +++ b/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php @@ -46,7 +46,8 @@ class CreateTableDecorator extends CreateTable implements PlatformDecoratorInter * @param $subject * @return mixed */ - public function setSubject($subject) { + public function setSubject($subject) + { $this->subject = $subject; $this->specifications = array_merge($this->specifications, $this->indexSpecification); @@ -71,11 +72,11 @@ protected function buildSqlString( private function separateIndexesFromConstraints() { // take advantage of PHP's ability to access protected properties of different instances created from same class - $this->indexes = array_filter($this->subject->constraints, function($constraint) { + $this->indexes = array_filter($this->subject->constraints, function ($constraint) { return $constraint instanceof Index; }); - $filteredConstraints = array_filter($this->subject->constraints, function($constraint) { + $filteredConstraints = array_filter($this->subject->constraints, function ($constraint) { return !($constraint instanceof Index); }); @@ -93,7 +94,8 @@ private function separateIndexesFromConstraints() * @param PlatformInterface|null $adapterPlatform * @return array|void */ - protected function processIndexes(PlatformInterface $adapterPlatform = null) { + protected function processIndexes(PlatformInterface $adapterPlatform = null) + { if (!$this->indexes) { return; } @@ -115,5 +117,4 @@ protected function processStatementEnd(PlatformInterface $adapterPlatform = null { return ["\n);"]; } - } \ No newline at end of file diff --git a/src/Sql/Platform/Postgresql/Postgresql.php b/src/Sql/Platform/Postgresql/Postgresql.php index f54760d9c4..83b114a496 100644 --- a/src/Sql/Platform/Postgresql/Postgresql.php +++ b/src/Sql/Platform/Postgresql/Postgresql.php @@ -18,4 +18,4 @@ public function __construct() $this->setTypeDecorator('Zend\Db\Sql\Ddl\CreateTable', new Ddl\CreateTableDecorator()); $this->setTypeDecorator('Zend\Db\Sql\Ddl\AlterTable', new Ddl\AlterTableDecorator()); } -} \ No newline at end of file +} diff --git a/test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php b/test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php index be6155dac2..c42fa852a1 100644 --- a/test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php +++ b/test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php @@ -12,9 +12,9 @@ use Zend\Db\Adapter\Platform\Postgresql; use Zend\Db\Sql\Ddl\AlterTable; use Zend\Db\Sql\Ddl; -use Zend\Db\Sql\Platform\Postgresql\Ddl\AlterTableDecorator; -class AlterTableDecoratorTest extends \PHPUnit_Framework_TestCase { +class AlterTableDecoratorTest extends \PHPUnit_Framework_TestCase +{ /** * @dataProvider tableAlterationsProvider */ @@ -74,7 +74,8 @@ public function tableAlterationsProvider() ]; } - private function trimExtraIndents($sqlString) { - return join("\n", array_map("trim", explode("\n", $sqlString))); + private function trimExtraIndents($sqlString) + { + return implode("\n", array_map("trim", explode("\n", $sqlString))); } } diff --git a/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php b/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php index 2884ceb27e..695e599d12 100644 --- a/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php +++ b/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php @@ -9,7 +9,6 @@ namespace Zend\Db\Sql\Platform\Postgresql\Ddl; - use Zend\Db\Adapter\Platform\Postgresql; use Zend\Db\Sql\Ddl\CreateTable; use Zend\Db\Sql\Ddl; @@ -88,5 +87,4 @@ public function tableDefinitionsProvider() [$mixed, $expectedMixed], ]; } - } diff --git a/test/Sql/Platform/Postgresql/PostgresqlTest.php b/test/Sql/Platform/Postgresql/PostgresqlTest.php index a368fa1c78..ac81400ae7 100644 --- a/test/Sql/Platform/Postgresql/PostgresqlTest.php +++ b/test/Sql/Platform/Postgresql/PostgresqlTest.php @@ -12,7 +12,8 @@ use Zend\Db\Sql\Platform\Postgresql\Postgresql; use Zend\Db\Sql\Platform\Postgresql\Ddl; -class PostgresqlTest extends \PHPUnit_Framework_TestCase { +class PostgresqlTest extends \PHPUnit_Framework_TestCase +{ /* * @testdox unit test / object test: Has CreateTable proxy * @covers Zend\Db\Sql\Platform\Postgresql\Postgresql::__construct From d5272e8d7f828a275f2c23e632590cab555cb375 Mon Sep 17 00:00:00 2001 From: Sasha Alex Romanenko Date: Tue, 6 Sep 2016 12:16:06 -0500 Subject: [PATCH 12/31] code sniffer rules --- src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php b/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php index 50ca7e1716..b626c42148 100644 --- a/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php +++ b/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php @@ -117,4 +117,4 @@ protected function processStatementEnd(PlatformInterface $adapterPlatform = null { return ["\n);"]; } -} \ No newline at end of file +} From ab1f2e89ed18a5b8eabc049a7bdc2e1d11cae855 Mon Sep 17 00:00:00 2001 From: Enrico Zimuel Date: Tue, 5 Dec 2017 14:46:01 +0100 Subject: [PATCH 13/31] Fixed CS issues --- src/Adapter/Driver/Sqlsrv/Statement.php | 2 +- test/Adapter/Driver/Sqlsrv/SqlSrvIntegrationTest.php | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Adapter/Driver/Sqlsrv/Statement.php b/src/Adapter/Driver/Sqlsrv/Statement.php index a0486e3ab7..ffc922acd9 100644 --- a/src/Adapter/Driver/Sqlsrv/Statement.php +++ b/src/Adapter/Driver/Sqlsrv/Statement.php @@ -211,7 +211,7 @@ public function prepare($sql = null, array $options = []) $pRef = &$this->parameterReferences; for ($position = 0, $count = substr_count($sql, '?'); $position < $count; $position++) { - if (!isset($this->prepareParams[$position])) { + if (! isset($this->prepareParams[$position])) { $pRef[$position] = [&$this->parameterReferenceValues[$position], SQLSRV_PARAM_IN, null, null]; } else { $pRef[$position] = &$this->prepareParams[$position]; diff --git a/test/Adapter/Driver/Sqlsrv/SqlSrvIntegrationTest.php b/test/Adapter/Driver/Sqlsrv/SqlSrvIntegrationTest.php index 72b2c3102a..33f9e5adba 100644 --- a/test/Adapter/Driver/Sqlsrv/SqlSrvIntegrationTest.php +++ b/test/Adapter/Driver/Sqlsrv/SqlSrvIntegrationTest.php @@ -53,7 +53,10 @@ public function testCreateStatement() $stmt = $this->driver->createStatement(); $this->assertInstanceOf('Zend\Db\Adapter\Driver\Sqlsrv\Statement', $stmt); - $this->setExpectedException('Zend\Db\Adapter\Exception\InvalidArgumentException', 'only accepts an SQL string or a Sqlsrv resource'); + $this->setExpectedException( + 'Zend\Db\Adapter\Exception\InvalidArgumentException', + 'only accepts an SQL string or a Sqlsrv resource' + ); $this->driver->createStatement(new \stdClass); } From 9f589dd36c60136b5d3b8c58420ac254be00699c Mon Sep 17 00:00:00 2001 From: Enrico Zimuel Date: Wed, 6 Dec 2017 10:59:02 +0100 Subject: [PATCH 14/31] Updated CHANGELOG --- CHANGELOG.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48a7be35c0..006d6224f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,24 @@ All notable changes to this project will be documented in this file, in reverse chronological order by release. +## 2.9.1 - TBD + +### Added + +- Nothing. + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- Nothing. + ## 2.9.0 - 2017-12-06 ### Added From fec7d8ce219ab407189bbdb3cc24dd196c46e710 Mon Sep 17 00:00:00 2001 From: Enrico Zimuel Date: Thu, 7 Dec 2017 14:39:42 +0100 Subject: [PATCH 15/31] Revert PR #224 due to issue like #288 --- src/Adapter/Driver/Pdo/Pdo.php | 3 +-- test/Adapter/Driver/Pdo/PdoTest.php | 14 ++++------- .../Driver/Pdo/StatementIntegrationTest.php | 4 ++-- test/Adapter/Driver/Pdo/StatementTest.php | 24 ------------------- 4 files changed, 8 insertions(+), 37 deletions(-) diff --git a/src/Adapter/Driver/Pdo/Pdo.php b/src/Adapter/Driver/Pdo/Pdo.php index ddc9ad67ec..569e10da5a 100644 --- a/src/Adapter/Driver/Pdo/Pdo.php +++ b/src/Adapter/Driver/Pdo/Pdo.php @@ -304,8 +304,7 @@ public function getPrepareType() public function formatParameterName($name, $type = null) { if ($type === null && ! is_numeric($name) || $type == self::PARAMETERIZATION_NAMED) { - // using MD5 because of the PDO restriction [A-Za-z0-9_] for bindParam name - return ':' . md5($name); + return ':' . $name; } return '?'; diff --git a/test/Adapter/Driver/Pdo/PdoTest.php b/test/Adapter/Driver/Pdo/PdoTest.php index 6ac716e143..4d70ed5bcd 100644 --- a/test/Adapter/Driver/Pdo/PdoTest.php +++ b/test/Adapter/Driver/Pdo/PdoTest.php @@ -43,16 +43,12 @@ public function testGetDatabasePlatformName() public function getParamsAndType() { return [ - [ 'foo', null, ':' . md5('foo')], - [ 'foo-', null, ':' . md5('foo-')], - [ 'foo$', null, ':' . md5('foo$')], + [ 'foo', null, ':foo' ], [ 1, null, '?' ], - [ '1', null, '?'], - [ 'foo', Pdo::PARAMETERIZATION_NAMED, ':' . md5('foo')], - [ 'foo-', Pdo::PARAMETERIZATION_NAMED, ':' . md5('foo-')], - [ 'foo$', Pdo::PARAMETERIZATION_NAMED, ':' . md5('foo$')], - [ 1, Pdo::PARAMETERIZATION_NAMED, ':' . md5('1')], - [ '1', Pdo::PARAMETERIZATION_NAMED, ':' . md5('1')], + [ '1', null, '?' ], + [ 'foo', Pdo::PARAMETERIZATION_NAMED, ':foo' ], + [ 1, Pdo::PARAMETERIZATION_NAMED, ':1' ], + [ '1', Pdo::PARAMETERIZATION_NAMED, ':1' ], ]; } diff --git a/test/Adapter/Driver/Pdo/StatementIntegrationTest.php b/test/Adapter/Driver/Pdo/StatementIntegrationTest.php index 871f43b96e..a25f12867b 100644 --- a/test/Adapter/Driver/Pdo/StatementIntegrationTest.php +++ b/test/Adapter/Driver/Pdo/StatementIntegrationTest.php @@ -55,7 +55,7 @@ protected function tearDown() public function testStatementExecuteWillConvertPhpBoolToPdoBoolWhenBinding() { $this->pdoStatementMock->expects($this->any())->method('bindParam')->with( - $this->equalTo(':' . md5('foo')), + $this->equalTo(':foo'), $this->equalTo(false), $this->equalTo(\PDO::PARAM_BOOL) ); @@ -65,7 +65,7 @@ public function testStatementExecuteWillConvertPhpBoolToPdoBoolWhenBinding() public function testStatementExecuteWillUsePdoStrByDefaultWhenBinding() { $this->pdoStatementMock->expects($this->any())->method('bindParam')->with( - $this->equalTo(':' . md5('foo')), + $this->equalTo(':foo'), $this->equalTo('bar'), $this->equalTo(\PDO::PARAM_STR) ); diff --git a/test/Adapter/Driver/Pdo/StatementTest.php b/test/Adapter/Driver/Pdo/StatementTest.php index b36294bff7..d103b494cd 100644 --- a/test/Adapter/Driver/Pdo/StatementTest.php +++ b/test/Adapter/Driver/Pdo/StatementTest.php @@ -128,28 +128,4 @@ public function testExecute() $this->statement->prepare('SELECT 1'); self::assertInstanceOf('Zend\Db\Adapter\Driver\Pdo\Result', $this->statement->execute()); } - - /** - * @see https://github.com/zendframework/zend-db/pull/224 - */ - public function testExecuteWithSpecialCharInBindParam() - { - $testSqlite = new TestAsset\SqliteMemoryPdo('CREATE TABLE test (text_ TEXT, text$ TEXT);'); - $this->statement->setDriver(new Pdo(new Connection($testSqlite))); - $this->statement->initialize($testSqlite); - - $this->statement->prepare(sprintf( - 'INSERT INTO test (text_, text$) VALUES (:%s, :%s)', - md5('text_'), - md5('text$') - )); - $result = $this->statement->execute([ 'text_' => 'foo', 'text$' => 'bar']); - $this->assertInstanceOf(Result::class, $result); - $this->assertTrue($result->valid()); - - $result = $testSqlite->query('SELECT * FROM test'); - $values = $result->fetch(); - $this->assertEquals('foo', $values['text_']); - $this->assertEquals('bar', $values['text$']); - } } From 43104a8ffb0c9577ae6798a2beb91ab852384105 Mon Sep 17 00:00:00 2001 From: Enrico Zimuel Date: Thu, 7 Dec 2017 16:43:11 +0100 Subject: [PATCH 16/31] Throw Exception for invalid PDO param name --- src/Adapter/Driver/Pdo/Pdo.php | 8 ++++++++ test/Adapter/Driver/Pdo/PdoTest.php | 23 +++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/src/Adapter/Driver/Pdo/Pdo.php b/src/Adapter/Driver/Pdo/Pdo.php index 569e10da5a..ff96b8ccb5 100644 --- a/src/Adapter/Driver/Pdo/Pdo.php +++ b/src/Adapter/Driver/Pdo/Pdo.php @@ -304,6 +304,14 @@ public function getPrepareType() public function formatParameterName($name, $type = null) { if ($type === null && ! is_numeric($name) || $type == self::PARAMETERIZATION_NAMED) { + // @see https://bugs.php.net/bug.php?id=43130 + if (preg_match('/[^a-zA-Z0-9_]/', $name)) { + throw new Exception\RuntimeException(sprintf( + "The PDO param %s contains characters not allowed. " . + "You can use only letter, digit, and underscore (_)", + $name + )); + } return ':' . $name; } diff --git a/test/Adapter/Driver/Pdo/PdoTest.php b/test/Adapter/Driver/Pdo/PdoTest.php index 4d70ed5bcd..ae6b58bcc6 100644 --- a/test/Adapter/Driver/Pdo/PdoTest.php +++ b/test/Adapter/Driver/Pdo/PdoTest.php @@ -44,9 +44,13 @@ public function getParamsAndType() { return [ [ 'foo', null, ':foo' ], + [ 'foo_bar', null, ':foo_bar' ], + [ '123foo', null, ':123foo' ], [ 1, null, '?' ], [ '1', null, '?' ], [ 'foo', Pdo::PARAMETERIZATION_NAMED, ':foo' ], + [ 'foo_bar', Pdo::PARAMETERIZATION_NAMED, ':foo_bar' ], + [ '123foo', Pdo::PARAMETERIZATION_NAMED, ':123foo' ], [ 1, Pdo::PARAMETERIZATION_NAMED, ':1' ], [ '1', Pdo::PARAMETERIZATION_NAMED, ':1' ], ]; @@ -60,4 +64,23 @@ public function testFormatParameterName($name, $type, $expected) $result = $this->pdo->formatParameterName($name, $type); $this->assertEquals($expected, $result); } + + public function getInvalidParamName() + { + return [ + [ 'foo%' ], + [ 'foo-' ], + [ 'foo$' ], + [ 'foo0!' ] + ]; + } + + /** + * @dataProvider getInvalidParamName + * @expectedException Zend\Db\Exception\RuntimeException + */ + public function testFormatParameterNameWithInvalidCharacters($name) + { + $this->pdo->formatParameterName($name); + } } From e59f2d8ab7cc4d574e04281f19b407cf939297d1 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Thu, 7 Dec 2017 09:48:59 -0600 Subject: [PATCH 17/31] Updates to exception message introduced in #289 --- src/Adapter/Driver/Pdo/Pdo.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Adapter/Driver/Pdo/Pdo.php b/src/Adapter/Driver/Pdo/Pdo.php index ff96b8ccb5..9c57701f57 100644 --- a/src/Adapter/Driver/Pdo/Pdo.php +++ b/src/Adapter/Driver/Pdo/Pdo.php @@ -307,8 +307,9 @@ public function formatParameterName($name, $type = null) // @see https://bugs.php.net/bug.php?id=43130 if (preg_match('/[^a-zA-Z0-9_]/', $name)) { throw new Exception\RuntimeException(sprintf( - "The PDO param %s contains characters not allowed. " . - "You can use only letter, digit, and underscore (_)", + 'The PDO param %s contains invalid characters.' + . ' Only alphabetic characters, digits, and underscores (_)' + . ' are allowed.', $name )); } From 1a374597b7a44f0258b5b5ae94d8454027f6288e Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Thu, 7 Dec 2017 09:52:31 -0600 Subject: [PATCH 18/31] Adds CHANGELOG entry for #289 --- CHANGELOG.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 006d6224f4..486c88b93d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,12 +2,24 @@ All notable changes to this project will be documented in this file, in reverse chronological order by release. -## 2.9.1 - TBD +## 2.9.1 - 2017-12-07 ### Added - Nothing. +### Changed + +- [#289](https://github.com/zendframework/zend-db/pull/289) reverts a change + introduced in 2.9.0 and modifies the behavior of the PDO adapter slightly + to remove a regression. In 2.9.0, when binding parameters with names that + contained characters not supported by PDO, we would pass the parameter names + to `md5()`; this caused a regression, as the SQL string containing the + parameter name was not also updated. + + This patch modifies the behavior during a bind-operation to instead raise an + exception if a parameter name contains characters not supported by PDO. + ### Deprecated - Nothing. From 4d691cf048fd94611e2a78b237b8f394ab19c962 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Thu, 7 Dec 2017 09:56:03 -0600 Subject: [PATCH 19/31] Bumped to next dev version (2.9.2) --- CHANGELOG.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 486c88b93d..866562f23e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,28 @@ All notable changes to this project will be documented in this file, in reverse chronological order by release. +## 2.9.2 - TBD + +### Added + +- Nothing. + +### Changed + +- Nothing. + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- Nothing. + ## 2.9.1 - 2017-12-07 ### Added From 128913ac4e13aa807ef6d59e2fd002025f25f980 Mon Sep 17 00:00:00 2001 From: Sasha Alex Romanenko Date: Sat, 3 Sep 2016 00:54:09 -0500 Subject: [PATCH 20/31] Add PostgreSQL platform. --- src/Sql/Platform/Platform.php | 25 ++++++----- src/Sql/Platform/Postgresql/Postgresql.php | 20 +++++++++ test/Sql/Platform/PlatformTest.php | 43 +++++++++++++++++-- .../Platform/Postgresql/PostgresqlTest.php | 24 +++++++++++ test/TestAsset/TrustingPostgresqlPlatform.php | 20 +++++++++ 5 files changed, 117 insertions(+), 15 deletions(-) create mode 100644 src/Sql/Platform/Postgresql/Postgresql.php create mode 100644 test/Sql/Platform/Postgresql/PostgresqlTest.php create mode 100644 test/TestAsset/TrustingPostgresqlPlatform.php diff --git a/src/Sql/Platform/Platform.php b/src/Sql/Platform/Platform.php index e702caac46..6e7637671e 100644 --- a/src/Sql/Platform/Platform.php +++ b/src/Sql/Platform/Platform.php @@ -13,6 +13,7 @@ use Zend\Db\Adapter\Platform\PlatformInterface; use Zend\Db\Adapter\StatementContainerInterface; use Zend\Db\Sql\Exception; +use Zend\Db\Sql\Platform\Postgresql; use Zend\Db\Sql\PreparableSqlInterface; use Zend\Db\Sql\SqlInterface; @@ -32,17 +33,19 @@ public function __construct(AdapterInterface $adapter) { $this->defaultPlatform = $adapter->getPlatform(); - $mySqlPlatform = new Mysql\Mysql(); - $sqlServerPlatform = new SqlServer\SqlServer(); - $oraclePlatform = new Oracle\Oracle(); - $ibmDb2Platform = new IbmDb2\IbmDb2(); - $sqlitePlatform = new Sqlite\Sqlite(); - - $this->decorators['mysql'] = $mySqlPlatform->getDecorators(); - $this->decorators['sqlserver'] = $sqlServerPlatform->getDecorators(); - $this->decorators['oracle'] = $oraclePlatform->getDecorators(); - $this->decorators['ibmdb2'] = $ibmDb2Platform->getDecorators(); - $this->decorators['sqlite'] = $sqlitePlatform->getDecorators(); + $mySqlPlatform = new Mysql\Mysql(); + $sqlServerPlatform = new SqlServer\SqlServer(); + $oraclePlatform = new Oracle\Oracle(); + $ibmDb2Platform = new IbmDb2\IbmDb2(); + $sqlitePlatform = new Sqlite\Sqlite(); + $postgresqlPlatform = new Postgresql\Postgresql(); + + $this->decorators['mysql'] = $mySqlPlatform->getDecorators(); + $this->decorators['sqlserver'] = $sqlServerPlatform->getDecorators(); + $this->decorators['oracle'] = $oraclePlatform->getDecorators(); + $this->decorators['ibmdb2'] = $ibmDb2Platform->getDecorators(); + $this->decorators['sqlite'] = $sqlitePlatform->getDecorators(); + $this->decorators['postgresql'] = $postgresqlPlatform->getDecorators(); } /** diff --git a/src/Sql/Platform/Postgresql/Postgresql.php b/src/Sql/Platform/Postgresql/Postgresql.php new file mode 100644 index 0000000000..7eb530ed59 --- /dev/null +++ b/src/Sql/Platform/Postgresql/Postgresql.php @@ -0,0 +1,20 @@ +setAccessible(true); - self::assertEquals('mysql', $reflectionMethod->invoke($platform, new TestAsset\TrustingMysqlPlatform())); + self::assertEquals('mysql', $reflectionMethod->invoke( + $platform, + new TestAsset\TrustingMysqlPlatform() + )); self::assertEquals('sqlserver', $reflectionMethod->invoke( $platform, new TestAsset\TrustingSqlServerPlatform() )); - self::assertEquals('oracle', $reflectionMethod->invoke($platform, new TestAsset\TrustingOraclePlatform())); - self::assertEquals('sql92', $reflectionMethod->invoke($platform, new TestAsset\TrustingSql92Platform())); + self::assertEquals('oracle', $reflectionMethod->invoke( + $platform, + new TestAsset\TrustingOraclePlatform() + )); + self::assertEquals('sql92', $reflectionMethod->invoke( + $platform, + new TestAsset\TrustingSql92Platform() + )); + $this->assertEquals('postgresql', $reflectionMethod->invoke( + $platform, + new TestAsset\TrustingPostgresqlPlatform() + )); } /** @@ -89,6 +103,17 @@ public function testAbstractPlatformCrashesGracefullyOnMissingDefaultPlatformWit $platform->getDecorators(); } + /** + * @dataProvider availablePlatformDecorators + */ + public function testDecoratorsRegistered($adapter, $decorators) + { + $platform = new Platform($adapter); + $registeredDecorators = $platform->getDecorators(); + + $this->assertEquals($registeredDecorators, $decorators); + } + /** * @param string $platformName * @@ -111,6 +136,8 @@ protected function resolveAdapter($platformName) case 'SqlServer': $platform = new TestAsset\TrustingSqlServerPlatform(); break; + case 'PostgreSQL' : + $platform = new TestAsset\TrustingPostgresqlPlatform(); } /* @var $mockDriver \Zend\Db\Adapter\Driver\DriverInterface|\PHPUnit_Framework_MockObject_MockObject */ @@ -123,4 +150,12 @@ protected function resolveAdapter($platformName) return new Adapter($mockDriver, $platform); } + + public function availablePlatformDecorators() + { + return [ + //@TODO add all supported platforms + [$this->resolveAdapter('PostgreSQL'), (new Postgresql())->getDecorators()], + ]; + } } diff --git a/test/Sql/Platform/Postgresql/PostgresqlTest.php b/test/Sql/Platform/Postgresql/PostgresqlTest.php new file mode 100644 index 0000000000..5264177276 --- /dev/null +++ b/test/Sql/Platform/Postgresql/PostgresqlTest.php @@ -0,0 +1,24 @@ +quoteTrustedValue($value); + } +} \ No newline at end of file From c37d3e58a731a302d060bd42ba5bd7c397d57111 Mon Sep 17 00:00:00 2001 From: Sasha Alex Romanenko Date: Sat, 3 Sep 2016 10:12:28 -0500 Subject: [PATCH 21/31] Add CreateTableDecorator. Change ends of commands with ';' --- .../Postgresql/Ddl/CreateTableDecorator.php | 37 ++++++++++++ src/Sql/Platform/Postgresql/Postgresql.php | 2 +- .../Ddl/CreateTableDecoratorTest.php | 57 +++++++++++++++++++ .../Platform/Postgresql/PostgresqlTest.php | 6 +- 4 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php create mode 100644 test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php diff --git a/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php b/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php new file mode 100644 index 0000000000..bf7fcaa10c --- /dev/null +++ b/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php @@ -0,0 +1,37 @@ +subject = $subject; + } + + protected function processStatementEnd(PlatformInterface $adapterPlatform = null) + { + return ["\n);"]; + } + +} \ No newline at end of file diff --git a/src/Sql/Platform/Postgresql/Postgresql.php b/src/Sql/Platform/Postgresql/Postgresql.php index 7eb530ed59..0bf52ae4f0 100644 --- a/src/Sql/Platform/Postgresql/Postgresql.php +++ b/src/Sql/Platform/Postgresql/Postgresql.php @@ -15,6 +15,6 @@ class Postgresql extends AbstractPlatform { public function __construct() { - + $this->setTypeDecorator('Zend\Db\Sql\Ddl\CreateTable', new Ddl\CreateTableDecorator()); } } \ No newline at end of file diff --git a/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php b/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php new file mode 100644 index 0000000000..03e83e99ea --- /dev/null +++ b/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php @@ -0,0 +1,57 @@ +setSubject($createTable); + + $createTableSql = $createTableDecorator->getSqlString(new Postgresql()); + $this->assertEquals($expectedSql, $createTableSql); + } + + public function tableDefinitionsProvider() + { + $id = new Ddl\Column\Integer('id', false, null); + $name = new Ddl\Column\Varchar('username', false, null); + $name->setLength(1024); + + + $columnsOnly = new CreateTable('columns_only'); + $columnsOnly->addColumn($id); + $columnsOnly->addColumn($name); + + $expectedColumnsOnly = 'CREATE TABLE "columns_only" ( '."\n". + ' "id" INTEGER NOT NULL,'."\n". + ' "username" VARCHAR(1024) NOT NULL '."\n". + ');'; + + return [ + [$columnsOnly, $expectedColumnsOnly] + ]; + } + +} diff --git a/test/Sql/Platform/Postgresql/PostgresqlTest.php b/test/Sql/Platform/Postgresql/PostgresqlTest.php index 5264177276..ac12f1c887 100644 --- a/test/Sql/Platform/Postgresql/PostgresqlTest.php +++ b/test/Sql/Platform/Postgresql/PostgresqlTest.php @@ -14,11 +14,15 @@ class PostgresqlTest extends \PHPUnit_Framework_TestCase { /* - * @testdox unit test / object test: + * @testdox unit test / object test: Has CreateTable proxy * @covers Zend\Db\Sql\Platform\Postgresql\Postgresql::__construct */ public function testConstruct() { + $postgresql = new Postgresql(); + $decorators = $postgresql->getDecorators(); + $this->assertArrayHasKey('Zend\Db\Sql\Ddl\CreateTable', $decorators); + $this->assertInstanceOf(Ddl\CreateTableDecorator::class, $decorators['Zend\Db\Sql\Ddl\CreateTable']); } } From 05ff2d5be105add1cfc5d57b4d5eba459c2cd17a Mon Sep 17 00:00:00 2001 From: Sasha Alex Romanenko Date: Sat, 3 Sep 2016 11:06:36 -0500 Subject: [PATCH 22/31] Index constructor signature does not follow docs and the overridden Constraint construct. It also makes decorator creation at Platform level impossible. To avoid missing parameter and causing invalid syntaxes, will propose adding exceptions in all DB components instead of getting cryptic SQL errors in logs. --- src/Sql/Ddl/Index/Index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Sql/Ddl/Index/Index.php b/src/Sql/Ddl/Index/Index.php index a0239089a4..704fab0a14 100644 --- a/src/Sql/Ddl/Index/Index.php +++ b/src/Sql/Ddl/Index/Index.php @@ -26,7 +26,7 @@ class Index extends AbstractIndex * @param null|string $name * @param array $lengths */ - public function __construct($columns, $name = null, array $lengths = []) + public function __construct($columns = null, $name = null, array $lengths = []) { $this->setColumns($columns); From 25da6e970f8dc0a9acae3162828cf23a4096f467 Mon Sep 17 00:00:00 2001 From: Sasha Alex Romanenko Date: Sat, 3 Sep 2016 11:30:08 -0500 Subject: [PATCH 23/31] Add Postgres Index Decorator. Modifies specification to eventually construct CREATE INDEX index_name ON table_name (columns...) Intended to be used within CreateTableDecorator, but can also add it to platform if needed, as long as table name is supplied somehow. --- .../Postgresql/Ddl/Index/IndexDecorator.php | 61 +++++++++++++++++++ .../Ddl/CreateTableDecoratorTest.php | 9 +-- .../Ddl/Index/IndexDecoratorTest.php | 52 ++++++++++++++++ 3 files changed, 118 insertions(+), 4 deletions(-) create mode 100644 src/Sql/Platform/Postgresql/Ddl/Index/IndexDecorator.php create mode 100644 test/Sql/Platform/Postgresql/Ddl/Index/IndexDecoratorTest.php diff --git a/src/Sql/Platform/Postgresql/Ddl/Index/IndexDecorator.php b/src/Sql/Platform/Postgresql/Ddl/Index/IndexDecorator.php new file mode 100644 index 0000000000..ec0e0428aa --- /dev/null +++ b/src/Sql/Platform/Postgresql/Ddl/Index/IndexDecorator.php @@ -0,0 +1,61 @@ +subject = $subject; + $this->subject->specification = $this->specification; + } + + public function setTable($table) + { + $this->table = $table; + } + + /** + * @inheritDoc + */ + public function getExpressionData() + { + if (!$this->table) { + throw new InvalidQueryException('PostgreSQL Index needs table name specified.'); + } + + $expressionData = $this->subject->getExpressionData(); + + // [0] => specification + // [1] => values + // [2] => types + array_splice($expressionData[0][1], 1, 0, $this->table); + array_splice($expressionData[0][2], 1, 0, self::TYPE_IDENTIFIER); + + return $expressionData; + } +} diff --git a/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php b/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php index 03e83e99ea..c309aa4405 100644 --- a/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php +++ b/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php @@ -44,10 +44,11 @@ public function tableDefinitionsProvider() $columnsOnly->addColumn($id); $columnsOnly->addColumn($name); - $expectedColumnsOnly = 'CREATE TABLE "columns_only" ( '."\n". - ' "id" INTEGER NOT NULL,'."\n". - ' "username" VARCHAR(1024) NOT NULL '."\n". - ');'; + $expectedColumnsOnly = + 'CREATE TABLE "columns_only" ( '."\n". + ' "id" INTEGER NOT NULL,'."\n". + ' "username" VARCHAR(1024) NOT NULL '."\n". + ');'; return [ [$columnsOnly, $expectedColumnsOnly] diff --git a/test/Sql/Platform/Postgresql/Ddl/Index/IndexDecoratorTest.php b/test/Sql/Platform/Postgresql/Ddl/Index/IndexDecoratorTest.php new file mode 100644 index 0000000000..fc156298f1 --- /dev/null +++ b/test/Sql/Platform/Postgresql/Ddl/Index/IndexDecoratorTest.php @@ -0,0 +1,52 @@ +setName('test_index'); + $index->setColumns(['test_column_one', 'test_column_two']); + + $postgresIndex = new IndexDecorator(); + $postgresIndex->setSubject($index); + $postgresIndex->setTable('test_table'); // PostgreSQL must have table name to operate on, unlike other engines + + $expressionData = $postgresIndex->getExpressionData()[0]; + + // [0] => specification + // [1] => values + // [2] => types + $this->assertEquals('CREATE INDEX %s ON %s(%s, %s)', $expressionData[0]); + $this->assertEquals(['test_index', 'test_table', 'test_column_one', 'test_column_two'], $expressionData[1]); + $this->assertEquals( + [Index::TYPE_IDENTIFIER, Index::TYPE_IDENTIFIER, Index::TYPE_IDENTIFIER, Index::TYPE_IDENTIFIER], + $expressionData[2] + ); + } + + public function testExceptionThrownIfNoTableSpecified() + { + $postgresIndex = new IndexDecorator(); + + $this->setExpectedException(InvalidQueryException::class); + $postgresIndex->getExpressionData(); + } +} From 8bd2b484d16670d3063efb153e7b050a115750fc Mon Sep 17 00:00:00 2001 From: Sasha Alex Romanenko Date: Sat, 3 Sep 2016 15:11:23 -0500 Subject: [PATCH 24/31] Process indexes separately from constraints, after CreateTable specification. --- .../Postgresql/Ddl/CreateTableDecorator.php | 86 ++++++++++++++++++- .../Ddl/CreateTableDecoratorTest.php | 46 ++++++++-- 2 files changed, 123 insertions(+), 9 deletions(-) diff --git a/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php b/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php index bf7fcaa10c..05f83461f1 100644 --- a/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php +++ b/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php @@ -9,26 +9,108 @@ namespace Zend\Db\Sql\Platform\Postgresql\Ddl; +use Zend\Db\Adapter\Driver\DriverInterface; +use Zend\Db\Adapter\ParameterContainer; use Zend\Db\Sql\Ddl\CreateTable; +use Zend\Db\Sql\Ddl\Index\Index; use Zend\Db\Sql\Platform\PlatformDecoratorInterface; use Zend\Db\Adapter\Platform\PlatformInterface; +use Zend\Db\Sql\Platform\Postgresql\Ddl\Index\IndexDecorator; class CreateTableDecorator extends CreateTable implements PlatformDecoratorInterface { + const INDEXES = 'indexes'; /** * @var CreateTable */ protected $subject; + /** + * @var string[] + */ + protected $indexes = []; + /** * @inheritDoc */ - public function setSubject($subject) - { + protected $indexSpecification = [ + self::INDEXES => [ + "\n%1\$s" => [ + [1 => '%1$s;', 'combinedby' => "\n"] + ] + ] + ]; + + /** + * @param $subject + * @return mixed + */ + public function setSubject($subject) { $this->subject = $subject; + + $this->specifications = array_merge($this->specifications, $this->indexSpecification); + $this->subject->specifications = $this->specifications; + + return $this; + } + + /** + * @inheritDoc + */ + protected function buildSqlString( + PlatformInterface $platform, + DriverInterface $driver = null, + ParameterContainer $parameterContainer = null + ) { + $this->separateIndexesFromConstraints(); + + return parent::buildSqlString($platform, $driver, $parameterContainer); } + private function separateIndexesFromConstraints() + { + // take advantage of PHP's ability to access protected properties of different instances created from same class + $this->indexes = array_filter($this->subject->constraints, function($constraint) { + return $constraint instanceof Index; + }); + + $filteredConstraints = array_filter($this->subject->constraints, function($constraint) { + return !($constraint instanceof Index); + }); + + $this->subject->constraints = $filteredConstraints; + + array_walk($this->indexes, function (&$index, $key) { + $indexDecorator = new IndexDecorator(); + $indexDecorator->setSubject($index); + $indexDecorator->setTable($this->subject->table); + $index = $indexDecorator; + }); + } + + /** + * @param PlatformInterface|null $adapterPlatform + * @return array|void + */ + protected function processIndexes(PlatformInterface $adapterPlatform = null) { + if (!$this->indexes) { + return; + } + + $sqls = []; + + foreach ($this->indexes as $index) { + $sqls[] = $this->processExpression($index, $adapterPlatform); + } + + return [$sqls]; + } + + /** + * @param PlatformInterface|null $adapterPlatform + * @return array|void + */ protected function processStatementEnd(PlatformInterface $adapterPlatform = null) { return ["\n);"]; diff --git a/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php b/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php index c309aa4405..6a15e61c47 100644 --- a/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php +++ b/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php @@ -19,7 +19,7 @@ class CreateTableDecoratorTest extends \PHPUnit_Framework_TestCase /** * @testdox integration test: Testing CreateTableDecorator will use CreateTable as an internal state to adjust Index creation to be a separate statement * @covers Zend\Db\Sql\Platform\Postgresql\Ddl\CreateTableDecorator::setSubject - * @covers Zend\Db\Sql\Platform\Postgresql\Ddl\CreateTableDecorator::processConstraints + * @covers Zend\Db\Sql\Platform\Postgresql\Ddl\CreateTableDecorator::buildSqlString * @covers Zend\Db\Sql\Platform\Postgresql\Ddl\CreateTableDecorator::processIndexes * @covers Zend\Db\Sql\Platform\Postgresql\Ddl\CreateTableDecorator::processStatementEnd * @dataProvider tableDefinitionsProvider @@ -38,20 +38,52 @@ public function tableDefinitionsProvider() $id = new Ddl\Column\Integer('id', false, null); $name = new Ddl\Column\Varchar('username', false, null); $name->setLength(1024); + $nameUnique = new Ddl\Constraint\UniqueKey('username'); + + $idIndex = new Ddl\Index\Index('id', 'id_idx'); + $nameIndex = new Ddl\Index\Index('username', 'username_index'); $columnsOnly = new CreateTable('columns_only'); $columnsOnly->addColumn($id); $columnsOnly->addColumn($name); - $expectedColumnsOnly = - 'CREATE TABLE "columns_only" ( '."\n". - ' "id" INTEGER NOT NULL,'."\n". - ' "username" VARCHAR(1024) NOT NULL '."\n". - ');'; + $withSingleIndex = new CreateTable('with_single_index'); + $withSingleIndex->addColumn($id); + $withSingleIndex->addColumn($name); + $withSingleIndex->addConstraint($idIndex); + + $mixed = new CreateTable('mixed'); + $mixed->addColumn($id); + $mixed->addColumn($name); + $mixed->addConstraint($nameUnique); + $mixed->addConstraint($idIndex); + $mixed->addConstraint($nameIndex); + + + $expectedColumnsOnly = 'CREATE TABLE "columns_only" ( ' . "\n" + . ' "id" INTEGER NOT NULL,' . "\n" + . ' "username" VARCHAR(1024) NOT NULL ' . "\n" + . ');'; + + $expectedWithSingleInstance = 'CREATE TABLE "with_single_index" ( ' . "\n" + . ' "id" INTEGER NOT NULL,' . "\n" + . ' "username" VARCHAR(1024) NOT NULL ' . "\n" + . '); ' . "\n" + . 'CREATE INDEX "id_idx" ON "with_single_index"("id");'; + + $expectedMixed = 'CREATE TABLE "mixed" ( ' . "\n" + . ' "id" INTEGER NOT NULL,' . "\n" + . ' "username" VARCHAR(1024) NOT NULL , ' . "\n" + . ' UNIQUE ("username") ' . "\n" + . '); ' . "\n" + . 'CREATE INDEX "id_idx" ON "mixed"("id");' . "\n" + . 'CREATE INDEX "username_index" ON "mixed"("username");'; return [ - [$columnsOnly, $expectedColumnsOnly] + [$columnsOnly, $expectedColumnsOnly], + [$withSingleIndex, $expectedWithSingleInstance], + [$mixed, $expectedMixed] ]; } From c506e15a4e454f64e0d6d441329d63a60555d638 Mon Sep 17 00:00:00 2001 From: Sasha Alex Romanenko Date: Sat, 3 Sep 2016 16:17:17 -0500 Subject: [PATCH 25/31] Add AlterTableDecorator to PostgreSQL platform. --- .../Postgresql/Ddl/AlterTableDecorator.php | 22 +++++++++++++++++++ src/Sql/Platform/Postgresql/Postgresql.php | 1 + .../Platform/Postgresql/PostgresqlTest.php | 2 ++ 3 files changed, 25 insertions(+) create mode 100644 src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php diff --git a/src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php b/src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php new file mode 100644 index 0000000000..c96dac0e9a --- /dev/null +++ b/src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php @@ -0,0 +1,22 @@ +subject = $subject; + } + +} diff --git a/src/Sql/Platform/Postgresql/Postgresql.php b/src/Sql/Platform/Postgresql/Postgresql.php index 0bf52ae4f0..f54760d9c4 100644 --- a/src/Sql/Platform/Postgresql/Postgresql.php +++ b/src/Sql/Platform/Postgresql/Postgresql.php @@ -16,5 +16,6 @@ class Postgresql extends AbstractPlatform public function __construct() { $this->setTypeDecorator('Zend\Db\Sql\Ddl\CreateTable', new Ddl\CreateTableDecorator()); + $this->setTypeDecorator('Zend\Db\Sql\Ddl\AlterTable', new Ddl\AlterTableDecorator()); } } \ No newline at end of file diff --git a/test/Sql/Platform/Postgresql/PostgresqlTest.php b/test/Sql/Platform/Postgresql/PostgresqlTest.php index ac12f1c887..a368fa1c78 100644 --- a/test/Sql/Platform/Postgresql/PostgresqlTest.php +++ b/test/Sql/Platform/Postgresql/PostgresqlTest.php @@ -24,5 +24,7 @@ public function testConstruct() $this->assertArrayHasKey('Zend\Db\Sql\Ddl\CreateTable', $decorators); $this->assertInstanceOf(Ddl\CreateTableDecorator::class, $decorators['Zend\Db\Sql\Ddl\CreateTable']); + $this->assertArrayHasKey('Zend\Db\Sql\Ddl\AlterTable', $decorators); + $this->assertInstanceOf(Ddl\AlterTableDecorator::class, $decorators['Zend\Db\Sql\Ddl\AlterTable']); } } From 3df020445bdcf567bc205bb291bb978624dcf692 Mon Sep 17 00:00:00 2001 From: Sasha Alex Romanenko Date: Sat, 3 Sep 2016 18:09:37 -0500 Subject: [PATCH 26/31] styles --- src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php | 2 +- test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php b/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php index 05f83461f1..7266c1048e 100644 --- a/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php +++ b/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php @@ -27,7 +27,7 @@ class CreateTableDecorator extends CreateTable implements PlatformDecoratorInter protected $subject; /** - * @var string[] + * @var IndexDecorator[] */ protected $indexes = []; diff --git a/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php b/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php index 6a15e61c47..b9cedbf151 100644 --- a/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php +++ b/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php @@ -83,7 +83,7 @@ public function tableDefinitionsProvider() return [ [$columnsOnly, $expectedColumnsOnly], [$withSingleIndex, $expectedWithSingleInstance], - [$mixed, $expectedMixed] + [$mixed, $expectedMixed], ]; } From fef46341505a87f12e4736ac319e085b365e20cf Mon Sep 17 00:00:00 2001 From: Sasha Alex Romanenko Date: Mon, 5 Sep 2016 00:12:41 -0500 Subject: [PATCH 27/31] Add support for index creation in PostgreSQL AlterTableDecorator --- .../Postgresql/Ddl/AlterTableDecorator.php | 118 +++++++++++++++++- .../Ddl/AlterTableDecoratorTest.php | 65 ++++++++++ 2 files changed, 182 insertions(+), 1 deletion(-) create mode 100644 test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php diff --git a/src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php b/src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php index c96dac0e9a..b7d07c51e1 100644 --- a/src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php +++ b/src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php @@ -1,22 +1,138 @@ '%1$s', + self::ADD_INDEXES => [ + "%1\$s" => [ + [1 => '%1$s;', 'combinedby' => "\n"] + ] + ], + ]; /** * @inheritDoc */ public function setSubject($subject) { $this->subject = $subject; + + return $this; } + /** + * @inheritDoc + */ + protected function buildSqlString( + PlatformInterface $platform, + DriverInterface $driver = null, + ParameterContainer $parameterContainer = null + ) { + $this->separateIndexesFromConstraints(); + $this->deleteUnneededSpecification(); + + $alterTable = ''; + if ($this->hasCreateTable) { + $alterTable = parent::buildSqlString($platform, $driver, $parameterContainer); + $this->subject->specifications = $this->specifications = $this->indexSpecification; + } + + $indexes = parent::buildSqlString($platform, $driver, $parameterContainer); + return $alterTable.$indexes; + } + + private function separateIndexesFromConstraints() + { + // take advantage of PHP's ability to access protected properties of different instances created from same class + $this->addIndexes = array_filter($this->subject->addConstraints, function($constraint) { + return $constraint instanceof Index; + }); + + $filteredConstraints = array_filter($this->subject->addConstraints, function($constraint) { + return !($constraint instanceof Index); + }); + + $this->subject->addConstraints = $filteredConstraints; + + array_walk($this->addIndexes, function (&$index, $key) { + $indexDecorator = new IndexDecorator(); + $indexDecorator->setSubject($index); + $indexDecorator->setTable($this->subject->table); + $index = $indexDecorator; + }); + } + + /** + * @param PlatformInterface|null $adapterPlatform + * @return array|void + */ + protected function processAddIndexes(PlatformInterface $adapterPlatform = null) { + if (!$this->addIndexes) { + return; + } + + $sqls = []; + + foreach ($this->addIndexes as $index) { + $sqls[] = $this->processExpression($index, $adapterPlatform); + } + + return [$sqls]; + } + + /** + * @param PlatformInterface|null $adapterPlatform + * @return array|void + */ + protected function processStatementEnd(PlatformInterface $adapterPlatform = null) + { + return [";\n"]; + } + + private function deleteUnneededSpecification() + { + $subject = $this->subject; + if (!($subject->addColumns || $subject->changeColumns || $subject->dropColumns + || $subject->addConstraints || $subject->dropConstraints)) { + + $this->hasCreateTable = false; + + unset($this->indexSpecification['statementEnd']); + $this->subject->specifications = $this->specifications = $this->indexSpecification; + } + } } diff --git a/test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php b/test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php new file mode 100644 index 0000000000..f38d610bed --- /dev/null +++ b/test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php @@ -0,0 +1,65 @@ +setSubject($createTable); + + $adapterPlatform = new Postgresql(); + $createTableSql = $alterTableDecorator->getSqlString($adapterPlatform); + $this->assertEquals($expectedSql, $createTableSql); + } + + public function tableAlterationsProvider() + { + $newIdx = new Ddl\Index\Index('field_1', 'new_idx'); + $newField_2 = new Ddl\Column\Varchar('field_2'); + $newField_2->setLength(1024); + + // AlterTable on its own + $noIndex = new Ddl\AlterTable('no_index'); + $noIndex->addColumn($newField_2); + + // AlterTable on its own + $onlyIndex = new Ddl\AlterTable('only_index'); + $onlyIndex->addConstraint($newIdx); + + // AlterTable with Create Index + $mixedAddIndex = new Ddl\AlterTable('mixed_index'); + $mixedAddIndex->addColumn($newField_2); + $mixedAddIndex->addConstraint($newIdx); + + $expectedNewFieldNoIndex = 'ALTER TABLE "no_index"' . "\n" + . ' ADD COLUMN "field_2" VARCHAR(1024) NOT NULL;'; + + $expectedOnlyIndex = 'CREATE INDEX "new_idx" ON "only_index"("field_1");'; + + $expectedMixedAddIndex = 'ALTER TABLE "mixed_index"' . "\n" + . ' ADD COLUMN "field_2" VARCHAR(1024) NOT NULL;' . "\n" + . ' CREATE INDEX "new_idx" ON "mixed_index"("field_1");'; + + return [ + [$noIndex, $expectedNewFieldNoIndex], + [$onlyIndex, $expectedOnlyIndex], + [$mixedAddIndex, $expectedMixedAddIndex], + ]; + } +} From 7772e19ef0c770d56d3124843ef58f32d8bb8706 Mon Sep 17 00:00:00 2001 From: Sasha Alex Romanenko Date: Mon, 5 Sep 2016 00:17:56 -0500 Subject: [PATCH 28/31] Keep expected strings together with test API stubs. --- .../Ddl/AlterTableDecoratorTest.php | 10 +++---- .../Ddl/CreateTableDecoratorTest.php | 28 ++++++++++--------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php b/test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php index f38d610bed..9efeb71b4c 100644 --- a/test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php +++ b/test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php @@ -38,20 +38,20 @@ public function tableAlterationsProvider() $noIndex = new Ddl\AlterTable('no_index'); $noIndex->addColumn($newField_2); + $expectedNewFieldNoIndex = 'ALTER TABLE "no_index"' . "\n" + . ' ADD COLUMN "field_2" VARCHAR(1024) NOT NULL;'; + // AlterTable on its own $onlyIndex = new Ddl\AlterTable('only_index'); $onlyIndex->addConstraint($newIdx); + $expectedOnlyIndex = 'CREATE INDEX "new_idx" ON "only_index"("field_1");'; + // AlterTable with Create Index $mixedAddIndex = new Ddl\AlterTable('mixed_index'); $mixedAddIndex->addColumn($newField_2); $mixedAddIndex->addConstraint($newIdx); - $expectedNewFieldNoIndex = 'ALTER TABLE "no_index"' . "\n" - . ' ADD COLUMN "field_2" VARCHAR(1024) NOT NULL;'; - - $expectedOnlyIndex = 'CREATE INDEX "new_idx" ON "only_index"("field_1");'; - $expectedMixedAddIndex = 'ALTER TABLE "mixed_index"' . "\n" . ' ADD COLUMN "field_2" VARCHAR(1024) NOT NULL;' . "\n" . ' CREATE INDEX "new_idx" ON "mixed_index"("field_1");'; diff --git a/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php b/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php index b9cedbf151..2884ceb27e 100644 --- a/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php +++ b/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php @@ -43,16 +43,30 @@ public function tableDefinitionsProvider() $idIndex = new Ddl\Index\Index('id', 'id_idx'); $nameIndex = new Ddl\Index\Index('username', 'username_index'); - + // CREATE TABLE only. $columnsOnly = new CreateTable('columns_only'); $columnsOnly->addColumn($id); $columnsOnly->addColumn($name); + $expectedColumnsOnly = 'CREATE TABLE "columns_only" ( ' . "\n" + . ' "id" INTEGER NOT NULL,' . "\n" + . ' "username" VARCHAR(1024) NOT NULL ' . "\n" + . ');'; + + // CreateTable with Create Index $withSingleIndex = new CreateTable('with_single_index'); $withSingleIndex->addColumn($id); $withSingleIndex->addColumn($name); $withSingleIndex->addConstraint($idIndex); + $expectedWithSingleInstance = 'CREATE TABLE "with_single_index" ( ' . "\n" + . ' "id" INTEGER NOT NULL,' . "\n" + . ' "username" VARCHAR(1024) NOT NULL ' . "\n" + . '); ' . "\n" + . 'CREATE INDEX "id_idx" ON "with_single_index"("id");'; + + + // Mixed handling of index separation from constraints. $mixed = new CreateTable('mixed'); $mixed->addColumn($id); $mixed->addColumn($name); @@ -60,18 +74,6 @@ public function tableDefinitionsProvider() $mixed->addConstraint($idIndex); $mixed->addConstraint($nameIndex); - - $expectedColumnsOnly = 'CREATE TABLE "columns_only" ( ' . "\n" - . ' "id" INTEGER NOT NULL,' . "\n" - . ' "username" VARCHAR(1024) NOT NULL ' . "\n" - . ');'; - - $expectedWithSingleInstance = 'CREATE TABLE "with_single_index" ( ' . "\n" - . ' "id" INTEGER NOT NULL,' . "\n" - . ' "username" VARCHAR(1024) NOT NULL ' . "\n" - . '); ' . "\n" - . 'CREATE INDEX "id_idx" ON "with_single_index"("id");'; - $expectedMixed = 'CREATE TABLE "mixed" ( ' . "\n" . ' "id" INTEGER NOT NULL,' . "\n" . ' "username" VARCHAR(1024) NOT NULL , ' . "\n" From 737ec63c936b3087031f27303dd8afc2cf4a7796 Mon Sep 17 00:00:00 2001 From: Sasha Alex Romanenko Date: Mon, 5 Sep 2016 12:36:35 -0500 Subject: [PATCH 29/31] dropConstraint() now supports index-related constraints. Because AlterTable interface signature currently only takes in string, not AbstractConstraint, not able to inspect the types to determine best query to run. Modify specification to have IF EXISTS and perform operation both in ALTER TABLE and DROP INDEX DDL to account for possibility of either. Get a notice in database logs, but better than overhead and extra dependency on performing a meta query. --- .../Postgresql/Ddl/AlterTableDecorator.php | 55 +++++++++++++++++++ .../Ddl/AlterTableDecoratorTest.php | 23 ++++++-- 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php b/src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php index b7d07c51e1..0853a89e4c 100644 --- a/src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php +++ b/src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php @@ -20,6 +20,7 @@ class AlterTableDecorator extends AlterTable implements PlatformDecoratorInterface { const ADD_INDEXES = 'addIndexes'; + const DROP_INDEXES = 'dropIndexes'; /** * @var AlterTable @@ -31,11 +32,28 @@ class AlterTableDecorator extends AlterTable implements PlatformDecoratorInterfa */ protected $addIndexes = []; + /** + * @var string[] + */ + protected $dropIndexes = []; + /** * @var bool */ private $hasCreateTable = true; + /** + * Compensate for dropConstraint() interface not distinguishing between string and index object. + * Add IF EXISTS for safety to handle either until/if new signature is approved. + * + * @var array + */ + protected $dropConstraintSpecification = [ + "%1\$s" => [ + [1 => "DROP CONSTRAINT IF EXISTS %1\$s,\n", 'combinedby' => ""], + ] + ]; + protected $indexSpecification = [ 'statementEnd' => '%1$s', self::ADD_INDEXES => [ @@ -43,6 +61,11 @@ class AlterTableDecorator extends AlterTable implements PlatformDecoratorInterfa [1 => '%1$s;', 'combinedby' => "\n"] ] ], + self::DROP_INDEXES => [ + "%1\$s" => [ + [1 => 'DROP INDEX IF EXISTS %1$s;', 'combinedby' => "\n"] + ] + ] ]; /** @@ -51,6 +74,9 @@ class AlterTableDecorator extends AlterTable implements PlatformDecoratorInterfa public function setSubject($subject) { $this->subject = $subject; + $this->specifications[self::DROP_CONSTRAINTS] = $this->dropConstraintSpecification; + $this->subject->specifications = $this->specifications; + return $this; } @@ -63,8 +89,13 @@ protected function buildSqlString( ParameterContainer $parameterContainer = null ) { $this->separateIndexesFromConstraints(); + $this->duplicateDropConstraintToDropIndex(); $this->deleteUnneededSpecification(); + // unlike CreateTableDecorator where CREATE TABLE is always present for new tables, regardless of Incex creation + // PostgreSQL does not use ALTER TABLE to add/drop indexes to existing tables. + // Therefore, if the only change is index related, DDL would have dangling ALTER TABLE. + // Consequently, table alterations outside of ALTER TABLE syntax get processed as whole different specification chunk $alterTable = ''; if ($this->hasCreateTable) { $alterTable = parent::buildSqlString($platform, $driver, $parameterContainer); @@ -96,6 +127,16 @@ private function separateIndexesFromConstraints() }); } + /** + * DROP CONSTRAINT always with DROP INDEX to compensate for dropConstraint() interface + * only accepting strings, not inspectable objects. + * @TODO if new signature removeConstraint(string|AbstractConstraint) gets approved, delete this method + */ + private function duplicateDropConstraintToDropIndex() + { + $this->dropIndexes = $this->subject->dropConstraints; + } + /** * @param PlatformInterface|null $adapterPlatform * @return array|void @@ -114,6 +155,20 @@ protected function processAddIndexes(PlatformInterface $adapterPlatform = null) return [$sqls]; } + protected function processDropIndexes(PlatformInterface $adapterPlatform = null) { + if (!$this->dropIndexes) { + return; + } + + $sqls = []; + + foreach ($this->dropIndexes as $index) { + $sqls[] = $adapterPlatform->quoteIdentifier($index); + } + + return [$sqls]; + } + /** * @param PlatformInterface|null $adapterPlatform * @return array|void diff --git a/test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php b/test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php index 9efeb71b4c..be6155dac2 100644 --- a/test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php +++ b/test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php @@ -24,7 +24,7 @@ public function testGetSqlString(AlterTable $createTable, $expectedSql) $alterTableDecorator->setSubject($createTable); $adapterPlatform = new Postgresql(); - $createTableSql = $alterTableDecorator->getSqlString($adapterPlatform); + $createTableSql = $this->trimExtraIndents($alterTableDecorator->getSqlString($adapterPlatform)); $this->assertEquals($expectedSql, $createTableSql); } @@ -39,7 +39,7 @@ public function tableAlterationsProvider() $noIndex->addColumn($newField_2); $expectedNewFieldNoIndex = 'ALTER TABLE "no_index"' . "\n" - . ' ADD COLUMN "field_2" VARCHAR(1024) NOT NULL;'; + . 'ADD COLUMN "field_2" VARCHAR(1024) NOT NULL;'; // AlterTable on its own $onlyIndex = new Ddl\AlterTable('only_index'); @@ -53,13 +53,28 @@ public function tableAlterationsProvider() $mixedAddIndex->addConstraint($newIdx); $expectedMixedAddIndex = 'ALTER TABLE "mixed_index"' . "\n" - . ' ADD COLUMN "field_2" VARCHAR(1024) NOT NULL;' . "\n" - . ' CREATE INDEX "new_idx" ON "mixed_index"("field_1");'; + . 'ADD COLUMN "field_2" VARCHAR(1024) NOT NULL;' . "\n" + . 'CREATE INDEX "new_idx" ON "mixed_index"("field_1");'; + + // Drop Index + // DROP CONSTRAINT always with DROP INDEX to compensate for dropConstraint() + // interface only accepting strings, not inspectable object. + $dropIndex = new Ddl\AlterTable('drop_index'); + $dropIndex->dropConstraint('new_idx'); + + $expectedOnlyDropIndex = 'ALTER TABLE "drop_index"' . "\n" + . 'DROP CONSTRAINT IF EXISTS "new_idx";' . "\n" + . 'DROP INDEX IF EXISTS "new_idx";'; return [ [$noIndex, $expectedNewFieldNoIndex], [$onlyIndex, $expectedOnlyIndex], [$mixedAddIndex, $expectedMixedAddIndex], + [$dropIndex, $expectedOnlyDropIndex] ]; } + + private function trimExtraIndents($sqlString) { + return join("\n", array_map("trim", explode("\n", $sqlString))); + } } From 379defc44fa68e018c4df2559991d028407916f5 Mon Sep 17 00:00:00 2001 From: Sasha Alex Romanenko Date: Tue, 6 Sep 2016 12:13:02 -0500 Subject: [PATCH 30/31] code sniffer rules --- src/Sql/Platform/Platform.php | 1 - .../Postgresql/Ddl/AlterTableDecorator.php | 14 ++++++++------ .../Postgresql/Ddl/CreateTableDecorator.php | 11 ++++++----- src/Sql/Platform/Postgresql/Postgresql.php | 2 +- .../Postgresql/Ddl/AlterTableDecoratorTest.php | 9 +++++---- .../Postgresql/Ddl/CreateTableDecoratorTest.php | 2 -- test/Sql/Platform/Postgresql/PostgresqlTest.php | 3 ++- 7 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/Sql/Platform/Platform.php b/src/Sql/Platform/Platform.php index 6e7637671e..0aaab84907 100644 --- a/src/Sql/Platform/Platform.php +++ b/src/Sql/Platform/Platform.php @@ -13,7 +13,6 @@ use Zend\Db\Adapter\Platform\PlatformInterface; use Zend\Db\Adapter\StatementContainerInterface; use Zend\Db\Sql\Exception; -use Zend\Db\Sql\Platform\Postgresql; use Zend\Db\Sql\PreparableSqlInterface; use Zend\Db\Sql\SqlInterface; diff --git a/src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php b/src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php index 0853a89e4c..c02a2c1967 100644 --- a/src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php +++ b/src/Sql/Platform/Postgresql/Ddl/AlterTableDecorator.php @@ -71,7 +71,8 @@ class AlterTableDecorator extends AlterTable implements PlatformDecoratorInterfa /** * @inheritDoc */ - public function setSubject($subject) { + public function setSubject($subject) + { $this->subject = $subject; $this->specifications[self::DROP_CONSTRAINTS] = $this->dropConstraintSpecification; @@ -109,11 +110,11 @@ protected function buildSqlString( private function separateIndexesFromConstraints() { // take advantage of PHP's ability to access protected properties of different instances created from same class - $this->addIndexes = array_filter($this->subject->addConstraints, function($constraint) { + $this->addIndexes = array_filter($this->subject->addConstraints, function ($constraint) { return $constraint instanceof Index; }); - $filteredConstraints = array_filter($this->subject->addConstraints, function($constraint) { + $filteredConstraints = array_filter($this->subject->addConstraints, function ($constraint) { return !($constraint instanceof Index); }); @@ -141,7 +142,8 @@ private function duplicateDropConstraintToDropIndex() * @param PlatformInterface|null $adapterPlatform * @return array|void */ - protected function processAddIndexes(PlatformInterface $adapterPlatform = null) { + protected function processAddIndexes(PlatformInterface $adapterPlatform = null) + { if (!$this->addIndexes) { return; } @@ -155,7 +157,8 @@ protected function processAddIndexes(PlatformInterface $adapterPlatform = null) return [$sqls]; } - protected function processDropIndexes(PlatformInterface $adapterPlatform = null) { + protected function processDropIndexes(PlatformInterface $adapterPlatform = null) + { if (!$this->dropIndexes) { return; } @@ -183,7 +186,6 @@ private function deleteUnneededSpecification() $subject = $this->subject; if (!($subject->addColumns || $subject->changeColumns || $subject->dropColumns || $subject->addConstraints || $subject->dropConstraints)) { - $this->hasCreateTable = false; unset($this->indexSpecification['statementEnd']); diff --git a/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php b/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php index 7266c1048e..50ca7e1716 100644 --- a/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php +++ b/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php @@ -46,7 +46,8 @@ class CreateTableDecorator extends CreateTable implements PlatformDecoratorInter * @param $subject * @return mixed */ - public function setSubject($subject) { + public function setSubject($subject) + { $this->subject = $subject; $this->specifications = array_merge($this->specifications, $this->indexSpecification); @@ -71,11 +72,11 @@ protected function buildSqlString( private function separateIndexesFromConstraints() { // take advantage of PHP's ability to access protected properties of different instances created from same class - $this->indexes = array_filter($this->subject->constraints, function($constraint) { + $this->indexes = array_filter($this->subject->constraints, function ($constraint) { return $constraint instanceof Index; }); - $filteredConstraints = array_filter($this->subject->constraints, function($constraint) { + $filteredConstraints = array_filter($this->subject->constraints, function ($constraint) { return !($constraint instanceof Index); }); @@ -93,7 +94,8 @@ private function separateIndexesFromConstraints() * @param PlatformInterface|null $adapterPlatform * @return array|void */ - protected function processIndexes(PlatformInterface $adapterPlatform = null) { + protected function processIndexes(PlatformInterface $adapterPlatform = null) + { if (!$this->indexes) { return; } @@ -115,5 +117,4 @@ protected function processStatementEnd(PlatformInterface $adapterPlatform = null { return ["\n);"]; } - } \ No newline at end of file diff --git a/src/Sql/Platform/Postgresql/Postgresql.php b/src/Sql/Platform/Postgresql/Postgresql.php index f54760d9c4..83b114a496 100644 --- a/src/Sql/Platform/Postgresql/Postgresql.php +++ b/src/Sql/Platform/Postgresql/Postgresql.php @@ -18,4 +18,4 @@ public function __construct() $this->setTypeDecorator('Zend\Db\Sql\Ddl\CreateTable', new Ddl\CreateTableDecorator()); $this->setTypeDecorator('Zend\Db\Sql\Ddl\AlterTable', new Ddl\AlterTableDecorator()); } -} \ No newline at end of file +} diff --git a/test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php b/test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php index be6155dac2..c42fa852a1 100644 --- a/test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php +++ b/test/Sql/Platform/Postgresql/Ddl/AlterTableDecoratorTest.php @@ -12,9 +12,9 @@ use Zend\Db\Adapter\Platform\Postgresql; use Zend\Db\Sql\Ddl\AlterTable; use Zend\Db\Sql\Ddl; -use Zend\Db\Sql\Platform\Postgresql\Ddl\AlterTableDecorator; -class AlterTableDecoratorTest extends \PHPUnit_Framework_TestCase { +class AlterTableDecoratorTest extends \PHPUnit_Framework_TestCase +{ /** * @dataProvider tableAlterationsProvider */ @@ -74,7 +74,8 @@ public function tableAlterationsProvider() ]; } - private function trimExtraIndents($sqlString) { - return join("\n", array_map("trim", explode("\n", $sqlString))); + private function trimExtraIndents($sqlString) + { + return implode("\n", array_map("trim", explode("\n", $sqlString))); } } diff --git a/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php b/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php index 2884ceb27e..695e599d12 100644 --- a/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php +++ b/test/Sql/Platform/Postgresql/Ddl/CreateTableDecoratorTest.php @@ -9,7 +9,6 @@ namespace Zend\Db\Sql\Platform\Postgresql\Ddl; - use Zend\Db\Adapter\Platform\Postgresql; use Zend\Db\Sql\Ddl\CreateTable; use Zend\Db\Sql\Ddl; @@ -88,5 +87,4 @@ public function tableDefinitionsProvider() [$mixed, $expectedMixed], ]; } - } diff --git a/test/Sql/Platform/Postgresql/PostgresqlTest.php b/test/Sql/Platform/Postgresql/PostgresqlTest.php index a368fa1c78..ac81400ae7 100644 --- a/test/Sql/Platform/Postgresql/PostgresqlTest.php +++ b/test/Sql/Platform/Postgresql/PostgresqlTest.php @@ -12,7 +12,8 @@ use Zend\Db\Sql\Platform\Postgresql\Postgresql; use Zend\Db\Sql\Platform\Postgresql\Ddl; -class PostgresqlTest extends \PHPUnit_Framework_TestCase { +class PostgresqlTest extends \PHPUnit_Framework_TestCase +{ /* * @testdox unit test / object test: Has CreateTable proxy * @covers Zend\Db\Sql\Platform\Postgresql\Postgresql::__construct From 7af3bfabc3c90063a4bf5987a07796059a6875d5 Mon Sep 17 00:00:00 2001 From: Sasha Alex Romanenko Date: Tue, 6 Sep 2016 12:16:06 -0500 Subject: [PATCH 31/31] code sniffer rules --- src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php b/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php index 50ca7e1716..b626c42148 100644 --- a/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php +++ b/src/Sql/Platform/Postgresql/Ddl/CreateTableDecorator.php @@ -117,4 +117,4 @@ protected function processStatementEnd(PlatformInterface $adapterPlatform = null { return ["\n);"]; } -} \ No newline at end of file +}