Skip to content

Commit

Permalink
ANSI_QUOTES lexer and parser test-suite proposal
Browse files Browse the repository at this point in the history
  • Loading branch information
smarek authored and williamdes committed Mar 20, 2020
1 parent 1665e9a commit bd519e4
Show file tree
Hide file tree
Showing 11 changed files with 282 additions and 26 deletions.
27 changes: 26 additions & 1 deletion src/Context.php
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ public static function isSymbol($str)
}
if ($str[0] === '@') {
return Token::FLAG_SYMBOL_VARIABLE;
} elseif ($str[0] === '`') {
} elseif ($str[0] === self::getIdentifierQuote()) {
return Token::FLAG_SYMBOL_BACKTICK;
} elseif ($str[0] === ':' || $str[0] === '?') {
return Token::FLAG_SYMBOL_PARAMETER;
Expand All @@ -443,6 +443,8 @@ public static function isString($str)
}
if ($str[0] === '\'') {
return Token::FLAG_STRING_SINGLE_QUOTES;
} elseif (self::hasMode(self::SQL_MODE_ANSI_QUOTES) && $str[0] === '"') {
return null;
} elseif ($str[0] === '"') {
return Token::FLAG_STRING_DOUBLE_QUOTES;
}
Expand Down Expand Up @@ -589,6 +591,29 @@ public static function escape($str, $quote = '`')

return $quote . str_replace($quote, $quote . $quote, $str) . $quote;
}

/**
* Returns char used to quote identifiers based on currently set SQL Mode (ie. standard or ANSI_QUOTES)
* @return string either " (double quote, ansi_quotes mode) or ` (backtick, standard mode)
*/
public static function getIdentifierQuote()
{
return self::hasMode(self::SQL_MODE_ANSI_QUOTES) ? '"' : '`';
}

/**
* Function verifies that given SQL Mode constant is currently set
*
* @return boolean false on empty param, true/false on given constant/int value
* @param int $flag for example Context::SQL_MODE_ANSI_QUOTES
*/
public static function hasMode($flag = null)
{
if (empty($flag)) {
return false;
}
return (self::$MODE & $flag) === $flag;
}
}

// Initializing the default context.
Expand Down
4 changes: 3 additions & 1 deletion src/Lexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -863,6 +863,7 @@ public function parseNumber()
* @param string $quote additional starting symbol
*
* @return null|Token
* @throws LexerException
*/
public function parseString($quote = '')
{
Expand Down Expand Up @@ -908,6 +909,7 @@ public function parseString($quote = '')
* Parses a symbol.
*
* @return null|Token
* @throws LexerException
*/
public function parseSymbol()
{
Expand All @@ -933,7 +935,7 @@ public function parseSymbol()
$str = null;

if ($this->last < $this->len) {
if (($str = $this->parseString('`')) === null) {
if (($str = $this->parseString(Context::getIdentifierQuote())) === null) {
if (($str = $this->parseUnknown()) === null) {
$this->error(
'Variable name was expected.',
Expand Down
33 changes: 24 additions & 9 deletions src/Utils/CLI.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public function mergeLongOpts(&$params, &$longopts)

public function usageHighlight()
{
echo "Usage: highlight-query --query SQL [--format html|cli|text]\n";
echo "Usage: highlight-query --query SQL [--format html|cli|text] [--ansi]\n";
echo " cat file.sql | highlight-query\n";
}

Expand All @@ -45,10 +45,11 @@ public function parseHighlight()
$longopts = array(
'help',
'query:',
'format:'
'format:',
'ansi'
);
$params = $this->getopt(
'hq:f:',
'hq:f:a',
$longopts
);
if ($params === false) {
Expand Down Expand Up @@ -83,6 +84,10 @@ public function runHighlight()
$params['q'] = $stdIn;
}
}

if (isset($params['a'])) {
Context::setMode('ANSI_QUOTES');
}
if (isset($params['q'])) {
echo Formatter::format(
$params['q'],
Expand All @@ -100,7 +105,7 @@ public function runHighlight()

public function usageLint()
{
echo "Usage: lint-query --query SQL\n";
echo "Usage: lint-query --query SQL [--ansi]\n";
echo " cat file.sql | lint-query\n";
}

Expand All @@ -109,10 +114,11 @@ public function parseLint()
$longopts = array(
'help',
'query:',
'context:'
'context:',
'ansi'
);
$params = $this->getopt(
'hq:c:',
'hq:c:a',
$longopts
);
$this->mergeLongOpts($params, $longopts);
Expand All @@ -139,6 +145,10 @@ public function runLint()
$params['q'] = $stdIn;
}
}
if (isset($params['a'])) {
Context::setMode('ANSI_QUOTES');
}

if (isset($params['q'])) {
$lexer = new Lexer($params['q'], false);
$parser = new Parser($lexer->list);
Expand All @@ -160,18 +170,19 @@ public function runLint()

public function usageTokenize()
{
echo "Usage: tokenize-query --query SQL\n";
echo "Usage: tokenize-query --query SQL [--ansi]\n";
echo " cat file.sql | tokenize-query\n";
}

public function parseTokenize()
{
$longopts = array(
'help',
'query:'
'query:',
'ansi'
);
$params = $this->getopt(
'hq:',
'hq:a',
$longopts
);
$this->mergeLongOpts($params, $longopts);
Expand All @@ -195,6 +206,10 @@ public function runTokenize()
$params['q'] = $stdIn;
}
}

if (isset($params['a'])) {
Context::setMode('ANSI_QUOTES');
}
if (isset($params['q'])) {
$lexer = new Lexer($params['q'], false);
foreach ($lexer->list->tokens as $idx => $token) {
Expand Down
3 changes: 2 additions & 1 deletion tests/Parser/ParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ public function parseProvider()
return array(
array('parser/parse'),
array('parser/parse2'),
array('parser/parseDelimiter')
array('parser/parseDelimiter'),
array('parser/ansi/parseAnsi')
);
}

Expand Down
9 changes: 9 additions & 0 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use PhpMyAdmin\SqlParser\Lexer;
use PhpMyAdmin\SqlParser\Parser;
use PhpMyAdmin\SqlParser\TokensList;
use PhpMyAdmin\SqlParser\Context;
use PHPUnit\Framework\TestCase as BaseTestCase;

$GLOBALS['lang'] = 'en';
Expand Down Expand Up @@ -98,6 +99,11 @@ public function runParserTest($name)
*/
$data = $this->getData($name);

if (strpos($name, '/ansi/') !== false) {
// set mode if appropriate
Context::setMode('ANSI_QUOTES');
}

// Lexer.
$lexer = new Lexer($data['query']);
$lexerErrors = $this->getErrorsAsArray($lexer);
Expand All @@ -118,5 +124,8 @@ public function runParserTest($name)
// Testing errors.
$this->assertEquals($data['errors']['parser'], $parserErrors);
$this->assertEquals($data['errors']['lexer'], $lexerErrors);

// reset mode after test run
Context::setMode();
}
}
24 changes: 12 additions & 12 deletions tests/Utils/CLITest.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,14 @@ public function highlightParams()
),
array(
array('h' => true),
'Usage: highlight-query --query SQL [--format html|cli|text]' . "\n" .
'Usage: highlight-query --query SQL [--format html|cli|text] [--ansi]' . "\n" .
' cat file.sql | highlight-query' . "\n",
0
),
array(
array(),
'ERROR: Missing parameters!' . "\n" .
'Usage: highlight-query --query SQL [--format html|cli|text]' . "\n" .
'Usage: highlight-query --query SQL [--format html|cli|text] [--ansi]' . "\n" .
' cat file.sql | highlight-query' . "\n",
1,
),
Expand Down Expand Up @@ -162,15 +162,15 @@ public function highlightParamsStdIn()
array(
'',
array('h' => true),
'Usage: highlight-query --query SQL [--format html|cli|text]' . "\n" .
'Usage: highlight-query --query SQL [--format html|cli|text] [--ansi]' . "\n" .
' cat file.sql | highlight-query' . "\n",
0
),
array(
'',
array(),
'ERROR: Missing parameters!' . "\n" .
'Usage: highlight-query --query SQL [--format html|cli|text]' . "\n" .
'Usage: highlight-query --query SQL [--format html|cli|text] [--ansi]' . "\n" .
' cat file.sql | highlight-query' . "\n",
1,
),
Expand Down Expand Up @@ -227,14 +227,14 @@ public function lintParamsStdIn()
'',
array(),
'ERROR: Missing parameters!' . "\n" .
'Usage: lint-query --query SQL' . "\n" .
'Usage: lint-query --query SQL [--ansi]' . "\n" .
' cat file.sql | lint-query' . "\n",
1,
),
array(
'',
array('h' => true),
'Usage: lint-query --query SQL' . "\n" .
'Usage: lint-query --query SQL [--ansi]' . "\n" .
' cat file.sql | lint-query' . "\n",
0,
),
Expand Down Expand Up @@ -290,14 +290,14 @@ public function lintParams()
),
array(
array('h' => true),
'Usage: lint-query --query SQL' . "\n" .
'Usage: lint-query --query SQL [--ansi]' . "\n" .
' cat file.sql | lint-query' . "\n",
0,
),
array(
array(),
'ERROR: Missing parameters!' . "\n" .
'Usage: lint-query --query SQL' . "\n" .
'Usage: lint-query --query SQL [--ansi]' . "\n" .
' cat file.sql | lint-query' . "\n",
1,
),
Expand Down Expand Up @@ -345,14 +345,14 @@ public function tokenizeParams()
),
array(
array('h' => true),
'Usage: tokenize-query --query SQL' . "\n" .
'Usage: tokenize-query --query SQL [--ansi]' . "\n" .
' cat file.sql | tokenize-query' . "\n",
0,
),
array(
array(),
'ERROR: Missing parameters!' . "\n" .
'Usage: tokenize-query --query SQL' . "\n" .
'Usage: tokenize-query --query SQL [--ansi]' . "\n" .
' cat file.sql | tokenize-query' . "\n",
1,
),
Expand Down Expand Up @@ -398,15 +398,15 @@ public function tokenizeParamsStdIn()
array(
'',
array('h' => true),
'Usage: tokenize-query --query SQL' . "\n" .
'Usage: tokenize-query --query SQL [--ansi]' . "\n" .
' cat file.sql | tokenize-query' . "\n",
0,
),
array(
'',
array(),
'ERROR: Missing parameters!' . "\n" .
'Usage: tokenize-query --query SQL' . "\n" .
'Usage: tokenize-query --query SQL [--ansi]' . "\n" .
' cat file.sql | tokenize-query' . "\n",
1,
),
Expand Down
24 changes: 24 additions & 0 deletions tests/data/lexer/ansi/lexAnsi.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
CREATE TABLE "addresses" (
"id" int(11) NOT NULL AUTO_INCREMENT,
"country_id" int(11) NOT NULL,
"company_id" int(11) DEFAULT NULL,
"censored" int(11) DEFAULT NULL,
"company_name" varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
"address_type_id" int(11) DEFAULT NULL,
"street" varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
"city" varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
"postal_code" varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
"country_region" varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
"modified" datetime DEFAULT NULL,
"created" datetime DEFAULT NULL,
PRIMARY KEY ("id"),
UNIQUE KEY "censored" ("censored"),
KEY "country_id" ("country_id"),
KEY "company_id" ("company_id"),
KEY "address_type_id" ("address_type_id"),
KEY "censored" ("censored"),
CONSTRAINT "addresses_ibfk_1" FOREIGN KEY ("address_type_id") REFERENCES "address_types" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT "addresses_ibfk_2" FOREIGN KEY ("company_id") REFERENCES "companies" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT "addresses_ibfk_3" FOREIGN KEY ("country_id") REFERENCES "countries" ("id"),
CONSTRAINT "addresses_ibfk_4" FOREIGN KEY ("censored") REFERENCES "censored" ("id") ON DELETE CASCADE ON UPDATE CASCADE
)
Loading

0 comments on commit bd519e4

Please sign in to comment.