PageRenderTime 61ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/tests/dbal/db_tools_test.php

http://github.com/phpbb/phpbb
PHP | 477 lines | 365 code | 73 blank | 39 comment | 6 complexity | 6d79de2284d9aab00592900500ea8cc6 MD5 | raw file
Possible License(s): GPL-3.0, AGPL-1.0
  1. <?php
  2. /**
  3. *
  4. * This file is part of the phpBB Forum Software package.
  5. *
  6. * @copyright (c) phpBB Limited <https://www.phpbb.com>
  7. * @license GNU General Public License, version 2 (GPL-2.0)
  8. *
  9. * For full copyright and license information, please see
  10. * the docs/CREDITS.txt file.
  11. *
  12. */
  13. class phpbb_dbal_db_tools_test extends phpbb_database_test_case
  14. {
  15. /** @var \phpbb\db\driver\driver_interface */
  16. protected $db;
  17. /** @var \phpbb\db\tools\tools_interface */
  18. protected $tools;
  19. protected $table_exists;
  20. protected $table_data;
  21. public function getDataSet()
  22. {
  23. return $this->createXMLDataSet(dirname(__FILE__).'/fixtures/config.xml');
  24. }
  25. protected function setUp(): void
  26. {
  27. parent::setUp();
  28. $this->db = $this->new_dbal();
  29. $factory = new \phpbb\db\tools\factory();
  30. $this->tools = $factory->get($this->db);
  31. $this->table_data = array(
  32. 'COLUMNS' => array(
  33. 'c_id' => array('UINT', NULL, 'auto_increment'),
  34. 'c_int_size' => array('INT:4', 4),
  35. 'c_bint' => array('BINT', 4),
  36. 'c_uint' => array('UINT', 4),
  37. 'c_uint_size' => array('UINT:4', 4),
  38. 'c_tint_size' => array('TINT:2', 4),
  39. 'c_usint' => array('USINT', 4),
  40. 'c_bool' => array('BOOL', 1),
  41. 'c_vchar' => array('VCHAR', 'foo'),
  42. 'c_vchar_size' => array('VCHAR:4', 'foo'),
  43. 'c_vchar_null' => array('VCHAR', null),
  44. 'c_char_size' => array('CHAR:4', 'foo'),
  45. 'c_xstext' => array('XSTEXT', 'foo'),
  46. 'c_stext' => array('STEXT', 'foo'),
  47. 'c_text' => array('TEXT', 'foo'),
  48. 'c_mtext' => array('MTEXT', 'foo'),
  49. 'c_xstext_uni' => array('XSTEXT_UNI', 'foo'),
  50. 'c_stext_uni' => array('STEXT_UNI', 'foo'),
  51. 'c_text_uni' => array('TEXT_UNI', 'foo'),
  52. 'c_mtext_uni' => array('MTEXT_UNI', 'foo'),
  53. 'c_timestamp' => array('TIMESTAMP', 4),
  54. 'c_decimal' => array('DECIMAL', 4.2),
  55. 'c_decimal_size' => array('DECIMAL:6', 4.2),
  56. 'c_pdecimal' => array('PDECIMAL', 4.2),
  57. 'c_pdecimal_size' => array('PDECIMAL:7', 4.2),
  58. 'c_vchar_uni' => array('VCHAR_UNI', 'foo'),
  59. 'c_vchar_uni_size' => array('VCHAR_UNI:4', 'foo'),
  60. 'c_vchar_ci' => array('VCHAR_CI', 'foo'),
  61. 'c_varbinary' => array('VARBINARY', 'foo'),
  62. ),
  63. 'PRIMARY_KEY' => 'c_id',
  64. 'KEYS' => array(
  65. 'i_simple' => array('INDEX', 'c_uint'),
  66. 'i_uniq' => array('UNIQUE', 'c_vchar'),
  67. 'i_comp' => array('INDEX', array('c_vchar_uni', 'c_bool')),
  68. 'i_comp_uniq' => array('UNIQUE', array('c_vchar_size', 'c_usint')),
  69. ),
  70. );
  71. $this->tools->sql_create_table('prefix_table_name', $this->table_data);
  72. $this->table_exists = true;
  73. }
  74. protected function tearDown(): void
  75. {
  76. if ($this->table_exists)
  77. {
  78. $this->tools->sql_table_drop('prefix_table_name');
  79. }
  80. parent::tearDown();
  81. }
  82. public function test_created_and_drop_table()
  83. {
  84. // table is empty after creation and queryable
  85. $sql = 'SELECT * FROM prefix_table_name';
  86. $result = $this->db->sql_query($sql);
  87. $this->assertTrue(! $this->db->sql_fetchrow($result));
  88. $this->db->sql_freeresult($result);
  89. $this->table_exists = false;
  90. $this->tools->sql_table_drop('prefix_table_name');
  91. }
  92. static protected function get_default_values()
  93. {
  94. return array(
  95. 'c_int_size' => 0,
  96. 'c_bint' => 0,
  97. 'c_uint' => 0,
  98. 'c_uint_size' => 0,
  99. 'c_tint_size' => 0,
  100. 'c_usint' => 0,
  101. 'c_bool' => 0,
  102. 'c_vchar' => '',
  103. 'c_vchar_size' => '',
  104. 'c_vchar_null' => null,
  105. 'c_char_size' => 'abcd',
  106. 'c_xstext' => '',
  107. 'c_stext' => '',
  108. 'c_text' => '',
  109. 'c_mtext' => '',
  110. 'c_xstext_uni' => '',
  111. 'c_stext_uni' => '',
  112. 'c_text_uni' => '',
  113. 'c_mtext_uni' => '',
  114. 'c_timestamp' => 0,
  115. 'c_decimal' => 0,
  116. 'c_decimal_size' => 0,
  117. 'c_pdecimal' => 0,
  118. 'c_pdecimal_size' => 0,
  119. 'c_vchar_uni' => '',
  120. 'c_vchar_uni_size' => '',
  121. 'c_vchar_ci' => '',
  122. 'c_varbinary' => '',
  123. );
  124. }
  125. static public function column_values()
  126. {
  127. return array(
  128. array('c_int_size', -9999),
  129. array('c_bint', '99999999999999999'),
  130. array('c_uint', 16777215),
  131. array('c_uint_size', 9999),
  132. array('c_tint_size', -99),
  133. array('c_usint', 99),
  134. array('c_bool', 0),
  135. array('c_vchar', str_repeat('a', 255)),
  136. array('c_vchar_size', str_repeat('a', 4)),
  137. array('c_vchar_null', str_repeat('a', 4)),
  138. array('c_char_size', str_repeat('a', 4)),
  139. array('c_xstext', str_repeat('a', 1000)),
  140. array('c_stext', str_repeat('a', 3000)),
  141. array('c_text', str_repeat('a', 8000)),
  142. array('c_mtext', str_repeat('a', 10000)),
  143. array('c_xstext_uni', str_repeat("\xC3\x84", 100)),
  144. array('c_stext_uni', str_repeat("\xC3\x84", 255)),
  145. array('c_text_uni', str_repeat("\xC3\x84", 4000)),
  146. array('c_mtext_uni', str_repeat("\xC3\x84", 10000)),
  147. array('c_timestamp', 2147483647),
  148. array('c_decimal', 999.99),
  149. array('c_decimal_size', 9999.99),
  150. array('c_pdecimal', 999.999),
  151. array('c_pdecimal_size', 9999.999),
  152. array('c_vchar_uni', str_repeat("\xC3\x84", 255)),
  153. array('c_vchar_uni_size', str_repeat("\xC3\x84", 4)),
  154. array('c_vchar_ci', str_repeat("\xC3\x84", 255)),
  155. array('c_varbinary', str_repeat("\x00\xFF", 127)),
  156. );
  157. }
  158. /**
  159. * @dataProvider column_values
  160. */
  161. public function test_created_column($column_name, $column_value)
  162. {
  163. if ($column_name === 'c_varbinary' && stripos(get_class($this->db), 'mysql') === false)
  164. {
  165. $this->markTestIncomplete('Binary handling is not implemented properly on non-MySQL DBMSes.');
  166. }
  167. $row_insert = self::get_default_values();
  168. $row_insert[$column_name] = $column_value;
  169. // empty table
  170. $sql = 'DELETE FROM prefix_table_name';
  171. $result = $this->db->sql_query($sql);
  172. $sql = 'INSERT INTO prefix_table_name ' . $this->db->sql_build_array('INSERT', $row_insert);
  173. $result = $this->db->sql_query($sql);
  174. $sql = "SELECT *
  175. FROM prefix_table_name";
  176. $result = $this->db->sql_query($sql);
  177. $row_actual = $this->db->sql_fetchrow($result);
  178. $this->db->sql_freeresult($result);
  179. $row_expect = $row_insert;
  180. unset($row_actual['id']); // auto increment id changes, so ignore
  181. $type = $this->table_data['COLUMNS'][$column_name][0];
  182. $this->assertEquals($row_expect[$column_name], $row_actual[$column_name], "Column $column_name of type $type should have equal return and input value.");
  183. }
  184. public function test_list_columns()
  185. {
  186. $config = $this->get_database_config();
  187. $table_columns = $this->table_data['COLUMNS'];
  188. if (strpos($config['dbms'], 'mssql') !== false)
  189. {
  190. ksort($table_columns);
  191. }
  192. $this->assertEquals(
  193. array_keys($table_columns),
  194. array_values($this->tools->sql_list_columns('prefix_table_name'))
  195. );
  196. }
  197. public function test_column_exists()
  198. {
  199. $this->assertTrue($this->tools->sql_column_exists('prefix_table_name', 'c_id'));
  200. $this->assertFalse($this->tools->sql_column_exists('prefix_table_name', 'column_does_not_exist'));
  201. }
  202. public function test_column_change_with_index()
  203. {
  204. // Create column
  205. $this->assertFalse($this->tools->sql_column_exists('prefix_table_name', 'c_bug_12012'));
  206. $this->assertTrue($this->tools->sql_column_add('prefix_table_name', 'c_bug_12012', array('DECIMAL', 0)));
  207. $this->assertTrue($this->tools->sql_column_exists('prefix_table_name', 'c_bug_12012'));
  208. // Create index over the column
  209. $this->assertFalse($this->tools->sql_index_exists('prefix_table_name', 'i_bug_12012'));
  210. $this->assertTrue($this->tools->sql_create_index('prefix_table_name', 'i_bug_12012', array('c_bug_12012', 'c_bool')));
  211. $this->assertTrue($this->tools->sql_index_exists('prefix_table_name', 'i_bug_12012'));
  212. // Change type from int to string
  213. $this->assertTrue($this->tools->sql_column_change('prefix_table_name', 'c_bug_12012', array('VCHAR:100', '')));
  214. // Remove the index
  215. $this->assertTrue($this->tools->sql_index_exists('prefix_table_name', 'i_bug_12012'));
  216. $this->assertTrue($this->tools->sql_index_drop('prefix_table_name', 'i_bug_12012'));
  217. $this->assertFalse($this->tools->sql_index_exists('prefix_table_name', 'i_bug_12012'));
  218. // Remove the column
  219. $this->assertTrue($this->tools->sql_column_exists('prefix_table_name', 'c_bug_12012'));
  220. $this->assertTrue($this->tools->sql_column_remove('prefix_table_name', 'c_bug_12012'));
  221. $this->assertFalse($this->tools->sql_column_exists('prefix_table_name', 'c_bug_12012'));
  222. }
  223. public function test_column_change_with_composite_primary()
  224. {
  225. // Remove the old primary key
  226. $this->assertTrue($this->tools->sql_column_remove('prefix_table_name', 'c_id'));
  227. $this->assertTrue($this->tools->sql_column_add('prefix_table_name', 'c_id', array('UINT', 0)));
  228. // Create a composite key
  229. $this->assertTrue($this->tools->sql_create_primary_key('prefix_table_name', array('c_id', 'c_uint')));
  230. // Create column
  231. $this->assertFalse($this->tools->sql_column_exists('prefix_table_name', 'c_bug_12643'));
  232. $this->assertTrue($this->tools->sql_column_add('prefix_table_name', 'c_bug_12643', array('DECIMAL', 0)));
  233. $this->assertTrue($this->tools->sql_column_exists('prefix_table_name', 'c_bug_12643'));
  234. // Change type from int to string
  235. $this->assertTrue($this->tools->sql_column_change('prefix_table_name', 'c_bug_12643', array('VCHAR:100', '')));
  236. }
  237. public function test_column_remove()
  238. {
  239. $this->assertTrue($this->tools->sql_column_exists('prefix_table_name', 'c_int_size'));
  240. $this->assertTrue($this->tools->sql_column_remove('prefix_table_name', 'c_int_size'));
  241. $this->assertFalse($this->tools->sql_column_exists('prefix_table_name', 'c_int_size'));
  242. }
  243. public function test_column_remove_similar_name()
  244. {
  245. $this->assertTrue($this->tools->sql_column_exists('prefix_table_name', 'c_vchar'));
  246. $this->assertTrue($this->tools->sql_column_exists('prefix_table_name', 'c_vchar_size'));
  247. $this->assertTrue($this->tools->sql_column_remove('prefix_table_name', 'c_vchar'));
  248. $this->assertFalse($this->tools->sql_column_exists('prefix_table_name', 'c_vchar'));
  249. $this->assertTrue($this->tools->sql_column_exists('prefix_table_name', 'c_vchar_size'));
  250. }
  251. public function test_column_remove_with_index()
  252. {
  253. // Create column
  254. $this->assertFalse($this->tools->sql_column_exists('prefix_table_name', 'c_bug_12012_2'));
  255. $this->assertTrue($this->tools->sql_column_add('prefix_table_name', 'c_bug_12012_2', array('UINT', 4)));
  256. $this->assertTrue($this->tools->sql_column_exists('prefix_table_name', 'c_bug_12012_2'));
  257. // Create index over the column
  258. $this->assertFalse($this->tools->sql_index_exists('prefix_table_name', 'bug_12012_2'));
  259. $this->assertTrue($this->tools->sql_create_index('prefix_table_name', 'bug_12012_2', array('c_bug_12012_2', 'c_bool')));
  260. $this->assertTrue($this->tools->sql_index_exists('prefix_table_name', 'bug_12012_2'));
  261. $this->assertFalse($this->tools->sql_index_exists('prefix_table_name', 'bug_12012_3'));
  262. $this->assertTrue($this->tools->sql_create_index('prefix_table_name', 'bug_12012_3', array('c_bug_12012_2')));
  263. $this->assertTrue($this->tools->sql_index_exists('prefix_table_name', 'bug_12012_3'));
  264. // Remove the column
  265. $this->assertTrue($this->tools->sql_column_exists('prefix_table_name', 'c_bug_12012_2'));
  266. $this->assertTrue($this->tools->sql_column_remove('prefix_table_name', 'c_bug_12012_2'));
  267. $this->assertFalse($this->tools->sql_column_exists('prefix_table_name', 'c_bug_12012_2'));
  268. }
  269. public function test_column_remove_primary()
  270. {
  271. $this->assertTrue($this->tools->sql_column_exists('prefix_table_name', 'c_id'));
  272. $this->assertTrue($this->tools->sql_column_remove('prefix_table_name', 'c_id'));
  273. $this->assertFalse($this->tools->sql_column_exists('prefix_table_name', 'c_id'));
  274. }
  275. public function test_list_tables()
  276. {
  277. $tables = $this->tools->sql_list_tables();
  278. $this->assertTrue(isset($tables['prefix_table_name']));
  279. $this->assertFalse(isset($tables['prefix_does_not_exist']));
  280. }
  281. public function test_table_exists()
  282. {
  283. $this->assertTrue($this->tools->sql_table_exists('prefix_table_name'));
  284. $this->assertFalse($this->tools->sql_table_exists('prefix_does_not_exist'));
  285. }
  286. public function test_table_drop()
  287. {
  288. $this->tools->sql_create_table('prefix_test_table',
  289. array('COLUMNS' => array(
  290. 'foo' => array('UINT', 42)))
  291. );
  292. $this->assertTrue($this->tools->sql_table_exists('prefix_test_table'));
  293. $this->tools->sql_table_drop('prefix_test_table');
  294. $this->assertFalse($this->tools->sql_table_exists('prefix_test_table'));
  295. }
  296. public function test_perform_schema_changes_drop_tables()
  297. {
  298. $db_tools = $this->getMockBuilder('\phpbb\db\tools\tools')
  299. ->setMethods(array('sql_table_exists', 'sql_table_drop'))
  300. ->setConstructorArgs(array(&$this->db))
  301. ->getMock();
  302. // pretend all tables exist
  303. $db_tools->expects($this->any())->method('sql_table_exists')
  304. ->will($this->returnValue(true));
  305. // drop tables
  306. $db_tools->expects($this->exactly(2))->method('sql_table_drop');
  307. $db_tools->expects($this->at(1))->method('sql_table_drop')
  308. ->with($this->equalTo('dropped_table_1'));
  309. $db_tools->expects($this->at(3))->method('sql_table_drop')
  310. ->with($this->equalTo('dropped_table_2'));
  311. $db_tools->perform_schema_changes(array(
  312. 'drop_tables' => array(
  313. 'dropped_table_1',
  314. 'dropped_table_2',
  315. ),
  316. ));
  317. }
  318. public function test_perform_schema_changes_drop_columns()
  319. {
  320. $db_tools = $this->getMockBuilder('\phpbb\db\tools\tools')
  321. ->setMethods(array('sql_column_exists', 'sql_column_remove'))
  322. ->setConstructorArgs(array(&$this->db))
  323. ->getMock();
  324. // pretend all columns exist
  325. $db_tools->expects($this->any())->method('sql_column_exists')
  326. ->will($this->returnValue(true));
  327. $db_tools->expects($this->any())->method('sql_column_exists')
  328. ->will($this->returnValue(true));
  329. // drop columns
  330. $db_tools->expects($this->exactly(2))->method('sql_column_remove');
  331. $db_tools->expects($this->at(1))->method('sql_column_remove')
  332. ->with($this->equalTo('existing_table'), $this->equalTo('dropped_column_1'));
  333. $db_tools->expects($this->at(3))->method('sql_column_remove')
  334. ->with($this->equalTo('existing_table'), $this->equalTo('dropped_column_2'));
  335. $db_tools->perform_schema_changes(array(
  336. 'drop_columns' => array(
  337. 'existing_table' => array(
  338. 'dropped_column_1',
  339. 'dropped_column_2',
  340. ),
  341. ),
  342. ));
  343. }
  344. public function test_index_exists()
  345. {
  346. $this->assertTrue($this->tools->sql_index_exists('prefix_table_name', 'i_simple'));
  347. }
  348. public function test_unique_index_exists()
  349. {
  350. $this->assertTrue($this->tools->sql_unique_index_exists('prefix_table_name', 'i_uniq'));
  351. }
  352. public function test_create_index_against_index_exists()
  353. {
  354. $this->tools->sql_create_index('prefix_table_name', 'fookey', array('c_timestamp', 'c_decimal'));
  355. $this->assertTrue($this->tools->sql_index_exists('prefix_table_name', 'fookey'));
  356. }
  357. public function test_create_unique_index_against_unique_index_exists()
  358. {
  359. $this->tools->sql_create_unique_index('prefix_table_name', 'i_uniq_ts_id', array('c_timestamp', 'c_id'));
  360. $this->assertTrue($this->tools->sql_unique_index_exists('prefix_table_name', 'i_uniq_ts_id'));
  361. }
  362. public function test_create_int_default_null()
  363. {
  364. $this->assertFalse($this->tools->sql_column_exists('prefix_table_name', 'c_bug_13282'));
  365. $this->assertTrue($this->tools->sql_column_add('prefix_table_name', 'c_bug_13282', array('TINT:2')));
  366. $this->assertTrue($this->tools->sql_column_exists('prefix_table_name', 'c_bug_13282'));
  367. }
  368. public function test_create_index_with_long_name()
  369. {
  370. // This constant is being used for checking table prefix.
  371. $table_prefix = substr(CONFIG_TABLE, 0, -6); // strlen(config)
  372. if (strlen($table_prefix) > 20)
  373. {
  374. $this->markTestIncomplete('The table prefix length is too long for proper testing of index shortening function.');
  375. }
  376. $max_index_length = 30;
  377. if ($this->tools instanceof \phpbb\db\tools\mssql)
  378. {
  379. $max_length_method = new ReflectionMethod('\phpbb\db\tools\mssql', 'get_max_index_name_length');
  380. $max_length_method->setAccessible(true);
  381. $max_index_length = $max_length_method->invoke($this->tools);
  382. }
  383. $table_suffix = str_repeat('a', 25 - strlen($table_prefix));
  384. $table_name = $table_prefix . $table_suffix;
  385. $this->tools->sql_create_table($table_name, $this->table_data);
  386. // Index name and table suffix and table prefix have > maximum index length chars in total.
  387. // Index name and table suffix have <= maximum index length chars in total.
  388. $long_index_name = str_repeat('i', $max_index_length - strlen($table_suffix));
  389. $this->assertFalse($this->tools->sql_index_exists($table_name, $long_index_name));
  390. $this->assertTrue($this->tools->sql_create_index($table_name, $long_index_name, array('c_timestamp')));
  391. $this->assertTrue($this->tools->sql_index_exists($table_name, $long_index_name));
  392. // Index name and table suffix have > maximum index length chars in total.
  393. $very_long_index_name = str_repeat('i', $max_index_length);
  394. $this->assertFalse($this->tools->sql_index_exists($table_name, $very_long_index_name));
  395. $this->assertTrue($this->tools->sql_create_index($table_name, $very_long_index_name, array('c_timestamp')));
  396. $this->assertTrue($this->tools->sql_index_exists($table_name, $very_long_index_name));
  397. $this->tools->sql_table_drop($table_name);
  398. // Index name has > maximum index length chars - that should not be possible.
  399. $too_long_index_name = str_repeat('i', $max_index_length + 1);
  400. $this->assertFalse($this->tools->sql_index_exists('prefix_table_name', $too_long_index_name));
  401. $this->setExpectedTriggerError(E_USER_ERROR);
  402. $this->tools->sql_create_index('prefix_table_name', $too_long_index_name, array('c_timestamp'));
  403. }
  404. }