Merge pull request #19552 from kamil-tekiela/Fix-default-values
[phpmyadmin.git] / tests / unit / Table / TableTest.php
blob31a843fac2fa7c5b7c49c5c7abd47a4a7e5ea9b7
1 <?php
3 declare(strict_types=1);
5 namespace PhpMyAdmin\Tests\Table;
7 use PhpMyAdmin\Config;
8 use PhpMyAdmin\ConfigStorage\Relation;
9 use PhpMyAdmin\ConfigStorage\RelationParameters;
10 use PhpMyAdmin\Current;
11 use PhpMyAdmin\Dbal\ConnectionType;
12 use PhpMyAdmin\Dbal\DatabaseInterface;
13 use PhpMyAdmin\ListDatabase;
14 use PhpMyAdmin\Query\Cache;
15 use PhpMyAdmin\SqlParser\Context;
16 use PhpMyAdmin\Table\MoveMode;
17 use PhpMyAdmin\Table\MoveScope;
18 use PhpMyAdmin\Table\Table;
19 use PhpMyAdmin\Table\TableMover;
20 use PhpMyAdmin\Table\UiProperty;
21 use PhpMyAdmin\Tests\AbstractTestCase;
22 use PhpMyAdmin\Tests\FieldHelper;
23 use PhpMyAdmin\Tests\Stubs\DbiDummy;
24 use PhpMyAdmin\Tests\Stubs\DummyResult;
25 use PHPUnit\Framework\Attributes\CoversClass;
26 use PHPUnit\Framework\Attributes\DataProvider;
27 use PHPUnit\Framework\MockObject\MockObject;
28 use ReflectionProperty;
30 use const MYSQLI_TYPE_STRING;
32 #[CoversClass(Table::class)]
33 class TableTest extends AbstractTestCase
35 private DatabaseInterface&MockObject $mockedDbi;
37 /**
38 * Configures environment
40 protected function setUp(): void
42 parent::setUp();
44 /**
45 * SET these to avoid undefined index error
47 $config = Config::getInstance();
48 $config->selectedServer['DisableIS'] = false;
49 $config->settings['MaxExactCount'] = 100;
50 $config->settings['MaxExactCountViews'] = 100;
51 $config->selectedServer['pmadb'] = 'pmadb';
52 $config->selectedServer['table_uiprefs'] = 'pma__table_uiprefs';
54 $sqlIsViewTrue = 'SELECT 1'
55 . ' FROM information_schema.VIEWS'
56 . ' WHERE TABLE_SCHEMA = \'PMA\''
57 . ' AND TABLE_NAME = \'PMA_BookMark\'';
59 $sqlIsViewFalse = 'SELECT 1'
60 . ' FROM information_schema.VIEWS'
61 . ' WHERE TABLE_SCHEMA = \'PMA\''
62 . ' AND TABLE_NAME = \'PMA_BookMark_2\'';
64 $sqlIsUpdatableViewTrue = 'SELECT 1'
65 . ' FROM information_schema.VIEWS'
66 . ' WHERE TABLE_SCHEMA = \'PMA\''
67 . ' AND TABLE_NAME = \'PMA_BookMark\''
68 . ' AND IS_UPDATABLE = \'YES\'';
70 $sqlIsUpdatableViewFalse = 'SELECT 1'
71 . ' FROM information_schema.VIEWS'
72 . ' WHERE TABLE_SCHEMA = \'PMA\''
73 . ' AND TABLE_NAME = \'PMA_BookMark_2\''
74 . ' AND IS_UPDATABLE = \'YES\'';
76 $sqlAnalyzeStructureTrue = 'SELECT COLUMN_NAME, DATA_TYPE'
77 . ' FROM information_schema.COLUMNS'
78 . ' WHERE TABLE_SCHEMA = \'PMA\''
79 . ' AND TABLE_NAME = \'PMA_BookMark\'';
81 $sqlCopyData = 'SELECT 1'
82 . ' FROM information_schema.VIEWS'
83 . ' WHERE TABLE_SCHEMA = \'PMA_new\''
84 . ' AND TABLE_NAME = \'PMA_BookMark_new\'';
86 $getUniqueColumnsSql = 'SHOW INDEXES FROM `PMA`.`PMA_BookMark`';
88 $fetchResultSimple = [
90 $sqlAnalyzeStructureTrue,
91 ConnectionType::User,
92 [['COLUMN_NAME' => 'COLUMN_NAME', 'DATA_TYPE' => 'DATA_TYPE']],
95 'SHOW COLUMNS FROM `PMA`.`PMA_BookMark`',
96 ConnectionType::User,
99 'Field' => 'COLUMN_NAME1',
100 'Type' => 'INT(10)',
101 'Null' => 'NO',
102 'Key' => '',
103 'Default' => null,
104 'Extra' => '',
107 'Field' => 'COLUMN_NAME2',
108 'Type' => 'INT(10)',
109 'Null' => 'YES',
110 'Key' => '',
111 'Default' => null,
112 'Extra' => 'STORED GENERATED',
117 'SHOW TRIGGERS FROM `PMA` LIKE \'PMA_BookMark\';',
118 ConnectionType::User,
121 'Trigger' => 'name1',
122 'Event' => 'INSERT',
123 'Table' => 'PMA_BookMark',
124 'Timing' => 'AFTER',
125 'Statement' => 'BEGIN END',
126 'Definer' => 'test_user@localhost',
129 'Trigger' => 'name2',
130 'Event' => 'INSERT',
131 'Table' => 'PMA_BookMark',
132 'Timing' => 'AFTER',
133 'Statement' => 'BEGIN END',
134 'Definer' => 'test_user@localhost',
137 'Trigger' => 'name3',
138 'Event' => 'INSERT',
139 'Table' => 'PMA_BookMark',
140 'Timing' => 'AFTER',
141 'Statement' => 'BEGIN END',
142 'Definer' => 'test_user@localhost',
147 'SHOW TRIGGERS FROM `PMA` LIKE \'PMA_.BookMark\';',
148 ConnectionType::User,
151 'Trigger' => 'name1',
152 'Event' => 'INSERT',
153 'Table' => 'PMA_.BookMark',
154 'Timing' => 'AFTER',
155 'Statement' => 'BEGIN END',
156 'Definer' => 'test_user@localhost',
159 'Trigger' => 'name2',
160 'Event' => 'INSERT',
161 'Table' => 'PMA_.BookMark',
162 'Timing' => 'AFTER',
163 'Statement' => 'BEGIN END',
164 'Definer' => 'test_user@localhost',
167 'Trigger' => 'name3',
168 'Event' => 'INSERT',
169 'Table' => 'PMA_.BookMark',
170 'Timing' => 'AFTER',
171 'Statement' => 'BEGIN END',
172 'Definer' => 'test_user@localhost',
177 'SELECT TRIGGER_SCHEMA, TRIGGER_NAME, EVENT_MANIPULATION, EVENT_OBJECT_TABLE, ACTION_TIMING, '
178 . 'ACTION_STATEMENT, EVENT_OBJECT_SCHEMA, EVENT_OBJECT_TABLE, DEFINER FROM '
179 . "information_schema.TRIGGERS WHERE EVENT_OBJECT_SCHEMA COLLATE utf8_bin= 'PMA' "
180 . "AND EVENT_OBJECT_TABLE COLLATE utf8_bin = 'PMA_BookMark';",
181 ConnectionType::User,
187 'SELECT TRIGGER_SCHEMA, TRIGGER_NAME, EVENT_MANIPULATION, EVENT_OBJECT_TABLE, ACTION_TIMING, '
188 . 'ACTION_STATEMENT, EVENT_OBJECT_SCHEMA, EVENT_OBJECT_TABLE, DEFINER FROM '
189 . "information_schema.TRIGGERS WHERE EVENT_OBJECT_SCHEMA COLLATE utf8_bin= 'aa' "
190 . "AND EVENT_OBJECT_TABLE COLLATE utf8_bin = 'ad';",
191 ConnectionType::User,
197 'SHOW COLUMNS FROM `aa`.`ad`',
198 ConnectionType::User,
203 $fetchResult = [
205 $getUniqueColumnsSql . ' WHERE (Non_unique = 0)',
206 ['Key_name', null],
207 'Column_name',
208 ConnectionType::User,
209 [['index1'], ['index3'], ['index5']],
212 $getUniqueColumnsSql,
213 'Column_name',
214 'Column_name',
215 ConnectionType::User,
216 ['column1', 'column3', 'column5', 'ACCESSIBLE', 'ADD', 'ALL'],
219 'SHOW COLUMNS FROM `PMA`.`PMA_BookMark`',
220 'Field',
221 'Field',
222 ConnectionType::User,
223 ['column1', 'column3', 'column5', 'ACCESSIBLE', 'ADD', 'ALL'],
227 $fetchValue = [
228 [$sqlIsViewTrue, 0, ConnectionType::User, 'PMA_BookMark'],
229 [$sqlCopyData, 0, ConnectionType::User, false],
230 [$sqlIsViewFalse, 0, ConnectionType::User, false],
231 [$sqlIsUpdatableViewTrue, 0, ConnectionType::User, 'PMA_BookMark'],
232 [$sqlIsUpdatableViewFalse, 0, ConnectionType::User, false],
234 "SELECT 1 FROM information_schema.VIEWS WHERE TABLE_SCHEMA = 'aa' AND TABLE_NAME = 'ad'",
236 ConnectionType::User,
237 'ad',
240 "SELECT 1 FROM information_schema.VIEWS WHERE TABLE_SCHEMA = 'bb' AND TABLE_NAME = 'ad'",
242 ConnectionType::User,
243 false,
247 $resultStub = $this->createMock(DummyResult::class);
249 $dbi = $this->getMockBuilder(DatabaseInterface::class)
250 ->disableOriginalConstructor()
251 ->getMock();
253 $databaseList = self::createStub(ListDatabase::class);
254 $databaseList->method('exists')->willReturn(true);
255 $dbi->expects(self::any())->method('getDatabaseList')->willReturn($databaseList);
257 $dbi->expects(self::any())->method('fetchResult')
258 ->willReturnMap($fetchResult);
260 $dbi->expects(self::any())->method('fetchResultSimple')
261 ->willReturnMap($fetchResultSimple);
263 $dbi->expects(self::any())->method('fetchValue')
264 ->willReturnMap($fetchValue);
266 $cache = new Cache();
267 $dbi->expects(self::any())->method('getCache')
268 ->willReturn($cache);
270 $dbi->expects(self::any())->method('getColumnNames')
271 ->willReturnMap([
273 'PMA',
274 'PMA_BookMark',
275 ConnectionType::User,
276 ['column1', 'column3', 'column5', 'ACCESSIBLE', 'ADD', 'ALL'],
280 $databases = [];
281 $databaseName = 'PMA';
282 $databases[$databaseName]['SCHEMA_TABLES'] = 1;
283 $databases[$databaseName]['SCHEMA_TABLE_ROWS'] = 3;
284 $databases[$databaseName]['SCHEMA_DATA_LENGTH'] = 5;
285 $databases[$databaseName]['SCHEMA_MAX_DATA_LENGTH'] = 10;
286 $databases[$databaseName]['SCHEMA_INDEX_LENGTH'] = 10;
287 $databases[$databaseName]['SCHEMA_LENGTH'] = 10;
289 $dbi->expects(self::any())->method('getTablesFull')
290 ->willReturn($databases);
292 $dbi->expects(self::any())->method('query')
293 ->willReturn($resultStub);
295 $dbi->expects(self::any())->method('insertId')
296 ->willReturn(10);
298 $resultStub->expects(self::any())->method('fetchAssoc')
299 ->willReturn([]);
301 $value = ['Auto_increment' => 'Auto_increment'];
302 $dbi->expects(self::any())->method('fetchSingleRow')
303 ->willReturn($value);
305 $resultStub->expects(self::any())->method('fetchRow')
306 ->willReturn([]);
308 $dbi->expects(self::any())->method('quoteString')
309 ->willReturnCallback(static fn (string $string): string => "'" . $string . "'");
311 DatabaseInterface::$instance = $dbi;
312 $this->mockedDbi = $dbi;
315 protected function tearDown(): void
317 parent::tearDown();
319 DatabaseInterface::$instance = null;
323 * Test for constructor
325 public function testConstruct(): void
327 $table = new Table('PMA_BookMark', 'PMA', $this->mockedDbi);
328 self::assertSame(
329 'PMA_BookMark',
330 $table->__toString(),
332 self::assertSame(
333 'PMA_BookMark',
334 $table->getName(),
336 self::assertSame(
337 'PMA',
338 $table->getDbName(),
340 self::assertSame(
341 'PMA.PMA_BookMark',
342 $table->getFullName(),
347 * Test getName & getDbName
349 public function testGetName(): void
351 $table = new Table('table1', 'pma_test', $this->mockedDbi);
352 self::assertSame(
353 'table1',
354 $table->getName(),
356 self::assertSame(
357 '`table1`',
358 $table->getName(true),
360 self::assertSame(
361 'pma_test',
362 $table->getDbName(),
364 self::assertSame(
365 '`pma_test`',
366 $table->getDbName(true),
371 * Test getLastError & getLastMessage
373 public function testGetLastErrorAndMessage(): void
375 $table = new Table('table1', 'pma_test', $this->mockedDbi);
376 $table->errors[] = 'error1';
377 $table->errors[] = 'error2';
378 $table->errors[] = 'error3';
380 $table->messages[] = 'messages1';
381 $table->messages[] = 'messages2';
382 $table->messages[] = 'messages3';
384 self::assertSame(
385 'error3',
386 $table->getLastError(),
388 self::assertSame(
389 'messages3',
390 $table->getLastMessage(),
393 $table->errors = [];
394 self::assertSame(
396 $table->getLastError(),
399 $table->messages = [];
400 self::assertSame(
402 $table->getLastMessage(),
407 * Test name validation
409 * @param string $name name to test
410 * @param bool $result expected result
411 * @param bool $isBackquoted is backquoted
413 #[DataProvider('dataValidateName')]
414 public function testValidateName(string $name, bool $result, bool $isBackquoted = false): void
416 self::assertSame(
417 $result,
418 Table::isValidName($name, $isBackquoted),
422 /** @return array<array{0: string, 1: bool, 2?: bool}> */
423 public static function dataValidateName(): array
425 return [
426 ['test', true],
427 ['te/st', false],
428 ['te.st', false],
429 ['te\\st', false],
430 ['te st', false],
431 [' te st', true, true],
432 ['test ', false],
433 ['te.st', false],
434 ['test ', false, true],
435 ['te.st ', false, true],
440 * Test for isView
442 public function testIsView(): void
444 $table = new Table('', '', $this->mockedDbi);
445 self::assertFalse(
446 $table->isView(),
449 //validate that it is the same as DBI fetchResult
450 $table = new Table('PMA_BookMark', 'PMA', $this->mockedDbi);
451 self::assertTrue(
452 $table->isView(),
455 $table = new Table('PMA_BookMark_2', 'PMA', $this->mockedDbi);
456 self::assertFalse(
457 $table->isView(),
462 * Test for generateFieldSpec
464 public function testGenerateFieldSpec(): void
466 //type is BIT
467 $name = 'PMA_name';
468 $type = 'BIT';
469 $length = '12';
470 $attribute = 'PMA_attribute';
471 $collation = 'PMA_collation';
472 $null = 'YES';
473 $defaultType = 'USER_DEFINED';
474 $defaultValue = '12';
475 $extra = 'AUTO_INCREMENT';
476 $comment = 'PMA_comment';
477 $virtuality = '';
478 $expression = '';
479 $moveTo = '-first';
481 $query = Table::generateFieldSpec(
482 $name,
483 $type,
484 $length,
485 $attribute,
486 $collation,
487 $null,
488 $defaultType,
489 $defaultValue,
490 $extra,
491 $comment,
492 $virtuality,
493 $expression,
494 $moveTo,
496 self::assertSame(
497 '`PMA_name` BIT(12) PMA_attribute NULL DEFAULT b\'10\' AUTO_INCREMENT COMMENT \'PMA_comment\' FIRST',
498 $query,
501 //type is DOUBLE
502 $type = 'DOUBLE';
503 $query = Table::generateFieldSpec(
504 $name,
505 $type,
506 $length,
507 $attribute,
508 $collation,
509 $null,
510 $defaultType,
511 $defaultValue,
512 $extra,
513 $comment,
514 $virtuality,
515 $expression,
516 $moveTo,
518 self::assertSame(
519 '`PMA_name` DOUBLE(12) PMA_attribute NULL DEFAULT \'12\' AUTO_INCREMENT COMMENT \'PMA_comment\' FIRST',
520 $query,
523 //type is BOOLEAN
524 $type = 'BOOLEAN';
525 $query = Table::generateFieldSpec(
526 $name,
527 $type,
528 $length,
529 $attribute,
530 $collation,
531 $null,
532 $defaultType,
533 $defaultValue,
534 $extra,
535 $comment,
536 $virtuality,
537 $expression,
538 $moveTo,
540 self::assertSame(
541 '`PMA_name` BOOLEAN PMA_attribute NULL DEFAULT TRUE AUTO_INCREMENT COMMENT \'PMA_comment\' FIRST',
542 $query,
545 //$default_type is NULL
546 $defaultType = 'NULL';
547 $query = Table::generateFieldSpec(
548 $name,
549 $type,
550 $length,
551 $attribute,
552 $collation,
553 $null,
554 $defaultType,
555 $defaultValue,
556 $extra,
557 $comment,
558 $virtuality,
559 $expression,
560 $moveTo,
562 self::assertSame(
563 '`PMA_name` BOOLEAN PMA_attribute NULL DEFAULT NULL AUTO_INCREMENT COMMENT \'PMA_comment\' FIRST',
564 $query,
567 //$default_type is CURRENT_TIMESTAMP
568 $defaultType = 'CURRENT_TIMESTAMP';
569 $query = Table::generateFieldSpec(
570 $name,
571 $type,
572 $length,
573 $attribute,
574 $collation,
575 $null,
576 $defaultType,
577 $defaultValue,
578 $extra,
579 $comment,
580 $virtuality,
581 $expression,
582 $moveTo,
584 self::assertSame(
585 '`PMA_name` BOOLEAN PMA_attribute NULL DEFAULT CURRENT_TIMESTAMP '
586 . "AUTO_INCREMENT COMMENT 'PMA_comment' FIRST",
587 $query,
590 //$default_type is current_timestamp()
591 $defaultType = 'current_timestamp()';
592 $query = Table::generateFieldSpec(
593 $name,
594 $type,
595 $length,
596 $attribute,
597 $collation,
598 $null,
599 $defaultType,
600 $defaultValue,
601 $extra,
602 $comment,
603 $virtuality,
604 $expression,
605 $moveTo,
607 self::assertSame(
608 '`PMA_name` BOOLEAN PMA_attribute NULL DEFAULT current_timestamp() '
609 . "AUTO_INCREMENT COMMENT 'PMA_comment' FIRST",
610 $query,
613 // $type is 'TIMESTAMP(3), $default_type is CURRENT_TIMESTAMP(3)
614 $type = 'TIMESTAMP';
615 $length = '3';
616 $extra = '';
617 $defaultType = 'CURRENT_TIMESTAMP';
618 $query = Table::generateFieldSpec(
619 $name,
620 $type,
621 $length,
622 $attribute,
623 $collation,
624 $null,
625 $defaultType,
626 $defaultValue,
627 $extra,
628 $comment,
629 $virtuality,
630 $expression,
631 $moveTo,
633 self::assertSame(
634 '`PMA_name` TIMESTAMP(3) PMA_attribute NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT \'PMA_comment\' FIRST',
635 $query,
638 $type = 'TIMESTAMP';
639 $length = '';
640 $extra = '';
641 $defaultType = 'USER_DEFINED';
642 $defaultValue = '\'0000-00-00 00:00:00\'';
643 $query = Table::generateFieldSpec(
644 $name,
645 $type,
646 $length,
647 $attribute,
648 $collation,
649 $null,
650 $defaultType,
651 $defaultValue,
652 $extra,
653 $comment,
654 $virtuality,
655 $expression,
656 $moveTo,
658 self::assertSame(
659 '`PMA_name` TIMESTAMP PMA_attribute NULL DEFAULT \'0000-00-00 00:00:00\' COMMENT \'PMA_comment\' FIRST',
660 $query,
663 $type = 'TIMESTAMP';
664 $length = '';
665 $extra = '';
666 $defaultType = 'USER_DEFINED';
667 $defaultValue = '\'0000-00-00 00:00:00.0\'';
668 $query = Table::generateFieldSpec(
669 $name,
670 $type,
671 $length,
672 $attribute,
673 $collation,
674 $null,
675 $defaultType,
676 $defaultValue,
677 $extra,
678 $comment,
679 $virtuality,
680 $expression,
681 $moveTo,
683 self::assertSame(
684 '`PMA_name` TIMESTAMP PMA_attribute NULL DEFAULT \'0000-00-00 00:00:00.0\' COMMENT \'PMA_comment\' FIRST',
685 $query,
688 $type = 'TIMESTAMP';
689 $length = '';
690 $extra = '';
691 $defaultType = 'USER_DEFINED';
692 $defaultValue = '\'0000-00-00 00:00:00.000000\'';
693 $query = Table::generateFieldSpec(
694 $name,
695 $type,
696 $length,
697 $attribute,
698 $collation,
699 $null,
700 $defaultType,
701 $defaultValue,
702 $extra,
703 $comment,
704 $virtuality,
705 $expression,
706 $moveTo,
708 self::assertSame(
709 '`PMA_name` TIMESTAMP PMA_attribute NULL DEFAULT \'0000-00-00 00:00:00.000000\' '
710 . "COMMENT 'PMA_comment' FIRST",
711 $query,
714 //$default_type is UUID
715 $type = 'UUID';
716 $defaultType = 'UUID';
717 $moveTo = '';
718 $query = Table::generateFieldSpec(
719 $name,
720 $type,
721 $length,
722 $attribute,
723 $collation,
724 $null,
725 $defaultType,
726 $defaultValue,
727 $extra,
729 $virtuality,
730 $expression,
731 $moveTo,
733 self::assertSame('`PMA_name` UUID PMA_attribute NULL DEFAULT uuid()', $query);
735 //$default_type is uuid()
736 $type = 'UUID';
737 $defaultType = 'uuid()';
738 $moveTo = '';
739 $query = Table::generateFieldSpec(
740 $name,
741 $type,
742 $length,
743 $attribute,
744 $collation,
745 $null,
746 $defaultType,
747 $defaultValue,
748 $extra,
750 $virtuality,
751 $expression,
752 $moveTo,
754 self::assertSame('`PMA_name` UUID PMA_attribute NULL DEFAULT uuid()', $query);
756 //$default_type is NONE
757 $type = 'BOOLEAN';
758 $defaultType = 'NONE';
759 $extra = 'INCREMENT';
760 $moveTo = '-first';
761 $query = Table::generateFieldSpec(
762 $name,
763 $type,
764 $length,
765 $attribute,
766 $collation,
767 $null,
768 $defaultType,
769 $defaultValue,
770 $extra,
771 $comment,
772 $virtuality,
773 $expression,
774 $moveTo,
776 self::assertSame('`PMA_name` BOOLEAN PMA_attribute NULL INCREMENT COMMENT \'PMA_comment\' FIRST', $query);
778 $defaultType = 'NONE';
779 $moveTo = '-first';
780 $query = Table::generateFieldSpec(
781 'ids',
782 'INT',
783 '11',
784 $attribute,
785 $collation,
786 $null,
787 $defaultType,
788 $defaultValue,
789 'AUTO_INCREMENT',
790 $comment,
791 $virtuality,
792 $expression,
793 $moveTo,
794 ['id'],
795 'id',
797 self::assertSame('`ids` INT(11) PMA_attribute NULL AUTO_INCREMENT COMMENT \'PMA_comment\' FIRST', $query);
799 $defaultType = 'NONE';
800 $moveTo = '-first';
801 $query = Table::generateFieldSpec(
802 'ids',
803 'INT',
804 '11',
805 $attribute,
806 $collation,
807 $null,
808 $defaultType,
809 $defaultValue,
810 'AUTO_INCREMENT',
811 $comment,
812 $virtuality,
813 $expression,
814 $moveTo,
815 ['othercol'],
816 'id',
818 // Add primary key for AUTO_INCREMENT if missing
819 self::assertSame(
820 '`ids` INT(11) PMA_attribute NULL AUTO_INCREMENT '
821 . "COMMENT 'PMA_comment' FIRST, ADD PRIMARY KEY (`ids`)",
822 $query,
825 $defaultType = 'NONE';
826 $moveTo = '-first';
827 $query = Table::generateFieldSpec(
828 'id',
829 'INT',
830 '11',
831 $attribute,
832 $collation,
833 $null,
834 $defaultType,
835 $defaultValue,
836 'DEF',
837 $comment,
838 $virtuality,
839 $expression,
840 $moveTo,
841 ['id'],
842 'id',
844 // Do not add PK
845 self::assertSame('`id` INT(11) PMA_attribute NULL DEF COMMENT \'PMA_comment\' FIRST', $query);
847 $defaultType = 'NONE';
848 $moveTo = '-first';
849 $query = Table::generateFieldSpec(
850 'ids',
851 'INT',
852 '11',
853 $attribute,
854 $collation,
855 $null,
856 $defaultType,
857 $defaultValue,
858 'DEF',
859 $comment,
860 $virtuality,
861 $expression,
862 $moveTo,
863 ['id'],
864 'id',
866 // Do not add PK
867 self::assertSame('`ids` INT(11) PMA_attribute NULL DEF COMMENT \'PMA_comment\' FIRST', $query);
869 $defaultType = 'NONE';
870 $moveTo = '-first';
871 $query = Table::generateFieldSpec(
872 'ids',
873 'INT',
874 '11',
875 $attribute,
876 $collation,
877 $null,
878 $defaultType,
879 $defaultValue,
880 'DEF',
881 $comment,
882 $virtuality,
883 $expression,
884 $moveTo,
885 ['ids'],
886 'id',
888 // Add it beaucause it is missing
889 self::assertSame(
890 '`ids` INT(11) PMA_attribute NULL DEF COMMENT \'PMA_comment\' FIRST, ADD PRIMARY KEY (`ids`)',
891 $query,
894 $defaultType = 'NONE';
895 $moveTo = '-first';
896 $query = Table::generateFieldSpec(
897 'ids',
898 'INT',
899 '11',
900 $attribute,
901 $collation,
902 $null,
903 $defaultType,
904 $defaultValue,
905 'USER_DEFINED',
906 $comment,
907 'VIRTUAL',
908 '1',
909 $moveTo,
910 ['othercol'],
911 'id',
913 // Do not add PK since it is not a AUTO_INCREMENT
914 self::assertSame(
915 '`ids` INT(11) PMA_attribute AS (1) VIRTUAL NULL USER_DEFINED COMMENT \'PMA_comment\' FIRST',
916 $query,
921 * Test for duplicateInfo
923 public function testDuplicateInfo(): void
925 $getFields = ['filed0', 'field6'];
926 $whereFields = ['field2', 'filed5'];
927 $newFields = ['field3', 'filed4'];
929 $relationParameters = RelationParameters::fromArray([
930 'db' => 'PMA_db',
931 'relwork' => true,
932 'relation' => 'relation',
934 (new ReflectionProperty(Relation::class, 'cache'))->setValue(null, $relationParameters);
936 $object = new TableMover($this->mockedDbi, new Relation($this->mockedDbi));
937 $ret = $object->duplicateInfo('relwork', 'relation', $getFields, $whereFields, $newFields);
938 self::assertSame(-1, $ret);
942 * Test for isUpdatableView
944 public function testIsUpdatableView(): void
946 $table = new Table('', '', $this->mockedDbi);
947 self::assertFalse(
948 $table->isUpdatableView(),
951 //validate that it is the same as DBI fetchResult
952 $table = new Table('PMA_BookMark', 'PMA', $this->mockedDbi);
953 self::assertTrue(
954 $table->isUpdatableView(),
957 $table = new Table('PMA_BookMark_2', 'PMA', $this->mockedDbi);
958 self::assertFalse(
959 $table->isUpdatableView(),
964 * Test for isMerge -- when there's no ENGINE info cached
966 public function testIsMergeCase1(): void
968 $tableObj = new Table('PMA_BookMark', 'PMA', $this->mockedDbi);
969 self::assertFalse(
970 $tableObj->isMerge(),
975 * Test for isMerge -- when ENGINE info is MERGE
977 public function testIsMergeCase2(): void
979 $this->mockedDbi->getCache()->cacheTableValue('PMA', 'PMA_BookMark', 'ENGINE', 'MERGE');
981 $tableObj = new Table('PMA_BookMark', 'PMA', $this->mockedDbi);
982 self::assertTrue(
983 $tableObj->isMerge(),
988 * Test for isMerge -- when ENGINE info is MRG_MYISAM
990 public function testIsMergeCase3(): void
992 $this->mockedDbi->getCache()->cacheTableValue('PMA', 'PMA_BookMark', 'ENGINE', 'MRG_MYISAM');
994 $tableObj = new Table('PMA_BookMark', 'PMA', $this->mockedDbi);
995 self::assertTrue(
996 $tableObj->isMerge(),
1001 * Test for Table::isMerge -- when ENGINE info is ISDB
1003 public function testIsMergeCase4(): void
1005 $tableObj = new Table('PMA_BookMark', 'PMA', $this->mockedDbi);
1006 self::assertFalse(
1007 $tableObj->isMerge(),
1012 * Test for generateAlter
1014 public function testGenerateAlter(): void
1016 //parameter
1017 $oldcol = 'name';
1018 $newcol = 'new_name';
1019 $type = 'VARCHAR';
1020 $length = '2';
1021 $attribute = 'new_name';
1022 $collation = 'charset1';
1023 $null = 'YES';
1024 $defaultType = 'USER_DEFINED';
1025 $defaultValue = 'VARCHAR';
1026 $extra = 'AUTO_INCREMENT';
1027 $comment = 'PMA comment';
1028 $virtuality = '';
1029 $expression = '';
1030 $moveTo = 'new_name';
1032 $result = Table::generateAlter(
1033 $oldcol,
1034 $newcol,
1035 $type,
1036 $length,
1037 $attribute,
1038 $collation,
1039 $null,
1040 $defaultType,
1041 $defaultValue,
1042 $extra,
1043 $comment,
1044 $virtuality,
1045 $expression,
1046 $moveTo,
1049 $expect = '`name` `new_name` VARCHAR(2) new_name CHARACTER SET '
1050 . "charset1 NULL DEFAULT 'VARCHAR' "
1051 . "AUTO_INCREMENT COMMENT 'PMA comment' AFTER `new_name`";
1053 self::assertSame($expect, $result);
1057 * Test for rename
1059 public function testRename(): void
1061 Config::getInstance()->selectedServer['DisableIS'] = true;
1063 $table = 'PMA_BookMark';
1064 $db = 'PMA';
1066 $this->mockedDbi->expects(self::any())->method('tryQuery')->willReturn($this->createMock(DummyResult::class));
1068 $table = new Table($table, $db, $this->mockedDbi);
1070 //rename to same name
1071 $tableNew = 'PMA_BookMark';
1072 $result = $table->rename($tableNew);
1073 self::assertTrue($result);
1075 //isValidName
1076 //space in table name
1077 $tableNew = 'PMA_BookMark ';
1078 $result = $table->rename($tableNew);
1079 self::assertFalse($result);
1080 //empty name
1081 $tableNew = '';
1082 $result = $table->rename($tableNew);
1083 self::assertFalse($result);
1084 //dot in table name
1085 $tableNew = 'PMA_.BookMark';
1086 $result = $table->rename($tableNew);
1087 self::assertTrue($result);
1089 //message
1090 self::assertSame(
1091 'Table PMA_BookMark has been renamed to PMA_.BookMark.',
1092 $table->getLastMessage(),
1095 $tableNew = 'PMA_BookMark_new';
1096 $dbNew = 'PMA_new';
1097 $result = $table->rename($tableNew, $dbNew);
1098 self::assertTrue($result);
1099 //message
1100 self::assertSame(
1101 'Table PMA_.BookMark has been renamed to PMA_BookMark_new.',
1102 $table->getLastMessage(),
1107 * Test for getUniqueColumns
1109 public function testGetUniqueColumns(): void
1111 $table = 'PMA_BookMark';
1112 $db = 'PMA';
1114 $table = new Table($table, $db, $this->mockedDbi);
1115 $return = $table->getUniqueColumns();
1116 $expect = ['`PMA`.`PMA_BookMark`.`index1`', '`PMA`.`PMA_BookMark`.`index3`', '`PMA`.`PMA_BookMark`.`index5`'];
1117 self::assertSame($expect, $return);
1121 * Test for getIndexedColumns
1123 public function testGetIndexedColumns(): void
1125 $table = 'PMA_BookMark';
1126 $db = 'PMA';
1128 $table = new Table($table, $db, $this->mockedDbi);
1129 $return = $table->getIndexedColumns();
1130 $expect = [
1131 '`PMA`.`PMA_BookMark`.`column1`',
1132 '`PMA`.`PMA_BookMark`.`column3`',
1133 '`PMA`.`PMA_BookMark`.`column5`',
1134 '`PMA`.`PMA_BookMark`.`ACCESSIBLE`',
1135 '`PMA`.`PMA_BookMark`.`ADD`',
1136 '`PMA`.`PMA_BookMark`.`ALL`',
1138 self::assertSame($expect, $return);
1142 * Test for getColumnsMeta
1144 public function testGetColumnsMeta(): void
1146 $dbi = $this->getMockBuilder(DatabaseInterface::class)
1147 ->disableOriginalConstructor()
1148 ->getMock();
1150 $resultStub = $this->createMock(DummyResult::class);
1152 $dbi->expects(self::once())
1153 ->method('tryQuery')
1154 ->with('SELECT * FROM `db`.`table` LIMIT 1')
1155 ->willReturn($resultStub);
1157 $dummyFieldMetadata = FieldHelper::fromArray(['type' => MYSQLI_TYPE_STRING]);
1159 $dbi->expects(self::once())
1160 ->method('getFieldsMeta')
1161 ->with($resultStub)
1162 ->willReturn([$dummyFieldMetadata]);
1164 DatabaseInterface::$instance = $dbi;
1166 $tableObj = new Table('table', 'db', $dbi);
1168 self::assertSame(
1169 $tableObj->getColumnsMeta(),
1170 [$dummyFieldMetadata],
1175 * Tests for getSQLToCreateForeignKey() method.
1177 public function testGetSQLToCreateForeignKey(): void
1179 $table = 'PMA_table';
1180 $field = ['PMA_field1', 'PMA_field2'];
1181 $foreignDb = 'foreignDb';
1182 $foreignTable = 'foreignTable';
1183 $foreignField = ['foreignField1', 'foreignField2'];
1185 $tableObj = new Table('PMA_table', 'db', $this->mockedDbi);
1187 $sql = $this->callFunction(
1188 $tableObj,
1189 Table::class,
1190 'getSQLToCreateForeignKey',
1191 [$table, $field, $foreignDb, $foreignTable, $foreignField],
1193 $sqlExcepted = 'ALTER TABLE `PMA_table` ADD '
1194 . 'FOREIGN KEY (`PMA_field1`, `PMA_field2`) REFERENCES '
1195 . '`foreignDb`.`foreignTable`(`foreignField1`, `foreignField2`);';
1196 self::assertSame($sqlExcepted, $sql);
1198 // Exclude db name when relations are made between table in the same db
1199 $sql = $this->callFunction(
1200 $tableObj,
1201 Table::class,
1202 'getSQLToCreateForeignKey',
1203 [$table, $field, 'db', $foreignTable, $foreignField],
1205 $sqlExcepted = 'ALTER TABLE `PMA_table` ADD '
1206 . 'FOREIGN KEY (`PMA_field1`, `PMA_field2`) REFERENCES '
1207 . '`foreignTable`(`foreignField1`, `foreignField2`);';
1208 self::assertSame($sqlExcepted, $sql);
1212 * Test for getColumns
1214 public function testGetColumns(): void
1216 Context::load();
1217 $table = 'PMA_BookMark';
1218 $db = 'PMA';
1220 $table = new Table($table, $db, $this->mockedDbi);
1221 $return = $table->getColumns();
1222 $expect = [
1223 '`PMA`.`PMA_BookMark`.`column1`',
1224 '`PMA`.`PMA_BookMark`.`column3`',
1225 '`PMA`.`PMA_BookMark`.`column5`',
1226 '`PMA`.`PMA_BookMark`.`ACCESSIBLE`',
1227 '`PMA`.`PMA_BookMark`.`ADD`',
1228 '`PMA`.`PMA_BookMark`.`ALL`',
1230 self::assertSame($expect, $return);
1234 * Test for checkIfMinRecordsExist
1236 public function testCheckIfMinRecordsExist(): void
1238 $oldDbi = $this->mockedDbi;
1240 $resultStub = $this->createMock(DummyResult::class);
1242 $dbi = $this->getMockBuilder(DatabaseInterface::class)
1243 ->disableOriginalConstructor()
1244 ->getMock();
1245 $dbi->expects(self::any())
1246 ->method('tryQuery')
1247 ->willReturn($resultStub);
1248 $resultStub->expects(self::any())
1249 ->method('numRows')
1250 ->willReturn(0, 10, 200);
1251 $dbi->expects(self::any())
1252 ->method('fetchResult')
1253 ->willReturn(
1254 [['`one_pk`']],
1255 [], // No Uniques found
1256 ['`one_ind`', '`sec_ind`'],
1257 [], // No Uniques found
1258 [], // No Indexed found
1261 DatabaseInterface::$instance = $dbi;
1263 $table = 'PMA_BookMark';
1264 $db = 'PMA';
1265 $tableObj = new Table($table, $db, $dbi);
1267 // Case 1 : Check if table is non-empty
1268 $return = $tableObj->checkIfMinRecordsExist();
1269 self::assertTrue($return);
1271 // Case 2 : Check if table contains at least 100
1272 $return = $tableObj->checkIfMinRecordsExist(100);
1273 self::assertFalse($return);
1275 // Case 3 : Check if table contains at least 100
1276 $return = $tableObj->checkIfMinRecordsExist(100);
1277 self::assertTrue($return);
1279 DatabaseInterface::$instance = $oldDbi;
1283 * Test for Table::countRecords
1285 public function testCountRecords(): void
1287 $resultStub = $this->createMock(DummyResult::class);
1288 $resultStub->expects(self::any())
1289 ->method('numRows')
1290 ->willReturn(20);
1292 $dbi = clone $this->mockedDbi;
1293 $dbi->expects(self::any())->method('tryQuery')
1294 ->willReturn($resultStub);
1296 $table = 'PMA_BookMark';
1297 $db = 'PMA';
1298 $tableObj = new Table($table, $db, $dbi);
1300 self::assertSame(
1302 $tableObj->countRecords(true),
1307 * Test for setUiProp
1309 public function testSetUiProp(): void
1311 $tableName = 'PMA_BookMark';
1312 $db = 'PMA';
1314 $table = new Table($tableName, $db, $this->mockedDbi);
1316 $property = UiProperty::ColumnOrder;
1317 $value = 'UiProp_value';
1318 $tableCreateTime = null;
1319 $table->setUiProp($property, $value, $tableCreateTime);
1321 //set UI prop successfully
1322 self::assertSame($value, $table->uiprefs[$property->value]);
1324 //removeUiProp
1325 $table->removeUiProp($property);
1326 $isDefineProperty = isset($table->uiprefs[$property->value]);
1327 self::assertFalse($isDefineProperty);
1329 //getUiProp after removeUiProp
1330 $isDefineProperty = $table->getUiProp($property);
1331 self::assertFalse($isDefineProperty);
1335 * Test for moveCopy
1337 public function testMoveCopy(): void
1339 $sourceTable = 'PMA_BookMark';
1340 $sourceDb = 'PMA';
1341 $targetTable = 'PMA_BookMark_new';
1342 $targetDb = 'PMA_new';
1344 $getTableMap = [
1345 [$targetDb, $targetTable, new Table($targetTable, $targetDb, $this->mockedDbi)],
1346 ['aa', 'ad', new Table('ad', 'aa', $this->mockedDbi)],
1349 $this->mockedDbi->expects(self::any())->method('getTable')
1350 ->willReturnMap($getTableMap);
1352 $object = new TableMover($this->mockedDbi, new Relation($this->mockedDbi));
1354 $return = $object->moveCopy(
1355 $sourceDb,
1356 $sourceTable,
1357 $targetDb,
1358 $targetTable,
1359 MoveScope::Move,
1360 MoveMode::SingleTable,
1361 true,
1364 //successfully
1365 self::assertTrue($return);
1366 $sqlQuery = 'INSERT INTO `PMA_new`.`PMA_BookMark_new`(`COLUMN_NAME1`)'
1367 . ' SELECT `COLUMN_NAME1` FROM '
1368 . '`PMA`.`PMA_BookMark`';
1369 self::assertStringContainsString($sqlQuery, Current::$sqlQuery);
1370 $sqlQuery = 'DROP VIEW `PMA`.`PMA_BookMark`';
1371 self::assertStringContainsString($sqlQuery, Current::$sqlQuery);
1373 $return = $object->moveCopy(
1374 $sourceDb,
1375 $sourceTable,
1376 $targetDb,
1377 $targetTable,
1378 MoveScope::DataOnly,
1379 MoveMode::SingleTable,
1380 true,
1383 //successfully
1384 self::assertTrue($return);
1385 $sqlQuery = 'INSERT INTO `PMA_new`.`PMA_BookMark_new`(`COLUMN_NAME1`)'
1386 . ' SELECT `COLUMN_NAME1` FROM '
1387 . '`PMA`.`PMA_BookMark`';
1388 self::assertStringContainsString($sqlQuery, Current::$sqlQuery);
1389 $sqlQuery = 'DROP VIEW `PMA`.`PMA_BookMark`';
1390 self::assertStringNotContainsString($sqlQuery, Current::$sqlQuery);
1392 // Renaming DB with a view bug
1393 $resultStub = $this->createMock(DummyResult::class);
1394 $this->mockedDbi->expects(self::any())->method('tryQuery')
1395 ->willReturnMap([
1397 'SHOW CREATE TABLE `aa`.`ad`',
1398 ConnectionType::User,
1399 false,
1400 true,
1401 $resultStub,
1404 'SHOW TABLE STATUS FROM `aa` WHERE Name = \'ad\'',
1405 ConnectionType::User,
1406 false,
1407 true,
1408 $resultStub,
1410 ['USE `aa`', ConnectionType::User, false, true, $resultStub],
1412 'RENAME TABLE `PMA`.`PMA_BookMark` TO `PMA`.`PMA_.BookMark`;',
1413 ConnectionType::User,
1414 false,
1415 true,
1416 false,
1419 'RENAME TABLE `aa`.`ad` TO `bb`.`ad`;',
1420 ConnectionType::User,
1421 false,
1422 true,
1423 false,
1426 $resultStub->expects(self::any())
1427 ->method('fetchRow')
1428 ->willReturn([
1429 'ad',
1430 'CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost`' .
1431 ' SQL SECURITY DEFINER VIEW `ad` AS select `aa`.`bb`.`ac` AS `ac` from `bb`',
1432 'utf8mb4',
1433 'utf8mb4_unicode_ci',
1436 Current::$sqlQuery = '';
1437 $return = $object->moveCopy('aa', 'ad', 'bb', 'ad', MoveScope::Move, MoveMode::WholeDatabase, true);
1438 self::assertTrue($return);
1439 self::assertStringContainsString('DROP TABLE IF EXISTS `bb`.`ad`;', Current::$sqlQuery);
1440 self::assertStringContainsString(
1441 'CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost`' .
1442 ' SQL SECURITY DEFINER VIEW `bb`.`ad` AS SELECT `bb`.`ac` AS `ac` FROM `bb` ;',
1443 Current::$sqlQuery,
1445 self::assertStringContainsString('DROP VIEW `aa`.`ad`;', Current::$sqlQuery);
1449 * Test for getStorageEngine
1451 public function testGetStorageEngine(): void
1453 $targetTable = 'table1';
1454 $targetDb = 'pma_test';
1455 $extension = new DbiDummy();
1456 $dbi = DatabaseInterface::getInstanceForTest($extension);
1457 $tblObject = new Table($targetTable, $targetDb, $dbi);
1458 $tblObject->getStatusInfo(null);
1459 $expect = 'DBIDUMMY';
1460 $tblStorageEngine = $dbi->getTable($targetDb, $targetTable)->getStorageEngine();
1461 self::assertSame($expect, $tblStorageEngine);
1465 * Test for getComment
1467 public function testGetComment(): void
1469 $targetTable = 'table1';
1470 $targetDb = 'pma_test';
1471 $extension = new DbiDummy();
1472 $dbi = DatabaseInterface::getInstanceForTest($extension);
1473 $tblObject = new Table($targetTable, $targetDb, $dbi);
1474 $tblObject->getStatusInfo(null);
1475 $expect = 'Test comment for "table1" in \'pma_test\'';
1476 $showComment = $dbi->getTable($targetDb, $targetTable)->getComment();
1477 self::assertSame($expect, $showComment);
1481 * Test for getCollation
1483 public function testGetCollation(): void
1485 $targetTable = 'table1';
1486 $targetDb = 'pma_test';
1487 $extension = new DbiDummy();
1488 $dbi = DatabaseInterface::getInstanceForTest($extension);
1489 $tblObject = new Table($targetTable, $targetDb, $dbi);
1490 $tblObject->getStatusInfo(null);
1491 $expect = 'utf8mb4_general_ci';
1492 $tblCollation = $dbi->getTable($targetDb, $targetTable)->getCollation();
1493 self::assertSame($expect, $tblCollation);
1497 * Test for getRowFormat
1499 public function testGetRowFormat(): void
1501 $targetTable = 'table1';
1502 $targetDb = 'pma_test';
1503 $extension = new DbiDummy();
1504 $dbi = DatabaseInterface::getInstanceForTest($extension);
1505 $tblObject = new Table($targetTable, $targetDb, $dbi);
1506 $tblObject->getStatusInfo(null);
1507 $expect = 'Redundant';
1508 $rowFormat = $dbi->getTable($targetDb, $targetTable)->getRowFormat();
1509 self::assertSame($expect, $rowFormat);
1513 * Test for getAutoIncrement
1515 public function testGetAutoIncrement(): void
1517 $targetTable = 'table1';
1518 $targetDb = 'pma_test';
1519 $extension = new DbiDummy();
1520 $dbi = DatabaseInterface::getInstanceForTest($extension);
1521 $tblObject = new Table($targetTable, $targetDb, $dbi);
1522 $tblObject->getStatusInfo(null);
1523 $expect = '5';
1524 $autoIncrement = $dbi->getTable($targetDb, $targetTable)->getAutoIncrement();
1525 self::assertSame($expect, $autoIncrement);
1529 * Test for getCreateOptions
1531 public function testGetCreateOptions(): void
1533 $targetTable = 'table1';
1534 $targetDb = 'pma_test';
1535 $extension = new DbiDummy();
1536 $dbi = DatabaseInterface::getInstanceForTest($extension);
1537 $tblObject = new Table($targetTable, $targetDb, $dbi);
1538 $tblObject->getStatusInfo(null);
1539 $expect = ['pack_keys' => 'DEFAULT', 'row_format' => 'REDUNDANT'];
1540 $createOptions = $dbi->getTable($targetDb, $targetTable)->getCreateOptions();
1541 self::assertEquals($expect, $createOptions);