GeezerVison is showing github.com — ANSI_QUOTES lexer and parser test-suite proposal · phpmyadmin/sql-parser@bd519e4
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: src/Context.php +26-1Lines changed: 26 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -417,7 +417,7 @@ public static function isSymbol($str) | 417 | 417 | } | 418 | 418 | if ($str[0] === '@') { | 419 | 419 | return Token::FLAG_SYMBOL_VARIABLE; | 420 | | - } elseif ($str[0] === '`') { | | 420 | + } elseif ($str[0] === self::getIdentifierQuote()) { | 421 | 421 | return Token::FLAG_SYMBOL_BACKTICK; | 422 | 422 | } elseif ($str[0] === ':' || $str[0] === '?') { | 423 | 423 | return Token::FLAG_SYMBOL_PARAMETER; | @@ -443,6 +443,8 @@ public static function isString($str) | 443 | 443 | } | 444 | 444 | if ($str[0] === '\'') { | 445 | 445 | return Token::FLAG_STRING_SINGLE_QUOTES; | | 446 | + } elseif (self::hasMode(self::SQL_MODE_ANSI_QUOTES) && $str[0] === '"') { | | 447 | + return null; | 446 | 448 | } elseif ($str[0] === '"') { | 447 | 449 | return Token::FLAG_STRING_DOUBLE_QUOTES; | 448 | 450 | } | @@ -589,6 +591,29 @@ public static function escape($str, $quote = '`') | 589 | 591 |
| 590 | 592 | return $quote . str_replace($quote, $quote . $quote, $str) . $quote; | 591 | 593 | } | | 594 | + | | 595 | + /** | | 596 | + * Returns char used to quote identifiers based on currently set SQL Mode (ie. standard or ANSI_QUOTES) | | 597 | + * @return string either " (double quote, ansi_quotes mode) or ` (backtick, standard mode) | | 598 | + */ | | 599 | + public static function getIdentifierQuote() | | 600 | + { | | 601 | + return self::hasMode(self::SQL_MODE_ANSI_QUOTES) ? '"' : '`'; | | 602 | + } | | 603 | + | | 604 | + /** | | 605 | + * Function verifies that given SQL Mode constant is currently set | | 606 | + * | | 607 | + * @return boolean false on empty param, true/false on given constant/int value | | 608 | + * @param int $flag for example Context::SQL_MODE_ANSI_QUOTES | | 609 | + */ | | 610 | + public static function hasMode($flag = null) | | 611 | + { | | 612 | + if (empty($flag)) { | | 613 | + return false; | | 614 | + } | | 615 | + return (self::$MODE & $flag) === $flag; | | 616 | + } | 592 | 617 | } | 593 | 618 |
| 594 | 619 | // Initializing the default context. | |
Collapse file Copy file name to clipboardExpand all lines: src/Lexer.php +3-1Lines changed: 3 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -863,6 +863,7 @@ public function parseNumber() | 863 | 863 | * @param string $quote additional starting symbol | 864 | 864 | * | 865 | 865 | * @return null|Token | | 866 | + * @throws LexerException | 866 | 867 | */ | 867 | 868 | public function parseString($quote = '') | 868 | 869 | { | @@ -908,6 +909,7 @@ public function parseString($quote = '') | 908 | 909 | * Parses a symbol. | 909 | 910 | * | 910 | 911 | * @return null|Token | | 912 | + * @throws LexerException | 911 | 913 | */ | 912 | 914 | public function parseSymbol() | 913 | 915 | { | @@ -933,7 +935,7 @@ public function parseSymbol() | 933 | 935 | $str = null; | 934 | 936 |
| 935 | 937 | if ($this->last < $this->len) { | 936 | | - if (($str = $this->parseString('`')) === null) { | | 938 | + if (($str = $this->parseString(Context::getIdentifierQuote())) === null) { | 937 | 939 | if (($str = $this->parseUnknown()) === null) { | 938 | 940 | $this->error( | 939 | 941 | 'Variable name was expected.', | |
Collapse file Copy file name to clipboardExpand all lines: src/Utils/CLI.php +24-9Lines changed: 24 additions & 9 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -31,7 +31,7 @@ public function mergeLongOpts(&$params, &$longopts) | 31 | 31 |
| 32 | 32 | public function usageHighlight() | 33 | 33 | { | 34 | | - echo "Usage: highlight-query --query SQL [--format html|cli|text]\n"; | | 34 | + echo "Usage: highlight-query --query SQL [--format html|cli|text] [--ansi]\n"; | 35 | 35 | echo " cat file.sql | highlight-query\n"; | 36 | 36 | } | 37 | 37 |
| @@ -45,10 +45,11 @@ public function parseHighlight() | 45 | 45 | $longopts = array( | 46 | 46 | 'help', | 47 | 47 | 'query:', | 48 | | - 'format:' | | 48 | + 'format:', | | 49 | + 'ansi' | 49 | 50 | ); | 50 | 51 | $params = $this->getopt( | 51 | | - 'hq:f:', | | 52 | + 'hq:f:a', | 52 | 53 | $longopts | 53 | 54 | ); | 54 | 55 | if ($params === false) { | @@ -83,6 +84,10 @@ public function runHighlight() | 83 | 84 | $params['q'] = $stdIn; | 84 | 85 | } | 85 | 86 | } | | 87 | + | | 88 | + if (isset($params['a'])) { | | 89 | + Context::setMode('ANSI_QUOTES'); | | 90 | + } | 86 | 91 | if (isset($params['q'])) { | 87 | 92 | echo Formatter::format( | 88 | 93 | $params['q'], | @@ -100,7 +105,7 @@ public function runHighlight() | 100 | 105 |
| 101 | 106 | public function usageLint() | 102 | 107 | { | 103 | | - echo "Usage: lint-query --query SQL\n"; | | 108 | + echo "Usage: lint-query --query SQL [--ansi]\n"; | 104 | 109 | echo " cat file.sql | lint-query\n"; | 105 | 110 | } | 106 | 111 |
| @@ -109,10 +114,11 @@ public function parseLint() | 109 | 114 | $longopts = array( | 110 | 115 | 'help', | 111 | 116 | 'query:', | 112 | | - 'context:' | | 117 | + 'context:', | | 118 | + 'ansi' | 113 | 119 | ); | 114 | 120 | $params = $this->getopt( | 115 | | - 'hq:c:', | | 121 | + 'hq:c:a', | 116 | 122 | $longopts | 117 | 123 | ); | 118 | 124 | $this->mergeLongOpts($params, $longopts); | @@ -139,6 +145,10 @@ public function runLint() | 139 | 145 | $params['q'] = $stdIn; | 140 | 146 | } | 141 | 147 | } | | 148 | + if (isset($params['a'])) { | | 149 | + Context::setMode('ANSI_QUOTES'); | | 150 | + } | | 151 | + | 142 | 152 | if (isset($params['q'])) { | 143 | 153 | $lexer = new Lexer($params['q'], false); | 144 | 154 | $parser = new Parser($lexer->list); | @@ -160,18 +170,19 @@ public function runLint() | 160 | 170 |
| 161 | 171 | public function usageTokenize() | 162 | 172 | { | 163 | | - echo "Usage: tokenize-query --query SQL\n"; | | 173 | + echo "Usage: tokenize-query --query SQL [--ansi]\n"; | 164 | 174 | echo " cat file.sql | tokenize-query\n"; | 165 | 175 | } | 166 | 176 |
| 167 | 177 | public function parseTokenize() | 168 | 178 | { | 169 | 179 | $longopts = array( | 170 | 180 | 'help', | 171 | | - 'query:' | | 181 | + 'query:', | | 182 | + 'ansi' | 172 | 183 | ); | 173 | 184 | $params = $this->getopt( | 174 | | - 'hq:', | | 185 | + 'hq:a', | 175 | 186 | $longopts | 176 | 187 | ); | 177 | 188 | $this->mergeLongOpts($params, $longopts); | @@ -195,6 +206,10 @@ public function runTokenize() | 195 | 206 | $params['q'] = $stdIn; | 196 | 207 | } | 197 | 208 | } | | 209 | + | | 210 | + if (isset($params['a'])) { | | 211 | + Context::setMode('ANSI_QUOTES'); | | 212 | + } | 198 | 213 | if (isset($params['q'])) { | 199 | 214 | $lexer = new Lexer($params['q'], false); | 200 | 215 | foreach ($lexer->list->tokens as $idx => $token) { | |
Collapse file Copy file name to clipboardExpand all lines: tests/Parser/ParserTest.php +2-1Lines changed: 2 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -25,7 +25,8 @@ public function parseProvider() | 25 | 25 | return array( | 26 | 26 | array('parser/parse'), | 27 | 27 | array('parser/parse2'), | 28 | | - array('parser/parseDelimiter') | | 28 | + array('parser/parseDelimiter'), | | 29 | + array('parser/ansi/parseAnsi') | 29 | 30 | ); | 30 | 31 | } | 31 | 32 |
| |
Collapse file Copy file name to clipboardExpand all lines: tests/TestCase.php +9Lines changed: 9 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 9 | 9 | use PhpMyAdmin\SqlParser\Lexer; | 10 | 10 | use PhpMyAdmin\SqlParser\Parser; | 11 | 11 | use PhpMyAdmin\SqlParser\TokensList; | | 12 | +use PhpMyAdmin\SqlParser\Context; | 12 | 13 | use PHPUnit\Framework\TestCase as BaseTestCase; | 13 | 14 |
| 14 | 15 | $GLOBALS['lang'] = 'en'; | @@ -98,6 +99,11 @@ public function runParserTest($name) | 98 | 99 | */ | 99 | 100 | $data = $this->getData($name); | 100 | 101 |
| | 102 | + if (strpos($name, '/ansi/') !== false) { | | 103 | + // set mode if appropriate | | 104 | + Context::setMode('ANSI_QUOTES'); | | 105 | + } | | 106 | + | 101 | 107 | // Lexer. | 102 | 108 | $lexer = new Lexer($data['query']); | 103 | 109 | $lexerErrors = $this->getErrorsAsArray($lexer); | @@ -118,5 +124,8 @@ public function runParserTest($name) | 118 | 124 | // Testing errors. | 119 | 125 | $this->assertEquals($data['errors']['parser'], $parserErrors); | 120 | 126 | $this->assertEquals($data['errors']['lexer'], $lexerErrors); | | 127 | + | | 128 | + // reset mode after test run | | 129 | + Context::setMode(); | 121 | 130 | } | 122 | 131 | } |
Collapse file Copy file name to clipboardExpand all lines: tests/Utils/CLITest.php +12-12Lines changed: 12 additions & 12 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -90,14 +90,14 @@ public function highlightParams() | 90 | 90 | ), | 91 | 91 | array( | 92 | 92 | array('h' => true), | 93 | | - 'Usage: highlight-query --query SQL [--format html|cli|text]' . "\n" . | | 93 | + 'Usage: highlight-query --query SQL [--format html|cli|text] [--ansi]' . "\n" . | 94 | 94 | ' cat file.sql | highlight-query' . "\n", | 95 | 95 | 0 | 96 | 96 | ), | 97 | 97 | array( | 98 | 98 | array(), | 99 | 99 | 'ERROR: Missing parameters!' . "\n" . | 100 | | - 'Usage: highlight-query --query SQL [--format html|cli|text]' . "\n" . | | 100 | + 'Usage: highlight-query --query SQL [--format html|cli|text] [--ansi]' . "\n" . | 101 | 101 | ' cat file.sql | highlight-query' . "\n", | 102 | 102 | 1, | 103 | 103 | ), | @@ -162,15 +162,15 @@ public function highlightParamsStdIn() | 162 | 162 | array( | 163 | 163 | '', | 164 | 164 | array('h' => true), | 165 | | - 'Usage: highlight-query --query SQL [--format html|cli|text]' . "\n" . | | 165 | + 'Usage: highlight-query --query SQL [--format html|cli|text] [--ansi]' . "\n" . | 166 | 166 | ' cat file.sql | highlight-query' . "\n", | 167 | 167 | 0 | 168 | 168 | ), | 169 | 169 | array( | 170 | 170 | '', | 171 | 171 | array(), | 172 | 172 | 'ERROR: Missing parameters!' . "\n" . | 173 | | - 'Usage: highlight-query --query SQL [--format html|cli|text]' . "\n" . | | 173 | + 'Usage: highlight-query --query SQL [--format html|cli|text] [--ansi]' . "\n" . | 174 | 174 | ' cat file.sql | highlight-query' . "\n", | 175 | 175 | 1, | 176 | 176 | ), | @@ -227,14 +227,14 @@ public function lintParamsStdIn() | 227 | 227 | '', | 228 | 228 | array(), | 229 | 229 | 'ERROR: Missing parameters!' . "\n" . | 230 | | - 'Usage: lint-query --query SQL' . "\n" . | | 230 | + 'Usage: lint-query --query SQL [--ansi]' . "\n" . | 231 | 231 | ' cat file.sql | lint-query' . "\n", | 232 | 232 | 1, | 233 | 233 | ), | 234 | 234 | array( | 235 | 235 | '', | 236 | 236 | array('h' => true), | 237 | | - 'Usage: lint-query --query SQL' . "\n" . | | 237 | + 'Usage: lint-query --query SQL [--ansi]' . "\n" . | 238 | 238 | ' cat file.sql | lint-query' . "\n", | 239 | 239 | 0, | 240 | 240 | ), | @@ -290,14 +290,14 @@ public function lintParams() | 290 | 290 | ), | 291 | 291 | array( | 292 | 292 | array('h' => true), | 293 | | - 'Usage: lint-query --query SQL' . "\n" . | | 293 | + 'Usage: lint-query --query SQL [--ansi]' . "\n" . | 294 | 294 | ' cat file.sql | lint-query' . "\n", | 295 | 295 | 0, | 296 | 296 | ), | 297 | 297 | array( | 298 | 298 | array(), | 299 | 299 | 'ERROR: Missing parameters!' . "\n" . | 300 | | - 'Usage: lint-query --query SQL' . "\n" . | | 300 | + 'Usage: lint-query --query SQL [--ansi]' . "\n" . | 301 | 301 | ' cat file.sql | lint-query' . "\n", | 302 | 302 | 1, | 303 | 303 | ), | @@ -345,14 +345,14 @@ public function tokenizeParams() | 345 | 345 | ), | 346 | 346 | array( | 347 | 347 | array('h' => true), | 348 | | - 'Usage: tokenize-query --query SQL' . "\n" . | | 348 | + 'Usage: tokenize-query --query SQL [--ansi]' . "\n" . | 349 | 349 | ' cat file.sql | tokenize-query' . "\n", | 350 | 350 | 0, | 351 | 351 | ), | 352 | 352 | array( | 353 | 353 | array(), | 354 | 354 | 'ERROR: Missing parameters!' . "\n" . | 355 | | - 'Usage: tokenize-query --query SQL' . "\n" . | | 355 | + 'Usage: tokenize-query --query SQL [--ansi]' . "\n" . | 356 | 356 | ' cat file.sql | tokenize-query' . "\n", | 357 | 357 | 1, | 358 | 358 | ), | @@ -398,15 +398,15 @@ public function tokenizeParamsStdIn() | 398 | 398 | array( | 399 | 399 | '', | 400 | 400 | array('h' => true), | 401 | | - 'Usage: tokenize-query --query SQL' . "\n" . | | 401 | + 'Usage: tokenize-query --query SQL [--ansi]' . "\n" . | 402 | 402 | ' cat file.sql | tokenize-query' . "\n", | 403 | 403 | 0, | 404 | 404 | ), | 405 | 405 | array( | 406 | 406 | '', | 407 | 407 | array(), | 408 | 408 | 'ERROR: Missing parameters!' . "\n" . | 409 | | - 'Usage: tokenize-query --query SQL' . "\n" . | | 409 | + 'Usage: tokenize-query --query SQL [--ansi]' . "\n" . | 410 | 410 | ' cat file.sql | tokenize-query' . "\n", | 411 | 411 | 1, | 412 | 412 | ), | |
Collapse file Copy file name to clipboard +24Lines changed: 24 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +CREATE TABLE "addresses" ( | | 2 | + "id" int(11) NOT NULL AUTO_INCREMENT, | | 3 | + "country_id" int(11) NOT NULL, | | 4 | + "company_id" int(11) DEFAULT NULL, | | 5 | + "censored" int(11) DEFAULT NULL, | | 6 | + "company_name" varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, | | 7 | + "address_type_id" int(11) DEFAULT NULL, | | 8 | + "street" varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, | | 9 | + "city" varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, | | 10 | + "postal_code" varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, | | 11 | + "country_region" varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, | | 12 | + "modified" datetime DEFAULT NULL, | | 13 | + "created" datetime DEFAULT NULL, | | 14 | + PRIMARY KEY ("id"), | | 15 | + UNIQUE KEY "censored" ("censored"), | | 16 | + KEY "country_id" ("country_id"), | | 17 | + KEY "company_id" ("company_id"), | | 18 | + KEY "address_type_id" ("address_type_id"), | | 19 | + KEY "censored" ("censored"), | | 20 | + CONSTRAINT "addresses_ibfk_1" FOREIGN KEY ("address_type_id") REFERENCES "address_types" ("id") ON DELETE CASCADE ON UPDATE CASCADE, | | 21 | + CONSTRAINT "addresses_ibfk_2" FOREIGN KEY ("company_id") REFERENCES "companies" ("id") ON DELETE CASCADE ON UPDATE CASCADE, | | 22 | + CONSTRAINT "addresses_ibfk_3" FOREIGN KEY ("country_id") REFERENCES "countries" ("id"), | | 23 | + CONSTRAINT "addresses_ibfk_4" FOREIGN KEY ("censored") REFERENCES "censored" ("id") ON DELETE CASCADE ON UPDATE CASCADE | | 24 | +) |
|