diff --git a/CHANGELOG.md b/CHANGELOG.md index e5e0740b32..53d2aa4b29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,46 @@ All notable changes to this project will be documented in this file, in reverse chronological order by release. +## 2.9.0 - TBD + +### Added + +- [#216](https://github.com/zendframework/zend-db/pull/216) added AFTER support + in ALTER TABLE syntax for MySQL +- [#223](https://github.com/zendframework/zend-db/pull/223) added support for + empty values set with IN predicate +- [#271](https://github.com/zendframework/zend-db/pull/271) added support for + dash character on MySQL identifier +- [#273](https://github.com/zendframework/zend-db/pull/273) added support for + implementing an error handler for db2_prepare +- [#275](https://github.com/zendframework/zend-db/pull/275) added support for + LIMIT OFFSET for db2 +- [#280](https://github.com/zendframework/zend-db/pull/280) added version dsn + parameter for pdo_dblib + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- [#205](https://github.com/zendframework/zend-db/pull/205) fixes the spaces in + ORDER BY syntax +- [#229](https://github.com/zendframework/zend-db/pull/229) fixes the support + of SSL for mysqli +- [#255](https://github.com/zendframework/zend-db/pull/255) fixes ResultSet with + array values +- [#261](https://github.com/zendframework/zend-db/pull/261) fixes Exception in + Firebird driver doesn't support lastInsertId +- [#276](https://github.com/zendframework/zend-db/pull/276) fixes the support + of PHP 7.2 +- [#287](https://github.com/zendframework/zend-db/pull/287) fixes the usage of + count() with PHP 7.2 + ## 2.8.3 - TBD ### Added diff --git a/docs/book/sql.md b/docs/book/sql.md index a7b3eed6ec..52f5ab8b43 100644 --- a/docs/book/sql.md +++ b/docs/book/sql.md @@ -161,7 +161,16 @@ $select->columns(['foo', 'bar']); // As an associative array with aliases as the keys // (produces 'bar' AS 'foo', 'bax' AS 'baz') -$select->columns(['foo' => 'bar', 'baz' => 'bax']); +$select->columns([ + 'foo' => 'bar', + 'baz' => 'bax' +]); + +// Sql function call on the column +// (produces CONCAT_WS('/', 'bar', 'bax') AS 'foo') +$select->columns([ + 'foo' => new \Zend\Db\Sql\Expression("CONCAT_WS('/', 'bar', 'bax')") +]); ``` ### join() diff --git a/phpcs.xml b/phpcs.xml index 9e6a751166..9a2cd69887 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -1,6 +1,9 @@  - + + + */test/Adapter/Driver/IbmDb2/StatementTest.php + src diff --git a/src/Adapter/Driver/IbmDb2/Statement.php b/src/Adapter/Driver/IbmDb2/Statement.php index 830aa3a8c4..a3b213d285 100644 --- a/src/Adapter/Driver/IbmDb2/Statement.php +++ b/src/Adapter/Driver/IbmDb2/Statement.php @@ -9,6 +9,7 @@ namespace Zend\Db\Adapter\Driver\IbmDb2; +use ErrorException; use Zend\Db\Adapter\Driver\StatementInterface; use Zend\Db\Adapter\Exception; use Zend\Db\Adapter\ParameterContainer; @@ -172,7 +173,14 @@ public function prepare($sql = null) $sql = $this->sql; } - $this->resource = db2_prepare($this->db2, $sql); + try { + set_error_handler($this->createErrorHandler()); + $this->resource = db2_prepare($this->db2, $sql); + } catch (ErrorException $e) { + throw new Exception\RuntimeException($e->getMessage() . '. ' . db2_stmt_errormsg(), db2_stmt_error(), $e); + } finally { + restore_error_handler(); + } if ($this->resource === false) { throw new Exception\RuntimeException(db2_stmt_errormsg(), db2_stmt_error()); @@ -239,4 +247,31 @@ public function execute($parameters = null) $result = $this->driver->createResult($this->resource); return $result; } + + /** + * Creates and returns a callable error handler that raises exceptions. + * + * Only raises exceptions for errors that are within the error_reporting mask. + * + * @return callable + */ + private function createErrorHandler() + { + /** + * @param int $errno + * @param string $errstr + * @param string $errfile + * @param int $errline + * @return void + * @throws ErrorException if error is not within the error_reporting mask. + */ + return function ($errno, $errstr, $errfile, $errline) { + if (! (error_reporting() & $errno)) { + // error_reporting does not include this error + return; + } + + throw new ErrorException($errstr, 0, $errno, $errfile, $errline); + }; + } } diff --git a/src/Adapter/Driver/Mysqli/Result.php b/src/Adapter/Driver/Mysqli/Result.php index c38baa3675..e83b3c0831 100644 --- a/src/Adapter/Driver/Mysqli/Result.php +++ b/src/Adapter/Driver/Mysqli/Result.php @@ -279,11 +279,10 @@ public function key() */ public function rewind() { - if ($this->position !== 0) { - if ($this->isBuffered === false) { - throw new Exception\RuntimeException('Unbuffered results cannot be rewound for multiple iterations'); - } + if (0 !== $this->position && false === $this->isBuffered) { + throw new Exception\RuntimeException('Unbuffered results cannot be rewound for multiple iterations'); } + $this->resource->data_seek(0); // works for both mysqli_result & mysqli_stmt $this->currentComplete = false; $this->position = 0; diff --git a/src/Adapter/Driver/Pdo/Connection.php b/src/Adapter/Driver/Pdo/Connection.php index de5ea112d4..196aade6fa 100644 --- a/src/Adapter/Driver/Pdo/Connection.php +++ b/src/Adapter/Driver/Pdo/Connection.php @@ -202,6 +202,9 @@ public function connect() case 'unix_socket': $unix_socket = (string) $value; break; + case 'version': + $version = (string) $value; + break; case 'driver_options': case 'options': $value = (array) $value; @@ -250,6 +253,9 @@ public function connect() if (isset($unix_socket)) { $dsn[] = "unix_socket={$unix_socket}"; } + if (isset($version)) { + $dsn[] = "version={$version}"; + } break; } $dsn = $pdoDriver . ':' . implode(';', $dsn); diff --git a/src/Adapter/Platform/AbstractPlatform.php b/src/Adapter/Platform/AbstractPlatform.php index 1857c7ba92..42b8857510 100644 --- a/src/Adapter/Platform/AbstractPlatform.php +++ b/src/Adapter/Platform/AbstractPlatform.php @@ -26,6 +26,11 @@ abstract class AbstractPlatform implements PlatformInterface */ protected $quoteIdentifiers = true; + /** + * @var string + */ + protected $quoteIdentifierFragmentPattern = '/([^0-9,a-z,A-Z$_:])/i'; + /** * {@inheritDoc} */ @@ -42,7 +47,7 @@ public function quoteIdentifierInFragment($identifier, array $safeWords = []) } $parts = preg_split( - '/([^0-9,a-z,A-Z$_:])/i', + $this->quoteIdentifierFragmentPattern, $identifier, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY diff --git a/src/Adapter/Platform/Mysql.php b/src/Adapter/Platform/Mysql.php index a0b091ea92..3aae7bbc40 100644 --- a/src/Adapter/Platform/Mysql.php +++ b/src/Adapter/Platform/Mysql.php @@ -31,6 +31,13 @@ class Mysql extends AbstractPlatform */ protected $resource = null; + /** + * NOTE: Include dashes for MySQL only, need tests for others platforms + * + * @var string + */ + protected $quoteIdentifierFragmentPattern = '/([^0-9,a-z,A-Z$_\-:])/i'; + /** * @param null|\Zend\Db\Adapter\Driver\Mysqli\Mysqli|\Zend\Db\Adapter\Driver\Pdo\Pdo|\mysqli|\PDO $driver */ diff --git a/src/Sql/Platform/IbmDb2/SelectDecorator.php b/src/Sql/Platform/IbmDb2/SelectDecorator.php index c9bfe32ecf..f1988c09b5 100644 --- a/src/Sql/Platform/IbmDb2/SelectDecorator.php +++ b/src/Sql/Platform/IbmDb2/SelectDecorator.php @@ -27,7 +27,13 @@ class SelectDecorator extends Select implements PlatformDecoratorInterface */ protected $subject = null; - /** + /** + * @var bool + */ + protected $supportsLimitOffset = false; + + + /** * @return bool */ public function getIsSelectContainDistinct() @@ -51,6 +57,22 @@ public function setSubject($select) $this->subject = $select; } + /** + * @return bool + */ + public function getSupportsLimitOffset() + { + return $this->supportsLimitOffset; + } + + /** + * @param bool $supportsLimitOffset + */ + public function setSupportsLimitOffset($supportsLimitOffset) + { + $this->supportsLimitOffset = $supportsLimitOffset; + } + /** * @see Select::renderTable */ @@ -87,6 +109,23 @@ protected function processLimitOffset( return; } + if ($this->supportsLimitOffset) { + // Note: db2_prepare/db2_execute fails with positional parameters, for LIMIT & OFFSET + $limit = (int) $this->limit; + if (! $limit) { + return; + } + + $offset = (int) $this->offset; + if ($offset) { + array_push($sqls, sprintf("LIMIT %s OFFSET %s", $limit, $offset)); + return; + } + + array_push($sqls, sprintf("LIMIT %s", $limit)); + return; + } + $selectParameters = $parameters[self::SELECT]; $starSuffix = $platform->getIdentifierSeparator() . self::SQL_STAR; diff --git a/src/Sql/Platform/Mysql/Ddl/AlterTableDecorator.php b/src/Sql/Platform/Mysql/Ddl/AlterTableDecorator.php index 2de7fc3ce8..ea3176532d 100644 --- a/src/Sql/Platform/Mysql/Ddl/AlterTableDecorator.php +++ b/src/Sql/Platform/Mysql/Ddl/AlterTableDecorator.php @@ -33,6 +33,7 @@ class AlterTableDecorator extends AlterTable implements PlatformDecoratorInterfa 'columnformat' => 4, 'format' => 4, 'storage' => 5, + 'after' => 6 ]; /** @@ -130,6 +131,9 @@ protected function processAddColumns(PlatformInterface $adapterPlatform = null) $insert = ' STORAGE ' . strtoupper($coValue); $j = 2; break; + case 'after': + $insert = ' AFTER ' . $adapterPlatform->quoteIdentifier($coValue); + $j = 2; } if ($insert) { diff --git a/src/Sql/Predicate/In.php b/src/Sql/Predicate/In.php index 2e30b6ef85..eabfec0037 100644 --- a/src/Sql/Predicate/In.php +++ b/src/Sql/Predicate/In.php @@ -33,7 +33,7 @@ public function __construct($identifier = null, $valueSet = null) if ($identifier) { $this->setIdentifier($identifier); } - if ($valueSet) { + if ($valueSet !== null) { $this->setValueSet($valueSet); } } @@ -122,9 +122,10 @@ public function getExpressionData() foreach ($values as $argument) { list($replacements[], $types[]) = $this->normalizeArgument($argument, self::TYPE_VALUE); } + $valuePlaceholders = count($values) > 0 ? array_fill(0, count($values), '%s') : []; $specification = vsprintf( $this->specification, - [$identifierSpecFragment, '(' . implode(', ', array_fill(0, count($values), '%s')) . ')'] + [$identifierSpecFragment, '(' . implode(', ', $valuePlaceholders) . ')'] ); } diff --git a/src/TableGateway/AbstractTableGateway.php b/src/TableGateway/AbstractTableGateway.php index 122bb1f146..014c08eaa2 100644 --- a/src/TableGateway/AbstractTableGateway.php +++ b/src/TableGateway/AbstractTableGateway.php @@ -198,7 +198,7 @@ public function select($where = null) /** * @param Select $select - * @return null|ResultSetInterface + * @return ResultSetInterface * @throws \RuntimeException */ public function selectWith(Select $select) diff --git a/test/Adapter/Driver/IbmDb2/StatementIntegrationTest.php b/test/Adapter/Driver/IbmDb2/StatementIntegrationTest.php index 7478479bd5..0b9960e99a 100644 --- a/test/Adapter/Driver/IbmDb2/StatementIntegrationTest.php +++ b/test/Adapter/Driver/IbmDb2/StatementIntegrationTest.php @@ -12,6 +12,7 @@ use PHPUnit\Framework\TestCase; use Zend\Db\Adapter\Driver\IbmDb2\IbmDb2; use Zend\Db\Adapter\Driver\IbmDb2\Statement; +use Zend\Db\Adapter\Exception\RuntimeException; /** * @group integration @@ -74,7 +75,7 @@ public function testGetResource() $statement = new Statement; $statement->initialize($db2Resource); - $statement->prepare("SELECT 'foo'"); + $statement->prepare("SELECT 'foo' FROM sysibm.sysdummy1"); $resource = $statement->getResource(); self::assertEquals('DB2 Statement', get_resource_type($resource)); unset($resource, $db2Resource); @@ -101,7 +102,23 @@ public function testPrepare() } /** - * @covers \Zend\Db\Adapter\Driver\IbmDb2\Statement::execute + * @covers Zend\Db\Adapter\Driver\IbmDb2\Statement::prepare + */ + public function testPrepareThrowsAnExceptionOnFailure() + { + $db2Resource = db2_connect( + $this->variables['database'], + $this->variables['username'], + $this->variables['password'] + ); + $statement = new Statement; + $statement->initialize($db2Resource); + $this->setExpectedException(RuntimeException::class); + $statement->prepare("SELECT"); + } + + /** + * @covers Zend\Db\Adapter\Driver\IbmDb2\Statement::execute */ public function testExecute() { diff --git a/test/Adapter/Driver/IbmDb2/StatementTest.php b/test/Adapter/Driver/IbmDb2/StatementTest.php index 9f74755901..8c600219a0 100644 --- a/test/Adapter/Driver/IbmDb2/StatementTest.php +++ b/test/Adapter/Driver/IbmDb2/StatementTest.php @@ -13,6 +13,9 @@ use Zend\Db\Adapter\Driver\IbmDb2\IbmDb2; use Zend\Db\Adapter\Driver\IbmDb2\Statement; use Zend\Db\Adapter\ParameterContainer; +use Zend\Db\Adapter\Exception\RuntimeException; + +include __DIR__ . '/TestAsset/Db2Functions.php'; class StatementTest extends TestCase { @@ -27,6 +30,9 @@ class StatementTest extends TestCase */ protected function setUp() { + // store current error_reporting value as we may change it + // in a test + $this->currentErrorReporting = error_reporting(); $this->statement = new Statement; } @@ -36,6 +42,8 @@ protected function setUp() */ protected function tearDown() { + // ensure error_reporting is set back to correct value + error_reporting($this->currentErrorReporting); } /** @@ -102,27 +110,68 @@ public function testGetSql() } /** - * @covers \Zend\Db\Adapter\Driver\IbmDb2\Statement::prepare - * @todo Implement testPrepare(). + * @covers Zend\Db\Adapter\Driver\IbmDb2\Statement::prepare + * @covers Zend\Db\Adapter\Driver\IbmDb2\Statement::isPrepared */ public function testPrepare() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' + $sql = "SELECT 'foo' FROM SYSIBM.SYSDUMMY1"; + $this->statement->prepare($sql); + $this->assertTrue($this->statement->isPrepared()); + } + + /** + * @covers Zend\Db\Adapter\Driver\IbmDb2\Statement::prepare + * @covers Zend\Db\Adapter\Driver\IbmDb2\Statement::isPrepared + */ + public function testPreparingTwiceErrors() + { + $sql = "SELECT 'foo' FROM SYSIBM.SYSDUMMY1"; + $this->statement->prepare($sql); + $this->assertTrue($this->statement->isPrepared()); + + $this->expectException( + RuntimeException::class, + 'This statement has been prepared already' ); + $this->statement->prepare($sql); } /** - * @covers \Zend\Db\Adapter\Driver\IbmDb2\Statement::isPrepared - * @todo Implement testIsPrepared(). + * @covers Zend\Db\Adapter\Driver\IbmDb2\Statement::prepare + * @covers Zend\Db\Adapter\Driver\IbmDb2\Statement::setSql */ - public function testIsPrepared() + public function testPrepareThrowsRuntimeExceptionOnInvalidSql() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' + $sql = "INVALID SQL"; + $this->statement->setSql($sql); + + $this->expectException( + RuntimeException::class, + 'SQL is invalid. Error message' + ); + $this->statement->prepare(); + } + + /** + * If error_reporting() is turned off, then the error handler will not + * be called, but a RuntimeException will still be generated as the + * resource is false + * + * @covers Zend\Db\Adapter\Driver\IbmDb2\Statement::prepare + * @covers Zend\Db\Adapter\Driver\IbmDb2\Statement::setSql + */ + public function testPrepareThrowsRuntimeExceptionOnInvalidSqlWithErrorReportingDisabled() + { + error_reporting(0); + $sql = "INVALID SQL"; + $this->statement->setSql($sql); + + $this->expectException( + RuntimeException::class, + 'Error message' ); + $this->statement->prepare(); } /** diff --git a/test/Adapter/Driver/IbmDb2/TestAsset/Db2Functions.php b/test/Adapter/Driver/IbmDb2/TestAsset/Db2Functions.php new file mode 100644 index 0000000000..7ac4cacec2 --- /dev/null +++ b/test/Adapter/Driver/IbmDb2/TestAsset/Db2Functions.php @@ -0,0 +1,57 @@ +connection->connect(); } + + public function testDblibArrayOfConnectionParametersCreatesCorrectDsn() + { + $this->connection->setConnectionParameters([ + 'driver' => 'pdo_dblib', + 'charset' => 'UTF-8', + 'dbname' => 'foo', + 'port' => '1433', + 'version' => '7.3', + ]); + try { + $this->connection->connect(); + } catch (\Exception $e) { + } + $responseString = $this->connection->getDsn(); + + $this->assertStringStartsWith('dblib:', $responseString); + $this->assertContains('charset=UTF-8', $responseString); + $this->assertContains('dbname=foo', $responseString); + $this->assertContains('port=1433', $responseString); + $this->assertContains('version=7.3', $responseString); + } } diff --git a/test/Adapter/Platform/MysqlTest.php b/test/Adapter/Platform/MysqlTest.php index b2c94cf22a..a5d8782ffc 100644 --- a/test/Adapter/Platform/MysqlTest.php +++ b/test/Adapter/Platform/MysqlTest.php @@ -163,6 +163,23 @@ public function testQuoteIdentifierInFragment() $this->platform->quoteIdentifierInFragment('cmis:$TableName as cmis:TableAlias') ); + $this->assertEquals( + '`foo-bar`.`bar-foo`', + $this->platform->quoteIdentifierInFragment('foo-bar.bar-foo') + ); + $this->assertEquals( + '`foo-bar` as `bar-foo`', + $this->platform->quoteIdentifierInFragment('foo-bar as bar-foo') + ); + $this->assertEquals( + '`$TableName-$ColumnName`.`bar-foo`', + $this->platform->quoteIdentifierInFragment('$TableName-$ColumnName.bar-foo') + ); + $this->assertEquals( + '`cmis:$TableName-$ColumnName` as `cmis:TableAlias-ColumnAlias`', + $this->platform->quoteIdentifierInFragment('cmis:$TableName-$ColumnName as cmis:TableAlias-ColumnAlias') + ); + // single char words self::assertEquals( '(`foo`.`bar` = `boo`.`baz`)', @@ -174,6 +191,19 @@ public function testQuoteIdentifierInFragment() ); self::assertEquals('`foo`=`bar`', $this->platform->quoteIdentifierInFragment('foo=bar', ['='])); + $this->assertEquals( + '(`foo-bar`.`bar-foo` = `boo-baz`.`baz-boo`)', + $this->platform->quoteIdentifierInFragment('(foo-bar.bar-foo = boo-baz.baz-boo)', ['(', ')', '=']) + ); + $this->assertEquals( + '(`foo-bar`.`bar-foo`=`boo-baz`.`baz-boo`)', + $this->platform->quoteIdentifierInFragment('(foo-bar.bar-foo=boo-baz.baz-boo)', ['(', ')', '=']) + ); + $this->assertEquals( + '`foo-bar`=`bar-foo`', + $this->platform->quoteIdentifierInFragment('foo-bar=bar-foo', ['=']) + ); + // case insensitive safe words self::assertEquals( '(`foo`.`bar` = `boo`.`baz`) AND (`foo`.`baz` = `boo`.`baz`)', @@ -183,6 +213,14 @@ public function testQuoteIdentifierInFragment() ) ); + $this->assertEquals( + '(`foo-bar`.`bar-foo` = `boo-baz`.`baz-boo`) AND (`foo-baz`.`baz-foo` = `boo-baz`.`baz-boo`)', + $this->platform->quoteIdentifierInFragment( + '(foo-bar.bar-foo = boo-baz.baz-boo) AND (foo-baz.baz-foo = boo-baz.baz-boo)', + ['(', ')', '=', 'and'] + ) + ); + // case insensitive safe words in field self::assertEquals( '(`foo`.`bar` = `boo`.baz) AND (`foo`.baz = `boo`.baz)', @@ -191,5 +229,14 @@ public function testQuoteIdentifierInFragment() ['(', ')', '=', 'and', 'bAz'] ) ); + + // case insensitive safe words in field + $this->assertEquals( + '(`foo-bar`.`bar-foo` = `boo-baz`.baz-boo) AND (`foo-baz`.`baz-foo` = `boo-baz`.baz-boo)', + $this->platform->quoteIdentifierInFragment( + '(foo-bar.bar-foo = boo-baz.baz-boo) AND (foo-baz.baz-foo = boo-baz.baz-boo)', + ['(', ')', '=', 'and', 'bAz-BOo'] + ) + ); } } diff --git a/test/Sql/Platform/IbmDb2/SelectDecoratorTest.php b/test/Sql/Platform/IbmDb2/SelectDecoratorTest.php index 3dce367f42..1ad37267a4 100644 --- a/test/Sql/Platform/IbmDb2/SelectDecoratorTest.php +++ b/test/Sql/Platform/IbmDb2/SelectDecoratorTest.php @@ -26,8 +26,13 @@ class SelectDecoratorTest extends TestCase * @covers \Zend\Db\Sql\Platform\SqlServer\SelectDecorator::processLimitOffset * @dataProvider dataProvider */ - public function testPrepareStatement(Select $select, $expectedPrepareSql, $expectedParams, $notUsed) - { + public function testPrepareStatement( + Select $select, + $expectedPrepareSql, + $expectedParams, + $notUsed, + $supportsLimitOffset + ) { $driver = $this->getMockBuilder('Zend\Db\Adapter\Driver\DriverInterface')->getMock(); $driver->expects($this->any())->method('formatParameterName')->will($this->returnValue('?')); @@ -49,6 +54,7 @@ public function testPrepareStatement(Select $select, $expectedPrepareSql, $expec $selectDecorator = new SelectDecorator; $selectDecorator->setSubject($select); + $selectDecorator->setSupportsLimitOffset($supportsLimitOffset); $selectDecorator->prepareStatement($adapter, $statement); self::assertEquals($expectedParams, $parameterContainer->getNamedArray()); @@ -60,7 +66,7 @@ public function testPrepareStatement(Select $select, $expectedPrepareSql, $expec * @covers \Zend\Db\Sql\Platform\IbmDb2\SelectDecorator::getSqlString * @dataProvider dataProvider */ - public function testGetSqlString(Select $select, $ignored0, $ignored1, $expectedSql) + public function testGetSqlString(Select $select, $ignored0, $ignored1, $expectedSql, $supportsLimitOffset) { $parameterContainer = new ParameterContainer; $statement = $this->getMockBuilder('Zend\Db\Adapter\Driver\StatementInterface')->getMock(); @@ -69,6 +75,7 @@ public function testGetSqlString(Select $select, $ignored0, $ignored1, $expected $selectDecorator = new SelectDecorator; $selectDecorator->setSubject($select); + $selectDecorator->setSupportsLimitOffset($supportsLimitOffset); self::assertEquals($expectedSql, @$selectDecorator->getSqlString(new IbmDb2Platform)); } @@ -123,12 +130,26 @@ public function dataProvider() $expectedSql4 = 'SELECT * FROM ( SELECT "x".*, ROW_NUMBER() OVER () AS ZEND_DB_ROWNUM FROM "foo" "x" WHERE "x"."id" > \'10\' AND "x"."id" < \'31\' ) AS ZEND_IBMDB2_SERVER_LIMIT_OFFSET_EMULATION WHERE ZEND_IBMDB2_SERVER_LIMIT_OFFSET_EMULATION.ZEND_DB_ROWNUM BETWEEN 0 AND 5'; // @codingStandardsIgnoreEnd + $select5 = new Select; + $select5->from(['x' => 'foo'])->limit(5); + $expectedParams5 = []; + $expectedPrepareSql5 = 'SELECT "x".* FROM "foo" "x" LIMIT 5'; + $expectedSql5 = 'SELECT "x".* FROM "foo" "x" LIMIT 5'; + + $select6 = new Select; + $select6->columns([new Expression('DISTINCT(id) as id')])->from(['x' => 'foo'])->limit(5)->offset(10); + $expectedParams6 = []; + $expectedPrepareSql6 = 'SELECT DISTINCT(id) as id FROM "foo" "x" LIMIT 5 OFFSET 10'; + $expectedSql6 = 'SELECT DISTINCT(id) as id FROM "foo" "x" LIMIT 5 OFFSET 10'; + return [ - [$select0, $expectedPrepareSql0, $expectedParams0, $expectedSql0], - [$select1, $expectedPrepareSql1, $expectedParams1, $expectedSql1], - [$select2, $expectedPrepareSql2, $expectedParams2, $expectedSql2], - [$select3, $expectedPrepareSql3, $expectedParams3, $expectedSql3], - [$select4, $expectedPrepareSql4, $expectedParams4, $expectedSql4], + [$select0, $expectedPrepareSql0, $expectedParams0, $expectedSql0, false], + [$select1, $expectedPrepareSql1, $expectedParams1, $expectedSql1, false], + [$select2, $expectedPrepareSql2, $expectedParams2, $expectedSql2, false], + [$select3, $expectedPrepareSql3, $expectedParams3, $expectedSql3, false], + [$select4, $expectedPrepareSql4, $expectedParams4, $expectedSql4, false], + [$select5, $expectedPrepareSql5, $expectedParams5, $expectedSql5, true], + [$select6, $expectedPrepareSql6, $expectedParams6, $expectedSql6, true], ]; } } diff --git a/test/Sql/Platform/Mysql/Ddl/AlterTableDecoratorTest.php b/test/Sql/Platform/Mysql/Ddl/AlterTableDecoratorTest.php new file mode 100644 index 0000000000..5be80ff688 --- /dev/null +++ b/test/Sql/Platform/Mysql/Ddl/AlterTableDecoratorTest.php @@ -0,0 +1,55 @@ +setSubject($ct)); + } + + /** + * @covers Zend\Db\Sql\Platform\Mysql\Ddl\AlterTableDecorator::getSqlString + */ + public function testGetSqlString() + { + $ctd = new AlterTableDecorator(); + $ct = new AlterTable('foo'); + $ctd->setSubject($ct); + + $col = new Column('bar'); + $col->setOption('zerofill', true); + $col->setOption('unsigned', true); + $col->setOption('identity', true); + $col->setOption('comment', 'baz'); + $col->setOption('after', 'bar'); + $col->addConstraint(new PrimaryKey()); + $ct->addColumn($col); + + self::assertEquals( + "ALTER TABLE `foo`\n ADD COLUMN `bar` INTEGER UNSIGNED ZEROFILL " . + "NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'baz' AFTER `bar`", + @$ctd->getSqlString(new Mysql()) + ); + } +} diff --git a/test/Sql/Predicate/InTest.php b/test/Sql/Predicate/InTest.php index 1b1f8a0e5c..58b78a7c9f 100644 --- a/test/Sql/Predicate/InTest.php +++ b/test/Sql/Predicate/InTest.php @@ -29,6 +29,13 @@ public function testCanPassIdentifierAndValueSetToConstructor() self::assertEquals([1, 2], $in->getValueSet()); } + public function testCanPassIdentifierAndEmptyValueSetToConstructor() + { + $in = new In('foo.bar', []); + $this->assertEquals('foo.bar', $in->getIdentifier()); + $this->assertEquals([], $in->getValueSet()); + } + public function testIdentifierIsMutable() { $in = new In(); @@ -82,6 +89,18 @@ public function testGetExpressionDataWithSubselect() self::assertEquals($expected, $in->getExpressionData()); } + public function testGetExpressionDataWithEmptyValues() + { + $select = new Select; + $in = new In('foo', []); + $expected = [[ + '%s IN ()', + ['foo'], + [$in::TYPE_IDENTIFIER] + ]]; + $this->assertEquals($expected, $in->getExpressionData()); + } + public function testGetExpressionDataWithSubselectAndIdentifier() { $select = new Select;