mirror of
https://gitlab.com/signalytic/client-external/streamline/streamline-emr.git
synced 2026-09-11 18:51:31 +00:00
updated streamline-setup v2
This commit is contained in:
+260
@@ -0,0 +1,260 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Builder;
|
||||
|
||||
use Generator;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class AlterStatementTest extends TestCase
|
||||
{
|
||||
public function testBuilder(): void
|
||||
{
|
||||
$query = 'ALTER TABLE `actor` ' .
|
||||
'ADD PRIMARY KEY (`actor_id`), ' .
|
||||
'ADD KEY `idx_actor_last_name` (`last_name`)';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals($query, $stmt->build());
|
||||
}
|
||||
|
||||
public function testBuilderWithExpression(): void
|
||||
{
|
||||
$query = 'ALTER TABLE `table` '
|
||||
. 'ADD UNIQUE KEY `functional_index`'
|
||||
. ' (`field1`,`field2`, (IFNULL(`field3`,0)))';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals($query, $stmt->build());
|
||||
}
|
||||
|
||||
public function testBuilderWithComments(): void
|
||||
{
|
||||
$query = 'ALTER /* comment */ TABLE `actor` ' .
|
||||
'ADD PRIMARY KEY (`actor_id`), -- comment at the end of the line' . "\n" .
|
||||
'ADD KEY `idx_actor_last_name` (`last_name`) -- and that is the last comment.';
|
||||
|
||||
$expectedQuery = 'ALTER TABLE `actor` ' .
|
||||
'ADD PRIMARY KEY (`actor_id`), ' .
|
||||
'ADD KEY `idx_actor_last_name` (`last_name`)';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals($expectedQuery, $stmt->build());
|
||||
}
|
||||
|
||||
public function testBuilderWithCommentsOnOptions(): void
|
||||
{
|
||||
$query = 'ALTER EVENT `myEvent` /* comment */ ' .
|
||||
'ON SCHEDULE -- Comment at the end of the line' . "\n" .
|
||||
'AT "2023-01-01 01:23:45"';
|
||||
|
||||
$expectedQuery = 'ALTER EVENT `myEvent` ' .
|
||||
'ON SCHEDULE AT "2023-01-01 01:23:45"';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals($expectedQuery, $stmt->build());
|
||||
}
|
||||
|
||||
public function testBuilderCompressed(): void
|
||||
{
|
||||
$query = 'ALTER TABLE `user` CHANGE `message` `message` TEXT COMPRESSED';
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals($query, $stmt->build());
|
||||
}
|
||||
|
||||
public function testBuilderPartitions(): void
|
||||
{
|
||||
$parser = new Parser('ALTER TABLE t1 PARTITION BY HASH(id) PARTITIONS 8');
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals('ALTER TABLE t1 PARTITION BY HASH(id) PARTITIONS 8', $stmt->build());
|
||||
|
||||
$parser = new Parser('ALTER TABLE t1 ADD PARTITION (PARTITION p3 VALUES LESS THAN (2002))');
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
"ALTER TABLE t1 ADD PARTITION (\n" .
|
||||
"PARTITION p3 VALUES LESS THAN (2002)\n" .
|
||||
')',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
$parser = new Parser('ALTER TABLE p PARTITION BY LINEAR KEY ALGORITHM=2 (id) PARTITIONS 32;');
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'ALTER TABLE p PARTITION BY LINEAR KEY ALGORITHM=2 (id) PARTITIONS 32',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
$parser = new Parser('ALTER TABLE t1 DROP PARTITION p0, p1;');
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'ALTER TABLE t1 DROP PARTITION p0, p1',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
$parser = new Parser(
|
||||
'ALTER TABLE trips PARTITION BY RANGE (MONTH(trip_date))'
|
||||
. ' (' . "\n"
|
||||
. ' PARTITION p01 VALUES LESS THAN (02),' . "\n"
|
||||
. ' PARTITION p02 VALUES LESS THAN (03),' . "\n"
|
||||
. ' PARTITION p03 VALUES LESS THAN (04),' . "\n"
|
||||
. ' PARTITION p04 VALUES LESS THAN (05),' . "\n"
|
||||
. ' PARTITION p05 VALUES LESS THAN (06),' . "\n"
|
||||
. ' PARTITION p06 VALUES LESS THAN (07),' . "\n"
|
||||
. ' PARTITION p07 VALUES LESS THAN (08),' . "\n"
|
||||
. ' PARTITION p08 VALUES LESS THAN (09),' . "\n"
|
||||
. ' PARTITION p09 VALUES LESS THAN (10),' . "\n"
|
||||
. ' PARTITION p10 VALUES LESS THAN (11),' . "\n"
|
||||
. ' PARTITION p11 VALUES LESS THAN (12),' . "\n"
|
||||
. ' PARTITION p12 VALUES LESS THAN (13),' . "\n"
|
||||
. ' PARTITION pmaxval VALUES LESS THAN MAXVALUE' . "\n"
|
||||
. ');'
|
||||
);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'ALTER TABLE trips PARTITION BY RANGE (MONTH(trip_date)) (' . "\n"
|
||||
. 'PARTITION p01 VALUES LESS THAN (02),' . "\n"
|
||||
. 'PARTITION p02 VALUES LESS THAN (03),' . "\n"
|
||||
. 'PARTITION p03 VALUES LESS THAN (04),' . "\n"
|
||||
. 'PARTITION p04 VALUES LESS THAN (05),' . "\n"
|
||||
. 'PARTITION p05 VALUES LESS THAN (06),' . "\n"
|
||||
. 'PARTITION p06 VALUES LESS THAN (07),' . "\n"
|
||||
. 'PARTITION p07 VALUES LESS THAN (08),' . "\n"
|
||||
. 'PARTITION p08 VALUES LESS THAN (09),' . "\n"
|
||||
. 'PARTITION p09 VALUES LESS THAN (10),' . "\n"
|
||||
. 'PARTITION p10 VALUES LESS THAN (11),' . "\n"
|
||||
. 'PARTITION p11 VALUES LESS THAN (12),' . "\n"
|
||||
. 'PARTITION p12 VALUES LESS THAN (13),' . "\n"
|
||||
. 'PARTITION pmaxval VALUES LESS THAN MAXVALUE' . "\n"
|
||||
. ')',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderEventWithDefiner(): void
|
||||
{
|
||||
$query = 'ALTER DEFINER=user EVENT myEvent ENABLE';
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals($query, $stmt->build());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Generator<string, array{string}>
|
||||
*/
|
||||
public static function provideBuilderForRenameColumn(): Generator
|
||||
{
|
||||
$query = 'ALTER TABLE myTable RENAME COLUMN a TO b';
|
||||
|
||||
yield 'Single RENAME COLUMN' => [$query];
|
||||
|
||||
$query = 'ALTER TABLE myTable RENAME COLUMN a TO b, RENAME COLUMN b TO a';
|
||||
|
||||
yield 'Multiple RENAME COLUMN' => [$query];
|
||||
|
||||
$query = 'ALTER TABLE myTable ' .
|
||||
'RENAME COLUMN a TO b, ' .
|
||||
'RENAME COLUMN b TO a, ' .
|
||||
'RENAME INDEX oldIndex TO newIndex, ' .
|
||||
'RENAME TO newTable';
|
||||
|
||||
yield 'Mixed RENAME COLUMN + RENAME INDEX + RENAME table' => [$query];
|
||||
|
||||
$query = 'ALTER TABLE myTable ' .
|
||||
'RENAME TO newTable, ' .
|
||||
'RENAME INDEX oldIndex TO newIndex, ' .
|
||||
'RENAME COLUMN b TO a, ' .
|
||||
'RENAME COLUMN a TO b';
|
||||
|
||||
yield 'Mixed RENAME table + RENAME INDEX + RENAME COLUMNS' => [$query];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideBuilderForRenameColumn
|
||||
*/
|
||||
public function testBuilderRenameColumn(string $query): void
|
||||
{
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals($query, $stmt->build());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Generator<string, array{string}>
|
||||
*/
|
||||
public static function provideBuilderForAlterRoutine(): Generator
|
||||
{
|
||||
$query = 'ALTER FUNCTION func_name COMMENT "test"';
|
||||
|
||||
yield 'Function with only comment' => [$query];
|
||||
|
||||
$query = 'ALTER FUNCTION func_name LANGUAGE SQL';
|
||||
|
||||
yield 'Function with only language' => [$query];
|
||||
|
||||
$query = 'ALTER FUNCTION func_name COMMENT "test" LANGUAGE SQL CONTAINS SQL SQL SECURITY DEFINER';
|
||||
|
||||
yield 'Function with all options combinations #1' => [$query];
|
||||
|
||||
$query = 'ALTER FUNCTION func_name COMMENT "test" LANGUAGE SQL NO SQL SQL SECURITY INVOKER';
|
||||
|
||||
yield 'Function with all options combinations #2' => [$query];
|
||||
|
||||
$query = 'ALTER FUNCTION func_name COMMENT "test" LANGUAGE SQL READS SQL DATA';
|
||||
|
||||
yield 'Function with all remaining options #1' => [$query];
|
||||
|
||||
$query = 'ALTER FUNCTION func_name COMMENT "test" LANGUAGE SQL MODIFIES SQL DATA';
|
||||
|
||||
yield 'Function with all remaining options #2' => [$query];
|
||||
|
||||
$query = 'ALTER PROCEDURE func_name COMMENT "test"';
|
||||
|
||||
yield 'Procedure with only comment' => [$query];
|
||||
|
||||
$query = 'ALTER PROCEDURE proc_name LANGUAGE SQL';
|
||||
|
||||
yield 'Procedure with only language' => [$query];
|
||||
|
||||
$query = 'ALTER PROCEDURE proc_name COMMENT "test" LANGUAGE SQL CONTAINS SQL SQL SECURITY DEFINER';
|
||||
|
||||
yield 'Procedure with all options combinations #1' => [$query];
|
||||
|
||||
$query = 'ALTER PROCEDURE proc_name COMMENT "test" LANGUAGE SQL NO SQL SQL SECURITY INVOKER';
|
||||
|
||||
yield 'Procedure with all options combinations #2' => [$query];
|
||||
|
||||
$query = 'ALTER PROCEDURE proc_name COMMENT "test" LANGUAGE SQL READS SQL DATA';
|
||||
|
||||
yield 'Procedure with all remaining options #1' => [$query];
|
||||
|
||||
$query = 'ALTER PROCEDURE proc_name COMMENT "test" LANGUAGE SQL MODIFIES SQL DATA';
|
||||
|
||||
yield 'Procedure with all remaining options #2' => [$query];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideBuilderForAlterRoutine
|
||||
*/
|
||||
public function testBuilderForAlterRoutine(string $query): void
|
||||
{
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals($query, $stmt->build());
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Builder;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class CallStatementTest extends TestCase
|
||||
{
|
||||
public function testBuilder(): void
|
||||
{
|
||||
$query = 'CALL foo()';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals($query, $stmt->build());
|
||||
}
|
||||
|
||||
public function testBuilderShort(): void
|
||||
{
|
||||
$query = 'CALL foo';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals($query . '()', $stmt->build());
|
||||
}
|
||||
|
||||
public function testBuilderWithDbName(): void
|
||||
{
|
||||
$query = 'CALL mydb.foo()';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals($query, $stmt->build());
|
||||
}
|
||||
|
||||
public function testBuilderWithDbNameShort(): void
|
||||
{
|
||||
$query = 'CALL mydb.foo';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals($query . '()', $stmt->build());
|
||||
}
|
||||
|
||||
public function testBuilderWithDbNameAndParams(): void
|
||||
{
|
||||
$query = 'CALL mydb.foo(@bar, @baz);';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals('CALL mydb.foo(@bar,@baz)', $stmt->build());
|
||||
}
|
||||
|
||||
public function testBuilderMultiCallsShort(): void
|
||||
{
|
||||
$query = 'call e;call f';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals('CALL e()', $stmt->build());
|
||||
$stmt = $parser->statements[1];
|
||||
|
||||
$this->assertEquals('CALL f()', $stmt->build());
|
||||
}
|
||||
|
||||
public function testBuilderMultiCalls(): void
|
||||
{
|
||||
$query = 'call e();call f';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals('CALL e()', $stmt->build());
|
||||
$stmt = $parser->statements[1];
|
||||
|
||||
$this->assertEquals('CALL f()', $stmt->build());
|
||||
}
|
||||
|
||||
public function testBuilderMultiCallsArgs(): void
|
||||
{
|
||||
$query = 'call e("foo");call f';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals('CALL e("foo")', $stmt->build());
|
||||
$stmt = $parser->statements[1];
|
||||
|
||||
$this->assertEquals('CALL f()', $stmt->build());
|
||||
}
|
||||
}
|
||||
+845
@@ -0,0 +1,845 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Builder;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\CreateDefinition;
|
||||
use PhpMyAdmin\SqlParser\Components\DataType;
|
||||
use PhpMyAdmin\SqlParser\Components\Expression;
|
||||
use PhpMyAdmin\SqlParser\Components\Key;
|
||||
use PhpMyAdmin\SqlParser\Components\OptionsArray;
|
||||
use PhpMyAdmin\SqlParser\Components\ParameterDefinition;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Statements\CreateStatement;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
use PhpMyAdmin\SqlParser\TokensList;
|
||||
|
||||
class CreateStatementTest extends TestCase
|
||||
{
|
||||
public function testBuilder(): void
|
||||
{
|
||||
$parser = new Parser('CREATE USER "jeffrey"@"localhost" IDENTIFIED BY "mypass"');
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
'CREATE USER "jeffrey"@"localhost" IDENTIFIED BY "mypass"',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderDatabase(): void
|
||||
{
|
||||
// CREATE DATABASE ...
|
||||
$parser = new Parser(
|
||||
'CREATE DATABASE `mydb` ' .
|
||||
'DEFAULT CHARACTER SET = utf8 DEFAULT COLLATE = utf8_general_ci'
|
||||
);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'CREATE DATABASE `mydb` ' .
|
||||
'DEFAULT CHARACTER SET=utf8 DEFAULT COLLATE=utf8_general_ci',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
// CREATE SCHEMA ...
|
||||
$parser = new Parser('CREATE SCHEMA `mydb` DEFAULT CHARACTER SET = utf8 DEFAULT COLLATE = utf8_general_ci');
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'CREATE SCHEMA `mydb` ' .
|
||||
'DEFAULT CHARACTER SET=utf8 DEFAULT COLLATE=utf8_general_ci',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderDefaultInt(): void
|
||||
{
|
||||
$parser = new Parser(
|
||||
'CREATE TABLE IF NOT EXISTS t1 (' .
|
||||
" c1 int(11) NOT NULL DEFAULT '0' COMMENT 'xxx'" .
|
||||
') ENGINE=MyISAM'
|
||||
);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
"CREATE TABLE IF NOT EXISTS t1 (\n" .
|
||||
" `c1` int(11) NOT NULL DEFAULT '0' COMMENT 'xxx'\n" .
|
||||
') ENGINE=MyISAM',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderWithComments(): void
|
||||
{
|
||||
$parser = new Parser('CREATE TABLE tab1 (`col1` TIMESTAMP /*!40100 DEFAULT NULL */)');
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
// TODO: fix with https://github.com/phpmyadmin/sql-parser/issues/256
|
||||
"CREATE TABLE tab1 (\n `col1` timestamp DEFAULT NULL\n) ",
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderCompressed(): void
|
||||
{
|
||||
$parser = new Parser('CREATE TABLE users ( user_id int ) PAGE_COMPRESSED=1 PAGE_COMPRESSION_LEVEL=9;');
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
"CREATE TABLE users (\n `user_id` int\n) PAGE_COMPRESSED=1 PAGE_COMPRESSION_LEVEL=9",
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderCollate(): void
|
||||
{
|
||||
$parser = new Parser(
|
||||
'CREATE TABLE IF NOT EXISTS t1 (' .
|
||||
" c1 varchar(11) NOT NULL DEFAULT '0' COLLATE 'utf8_czech_ci' COMMENT 'xxx'" .
|
||||
') ENGINE=MyISAM'
|
||||
);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
"CREATE TABLE IF NOT EXISTS t1 (\n" .
|
||||
" `c1` varchar(11) NOT NULL DEFAULT '0' COLLATE 'utf8_czech_ci' COMMENT 'xxx'\n" .
|
||||
') ENGINE=MyISAM',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderDefaultComment(): void
|
||||
{
|
||||
$parser = new Parser(
|
||||
'CREATE TABLE `wp_audio` (' .
|
||||
" `somedata` int(11) DEFAULT NULL COMMENT 'ma data', " .
|
||||
" `someinfo` int(11) DEFAULT NULL COMMENT 'ma info' " .
|
||||
' )'
|
||||
);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
"CREATE TABLE `wp_audio` (\n" .
|
||||
" `somedata` int(11) DEFAULT NULL COMMENT 'ma data',\n" .
|
||||
" `someinfo` int(11) DEFAULT NULL COMMENT 'ma info'\n" .
|
||||
') ',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderTable(): void
|
||||
{
|
||||
/* Assertion 1 */
|
||||
$stmt = new CreateStatement();
|
||||
|
||||
$stmt->name = new Expression('', 'test', '');
|
||||
$stmt->options = new OptionsArray(['TABLE']);
|
||||
$stmt->fields = [
|
||||
new CreateDefinition(
|
||||
'id',
|
||||
new OptionsArray(['NOT NULL', 'AUTO_INCREMENT']),
|
||||
new DataType('INT', [11], new OptionsArray(['UNSIGNED']))
|
||||
),
|
||||
new CreateDefinition(
|
||||
'',
|
||||
null,
|
||||
new Key('', [['name' => 'id']], 'PRIMARY KEY')
|
||||
),
|
||||
];
|
||||
|
||||
$this->assertEquals(
|
||||
"CREATE TABLE `test` (\n" .
|
||||
" `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,\n" .
|
||||
" PRIMARY KEY (`id`)\n" .
|
||||
') ',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 2 */
|
||||
$query =
|
||||
"CREATE TABLE `jos_core_acl_aro` (\n" .
|
||||
" `id` int(11) NOT NULL,\n" .
|
||||
" `section_value` varchar(240) NOT NULL DEFAULT '0',\n" .
|
||||
" `value` varchar(240) NOT NULL DEFAULT '',\n" .
|
||||
" `order_value` int(11) NOT NULL DEFAULT '0',\n" .
|
||||
" `name` varchar(255) NOT NULL DEFAULT '',\n" .
|
||||
" `hidden` int(11) NOT NULL DEFAULT '0',\n" .
|
||||
" PRIMARY KEY (`id`),\n" .
|
||||
" UNIQUE KEY `jos_section_value_value_aro` (`section_value`(100),`value`(15)) USING BTREE,\n" .
|
||||
" KEY `jos_gacl_hidden_aro` (`hidden`)\n" .
|
||||
') ENGINE=InnoDB DEFAULT CHARSET=latin1';
|
||||
$parser = new Parser($query);
|
||||
$this->assertEquals($query, $parser->statements[0]->build());
|
||||
|
||||
/* Assertion 3 */
|
||||
$query = 'CREATE TABLE `table_copy` LIKE `old_table`';
|
||||
$parser = new Parser($query);
|
||||
$this->assertEquals($query, $parser->statements[0]->build());
|
||||
|
||||
/* Assertion 4 */
|
||||
$query =
|
||||
"CREATE TABLE `aa` (\n" .
|
||||
" `id` int(11) NOT NULL,\n" .
|
||||
" `rTime` timestamp(3) NOT NULL DEFAULT '0000-00-00 00:00:00.000' ON UPDATE CURRENT_TIMESTAMP(3),\n" .
|
||||
" PRIMARY KEY (`id`)\n" .
|
||||
') ENGINE=InnoDB DEFAULT CHARSET=latin1';
|
||||
$parser = new Parser($query);
|
||||
$this->assertEquals($query, $parser->statements[0]->build());
|
||||
|
||||
/* Assertion 5 */
|
||||
$parser = new Parser(
|
||||
'CREATE table table_name WITH' .
|
||||
' cte (col1) AS ( SELECT 1 UNION ALL SELECT 2 )' .
|
||||
' SELECT col1 FROM cte'
|
||||
);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'CREATE TABLE table_name WITH' .
|
||||
' cte(col1) AS (SELECT 1 UNION ALL SELECT 2)' .
|
||||
' SELECT col1 FROM cte',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderPartitions(): void
|
||||
{
|
||||
/* Assertion 1 */
|
||||
$query = 'CREATE TABLE ts (' . "\n"
|
||||
. ' `id` int,' . "\n"
|
||||
. ' `purchased` date' . "\n"
|
||||
. ') ' . "\n"
|
||||
. 'PARTITION BY RANGE(YEAR(purchased))' . "\n"
|
||||
. 'PARTITIONS 3' . "\n"
|
||||
. 'SUBPARTITION BY HASH(TO_DAYS(purchased))' . "\n"
|
||||
. 'SUBPARTITIONS 2' . "\n"
|
||||
. '(' . "\n"
|
||||
. 'PARTITION p0 VALUES LESS THAN (1990) (' . "\n"
|
||||
. 'SUBPARTITION s0,' . "\n"
|
||||
. 'SUBPARTITION s1' . "\n"
|
||||
. '),' . "\n"
|
||||
. 'PARTITION p1 VALUES LESS THAN (2000) (' . "\n"
|
||||
. 'SUBPARTITION s2,' . "\n"
|
||||
. 'SUBPARTITION s3' . "\n"
|
||||
. '),' . "\n"
|
||||
. 'PARTITION p2 VALUES LESS THAN MAXVALUE (' . "\n"
|
||||
. 'SUBPARTITION s4,' . "\n"
|
||||
. 'SUBPARTITION s5' . "\n"
|
||||
. ')' . "\n"
|
||||
. ')';
|
||||
$parser = new Parser($query);
|
||||
$this->assertEquals($query, $parser->statements[0]->build());
|
||||
|
||||
/* Assertion 2 */
|
||||
$query = 'CREATE TABLE `pma_test` (' . "\n"
|
||||
. ' `test_id` int(32) NOT NULL,' . "\n"
|
||||
. ' `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP' . "\n"
|
||||
. ') ENGINE=InnoDB DEFAULT CHARSET=utf8' . "\n"
|
||||
. 'PARTITION BY RANGE (test_id)' . "\n"
|
||||
. '(' . "\n"
|
||||
. 'PARTITION p0 VALUES LESS THAN (250000) ENGINE=InnoDB,' . "\n"
|
||||
. 'PARTITION p1 VALUES LESS THAN (500000) ENGINE=InnoDB,' . "\n"
|
||||
. 'PARTITION p2 VALUES LESS THAN (750000) ENGINE=InnoDB,' . "\n"
|
||||
. 'PARTITION p3 VALUES LESS THAN (1000000) ENGINE=InnoDB,' . "\n"
|
||||
. 'PARTITION p4 VALUES LESS THAN (1250000) ENGINE=InnoDB,' . "\n"
|
||||
. 'PARTITION p5 VALUES LESS THAN (1500000) ENGINE=InnoDB,' . "\n"
|
||||
. 'PARTITION p6 VALUES LESS THAN MAXVALUE ENGINE=InnoDB' . "\n"
|
||||
. ')';
|
||||
$parser = new Parser($query);
|
||||
$this->assertEquals($query, $parser->statements[0]->build());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function partitionQueriesProvider(): array
|
||||
{
|
||||
$subPartitions = <<<EOT
|
||||
CREATE TABLE `ts` (
|
||||
`id` int(11) DEFAULT NULL,
|
||||
`purchased` date DEFAULT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
||||
PARTITION BY RANGE (YEAR(purchased))
|
||||
SUBPARTITION BY HASH (TO_DAYS(purchased))
|
||||
(
|
||||
PARTITION p0 VALUES LESS THAN (1990) (
|
||||
SUBPARTITION s0 ENGINE=InnoDB,
|
||||
SUBPARTITION s1 ENGINE=InnoDB
|
||||
),
|
||||
PARTITION p1 VALUES LESS THAN (2000) (
|
||||
SUBPARTITION s2 ENGINE=InnoDB,
|
||||
SUBPARTITION s3 ENGINE=InnoDB
|
||||
),
|
||||
PARTITION p2 VALUES LESS THAN MAXVALUE (
|
||||
SUBPARTITION s4 ENGINE=InnoDB,
|
||||
SUBPARTITION s5 ENGINE=InnoDB
|
||||
)
|
||||
)
|
||||
EOT;
|
||||
|
||||
$partitions = <<<EOT
|
||||
CREATE TABLE ptest (
|
||||
`event_date` date NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=DYNAMIC
|
||||
PARTITION BY HASH (TO_DAYS(event_date))
|
||||
(
|
||||
PARTITION p0 ENGINE=InnoDB,
|
||||
PARTITION p1 ENGINE=InnoDB,
|
||||
PARTITION p2 ENGINE=InnoDB,
|
||||
PARTITION p3 ENGINE=InnoDB,
|
||||
PARTITION p4 ENGINE=InnoDB
|
||||
)
|
||||
EOT;
|
||||
|
||||
return ['subpartitions' => [$subPartitions], 'partitions' => [$partitions]];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider partitionQueriesProvider
|
||||
*/
|
||||
public function testBuilderPartitionsEngine(string $query): void
|
||||
{
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals($query, $stmt->build());
|
||||
}
|
||||
|
||||
public function testBuilderView(): void
|
||||
{
|
||||
$parser = new Parser(
|
||||
'CREATE OR REPLACE VIEW xviewmytable AS SELECT mytable.id '
|
||||
. 'AS id, mytable.personid AS personid FROM mytable '
|
||||
. 'WHERE (mytable.birth > \'1990-01-19\') GROUP BY mytable.personid ;'
|
||||
);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'CREATE OR REPLACE VIEW xviewmytable AS SELECT mytable.id '
|
||||
. 'AS `id`, mytable.personid AS `personid` FROM mytable '
|
||||
. 'WHERE (mytable.birth > \'1990-01-19\') GROUP BY mytable.personid ',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
$parser = new Parser(
|
||||
'CREATE VIEW myView (vid, vfirstname) AS ' .
|
||||
'SELECT id, first_name FROM employee WHERE id = 1'
|
||||
);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'CREATE VIEW myView (vid, vfirstname) AS ' .
|
||||
'SELECT id, first_name FROM employee WHERE id = 1 ',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
$parser = new Parser(
|
||||
'CREATE OR REPLACE VIEW myView (vid, vfirstname) AS ' .
|
||||
'SELECT id, first_name FROM employee WHERE id = 1'
|
||||
);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'CREATE OR REPLACE VIEW myView (vid, vfirstname) AS ' .
|
||||
'SELECT id, first_name FROM employee WHERE id = 1 ',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
// Assert the builder can build wrong syntax select expressions
|
||||
$parser = new Parser(
|
||||
'CREATE OR REPLACE VIEW myView (vid, vfirstname) AS ' .
|
||||
'SELECT id, first_name, FROMzz employee WHERE id = 1'
|
||||
);
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
'CREATE OR REPLACE VIEW myView (vid, vfirstname) AS ' .
|
||||
'SELECT id, first_name, FROMzz employee WHERE id = 1 ',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
$parser = new Parser(
|
||||
'CREATE OR REPLACE VIEW myView (vid, vfirstname) AS ' .
|
||||
'SELECT id, first_name, FROMzz employee WHERE id = 1 ' .
|
||||
'UNION ' .
|
||||
'SELECT id, first_name, FROMzz employee WHERE id = 2 '
|
||||
);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'CREATE OR REPLACE VIEW myView (vid, vfirstname) AS ' .
|
||||
'SELECT id, first_name, FROMzz employee WHERE id = 1 ' .
|
||||
'UNION ' .
|
||||
'SELECT id, first_name, FROMzz employee WHERE id = 2 ',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
$parser = new Parser('CREATE VIEW `view_programlevelpartner` AS SELECT `p`.`country_id`'
|
||||
. 'AS `country_id` FROM `program_level_partner` `p` ORDER BY `p`.`id` asc');
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
'CREATE VIEW `view_programlevelpartner` AS SELECT `p`.`country_id`'
|
||||
. ' AS `country_id` FROM `program_level_partner` AS `p` ORDER BY `p`.`id` ASC ',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
$parser = new Parser('CREATE VIEW `view_zg_bycountry` AS '
|
||||
. 'SELECT `d`.`zg_id` FROM `view_zg_value` AS `d` GROUP BY `d`.`ind_id`;');
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
'CREATE VIEW `view_zg_bycountry` AS '
|
||||
. 'SELECT `d`.`zg_id` FROM `view_zg_value` AS `d` GROUP BY `d`.`ind_id` ',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
$parser = new Parser('CREATE view view_name AS WITH aa(col1)'
|
||||
. ' AS ( SELECT 1 UNION ALL SELECT 2 ) SELECT col1 FROM cte AS `d` ');
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
'CREATE view view_name AS WITH aa(col1)'
|
||||
. ' AS (SELECT 1 UNION ALL SELECT 2) SELECT col1 FROM cte AS `d` ',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderViewComplex(): void
|
||||
{
|
||||
$parser = new Parser(
|
||||
'CREATE VIEW withclause AS' . "\n"
|
||||
. "\n"
|
||||
. 'WITH cte AS (' . "\n"
|
||||
. 'SELECT p.name, p.shape' . "\n"
|
||||
. 'FROM gis_all as p' . "\n"
|
||||
. ')' . "\n"
|
||||
. "\n"
|
||||
. 'SELECT cte.*' . "\n"
|
||||
. 'FROM cte' . "\n"
|
||||
. 'CROSS JOIN gis_all;'
|
||||
);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'CREATE VIEW withclause AS '
|
||||
. 'WITH cte AS ('
|
||||
. 'SELECT p.name, p.shape '
|
||||
. 'FROM gis_all AS `p`'
|
||||
. ') '
|
||||
. 'SELECT cte.* '
|
||||
. 'FROM cte '
|
||||
. 'CROSS JOIN gis_all ',
|
||||
$stmt->build()
|
||||
);
|
||||
$parser = new Parser(
|
||||
'CREATE VIEW withclause2 AS' . "\n"
|
||||
. "\n"
|
||||
. 'WITH cte AS (' . "\n"
|
||||
. "\t" . 'SELECT p.name, p.shape' . "\n"
|
||||
. "\t" . 'FROM gis_all as p' . "\n"
|
||||
. '), cte2 AS (' . "\n"
|
||||
. "\t" . 'SELECT p.name as n2, p.shape as sh2' . "\n"
|
||||
. "\t" . 'FROM gis_all as p' . "\n"
|
||||
. ')' . "\n"
|
||||
. "\n"
|
||||
. 'SELECT cte.*,cte2.*' . "\n"
|
||||
. 'FROM cte,cte2' . "\n"
|
||||
. 'CROSS JOIN gis_all;'
|
||||
);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'CREATE VIEW withclause2 AS '
|
||||
. 'WITH cte AS ('
|
||||
. 'SELECT p.name, p.shape'
|
||||
. ' FROM gis_all AS `p`'
|
||||
. '), cte2 AS ('
|
||||
. 'SELECT p.name AS `n2`, p.shape AS `sh2`'
|
||||
. ' FROM gis_all AS `p`'
|
||||
. ')'
|
||||
. ' SELECT cte.*, cte2.* '
|
||||
. 'FROM cte, cte2'
|
||||
. ' CROSS JOIN gis_all ',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderCreateProcedure(): void
|
||||
{
|
||||
$parser = new Parser(
|
||||
'CREATE DEFINER=`root`@`%`'
|
||||
. ' PROCEDURE `test2`(IN `_var` INT) DETERMINISTIC'
|
||||
. ' MODIFIES SQL DATA SELECT _var'
|
||||
);
|
||||
|
||||
/** @var CreateStatement $stmt */
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertSame(
|
||||
'CREATE DEFINER=`root`@`%`'
|
||||
. ' PROCEDURE `test2` (IN `_var` INT) DETERMINISTIC'
|
||||
. ' MODIFIES SQL DATA SELECT _var',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
$parser = new Parser(
|
||||
'CREATE DEFINER=`root`@`%`'
|
||||
. ' PROCEDURE `test2`(IN `_var` INT) NOT DETERMINISTIC NO SQL'
|
||||
. ' SQL SECURITY INVOKER NO SQL SQL SECURITY INVOKER SELECT _var'
|
||||
);
|
||||
|
||||
/** @var CreateStatement $stmt */
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertSame(
|
||||
'CREATE DEFINER=`root`@`%`'
|
||||
. ' PROCEDURE `test2` (IN `_var` INT) NOT DETERMINISTIC NO SQL'
|
||||
. ' SQL SECURITY INVOKER NO SQL SQL SECURITY INVOKER SELECT _var',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
$this->assertFalse($stmt->entityOptions->isEmpty());
|
||||
$this->assertFalse($stmt->options->isEmpty());
|
||||
$this->assertSame(
|
||||
'DEFINER=`root`@`%` PROCEDURE',
|
||||
$stmt->options->__toString()
|
||||
);
|
||||
|
||||
$this->assertSame(
|
||||
'`test2`',
|
||||
$stmt->name->__toString()
|
||||
);
|
||||
|
||||
$this->assertSame(
|
||||
'(IN `_var` INT)',
|
||||
ParameterDefinition::build($stmt->parameters)
|
||||
);
|
||||
|
||||
$this->assertSame(
|
||||
'NOT DETERMINISTIC NO SQL SQL SECURITY INVOKER NO SQL SQL SECURITY INVOKER',
|
||||
$stmt->entityOptions->__toString()
|
||||
);
|
||||
|
||||
$this->assertSame(
|
||||
'SELECT _var',
|
||||
TokensList::build($stmt->body)
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderCreateFunction(): void
|
||||
{
|
||||
$parser = new Parser(
|
||||
'DELIMITER $$' . "\n"
|
||||
. 'CREATE DEFINER=`root`@`localhost`'
|
||||
. ' FUNCTION `inventory_in_stock`(`p_inventory_id` INT) RETURNS tinyint(1)'
|
||||
. ' READS SQL DATA'
|
||||
. ' COMMENT \'My best function written by a friend\'\'s friend\''
|
||||
. ' BEGIN' . "\n"
|
||||
. ' DECLARE v_rentals INT;' . "\n"
|
||||
. ' DECLARE v_out INT;' . "\n"
|
||||
. "\n"
|
||||
. ' ' . "\n"
|
||||
. ' ' . "\n"
|
||||
. "\n"
|
||||
. ' SELECT COUNT(*) INTO v_rentals' . "\n"
|
||||
. ' FROM rental' . "\n"
|
||||
. ' WHERE inventory_id = p_inventory_id;' . "\n"
|
||||
. "\n"
|
||||
. ' IF v_rentals = 0 THEN' . "\n"
|
||||
. ' RETURN TRUE;' . "\n"
|
||||
. ' END IF;' . "\n"
|
||||
. "\n"
|
||||
. ' SELECT COUNT(rental_id) INTO v_out' . "\n"
|
||||
. ' FROM inventory LEFT JOIN rental USING(inventory_id)' . "\n"
|
||||
. ' WHERE inventory.inventory_id = p_inventory_id' . "\n"
|
||||
. ' AND rental.return_date IS NULL;' . "\n"
|
||||
. "\n"
|
||||
. ' IF v_out > 0 THEN' . "\n"
|
||||
. ' RETURN FALSE;' . "\n"
|
||||
. ' ELSE' . "\n"
|
||||
. ' RETURN TRUE;' . "\n"
|
||||
. ' END IF;' . "\n"
|
||||
. 'END'
|
||||
);
|
||||
|
||||
/** @var CreateStatement $stmt */
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertSame(
|
||||
'CREATE DEFINER=`root`@`localhost`'
|
||||
. ' FUNCTION `inventory_in_stock` (`p_inventory_id` INT) RETURNS TINYINT(1)'
|
||||
. ' READS SQL DATA'
|
||||
. ' COMMENT \'My best function written by a friend\'\'s friend\''
|
||||
. ' BEGIN' . "\n"
|
||||
. ' DECLARE v_rentals INT;' . "\n"
|
||||
. ' DECLARE v_out INT;' . "\n"
|
||||
. "\n"
|
||||
. ' ' . "\n"
|
||||
. ' ' . "\n"
|
||||
. "\n"
|
||||
. ' SELECT COUNT(*) INTO v_rentals' . "\n"
|
||||
. ' FROM rental' . "\n"
|
||||
. ' WHERE inventory_id = p_inventory_id;' . "\n"
|
||||
. "\n"
|
||||
. ' IF v_rentals = 0 THEN' . "\n"
|
||||
. ' RETURN TRUE;' . "\n"
|
||||
. ' END IF;' . "\n"
|
||||
. "\n"
|
||||
. ' SELECT COUNT(rental_id) INTO v_out' . "\n"
|
||||
. ' FROM inventory LEFT JOIN rental USING(inventory_id)' . "\n"
|
||||
. ' WHERE inventory.inventory_id = p_inventory_id' . "\n"
|
||||
. ' AND rental.return_date IS NULL;' . "\n"
|
||||
. "\n"
|
||||
. ' IF v_out > 0 THEN' . "\n"
|
||||
. ' RETURN FALSE;' . "\n"
|
||||
. ' ELSE' . "\n"
|
||||
. ' RETURN TRUE;' . "\n"
|
||||
. ' END IF;' . "\n"
|
||||
. 'END',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
$this->assertFalse($stmt->entityOptions->isEmpty());
|
||||
$this->assertFalse($stmt->options->isEmpty());
|
||||
|
||||
$this->assertSame(
|
||||
'DEFINER=`root`@`localhost` FUNCTION',
|
||||
$stmt->options->__toString()
|
||||
);
|
||||
|
||||
$this->assertSame(
|
||||
'`inventory_in_stock`',
|
||||
$stmt->name->__toString()
|
||||
);
|
||||
|
||||
$this->assertSame(
|
||||
'(`p_inventory_id` INT)',
|
||||
ParameterDefinition::build($stmt->parameters)
|
||||
);
|
||||
|
||||
$this->assertSame(
|
||||
'READS SQL DATA COMMENT \'My best function written by a friend\'\'s friend\'',
|
||||
$stmt->entityOptions->__toString()
|
||||
);
|
||||
|
||||
$this->assertSame(
|
||||
'BEGIN' . "\n"
|
||||
. ' DECLARE v_rentals INT;' . "\n"
|
||||
. ' DECLARE v_out INT;' . "\n"
|
||||
. "\n"
|
||||
. ' ' . "\n"
|
||||
. ' ' . "\n"
|
||||
. "\n"
|
||||
. ' SELECT COUNT(*) INTO v_rentals' . "\n"
|
||||
. ' FROM rental' . "\n"
|
||||
. ' WHERE inventory_id = p_inventory_id;' . "\n"
|
||||
. "\n"
|
||||
. ' IF v_rentals = 0 THEN' . "\n"
|
||||
. ' RETURN TRUE;' . "\n"
|
||||
. ' END IF;' . "\n"
|
||||
. "\n"
|
||||
. ' SELECT COUNT(rental_id) INTO v_out' . "\n"
|
||||
. ' FROM inventory LEFT JOIN rental USING(inventory_id)' . "\n"
|
||||
. ' WHERE inventory.inventory_id = p_inventory_id' . "\n"
|
||||
. ' AND rental.return_date IS NULL;' . "\n"
|
||||
. "\n"
|
||||
. ' IF v_out > 0 THEN' . "\n"
|
||||
. ' RETURN FALSE;' . "\n"
|
||||
. ' ELSE' . "\n"
|
||||
. ' RETURN TRUE;' . "\n"
|
||||
. ' END IF;' . "\n"
|
||||
. 'END',
|
||||
TokensList::build($stmt->body)
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderTrigger(): void
|
||||
{
|
||||
$stmt = new CreateStatement();
|
||||
|
||||
$stmt->options = new OptionsArray(['TRIGGER']);
|
||||
$stmt->name = new Expression('ins_sum');
|
||||
$stmt->entityOptions = new OptionsArray(['BEFORE', 'INSERT']);
|
||||
$stmt->table = new Expression('account');
|
||||
$stmt->body = 'SET @sum = @sum + NEW.amount';
|
||||
|
||||
$this->assertEquals(
|
||||
'CREATE TRIGGER ins_sum BEFORE INSERT ON account ' .
|
||||
'FOR EACH ROW SET @sum = @sum + NEW.amount',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderRoutine(): void
|
||||
{
|
||||
$parser = new Parser(
|
||||
'DELIMITER $$' . "\n" .
|
||||
'CREATE FUNCTION test (IN `i` INT) RETURNS VARCHAR ' .
|
||||
'BEGIN ' .
|
||||
'DECLARE name VARCHAR DEFAULT ""; ' .
|
||||
'SELECT name INTO name FROM employees WHERE id = i; ' .
|
||||
'RETURN name; ' .
|
||||
'END'
|
||||
);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'CREATE FUNCTION test (IN `i` INT) RETURNS VARCHAR ' .
|
||||
' BEGIN ' .
|
||||
'DECLARE name VARCHAR DEFAULT ""; ' .
|
||||
'SELECT name INTO name FROM employees WHERE id = i; ' .
|
||||
'RETURN name; ' .
|
||||
'END',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuildSelect(): void
|
||||
{
|
||||
$parser = new Parser('CREATE TABLE new_tbl SELECT * FROM orig_tbl');
|
||||
$this->assertEquals(
|
||||
'CREATE TABLE new_tbl SELECT * FROM orig_tbl',
|
||||
$parser->statements[0]->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuildCreateTableSortedIndex(): void
|
||||
{
|
||||
$parser = new Parser(
|
||||
<<<'SQL'
|
||||
CREATE TABLE `entries` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`fk_ug_id` int(11) DEFAULT NULL,
|
||||
`amount` decimal(10,2) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `entries__ug` (`fk_ug_id` DESC),
|
||||
KEY `entries__ug2` (`fk_ug_id` ASC),
|
||||
KEY `33` (`id` ASC, `fk_ug_id` DESC)
|
||||
) /*!50100 TABLESPACE `innodb_system` */ ENGINE=InnoDB AUTO_INCREMENT=4465 DEFAULT CHARSET=utf8
|
||||
SQL
|
||||
);
|
||||
|
||||
/** @var CreateStatement $stmt */
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$tableBody = <<<'SQL'
|
||||
(
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`fk_ug_id` int(11) DEFAULT NULL,
|
||||
`amount` decimal(10,2) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `entries__ug` (`fk_ug_id` DESC),
|
||||
KEY `entries__ug2` (`fk_ug_id` ASC),
|
||||
KEY `33` (`id` ASC,`fk_ug_id` DESC)
|
||||
)
|
||||
SQL;
|
||||
|
||||
$this->assertEquals(
|
||||
$tableBody,
|
||||
CreateDefinition::build($stmt->fields)
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
'CREATE TABLE `entries` '
|
||||
. $tableBody
|
||||
. ' ENGINE=InnoDB AUTO_INCREMENT=4465 DEFAULT CHARSET=utf8 TABLESPACE `innodb_system`',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuildCreateTableComplexIndexes(): void
|
||||
{
|
||||
// phpcs:disable Generic.Files.LineLength.TooLong
|
||||
$parser = new Parser(
|
||||
<<<'SQL'
|
||||
CREATE TABLE `page_rebuild_control` (
|
||||
`proc_row_number` int DEFAULT NULL,
|
||||
`place_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`place_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`place_type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`waterway_id` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`cache_updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`place_active` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`alias_type` int NOT NULL DEFAULT '0',
|
||||
`status` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`time_taken` float DEFAULT NULL,
|
||||
PRIMARY KEY (`place_id`,`place_type`) USING BTREE,
|
||||
KEY `place_type_idx` (`place_type`(10)),
|
||||
KEY `cached_time_idx` (`cache_updated`),
|
||||
KEY `active_idx` (`place_active`),
|
||||
KEY `status_idx` (`status`),
|
||||
KEY `waterway_idx` (`waterway_id`),
|
||||
KEY `time_taken_idx` (`time_taken`),
|
||||
KEY `updated_tz_ind3` (
|
||||
-- my expression
|
||||
(convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'GB'))
|
||||
) COMMENT 'foo\'s',
|
||||
KEY `updated_tz_ind_two_indexes_commented` (
|
||||
-- first expression
|
||||
(
|
||||
convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'GB')
|
||||
)
|
||||
,
|
||||
-- second expression
|
||||
(
|
||||
convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'FR')
|
||||
)
|
||||
)
|
||||
-- and now some options
|
||||
COMMENT 'haha, this is a complex and indented case',
|
||||
KEY `alias_type_idx` (`alias_type`),
|
||||
KEY `updated_tz_ind2` ((convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'GB'))) COMMENT 'foo\'s',
|
||||
KEY `updated_tz_ind_two_indexes` ((convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'GB')), (convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'FR'))) COMMENT 'bar\'s',
|
||||
KEY `updated_tz_ind` ((convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'GB')))
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
SQL
|
||||
);
|
||||
// phpcs:enable
|
||||
|
||||
/** @var CreateStatement $stmt */
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
// phpcs:disable Generic.Files.LineLength.TooLong
|
||||
$tableBody = <<<'SQL'
|
||||
(
|
||||
`proc_row_number` int DEFAULT NULL,
|
||||
`place_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`place_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`place_type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`waterway_id` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`cache_updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`place_active` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`alias_type` int NOT NULL DEFAULT '0',
|
||||
`status` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`time_taken` float DEFAULT NULL,
|
||||
PRIMARY KEY (`place_id`,`place_type`) USING BTREE,
|
||||
KEY `place_type_idx` (`place_type`(10)),
|
||||
KEY `cached_time_idx` (`cache_updated`),
|
||||
KEY `active_idx` (`place_active`),
|
||||
KEY `status_idx` (`status`),
|
||||
KEY `waterway_idx` (`waterway_id`),
|
||||
KEY `time_taken_idx` (`time_taken`),
|
||||
KEY `updated_tz_ind3` ((convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'GB'))) COMMENT 'foo\'s',
|
||||
KEY `updated_tz_ind_two_indexes_commented` ((
|
||||
convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'GB')
|
||||
), (
|
||||
convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'FR')
|
||||
)) COMMENT 'haha, this is a complex and indented case',
|
||||
KEY `alias_type_idx` (`alias_type`),
|
||||
KEY `updated_tz_ind2` ((convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'GB'))) COMMENT 'foo\'s',
|
||||
KEY `updated_tz_ind_two_indexes` ((convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'GB')), (convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'FR'))) COMMENT 'bar\'s',
|
||||
KEY `updated_tz_ind` ((convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'GB')))
|
||||
)
|
||||
SQL;
|
||||
// phpcs:enable
|
||||
|
||||
$this->assertEquals(
|
||||
$tableBody,
|
||||
CreateDefinition::build($stmt->fields)
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
'CREATE TABLE `page_rebuild_control` '
|
||||
. $tableBody
|
||||
. ' ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Builder;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class DeleteStatementTest extends TestCase
|
||||
{
|
||||
public function testBuilderSingleTable(): void
|
||||
{
|
||||
/* Assertion 1 */
|
||||
$query = 'DELETE IGNORE FROM t1';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals($query, $stmt->build());
|
||||
|
||||
/* Assertion 2 */
|
||||
$query = 'DELETE IGNORE FROM t1 WHERE 1=1';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals($query, $stmt->build());
|
||||
|
||||
/* Assertion 3 */
|
||||
$query = 'DELETE IGNORE FROM t1 WHERE 1=1 ORDER BY id ASC';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals($query, $stmt->build());
|
||||
|
||||
/* Assertion 4 */
|
||||
$query = 'DELETE IGNORE FROM t1 WHERE 1=1 ORDER BY id ASC LIMIT 0, 25';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals($query, $stmt->build());
|
||||
|
||||
/* Assertion 5 */
|
||||
$query = 'DELETE IGNORE FROM t1';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals($query, $stmt->build());
|
||||
|
||||
/* Assertion 6 */
|
||||
$query = 'DELETE LOW_PRIORITY FROM `test`.users '
|
||||
. 'WHERE `id`<3 AND (username="Dan" OR username="Paul") ORDER BY id ASC';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals($query, $stmt->build());
|
||||
}
|
||||
|
||||
public function testBuilderMultiTable(): void
|
||||
{
|
||||
/* Assertion 1 */
|
||||
$query = 'DELETE QUICK table1, table2.* FROM table1 AS `t1`, table2 AS `t2`';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals($query, $stmt->build());
|
||||
|
||||
/* Assertion 2 */
|
||||
$query = 'DELETE QUICK table1, table2.* FROM table1 AS `t1`, table2 AS `t2` WHERE 1=1';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals($query, $stmt->build());
|
||||
|
||||
/* Assertion 3 */
|
||||
$query = 'DELETE QUICK FROM table1, table2.* USING table1 AS `t1`, table2 AS `t2` WHERE 1=1';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals($query, $stmt->build());
|
||||
|
||||
/* Assertion 4 */
|
||||
$query = 'DELETE LOW_PRIORITY t1, t2 FROM t1 INNER JOIN t2 '
|
||||
. 'INNER JOIN t3 WHERE t1.id=t2.id AND t2.id=t3.id';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals($query, $stmt->build());
|
||||
}
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Builder;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class ExplainStatementTest extends TestCase
|
||||
{
|
||||
public function testBuilder(): void
|
||||
{
|
||||
/* Assertion 1 */
|
||||
$query = 'EXPLAIN SELECT * FROM test;';
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
'EXPLAIN SELECT * FROM test',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 2 */
|
||||
$query = 'EXPLAIN ANALYZE SELECT * FROM tablename;';
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
'EXPLAIN ANALYZE SELECT * FROM tablename',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 3 */
|
||||
$query = 'DESC ANALYZE SELECT * FROM tablename;';
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
'DESC ANALYZE SELECT * FROM tablename',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 4 */
|
||||
$query = 'ANALYZE SELECT * FROM tablename;';
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
'ANALYZE SELECT * FROM tablename',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 5 */
|
||||
$query = 'DESCRIBE tablename;';
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
'DESCRIBE `tablename`',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 6 */
|
||||
$query = 'DESC FOR CONNECTION 458';
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
'DESC FOR CONNECTION 458',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 7 */
|
||||
$query = 'EXPLAIN FORMAT=TREE SELECT * FROM db;';
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
'EXPLAIN FORMAT=TREE SELECT * FROM db',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 8 */
|
||||
$query = 'DESCRIBE tablename colname;';
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
'DESCRIBE `tablename` `colname`',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 9 */
|
||||
$query = 'DESCRIBE tablename \'col%me\';';
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
'DESCRIBE `tablename` `col%me`',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 9 */
|
||||
$query = 'DESCRIBE db.tablename \'col%me\';';
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
'DESCRIBE `db`.`tablename` `col%me`',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Builder;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class InsertStatementTest extends TestCase
|
||||
{
|
||||
public function testBuilder(): void
|
||||
{
|
||||
/* Assertion 1 */
|
||||
$parser = new Parser('INSERT INTO tbl(`col1`, `col2`, `col3`) VALUES (1, "str", 3.14)');
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
'INSERT INTO tbl(`col1`, `col2`, `col3`) VALUES (1, "str", 3.14)',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 2 */
|
||||
/* Reserved keywords (with backquotes as field name) */
|
||||
$parser = new Parser('INSERT INTO tbl(`order`) VALUES (1)');
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
'INSERT INTO tbl(`order`) VALUES (1)',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 3 */
|
||||
/* INSERT ... SET ... */
|
||||
$parser = new Parser('INSERT INTO tbl SET FOO = 1');
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
'INSERT INTO tbl SET FOO = 1',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 4 */
|
||||
/* INSERT ... SELECT ... */
|
||||
$parser = new Parser('INSERT INTO tbl SELECT * FROM bar');
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
'INSERT INTO tbl SELECT * FROM bar',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 5 */
|
||||
/* INSERT ... ON DUPLICATE KEY UPDATE ... */
|
||||
$parser = new Parser('INSERT INTO tbl SELECT * FROM bar ON DUPLICATE KEY UPDATE baz = 1');
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
'INSERT INTO tbl SELECT * FROM bar ON DUPLICATE KEY UPDATE baz = 1',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 6 */
|
||||
/* INSERT [OPTIONS] INTO ... */
|
||||
$parser = new Parser('INSERT DELAYED IGNORE INTO tbl SELECT * FROM bar');
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
'INSERT DELAYED IGNORE INTO tbl SELECT * FROM bar',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Builder;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class LoadStatementTest extends TestCase
|
||||
{
|
||||
public function testBuilder(): void
|
||||
{
|
||||
/* Assertion 1 */
|
||||
$query = 'LOAD DATA CONCURRENT INFILE '
|
||||
. '\'employee1.txt\' INTO TABLE employee';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'LOAD DATA CONCURRENT INFILE '
|
||||
. '\'employee1.txt\' INTO TABLE employee',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 2 */
|
||||
$query = 'LOAD DATA INFILE \'/tmp/test.txt\' '
|
||||
. 'INTO TABLE test FIELDS TERMINATED BY '
|
||||
. '\',\' IGNORE 1 LINES';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'LOAD DATA INFILE \'/tmp/test.txt\' '
|
||||
. 'INTO TABLE test FIELDS TERMINATED BY '
|
||||
. '\',\' IGNORE 1 LINES',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 3 */
|
||||
$query = 'LOAD DATA INFILE \'employee3.txt\' '
|
||||
. 'INTO TABLE employee FIELDS TERMINATED BY '
|
||||
. '\',\' ENCLOSED BY \'"\'';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'LOAD DATA INFILE \'employee3.txt\' '
|
||||
. 'INTO TABLE employee FIELDS TERMINATED BY '
|
||||
. '\',\' ENCLOSED BY \'"\'',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 4 */
|
||||
$query = 'LOAD DATA INFILE \'/tmp/test.txt\' IGNORE '
|
||||
. 'INTO TABLE test '
|
||||
. 'CHARACTER SET \'utf8\' '
|
||||
. 'COLUMNS TERMINATED BY \',\' '
|
||||
. 'LINES TERMINATED BY \';\' '
|
||||
. 'IGNORE 1 LINES (col1, col2) SET @a = 1';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'LOAD DATA INFILE \'/tmp/test.txt\' IGNORE '
|
||||
. 'INTO TABLE test '
|
||||
. 'CHARACTER SET \'utf8\' '
|
||||
. 'COLUMNS TERMINATED BY \',\' '
|
||||
. 'LINES TERMINATED BY \';\' '
|
||||
. 'IGNORE 1 LINES (col1, col2) SET @a = 1',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 5 */
|
||||
$query = 'LOAD DATA INFILE \'/tmp/test.txt\' REPLACE '
|
||||
. 'INTO TABLE test COLUMNS TERMINATED BY \',\' IGNORE 1 ROWS';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'LOAD DATA INFILE \'/tmp/test.txt\' REPLACE '
|
||||
. 'INTO TABLE test COLUMNS TERMINATED BY \',\' IGNORE 1 ROWS',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 6 */
|
||||
$query = 'LOAD DATA INFILE \'/tmp/test.txt\' IGNORE '
|
||||
. 'INTO TABLE test PARTITION (p0, p1, p2) CHARACTER SET \'utf8\' '
|
||||
. 'COLUMNS TERMINATED BY \',\' LINES TERMINATED BY \';\' '
|
||||
. 'IGNORE 1 LINES (col1, col2) SET @a = 1';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'LOAD DATA INFILE \'/tmp/test.txt\' IGNORE '
|
||||
. 'INTO TABLE test PARTITION (p0, p1, p2) CHARACTER SET \'utf8\' '
|
||||
. 'COLUMNS TERMINATED BY \',\' LINES TERMINATED BY \';\' '
|
||||
. 'IGNORE 1 LINES (col1, col2) SET @a = 1',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Builder;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class LockStatementTest extends TestCase
|
||||
{
|
||||
public function testBuilder(): void
|
||||
{
|
||||
/* Assertion 1 */
|
||||
$query = 'LOCK TABLES table1 AS `t1` READ LOCAL';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals($query, $stmt->build());
|
||||
|
||||
/* Assertion 2 */
|
||||
$query = 'LOCK TABLES table1 AS `t1` READ';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals($query, $stmt->build());
|
||||
|
||||
/* Assertion 3 */
|
||||
$query = 'LOCK TABLES table1 AS `t1` LOW_PRIORITY WRITE';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals($query, $stmt->build());
|
||||
|
||||
/* Assertion 4 */
|
||||
$query = 'LOCK TABLES table1 AS `t1` WRITE';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals($query, $stmt->build());
|
||||
|
||||
/* Assertion 5 */
|
||||
$query = 'LOCK TABLES table1 AS `t1` READ LOCAL, table2 AS `t2` WRITE';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals($query, $stmt->build());
|
||||
|
||||
/* Assertion 6 */
|
||||
$query = 'LOCK TABLES table1 READ LOCAL, table2 AS `t2` WRITE';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals($query, $stmt->build());
|
||||
|
||||
/* Assertion 7 */
|
||||
$query = 'UNLOCK TABLES';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals($query, $stmt->build());
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Builder;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class PurgeStatementTest extends TestCase
|
||||
{
|
||||
public function testBuilder(): void
|
||||
{
|
||||
$query = 'PURGE BINARY LOGS TO \'mysql-bin.010\'';
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals($query, $stmt->build());
|
||||
|
||||
$query = 'PURGE BINARY LOGS BEFORE \'2008-04-02 22:46:26\'';
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals($query, $stmt->build());
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Builder;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class RenameStatementTest extends TestCase
|
||||
{
|
||||
public function testBuilder(): void
|
||||
{
|
||||
$query = 'RENAME TABLE old_table TO new_table';
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
$query,
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
$query = 'RENAME TABLE current_db.tbl_name TO other_db.tbl_name';
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
$query,
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
$query = 'RENAME TABLE old_table1 TO new_table1, old_table2 TO new_table2, old_table3 TO new_table3';
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
$query,
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Builder;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class ReplaceStatementTest extends TestCase
|
||||
{
|
||||
public function testBuilder(): void
|
||||
{
|
||||
$parser = new Parser('REPLACE INTO tbl(col1, col2, col3) VALUES (1, "str", 3.14)');
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
'REPLACE INTO tbl(`col1`, `col2`, `col3`) VALUES (1, "str", 3.14)',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderSet(): void
|
||||
{
|
||||
$parser = new Parser('REPLACE INTO tbl(col1, col2, col3) SET col1=1, col2="str", col3=3.14');
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
'REPLACE INTO tbl(`col1`, `col2`, `col3`) SET col1 = 1, col2 = "str", col3 = 3.14',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderSelect(): void
|
||||
{
|
||||
$parser = new Parser('REPLACE INTO tbl(col1, col2, col3) SELECT col1, col2, col3 FROM tbl2');
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
'REPLACE INTO tbl(`col1`, `col2`, `col3`) SELECT col1, col2, col3 FROM tbl2',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderSelectDelayed(): void
|
||||
{
|
||||
$parser = new Parser('REPLACE DELAYED INTO tbl(col1, col2, col3) SELECT col1, col2, col3 FROM tbl2');
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
'REPLACE DELAYED INTO tbl(`col1`, `col2`, `col3`) SELECT col1, col2, col3 FROM tbl2',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
}
|
||||
+356
@@ -0,0 +1,356 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Builder;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class SelectStatementTest extends TestCase
|
||||
{
|
||||
public function testBuilder(): void
|
||||
{
|
||||
$query = 'SELECT * FROM t1 LEFT JOIN (t2, t3, t4) '
|
||||
. 'ON (t2.a=t1.a AND t3.b=t1.b AND t4.c=t1.c)';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'SELECT * FROM t1 LEFT JOIN (t2, t3, t4) '
|
||||
. 'ON (t2.a=t1.a AND t3.b=t1.b AND t4.c=t1.c)',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
$parser = new Parser('SELECT NULL IS NULL');
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals('SELECT NULL IS NULL', $stmt->build());
|
||||
|
||||
$parser = new Parser('SELECT NOT 1');
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals('SELECT NOT 1', $stmt->build());
|
||||
|
||||
$parser = new Parser('SELECT 1 BETWEEN 0 AND 2');
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals('SELECT 1 BETWEEN 0 AND 2', $stmt->build());
|
||||
|
||||
$parser = new Parser("SELECT 'a' NOT REGEXP '^[a-d]'");
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals("SELECT 'a' NOT REGEXP '^[a-d]'", $stmt->build());
|
||||
|
||||
$parser = new Parser("SELECT 'a' RLIKE 'a'");
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals("SELECT 'a' RLIKE 'a'", $stmt->build());
|
||||
}
|
||||
|
||||
public function testBuilderUnion(): void
|
||||
{
|
||||
$parser = new Parser('SELECT 1 UNION SELECT 2');
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'SELECT 1 UNION SELECT 2',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderWithIsNull(): void
|
||||
{
|
||||
$parser = new Parser('SELECT `test3`.`t1` is not null AS `is_not_null` FROM `test3` ;');
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals('SELECT `test3`.`t1` is not null AS `is_not_null` FROM `test3`', $stmt->build());
|
||||
|
||||
$parser = new Parser('SELECT test3.t1 is null AS `col1` FROM test3');
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals('SELECT test3.t1 is null AS `col1` FROM test3', $stmt->build());
|
||||
}
|
||||
|
||||
public function testBuilderOrderByNull(): void
|
||||
{
|
||||
$query = 'SELECT * FROM some_table ORDER BY some_col IS NULL DESC;';
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals('SELECT * FROM some_table ORDER BY some_col IS NULL DESC', $stmt->build());
|
||||
|
||||
$query = 'SELECT * FROM some_table ORDER BY some_col IS NOT NULL;';
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals('SELECT * FROM some_table ORDER BY some_col IS NOT NULL ASC', $stmt->build());
|
||||
}
|
||||
|
||||
public function testBuilderAlias(): void
|
||||
{
|
||||
$parser = new Parser(
|
||||
'SELECT sgu.id, sgu.email_address FROM `sf_guard_user` sgu '
|
||||
. 'RIGHT JOIN `student_course_booking` scb ON sgu.id = scb.user_id '
|
||||
. 'WHERE `has_found_course` = \'1\' GROUP BY sgu.id '
|
||||
. 'ORDER BY scb.id DESC LIMIT 0,300'
|
||||
);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'SELECT sgu.id, sgu.email_address FROM `sf_guard_user` AS `sgu` '
|
||||
. 'RIGHT JOIN `student_course_booking` AS `scb` ON sgu.id = scb.user_id '
|
||||
. 'WHERE `has_found_course` = \'1\' GROUP BY sgu.id '
|
||||
. 'ORDER BY scb.id DESC LIMIT 0, 300',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderAliasOrder(): void
|
||||
{
|
||||
$parser = new Parser(
|
||||
'SELECT sgu.id, sgu.email_address FROM `sf_guard_user` sgu '
|
||||
. 'RIGHT JOIN `student_course_booking` scb ON sgu.id = scb.user_id '
|
||||
. 'WHERE `has_found_course` = \'1\' GROUP BY sgu.id '
|
||||
. 'ORDER BY scb.id LIMIT 0,300'
|
||||
);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'SELECT sgu.id, sgu.email_address FROM `sf_guard_user` AS `sgu` '
|
||||
. 'RIGHT JOIN `student_course_booking` AS `scb` ON sgu.id = scb.user_id '
|
||||
. 'WHERE `has_found_course` = \'1\' GROUP BY sgu.id '
|
||||
. 'ORDER BY scb.id ASC LIMIT 0, 300',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderAliasOrderMultiple(): void
|
||||
{
|
||||
$parser = new Parser(
|
||||
'SELECT sgu.id, sgu.email_address FROM `sf_guard_user` sgu '
|
||||
. 'RIGHT JOIN `student_course_booking` scb ON sgu.id = scb.user_id '
|
||||
. 'WHERE `has_found_course` = \'1\' GROUP BY sgu.id '
|
||||
. 'ORDER BY scb.id DESC, scb.order LIMIT 0,300'
|
||||
);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'SELECT sgu.id, sgu.email_address FROM `sf_guard_user` AS `sgu` '
|
||||
. 'RIGHT JOIN `student_course_booking` AS `scb` ON sgu.id = scb.user_id '
|
||||
. 'WHERE `has_found_course` = \'1\' GROUP BY sgu.id '
|
||||
. 'ORDER BY scb.id DESC, scb.order ASC LIMIT 0, 300',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderAliasOrderMultipleFunctions(): void
|
||||
{
|
||||
$parser = new Parser(
|
||||
'SELECT sgu.id, sgu.email_address FROM `sf_guard_user` sgu '
|
||||
. 'RIGHT JOIN `student_course_booking` scb ON sgu.id = scb.user_id '
|
||||
. 'WHERE `has_found_course` = \'1\' GROUP BY sgu.id '
|
||||
. 'ORDER BY scb.id DESC, YEAR(scb.dob) LIMIT 0,300'
|
||||
);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'SELECT sgu.id, sgu.email_address FROM `sf_guard_user` AS `sgu` '
|
||||
. 'RIGHT JOIN `student_course_booking` AS `scb` ON sgu.id = scb.user_id '
|
||||
. 'WHERE `has_found_course` = \'1\' GROUP BY sgu.id '
|
||||
. 'ORDER BY scb.id DESC, YEAR(scb.dob) ASC LIMIT 0, 300',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderAliasGroupByMultipleFunctions(): void
|
||||
{
|
||||
$parser = new Parser(
|
||||
'SELECT sgu.id, sgu.email_address FROM `sf_guard_user` sgu '
|
||||
. 'RIGHT JOIN `student_course_booking` scb ON sgu.id = scb.user_id '
|
||||
. 'WHERE `has_found_course` = \'1\' '
|
||||
. 'GROUP BY scb.id, YEAR(scb.dob) LIMIT 0,300'
|
||||
);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'SELECT sgu.id, sgu.email_address FROM `sf_guard_user` AS `sgu` '
|
||||
. 'RIGHT JOIN `student_course_booking` AS `scb` ON sgu.id = scb.user_id '
|
||||
. 'WHERE `has_found_course` = \'1\' '
|
||||
. 'GROUP BY scb.id, YEAR(scb.dob) LIMIT 0, 300',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderAliasGroupByMultipleFunctionsOrderRemoved(): void
|
||||
{
|
||||
$parser = new Parser(
|
||||
'SELECT sgu.id, sgu.email_address FROM `sf_guard_user` sgu '
|
||||
. 'RIGHT JOIN `student_course_booking` scb ON sgu.id = scb.user_id '
|
||||
. 'WHERE `has_found_course` = \'1\' '
|
||||
. 'GROUP BY scb.id ASC, YEAR(scb.dob) DESC LIMIT 0,300'
|
||||
);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
// The order is not kept, is this an expected behavior ?
|
||||
// Ref: 4af06d24b041e499fb0e75ab3a98caf9a91700ef
|
||||
// Issue: #154
|
||||
$this->assertEquals(
|
||||
'SELECT sgu.id, sgu.email_address FROM `sf_guard_user` AS `sgu` '
|
||||
. 'RIGHT JOIN `student_course_booking` AS `scb` ON sgu.id = scb.user_id '
|
||||
. 'WHERE `has_found_course` = \'1\' '
|
||||
. 'GROUP BY scb.id, YEAR(scb.dob) LIMIT 0, 300',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderAliasOrderCase(): void
|
||||
{
|
||||
$parser = new Parser(
|
||||
'SELECT * FROM `world_borders` ORDER BY CASE '
|
||||
. 'WHEN REGION = 2 THEN 99 '
|
||||
. 'WHEN REGION > 3 THEN REGION+1 '
|
||||
. 'ELSE 100 END LIMIT 0,300'
|
||||
);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'SELECT * FROM `world_borders` ORDER BY CASE '
|
||||
. 'WHEN REGION = 2 THEN 99 '
|
||||
. 'WHEN REGION > 3 THEN REGION+1 '
|
||||
. 'ELSE 100 END ASC LIMIT 0, 300',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderAliasGroupByCase(): void
|
||||
{
|
||||
$parser = new Parser(
|
||||
'SELECT * FROM `world_borders` GROUP BY CASE '
|
||||
. 'WHEN REGION = 2 THEN 99 '
|
||||
. 'WHEN REGION > 3 THEN REGION+1 '
|
||||
. 'ELSE 100 END LIMIT 0,300'
|
||||
);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'SELECT * FROM `world_borders` GROUP BY CASE '
|
||||
. 'WHEN REGION = 2 THEN 99 '
|
||||
. 'WHEN REGION > 3 THEN REGION+1 '
|
||||
. 'ELSE 100 END LIMIT 0, 300',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderEndOptions(): void
|
||||
{
|
||||
/* Assertion 1 */
|
||||
$query = 'SELECT pid, name2 FROM tablename WHERE pid = 20 FOR UPDATE';
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
$query,
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 2 */
|
||||
$query = 'SELECT pid, name2 FROM tablename WHERE pid = 20 LOCK IN SHARE MODE';
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
$query,
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderIntoOptions(): void
|
||||
{
|
||||
/* Assertion 1 */
|
||||
$query = 'SELECT a, b, a+b INTO OUTFILE "/tmp/result.txt"'
|
||||
. ' COLUMNS TERMINATED BY \',\' OPTIONALLY ENCLOSED BY \'"\''
|
||||
. ' LINES TERMINATED BY \'\n\''
|
||||
. ' FROM test_table';
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
$query,
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderGroupBy(): void
|
||||
{
|
||||
$query = 'SELECT COUNT(CustomerID), Country FROM Customers GROUP BY Country';
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
$query,
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderGroupByWithRollup(): void
|
||||
{
|
||||
$query = 'SELECT year FROM movies GROUP BY year WITH ROLLUP';
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
$query,
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderGroupByMultipleColumnsWithRollup(): void
|
||||
{
|
||||
$query = 'SELECT title, year FROM movies GROUP BY title, year WITH ROLLUP';
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
$query,
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderGroupByWithRollupWithOtherClauses(): void
|
||||
{
|
||||
$query = 'SELECT year FROM movies GROUP BY year WITH ROLLUP ORDER BY year ASC LIMIT 0, 5';
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
$query,
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderIndexHint(): void
|
||||
{
|
||||
$query = 'SELECT * FROM address FORCE INDEX (idx_fk_city_id) IGNORE KEY FOR GROUP BY (a, b,c) WHERE city_id<0';
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
$query,
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuilderSurroundedByParanthesisWithLimit(): void
|
||||
{
|
||||
$query = '(SELECT first_name FROM `actor` LIMIT 1, 2)';
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'SELECT first_name FROM `actor` LIMIT 1, 2',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
}
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Builder;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class SetStatementTest extends TestCase
|
||||
{
|
||||
public function testBuilderView(): void
|
||||
{
|
||||
/* Assertion 1 */
|
||||
$query = 'SET CHARACTER SET \'utf8\'';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
$query,
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 2 */
|
||||
$query = 'SET CHARSET \'utf8\'';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
$query,
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 3 */
|
||||
$query = 'SET NAMES \'utf8\'';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
$query,
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 4 */
|
||||
$query = 'SET NAMES \'utf8\' COLLATE \'utf8_general_ci\'';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'SET NAMES \'utf8\' COLLATE \'utf8_general_ci\'',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 5 */
|
||||
$query = 'SET NAMES \'utf8\' DEFAULT';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'SET NAMES \'utf8\' DEFAULT',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 6 */
|
||||
$query = 'SET sql_mode = \'TRADITIONAL\'';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'SET sql_mode = \'TRADITIONAL\'',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 7 */
|
||||
$query = 'SET SESSION sql_mode = \'TRADITIONAL\'';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'SET SESSION sql_mode = \'TRADITIONAL\'',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 8 */
|
||||
$query = 'SET GLOBAL sql_mode = \'TRADITIONAL\'';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'SET GLOBAL sql_mode = \'TRADITIONAL\'',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 9 */
|
||||
$query = 'SET @@SESSION sql_mode = \'TRADITIONAL\'';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'SET SESSION sql_mode = \'TRADITIONAL\'',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 10 */
|
||||
$query = 'SET @@GLOBAL sql_mode = \'TRADITIONAL\'';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'SET GLOBAL sql_mode = \'TRADITIONAL\'',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 11 */
|
||||
$query = 'SET @@sql_mode = \'TRADITIONAL\'';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'SET @@sql_mode = \'TRADITIONAL\'',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 12 */
|
||||
$query = 'SET PERSIST sql_mode = \'TRADITIONAL\'';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'SET PERSIST sql_mode = \'TRADITIONAL\'',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 13 */
|
||||
$query = 'SET PERSIST_ONLY sql_mode = \'TRADITIONAL\'';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'SET PERSIST_ONLY sql_mode = \'TRADITIONAL\'',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 14 */
|
||||
$query = 'SET @@PERSIST sql_mode = \'TRADITIONAL\'';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'SET PERSIST sql_mode = \'TRADITIONAL\'',
|
||||
$stmt->build()
|
||||
);
|
||||
|
||||
/* Assertion 15 */
|
||||
$query = 'SET @@PERSIST_ONLY sql_mode = \'TRADITIONAL\'';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'SET PERSIST_ONLY sql_mode = \'TRADITIONAL\'',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Builder;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\Condition;
|
||||
use PhpMyAdmin\SqlParser\Components\Expression;
|
||||
use PhpMyAdmin\SqlParser\Components\Limit;
|
||||
use PhpMyAdmin\SqlParser\Components\OptionsArray;
|
||||
use PhpMyAdmin\SqlParser\Statements\SelectStatement;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class StatementTest extends TestCase
|
||||
{
|
||||
public function testBuilder(): void
|
||||
{
|
||||
$stmt = new SelectStatement();
|
||||
|
||||
$stmt->options = new OptionsArray(['DISTINCT']);
|
||||
|
||||
$stmt->expr[] = new Expression('sakila', 'film', 'film_id', 'fid');
|
||||
$stmt->expr[] = new Expression('COUNT(film_id)');
|
||||
|
||||
$stmt->from[] = new Expression('', 'film', '');
|
||||
$stmt->from[] = new Expression('', 'actor', '');
|
||||
|
||||
$stmt->where[] = new Condition('film_id > 10');
|
||||
$stmt->where[] = new Condition('OR');
|
||||
$stmt->where[] = new Condition('actor.age > 25');
|
||||
|
||||
$stmt->limit = new Limit(1, 10);
|
||||
|
||||
$this->assertEquals(
|
||||
'SELECT DISTINCT `sakila`.`film`.`film_id` AS `fid`, COUNT(film_id) ' .
|
||||
'FROM `film`, `actor` ' .
|
||||
'WHERE film_id > 10 OR actor.age > 25 ' .
|
||||
'LIMIT 10, 1',
|
||||
(string) $stmt
|
||||
);
|
||||
}
|
||||
}
|
||||
Vendored
+30
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Builder;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class TransactionStatementTest extends TestCase
|
||||
{
|
||||
public function testBuilder(): void
|
||||
{
|
||||
$query = 'START TRANSACTION;' .
|
||||
'SELECT @A:=SUM(salary) FROM table1 WHERE type=1;' .
|
||||
'UPDATE table2 SET summary=@A WHERE type=1;' .
|
||||
'COMMIT;';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals(
|
||||
'START TRANSACTION;' .
|
||||
'SELECT @A:=SUM(salary) FROM table1 WHERE type=1;' .
|
||||
'UPDATE table2 SET summary = @A WHERE type=1;' .
|
||||
'COMMIT',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Builder;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class TruncateStatementTest extends TestCase
|
||||
{
|
||||
public function testBuilder(): void
|
||||
{
|
||||
$query = 'TRUNCATE TABLE mytable;';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals($query, $stmt->build());
|
||||
}
|
||||
|
||||
public function testBuilderDbtable(): void
|
||||
{
|
||||
$query = 'TRUNCATE TABLE mydb.mytable;';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals($query, $stmt->build());
|
||||
}
|
||||
|
||||
public function testBuilderDbtableBackQuotes(): void
|
||||
{
|
||||
$query = 'TRUNCATE TABLE `mydb`.`mytable`;';
|
||||
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$this->assertEquals($query, $stmt->build());
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Builder;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class UpdateStatementTest extends TestCase
|
||||
{
|
||||
public function testBuilder(): void
|
||||
{
|
||||
/* Assertion 1 */
|
||||
$parser = new Parser(
|
||||
'update user u left join user_detail ud on u.id = ud.user_id set ud.ip =\'33\' where u.id = 1'
|
||||
);
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
'UPDATE user AS `u` LEFT JOIN user_detail AS `ud` ON u.id = ud.user_id SET ud.ip = \'33\' WHERE u.id = 1',
|
||||
$stmt->build()
|
||||
);
|
||||
/* Assertion 2 */
|
||||
$parser = new Parser('update user u join user_detail ud on u.id = ud.user_id set ud.ip =\'33\' where u.id = 1');
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
'UPDATE user AS `u` JOIN user_detail AS `ud` ON u.id = ud.user_id SET ud.ip = \'33\' WHERE u.id = 1',
|
||||
$stmt->build()
|
||||
);
|
||||
/* Assertion 3 */
|
||||
$parser = new Parser(
|
||||
'update user u inner join user_detail ud on u.id = ud.user_id set ud.ip =\'33\' where u.id = 1'
|
||||
);
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
'UPDATE user AS `u` INNER JOIN user_detail AS `ud` ON u.id = ud.user_id SET ud.ip = \'33\' WHERE u.id = 1',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Components;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\Array2d;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class Array2dTest extends TestCase
|
||||
{
|
||||
public function testParse(): void
|
||||
{
|
||||
$parser = new Parser();
|
||||
$arrays = Array2d::parse($parser, $this->getTokensList('(1, 2) +'));
|
||||
$this->assertEquals(
|
||||
[
|
||||
1,
|
||||
2,
|
||||
],
|
||||
$arrays[0]->values
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuild(): void
|
||||
{
|
||||
$arrays = Array2d::parse(new Parser(), $this->getTokensList('(1, 2), (3, 4), (5, 6)'));
|
||||
$this->assertEquals(
|
||||
'(1, 2), (3, 4), (5, 6)',
|
||||
Array2d::build($arrays)
|
||||
);
|
||||
}
|
||||
|
||||
public function testParseErr1(): void
|
||||
{
|
||||
$parser = new Parser();
|
||||
Array2d::parse($parser, $this->getTokensList('(1, 2 +'));
|
||||
$this->assertCount(1, $parser->errors);
|
||||
$this->assertEquals('A closing bracket was expected.', $parser->errors[0]->getMessage());
|
||||
}
|
||||
|
||||
public function testParseErr2(): void
|
||||
{
|
||||
$parser = new Parser();
|
||||
Array2d::parse($parser, $this->getTokensList('(1, 2 TABLE'));
|
||||
$this->assertCount(1, $parser->errors);
|
||||
$this->assertEquals('A closing bracket was expected.', $parser->errors[0]->getMessage());
|
||||
}
|
||||
|
||||
public function testParseErr3(): void
|
||||
{
|
||||
$parser = new Parser();
|
||||
Array2d::parse($parser, $this->getTokensList(')'));
|
||||
$this->assertCount(1, $parser->errors);
|
||||
$this->assertEquals(
|
||||
'An opening bracket followed by a set of values was expected.',
|
||||
$parser->errors[0]->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
public function testParseErr4(): void
|
||||
{
|
||||
$parser = new Parser();
|
||||
Array2d::parse($parser, $this->getTokensList('TABLE'));
|
||||
$this->assertCount(1, $parser->errors);
|
||||
$this->assertEquals(
|
||||
'An opening bracket followed by a set of values was expected.',
|
||||
$parser->errors[0]->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
public function testParseErr5(): void
|
||||
{
|
||||
$parser = new Parser();
|
||||
Array2d::parse($parser, $this->getTokensList('(1, 2),'));
|
||||
$this->assertCount(1, $parser->errors);
|
||||
$this->assertEquals(
|
||||
'An opening bracket followed by a set of values was expected.',
|
||||
$parser->errors[0]->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
public function testParseErr6(): void
|
||||
{
|
||||
$parser = new Parser();
|
||||
Array2d::parse($parser, $this->getTokensList('(1, 2),(3)'));
|
||||
$this->assertCount(1, $parser->errors);
|
||||
$this->assertEquals(
|
||||
'2 values were expected, but found 1.',
|
||||
$parser->errors[0]->getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Components;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\ArrayObj;
|
||||
use PhpMyAdmin\SqlParser\Components\Expression;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class ArrayObjTest extends TestCase
|
||||
{
|
||||
public function testBuildRaw(): void
|
||||
{
|
||||
$component = new ArrayObj(['a', 'b'], []);
|
||||
$this->assertEquals('(a, b)', ArrayObj::build($component));
|
||||
}
|
||||
|
||||
public function testBuildValues(): void
|
||||
{
|
||||
$component = new ArrayObj([], ['a', 'b']);
|
||||
$this->assertEquals('(a, b)', ArrayObj::build($component));
|
||||
}
|
||||
|
||||
public function testParseType(): void
|
||||
{
|
||||
$components = ArrayObj::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList('(1 + 2, 3 + 4)'),
|
||||
[
|
||||
'type' => Expression::class,
|
||||
'typeOptions' => ['breakOnParentheses' => true],
|
||||
]
|
||||
);
|
||||
$this->assertInstanceOf(Expression::class, $components[0]);
|
||||
$this->assertInstanceOf(Expression::class, $components[1]);
|
||||
$this->assertEquals($components[0]->expr, '1 + 2');
|
||||
$this->assertEquals($components[1]->expr, '3 + 4');
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider parseProvider
|
||||
*/
|
||||
public function testParse(string $test): void
|
||||
{
|
||||
$this->runParserTest($test);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function parseProvider(): array
|
||||
{
|
||||
return [
|
||||
['parser/parseArrayErr1'],
|
||||
['parser/parseArrayErr3'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Components;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\CaseExpression;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class CaseExpressionTest extends TestCase
|
||||
{
|
||||
public function testParseBuild(): void
|
||||
{
|
||||
$caseExprQuery = 'case 1 when 1 then "Some" else "Other" end';
|
||||
$component = CaseExpression::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList($caseExprQuery)
|
||||
);
|
||||
$this->assertEquals(
|
||||
CaseExpression::build($component),
|
||||
'CASE 1 WHEN 1 THEN "Some" ELSE "Other" END'
|
||||
);
|
||||
}
|
||||
|
||||
public function testParseBuild2(): void
|
||||
{
|
||||
$caseExprQuery = 'case when 1=1 then "India" else "Other" end';
|
||||
$component = CaseExpression::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList($caseExprQuery)
|
||||
);
|
||||
$this->assertEquals(
|
||||
CaseExpression::build($component),
|
||||
'CASE WHEN 1=1 THEN "India" ELSE "Other" END'
|
||||
);
|
||||
}
|
||||
|
||||
public function testParseBuild3(): void
|
||||
{
|
||||
$caseExprQuery = 'case 1 when 1 then "Some" '
|
||||
. 'when 2 then "SomeOther" else "Other" end';
|
||||
$component = CaseExpression::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList($caseExprQuery)
|
||||
);
|
||||
$this->assertEquals(
|
||||
CaseExpression::build($component),
|
||||
'CASE 1 WHEN 1 THEN "Some" WHEN 2 THEN "SomeOther" ELSE "Other" END'
|
||||
);
|
||||
}
|
||||
|
||||
public function testParseBuild4(): void
|
||||
{
|
||||
$caseExprQuery = 'case 1 when 1 then "Some" '
|
||||
. 'when 2 then "SomeOther" end';
|
||||
$component = CaseExpression::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList($caseExprQuery)
|
||||
);
|
||||
$this->assertEquals(
|
||||
CaseExpression::build($component),
|
||||
'CASE 1 WHEN 1 THEN "Some" WHEN 2 THEN "SomeOther" END'
|
||||
);
|
||||
}
|
||||
|
||||
public function testParseBuild5(): void
|
||||
{
|
||||
$caseExprQuery = 'case when 1=1 then "Some" '
|
||||
. 'when 1=2 then "SomeOther" else "Other" end';
|
||||
$component = CaseExpression::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList($caseExprQuery)
|
||||
);
|
||||
$this->assertEquals(
|
||||
CaseExpression::build($component),
|
||||
'CASE WHEN 1=1 THEN "Some" WHEN 1=2 THEN "SomeOther" ELSE "Other" END'
|
||||
);
|
||||
}
|
||||
|
||||
public function testParseBuild6(): void
|
||||
{
|
||||
$caseExprQuery = 'case when 1=1 then "Some" '
|
||||
. 'when 1=2 then "SomeOther" end';
|
||||
$component = CaseExpression::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList($caseExprQuery)
|
||||
);
|
||||
$this->assertEquals(
|
||||
CaseExpression::build($component),
|
||||
'CASE WHEN 1=1 THEN "Some" WHEN 1=2 THEN "SomeOther" END'
|
||||
);
|
||||
}
|
||||
|
||||
public function testParseBuild7(): void
|
||||
{
|
||||
$caseExprQuery = 'case when 1=1 then "Some" '
|
||||
. 'when 1=2 then "SomeOther" end AS foo';
|
||||
$component = CaseExpression::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList($caseExprQuery)
|
||||
);
|
||||
$this->assertEquals(
|
||||
CaseExpression::build($component),
|
||||
'CASE WHEN 1=1 THEN "Some" WHEN 1=2 THEN "SomeOther" END AS `foo`'
|
||||
);
|
||||
}
|
||||
|
||||
public function testParseBuild8(): void
|
||||
{
|
||||
$caseExprQuery = 'case when 1=1 then "Some" '
|
||||
. 'when 1=2 then "SomeOther" end foo';
|
||||
$component = CaseExpression::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList($caseExprQuery)
|
||||
);
|
||||
$this->assertEquals(
|
||||
CaseExpression::build($component),
|
||||
'CASE WHEN 1=1 THEN "Some" WHEN 1=2 THEN "SomeOther" END AS `foo`'
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuildWithIncompleteCaseExpression(): void
|
||||
{
|
||||
$incompleteCaseExpressionComponent = new CaseExpression();
|
||||
$this->assertEquals('CASE END', CaseExpression::build($incompleteCaseExpressionComponent));
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Components;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Component;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
use PhpMyAdmin\SqlParser\TokensList;
|
||||
use Throwable;
|
||||
|
||||
class ComponentTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function testParse(): void
|
||||
{
|
||||
$this->expectExceptionMessage('Not implemented yet.');
|
||||
$this->expectException(Throwable::class);
|
||||
Component::parse(new Parser(), new TokensList());
|
||||
}
|
||||
|
||||
public function testBuild(): void
|
||||
{
|
||||
$this->expectExceptionMessage('Not implemented yet.');
|
||||
$this->expectException(Throwable::class);
|
||||
Component::build(null);
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Components;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\Condition;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class ConditionTest extends TestCase
|
||||
{
|
||||
public function testParse(): void
|
||||
{
|
||||
$component = Condition::parse(new Parser(), $this->getTokensList('/* id = */ id = 10'));
|
||||
$this->assertEquals($component[0]->expr, 'id = 10');
|
||||
}
|
||||
|
||||
public function testParseBetween(): void
|
||||
{
|
||||
$component = Condition::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList('(id BETWEEN 10 AND 20) OR (id BETWEEN 30 AND 40)')
|
||||
);
|
||||
$this->assertEquals($component[0]->expr, '(id BETWEEN 10 AND 20)');
|
||||
$this->assertEquals($component[1]->expr, 'OR');
|
||||
$this->assertEquals($component[2]->expr, '(id BETWEEN 30 AND 40)');
|
||||
}
|
||||
|
||||
public function testParseAnd(): void
|
||||
{
|
||||
$component = Condition::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList("`col` LIKE 'AND'")
|
||||
);
|
||||
$this->assertEquals(
|
||||
"`col` LIKE 'AND'",
|
||||
Condition::build($component)
|
||||
);
|
||||
}
|
||||
}
|
||||
Vendored
+162
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Components;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\CreateDefinition;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Statements\CreateStatement;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class CreateDefinitionTest extends TestCase
|
||||
{
|
||||
public function testParse(): void
|
||||
{
|
||||
$component = CreateDefinition::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList('(str TEXT, FULLTEXT INDEX indx (str))')
|
||||
);
|
||||
$this->assertEquals('str', $component[0]->name);
|
||||
$this->assertEquals('FULLTEXT INDEX', $component[1]->key->type);
|
||||
$this->assertEquals('indx', $component[1]->key->name);
|
||||
$this->assertEquals('FULLTEXT INDEX `indx` (`str`)', (string) $component[1]);
|
||||
}
|
||||
|
||||
public function testParse2(): void
|
||||
{
|
||||
$component = CreateDefinition::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList('(str TEXT NOT NULL INVISIBLE)')
|
||||
);
|
||||
$this->assertEquals('str', $component[0]->name);
|
||||
$this->assertEquals('TEXT', $component[0]->type->name);
|
||||
$this->assertTrue($component[0]->options->has('INVISIBLE'));
|
||||
$this->assertTrue($component[0]->options->has('NOT NULL'));
|
||||
}
|
||||
|
||||
public function testParseErr1(): void
|
||||
{
|
||||
$parser = new Parser();
|
||||
$component = CreateDefinition::parse(
|
||||
$parser,
|
||||
$this->getTokensList('(str TEXT, FULLTEXT INDEX indx (str)')
|
||||
);
|
||||
$this->assertCount(2, $component);
|
||||
|
||||
$this->assertEquals(
|
||||
'A closing bracket was expected.',
|
||||
$parser->errors[0]->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
public function testParseErr2(): void
|
||||
{
|
||||
$parser = new Parser();
|
||||
CreateDefinition::parse(
|
||||
$parser,
|
||||
$this->getTokensList(')')
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
'An opening bracket was expected.',
|
||||
$parser->errors[0]->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuild(): void
|
||||
{
|
||||
$parser = new Parser(
|
||||
'CREATE TABLE `payment` (' .
|
||||
'-- snippet' . "\n" .
|
||||
'`customer_id` smallint(5) unsigned NOT NULL,' .
|
||||
'CONSTRAINT `fk_payment_customer` FOREIGN KEY (`customer_id`) ' .
|
||||
'REFERENCES `customer` (`customer_id`) ON UPDATE CASCADE' .
|
||||
') ENGINE=InnoDB"'
|
||||
);
|
||||
$this->assertInstanceOf(CreateStatement::class, $parser->statements[0]);
|
||||
$this->assertEquals(
|
||||
'CONSTRAINT `fk_payment_customer` FOREIGN KEY (`customer_id`) ' .
|
||||
'REFERENCES `customer` (`customer_id`) ON UPDATE CASCADE',
|
||||
CreateDefinition::build($parser->statements[0]->fields[1])
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuild2(): void
|
||||
{
|
||||
$parser = new Parser(
|
||||
'CREATE TABLE `payment` (' .
|
||||
'-- snippet' . "\n" .
|
||||
'`customer_id` smallint(5) unsigned NOT NULL,' .
|
||||
'`customer_data` longtext CHARACTER SET utf8mb4 CHARSET utf8mb4_bin NOT NULL ' .
|
||||
'CHECK (json_valid(customer_data)),CONSTRAINT `fk_payment_customer` FOREIGN KEY ' .
|
||||
'(`customer_id`) REFERENCES `customer` (`customer_id`) ON UPDATE CASCADE' .
|
||||
') ENGINE=InnoDB"'
|
||||
);
|
||||
$this->assertInstanceOf(CreateStatement::class, $parser->statements[0]);
|
||||
$this->assertEquals(
|
||||
'CONSTRAINT `fk_payment_customer` FOREIGN KEY (`customer_id`) ' .
|
||||
'REFERENCES `customer` (`customer_id`) ON UPDATE CASCADE',
|
||||
CreateDefinition::build($parser->statements[0]->fields[2])
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuild3(): void
|
||||
{
|
||||
$parser = new Parser(
|
||||
'DROP TABLE IF EXISTS `searches`;'
|
||||
. 'CREATE TABLE `searches` ('
|
||||
. ' `id` int(10) unsigned NOT NULL AUTO_INCREMENT,'
|
||||
. ' `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,'
|
||||
. ' `public_name` varchar(120) COLLATE utf8_unicode_ci NOT NULL,'
|
||||
. ' `group_id` smallint(5) unsigned NOT NULL DEFAULT \'0\','
|
||||
. ' `shortdesc` tinytext COLLATE utf8_unicode_ci,'
|
||||
. ' `show_separators` tinyint(1) NOT NULL DEFAULT \'0\','
|
||||
. ' `show_separators_two` tinyint(1) NOT NULL DEFAULT FALSE,'
|
||||
. ' `deleted` tinyint(1) NOT NULL DEFAULT \'0\','
|
||||
. ' PRIMARY KEY (`id`)'
|
||||
. ') ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ;'
|
||||
. ''
|
||||
. 'ALTER TABLE `searches` ADD `admins_only` BOOLEAN NOT NULL DEFAULT FALSE AFTER `show_separators`;'
|
||||
);
|
||||
$this->assertInstanceOf(CreateStatement::class, $parser->statements[1]);
|
||||
$this->assertEquals(
|
||||
'`public_name` varchar(120) COLLATE utf8_unicode_ci NOT NULL',
|
||||
CreateDefinition::build($parser->statements[1]->fields[2])
|
||||
);
|
||||
$this->assertEquals(
|
||||
'`show_separators` tinyint(1) NOT NULL DEFAULT \'0\'',
|
||||
CreateDefinition::build($parser->statements[1]->fields[5])
|
||||
);
|
||||
$this->assertEquals(
|
||||
'`show_separators_two` tinyint(1) NOT NULL DEFAULT FALSE',
|
||||
CreateDefinition::build($parser->statements[1]->fields[6])
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuildWithInvisibleKeyword(): void
|
||||
{
|
||||
$parser = new Parser(
|
||||
'CREATE TABLE `payment` (' .
|
||||
'-- snippet' . "\n" .
|
||||
'`customer_id` smallint(5) unsigned NOT NULL INVISIBLE,' .
|
||||
'`customer_data` longtext CHARACTER SET utf8mb4 CHARSET utf8mb4_bin NOT NULL ' .
|
||||
'CHECK (json_valid(customer_data)),CONSTRAINT `fk_payment_customer` FOREIGN KEY ' .
|
||||
'(`customer_id`) REFERENCES `customer` (`customer_id`) ON UPDATE CASCADE' .
|
||||
') ENGINE=InnoDB"'
|
||||
);
|
||||
$this->assertInstanceOf(CreateStatement::class, $parser->statements[0]);
|
||||
$this->assertEquals(
|
||||
'`customer_id` smallint(5) UNSIGNED NOT NULL INVISIBLE',
|
||||
CreateDefinition::build($parser->statements[0]->fields[0])
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuildWithCompressed(): void
|
||||
{
|
||||
$query = 'CREATE TABLE `user` ( `message2` TEXT COMPRESSED )';
|
||||
$parser = new Parser($query);
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals("CREATE TABLE `user` (\n `message2` text COMPRESSED\n) ", $stmt->build());
|
||||
}
|
||||
}
|
||||
Vendored
+54
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Components;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\ExpressionArray;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class ExpressionArrayTest extends TestCase
|
||||
{
|
||||
public function testParse(): void
|
||||
{
|
||||
$component = ExpressionArray::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList('(expr)'),
|
||||
['breakOnParentheses' => true]
|
||||
);
|
||||
$this->assertEquals([], $component);
|
||||
}
|
||||
|
||||
public function testParse2(): void
|
||||
{
|
||||
$component = ExpressionArray::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList('(expr) +'),
|
||||
['parenthesesDelimited' => true]
|
||||
);
|
||||
$this->assertCount(1, $component);
|
||||
$this->assertEquals('(expr)', $component[0]->expr);
|
||||
}
|
||||
|
||||
public function testParseWithCommentsNoOptions(): void
|
||||
{
|
||||
$component = ExpressionArray::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList('(expr) -- comment ?')
|
||||
);
|
||||
$this->assertCount(1, $component);
|
||||
$this->assertEquals('(expr)', $component[0]->expr);
|
||||
}
|
||||
|
||||
public function testParseWithCommentsAndOptions(): void
|
||||
{
|
||||
$component = ExpressionArray::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList('(expr -- comment ?)'),
|
||||
['parenthesesDelimited' => true]
|
||||
);
|
||||
$this->assertCount(1, $component);
|
||||
$this->assertEquals('(expr', $component[0]->expr);
|
||||
}
|
||||
}
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Components;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\Expression;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class ExpressionTest extends TestCase
|
||||
{
|
||||
public function testParse(): void
|
||||
{
|
||||
$component = Expression::parse(new Parser(), $this->getTokensList('IF(film_id > 0, film_id, film_id)'));
|
||||
$this->assertEquals($component->expr, 'IF(film_id > 0, film_id, film_id)');
|
||||
}
|
||||
|
||||
public function testParse2(): void
|
||||
{
|
||||
$component = Expression::parse(new Parser(), $this->getTokensList('col`test`'));
|
||||
$this->assertEquals($component->expr, 'col');
|
||||
}
|
||||
|
||||
public function testParse3(): void
|
||||
{
|
||||
$component = Expression::parse(new Parser(), $this->getTokensList('col xx'));
|
||||
$this->assertEquals($component->alias, 'xx');
|
||||
|
||||
$component = Expression::parse(new Parser(), $this->getTokensList('col y'));
|
||||
$this->assertEquals($component->alias, 'y');
|
||||
|
||||
$component = Expression::parse(new Parser(), $this->getTokensList('avg.col FROM (SELECT ev.col FROM ev)'));
|
||||
$this->assertEquals($component->table, 'avg');
|
||||
$this->assertEquals($component->expr, 'avg.col');
|
||||
|
||||
$component = Expression::parse(new Parser(), $this->getTokensList('x.id FROM (SELECT a.id FROM a) x'));
|
||||
$this->assertEquals($component->table, 'x');
|
||||
$this->assertEquals($component->expr, 'x.id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider parseErrProvider
|
||||
*/
|
||||
public function testParseErr(string $expr, string $error): void
|
||||
{
|
||||
$parser = new Parser();
|
||||
Expression::parse($parser, $this->getTokensList($expr));
|
||||
$errors = $this->getErrorsAsArray($parser);
|
||||
$this->assertEquals($errors[0][0], $error);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function parseErrProvider(): array
|
||||
{
|
||||
return [
|
||||
/*
|
||||
[
|
||||
'(1))',
|
||||
'Unexpected closing bracket.',
|
||||
],
|
||||
*/
|
||||
[
|
||||
'tbl..col',
|
||||
'Unexpected dot.',
|
||||
],
|
||||
[
|
||||
'id AS AS id2',
|
||||
'An alias was expected.',
|
||||
],
|
||||
[
|
||||
'id`id2`\'id3\'',
|
||||
'An alias was previously found.',
|
||||
],
|
||||
[
|
||||
'(id) id2 id3',
|
||||
'An alias was previously found.',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function testBuild(): void
|
||||
{
|
||||
$component = [
|
||||
new Expression('1 + 2', 'three'),
|
||||
new Expression('1 + 3', 'four'),
|
||||
];
|
||||
$this->assertEquals(
|
||||
Expression::build($component),
|
||||
'1 + 2 AS `three`, 1 + 3 AS `four`'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function mysqlCommandsProvider(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;',
|
||||
'SET @OLD_CHARACTER_SET_CLIENT = @@CHARACTER_SET_CLIENT',
|
||||
],
|
||||
[
|
||||
'/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;',
|
||||
'SET @OLD_CHARACTER_SET_RESULTS = @@CHARACTER_SET_RESULTS',
|
||||
],
|
||||
[
|
||||
'/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;',
|
||||
'SET @OLD_COLLATION_CONNECTION = @@COLLATION_CONNECTION',
|
||||
],
|
||||
[
|
||||
'/*!40101 SET NAMES utf8 */;',
|
||||
'SET NAMES utf8',
|
||||
],
|
||||
[
|
||||
'/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;',
|
||||
'SET @OLD_TIME_ZONE = @@TIME_ZONE',
|
||||
],
|
||||
[
|
||||
"/*!40103 SET TIME_ZONE='+00:00' */;",
|
||||
"SET TIME_ZONE = '+00:00'",
|
||||
],
|
||||
[
|
||||
'/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;',
|
||||
'SET @OLD_UNIQUE_CHECKS = @@UNIQUE_CHECKS, UNIQUE_CHECKS = 0',
|
||||
],
|
||||
[
|
||||
'/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;',
|
||||
'SET @OLD_FOREIGN_KEY_CHECKS = @@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS = 0',
|
||||
],
|
||||
[
|
||||
"/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;",
|
||||
"SET @OLD_SQL_MODE = @@SQL_MODE, SQL_MODE = 'NO_AUTO_VALUE_ON_ZERO'",
|
||||
],
|
||||
[
|
||||
'/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;',
|
||||
'SET @OLD_SQL_NOTES = @@SQL_NOTES, SQL_NOTES = 0',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider mysqlCommandsProvider
|
||||
*/
|
||||
public function testMysqlCommands(string $expr, string $expected): void
|
||||
{
|
||||
$parser = new Parser($expr, true);
|
||||
$parser->parse();
|
||||
self::assertSame($expected, $parser->statements[0]->build());
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Components;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\ArrayObj;
|
||||
use PhpMyAdmin\SqlParser\Components\FunctionCall;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class FunctionCallTest extends TestCase
|
||||
{
|
||||
public function testBuildArray(): void
|
||||
{
|
||||
$component = new FunctionCall('func', ['a', 'b']);
|
||||
$this->assertEquals('func(a, b)', FunctionCall::build($component));
|
||||
}
|
||||
|
||||
public function testBuildArrayObj(): void
|
||||
{
|
||||
$component = new FunctionCall('func', new ArrayObj(['a', 'b']));
|
||||
$this->assertEquals('func(a, b)', FunctionCall::build($component));
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Components;
|
||||
|
||||
use Generator;
|
||||
use PhpMyAdmin\SqlParser\Components\Expression;
|
||||
use PhpMyAdmin\SqlParser\Components\GroupKeyword;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
use function array_map;
|
||||
|
||||
class GroupKeywordTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @return Generator<string, array{GroupKeyword|array<GroupKeyword>, string}>
|
||||
*/
|
||||
public static function provideExpressions(): Generator
|
||||
{
|
||||
yield 'With no expression at all' => [[], ''];
|
||||
|
||||
yield 'With single simple expression' => [
|
||||
self::makeComponentFrom('a'),
|
||||
'a',
|
||||
];
|
||||
|
||||
yield 'With multiple simple expressions' => [
|
||||
self::makeComponentsFrom('a', 'b', 'c'),
|
||||
'a, b, c',
|
||||
];
|
||||
|
||||
yield 'With single untrimmed expression' => [
|
||||
self::makeComponentFrom(' o '),
|
||||
'o',
|
||||
];
|
||||
|
||||
yield 'With single untrimmed expression having several kinds of whitespaces' => [
|
||||
self::makeComponentFrom(" \n\r foo \t\v\x00 "),
|
||||
'foo',
|
||||
];
|
||||
|
||||
yield 'With multiple untrimmed expressions' => [
|
||||
self::makeComponentsFrom(' x', ' y ', 'z '),
|
||||
'x, y, z',
|
||||
];
|
||||
|
||||
yield 'With multiple untrimmed expression having several kinds of whitespaces' => [
|
||||
self::makeComponentsFrom(" \n\r\t\v\x00foo", " \n\r\tbar\v\x00", "baz \n\r\t\v\x00"),
|
||||
'foo, bar, baz',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param GroupKeyword|array<GroupKeyword> $component
|
||||
*
|
||||
* @dataProvider provideExpressions
|
||||
*/
|
||||
public function testBuild($component, string $expected): void
|
||||
{
|
||||
$this->assertSame($expected, GroupKeyword::build($component));
|
||||
}
|
||||
|
||||
private static function makeComponentFrom(string $string): GroupKeyword
|
||||
{
|
||||
return new GroupKeyword(new Expression($string));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<GroupKeyword>
|
||||
*/
|
||||
private static function makeComponentsFrom(string ...$string): array
|
||||
{
|
||||
return array_map([self::class, 'makeComponentFrom'], $string);
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Components;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\IntoKeyword;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class IntoKeywordTest extends TestCase
|
||||
{
|
||||
public function testParse(): void
|
||||
{
|
||||
$component = IntoKeyword::parse(new Parser(), $this->getTokensList('OUTFILE "/tmp/outfile.txt"'));
|
||||
$this->assertEquals($component->type, 'OUTFILE');
|
||||
$this->assertEquals($component->dest, '/tmp/outfile.txt');
|
||||
}
|
||||
|
||||
public function testBuild(): void
|
||||
{
|
||||
$component = IntoKeyword::parse(new Parser(), $this->getTokensList('tbl(`col1`, `col2`)'));
|
||||
$this->assertEquals('tbl(`col1`, `col2`)', IntoKeyword::build($component));
|
||||
}
|
||||
|
||||
public function testBuildValues(): void
|
||||
{
|
||||
$component = IntoKeyword::parse(new Parser(), $this->getTokensList('@a1, @a2, @a3'));
|
||||
$this->assertEquals('@a1, @a2, @a3', IntoKeyword::build($component));
|
||||
}
|
||||
|
||||
public function testBuildOutfile(): void
|
||||
{
|
||||
$component = IntoKeyword::parse(new Parser(), $this->getTokensList('OUTFILE "/tmp/outfile.txt"'));
|
||||
$this->assertEquals('OUTFILE "/tmp/outfile.txt"', IntoKeyword::build($component));
|
||||
}
|
||||
|
||||
public function testParseErr1(): void
|
||||
{
|
||||
$component = IntoKeyword::parse(new Parser(), $this->getTokensList('OUTFILE;'));
|
||||
$this->assertEquals($component->type, 'OUTFILE');
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Components;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\JoinKeyword;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class JoinKeywordTest extends TestCase
|
||||
{
|
||||
public function testParseIncomplete(): void
|
||||
{
|
||||
$component = JoinKeyword::parse(new Parser(), $this->getTokensList('JOIN a'));
|
||||
$this->assertCount(1, $component);
|
||||
$this->assertEquals('a', $component[0]->expr->expr);
|
||||
$this->assertNull($component[0]->on);
|
||||
$this->assertNull($component[0]->using);
|
||||
}
|
||||
|
||||
public function testParseIncompleteUsing(): void
|
||||
{
|
||||
$component = JoinKeyword::parse(new Parser(), $this->getTokensList('JOIN table2 USING (id)'));
|
||||
$this->assertCount(1, $component);
|
||||
$this->assertEquals('table2', $component[0]->expr->expr);
|
||||
$this->assertNull($component[0]->on);
|
||||
$this->assertEquals(['id'], $component[0]->using->values);
|
||||
}
|
||||
|
||||
public function testBuild(): void
|
||||
{
|
||||
$component = JoinKeyword::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList(
|
||||
'LEFT JOIN (t2 CROSS JOIN t3 CROSS JOIN t4) ' .
|
||||
'ON (t2.a=t1.a AND t3.b=t1.b AND t4.c=t1.c)'
|
||||
)
|
||||
);
|
||||
$this->assertEquals(
|
||||
'LEFT JOIN (t2 CROSS JOIN t3 CROSS JOIN t4) ' .
|
||||
'ON (t2.a=t1.a AND t3.b=t1.b AND t4.c=t1.c)',
|
||||
JoinKeyword::build($component)
|
||||
);
|
||||
}
|
||||
}
|
||||
+374
@@ -0,0 +1,374 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Components;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\Expression;
|
||||
use PhpMyAdmin\SqlParser\Components\Key;
|
||||
use PhpMyAdmin\SqlParser\Components\OptionsArray;
|
||||
use PhpMyAdmin\SqlParser\Exceptions\ParserException;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
use PhpMyAdmin\SqlParser\Token;
|
||||
|
||||
class KeyTest extends TestCase
|
||||
{
|
||||
public function testParse(): void
|
||||
{
|
||||
$component = Key::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList('')
|
||||
);
|
||||
$this->assertNull($component->type);
|
||||
$this->assertNull($component->options);
|
||||
$this->assertNull($component->name);
|
||||
$this->assertNull($component->expr);
|
||||
$this->assertSame([], $component->columns);
|
||||
$this->assertSame(
|
||||
'()',
|
||||
Key::build($component)
|
||||
);
|
||||
}
|
||||
|
||||
public function testParseKeyWithoutOptions(): void
|
||||
{
|
||||
$component = Key::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList('KEY `alias_type_idx` (`alias_type`),')
|
||||
);
|
||||
$this->assertEquals('KEY', $component->type);
|
||||
$this->assertEquals('alias_type_idx', $component->name);
|
||||
$this->assertEquals(new OptionsArray(), $component->options);
|
||||
$this->assertNull($component->expr);
|
||||
$this->assertSame([['name' => 'alias_type']], $component->columns);
|
||||
$this->assertSame(
|
||||
'KEY `alias_type_idx` (`alias_type`)',
|
||||
Key::build($component)
|
||||
);
|
||||
}
|
||||
|
||||
public function testParseKeyWithLengthWithoutOptions(): void
|
||||
{
|
||||
$component = Key::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList('KEY `alias_type_idx` (`alias_type`(10)),')
|
||||
);
|
||||
$this->assertEquals('KEY', $component->type);
|
||||
$this->assertEquals('alias_type_idx', $component->name);
|
||||
$this->assertEquals(new OptionsArray(), $component->options);
|
||||
$this->assertNull($component->expr);
|
||||
$this->assertSame([['name' => 'alias_type', 'length' => 10]], $component->columns);
|
||||
$this->assertSame(
|
||||
'KEY `alias_type_idx` (`alias_type`(10))',
|
||||
Key::build($component)
|
||||
);
|
||||
}
|
||||
|
||||
public function testParseKeyWithLengthWithoutOptionsWithOrder(): void
|
||||
{
|
||||
$component = Key::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList('KEY `alias_type_idx` (`alias_type`(10) ASC),')
|
||||
);
|
||||
$this->assertEquals('KEY', $component->type);
|
||||
$this->assertEquals('alias_type_idx', $component->name);
|
||||
$this->assertEquals(new OptionsArray(), $component->options);
|
||||
$this->assertNull($component->expr);
|
||||
$this->assertSame([['name' => 'alias_type', 'length' => 10, 'order' => 'ASC']], $component->columns);
|
||||
$this->assertSame(
|
||||
'KEY `alias_type_idx` (`alias_type`(10) ASC)',
|
||||
Key::build($component)
|
||||
);
|
||||
}
|
||||
|
||||
public function testParseKeyWithoutOptionsWithOrderLowercase(): void
|
||||
{
|
||||
$component = Key::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList('KEY `alias_type_idx` (`alias_type` desc),')
|
||||
);
|
||||
$this->assertEquals('KEY', $component->type);
|
||||
$this->assertEquals('alias_type_idx', $component->name);
|
||||
$this->assertEquals(new OptionsArray(), $component->options);
|
||||
$this->assertNull($component->expr);
|
||||
$this->assertSame([['name' => 'alias_type', 'order' => 'DESC']], $component->columns);
|
||||
$this->assertSame(
|
||||
'KEY `alias_type_idx` (`alias_type` DESC)',
|
||||
Key::build($component)
|
||||
);
|
||||
}
|
||||
|
||||
public function testParseKeyWithoutOptionsWithOrder(): void
|
||||
{
|
||||
$component = Key::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList('KEY `alias_type_idx` (`alias_type` DESC),')
|
||||
);
|
||||
$this->assertEquals('KEY', $component->type);
|
||||
$this->assertEquals('alias_type_idx', $component->name);
|
||||
$this->assertEquals(new OptionsArray(), $component->options);
|
||||
$this->assertNull($component->expr);
|
||||
$this->assertSame([['name' => 'alias_type', 'order' => 'DESC']], $component->columns);
|
||||
$this->assertSame(
|
||||
'KEY `alias_type_idx` (`alias_type` DESC)',
|
||||
Key::build($component)
|
||||
);
|
||||
}
|
||||
|
||||
public function testParseKeyWithLengthWithOptions(): void
|
||||
{
|
||||
$component = Key::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList('KEY `alias_type_idx` (`alias_type`(10)) COMMENT \'my comment\',')
|
||||
);
|
||||
$this->assertEquals('KEY', $component->type);
|
||||
$this->assertEquals('alias_type_idx', $component->name);
|
||||
$this->assertEquals(new OptionsArray(
|
||||
[
|
||||
4 => [
|
||||
'name' => 'COMMENT',
|
||||
'equals' => false,
|
||||
'expr' => '\'my comment\'',
|
||||
'value' => 'my comment',
|
||||
],
|
||||
]
|
||||
), $component->options);
|
||||
$this->assertNull($component->expr);
|
||||
$this->assertSame([['name' => 'alias_type', 'length' => 10]], $component->columns);
|
||||
$this->assertSame(
|
||||
'KEY `alias_type_idx` (`alias_type`(10)) COMMENT \'my comment\'',
|
||||
Key::build($component)
|
||||
);
|
||||
}
|
||||
|
||||
public function testParseKeyWithLengthWithAllOptions(): void
|
||||
{
|
||||
$component = Key::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList(
|
||||
// This is not a vary plausible example but it runs
|
||||
// Only ENGINE_ATTRIBUTE gives a not supported error but is still a valid syntax
|
||||
'KEY `alias_type_idx` (`alias_type`(10))'
|
||||
. ' COMMENT \'my comment\' VISIBLE KEY_BLOCK_SIZE=1'
|
||||
. ' INVISIBLE ENGINE_ATTRIBUTE \'foo\' SECONDARY_ENGINE_ATTRIBUTE=\'bar\' USING BTREE,'
|
||||
)
|
||||
);
|
||||
$this->assertEquals('KEY', $component->type);
|
||||
$this->assertEquals('alias_type_idx', $component->name);
|
||||
$this->assertEquals(new OptionsArray(
|
||||
[
|
||||
1 => [
|
||||
'name' => 'KEY_BLOCK_SIZE',
|
||||
'equals' => true,
|
||||
'expr' => '1',
|
||||
'value' => '1',
|
||||
],
|
||||
2 => [
|
||||
'name' => 'USING',
|
||||
'equals' => false,
|
||||
'expr' => 'BTREE',
|
||||
'value' => 'BTREE',
|
||||
],
|
||||
4 => [
|
||||
'name' => 'COMMENT',
|
||||
'equals' => false,
|
||||
'expr' => '\'my comment\'',
|
||||
'value' => 'my comment',
|
||||
],
|
||||
5 => [
|
||||
'name' => 'ENGINE_ATTRIBUTE',
|
||||
'equals' => true,
|
||||
'expr' => '\'foo\'',
|
||||
'value' => 'foo',
|
||||
],
|
||||
6 => 'VISIBLE',
|
||||
12 => 'INVISIBLE',
|
||||
13 => [
|
||||
'name' => 'SECONDARY_ENGINE_ATTRIBUTE',
|
||||
'equals' => true,
|
||||
'expr' => '\'bar\'',
|
||||
'value' => 'bar',
|
||||
],
|
||||
]
|
||||
), $component->options);
|
||||
$this->assertNull($component->expr);
|
||||
$this->assertSame([['name' => 'alias_type', 'length' => 10]], $component->columns);
|
||||
}
|
||||
|
||||
public function testParseKeyExpressionWithoutOptions(): void
|
||||
{
|
||||
$component = Key::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList(
|
||||
'KEY `updated_tz_ind2` ((convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'GB\'))),'
|
||||
)
|
||||
);
|
||||
$this->assertEquals('KEY', $component->type);
|
||||
$this->assertEquals('updated_tz_ind2', $component->name);
|
||||
$this->assertEquals(new OptionsArray(), $component->options);
|
||||
$expr = new Expression('(convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'GB\'))');
|
||||
$expr->function = 'convert_tz';
|
||||
$this->assertEquals($expr, $component->expr);
|
||||
$this->assertSame([], $component->columns);
|
||||
$this->assertSame(
|
||||
'KEY `updated_tz_ind2` ((convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'GB\'))) ',
|
||||
Key::build($component)
|
||||
);
|
||||
}
|
||||
|
||||
public function testParseKeyExpressionWithOptions(): void
|
||||
{
|
||||
$component = Key::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList(
|
||||
'KEY `updated_tz_ind2`'
|
||||
. ' ((convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'GB\')))'
|
||||
. ' COMMENT \'my comment\','
|
||||
)
|
||||
);
|
||||
$this->assertEquals('KEY', $component->type);
|
||||
$this->assertEquals('updated_tz_ind2', $component->name);
|
||||
$this->assertEquals(new OptionsArray(
|
||||
[
|
||||
4 => [
|
||||
'name' => 'COMMENT',
|
||||
'equals' => false,
|
||||
'expr' => '\'my comment\'',
|
||||
'value' => 'my comment',
|
||||
],
|
||||
]
|
||||
), $component->options);
|
||||
$expr = new Expression('(convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'GB\'))');
|
||||
$expr->function = 'convert_tz';
|
||||
$this->assertEquals($expr, $component->expr);
|
||||
$this->assertSame([], $component->columns);
|
||||
$this->assertSame(
|
||||
'KEY `updated_tz_ind2`'
|
||||
. ' ((convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'GB\')))'
|
||||
. ' COMMENT \'my comment\'',
|
||||
Key::build($component)
|
||||
);
|
||||
}
|
||||
|
||||
public function testParseKeyExpressionWithOptionsError(): void
|
||||
{
|
||||
$parser = new Parser();
|
||||
$component = Key::parse(
|
||||
$parser,
|
||||
$this->getTokensList(
|
||||
'KEY `updated_tz_ind2` (()convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'GB\')))'
|
||||
. ' COMMENT \'my comment\','
|
||||
)
|
||||
);
|
||||
$this->assertEquals('KEY', $component->type);
|
||||
$this->assertEquals('updated_tz_ind2', $component->name);
|
||||
$this->assertEquals(new OptionsArray(
|
||||
[]
|
||||
), $component->options);
|
||||
$t = new Token('convert_tz', Token::TYPE_KEYWORD, 33);
|
||||
$t->position = 25;
|
||||
|
||||
$this->assertEquals([
|
||||
new ParserException(
|
||||
'Unexpected token.',
|
||||
$t
|
||||
),
|
||||
], $parser->errors);
|
||||
$expr = new Expression('(convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'GB\'))');
|
||||
$expr->function = 'convert_tz';
|
||||
$this->assertEquals('()(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'GB\')', $component->expr);
|
||||
$this->assertSame([], $component->columns);
|
||||
$this->assertSame(
|
||||
'KEY `updated_tz_ind2` (()(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'GB\')) ',
|
||||
Key::build($component)
|
||||
);
|
||||
}
|
||||
|
||||
public function testParseKeyOneExpressionWithOptions(): void
|
||||
{
|
||||
$parser = new Parser();
|
||||
$component = Key::parse(
|
||||
$parser,
|
||||
$this->getTokensList(
|
||||
'KEY `updated_tz_ind2`'
|
||||
. ' ('
|
||||
. '(convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'GB\')), '
|
||||
. '(convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'FR\'))'
|
||||
. ')'
|
||||
. ' COMMENT \'my comment\','
|
||||
)
|
||||
);
|
||||
$this->assertEquals('KEY', $component->type);
|
||||
$this->assertEquals('updated_tz_ind2', $component->name);
|
||||
$this->assertEquals(new OptionsArray(
|
||||
[
|
||||
4 => [
|
||||
'name' => 'COMMENT',
|
||||
'equals' => false,
|
||||
'expr' => '\'my comment\'',
|
||||
'value' => 'my comment',
|
||||
],
|
||||
]
|
||||
), $component->options);
|
||||
$this->assertSame([], $parser->errors);
|
||||
$expr = new Expression(
|
||||
'(convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'GB\')),'
|
||||
. ' (convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'FR\'))'
|
||||
);
|
||||
$expr->function = 'convert_tz';
|
||||
$this->assertEquals($expr, $component->expr);
|
||||
$this->assertSame([], $component->columns);
|
||||
$this->assertSame(
|
||||
'KEY `updated_tz_ind2` ((convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'GB\')),'
|
||||
. ' (convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'FR\'))'
|
||||
. ') COMMENT \'my comment\'',
|
||||
Key::build($component)
|
||||
);
|
||||
}
|
||||
|
||||
public function testParseKeyMultipleExpressionsWithOptions(): void
|
||||
{
|
||||
$parser = new Parser();
|
||||
$component = Key::parse(
|
||||
$parser,
|
||||
$this->getTokensList(
|
||||
'KEY `updated_tz_ind2`'
|
||||
. ' ('
|
||||
. '(convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'GB\')), '
|
||||
. '(convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'FR\')), '
|
||||
. '(convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'RU\'))'
|
||||
. ')'
|
||||
. ' COMMENT \'my comment\','
|
||||
)
|
||||
);
|
||||
$this->assertEquals('KEY', $component->type);
|
||||
$this->assertEquals('updated_tz_ind2', $component->name);
|
||||
$this->assertEquals(new OptionsArray(
|
||||
[
|
||||
4 => [
|
||||
'name' => 'COMMENT',
|
||||
'equals' => false,
|
||||
'expr' => '\'my comment\'',
|
||||
'value' => 'my comment',
|
||||
],
|
||||
]
|
||||
), $component->options);
|
||||
$expr = new Expression(
|
||||
'(convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'GB\')),'
|
||||
. ' (convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'FR\')),'
|
||||
. ' (convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'RU\'))'
|
||||
);
|
||||
$expr->function = 'convert_tz';
|
||||
$this->assertEquals($expr, $component->expr);
|
||||
$this->assertSame([], $component->columns);
|
||||
$this->assertSame([], $parser->errors);
|
||||
$this->assertSame(
|
||||
'KEY `updated_tz_ind2` ((convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'GB\')),'
|
||||
. ' (convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'FR\')),'
|
||||
. ' (convert_tz(`cache_updated`,_utf8mb4\'GMT\',_utf8mb4\'RU\'))'
|
||||
. ') COMMENT \'my comment\'',
|
||||
Key::build($component)
|
||||
);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Components;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\Limit;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class LimitTest extends TestCase
|
||||
{
|
||||
public function testBuildWithoutOffset(): void
|
||||
{
|
||||
$component = new Limit(1);
|
||||
$this->assertEquals(Limit::build($component), '0, 1');
|
||||
}
|
||||
|
||||
public function testBuildWithOffset(): void
|
||||
{
|
||||
$component = new Limit(1, 2);
|
||||
$this->assertEquals(Limit::build($component), '2, 1');
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider parseProvider
|
||||
*/
|
||||
public function testParse(string $test): void
|
||||
{
|
||||
$this->runParserTest($test);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function parseProvider(): array
|
||||
{
|
||||
return [
|
||||
['parser/parseLimitErr1'],
|
||||
['parser/parseLimitErr2'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Components;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\LockExpression;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class LockExpressionTest extends TestCase
|
||||
{
|
||||
public function testParse(): void
|
||||
{
|
||||
$component = LockExpression::parse(new Parser(), $this->getTokensList('table1 AS t1 READ LOCAL'));
|
||||
$this->assertNotNull($component->table);
|
||||
$this->assertEquals($component->table->table, 'table1');
|
||||
$this->assertEquals($component->table->alias, 't1');
|
||||
$this->assertEquals($component->type, 'READ LOCAL');
|
||||
}
|
||||
|
||||
public function testParse2(): void
|
||||
{
|
||||
$component = LockExpression::parse(new Parser(), $this->getTokensList('table1 LOW_PRIORITY WRITE'));
|
||||
$this->assertNotNull($component->table);
|
||||
$this->assertEquals($component->table->table, 'table1');
|
||||
$this->assertEquals($component->type, 'LOW_PRIORITY WRITE');
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider parseErrProvider
|
||||
*/
|
||||
public function testParseErr(string $expr, string $error): void
|
||||
{
|
||||
$parser = new Parser();
|
||||
LockExpression::parse($parser, $this->getTokensList($expr));
|
||||
$errors = $this->getErrorsAsArray($parser);
|
||||
$this->assertEquals($errors[0][0], $error);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function parseErrProvider(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'table1 AS t1',
|
||||
'Unexpected end of LOCK expression.',
|
||||
],
|
||||
[
|
||||
'table1 AS t1 READ WRITE',
|
||||
'Unexpected keyword.',
|
||||
],
|
||||
[
|
||||
'table1 AS t1 READ 2',
|
||||
'Unexpected token.',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function testBuild(): void
|
||||
{
|
||||
$component = [
|
||||
LockExpression::parse(new Parser(), $this->getTokensList('table1 AS t1 READ LOCAL')),
|
||||
LockExpression::parse(new Parser(), $this->getTokensList('table2 LOW_PRIORITY WRITE')),
|
||||
];
|
||||
$this->assertEquals(
|
||||
LockExpression::build($component),
|
||||
'table1 AS `t1` READ LOCAL, table2 LOW_PRIORITY WRITE'
|
||||
);
|
||||
}
|
||||
}
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Components;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\OptionsArray;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class OptionsArrayTest extends TestCase
|
||||
{
|
||||
public function testParse(): void
|
||||
{
|
||||
$component = OptionsArray::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList('A B = /*comment*/ (test) C'),
|
||||
[
|
||||
'A' => 1,
|
||||
'B' => [
|
||||
2,
|
||||
'var',
|
||||
],
|
||||
'C' => 3,
|
||||
]
|
||||
);
|
||||
$this->assertEquals(
|
||||
[
|
||||
1 => 'A',
|
||||
2 => [
|
||||
'name' => 'B',
|
||||
'expr' => '(test)',
|
||||
'value' => 'test',
|
||||
'equals' => true,
|
||||
],
|
||||
3 => 'C',
|
||||
],
|
||||
$component->options
|
||||
);
|
||||
}
|
||||
|
||||
public function testParseExpr(): void
|
||||
{
|
||||
$component = OptionsArray::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList('SUM = (3 + 5) RESULT = 8'),
|
||||
[
|
||||
'SUM' => [
|
||||
1,
|
||||
'expr',
|
||||
['parenthesesDelimited' => true],
|
||||
],
|
||||
'RESULT' => [
|
||||
2,
|
||||
'var',
|
||||
],
|
||||
]
|
||||
);
|
||||
$this->assertEquals('(3 + 5)', (string) $component->has('SUM', true));
|
||||
$this->assertEquals('8', $component->has('RESULT'));
|
||||
}
|
||||
|
||||
public function testHas(): void
|
||||
{
|
||||
$component = OptionsArray::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList('A B = /*comment*/ (test) C'),
|
||||
[
|
||||
'A' => 1,
|
||||
'B' => [
|
||||
2,
|
||||
'var',
|
||||
],
|
||||
'C' => 3,
|
||||
]
|
||||
);
|
||||
$this->assertTrue($component->has('A'));
|
||||
$this->assertEquals('test', $component->has('B'));
|
||||
$this->assertTrue($component->has('C'));
|
||||
$this->assertFalse($component->has('D'));
|
||||
}
|
||||
|
||||
public function testRemove(): void
|
||||
{
|
||||
/* Assertion 1 */
|
||||
$component = new OptionsArray(['a', 'b', 'c']);
|
||||
$this->assertTrue($component->remove('b'));
|
||||
$this->assertFalse($component->remove('d'));
|
||||
$this->assertEquals($component->options, [0 => 'a', 2 => 'c']);
|
||||
|
||||
/* Assertion 2 */
|
||||
$component = OptionsArray::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList('A B = /*comment*/ (test) C'),
|
||||
[
|
||||
'A' => 1,
|
||||
'B' => [
|
||||
2,
|
||||
'var',
|
||||
],
|
||||
'C' => 3,
|
||||
]
|
||||
);
|
||||
$this->assertEquals('test', $component->has('B'));
|
||||
$component->remove('B');
|
||||
$this->assertFalse($component->has('B'));
|
||||
}
|
||||
|
||||
public function testMerge(): void
|
||||
{
|
||||
$component = new OptionsArray(['a']);
|
||||
$component->merge(['b', 'c']);
|
||||
$this->assertEquals($component->options, ['a', 'b', 'c']);
|
||||
}
|
||||
|
||||
public function testBuild(): void
|
||||
{
|
||||
$component = new OptionsArray(
|
||||
[
|
||||
'ALL',
|
||||
'SQL_CALC_FOUND_ROWS',
|
||||
[
|
||||
'name' => 'MAX_STATEMENT_TIME',
|
||||
'value' => '42',
|
||||
'equals' => true,
|
||||
],
|
||||
]
|
||||
);
|
||||
$this->assertEquals(
|
||||
OptionsArray::build($component),
|
||||
'ALL SQL_CALC_FOUND_ROWS MAX_STATEMENT_TIME=42'
|
||||
);
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Components;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\Expression;
|
||||
use PhpMyAdmin\SqlParser\Components\OrderKeyword;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class OrderKeywordTest extends TestCase
|
||||
{
|
||||
public function testBuild(): void
|
||||
{
|
||||
$this->assertEquals(
|
||||
OrderKeyword::build(
|
||||
[
|
||||
new OrderKeyword(new Expression('a'), 'ASC'),
|
||||
new OrderKeyword(new Expression('b'), 'DESC'),
|
||||
]
|
||||
),
|
||||
'a ASC, b DESC'
|
||||
);
|
||||
}
|
||||
}
|
||||
Vendored
+32
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Components;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\ParameterDefinition;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class ParameterDefinitionTest extends TestCase
|
||||
{
|
||||
public function testParse(): void
|
||||
{
|
||||
$component = ParameterDefinition::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList('(a INT, b INT')
|
||||
);
|
||||
$this->assertEquals('a', $component[0]->name);
|
||||
$this->assertEquals('b', $component[1]->name);
|
||||
}
|
||||
|
||||
public function testParseComplex(): void
|
||||
{
|
||||
$parser = new Parser();
|
||||
$component = ParameterDefinition::parse(
|
||||
$parser,
|
||||
$this->getTokensList('CREATE DEFINER=`root`@`%` PROCEDURE `foo`( $bar int )')
|
||||
);
|
||||
$this->assertEquals('$bar', $component[0]->name);
|
||||
}
|
||||
}
|
||||
Vendored
+36
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Components;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\PartitionDefinition;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class PartitionDefinitionTest extends TestCase
|
||||
{
|
||||
public function testParse(): void
|
||||
{
|
||||
$component = PartitionDefinition::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList('PARTITION p0 VALUES LESS THAN(1990)')
|
||||
);
|
||||
$this->assertFalse($component->isSubpartition);
|
||||
$this->assertEquals('p0', $component->name);
|
||||
$this->assertEquals('LESS THAN', $component->type);
|
||||
$this->assertEquals('(1990)', $component->expr->expr);
|
||||
}
|
||||
|
||||
public function testParseNameWithUnderscore(): void
|
||||
{
|
||||
$component = PartitionDefinition::parse(
|
||||
new Parser(),
|
||||
$this->getTokensList('PARTITION 2017_12 VALUES LESS THAN (\'2018-01-01 00:00:00\') ENGINE = MyISAM')
|
||||
);
|
||||
$this->assertFalse($component->isSubpartition);
|
||||
$this->assertEquals('2017_12', $component->name);
|
||||
$this->assertEquals('LESS THAN', $component->type);
|
||||
$this->assertEquals('(\'2018-01-01 00:00:00\')', $component->expr->expr);
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Components;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\Expression;
|
||||
use PhpMyAdmin\SqlParser\Components\Reference;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class ReferenceTest extends TestCase
|
||||
{
|
||||
public function testParse(): void
|
||||
{
|
||||
$component = Reference::parse(new Parser(), $this->getTokensList('tbl (id)'));
|
||||
$this->assertEquals('tbl', $component->table->table);
|
||||
$this->assertEquals(['id'], $component->columns);
|
||||
}
|
||||
|
||||
public function testBuild(): void
|
||||
{
|
||||
$component = new Reference(new Expression('`tbl`'), ['id']);
|
||||
$this->assertEquals('`tbl` (`id`)', Reference::build($component));
|
||||
}
|
||||
}
|
||||
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Components;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\RenameOperation;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class RenameOperationTest extends TestCase
|
||||
{
|
||||
public function testBuild(): void
|
||||
{
|
||||
$component = RenameOperation::parse(new Parser(), $this->getTokensList('a TO b, c TO d'));
|
||||
$this->assertEquals(RenameOperation::build($component), 'a TO b, c TO d');
|
||||
}
|
||||
}
|
||||
+313
@@ -0,0 +1,313 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Lexer;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Context;
|
||||
use PhpMyAdmin\SqlParser\Contexts;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
use Throwable;
|
||||
|
||||
use function class_exists;
|
||||
|
||||
class ContextTest extends TestCase
|
||||
{
|
||||
public static function tearDownAfterClass(): void
|
||||
{
|
||||
Context::setMode();
|
||||
}
|
||||
|
||||
public function testLoad(): void
|
||||
{
|
||||
// Default context is 5.7.0.
|
||||
$this->assertEquals('\\PhpMyAdmin\\SqlParser\\Contexts\\ContextMySql50700', Context::$loadedContext);
|
||||
$this->assertArrayHasKey('STORED', Context::$KEYWORDS);
|
||||
$this->assertArrayNotHasKey('AUTHORS', Context::$KEYWORDS);
|
||||
|
||||
// Restoring context.
|
||||
Context::load('');
|
||||
$this->assertEquals('\\PhpMyAdmin\\SqlParser\\Contexts\\ContextMySql50700', Context::$defaultContext);
|
||||
$this->assertArrayHasKey('STORED', Context::$KEYWORDS);
|
||||
$this->assertArrayNotHasKey('AUTHORS', Context::$KEYWORDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for loading closest SQL context
|
||||
*
|
||||
* @dataProvider contextLoadingProvider
|
||||
*/
|
||||
public function testLoadClosest(string $context, ?string $expected): void
|
||||
{
|
||||
$this->assertEquals($expected, Context::loadClosest($context));
|
||||
if ($expected !== null) {
|
||||
$this->assertEquals('\\PhpMyAdmin\\SqlParser\\Contexts\\Context' . $expected, Context::$loadedContext);
|
||||
$this->assertTrue(class_exists(Context::$loadedContext));
|
||||
}
|
||||
|
||||
// Restoring context.
|
||||
Context::load('');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array<int, string|null>>
|
||||
* @psalm-return array<string, array{string, (string|null)}>
|
||||
*/
|
||||
public static function contextLoadingProvider(): array
|
||||
{
|
||||
return [
|
||||
'MySQL match' => [
|
||||
'MySql50500',
|
||||
'MySql50500',
|
||||
],
|
||||
'MySQL strip' => [
|
||||
'MySql50712',
|
||||
'MySql50700',
|
||||
],
|
||||
'MySQL fallback' => [
|
||||
'MySql9897969594',
|
||||
'MySql50700',
|
||||
],
|
||||
'MariaDB match' => [
|
||||
'MariaDb100000',
|
||||
'MariaDb100000',
|
||||
],
|
||||
'MariaDB stripg' => [
|
||||
'MariaDb109900',
|
||||
'MariaDb100000',
|
||||
],
|
||||
'MariaDB fallback' => [
|
||||
'MariaDb990000',
|
||||
'MariaDb100300',
|
||||
],
|
||||
'Invalid' => [
|
||||
'Sql',
|
||||
null,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider contextNamesProvider
|
||||
*/
|
||||
public function testLoadAll(string $context): void
|
||||
{
|
||||
Context::load($context);
|
||||
$this->assertEquals('\\PhpMyAdmin\\SqlParser\\Contexts\\Context' . $context, Context::$loadedContext);
|
||||
|
||||
// Restoring context.
|
||||
Context::load('');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function contextNamesProvider(): array
|
||||
{
|
||||
return [
|
||||
['MySql50000'],
|
||||
['MySql50100'],
|
||||
['MySql50500'],
|
||||
['MySql50600'],
|
||||
['MySql50700'],
|
||||
['MySql80000'],
|
||||
['MySql80100'],
|
||||
['MySql80200'],
|
||||
['MySql80300'],
|
||||
['MySql80400'],
|
||||
['MySql90000'],
|
||||
['MySql90100'],
|
||||
['MariaDb100000'],
|
||||
['MariaDb100100'],
|
||||
['MariaDb100200'],
|
||||
['MariaDb100300'],
|
||||
['MariaDb100400'],
|
||||
['MariaDb100500'],
|
||||
['MariaDb100600'],
|
||||
['MariaDb100700'],
|
||||
['MariaDb100800'],
|
||||
['MariaDb100900'],
|
||||
['MariaDb101000'],
|
||||
['MariaDb101100'],
|
||||
['MariaDb110000'],
|
||||
['MariaDb110100'],
|
||||
['MariaDb110200'],
|
||||
['MariaDb110300'],
|
||||
['MariaDb110400'],
|
||||
['MariaDb110500'],
|
||||
['MariaDb110600'],
|
||||
['MariaDb110700'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider contextClassesProvider
|
||||
*/
|
||||
public function testLoadAllByClass(string $context): void
|
||||
{
|
||||
Context::load($context);
|
||||
$this->assertEquals('\\' . $context, Context::$loadedContext);
|
||||
|
||||
// Restoring context.
|
||||
Context::load('');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function contextClassesProvider(): array
|
||||
{
|
||||
return [
|
||||
[Contexts\ContextMySql50000::class],
|
||||
[Contexts\ContextMySql50100::class],
|
||||
[Contexts\ContextMySql50500::class],
|
||||
[Contexts\ContextMySql50600::class],
|
||||
[Contexts\ContextMySql50700::class],
|
||||
[Contexts\ContextMySql80000::class],
|
||||
[Contexts\ContextMySql80100::class],
|
||||
[Contexts\ContextMySql80200::class],
|
||||
[Contexts\ContextMySql80300::class],
|
||||
[Contexts\ContextMySql80400::class],
|
||||
[Contexts\ContextMySql90000::class],
|
||||
[Contexts\ContextMySql90100::class],
|
||||
[Contexts\ContextMariaDb100000::class],
|
||||
[Contexts\ContextMariaDb100100::class],
|
||||
[Contexts\ContextMariaDb100200::class],
|
||||
[Contexts\ContextMariaDb100300::class],
|
||||
[Contexts\ContextMariaDb100400::class],
|
||||
[Contexts\ContextMariaDb100500::class],
|
||||
[Contexts\ContextMariaDb100600::class],
|
||||
[Contexts\ContextMariaDb100700::class],
|
||||
[Contexts\ContextMariaDb100800::class],
|
||||
[Contexts\ContextMariaDb100900::class],
|
||||
[Contexts\ContextMariaDb101000::class],
|
||||
[Contexts\ContextMariaDb101100::class],
|
||||
[Contexts\ContextMariaDb110000::class],
|
||||
[Contexts\ContextMariaDb110100::class],
|
||||
[Contexts\ContextMariaDb110200::class],
|
||||
[Contexts\ContextMariaDb110300::class],
|
||||
[Contexts\ContextMariaDb110400::class],
|
||||
[Contexts\ContextMariaDb110500::class],
|
||||
[Contexts\ContextMariaDb110600::class],
|
||||
[Contexts\ContextMariaDb110700::class],
|
||||
];
|
||||
}
|
||||
|
||||
public function testLoadError(): void
|
||||
{
|
||||
$this->expectExceptionMessage(
|
||||
'Specified context ("\PhpMyAdmin\SqlParser\Contexts\ContextFoo") does not exist.'
|
||||
);
|
||||
$this->expectException(Throwable::class);
|
||||
Context::load('Foo');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|string $mode
|
||||
*
|
||||
* @dataProvider providerForTestMode
|
||||
*/
|
||||
public function testMode($mode, int $expected): void
|
||||
{
|
||||
Context::setMode($mode);
|
||||
$this->assertSame($expected, Context::getMode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<int, int|string>>
|
||||
* @psalm-return list<array{int|string, int}>
|
||||
*/
|
||||
public static function providerForTestMode(): array
|
||||
{
|
||||
return [
|
||||
[0, Context::SQL_MODE_NONE],
|
||||
[1, 1],
|
||||
['', Context::SQL_MODE_NONE],
|
||||
['invalid', Context::SQL_MODE_NONE],
|
||||
['ALLOW_INVALID_DATES', Context::SQL_MODE_ALLOW_INVALID_DATES],
|
||||
['ANSI_QUOTES', Context::SQL_MODE_ANSI_QUOTES],
|
||||
['COMPAT_MYSQL', Context::SQL_MODE_COMPAT_MYSQL],
|
||||
['ERROR_FOR_DIVISION_BY_ZERO', Context::SQL_MODE_ERROR_FOR_DIVISION_BY_ZERO],
|
||||
['HIGH_NOT_PRECEDENCE', Context::SQL_MODE_HIGH_NOT_PRECEDENCE],
|
||||
['IGNORE_SPACE', Context::SQL_MODE_IGNORE_SPACE],
|
||||
['NO_AUTO_CREATE_USER', Context::SQL_MODE_NO_AUTO_CREATE_USER],
|
||||
['NO_AUTO_VALUE_ON_ZERO', Context::SQL_MODE_NO_AUTO_VALUE_ON_ZERO],
|
||||
['NO_BACKSLASH_ESCAPES', Context::SQL_MODE_NO_BACKSLASH_ESCAPES],
|
||||
['NO_DIR_IN_CREATE', Context::SQL_MODE_NO_DIR_IN_CREATE],
|
||||
['NO_ENGINE_SUBSTITUTION', Context::SQL_MODE_NO_ENGINE_SUBSTITUTION],
|
||||
['NO_FIELD_OPTIONS', Context::SQL_MODE_NO_FIELD_OPTIONS],
|
||||
['NO_KEY_OPTIONS', Context::SQL_MODE_NO_KEY_OPTIONS],
|
||||
['NO_TABLE_OPTIONS', Context::SQL_MODE_NO_TABLE_OPTIONS],
|
||||
['NO_UNSIGNED_SUBTRACTION', Context::SQL_MODE_NO_UNSIGNED_SUBTRACTION],
|
||||
['NO_ZERO_DATE', Context::SQL_MODE_NO_ZERO_DATE],
|
||||
['NO_ZERO_IN_DATE', Context::SQL_MODE_NO_ZERO_IN_DATE],
|
||||
['ONLY_FULL_GROUP_BY', Context::SQL_MODE_ONLY_FULL_GROUP_BY],
|
||||
['PIPES_AS_CONCAT', Context::SQL_MODE_PIPES_AS_CONCAT],
|
||||
['REAL_AS_FLOAT', Context::SQL_MODE_REAL_AS_FLOAT],
|
||||
['STRICT_ALL_TABLES', Context::SQL_MODE_STRICT_ALL_TABLES],
|
||||
['STRICT_TRANS_TABLES', Context::SQL_MODE_STRICT_TRANS_TABLES],
|
||||
['NO_ENCLOSING_QUOTES', Context::SQL_MODE_NO_ENCLOSING_QUOTES],
|
||||
['ANSI', Context::SQL_MODE_ANSI],
|
||||
['DB2', Context::SQL_MODE_DB2],
|
||||
['MAXDB', Context::SQL_MODE_MAXDB],
|
||||
['MSSQL', Context::SQL_MODE_MSSQL],
|
||||
['ORACLE', Context::SQL_MODE_ORACLE],
|
||||
['POSTGRESQL', Context::SQL_MODE_POSTGRESQL],
|
||||
['TRADITIONAL', Context::SQL_MODE_TRADITIONAL],
|
||||
];
|
||||
}
|
||||
|
||||
public function testModeWithCombinedModes(): void
|
||||
{
|
||||
Context::setMode(
|
||||
Context::SQL_MODE_REAL_AS_FLOAT | Context::SQL_MODE_ANSI_QUOTES | Context::SQL_MODE_IGNORE_SPACE
|
||||
);
|
||||
$this->assertSame(
|
||||
Context::SQL_MODE_REAL_AS_FLOAT | Context::SQL_MODE_ANSI_QUOTES | Context::SQL_MODE_IGNORE_SPACE,
|
||||
Context::getMode()
|
||||
);
|
||||
$this->assertTrue(Context::hasMode(Context::SQL_MODE_REAL_AS_FLOAT | Context::SQL_MODE_IGNORE_SPACE));
|
||||
$this->assertTrue(Context::hasMode(Context::SQL_MODE_ANSI_QUOTES));
|
||||
$this->assertFalse(Context::hasMode(Context::SQL_MODE_REAL_AS_FLOAT | Context::SQL_MODE_ALLOW_INVALID_DATES));
|
||||
$this->assertFalse(Context::hasMode(Context::SQL_MODE_ALLOW_INVALID_DATES));
|
||||
|
||||
Context::setMode(Context::SQL_MODE_TRADITIONAL);
|
||||
$this->assertSame(Context::SQL_MODE_TRADITIONAL, Context::getMode());
|
||||
|
||||
Context::setMode();
|
||||
$this->assertSame(Context::SQL_MODE_NONE, Context::getMode());
|
||||
}
|
||||
|
||||
public function testModeWithString(): void
|
||||
{
|
||||
Context::setMode('REAL_AS_FLOAT,ANSI_QUOTES,IGNORE_SPACE');
|
||||
$this->assertSame(
|
||||
Context::SQL_MODE_REAL_AS_FLOAT | Context::SQL_MODE_ANSI_QUOTES | Context::SQL_MODE_IGNORE_SPACE,
|
||||
Context::getMode()
|
||||
);
|
||||
$this->assertTrue(Context::hasMode(Context::SQL_MODE_REAL_AS_FLOAT | Context::SQL_MODE_IGNORE_SPACE));
|
||||
$this->assertTrue(Context::hasMode(Context::SQL_MODE_ANSI_QUOTES));
|
||||
$this->assertFalse(Context::hasMode(Context::SQL_MODE_REAL_AS_FLOAT | Context::SQL_MODE_ALLOW_INVALID_DATES));
|
||||
$this->assertFalse(Context::hasMode(Context::SQL_MODE_ALLOW_INVALID_DATES));
|
||||
|
||||
Context::setMode('TRADITIONAL');
|
||||
$this->assertSame(Context::SQL_MODE_TRADITIONAL, Context::getMode());
|
||||
|
||||
Context::setMode('');
|
||||
$this->assertSame(Context::SQL_MODE_NONE, Context::getMode());
|
||||
}
|
||||
|
||||
public function testEscape(): void
|
||||
{
|
||||
Context::setMode(Context::SQL_MODE_NO_ENCLOSING_QUOTES);
|
||||
$this->assertEquals('test', Context::escape('test'));
|
||||
|
||||
Context::setMode(Context::SQL_MODE_ANSI_QUOTES);
|
||||
$this->assertEquals('"test"', Context::escape('test'));
|
||||
|
||||
Context::setMode();
|
||||
$this->assertEquals('`test`', Context::escape('test'));
|
||||
|
||||
$this->assertEquals(['`a`', '`b`'], Context::escape(['a', 'b']));
|
||||
}
|
||||
}
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Lexer;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Context;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
use PhpMyAdmin\SqlParser\Token;
|
||||
|
||||
class IsMethodsTest extends TestCase
|
||||
{
|
||||
public function testIsKeyword(): void
|
||||
{
|
||||
$this->assertEquals(1 | Token::FLAG_KEYWORD_RESERVED, Context::isKeyword('SELECT'));
|
||||
$this->assertEquals(1 | Token::FLAG_KEYWORD_RESERVED, Context::isKeyword('ALL'));
|
||||
$this->assertEquals(1 | Token::FLAG_KEYWORD_RESERVED, Context::isKeyword('DISTINCT'));
|
||||
|
||||
$this->assertEquals(
|
||||
1 | Token::FLAG_KEYWORD_RESERVED | Token::FLAG_KEYWORD_COMPOSED | Token::FLAG_KEYWORD_KEY,
|
||||
Context::isKeyword('PRIMARY KEY')
|
||||
);
|
||||
$this->assertEquals(
|
||||
1 | Token::FLAG_KEYWORD_RESERVED | Token::FLAG_KEYWORD_COMPOSED,
|
||||
Context::isKeyword('CHARACTER SET')
|
||||
);
|
||||
|
||||
$this->assertEquals(1 | Token::FLAG_KEYWORD_RESERVED, Context::isKeyword('FROM', true));
|
||||
$this->assertNull(Context::isKeyword('MODIFY', true));
|
||||
|
||||
$this->assertNull(Context::isKeyword('foo'));
|
||||
$this->assertNull(Context::isKeyword('bar baz'));
|
||||
}
|
||||
|
||||
public function testIsOperator(): void
|
||||
{
|
||||
$this->assertEquals(Token::FLAG_OPERATOR_ARITHMETIC, Context::isOperator('%'));
|
||||
$this->assertEquals(Token::FLAG_OPERATOR_LOGICAL, Context::isOperator('!'));
|
||||
$this->assertEquals(Token::FLAG_OPERATOR_LOGICAL, Context::isOperator('&&'));
|
||||
$this->assertEquals(Token::FLAG_OPERATOR_LOGICAL, Context::isOperator('<=>'));
|
||||
$this->assertEquals(Token::FLAG_OPERATOR_BITWISE, Context::isOperator('&'));
|
||||
$this->assertEquals(Token::FLAG_OPERATOR_ASSIGNMENT, Context::isOperator(':='));
|
||||
$this->assertEquals(Token::FLAG_OPERATOR_SQL, Context::isOperator(','));
|
||||
|
||||
$this->assertNull(Context::isOperator('a'));
|
||||
}
|
||||
|
||||
public function testIsWhitespace(): void
|
||||
{
|
||||
$this->assertTrue(Context::isWhitespace(' '));
|
||||
$this->assertTrue(Context::isWhitespace("\r"));
|
||||
$this->assertTrue(Context::isWhitespace("\n"));
|
||||
$this->assertTrue(Context::isWhitespace("\t"));
|
||||
|
||||
$this->assertFalse(Context::isWhitespace('a'));
|
||||
$this->assertFalse(Context::isWhitespace("\b"));
|
||||
$this->assertFalse(Context::isWhitespace("\u1000"));
|
||||
}
|
||||
|
||||
public function testIsComment(): void
|
||||
{
|
||||
$this->assertEquals(Token::FLAG_COMMENT_BASH, Context::isComment('#'));
|
||||
$this->assertEquals(Token::FLAG_COMMENT_C, Context::isComment('/*'));
|
||||
$this->assertEquals(Token::FLAG_COMMENT_C, Context::isComment('*/'));
|
||||
$this->assertEquals(Token::FLAG_COMMENT_SQL, Context::isComment('-- '));
|
||||
$this->assertEquals(Token::FLAG_COMMENT_SQL, Context::isComment("--\t"));
|
||||
$this->assertEquals(Token::FLAG_COMMENT_SQL, Context::isComment("--\n"));
|
||||
|
||||
$this->assertEquals(Token::FLAG_COMMENT_BASH, Context::isComment('# a comment'));
|
||||
$this->assertEquals(Token::FLAG_COMMENT_C, Context::isComment('/*comment */'));
|
||||
$this->assertEquals(Token::FLAG_COMMENT_SQL, Context::isComment('-- my comment'));
|
||||
|
||||
$this->assertNull(Context::isComment(''));
|
||||
$this->assertNull(Context::isComment('--not a comment'));
|
||||
}
|
||||
|
||||
public function testIsBool(): void
|
||||
{
|
||||
$this->assertTrue(Context::isBool('true'));
|
||||
$this->assertTrue(Context::isBool('false'));
|
||||
|
||||
$this->assertFalse(Context::isBool('tru'));
|
||||
$this->assertFalse(Context::isBool('falsee'));
|
||||
}
|
||||
|
||||
public function testIsNumber(): void
|
||||
{
|
||||
$this->assertTrue(Context::isNumber('+'));
|
||||
$this->assertTrue(Context::isNumber('-'));
|
||||
$this->assertTrue(Context::isNumber('.'));
|
||||
$this->assertTrue(Context::isNumber('0'));
|
||||
$this->assertTrue(Context::isNumber('1'));
|
||||
$this->assertTrue(Context::isNumber('2'));
|
||||
$this->assertTrue(Context::isNumber('3'));
|
||||
$this->assertTrue(Context::isNumber('4'));
|
||||
$this->assertTrue(Context::isNumber('5'));
|
||||
$this->assertTrue(Context::isNumber('6'));
|
||||
$this->assertTrue(Context::isNumber('7'));
|
||||
$this->assertTrue(Context::isNumber('8'));
|
||||
$this->assertTrue(Context::isNumber('9'));
|
||||
$this->assertTrue(Context::isNumber('e'));
|
||||
$this->assertTrue(Context::isNumber('E'));
|
||||
}
|
||||
|
||||
public function testIsString(): void
|
||||
{
|
||||
$this->assertEquals(Token::FLAG_STRING_SINGLE_QUOTES, Context::isString("'"));
|
||||
$this->assertEquals(Token::FLAG_STRING_DOUBLE_QUOTES, Context::isString('"'));
|
||||
|
||||
$this->assertEquals(Token::FLAG_STRING_SINGLE_QUOTES, Context::isString("'foo bar'"));
|
||||
$this->assertEquals(Token::FLAG_STRING_DOUBLE_QUOTES, Context::isString('"foo bar"'));
|
||||
|
||||
$this->assertNull(Context::isString(''));
|
||||
$this->assertNull(Context::isString('foo bar'));
|
||||
}
|
||||
|
||||
public function testIsSymbol(): void
|
||||
{
|
||||
$this->assertEquals(Token::FLAG_SYMBOL_VARIABLE, Context::isSymbol('@'));
|
||||
$this->assertEquals(Token::FLAG_SYMBOL_BACKTICK, Context::isSymbol('`'));
|
||||
|
||||
$this->assertEquals(Token::FLAG_SYMBOL_VARIABLE, Context::isSymbol('@id'));
|
||||
$this->assertEquals(Token::FLAG_SYMBOL_BACKTICK, Context::isSymbol('`id`'));
|
||||
|
||||
$this->assertNull(Context::isSymbol(''));
|
||||
$this->assertNull(Context::isSymbol('id'));
|
||||
}
|
||||
|
||||
public function testisSeparator(): void
|
||||
{
|
||||
$this->assertTrue(Context::isSeparator('+'));
|
||||
$this->assertTrue(Context::isSeparator('.'));
|
||||
|
||||
$this->assertFalse(Context::isSeparator('1'));
|
||||
$this->assertFalse(Context::isSeparator('E'));
|
||||
$this->assertFalse(Context::isSeparator('_'));
|
||||
}
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Lexer;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Exceptions\LexerException;
|
||||
use PhpMyAdmin\SqlParser\Lexer;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
class LexerTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function testError(): void
|
||||
{
|
||||
$lexer = new Lexer('');
|
||||
|
||||
$lexer->error('error #1', 'foo', 1, 2);
|
||||
$lexer->error(
|
||||
sprintf('%2$s #%1$d', 2, 'error'),
|
||||
'bar',
|
||||
3,
|
||||
4
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
$lexer->errors,
|
||||
[
|
||||
new LexerException('error #1', 'foo', 1, 2),
|
||||
new LexerException('error #2', 'bar', 3, 4),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function testErrorStrict(): void
|
||||
{
|
||||
$this->expectExceptionCode(4);
|
||||
$this->expectExceptionMessage('strict error');
|
||||
$this->expectException(LexerException::class);
|
||||
$lexer = new Lexer('');
|
||||
$lexer->strict = true;
|
||||
|
||||
$lexer->error('strict error', 'foo', 1, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider lexProvider
|
||||
*/
|
||||
public function testLex(string $test): void
|
||||
{
|
||||
$this->runParserTest($test);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function lexProvider(): array
|
||||
{
|
||||
return [
|
||||
['lexer/lex'],
|
||||
['lexer/lexUtf8'],
|
||||
['lexer/lexBool'],
|
||||
['lexer/lexComment'],
|
||||
['lexer/lexCommentEnd'],
|
||||
['lexer/lexDelimiter'],
|
||||
['lexer/lexDelimiter2'],
|
||||
['lexer/lexDelimiterErr1'],
|
||||
['lexer/lexDelimiterErr2'],
|
||||
['lexer/lexDelimiterErr3'],
|
||||
['lexer/lexDelimiterLen'],
|
||||
['lexer/lexKeyword'],
|
||||
['lexer/lexKeyword2'],
|
||||
['lexer/lexNumber'],
|
||||
['lexer/lexOperator'],
|
||||
['lexer/lexOperatorStarIsArithmetic'],
|
||||
['lexer/lexOperatorStarIsWildcard'],
|
||||
['lexer/lexEmptyCStyleComment'],
|
||||
['lexer/lexString'],
|
||||
['lexer/lexStringErr1'],
|
||||
['lexer/lexSymbol'],
|
||||
['lexer/lexSymbolErr1'],
|
||||
['lexer/lexSymbolErr2'],
|
||||
['lexer/lexSymbolErr3'],
|
||||
['lexer/lexSymbolUser1'],
|
||||
['lexer/lexSymbolUser2'],
|
||||
['lexer/lexSymbolUser3'],
|
||||
['lexer/lexSymbolUser4_mariadb_100400'],
|
||||
['lexer/lexSymbolUser5_mariadb_100400'],
|
||||
['lexer/lexWhitespace'],
|
||||
['lexer/lexLabel1'],
|
||||
['lexer/lexLabel2'],
|
||||
['lexer/lexNoLabel'],
|
||||
['lexer/lexWildcardThenComment'],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Lexer;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
use PhpMyAdmin\SqlParser\Token;
|
||||
|
||||
class TokenTest extends TestCase
|
||||
{
|
||||
public function testExtractKeyword(): void
|
||||
{
|
||||
$tok = new Token('SelecT', Token::TYPE_KEYWORD, Token::FLAG_KEYWORD_RESERVED);
|
||||
$this->assertEquals($tok->value, 'SELECT');
|
||||
|
||||
$tok = new Token('aS', Token::TYPE_KEYWORD, Token::FLAG_KEYWORD_RESERVED);
|
||||
$this->assertEquals($tok->value, 'AS');
|
||||
}
|
||||
|
||||
public function testExtractWhitespace(): void
|
||||
{
|
||||
$tok = new Token(" \t \r \n ", Token::TYPE_WHITESPACE);
|
||||
$this->assertEquals($tok->value, ' ');
|
||||
}
|
||||
|
||||
public function testExtractBool(): void
|
||||
{
|
||||
$tok = new Token('false', Token::TYPE_BOOL);
|
||||
$this->assertFalse($tok->value);
|
||||
|
||||
$tok = new Token('True', Token::TYPE_BOOL);
|
||||
$this->assertTrue($tok->value);
|
||||
}
|
||||
|
||||
public function testExtractNumber(): void
|
||||
{
|
||||
$tok = new Token('--42', Token::TYPE_NUMBER, Token::FLAG_NUMBER_NEGATIVE);
|
||||
$this->assertEquals($tok->value, 42);
|
||||
|
||||
$tok = new Token('---42', Token::TYPE_NUMBER, Token::FLAG_NUMBER_NEGATIVE);
|
||||
$this->assertEquals($tok->value, -42);
|
||||
|
||||
$tok = new Token('0xFE', Token::TYPE_NUMBER, Token::FLAG_NUMBER_HEX);
|
||||
$this->assertEquals($tok->value, 0xFE);
|
||||
|
||||
$tok = new Token('-0xEF', Token::TYPE_NUMBER, Token::FLAG_NUMBER_NEGATIVE | Token::FLAG_NUMBER_HEX);
|
||||
$this->assertEquals($tok->value, -0xEF);
|
||||
|
||||
$tok = new Token('3.14', Token::TYPE_NUMBER, Token::FLAG_NUMBER_FLOAT);
|
||||
$this->assertEquals($tok->value, 3.14);
|
||||
}
|
||||
|
||||
public function testExtractString(): void
|
||||
{
|
||||
$tok = new Token('"foo bar "', Token::TYPE_STRING);
|
||||
$this->assertEquals($tok->value, 'foo bar ');
|
||||
|
||||
$tok = new Token("' bar foo '", Token::TYPE_STRING);
|
||||
$this->assertEquals($tok->value, ' bar foo ');
|
||||
|
||||
$tok = new Token("'\''", Token::TYPE_STRING);
|
||||
$this->assertEquals($tok->value, '\'');
|
||||
|
||||
$tok = new Token('"\c\d\e\f\g\h\i\j\k\l\m\p\q\s\u\v\w\x\y\z"', Token::TYPE_STRING);
|
||||
$this->assertEquals($tok->value, 'cdefghijklmpqsuvwxyz');
|
||||
}
|
||||
|
||||
public function testExtractSymbol(): void
|
||||
{
|
||||
$tok = new Token('@foo', Token::TYPE_SYMBOL, Token::FLAG_SYMBOL_VARIABLE);
|
||||
$this->assertEquals($tok->value, 'foo');
|
||||
|
||||
$tok = new Token('`foo`', Token::TYPE_SYMBOL, Token::FLAG_SYMBOL_BACKTICK);
|
||||
$this->assertEquals($tok->value, 'foo');
|
||||
|
||||
$tok = new Token('@`foo`', Token::TYPE_SYMBOL, Token::FLAG_SYMBOL_VARIABLE);
|
||||
$this->assertEquals($tok->value, 'foo');
|
||||
|
||||
$tok = new Token(':foo', Token::TYPE_SYMBOL, Token::FLAG_SYMBOL_PARAMETER);
|
||||
$this->assertEquals($tok->value, 'foo');
|
||||
|
||||
$tok = new Token('?', Token::TYPE_SYMBOL, Token::FLAG_SYMBOL_PARAMETER);
|
||||
$this->assertEquals($tok->value, '?');
|
||||
}
|
||||
|
||||
public function testInlineToken(): void
|
||||
{
|
||||
$token = new Token(" \r \n \t ");
|
||||
$this->assertEquals($token->getInlineToken(), ' \r \n \t ');
|
||||
}
|
||||
}
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Lexer;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
use PhpMyAdmin\SqlParser\Token;
|
||||
use PhpMyAdmin\SqlParser\TokensList;
|
||||
|
||||
use function count;
|
||||
|
||||
class TokensListTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* ArrayObj of tokens that are used for testing.
|
||||
*
|
||||
* @var Token[]
|
||||
*/
|
||||
public $tokens;
|
||||
|
||||
/**
|
||||
* Test setup.
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->tokens = [
|
||||
new Token('SELECT', Token::TYPE_KEYWORD),
|
||||
new Token(' ', Token::TYPE_WHITESPACE),
|
||||
new Token('*', Token::TYPE_OPERATOR),
|
||||
new Token(' ', Token::TYPE_WHITESPACE),
|
||||
new Token('FROM', Token::TYPE_KEYWORD, Token::FLAG_KEYWORD_RESERVED),
|
||||
new Token(' ', Token::TYPE_WHITESPACE),
|
||||
new Token('`test`', Token::TYPE_SYMBOL),
|
||||
new Token(' ', Token::TYPE_WHITESPACE),
|
||||
new Token('WHERE', Token::TYPE_KEYWORD, Token::FLAG_KEYWORD_RESERVED),
|
||||
new Token(' ', Token::TYPE_WHITESPACE),
|
||||
new Token('name', Token::TYPE_NONE),
|
||||
new Token('=', Token::TYPE_OPERATOR),
|
||||
new Token('fa', Token::TYPE_NONE),
|
||||
];
|
||||
}
|
||||
|
||||
public function testBuild(): void
|
||||
{
|
||||
$list = new TokensList($this->tokens);
|
||||
$this->assertEquals('SELECT * FROM `test` WHERE name=fa', TokensList::build($list));
|
||||
}
|
||||
|
||||
public function testAdd(): void
|
||||
{
|
||||
$list = new TokensList();
|
||||
foreach ($this->tokens as $token) {
|
||||
$list->add($token);
|
||||
}
|
||||
|
||||
$this->assertEquals(new TokensList($this->tokens), $list);
|
||||
}
|
||||
|
||||
public function testGetNext(): void
|
||||
{
|
||||
$list = new TokensList($this->tokens);
|
||||
$this->assertEquals($this->tokens[0], $list->getNext());
|
||||
$this->assertEquals($this->tokens[2], $list->getNext());
|
||||
$this->assertEquals($this->tokens[4], $list->getNext());
|
||||
$this->assertEquals($this->tokens[6], $list->getNext());
|
||||
$this->assertEquals($this->tokens[8], $list->getNext());
|
||||
$this->assertEquals($this->tokens[10], $list->getNext());
|
||||
$this->assertEquals($this->tokens[11], $list->getNext());
|
||||
$this->assertEquals($this->tokens[12], $list->getNext());
|
||||
$this->assertNull($list->getNext());
|
||||
}
|
||||
|
||||
public function testGetPrevious(): void
|
||||
{
|
||||
$list = new TokensList($this->tokens);
|
||||
$list->idx = 7;
|
||||
$this->assertEquals($this->tokens[6], $list->getPrevious());
|
||||
$this->assertEquals($this->tokens[4], $list->getPrevious());
|
||||
$this->assertEquals($this->tokens[2], $list->getPrevious());
|
||||
$this->assertEquals($this->tokens[0], $list->getPrevious());
|
||||
$this->assertNull($list->getPrevious());
|
||||
}
|
||||
|
||||
public function testGetNextOfType(): void
|
||||
{
|
||||
$list = new TokensList($this->tokens);
|
||||
$this->assertEquals($this->tokens[0], $list->getNextOfType(Token::TYPE_KEYWORD));
|
||||
$this->assertEquals($this->tokens[4], $list->getNextOfType([Token::TYPE_KEYWORD]));
|
||||
$this->assertEquals($this->tokens[6], $list->getNextOfType([Token::TYPE_KEYWORD, Token::TYPE_SYMBOL]));
|
||||
$this->assertEquals($this->tokens[8], $list->getNextOfType([Token::TYPE_KEYWORD, Token::TYPE_SYMBOL]));
|
||||
$this->assertNull($list->getNextOfType(Token::TYPE_KEYWORD));
|
||||
}
|
||||
|
||||
public function testGetPreviousOfType(): void
|
||||
{
|
||||
$list = new TokensList($this->tokens);
|
||||
$list->idx = 9;
|
||||
$this->assertEquals($this->tokens[8], $list->getPreviousOfType([Token::TYPE_KEYWORD, Token::TYPE_SYMBOL]));
|
||||
$this->assertEquals($this->tokens[6], $list->getPreviousOfType([Token::TYPE_KEYWORD, Token::TYPE_SYMBOL]));
|
||||
$this->assertEquals($this->tokens[4], $list->getPreviousOfType([Token::TYPE_KEYWORD]));
|
||||
$this->assertEquals($this->tokens[0], $list->getPreviousOfType(Token::TYPE_KEYWORD));
|
||||
$this->assertNull($list->getPreviousOfType(Token::TYPE_KEYWORD));
|
||||
}
|
||||
|
||||
public function testGetNextOfTypeAndFlag(): void
|
||||
{
|
||||
$list = new TokensList($this->tokens);
|
||||
$this->assertEquals($this->tokens[4], $list->getNextOfTypeAndFlag(
|
||||
Token::TYPE_KEYWORD,
|
||||
Token::FLAG_KEYWORD_RESERVED
|
||||
));
|
||||
$this->assertEquals($this->tokens[8], $list->getNextOfTypeAndFlag(
|
||||
Token::TYPE_KEYWORD,
|
||||
Token::FLAG_KEYWORD_RESERVED
|
||||
));
|
||||
$this->assertNull($list->getNextOfTypeAndFlag(Token::TYPE_KEYWORD, Token::FLAG_KEYWORD_RESERVED));
|
||||
}
|
||||
|
||||
public function testGetNextOfTypeAndValue(): void
|
||||
{
|
||||
$list = new TokensList($this->tokens);
|
||||
$this->assertEquals($this->tokens[0], $list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'SELECT'));
|
||||
$this->assertNull($list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'SELECT'));
|
||||
}
|
||||
|
||||
public function testArrayAccess(): void
|
||||
{
|
||||
$list = new TokensList();
|
||||
|
||||
// offsetSet(NULL, $value)
|
||||
foreach ($this->tokens as $token) {
|
||||
$list[] = $token;
|
||||
}
|
||||
|
||||
// offsetSet($offset, $value)
|
||||
$list[2] = $this->tokens[2];
|
||||
|
||||
// offsetSet($offset, $value) with overflowed offset
|
||||
$list[$list->count] = $this->tokens[1];
|
||||
$this->assertSame($list[$list->count - 1], $this->tokens[1]);
|
||||
$this->assertSame($list->count, count($this->tokens) + 1);
|
||||
|
||||
// offsetGet($offset) with negative offset
|
||||
$this->assertNull($list[-1]);
|
||||
|
||||
// offsetGet($offset) with overflow offset
|
||||
$this->assertNull($list[$list->count]);
|
||||
|
||||
// offsetGet($offset)
|
||||
for ($i = 0, $count = count($this->tokens); $i < $count; ++$i) {
|
||||
$this->assertSame($this->tokens[$i], $list[$i]);
|
||||
}
|
||||
|
||||
// offsetExists($offset)
|
||||
$this->assertArrayHasKey(2, $list);
|
||||
$this->assertArrayNotHasKey(14, $list);
|
||||
$this->assertArrayNotHasKey(-8, $list);
|
||||
|
||||
// offsetUnset($offset)
|
||||
$currentCountTokens = $list->count;
|
||||
unset($list[2]);
|
||||
$newCountTokens = $list->count;
|
||||
$this->assertSame($this->tokens[3], $list[2]);
|
||||
$this->assertSame($currentCountTokens - 1, $newCountTokens);
|
||||
|
||||
// offsetUnset($offset) with invalid offset (negative or overflowed)
|
||||
$currentListTokens = $list->tokens;
|
||||
unset($list[-1]);
|
||||
$this->assertSame($currentListTokens, $list->tokens);
|
||||
$this->assertSame($newCountTokens, $list->count); // No unset actually performed.
|
||||
unset($list[999]);
|
||||
$this->assertSame($currentListTokens, $list->tokens);
|
||||
$this->assertSame($newCountTokens, $list->count); // No unset actually performed.
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Misc;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class BugsTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider bugProvider
|
||||
*/
|
||||
public function testBug(string $test): void
|
||||
{
|
||||
$this->runParserTest($test);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function bugProvider(): array
|
||||
{
|
||||
return [
|
||||
['bugs/fuzz1'],
|
||||
['bugs/fuzz2'],
|
||||
['bugs/fuzz3'],
|
||||
['bugs/fuzz4'],
|
||||
['bugs/gh9'],
|
||||
['bugs/gh14'],
|
||||
['bugs/gh16'],
|
||||
['bugs/gh202'],
|
||||
['bugs/gh234'],
|
||||
['bugs/gh317'],
|
||||
['bugs/gh412'],
|
||||
['bugs/gh478'],
|
||||
['bugs/gh492'],
|
||||
['bugs/gh496'],
|
||||
['bugs/gh498'],
|
||||
['bugs/gh499'],
|
||||
['bugs/gh508'],
|
||||
['bugs/gh511'],
|
||||
['bugs/pma11800'],
|
||||
['bugs/pma11836'],
|
||||
['bugs/pma11843'],
|
||||
['bugs/pma11879'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Misc;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class ParameterTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider parameterProvider
|
||||
*/
|
||||
public function testParameter(string $test): void
|
||||
{
|
||||
$this->runParserTest($test);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function parameterProvider(): array
|
||||
{
|
||||
return [
|
||||
['misc/parseParameter'],
|
||||
['misc/parseParameter2'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Misc;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
use PhpMyAdmin\SqlParser\UtfString;
|
||||
use Throwable;
|
||||
|
||||
use function chr;
|
||||
|
||||
class UtfStringTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* Sample phrase in French.
|
||||
*/
|
||||
public const TEST_PHRASE = 'Les naïfs ægithales hâtifs pondant à Noël où il '
|
||||
. 'gèle sont sûrs d\'être déçus en voyant leurs drôles d\'œufs abîmés.';
|
||||
|
||||
/**
|
||||
* The length of the sample phrase.
|
||||
*/
|
||||
public const TEST_PHRASE_LEN = 113;
|
||||
|
||||
public function testArrayAccess(): void
|
||||
{
|
||||
$str = new UtfString(self::TEST_PHRASE);
|
||||
|
||||
// offsetExists
|
||||
$this->assertArrayHasKey(self::TEST_PHRASE_LEN - 1, $str);
|
||||
$this->assertArrayNotHasKey(-1, $str);
|
||||
$this->assertArrayNotHasKey(self::TEST_PHRASE_LEN, $str);
|
||||
|
||||
// offsetGet
|
||||
$this->assertEquals('.', $str[self::TEST_PHRASE_LEN - 1]);
|
||||
$this->assertNull($str[-1]);
|
||||
$this->assertNull($str[self::TEST_PHRASE_LEN]);
|
||||
}
|
||||
|
||||
public function testSet(): void
|
||||
{
|
||||
$this->expectExceptionMessage('Not implemented.');
|
||||
$this->expectException(Throwable::class);
|
||||
$str = new UtfString('');
|
||||
$str[0] = 'a';
|
||||
}
|
||||
|
||||
public function testUnset(): void
|
||||
{
|
||||
$this->expectExceptionMessage('Not implemented.');
|
||||
$this->expectException(Throwable::class);
|
||||
$str = new UtfString('');
|
||||
unset($str[0]);
|
||||
}
|
||||
|
||||
public function testGetCharLength(): void
|
||||
{
|
||||
$this->assertEquals(1, UtfString::getCharLength(chr(0x00))); // 00000000
|
||||
$this->assertEquals(1, UtfString::getCharLength(chr(0x7F))); // 01111111
|
||||
|
||||
$this->assertEquals(2, UtfString::getCharLength(chr(0xC0))); // 11000000
|
||||
$this->assertEquals(2, UtfString::getCharLength(chr(0xDF))); // 11011111
|
||||
|
||||
$this->assertEquals(3, UtfString::getCharLength(chr(0xE0))); // 11100000
|
||||
$this->assertEquals(3, UtfString::getCharLength(chr(0xEF))); // 11101111
|
||||
|
||||
$this->assertEquals(4, UtfString::getCharLength(chr(0xF0))); // 11110000
|
||||
$this->assertEquals(4, UtfString::getCharLength(chr(0xF7))); // 11110111
|
||||
|
||||
$this->assertEquals(5, UtfString::getCharLength(chr(0xF8))); // 11111000
|
||||
$this->assertEquals(5, UtfString::getCharLength(chr(0xFB))); // 11111011
|
||||
|
||||
$this->assertEquals(6, UtfString::getCharLength(chr(0xFC))); // 11111100
|
||||
$this->assertEquals(6, UtfString::getCharLength(chr(0xFD))); // 11111101
|
||||
}
|
||||
|
||||
public function testToString(): void
|
||||
{
|
||||
$str = new UtfString(self::TEST_PHRASE);
|
||||
$this->assertEquals(self::TEST_PHRASE, (string) $str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test access to string.
|
||||
*
|
||||
* @dataProvider utf8StringsProvider
|
||||
*/
|
||||
public function testAccess(string $text, ?string $pos10, ?string $pos20): void
|
||||
{
|
||||
$str = new UtfString($text);
|
||||
$this->assertEquals($pos10, $str->offsetGet(10));
|
||||
$this->assertEquals($pos20, $str->offsetGet(20));
|
||||
$this->assertEquals($pos10, $str->offsetGet(10));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array<int, string|null>>
|
||||
* @psalm-return array<string, array{string, (string|null), (string|null)}>
|
||||
*/
|
||||
public static function utf8StringsProvider(): array
|
||||
{
|
||||
return [
|
||||
'ascii' => [
|
||||
'abcdefghijklmnopqrstuvwxyz',
|
||||
'k',
|
||||
'u',
|
||||
],
|
||||
'unicode' => [
|
||||
'áéíóúýěřťǔǐǒǎšďȟǰǩľžčǚň',
|
||||
'ǐ',
|
||||
'č',
|
||||
],
|
||||
'emoji' => [
|
||||
'😂😄😃😀😊😉😍😘😚😗😂👿😮😨😱😠😡😤😖😆😋👯',
|
||||
'😂',
|
||||
'😋',
|
||||
],
|
||||
'iso' => [
|
||||
"P\xf8\xed\xb9ern\xec \xbelu\xbbou\xe8k\xfd k\xf3d \xfap\xecl \xef\xe1belsk\xe9 k\xf3dy",
|
||||
null,
|
||||
null,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Parser;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class AlterStatementTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider alterProvider
|
||||
*/
|
||||
public function testAlter(string $test): void
|
||||
{
|
||||
$this->runParserTest($test);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function alterProvider(): array
|
||||
{
|
||||
return [
|
||||
['parser/parseAlter'],
|
||||
['parser/parseAlter2'],
|
||||
['parser/parseAlter3'],
|
||||
['parser/parseAlter4'],
|
||||
['parser/parseAlter5'],
|
||||
['parser/parseAlter6'],
|
||||
['parser/parseAlter7'],
|
||||
['parser/parseAlter8'],
|
||||
['parser/parseAlter9'],
|
||||
['parser/parseAlter10'],
|
||||
['parser/parseAlter11'],
|
||||
['parser/parseAlter12'],
|
||||
['parser/parseAlter13'],
|
||||
['parser/parseAlter14'],
|
||||
['parser/parseAlterErr'],
|
||||
['parser/parseAlterErr2'],
|
||||
['parser/parseAlterErr3'],
|
||||
['parser/parseAlterErr4'],
|
||||
['parser/parseAlterTableRenameIndex1'],
|
||||
['parser/parseAlterTableRenameIndex2'],
|
||||
['parser/parseAlterTableRenameKey1'],
|
||||
['parser/parseAlterTableRenameKey2'],
|
||||
['parser/parseAlterTablePartitionByRange1'],
|
||||
['parser/parseAlterTablePartitionByRange2'],
|
||||
['parser/parseAlterTableCoalescePartition'],
|
||||
['parser/parseAlterTableAddColumnWithCheck'],
|
||||
['parser/parseAlterTableAddSpatialIndex1'],
|
||||
['parser/parseAlterTableDropAddIndex1'],
|
||||
['parser/parseAlterTableDropColumn1'],
|
||||
['parser/parseAlterTableModifyColumn'],
|
||||
['parser/parseAlterTableModifyColumnEnum1'],
|
||||
['parser/parseAlterTableModifyColumnEnum2'],
|
||||
['parser/parseAlterTableModifyColumnEnum3'],
|
||||
['parser/parseAlterWithInvisible'],
|
||||
['parser/parseAlterTableCharacterSet1'],
|
||||
['parser/parseAlterTableCharacterSet2'],
|
||||
['parser/parseAlterTableCharacterSet3'],
|
||||
['parser/parseAlterTableCharacterSet4'],
|
||||
['parser/parseAlterTableCharacterSet5'],
|
||||
['parser/parseAlterTableCharacterSet6'],
|
||||
['parser/parseAlterTableCharacterSet7'],
|
||||
['parser/parseAlterUser'],
|
||||
['parser/parseAlterUser1'],
|
||||
['parser/parseAlterUser2'],
|
||||
['parser/parseAlterUser3'],
|
||||
['parser/parseAlterUser4'],
|
||||
['parser/parseAlterUser5'],
|
||||
['parser/parseAlterUser6'],
|
||||
['parser/parseAlterUser7'],
|
||||
['parser/parseAlterUser8'],
|
||||
['parser/parseAlterUser9'],
|
||||
['parser/parseAlterUser10'],
|
||||
['parser/parseAlterEvent'],
|
||||
['parser/parseAlterEvent2'],
|
||||
['parser/parseAlterEvent3'],
|
||||
['parser/parseAlterEvent4'],
|
||||
['parser/parseAlterEvent5'],
|
||||
['parser/parseAlterEvent6'],
|
||||
['parser/parseAlterEvent7'],
|
||||
['parser/parseAlterEvent8'],
|
||||
['parser/parseAlterEvent9'],
|
||||
['parser/parseAlterEventComplete'],
|
||||
['parser/parseAlterEventErr'],
|
||||
['parser/parseAlterEventOnScheduleAt'],
|
||||
['parser/parseAlterEventOnScheduleAt2'],
|
||||
['parser/parseAlterEventOnScheduleEvery'],
|
||||
['parser/parseAlterEventOnScheduleEvery2'],
|
||||
['parser/parseAlterEventOnScheduleEvery3'],
|
||||
['parser/parseAlterEventOnScheduleEvery4'],
|
||||
['parser/parseAlterEventOnScheduleEvery5'],
|
||||
['parser/parseAlterEventOnScheduleEvery6'],
|
||||
['parser/parseAlterEventWithDefiner'],
|
||||
['parser/parseAlterEventWithOtherDefiners'],
|
||||
['parser/parseAlterRenameColumn'],
|
||||
['parser/parseAlterRenameColumns'],
|
||||
['parser/parseAlterFunction1'],
|
||||
['parser/parseAlterFunction2'],
|
||||
['parser/parseAlterFunction3'],
|
||||
['parser/parseAlterFunction4'],
|
||||
['parser/parseAlterFunction5'],
|
||||
['parser/parseAlterFunction6'],
|
||||
['parser/parseAlterProcedure1'],
|
||||
['parser/parseAlterProcedure2'],
|
||||
['parser/parseAlterProcedure3'],
|
||||
['parser/parseAlterProcedure4'],
|
||||
['parser/parseAlterProcedure5'],
|
||||
['parser/parseAlterProcedure6'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Parser;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class AnalyzeStatementTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider analyzeProvider
|
||||
*/
|
||||
public function testAnalyze(string $test): void
|
||||
{
|
||||
$this->runParserTest($test);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function analyzeProvider(): array
|
||||
{
|
||||
return [
|
||||
['parser/parseAnalyzeTable'],
|
||||
['parser/parseAnalyzeTable1'],
|
||||
['parser/parseAnalyzeErr1'],
|
||||
['parser/parseAnalyzeErr2'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Parser;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class CallStatementTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider callProvider
|
||||
*/
|
||||
public function testCall(string $test): void
|
||||
{
|
||||
$this->runParserTest($test);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function callProvider(): array
|
||||
{
|
||||
return [
|
||||
['parser/parseCall'],
|
||||
['parser/parseCall2'],
|
||||
['parser/parseCall3'],
|
||||
['parser/parseCall4'],
|
||||
['parser/parseCall5'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Parser;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class CreateStatementTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider createProvider
|
||||
*/
|
||||
public function testCreate(string $test): void
|
||||
{
|
||||
$this->runParserTest($test);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function createProvider(): array
|
||||
{
|
||||
return [
|
||||
['parser/parseCreateDatabase'],
|
||||
['parser/parseCreateDatabaseErr'],
|
||||
['parser/parseCreateFunction'],
|
||||
['parser/parseCreateFunctionErr1'],
|
||||
['parser/parseCreateFunctionErr2'],
|
||||
['parser/parseCreateFunctionErr3'],
|
||||
['parser/parseCreateProcedure'],
|
||||
['parser/parseCreateProcedure1'],
|
||||
['parser/parseCreateProcedure2'],
|
||||
['parser/parseCreateProcedure3'],
|
||||
['parser/parseCreateProcedure4'],
|
||||
['parser/parseCreateSchema'],
|
||||
['parser/parseCreateSchemaErr'],
|
||||
['parser/parseCreateTable'],
|
||||
['parser/parseCreateTable2'],
|
||||
['parser/parseCreateTable3'],
|
||||
['parser/parseCreateTable4'],
|
||||
['parser/parseCreateTable5'],
|
||||
['parser/parseCreateTable6'],
|
||||
['parser/parseCreateTable7'],
|
||||
['parser/parseCreateTable8'],
|
||||
['parser/parseCreateTable9'],
|
||||
['parser/parseCreateTable10'],
|
||||
['parser/parseCreateTable11'],
|
||||
['parser/parseCreateTable12'],
|
||||
['parser/parseCreateTable13'],
|
||||
['parser/parseCreateTable14'],
|
||||
['parser/parseCreateTable15'],
|
||||
['parser/parseCreateTable16'],
|
||||
['parser/parseCreateTable17'],
|
||||
['parser/parseCreateTable18'],
|
||||
['parser/parseCreateTableErr1'],
|
||||
['parser/parseCreateTableErr2'],
|
||||
['parser/parseCreateTableErr3'],
|
||||
['parser/parseCreateTableErr4'],
|
||||
['parser/parseCreateTableErr5'],
|
||||
['parser/parseCreateTableSelect'],
|
||||
['parser/parseCreateTableAsSelect'],
|
||||
['parser/parseCreateTableLike'],
|
||||
['parser/parseCreateTableSpatial'],
|
||||
['parser/parseCreateTableSRID'],
|
||||
['parser/parseCreateTableTimestampWithPrecision'],
|
||||
['parser/parseCreateTableEnforcedCheck'],
|
||||
['parser/parseCreateTableNotEnforcedCheck'],
|
||||
['parser/parseCreateTableWithInvisibleKey'],
|
||||
['parser/parseCreateTableWithParser'],
|
||||
['parser/parseCreateTrigger'],
|
||||
['parser/parseCreateUser1'],
|
||||
['parser/parseCreateUser2'],
|
||||
['parser/parseCreateView'],
|
||||
['parser/parseCreateView2'],
|
||||
['parser/parseCreateView3'],
|
||||
['parser/parseCreateView4'],
|
||||
['parser/parseCreateView5'],
|
||||
['parser/parseCreateViewMultiple'],
|
||||
['parser/parseCreateViewWithoutQuotes'],
|
||||
['parser/parseCreateViewWithQuotes'],
|
||||
['parser/parseCreateViewWithWrongSyntax'],
|
||||
['parser/parseCreateViewWithUnion'],
|
||||
['parser/parseCreateViewAsWithAs'],
|
||||
['parser/parseCreateOrReplaceView1'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Parser;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class DeleteStatementTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider deleteProvider
|
||||
*/
|
||||
public function testDelete(string $test): void
|
||||
{
|
||||
$this->runParserTest($test);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function deleteProvider(): array
|
||||
{
|
||||
return [
|
||||
['parser/parseDelete'],
|
||||
['parser/parseDelete2'],
|
||||
['parser/parseDelete3'],
|
||||
['parser/parseDelete4'],
|
||||
['parser/parseDelete5'],
|
||||
['parser/parseDelete6'],
|
||||
['parser/parseDelete7'],
|
||||
['parser/parseDelete8'],
|
||||
['parser/parseDelete9'],
|
||||
['parser/parseDelete10'],
|
||||
['parser/parseDelete11'],
|
||||
['parser/parseDelete12'],
|
||||
['parser/parseDeleteErr1'],
|
||||
['parser/parseDeleteErr2'],
|
||||
['parser/parseDeleteErr3'],
|
||||
['parser/parseDeleteErr4'],
|
||||
['parser/parseDeleteErr5'],
|
||||
['parser/parseDeleteErr6'],
|
||||
['parser/parseDeleteErr7'],
|
||||
['parser/parseDeleteErr8'],
|
||||
['parser/parseDeleteErr9'],
|
||||
['parser/parseDeleteErr10'],
|
||||
['parser/parseDeleteErr11'],
|
||||
['parser/parseDeleteErr12'],
|
||||
['parser/parseDeleteJoin'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Parser;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class DropStatementTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider dropProvider
|
||||
*/
|
||||
public function testDrop(string $test): void
|
||||
{
|
||||
$this->runParserTest($test);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function dropProvider(): array
|
||||
{
|
||||
return [
|
||||
['parser/parseDrop'],
|
||||
['parser/parseDrop2'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Parser;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class ExplainStatementTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider explainProvider
|
||||
*/
|
||||
public function testExplain(string $test): void
|
||||
{
|
||||
$this->runParserTest($test);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function explainProvider(): array
|
||||
{
|
||||
return [
|
||||
['parser/parseExplain'],
|
||||
['parser/parseExplain1'],
|
||||
['parser/parseExplain2'],
|
||||
['parser/parseExplain3'],
|
||||
['parser/parseExplain4'],
|
||||
['parser/parseExplain5'],
|
||||
['parser/parseExplain6'],
|
||||
['parser/parseExplain7'],
|
||||
['parser/parseExplain8'],
|
||||
['parser/parseExplain9'],
|
||||
['parser/parseExplain10'],
|
||||
['parser/parseExplain11'],
|
||||
['parser/parseExplain12'],
|
||||
['parser/parseExplain13'],
|
||||
['parser/parseExplain14'],
|
||||
['parser/parseExplainErr'],
|
||||
['parser/parseExplainErr1'],
|
||||
['parser/parseExplainErr2'],
|
||||
['parser/parseExplainErr3'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Parser;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class InsertStatementTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider insertProvider
|
||||
*/
|
||||
public function testInsert(string $test): void
|
||||
{
|
||||
$this->runParserTest($test);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function insertProvider(): array
|
||||
{
|
||||
return [
|
||||
['parser/parseInsert'],
|
||||
['parser/parseInsertFunction'],
|
||||
['parser/parseInsertSelect'],
|
||||
['parser/parseInsertOnDuplicateKey'],
|
||||
['parser/parseInsertSetOnDuplicateKey'],
|
||||
['parser/parseInsertSelectOnDuplicateKey'],
|
||||
['parser/parseInsertOnDuplicateKeyErr'],
|
||||
['parser/parseInsertErr'],
|
||||
['parser/parseInsertErr2'],
|
||||
['parser/parseInsertIntoErr'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Parser;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Statements\KillStatement;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class KillStatementTest extends TestCase
|
||||
{
|
||||
/** @dataProvider killProvider */
|
||||
public function testKill(string $test): void
|
||||
{
|
||||
$this->runParserTest($test);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function killProvider(): array
|
||||
{
|
||||
return [
|
||||
['parser/parseKill'],
|
||||
['parser/parseKill2'],
|
||||
['parser/parseKill3'],
|
||||
['parser/parseKillConnection'],
|
||||
['parser/parseKillQuery'],
|
||||
['parser/parseKillErr1'],
|
||||
['parser/parseKillErr2'],
|
||||
['parser/parseKillErr3'],
|
||||
['parser/parseKillErr4'],
|
||||
];
|
||||
}
|
||||
|
||||
/** @dataProvider buildKillProvider */
|
||||
public function testBuildKill(string $sql): void
|
||||
{
|
||||
$parser = new Parser($sql);
|
||||
$this->assertCount(1, $parser->statements);
|
||||
$statement = $parser->statements[0];
|
||||
$this->assertInstanceOf(KillStatement::class, $statement);
|
||||
$builtSql = $statement->build();
|
||||
$this->assertEquals($sql, $builtSql);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<int, string>>
|
||||
* @psalm-return list<list<string>>
|
||||
*/
|
||||
public static function buildKillProvider(): array
|
||||
{
|
||||
return [
|
||||
['KILL (SELECT 3 + 4)'],
|
||||
['KILL QUERY 4'],
|
||||
['KILL CONNECTION 5'],
|
||||
['KILL 6'],
|
||||
['KILL QUERY (SELECT 7)'],
|
||||
['KILL SOFT QUERY (SELECT 8)'],
|
||||
['KILL HARD 9'],
|
||||
['KILL USER 10'],
|
||||
['KILL SOFT (SELECT 1)'],
|
||||
['KILL (2)'],
|
||||
['KILL QUERY ID (2)'],
|
||||
['KILL QUERY ID (SELECT ID FROM INFORMATION_SCHEMA.PROCESSLIST LIMIT 0, 1)'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Parser;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class LoadStatementTest extends TestCase
|
||||
{
|
||||
public function testLoadOptions(): void
|
||||
{
|
||||
$data = $this->getData('parser/parseLoad1');
|
||||
$parser = new Parser($data['query']);
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(10, $stmt->options->has('CONCURRENT'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider loadProvider
|
||||
*/
|
||||
public function testLoad(string $test): void
|
||||
{
|
||||
$this->runParserTest($test);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function loadProvider(): array
|
||||
{
|
||||
return [
|
||||
['parser/parseLoad1'],
|
||||
['parser/parseLoad2'],
|
||||
['parser/parseLoad3'],
|
||||
['parser/parseLoad4'],
|
||||
['parser/parseLoad5'],
|
||||
['parser/parseLoad6'],
|
||||
['parser/parseLoad7'],
|
||||
['parser/parseLoad8'],
|
||||
['parser/parseLoadErr1'],
|
||||
['parser/parseLoadErr2'],
|
||||
['parser/parseLoadErr3'],
|
||||
['parser/parseLoadErr4'],
|
||||
['parser/parseLoadErr5'],
|
||||
['parser/parseLoadErr6'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Parser;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class LockStatementTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider lockProvider
|
||||
*/
|
||||
public function testLock(string $test): void
|
||||
{
|
||||
$this->runParserTest($test);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function lockProvider(): array
|
||||
{
|
||||
return [
|
||||
['parser/parseLock1'],
|
||||
['parser/parseLock2'],
|
||||
['parser/parseLock3'],
|
||||
['parser/parseLock4'],
|
||||
['parser/parseLock5'],
|
||||
['parser/parseLockErr1'],
|
||||
['parser/parseLockErr2'],
|
||||
['parser/parseLockErr3'],
|
||||
['parser/parseLockErr4'],
|
||||
['parser/parseLockErr5'],
|
||||
['parser/parseLockErr6'],
|
||||
['parser/parseLockErr7'],
|
||||
['parser/parseLockErr8'],
|
||||
['parser/parseLockErr9'],
|
||||
['parser/parseLockErr10'],
|
||||
['parser/parseUnlock1'],
|
||||
['parser/parseUnlockErr1'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Parser;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Statements\SetStatement;
|
||||
use PhpMyAdmin\SqlParser\Statements\TransactionStatement;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class ParserLongExportsTest extends TestCase
|
||||
{
|
||||
public function testMysqldump(): void
|
||||
{
|
||||
$sql = <<<SQL
|
||||
-- MySQL dump 10.13 Distrib 5.7.24, for Linux (x86_64)ldump e-nocleg-2-dev 01-te
|
||||
--
|
||||
-- Host: localhost Database: x
|
||||
-- ------------------------------------------------------
|
||||
-- Server version 5.7.24
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||
/*!40101 SET NAMES utf8 */;
|
||||
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
||||
/*!40103 SET TIME_ZONE='+00:00' */;
|
||||
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
||||
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||
SQL;
|
||||
|
||||
$expectedSql = [
|
||||
'SET @OLD_CHARACTER_SET_CLIENT = @@CHARACTER_SET_CLIENT',
|
||||
'SET @OLD_CHARACTER_SET_RESULTS = @@CHARACTER_SET_RESULTS',
|
||||
'SET @OLD_COLLATION_CONNECTION = @@COLLATION_CONNECTION',
|
||||
'SET NAMES utf8',
|
||||
'SET @OLD_TIME_ZONE = @@TIME_ZONE',
|
||||
"SET TIME_ZONE = '+00:00'",
|
||||
'SET @OLD_UNIQUE_CHECKS = @@UNIQUE_CHECKS, UNIQUE_CHECKS = 0',
|
||||
'SET @OLD_FOREIGN_KEY_CHECKS = @@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS = 0',
|
||||
"SET @OLD_SQL_MODE = @@SQL_MODE, SQL_MODE = 'NO_AUTO_VALUE_ON_ZERO'",
|
||||
'SET @OLD_SQL_NOTES = @@SQL_NOTES, SQL_NOTES = 0',
|
||||
];
|
||||
|
||||
$parser = new Parser($sql, true);
|
||||
$parser->parse();
|
||||
|
||||
$sql = [];
|
||||
foreach ($parser->statements as $stmt) {
|
||||
$sql[] = $stmt->__toString();
|
||||
}
|
||||
|
||||
$this->assertEquals($expectedSql, $sql);
|
||||
}
|
||||
|
||||
public function testParsephpMyAdminDump(): void
|
||||
{
|
||||
$data = $this->getData('parser/parsephpMyAdminExport1');
|
||||
$parser = new Parser($data['query']);
|
||||
$collectedSetStatements = [];
|
||||
foreach ($parser->statements as $statement) {
|
||||
if ($statement instanceof TransactionStatement) {
|
||||
foreach ($statement->statements as $transactionStatement) {
|
||||
if (! $transactionStatement instanceof SetStatement) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$collectedSetStatements[] = $transactionStatement->build();
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! $statement instanceof SetStatement) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$collectedSetStatements[] = $statement->build();
|
||||
}
|
||||
|
||||
$this->assertEquals([
|
||||
'SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"',
|
||||
'SET AUTOCOMMIT = 0',
|
||||
'SET time_zone = "+00:00"',
|
||||
'SET @OLD_CHARACTER_SET_CLIENT = @@CHARACTER_SET_CLIENT',
|
||||
'SET @OLD_CHARACTER_SET_RESULTS = @@CHARACTER_SET_RESULTS',
|
||||
'SET @OLD_COLLATION_CONNECTION = @@COLLATION_CONNECTION',
|
||||
'SET NAMES utf8mb4',
|
||||
'SET CHARACTER_SET_CLIENT = @OLD_CHARACTER_SET_CLIENT',
|
||||
'SET CHARACTER_SET_RESULTS = @OLD_CHARACTER_SET_RESULTS',
|
||||
'SET COLLATION_CONNECTION = @OLD_COLLATION_CONNECTION',
|
||||
], $collectedSetStatements);
|
||||
|
||||
foreach ($parser->statements as $stmt) {
|
||||
// Check they all build
|
||||
$this->assertIsString($stmt->build());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider exportFileProvider
|
||||
*/
|
||||
public function testParseExport(string $test): void
|
||||
{
|
||||
$this->runParserTest($test);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function exportFileProvider(): array
|
||||
{
|
||||
return [
|
||||
['parser/parsephpMyAdminExport1'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Parser;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Exceptions\ParserException;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
use PhpMyAdmin\SqlParser\Token;
|
||||
use PhpMyAdmin\SqlParser\TokensList;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
class ParserTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider parseProvider
|
||||
*/
|
||||
public function testParse(string $test): void
|
||||
{
|
||||
$this->runParserTest($test);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function parseProvider(): array
|
||||
{
|
||||
return [
|
||||
['parser/parse'],
|
||||
['parser/parse2'],
|
||||
['parser/parseDelimiter'],
|
||||
];
|
||||
}
|
||||
|
||||
public function testUnrecognizedStatement(): void
|
||||
{
|
||||
$parser = new Parser('SELECT 1; FROM');
|
||||
$this->assertEquals(
|
||||
'Unrecognized statement type.',
|
||||
$parser->errors[0]->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
public function testUnrecognizedKeyword(): void
|
||||
{
|
||||
$parser = new Parser('SELECT 1 FROM foo PARTITION(bar, baz) AS');
|
||||
$this->assertEquals(
|
||||
'Unrecognized keyword.',
|
||||
$parser->errors[0]->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function testError(): void
|
||||
{
|
||||
$parser = new Parser(new TokensList());
|
||||
|
||||
$parser->error('error #1', new Token('foo'), 1);
|
||||
$parser->error(sprintf('%2$s #%1$d', 2, 'error'), new Token('bar'), 2);
|
||||
|
||||
$this->assertEquals(
|
||||
$parser->errors,
|
||||
[
|
||||
new ParserException('error #1', new Token('foo'), 1),
|
||||
new ParserException('error #2', new Token('bar'), 2),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function testErrorStrict(): void
|
||||
{
|
||||
$this->expectExceptionCode(3);
|
||||
$this->expectExceptionMessage('strict error');
|
||||
$this->expectException(ParserException::class);
|
||||
$parser = new Parser(new TokensList());
|
||||
$parser->strict = true;
|
||||
|
||||
$parser->error('strict error', new Token('foo'), 3);
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Parser;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class PurgeStatementTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider purgeProvider
|
||||
*/
|
||||
public function testPurge(string $test): void
|
||||
{
|
||||
$this->runParserTest($test);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function purgeProvider(): array
|
||||
{
|
||||
return [
|
||||
['parser/parsePurge'],
|
||||
['parser/parsePurge2'],
|
||||
['parser/parsePurge3'],
|
||||
['parser/parsePurge4'],
|
||||
['parser/parsePurgeErr'],
|
||||
['parser/parsePurgeErr2'],
|
||||
['parser/parsePurgeErr3'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Parser;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class RenameStatementTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider renameProvider
|
||||
*/
|
||||
public function testRename(string $test): void
|
||||
{
|
||||
$this->runParserTest($test);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function renameProvider(): array
|
||||
{
|
||||
return [
|
||||
['parser/parseRename'],
|
||||
['parser/parseRename2'],
|
||||
['parser/parseRenameErr1'],
|
||||
['parser/parseRenameErr2'],
|
||||
['parser/parseRenameErr3'],
|
||||
['parser/parseRenameErr4'],
|
||||
['parser/parseRenameErr5'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Parser;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class ReplaceStatementTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider replaceProvider
|
||||
*/
|
||||
public function testReplace(string $test): void
|
||||
{
|
||||
$this->runParserTest($test);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function replaceProvider(): array
|
||||
{
|
||||
return [
|
||||
['parser/parseReplace'],
|
||||
['parser/parseReplace2'],
|
||||
['parser/parseReplaceValues'],
|
||||
['parser/parseReplaceSet'],
|
||||
['parser/parseReplaceSelect'],
|
||||
['parser/parseReplaceErr'],
|
||||
['parser/parseReplaceErr2'],
|
||||
['parser/parseReplaceErr3'],
|
||||
['parser/parseReplaceIntoErr'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Parser;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class RestoreStatementTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider restoreProvider
|
||||
*/
|
||||
public function testRestore(string $test): void
|
||||
{
|
||||
$this->runParserTest($test);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function restoreProvider(): array
|
||||
{
|
||||
return [
|
||||
['parser/parseRestore'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Parser;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class SelectStatementTest extends TestCase
|
||||
{
|
||||
public function testSelectOptions(): void
|
||||
{
|
||||
$data = $this->getData('parser/parseSelect');
|
||||
$parser = new Parser($data['query']);
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(10, $stmt->options->has('MAX_STATEMENT_TIME'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider selectProvider
|
||||
*/
|
||||
public function testSelect(string $test): void
|
||||
{
|
||||
$this->runParserTest($test);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function selectProvider(): array
|
||||
{
|
||||
return [
|
||||
['parser/parseSelect2'],
|
||||
['parser/parseSelect3'],
|
||||
['parser/parseSelect4'],
|
||||
['parser/parseSelect5'],
|
||||
['parser/parseSelect6'],
|
||||
['parser/parseSelect7'],
|
||||
['parser/parseSelect8'],
|
||||
['parser/parseSelect9'],
|
||||
['parser/parseSelect10'],
|
||||
['parser/parseSelect11'],
|
||||
['parser/parseSelect12'],
|
||||
['parser/parseSelect13'],
|
||||
['parser/parseSelect14'],
|
||||
['parser/parseSelect15'],
|
||||
['parser/parseSelect16'],
|
||||
['parser/parseSelectAggregateWithPartitionAndAlias'],
|
||||
['parser/parseSelectErr1'],
|
||||
['parser/parseSelectErr2'],
|
||||
['parser/parseSelectNested'],
|
||||
['parser/parseSelectCase1'],
|
||||
['parser/parseSelectCase2'],
|
||||
['parser/parseSelectCase3'],
|
||||
['parser/parseSelectCase4'],
|
||||
['parser/parseSelectCase5'],
|
||||
['parser/parseSelectCaseErr1'],
|
||||
['parser/parseSelectCaseErr2'],
|
||||
['parser/parseSelectCaseErr3'],
|
||||
['parser/parseSelectCaseErr4'],
|
||||
['parser/parseSelectCaseErr5'],
|
||||
['parser/parseSelectCaseAlias1'],
|
||||
['parser/parseSelectCaseAlias2'],
|
||||
['parser/parseSelectCaseAlias3'],
|
||||
['parser/parseSelectCaseAlias4'],
|
||||
['parser/parseSelectCaseAlias5'],
|
||||
['parser/parseSelectCaseAlias6'],
|
||||
['parser/parseSelectCaseAliasErr1'],
|
||||
['parser/parseSelectCaseAliasErr2'],
|
||||
['parser/parseSelectCaseAliasErr3'],
|
||||
['parser/parseSelectCaseAliasErr4'],
|
||||
['parser/parseSelectExists'],
|
||||
['parser/parseSelectIntoOptions1'],
|
||||
['parser/parseSelectIntoOptions2'],
|
||||
['parser/parseSelectIntoOptions3'],
|
||||
['parser/parseSelectJoinCross'],
|
||||
['parser/parseSelectJoinNatural'],
|
||||
['parser/parseSelectJoinNaturalLeft'],
|
||||
['parser/parseSelectJoinNaturalRight'],
|
||||
['parser/parseSelectJoinNaturalLeftOuter'],
|
||||
['parser/parseSelectJoinNaturalRightOuter'],
|
||||
['parser/parseSelectJoinMultiple'],
|
||||
['parser/parseSelectJoinMultiple2'],
|
||||
['parser/parseSelectWrongOrder'],
|
||||
['parser/parseSelectWrongOrder2'],
|
||||
['parser/parseSelectEndOptions1'],
|
||||
['parser/parseSelectEndOptions2'],
|
||||
['parser/parseSelectEndOptionsErr'],
|
||||
['parser/parseSelectUnion'],
|
||||
['parser/parseSelectUnion2'],
|
||||
['parser/parseSelectWhere'],
|
||||
['parser/parseSelectWhereCollate'],
|
||||
['parser/parseSelectIndexHint1'],
|
||||
['parser/parseSelectIndexHint2'],
|
||||
['parser/parseSelectOrderByIsNull'],
|
||||
['parser/parseSelectIndexHintErr1'],
|
||||
['parser/parseSelectIndexHintErr2'],
|
||||
['parser/parseSelectIndexHintErr3'],
|
||||
['parser/parseSelectIndexHintErr4'],
|
||||
['parser/parseSelectWithParenthesis'],
|
||||
['parser/parseSelectOrderByComment'],
|
||||
['parser/parseSelectOverAlias_mariadb_100600'],
|
||||
['parser/parseSelectGroupBy'],
|
||||
['parser/parseSelectGroupByErr'],
|
||||
['parser/parseSelectGroupByWithComments'],
|
||||
['parser/parseTable1'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Parser;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class SetStatementTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider setProvider
|
||||
*/
|
||||
public function testSet(string $test): void
|
||||
{
|
||||
$this->runParserTest($test);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function setProvider(): array
|
||||
{
|
||||
return [
|
||||
['parser/parseSetCharset'],
|
||||
['parser/parseSetCharsetError'],
|
||||
['parser/parseSetCharacterSet'],
|
||||
['parser/parseSetCharacterSetError'],
|
||||
['parser/parseAlterTableSetAutoIncrementError'],
|
||||
['parser/parseSetNames'],
|
||||
['parser/parseSetNamesError'],
|
||||
['parser/parseSetError1'],
|
||||
['parser/parseInsertIntoSet'],
|
||||
['parser/parseSetVariable'],
|
||||
['parser/parseSetVariable2'],
|
||||
['parser/parseSetGlobalVariable'],
|
||||
];
|
||||
}
|
||||
}
|
||||
Vendored
+47
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Parser;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class TransactionStatementTest extends TestCase
|
||||
{
|
||||
public function testBuildWithoutEnd(): void
|
||||
{
|
||||
$data = $this->getData('parser/parseTransaction4');
|
||||
$parser = new Parser($data['query']);
|
||||
$stmt = $parser->statements[0];
|
||||
$this->assertEquals(
|
||||
'START TRANSACTION;SET time_zone = "+00:00";',
|
||||
$stmt->build()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider transactionProvider
|
||||
*/
|
||||
public function testTransaction(string $test): void
|
||||
{
|
||||
$this->runParserTest($test);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function transactionProvider(): array
|
||||
{
|
||||
return [
|
||||
['parser/parseTransaction'],
|
||||
['parser/parseTransaction2'],
|
||||
['parser/parseTransaction3'],
|
||||
['parser/parseTransaction4'],
|
||||
['parser/parseTransaction5'],
|
||||
['parser/parseTransaction6'],
|
||||
['parser/parseTransaction7'],
|
||||
['parser/parseTransactionErr1'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Parser;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
|
||||
class UpdateStatementTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider updateProvider
|
||||
*/
|
||||
public function testUpdate(string $test): void
|
||||
{
|
||||
$this->runParserTest($test);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function updateProvider(): array
|
||||
{
|
||||
return [
|
||||
['parser/parseUpdate1'],
|
||||
['parser/parseUpdate2'],
|
||||
['parser/parseUpdate3'],
|
||||
['parser/parseUpdate4'],
|
||||
['parser/parseUpdate5'],
|
||||
['parser/parseUpdate6'],
|
||||
['parser/parseUpdate7'],
|
||||
['parser/parseUpdateErr'],
|
||||
['parser/parseUpdateEmptySet'],
|
||||
];
|
||||
}
|
||||
}
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Parser;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\WithKeyword;
|
||||
use PhpMyAdmin\SqlParser\Lexer;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
use stdClass;
|
||||
|
||||
class WithStatementTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider parseWith
|
||||
*/
|
||||
public function testParse(string $test): void
|
||||
{
|
||||
$this->runParserTest($test);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function parseWith(): array
|
||||
{
|
||||
return [
|
||||
['parser/parseWithStatement'],
|
||||
['parser/parseWithStatement1'],
|
||||
['parser/parseWithStatement2'],
|
||||
['parser/parseWithStatement3'],
|
||||
['parser/parseWithStatement4'],
|
||||
['parser/parseWithStatement5'],
|
||||
['parser/parseWithStatement6'],
|
||||
['parser/parseWithStatement7'],
|
||||
['parser/parseWithStatementErr'],
|
||||
['parser/parseWithStatementErr1'],
|
||||
['parser/parseWithStatementErr2'],
|
||||
['parser/parseWithStatementErr3'],
|
||||
['parser/parseWithStatementErr4'],
|
||||
['parser/parseWithStatementErr5'],
|
||||
['parser/parseWithStatementErr6'],
|
||||
['parser/parseWithStatementErr7'],
|
||||
['parser/parseWithStatementErr8'],
|
||||
];
|
||||
}
|
||||
|
||||
public function testWith(): void
|
||||
{
|
||||
$sql = <<<SQL
|
||||
WITH categories(identifier, name, parent_id) AS (
|
||||
SELECT c.identifier, c.name, c.parent_id FROM category c WHERE c.identifier = 'a'
|
||||
UNION ALL
|
||||
SELECT c.identifier, c.name, c.parent_id FROM categories, category c WHERE c.identifier = categories.parent_id
|
||||
), foo AS ( SELECT * FROM test )
|
||||
SELECT * FROM categories
|
||||
SQL;
|
||||
|
||||
$lexer = new Lexer($sql);
|
||||
|
||||
$lexerErrors = $this->getErrorsAsArray($lexer);
|
||||
$this->assertCount(0, $lexerErrors);
|
||||
$parser = new Parser($lexer->list);
|
||||
$parserErrors = $this->getErrorsAsArray($parser);
|
||||
$this->assertCount(0, $parserErrors);
|
||||
$this->assertCount(1, $parser->statements);
|
||||
|
||||
// phpcs:disable Generic.Files.LineLength.TooLong
|
||||
$expected = <<<SQL
|
||||
WITH categories(identifier, name, parent_id) AS (SELECT c.identifier, c.name, c.parent_id FROM category AS `c` WHERE c.identifier = 'a' UNION ALL SELECT c.identifier, c.name, c.parent_id FROM categories, category AS `c` WHERE c.identifier = categories.parent_id), foo AS (SELECT * FROM test) SELECT * FROM categories
|
||||
SQL;
|
||||
// phpcs:enable
|
||||
$this->assertEquals($expected, $parser->statements[0]->build());
|
||||
}
|
||||
|
||||
public function testWithHasErrors(): void
|
||||
{
|
||||
$sql = <<<SQL
|
||||
WITH categories(identifier, name, parent_id) AS (
|
||||
SOMETHING * FROM foo
|
||||
)
|
||||
SELECT * FROM categories
|
||||
SQL;
|
||||
|
||||
$lexer = new Lexer($sql);
|
||||
|
||||
$lexerErrors = $this->getErrorsAsArray($lexer);
|
||||
$this->assertCount(0, $lexerErrors);
|
||||
$parser = new Parser($lexer->list);
|
||||
$parserErrors = $this->getErrorsAsArray($parser);
|
||||
$this->assertCount(4, $parserErrors);
|
||||
}
|
||||
|
||||
public function testWithEmbedParenthesis(): void
|
||||
{
|
||||
$sql = <<<SQL
|
||||
WITH categories AS (
|
||||
SELECT * FROM (SELECT * FROM foo)
|
||||
)
|
||||
SELECT * FROM categories
|
||||
SQL;
|
||||
|
||||
$lexer = new Lexer($sql);
|
||||
$lexerErrors = $this->getErrorsAsArray($lexer);
|
||||
$this->assertCount(0, $lexerErrors);
|
||||
$parser = new Parser($lexer->list);
|
||||
$parserErrors = $this->getErrorsAsArray($parser);
|
||||
$this->assertCount(0, $parserErrors);
|
||||
|
||||
// phpcs:disable Generic.Files.LineLength.TooLong
|
||||
$expected = <<<SQL
|
||||
WITH categories AS (SELECT * FROM (SELECT * FROM foo)) SELECT * FROM categories
|
||||
SQL;
|
||||
// phpcs:enable
|
||||
$this->assertEquals($expected, $parser->statements[0]->build());
|
||||
}
|
||||
|
||||
public function testWithHasUnclosedParenthesis(): void
|
||||
{
|
||||
$sql = <<<SQL
|
||||
WITH categories(identifier, name, parent_id) AS (
|
||||
SELECT * FROM (SELECT * FROM foo
|
||||
)
|
||||
SELECT * FROM categories
|
||||
SQL;
|
||||
|
||||
$lexer = new Lexer($sql);
|
||||
|
||||
$lexerErrors = $this->getErrorsAsArray($lexer);
|
||||
$this->assertCount(0, $lexerErrors);
|
||||
$parser = new Parser($lexer->list);
|
||||
$parserErrors = $this->getErrorsAsArray($parser);
|
||||
$this->assertEquals($parserErrors[0][0], 'A closing bracket was expected.');
|
||||
}
|
||||
|
||||
public function testBuildWrongWithKeyword(): void
|
||||
{
|
||||
$this->expectExceptionMessage('Can not build a component that is not a WithKeyword');
|
||||
WithKeyword::build(new stdClass());
|
||||
}
|
||||
|
||||
public function testBuildBadWithKeyword(): void
|
||||
{
|
||||
$this->expectExceptionMessage('No statement inside WITH');
|
||||
WithKeyword::build(new WithKeyword('test'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Context;
|
||||
use PhpMyAdmin\SqlParser\Exceptions\LexerException;
|
||||
use PhpMyAdmin\SqlParser\Exceptions\ParserException;
|
||||
use PhpMyAdmin\SqlParser\Lexer;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Token;
|
||||
use PhpMyAdmin\SqlParser\TokensList;
|
||||
use PhpMyAdmin\SqlParser\Tools\CustomJsonSerializer;
|
||||
use PHPUnit\Framework\TestCase as BaseTestCase;
|
||||
|
||||
use function file_get_contents;
|
||||
use function str_contains;
|
||||
use function strpos;
|
||||
use function substr;
|
||||
|
||||
/**
|
||||
* Implements useful methods for testing.
|
||||
*/
|
||||
abstract class TestCase extends BaseTestCase
|
||||
{
|
||||
public function setUp(): void
|
||||
{
|
||||
global $lang;
|
||||
// This line makes sure the test suite uses English so we can assert
|
||||
// on the error messages, if it is not here you will need to use
|
||||
// LC_ALL=C ./vendor/bin/phpunit
|
||||
// Users can have French language as default on their OS
|
||||
// That would make the assertions fail
|
||||
$lang = 'en';
|
||||
Context::load();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the token list generated by lexing this query.
|
||||
*
|
||||
* @param string $query the query to be lexed
|
||||
*/
|
||||
public function getTokensList(string $query): TokensList
|
||||
{
|
||||
$lexer = new Lexer($query);
|
||||
|
||||
return $lexer->list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the errors as an array.
|
||||
*
|
||||
* @param Lexer|Parser $obj object containing the errors
|
||||
*
|
||||
* @return array<int, array<int, Token|string|int>>
|
||||
* @psalm-return (
|
||||
* $obj is Lexer
|
||||
* ? list<array{string, string, int, int}>
|
||||
* : list<array{string, Token|null, int}>
|
||||
* )
|
||||
*/
|
||||
public function getErrorsAsArray($obj): array
|
||||
{
|
||||
$ret = [];
|
||||
if ($obj instanceof Lexer) {
|
||||
/** @var LexerException $err */
|
||||
foreach ($obj->errors as $err) {
|
||||
$ret[] = [$err->getMessage(), $err->ch, $err->pos, (int) $err->getCode()];
|
||||
}
|
||||
} elseif ($obj instanceof Parser) {
|
||||
/** @var ParserException $err */
|
||||
foreach ($obj->errors as $err) {
|
||||
$ret[] = [$err->getMessage(), $err->token, (int) $err->getCode()];
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets test's input and expected output.
|
||||
*
|
||||
* @param string $name the name of the test
|
||||
*
|
||||
* @return array<string, string|Lexer|Parser|array<string, array<int, int|string|Token>[]>|null>
|
||||
* @psalm-return array{
|
||||
* query: string,
|
||||
* lexer: Lexer,
|
||||
* parser: Parser|null,
|
||||
* errors: array{lexer: list<array{string, string, int, int}>, parser: list<array{string, Token, int}>}
|
||||
* }
|
||||
*/
|
||||
public function getData(string $name): array
|
||||
{
|
||||
$serializedData = file_get_contents('tests/data/' . $name . '.out');
|
||||
$this->assertIsString($serializedData);
|
||||
|
||||
$serializer = new CustomJsonSerializer();
|
||||
$data = $serializer->unserialize($serializedData);
|
||||
|
||||
$this->assertIsArray($data);
|
||||
$this->assertArrayHasKey('query', $data);
|
||||
$this->assertArrayHasKey('lexer', $data);
|
||||
$this->assertArrayHasKey('parser', $data);
|
||||
$this->assertArrayHasKey('errors', $data);
|
||||
$this->assertIsString($data['query']);
|
||||
$this->assertInstanceOf(Lexer::class, $data['lexer']);
|
||||
if ($data['parser'] !== null) {
|
||||
$this->assertInstanceOf(Parser::class, $data['parser']);
|
||||
}
|
||||
|
||||
$this->assertIsArray($data['errors']);
|
||||
$this->assertArrayHasKey('lexer', $data['errors']);
|
||||
$this->assertArrayHasKey('parser', $data['errors']);
|
||||
$this->assertIsArray($data['errors']['lexer']);
|
||||
$this->assertIsArray($data['errors']['parser']);
|
||||
|
||||
$data['query'] = file_get_contents('tests/data/' . $name . '.in');
|
||||
$this->assertIsString($data['query']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a test.
|
||||
*
|
||||
* @param string $name the name of the test
|
||||
*/
|
||||
public function runParserTest(string $name): void
|
||||
{
|
||||
/**
|
||||
* Test's data.
|
||||
*/
|
||||
$data = $this->getData($name);
|
||||
|
||||
if (str_contains($name, '/ansi/')) {
|
||||
// set mode if appropriate
|
||||
Context::setMode(Context::SQL_MODE_ANSI_QUOTES);
|
||||
}
|
||||
|
||||
$mariaDbPos = strpos($name, '_mariadb_');
|
||||
if ($mariaDbPos !== false) {// Keep in sync with TestGenerator.php
|
||||
// set context
|
||||
$mariaDbVersion = (int) substr($name, $mariaDbPos + 9, 6);
|
||||
Context::load('MariaDb' . $mariaDbVersion);
|
||||
}
|
||||
|
||||
// Lexer.
|
||||
$lexer = new Lexer($data['query']);
|
||||
$lexerErrors = $this->getErrorsAsArray($lexer);
|
||||
$lexer->errors = [];
|
||||
|
||||
// Parser.
|
||||
$parser = empty($data['parser']) ? null : new Parser($lexer->list);
|
||||
$parserErrors = [];
|
||||
if ($parser !== null) {
|
||||
$parserErrors = $this->getErrorsAsArray($parser);
|
||||
$parser->errors = [];
|
||||
}
|
||||
|
||||
// Testing objects.
|
||||
$this->assertEquals($data['lexer'], $lexer);
|
||||
$this->assertEquals($data['parser'], $parser);
|
||||
|
||||
// Testing errors.
|
||||
$this->assertEquals($data['errors']['parser'], $parserErrors);
|
||||
$this->assertEquals($data['errors']['lexer'], $lexerErrors);
|
||||
|
||||
// reset mode after test run
|
||||
Context::setMode();
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Tools;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
use PhpMyAdmin\SqlParser\Token;
|
||||
use PhpMyAdmin\SqlParser\Tools\ContextGenerator;
|
||||
|
||||
use function file_get_contents;
|
||||
use function getcwd;
|
||||
|
||||
class ContextGeneratorTest extends TestCase
|
||||
{
|
||||
public function testFormatName(): void
|
||||
{
|
||||
$name = ContextGenerator::formatName('Invalid00Format00');
|
||||
$this->assertEquals('Invalid00Format00', $name);
|
||||
|
||||
$name = ContextGenerator::formatName('MySql80000');
|
||||
$this->assertEquals('MySQL 8.0', $name);
|
||||
|
||||
$name = ContextGenerator::formatName('MariaDb100200');
|
||||
$this->assertEquals('MariaDB 10.2', $name);
|
||||
|
||||
$name = ContextGenerator::formatName('MariaDb100000');
|
||||
$this->assertEquals('MariaDB 10.0', $name);
|
||||
|
||||
$name = ContextGenerator::formatName('FutureDBMS45784012500');
|
||||
$this->assertEquals('FutureDBMS 4.57.84.1.25', $name);
|
||||
}
|
||||
|
||||
public function testSortWords(): void
|
||||
{
|
||||
$wordsArray = ['41' => ['GEOMETRYCOLLECTION', 'DATE'], '35' => ['SCHEMA', 'REPEAT', 'VALUES']];
|
||||
ContextGenerator::sortWords($wordsArray);
|
||||
$this->assertEquals([
|
||||
'41' => ['DATE', 'GEOMETRYCOLLECTION'],
|
||||
'35' => ['REPEAT', 'SCHEMA', 'VALUES'],
|
||||
], $wordsArray);
|
||||
}
|
||||
|
||||
public function testReadWords(): void
|
||||
{
|
||||
$testFiles = [getcwd() . '/tests/Tools/contexts/testContext.txt'];
|
||||
$readWords = ContextGenerator::readWords($testFiles);
|
||||
$this->assertEquals([
|
||||
Token::FLAG_KEYWORD | Token::FLAG_KEYWORD_RESERVED => [
|
||||
'RESERVED',
|
||||
'RESERVED2',
|
||||
'RESERVED3',
|
||||
'RESERVED4',
|
||||
'RESERVED5',
|
||||
],
|
||||
Token::FLAG_KEYWORD | Token::FLAG_KEYWORD_FUNCTION => ['FUNCTION'],
|
||||
Token::FLAG_KEYWORD | Token::FLAG_KEYWORD_DATA_TYPE => ['DATATYPE'],
|
||||
Token::FLAG_KEYWORD | Token::FLAG_KEYWORD_KEY => ['KEYWORD'],
|
||||
Token::FLAG_KEYWORD => ['NO_FLAG'],
|
||||
Token::FLAG_KEYWORD | Token::FLAG_KEYWORD_RESERVED | Token::FLAG_KEYWORD_COMPOSED => ['COMPOSED KEYWORD'],
|
||||
], $readWords);
|
||||
}
|
||||
|
||||
public function testGenerate(): void
|
||||
{
|
||||
$testFiles = [getcwd() . '/tests/Tools/contexts/testContext.txt'];
|
||||
$readWords = ContextGenerator::readWords($testFiles);
|
||||
ContextGenerator::printWords($readWords);
|
||||
$options = [
|
||||
'keywords' => $readWords,
|
||||
'name' => 'MYSQL TEST',
|
||||
'class' => 'TestContext',
|
||||
'link' => 'https://www.phpmyadmin.net/contribute',
|
||||
];
|
||||
$generatedTemplate = ContextGenerator::generate($options);
|
||||
$expectedTemplate = file_get_contents(getcwd() . '/tests/Tools/templates/TestContext.php');
|
||||
$this->assertEquals($expectedTemplate, $generatedTemplate);
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
RESERVED (R)
|
||||
RESERVED2 (R)
|
||||
RESERVED3 (R)
|
||||
RESERVED4 (R)
|
||||
reserved5 (R)
|
||||
|
||||
FUNCTION (F)
|
||||
DATATYPE (D)
|
||||
KEYWORD (K)
|
||||
|
||||
|
||||
|
||||
NO_FLAG
|
||||
COMPOSED KEYWORD
|
||||
|
||||
FUNCTION
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Contexts;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Context;
|
||||
use PhpMyAdmin\SqlParser\Token;
|
||||
|
||||
/**
|
||||
* Context for MYSQL TEST.
|
||||
*
|
||||
* This class was auto-generated from tools/contexts/*.txt.
|
||||
* Use tools/run_generators.sh for update.
|
||||
*
|
||||
* @see https://www.phpmyadmin.net/contribute
|
||||
*/
|
||||
class TestContext extends Context
|
||||
{
|
||||
/**
|
||||
* List of keywords.
|
||||
*
|
||||
* The value associated to each keyword represents its flags.
|
||||
*
|
||||
* @see Token
|
||||
*
|
||||
* @var array<string,int>
|
||||
* @phpstan-var non-empty-array<non-empty-string,Token::FLAG_KEYWORD_*|int>
|
||||
*/
|
||||
public static $KEYWORDS = [
|
||||
'NO_FLAG' => Token::FLAG_KEYWORD,
|
||||
'RESERVED' => Token::FLAG_KEYWORD | Token::FLAG_KEYWORD_RESERVED,
|
||||
'RESERVED2' => Token::FLAG_KEYWORD | Token::FLAG_KEYWORD_RESERVED,
|
||||
'RESERVED3' => Token::FLAG_KEYWORD | Token::FLAG_KEYWORD_RESERVED,
|
||||
'RESERVED4' => Token::FLAG_KEYWORD | Token::FLAG_KEYWORD_RESERVED,
|
||||
'RESERVED5' => Token::FLAG_KEYWORD | Token::FLAG_KEYWORD_RESERVED,
|
||||
'COMPOSED KEYWORD' => Token::FLAG_KEYWORD | Token::FLAG_KEYWORD_RESERVED | Token::FLAG_KEYWORD_COMPOSED,
|
||||
'DATATYPE' => Token::FLAG_KEYWORD | Token::FLAG_KEYWORD_DATA_TYPE,
|
||||
'KEYWORD' => Token::FLAG_KEYWORD | Token::FLAG_KEYWORD_KEY,
|
||||
'FUNCTION' => Token::FLAG_KEYWORD | Token::FLAG_KEYWORD_FUNCTION,
|
||||
];
|
||||
}
|
||||
+358
@@ -0,0 +1,358 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Utils;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
use PhpMyAdmin\SqlParser\Utils\BufferedQuery;
|
||||
|
||||
use function count;
|
||||
use function str_split;
|
||||
|
||||
class BufferedQueryTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @param array<string, bool> $options
|
||||
* @param string[] $expected
|
||||
* @psalm-param array{delimiter?: non-empty-string, parse_delimiter?: bool, add_delimiter?: bool} $options
|
||||
* @psalm-param positive-int $chunkSize
|
||||
*
|
||||
* @dataProvider extractProvider
|
||||
*/
|
||||
public function testExtract(
|
||||
string $query,
|
||||
int $chunkSize,
|
||||
array $options,
|
||||
array $expected
|
||||
): void {
|
||||
$chunks = str_split($query, $chunkSize);
|
||||
$count = count($chunks);
|
||||
|
||||
/**
|
||||
* The array of extracted statements.
|
||||
*/
|
||||
$statements = [];
|
||||
|
||||
/**
|
||||
* The `BufferedQuery` instance used for extraction.
|
||||
*/
|
||||
$bq = new BufferedQuery('', $options);
|
||||
|
||||
// Feeding chunks and extracting queries.
|
||||
$i = 0;
|
||||
while ($i < $count) {
|
||||
$stmt = $bq->extract();
|
||||
|
||||
if ($stmt) {
|
||||
$statements[] = $stmt;
|
||||
} else {
|
||||
$bq->query .= $chunks[$i++];
|
||||
}
|
||||
}
|
||||
|
||||
// Feeding ended, extracting remaining queries.
|
||||
while ($stmt = $bq->extract(true)) {
|
||||
$statements[] = $stmt;
|
||||
}
|
||||
|
||||
$this->assertEquals($expected, $statements);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<int, int|string|string[]|bool[]>>
|
||||
* @psalm-return list<array{string, positive-int, array{parse_delimiter: bool, add_delimiter: bool}, string[]}>
|
||||
*/
|
||||
public static function extractProvider(): array
|
||||
{
|
||||
$query =
|
||||
'/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;' . "\n" .
|
||||
'/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;' . "\n" .
|
||||
'/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;' . "\n" .
|
||||
'/*!40101 SET NAMES utf8mb4 */;' . "\n" .
|
||||
'' . "\n" .
|
||||
'SET SQL_MODE = \'NO_AUTO_VALUE_ON_ZERO\';' . "\n" .
|
||||
'SET time_zone = "+00:00";' . "\n" .
|
||||
'' . "\n" .
|
||||
'/* a comment */ DELIMITER $$' . "\n" .
|
||||
'' . "\n" .
|
||||
'# Bash-like comment sytanx.' . "\n" .
|
||||
'CREATE DEFINER=`root`@`localhost` PROCEDURE `film_in_stock` (IN `p_film_id` ' .
|
||||
'INT, IN `p_store_id` INT, OUT `p_film_count` INT) READS SQL DATA' . "\n" .
|
||||
'BEGIN' . "\n" .
|
||||
' SELECT inventory_id' . "\n" .
|
||||
' FROM inventory' . "\n" .
|
||||
' WHERE film_id = p_film_id' . "\n" .
|
||||
' AND store_id = p_store_id' . "\n" .
|
||||
' AND inventory_in_stock(inventory_id);' . "\n" .
|
||||
'' . "\n" .
|
||||
' SELECT FOUND_ROWS() INTO p_film_count;' . "\n" .
|
||||
'END$$' . "\n" .
|
||||
'' . "\n" .
|
||||
'DELIMITER ;' . "\n" .
|
||||
'' . "\n" .
|
||||
'-- --------------------------------------------------------' . "\n" .
|
||||
'' . "\n" .
|
||||
'--' . "\n" .
|
||||
'-- Table structure for `actor`' . "\n" .
|
||||
'--' . "\n" .
|
||||
'' . "\n" .
|
||||
'/* C-like comment syntax. */' . "\n" .
|
||||
'CREATE TABLE IF NOT EXISTS `actor` (' . "\n" .
|
||||
'`actor_id` SMALLINT(5) UNSIGNED NOT NULL,' . "\n" .
|
||||
'`first_name` VARCHAR(45) NOT NULL,' . "\n" .
|
||||
'`last_name` VARCHAR(45) NOT NULL,' . "\n" .
|
||||
'`last_update` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP' . "\n" .
|
||||
') ENGINE=InnoDB DEFAULT CHARSET=utf8;' . "\n" .
|
||||
'' . "\n" .
|
||||
'/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;' . "\n" .
|
||||
'/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;' . "\n" .
|
||||
'/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */';
|
||||
|
||||
return [
|
||||
[
|
||||
"SELECT '\'';\nSELECT '\'';",
|
||||
8,
|
||||
[
|
||||
'parse_delimiter' => true,
|
||||
'add_delimiter' => true,
|
||||
],
|
||||
[
|
||||
"SELECT '\'';",
|
||||
"SELECT '\'';",
|
||||
],
|
||||
],
|
||||
|
||||
[
|
||||
'SELECT \\',
|
||||
8,
|
||||
[
|
||||
'parse_delimiter' => false,
|
||||
'add_delimiter' => false,
|
||||
],
|
||||
['SELECT \\'],
|
||||
],
|
||||
|
||||
[
|
||||
"CREATE TABLE `test` (\n" .
|
||||
" `txt` varchar(10)\n" .
|
||||
");\n" .
|
||||
"INSERT INTO `test` (`txt`) VALUES('abc');\n" .
|
||||
"INSERT INTO `test` (`txt`) VALUES('\\\\');\n" .
|
||||
"INSERT INTO `test` (`txt`) VALUES('xyz');\n",
|
||||
8,
|
||||
[
|
||||
'parse_delimiter' => true,
|
||||
'add_delimiter' => true,
|
||||
],
|
||||
[
|
||||
"CREATE TABLE `test` (\n" .
|
||||
" `txt` varchar(10)\n" .
|
||||
');',
|
||||
"INSERT INTO `test` (`txt`) VALUES('abc');",
|
||||
"INSERT INTO `test` (`txt`) VALUES('\\\\');",
|
||||
"INSERT INTO `test` (`txt`) VALUES('xyz');",
|
||||
],
|
||||
],
|
||||
|
||||
[
|
||||
'SELECT """""""";' .
|
||||
'SELECT """\\\\"""',
|
||||
8,
|
||||
[
|
||||
'parse_delimiter' => true,
|
||||
'add_delimiter' => true,
|
||||
],
|
||||
[
|
||||
'SELECT """""""";',
|
||||
'SELECT """\\\\"""',
|
||||
],
|
||||
],
|
||||
|
||||
[
|
||||
'DELIMITER A_VERY_LONG_DEL' . "\n" .
|
||||
'SELECT 1 A_VERY_LONG_DEL' . "\n" .
|
||||
'DELIMITER ;',
|
||||
3,
|
||||
[
|
||||
'parse_delimiter' => true,
|
||||
'add_delimiter' => true,
|
||||
],
|
||||
[
|
||||
'DELIMITER A_VERY_LONG_DEL',
|
||||
'SELECT 1 A_VERY_LONG_DEL',
|
||||
'DELIMITER ;',
|
||||
],
|
||||
],
|
||||
|
||||
[
|
||||
$query,
|
||||
32,
|
||||
[
|
||||
'parse_delimiter' => false,
|
||||
'add_delimiter' => false,
|
||||
],
|
||||
[
|
||||
'/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */',
|
||||
|
||||
'/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */',
|
||||
|
||||
'/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */',
|
||||
|
||||
'/*!40101 SET NAMES utf8mb4 */',
|
||||
|
||||
'SET SQL_MODE = \'NO_AUTO_VALUE_ON_ZERO\'',
|
||||
|
||||
'SET time_zone = "+00:00"',
|
||||
|
||||
'# Bash-like comment sytanx.' . "\n" .
|
||||
'CREATE DEFINER=`root`@`localhost` PROCEDURE `film_in_stock` (IN `p_film_id` ' .
|
||||
'INT, IN `p_store_id` INT, OUT `p_film_count` INT) READS SQL DATA' . "\n" .
|
||||
'BEGIN' . "\n" .
|
||||
' SELECT inventory_id' . "\n" .
|
||||
' FROM inventory' . "\n" .
|
||||
' WHERE film_id = p_film_id' . "\n" .
|
||||
' AND store_id = p_store_id' . "\n" .
|
||||
' AND inventory_in_stock(inventory_id);' . "\n" .
|
||||
'' . "\n" .
|
||||
' SELECT FOUND_ROWS() INTO p_film_count;' . "\n" .
|
||||
'END',
|
||||
|
||||
'-- --------------------------------------------------------' . "\n" .
|
||||
'' . "\n" .
|
||||
'--' . "\n" .
|
||||
'-- Table structure for `actor`' . "\n" .
|
||||
'--' . "\n" .
|
||||
'' . "\n" .
|
||||
'/* C-like comment syntax. */' . "\n" .
|
||||
'CREATE TABLE IF NOT EXISTS `actor` (' . "\n" .
|
||||
'`actor_id` SMALLINT(5) UNSIGNED NOT NULL,' . "\n" .
|
||||
'`first_name` VARCHAR(45) NOT NULL,' . "\n" .
|
||||
'`last_name` VARCHAR(45) NOT NULL,' . "\n" .
|
||||
'`last_update` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP' . "\n" .
|
||||
') ENGINE=InnoDB DEFAULT CHARSET=utf8',
|
||||
|
||||
'/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */',
|
||||
|
||||
'/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */',
|
||||
|
||||
'/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */',
|
||||
],
|
||||
],
|
||||
|
||||
[
|
||||
$query,
|
||||
32,
|
||||
[
|
||||
'parse_delimiter' => true,
|
||||
'add_delimiter' => false,
|
||||
],
|
||||
[
|
||||
'/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */',
|
||||
|
||||
'/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */',
|
||||
|
||||
'/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */',
|
||||
|
||||
'/*!40101 SET NAMES utf8mb4 */',
|
||||
|
||||
'SET SQL_MODE = \'NO_AUTO_VALUE_ON_ZERO\'',
|
||||
|
||||
'SET time_zone = "+00:00"',
|
||||
|
||||
'/* a comment */ DELIMITER $$',
|
||||
|
||||
'# Bash-like comment sytanx.' . "\n" .
|
||||
'CREATE DEFINER=`root`@`localhost` PROCEDURE `film_in_stock` (IN `p_film_id` ' .
|
||||
'INT, IN `p_store_id` INT, OUT `p_film_count` INT) READS SQL DATA' . "\n" .
|
||||
'BEGIN' . "\n" .
|
||||
' SELECT inventory_id' . "\n" .
|
||||
' FROM inventory' . "\n" .
|
||||
' WHERE film_id = p_film_id' . "\n" .
|
||||
' AND store_id = p_store_id' . "\n" .
|
||||
' AND inventory_in_stock(inventory_id);' . "\n" .
|
||||
'' . "\n" .
|
||||
' SELECT FOUND_ROWS() INTO p_film_count;' . "\n" .
|
||||
'END',
|
||||
|
||||
'DELIMITER ;',
|
||||
|
||||
'-- --------------------------------------------------------' . "\n" .
|
||||
'' . "\n" .
|
||||
'--' . "\n" .
|
||||
'-- Table structure for `actor`' . "\n" .
|
||||
'--' . "\n" .
|
||||
'' . "\n" .
|
||||
'/* C-like comment syntax. */' . "\n" .
|
||||
'CREATE TABLE IF NOT EXISTS `actor` (' . "\n" .
|
||||
'`actor_id` SMALLINT(5) UNSIGNED NOT NULL,' . "\n" .
|
||||
'`first_name` VARCHAR(45) NOT NULL,' . "\n" .
|
||||
'`last_name` VARCHAR(45) NOT NULL,' . "\n" .
|
||||
'`last_update` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP' . "\n" .
|
||||
') ENGINE=InnoDB DEFAULT CHARSET=utf8',
|
||||
|
||||
'/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */',
|
||||
|
||||
'/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */',
|
||||
|
||||
'/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */',
|
||||
],
|
||||
],
|
||||
|
||||
[
|
||||
$query,
|
||||
64,
|
||||
[
|
||||
'parse_delimiter' => false,
|
||||
'add_delimiter' => true,
|
||||
],
|
||||
[
|
||||
'/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;',
|
||||
|
||||
'/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;',
|
||||
|
||||
'/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;',
|
||||
|
||||
'/*!40101 SET NAMES utf8mb4 */;',
|
||||
|
||||
'SET SQL_MODE = \'NO_AUTO_VALUE_ON_ZERO\';',
|
||||
|
||||
'SET time_zone = "+00:00";',
|
||||
|
||||
'# Bash-like comment sytanx.' . "\n" .
|
||||
'CREATE DEFINER=`root`@`localhost` PROCEDURE `film_in_stock` (IN `p_film_id` ' .
|
||||
'INT, IN `p_store_id` INT, OUT `p_film_count` INT) READS SQL DATA' . "\n" .
|
||||
'BEGIN' . "\n" .
|
||||
' SELECT inventory_id' . "\n" .
|
||||
' FROM inventory' . "\n" .
|
||||
' WHERE film_id = p_film_id' . "\n" .
|
||||
' AND store_id = p_store_id' . "\n" .
|
||||
' AND inventory_in_stock(inventory_id);' . "\n" .
|
||||
'' . "\n" .
|
||||
' SELECT FOUND_ROWS() INTO p_film_count;' . "\n" .
|
||||
'END$$',
|
||||
|
||||
'-- --------------------------------------------------------' . "\n" .
|
||||
'' . "\n" .
|
||||
'--' . "\n" .
|
||||
'-- Table structure for `actor`' . "\n" .
|
||||
'--' . "\n" .
|
||||
'' . "\n" .
|
||||
'/* C-like comment syntax. */' . "\n" .
|
||||
'CREATE TABLE IF NOT EXISTS `actor` (' . "\n" .
|
||||
'`actor_id` SMALLINT(5) UNSIGNED NOT NULL,' . "\n" .
|
||||
'`first_name` VARCHAR(45) NOT NULL,' . "\n" .
|
||||
'`last_name` VARCHAR(45) NOT NULL,' . "\n" .
|
||||
'`last_update` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP' . "\n" .
|
||||
') ENGINE=InnoDB DEFAULT CHARSET=utf8;',
|
||||
|
||||
'/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;',
|
||||
|
||||
'/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;',
|
||||
|
||||
'/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,482 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Utils;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
use PhpMyAdmin\SqlParser\Utils\CLI;
|
||||
|
||||
use function dirname;
|
||||
use function exec;
|
||||
|
||||
use const PHP_BINARY;
|
||||
|
||||
class CLITest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @param array<string, bool|string>|false $getopt
|
||||
*/
|
||||
private function getCLI($getopt): CLI
|
||||
{
|
||||
$cli = $this->createPartialMock(CLI::class, ['getopt']);
|
||||
$cli->method('getopt')->willReturn($getopt);
|
||||
|
||||
return $cli;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, bool|string>|false $getopt
|
||||
*/
|
||||
private function getCLIStdIn(string $input, $getopt): CLI
|
||||
{
|
||||
$cli = $this->createPartialMock(CLI::class, ['getopt', 'readStdin']);
|
||||
$cli->method('getopt')->willReturn($getopt);
|
||||
$cli->method('readStdin')->willReturn($input);
|
||||
|
||||
return $cli;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that getopt call works.
|
||||
*
|
||||
* We do mock it for other tests to return values we want.
|
||||
*/
|
||||
public function testGetopt(): void
|
||||
{
|
||||
$cli = new CLI();
|
||||
$this->assertEquals(
|
||||
$cli->getopt('', []),
|
||||
[]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, bool|string>|false $getopt
|
||||
*
|
||||
* @dataProvider highlightParamsProvider
|
||||
*/
|
||||
public function testRunHighlight($getopt, string $output, int $result): void
|
||||
{
|
||||
$cli = $this->getCLI($getopt);
|
||||
$this->expectOutputString($output);
|
||||
$this->assertEquals($result, $cli->runHighlight());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<int, int|string|array<string, bool|string>|false>>
|
||||
* @psalm-return list<array{(array<string, bool|string>|false), string, int}>
|
||||
*/
|
||||
public static function highlightParamsProvider(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
['q' => 'SELECT 1'],
|
||||
"\x1b[35mSELECT\n \x1b[92m1\x1b[0m\n",
|
||||
0,
|
||||
],
|
||||
[
|
||||
['query' => 'SELECT 1'],
|
||||
"\x1b[35mSELECT\n \x1b[92m1\x1b[0m\n",
|
||||
0,
|
||||
],
|
||||
[
|
||||
[
|
||||
'q' => 'SELECT /* comment */ 1 /* other */',
|
||||
'f' => 'text',
|
||||
],
|
||||
"SELECT\n /* comment */ 1 /* other */\n",
|
||||
0,
|
||||
],
|
||||
[
|
||||
[
|
||||
'q' => 'SELECT 1',
|
||||
'f' => 'foo',
|
||||
],
|
||||
"ERROR: Invalid value for format!\n",
|
||||
1,
|
||||
],
|
||||
[
|
||||
[
|
||||
'q' => 'SELECT 1',
|
||||
'f' => 'html',
|
||||
],
|
||||
'<span class="sql-reserved">SELECT</span><br/>' .
|
||||
' <span class="sql-number">1</span>' . "\n",
|
||||
0,
|
||||
],
|
||||
[
|
||||
['h' => true],
|
||||
'Usage: highlight-query --query SQL [--format html|cli|text] [--ansi]' . "\n" .
|
||||
' cat file.sql | highlight-query' . "\n",
|
||||
0,
|
||||
],
|
||||
[
|
||||
[],
|
||||
'ERROR: Missing parameters!' . "\n" .
|
||||
'Usage: highlight-query --query SQL [--format html|cli|text] [--ansi]' . "\n" .
|
||||
' cat file.sql | highlight-query' . "\n",
|
||||
1,
|
||||
],
|
||||
[
|
||||
false,
|
||||
'',
|
||||
1,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, bool|string>|false $getopt
|
||||
*
|
||||
* @dataProvider highlightParamsStdInProvider
|
||||
*/
|
||||
public function testRunHighlightStdIn(string $input, $getopt, string $output, int $result): void
|
||||
{
|
||||
$cli = $this->getCLIStdIn($input, $getopt);
|
||||
$this->expectOutputString($output);
|
||||
$this->assertEquals($result, $cli->runHighlight());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<int, int|string|array<string, bool|string>|false>>
|
||||
* @psalm-return list<array{string, (array<string, bool|string>|false), string, int}>
|
||||
*/
|
||||
public static function highlightParamsStdInProvider(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'SELECT 1',
|
||||
[],
|
||||
"\x1b[35mSELECT\n \x1b[92m1\x1b[0m\n",
|
||||
0,
|
||||
],
|
||||
[
|
||||
'SELECT /* comment */ 1 /* other */',
|
||||
['f' => 'text'],
|
||||
"SELECT\n /* comment */ 1 /* other */\n",
|
||||
0,
|
||||
],
|
||||
[
|
||||
'SELECT 1',
|
||||
['f' => 'foo'],
|
||||
"ERROR: Invalid value for format!\n",
|
||||
1,
|
||||
],
|
||||
[
|
||||
'SELECT 1',
|
||||
['f' => 'html'],
|
||||
'<span class="sql-reserved">SELECT</span><br/>' .
|
||||
' <span class="sql-number">1</span>' . "\n",
|
||||
0,
|
||||
],
|
||||
[
|
||||
'',
|
||||
['h' => true],
|
||||
'Usage: highlight-query --query SQL [--format html|cli|text] [--ansi]' . "\n" .
|
||||
' cat file.sql | highlight-query' . "\n",
|
||||
0,
|
||||
],
|
||||
[
|
||||
'',
|
||||
[],
|
||||
'ERROR: Missing parameters!' . "\n" .
|
||||
'Usage: highlight-query --query SQL [--format html|cli|text] [--ansi]' . "\n" .
|
||||
' cat file.sql | highlight-query' . "\n",
|
||||
1,
|
||||
],
|
||||
[
|
||||
'',
|
||||
false,
|
||||
'',
|
||||
1,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, bool|string>|false $getopt
|
||||
*
|
||||
* @dataProvider lintParamsStdInProvider
|
||||
*/
|
||||
public function testRunLintFromStdIn(string $input, $getopt, string $output, int $result): void
|
||||
{
|
||||
$cli = $this->getCLIStdIn($input, $getopt);
|
||||
$this->expectOutputString($output);
|
||||
$this->assertEquals($result, $cli->runLint());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<int, int|string|array<string, bool|string>|false>>
|
||||
* @psalm-return list<array{string, (array<string, bool|string>|false), string, int}>
|
||||
*/
|
||||
public static function lintParamsStdInProvider(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'SELECT 1',
|
||||
[],
|
||||
'',
|
||||
0,
|
||||
],
|
||||
[
|
||||
'SELECT SELECT',
|
||||
[],
|
||||
'#1: An expression was expected. (near "SELECT" at position 7)' . "\n" .
|
||||
'#2: This type of clause was previously parsed. (near "SELECT" at position 7)' . "\n" .
|
||||
'#3: An expression was expected. (near "" at position 0)' . "\n",
|
||||
10,
|
||||
],
|
||||
[
|
||||
'SELECT SELECT',
|
||||
['c' => 'MySql80000'],
|
||||
'#1: An expression was expected. (near "SELECT" at position 7)' . "\n" .
|
||||
'#2: This type of clause was previously parsed. (near "SELECT" at position 7)' . "\n" .
|
||||
'#3: An expression was expected. (near "" at position 0)' . "\n",
|
||||
10,
|
||||
],
|
||||
[
|
||||
'',
|
||||
[],
|
||||
'ERROR: Missing parameters!' . "\n" .
|
||||
'Usage: lint-query --query SQL [--ansi]' . "\n" .
|
||||
' cat file.sql | lint-query' . "\n",
|
||||
1,
|
||||
],
|
||||
[
|
||||
'',
|
||||
['h' => true],
|
||||
'Usage: lint-query --query SQL [--ansi]' . "\n" .
|
||||
' cat file.sql | lint-query' . "\n",
|
||||
0,
|
||||
],
|
||||
[
|
||||
'',
|
||||
false,
|
||||
'',
|
||||
1,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, bool|string>|false $getopt
|
||||
*
|
||||
* @dataProvider lintParamsProvider
|
||||
*/
|
||||
public function testRunLint($getopt, string $output, int $result): void
|
||||
{
|
||||
$cli = $this->getCLI($getopt);
|
||||
$this->expectOutputString($output);
|
||||
$this->assertEquals($result, $cli->runLint());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<int, int|string|array<string, bool|string>|false>>
|
||||
* @psalm-return list<array{(array<string, bool|string>|false), string, int}>
|
||||
*/
|
||||
public static function lintParamsProvider(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
['q' => 'SELECT 1'],
|
||||
'',
|
||||
0,
|
||||
],
|
||||
[
|
||||
['query' => 'SELECT 1'],
|
||||
'',
|
||||
0,
|
||||
],
|
||||
[
|
||||
['q' => 'SELECT SELECT'],
|
||||
'#1: An expression was expected. (near "SELECT" at position 7)' . "\n" .
|
||||
'#2: This type of clause was previously parsed. (near "SELECT" at position 7)' . "\n" .
|
||||
'#3: An expression was expected. (near "" at position 0)' . "\n",
|
||||
10,
|
||||
],
|
||||
[
|
||||
[
|
||||
'q' => 'SELECT SELECT',
|
||||
'c' => 'MySql80000',
|
||||
],
|
||||
'#1: An expression was expected. (near "SELECT" at position 7)' . "\n" .
|
||||
'#2: This type of clause was previously parsed. (near "SELECT" at position 7)' . "\n" .
|
||||
'#3: An expression was expected. (near "" at position 0)' . "\n",
|
||||
10,
|
||||
],
|
||||
[
|
||||
['h' => true],
|
||||
'Usage: lint-query --query SQL [--ansi]' . "\n" .
|
||||
' cat file.sql | lint-query' . "\n",
|
||||
0,
|
||||
],
|
||||
[
|
||||
[],
|
||||
'ERROR: Missing parameters!' . "\n" .
|
||||
'Usage: lint-query --query SQL [--ansi]' . "\n" .
|
||||
' cat file.sql | lint-query' . "\n",
|
||||
1,
|
||||
],
|
||||
[
|
||||
false,
|
||||
'',
|
||||
1,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, bool|string>|false $getopt
|
||||
*
|
||||
* @dataProvider tokenizeParamsProvider
|
||||
*/
|
||||
public function testRunTokenize($getopt, string $output, int $result): void
|
||||
{
|
||||
$cli = $this->getCLI($getopt);
|
||||
$this->expectOutputString($output);
|
||||
$this->assertEquals($result, $cli->runTokenize());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<int, int|string|array<string, bool|string>|false>>
|
||||
* @psalm-return list<array{(array<string, bool|string>|false), string, int}>
|
||||
*/
|
||||
public static function tokenizeParamsProvider(): array
|
||||
{
|
||||
$result = "[TOKEN 0]\nType = 1\nFlags = 3\nValue = 'SELECT'\nToken = 'SELECT'\n\n"
|
||||
. "[TOKEN 1]\nType = 3\nFlags = 0\nValue = ' '\nToken = ' '\n\n"
|
||||
. "[TOKEN 2]\nType = 6\nFlags = 0\nValue = 1\nToken = '1'\n\n"
|
||||
. "[TOKEN 3]\nType = 9\nFlags = 0\nValue = NULL\nToken = NULL\n\n";
|
||||
|
||||
return [
|
||||
[
|
||||
['q' => 'SELECT 1'],
|
||||
$result,
|
||||
0,
|
||||
],
|
||||
[
|
||||
['query' => 'SELECT 1'],
|
||||
$result,
|
||||
0,
|
||||
],
|
||||
[
|
||||
['h' => true],
|
||||
'Usage: tokenize-query --query SQL [--ansi]' . "\n" .
|
||||
' cat file.sql | tokenize-query' . "\n",
|
||||
0,
|
||||
],
|
||||
[
|
||||
[],
|
||||
'ERROR: Missing parameters!' . "\n" .
|
||||
'Usage: tokenize-query --query SQL [--ansi]' . "\n" .
|
||||
' cat file.sql | tokenize-query' . "\n",
|
||||
1,
|
||||
],
|
||||
[
|
||||
false,
|
||||
'',
|
||||
1,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, bool|string>|false $getopt
|
||||
*
|
||||
* @dataProvider tokenizeParamsStdInProvider
|
||||
*/
|
||||
public function testRunTokenizeStdIn(string $input, $getopt, string $output, int $result): void
|
||||
{
|
||||
$cli = $this->getCLIStdIn($input, $getopt);
|
||||
$this->expectOutputString($output);
|
||||
$this->assertEquals($result, $cli->runTokenize());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<int, int|string|array<string, bool|string>|false>>
|
||||
* @psalm-return list<array{string, (array<string, bool|string>|false), string, int}>
|
||||
*/
|
||||
public static function tokenizeParamsStdInProvider(): array
|
||||
{
|
||||
$result = "[TOKEN 0]\nType = 1\nFlags = 3\nValue = 'SELECT'\nToken = 'SELECT'\n\n"
|
||||
. "[TOKEN 1]\nType = 3\nFlags = 0\nValue = ' '\nToken = ' '\n\n"
|
||||
. "[TOKEN 2]\nType = 6\nFlags = 0\nValue = 1\nToken = '1'\n\n"
|
||||
. "[TOKEN 3]\nType = 9\nFlags = 0\nValue = NULL\nToken = NULL\n\n";
|
||||
|
||||
return [
|
||||
[
|
||||
'SELECT 1',
|
||||
[],
|
||||
$result,
|
||||
0,
|
||||
],
|
||||
[
|
||||
'',
|
||||
['h' => true],
|
||||
'Usage: tokenize-query --query SQL [--ansi]' . "\n" .
|
||||
' cat file.sql | tokenize-query' . "\n",
|
||||
0,
|
||||
],
|
||||
[
|
||||
'',
|
||||
[],
|
||||
'ERROR: Missing parameters!' . "\n" .
|
||||
'Usage: tokenize-query --query SQL [--ansi]' . "\n" .
|
||||
' cat file.sql | tokenize-query' . "\n",
|
||||
1,
|
||||
],
|
||||
[
|
||||
'',
|
||||
false,
|
||||
'',
|
||||
1,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider stdinParamsProvider
|
||||
*/
|
||||
public function testStdinPipe(string $cmd, int $result): void
|
||||
{
|
||||
exec($cmd, $out, $ret);
|
||||
$this->assertSame($result, $ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<int, int|string>>
|
||||
* @psalm-return list<array{string, int}>
|
||||
*/
|
||||
public static function stdinParamsProvider(): array
|
||||
{
|
||||
$binPath = PHP_BINARY . ' ' . dirname(__DIR__, 2) . '/bin/';
|
||||
|
||||
return [
|
||||
[
|
||||
'echo "SELECT 1" | ' . $binPath . 'highlight-query',
|
||||
0,
|
||||
],
|
||||
[
|
||||
'echo "invalid query" | ' . $binPath . 'highlight-query',
|
||||
0,
|
||||
],
|
||||
[
|
||||
'echo "SELECT 1" | ' . $binPath . 'lint-query',
|
||||
0,
|
||||
],
|
||||
[
|
||||
'echo "invalid query" | ' . $binPath . 'lint-query',
|
||||
10,
|
||||
],
|
||||
[
|
||||
'echo "SELECT 1" | ' . $binPath . 'tokenize-query',
|
||||
0,
|
||||
],
|
||||
[
|
||||
'echo "invalid query" | ' . $binPath . 'tokenize-query',
|
||||
0,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Utils;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Lexer;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
use PhpMyAdmin\SqlParser\Utils\Error;
|
||||
|
||||
class ErrorTest extends TestCase
|
||||
{
|
||||
public function testGet(): void
|
||||
{
|
||||
$lexer = new Lexer('SELECT * FROM db..tbl }');
|
||||
$parser = new Parser($lexer->list);
|
||||
$this->assertEquals(
|
||||
[
|
||||
[
|
||||
'Unexpected character.',
|
||||
0,
|
||||
'}',
|
||||
22,
|
||||
],
|
||||
[
|
||||
'Unexpected dot.',
|
||||
0,
|
||||
'.',
|
||||
17,
|
||||
],
|
||||
],
|
||||
Error::get([$lexer, $parser])
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetWithNullToken(): void
|
||||
{
|
||||
$lexer = new Lexer('LOCK TABLES table1 AS `t1` LOCAL');
|
||||
$parser = new Parser($lexer->list);
|
||||
$this->assertSame(
|
||||
[
|
||||
['An alias was previously found.', 0, 'LOCAL', 27],
|
||||
['Unexpected keyword.', 0, 'LOCAL', 27],
|
||||
['Unexpected end of LOCK expression.', 0, '', null],
|
||||
],
|
||||
Error::get([$lexer, $parser])
|
||||
);
|
||||
}
|
||||
|
||||
public function testFormat(): void
|
||||
{
|
||||
$this->assertEquals(
|
||||
['#1: error msg (near "token" at position 100)'],
|
||||
Error::format([['error msg', 42, 'token', 100]])
|
||||
);
|
||||
$this->assertEquals(
|
||||
[
|
||||
'#1: error msg (near "token" at position 100)',
|
||||
'#2: error msg (near "token" at position 200)',
|
||||
],
|
||||
Error::format([['error msg', 42, 'token', 100], ['error msg', 42, 'token', 200]])
|
||||
);
|
||||
}
|
||||
}
|
||||
+613
@@ -0,0 +1,613 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Utils;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
use PhpMyAdmin\SqlParser\Utils\Formatter;
|
||||
use ReflectionMethod;
|
||||
|
||||
class FormatterTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @param array<int, array<string, int|string>> $default
|
||||
* @param array<int, array<string, int|string>> $overriding
|
||||
* @param array<int, array<string, int|string>> $expected
|
||||
* @psalm-param list<array{type: int, flags: int, html: string, cli: string, function?: string}> $default
|
||||
* @psalm-param list<array{type?: int, flags?: int, html?: string, cli?: string, function?: string}> $overriding
|
||||
* @psalm-param list<array{type: int, flags: int, html: string, cli: string, function?: string}> $expected
|
||||
*
|
||||
* @dataProvider mergeFormatsProvider
|
||||
*/
|
||||
public function testMergeFormats(array $default, array $overriding, array $expected): void
|
||||
{
|
||||
$formatter = $this->createPartialMock(Formatter::class, ['getDefaultOptions', 'getDefaultFormats']);
|
||||
|
||||
$formatter->expects($this->once())
|
||||
->method('getDefaultOptions')
|
||||
->willReturn([
|
||||
'type' => 'text',
|
||||
'line_ending' => null,
|
||||
'indentation' => null,
|
||||
'clause_newline' => null,
|
||||
'parts_newline' => null,
|
||||
]);
|
||||
|
||||
$formatter->expects($this->once())
|
||||
->method('getDefaultFormats')
|
||||
->willReturn($default);
|
||||
|
||||
$expectedOptions = [
|
||||
'type' => 'test-type',
|
||||
'line_ending' => '<br>',
|
||||
'indentation' => ' ',
|
||||
'clause_newline' => null,
|
||||
'parts_newline' => 0,
|
||||
'formats' => $expected,
|
||||
];
|
||||
|
||||
$overridingOptions = [
|
||||
'type' => 'test-type',
|
||||
'line_ending' => '<br>',
|
||||
'formats' => $overriding,
|
||||
];
|
||||
|
||||
$reflectionMethod = new ReflectionMethod($formatter, 'getMergedOptions');
|
||||
$reflectionMethod->setAccessible(true);
|
||||
$this->assertEquals($expectedOptions, $reflectionMethod->invoke($formatter, $overridingOptions));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array<string, array<int, array<string, int|string>>>>
|
||||
* @psalm-return array<string, array{
|
||||
* default: list<array{type: int, flags: int, html: string, cli: string, function?: string}>,
|
||||
* overriding: list<array{type?: int, flags?: int, html?: string, cli?: string, function?: string}>,
|
||||
* expected: list<array{type: int, flags: int, html: string, cli: string, function?: string}>
|
||||
* }>
|
||||
*/
|
||||
public static function mergeFormatsProvider(): array
|
||||
{
|
||||
// [default[], overriding[], expected[]]
|
||||
return [
|
||||
'empty formats' => [
|
||||
'default' => [
|
||||
[
|
||||
'type' => 0,
|
||||
'flags' => 0,
|
||||
'html' => '',
|
||||
'cli' => '',
|
||||
'function' => '',
|
||||
],
|
||||
],
|
||||
'overriding' => [
|
||||
[],
|
||||
],
|
||||
'expected' => [
|
||||
[
|
||||
'type' => 0,
|
||||
'flags' => 0,
|
||||
'html' => '',
|
||||
'cli' => '',
|
||||
'function' => '',
|
||||
],
|
||||
],
|
||||
],
|
||||
'no flags' => [
|
||||
'default' => [
|
||||
[
|
||||
'type' => 0,
|
||||
'flags' => 0,
|
||||
'html' => 'html',
|
||||
'cli' => 'cli',
|
||||
],
|
||||
[
|
||||
'type' => 0,
|
||||
'flags' => 1,
|
||||
'html' => 'html',
|
||||
'cli' => 'cli',
|
||||
],
|
||||
],
|
||||
'overriding' => [
|
||||
[
|
||||
'type' => 0,
|
||||
'html' => 'new html',
|
||||
'cli' => 'new cli',
|
||||
],
|
||||
],
|
||||
'expected' => [
|
||||
[
|
||||
'type' => 0,
|
||||
'flags' => 0,
|
||||
'html' => 'new html',
|
||||
'cli' => 'new cli',
|
||||
'function' => '',
|
||||
],
|
||||
[
|
||||
'type' => 0,
|
||||
'flags' => 1,
|
||||
'html' => 'html',
|
||||
'cli' => 'cli',
|
||||
],
|
||||
],
|
||||
],
|
||||
'with flags' => [
|
||||
'default' => [
|
||||
[
|
||||
'type' => -1,
|
||||
'flags' => 0,
|
||||
'html' => 'html',
|
||||
'cli' => 'cli',
|
||||
],
|
||||
[
|
||||
'type' => 0,
|
||||
'flags' => 0,
|
||||
'html' => 'html',
|
||||
'cli' => 'cli',
|
||||
],
|
||||
[
|
||||
'type' => 0,
|
||||
'flags' => 1,
|
||||
'html' => 'html',
|
||||
'cli' => 'cli',
|
||||
],
|
||||
],
|
||||
'overriding' => [
|
||||
[
|
||||
'type' => 0,
|
||||
'flags' => 0,
|
||||
'html' => 'new html',
|
||||
'cli' => 'new cli',
|
||||
],
|
||||
],
|
||||
'expected' => [
|
||||
[
|
||||
'type' => -1,
|
||||
'flags' => 0,
|
||||
'html' => 'html',
|
||||
'cli' => 'cli',
|
||||
],
|
||||
[
|
||||
'type' => 0,
|
||||
'flags' => 0,
|
||||
'html' => 'new html',
|
||||
'cli' => 'new cli',
|
||||
'function' => '',
|
||||
],
|
||||
[
|
||||
'type' => 0,
|
||||
'flags' => 1,
|
||||
'html' => 'html',
|
||||
'cli' => 'cli',
|
||||
],
|
||||
],
|
||||
],
|
||||
'with extra formats' => [
|
||||
'default' => [
|
||||
[
|
||||
'type' => 0,
|
||||
'flags' => 0,
|
||||
'html' => 'html',
|
||||
'cli' => 'cli',
|
||||
],
|
||||
],
|
||||
'overriding' => [
|
||||
[
|
||||
'type' => 0,
|
||||
'flags' => 1,
|
||||
'html' => 'new html',
|
||||
'cli' => 'new cli',
|
||||
],
|
||||
[
|
||||
'type' => 1,
|
||||
'html' => 'new html',
|
||||
'cli' => 'new cli',
|
||||
],
|
||||
[
|
||||
'type' => 1,
|
||||
'flags' => 1,
|
||||
'html' => 'new html',
|
||||
'cli' => 'new cli',
|
||||
],
|
||||
],
|
||||
'expected' => [
|
||||
[
|
||||
'type' => 0,
|
||||
'flags' => 0,
|
||||
'html' => 'html',
|
||||
'cli' => 'cli',
|
||||
],
|
||||
[
|
||||
'type' => 0,
|
||||
'flags' => 1,
|
||||
'html' => 'new html',
|
||||
'cli' => 'new cli',
|
||||
'function' => '',
|
||||
],
|
||||
[
|
||||
'type' => 1,
|
||||
'flags' => 0,
|
||||
'html' => 'new html',
|
||||
'cli' => 'new cli',
|
||||
'function' => '',
|
||||
],
|
||||
[
|
||||
'type' => 1,
|
||||
'flags' => 1,
|
||||
'html' => 'new html',
|
||||
'cli' => 'new cli',
|
||||
'function' => '',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, bool> $options
|
||||
*
|
||||
* @dataProvider formatQueriesProviders
|
||||
*/
|
||||
public function testFormat(string $query, string $text, string $cli, string $html, array $options = []): void
|
||||
{
|
||||
// Test TEXT format
|
||||
$this->assertEquals(
|
||||
$text,
|
||||
Formatter::format($query, ['type' => 'text'] + $options),
|
||||
'Text formatting failed.'
|
||||
);
|
||||
|
||||
// Test CLI format
|
||||
$this->assertEquals(
|
||||
$cli,
|
||||
Formatter::format($query, ['type' => 'cli'] + $options),
|
||||
'CLI formatting failed.'
|
||||
);
|
||||
|
||||
// Test HTML format
|
||||
$this->assertEquals(
|
||||
$html,
|
||||
Formatter::format($query, ['type' => 'html'] + $options),
|
||||
'HTML formatting failed.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array<string, string|array<string, string|bool>>>
|
||||
* @psalm-return array<string, array{
|
||||
* query: string,
|
||||
* text: string,
|
||||
* cli: string,
|
||||
* html: string,
|
||||
* options?: array<string, bool>
|
||||
* }>
|
||||
*/
|
||||
public static function formatQueriesProviders(): array
|
||||
{
|
||||
return [
|
||||
'empty' => [
|
||||
'query' => '',
|
||||
'text' => '',
|
||||
'cli' => "\x1b[0m",
|
||||
'html' => '',
|
||||
],
|
||||
'minimal' => [
|
||||
'query' => 'select 1',
|
||||
'text' => 'SELECT' . "\n" .
|
||||
' 1',
|
||||
'cli' => "\x1b[35mSELECT\n" .
|
||||
" \x1b[92m1\x1b[0m",
|
||||
'html' => '<span class="sql-reserved">SELECT</span><br/>' .
|
||||
' <span class="sql-number">1</span>',
|
||||
],
|
||||
'simply' => [
|
||||
'query' => 'select * from tbl where 1',
|
||||
'text' => 'SELECT' . "\n" .
|
||||
' *' . "\n" .
|
||||
'FROM' . "\n" .
|
||||
' tbl' . "\n" .
|
||||
'WHERE' . "\n" .
|
||||
' 1',
|
||||
'cli' => "\x1b[35mSELECT\n" .
|
||||
" \x1b[39m*\n" .
|
||||
"\x1b[35mFROM\n" .
|
||||
" \x1b[39mtbl\n" .
|
||||
"\x1b[35mWHERE\n" .
|
||||
" \x1b[92m1\x1b[0m",
|
||||
'html' => '<span class="sql-reserved">SELECT</span><br/>' .
|
||||
' *<br/>' .
|
||||
'<span class="sql-reserved">FROM</span><br/>' .
|
||||
' tbl<br/>' .
|
||||
'<span class="sql-reserved">WHERE</span><br/>' .
|
||||
' <span class="sql-number">1</span>',
|
||||
],
|
||||
'typical' => [
|
||||
'query' => 'SELECT id, if(id=1,"Si","No") from `tbl` where id = 0 or ' .
|
||||
'id = 1 group by id order by id desc limit 1 offset 0',
|
||||
'text' => 'SELECT' . "\n" .
|
||||
' id,' . "\n" .
|
||||
' IF(id = 1, "Si", "No")' . "\n" .
|
||||
'FROM' . "\n" .
|
||||
' `tbl`' . "\n" .
|
||||
'WHERE' . "\n" .
|
||||
' id = 0 OR id = 1' . "\n" .
|
||||
'GROUP BY' . "\n" .
|
||||
' id' . "\n" .
|
||||
'ORDER BY' . "\n" .
|
||||
' id' . "\n" .
|
||||
'DESC' . "\n" .
|
||||
'LIMIT 1 OFFSET 0',
|
||||
'cli' => "\x1b[35mSELECT\n" .
|
||||
" \x1b[39mid,\n" .
|
||||
" \x1b[35mIF\x1b[39m(id = \x1b[92m1\x1b[39m, \x1b[91m\"Si\"\x1b[39m, \x1b[91m\"No\"\x1b[39m)\n" .
|
||||
"\x1b[35mFROM\n" .
|
||||
" \x1b[36m`tbl`\n" .
|
||||
"\x1b[35mWHERE\n" .
|
||||
" \x1b[39mid = \x1b[92m0 \x1b[35mOR \x1b[39mid = \x1b[92m1\n" .
|
||||
"\x1b[35mGROUP BY\n" .
|
||||
" \x1b[39mid\n" .
|
||||
"\x1b[35mORDER BY\n" .
|
||||
" \x1b[39mid\n" .
|
||||
"\x1b[35mDESC\n" .
|
||||
"LIMIT \x1b[92m1 \x1b[95mOFFSET \x1b[92m0\x1b[0m",
|
||||
'html' => '<span class="sql-reserved">SELECT</span><br/>' .
|
||||
' id,<br/>' .
|
||||
' <span class="sql-reserved">IF</span>(id = ' .
|
||||
'<span class="sql-number">1</span>, <span class="sql-string">"Si"</span>, ' .
|
||||
'<span class="sql-string">"No"</span>)<br/>' .
|
||||
'<span class="sql-reserved">FROM</span><br/>' .
|
||||
' <span class="sql-variable">`tbl`</span><br/>' .
|
||||
'<span class="sql-reserved">WHERE</span><br/>' .
|
||||
' id = <span class="sql-number">0</span> ' .
|
||||
'<span class="sql-reserved">OR</span> id = <span class="sql-number">1</span><br/>' .
|
||||
'<span class="sql-reserved">GROUP BY</span><br/>' .
|
||||
' id<br/>' .
|
||||
'<span class="sql-reserved">ORDER BY</span><br/>' .
|
||||
' id<br/>' .
|
||||
'<span class="sql-reserved">DESC</span><br/>' .
|
||||
'<span class="sql-reserved">LIMIT</span> <span class="sql-number">1</span> ' .
|
||||
'<span class="sql-keyword">OFFSET</span> <span class="sql-number">0</span>',
|
||||
],
|
||||
'comments' => [
|
||||
'query' => 'select /* Comment */ *' . "\n" .
|
||||
'from tbl # Comment' . "\n" .
|
||||
'where 1 -- Comment',
|
||||
'text' => 'SELECT' . "\n" .
|
||||
' /* Comment */ *' . "\n" .
|
||||
'FROM' . "\n" .
|
||||
' tbl # Comment' . "\n" .
|
||||
'WHERE' . "\n" .
|
||||
' 1 -- Comment',
|
||||
'cli' => "\x1b[35mSELECT\n" .
|
||||
" \x1b[37m/* Comment */ \x1b[39m*\n" .
|
||||
"\x1b[35mFROM\n" .
|
||||
" \x1b[39mtbl \x1b[37m# Comment\n" .
|
||||
"\x1b[35mWHERE\n" .
|
||||
" \x1b[92m1 \x1b[37m-- Comment\x1b[0m",
|
||||
'html' => '<span class="sql-reserved">SELECT</span><br/>' .
|
||||
' <span class="sql-comment">/* Comment */</span> *<br/>' .
|
||||
'<span class="sql-reserved">FROM</span><br/>' .
|
||||
' tbl <span class="sql-comment"># Comment</span><br/>' .
|
||||
'<span class="sql-reserved">WHERE</span><br/>' .
|
||||
' <span class="sql-number">1</span> ' .
|
||||
'<span class="sql-comment">-- Comment</span>',
|
||||
],
|
||||
'strip comments' => [
|
||||
'query' => 'select /* Comment */ *' . "\n" .
|
||||
'from tbl # Comment' . "\n" .
|
||||
'where 1 -- Comment',
|
||||
'text' => 'SELECT' . "\n" .
|
||||
' *' . "\n" .
|
||||
'FROM' . "\n" .
|
||||
' tbl' . "\n" .
|
||||
'WHERE' . "\n" .
|
||||
' 1',
|
||||
'cli' => "\x1b[35mSELECT\n" .
|
||||
" \x1b[39m*\n" .
|
||||
"\x1b[35mFROM\n" .
|
||||
" \x1b[39mtbl\n" .
|
||||
"\x1b[35mWHERE\n" .
|
||||
" \x1b[92m1\x1b[0m",
|
||||
'html' => '<span class="sql-reserved">SELECT</span><br/>' .
|
||||
' *<br/>' .
|
||||
'<span class="sql-reserved">FROM</span><br/>' .
|
||||
' tbl<br/>' .
|
||||
'<span class="sql-reserved">WHERE</span><br/>' .
|
||||
' <span class="sql-number">1</span>',
|
||||
'options' => ['remove_comments' => true],
|
||||
],
|
||||
'keywords' => [
|
||||
'query' => 'select hex("1")',
|
||||
'text' => 'SELECT' . "\n" .
|
||||
' HEX("1")',
|
||||
'cli' => "\x1b[35mSELECT\n" .
|
||||
" \x1b[95mHEX\x1b[39m(\x1b[91m\"1\"\x1b[39m)\x1b[0m",
|
||||
'html' => '<span class="sql-reserved">SELECT</span><br/>' .
|
||||
' <span class="sql-keyword">HEX</span>(<span class="sql-string">"1"</span>)',
|
||||
],
|
||||
'distinct count' => [
|
||||
'query' => 'select distinct count(*)',
|
||||
'text' => 'SELECT DISTINCT' . "\n" .
|
||||
' COUNT(*)',
|
||||
'cli' => "\x1b[35mSELECT DISTINCT\n" .
|
||||
" \x1b[95mCOUNT\x1b[39m(*)\x1b[0m",
|
||||
'html' => '<span class="sql-reserved">SELECT</span> <span class="sql-reserved">DISTINCT</span><br/>' .
|
||||
' <span class="sql-keyword">COUNT</span>(*)',
|
||||
],
|
||||
'create procedure' => [
|
||||
'query' => 'create procedure test_procedure() begin from tbl select *; end',
|
||||
'text' => 'CREATE PROCEDURE test_procedure()' . "\n" .
|
||||
'BEGIN' . "\n" .
|
||||
' FROM' . "\n" .
|
||||
' tbl' . "\n" .
|
||||
' SELECT' . "\n" .
|
||||
' *;' . "\n" .
|
||||
'END',
|
||||
'cli' => "\x1b[35mCREATE PROCEDURE \x1b[39mtest_procedure()\n" .
|
||||
"\x1b[95mBEGIN\n" .
|
||||
" \x1b[35mFROM\n" .
|
||||
" \x1b[39mtbl\n" .
|
||||
" \x1b[35mSELECT\n" .
|
||||
" \x1b[39m*;\n" .
|
||||
"\x1b[95mEND\x1b[0m",
|
||||
'html' => '<span class="sql-reserved">CREATE</span> ' .
|
||||
'<span class="sql-reserved">PROCEDURE</span> test_procedure()<br/>' .
|
||||
'<span class="sql-keyword">BEGIN</span><br/>' .
|
||||
' <span class="sql-reserved">FROM</span><br/>' .
|
||||
' tbl<br/>' .
|
||||
' <span class="sql-reserved">SELECT</span><br/>' .
|
||||
' *;<br/>' .
|
||||
'<span class="sql-keyword">END</span>',
|
||||
],
|
||||
'insert' => [
|
||||
'query' => 'insert into foo values (0, 0, 0), (1, 1, 1)',
|
||||
'text' => 'INSERT INTO foo' . "\n" .
|
||||
'VALUES(0, 0, 0),(1, 1, 1)',
|
||||
'cli' => "\x1b[35mINSERT INTO \x1b[39mfoo\n" .
|
||||
"\x1b[35mVALUES\x1b[39m(\x1b[92m0\x1b[39m, \x1b[92m0\x1b[39m, " .
|
||||
"\x1b[92m0\x1b[39m),(\x1b[92m1\x1b[39m, \x1b[92m1\x1b[39m, \x1b[92m1\x1b[39m)\x1b[0m",
|
||||
'html' => '<span class="sql-reserved">INSERT</span> <span class="sql-reserved">INTO</span> foo<br/>' .
|
||||
'<span class="sql-reserved">VALUES</span>(<span class="sql-number">0</span>, ' .
|
||||
'<span class="sql-number">0</span>, <span class="sql-number">0</span>),(' .
|
||||
'<span class="sql-number">1</span>, <span class="sql-number">1</span>, ' .
|
||||
'<span class="sql-number">1</span>)',
|
||||
],
|
||||
'string as alias' => [
|
||||
'query' => 'select "Text" as bar',
|
||||
'text' => 'SELECT' . "\n" .
|
||||
' "Text" AS bar',
|
||||
'cli' => "\x1b[35mSELECT\n" .
|
||||
" \x1b[91m\"Text\" \x1b[35mAS \x1b[39mbar\x1b[0m",
|
||||
'html' => '<span class="sql-reserved">SELECT</span><br/>' .
|
||||
' <span class="sql-string">"Text"</span> ' .
|
||||
'<span class="sql-reserved">AS</span> bar',
|
||||
],
|
||||
'escape cli' => [
|
||||
'query' => "select 'text\x1b[33mcolor-inj'",
|
||||
'text' => 'SELECT' . "\n" .
|
||||
" 'text\x1B[33mcolor-inj'",
|
||||
'cli' => "\x1b[35mSELECT\n" .
|
||||
" \x1b[91m'text\\x1B[33mcolor-inj'\x1b[0m",
|
||||
'html' => '<span class="sql-reserved">SELECT</span><br/>' .
|
||||
' <span class="sql-string">\'text' . "\x1b[33m" . 'color-inj\'</span>',
|
||||
],
|
||||
'escape html' => [
|
||||
'query' => "select '<s>xss' from `<s>xss` , <s>nxss /*s<s>xss*/",
|
||||
'text' => 'SELECT' . "\n" .
|
||||
' \'<s>xss\'' . "\n" .
|
||||
'FROM' . "\n" .
|
||||
' `<s>xss`,' . "\n" .
|
||||
' < s > nxss /*s<s>xss*/',
|
||||
'cli' => "\x1b[35mSELECT\n" .
|
||||
" \x1b[91m'<s>xss'\n" .
|
||||
"\x1b[35mFROM\n" .
|
||||
" \x1b[36m`<s>xss`\x1b[39m,\n" .
|
||||
" < s > nxss \x1b[37m/*s<s>xss*/\x1b[0m",
|
||||
'html' => '<span class="sql-reserved">SELECT</span><br/>' .
|
||||
' <span class="sql-string">\'<s>xss\'</span><br/>' .
|
||||
'<span class="sql-reserved">FROM</span><br/>' .
|
||||
' <span class="sql-variable">`<s>xss`</span>,' .
|
||||
'<br/> < s > nxss <span class="sql-comment">/*s<s>xss*/</span>',
|
||||
],
|
||||
'create table' => [
|
||||
'query' => 'create table if not exists `pma__bookmark` (' . "\n" .
|
||||
'`id` int(11) not null auto_increment,' . "\n" .
|
||||
'`dbase` varchar(255) not null default "",' . "\n" .
|
||||
'`user` varchar(255) not null default "",' . "\n" .
|
||||
'`label` varchar(255) collate utf8_general_ci not null default "",' . "\n" .
|
||||
'`query` text not null,' . "\n" .
|
||||
'primary key (`id`)',
|
||||
'text' => 'CREATE TABLE IF NOT EXISTS `pma__bookmark`(' . "\n" .
|
||||
' `id` INT(11) NOT NULL AUTO_INCREMENT,' . "\n" .
|
||||
' `dbase` VARCHAR(255) NOT NULL DEFAULT "",' . "\n" .
|
||||
' `user` VARCHAR(255) NOT NULL DEFAULT "",' . "\n" .
|
||||
' `label` VARCHAR(255) COLLATE utf8_general_ci NOT NULL DEFAULT "",' . "\n" .
|
||||
' `query` TEXT NOT NULL,' . "\n" .
|
||||
' PRIMARY KEY(`id`)',
|
||||
'cli' => "\x1b[35mCREATE TABLE IF NOT EXISTS \x1b[36m`pma__bookmark`\x1b[39m(\n" .
|
||||
" \x1b[36m`id` \x1b[35mINT\x1b[39m(\x1b[92m11\x1b[39m) " .
|
||||
"\x1b[35mNOT NULL \x1b[95mAUTO_INCREMENT\x1b[39m,\n" .
|
||||
" \x1b[36m`dbase` \x1b[35mVARCHAR\x1b[39m(\x1b[92m255\x1b[39m) " .
|
||||
"\x1b[35mNOT NULL DEFAULT \x1b[91m\"\"\x1b[39m,\n" .
|
||||
" \x1b[36m`user` \x1b[35mVARCHAR\x1b[39m(\x1b[92m255\x1b[39m) " .
|
||||
"\x1b[35mNOT NULL DEFAULT \x1b[91m\"\"\x1b[39m,\n" .
|
||||
" \x1b[36m`label` \x1b[35mVARCHAR\x1b[39m(\x1b[92m255\x1b[39m) " .
|
||||
"\x1b[35mCOLLATE \x1b[39mutf8_general_ci \x1b[35mNOT NULL DEFAULT \x1b[91m\"\"\x1b[39m,\n" .
|
||||
" \x1b[36m`query` \x1b[95mTEXT \x1b[35mNOT NULL\x1b[39m,\n" .
|
||||
" \x1b[35mPRIMARY KEY\x1b[39m(\x1b[36m`id`\x1b[39m)\x1b[0m",
|
||||
'html' => '<span class="sql-reserved">CREATE</span> <span class="sql-reserved">TABLE</span> ' .
|
||||
'<span class="sql-reserved">IF NOT EXISTS</span> <span class="sql-variable">' .
|
||||
'`pma__bookmark`</span>(<br/>' .
|
||||
' <span class="sql-variable">`id`</span> ' .
|
||||
'<span class="sql-reserved">INT</span>(<span class="sql-number">11</span>) ' .
|
||||
'<span class="sql-reserved">NOT NULL</span> <span class="sql-keyword">AUTO_INCREMENT</span>,<br/>' .
|
||||
' <span class="sql-variable">`dbase`</span> ' .
|
||||
'<span class="sql-reserved">VARCHAR</span>(<span class="sql-number">255</span>) ' .
|
||||
'<span class="sql-reserved">NOT NULL</span> <span class="sql-reserved">DEFAULT</span> ' .
|
||||
'<span class="sql-string">""</span>,<br/>' .
|
||||
' <span class="sql-variable">`user`</span> ' .
|
||||
'<span class="sql-reserved">VARCHAR</span>(<span class="sql-number">255</span>) ' .
|
||||
'<span class="sql-reserved">NOT NULL</span> <span class="sql-reserved">DEFAULT</span> ' .
|
||||
'<span class="sql-string">""</span>,<br/>' .
|
||||
' <span class="sql-variable">`label`</span> ' .
|
||||
'<span class="sql-reserved">VARCHAR</span>(<span class="sql-number">255</span>) ' .
|
||||
'<span class="sql-reserved">COLLATE</span> utf8_general_ci ' .
|
||||
'<span class="sql-reserved">NOT NULL</span> <span class="sql-reserved">DEFAULT</span> ' .
|
||||
'<span class="sql-string">""</span>,<br/>' .
|
||||
' <span class="sql-variable">`query`</span> ' .
|
||||
'<span class="sql-keyword">TEXT</span> <span class="sql-reserved">NOT NULL</span>,<br/>' .
|
||||
' <span class="sql-reserved">' .
|
||||
'PRIMARY KEY</span>(<span class="sql-variable">`id`</span>)',
|
||||
],
|
||||
'join' => [
|
||||
'query' => 'join tbl2 on c1=c2',
|
||||
'text' => 'JOIN tbl2 ON c1 = c2',
|
||||
'cli' => "\x1b[35mJOIN \x1b[39mtbl2 \x1b[35mON \x1b[39mc1 = c2" .
|
||||
"\x1b[0m",
|
||||
'html' => '<span class="sql-reserved">JOIN</span> tbl2 <span class="sql-reserved">ON</span> c1 = c2',
|
||||
],
|
||||
'named param' => [
|
||||
'query' => 'select * from tbl where col = :param',
|
||||
'text' => 'SELECT' . "\n" .
|
||||
' *' . "\n" .
|
||||
'FROM' . "\n" .
|
||||
' tbl' . "\n" .
|
||||
'WHERE' . "\n" .
|
||||
' col = :param',
|
||||
'cli' => "\x1b[35mSELECT\n" .
|
||||
" \x1b[39m*\n" .
|
||||
"\x1b[35mFROM\n" .
|
||||
" \x1b[39mtbl\n" .
|
||||
"\x1b[35mWHERE\n" .
|
||||
" \x1b[39mcol = \x1b[31m:param\x1b[0m",
|
||||
'html' => '<span class="sql-reserved">SELECT</span><br/>' .
|
||||
' *<br/>' .
|
||||
'<span class="sql-reserved">FROM</span><br/>' .
|
||||
' tbl<br/>' .
|
||||
'<span class="sql-reserved">WHERE</span><br/>' .
|
||||
' col = <span class="sql-parameter">:param</span>',
|
||||
],
|
||||
'anon param' => [
|
||||
'query' => 'select * from tbl where col = ?',
|
||||
'text' => 'SELECT' . "\n" .
|
||||
' *' . "\n" .
|
||||
'FROM' . "\n" .
|
||||
' tbl' . "\n" .
|
||||
'WHERE' . "\n" .
|
||||
' col = ?',
|
||||
'cli' => "\x1b[35mSELECT\n" .
|
||||
" \x1b[39m*\n" .
|
||||
"\x1b[35mFROM\n" .
|
||||
" \x1b[39mtbl\n" .
|
||||
"\x1b[35mWHERE\n" .
|
||||
" \x1b[39mcol = \x1b[31m?\x1b[0m",
|
||||
'html' => '<span class="sql-reserved">SELECT</span><br/>' .
|
||||
' *<br/>' .
|
||||
'<span class="sql-reserved">FROM</span><br/>' .
|
||||
' tbl<br/>' .
|
||||
'<span class="sql-reserved">WHERE</span><br/>' .
|
||||
' col = <span class="sql-parameter">?</span>',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Utils;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
use PhpMyAdmin\SqlParser\Utils\Misc;
|
||||
|
||||
class MiscTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @param mixed[] $expected
|
||||
* @psalm-param array<string, array{
|
||||
* alias: (string|null),
|
||||
* tables: array<string, array{alias: (string|null), columns: array<string, string>}>
|
||||
* }> $expected
|
||||
*
|
||||
* @dataProvider getAliasesProvider
|
||||
*/
|
||||
public function testGetAliases(string $query, ?string $db, array $expected): void
|
||||
{
|
||||
$parser = new Parser($query);
|
||||
$statement = empty($parser->statements[0]) ?
|
||||
null : $parser->statements[0];
|
||||
$this->assertEquals($expected, Misc::getAliases($statement, $db));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<int, string|mixed[]|null>>
|
||||
* @psalm-return list<array{string, (string|null), array<string, array{
|
||||
* alias: (string|null),
|
||||
* tables: array<string, array{alias: (string|null), columns: array<string, string>}>
|
||||
* }>}>
|
||||
*/
|
||||
public static function getAliasesProvider(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'select * from (select 1) tbl',
|
||||
'mydb',
|
||||
[],
|
||||
],
|
||||
[
|
||||
'select i.name as `n`,abcdef gh from qwerty i',
|
||||
'mydb',
|
||||
[
|
||||
'mydb' => [
|
||||
'alias' => null,
|
||||
'tables' => [
|
||||
'qwerty' => [
|
||||
'alias' => 'i',
|
||||
'columns' => [
|
||||
'name' => 'n',
|
||||
'abcdef' => 'gh',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'select film_id id,title from film',
|
||||
'sakila',
|
||||
[
|
||||
'sakila' => [
|
||||
'alias' => null,
|
||||
'tables' => [
|
||||
'film' => [
|
||||
'alias' => null,
|
||||
'columns' => ['film_id' => 'id'],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'select `sakila`.`A`.`actor_id` as aid,`F`.`film_id` `fid`,'
|
||||
. 'last_update updated from `sakila`.actor A join `film_actor` as '
|
||||
. '`F` on F.actor_id = A.`actor_id`',
|
||||
'sakila',
|
||||
[
|
||||
'sakila' => [
|
||||
'alias' => null,
|
||||
'tables' => [
|
||||
'film_actor' => [
|
||||
'alias' => 'F',
|
||||
'columns' => [
|
||||
'film_id' => 'fid',
|
||||
'last_update' => 'updated',
|
||||
],
|
||||
],
|
||||
'actor' => [
|
||||
'alias' => 'A',
|
||||
'columns' => [
|
||||
'actor_id' => 'aid',
|
||||
'last_update' => 'updated',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'SELECT film_id FROM (SELECT * FROM film) as f;',
|
||||
'sakila',
|
||||
[],
|
||||
],
|
||||
[
|
||||
'',
|
||||
null,
|
||||
[],
|
||||
],
|
||||
[
|
||||
'SELECT 1',
|
||||
null,
|
||||
[],
|
||||
],
|
||||
[
|
||||
'SELECT * FROM orders AS ord WHERE 1',
|
||||
'db',
|
||||
[
|
||||
'db' => [
|
||||
'alias' => null,
|
||||
'tables' => [
|
||||
'orders' => [
|
||||
'alias' => 'ord',
|
||||
'columns' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
+747
@@ -0,0 +1,747 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Utils;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
use PhpMyAdmin\SqlParser\Utils\Query;
|
||||
|
||||
use function array_merge;
|
||||
|
||||
/**
|
||||
* @psalm-import-type QueryFlagsType from Query
|
||||
*/
|
||||
class QueryTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @param array<string, bool|string> $expected
|
||||
* @psalm-param QueryFlagsType $expected
|
||||
*
|
||||
* @dataProvider getFlagsProvider
|
||||
*/
|
||||
public function testGetFlags(string $query, array $expected): void
|
||||
{
|
||||
$parser = new Parser($query);
|
||||
$this->assertEquals($expected, Query::getFlags($parser->statements[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<int, string|array<string, bool|string>>>
|
||||
* @psalm-return list<array{non-empty-string, QueryFlagsType}>
|
||||
*/
|
||||
public static function getFlagsProvider(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'ALTER TABLE DROP col',
|
||||
[
|
||||
'reload' => true,
|
||||
'querytype' => 'ALTER',
|
||||
],
|
||||
],
|
||||
[
|
||||
'CALL test()',
|
||||
[
|
||||
'is_procedure' => true,
|
||||
'querytype' => 'CALL',
|
||||
],
|
||||
],
|
||||
[
|
||||
'CREATE TABLE tbl (id INT)',
|
||||
[
|
||||
'reload' => true,
|
||||
'querytype' => 'CREATE',
|
||||
],
|
||||
],
|
||||
[
|
||||
'CHECK TABLE tbl',
|
||||
[
|
||||
'is_maint' => true,
|
||||
'querytype' => 'CHECK',
|
||||
],
|
||||
],
|
||||
[
|
||||
'DELETE FROM tbl',
|
||||
[
|
||||
'is_affected' => true,
|
||||
'is_delete' => true,
|
||||
'querytype' => 'DELETE',
|
||||
],
|
||||
],
|
||||
[
|
||||
'DROP VIEW v',
|
||||
[
|
||||
'reload' => true,
|
||||
'querytype' => 'DROP',
|
||||
],
|
||||
],
|
||||
[
|
||||
'DROP DATABASE db',
|
||||
[
|
||||
'drop_database' => true,
|
||||
'reload' => true,
|
||||
'querytype' => 'DROP',
|
||||
],
|
||||
],
|
||||
[
|
||||
'EXPLAIN tbl',
|
||||
[
|
||||
'is_explain' => true,
|
||||
'querytype' => 'EXPLAIN',
|
||||
],
|
||||
],
|
||||
[
|
||||
'LOAD DATA INFILE \'/tmp/test.txt\' INTO TABLE test',
|
||||
[
|
||||
'is_affected' => true,
|
||||
'is_insert' => true,
|
||||
'querytype' => 'LOAD',
|
||||
],
|
||||
],
|
||||
[
|
||||
'INSERT INTO tbl VALUES (1)',
|
||||
[
|
||||
'is_affected' => true,
|
||||
'is_insert' => true,
|
||||
'querytype' => 'INSERT',
|
||||
],
|
||||
],
|
||||
[
|
||||
'REPLACE INTO tbl VALUES (2)',
|
||||
[
|
||||
'is_affected' => true,
|
||||
'is_replace' => true,
|
||||
'is_insert' => true,
|
||||
'querytype' => 'REPLACE',
|
||||
],
|
||||
],
|
||||
[
|
||||
'SELECT 1',
|
||||
[
|
||||
'is_select' => true,
|
||||
'querytype' => 'SELECT',
|
||||
],
|
||||
],
|
||||
[
|
||||
'SELECT * FROM tbl',
|
||||
[
|
||||
'is_select' => true,
|
||||
'select_from' => true,
|
||||
'querytype' => 'SELECT',
|
||||
],
|
||||
],
|
||||
[
|
||||
'SELECT DISTINCT * FROM tbl LIMIT 0, 10 ORDER BY id',
|
||||
[
|
||||
'distinct' => true,
|
||||
'is_select' => true,
|
||||
'select_from' => true,
|
||||
'limit' => true,
|
||||
'order' => true,
|
||||
'querytype' => 'SELECT',
|
||||
],
|
||||
],
|
||||
[
|
||||
'SELECT * FROM actor GROUP BY actor_id',
|
||||
[
|
||||
'is_group' => true,
|
||||
'is_select' => true,
|
||||
'select_from' => true,
|
||||
'group' => true,
|
||||
'querytype' => 'SELECT',
|
||||
],
|
||||
],
|
||||
[
|
||||
'SELECT col1, col2 FROM table1 PROCEDURE ANALYSE(10, 2000);',
|
||||
[
|
||||
'is_analyse' => true,
|
||||
'is_select' => true,
|
||||
'select_from' => true,
|
||||
'querytype' => 'SELECT',
|
||||
],
|
||||
],
|
||||
[
|
||||
'SELECT * FROM tbl INTO OUTFILE "/tmp/export.txt"',
|
||||
[
|
||||
'is_export' => true,
|
||||
'is_select' => true,
|
||||
'select_from' => true,
|
||||
'querytype' => 'SELECT',
|
||||
],
|
||||
],
|
||||
[
|
||||
'SELECT COUNT(id), SUM(id) FROM tbl',
|
||||
[
|
||||
'is_count' => true,
|
||||
'is_func' => true,
|
||||
'is_select' => true,
|
||||
'select_from' => true,
|
||||
'querytype' => 'SELECT',
|
||||
],
|
||||
],
|
||||
[
|
||||
'SELECT count(*) FROM tbl',
|
||||
[
|
||||
'is_count' => true,
|
||||
'is_select' => true,
|
||||
'select_from' => true,
|
||||
'querytype' => 'SELECT',
|
||||
],
|
||||
],
|
||||
[
|
||||
'SELECT sum(*) FROM tbl',
|
||||
[
|
||||
'is_func' => true,
|
||||
'is_select' => true,
|
||||
'select_from' => true,
|
||||
'querytype' => 'SELECT',
|
||||
],
|
||||
],
|
||||
[
|
||||
'SELECT (SELECT "foo")',
|
||||
[
|
||||
'is_select' => true,
|
||||
'is_subquery' => true,
|
||||
'querytype' => 'SELECT',
|
||||
],
|
||||
],
|
||||
[
|
||||
'SELECT * FROM customer HAVING store_id = 2;',
|
||||
[
|
||||
'is_select' => true,
|
||||
'select_from' => true,
|
||||
'is_group' => true,
|
||||
'having' => true,
|
||||
'querytype' => 'SELECT',
|
||||
],
|
||||
],
|
||||
[
|
||||
'SELECT * FROM table1 INNER JOIN table2 ON table1.id=table2.id;',
|
||||
[
|
||||
'is_select' => true,
|
||||
'select_from' => true,
|
||||
'join' => true,
|
||||
'querytype' => 'SELECT',
|
||||
],
|
||||
],
|
||||
[
|
||||
'SHOW CREATE TABLE tbl',
|
||||
[
|
||||
'is_show' => true,
|
||||
'querytype' => 'SHOW',
|
||||
],
|
||||
],
|
||||
[
|
||||
'UPDATE tbl SET id = 1',
|
||||
[
|
||||
'is_affected' => true,
|
||||
'querytype' => 'UPDATE',
|
||||
],
|
||||
],
|
||||
[
|
||||
'ANALYZE TABLE tbl',
|
||||
[
|
||||
'is_maint' => true,
|
||||
'querytype' => 'ANALYZE',
|
||||
],
|
||||
],
|
||||
[
|
||||
'CHECKSUM TABLE tbl',
|
||||
[
|
||||
'is_maint' => true,
|
||||
'querytype' => 'CHECKSUM',
|
||||
],
|
||||
],
|
||||
[
|
||||
'OPTIMIZE TABLE tbl',
|
||||
[
|
||||
'is_maint' => true,
|
||||
'querytype' => 'OPTIMIZE',
|
||||
],
|
||||
],
|
||||
[
|
||||
'REPAIR TABLE tbl',
|
||||
[
|
||||
'is_maint' => true,
|
||||
'querytype' => 'REPAIR',
|
||||
],
|
||||
],
|
||||
[
|
||||
'(SELECT a FROM t1 WHERE a=10 AND B=1 ORDER BY a LIMIT 10) ' .
|
||||
'UNION ' .
|
||||
'(SELECT a FROM t2 WHERE a=11 AND B=2 ORDER BY a LIMIT 10);',
|
||||
[
|
||||
'is_select' => true,
|
||||
'select_from' => true,
|
||||
'limit' => true,
|
||||
'order' => true,
|
||||
'union' => true,
|
||||
'querytype' => 'SELECT',
|
||||
],
|
||||
],
|
||||
[
|
||||
'SELECT * FROM orders AS ord WHERE 1',
|
||||
[
|
||||
'querytype' => 'SELECT',
|
||||
'is_select' => true,
|
||||
'select_from' => true,
|
||||
],
|
||||
],
|
||||
[
|
||||
'SET NAMES \'latin\'',
|
||||
['querytype' => 'SET'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function testGetAll(): void
|
||||
{
|
||||
$this->assertEquals(
|
||||
[
|
||||
'distinct' => false,
|
||||
'drop_database' => false,
|
||||
'group' => false,
|
||||
'having' => false,
|
||||
'is_affected' => false,
|
||||
'is_analyse' => false,
|
||||
'is_count' => false,
|
||||
'is_delete' => false,
|
||||
'is_explain' => false,
|
||||
'is_export' => false,
|
||||
'is_func' => false,
|
||||
'is_group' => false,
|
||||
'is_insert' => false,
|
||||
'is_maint' => false,
|
||||
'is_procedure' => false,
|
||||
'is_replace' => false,
|
||||
'is_select' => false,
|
||||
'is_show' => false,
|
||||
'is_subquery' => false,
|
||||
'join' => false,
|
||||
'limit' => false,
|
||||
'offset' => false,
|
||||
'order' => false,
|
||||
'querytype' => false,
|
||||
'reload' => false,
|
||||
'select_from' => false,
|
||||
'union' => false,
|
||||
],
|
||||
Query::getAll('')
|
||||
);
|
||||
|
||||
$query = 'SELECT *, actor.actor_id, sakila2.film.*
|
||||
FROM sakila2.city, sakila2.film, actor';
|
||||
$parser = new Parser($query);
|
||||
$this->assertEquals(
|
||||
array_merge(
|
||||
Query::getFlags($parser->statements[0], true),
|
||||
[
|
||||
'parser' => $parser,
|
||||
'statement' => $parser->statements[0],
|
||||
'select_expr' => ['*'],
|
||||
'select_tables' => [
|
||||
[
|
||||
'actor',
|
||||
null,
|
||||
],
|
||||
[
|
||||
'film',
|
||||
'sakila2',
|
||||
],
|
||||
],
|
||||
]
|
||||
),
|
||||
Query::getAll($query)
|
||||
);
|
||||
|
||||
$query = 'SELECT * FROM sakila.actor, film';
|
||||
$parser = new Parser($query);
|
||||
$this->assertEquals(
|
||||
array_merge(
|
||||
Query::getFlags($parser->statements[0], true),
|
||||
[
|
||||
'parser' => $parser,
|
||||
'statement' => $parser->statements[0],
|
||||
'select_expr' => ['*'],
|
||||
'select_tables' => [
|
||||
[
|
||||
'actor',
|
||||
'sakila',
|
||||
],
|
||||
[
|
||||
'film',
|
||||
null,
|
||||
],
|
||||
],
|
||||
]
|
||||
),
|
||||
Query::getAll($query)
|
||||
);
|
||||
|
||||
$query = 'SELECT a.actor_id FROM sakila.actor AS a, film';
|
||||
$parser = new Parser($query);
|
||||
$this->assertEquals(
|
||||
array_merge(
|
||||
Query::getFlags($parser->statements[0], true),
|
||||
[
|
||||
'parser' => $parser,
|
||||
'statement' => $parser->statements[0],
|
||||
'select_expr' => [],
|
||||
'select_tables' => [
|
||||
[
|
||||
'actor',
|
||||
'sakila',
|
||||
],
|
||||
],
|
||||
]
|
||||
),
|
||||
Query::getAll($query)
|
||||
);
|
||||
|
||||
$query = 'SELECT CASE WHEN 2 IS NULL THEN "this is true" ELSE "this is false" END';
|
||||
$parser = new Parser($query);
|
||||
$this->assertEquals(
|
||||
array_merge(
|
||||
Query::getFlags($parser->statements[0], true),
|
||||
[
|
||||
'parser' => $parser,
|
||||
'statement' => $parser->statements[0],
|
||||
'select_expr' => ['CASE WHEN 2 IS NULL THEN "this is true" ELSE "this is false" END'],
|
||||
'select_tables' => [],
|
||||
]
|
||||
),
|
||||
Query::getAll($query)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $expected
|
||||
*
|
||||
* @dataProvider getTablesProvider
|
||||
*/
|
||||
public function testGetTables(string $query, array $expected): void
|
||||
{
|
||||
$parser = new Parser($query);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
Query::getTables($parser->statements[0])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<int, string|string[]>>
|
||||
* @psalm-return list<array{string, string[]}>
|
||||
*/
|
||||
public static function getTablesProvider(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'INSERT INTO tbl(`id`, `name`) VALUES (1, "Name")',
|
||||
['`tbl`'],
|
||||
],
|
||||
[
|
||||
'INSERT INTO 0tbl(`id`, `name`) VALUES (1, "Name")',
|
||||
['`0tbl`'],
|
||||
],
|
||||
[
|
||||
'UPDATE tbl SET id = 0',
|
||||
['`tbl`'],
|
||||
],
|
||||
[
|
||||
'UPDATE 0tbl SET id = 0',
|
||||
['`0tbl`'],
|
||||
],
|
||||
[
|
||||
'DELETE FROM tbl WHERE id < 10',
|
||||
['`tbl`'],
|
||||
],
|
||||
[
|
||||
'DELETE FROM 0tbl WHERE id < 10',
|
||||
['`0tbl`'],
|
||||
],
|
||||
[
|
||||
'TRUNCATE tbl',
|
||||
['`tbl`'],
|
||||
],
|
||||
[
|
||||
'DROP VIEW v',
|
||||
[],
|
||||
],
|
||||
[
|
||||
'DROP TABLE tbl1, tbl2',
|
||||
[
|
||||
'`tbl1`',
|
||||
'`tbl2`',
|
||||
],
|
||||
],
|
||||
[
|
||||
'RENAME TABLE a TO b, c TO d',
|
||||
[
|
||||
'`a`',
|
||||
'`c`',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function testGetClause(): void
|
||||
{
|
||||
/* Assertion 1 */
|
||||
$parser = new Parser(
|
||||
'SELECT c.city_id, c.country_id ' .
|
||||
'FROM `city` ' .
|
||||
'WHERE city_id < 1 /* test */' .
|
||||
'ORDER BY city_id ASC ' .
|
||||
'LIMIT 0, 1 ' .
|
||||
'INTO OUTFILE "/dev/null"'
|
||||
);
|
||||
$this->assertEquals(
|
||||
'0, 1 INTO OUTFILE "/dev/null"',
|
||||
Query::getClause(
|
||||
$parser->statements[0],
|
||||
$parser->list,
|
||||
'LIMIT',
|
||||
0
|
||||
)
|
||||
);
|
||||
// Assert it returns all clauses between FROM and LIMIT
|
||||
$this->assertEquals(
|
||||
'WHERE city_id < 1 ORDER BY city_id ASC',
|
||||
Query::getClause(
|
||||
$parser->statements[0],
|
||||
$parser->list,
|
||||
'FROM',
|
||||
'LIMIT'
|
||||
)
|
||||
);
|
||||
// Assert it returns all clauses between SELECT and LIMIT
|
||||
$this->assertEquals(
|
||||
'FROM `city` WHERE city_id < 1 ORDER BY city_id ASC',
|
||||
Query::getClause(
|
||||
$parser->statements[0],
|
||||
$parser->list,
|
||||
'LIMIT',
|
||||
'SELECT'
|
||||
)
|
||||
);
|
||||
|
||||
/* Assertion 2 */
|
||||
$parser = new Parser(
|
||||
'DELETE FROM `renewal` ' .
|
||||
'WHERE number = "1DB" AND actionDate <= CURRENT_DATE() ' .
|
||||
'ORDER BY id ASC ' .
|
||||
'LIMIT 1'
|
||||
);
|
||||
$this->assertEquals(
|
||||
'number = "1DB" AND actionDate <= CURRENT_DATE()',
|
||||
Query::getClause(
|
||||
$parser->statements[0],
|
||||
$parser->list,
|
||||
'WHERE'
|
||||
)
|
||||
);
|
||||
$this->assertEquals(
|
||||
'1',
|
||||
Query::getClause(
|
||||
$parser->statements[0],
|
||||
$parser->list,
|
||||
'LIMIT'
|
||||
)
|
||||
);
|
||||
$this->assertEquals(
|
||||
'id ASC',
|
||||
Query::getClause(
|
||||
$parser->statements[0],
|
||||
$parser->list,
|
||||
'ORDER BY'
|
||||
)
|
||||
);
|
||||
|
||||
/* Assertion 3 */
|
||||
$parser = new Parser(
|
||||
'UPDATE `renewal` SET `some_column` = 1 ' .
|
||||
'WHERE number = "1DB" AND actionDate <= CURRENT_DATE() ' .
|
||||
'ORDER BY id ASC ' .
|
||||
'LIMIT 1'
|
||||
);
|
||||
$this->assertEquals(
|
||||
'number = "1DB" AND actionDate <= CURRENT_DATE()',
|
||||
Query::getClause(
|
||||
$parser->statements[0],
|
||||
$parser->list,
|
||||
'WHERE'
|
||||
)
|
||||
);
|
||||
$this->assertEquals(
|
||||
'1',
|
||||
Query::getClause(
|
||||
$parser->statements[0],
|
||||
$parser->list,
|
||||
'LIMIT'
|
||||
)
|
||||
);
|
||||
$this->assertEquals(
|
||||
'id ASC',
|
||||
Query::getClause(
|
||||
$parser->statements[0],
|
||||
$parser->list,
|
||||
'ORDER BY'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function testReplaceClause(): void
|
||||
{
|
||||
$parser = new Parser('SELECT *, (SELECT 1) FROM film LIMIT 0, 10;');
|
||||
$this->assertEquals(
|
||||
'SELECT *, (SELECT 1) FROM film WHERE film_id > 0 LIMIT 0, 10',
|
||||
Query::replaceClause(
|
||||
$parser->statements[0],
|
||||
$parser->list,
|
||||
'WHERE film_id > 0'
|
||||
)
|
||||
);
|
||||
|
||||
$parser = new Parser(
|
||||
'select supplier.city, supplier.id from supplier '
|
||||
. 'union select customer.city, customer.id from customer'
|
||||
);
|
||||
$this->assertEquals(
|
||||
'select supplier.city, supplier.id from supplier '
|
||||
. 'union select customer.city, customer.id from customer'
|
||||
. ' ORDER BY city ',
|
||||
Query::replaceClause(
|
||||
$parser->statements[0],
|
||||
$parser->list,
|
||||
'ORDER BY city'
|
||||
)
|
||||
);
|
||||
|
||||
$parser = new Parser('SELECT * FROM `t` FOR UPDATE');
|
||||
$this->assertEquals(
|
||||
'SELECT * FROM `t` LIMIT 0, 25 FOR UPDATE',
|
||||
Query::replaceClause(
|
||||
$parser->statements[0],
|
||||
$parser->list,
|
||||
'LIMIT 0, 25'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function testReplaceClauseOnlyKeyword(): void
|
||||
{
|
||||
$parser = new Parser('SELECT *, (SELECT 1) FROM film LIMIT 0, 10');
|
||||
$this->assertEquals(
|
||||
' SELECT SQL_CALC_FOUND_ROWS *, (SELECT 1) FROM film LIMIT 0, 10',
|
||||
Query::replaceClause(
|
||||
$parser->statements[0],
|
||||
$parser->list,
|
||||
'SELECT SQL_CALC_FOUND_ROWS',
|
||||
null,
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function testReplaceNonExistingPart(): void
|
||||
{
|
||||
$parser = new Parser('ALTER TABLE `sale_mast` OPTIMIZE PARTITION p3');
|
||||
$this->assertEquals(
|
||||
' ALTER TABLE `sale_mast` OPTIMIZE PARTITION p3',
|
||||
Query::replaceClause(
|
||||
$parser->statements[0],
|
||||
$parser->list,
|
||||
'ORDER BY',
|
||||
''
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function testReplaceClauses(): void
|
||||
{
|
||||
$parser = new Parser('SELECT *, (SELECT 1) FROM film LIMIT 0, 10;');
|
||||
$this->assertSame('', Query::replaceClauses($parser->statements[0], $parser->list, []));
|
||||
$this->assertEquals(
|
||||
'SELECT *, (SELECT 1) FROM film WHERE film_id > 0 LIMIT 0, 10',
|
||||
Query::replaceClauses(
|
||||
$parser->statements[0],
|
||||
$parser->list,
|
||||
[
|
||||
[
|
||||
'WHERE',
|
||||
'WHERE film_id > 0',
|
||||
],
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
$parser = new Parser(
|
||||
'SELECT c.city_id, c.country_id ' .
|
||||
'INTO OUTFILE "/dev/null" ' .
|
||||
'FROM `city` ' .
|
||||
'WHERE city_id < 1 ' .
|
||||
'ORDER BY city_id ASC ' .
|
||||
'LIMIT 0, 1 '
|
||||
);
|
||||
$this->assertEquals(
|
||||
'SELECT c.city_id, c.country_id ' .
|
||||
'INTO OUTFILE "/dev/null" ' .
|
||||
'FROM city AS c ' .
|
||||
'ORDER BY city_id ASC ' .
|
||||
'LIMIT 0, 10 ',
|
||||
Query::replaceClauses(
|
||||
$parser->statements[0],
|
||||
$parser->list,
|
||||
[
|
||||
[
|
||||
'FROM',
|
||||
'FROM city AS c',
|
||||
],
|
||||
[
|
||||
'WHERE',
|
||||
'',
|
||||
],
|
||||
[
|
||||
'LIMIT',
|
||||
'LIMIT 0, 10',
|
||||
],
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetFirstStatement(): void
|
||||
{
|
||||
$query = 'USE saki';
|
||||
$delimiter = null;
|
||||
[$statement, $query, $delimiter] = Query::getFirstStatement($query, $delimiter);
|
||||
$this->assertNull($statement);
|
||||
$this->assertEquals('USE saki', $query);
|
||||
|
||||
$query = 'USE sakila; ' .
|
||||
'/*test comment*/' .
|
||||
'SELECT * FROM actor; ' .
|
||||
'DELIMITER $$ ' .
|
||||
'UPDATE actor SET last_name = "abc"$$' .
|
||||
'/*!SELECT * FROM actor WHERE last_name = "abc"*/$$';
|
||||
$delimiter = null;
|
||||
|
||||
[$statement, $query, $delimiter] = Query::getFirstStatement($query, $delimiter);
|
||||
$this->assertEquals('USE sakila;', $statement);
|
||||
|
||||
[$statement, $query, $delimiter] = Query::getFirstStatement($query, $delimiter);
|
||||
$this->assertEquals('SELECT * FROM actor;', $statement);
|
||||
|
||||
[$statement, $query, $delimiter] = Query::getFirstStatement($query, $delimiter);
|
||||
$this->assertEquals('DELIMITER $$', $statement);
|
||||
$this->assertEquals('$$', $delimiter);
|
||||
|
||||
[$statement, $query, $delimiter] = Query::getFirstStatement($query, $delimiter);
|
||||
$this->assertEquals('UPDATE actor SET last_name = "abc"$$', $statement);
|
||||
|
||||
[$statement, $query, $delimiter] = Query::getFirstStatement($query, $delimiter);
|
||||
$this->assertEquals('SELECT * FROM actor WHERE last_name = "abc"$$', $statement);
|
||||
}
|
||||
}
|
||||
+321
@@ -0,0 +1,321 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Utils;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
use PhpMyAdmin\SqlParser\Utils\Routine;
|
||||
|
||||
class RoutineTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @param string[] $expected
|
||||
*
|
||||
* @dataProvider getReturnTypeProvider
|
||||
*/
|
||||
public function testGetReturnType(string $def, array $expected): void
|
||||
{
|
||||
$this->assertEquals($expected, Routine::getReturnType($def));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<int, string|array<int, string>>>
|
||||
* @psalm-return list<array{string, string[]}>
|
||||
*/
|
||||
public static function getReturnTypeProvider(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'',
|
||||
[
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
],
|
||||
],
|
||||
[
|
||||
'TEXT',
|
||||
[
|
||||
'',
|
||||
'',
|
||||
'TEXT',
|
||||
'',
|
||||
'',
|
||||
],
|
||||
],
|
||||
[
|
||||
'INT(20)',
|
||||
[
|
||||
'',
|
||||
'',
|
||||
'INT',
|
||||
'20',
|
||||
'',
|
||||
],
|
||||
],
|
||||
[
|
||||
'INT UNSIGNED',
|
||||
[
|
||||
'',
|
||||
'',
|
||||
'INT',
|
||||
'',
|
||||
'UNSIGNED',
|
||||
],
|
||||
],
|
||||
[
|
||||
'VARCHAR(1) CHARSET utf8',
|
||||
[
|
||||
'',
|
||||
'',
|
||||
'VARCHAR',
|
||||
'1',
|
||||
'utf8',
|
||||
],
|
||||
],
|
||||
[
|
||||
'ENUM(\'a\', \'b\') CHARSET latin1',
|
||||
[
|
||||
'',
|
||||
'',
|
||||
'ENUM',
|
||||
'\'a\',\'b\'',
|
||||
'latin1',
|
||||
],
|
||||
],
|
||||
[
|
||||
'DECIMAL(5,2) UNSIGNED ZEROFILL',
|
||||
[
|
||||
'',
|
||||
'',
|
||||
'DECIMAL',
|
||||
'5,2',
|
||||
'UNSIGNED ZEROFILL',
|
||||
],
|
||||
],
|
||||
[
|
||||
'SET(\'test\'\'esc"\', \'more\\\'esc\')',
|
||||
[
|
||||
'',
|
||||
'',
|
||||
'SET',
|
||||
'\'test\'\'esc"\',\'more\\\'esc\'',
|
||||
'',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $expected
|
||||
*
|
||||
* @dataProvider getParameterProvider
|
||||
*/
|
||||
public function testGetParameter(string $def, array $expected): void
|
||||
{
|
||||
$this->assertEquals($expected, Routine::getParameter($def));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<int, string|array<int, string>>>
|
||||
* @psalm-return list<array{string, string[]}>
|
||||
*/
|
||||
public static function getParameterProvider(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'',
|
||||
[
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
],
|
||||
],
|
||||
[
|
||||
'`foo` TEXT',
|
||||
[
|
||||
'',
|
||||
'foo',
|
||||
'TEXT',
|
||||
'',
|
||||
'',
|
||||
],
|
||||
],
|
||||
[
|
||||
'`foo` INT(20)',
|
||||
[
|
||||
'',
|
||||
'foo',
|
||||
'INT',
|
||||
'20',
|
||||
'',
|
||||
],
|
||||
],
|
||||
[
|
||||
'IN `fo``fo` INT UNSIGNED',
|
||||
[
|
||||
'IN',
|
||||
'fo`fo',
|
||||
'INT',
|
||||
'',
|
||||
'UNSIGNED',
|
||||
],
|
||||
],
|
||||
[
|
||||
'OUT bar VARCHAR(1) CHARSET utf8',
|
||||
[
|
||||
'OUT',
|
||||
'bar',
|
||||
'VARCHAR',
|
||||
'1',
|
||||
'utf8',
|
||||
],
|
||||
],
|
||||
[
|
||||
'`"baz\'\'` ENUM(\'a\', \'b\') CHARSET latin1',
|
||||
[
|
||||
'',
|
||||
'"baz\'\'',
|
||||
'ENUM',
|
||||
'\'a\',\'b\'',
|
||||
'latin1',
|
||||
],
|
||||
],
|
||||
[
|
||||
'INOUT `foo` DECIMAL(5,2) UNSIGNED ZEROFILL',
|
||||
[
|
||||
'INOUT',
|
||||
'foo',
|
||||
'DECIMAL',
|
||||
'5,2',
|
||||
'UNSIGNED ZEROFILL',
|
||||
],
|
||||
],
|
||||
[
|
||||
'`foo``s func` SET(\'test\'\'esc"\', \'more\\\'esc\')',
|
||||
[
|
||||
'',
|
||||
'foo`s func',
|
||||
'SET',
|
||||
'\'test\'\'esc"\',\'more\\\'esc\'',
|
||||
'',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, int|string[]|string[][]> $expected
|
||||
* @psalm-param array{
|
||||
* num: int,
|
||||
* dir: string[],
|
||||
* name: string[],
|
||||
* type: string[],
|
||||
* length: string[],
|
||||
* length_arr: string[][],
|
||||
* opts: string[]
|
||||
* } $expected
|
||||
*
|
||||
* @dataProvider getParametersProvider
|
||||
*/
|
||||
public function testGetParameters(string $query, array $expected): void
|
||||
{
|
||||
$parser = new Parser($query);
|
||||
$this->assertEquals($expected, Routine::getParameters($parser->statements[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<int, string|array<string, int|string[]|string[][]>>>
|
||||
* @psalm-return list<array{string, array{
|
||||
* num: int,
|
||||
* dir: string[],
|
||||
* name: string[],
|
||||
* type: string[],
|
||||
* length: string[],
|
||||
* length_arr: string[][],
|
||||
* opts: string[]
|
||||
* }}>
|
||||
*/
|
||||
public static function getParametersProvider(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'CREATE PROCEDURE `foo`() SET @A=0',
|
||||
[
|
||||
'num' => 0,
|
||||
'dir' => [],
|
||||
'name' => [],
|
||||
'type' => [],
|
||||
'length' => [],
|
||||
'length_arr' => [],
|
||||
'opts' => [],
|
||||
],
|
||||
],
|
||||
[
|
||||
'CREATE DEFINER=`user\\`@`somehost``(` FUNCTION `foo```(`baz` INT) BEGIN SELECT NULL; END',
|
||||
[
|
||||
'num' => 1,
|
||||
'dir' => [0 => ''],
|
||||
'name' => [0 => 'baz'],
|
||||
'type' => [0 => 'INT'],
|
||||
'length' => [0 => ''],
|
||||
'length_arr' => [
|
||||
0 => [],
|
||||
],
|
||||
'opts' => [0 => ''],
|
||||
],
|
||||
],
|
||||
[
|
||||
'CREATE PROCEDURE `foo`(IN `baz\\)` INT(25) zerofill unsigned) BEGIN SELECT NULL; END',
|
||||
[
|
||||
'num' => 1,
|
||||
'dir' => [0 => 'IN'],
|
||||
'name' => [0 => 'baz\\)'],
|
||||
'type' => [0 => 'INT'],
|
||||
'length' => [0 => '25'],
|
||||
'length_arr' => [
|
||||
0 => ['25'],
|
||||
],
|
||||
'opts' => [0 => 'UNSIGNED ZEROFILL'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'CREATE PROCEDURE `foo`(IN `baz\\` INT(001) zerofill, out bazz varchar(15) charset utf8) ' .
|
||||
'BEGIN SELECT NULL; END',
|
||||
[
|
||||
'num' => 2,
|
||||
'dir' => [
|
||||
0 => 'IN',
|
||||
1 => 'OUT',
|
||||
],
|
||||
'name' => [
|
||||
0 => 'baz\\',
|
||||
1 => 'bazz',
|
||||
],
|
||||
'type' => [
|
||||
0 => 'INT',
|
||||
1 => 'VARCHAR',
|
||||
],
|
||||
'length' => [
|
||||
0 => '1',
|
||||
1 => '15',
|
||||
],
|
||||
'length_arr' => [
|
||||
0 => ['1'],
|
||||
1 => ['15'],
|
||||
],
|
||||
'opts' => [
|
||||
0 => 'ZEROFILL',
|
||||
1 => 'utf8',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
+262
@@ -0,0 +1,262 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Utils;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
use PhpMyAdmin\SqlParser\Utils\Table;
|
||||
|
||||
class TableTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @param array<string, string|string[]|null>[] $expected
|
||||
* @psalm-param list<array{
|
||||
* constraint: string,
|
||||
* index_list: string[],
|
||||
* ref_db_name: null,
|
||||
* ref_table_name: string,
|
||||
* ref_index_list: string[],
|
||||
* on_update: string,
|
||||
* on_delete?: string
|
||||
* }> $expected
|
||||
*
|
||||
* @dataProvider getForeignKeysProvider
|
||||
*/
|
||||
public function testGetForeignKeys(string $query, array $expected): void
|
||||
{
|
||||
$parser = new Parser($query);
|
||||
$this->assertEquals($expected, Table::getForeignKeys($parser->statements[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<int, string|array<string, string|string[]|null>[]>>
|
||||
* @psalm-return list<array{string, list<array{
|
||||
* constraint: string,
|
||||
* index_list: string[],
|
||||
* ref_db_name: null,
|
||||
* ref_table_name: string,
|
||||
* ref_index_list: string[],
|
||||
* on_update: string,
|
||||
* on_delete?: string
|
||||
* }>}>
|
||||
*/
|
||||
public static function getForeignKeysProvider(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'CREATE USER test',
|
||||
[],
|
||||
],
|
||||
[
|
||||
'CREATE TABLE `payment` (
|
||||
`payment_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`customer_id` smallint(5) unsigned NOT NULL,
|
||||
`staff_id` tinyint(3) unsigned NOT NULL,
|
||||
`rental_id` int(11) DEFAULT NULL,
|
||||
`amount` decimal(5,2) NOT NULL,
|
||||
`payment_date` datetime NOT NULL,
|
||||
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`payment_id`),
|
||||
KEY `idx_fk_staff_id` (`staff_id`),
|
||||
KEY `idx_fk_customer_id` (`customer_id`),
|
||||
KEY `fk_payment_rental` (`rental_id`),
|
||||
CONSTRAINT `fk_payment_customer` FOREIGN KEY (`customer_id`)
|
||||
REFERENCES `customer` (`customer_id`) ON UPDATE CASCADE,
|
||||
CONSTRAINT `fk_payment_rental` FOREIGN KEY (`rental_id`)
|
||||
REFERENCES `rental` (`rental_id`) ON DELETE SET NULL ON UPDATE CASCADE,
|
||||
CONSTRAINT `fk_payment_staff` FOREIGN KEY (`staff_id`)
|
||||
REFERENCES `staff` (`staff_id`) ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=16050 DEFAULT CHARSET=utf8',
|
||||
[
|
||||
[
|
||||
'constraint' => 'fk_payment_customer',
|
||||
'index_list' => ['customer_id'],
|
||||
'ref_db_name' => null,
|
||||
'ref_table_name' => 'customer',
|
||||
'ref_index_list' => ['customer_id'],
|
||||
'on_update' => 'CASCADE',
|
||||
],
|
||||
[
|
||||
'constraint' => 'fk_payment_rental',
|
||||
'index_list' => ['rental_id'],
|
||||
'ref_db_name' => null,
|
||||
'ref_table_name' => 'rental',
|
||||
'ref_index_list' => ['rental_id'],
|
||||
'on_delete' => 'SET_NULL',
|
||||
'on_update' => 'CASCADE',
|
||||
],
|
||||
[
|
||||
'constraint' => 'fk_payment_staff',
|
||||
'index_list' => ['staff_id'],
|
||||
'ref_db_name' => null,
|
||||
'ref_table_name' => 'staff',
|
||||
'ref_index_list' => ['staff_id'],
|
||||
'on_update' => 'CASCADE',
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'CREATE TABLE `actor` (
|
||||
`actor_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`first_name` varchar(45) NOT NULL,
|
||||
`last_name` varchar(45) NOT NULL,
|
||||
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`actor_id`),
|
||||
KEY `idx_actor_last_name` (`last_name`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=201 DEFAULT CHARSET=utf8',
|
||||
[],
|
||||
],
|
||||
[
|
||||
'CREATE TABLE `address` (
|
||||
`address_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`address` varchar(50) NOT NULL,
|
||||
`address2` varchar(50) DEFAULT NULL,
|
||||
`district` varchar(20) NOT NULL,
|
||||
`city_id` smallint(5) unsigned NOT NULL,
|
||||
`postal_code` varchar(10) DEFAULT NULL,
|
||||
`phone` varchar(20) NOT NULL,
|
||||
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`address_id`),
|
||||
KEY `idx_fk_city_id` (`city_id`),
|
||||
CONSTRAINT `fk_address_city` FOREIGN KEY (`city_id`) REFERENCES `city` (`city_id`) ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=606 DEFAULT CHARSET=utf8',
|
||||
[
|
||||
[
|
||||
'constraint' => 'fk_address_city',
|
||||
'index_list' => ['city_id'],
|
||||
'ref_db_name' => null,
|
||||
'ref_table_name' => 'city',
|
||||
'ref_index_list' => ['city_id'],
|
||||
'on_update' => 'CASCADE',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, array<string, bool|string>> $expected
|
||||
* @psalm-param array<string, array{
|
||||
* type: string,
|
||||
* timestamp_not_null: bool,
|
||||
* default_value?: string,
|
||||
* default_current_timestamp?: bool,
|
||||
* on_update_current_timestamp?: bool,
|
||||
* expr?: string
|
||||
* }> $expected
|
||||
*
|
||||
* @dataProvider getFieldsProvider
|
||||
*/
|
||||
public function testGetFields(string $query, array $expected): void
|
||||
{
|
||||
$parser = new Parser($query);
|
||||
$this->assertEquals($expected, Table::getFields($parser->statements[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<int, string|array<string, array<string, bool|string>>>>
|
||||
* @psalm-return list<array{string, array<string, array{
|
||||
* type: string,
|
||||
* timestamp_not_null: bool,
|
||||
* default_value?: string,
|
||||
* default_current_timestamp?: bool,
|
||||
* on_update_current_timestamp?: bool,
|
||||
* expr?: string
|
||||
* }>}>
|
||||
*/
|
||||
public static function getFieldsProvider(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'CREATE USER test',
|
||||
[],
|
||||
],
|
||||
[
|
||||
'CREATE TABLE `address` (
|
||||
`address_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`address` varchar(50) NOT NULL,
|
||||
`address2` varchar(50) DEFAULT NULL,
|
||||
`district` varchar(20) NOT NULL,
|
||||
`city_id` smallint(5) unsigned NOT NULL,
|
||||
`postal_code` varchar(10) DEFAULT NULL,
|
||||
`phone` varchar(20) NOT NULL,
|
||||
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`address_id`),
|
||||
KEY `idx_fk_city_id` (`city_id`),
|
||||
CONSTRAINT `fk_address_city` FOREIGN KEY (`city_id`) REFERENCES `city` (`city_id`) ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=606 DEFAULT CHARSET=utf8',
|
||||
[
|
||||
'address_id' => [
|
||||
'type' => 'SMALLINT',
|
||||
'timestamp_not_null' => false,
|
||||
],
|
||||
'address' => [
|
||||
'type' => 'VARCHAR',
|
||||
'timestamp_not_null' => false,
|
||||
],
|
||||
'address2' => [
|
||||
'type' => 'VARCHAR',
|
||||
'timestamp_not_null' => false,
|
||||
'default_value' => 'NULL',
|
||||
],
|
||||
'district' => [
|
||||
'type' => 'VARCHAR',
|
||||
'timestamp_not_null' => false,
|
||||
],
|
||||
'city_id' => [
|
||||
'type' => 'SMALLINT',
|
||||
'timestamp_not_null' => false,
|
||||
],
|
||||
'postal_code' => [
|
||||
'type' => 'VARCHAR',
|
||||
'timestamp_not_null' => false,
|
||||
'default_value' => 'NULL',
|
||||
],
|
||||
'phone' => [
|
||||
'type' => 'VARCHAR',
|
||||
'timestamp_not_null' => false,
|
||||
],
|
||||
'last_update' => [
|
||||
'type' => 'TIMESTAMP',
|
||||
'timestamp_not_null' => true,
|
||||
'default_value' => 'CURRENT_TIMESTAMP',
|
||||
'default_current_timestamp' => true,
|
||||
'on_update_current_timestamp' => true,
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'CREATE TABLE table1 (
|
||||
a INT NOT NULL,
|
||||
b VARCHAR(32),
|
||||
c INT AS (a mod 10) VIRTUAL,
|
||||
d VARCHAR(5) AS (left(b,5)) PERSISTENT
|
||||
)',
|
||||
[
|
||||
'a' => [
|
||||
'type' => 'INT',
|
||||
'timestamp_not_null' => false,
|
||||
],
|
||||
'b' => [
|
||||
'type' => 'VARCHAR',
|
||||
'timestamp_not_null' => false,
|
||||
],
|
||||
'c' => [
|
||||
'type' => 'INT',
|
||||
'timestamp_not_null' => false,
|
||||
'generated' => true,
|
||||
'expr' => '(a mod 10)',
|
||||
],
|
||||
'd' => [
|
||||
'type' => 'VARCHAR',
|
||||
'timestamp_not_null' => false,
|
||||
'generated' => true,
|
||||
'expr' => '(left(b,5))',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Tests\Utils;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Tests\TestCase;
|
||||
use PhpMyAdmin\SqlParser\Token;
|
||||
use PhpMyAdmin\SqlParser\Utils\Tokens;
|
||||
|
||||
class TokensTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @param array<string, string>[] $find
|
||||
* @param Token[] $replace
|
||||
*
|
||||
* @dataProvider replaceTokensProvider
|
||||
*/
|
||||
public function testReplaceTokens(string $list, array $find, array $replace, string $expected): void
|
||||
{
|
||||
$this->assertEquals($expected, Tokens::replaceTokens($list, $find, $replace));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<int, string|array<string, string>[]|Token[]>>
|
||||
* @psalm-return list<array{string, list<array<string, string>>, Token[], string}>
|
||||
*/
|
||||
public static function replaceTokensProvider(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'SELECT * FROM /*x*/a/*c*/.b',
|
||||
[
|
||||
['value_str' => 'a'],
|
||||
['token' => '.'],
|
||||
],
|
||||
[
|
||||
new Token('c'),
|
||||
new Token('.'),
|
||||
],
|
||||
'SELECT * FROM /*x*/c.b',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, int|string> $pattern
|
||||
*
|
||||
* @dataProvider matchProvider
|
||||
*/
|
||||
public function testMatch(Token $token, array $pattern, bool $expected): void
|
||||
{
|
||||
$this->assertSame($expected, Tokens::match($token, $pattern));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<int, Token|bool|array<string, int|string>>>
|
||||
* @psalm-return list<array{Token, array<string, (int|string)>, bool}>
|
||||
*/
|
||||
public static function matchProvider(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
new Token(''),
|
||||
[],
|
||||
true,
|
||||
],
|
||||
|
||||
[
|
||||
new Token('"abc"', Token::TYPE_STRING, Token::FLAG_STRING_DOUBLE_QUOTES),
|
||||
['token' => '"abc"'],
|
||||
true,
|
||||
],
|
||||
[
|
||||
new Token('"abc"', Token::TYPE_STRING, Token::FLAG_STRING_DOUBLE_QUOTES),
|
||||
['value' => 'abc'],
|
||||
true,
|
||||
],
|
||||
[
|
||||
new Token('"abc"', Token::TYPE_STRING, Token::FLAG_STRING_DOUBLE_QUOTES),
|
||||
['value_str' => 'ABC'],
|
||||
true,
|
||||
],
|
||||
[
|
||||
new Token('"abc"', Token::TYPE_STRING, Token::FLAG_STRING_DOUBLE_QUOTES),
|
||||
['type' => Token::TYPE_STRING],
|
||||
true,
|
||||
],
|
||||
[
|
||||
new Token('"abc"', Token::TYPE_STRING, Token::FLAG_STRING_DOUBLE_QUOTES),
|
||||
['flags' => Token::FLAG_STRING_DOUBLE_QUOTES],
|
||||
true,
|
||||
],
|
||||
|
||||
[
|
||||
new Token('"abc"', Token::TYPE_STRING, Token::FLAG_STRING_DOUBLE_QUOTES),
|
||||
['token' => '"abcd"'],
|
||||
false,
|
||||
],
|
||||
[
|
||||
new Token('"abc"', Token::TYPE_STRING, Token::FLAG_STRING_DOUBLE_QUOTES),
|
||||
['value' => 'abcd'],
|
||||
false,
|
||||
],
|
||||
[
|
||||
new Token('"abc"', Token::TYPE_STRING, Token::FLAG_STRING_DOUBLE_QUOTES),
|
||||
['value_str' => 'ABCd'],
|
||||
false,
|
||||
],
|
||||
[
|
||||
new Token('"abc"', Token::TYPE_STRING, Token::FLAG_STRING_DOUBLE_QUOTES),
|
||||
['type' => Token::TYPE_NUMBER],
|
||||
false,
|
||||
],
|
||||
[
|
||||
new Token('"abc"', Token::TYPE_STRING, Token::FLAG_STRING_DOUBLE_QUOTES),
|
||||
['flags' => Token::FLAG_STRING_SINGLE_QUOTES],
|
||||
false,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
ALTER..2
|
||||
@@ -0,0 +1,94 @@
|
||||
{
|
||||
"query": "ALTER..2",
|
||||
"lexer": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Lexer",
|
||||
"str": "ALTER..2",
|
||||
"len": 8,
|
||||
"last": 8,
|
||||
"list": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\TokensList",
|
||||
"tokens": [
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "ALTER",
|
||||
"value": "ALTER",
|
||||
"keyword": "ALTER",
|
||||
"type": 1,
|
||||
"flags": 3,
|
||||
"position": 0
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": ".",
|
||||
"value": ".",
|
||||
"keyword": null,
|
||||
"type": 2,
|
||||
"flags": 16,
|
||||
"position": 5
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": ".2",
|
||||
"value": 0.2,
|
||||
"keyword": null,
|
||||
"type": 6,
|
||||
"flags": 2,
|
||||
"position": 6
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": null,
|
||||
"value": null,
|
||||
"keyword": null,
|
||||
"type": 9,
|
||||
"flags": 0,
|
||||
"position": null
|
||||
}
|
||||
],
|
||||
"count": 4,
|
||||
"idx": 4
|
||||
},
|
||||
"delimiter": ";",
|
||||
"delimiterLen": 1,
|
||||
"strict": false,
|
||||
"errors": []
|
||||
},
|
||||
"parser": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Parser",
|
||||
"list": {
|
||||
"@type": "@1"
|
||||
},
|
||||
"statements": [
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Statements\\AlterStatement",
|
||||
"table": null,
|
||||
"altered": [],
|
||||
"options": null,
|
||||
"first": 0,
|
||||
"last": 0
|
||||
}
|
||||
],
|
||||
"brackets": 0,
|
||||
"strict": false,
|
||||
"errors": []
|
||||
},
|
||||
"errors": {
|
||||
"lexer": [],
|
||||
"parser": [
|
||||
[
|
||||
"Unrecognized alter operation.",
|
||||
{
|
||||
"@type": "@2"
|
||||
},
|
||||
0
|
||||
],
|
||||
[
|
||||
"Unexpected beginning of statement.",
|
||||
{
|
||||
"@type": "@4"
|
||||
},
|
||||
0
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
WITH](
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
{
|
||||
"query": "WITH](",
|
||||
"lexer": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Lexer",
|
||||
"str": "WITH](",
|
||||
"len": 6,
|
||||
"last": 6,
|
||||
"list": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\TokensList",
|
||||
"tokens": [
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "WITH",
|
||||
"value": "WITH",
|
||||
"keyword": "WITH",
|
||||
"type": 1,
|
||||
"flags": 3,
|
||||
"position": 0
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "]",
|
||||
"value": "]",
|
||||
"keyword": null,
|
||||
"type": 0,
|
||||
"flags": 0,
|
||||
"position": 4
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "(",
|
||||
"value": "(",
|
||||
"keyword": null,
|
||||
"type": 2,
|
||||
"flags": 16,
|
||||
"position": 5
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": null,
|
||||
"value": null,
|
||||
"keyword": null,
|
||||
"type": 9,
|
||||
"flags": 0,
|
||||
"position": null
|
||||
}
|
||||
],
|
||||
"count": 4,
|
||||
"idx": 4
|
||||
},
|
||||
"delimiter": ";",
|
||||
"delimiterLen": 1,
|
||||
"strict": false,
|
||||
"errors": []
|
||||
},
|
||||
"parser": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Parser",
|
||||
"list": {
|
||||
"@type": "@1"
|
||||
},
|
||||
"statements": [
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Statements\\WithStatement",
|
||||
"withers": [],
|
||||
"cteStatementParser": null,
|
||||
"options": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Components\\OptionsArray",
|
||||
"options": []
|
||||
},
|
||||
"first": 0,
|
||||
"last": 0
|
||||
}
|
||||
],
|
||||
"brackets": 1,
|
||||
"strict": false,
|
||||
"errors": []
|
||||
},
|
||||
"errors": {
|
||||
"lexer": [
|
||||
[
|
||||
"Unexpected character.",
|
||||
"]",
|
||||
4,
|
||||
0
|
||||
]
|
||||
],
|
||||
"parser": [
|
||||
[
|
||||
"The name of the CTE was expected.",
|
||||
{
|
||||
"@type": "@3"
|
||||
},
|
||||
0
|
||||
],
|
||||
[
|
||||
"Unexpected end of the WITH CTE.",
|
||||
{
|
||||
"@type": "@3"
|
||||
},
|
||||
0
|
||||
],
|
||||
[
|
||||
"Unexpected beginning of statement.",
|
||||
{
|
||||
"@type": "@3"
|
||||
},
|
||||
0
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
WITH*/A(
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
{
|
||||
"query": "WITH*/A(",
|
||||
"lexer": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Lexer",
|
||||
"str": "WITH*/A(",
|
||||
"len": 8,
|
||||
"last": 8,
|
||||
"list": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\TokensList",
|
||||
"tokens": [
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "WITH",
|
||||
"value": "WITH",
|
||||
"keyword": "WITH",
|
||||
"type": 1,
|
||||
"flags": 3,
|
||||
"position": 0
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "*/",
|
||||
"value": "*/",
|
||||
"keyword": null,
|
||||
"type": 4,
|
||||
"flags": 2,
|
||||
"position": 4
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "A",
|
||||
"value": "A",
|
||||
"keyword": null,
|
||||
"type": 0,
|
||||
"flags": 0,
|
||||
"position": 6
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "(",
|
||||
"value": "(",
|
||||
"keyword": null,
|
||||
"type": 2,
|
||||
"flags": 16,
|
||||
"position": 7
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": null,
|
||||
"value": null,
|
||||
"keyword": null,
|
||||
"type": 9,
|
||||
"flags": 0,
|
||||
"position": null
|
||||
}
|
||||
],
|
||||
"count": 5,
|
||||
"idx": 5
|
||||
},
|
||||
"delimiter": ";",
|
||||
"delimiterLen": 1,
|
||||
"strict": false,
|
||||
"errors": []
|
||||
},
|
||||
"parser": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Parser",
|
||||
"list": {
|
||||
"@type": "@1"
|
||||
},
|
||||
"statements": [
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Statements\\WithStatement",
|
||||
"withers": {
|
||||
"A": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Components\\WithKeyword",
|
||||
"name": "A",
|
||||
"columns": [],
|
||||
"statement": null
|
||||
}
|
||||
},
|
||||
"cteStatementParser": null,
|
||||
"options": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Components\\OptionsArray",
|
||||
"options": []
|
||||
},
|
||||
"first": 0,
|
||||
"last": 3
|
||||
}
|
||||
],
|
||||
"brackets": 0,
|
||||
"strict": false,
|
||||
"errors": []
|
||||
},
|
||||
"errors": {
|
||||
"lexer": [],
|
||||
"parser": [
|
||||
[
|
||||
"A closing bracket was expected.",
|
||||
{
|
||||
"@type": "@6"
|
||||
},
|
||||
0
|
||||
],
|
||||
[
|
||||
"Unexpected end of the WITH CTE.",
|
||||
{
|
||||
"@type": "@6"
|
||||
},
|
||||
0
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
ALTeR=SET
|
||||
@@ -0,0 +1,98 @@
|
||||
{
|
||||
"query": "ALTeR=SET",
|
||||
"lexer": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Lexer",
|
||||
"str": "ALTeR=SET",
|
||||
"len": 9,
|
||||
"last": 9,
|
||||
"list": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\TokensList",
|
||||
"tokens": [
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "ALTeR",
|
||||
"value": "ALTER",
|
||||
"keyword": "ALTER",
|
||||
"type": 1,
|
||||
"flags": 3,
|
||||
"position": 0
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "=",
|
||||
"value": "=",
|
||||
"keyword": null,
|
||||
"type": 2,
|
||||
"flags": 2,
|
||||
"position": 5
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "SET",
|
||||
"value": "SET",
|
||||
"keyword": "SET",
|
||||
"type": 1,
|
||||
"flags": 11,
|
||||
"position": 6
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": null,
|
||||
"value": null,
|
||||
"keyword": null,
|
||||
"type": 9,
|
||||
"flags": 0,
|
||||
"position": null
|
||||
}
|
||||
],
|
||||
"count": 4,
|
||||
"idx": 4
|
||||
},
|
||||
"delimiter": ";",
|
||||
"delimiterLen": 1,
|
||||
"strict": false,
|
||||
"errors": []
|
||||
},
|
||||
"parser": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Parser",
|
||||
"list": {
|
||||
"@type": "@1"
|
||||
},
|
||||
"statements": [
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Statements\\AlterStatement",
|
||||
"table": null,
|
||||
"altered": [],
|
||||
"options": null,
|
||||
"first": 0,
|
||||
"last": 0
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Statements\\SetStatement",
|
||||
"options": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Components\\OptionsArray",
|
||||
"options": []
|
||||
},
|
||||
"end_options": null,
|
||||
"set": [],
|
||||
"first": 1,
|
||||
"last": 2
|
||||
}
|
||||
],
|
||||
"brackets": 0,
|
||||
"strict": false,
|
||||
"errors": []
|
||||
},
|
||||
"errors": {
|
||||
"lexer": [],
|
||||
"parser": [
|
||||
[
|
||||
"Unrecognized alter operation.",
|
||||
{
|
||||
"@type": "@2"
|
||||
},
|
||||
0
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
+0xO
|
||||
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"query": "+0xO",
|
||||
"lexer": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Lexer",
|
||||
"str": "+0xO",
|
||||
"len": 4,
|
||||
"last": 4,
|
||||
"list": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\TokensList",
|
||||
"tokens": [
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "+0x",
|
||||
"value": 0,
|
||||
"keyword": null,
|
||||
"type": 6,
|
||||
"flags": 1,
|
||||
"position": 0
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "O",
|
||||
"value": "O",
|
||||
"keyword": null,
|
||||
"type": 0,
|
||||
"flags": 0,
|
||||
"position": 3
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": null,
|
||||
"value": null,
|
||||
"keyword": null,
|
||||
"type": 9,
|
||||
"flags": 0,
|
||||
"position": null
|
||||
}
|
||||
],
|
||||
"count": 3,
|
||||
"idx": 3
|
||||
},
|
||||
"delimiter": ";",
|
||||
"delimiterLen": 1,
|
||||
"strict": false,
|
||||
"errors": []
|
||||
},
|
||||
"parser": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Parser",
|
||||
"list": {
|
||||
"@type": "@1"
|
||||
},
|
||||
"statements": [],
|
||||
"brackets": 0,
|
||||
"strict": false,
|
||||
"errors": []
|
||||
},
|
||||
"errors": {
|
||||
"lexer": [],
|
||||
"parser": [
|
||||
[
|
||||
"Unexpected beginning of statement.",
|
||||
{
|
||||
"@type": "@2"
|
||||
},
|
||||
0
|
||||
],
|
||||
[
|
||||
"Unexpected beginning of statement.",
|
||||
{
|
||||
"@type": "@3"
|
||||
},
|
||||
0
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
-+0x!
|
||||
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"query": "-+0x!",
|
||||
"lexer": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Lexer",
|
||||
"str": "-+0x!",
|
||||
"len": 5,
|
||||
"last": 5,
|
||||
"list": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\TokensList",
|
||||
"tokens": [
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "-+0x",
|
||||
"value": 0,
|
||||
"keyword": null,
|
||||
"type": 6,
|
||||
"flags": 9,
|
||||
"position": 0
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "!",
|
||||
"value": "!",
|
||||
"keyword": null,
|
||||
"type": 2,
|
||||
"flags": 2,
|
||||
"position": 4
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": null,
|
||||
"value": null,
|
||||
"keyword": null,
|
||||
"type": 9,
|
||||
"flags": 0,
|
||||
"position": null
|
||||
}
|
||||
],
|
||||
"count": 3,
|
||||
"idx": 3
|
||||
},
|
||||
"delimiter": ";",
|
||||
"delimiterLen": 1,
|
||||
"strict": false,
|
||||
"errors": []
|
||||
},
|
||||
"parser": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Parser",
|
||||
"list": {
|
||||
"@type": "@1"
|
||||
},
|
||||
"statements": [],
|
||||
"brackets": 0,
|
||||
"strict": false,
|
||||
"errors": []
|
||||
},
|
||||
"errors": {
|
||||
"lexer": [],
|
||||
"parser": [
|
||||
[
|
||||
"Unexpected beginning of statement.",
|
||||
{
|
||||
"@type": "@2"
|
||||
},
|
||||
0
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE
|
||||
`actor` MODIFY `actor_id` SMALLINT(5) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
AUTO_INCREMENT = 201;
|
||||
@@ -0,0 +1,416 @@
|
||||
{
|
||||
"query": "ALTER TABLE\n `actor` MODIFY `actor_id` SMALLINT(5) UNSIGNED NOT NULL AUTO_INCREMENT,\n AUTO_INCREMENT = 201;",
|
||||
"lexer": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Lexer",
|
||||
"str": "ALTER TABLE\n `actor` MODIFY `actor_id` SMALLINT(5) UNSIGNED NOT NULL AUTO_INCREMENT,\n AUTO_INCREMENT = 201;",
|
||||
"len": 113,
|
||||
"last": 113,
|
||||
"list": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\TokensList",
|
||||
"tokens": [
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "ALTER",
|
||||
"value": "ALTER",
|
||||
"keyword": "ALTER",
|
||||
"type": 1,
|
||||
"flags": 3,
|
||||
"position": 0
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": " ",
|
||||
"value": " ",
|
||||
"keyword": null,
|
||||
"type": 3,
|
||||
"flags": 0,
|
||||
"position": 5
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "TABLE",
|
||||
"value": "TABLE",
|
||||
"keyword": "TABLE",
|
||||
"type": 1,
|
||||
"flags": 3,
|
||||
"position": 6
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "\n ",
|
||||
"value": " ",
|
||||
"keyword": null,
|
||||
"type": 3,
|
||||
"flags": 0,
|
||||
"position": 11
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "`actor`",
|
||||
"value": "actor",
|
||||
"keyword": null,
|
||||
"type": 8,
|
||||
"flags": 2,
|
||||
"position": 16
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": " ",
|
||||
"value": " ",
|
||||
"keyword": null,
|
||||
"type": 3,
|
||||
"flags": 0,
|
||||
"position": 23
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "MODIFY",
|
||||
"value": "MODIFY",
|
||||
"keyword": "MODIFY",
|
||||
"type": 1,
|
||||
"flags": 1,
|
||||
"position": 24
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": " ",
|
||||
"value": " ",
|
||||
"keyword": null,
|
||||
"type": 3,
|
||||
"flags": 0,
|
||||
"position": 30
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "`actor_id`",
|
||||
"value": "actor_id",
|
||||
"keyword": null,
|
||||
"type": 8,
|
||||
"flags": 2,
|
||||
"position": 31
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": " ",
|
||||
"value": " ",
|
||||
"keyword": null,
|
||||
"type": 3,
|
||||
"flags": 0,
|
||||
"position": 41
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "SMALLINT",
|
||||
"value": "SMALLINT",
|
||||
"keyword": "SMALLINT",
|
||||
"type": 1,
|
||||
"flags": 11,
|
||||
"position": 42
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "(",
|
||||
"value": "(",
|
||||
"keyword": null,
|
||||
"type": 2,
|
||||
"flags": 16,
|
||||
"position": 50
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "5",
|
||||
"value": 5,
|
||||
"keyword": null,
|
||||
"type": 6,
|
||||
"flags": 0,
|
||||
"position": 51
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": ")",
|
||||
"value": ")",
|
||||
"keyword": null,
|
||||
"type": 2,
|
||||
"flags": 16,
|
||||
"position": 52
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": " ",
|
||||
"value": " ",
|
||||
"keyword": null,
|
||||
"type": 3,
|
||||
"flags": 0,
|
||||
"position": 53
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "UNSIGNED",
|
||||
"value": "UNSIGNED",
|
||||
"keyword": "UNSIGNED",
|
||||
"type": 1,
|
||||
"flags": 3,
|
||||
"position": 54
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": " ",
|
||||
"value": " ",
|
||||
"keyword": null,
|
||||
"type": 3,
|
||||
"flags": 0,
|
||||
"position": 62
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "NOT NULL",
|
||||
"value": "NOT NULL",
|
||||
"keyword": "NOT NULL",
|
||||
"type": 1,
|
||||
"flags": 7,
|
||||
"position": 63
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": " ",
|
||||
"value": " ",
|
||||
"keyword": null,
|
||||
"type": 3,
|
||||
"flags": 0,
|
||||
"position": 71
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "AUTO_INCREMENT",
|
||||
"value": "AUTO_INCREMENT",
|
||||
"keyword": "AUTO_INCREMENT",
|
||||
"type": 1,
|
||||
"flags": 1,
|
||||
"position": 72
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": ",",
|
||||
"value": ",",
|
||||
"keyword": null,
|
||||
"type": 2,
|
||||
"flags": 16,
|
||||
"position": 86
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "\n ",
|
||||
"value": " ",
|
||||
"keyword": null,
|
||||
"type": 3,
|
||||
"flags": 0,
|
||||
"position": 87
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "AUTO_INCREMENT",
|
||||
"value": "AUTO_INCREMENT",
|
||||
"keyword": "AUTO_INCREMENT",
|
||||
"type": 1,
|
||||
"flags": 1,
|
||||
"position": 92
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": " ",
|
||||
"value": " ",
|
||||
"keyword": null,
|
||||
"type": 3,
|
||||
"flags": 0,
|
||||
"position": 106
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "=",
|
||||
"value": "=",
|
||||
"keyword": null,
|
||||
"type": 2,
|
||||
"flags": 2,
|
||||
"position": 107
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": " ",
|
||||
"value": " ",
|
||||
"keyword": null,
|
||||
"type": 3,
|
||||
"flags": 0,
|
||||
"position": 108
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "201",
|
||||
"value": 201,
|
||||
"keyword": null,
|
||||
"type": 6,
|
||||
"flags": 0,
|
||||
"position": 109
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": ";",
|
||||
"value": ";",
|
||||
"keyword": null,
|
||||
"type": 9,
|
||||
"flags": 0,
|
||||
"position": 112
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": null,
|
||||
"value": null,
|
||||
"keyword": null,
|
||||
"type": 9,
|
||||
"flags": 0,
|
||||
"position": null
|
||||
}
|
||||
],
|
||||
"count": 29,
|
||||
"idx": 29
|
||||
},
|
||||
"delimiter": ";",
|
||||
"delimiterLen": 1,
|
||||
"strict": false,
|
||||
"errors": []
|
||||
},
|
||||
"parser": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Parser",
|
||||
"list": {
|
||||
"@type": "@1"
|
||||
},
|
||||
"statements": [
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Statements\\AlterStatement",
|
||||
"table": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Components\\Expression",
|
||||
"database": null,
|
||||
"table": "actor",
|
||||
"column": null,
|
||||
"expr": "`actor`",
|
||||
"alias": null,
|
||||
"function": null,
|
||||
"subquery": null
|
||||
},
|
||||
"altered": [
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Components\\AlterOperation",
|
||||
"ROUTINE_OPTIONS": {
|
||||
"COMMENT": [
|
||||
1,
|
||||
"var"
|
||||
],
|
||||
"LANGUAGE SQL": 2,
|
||||
"CONTAINS SQL": 3,
|
||||
"NO SQL": 3,
|
||||
"READS SQL DATA": 3,
|
||||
"MODIFIES SQL DATA": 3,
|
||||
"SQL SECURITY": 4,
|
||||
"DEFINER": 5,
|
||||
"INVOKER": 5
|
||||
},
|
||||
"options": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Components\\OptionsArray",
|
||||
"options": {
|
||||
"1": "MODIFY"
|
||||
}
|
||||
},
|
||||
"field": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Components\\Expression",
|
||||
"database": null,
|
||||
"table": null,
|
||||
"column": "actor_id",
|
||||
"expr": "`actor_id`",
|
||||
"alias": null,
|
||||
"function": null,
|
||||
"subquery": null
|
||||
},
|
||||
"partitions": null,
|
||||
"unknown": [
|
||||
{
|
||||
"@type": "@12"
|
||||
},
|
||||
{
|
||||
"@type": "@13"
|
||||
},
|
||||
{
|
||||
"@type": "@14"
|
||||
},
|
||||
{
|
||||
"@type": "@15"
|
||||
},
|
||||
{
|
||||
"@type": "@16"
|
||||
},
|
||||
{
|
||||
"@type": "@17"
|
||||
},
|
||||
{
|
||||
"@type": "@18"
|
||||
},
|
||||
{
|
||||
"@type": "@19"
|
||||
},
|
||||
{
|
||||
"@type": "@20"
|
||||
},
|
||||
{
|
||||
"@type": "@21"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Components\\AlterOperation",
|
||||
"ROUTINE_OPTIONS": {
|
||||
"COMMENT": [
|
||||
1,
|
||||
"var"
|
||||
],
|
||||
"LANGUAGE SQL": 2,
|
||||
"CONTAINS SQL": 3,
|
||||
"NO SQL": 3,
|
||||
"READS SQL DATA": 3,
|
||||
"MODIFIES SQL DATA": 3,
|
||||
"SQL SECURITY": 4,
|
||||
"DEFINER": 5,
|
||||
"INVOKER": 5
|
||||
},
|
||||
"options": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Components\\OptionsArray",
|
||||
"options": {
|
||||
"1": {
|
||||
"name": "AUTO_INCREMENT",
|
||||
"equals": true,
|
||||
"expr": "201",
|
||||
"value": "201"
|
||||
}
|
||||
}
|
||||
},
|
||||
"field": null,
|
||||
"partitions": null,
|
||||
"unknown": []
|
||||
}
|
||||
],
|
||||
"options": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Components\\OptionsArray",
|
||||
"options": {
|
||||
"3": "TABLE"
|
||||
}
|
||||
},
|
||||
"first": 0,
|
||||
"last": 27
|
||||
}
|
||||
],
|
||||
"brackets": 0,
|
||||
"strict": false,
|
||||
"errors": []
|
||||
},
|
||||
"errors": {
|
||||
"lexer": [],
|
||||
"parser": []
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
CREATE TABLE `jos_core_acl_aro` (
|
||||
`id` int(11) NOT NULL,
|
||||
`section_value` varchar(240) NOT NULL DEFAULT '0',
|
||||
`value` varchar(240) NOT NULL DEFAULT '',
|
||||
`order_value` int(11) NOT NULL DEFAULT '0',
|
||||
`name` varchar(255) NOT NULL DEFAULT '',
|
||||
`hidden` int(11) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `jos_section_value_value_aro` (`section_value`(100),`value`(15)) USING BTREE,
|
||||
KEY `jos_gacl_hidden_aro` (`hidden`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1
|
||||
+1593
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
update tbl_customer t set t.`description`=:yp where t.id=1;
|
||||
+284
@@ -0,0 +1,284 @@
|
||||
{
|
||||
"query": "update tbl_customer t set t.`description`=:yp where t.id=1;",
|
||||
"lexer": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Lexer",
|
||||
"str": "update tbl_customer t set t.`description`=:yp where t.id=1;",
|
||||
"len": 59,
|
||||
"last": 59,
|
||||
"list": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\TokensList",
|
||||
"tokens": [
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "update",
|
||||
"value": "UPDATE",
|
||||
"keyword": "UPDATE",
|
||||
"type": 1,
|
||||
"flags": 3,
|
||||
"position": 0
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": " ",
|
||||
"value": " ",
|
||||
"keyword": null,
|
||||
"type": 3,
|
||||
"flags": 0,
|
||||
"position": 6
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "tbl_customer",
|
||||
"value": "tbl_customer",
|
||||
"keyword": null,
|
||||
"type": 0,
|
||||
"flags": 0,
|
||||
"position": 7
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": " ",
|
||||
"value": " ",
|
||||
"keyword": null,
|
||||
"type": 3,
|
||||
"flags": 0,
|
||||
"position": 19
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "t",
|
||||
"value": "t",
|
||||
"keyword": null,
|
||||
"type": 0,
|
||||
"flags": 0,
|
||||
"position": 20
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": " ",
|
||||
"value": " ",
|
||||
"keyword": null,
|
||||
"type": 3,
|
||||
"flags": 0,
|
||||
"position": 21
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "set",
|
||||
"value": "SET",
|
||||
"keyword": "SET",
|
||||
"type": 1,
|
||||
"flags": 11,
|
||||
"position": 22
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": " ",
|
||||
"value": " ",
|
||||
"keyword": null,
|
||||
"type": 3,
|
||||
"flags": 0,
|
||||
"position": 25
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "t",
|
||||
"value": "t",
|
||||
"keyword": null,
|
||||
"type": 0,
|
||||
"flags": 0,
|
||||
"position": 26
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": ".",
|
||||
"value": ".",
|
||||
"keyword": null,
|
||||
"type": 2,
|
||||
"flags": 16,
|
||||
"position": 27
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "`description`",
|
||||
"value": "description",
|
||||
"keyword": null,
|
||||
"type": 8,
|
||||
"flags": 2,
|
||||
"position": 28
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "=",
|
||||
"value": "=",
|
||||
"keyword": null,
|
||||
"type": 2,
|
||||
"flags": 2,
|
||||
"position": 41
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": ":yp",
|
||||
"value": "yp",
|
||||
"keyword": null,
|
||||
"type": 8,
|
||||
"flags": 16,
|
||||
"position": 42
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": " ",
|
||||
"value": " ",
|
||||
"keyword": null,
|
||||
"type": 3,
|
||||
"flags": 0,
|
||||
"position": 45
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "where",
|
||||
"value": "WHERE",
|
||||
"keyword": "WHERE",
|
||||
"type": 1,
|
||||
"flags": 3,
|
||||
"position": 46
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": " ",
|
||||
"value": " ",
|
||||
"keyword": null,
|
||||
"type": 3,
|
||||
"flags": 0,
|
||||
"position": 51
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "t",
|
||||
"value": "t",
|
||||
"keyword": null,
|
||||
"type": 0,
|
||||
"flags": 0,
|
||||
"position": 52
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": ".",
|
||||
"value": ".",
|
||||
"keyword": null,
|
||||
"type": 2,
|
||||
"flags": 16,
|
||||
"position": 53
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "id",
|
||||
"value": "id",
|
||||
"keyword": null,
|
||||
"type": 0,
|
||||
"flags": 0,
|
||||
"position": 54
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "=",
|
||||
"value": "=",
|
||||
"keyword": null,
|
||||
"type": 2,
|
||||
"flags": 2,
|
||||
"position": 56
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": "1",
|
||||
"value": 1,
|
||||
"keyword": null,
|
||||
"type": 6,
|
||||
"flags": 0,
|
||||
"position": 57
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": ";",
|
||||
"value": ";",
|
||||
"keyword": null,
|
||||
"type": 9,
|
||||
"flags": 0,
|
||||
"position": 58
|
||||
},
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Token",
|
||||
"token": null,
|
||||
"value": null,
|
||||
"keyword": null,
|
||||
"type": 9,
|
||||
"flags": 0,
|
||||
"position": null
|
||||
}
|
||||
],
|
||||
"count": 23,
|
||||
"idx": 23
|
||||
},
|
||||
"delimiter": ";",
|
||||
"delimiterLen": 1,
|
||||
"strict": false,
|
||||
"errors": []
|
||||
},
|
||||
"parser": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Parser",
|
||||
"list": {
|
||||
"@type": "@1"
|
||||
},
|
||||
"statements": [
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Statements\\UpdateStatement",
|
||||
"tables": [
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Components\\Expression",
|
||||
"database": null,
|
||||
"table": "tbl_customer",
|
||||
"column": null,
|
||||
"expr": "tbl_customer",
|
||||
"alias": "t",
|
||||
"function": null,
|
||||
"subquery": null
|
||||
}
|
||||
],
|
||||
"set": [
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Components\\SetOperation",
|
||||
"column": "t.`description`",
|
||||
"value": ":yp"
|
||||
}
|
||||
],
|
||||
"where": [
|
||||
{
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Components\\Condition",
|
||||
"identifiers": [
|
||||
"t",
|
||||
"id"
|
||||
],
|
||||
"isOperator": false,
|
||||
"expr": "t.id=1"
|
||||
}
|
||||
],
|
||||
"order": null,
|
||||
"limit": null,
|
||||
"join": null,
|
||||
"options": {
|
||||
"@type": "PhpMyAdmin\\SqlParser\\Components\\OptionsArray",
|
||||
"options": []
|
||||
},
|
||||
"first": 0,
|
||||
"last": 20
|
||||
}
|
||||
],
|
||||
"brackets": 0,
|
||||
"strict": false,
|
||||
"errors": []
|
||||
},
|
||||
"errors": {
|
||||
"lexer": [],
|
||||
"parser": []
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE `mail_template` CHANGE COLUMN `mtpl_group` `mtpl_group` ENUM('ORDER') NULL DEFAULT NULL ;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user