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 185e98b..dc5dd0a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -7,3 +7,7 @@ 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 +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 24afab7..e113d48 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,13 @@ 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) +- 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) - 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) @@ -33,6 +37,18 @@ 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 +``` + +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 @@ -58,10 +74,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 796b08c..bd3083b 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { "name": "js-sql-parser", - "version": "1.2.2", + "version": "1.6.0", "description": "", "main": "./dist/parser/sqlParser.js", "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" diff --git a/src/sqlParser.jison b/src/sqlParser.jison index ee5b329..271c94a 100644 --- a/src/sqlParser.jison +++ b/src/sqlParser.jison @@ -11,9 +11,10 @@ [#]\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' +[$][{](.+?)[}] 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' @@ -124,10 +125,11 @@ 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 'QUOTED_IDENTIFIER' -[`].+[`] return 'QUOTED_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' +([`])(?:(?=(\\?))\2.)*?\1 return 'IDENTIFIER' <> return 'EOF' . return 'INVALID' @@ -280,13 +282,11 @@ 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} } ; - string - : QUOTED_IDENTIFIER { $$ = { type: 'String', value: $1 } } - | STRING { $$ = { type: 'String', value: $1 } } + : STRING { $$ = { type: 'String', value: $1 } } ; number : NUMERIC { $$ = { type: 'Number', value: $1 } } @@ -305,6 +305,7 @@ literal | number { $$ = $1 } | boolean { $$ = $1 } | null { $$ = $1 } + | place_holder { $$ = $1 } ; function_call : IDENTIFIER '(' function_call_param_list ')' { $$ = {type: 'FunctionCall', name: $1, params: $3} } @@ -585,6 +586,10 @@ 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 } + | 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)} } + ; \ No newline at end of file diff --git a/src/stringify.js b/src/stringify.js index 57ea74e..4679850 100644 --- a/src/stringify.js +++ b/src/stringify.js @@ -551,3 +551,8 @@ Sql.prototype.travelSelectParenthesized = function(ast) { this.travel(ast.value); this.appendKeyword(')'); }; +Sql.prototype.travelPlaceHolder = function (ast) { + 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 558d133..4bf50b6 100644 --- a/test/main.test.js +++ b/test/main.test.js @@ -2,8 +2,9 @@ const debug = require('debug')('js-sql-parser'); const parser = require('../'); +const assert = require('assert'); -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 +23,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 +59,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 +90,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 +100,7 @@ describe('select grammar support', function() { `); }); - it('test9', function() { + it('test9', function () { testParser(` SELECT PLAYERS.PLAYERNO, NAME, (SELECT COUNT(*) @@ -114,7 +115,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 +149,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 +203,7 @@ describe('select grammar support', function() { `); }); - it('test13', function() { + it('test13', function () { testParser(` SELECT a.*, f.ORG_NAME DEPT_NAME, @@ -286,7 +287,7 @@ describe('select grammar support', function() { `); }); - it('test14', function() { + it('test14', function () { testParser(` SELECT k.*, @@ -345,7 +346,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 +355,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;'); @@ -397,4 +398,74 @@ 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') + }); + 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})" + ) + }); + 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'); + 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'); + }); + + 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('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`) + }) + + 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 + `) + }) }); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy