From 4724fab294d2277322306ed9df2957da1397d97c Mon Sep 17 00:00:00 2001 From: Albin Zeng Date: Tue, 9 Jul 2019 10:51:47 +0800 Subject: [PATCH 01/37] feat: union grammar, to resolve grammar conflicts. #2 --- src/sqlParser.jison | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/sqlParser.jison b/src/sqlParser.jison index 40b0631..1550d71 100644 --- a/src/sqlParser.jison +++ b/src/sqlParser.jison @@ -89,6 +89,7 @@ SHARE return 'SHARE' MODE return 'MODE' OJ return 'OJ' LIMIT return 'LIMIT' +UNION return 'UNION' "," return ',' "=" return '=' @@ -159,8 +160,32 @@ LIMIT return 'LIMIT' %% /* language grammar */ main - : selectClause EOF { return {nodeType: 'Main', value: $1}; } - | selectClause ';' EOF { return {nodeType: 'Main', value: $1, hasSemicolon: true}; } + : selectClause semicolonOpt EOF { return {nodeType: 'Main', value: $1, hasSemicolon: $2}; } + | unionClause semicolonOpt EOF { return {nodeType: 'Main', value: $1, hasSemicolon: $2}; } + ; + +semicolonOpt + : ';' { $$ = true } + | { $$ = false } + ; + +unionClause + : unionClauseNotParenthesized { $$ = $1 } + | unionClauseParenthesized { $$ = $1 } + ; + +unionClauseParenthesized + : selectClauseParenthesized UNION distinctOpt selectClauseParenthesized order_by_opt limit_opt { $$ = { type: 'Union', left: $1, distinctOpt: $3, right: $4, orderBy: $5, limit: $6 }; } + | selectClauseParenthesized UNION distinctOpt unionClauseParenthesized order_by_opt limit_opt { $$ = { type: 'Union', left: $1, distinctOpt: $3, right: $4, orderBy: $5, limit: $6 } } + ; + +selectClauseParenthesized + : '(' selectClause ')' { $$ = { type: 'SelectParenthesized', value: $2 } } + ; + +unionClauseNotParenthesized + : selectClause UNION distinctOpt selectClause { $$ = { type: 'Union', left: $1, distinctOpt: $3, right: $4 } } + | selectClause UNION distinctOpt unionClauseNotParenthesized { $$ = { type: 'Union', left: $1, distinctOpt: $3, right: $4 } } ; selectClause From 2bbd726f37b299bbd8a20b4a4276457278cb9fb1 Mon Sep 17 00:00:00 2001 From: Albin Zeng Date: Mon, 22 Jul 2019 12:11:24 +0800 Subject: [PATCH 02/37] feat: union supported --- src/sqlParser.jison | 8 ++++---- src/stringify.js | 19 +++++++++++++++++-- test/main.test.js | 23 ++++++++++++++++++++--- 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/src/sqlParser.jison b/src/sqlParser.jison index 1550d71..f322390 100644 --- a/src/sqlParser.jison +++ b/src/sqlParser.jison @@ -171,16 +171,16 @@ semicolonOpt unionClause : unionClauseNotParenthesized { $$ = $1 } - | unionClauseParenthesized { $$ = $1 } + | unionClauseParenthesized order_by_opt limit_opt { $$ = $1, $$.orderBy = $2, $$.limit = $3; } ; unionClauseParenthesized - : selectClauseParenthesized UNION distinctOpt selectClauseParenthesized order_by_opt limit_opt { $$ = { type: 'Union', left: $1, distinctOpt: $3, right: $4, orderBy: $5, limit: $6 }; } - | selectClauseParenthesized UNION distinctOpt unionClauseParenthesized order_by_opt limit_opt { $$ = { type: 'Union', left: $1, distinctOpt: $3, right: $4, orderBy: $5, limit: $6 } } + : selectClauseParenthesized UNION distinctOpt selectClauseParenthesized { $$ = { type: 'Union', left: $1, distinctOpt: $3, right: $4 }; } + | selectClauseParenthesized UNION distinctOpt unionClauseParenthesized { $$ = { type: 'Union', left: $1, distinctOpt: $3, right: $4 }; } ; selectClauseParenthesized - : '(' selectClause ')' { $$ = { type: 'SelectParenthesized', value: $2 } } + : '(' selectClause ')' { $$ = { type: 'SelectParenthesized', value: $2 }; } ; unionClauseNotParenthesized diff --git a/src/stringify.js b/src/stringify.js index fd1d289..338479e 100644 --- a/src/stringify.js +++ b/src/stringify.js @@ -1,3 +1,5 @@ +import { timingSafeEqual } from "crypto"; + if (!sqlParser) { sqlParser = {}; } @@ -55,13 +57,13 @@ Sql.prototype.append = function(word, noPrefix, noSuffix) { } } Sql.prototype.travelMain = function(ast) { - this.travelSelect(ast.value); + this.travel(ast.value); if (ast.hasSemicolon) { this.append(';', true); } } Sql.prototype.travelSelect = function(ast) { - this.appendKeyword('select', true); + this.appendKeyword('select'); if (ast.distinctOpt) { this.appendKeyword(ast.distinctOpt); } @@ -537,3 +539,16 @@ Sql.prototype.travelTableFactor = function (ast) { this.travel(ast.indexHintOpt); } } +Sql.prototype.travelUnion = function (ast) { + this.travel(ast.left); + this.appendKeyword('UNION'); + if (ast.distinctOpt) { + this.appendKeyword(ast.distinctOpt) + } + this.travel(ast.right); +} +Sql.prototype.travelSelectParenthesized = function (ast) { + this.appendKeyword('('); + this.travel(ast.value); + this.appendKeyword(')'); +} diff --git a/test/main.test.js b/test/main.test.js index b4114ba..8908c22 100644 --- a/test/main.test.js +++ b/test/main.test.js @@ -5,9 +5,13 @@ const parser = require('../'); const testParser = function (sql) { let firstAst = parser.parse(sql); + debug(JSON.stringify(firstAst, null, 2)); let firstSql = parser.stringify(firstAst); + debug(firstSql); let secondAst = parser.parse(firstSql); + debug(parser.stringify(secondAst)); let secondSql = parser.stringify(secondAst); + debug(JSON.stringify(secondAst, null, 2)); if (firstSql !== secondSql) { console.log('firstSql', firstSql); @@ -15,9 +19,6 @@ const testParser = function (sql) { throw 'err firstSql don\'t equals secondSql. '; } - debug(JSON.stringify(secondAst, null, 2)); - debug(parser.stringify(secondAst)); - return secondAst; } @@ -360,5 +361,21 @@ describe('select grammar support', function () { testParser('SELECT COUNT(*) AS total, a b, b as c, c/2 d, d & e an FROM b'); }); + it ('union support, https://dev.mysql.com/doc/refman/8.0/en/union.html', function () { + testParser('select a from dual union select a from foo;'); + }); + + it ('union Parenthesized support, https://dev.mysql.com/doc/refman/8.0/en/union.html', function () { + testParser('(select a from dual) union (select a from foo) order by a desc limit 100, 100;'); + }); + + it ('union all support, https://dev.mysql.com/doc/refman/8.0/en/union.html', function () { + testParser('(select a from dual) union all (select a from foo) order by a limit 100'); + }); + + it ('union distinct support, https://dev.mysql.com/doc/refman/8.0/en/union.html', function () { + testParser('select a from dual order by a desc limit 1, 1 union distinct select a from foo order by a limit 1'); + }); + }); From 9e6128284a6f22e6e2bd1f95f98df472fc4eb258 Mon Sep 17 00:00:00 2001 From: Albin Zeng Date: Mon, 22 Jul 2019 12:30:08 +0800 Subject: [PATCH 03/37] release: 1.0.8 --- CHANGELOG | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 73b22bb..77f18d1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,2 +1,3 @@ 1.0.6 add MIT license. 1.0.7 fix alias for identifier #16 , pr: #17 +1.0.8 support union grammar follow: https://dev.mysql.com/doc/refman/8.0/en/union.html diff --git a/package.json b/package.json index 07942c0..9f90f81 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "js-sql-parser", - "version": "1.0.7", + "version": "1.0.8", "description": "", "main": "./dist/parser/sqlParser.js", "scripts": { From 6bf4dc7e1f626e47fcdc87874e4e9516ae72db2f Mon Sep 17 00:00:00 2001 From: Michael Mior Date: Tue, 23 Jul 2019 16:35:25 -0400 Subject: [PATCH 04/37] Remove unused import --- src/stringify.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/stringify.js b/src/stringify.js index 338479e..c9eb348 100644 --- a/src/stringify.js +++ b/src/stringify.js @@ -1,5 +1,3 @@ -import { timingSafeEqual } from "crypto"; - if (!sqlParser) { sqlParser = {}; } From ff9b7ce20c31b115012fc3a34f1041e3fbee33ec Mon Sep 17 00:00:00 2001 From: Albin Zeng Date: Thu, 25 Jul 2019 10:05:09 +0800 Subject: [PATCH 05/37] release: v1.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9f90f81..b2c8cb7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "js-sql-parser", - "version": "1.0.8", + "version": "1.1.0", "description": "", "main": "./dist/parser/sqlParser.js", "scripts": { From 499bc189d83ca084026c606163c700a9ff974440 Mon Sep 17 00:00:00 2001 From: Caleb Rogers Date: Tue, 30 Jul 2019 15:42:36 -0700 Subject: [PATCH 06/37] Change "refrence" to "reference" in all instances "Refrence" is a misspelling of "reference." --- src/sqlParser.jison | 18 +++++++++--------- src/stringify.js | 8 ++++---- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/sqlParser.jison b/src/sqlParser.jison index f322390..bc15230 100644 --- a/src/sqlParser.jison +++ b/src/sqlParser.jison @@ -10,7 +10,7 @@ [-][-]\s.*\n /* skip sql comments */ [#]\s.*\n /* skip sql comments */ \s+ /* skip whitespace */ - + [`][a-zA-Z_\u4e00-\u9fa5][a-zA-Z0-9_\u4e00-\u9fa5]*[`] return 'IDENTIFIER' [\w]+[\u4e00-\u9fa5]+[0-9a-zA-Z_\u4e00-\u9fa5]* return 'IDENTIFIER' [\u4e00-\u9fa5][0-9a-zA-Z_\u4e00-\u9fa5]* return 'IDENTIFIER' @@ -117,7 +117,7 @@ UNION return 'UNION' "{" return '{' "}" return '}' ";" return ';' - + ['](\\.|[^'])*['] return 'STRING' ["](\\.|[^"])*["] return 'STRING' [0][x][0-9a-fA-F]+ return 'HEX_NUMERIC' @@ -127,7 +127,7 @@ UNION return 'UNION' [a-zA-Z_\u4e00-\u9fa5][a-zA-Z0-9_\u4e00-\u9fa5]* return 'IDENTIFIER' \. return 'DOT' ['"][a-zA-Z_\u4e00-\u9fa5][a-zA-Z0-9_\u4e00-\u9fa5]*["'] return 'QUOTED_IDENTIFIER' - + <> return 'EOF' . return 'INVALID' @@ -189,7 +189,7 @@ unionClauseNotParenthesized ; selectClause - : SELECT + : SELECT distinctOpt highPriorityOpt maxStateMentTimeOpt @@ -228,7 +228,7 @@ selectClause ; distinctOpt - : ALL { $$ = $1 } + : ALL { $$ = $1 } | DISTINCT { $$ = $1 } | DISTINCTROW { $$ = $1 } | { $$ = null } @@ -361,7 +361,7 @@ simple_expr ; bit_expr : simple_expr { $$ = $1 } - | bit_expr '|' bit_expr { $$ = { type: 'BitExpression', operator: '|', left: $1, right: $3 } } + | bit_expr '|' bit_expr { $$ = { type: 'BitExpression', operator: '|', left: $1, right: $3 } } | bit_expr '&' bit_expr { $$ = { type: 'BitExpression', operator: '&', left: $1, right: $3 } } | bit_expr '<<' bit_expr { $$ = { type: 'BitExpression', operator: '<<', left: $1, right: $3 } } | bit_expr '>>' bit_expr { $$ = { type: 'BitExpression', operator: '>>', left: $1, right: $3 } } @@ -494,12 +494,12 @@ selectDataSetOpt { $$ = { from: $2, partition: $3, where: $4, groupBy: $5, having: $6, orderBy: $7, limit: $8, procedure: $9, updateLockMode: $10 } } ; table_refrences - : escaped_table_reference { $$ = { type: 'TableRefrences', value: [ $1 ] } } + : escaped_table_reference { $$ = { type: 'TableReferences', value: [ $1 ] } } | table_refrences ',' escaped_table_reference %prec TABLE_REF_COMMA { $$ = $1; $1.value.push($3); } ; escaped_table_reference - : table_reference { $$ = { type: 'TableRefrence', value: $1 } } - | '{' OJ table_reference '}' { $$ = { type: 'TableRefrence', hasOj: true, value: $3 } } + : table_reference { $$ = { type: 'TableReference', value: $1 } } + | '{' OJ table_reference '}' { $$ = { type: 'TableReference', hasOj: true, value: $3 } } ; join_inner_cross : { $$ = null } diff --git a/src/stringify.js b/src/stringify.js index c9eb348..14b7e05 100644 --- a/src/stringify.js +++ b/src/stringify.js @@ -387,9 +387,9 @@ Sql.prototype.travelLimit = function (ast) { } } } -Sql.prototype.travelTableRefrences = function (ast) { +Sql.prototype.travelTableReferences = function (ast) { var list = ast.value; - if (ast.TableRefrences) { + if (ast.TableReferences) { this.append('(', false, true); } for (var i = 0; i < list.length; i++) { @@ -398,11 +398,11 @@ Sql.prototype.travelTableRefrences = function (ast) { this.append(',', true); } } - if (ast.TableRefrences) { + if (ast.TableReferences) { this.append(')'); } } -Sql.prototype.travelTableRefrence = function (ast) { +Sql.prototype.travelTableReference = function (ast) { if (ast.hasOj) { this.append('{'); this.appendKeyword('oj'); From c8758ee7c157f350cf04cf59ba6cebeb633fad8d Mon Sep 17 00:00:00 2001 From: Albin Zeng Date: Wed, 31 Jul 2019 14:37:08 +0800 Subject: [PATCH 07/37] release: v1.2.0 & append fix for typo. --- CHANGELOG | 2 ++ package.json | 2 +- src/sqlParser.jison | 8 ++++---- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 77f18d1..9165fdc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,5 @@ 1.0.6 add MIT license. 1.0.7 fix alias for identifier #16 , pr: #17 1.0.8 support union grammar follow: https://dev.mysql.com/doc/refman/8.0/en/union.html +1.1.0 hotfix: Remove unused import +1.2.0 fix typo "refrence" to "reference" #24 diff --git a/package.json b/package.json index b2c8cb7..d971222 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "js-sql-parser", - "version": "1.1.0", + "version": "1.2.0", "description": "", "main": "./dist/parser/sqlParser.js", "scripts": { diff --git a/src/sqlParser.jison b/src/sqlParser.jison index bc15230..e3cb798 100644 --- a/src/sqlParser.jison +++ b/src/sqlParser.jison @@ -490,12 +490,12 @@ for_update_lock_in_share_mode_opt ; selectDataSetOpt : { $$ = {} } - | FROM table_refrences partitionOpt where_opt group_by_opt having_opt order_by_opt limit_opt procedure_opt for_update_lock_in_share_mode_opt + | FROM table_references partitionOpt where_opt group_by_opt having_opt order_by_opt limit_opt procedure_opt for_update_lock_in_share_mode_opt { $$ = { from: $2, partition: $3, where: $4, groupBy: $5, having: $6, orderBy: $7, limit: $8, procedure: $9, updateLockMode: $10 } } ; -table_refrences +table_references : escaped_table_reference { $$ = { type: 'TableReferences', value: [ $1 ] } } - | table_refrences ',' escaped_table_reference %prec TABLE_REF_COMMA { $$ = $1; $1.value.push($3); } + | table_references ',' escaped_table_reference %prec TABLE_REF_COMMA { $$ = $1; $1.value.push($3); } ; escaped_table_reference : table_reference { $$ = { type: 'TableReference', value: $1 } } @@ -583,5 +583,5 @@ index_hint table_factor : identifier partitionOpt aliasOpt index_hint_list_opt { $$ = { type: 'TableFactor', value: $1, partition: $2, alias: $3.alias, hasAs: $3.hasAs, indexHintOpt: $4 } } | '(' selectClause ')' aliasOpt { $$ = { type: 'SubQuery', value: $2, alias: $4.alias, hasAs: $4.hasAs } } - | '(' table_refrences ')' { $$ = $2; $$.hasParentheses = true } + | '(' table_references ')' { $$ = $2; $$.hasParentheses = true } ; From 508f697ca407165be084b894b1248bc93791ce42 Mon Sep 17 00:00:00 2001 From: Albin Zeng Date: Wed, 31 Jul 2019 14:48:28 +0800 Subject: [PATCH 08/37] chore: put `news` into README.md --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 642fa81..e7297dc 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,12 @@ sql grammar follows https://dev.mysql.com/doc/refman/5.7/en/select.html +## news + +- Typo 'refrence' has been fixed to 'reference' since v1.2.0. + +for more changes see [CHANGELOG](./CHANGELOG) + ## commonjs usage `npm install --save js-sql-parser` From e8e7cc41a4bac167ac7d73ca0917495a1d501e22 Mon Sep 17 00:00:00 2001 From: Ryan Mentzer Date: Mon, 21 Oct 2019 10:16:31 -0500 Subject: [PATCH 09/37] support stringifying the having keyword --- package.json | 5 +- src/stringify.js | 229 +++++++++++++++++++++++----------------------- test/main.test.js | 69 ++++++++------ 3 files changed, 157 insertions(+), 146 deletions(-) diff --git a/package.json b/package.json index d971222..728c4f8 100644 --- a/package.json +++ b/package.json @@ -5,11 +5,11 @@ "main": "./dist/parser/sqlParser.js", "scripts": { "build": "jison -m js ./src/sqlParser.jison -o ./dist/parser/sqlParser.js && npm run build-concat", - "build-concat": "cat src/stringify.js src/suffix.js >> ./dist/parser/sqlParser.js", + "build-concat": "minicat src/stringify.js src/suffix.js >> dist/parser/sqlParser.js", "postbuild": "npm run test:build", "test": "npm run build", "test:all": "mocha --require babel-register", - "test:build": "mocha --require babel-register 'test/*.test.js'", + "test:build": "mocha --require babel-register test/*.test.js", "test:benchmark": "mocha --require babel-register test/benchmark.js" }, "repository": { @@ -33,6 +33,7 @@ "concat": "^1.0.3", "debug": "^3.1.0", "jison": "^0.4.17", + "minicat": "^1.0.0", "mocha": "^3.2.0" } } diff --git a/src/stringify.js b/src/stringify.js index 14b7e05..57ea74e 100644 --- a/src/stringify.js +++ b/src/stringify.js @@ -6,13 +6,13 @@ function Sql() { this.buffer = ''; } -sqlParser.stringify = function (ast) { +sqlParser.stringify = function(ast) { var sql = new Sql(); sql.travelMain(ast); return sql.buffer; -} +}; -Sql.prototype.travel = function (ast) { +Sql.prototype.travel = function(ast) { if (!ast) return; if (typeof ast === 'string') { @@ -21,7 +21,7 @@ Sql.prototype.travel = function (ast) { var processor = this['travel' + ast.type]; processor.call(this, ast); -} +}; var noSuffixFlag = false; Sql.prototype.appendKeyword = function(keyword, noPrefix, noSuffix) { @@ -38,7 +38,7 @@ Sql.prototype.appendKeyword = function(keyword, noPrefix, noSuffix) { if (noSuffix) { noSuffixFlag = true; } -} +}; Sql.prototype.append = function(word, noPrefix, noSuffix) { if (noSuffixFlag) { noPrefix = true; @@ -53,13 +53,13 @@ Sql.prototype.append = function(word, noPrefix, noSuffix) { if (noSuffix) { noSuffixFlag = true; } -} +}; Sql.prototype.travelMain = function(ast) { this.travel(ast.value); if (ast.hasSemicolon) { this.append(';', true); } -} +}; Sql.prototype.travelSelect = function(ast) { this.appendKeyword('select'); if (ast.distinctOpt) { @@ -107,6 +107,7 @@ Sql.prototype.travelSelect = function(ast) { this.travel(ast.groupBy); } if (ast.having) { + this.appendKeyword('having'); this.travel(ast.having); } if (ast.orderBy) { @@ -122,8 +123,8 @@ Sql.prototype.travelSelect = function(ast) { if (ast.updateLockMode) { this.appendKeyword(ast.updateLockMode); } -} -Sql.prototype.travelSelectExpr = function (ast) { +}; +Sql.prototype.travelSelectExpr = function(ast) { var exprList = ast.value; for (var i = 0; i < exprList.length; i++) { if (typeof ast === 'string') { @@ -141,69 +142,69 @@ Sql.prototype.travelSelectExpr = function (ast) { this.append(',', true); } } -} -Sql.prototype.travelIsExpression = function (ast) { +}; +Sql.prototype.travelIsExpression = function(ast) { this.travel(ast.left); this.appendKeyword('in'); if (ast.hasNot) { this.appendKeyword('not'); } this.append(ast.right); -} -Sql.prototype.travelNotExpression = function (ast) { +}; +Sql.prototype.travelNotExpression = function(ast) { this.appendKeyword('not'); this.travel(ast.value); -} -Sql.prototype.travelOrExpression = -Sql.prototype.travelAndExpression = -Sql.prototype.travelXORExpression = function (ast) { +}; +Sql.prototype.travelOrExpression = Sql.prototype.travelAndExpression = Sql.prototype.travelXORExpression = function( + ast +) { this.travel(ast.left); this.appendKeyword(ast.operator); this.travel(ast.right); -} -Sql.prototype.travelNull = -Sql.prototype.travelBoolean = -Sql.prototype.travelBooleanExtra = function (ast) { +}; +Sql.prototype.travelNull = Sql.prototype.travelBoolean = Sql.prototype.travelBooleanExtra = function( + ast +) { this.appendKeyword(ast.value); -} -Sql.prototype.travelNumber = function (ast) { +}; +Sql.prototype.travelNumber = function(ast) { this.append(ast.value); -} -Sql.prototype.travelString = function (ast) { +}; +Sql.prototype.travelString = function(ast) { this.append(ast.value); -} -Sql.prototype.travelFunctionCall = function (ast) { +}; +Sql.prototype.travelFunctionCall = function(ast) { this.append(ast.name); this.append('(', true, true); var params = ast.params; for (var i = 0; i < params.length; i++) { var param = params[i]; this.travel(param); - if (i !== params.length -1) { + if (i !== params.length - 1) { this.append(',', true); } } this.append(')', true); -} -Sql.prototype.travelFunctionCallParam = function (ast) { +}; +Sql.prototype.travelFunctionCallParam = function(ast) { if (ast.distinctOpt) { this.appendKeyword(ast.distinctOpt); } this.travel(ast.value); -} -Sql.prototype.travelIdentifier = function (ast) { +}; +Sql.prototype.travelIdentifier = function(ast) { this.append(ast.value); -} -Sql.prototype.travelIdentifierList = function (ast) { +}; +Sql.prototype.travelIdentifierList = function(ast) { var list = ast.value; for (var i = 0; i < list.length; i++) { this.travel(list[i]); - if (i !== list.length -1) { + if (i !== list.length - 1) { this.append(',', true); } } -} -Sql.prototype.travelWhenThenList = function (ast) { +}; +Sql.prototype.travelWhenThenList = function(ast) { var list = ast.value; for (var i = 0; i < list.length; i++) { this.appendKeyword('when'); @@ -211,8 +212,8 @@ Sql.prototype.travelWhenThenList = function (ast) { this.appendKeyword('then'); this.travel(list[i].then); } -} -Sql.prototype.travelCaseWhen = function (ast) { +}; +Sql.prototype.travelCaseWhen = function(ast) { this.appendKeyword('case'); if (ast.caseExprOpt) { this.travel(ast.caseExprOpt); @@ -223,39 +224,39 @@ Sql.prototype.travelCaseWhen = function (ast) { this.travel(ast.else); } this.appendKeyword('end'); -} -Sql.prototype.travelPrefix = function (ast) { +}; +Sql.prototype.travelPrefix = function(ast) { this.appendKeyword(ast.prefix); this.travel(ast.value); -} -Sql.prototype.travelSimpleExprParentheses = function (ast) { +}; +Sql.prototype.travelSimpleExprParentheses = function(ast) { if (ast.hasRow) { this.appendKeyword('row'); } this.append('(', false, true); this.travel(ast.value); this.append(')', true); -} -Sql.prototype.travelSubQuery = function (ast) { +}; +Sql.prototype.travelSubQuery = function(ast) { if (ast.hasExists) { this.appendKeyword('exists'); } this.append('(', false, true); this.travel(ast.value); this.append(')', true); -} -Sql.prototype.travelIdentifierExpr = function (ast) { +}; +Sql.prototype.travelIdentifierExpr = function(ast) { this.append('{'); this.travel(ast.identifier); this.travel(ast.value); this.append('}'); -} -Sql.prototype.travelBitExpression = function (ast) { +}; +Sql.prototype.travelBitExpression = function(ast) { this.travel(ast.left); this.appendKeyword(ast.operator); this.travel(ast.right); -} -Sql.prototype.travelInSubQueryPredicate = function (ast) { +}; +Sql.prototype.travelInSubQueryPredicate = function(ast) { this.travel(ast.left); if (ast.hasNot) { this.appendKeyword('not'); @@ -264,8 +265,8 @@ Sql.prototype.travelInSubQueryPredicate = function (ast) { this.append('(', false, true); this.travel(ast.right); this.append(')'); -} -Sql.prototype.travelInExpressionListPredicate = function (ast) { +}; +Sql.prototype.travelInExpressionListPredicate = function(ast) { this.travel(ast.left); if (ast.hasNot) { this.appendKeyword('not'); @@ -274,8 +275,8 @@ Sql.prototype.travelInExpressionListPredicate = function (ast) { this.append('(', false, true); this.travel(ast.right); this.append(')'); -} -Sql.prototype.travelBetweenPredicate = function (ast) { +}; +Sql.prototype.travelBetweenPredicate = function(ast) { this.travel(ast.left); if (ast.hasNot) { this.appendKeyword('not'); @@ -284,14 +285,14 @@ Sql.prototype.travelBetweenPredicate = function (ast) { this.travel(ast.right.left); this.appendKeyword('and'); this.travel(ast.right.right); -} -Sql.prototype.travelSoundsLikePredicate = function (ast) { +}; +Sql.prototype.travelSoundsLikePredicate = function(ast) { this.travel(ast.left); this.appendKeyword('sounds'); this.appendKeyword('like'); this.travel(ast.right); -} -Sql.prototype.travelLikePredicate = function (ast) { +}; +Sql.prototype.travelLikePredicate = function(ast) { this.travel(ast.left); if (ast.hasNot) { this.appendKeyword('not'); @@ -299,40 +300,40 @@ Sql.prototype.travelLikePredicate = function (ast) { this.appendKeyword('like'); this.travel(ast.right); if (ast.escape) { - this.appendKeyword('escape') + this.appendKeyword('escape'); this.travel(ast.escape); } -} -Sql.prototype.travelRegexpPredicate = function (ast) { +}; +Sql.prototype.travelRegexpPredicate = function(ast) { this.travel(ast.left); if (ast.hasNot) { this.appendKeyword('not'); } this.appendKeyword('regexp'); this.travel(ast.right); -} -Sql.prototype.travelIsNullBooleanPrimary = function (ast) { +}; +Sql.prototype.travelIsNullBooleanPrimary = function(ast) { this.travel(ast.value); this.appendKeyword('is'); if (ast.hasNot) { this.appendKeyword('not'); } this.appendKeyword('null'); -} -Sql.prototype.travelComparisonBooleanPrimary = function (ast) { +}; +Sql.prototype.travelComparisonBooleanPrimary = function(ast) { this.travel(ast.left); this.append(ast.operator); this.travel(ast.right); -} -Sql.prototype.travelComparisonSubQueryBooleanPrimary = function (ast) { +}; +Sql.prototype.travelComparisonSubQueryBooleanPrimary = function(ast) { this.travel(ast.left); this.append(ast.operator); this.appendKeyword(ast.subQueryOpt); this.append('(', false, true); this.travel(ast.right); this.append(')'); -} -Sql.prototype.travelExpressionList = function (ast) { +}; +Sql.prototype.travelExpressionList = function(ast) { var list = ast.value; for (var i = 0; i < list.length; i++) { this.travel(list[i]); @@ -340,8 +341,8 @@ Sql.prototype.travelExpressionList = function (ast) { this.append(',', true); } } -} -Sql.prototype.travelGroupBy = function (ast) { +}; +Sql.prototype.travelGroupBy = function(ast) { this.appendKeyword('group by'); var list = ast.value; for (var i = 0; i < list.length; i++) { @@ -350,8 +351,8 @@ Sql.prototype.travelGroupBy = function (ast) { this.append(',', true); } } -} -Sql.prototype.travelOrderBy = function (ast) { +}; +Sql.prototype.travelOrderBy = function(ast) { this.appendKeyword('order by'); var list = ast.value; for (var i = 0; i < list.length; i++) { @@ -363,14 +364,14 @@ Sql.prototype.travelOrderBy = function (ast) { if (ast.rollUp) { this.appendKeyword('with rollup'); } -} -Sql.prototype.travelGroupByOrderByItem = function (ast) { +}; +Sql.prototype.travelGroupByOrderByItem = function(ast) { this.travel(ast.value); if (ast.sortOpt) { this.appendKeyword(ast.sortOpt); } -} -Sql.prototype.travelLimit = function (ast) { +}; +Sql.prototype.travelLimit = function(ast) { this.appendKeyword('limit'); var list = ast.value; if (list.length === 1) { @@ -386,8 +387,8 @@ Sql.prototype.travelLimit = function (ast) { this.append(list[1]); } } -} -Sql.prototype.travelTableReferences = function (ast) { +}; +Sql.prototype.travelTableReferences = function(ast) { var list = ast.value; if (ast.TableReferences) { this.append('(', false, true); @@ -401,8 +402,8 @@ Sql.prototype.travelTableReferences = function (ast) { if (ast.TableReferences) { this.append(')'); } -} -Sql.prototype.travelTableReference = function (ast) { +}; +Sql.prototype.travelTableReference = function(ast) { if (ast.hasOj) { this.append('{'); this.appendKeyword('oj'); @@ -411,8 +412,8 @@ Sql.prototype.travelTableReference = function (ast) { } else { this.travel(ast.value); } -} -Sql.prototype.travelInnerCrossJoinTable = function (ast) { +}; +Sql.prototype.travelInnerCrossJoinTable = function(ast) { this.travel(ast.left); if (ast.innerCrossOpt) { this.appendKeyword(ast.innerCrossOpt); @@ -422,14 +423,14 @@ Sql.prototype.travelInnerCrossJoinTable = function (ast) { if (ast.condition) { this.travel(ast.condition); } -} -Sql.prototype.travelStraightJoinTable = function (ast) { +}; +Sql.prototype.travelStraightJoinTable = function(ast) { this.travel(ast.left); this.appendKeyword('straight_join'); this.travel(ast.right); this.travel(ast.condition); -} -Sql.prototype.travelLeftRightJoinTable = function (ast) { +}; +Sql.prototype.travelLeftRightJoinTable = function(ast) { this.travel(ast.left); this.appendKeyword(ast.leftRight); if (ast.outOpt) { @@ -438,8 +439,8 @@ Sql.prototype.travelLeftRightJoinTable = function (ast) { this.appendKeyword('join'); this.travel(ast.right); this.travel(ast.condition); -} -Sql.prototype.travelNaturalJoinTable = function (ast) { +}; +Sql.prototype.travelNaturalJoinTable = function(ast) { this.travel(ast.left); this.appendKeyword('natural'); if (ast.leftRight) { @@ -450,18 +451,18 @@ Sql.prototype.travelNaturalJoinTable = function (ast) { } this.appendKeyword('join'); this.travel(ast.right); -} -Sql.prototype.travelOnJoinCondition = function (ast) { +}; +Sql.prototype.travelOnJoinCondition = function(ast) { this.appendKeyword('on'); this.travel(ast.value); -} -Sql.prototype.travelUsingJoinCondition = function (ast) { +}; +Sql.prototype.travelUsingJoinCondition = function(ast) { this.appendKeyword('using'); this.appendKeyword('(', false, true); this.travel(ast.value); this.appendKeyword(')'); -} -Sql.prototype.travelPartitions = function (ast) { +}; +Sql.prototype.travelPartitions = function(ast) { this.appendKeyword('partition'); this.appendKeyword('(', false, true); var list = ast.value; @@ -472,12 +473,12 @@ Sql.prototype.travelPartitions = function (ast) { } } this.appendKeyword(')'); -} -Sql.prototype.travelForOptIndexHint = function (ast) { +}; +Sql.prototype.travelForOptIndexHint = function(ast) { this.appendKeyword('for'); this.appendKeyword(ast.value); -} -Sql.prototype.travelIndexList = function (ast) { +}; +Sql.prototype.travelIndexList = function(ast) { var list = ast.value; for (var i = 0; i < list.length; i++) { this.travel(list[i]); @@ -485,8 +486,8 @@ Sql.prototype.travelIndexList = function (ast) { this.append(',', true); } } -} -Sql.prototype.travelUseIndexHint = function (ast) { +}; +Sql.prototype.travelUseIndexHint = function(ast) { this.appendKeyword('use'); this.appendKeyword(ast.indexOrKey); if (ast.forOpt) { @@ -497,8 +498,8 @@ Sql.prototype.travelUseIndexHint = function (ast) { this.travel(ast.value); } this.appendKeyword(')'); -} -Sql.prototype.travelIgnoreIndexHint = function (ast) { +}; +Sql.prototype.travelIgnoreIndexHint = function(ast) { this.appendKeyword('ignore'); this.appendKeyword(ast.indexOrKey); if (ast.forOpt) { @@ -509,8 +510,8 @@ Sql.prototype.travelIgnoreIndexHint = function (ast) { this.travel(ast.value); } this.appendKeyword(')'); -} -Sql.prototype.travelForceIndexHint = function (ast) { +}; +Sql.prototype.travelForceIndexHint = function(ast) { this.appendKeyword('force'); this.appendKeyword(ast.indexOrKey); if (ast.forOpt) { @@ -521,8 +522,8 @@ Sql.prototype.travelForceIndexHint = function (ast) { this.travel(ast.value); } this.appendKeyword(')'); -} -Sql.prototype.travelTableFactor = function (ast) { +}; +Sql.prototype.travelTableFactor = function(ast) { this.travel(ast.value); if (ast.partition) { this.travel(ast.partition); @@ -536,17 +537,17 @@ Sql.prototype.travelTableFactor = function (ast) { if (ast.indexHintOpt) { this.travel(ast.indexHintOpt); } -} -Sql.prototype.travelUnion = function (ast) { +}; +Sql.prototype.travelUnion = function(ast) { this.travel(ast.left); this.appendKeyword('UNION'); if (ast.distinctOpt) { - this.appendKeyword(ast.distinctOpt) + this.appendKeyword(ast.distinctOpt); } this.travel(ast.right); -} -Sql.prototype.travelSelectParenthesized = function (ast) { +}; +Sql.prototype.travelSelectParenthesized = function(ast) { this.appendKeyword('('); this.travel(ast.value); this.appendKeyword(')'); -} +}; diff --git a/test/main.test.js b/test/main.test.js index 8908c22..988c1cc 100644 --- a/test/main.test.js +++ b/test/main.test.js @@ -3,7 +3,7 @@ const debug = require('debug')('js-sql-parser'); const parser = require('../'); -const testParser = function (sql) { +const testParser = function(sql) { let firstAst = parser.parse(sql); debug(JSON.stringify(firstAst, null, 2)); let firstSql = parser.stringify(firstAst); @@ -16,30 +16,30 @@ const testParser = function (sql) { if (firstSql !== secondSql) { console.log('firstSql', firstSql); console.log('secondSql', secondSql); - throw 'err firstSql don\'t equals secondSql. '; + throw "err firstSql don't equals secondSql. "; } return secondAst; -} +}; -describe('select grammar support', function () { - it('test0', function () { +describe('select grammar support', function() { + it('test0', function() { testParser('select a from b where c > 1 group by d order by e desc;'); }); - it('test1', function () { + it('test1', function() { testParser('select distinct max_statement_time = 1.2 a '); }); - it('test2', function () { + it('test2', function() { testParser('select all 0x1f'); }); - it('test3', function () { + it('test3', function() { testParser('select distinctrow "xx", a in (1,2)'); }); - it('test4', function () { + it('test4', function() { testParser(` select tag_basic.gender as gender, @@ -58,17 +58,17 @@ describe('select grammar support', function () { `); }); - it ('test5', function () { + it('test5', function() { testParser('select function(), function(1, "sd", 0x1F)'); }); - it ('test6 unicode', function () { + it('test6 unicode', function() { testParser(` select in中文 from tags `); }); - it ('test7', function () { + it('test7', function() { testParser(` SELECT DISTINCT high_priority MAX_STATEMENT_TIME=1 STRAIGHT_JOIN SQL_SMALL_RESULT SQL_BIG_RESULT SQL_BUFFER_RESULT SQL_CACHE SQL_CALC_FOUND_ROWS fm_customer.lname AS name1, @@ -89,7 +89,7 @@ describe('select grammar support', function () { `); }); - it ('test8', function () { + it('test8', function() { testParser(` SELECT P1.PAYMENTNO, P1.AMOUNT, (P1.AMOUNT * 100) / SUM(P2.AMOUNT) @@ -99,7 +99,7 @@ describe('select grammar support', function () { `); }); - it ('test9', function () { + it('test9', function() { testParser(` SELECT PLAYERS.PLAYERNO, NAME, (SELECT COUNT(*) @@ -114,7 +114,7 @@ describe('select grammar support', function () { `); }); - it ('test10', function () { + it('test10', function() { testParser(` SELECT rd.*, rd.rd_numberofrooms - ( SELECT SUM(rn.reservation_numberofrooms) AS count_reserve_room @@ -148,11 +148,11 @@ describe('select grammar support', function () { `); }); - it ('test11 SELECT `LEFT`(a, 3) FROM b support.', function () { + it('test11 SELECT `LEFT`(a, 3) FROM b support.', function() { testParser('SELECT `LEFT`(a, 3) FROM b'); }); - it ('test12', function () { + it('test12', function() { testParser(` select a.product_id, @@ -202,7 +202,7 @@ describe('select grammar support', function () { `); }); - it ('test13', function () { + it('test13', function() { testParser(` SELECT a.*, f.ORG_NAME DEPT_NAME, @@ -286,7 +286,7 @@ describe('select grammar support', function () { `); }); - it ('test14', function () { + it('test14', function() { testParser(` SELECT k.*, @@ -345,37 +345,46 @@ describe('select grammar support', function () { `); }); - it ('limit support.', function () { + it('test15', function() { + testParser(` + SELECT P1.PAYMENTNO, P1.AMOUNT, (P1.AMOUNT * 100) / SUM(P2.AMOUNT) + FROM PENALTIES AS P1, PENALTIES AS P2 + GROUP BY P1.PAYMENTNO, P1.AMOUNT + HAVING MAX(P2.AMOUNT) > 0 + `); + }); + + it('limit support.', function() { testParser('select a from b limit 2, 3'); }); - it ('fix not equal.', function () { + it('fix not equal.', function() { testParser('select a from b where a <> 1 limit 2, 3'); }); - it ('restore semicolon.', function () { + it('restore semicolon.', function() { testParser('select a from b limit 2;'); }); - it ('recognoce alias for sql-function calls in stringify function.', function () { + it('recognoce alias for sql-function calls in stringify function.', function() { testParser('SELECT COUNT(*) AS total, a b, b as c, c/2 d, d & e an FROM b'); }); - it ('union support, https://dev.mysql.com/doc/refman/8.0/en/union.html', function () { + it('union support, https://dev.mysql.com/doc/refman/8.0/en/union.html', function() { testParser('select a from dual union select a from foo;'); }); - it ('union Parenthesized support, https://dev.mysql.com/doc/refman/8.0/en/union.html', function () { + it('union Parenthesized support, https://dev.mysql.com/doc/refman/8.0/en/union.html', function() { testParser('(select a from dual) union (select a from foo) order by a desc limit 100, 100;'); }); - it ('union all support, https://dev.mysql.com/doc/refman/8.0/en/union.html', function () { + it('union all support, https://dev.mysql.com/doc/refman/8.0/en/union.html', function() { testParser('(select a from dual) union all (select a from foo) order by a limit 100'); }); - it ('union distinct support, https://dev.mysql.com/doc/refman/8.0/en/union.html', function () { - testParser('select a from dual order by a desc limit 1, 1 union distinct select a from foo order by a limit 1'); + it('union distinct support, https://dev.mysql.com/doc/refman/8.0/en/union.html', function() { + testParser( + 'select a from dual order by a desc limit 1, 1 union distinct select a from foo order by a limit 1' + ); }); - }); - From f5ae2eca5a7eb4dc288b205b92b20ed9c632dfab Mon Sep 17 00:00:00 2001 From: Albin Zeng Date: Tue, 22 Oct 2019 09:55:33 +0800 Subject: [PATCH 10/37] release: 1.2.1 & update CHANGELOG. --- CHANGELOG | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 9165fdc..76f97c3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ 1.0.6 add MIT license. 1.0.7 fix alias for identifier #16 , pr: #17 1.0.8 support union grammar follow: https://dev.mysql.com/doc/refman/8.0/en/union.html +1.0.9 fix stringify having #29 , pr: #28 1.1.0 hotfix: Remove unused import +1.1.1 fix stringify having #29 , pr: #28 1.2.0 fix typo "refrence" to "reference" #24 +1.2.1 fix stringify having #29 , pr: #28 diff --git a/package.json b/package.json index 728c4f8..292c2d3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "js-sql-parser", - "version": "1.2.0", + "version": "1.2.1", "description": "", "main": "./dist/parser/sqlParser.js", "scripts": { From 047b8701d689c611396fe54d2c84c43501ea7243 Mon Sep 17 00:00:00 2001 From: Albin Zeng Date: Tue, 22 Oct 2019 10:06:46 +0800 Subject: [PATCH 11/37] chore: update news tab. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e7297dc..7d645b2 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ sql grammar follows https://dev.mysql.com/doc/refman/5.7/en/select.html ## news - Typo 'refrence' has been fixed to 'reference' since v1.2.0. +- Bug stringify keyword `having` has been fixed since v1.2.1. for more changes see [CHANGELOG](./CHANGELOG) From 2960e33c1d2a63a2b99895cdde75bcd3acc72f1b Mon Sep 17 00:00:00 2001 From: Albin Zeng Date: Tue, 22 Oct 2019 10:12:59 +0800 Subject: [PATCH 12/37] chore: add Makefile. --- Makefile | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..51dd70d --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +publish: test + @npm publish + +test: + @npm test + +.PHONY: publish test From 141ef504a39cc98ed250ff299606aeb4cb1a5f4a Mon Sep 17 00:00:00 2001 From: Albin Zeng Date: Tue, 22 Oct 2019 14:22:56 +0800 Subject: [PATCH 13/37] chore: readme.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7d645b2..08ce092 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ sql grammar follows https://dev.mysql.com/doc/refman/5.7/en/select.html ## news - Typo 'refrence' has been fixed to 'reference' since v1.2.0. -- Bug stringify keyword `having` has been fixed since v1.2.1. +- Fix bug stringify keyword `having` since v1.2.1. #29 for more changes see [CHANGELOG](./CHANGELOG) From 03743788078dcddcaf6d45e35e7f240f4cc9b620 Mon Sep 17 00:00:00 2001 From: Albin Zeng Date: Tue, 22 Oct 2019 14:24:41 +0800 Subject: [PATCH 14/37] chore: update readme.md. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 08ce092..c18376a 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ sql grammar follows https://dev.mysql.com/doc/refman/5.7/en/select.html ## news - Typo 'refrence' has been fixed to 'reference' since v1.2.0. -- Fix bug stringify keyword `having` since v1.2.1. #29 +- Fix bug stringify keyword `having` since v1.2.1. [#29](https://github.com/JavaScriptor/js-sql-parser/issues/29) for more changes see [CHANGELOG](./CHANGELOG) From d3c48713227e85f97011aec98ff6241e6d0cf33f Mon Sep 17 00:00:00 2001 From: Ernesto Rodriguez Ortiz Date: Thu, 26 Mar 2020 19:42:16 -0400 Subject: [PATCH 15/37] feat: add support for quoted alias --- src/sqlParser.jison | 3 +++ test/main.test.js | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/src/sqlParser.jison b/src/sqlParser.jison index e3cb798..ee5b329 100644 --- a/src/sqlParser.jison +++ b/src/sqlParser.jison @@ -127,6 +127,7 @@ UNION return 'UNION' [a-zA-Z_\u4e00-\u9fa5][a-zA-Z0-9_\u4e00-\u9fa5]* return 'IDENTIFIER' \. return 'DOT' ['"][a-zA-Z_\u4e00-\u9fa5][a-zA-Z0-9_\u4e00-\u9fa5]*["'] return 'QUOTED_IDENTIFIER' +[`].+[`] return 'QUOTED_IDENTIFIER' <> return 'EOF' . return 'INVALID' @@ -279,6 +280,8 @@ selectExprAliasOpt : { $$ = {alias: null, hasAs: null} } | AS IDENTIFIER { $$ = {alias: $2, hasAs: true} } | IDENTIFIER { $$ = {alias: $1, hasAs: false} } + | AS QUOTED_IDENTIFIER { $$ = {alias: $2, hasAs: true} } + | QUOTED_IDENTIFIER { $$ = {alias: $1, hasAs: false} } ; string diff --git a/test/main.test.js b/test/main.test.js index 988c1cc..558d133 100644 --- a/test/main.test.js +++ b/test/main.test.js @@ -387,4 +387,14 @@ describe('select grammar support', function() { 'select a from dual order by a desc limit 1, 1 union distinct select a from foo order by a limit 1' ); }); + + it('support quoted alias', function() { + testParser('select a as `A-A` from b limit 2;'); + testParser('select a as `A#A` from b limit 2;'); + testParser('select a as `A?A` from b limit 2;'); + testParser('select a as `A/B` from b limit 2;'); + testParser('select a as `A.A` from b limit 2;'); + testParser('select a as `A|A` from b limit 2;'); + testParser('select a as `A A` from b limit 2;'); + }); }); From 24381eee5acd06a844a3d2354328acc3e5312e8f Mon Sep 17 00:00:00 2001 From: Albin Zeng Date: Mon, 30 Mar 2020 11:11:24 +0800 Subject: [PATCH 16/37] release: v1.2.2 --- CHANGELOG | 1 + README.md | 1 + package.json | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 76f97c3..185e98b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,3 +6,4 @@ 1.1.1 fix stringify having #29 , pr: #28 1.2.0 fix typo "refrence" to "reference" #24 1.2.1 fix stringify having #29 , pr: #28 +1.2.2 feat: add support for "`" quoted alias, pr: #33 diff --git a/README.md b/README.md index c18376a..24afab7 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ sql grammar follows https://dev.mysql.com/doc/refman/5.7/en/select.html - Typo 'refrence' has been fixed to 'reference' since v1.2.0. - Fix bug stringify keyword `having` since v1.2.1. [#29](https://github.com/JavaScriptor/js-sql-parser/issues/29) +- Add support for "`" quoted alias since v1.2.2. [#33](https://github.com/JavaScriptor/js-sql-parser/issues/33) for more changes see [CHANGELOG](./CHANGELOG) diff --git a/package.json b/package.json index 292c2d3..b44a9e9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "js-sql-parser", - "version": "1.2.1", + "version": "1.2.2", "description": "", "main": "./dist/parser/sqlParser.js", "scripts": { From f8034b8637ce59e86bcbd2337a9275a12904c5e9 Mon Sep 17 00:00:00 2001 From: Albin Zeng Date: Mon, 30 Mar 2020 11:18:37 +0800 Subject: [PATCH 17/37] fix: test case. --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index b44a9e9..796b08c 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,7 @@ "scripts": { "build": "jison -m js ./src/sqlParser.jison -o ./dist/parser/sqlParser.js && npm run build-concat", "build-concat": "minicat src/stringify.js src/suffix.js >> dist/parser/sqlParser.js", - "postbuild": "npm run test:build", - "test": "npm run build", + "test": "npm run test:build", "test:all": "mocha --require babel-register", "test:build": "mocha --require babel-register test/*.test.js", "test:benchmark": "mocha --require babel-register test/benchmark.js" From 0c0a54f0348a16e17500c6323e374b31a07440bb Mon Sep 17 00:00:00 2001 From: Albin Zeng Date: Mon, 30 Mar 2020 11:29:39 +0800 Subject: [PATCH 18/37] fix: test case. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4ba6f27..b72d710 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,5 +12,5 @@ node_js: - "v8.11.3" - "v10.7.0" scripts: { - "test": "npm run test:build" - } + "test": "npm test" +} From 8f4464d9d92aa5dc5334807f0f42fbc0639ebe0e Mon Sep 17 00:00:00 2001 From: Albin Zeng Date: Mon, 30 Mar 2020 11:35:40 +0800 Subject: [PATCH 19/37] fix: test case. --- .travis.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index b72d710..b0cd44d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,4 @@ node_js: - "v6.9.1" - "v8.11.3" - "v10.7.0" -scripts: { - "test": "npm test" -} +script: npm run test:build From 915dc767373feb1a21264db121c5ce0ecab094ee Mon Sep 17 00:00:00 2001 From: Albin Zeng Date: Mon, 30 Mar 2020 11:39:13 +0800 Subject: [PATCH 20/37] fix: test case. --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b0cd44d..a94918b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,4 +11,6 @@ node_js: - "v6.9.1" - "v8.11.3" - "v10.7.0" -script: npm run test:build +scripts: + - npm run build + - npm run test From f674bffb7add53f38be5df6afe1114f855973241 Mon Sep 17 00:00:00 2001 From: Albin Zeng Date: Mon, 30 Mar 2020 11:41:54 +0800 Subject: [PATCH 21/37] fix: test case. --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index a94918b..5d6b173 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,3 @@ node_js: - "v6.9.1" - "v8.11.3" - "v10.7.0" -scripts: - - npm run build - - npm run test From 90031e321631f47c6dc6539aed513a3d108eb427 Mon Sep 17 00:00:00 2001 From: Albin Zeng Date: Mon, 30 Mar 2020 16:39:52 +0800 Subject: [PATCH 22/37] fix: test case. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 796b08c..5dfec0f 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "scripts": { "build": "jison -m js ./src/sqlParser.jison -o ./dist/parser/sqlParser.js && npm run build-concat", "build-concat": "minicat src/stringify.js src/suffix.js >> dist/parser/sqlParser.js", - "test": "npm run test:build", + "test": "npm run build && npm run test:build", "test:all": "mocha --require babel-register", "test:build": "mocha --require babel-register test/*.test.js", "test:benchmark": "mocha --require babel-register test/benchmark.js" From 394e5cc0d6aa255776214459314dc171a5bada0c Mon Sep 17 00:00:00 2001 From: Albin Zeng Date: Thu, 2 Apr 2020 15:08:21 +0800 Subject: [PATCH 23/37] fix: #34 --- src/sqlParser.jison | 2 +- test/main.test.js | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/sqlParser.jison b/src/sqlParser.jison index ee5b329..7cff3e8 100644 --- a/src/sqlParser.jison +++ b/src/sqlParser.jison @@ -585,6 +585,6 @@ index_hint ; table_factor : identifier partitionOpt aliasOpt index_hint_list_opt { $$ = { type: 'TableFactor', value: $1, partition: $2, alias: $3.alias, hasAs: $3.hasAs, indexHintOpt: $4 } } - | '(' selectClause ')' aliasOpt { $$ = { type: 'SubQuery', value: $2, alias: $4.alias, hasAs: $4.hasAs } } + | '(' selectClause ')' aliasOpt { $$ = { type: 'TableFactor', value: { type: 'SubQuery', value: $2 }, alias: $4.alias, hasAs: $4.hasAs} } | '(' table_references ')' { $$ = $2; $$.hasParentheses = true } ; diff --git a/test/main.test.js b/test/main.test.js index 558d133..834d3f1 100644 --- a/test/main.test.js +++ b/test/main.test.js @@ -397,4 +397,18 @@ describe('select grammar support', function() { testParser('select a as `A|A` from b limit 2;'); testParser('select a as `A A` from b limit 2;'); }); + + it('bugfix table alias', function() { + testParser(` + SELECT stime, A.names, B.names FROM ( + SELECT stime, names FROM iaas_data.iaas_d3c0d0681cc1900 + ) AS A LEFT JOIN ( + SELECT stime, names FROM iaas_data.iaas_1071f89feaa0e100 + ) AS B ON A.stime = B.stime + `); + }); + + it('bugfix table alias2', function() { + testParser('select a.* from a t1 join b t2 on t1.a = t2.a') + }) }); From 315faf7b44ac4a5ed0a281840a2787746e133072 Mon Sep 17 00:00:00 2001 From: Albin Zeng Date: Thu, 2 Apr 2020 15:12:23 +0800 Subject: [PATCH 24/37] release: v1.3.0 --- CHANGELOG | 1 + README.md | 5 +++-- package.json | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 185e98b..3d1a64c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -7,3 +7,4 @@ 1.2.0 fix typo "refrence" to "reference" #24 1.2.1 fix stringify having #29 , pr: #28 1.2.2 feat: add support for "`" quoted alias, pr: #33 +1.3.0 fix tableFactor alias bug. AST changed in tableFactor. #34 diff --git a/README.md b/README.md index 24afab7..467f6e6 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,10 @@ sql grammar follows https://dev.mysql.com/doc/refman/5.7/en/select.html ## news -- Typo 'refrence' has been fixed to 'reference' since v1.2.0. -- Fix bug stringify keyword `having` since v1.2.1. [#29](https://github.com/JavaScriptor/js-sql-parser/issues/29) +- Fix bug tableFactor alias since v1.3.0 [#34](https://github.com/JavaScriptor/js-sql-parser/issues/34) - Add support for "`" quoted alias since v1.2.2. [#33](https://github.com/JavaScriptor/js-sql-parser/issues/33) +- Fix bug stringify keyword `having` since v1.2.1. [#29](https://github.com/JavaScriptor/js-sql-parser/issues/29) +- Typo 'refrence' has been fixed to 'reference' since v1.2.0. for more changes see [CHANGELOG](./CHANGELOG) diff --git a/package.json b/package.json index 5dfec0f..3b620bc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "js-sql-parser", - "version": "1.2.2", + "version": "1.3.0", "description": "", "main": "./dist/parser/sqlParser.js", "scripts": { From 42efe32c247b5874ae8d66b0d99385c0e1f63168 Mon Sep 17 00:00:00 2001 From: Nicola Capovilla Date: Wed, 6 May 2020 10:27:54 +0200 Subject: [PATCH 25/37] fix: fix support for quoted alias --- src/sqlParser.jison | 14 +++++++++++--- test/main.test.js | 10 ++++++++-- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/sqlParser.jison b/src/sqlParser.jison index 7cff3e8..53444e0 100644 --- a/src/sqlParser.jison +++ b/src/sqlParser.jison @@ -127,7 +127,7 @@ UNION return 'UNION' [a-zA-Z_\u4e00-\u9fa5][a-zA-Z0-9_\u4e00-\u9fa5]* return 'IDENTIFIER' \. return 'DOT' ['"][a-zA-Z_\u4e00-\u9fa5][a-zA-Z0-9_\u4e00-\u9fa5]*["'] return 'QUOTED_IDENTIFIER' -[`].+[`] return 'QUOTED_IDENTIFIER' +([`])(?:(?=(\\?))\2.)*?\1 return 'QUOTED_IDENTIFIER' <> return 'EOF' . return 'INVALID' @@ -285,8 +285,7 @@ selectExprAliasOpt ; string - : QUOTED_IDENTIFIER { $$ = { type: 'String', value: $1 } } - | STRING { $$ = { type: 'String', value: $1 } } + : STRING { $$ = { type: 'String', value: $1 } } ; number : NUMERIC { $$ = { type: 'Number', value: $1 } } @@ -328,6 +327,14 @@ identifier_list : identifier { $$ = { type: 'IdentifierList', value: [ $1 ] } } | identifier_list ',' identifier { $$ = $1; $1.value.push($3); } ; +quoted_identifier + : QUOTED_IDENTIFIER { $$ = { type: 'Identifier', value: $1 } } + | quoted_identifier DOT QUOTED_IDENTIFIER { $$ = $1; $1.value += '.' + $3 } + ; +quoted_identifier_list + : quoted_identifier { $$ = { type: 'IdentifierList', value: [ $1 ] } } + | quoted_identifier_list ',' quoted_identifier { $$ = $1; $1.value.push($3); } + ; case_expr_opt : { $$ = null } | expr { $$ = $1 } @@ -353,6 +360,7 @@ simple_expr_prefix simple_expr : literal { $$ = $1 } | identifier { $$ = $1 } + | quoted_identifier { $$ = $1 } | function_call { $$ = $1 } | simple_expr_prefix { $$ = $1 } | '(' expr_list ')' { $$ = { type: 'SimpleExprParentheses', value: $2 } } diff --git a/test/main.test.js b/test/main.test.js index 834d3f1..56330d7 100644 --- a/test/main.test.js +++ b/test/main.test.js @@ -408,7 +408,13 @@ describe('select grammar support', function() { `); }); - it('bugfix table alias2', function() { + it('bugfix table alias2', function () { testParser('select a.* from a t1 join b t2 on t1.a = t2.a') - }) + }); + + it('support quoted alias: multiple alias and orderby support', function () { + testParser('select a as `A A`, b as `B B` from z'); + testParser('select a as `A A` from z order by `A A` desc'); + testParser('select a as `A A`, b as `B B` from z group by `A A`, `B B` order by `A A` desc'); + }); }); From b32a6ccc419f4da27eaf6397f61c1f871462fc45 Mon Sep 17 00:00:00 2001 From: Nicola Capovilla Date: Thu, 7 May 2020 17:30:57 +0200 Subject: [PATCH 26/37] support for double quote alias --- src/sqlParser.jison | 3 ++- test/main.test.js | 61 ++++++++++++++++++++++++--------------------- 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/src/sqlParser.jison b/src/sqlParser.jison index 53444e0..1c2c253 100644 --- a/src/sqlParser.jison +++ b/src/sqlParser.jison @@ -282,8 +282,9 @@ selectExprAliasOpt | IDENTIFIER { $$ = {alias: $1, hasAs: false} } | AS QUOTED_IDENTIFIER { $$ = {alias: $2, hasAs: true} } | QUOTED_IDENTIFIER { $$ = {alias: $1, hasAs: false} } + | AS STRING { $$ = {alias: $2, hasAs: true} } + | STRING { $$ = {alias: $2, hasAs: false} } ; - string : STRING { $$ = { type: 'String', value: $1 } } ; diff --git a/test/main.test.js b/test/main.test.js index 56330d7..8ba2249 100644 --- a/test/main.test.js +++ b/test/main.test.js @@ -3,7 +3,7 @@ const debug = require('debug')('js-sql-parser'); const parser = require('../'); -const testParser = function(sql) { +const testParser = function (sql) { let firstAst = parser.parse(sql); debug(JSON.stringify(firstAst, null, 2)); let firstSql = parser.stringify(firstAst); @@ -22,24 +22,24 @@ const testParser = function(sql) { return secondAst; }; -describe('select grammar support', function() { - it('test0', function() { +describe('select grammar support', function () { + it('test0', function () { testParser('select a from b where c > 1 group by d order by e desc;'); }); - it('test1', function() { + it('test1', function () { testParser('select distinct max_statement_time = 1.2 a '); }); - it('test2', function() { + it('test2', function () { testParser('select all 0x1f'); }); - it('test3', function() { + it('test3', function () { testParser('select distinctrow "xx", a in (1,2)'); }); - it('test4', function() { + it('test4', function () { testParser(` select tag_basic.gender as gender, @@ -58,17 +58,17 @@ describe('select grammar support', function() { `); }); - it('test5', function() { + it('test5', function () { testParser('select function(), function(1, "sd", 0x1F)'); }); - it('test6 unicode', function() { + it('test6 unicode', function () { testParser(` select in中文 from tags `); }); - it('test7', function() { + it('test7', function () { testParser(` SELECT DISTINCT high_priority MAX_STATEMENT_TIME=1 STRAIGHT_JOIN SQL_SMALL_RESULT SQL_BIG_RESULT SQL_BUFFER_RESULT SQL_CACHE SQL_CALC_FOUND_ROWS fm_customer.lname AS name1, @@ -89,7 +89,7 @@ describe('select grammar support', function() { `); }); - it('test8', function() { + it('test8', function () { testParser(` SELECT P1.PAYMENTNO, P1.AMOUNT, (P1.AMOUNT * 100) / SUM(P2.AMOUNT) @@ -99,7 +99,7 @@ describe('select grammar support', function() { `); }); - it('test9', function() { + it('test9', function () { testParser(` SELECT PLAYERS.PLAYERNO, NAME, (SELECT COUNT(*) @@ -114,7 +114,7 @@ describe('select grammar support', function() { `); }); - it('test10', function() { + it('test10', function () { testParser(` SELECT rd.*, rd.rd_numberofrooms - ( SELECT SUM(rn.reservation_numberofrooms) AS count_reserve_room @@ -148,11 +148,11 @@ describe('select grammar support', function() { `); }); - it('test11 SELECT `LEFT`(a, 3) FROM b support.', function() { + it('test11 SELECT `LEFT`(a, 3) FROM b support.', function () { testParser('SELECT `LEFT`(a, 3) FROM b'); }); - it('test12', function() { + it('test12', function () { testParser(` select a.product_id, @@ -202,7 +202,7 @@ describe('select grammar support', function() { `); }); - it('test13', function() { + it('test13', function () { testParser(` SELECT a.*, f.ORG_NAME DEPT_NAME, @@ -286,7 +286,7 @@ describe('select grammar support', function() { `); }); - it('test14', function() { + it('test14', function () { testParser(` SELECT k.*, @@ -345,7 +345,7 @@ describe('select grammar support', function() { `); }); - it('test15', function() { + it('test15', function () { testParser(` SELECT P1.PAYMENTNO, P1.AMOUNT, (P1.AMOUNT * 100) / SUM(P2.AMOUNT) FROM PENALTIES AS P1, PENALTIES AS P2 @@ -354,41 +354,41 @@ describe('select grammar support', function() { `); }); - it('limit support.', function() { + it('limit support.', function () { testParser('select a from b limit 2, 3'); }); - it('fix not equal.', function() { + it('fix not equal.', function () { testParser('select a from b where a <> 1 limit 2, 3'); }); - it('restore semicolon.', function() { + it('restore semicolon.', function () { testParser('select a from b limit 2;'); }); - it('recognoce alias for sql-function calls in stringify function.', function() { + it('recognoce alias for sql-function calls in stringify function.', function () { testParser('SELECT COUNT(*) AS total, a b, b as c, c/2 d, d & e an FROM b'); }); - it('union support, https://dev.mysql.com/doc/refman/8.0/en/union.html', function() { + it('union support, https://dev.mysql.com/doc/refman/8.0/en/union.html', function () { testParser('select a from dual union select a from foo;'); }); - it('union Parenthesized support, https://dev.mysql.com/doc/refman/8.0/en/union.html', function() { + it('union Parenthesized support, https://dev.mysql.com/doc/refman/8.0/en/union.html', function () { testParser('(select a from dual) union (select a from foo) order by a desc limit 100, 100;'); }); - it('union all support, https://dev.mysql.com/doc/refman/8.0/en/union.html', function() { + it('union all support, https://dev.mysql.com/doc/refman/8.0/en/union.html', function () { testParser('(select a from dual) union all (select a from foo) order by a limit 100'); }); - it('union distinct support, https://dev.mysql.com/doc/refman/8.0/en/union.html', function() { + it('union distinct support, https://dev.mysql.com/doc/refman/8.0/en/union.html', function () { testParser( 'select a from dual order by a desc limit 1, 1 union distinct select a from foo order by a limit 1' ); }); - it('support quoted alias', function() { + it('support quoted alias', function () { testParser('select a as `A-A` from b limit 2;'); testParser('select a as `A#A` from b limit 2;'); testParser('select a as `A?A` from b limit 2;'); @@ -398,7 +398,7 @@ describe('select grammar support', function() { testParser('select a as `A A` from b limit 2;'); }); - it('bugfix table alias', function() { + it('bugfix table alias', function () { testParser(` SELECT stime, A.names, B.names FROM ( SELECT stime, names FROM iaas_data.iaas_d3c0d0681cc1900 @@ -417,4 +417,9 @@ describe('select grammar support', function() { testParser('select a as `A A` from z order by `A A` desc'); testParser('select a as `A A`, b as `B B` from z group by `A A`, `B B` order by `A A` desc'); }); + + it('support double quoted alias', function () { + testParser('select a as "A A" from z'); + testParser('select a as "A A" from z order by "A A" desc'); + }); }); From cf6db04054810bb4d70303e78c2721fff2b75442 Mon Sep 17 00:00:00 2001 From: Nicola Capovilla Date: Tue, 12 May 2020 22:14:33 +0200 Subject: [PATCH 27/37] Merge quoted_identifier in identifier --- src/sqlParser.jison | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/sqlParser.jison b/src/sqlParser.jison index 1c2c253..3e0f452 100644 --- a/src/sqlParser.jison +++ b/src/sqlParser.jison @@ -126,8 +126,8 @@ UNION return 'UNION' [a-zA-Z_\u4e00-\u9fa5][a-zA-Z0-9_\u4e00-\u9fa5]* return 'IDENTIFIER' \. return 'DOT' -['"][a-zA-Z_\u4e00-\u9fa5][a-zA-Z0-9_\u4e00-\u9fa5]*["'] return 'QUOTED_IDENTIFIER' -([`])(?:(?=(\\?))\2.)*?\1 return 'QUOTED_IDENTIFIER' +['"][a-zA-Z_\u4e00-\u9fa5][a-zA-Z0-9_\u4e00-\u9fa5]*["'] return 'IDENTIFIER' +([`])(?:(?=(\\?))\2.)*?\1 return 'IDENTIFIER' <> return 'EOF' . return 'INVALID' @@ -280,8 +280,6 @@ selectExprAliasOpt : { $$ = {alias: null, hasAs: null} } | AS IDENTIFIER { $$ = {alias: $2, hasAs: true} } | IDENTIFIER { $$ = {alias: $1, hasAs: false} } - | AS QUOTED_IDENTIFIER { $$ = {alias: $2, hasAs: true} } - | QUOTED_IDENTIFIER { $$ = {alias: $1, hasAs: false} } | AS STRING { $$ = {alias: $2, hasAs: true} } | STRING { $$ = {alias: $2, hasAs: false} } ; @@ -328,14 +326,6 @@ identifier_list : identifier { $$ = { type: 'IdentifierList', value: [ $1 ] } } | identifier_list ',' identifier { $$ = $1; $1.value.push($3); } ; -quoted_identifier - : QUOTED_IDENTIFIER { $$ = { type: 'Identifier', value: $1 } } - | quoted_identifier DOT QUOTED_IDENTIFIER { $$ = $1; $1.value += '.' + $3 } - ; -quoted_identifier_list - : quoted_identifier { $$ = { type: 'IdentifierList', value: [ $1 ] } } - | quoted_identifier_list ',' quoted_identifier { $$ = $1; $1.value.push($3); } - ; case_expr_opt : { $$ = null } | expr { $$ = $1 } @@ -361,7 +351,6 @@ simple_expr_prefix simple_expr : literal { $$ = $1 } | identifier { $$ = $1 } - | quoted_identifier { $$ = $1 } | function_call { $$ = $1 } | simple_expr_prefix { $$ = $1 } | '(' expr_list ')' { $$ = { type: 'SimpleExprParentheses', value: $2 } } From 6503d59caecc30c34983054d6780311d5cd32cd2 Mon Sep 17 00:00:00 2001 From: sockstack <85564748@qq.com> Date: Mon, 31 Aug 2020 19:58:52 +0800 Subject: [PATCH 28/37] place holder support ${value} like value place holder support. --- src/sqlParser.jison | 5 +++++ src/stringify.js | 17 +++++++++++++++++ test/main.test.js | 6 ++++++ 3 files changed, 28 insertions(+) diff --git a/src/sqlParser.jison b/src/sqlParser.jison index 7cff3e8..110f77f 100644 --- a/src/sqlParser.jison +++ b/src/sqlParser.jison @@ -11,6 +11,7 @@ [#]\s.*\n /* skip sql comments */ \s+ /* skip whitespace */ +[$][{](.*?)[}] return 'PLACE_HOLDER' [`][a-zA-Z_\u4e00-\u9fa5][a-zA-Z0-9_\u4e00-\u9fa5]*[`] return 'IDENTIFIER' [\w]+[\u4e00-\u9fa5]+[0-9a-zA-Z_\u4e00-\u9fa5]* return 'IDENTIFIER' [\u4e00-\u9fa5][0-9a-zA-Z_\u4e00-\u9fa5]* return 'IDENTIFIER' @@ -305,6 +306,7 @@ literal | number { $$ = $1 } | boolean { $$ = $1 } | null { $$ = $1 } + | place_holder { $$ = $1 } ; function_call : IDENTIFIER '(' function_call_param_list ')' { $$ = {type: 'FunctionCall', name: $1, params: $3} } @@ -588,3 +590,6 @@ table_factor | '(' selectClause ')' aliasOpt { $$ = { type: 'TableFactor', value: { type: 'SubQuery', value: $2 }, alias: $4.alias, hasAs: $4.hasAs} } | '(' table_references ')' { $$ = $2; $$.hasParentheses = true } ; +place_holder + : PLACE_HOLDER { $$ = { type: 'PlaceHolder', value: $1, param: $1.slice(2, -1)} } + ; \ No newline at end of file diff --git a/src/stringify.js b/src/stringify.js index 57ea74e..635e801 100644 --- a/src/stringify.js +++ b/src/stringify.js @@ -551,3 +551,20 @@ Sql.prototype.travelSelectParenthesized = function(ast) { this.travel(ast.value); this.appendKeyword(')'); }; +Sql.prototype.travelPlaceHolder = function (ast) { + if (ast.left) { + this.travel(ast.left); + } + + if (ast.operator) { + this.append(ast.operator); + } + + if (ast.right) { + this.append(ast.right); + } + + if (ast.value) { + this.travel(ast.value); + } +}; \ No newline at end of file diff --git a/test/main.test.js b/test/main.test.js index 834d3f1..3d46b3d 100644 --- a/test/main.test.js +++ b/test/main.test.js @@ -411,4 +411,10 @@ describe('select grammar support', function() { it('bugfix table alias2', function() { testParser('select a.* from a t1 join b t2 on t1.a = t2.a') }) + + it('place holder support', function() { + testParser( + "select sum(quota_value) value, busi_col2 as sh, ${a} as a, YEAR(now()) from der_quota_summary where table_ename = 'gshmyyszje_derivedidx' and cd = (select id from t1 where a = ${t1})" + ) + }) }); From 2361c5723d9532703dee333883f82719c67b4bb2 Mon Sep 17 00:00:00 2001 From: Albin Zeng Date: Thu, 4 Feb 2021 16:26:10 +0800 Subject: [PATCH 29/37] fix: #40 --- src/sqlParser.jison | 5 +++-- test/main.test.js | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/sqlParser.jison b/src/sqlParser.jison index 3e0f452..cc99391 100644 --- a/src/sqlParser.jison +++ b/src/sqlParser.jison @@ -126,8 +126,9 @@ UNION return 'UNION' [a-zA-Z_\u4e00-\u9fa5][a-zA-Z0-9_\u4e00-\u9fa5]* return 'IDENTIFIER' \. return 'DOT' -['"][a-zA-Z_\u4e00-\u9fa5][a-zA-Z0-9_\u4e00-\u9fa5]*["'] return 'IDENTIFIER' -([`])(?:(?=(\\?))\2.)*?\1 return 'IDENTIFIER' +["][a-zA-Z_\u4e00-\u9fa5][a-zA-Z0-9_\u4e00-\u9fa5]*["] return 'STRING' +['][a-zA-Z_\u4e00-\u9fa5][a-zA-Z0-9_\u4e00-\u9fa5]*['] return 'STRING' +[`].+[`] return 'IDENTIFIER' <> return 'EOF' . return 'INVALID' diff --git a/test/main.test.js b/test/main.test.js index 8ba2249..88b9cda 100644 --- a/test/main.test.js +++ b/test/main.test.js @@ -422,4 +422,13 @@ describe('select grammar support', function () { testParser('select a as "A A" from z'); testParser('select a as "A A" from z order by "A A" desc'); }); + + it('support quoted alias', function () { + testParser('select a as \'A A\' from z'); + testParser('select a as \'"A#A\' from z order by \'"A#A\' desc'); + }); + + it.only('test IDENTIFIER', function () { + testParser('select `aa#sfs`(a) as \'A A\' from z'); + }); }); From b5450ee84ae0926693a50b5ed64f69e1fb9b6910 Mon Sep 17 00:00:00 2001 From: Albin Zeng Date: Thu, 4 Feb 2021 16:36:46 +0800 Subject: [PATCH 30/37] release: v1.4.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3b620bc..df03c49 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "js-sql-parser", - "version": "1.3.0", + "version": "1.4.0", "description": "", "main": "./dist/parser/sqlParser.js", "scripts": { From 088e17b4bbaa39d807490a348173aa5fd84ef08f Mon Sep 17 00:00:00 2001 From: Albin Zeng Date: Thu, 4 Feb 2021 16:43:46 +0800 Subject: [PATCH 31/37] chore: readme & changelog. --- CHANGELOG | 1 + README.md | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 3d1a64c..243b783 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -8,3 +8,4 @@ 1.2.1 fix stringify having #29 , pr: #28 1.2.2 feat: add support for "`" quoted alias, pr: #33 1.3.0 fix tableFactor alias bug. AST changed in tableFactor. #34 +1.4.0 fix bug `using ' & " for column alias?` #40 #44 diff --git a/README.md b/README.md index 467f6e6..7f9fd26 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ sql grammar follows https://dev.mysql.com/doc/refman/5.7/en/select.html ## news +- Fix bug `using ' & " for column alias?` since v1.4.0 [#40](https://github.com/JavaScriptor/js-sql-parser/issues/40), [#44](https://github.com/JavaScriptor/js-sql-parser/issues/44) - Fix bug tableFactor alias since v1.3.0 [#34](https://github.com/JavaScriptor/js-sql-parser/issues/34) - Add support for "`" quoted alias since v1.2.2. [#33](https://github.com/JavaScriptor/js-sql-parser/issues/33) - Fix bug stringify keyword `having` since v1.2.1. [#29](https://github.com/JavaScriptor/js-sql-parser/issues/29) From 09b15e42d288a34603999e9be305b7b33231f945 Mon Sep 17 00:00:00 2001 From: Albin Zeng Date: Thu, 4 Feb 2021 18:32:10 +0800 Subject: [PATCH 32/37] chore: hotfix support quoted alias: multiple alias and orderby support. --- CHANGELOG | 1 + README.md | 2 +- package.json | 2 +- src/sqlParser.jison | 2 +- test/main.test.js | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 243b783..35c0dd0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -9,3 +9,4 @@ 1.2.2 feat: add support for "`" quoted alias, pr: #33 1.3.0 fix tableFactor alias bug. AST changed in tableFactor. #34 1.4.0 fix bug `using ' & " for column alias?` #40 #44 +1.4.1 hogfix "support quoted alias: multiple alias and orderby support" diff --git a/README.md b/README.md index 7f9fd26..ef48aab 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ sql grammar follows https://dev.mysql.com/doc/refman/5.7/en/select.html ## news -- Fix bug `using ' & " for column alias?` since v1.4.0 [#40](https://github.com/JavaScriptor/js-sql-parser/issues/40), [#44](https://github.com/JavaScriptor/js-sql-parser/issues/44) +- Fix bug `using ' & " for column alias?` since v1.4.1 [#40](https://github.com/JavaScriptor/js-sql-parser/issues/40), [#44](https://github.com/JavaScriptor/js-sql-parser/issues/44) - Fix bug tableFactor alias since v1.3.0 [#34](https://github.com/JavaScriptor/js-sql-parser/issues/34) - Add support for "`" quoted alias since v1.2.2. [#33](https://github.com/JavaScriptor/js-sql-parser/issues/33) - Fix bug stringify keyword `having` since v1.2.1. [#29](https://github.com/JavaScriptor/js-sql-parser/issues/29) diff --git a/package.json b/package.json index df03c49..23bf5b5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "js-sql-parser", - "version": "1.4.0", + "version": "1.4.1", "description": "", "main": "./dist/parser/sqlParser.js", "scripts": { diff --git a/src/sqlParser.jison b/src/sqlParser.jison index cc99391..86e13fc 100644 --- a/src/sqlParser.jison +++ b/src/sqlParser.jison @@ -128,7 +128,7 @@ UNION return 'UNION' \. return 'DOT' ["][a-zA-Z_\u4e00-\u9fa5][a-zA-Z0-9_\u4e00-\u9fa5]*["] return 'STRING' ['][a-zA-Z_\u4e00-\u9fa5][a-zA-Z0-9_\u4e00-\u9fa5]*['] return 'STRING' -[`].+[`] return 'IDENTIFIER' +([`])(?:(?=(\\?))\2.)*?\1 return 'IDENTIFIER' <> return 'EOF' . return 'INVALID' diff --git a/test/main.test.js b/test/main.test.js index 88b9cda..188f57d 100644 --- a/test/main.test.js +++ b/test/main.test.js @@ -428,7 +428,7 @@ describe('select grammar support', function () { testParser('select a as \'"A#A\' from z order by \'"A#A\' desc'); }); - it.only('test IDENTIFIER', function () { + it('test IDENTIFIER', function () { testParser('select `aa#sfs`(a) as \'A A\' from z'); }); }); From 8043656061ce969981ebe3b099d862ea82642ea7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A9=AC=E8=82=B1?= Date: Mon, 30 Oct 2023 15:46:20 +0800 Subject: [PATCH 33/37] release: v1.5.0 --- .gitignore | 1 + CHANGELOG | 1 + Makefile | 5 ++++- README.md | 15 +++++++++++---- package.json | 2 +- src/sqlParser.jison | 2 +- src/stringify.js | 12 ------------ test/main.test.js | 14 ++++++++++++++ 8 files changed, 33 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index c1119a3..1a8c027 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ package-lock.json *.log *.swp dist/parser/sqlParser.js +.vscode/ diff --git a/CHANGELOG b/CHANGELOG index 35c0dd0..dc5dd0a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -10,3 +10,4 @@ 1.3.0 fix tableFactor alias bug. AST changed in tableFactor. #34 1.4.0 fix bug `using ' & " for column alias?` #40 #44 1.4.1 hogfix "support quoted alias: multiple alias and orderby support" +1.5.0 support feature placeholder. diff --git a/Makefile b/Makefile index 51dd70d..ca658ba 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,10 @@ publish: test @npm publish -test: +test: @npm test +test-with-log: + @DEBUG=js-sql-parser npm test + .PHONY: publish test diff --git a/README.md b/README.md index ef48aab..1960e46 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ sql grammar follows https://dev.mysql.com/doc/refman/5.7/en/select.html ## news +- Support feature `PlaceHolder like ${param}` since v1.5.0 [#43](https://github.com/JavaScriptor/js-sql-parser/pull/43) - Fix bug `using ' & " for column alias?` since v1.4.1 [#40](https://github.com/JavaScriptor/js-sql-parser/issues/40), [#44](https://github.com/JavaScriptor/js-sql-parser/issues/44) - Fix bug tableFactor alias since v1.3.0 [#34](https://github.com/JavaScriptor/js-sql-parser/issues/34) - Add support for "`" quoted alias since v1.2.2. [#33](https://github.com/JavaScriptor/js-sql-parser/issues/33) @@ -35,6 +36,16 @@ console.log(parser.stringify(ast)); // SELECT foo FROM bar ``` +```js +// placeholder test +const parser = require('js-sql-parser'); +const ast = parser.parse('select ${a} as a'); + +ast['value']['selectItems']['value'][0]['value'] = "'value'"; +console.log(parser.stringify(ast)); +// SELECT 'value' AS a +``` + ## script tag ```js @@ -60,10 +71,6 @@ var sql = sqlParser.stringify(ast); - intervalexpr: Date INTERVAL keyword. // to support - into outfile: INTO OUTFILE keyword. // to support -## TODO - -- ${value} like value place holder support. - ## Build - Run `npm run build` to build the distributable. diff --git a/package.json b/package.json index 23bf5b5..223a2f3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "js-sql-parser", - "version": "1.4.1", + "version": "1.5.0", "description": "", "main": "./dist/parser/sqlParser.js", "scripts": { diff --git a/src/sqlParser.jison b/src/sqlParser.jison index 4a8efbc..ace8618 100644 --- a/src/sqlParser.jison +++ b/src/sqlParser.jison @@ -11,7 +11,7 @@ [#]\s.*\n /* skip sql comments */ \s+ /* skip whitespace */ -[$][{](.*?)[}] return 'PLACE_HOLDER' +[$][{](.+?)[}] return 'PLACE_HOLDER' [`][a-zA-Z_\u4e00-\u9fa5][a-zA-Z0-9_\u4e00-\u9fa5]*[`] return 'IDENTIFIER' [\w]+[\u4e00-\u9fa5]+[0-9a-zA-Z_\u4e00-\u9fa5]* return 'IDENTIFIER' [\u4e00-\u9fa5][0-9a-zA-Z_\u4e00-\u9fa5]* return 'IDENTIFIER' diff --git a/src/stringify.js b/src/stringify.js index 635e801..4679850 100644 --- a/src/stringify.js +++ b/src/stringify.js @@ -552,18 +552,6 @@ Sql.prototype.travelSelectParenthesized = function(ast) { this.appendKeyword(')'); }; Sql.prototype.travelPlaceHolder = function (ast) { - if (ast.left) { - this.travel(ast.left); - } - - if (ast.operator) { - this.append(ast.operator); - } - - if (ast.right) { - this.append(ast.right); - } - if (ast.value) { this.travel(ast.value); } diff --git a/test/main.test.js b/test/main.test.js index 2ee9c1b..379f7f4 100644 --- a/test/main.test.js +++ b/test/main.test.js @@ -2,6 +2,7 @@ const debug = require('debug')('js-sql-parser'); const parser = require('../'); +const assert = require('assert'); const testParser = function (sql) { let firstAst = parser.parse(sql); @@ -416,6 +417,19 @@ describe('select grammar support', function () { "select sum(quota_value) value, busi_col2 as sh, ${a} as a, YEAR(now()) from der_quota_summary where table_ename = 'gshmyyszje_derivedidx' and cd = (select id from t1 where a = ${t1})" ) }); + it('place holder support2', function() { + testParser( + "select sum(quota_value) b, busi_col2 as sh, '${a}' as a, YEAR(now()) from der_quota_summary where table_ename = 'gshmyyszje_derivedidx' and cd = (select id from t1 where a = '${t1}')" + ) + }); + it('place holder support3', function() { + let firstAst = parser.parse('select ${a} as a'); + firstAst['value']['selectItems']['value'][0]['value'] = "'value'"; + let firstSql = parser.stringify(firstAst); + debug(JSON.stringify(firstAst, null, 2)); + assert.equal(firstSql.trim().toUpperCase(), "select 'value' as a".toUpperCase()); + testParser(firstSql); + }); it('support quoted alias: multiple alias and orderby support', function () { testParser('select a as `A A`, b as `B B` from z'); From 203c5dea8614cb7afe4e3a04effc9131b9f6e54b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A9=AC=E8=82=B1?= Date: Tue, 31 Oct 2023 10:51:15 +0800 Subject: [PATCH 34/37] chore: add placeholder Note to readme. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 1960e46..ffa2b84 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,8 @@ console.log(parser.stringify(ast)); // SELECT 'value' AS a ``` +Note: PlaceHolder is an `literal` value but not an `identifier`. Table_name / column_name / function_name are `identifier` thus should NOT be placed with placeholder. + ## script tag ```js From 199a1c6ce417defe64e5ccea7fa25d43239f6a5f Mon Sep 17 00:00:00 2001 From: Luiz Zappa Date: Sun, 22 Oct 2023 15:02:06 -0300 Subject: [PATCH 35/37] feat(): unicode extended char support for column name or alias --- src/sqlParser.jison | 10 +++++----- test/main.test.js | 12 ++++++++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/sqlParser.jison b/src/sqlParser.jison index 4a8efbc..1db84b0 100644 --- a/src/sqlParser.jison +++ b/src/sqlParser.jison @@ -11,10 +11,10 @@ [#]\s.*\n /* skip sql comments */ \s+ /* skip whitespace */ -[$][{](.*?)[}] return 'PLACE_HOLDER' -[`][a-zA-Z_\u4e00-\u9fa5][a-zA-Z0-9_\u4e00-\u9fa5]*[`] return 'IDENTIFIER' -[\w]+[\u4e00-\u9fa5]+[0-9a-zA-Z_\u4e00-\u9fa5]* return 'IDENTIFIER' -[\u4e00-\u9fa5][0-9a-zA-Z_\u4e00-\u9fa5]* return 'IDENTIFIER' +[$][{](.+?)[}] return 'PLACE_HOLDER' +[`][a-zA-Z0-9_\u0080-\uFFFF]*[`] return 'IDENTIFIER' +[\w]+[\u0080-\uFFFF]+[0-9a-zA-Z_\u0080-\uFFFF]* return 'IDENTIFIER' +[\u0080-\uFFFF][0-9a-zA-Z_\u0080-\uFFFF]* return 'IDENTIFIER' SELECT return 'SELECT' ALL return 'ALL' ANY return 'ANY' @@ -125,7 +125,7 @@ UNION return 'UNION' [-]?[0-9]+(\.[0-9]+)? return 'NUMERIC' [-]?[0-9]+(\.[0-9]+)?[eE][-][0-9]+(\.[0-9]+)? return 'EXPONENT_NUMERIC' -[a-zA-Z_\u4e00-\u9fa5][a-zA-Z0-9_\u4e00-\u9fa5]* return 'IDENTIFIER' +[a-zA-Z_\u0080-\uFFFF][a-zA-Z0-9_\u0080-\uFFFF]* return 'IDENTIFIER' \. return 'DOT' ["][a-zA-Z_\u4e00-\u9fa5][a-zA-Z0-9_\u4e00-\u9fa5]*["] return 'STRING' ['][a-zA-Z_\u4e00-\u9fa5][a-zA-Z0-9_\u4e00-\u9fa5]*['] return 'STRING' diff --git a/test/main.test.js b/test/main.test.js index 2ee9c1b..d7a1496 100644 --- a/test/main.test.js +++ b/test/main.test.js @@ -436,4 +436,16 @@ describe('select grammar support', function () { it('test IDENTIFIER', function () { testParser('select `aa#sfs`(a) as \'A A\' from z'); }); + + it('Support unicode extended char (U+0080..U+FFFF) as column name or alias', function() { + testParser(`select + país, + MAX(produção) as maior_produção, + Ĉapelo, + Δάσος, + Молоко, + سلام, + かわいい + from table`) + }) }); From 14b31468fffa081457dfb15022f9ba720ea4fa4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A9=AC=E8=82=B1?= Date: Wed, 20 Mar 2024 15:31:06 +0800 Subject: [PATCH 36/37] feat: support function call in table_factor. --- README.md | 1 + package.json | 2 +- src/sqlParser.jison | 1 + test/main.test.js | 6 ++++++ 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ffa2b84..3ce1532 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ sql grammar follows https://dev.mysql.com/doc/refman/5.7/en/select.html ## news +- Unicode extended char support for column name or alias & Function call in `table_factor` since v1.6.0 - Support feature `PlaceHolder like ${param}` since v1.5.0 [#43](https://github.com/JavaScriptor/js-sql-parser/pull/43) - Fix bug `using ' & " for column alias?` since v1.4.1 [#40](https://github.com/JavaScriptor/js-sql-parser/issues/40), [#44](https://github.com/JavaScriptor/js-sql-parser/issues/44) - Fix bug tableFactor alias since v1.3.0 [#34](https://github.com/JavaScriptor/js-sql-parser/issues/34) diff --git a/package.json b/package.json index 223a2f3..bd3083b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "js-sql-parser", - "version": "1.5.0", + "version": "1.6.0", "description": "", "main": "./dist/parser/sqlParser.js", "scripts": { diff --git a/src/sqlParser.jison b/src/sqlParser.jison index 1db84b0..271c94a 100644 --- a/src/sqlParser.jison +++ b/src/sqlParser.jison @@ -588,6 +588,7 @@ table_factor : identifier partitionOpt aliasOpt index_hint_list_opt { $$ = { type: 'TableFactor', value: $1, partition: $2, alias: $3.alias, hasAs: $3.hasAs, indexHintOpt: $4 } } | '(' selectClause ')' aliasOpt { $$ = { type: 'TableFactor', value: { type: 'SubQuery', value: $2 }, alias: $4.alias, hasAs: $4.hasAs} } | '(' table_references ')' { $$ = $2; $$.hasParentheses = true } + | function_call aliasOpt index_hint_list_opt { $$ = { type: 'TableFactor', value: $1, alias: $2.alias, hasAs: $2.hasAs, indexHintOpt: $3 } } ; place_holder : PLACE_HOLDER { $$ = { type: 'PlaceHolder', value: $1, param: $1.slice(2, -1)} } diff --git a/test/main.test.js b/test/main.test.js index e4af010..4bf50b6 100644 --- a/test/main.test.js +++ b/test/main.test.js @@ -462,4 +462,10 @@ describe('select grammar support', function () { かわいい from table`) }) + + it('#60 Call function in FROM clause', function() { + testParser(` + SELECT one.name, group_concat(j.value, ', ') FROM one, json_each(one.stringArray) AS j GROUP BY one.id + `) + }) }); From 4baa4a937450b529fb31799d8912f4bc2f4a5776 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A9=AC=E8=82=B1?= Date: Thu, 21 Mar 2024 11:00:50 +0800 Subject: [PATCH 37/37] release: v1.6.0 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3ce1532..e113d48 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ sql grammar follows https://dev.mysql.com/doc/refman/5.7/en/select.html ## news -- Unicode extended char support for column name or alias & Function call in `table_factor` since v1.6.0 +- Unicode extended char support for column name or alias & Function call in `table_factor` since v1.6.0 [#58](https://github.com/JavaScriptor/js-sql-parser/pull/58), [#60](https://github.com/JavaScriptor/js-sql-parser/issues/60) - Support feature `PlaceHolder like ${param}` since v1.5.0 [#43](https://github.com/JavaScriptor/js-sql-parser/pull/43) - Fix bug `using ' & " for column alias?` since v1.4.1 [#40](https://github.com/JavaScriptor/js-sql-parser/issues/40), [#44](https://github.com/JavaScriptor/js-sql-parser/issues/44) - Fix bug tableFactor alias since v1.3.0 [#34](https://github.com/JavaScriptor/js-sql-parser/issues/34)