PostgresSchemaTest.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719
  1. <?php
  2. /**
  3. * PHP Version 5.4
  4. *
  5. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  6. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  7. *
  8. * Licensed under The MIT License
  9. * For full copyright and license information, please see the LICENSE.txt
  10. * Redistributions of files must retain the above copyright notice.
  11. *
  12. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  13. * @link http://cakephp.org CakePHP(tm) Project
  14. * @since CakePHP(tm) v 3.0.0
  15. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  16. */
  17. namespace Cake\Test\TestCase\Database\Schema;
  18. use Cake\Core\Configure;
  19. use Cake\Database\ConnectionManager;
  20. use Cake\Database\Schema\Collection as SchemaCollection;
  21. use Cake\Database\Schema\PostgresSchema;
  22. use Cake\Database\Schema\Table;
  23. use Cake\TestSuite\TestCase;
  24. /**
  25. * Postgres schema test case.
  26. */
  27. class PostgresSchemaTest extends TestCase {
  28. /**
  29. * Helper method for skipping tests that need a real connection.
  30. *
  31. * @return void
  32. */
  33. protected function _needsConnection() {
  34. $config = ConnectionManager::config('test');
  35. $this->skipIf(strpos($config['className'], 'Postgres') === false, 'Not using Postgres for test config');
  36. }
  37. /**
  38. * Helper method for testing methods.
  39. *
  40. * @return void
  41. */
  42. protected function _createTables($connection) {
  43. $this->_needsConnection();
  44. $connection->execute('DROP TABLE IF EXISTS schema_articles');
  45. $connection->execute('DROP TABLE IF EXISTS schema_authors');
  46. $table = <<<SQL
  47. CREATE TABLE schema_authors (
  48. id SERIAL,
  49. name VARCHAR(50),
  50. bio DATE,
  51. created TIMESTAMP,
  52. PRIMARY KEY (id)
  53. )
  54. SQL;
  55. $connection->execute($table);
  56. $table = <<<SQL
  57. CREATE TABLE schema_articles (
  58. id BIGINT PRIMARY KEY,
  59. title VARCHAR(20),
  60. body TEXT,
  61. author_id INTEGER NOT NULL,
  62. published BOOLEAN DEFAULT false,
  63. views SMALLINT DEFAULT 0,
  64. created TIMESTAMP,
  65. CONSTRAINT "content_idx" UNIQUE ("title", "body"),
  66. CONSTRAINT "author_idx" FOREIGN KEY ("author_id") REFERENCES "schema_authors" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
  67. )
  68. SQL;
  69. $connection->execute($table);
  70. $connection->execute('COMMENT ON COLUMN "schema_articles"."title" IS \'a title\'');
  71. $connection->execute('CREATE INDEX "author_idx" ON "schema_articles" ("author_id")');
  72. }
  73. /**
  74. * Data provider for convert column testing
  75. *
  76. * @return array
  77. */
  78. public static function convertColumnProvider() {
  79. return [
  80. [
  81. 'TIMESTAMP',
  82. ['type' => 'datetime', 'length' => null]
  83. ],
  84. [
  85. 'TIMESTAMP WITHOUT TIME ZONE',
  86. ['type' => 'datetime', 'length' => null]
  87. ],
  88. [
  89. 'DATE',
  90. ['type' => 'date', 'length' => null]
  91. ],
  92. [
  93. 'TIME',
  94. ['type' => 'time', 'length' => null]
  95. ],
  96. [
  97. 'SMALLINT',
  98. ['type' => 'integer', 'length' => 5]
  99. ],
  100. [
  101. 'INTEGER',
  102. ['type' => 'integer', 'length' => 10]
  103. ],
  104. [
  105. 'SERIAL',
  106. ['type' => 'integer', 'length' => 10]
  107. ],
  108. [
  109. 'BIGINT',
  110. ['type' => 'biginteger', 'length' => 20]
  111. ],
  112. [
  113. 'NUMERIC',
  114. ['type' => 'decimal', 'length' => null]
  115. ],
  116. [
  117. 'DECIMAL(10,2)',
  118. ['type' => 'decimal', 'length' => null]
  119. ],
  120. [
  121. 'MONEY',
  122. ['type' => 'decimal', 'length' => null]
  123. ],
  124. [
  125. 'VARCHAR',
  126. ['type' => 'string', 'length' => null]
  127. ],
  128. [
  129. 'VARCHAR(10)',
  130. ['type' => 'string', 'length' => 10]
  131. ],
  132. [
  133. 'CHARACTER VARYING',
  134. ['type' => 'string', 'length' => null]
  135. ],
  136. [
  137. 'CHARACTER VARYING(10)',
  138. ['type' => 'string', 'length' => 10]
  139. ],
  140. [
  141. 'CHAR(10)',
  142. ['type' => 'string', 'fixed' => true, 'length' => 10]
  143. ],
  144. [
  145. 'CHARACTER(10)',
  146. ['type' => 'string', 'fixed' => true, 'length' => 10]
  147. ],
  148. [
  149. 'UUID',
  150. ['type' => 'string', 'fixed' => true, 'length' => 36]
  151. ],
  152. [
  153. 'INET',
  154. ['type' => 'string', 'length' => 39]
  155. ],
  156. [
  157. 'TEXT',
  158. ['type' => 'text', 'length' => null]
  159. ],
  160. [
  161. 'BYTEA',
  162. ['type' => 'binary', 'length' => null]
  163. ],
  164. [
  165. 'REAL',
  166. ['type' => 'float', 'length' => null]
  167. ],
  168. [
  169. 'DOUBLE PRECISION',
  170. ['type' => 'float', 'length' => null]
  171. ],
  172. [
  173. 'BIGSERIAL',
  174. ['type' => 'biginteger', 'length' => 20]
  175. ],
  176. ];
  177. }
  178. /**
  179. * Test parsing Postgres column types from field description.
  180. *
  181. * @dataProvider convertColumnProvider
  182. * @return void
  183. */
  184. public function testConvertColumn($type, $expected) {
  185. $field = [
  186. 'name' => 'field',
  187. 'type' => $type,
  188. 'null' => 'YES',
  189. 'default' => 'Default value',
  190. 'comment' => 'Comment section',
  191. 'char_length' => null,
  192. ];
  193. $expected += [
  194. 'null' => true,
  195. 'default' => 'Default value',
  196. 'comment' => 'Comment section',
  197. ];
  198. $driver = $this->getMock('Cake\Database\Driver\Postgres');
  199. $dialect = new PostgresSchema($driver);
  200. $table = $this->getMock('Cake\Database\Schema\Table', [], ['table']);
  201. $table->expects($this->at(0))->method('addColumn')->with('field', $expected);
  202. $dialect->convertFieldDescription($table, $field);
  203. }
  204. /**
  205. * Test listing tables with Postgres
  206. *
  207. * @return void
  208. */
  209. public function testListTables() {
  210. $connection = ConnectionManager::get('test');
  211. $this->_createTables($connection);
  212. $schema = new SchemaCollection($connection);
  213. $result = $schema->listTables();
  214. $this->assertInternalType('array', $result);
  215. $this->assertCount(2, $result);
  216. $this->assertEquals('schema_articles', $result[0]);
  217. $this->assertEquals('schema_authors', $result[1]);
  218. }
  219. /**
  220. * Test describing a table with Postgres
  221. *
  222. * @return void
  223. */
  224. public function testDescribeTable() {
  225. $connection = ConnectionManager::get('test');
  226. $this->_createTables($connection);
  227. $schema = new SchemaCollection($connection);
  228. $result = $schema->describe('schema_articles');
  229. $expected = [
  230. 'id' => [
  231. 'type' => 'biginteger',
  232. 'null' => false,
  233. 'default' => null,
  234. 'length' => 20,
  235. 'precision' => null,
  236. 'fixed' => null,
  237. 'comment' => null,
  238. ],
  239. 'title' => [
  240. 'type' => 'string',
  241. 'null' => true,
  242. 'default' => null,
  243. 'length' => 20,
  244. 'precision' => null,
  245. 'comment' => 'a title',
  246. 'fixed' => null,
  247. ],
  248. 'body' => [
  249. 'type' => 'text',
  250. 'null' => true,
  251. 'default' => null,
  252. 'length' => null,
  253. 'precision' => null,
  254. 'fixed' => null,
  255. 'comment' => null,
  256. ],
  257. 'author_id' => [
  258. 'type' => 'integer',
  259. 'null' => false,
  260. 'default' => null,
  261. 'length' => 10,
  262. 'precision' => null,
  263. 'fixed' => null,
  264. 'comment' => null,
  265. ],
  266. 'published' => [
  267. 'type' => 'boolean',
  268. 'null' => true,
  269. 'default' => 0,
  270. 'length' => null,
  271. 'precision' => null,
  272. 'fixed' => null,
  273. 'comment' => null,
  274. ],
  275. 'views' => [
  276. 'type' => 'integer',
  277. 'null' => true,
  278. 'default' => 0,
  279. 'length' => 5,
  280. 'precision' => null,
  281. 'fixed' => null,
  282. 'comment' => null,
  283. ],
  284. 'created' => [
  285. 'type' => 'datetime',
  286. 'null' => true,
  287. 'default' => null,
  288. 'length' => null,
  289. 'precision' => null,
  290. 'fixed' => null,
  291. 'comment' => null,
  292. ],
  293. ];
  294. $this->assertEquals(['id'], $result->primaryKey());
  295. foreach ($expected as $field => $definition) {
  296. $this->assertEquals($definition, $result->column($field));
  297. }
  298. }
  299. /**
  300. * Test describing a table with indexes
  301. *
  302. * @return void
  303. */
  304. public function testDescribeTableIndexes() {
  305. $connection = ConnectionManager::get('test');
  306. $this->_createTables($connection);
  307. $schema = new SchemaCollection($connection);
  308. $result = $schema->describe('schema_articles');
  309. $this->assertInstanceOf('Cake\Database\Schema\Table', $result);
  310. $expected = [
  311. 'primary' => [
  312. 'type' => 'primary',
  313. 'columns' => ['id'],
  314. 'length' => []
  315. ],
  316. 'content_idx' => [
  317. 'type' => 'unique',
  318. 'columns' => ['title', 'body'],
  319. 'length' => []
  320. ]
  321. ];
  322. $this->assertCount(3, $result->constraints());
  323. $expected = [
  324. 'primary' => [
  325. 'type' => 'primary',
  326. 'columns' => ['id'],
  327. 'length' => []
  328. ],
  329. 'content_idx' => [
  330. 'type' => 'unique',
  331. 'columns' => ['title', 'body'],
  332. 'length' => []
  333. ],
  334. 'author_idx' => [
  335. 'type' => 'foreign',
  336. 'columns' => ['author_id'],
  337. 'references' => ['schema_authors', 'id'],
  338. 'length' => [],
  339. 'update' => 'cascade',
  340. 'delete' => 'restrict',
  341. ]
  342. ];
  343. $this->assertEquals($expected['primary'], $result->constraint('primary'));
  344. $this->assertEquals($expected['content_idx'], $result->constraint('content_idx'));
  345. $this->assertEquals($expected['author_idx'], $result->constraint('author_idx'));
  346. $this->assertCount(1, $result->indexes());
  347. $expected = [
  348. 'type' => 'index',
  349. 'columns' => ['author_id'],
  350. 'length' => []
  351. ];
  352. $this->assertEquals($expected, $result->index('author_idx'));
  353. }
  354. /**
  355. * Column provider for creating column sql
  356. *
  357. * @return array
  358. */
  359. public static function columnSqlProvider() {
  360. return [
  361. // strings
  362. [
  363. 'title',
  364. ['type' => 'string', 'length' => 25, 'null' => false],
  365. '"title" VARCHAR(25) NOT NULL'
  366. ],
  367. [
  368. 'title',
  369. ['type' => 'string', 'length' => 25, 'null' => true, 'default' => 'ignored'],
  370. '"title" VARCHAR(25) DEFAULT NULL'
  371. ],
  372. [
  373. 'id',
  374. ['type' => 'string', 'length' => 32, 'fixed' => true, 'null' => false],
  375. '"id" CHAR(32) NOT NULL'
  376. ],
  377. [
  378. 'id',
  379. ['type' => 'string', 'length' => 36, 'fixed' => true, 'null' => false],
  380. '"id" UUID NOT NULL'
  381. ],
  382. [
  383. 'role',
  384. ['type' => 'string', 'length' => 10, 'null' => false, 'default' => 'admin'],
  385. '"role" VARCHAR(10) NOT NULL DEFAULT "admin"'
  386. ],
  387. [
  388. 'title',
  389. ['type' => 'string'],
  390. '"title" VARCHAR'
  391. ],
  392. // Text
  393. [
  394. 'body',
  395. ['type' => 'text', 'null' => false],
  396. '"body" TEXT NOT NULL'
  397. ],
  398. // Integers
  399. [
  400. 'post_id',
  401. ['type' => 'integer', 'length' => 11],
  402. '"post_id" INTEGER'
  403. ],
  404. [
  405. 'post_id',
  406. ['type' => 'biginteger', 'length' => 20],
  407. '"post_id" BIGINT'
  408. ],
  409. // Decimal
  410. [
  411. 'value',
  412. ['type' => 'decimal'],
  413. '"value" DECIMAL'
  414. ],
  415. [
  416. 'value',
  417. ['type' => 'decimal', 'length' => 11],
  418. '"value" DECIMAL(11,0)'
  419. ],
  420. [
  421. 'value',
  422. ['type' => 'decimal', 'length' => 12, 'precision' => 5],
  423. '"value" DECIMAL(12,5)'
  424. ],
  425. // Float
  426. [
  427. 'value',
  428. ['type' => 'float'],
  429. '"value" FLOAT'
  430. ],
  431. [
  432. 'value',
  433. ['type' => 'float', 'length' => 11, 'precision' => 3],
  434. '"value" FLOAT(3)'
  435. ],
  436. // Binary
  437. [
  438. 'img',
  439. ['type' => 'binary'],
  440. '"img" BYTEA'
  441. ],
  442. // Boolean
  443. [
  444. 'checked',
  445. ['type' => 'boolean', 'default' => false],
  446. '"checked" BOOLEAN DEFAULT FALSE'
  447. ],
  448. [
  449. 'checked',
  450. ['type' => 'boolean', 'default' => true, 'null' => false],
  451. '"checked" BOOLEAN NOT NULL DEFAULT TRUE'
  452. ],
  453. // datetimes
  454. [
  455. 'created',
  456. ['type' => 'datetime'],
  457. '"created" TIMESTAMP'
  458. ],
  459. // Date & Time
  460. [
  461. 'start_date',
  462. ['type' => 'date'],
  463. '"start_date" DATE'
  464. ],
  465. [
  466. 'start_time',
  467. ['type' => 'time'],
  468. '"start_time" TIME'
  469. ],
  470. // timestamps
  471. [
  472. 'created',
  473. ['type' => 'timestamp', 'null' => true],
  474. '"created" TIMESTAMP DEFAULT NULL'
  475. ],
  476. ];
  477. }
  478. /**
  479. * Test generating column definitions
  480. *
  481. * @dataProvider columnSqlProvider
  482. * @return void
  483. */
  484. public function testColumnSql($name, $data, $expected) {
  485. $driver = $this->_getMockedDriver();
  486. $schema = new PostgresSchema($driver);
  487. $table = (new Table('schema_articles'))->addColumn($name, $data);
  488. $this->assertEquals($expected, $schema->columnSql($table, $name));
  489. }
  490. /**
  491. * Test generating a column that is a primary key.
  492. *
  493. * @return void
  494. */
  495. public function testColumnSqlPrimaryKey() {
  496. $driver = $this->_getMockedDriver();
  497. $schema = new PostgresSchema($driver);
  498. $table = new Table('schema_articles');
  499. $table->addColumn('id', [
  500. 'type' => 'integer',
  501. 'null' => false
  502. ])
  503. ->addConstraint('primary', [
  504. 'type' => 'primary',
  505. 'columns' => ['id']
  506. ]);
  507. $result = $schema->columnSql($table, 'id');
  508. $this->assertEquals($result, '"id" SERIAL');
  509. }
  510. /**
  511. * Provide data for testing constraintSql
  512. *
  513. * @return array
  514. */
  515. public static function constraintSqlProvider() {
  516. return [
  517. [
  518. 'primary',
  519. ['type' => 'primary', 'columns' => ['title']],
  520. 'PRIMARY KEY ("title")'
  521. ],
  522. [
  523. 'unique_idx',
  524. ['type' => 'unique', 'columns' => ['title', 'author_id']],
  525. 'CONSTRAINT "unique_idx" UNIQUE ("title", "author_id")'
  526. ],
  527. [
  528. 'author_id_idx',
  529. ['type' => 'foreign', 'columns' => ['author_id'], 'references' => ['authors', 'id']],
  530. 'CONSTRAINT "author_id_idx" FOREIGN KEY ("author_id") ' .
  531. 'REFERENCES "authors" ("id") ON UPDATE RESTRICT ON DELETE RESTRICT'
  532. ],
  533. [
  534. 'author_id_idx',
  535. ['type' => 'foreign', 'columns' => ['author_id'], 'references' => ['authors', 'id'], 'update' => 'cascade'],
  536. 'CONSTRAINT "author_id_idx" FOREIGN KEY ("author_id") ' .
  537. 'REFERENCES "authors" ("id") ON UPDATE CASCADE ON DELETE RESTRICT'
  538. ],
  539. [
  540. 'author_id_idx',
  541. ['type' => 'foreign', 'columns' => ['author_id'], 'references' => ['authors', 'id'], 'update' => 'restrict'],
  542. 'CONSTRAINT "author_id_idx" FOREIGN KEY ("author_id") ' .
  543. 'REFERENCES "authors" ("id") ON UPDATE RESTRICT ON DELETE RESTRICT'
  544. ],
  545. [
  546. 'author_id_idx',
  547. ['type' => 'foreign', 'columns' => ['author_id'], 'references' => ['authors', 'id'], 'update' => 'setNull'],
  548. 'CONSTRAINT "author_id_idx" FOREIGN KEY ("author_id") ' .
  549. 'REFERENCES "authors" ("id") ON UPDATE SET NULL ON DELETE RESTRICT'
  550. ],
  551. [
  552. 'author_id_idx',
  553. ['type' => 'foreign', 'columns' => ['author_id'], 'references' => ['authors', 'id'], 'update' => 'noAction'],
  554. 'CONSTRAINT "author_id_idx" FOREIGN KEY ("author_id") ' .
  555. 'REFERENCES "authors" ("id") ON UPDATE NO ACTION ON DELETE RESTRICT'
  556. ],
  557. ];
  558. }
  559. /**
  560. * Test the constraintSql method.
  561. *
  562. * @dataProvider constraintSqlProvider
  563. */
  564. public function testConstraintSql($name, $data, $expected) {
  565. $driver = $this->_getMockedDriver();
  566. $schema = new PostgresSchema($driver);
  567. $table = (new Table('schema_articles'))->addColumn('title', [
  568. 'type' => 'string',
  569. 'length' => 255
  570. ])->addColumn('author_id', [
  571. 'type' => 'integer',
  572. ])->addConstraint($name, $data);
  573. $this->assertEquals($expected, $schema->constraintSql($table, $name));
  574. }
  575. /**
  576. * Integration test for converting a Schema\Table into MySQL table creates.
  577. *
  578. * @return void
  579. */
  580. public function testCreateSql() {
  581. $driver = $this->_getMockedDriver();
  582. $connection = $this->getMock('Cake\Database\Connection', [], [], '', false);
  583. $connection->expects($this->any())->method('driver')
  584. ->will($this->returnValue($driver));
  585. $table = (new Table('schema_articles'))->addColumn('id', [
  586. 'type' => 'integer',
  587. 'null' => false
  588. ])
  589. ->addColumn('title', [
  590. 'type' => 'string',
  591. 'null' => false,
  592. 'comment' => 'This is the title',
  593. ])
  594. ->addColumn('body', ['type' => 'text'])
  595. ->addColumn('created', 'datetime')
  596. ->addConstraint('primary', [
  597. 'type' => 'primary',
  598. 'columns' => ['id'],
  599. ])
  600. ->addIndex('title_idx', [
  601. 'type' => 'index',
  602. 'columns' => ['title'],
  603. ]);
  604. $expected = <<<SQL
  605. CREATE TABLE "schema_articles" (
  606. "id" SERIAL,
  607. "title" VARCHAR NOT NULL,
  608. "body" TEXT,
  609. "created" TIMESTAMP,
  610. PRIMARY KEY ("id")
  611. )
  612. SQL;
  613. $result = $table->createSql($connection);
  614. $this->assertCount(3, $result);
  615. $this->assertEquals($expected, $result[0]);
  616. $this->assertEquals(
  617. 'CREATE INDEX "title_idx" ON "schema_articles" ("title")',
  618. $result[1]
  619. );
  620. $this->assertEquals(
  621. 'COMMENT ON COLUMN "schema_articles"."title" IS "This is the title"',
  622. $result[2]
  623. );
  624. }
  625. /**
  626. * test dropSql
  627. *
  628. * @return void
  629. */
  630. public function testDropSql() {
  631. $driver = $this->_getMockedDriver();
  632. $connection = $this->getMock('Cake\Database\Connection', [], [], '', false);
  633. $connection->expects($this->any())->method('driver')
  634. ->will($this->returnValue($driver));
  635. $table = new Table('schema_articles');
  636. $result = $table->dropSql($connection);
  637. $this->assertCount(1, $result);
  638. $this->assertEquals('DROP TABLE "schema_articles"', $result[0]);
  639. }
  640. /**
  641. * Test truncateSql()
  642. *
  643. * @return void
  644. */
  645. public function testTruncateSql() {
  646. $driver = $this->_getMockedDriver();
  647. $connection = $this->getMock('Cake\Database\Connection', [], [], '', false);
  648. $connection->expects($this->any())->method('driver')
  649. ->will($this->returnValue($driver));
  650. $table = new Table('schema_articles');
  651. $table->addColumn('id', 'integer')
  652. ->addConstraint('primary', [
  653. 'type' => 'primary',
  654. 'columns' => ['id']
  655. ]);
  656. $result = $table->truncateSql($connection);
  657. $this->assertCount(1, $result);
  658. $this->assertEquals('TRUNCATE "schema_articles" RESTART IDENTITY', $result[0]);
  659. }
  660. /**
  661. * Get a schema instance with a mocked driver/pdo instances
  662. *
  663. * @return Driver
  664. */
  665. protected function _getMockedDriver() {
  666. $driver = new \Cake\Database\Driver\Postgres();
  667. $mock = $this->getMock('FakePdo', ['quote', 'quoteIdentifier']);
  668. $mock->expects($this->any())
  669. ->method('quote')
  670. ->will($this->returnCallback(function ($value) {
  671. return '"' . $value . '"';
  672. }));
  673. $mock->expects($this->any())
  674. ->method('quoteIdentifier')
  675. ->will($this->returnCallback(function ($value) {
  676. return '"' . $value . '"';
  677. }));
  678. $driver->connection($mock);
  679. return $driver;
  680. }
  681. }