@careteam/sql-parser
Advanced tools
+10
| # CHANGELOG | ||
| ## 0.0.2 (2021-03-10) | ||
| - query 参数添加多种格式, parsed内容添加 orign/parameter,详见文档 | ||
| - 抛出错误添加错误详情 | ||
| ## 0.0.1 (2020-12-30) | ||
| - query parse 添加 group 字段区分是否由 () 包裹 |
+3
-1
| { | ||
| "name": "@careteam/sql-parser", | ||
| "description": "Lexer and Parser for SQL Syntax", | ||
| "version": "0.0.1", | ||
| "version": "0.0.2", | ||
| "author": { | ||
@@ -34,2 +34,4 @@ "name": "Andy Kent", | ||
| "@babel/preset-env": "^7.2.3", | ||
| "@careteam/eslint-config-tencent": "^0.12.3", | ||
| "eslint": "^7.17.0", | ||
| "jest": "^26.0.0", | ||
@@ -36,0 +38,0 @@ "jison": "0.4.18", |
+26
-2
@@ -14,3 +14,3 @@ SQL Parser | ||
| npm install sql-parser | ||
| npm install @tencent/sql-parser | ||
@@ -63,4 +63,6 @@ To build from source you'll need to run the following from the root of the project... | ||
| ------------- | ||
| 针对 query 的 parse 格式修改,官方单个 query parse 格式为: | ||
| ### query parse 添加 group 字段区分是否由 () 包裹 | ||
| 官方单个 query parse 格式为: | ||
| { | ||
@@ -203,1 +205,23 @@ "conditions": "and / or / = /...", | ||
| ### query 参数格式支持多种 | ||
| ``` | ||
| @xxx:[type] $xxx:[type] #{xxx}:[type] | ||
| type 可为:number, float, string, date, boolean | ||
| select * from table where a = @abc:number; | ||
| select * from table where a = @abc; | ||
| select * from table where a = $abc; | ||
| select * from table where a = $abc:number; | ||
| select * from table where a = #{abc}; | ||
| select * from table where a = #{abc}:number; | ||
| parsed 内容: | ||
| { | ||
| "value": "abc", | ||
| "origin": "$abc:number", // :number 非必须 | ||
| "type": "number", // 有 type 时才有这个字段 | ||
| "parameter": true | ||
| } | ||
| ``` |
@@ -270,3 +270,3 @@ /* parser generated by jison 0.4.18 */ | ||
| case 92: | ||
| this.$ = new yy.StringValue($$[$0], "'"); | ||
| this.$ = new yy.StringValue($$[$0], '\''); | ||
| break; | ||
@@ -273,0 +273,0 @@ case 93: |
+449
-443
@@ -1,2 +0,7 @@ | ||
| const Parser = require('jison').Parser; | ||
| /* global Select, Table, Join, Union, Group, SubSelect, Where, Limit */ | ||
| /* global Order, OrderArgument, Offset, Op, Having, BetweenOp, UnaryOp, CaseWhen, WhitepaceList */ | ||
| /* global ListValue, Case, CaseElse, NumberValue, BooleanValue, ParameterValue, StringValue, LiteralValue */ | ||
| /* global FunctionValue, ArgumentListValue, Star, Field */ | ||
| /* eslint-disable prefer-arrow-callback */ | ||
| const { Parser } = require('jison'); | ||
@@ -7,421 +12,421 @@ const unwrap = /^function\s*\(.*?\)\s*{\s*return\s*([\s\S]*);\s*}/; | ||
| function o(patternString, action, options) { | ||
| patternString = patternString.replace(/\s{2,}/g, ' '); | ||
| patternString = patternString.replace(/\s{2,}/g, ' '); | ||
| if (!action) { | ||
| return [patternString, '$$ = $1;', options]; | ||
| } | ||
| if (!action) { | ||
| return [patternString, '$$ = $1;', options]; | ||
| } | ||
| let match; | ||
| if ((match = unwrap.exec(action.toString()))) { | ||
| action = match[1]; | ||
| } else if ((match = cleanup.exec(action.toString()))) { | ||
| action = `(function(){ ${match[1]} }())`; | ||
| } else { | ||
| throw `Invalid action ${action}`; | ||
| } | ||
| let match; | ||
| if ((match = unwrap.exec(action.toString()))) { | ||
| action = match[1]; | ||
| } else if ((match = cleanup.exec(action.toString()))) { | ||
| action = `(function(){ ${match[1]} }())`; | ||
| } else { | ||
| throw `Invalid action ${action}`; | ||
| } | ||
| action = action.replace(/\bnew /g, '$&yy.'); | ||
| action = action.replace(/\s+/g, ' '); | ||
| action = action.replace(/\bnew /g, '$&yy.'); | ||
| action = action.replace(/\s+/g, ' '); | ||
| return [patternString, `$$ = ${action};`, options]; | ||
| return [patternString, `$$ = ${action};`, options]; | ||
| } | ||
| const grammar = { | ||
| Root : [ | ||
| o('Query EOF') | ||
| ], | ||
| Query : [ | ||
| o('SelectQuery'), | ||
| o('SelectQuery Unions', function ($1, $2) { | ||
| $1.unions = $2; | ||
| return $1; | ||
| }) | ||
| ], | ||
| SelectQuery : [ | ||
| o('SelectWithLimitQuery'), | ||
| o('BasicSelectQuery') | ||
| ], | ||
| BasicSelectQuery : [ | ||
| o('Select'), | ||
| o('Select OrderClause', function ($1, $2) { | ||
| $1.order = $2; | ||
| return $1; | ||
| }), | ||
| o('Select GroupClause', function ($1, $2) { | ||
| $1.group = $2; | ||
| return $1; | ||
| }), | ||
| o('Select GroupClause OrderClause', function ($1, $2, $3) { | ||
| $1.group = $2; | ||
| $1.order = $3; | ||
| return $1; | ||
| }) | ||
| ], | ||
| SelectWithLimitQuery : [ | ||
| o('SelectQuery LimitClause', function ($1, $2) { | ||
| $1.limit = $2; | ||
| return $1; | ||
| }) | ||
| ], | ||
| Select : [ | ||
| o('SelectClause'), | ||
| o('SelectClause WhereClause', function ($1, $2) { | ||
| $1.where = $2; | ||
| return $1; | ||
| }) | ||
| ], | ||
| SelectClause : [ | ||
| o('SELECT Fields FROM Table', function ($2, $4) { | ||
| return new Select($2, $4, false); | ||
| }), | ||
| o('SELECT DISTINCT Fields FROM Table', function ($3, $5) { | ||
| return new Select($3, $5, true); | ||
| }), | ||
| o('SELECT Fields FROM Table Joins', function ($2, $4, $5) { | ||
| return new Select($2, $4, false, $5); | ||
| }), | ||
| o('SELECT DISTINCT Fields FROM Table Joins', function ($3, $5, $6) { | ||
| return new Select($3, $5, true, $6); | ||
| }) | ||
| ], | ||
| Table : [ | ||
| o('Literal', function ($1) { | ||
| return new Table($1); | ||
| }), | ||
| o('Literal Literal', function ($1, $2) { | ||
| return new Table($1, $2); | ||
| }), | ||
| o('Literal AS Literal', function ($1, $3) { | ||
| return new Table($1, $3); | ||
| }), | ||
| o('LEFT_PAREN List RIGHT_PAREN', function ($2) { | ||
| return $2; | ||
| }), | ||
| o('LEFT_PAREN Query RIGHT_PAREN', function ($2) { | ||
| return new SubSelect($2); | ||
| }), | ||
| o('LEFT_PAREN Query RIGHT_PAREN Literal', function ($2, $4) { | ||
| return new SubSelect($2, $4); | ||
| }), | ||
| o('Literal WINDOW WINDOW_FUNCTION LEFT_PAREN Number RIGHT_PAREN', function ($1, $2, $3, $5) { | ||
| return new Table($1, null, $2, $3, $5); | ||
| }) | ||
| ], | ||
| Unions : [ | ||
| o('Union', function ($1) { | ||
| return [$1]; | ||
| }), | ||
| o('Unions Union', function ($1, $2) { | ||
| return $1.concat($2); | ||
| }) | ||
| ], | ||
| Union : [ | ||
| o('UNION SelectQuery', function ($2) { | ||
| return new Union($2); | ||
| }), | ||
| o('UNION ALL SelectQuery', function ($3) { | ||
| return new Union($3, true); | ||
| }) | ||
| ], | ||
| Joins : [ | ||
| o('Join', function ($1) { | ||
| return [$1]; | ||
| }), | ||
| o('Joins Join', function ($1, $2) { | ||
| return $1.concat($2); | ||
| }) | ||
| ], | ||
| Join : [ | ||
| o('JOIN Table ON Expression', function ($2, $4) { | ||
| return new Join($2, $4); | ||
| }), | ||
| o('LEFT JOIN Table ON Expression', function ($3, $5) { | ||
| return new Join($3, $5, 'LEFT'); | ||
| }), | ||
| o('RIGHT JOIN Table ON Expression', function ($3, $5) { | ||
| return new Join($3, $5, 'RIGHT'); | ||
| }), | ||
| o('LEFT INNER JOIN Table ON Expression', function ($4, $6) { | ||
| return new Join($4, $6, 'LEFT', 'INNER'); | ||
| }), | ||
| o('RIGHT INNER JOIN Table ON Expression', function ($4, $6) { | ||
| return new Join($4, $6, 'RIGHT', 'INNER'); | ||
| }), | ||
| o('LEFT OUTER JOIN Table ON Expression', function ($4, $6) { | ||
| return new Join($4, $6, 'LEFT', 'OUTER'); | ||
| }), | ||
| o('RIGHT OUTER JOIN Table ON Expression', function ($4, $6) { | ||
| return new Join($4, $6, 'RIGHT', 'OUTER'); | ||
| }) | ||
| ], | ||
| WhereClause : [ | ||
| o('WHERE Expression', function ($2) { | ||
| return new Where($2); | ||
| }) | ||
| ], | ||
| LimitClause : [ | ||
| o('LIMIT Number', function ($2) { | ||
| return new Limit($2); | ||
| }), | ||
| o('LIMIT Number SEPARATOR Number', function ($2, $4) { | ||
| return new Limit($4, $2); | ||
| }), | ||
| o('LIMIT Number OFFSET Number', function ($2, $4) { | ||
| return new Limit($2, $4); | ||
| }) | ||
| ], | ||
| OrderClause : [ | ||
| o('ORDER BY OrderArgs', function ($3) { | ||
| return new Order($3); | ||
| }), | ||
| o('ORDER BY OrderArgs OffsetClause', function ($3, $4) { | ||
| return new Order($3, $4); | ||
| }) | ||
| ], | ||
| OrderArgs : [ | ||
| o('OrderArg', function ($1) { | ||
| return [$1]; | ||
| }), | ||
| o('OrderArgs SEPARATOR OrderArg', function ($1, $3) { | ||
| return $1.concat($3); | ||
| }) | ||
| ], | ||
| OrderArg : [ | ||
| o('Value', function ($1) { | ||
| return new OrderArgument($1, 'ASC'); | ||
| }), | ||
| o('Value DIRECTION', function ($1, $2) { | ||
| return new OrderArgument($1, $2); | ||
| }) | ||
| ], | ||
| OffsetClause : [ | ||
| // MS SQL Server 2012+ | ||
| o('OFFSET OffsetRows', function ($2) { | ||
| return new Offset($2); | ||
| }), | ||
| o('OFFSET OffsetRows FetchClause', function ($2, $3) { | ||
| return new Offset($2, $3); | ||
| }) | ||
| ], | ||
| OffsetRows : [ | ||
| o('Number ROW', function ($1) { | ||
| return $1; | ||
| }), | ||
| o('Number ROWS', function ($1) { | ||
| return $1; | ||
| }) | ||
| ], | ||
| FetchClause : [ | ||
| o('FETCH FIRST OffsetRows ONLY', function ($3) { | ||
| return $3; | ||
| }), | ||
| o('FETCH NEXT OffsetRows ONLY', function ($3) { | ||
| return $3; | ||
| }) | ||
| ], | ||
| GroupClause : [ | ||
| o('GroupBasicClause', function ($1) { | ||
| return $1; | ||
| }), | ||
| o('GroupBasicClause HavingClause', function ($1, $2) { | ||
| $1.having = $2; | ||
| return $1; | ||
| }) | ||
| ], | ||
| GroupBasicClause : [ | ||
| o('GROUP BY ArgumentList', function ($3) { | ||
| return new Group($3); | ||
| }) | ||
| ], | ||
| HavingClause : [ | ||
| o('HAVING Expression', function ($2) { | ||
| return new Having($2); | ||
| }) | ||
| ], | ||
| Expression : [ | ||
| o('LEFT_PAREN Expression RIGHT_PAREN', function ($2) { | ||
| $2.group = true; | ||
| return $2; | ||
| }), | ||
| o('Expression MATH Expression', function ($1, $2, $3) { | ||
| return new Op($2, $1, $3); | ||
| }), | ||
| o('Expression MATH_MULTI Expression', function ($1, $2, $3) { | ||
| return new Op($2, $1, $3); | ||
| }), | ||
| o('Expression OPERATOR Expression', function ($1, $2, $3) { | ||
| return new Op($2, $1, $3); | ||
| }), | ||
| o('Expression BETWEEN BetweenExpression', function ($1, $2, $3) { | ||
| return new Op($2, $1, $3); | ||
| }), | ||
| o('Expression CONDITIONAL Expression', function ($1, $2, $3) { | ||
| return new Op($2, $1, $3); | ||
| }), | ||
| o('Value SUB_SELECT_OP LEFT_PAREN List RIGHT_PAREN', function ($1, $2, $4) { | ||
| return new Op($2, $1, $4); | ||
| }), | ||
| o('Value SUB_SELECT_OP SubSelectExpression', function ($1, $2, $3) { | ||
| return new Op($2, $1, $3); | ||
| }), | ||
| o('SUB_SELECT_UNARY_OP SubSelectExpression', function ($1, $2) { | ||
| return new UnaryOp($1, $2); | ||
| }), | ||
| o('SubSelectExpression', function ($1) { | ||
| return $1; | ||
| }), | ||
| o('WhitepaceList', function ($1) { | ||
| return new WhitepaceList($1); | ||
| }), | ||
| o('CaseStatement', function ($1) { | ||
| return $1; | ||
| }), | ||
| o('Value', function ($1) { | ||
| return $1; | ||
| }), | ||
| o('LEFT_PAREN RIGHT_PAREN', function ($1, $2) { | ||
| return $1 + $2; | ||
| }), | ||
| ], | ||
| BetweenExpression : [ | ||
| o('Expression CONDITIONAL Expression', function ($1, $3) { | ||
| return new BetweenOp([$1, $3]); | ||
| }) | ||
| ], | ||
| CaseStatement : [ | ||
| o('CASE CaseWhens END', function ($2) { | ||
| return new Case($2); | ||
| }), | ||
| o('CASE CaseWhens CaseElse END', function ($2, $3) { | ||
| return new Case($2, $3); | ||
| }) | ||
| ], | ||
| CaseWhen : [ | ||
| o('WHEN Expression THEN Expression', function ($2, $4) { | ||
| return new CaseWhen($2, $4); | ||
| }) | ||
| ], | ||
| CaseWhens : [ | ||
| o('CaseWhens CaseWhen', function ($1, $2) { | ||
| return $1.concat($2); | ||
| }), | ||
| o('CaseWhen', function ($1) { | ||
| return [$1]; | ||
| }) | ||
| ], | ||
| CaseElse : [ | ||
| o('ELSE Expression', function ($2) { | ||
| return new CaseElse($2); | ||
| }) | ||
| ], | ||
| SubSelectExpression : [ | ||
| o('LEFT_PAREN Query RIGHT_PAREN', function ($2) { | ||
| return new SubSelect($2); | ||
| }) | ||
| ], | ||
| Value : [ | ||
| o('Literal'), | ||
| o('Number'), | ||
| o('String'), | ||
| o('Function'), | ||
| o('UserFunction'), | ||
| o('Boolean'), | ||
| o('Parameter') | ||
| ], | ||
| WhitepaceList : [ | ||
| o('Value Value', function ($1, $2) { | ||
| return [$1, $2]; | ||
| }), | ||
| o('WhitepaceList Value', function ($1, $2) { | ||
| $1.push($2); | ||
| return $1; | ||
| }) | ||
| ], | ||
| List : [ | ||
| o('ArgumentList', function ($1) { | ||
| return new ListValue($1); | ||
| }) | ||
| ], | ||
| Number : [ | ||
| o('NUMBER', function ($1) { | ||
| return new NumberValue($1); | ||
| }) | ||
| ], | ||
| Boolean : [ | ||
| o('BOOLEAN', function ($1) { | ||
| return new BooleanValue($1); | ||
| }) | ||
| ], | ||
| Parameter : [ | ||
| o('PARAMETER', function ($1) { | ||
| return new ParameterValue($1); | ||
| }) | ||
| ], | ||
| String : [ | ||
| o('STRING', function ($1) { | ||
| return new StringValue($1, "'"); | ||
| }), | ||
| o('DBLSTRING', function ($1) { | ||
| return new StringValue($1, '"'); | ||
| }) | ||
| ], | ||
| Literal : [ | ||
| o('LITERAL', function ($1) { | ||
| return new LiteralValue($1); | ||
| }), | ||
| o('Literal DOT LITERAL', function ($1, $3) { | ||
| return new LiteralValue($1, $3); | ||
| }) | ||
| ], | ||
| Function : [ | ||
| o('FUNCTION LEFT_PAREN AggregateArgumentList RIGHT_PAREN', function ($1, $3) { | ||
| return new FunctionValue($1, $3); | ||
| }) | ||
| ], | ||
| UserFunction : [ | ||
| o('LITERAL LEFT_PAREN RIGHT_PAREN', function ($1) { | ||
| return new FunctionValue($1, null, true); | ||
| }), | ||
| o('LITERAL LEFT_PAREN AggregateArgumentList RIGHT_PAREN', function ($1, $3) { | ||
| return new FunctionValue($1, $3, true); | ||
| }), | ||
| o('LITERAL LEFT_PAREN Case RIGHT_PAREN', function ($1, $3) { | ||
| return new FunctionValue($1, $3, true); | ||
| }) | ||
| ], | ||
| AggregateArgumentList: [ | ||
| o('ArgumentList', function ($1) { | ||
| return new ArgumentListValue($1); | ||
| }), | ||
| o('DISTINCT ArgumentList', function ($2) { | ||
| return new ArgumentListValue($2, true); | ||
| }) | ||
| ], | ||
| ArgumentList : [ | ||
| o('Expression', function ($1) { | ||
| return [$1]; | ||
| }), | ||
| o('ArgumentList SEPARATOR Expression', function ($1, $3) { | ||
| return $1.concat($3); | ||
| }) | ||
| ], | ||
| Fields : [ | ||
| o('Field', function ($1) { | ||
| return [$1]; | ||
| }), | ||
| o('Fields SEPARATOR Field', function ($1, $3) { | ||
| return $1.concat($3); | ||
| }) | ||
| ], | ||
| Field : [ | ||
| o('STAR', function () { | ||
| return new Star(); | ||
| }), | ||
| o('Expression', function ($1) { | ||
| return new Field($1); | ||
| }), | ||
| o('Expression AS Literal', function ($1, $3) { | ||
| return new Field($1, $3); | ||
| }) | ||
| ] | ||
| Root: [ | ||
| o('Query EOF'), | ||
| ], | ||
| Query: [ | ||
| o('SelectQuery'), | ||
| o('SelectQuery Unions', function ($1, $2) { | ||
| $1.unions = $2; | ||
| return $1; | ||
| }), | ||
| ], | ||
| SelectQuery: [ | ||
| o('SelectWithLimitQuery'), | ||
| o('BasicSelectQuery'), | ||
| ], | ||
| BasicSelectQuery: [ | ||
| o('Select'), | ||
| o('Select OrderClause', function ($1, $2) { | ||
| $1.order = $2; | ||
| return $1; | ||
| }), | ||
| o('Select GroupClause', function ($1, $2) { | ||
| $1.group = $2; | ||
| return $1; | ||
| }), | ||
| o('Select GroupClause OrderClause', function ($1, $2, $3) { | ||
| $1.group = $2; | ||
| $1.order = $3; | ||
| return $1; | ||
| }), | ||
| ], | ||
| SelectWithLimitQuery: [ | ||
| o('SelectQuery LimitClause', function ($1, $2) { | ||
| $1.limit = $2; | ||
| return $1; | ||
| }), | ||
| ], | ||
| Select: [ | ||
| o('SelectClause'), | ||
| o('SelectClause WhereClause', function ($1, $2) { | ||
| $1.where = $2; | ||
| return $1; | ||
| }), | ||
| ], | ||
| SelectClause: [ | ||
| o('SELECT Fields FROM Table', function ($2, $4) { | ||
| return new Select($2, $4, false); | ||
| }), | ||
| o('SELECT DISTINCT Fields FROM Table', function ($3, $5) { | ||
| return new Select($3, $5, true); | ||
| }), | ||
| o('SELECT Fields FROM Table Joins', function ($2, $4, $5) { | ||
| return new Select($2, $4, false, $5); | ||
| }), | ||
| o('SELECT DISTINCT Fields FROM Table Joins', function ($3, $5, $6) { | ||
| return new Select($3, $5, true, $6); | ||
| }), | ||
| ], | ||
| Table: [ | ||
| o('Literal', function ($1) { | ||
| return new Table($1); | ||
| }), | ||
| o('Literal Literal', function ($1, $2) { | ||
| return new Table($1, $2); | ||
| }), | ||
| o('Literal AS Literal', function ($1, $3) { | ||
| return new Table($1, $3); | ||
| }), | ||
| o('LEFT_PAREN List RIGHT_PAREN', function ($2) { | ||
| return $2; | ||
| }), | ||
| o('LEFT_PAREN Query RIGHT_PAREN', function ($2) { | ||
| return new SubSelect($2); | ||
| }), | ||
| o('LEFT_PAREN Query RIGHT_PAREN Literal', function ($2, $4) { | ||
| return new SubSelect($2, $4); | ||
| }), | ||
| o('Literal WINDOW WINDOW_FUNCTION LEFT_PAREN Number RIGHT_PAREN', function ($1, $2, $3, $5) { | ||
| return new Table($1, null, $2, $3, $5); | ||
| }), | ||
| ], | ||
| Unions: [ | ||
| o('Union', function ($1) { | ||
| return [$1]; | ||
| }), | ||
| o('Unions Union', function ($1, $2) { | ||
| return $1.concat($2); | ||
| }), | ||
| ], | ||
| Union: [ | ||
| o('UNION SelectQuery', function ($2) { | ||
| return new Union($2); | ||
| }), | ||
| o('UNION ALL SelectQuery', function ($3) { | ||
| return new Union($3, true); | ||
| }), | ||
| ], | ||
| Joins: [ | ||
| o('Join', function ($1) { | ||
| return [$1]; | ||
| }), | ||
| o('Joins Join', function ($1, $2) { | ||
| return $1.concat($2); | ||
| }), | ||
| ], | ||
| Join: [ | ||
| o('JOIN Table ON Expression', function ($2, $4) { | ||
| return new Join($2, $4); | ||
| }), | ||
| o('LEFT JOIN Table ON Expression', function ($3, $5) { | ||
| return new Join($3, $5, 'LEFT'); | ||
| }), | ||
| o('RIGHT JOIN Table ON Expression', function ($3, $5) { | ||
| return new Join($3, $5, 'RIGHT'); | ||
| }), | ||
| o('LEFT INNER JOIN Table ON Expression', function ($4, $6) { | ||
| return new Join($4, $6, 'LEFT', 'INNER'); | ||
| }), | ||
| o('RIGHT INNER JOIN Table ON Expression', function ($4, $6) { | ||
| return new Join($4, $6, 'RIGHT', 'INNER'); | ||
| }), | ||
| o('LEFT OUTER JOIN Table ON Expression', function ($4, $6) { | ||
| return new Join($4, $6, 'LEFT', 'OUTER'); | ||
| }), | ||
| o('RIGHT OUTER JOIN Table ON Expression', function ($4, $6) { | ||
| return new Join($4, $6, 'RIGHT', 'OUTER'); | ||
| }), | ||
| ], | ||
| WhereClause: [ | ||
| o('WHERE Expression', function ($2) { | ||
| return new Where($2); | ||
| }), | ||
| ], | ||
| LimitClause: [ | ||
| o('LIMIT Number', function ($2) { | ||
| return new Limit($2); | ||
| }), | ||
| o('LIMIT Number SEPARATOR Number', function ($2, $4) { | ||
| return new Limit($4, $2); | ||
| }), | ||
| o('LIMIT Number OFFSET Number', function ($2, $4) { | ||
| return new Limit($2, $4); | ||
| }), | ||
| ], | ||
| OrderClause: [ | ||
| o('ORDER BY OrderArgs', function ($3) { | ||
| return new Order($3); | ||
| }), | ||
| o('ORDER BY OrderArgs OffsetClause', function ($3, $4) { | ||
| return new Order($3, $4); | ||
| }), | ||
| ], | ||
| OrderArgs: [ | ||
| o('OrderArg', function ($1) { | ||
| return [$1]; | ||
| }), | ||
| o('OrderArgs SEPARATOR OrderArg', function ($1, $3) { | ||
| return $1.concat($3); | ||
| }), | ||
| ], | ||
| OrderArg: [ | ||
| o('Value', function ($1) { | ||
| return new OrderArgument($1, 'ASC'); | ||
| }), | ||
| o('Value DIRECTION', function ($1, $2) { | ||
| return new OrderArgument($1, $2); | ||
| }), | ||
| ], | ||
| OffsetClause: [ | ||
| // MS SQL Server 2012+ | ||
| o('OFFSET OffsetRows', function ($2) { | ||
| return new Offset($2); | ||
| }), | ||
| o('OFFSET OffsetRows FetchClause', function ($2, $3) { | ||
| return new Offset($2, $3); | ||
| }), | ||
| ], | ||
| OffsetRows: [ | ||
| o('Number ROW', function ($1) { | ||
| return $1; | ||
| }), | ||
| o('Number ROWS', function ($1) { | ||
| return $1; | ||
| }), | ||
| ], | ||
| FetchClause: [ | ||
| o('FETCH FIRST OffsetRows ONLY', function ($3) { | ||
| return $3; | ||
| }), | ||
| o('FETCH NEXT OffsetRows ONLY', function ($3) { | ||
| return $3; | ||
| }), | ||
| ], | ||
| GroupClause: [ | ||
| o('GroupBasicClause', function ($1) { | ||
| return $1; | ||
| }), | ||
| o('GroupBasicClause HavingClause', function ($1, $2) { | ||
| $1.having = $2; | ||
| return $1; | ||
| }), | ||
| ], | ||
| GroupBasicClause: [ | ||
| o('GROUP BY ArgumentList', function ($3) { | ||
| return new Group($3); | ||
| }), | ||
| ], | ||
| HavingClause: [ | ||
| o('HAVING Expression', function ($2) { | ||
| return new Having($2); | ||
| }), | ||
| ], | ||
| Expression: [ | ||
| o('LEFT_PAREN Expression RIGHT_PAREN', function ($2) { | ||
| $2.group = true; | ||
| return $2; | ||
| }), | ||
| o('Expression MATH Expression', function ($1, $2, $3) { | ||
| return new Op($2, $1, $3); | ||
| }), | ||
| o('Expression MATH_MULTI Expression', function ($1, $2, $3) { | ||
| return new Op($2, $1, $3); | ||
| }), | ||
| o('Expression OPERATOR Expression', function ($1, $2, $3) { | ||
| return new Op($2, $1, $3); | ||
| }), | ||
| o('Expression BETWEEN BetweenExpression', function ($1, $2, $3) { | ||
| return new Op($2, $1, $3); | ||
| }), | ||
| o('Expression CONDITIONAL Expression', function ($1, $2, $3) { | ||
| return new Op($2, $1, $3); | ||
| }), | ||
| o('Value SUB_SELECT_OP LEFT_PAREN List RIGHT_PAREN', function ($1, $2, $4) { | ||
| return new Op($2, $1, $4); | ||
| }), | ||
| o('Value SUB_SELECT_OP SubSelectExpression', function ($1, $2, $3) { | ||
| return new Op($2, $1, $3); | ||
| }), | ||
| o('SUB_SELECT_UNARY_OP SubSelectExpression', function ($1, $2) { | ||
| return new UnaryOp($1, $2); | ||
| }), | ||
| o('SubSelectExpression', function ($1) { | ||
| return $1; | ||
| }), | ||
| o('WhitepaceList', function ($1) { | ||
| return new WhitepaceList($1); | ||
| }), | ||
| o('CaseStatement', function ($1) { | ||
| return $1; | ||
| }), | ||
| o('Value', function ($1) { | ||
| return $1; | ||
| }), | ||
| o('LEFT_PAREN RIGHT_PAREN', function ($1, $2) { | ||
| return $1 + $2; | ||
| }), | ||
| ], | ||
| BetweenExpression: [ | ||
| o('Expression CONDITIONAL Expression', function ($1, $3) { | ||
| return new BetweenOp([$1, $3]); | ||
| }), | ||
| ], | ||
| CaseStatement: [ | ||
| o('CASE CaseWhens END', function ($2) { | ||
| return new Case($2); | ||
| }), | ||
| o('CASE CaseWhens CaseElse END', function ($2, $3) { | ||
| return new Case($2, $3); | ||
| }), | ||
| ], | ||
| CaseWhen: [ | ||
| o('WHEN Expression THEN Expression', function ($2, $4) { | ||
| return new CaseWhen($2, $4); | ||
| }), | ||
| ], | ||
| CaseWhens: [ | ||
| o('CaseWhens CaseWhen', function ($1, $2) { | ||
| return $1.concat($2); | ||
| }), | ||
| o('CaseWhen', function ($1) { | ||
| return [$1]; | ||
| }), | ||
| ], | ||
| CaseElse: [ | ||
| o('ELSE Expression', function ($2) { | ||
| return new CaseElse($2); | ||
| }), | ||
| ], | ||
| SubSelectExpression: [ | ||
| o('LEFT_PAREN Query RIGHT_PAREN', function ($2) { | ||
| return new SubSelect($2); | ||
| }), | ||
| ], | ||
| Value: [ | ||
| o('Literal'), | ||
| o('Number'), | ||
| o('String'), | ||
| o('Function'), | ||
| o('UserFunction'), | ||
| o('Boolean'), | ||
| o('Parameter'), | ||
| ], | ||
| WhitepaceList: [ | ||
| o('Value Value', function ($1, $2) { | ||
| return [$1, $2]; | ||
| }), | ||
| o('WhitepaceList Value', function ($1, $2) { | ||
| $1.push($2); | ||
| return $1; | ||
| }), | ||
| ], | ||
| List: [ | ||
| o('ArgumentList', function ($1) { | ||
| return new ListValue($1); | ||
| }), | ||
| ], | ||
| Number: [ | ||
| o('NUMBER', function ($1) { | ||
| return new NumberValue($1); | ||
| }), | ||
| ], | ||
| Boolean: [ | ||
| o('BOOLEAN', function ($1) { | ||
| return new BooleanValue($1); | ||
| }), | ||
| ], | ||
| Parameter: [ | ||
| o('PARAMETER', function ($1) { | ||
| return new ParameterValue($1); | ||
| }), | ||
| ], | ||
| String: [ | ||
| o('STRING', function ($1) { | ||
| return new StringValue($1, '\''); | ||
| }), | ||
| o('DBLSTRING', function ($1) { | ||
| return new StringValue($1, '"'); | ||
| }), | ||
| ], | ||
| Literal: [ | ||
| o('LITERAL', function ($1) { | ||
| return new LiteralValue($1); | ||
| }), | ||
| o('Literal DOT LITERAL', function ($1, $3) { | ||
| return new LiteralValue($1, $3); | ||
| }), | ||
| ], | ||
| Function: [ | ||
| o('FUNCTION LEFT_PAREN AggregateArgumentList RIGHT_PAREN', function ($1, $3) { | ||
| return new FunctionValue($1, $3); | ||
| }), | ||
| ], | ||
| UserFunction: [ | ||
| o('LITERAL LEFT_PAREN RIGHT_PAREN', function ($1) { | ||
| return new FunctionValue($1, null, true); | ||
| }), | ||
| o('LITERAL LEFT_PAREN AggregateArgumentList RIGHT_PAREN', function ($1, $3) { | ||
| return new FunctionValue($1, $3, true); | ||
| }), | ||
| o('LITERAL LEFT_PAREN Case RIGHT_PAREN', function ($1, $3) { | ||
| return new FunctionValue($1, $3, true); | ||
| }), | ||
| ], | ||
| AggregateArgumentList: [ | ||
| o('ArgumentList', function ($1) { | ||
| return new ArgumentListValue($1); | ||
| }), | ||
| o('DISTINCT ArgumentList', function ($2) { | ||
| return new ArgumentListValue($2, true); | ||
| }), | ||
| ], | ||
| ArgumentList: [ | ||
| o('Expression', function ($1) { | ||
| return [$1]; | ||
| }), | ||
| o('ArgumentList SEPARATOR Expression', function ($1, $3) { | ||
| return $1.concat($3); | ||
| }), | ||
| ], | ||
| Fields: [ | ||
| o('Field', function ($1) { | ||
| return [$1]; | ||
| }), | ||
| o('Fields SEPARATOR Field', function ($1, $3) { | ||
| return $1.concat($3); | ||
| }), | ||
| ], | ||
| Field: [ | ||
| o('STAR', function () { | ||
| return new Star(); | ||
| }), | ||
| o('Expression', function ($1) { | ||
| return new Field($1); | ||
| }), | ||
| o('Expression AS Literal', function ($1, $3) { | ||
| return new Field($1, $3); | ||
| }), | ||
| ], | ||
| }; | ||
@@ -432,38 +437,39 @@ | ||
| const operators = [ | ||
| ['left', 'Op'], | ||
| ['left', 'MATH_MULTI'], | ||
| ['left', 'MATH'], | ||
| ['left', 'OPERATOR'], | ||
| ['left', 'CONDITIONAL'] | ||
| ['left', 'Op'], | ||
| ['left', 'MATH_MULTI'], | ||
| ['left', 'MATH'], | ||
| ['left', 'OPERATOR'], | ||
| ['left', 'CONDITIONAL'], | ||
| ]; | ||
| for (let name in grammar) { | ||
| const alternatives = grammar[name]; | ||
| grammar[name] = (function () { | ||
| const results = []; | ||
| for (let i = 0, len = alternatives.length; i < len; i++) { | ||
| const alt = alternatives[i]; | ||
| const ref = alt[0].split(' '); | ||
| for (let j = 0, len1 = ref.length; j < len1; j++) { | ||
| token = ref[j]; | ||
| if (!grammar[token]) { | ||
| tokens.push(token); | ||
| } | ||
| } | ||
| if (name === 'Root') { | ||
| alt[1] = `return ${alt[1]}`; | ||
| } | ||
| results.push(alt); | ||
| } | ||
| return results; | ||
| })(); | ||
| function grammarFunc(name, alternatives) { | ||
| const results = []; | ||
| for (let i = 0, len = alternatives.length; i < len; i++) { | ||
| const alt = alternatives[i]; | ||
| const ref = alt[0].split(' '); | ||
| for (let j = 0, len1 = ref.length; j < len1; j++) { | ||
| const token = ref[j]; | ||
| if (!grammar[token]) { | ||
| tokens.push(token); | ||
| } | ||
| } | ||
| if (name === 'Root') { | ||
| alt[1] = `return ${alt[1]}`; | ||
| } | ||
| results.push(alt); | ||
| } | ||
| return results; | ||
| } | ||
| Object.keys(grammar).forEach((name) => { | ||
| const alternatives = grammar[name]; | ||
| grammar[name] = grammarFunc(name, alternatives); | ||
| }); | ||
| exports.parser = new Parser({ | ||
| tokens : tokens.join(' '), | ||
| bnf : grammar, | ||
| operators : operators.reverse(), | ||
| startSymbol: 'Root', | ||
| tokens: tokens.join(' '), | ||
| bnf: grammar, | ||
| operators: operators.reverse(), | ||
| startSymbol: 'Root', | ||
| }, { | ||
| moduleType: 'js', | ||
| moduleType: 'js', | ||
| }); |
+217
-210
@@ -14,4 +14,4 @@ const SQL_FUNCTIONS = ['AVG', 'COUNT', 'MIN', 'MAX', 'SUM']; | ||
| const WHITESPACE = /^[ \n\r]+/; | ||
| const LITERAL = /^`?([a-z_][a-z0-9_]{0,}(\:(number|float|string|date|boolean))?)`?/i; | ||
| const PARAMETER = /^\$([a-z0-9_]+(\:(number|float|string|date|boolean))?)/; | ||
| const LITERAL = /^`?([a-z_][a-z0-9_]{0,}(:(number|float|string|date|boolean))?)`?/i; | ||
| const PARAMETER = /^(((#{([a-z0-9_]+)})|(@([a-z0-9_]+))|(\$([a-z0-9_]+)))(:(number|float|string|date|boolean))?)/; | ||
| const NUMBER = /^[+-]?[0-9]+(\.[0-9]+)?/; | ||
@@ -22,254 +22,261 @@ const STRING = /^'((?:[^\\']+?|\\.|'')*)'(?!')/; | ||
| class Lexer { | ||
| constructor(sql, opts = {}) { | ||
| this.sql = sql; | ||
| this.preserveWhitespace = opts.preserveWhitespace || false; | ||
| this.tokens = []; | ||
| this.currentLine = 1; | ||
| this.currentOffset = 0; | ||
| constructor(sql, opts = {}) { | ||
| this.sql = sql; | ||
| this.preserveWhitespace = opts.preserveWhitespace || false; | ||
| this.tokens = []; | ||
| this.currentLine = 1; | ||
| this.currentOffset = 0; | ||
| let i = 0; | ||
| let i = 0; | ||
| while (!!(this.chunk = sql.slice(i))) { | ||
| const bytesConsumed = this.keywordToken() || | ||
| this.starToken() || | ||
| this.booleanToken() || | ||
| this.functionToken() || | ||
| this.windowExtension() || | ||
| this.sortOrderToken() || | ||
| this.seperatorToken() || | ||
| this.operatorToken() || | ||
| this.numberToken() || | ||
| this.mathToken() || | ||
| this.dotToken() || | ||
| this.conditionalToken() || | ||
| this.betweenToken() || | ||
| this.subSelectOpToken() || | ||
| this.subSelectUnaryOpToken() || | ||
| this.stringToken() || | ||
| this.parameterToken() || | ||
| this.parensToken() || | ||
| this.whitespaceToken() || | ||
| this.literalToken(); | ||
| while (sql.slice(i)) { | ||
| this.chunk = sql.slice(i); | ||
| const bytesConsumed = this.keywordToken() | ||
| || this.starToken() | ||
| || this.booleanToken() | ||
| || this.functionToken() | ||
| || this.windowExtension() | ||
| || this.sortOrderToken() | ||
| || this.seperatorToken() | ||
| || this.operatorToken() | ||
| || this.numberToken() | ||
| || this.mathToken() | ||
| || this.dotToken() | ||
| || this.conditionalToken() | ||
| || this.betweenToken() | ||
| || this.subSelectOpToken() | ||
| || this.subSelectUnaryOpToken() | ||
| || this.stringToken() | ||
| || this.parameterToken() | ||
| || this.parensToken() | ||
| || this.whitespaceToken() | ||
| || this.literalToken(); | ||
| if (bytesConsumed < 1) { | ||
| throw new Error(`NOTHING CONSUMED: Stopped at - '${this.chunk.slice(0, 30)}'`); | ||
| } | ||
| if (bytesConsumed < 1) { | ||
| const sub = this.sql.slice(0, this.currentOffset); | ||
| const arr = sub.split('\n'); | ||
| const chunk = this.chunk.replace(/\n/g, ' ').split(' '); | ||
| const e = new Error(`Parse error on line ${arr.length}, near ${chunk[0]}`); | ||
| e.hash = { | ||
| line: arr.length - 1, | ||
| loc: { start: arr[arr.length - 1].length }, | ||
| }; | ||
| throw e; | ||
| } | ||
| i += bytesConsumed; | ||
| this.currentOffset += bytesConsumed; | ||
| } | ||
| this.token('EOF', ''); | ||
| this.postProcess(); | ||
| i += bytesConsumed; | ||
| this.currentOffset += bytesConsumed; | ||
| } | ||
| postProcess() { | ||
| const results = []; | ||
| for (let i =0, j = 0, len = this.tokens.length; j < len; i = ++j) { | ||
| const token = this.tokens[i]; | ||
| if (token[0] === 'STAR') { | ||
| const next_token = this.tokens[i + 1]; | ||
| if (!(next_token[0] === 'SEPARATOR' || next_token[0] === 'FROM')) { | ||
| results.push(token[0] = 'MATH_MULTI'); | ||
| } | ||
| else { | ||
| results.push(void 0); | ||
| } | ||
| } | ||
| else { | ||
| results.push(void 0); | ||
| } | ||
| this.token('EOF', ''); | ||
| this.postProcess(); | ||
| } | ||
| postProcess() { | ||
| const results = []; | ||
| for (let i = 0, j = 0, len = this.tokens.length; j < len; i = j) { | ||
| j += 1; | ||
| const token = this.tokens[i]; | ||
| if (token[0] === 'STAR') { | ||
| const nexToken = this.tokens[i + 1]; | ||
| if (!(nexToken[0] === 'SEPARATOR' || nexToken[0] === 'FROM')) { | ||
| results.push(token[0] = 'MATH_MULTI'); | ||
| } else { | ||
| results.push(void 0); | ||
| } | ||
| return results; | ||
| } else { | ||
| results.push(void 0); | ||
| } | ||
| } | ||
| return results; | ||
| } | ||
| token(name, value) { | ||
| return this.tokens.push([name, value, this.currentLine, this.currentOffset]); | ||
| } | ||
| token(name, value) { | ||
| return this.tokens.push([name, value, this.currentLine, this.currentOffset]); | ||
| } | ||
| tokenizeFromStringRegex(name, regex, part = 0, lengthPart = part, output = true) { | ||
| const match = regex.exec(this.chunk); | ||
| if (!match) { | ||
| return 0; | ||
| } | ||
| const partMatch = match[part].replace(/''/g, '\''); | ||
| if (output) { | ||
| this.token(name, partMatch); | ||
| } | ||
| return match[lengthPart].length; | ||
| tokenizeFromStringRegex(name, regex, part = 0, lengthPart = part, output = true) { | ||
| const match = regex.exec(this.chunk); | ||
| if (!match) { | ||
| return 0; | ||
| } | ||
| tokenizeFromRegex(name, regex, part = 0, lengthPart = part, output = true) { | ||
| const match = regex.exec(this.chunk); | ||
| if (!match) { | ||
| return 0; | ||
| } | ||
| const partMatch = match[part]; | ||
| if (output) { | ||
| this.token(name, partMatch); | ||
| } | ||
| return match[lengthPart].length; | ||
| const partMatch = match[part].replace(/''/g, '\''); | ||
| if (output) { | ||
| this.token(name, partMatch); | ||
| } | ||
| return match[lengthPart].length; | ||
| } | ||
| tokenizeFromWord(name, word = name) { | ||
| word = this.regexEscape(word); | ||
| const matcher = /^\w+$/.test(word) ? new RegExp(`^(${word})\\b`, 'ig') : new RegExp(`^(${word})`, 'ig'); | ||
| const match = matcher.exec(this.chunk); | ||
| if (!match) { | ||
| return 0; | ||
| } | ||
| this.token(name, match[1]); | ||
| return match[1].length; | ||
| tokenizeFromRegex(name, regex, part = 0, lengthPart = part, output = true) { | ||
| const match = regex.exec(this.chunk); | ||
| if (!match) { | ||
| return 0; | ||
| } | ||
| tokenizeFromList(name, list) { | ||
| let ret = 0; | ||
| for (let j = 0, len = list.length; j < len; j++) { | ||
| const entry = list[j]; | ||
| ret = this.tokenizeFromWord(name, entry); | ||
| if (ret > 0) { | ||
| break; | ||
| } | ||
| } | ||
| return ret; | ||
| const partMatch = match[part]; | ||
| if (output) { | ||
| this.token(name, partMatch); | ||
| } | ||
| return match[lengthPart].length; | ||
| } | ||
| keywordToken() { | ||
| return this.tokenizeFromWord('SELECT') || | ||
| this.tokenizeFromWord('INSERT') || | ||
| this.tokenizeFromWord('INTO') || | ||
| this.tokenizeFromWord('DEFAULT') || | ||
| this.tokenizeFromWord('VALUES') || | ||
| this.tokenizeFromWord('DISTINCT') || | ||
| this.tokenizeFromWord('FROM') || | ||
| this.tokenizeFromWord('WHERE') || | ||
| this.tokenizeFromWord('GROUP') || | ||
| this.tokenizeFromWord('ORDER') || | ||
| this.tokenizeFromWord('BY') || | ||
| this.tokenizeFromWord('HAVING') || | ||
| this.tokenizeFromWord('LIMIT') || | ||
| this.tokenizeFromWord('JOIN') || | ||
| this.tokenizeFromWord('LEFT') || | ||
| this.tokenizeFromWord('RIGHT') || | ||
| this.tokenizeFromWord('INNER') || | ||
| this.tokenizeFromWord('OUTER') || | ||
| this.tokenizeFromWord('ON') || | ||
| this.tokenizeFromWord('AS') || | ||
| this.tokenizeFromWord('CASE') || | ||
| this.tokenizeFromWord('WHEN') || | ||
| this.tokenizeFromWord('THEN') || | ||
| this.tokenizeFromWord('ELSE') || | ||
| this.tokenizeFromWord('END') || | ||
| this.tokenizeFromWord('UNION') || | ||
| this.tokenizeFromWord('ALL') || | ||
| this.tokenizeFromWord('LIMIT') || | ||
| this.tokenizeFromWord('OFFSET') || | ||
| this.tokenizeFromWord('FETCH') || | ||
| this.tokenizeFromWord('ROW') || | ||
| this.tokenizeFromWord('ROWS') || | ||
| this.tokenizeFromWord('ONLY') || | ||
| this.tokenizeFromWord('NEXT') || | ||
| this.tokenizeFromWord('FIRST'); | ||
| tokenizeFromWord(name, word = name) { | ||
| word = this.regexEscape(word); | ||
| const matcher = /^\w+$/.test(word) ? new RegExp(`^(${word})\\b`, 'ig') : new RegExp(`^(${word})`, 'ig'); | ||
| const match = matcher.exec(this.chunk); | ||
| if (!match) { | ||
| return 0; | ||
| } | ||
| this.token(name, match[1]); | ||
| return match[1].length; | ||
| } | ||
| dotToken() { | ||
| return this.tokenizeFromWord('DOT', '.'); | ||
| tokenizeFromList(name, list) { | ||
| let ret = 0; | ||
| for (let j = 0, len = list.length; j < len; j++) { | ||
| const entry = list[j]; | ||
| ret = this.tokenizeFromWord(name, entry); | ||
| if (ret > 0) { | ||
| break; | ||
| } | ||
| } | ||
| return ret; | ||
| } | ||
| operatorToken() { | ||
| return this.tokenizeFromList('OPERATOR', SQL_OPERATORS); | ||
| } | ||
| keywordToken() { | ||
| return this.tokenizeFromWord('SELECT') | ||
| || this.tokenizeFromWord('INSERT') | ||
| || this.tokenizeFromWord('INTO') | ||
| || this.tokenizeFromWord('DEFAULT') | ||
| || this.tokenizeFromWord('VALUES') | ||
| || this.tokenizeFromWord('DISTINCT') | ||
| || this.tokenizeFromWord('FROM') | ||
| || this.tokenizeFromWord('WHERE') | ||
| || this.tokenizeFromWord('GROUP') | ||
| || this.tokenizeFromWord('ORDER') | ||
| || this.tokenizeFromWord('BY') | ||
| || this.tokenizeFromWord('HAVING') | ||
| || this.tokenizeFromWord('LIMIT') | ||
| || this.tokenizeFromWord('JOIN') | ||
| || this.tokenizeFromWord('LEFT') | ||
| || this.tokenizeFromWord('RIGHT') | ||
| || this.tokenizeFromWord('INNER') | ||
| || this.tokenizeFromWord('OUTER') | ||
| || this.tokenizeFromWord('ON') | ||
| || this.tokenizeFromWord('AS') | ||
| || this.tokenizeFromWord('CASE') | ||
| || this.tokenizeFromWord('WHEN') | ||
| || this.tokenizeFromWord('THEN') | ||
| || this.tokenizeFromWord('ELSE') | ||
| || this.tokenizeFromWord('END') | ||
| || this.tokenizeFromWord('UNION') | ||
| || this.tokenizeFromWord('ALL') | ||
| || this.tokenizeFromWord('LIMIT') | ||
| || this.tokenizeFromWord('OFFSET') | ||
| || this.tokenizeFromWord('FETCH') | ||
| || this.tokenizeFromWord('ROW') | ||
| || this.tokenizeFromWord('ROWS') | ||
| || this.tokenizeFromWord('ONLY') | ||
| || this.tokenizeFromWord('NEXT') | ||
| || this.tokenizeFromWord('FIRST'); | ||
| } | ||
| mathToken() { | ||
| return this.tokenizeFromList('MATH', MATH) || this.tokenizeFromList('MATH_MULTI', MATH_MULTI); | ||
| } | ||
| dotToken() { | ||
| return this.tokenizeFromWord('DOT', '.'); | ||
| } | ||
| conditionalToken() { | ||
| return this.tokenizeFromList('CONDITIONAL', SQL_CONDITIONALS); | ||
| } | ||
| operatorToken() { | ||
| return this.tokenizeFromList('OPERATOR', SQL_OPERATORS); | ||
| } | ||
| betweenToken() { | ||
| return this.tokenizeFromList('BETWEEN', SQL_BETWEENS); | ||
| } | ||
| mathToken() { | ||
| return this.tokenizeFromList('MATH', MATH) || this.tokenizeFromList('MATH_MULTI', MATH_MULTI); | ||
| } | ||
| subSelectOpToken() { | ||
| return this.tokenizeFromList('SUB_SELECT_OP', SUB_SELECT_OP); | ||
| } | ||
| conditionalToken() { | ||
| return this.tokenizeFromList('CONDITIONAL', SQL_CONDITIONALS); | ||
| } | ||
| subSelectUnaryOpToken() { | ||
| return this.tokenizeFromList('SUB_SELECT_UNARY_OP', SUB_SELECT_UNARY_OP); | ||
| } | ||
| betweenToken() { | ||
| return this.tokenizeFromList('BETWEEN', SQL_BETWEENS); | ||
| } | ||
| functionToken() { | ||
| return this.tokenizeFromList('FUNCTION', SQL_FUNCTIONS); | ||
| } | ||
| subSelectOpToken() { | ||
| return this.tokenizeFromList('SUB_SELECT_OP', SUB_SELECT_OP); | ||
| } | ||
| sortOrderToken() { | ||
| return this.tokenizeFromList('DIRECTION', SQL_SORT_ORDERS); | ||
| } | ||
| subSelectUnaryOpToken() { | ||
| return this.tokenizeFromList('SUB_SELECT_UNARY_OP', SUB_SELECT_UNARY_OP); | ||
| } | ||
| booleanToken() { | ||
| return this.tokenizeFromList('BOOLEAN', BOOLEAN); | ||
| } | ||
| functionToken() { | ||
| return this.tokenizeFromList('FUNCTION', SQL_FUNCTIONS); | ||
| } | ||
| starToken() { | ||
| return this.tokenizeFromRegex('STAR', STAR); | ||
| } | ||
| sortOrderToken() { | ||
| return this.tokenizeFromList('DIRECTION', SQL_SORT_ORDERS); | ||
| } | ||
| seperatorToken() { | ||
| return this.tokenizeFromRegex('SEPARATOR', SEPARATOR); | ||
| } | ||
| booleanToken() { | ||
| return this.tokenizeFromList('BOOLEAN', BOOLEAN); | ||
| } | ||
| literalToken() { | ||
| return this.tokenizeFromRegex('LITERAL', LITERAL, 1, 0); | ||
| } | ||
| starToken() { | ||
| return this.tokenizeFromRegex('STAR', STAR); | ||
| } | ||
| numberToken() { | ||
| return this.tokenizeFromRegex('NUMBER', NUMBER); | ||
| } | ||
| seperatorToken() { | ||
| return this.tokenizeFromRegex('SEPARATOR', SEPARATOR); | ||
| } | ||
| parameterToken() { | ||
| return this.tokenizeFromRegex('PARAMETER', PARAMETER, 1, 0); | ||
| } | ||
| literalToken() { | ||
| return this.tokenizeFromRegex('LITERAL', LITERAL, 1, 0); | ||
| } | ||
| stringToken() { | ||
| return this.tokenizeFromStringRegex('STRING', STRING, 1, 0) || this.tokenizeFromRegex('DBLSTRING', DBLSTRING, 1, 0); | ||
| } | ||
| numberToken() { | ||
| return this.tokenizeFromRegex('NUMBER', NUMBER); | ||
| } | ||
| parensToken() { | ||
| return this.tokenizeFromRegex('LEFT_PAREN', /^\(/) || this.tokenizeFromRegex('RIGHT_PAREN', /^\)/); | ||
| } | ||
| parameterToken() { | ||
| return this.tokenizeFromRegex('PARAMETER', PARAMETER, 1, 0); | ||
| } | ||
| windowExtension() { | ||
| const match = /^\.(win):(length|time)/i.exec(this.chunk); | ||
| if (!match) { | ||
| return 0; | ||
| } | ||
| this.token('WINDOW', match[1]); | ||
| this.token('WINDOW_FUNCTION', match[2]); | ||
| return match[0].length; | ||
| } | ||
| stringToken() { | ||
| return this.tokenizeFromStringRegex('STRING', STRING, 1, 0) || this.tokenizeFromRegex('DBLSTRING', DBLSTRING, 1, 0); | ||
| } | ||
| whitespaceToken() { | ||
| const match = WHITESPACE.exec(this.chunk); | ||
| if (!match) { | ||
| return 0; | ||
| } | ||
| const partMatch = match[0]; | ||
| if (this.preserveWhitespace) { | ||
| this.token('WHITESPACE', partMatch); | ||
| } | ||
| const newlines = partMatch.match(/\n/g, ''); | ||
| this.currentLine += (newlines != null ? newlines.length : void 0) || 0; | ||
| return partMatch.length; | ||
| parensToken() { | ||
| return this.tokenizeFromRegex('LEFT_PAREN', /^\(/) || this.tokenizeFromRegex('RIGHT_PAREN', /^\)/); | ||
| } | ||
| windowExtension() { | ||
| const match = /^\.(win):(length|time)/i.exec(this.chunk); | ||
| if (!match) { | ||
| return 0; | ||
| } | ||
| this.token('WINDOW', match[1]); | ||
| this.token('WINDOW_FUNCTION', match[2]); | ||
| return match[0].length; | ||
| } | ||
| regexEscape(str) { | ||
| return str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); | ||
| whitespaceToken() { | ||
| const match = WHITESPACE.exec(this.chunk); | ||
| if (!match) { | ||
| return 0; | ||
| } | ||
| const partMatch = match[0]; | ||
| if (this.preserveWhitespace) { | ||
| this.token('WHITESPACE', partMatch); | ||
| } | ||
| const newlines = partMatch.match(/\n/g, ''); | ||
| this.currentLine += (newlines != null ? newlines.length : void 0) || 0; | ||
| return partMatch.length; | ||
| } | ||
| regexEscape(str) { | ||
| return str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); | ||
| } | ||
| } | ||
| exports.tokenize = function (sql, opts) { | ||
| return (new Lexer(sql, opts)).tokens; | ||
| return (new Lexer(sql, opts)).tokens; | ||
| }; |
+303
-311
| function indent(str) { | ||
| return ((function () { | ||
| const ref = str.split('\n'); | ||
| const results = []; | ||
| for (let i = 0, len = ref.length; i < len; i++) { | ||
| results.push(` ${ref[i]}`); | ||
| } | ||
| return results; | ||
| })()).join('\n'); | ||
| return ((function () { | ||
| const ref = str.split('\n'); | ||
| const results = []; | ||
| for (let i = 0, len = ref.length; i < len; i++) { | ||
| results.push(` ${ref[i]}`); | ||
| } | ||
| return results; | ||
| })()).join('\n'); | ||
| } | ||
| exports.Select = class Select { | ||
| constructor(fields, source, distinct = false, joins = [], unions = []) { | ||
| this.fields = fields; | ||
| this.source = source; | ||
| this.distinct = distinct; | ||
| this.joins = joins; | ||
| this.unions = unions; | ||
| this.order = null; | ||
| this.group = null; | ||
| this.where = null; | ||
| this.limit = null; | ||
| } | ||
| constructor(fields, source, distinct = false, joins = [], unions = []) { | ||
| this.fields = fields; | ||
| this.source = source; | ||
| this.distinct = distinct; | ||
| this.joins = joins; | ||
| this.unions = unions; | ||
| this.order = null; | ||
| this.group = null; | ||
| this.where = null; | ||
| this.limit = null; | ||
| } | ||
| toString() { | ||
| const ret = [`SELECT ${this.fields.join(', ')}`]; | ||
| ret.push(indent(`FROM ${this.source}`)); | ||
| for (let i = 0, len = this.joins.length; i < len; i++) { | ||
| ret.push(indent(this.joins[i].toString())); | ||
| } | ||
| if (this.where) { | ||
| ret.push(indent(this.where.toString())); | ||
| } | ||
| if (this.group) { | ||
| ret.push(indent(this.group.toString())); | ||
| } | ||
| if (this.order) { | ||
| ret.push(indent(this.order.toString())); | ||
| } | ||
| if (this.limit) { | ||
| ret.push(indent(this.limit.toString())); | ||
| } | ||
| for (let j = 0, len1 = this.unions.length; j < len1; j++) { | ||
| ret.push(this.unions[j].toString()); | ||
| } | ||
| return ret.join('\n'); | ||
| toString() { | ||
| const ret = [`SELECT ${this.fields.join(', ')}`]; | ||
| ret.push(indent(`FROM ${this.source}`)); | ||
| for (let i = 0, len = this.joins.length; i < len; i++) { | ||
| ret.push(indent(this.joins[i].toString())); | ||
| } | ||
| if (this.where) { | ||
| ret.push(indent(this.where.toString())); | ||
| } | ||
| if (this.group) { | ||
| ret.push(indent(this.group.toString())); | ||
| } | ||
| if (this.order) { | ||
| ret.push(indent(this.order.toString())); | ||
| } | ||
| if (this.limit) { | ||
| ret.push(indent(this.limit.toString())); | ||
| } | ||
| for (let j = 0, len1 = this.unions.length; j < len1; j++) { | ||
| ret.push(this.unions[j].toString()); | ||
| } | ||
| return ret.join('\n'); | ||
| } | ||
| }; | ||
| exports.SubSelect = class SubSelect { | ||
| constructor(select, name = null) { | ||
| this.select = select; | ||
| this.name = name; | ||
| } | ||
| constructor(select, name = null) { | ||
| this.select = select; | ||
| this.name = name; | ||
| } | ||
| toString() { | ||
| const ret = []; | ||
| ret.push('('); | ||
| ret.push(indent(this.select.toString())); | ||
| ret.push(this.name ? `) ${this.name.toString()}` : ')'); | ||
| return ret.join('\n'); | ||
| } | ||
| toString() { | ||
| const ret = []; | ||
| ret.push('('); | ||
| ret.push(indent(this.select.toString())); | ||
| ret.push(this.name ? `) ${this.name.toString()}` : ')'); | ||
| return ret.join('\n'); | ||
| } | ||
| }; | ||
| exports.Join = class Join { | ||
| constructor(right, conditions = null, side = null, mode = null) { | ||
| this.right = right; | ||
| this.conditions = conditions; | ||
| this.side = side; | ||
| this.mode = mode; | ||
| } | ||
| constructor(right, conditions = null, side = null, mode = null) { | ||
| this.right = right; | ||
| this.conditions = conditions; | ||
| this.side = side; | ||
| this.mode = mode; | ||
| } | ||
| toString() { | ||
| let ret = ''; | ||
| if (this.side != null) { | ||
| ret += `${this.side} `; | ||
| } | ||
| if (this.mode != null) { | ||
| ret += `${this.mode} `; | ||
| } | ||
| return ret + `JOIN ${this.right}\n` + indent(`ON ${this.conditions}`); | ||
| toString() { | ||
| let ret = ''; | ||
| if (this.side != null) { | ||
| ret += `${this.side} `; | ||
| } | ||
| if (this.mode != null) { | ||
| ret += `${this.mode} `; | ||
| } | ||
| return `${ret}JOIN ${this.right}\n${indent(`ON ${this.conditions}`)}`; | ||
| } | ||
| }; | ||
| exports.Union = class Union { | ||
| constructor(query, all1 = false) { | ||
| this.query = query; | ||
| this.all = all1; | ||
| } | ||
| constructor(query, all1 = false) { | ||
| this.query = query; | ||
| this.all = all1; | ||
| } | ||
| toString() { | ||
| const all = this.all ? ' ALL' : ''; | ||
| return `UNION${all}\n${this.query.toString()}`; | ||
| } | ||
| toString() { | ||
| const all = this.all ? ' ALL' : ''; | ||
| return `UNION${all}\n${this.query.toString()}`; | ||
| } | ||
| }; | ||
| exports.LiteralValue = class LiteralValue { | ||
| constructor(value1, value2 = null) { | ||
| this.value = value1; | ||
| this.value2 = value2; | ||
| if (this.value2) { | ||
| this.nested = true; | ||
| this.values = this.value.values; | ||
| this.values.push(this.value2); | ||
| } | ||
| else { | ||
| this.nested = false; | ||
| this.values = [this.value]; | ||
| } | ||
| constructor(value1, value2 = null) { | ||
| this.value = value1; | ||
| this.value2 = value2; | ||
| if (this.value2) { | ||
| this.nested = true; | ||
| this.values = this.value.values; | ||
| this.values.push(this.value2); | ||
| } else { | ||
| this.nested = false; | ||
| this.values = [this.value]; | ||
| } | ||
| } | ||
| // TODO: Backtick quotes only supports MySQL, Postgres uses double-quotes | ||
| toString(quote = true) { | ||
| if (quote) { | ||
| return `\`${this.values.join('`.`')}\``; | ||
| } | ||
| else { | ||
| return `${this.values.join('.')}`; | ||
| } | ||
| // TODO: Backtick quotes only supports MySQL, Postgres uses double-quotes | ||
| toString(quote = true) { | ||
| if (quote) { | ||
| return `\`${this.values.join('`.`')}\``; | ||
| } | ||
| return `${this.values.join('.')}`; | ||
| } | ||
| }; | ||
| exports.StringValue = class StringValue { | ||
| constructor(value1, quoteType = '\'\'') { | ||
| this.value = value1; | ||
| this.quoteType = quoteType; | ||
| } | ||
| constructor(value1, quoteType = '\'\'') { | ||
| this.value = value1; | ||
| this.quoteType = quoteType; | ||
| } | ||
| toString() { | ||
| const escaped = this.quoteType === '\'' ? this.value.replace(/(^|[^\\])'/g, '$1\'\'') : this.value; | ||
| return `${this.quoteType}${escaped}${this.quoteType}`; | ||
| } | ||
| toString() { | ||
| const escaped = this.quoteType === '\'' ? this.value.replace(/(^|[^\\])'/g, '$1\'\'') : this.value; | ||
| return `${this.quoteType}${escaped}${this.quoteType}`; | ||
| } | ||
| }; | ||
| exports.NumberValue = class NumberValue { | ||
| constructor(value) { | ||
| this.value = Number(value); | ||
| } | ||
| constructor(value) { | ||
| this.value = Number(value); | ||
| } | ||
| toString() { | ||
| return this.value.toString(); | ||
| } | ||
| toString() { | ||
| return this.value.toString(); | ||
| } | ||
| }; | ||
| exports.ListValue = class ListValue { | ||
| constructor(value1) { | ||
| this.value = value1; | ||
| } | ||
| constructor(value1) { | ||
| this.value = value1; | ||
| } | ||
| toString() { | ||
| return `(${this.value.join(', ')})`; | ||
| } | ||
| toString() { | ||
| return `(${this.value.join(', ')})`; | ||
| } | ||
| }; | ||
| exports.WhitepaceList = class WhitepaceList { | ||
| constructor(value1) { | ||
| this.value = value1; | ||
| } | ||
| constructor(value1) { | ||
| this.value = value1; | ||
| } | ||
| toString() { | ||
| // not backtick for literals | ||
| return this.value.map(function (value) { | ||
| if (value instanceof exports.LiteralValue) { | ||
| return value.toString(false); | ||
| } | ||
| else { | ||
| return value.toString(); | ||
| } | ||
| }).join(' '); | ||
| } | ||
| toString() { | ||
| // not backtick for literals | ||
| return this.value.map((value) => { | ||
| if (value instanceof exports.LiteralValue) { | ||
| return value.toString(false); | ||
| } | ||
| return value.toString(); | ||
| }).join(' '); | ||
| } | ||
| }; | ||
| exports.ParameterValue = class ParameterValue { | ||
| constructor(value) { | ||
| this.value = value; | ||
| this.index = parseInt(value.substr(1), 10) - 1; | ||
| } | ||
| constructor(origin) { | ||
| const PARAMETER = /^(((#{([a-z0-9_]+)})|(@([a-z0-9_]+))|(\$([a-z0-9_]+)))(:(number|float|string|date|boolean))?)/; | ||
| const matchs = origin.match(PARAMETER); | ||
| this.type = matchs[10]; | ||
| this.value = matchs[4] || matchs[6] || matchs[8]; | ||
| this.origin = origin; | ||
| this.parameter = true; | ||
| } | ||
| toString() { | ||
| return `$${this.value}`; | ||
| } | ||
| toString() { | ||
| return this.origin; | ||
| } | ||
| }; | ||
| exports.ArgumentListValue = class ArgumentListValue { | ||
| constructor(value1, distinct = false) { | ||
| this.value = value1; | ||
| this.distinct = distinct; | ||
| constructor(value1, distinct = false) { | ||
| this.value = value1; | ||
| this.distinct = distinct; | ||
| } | ||
| toString() { | ||
| if (this.distinct) { | ||
| return `DISTINCT ${this.value.join(', ')}`; | ||
| } | ||
| toString() { | ||
| if (this.distinct) { | ||
| return `DISTINCT ${this.value.join(', ')}`; | ||
| } | ||
| else { | ||
| return `${this.value.join(', ')}`; | ||
| } | ||
| } | ||
| return `${this.value.join(', ')}`; | ||
| } | ||
| }; | ||
| exports.BooleanValue = class LiteralValue { | ||
| constructor(value) { | ||
| this.value = (function () { | ||
| switch (value.toLowerCase()) { | ||
| case 'true': | ||
| return true; | ||
| case 'false': | ||
| return false; | ||
| default: | ||
| return null; | ||
| } | ||
| })(); | ||
| constructor(value) { | ||
| this.value = (function () { | ||
| switch (value.toLowerCase()) { | ||
| case 'true': | ||
| return true; | ||
| case 'false': | ||
| return false; | ||
| default: | ||
| return null; | ||
| } | ||
| }()); | ||
| } | ||
| toString() { | ||
| if (this.value != null) { | ||
| return this.value.toString().toUpperCase(); | ||
| } | ||
| toString() { | ||
| if (this.value != null) { | ||
| return this.value.toString().toUpperCase(); | ||
| } | ||
| else { | ||
| return 'NULL'; | ||
| } | ||
| } | ||
| return 'NULL'; | ||
| } | ||
| }; | ||
| exports.FunctionValue = class FunctionValue { | ||
| constructor(name, _arguments = null, udf = false) { | ||
| this.name = name; | ||
| this.arguments = _arguments; | ||
| this.udf = udf; | ||
| constructor(name, _arguments = null, udf = false) { | ||
| this.name = name; | ||
| this.arguments = _arguments; | ||
| this.udf = udf; | ||
| } | ||
| toString() { | ||
| if (this.arguments) { | ||
| return `${this.name.toUpperCase()}(${this.arguments.toString()})`; | ||
| } | ||
| toString() { | ||
| if (this.arguments) { | ||
| return `${this.name.toUpperCase()}(${this.arguments.toString()})`; | ||
| } | ||
| else { | ||
| return `${this.name.toUpperCase()}()`; | ||
| } | ||
| } | ||
| return `${this.name.toUpperCase()}()`; | ||
| } | ||
| }; | ||
| exports.Case = class Case { | ||
| constructor(whens, _else) { | ||
| this.whens = whens; | ||
| this.else = _else; | ||
| constructor(whens, _else) { | ||
| this.whens = whens; | ||
| this.else = _else; | ||
| } | ||
| toString() { | ||
| const whensStr = this.whens.map(w => w.toString()).join(' '); | ||
| if (this.else) { | ||
| return `CASE ${whensStr} ${this.else.toString()} END`; | ||
| } | ||
| toString() { | ||
| const whensStr = this.whens.map(function (w) { | ||
| return w.toString(); | ||
| }).join(' '); | ||
| if (this.else) { | ||
| return `CASE ${whensStr} ${this.else.toString()} END`; | ||
| } | ||
| else { | ||
| return `CASE ${whensStr} END`; | ||
| } | ||
| } | ||
| return `CASE ${whensStr} END`; | ||
| } | ||
| }; | ||
| exports.CaseWhen = class CaseWhen { | ||
| constructor(whenCondition, resCondition) { | ||
| this.whenCondition = whenCondition; | ||
| this.resCondition = resCondition; | ||
| } | ||
| constructor(whenCondition, resCondition) { | ||
| this.whenCondition = whenCondition; | ||
| this.resCondition = resCondition; | ||
| } | ||
| toString() { | ||
| return `WHEN ${this.whenCondition} THEN ${this.resCondition}`; | ||
| } | ||
| toString() { | ||
| return `WHEN ${this.whenCondition} THEN ${this.resCondition}`; | ||
| } | ||
| }; | ||
| exports.CaseElse = class CaseElse { | ||
| constructor(elseCondition) { | ||
| this.elseCondition = elseCondition; | ||
| } | ||
| constructor(elseCondition) { | ||
| this.elseCondition = elseCondition; | ||
| } | ||
| toString() { | ||
| return `ELSE ${this.elseCondition}`; | ||
| } | ||
| toString() { | ||
| return `ELSE ${this.elseCondition}`; | ||
| } | ||
| }; | ||
| exports.Order = class Order { | ||
| constructor(orderings, offset) { | ||
| this.orderings = orderings; | ||
| this.offset = offset; | ||
| } | ||
| constructor(orderings, offset) { | ||
| this.orderings = orderings; | ||
| this.offset = offset; | ||
| } | ||
| toString() { | ||
| return `ORDER BY ${this.orderings.join(', ')}` + (this.offset ? '\n' + this.offset.toString() : ''); | ||
| } | ||
| toString() { | ||
| return `ORDER BY ${this.orderings.join(', ')}${this.offset ? `\n${this.offset.toString()}` : ''}`; | ||
| } | ||
| }; | ||
| exports.OrderArgument = class OrderArgument { | ||
| constructor(value, direction = 'ASC') { | ||
| this.value = value; | ||
| this.direction = direction; | ||
| null; | ||
| } | ||
| constructor(value, direction = 'ASC') { | ||
| this.value = value; | ||
| this.direction = direction; | ||
| null; | ||
| } | ||
| toString() { | ||
| return `${this.value} ${this.direction}`; | ||
| } | ||
| toString() { | ||
| return `${this.value} ${this.direction}`; | ||
| } | ||
| }; | ||
| exports.Offset = class Offset { | ||
| constructor(row_count, limit) { | ||
| this.row_count = row_count; | ||
| this.limit = limit; | ||
| } | ||
| constructor(rowCount, limit) { | ||
| this.row_count = rowCount; | ||
| this.limit = limit; | ||
| } | ||
| toString() { | ||
| return `OFFSET ${this.row_count} ROWS` + (this.limit ? `\nFETCH NEXT ${this.limit} ROWS ONLY` : ''); | ||
| } | ||
| toString() { | ||
| return `OFFSET ${this.row_count} ROWS${this.limit ? `\nFETCH NEXT ${this.limit} ROWS ONLY` : ''}`; | ||
| } | ||
| }; | ||
| exports.Limit = class Limit { | ||
| constructor(value1, offset) { | ||
| this.value = value1; | ||
| this.offset = offset; | ||
| } | ||
| constructor(value1, offset) { | ||
| this.value = value1; | ||
| this.offset = offset; | ||
| } | ||
| toString() { | ||
| return `LIMIT ${this.value}` + (this.offset ? `\nOFFSET ${this.offset}` : ''); | ||
| } | ||
| toString() { | ||
| return `LIMIT ${this.value}${this.offset ? `\nOFFSET ${this.offset}` : ''}`; | ||
| } | ||
| }; | ||
| exports.Table = class Table { | ||
| constructor(name, alias = null, win = null, winFn = null, winArg = null) { | ||
| this.name = name; | ||
| this.alias = alias; | ||
| this.win = win; | ||
| this.winFn = winFn; | ||
| this.winArg = winArg; | ||
| constructor(name, alias = null, win = null, winFn = null, winArg = null) { | ||
| this.name = name; | ||
| this.alias = alias; | ||
| this.win = win; | ||
| this.winFn = winFn; | ||
| this.winArg = winArg; | ||
| } | ||
| toString() { | ||
| if (this.win) { | ||
| return `${this.name}.${this.win}:${this.winFn}(${this.winArg})`; | ||
| } | ||
| if (this.alias) { | ||
| return `${this.name} AS ${this.alias}`; | ||
| } | ||
| toString() { | ||
| if (this.win) { | ||
| return `${this.name}.${this.win}:${this.winFn}(${this.winArg})`; | ||
| } | ||
| else if (this.alias) { | ||
| return `${this.name} AS ${this.alias}`; | ||
| } | ||
| else { | ||
| return this.name.toString(); | ||
| } | ||
| } | ||
| return this.name.toString(); | ||
| } | ||
| }; | ||
| exports.Group = class Group { | ||
| constructor(fields) { | ||
| this.fields = fields; | ||
| this.having = null; | ||
| } | ||
| constructor(fields) { | ||
| this.fields = fields; | ||
| this.having = null; | ||
| } | ||
| toString() { | ||
| const ret = [`GROUP BY ${this.fields.join(', ')}`]; | ||
| if (this.having) { | ||
| ret.push(this.having.toString()); | ||
| } | ||
| return ret.join('\n'); | ||
| toString() { | ||
| const ret = [`GROUP BY ${this.fields.join(', ')}`]; | ||
| if (this.having) { | ||
| ret.push(this.having.toString()); | ||
| } | ||
| return ret.join('\n'); | ||
| } | ||
| }; | ||
| exports.Where = class Where { | ||
| constructor(conditions) { | ||
| this.conditions = conditions; | ||
| } | ||
| constructor(conditions) { | ||
| this.conditions = conditions; | ||
| } | ||
| toString() { | ||
| return `WHERE ${this.conditions}`; | ||
| } | ||
| toString() { | ||
| return `WHERE ${this.conditions}`; | ||
| } | ||
| }; | ||
| exports.Having = class Having { | ||
| constructor(conditions) { | ||
| this.conditions = conditions; | ||
| } | ||
| constructor(conditions) { | ||
| this.conditions = conditions; | ||
| } | ||
| toString() { | ||
| return `HAVING ${this.conditions}`; | ||
| } | ||
| toString() { | ||
| return `HAVING ${this.conditions}`; | ||
| } | ||
| }; | ||
| exports.Op = class Op { | ||
| constructor(operation, left, right) { | ||
| this.operation = operation; | ||
| this.left = left; | ||
| this.group = false; | ||
| this.right = right; | ||
| } | ||
| constructor(operation, left, right) { | ||
| this.operation = operation; | ||
| this.left = left; | ||
| this.group = false; | ||
| this.right = right; | ||
| } | ||
| toString() { | ||
| const l = this.group ? '(' : ''; | ||
| const r = this.group ? ')' : ''; | ||
| return `${l}${this.left} ${this.operation.toUpperCase()} ${this.right}${r}`; | ||
| } | ||
| toString() { | ||
| const l = this.group ? '(' : ''; | ||
| const r = this.group ? ')' : ''; | ||
| return `${l}${this.left} ${this.operation.toUpperCase()} ${this.right}${r}`; | ||
| } | ||
| }; | ||
| exports.UnaryOp = class UnaryOp { | ||
| constructor(operator, operand) { | ||
| this.operator = operator; | ||
| this.operand = operand; | ||
| } | ||
| constructor(operator, operand) { | ||
| this.operator = operator; | ||
| this.operand = operand; | ||
| } | ||
| toString() { | ||
| return `(${this.operator.toUpperCase()} ${this.operand})`; | ||
| } | ||
| toString() { | ||
| return `(${this.operator.toUpperCase()} ${this.operand})`; | ||
| } | ||
| }; | ||
| exports.BetweenOp = class BetweenOp { | ||
| constructor(value) { | ||
| this.value = value; | ||
| } | ||
| constructor(value) { | ||
| this.value = value; | ||
| } | ||
| toString() { | ||
| return `${this.value.join(' AND ')}`; | ||
| } | ||
| toString() { | ||
| return `${this.value.join(' AND ')}`; | ||
| } | ||
| }; | ||
| exports.Field = class Field { | ||
| constructor(field, name = null) { | ||
| this.field = field; | ||
| this.name = name; | ||
| constructor(field, name = null) { | ||
| this.field = field; | ||
| this.name = name; | ||
| } | ||
| toString() { | ||
| if (this.name) { | ||
| return `${this.field} AS ${this.name}`; | ||
| } | ||
| toString() { | ||
| if (this.name) { | ||
| return `${this.field} AS ${this.name}`; | ||
| } | ||
| else { | ||
| return this.field.toString(); | ||
| } | ||
| } | ||
| return this.field.toString(); | ||
| } | ||
| }; | ||
| exports.Star = class Star { | ||
| toString() { | ||
| return '*'; | ||
| } | ||
| toString() { | ||
| return '*'; | ||
| } | ||
| }; |
+15
-14
@@ -1,17 +0,18 @@ | ||
| const parser = require('./compiled_parser').parser; | ||
| const { parser } = require('./compiled_parser'); | ||
| const nodes = require('./nodes'); | ||
| parser.lexer = { | ||
| lex : function () { | ||
| let tag; | ||
| [tag, this.yytext, this.yylineno] = this.tokens[this.pos++] || ['']; | ||
| return tag; | ||
| }, | ||
| setInput : function (tokens) { | ||
| this.tokens = tokens; | ||
| return this.pos = 0; | ||
| }, | ||
| upcomingInput: function () { | ||
| return ''; | ||
| } | ||
| lex() { | ||
| let tag; | ||
| [tag, this.yytext, this.yylineno] = this.tokens[this.pos] || ['']; | ||
| this.pos += 1; | ||
| return tag; | ||
| }, | ||
| setInput(tokens) { | ||
| this.tokens = tokens; | ||
| return this.pos = 0; | ||
| }, | ||
| upcomingInput() { | ||
| return ''; | ||
| }, | ||
| }; | ||
@@ -24,3 +25,3 @@ | ||
| exports.parse = function (str) { | ||
| return parser.parse(str); | ||
| return parser.parse(str); | ||
| }; |
@@ -6,3 +6,3 @@ exports.lexer = require('./lexer'); | ||
| exports.parse = function (sql) { | ||
| return exports.parser.parse(exports.lexer.tokenize(sql)); | ||
| return exports.parser.parse(exports.lexer.tokenize(sql)); | ||
| }; |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
14
7.69%4355
0.23%224
11.44%4
-20%264611
-1.12%9
28.57%