DataAccessTest.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  1. <?php
  2. namespace Doctrine\Tests\DBAL\Functional;
  3. use Doctrine\DBAL\Types\Type;
  4. use Doctrine\DBAL\Connection;
  5. use PDO;
  6. require_once __DIR__ . '/../../TestInit.php';
  7. class DataAccessTest extends \Doctrine\Tests\DbalFunctionalTestCase
  8. {
  9. static private $generated = false;
  10. public function setUp()
  11. {
  12. parent::setUp();
  13. if (self::$generated === false) {
  14. /* @var $sm \Doctrine\DBAL\Schema\AbstractSchemaManager */
  15. $table = new \Doctrine\DBAL\Schema\Table("fetch_table");
  16. $table->addColumn('test_int', 'integer');
  17. $table->addColumn('test_string', 'string');
  18. $table->addColumn('test_datetime', 'datetime', array('notnull' => false));
  19. $table->setPrimaryKey(array('test_int'));
  20. $sm = $this->_conn->getSchemaManager();
  21. $sm->createTable($table);
  22. $this->_conn->insert('fetch_table', array('test_int' => 1, 'test_string' => 'foo', 'test_datetime' => '2010-01-01 10:10:10'));
  23. self::$generated = true;
  24. }
  25. }
  26. public function testPrepareWithBindValue()
  27. {
  28. $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?";
  29. $stmt = $this->_conn->prepare($sql);
  30. $this->assertInstanceOf('Doctrine\DBAL\Statement', $stmt);
  31. $stmt->bindValue(1, 1);
  32. $stmt->bindValue(2, 'foo');
  33. $stmt->execute();
  34. $row = $stmt->fetch(\PDO::FETCH_ASSOC);
  35. $row = array_change_key_case($row, \CASE_LOWER);
  36. $this->assertEquals(array('test_int' => 1, 'test_string' => 'foo'), $row);
  37. }
  38. public function testPrepareWithBindParam()
  39. {
  40. $paramInt = 1;
  41. $paramStr = 'foo';
  42. $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?";
  43. $stmt = $this->_conn->prepare($sql);
  44. $this->assertInstanceOf('Doctrine\DBAL\Statement', $stmt);
  45. $stmt->bindParam(1, $paramInt);
  46. $stmt->bindParam(2, $paramStr);
  47. $stmt->execute();
  48. $row = $stmt->fetch(\PDO::FETCH_ASSOC);
  49. $row = array_change_key_case($row, \CASE_LOWER);
  50. $this->assertEquals(array('test_int' => 1, 'test_string' => 'foo'), $row);
  51. }
  52. public function testPrepareWithFetchAll()
  53. {
  54. $paramInt = 1;
  55. $paramStr = 'foo';
  56. $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?";
  57. $stmt = $this->_conn->prepare($sql);
  58. $this->assertInstanceOf('Doctrine\DBAL\Statement', $stmt);
  59. $stmt->bindParam(1, $paramInt);
  60. $stmt->bindParam(2, $paramStr);
  61. $stmt->execute();
  62. $rows = $stmt->fetchAll(\PDO::FETCH_ASSOC);
  63. $rows[0] = array_change_key_case($rows[0], \CASE_LOWER);
  64. $this->assertEquals(array('test_int' => 1, 'test_string' => 'foo'), $rows[0]);
  65. }
  66. /**
  67. * @group DBAL-228
  68. */
  69. public function testPrepareWithFetchAllBoth()
  70. {
  71. $paramInt = 1;
  72. $paramStr = 'foo';
  73. $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?";
  74. $stmt = $this->_conn->prepare($sql);
  75. $this->assertInstanceOf('Doctrine\DBAL\Statement', $stmt);
  76. $stmt->bindParam(1, $paramInt);
  77. $stmt->bindParam(2, $paramStr);
  78. $stmt->execute();
  79. $rows = $stmt->fetchAll(\PDO::FETCH_BOTH);
  80. $rows[0] = array_change_key_case($rows[0], \CASE_LOWER);
  81. $this->assertEquals(array('test_int' => 1, 'test_string' => 'foo', 0 => 1, 1 => 'foo'), $rows[0]);
  82. }
  83. public function testPrepareWithFetchColumn()
  84. {
  85. $paramInt = 1;
  86. $paramStr = 'foo';
  87. $sql = "SELECT test_int FROM fetch_table WHERE test_int = ? AND test_string = ?";
  88. $stmt = $this->_conn->prepare($sql);
  89. $this->assertInstanceOf('Doctrine\DBAL\Statement', $stmt);
  90. $stmt->bindParam(1, $paramInt);
  91. $stmt->bindParam(2, $paramStr);
  92. $stmt->execute();
  93. $column = $stmt->fetchColumn();
  94. $this->assertEquals(1, $column);
  95. }
  96. public function testPrepareWithIterator()
  97. {
  98. $paramInt = 1;
  99. $paramStr = 'foo';
  100. $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?";
  101. $stmt = $this->_conn->prepare($sql);
  102. $this->assertInstanceOf('Doctrine\DBAL\Statement', $stmt);
  103. $stmt->bindParam(1, $paramInt);
  104. $stmt->bindParam(2, $paramStr);
  105. $stmt->execute();
  106. $rows = array();
  107. $stmt->setFetchMode(\PDO::FETCH_ASSOC);
  108. foreach ($stmt as $row) {
  109. $rows[] = array_change_key_case($row, \CASE_LOWER);
  110. }
  111. $this->assertEquals(array('test_int' => 1, 'test_string' => 'foo'), $rows[0]);
  112. }
  113. public function testPrepareWithQuoted()
  114. {
  115. $table = 'fetch_table';
  116. $paramInt = 1;
  117. $paramStr = 'foo';
  118. $sql = "SELECT test_int, test_string FROM " . $this->_conn->quoteIdentifier($table) . " ".
  119. "WHERE test_int = " . $this->_conn->quote($paramInt) . " AND test_string = " . $this->_conn->quote($paramStr);
  120. $stmt = $this->_conn->prepare($sql);
  121. $this->assertInstanceOf('Doctrine\DBAL\Statement', $stmt);
  122. }
  123. public function testPrepareWithExecuteParams()
  124. {
  125. $paramInt = 1;
  126. $paramStr = 'foo';
  127. $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?";
  128. $stmt = $this->_conn->prepare($sql);
  129. $this->assertInstanceOf('Doctrine\DBAL\Statement', $stmt);
  130. $stmt->execute(array($paramInt, $paramStr));
  131. $row = $stmt->fetch(\PDO::FETCH_ASSOC);
  132. $this->assertTrue($row !== false);
  133. $row = array_change_key_case($row, \CASE_LOWER);
  134. $this->assertEquals(array('test_int' => 1, 'test_string' => 'foo'), $row);
  135. }
  136. public function testFetchAll()
  137. {
  138. $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?";
  139. $data = $this->_conn->fetchAll($sql, array(1, 'foo'));
  140. $this->assertEquals(1, count($data));
  141. $row = $data[0];
  142. $this->assertEquals(2, count($row));
  143. $row = array_change_key_case($row, \CASE_LOWER);
  144. $this->assertEquals(1, $row['test_int']);
  145. $this->assertEquals('foo', $row['test_string']);
  146. }
  147. public function testFetchBoth()
  148. {
  149. $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?";
  150. $row = $this->_conn->executeQuery($sql, array(1, 'foo'))->fetch(\PDO::FETCH_BOTH);
  151. $this->assertTrue($row !== false);
  152. $row = array_change_key_case($row, \CASE_LOWER);
  153. $this->assertEquals(1, $row['test_int']);
  154. $this->assertEquals('foo', $row['test_string']);
  155. $this->assertEquals(1, $row[0]);
  156. $this->assertEquals('foo', $row[1]);
  157. }
  158. public function testFetchRow()
  159. {
  160. $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?";
  161. $row = $this->_conn->fetchAssoc($sql, array(1, 'foo'));
  162. $this->assertTrue($row !== false);
  163. $row = array_change_key_case($row, \CASE_LOWER);
  164. $this->assertEquals(1, $row['test_int']);
  165. $this->assertEquals('foo', $row['test_string']);
  166. }
  167. public function testFetchArray()
  168. {
  169. $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?";
  170. $row = $this->_conn->fetchArray($sql, array(1, 'foo'));
  171. $this->assertEquals(1, $row[0]);
  172. $this->assertEquals('foo', $row[1]);
  173. }
  174. public function testFetchColumn()
  175. {
  176. $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?";
  177. $testInt = $this->_conn->fetchColumn($sql, array(1, 'foo'), 0);
  178. $this->assertEquals(1, $testInt);
  179. $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?";
  180. $testString = $this->_conn->fetchColumn($sql, array(1, 'foo'), 1);
  181. $this->assertEquals('foo', $testString);
  182. }
  183. /**
  184. * @group DDC-697
  185. */
  186. public function testExecuteQueryBindDateTimeType()
  187. {
  188. $sql = 'SELECT count(*) AS c FROM fetch_table WHERE test_datetime = ?';
  189. $stmt = $this->_conn->executeQuery($sql,
  190. array(1 => new \DateTime('2010-01-01 10:10:10')),
  191. array(1 => Type::DATETIME)
  192. );
  193. $this->assertEquals(1, $stmt->fetchColumn());
  194. }
  195. /**
  196. * @group DDC-697
  197. */
  198. public function testExecuteUpdateBindDateTimeType()
  199. {
  200. $datetime = new \DateTime('2010-02-02 20:20:20');
  201. $sql = 'INSERT INTO fetch_table (test_int, test_string, test_datetime) VALUES (?, ?, ?)';
  202. $affectedRows = $this->_conn->executeUpdate($sql,
  203. array(1 => 50, 2 => 'foo', 3 => $datetime),
  204. array(1 => PDO::PARAM_INT, 2 => PDO::PARAM_STR, 3 => Type::DATETIME)
  205. );
  206. $this->assertEquals(1, $affectedRows);
  207. $this->assertEquals(1, $this->_conn->executeQuery(
  208. 'SELECT count(*) AS c FROM fetch_table WHERE test_datetime = ?',
  209. array(1 => $datetime),
  210. array(1 => Type::DATETIME)
  211. )->fetchColumn());
  212. }
  213. /**
  214. * @group DDC-697
  215. */
  216. public function testPrepareQueryBindValueDateTimeType()
  217. {
  218. $sql = 'SELECT count(*) AS c FROM fetch_table WHERE test_datetime = ?';
  219. $stmt = $this->_conn->prepare($sql);
  220. $stmt->bindValue(1, new \DateTime('2010-01-01 10:10:10'), Type::DATETIME);
  221. $stmt->execute();
  222. $this->assertEquals(1, $stmt->fetchColumn());
  223. }
  224. /**
  225. * @group DBAL-78
  226. */
  227. public function testNativeArrayListSupport()
  228. {
  229. for ($i = 100; $i < 110; $i++) {
  230. $this->_conn->insert('fetch_table', array('test_int' => $i, 'test_string' => 'foo' . $i, 'test_datetime' => '2010-01-01 10:10:10'));
  231. }
  232. $stmt = $this->_conn->executeQuery('SELECT test_int FROM fetch_table WHERE test_int IN (?)',
  233. array(array(100, 101, 102, 103, 104)), array(Connection::PARAM_INT_ARRAY));
  234. $data = $stmt->fetchAll(PDO::FETCH_NUM);
  235. $this->assertEquals(5, count($data));
  236. $this->assertEquals(array(array(100), array(101), array(102), array(103), array(104)), $data);
  237. $stmt = $this->_conn->executeQuery('SELECT test_int FROM fetch_table WHERE test_string IN (?)',
  238. array(array('foo100', 'foo101', 'foo102', 'foo103', 'foo104')), array(Connection::PARAM_STR_ARRAY));
  239. $data = $stmt->fetchAll(PDO::FETCH_NUM);
  240. $this->assertEquals(5, count($data));
  241. $this->assertEquals(array(array(100), array(101), array(102), array(103), array(104)), $data);
  242. }
  243. /**
  244. * @group DDC-1014
  245. */
  246. public function testDateArithmetics()
  247. {
  248. $p = $this->_conn->getDatabasePlatform();
  249. $sql = 'SELECT ';
  250. $sql .= $p->getDateDiffExpression('test_datetime', $p->getCurrentTimestampSQL()) .' AS diff, ';
  251. $sql .= $p->getDateAddDaysExpression('test_datetime', 10) .' AS add_days, ';
  252. $sql .= $p->getDateSubDaysExpression('test_datetime', 10) .' AS sub_days, ';
  253. $sql .= $p->getDateAddMonthExpression('test_datetime', 2) .' AS add_month, ';
  254. $sql .= $p->getDateSubMonthExpression('test_datetime', 2) .' AS sub_month ';
  255. $sql .= 'FROM fetch_table';
  256. $row = $this->_conn->fetchAssoc($sql);
  257. $row = array_change_key_case($row, CASE_LOWER);
  258. $diff = floor( (strtotime('2010-01-01')-time()) / 3600 / 24);
  259. $this->assertEquals($diff, (int)$row['diff'], "Date difference should be approx. ".$diff." days.", 1);
  260. $this->assertEquals('2010-01-11', date('Y-m-d', strtotime($row['add_days'])), "Adding date should end up on 2010-01-11");
  261. $this->assertEquals('2009-12-22', date('Y-m-d', strtotime($row['sub_days'])), "Subtracting date should end up on 2009-12-22");
  262. $this->assertEquals('2010-03-01', date('Y-m-d', strtotime($row['add_month'])), "Adding month should end up on 2010-03-01");
  263. $this->assertEquals('2009-11-01', date('Y-m-d', strtotime($row['sub_month'])), "Adding month should end up on 2009-11-01");
  264. }
  265. public function testQuoteSQLInjection()
  266. {
  267. $sql = "SELECT * FROM fetch_table WHERE test_string = " . $this->_conn->quote("bar' OR '1'='1");
  268. $rows = $this->_conn->fetchAll($sql);
  269. $this->assertEquals(0, count($rows), "no result should be returned, otherwise SQL injection is possible");
  270. }
  271. /**
  272. * @group DDC-1213
  273. */
  274. public function testBitComparisonExpressionSupport()
  275. {
  276. $this->_conn->executeQuery('DELETE FROM fetch_table')->execute();
  277. $platform = $this->_conn->getDatabasePlatform();
  278. $bitmap = array();
  279. for ($i = 2; $i < 9; $i = $i + 2) {
  280. $bitmap[$i] = array(
  281. 'bit_or' => ($i | 2),
  282. 'bit_and' => ($i & 2)
  283. );
  284. $this->_conn->insert('fetch_table', array(
  285. 'test_int' => $i,
  286. 'test_string' => json_encode($bitmap[$i]),
  287. 'test_datetime' => '2010-01-01 10:10:10'
  288. ));
  289. }
  290. $sql[] = 'SELECT ';
  291. $sql[] = 'test_int, ';
  292. $sql[] = 'test_string, ';
  293. $sql[] = $platform->getBitOrComparisonExpression('test_int', 2) . ' AS bit_or, ';
  294. $sql[] = $platform->getBitAndComparisonExpression('test_int', 2) . ' AS bit_and ';
  295. $sql[] = 'FROM fetch_table';
  296. $stmt = $this->_conn->executeQuery(implode(PHP_EOL, $sql));
  297. $data = $stmt->fetchAll(PDO::FETCH_ASSOC);
  298. $this->assertEquals(4, count($data));
  299. $this->assertEquals(count($bitmap), count($data));
  300. foreach ($data as $row) {
  301. $row = array_change_key_case($row, CASE_LOWER);
  302. $this->assertArrayHasKey('test_int', $row);
  303. $id = $row['test_int'];
  304. $this->assertArrayHasKey($id, $bitmap);
  305. $this->assertArrayHasKey($id, $bitmap);
  306. $this->assertArrayHasKey('bit_or', $row);
  307. $this->assertArrayHasKey('bit_and', $row);
  308. $this->assertEquals($row['bit_or'], $bitmap[$id]['bit_or']);
  309. $this->assertEquals($row['bit_and'], $bitmap[$id]['bit_and']);
  310. }
  311. }
  312. public function testSetDefaultFetchMode()
  313. {
  314. $stmt = $this->_conn->query("SELECT * FROM fetch_table");
  315. $stmt->setFetchMode(\PDO::FETCH_NUM);
  316. $row = array_keys($stmt->fetch());
  317. $this->assertEquals(0, count( array_filter($row, function($v) { return ! is_numeric($v); })), "should be no non-numerical elements in the result.");
  318. }
  319. /**
  320. * @group DBAL-196
  321. */
  322. public function testFetchAllSupportFetchClass()
  323. {
  324. $this->skipOci8AndMysqli();
  325. $this->setupFixture();
  326. $sql = "SELECT test_int, test_string, test_datetime FROM fetch_table";
  327. $stmt = $this->_conn->prepare($sql);
  328. $stmt->execute();
  329. $results = $stmt->fetchAll(
  330. \PDO::FETCH_CLASS,
  331. __NAMESPACE__.'\\MyFetchClass'
  332. );
  333. $this->assertEquals(1, count($results));
  334. $this->assertInstanceOf(__NAMESPACE__.'\\MyFetchClass', $results[0]);
  335. $this->assertEquals(1, $results[0]->test_int);
  336. $this->assertEquals('foo', $results[0]->test_string);
  337. $this->assertStringStartsWith('2010-01-01 10:10:10', $results[0]->test_datetime);
  338. }
  339. /**
  340. * @group DBAL-241
  341. */
  342. public function testFetchAllStyleColumn()
  343. {
  344. $sql = "DELETE FROM fetch_table";
  345. $this->_conn->executeUpdate($sql);
  346. $this->_conn->insert('fetch_table', array('test_int' => 1, 'test_string' => 'foo'));
  347. $this->_conn->insert('fetch_table', array('test_int' => 10, 'test_string' => 'foo'));
  348. $sql = "SELECT test_int FROM fetch_table";
  349. $rows = $this->_conn->query($sql)->fetchAll(\PDO::FETCH_COLUMN);
  350. $this->assertEquals(array(1, 10), $rows);
  351. }
  352. /**
  353. * @group DBAL-214
  354. */
  355. public function testSetFetchModeClassFetchAll()
  356. {
  357. $this->skipOci8AndMysqli();
  358. $this->setupFixture();
  359. $sql = "SELECT * FROM fetch_table";
  360. $stmt = $this->_conn->query($sql);
  361. $stmt->setFetchMode(\PDO::FETCH_CLASS, __NAMESPACE__ . '\\MyFetchClass', array());
  362. $results = $stmt->fetchAll();
  363. $this->assertEquals(1, count($results));
  364. $this->assertInstanceOf(__NAMESPACE__.'\\MyFetchClass', $results[0]);
  365. $this->assertEquals(1, $results[0]->test_int);
  366. $this->assertEquals('foo', $results[0]->test_string);
  367. $this->assertStringStartsWith('2010-01-01 10:10:10', $results[0]->test_datetime);
  368. }
  369. /**
  370. * @group DBAL-214
  371. */
  372. public function testSetFetchModeClassFetch()
  373. {
  374. $this->skipOci8AndMysqli();
  375. $this->setupFixture();
  376. $sql = "SELECT * FROM fetch_table";
  377. $stmt = $this->_conn->query($sql);
  378. $stmt->setFetchMode(\PDO::FETCH_CLASS, __NAMESPACE__ . '\\MyFetchClass', array());
  379. $results = array();
  380. while ($row = $stmt->fetch()) {
  381. $results[] = $row;
  382. }
  383. $this->assertEquals(1, count($results));
  384. $this->assertInstanceOf(__NAMESPACE__.'\\MyFetchClass', $results[0]);
  385. $this->assertEquals(1, $results[0]->test_int);
  386. $this->assertEquals('foo', $results[0]->test_string);
  387. $this->assertStringStartsWith('2010-01-01 10:10:10', $results[0]->test_datetime);
  388. }
  389. /**
  390. * @group DBAL-257
  391. */
  392. public function testEmptyFetchColumnReturnsFalse()
  393. {
  394. $this->_conn->executeQuery('DELETE FROM fetch_table')->execute();
  395. $this->assertFalse($this->_conn->fetchColumn('SELECT test_int FROM fetch_table'));
  396. $this->assertFalse($this->_conn->query('SELECT test_int FROM fetch_table')->fetchColumn());
  397. }
  398. /**
  399. * @group DBAL-339
  400. */
  401. public function testSetFetchModeOnDbalStatement()
  402. {
  403. $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?";
  404. $stmt = $this->_conn->executeQuery($sql, array(1, "foo"));
  405. $stmt->setFetchMode(\PDO::FETCH_NUM);
  406. while ($row = $stmt->fetch()) {
  407. $this->assertTrue(isset($row[0]));
  408. $this->assertTrue(isset($row[1]));
  409. }
  410. }
  411. private function setupFixture()
  412. {
  413. $this->_conn->executeQuery('DELETE FROM fetch_table')->execute();
  414. $this->_conn->insert('fetch_table', array(
  415. 'test_int' => 1,
  416. 'test_string' => 'foo',
  417. 'test_datetime' => '2010-01-01 10:10:10'
  418. ));
  419. }
  420. private function skipOci8AndMysqli()
  421. {
  422. if (isset($GLOBALS['db_type']) && $GLOBALS['db_type'] == "oci8") {
  423. $this->markTestSkipped("Not supported by OCI8");
  424. }
  425. if ('mysqli' == $this->_conn->getDriver()->getName()) {
  426. $this->markTestSkipped('Mysqli driver dont support this feature.');
  427. }
  428. }
  429. }
  430. class MyFetchClass
  431. {
  432. public $test_int, $test_string, $test_datetime;
  433. }