diff --git a/pegjs/athena.pegjs b/pegjs/athena.pegjs index ed9f8192..bcc04019 100644 --- a/pegjs/athena.pegjs +++ b/pegjs/athena.pegjs @@ -439,12 +439,28 @@ create_column_definition } collate_expr - = KW_COLLATE __ ca:ident_name { + = KW_COLLATE __ ca:ident_name __ s:KW_ASSIGIN_EQUAL __ t:ident { return { type: 'collate', - value: ca, + keyword: 'collate', + collate: { + name: ca, + symbol: s, + value: t + } } } + / KW_COLLATE __ s:KW_ASSIGIN_EQUAL? __ ca:ident { + return { + type: 'collate', + keyword: 'collate', + collate: { + name: ca, + symbol: s, + } + } + } + column_format = k:'COLUMN_FORMAT'i __ f:('FIXED'i / 'DYNAMIC'i / 'DEFAULT'i) { return { diff --git a/pegjs/bigquery.pegjs b/pegjs/bigquery.pegjs index 6ddb6b88..af797825 100644 --- a/pegjs/bigquery.pegjs +++ b/pegjs/bigquery.pegjs @@ -1159,11 +1159,25 @@ keyword_comment } collate_expr - = KW_COLLATE __ s:KW_ASSIGIN_EQUAL? __ ca:ident_name { + = KW_COLLATE __ ca:ident_name __ s:KW_ASSIGIN_EQUAL __ t:ident { return { type: 'collate', - symbol: s, - value: ca, + keyword: 'collate', + collate: { + name: ca, + symbol: s, + value: t + } + } + } + / KW_COLLATE __ s:KW_ASSIGIN_EQUAL? __ ca:ident { + return { + type: 'collate', + keyword: 'collate', + collate: { + name: ca, + symbol: s, + } } } diff --git a/pegjs/db2.pegjs b/pegjs/db2.pegjs index 519c4354..ea544fab 100644 --- a/pegjs/db2.pegjs +++ b/pegjs/db2.pegjs @@ -438,10 +438,25 @@ create_column_definition } collate_expr - = KW_COLLATE __ ca:ident_name { + = KW_COLLATE __ ca:ident_name __ s:KW_ASSIGIN_EQUAL __ t:ident { return { type: 'collate', - value: ca, + keyword: 'collate', + collate: { + name: ca, + symbol: s, + value: t + } + } + } + / KW_COLLATE __ s:KW_ASSIGIN_EQUAL? __ ca:ident { + return { + type: 'collate', + keyword: 'collate', + collate: { + name: ca, + symbol: s, + } } } column_format @@ -1811,14 +1826,13 @@ unary_operator = '!' / '-' / '+' / '~' column_ref - = tbl:(ident __ DOT __)? col:column __ a:((DOUBLE_ARROW / SINGLE_ARROW) __ (literal_string / literal_numeric))+ __ ca:collate_expr? { + = tbl:(ident __ DOT __)? col:column __ a:((DOUBLE_ARROW / SINGLE_ARROW) __ (literal_string / literal_numeric))+ { const tableName = tbl && tbl[0] || null columnList.add(`select::${tableName}::${col}`); return { type: 'column_ref', table: tableName, column: col, - collate: ca, arrows: a.map(item => item[0]), properties: a.map(item => item[2]) }; diff --git a/pegjs/flinksql.pegjs b/pegjs/flinksql.pegjs index 9b33ed1a..9088f76a 100644 --- a/pegjs/flinksql.pegjs +++ b/pegjs/flinksql.pegjs @@ -892,11 +892,25 @@ create_column_definition collate_expr - = KW_COLLATE __ ca:ident { - // => { type: 'collate'; value: ident; } + = KW_COLLATE __ ca:ident_name __ s:KW_ASSIGIN_EQUAL __ t:ident { return { type: 'collate', - value: ca, + keyword: 'collate', + collate: { + name: ca, + symbol: s, + value: t + } + } + } + / KW_COLLATE __ s:KW_ASSIGIN_EQUAL? __ ca:ident { + return { + type: 'collate', + keyword: 'collate', + collate: { + name: ca, + symbol: s, + } } } column_format diff --git a/pegjs/hive.pegjs b/pegjs/hive.pegjs index bda0a29c..12d1edbb 100644 --- a/pegjs/hive.pegjs +++ b/pegjs/hive.pegjs @@ -439,10 +439,25 @@ create_column_definition } collate_expr - = KW_COLLATE __ ca:ident_name { + = KW_COLLATE __ ca:ident_name __ s:KW_ASSIGIN_EQUAL __ t:ident { return { type: 'collate', - value: ca, + keyword: 'collate', + collate: { + name: ca, + symbol: s, + value: t + } + } + } + / KW_COLLATE __ s:KW_ASSIGIN_EQUAL? __ ca:ident { + return { + type: 'collate', + keyword: 'collate', + collate: { + name: ca, + symbol: s, + } } } column_format diff --git a/pegjs/mariadb.pegjs b/pegjs/mariadb.pegjs index e2c2076b..69a9a696 100644 --- a/pegjs/mariadb.pegjs +++ b/pegjs/mariadb.pegjs @@ -811,11 +811,25 @@ create_trigger_stmt } collate_expr - = KW_COLLATE __ s:KW_ASSIGIN_EQUAL? __ ca:ident_name { + = KW_COLLATE __ ca:ident_name __ s:KW_ASSIGIN_EQUAL __ t:ident { return { type: 'collate', - symbol: s, - value: ca, + keyword: 'collate', + collate: { + name: ca, + symbol: s, + value: t + } + } + } + / KW_COLLATE __ s:KW_ASSIGIN_EQUAL? __ ca:ident { + return { + type: 'collate', + keyword: 'collate', + collate: { + name: ca, + symbol: s, + } } } @@ -1880,6 +1894,7 @@ select_stmt_nake g:group_by_clause? __ h:having_clause? __ o:order_by_clause? __ + ce:collate_expr? __ l:limit_clause? __ lr: locking_read? __ win:window_clause? __ @@ -1903,6 +1918,7 @@ select_stmt_nake groupby: g, having: h, orderby: o, + collate: ce, limit: l, locking_read: lr && lr, window: win, @@ -2205,8 +2221,7 @@ on_clause = KW_ON __ e:or_and_expr { return e; } where_clause - = KW_WHERE __ e:or_and_where_expr __ ca:collate_expr? { - if (ca) e.suffix = [ca] + = KW_WHERE __ e:or_and_where_expr { return e; } @@ -2719,9 +2734,8 @@ in_op / KW_IN like_op_right - = op:like_op __ right:(literal / param / comparison_expr ) __ ca:(__ collate_expr)? __ es:escape_op? { + = op:like_op __ right:(literal / param / comparison_expr ) __ es:escape_op? { if (es) right.escape = es - if (ca) right.suffix = { collate: ca[1] } return { op: op, right: right }; } @@ -3202,12 +3216,11 @@ trim_func_clause func_call = extract_func / trim_func_clause - / 'convert'i __ LPAREN __ l:convert_args __ RPAREN __ ca:collate_expr? { + / 'convert'i __ LPAREN __ l:convert_args __ RPAREN { return { type: 'function', name: { name: [{ type: 'origin', value: 'convert' }] }, args: l, - collate: ca, }; } / name:scalar_func __ LPAREN __ l:expr_list? __ RPAREN __ bc:over_partition? { @@ -3247,7 +3260,7 @@ scalar_func / KW_SYSTEM_USER cast_expr - = c:KW_CAST __ LPAREN __ e:expr __ KW_AS __ ch:character_string_type __ cs:create_option_character_set_kw __ v:ident_without_kw_type __ RPAREN __ ca:collate_expr? { + = c:KW_CAST __ LPAREN __ e:expr __ KW_AS __ ch:character_string_type __ cs:create_option_character_set_kw __ v:ident_without_kw_type __ RPAREN { const { dataType, length } = ch let dataTypeStr = dataType if (length !== undefined) dataTypeStr = `${dataTypeStr}(${length})` @@ -3260,7 +3273,6 @@ cast_expr dataType: dataTypeStr, suffix: [{ type: 'origin', value: cs }, v], }, - collate: ca, }; } / c:KW_CAST __ LPAREN __ e:expr __ KW_AS __ t:data_type __ RPAREN { diff --git a/pegjs/mysql.pegjs b/pegjs/mysql.pegjs index ee0afe92..1436784c 100644 --- a/pegjs/mysql.pegjs +++ b/pegjs/mysql.pegjs @@ -1010,13 +1010,28 @@ create_trigger_stmt } collate_expr - = KW_COLLATE __ s:KW_ASSIGIN_EQUAL? __ ca:ident_name { + = KW_COLLATE __ ca:ident_name __ s:KW_ASSIGIN_EQUAL __ t:ident { return { type: 'collate', - symbol: s, - value: ca, + keyword: 'collate', + collate: { + name: ca, + symbol: s, + value: t + } + } + } + / KW_COLLATE __ s:KW_ASSIGIN_EQUAL? __ ca:ident { + return { + type: 'collate', + keyword: 'collate', + collate: { + name: ca, + symbol: s, + } } } + column_format = k:'COLUMN_FORMAT'i __ f:('FIXED'i / 'DYNAMIC'i / 'DEFAULT'i) { return { @@ -2130,8 +2145,9 @@ select_stmt_nake g:group_by_clause? __ h:having_clause? __ o:order_by_clause? __ + ce:collate_expr? __ l:limit_clause? __ - lr: locking_read? __ + lr:locking_read? __ win:window_clause? __ li:into_clause? { if ((ci && fi) || (ci && li) || (fi && li) || (ci && fi && li)) { @@ -2156,6 +2172,7 @@ select_stmt_nake limit: l, locking_read: lr && lr, window: win, + collate: ce, ...getLocationObject(), }; } @@ -2458,8 +2475,7 @@ on_clause = KW_ON __ e:or_and_expr { return e; } where_clause - = KW_WHERE __ e:or_and_where_expr __ ca:collate_expr? { - if (ca) e.suffix = [ca] + = KW_WHERE __ e:or_and_where_expr { return e; } @@ -2992,9 +3008,8 @@ regexp_op_right } like_op_right - = op:like_op __ right:(literal / param / comparison_expr ) __ ca:(__ collate_expr)? __ es:escape_op? { + = op:like_op __ right:(literal / param / comparison_expr ) __ es:escape_op? { if (es) right.escape = es - if (ca) right.suffix = { collate: ca[1] } return { op: op, right: right }; } @@ -3062,14 +3077,13 @@ primary } column_ref - = tbl:(ident __ DOT __)? col:column __ a:((DOUBLE_ARROW / SINGLE_ARROW) __ (literal_string / literal_numeric))+ __ ca:collate_expr? { + = tbl:(ident __ DOT __)? col:column __ a:((DOUBLE_ARROW / SINGLE_ARROW) __ literal)+ { const tableName = tbl && tbl[0] || null columnList.add(`select::${tableName}::${col}`); return { type: 'column_ref', table: tableName, column: col, - collate: ca, arrows: a.map(item => item[0]), properties: a.map(item => item[2]), ...getLocationObject(), @@ -3487,12 +3501,11 @@ trim_func_clause func_call = extract_func / trim_func_clause - / 'convert'i __ LPAREN __ l:convert_args __ RPAREN __ ca:collate_expr? { + / 'convert'i __ LPAREN __ l:convert_args __ RPAREN { return { type: 'function', name: { name: [{ type: 'origin', value: 'convert' }] }, args: l, - collate: ca, }; } / name:scalar_func __ LPAREN __ l:expr_list? __ RPAREN __ bc:over_partition? { @@ -3532,7 +3545,7 @@ scalar_func / KW_SYSTEM_USER cast_expr - = c:KW_CAST __ LPAREN __ e:expr __ KW_AS __ ch:character_string_type __ cs:create_option_character_set_kw __ v:ident_without_kw_type __ RPAREN __ ca:collate_expr? { + = c:KW_CAST __ LPAREN __ e:expr __ KW_AS __ ch:character_string_type __ cs:create_option_character_set_kw __ v:ident_without_kw_type __ RPAREN { const { dataType, length } = ch let dataTypeStr = dataType if (length !== undefined) dataTypeStr = `${dataTypeStr}(${length})` @@ -3545,7 +3558,6 @@ cast_expr dataType: dataTypeStr, suffix: [{ type: 'origin', value: cs }, v], }, - collate: ca, }; } / c:KW_CAST __ LPAREN __ e:expr __ KW_AS __ t:data_type __ RPAREN { diff --git a/pegjs/noql.pegjs b/pegjs/noql.pegjs index 9248f379..410c41f0 100644 --- a/pegjs/noql.pegjs +++ b/pegjs/noql.pegjs @@ -1220,12 +1220,25 @@ column_constraint } collate_expr - = KW_COLLATE __ s:KW_ASSIGIN_EQUAL? __ ca:ident { - // => { type: 'collate'; symbol: '=' | null; value: ident; } + = KW_COLLATE __ ca:ident_name __ s:KW_ASSIGIN_EQUAL __ t:ident { return { type: 'collate', - symbol: s, - value: ca, + keyword: 'collate', + collate: { + name: ca, + symbol: s, + value: t + } + } + } + / KW_COLLATE __ s:KW_ASSIGIN_EQUAL? __ ca:ident { + return { + type: 'collate', + keyword: 'collate', + collate: { + name: ca, + symbol: s, + } } } column_format diff --git a/pegjs/postgresql.pegjs b/pegjs/postgresql.pegjs index c01bb084..3ab9cceb 100644 --- a/pegjs/postgresql.pegjs +++ b/pegjs/postgresql.pegjs @@ -1352,12 +1352,27 @@ column_constraint } collate_expr - = KW_COLLATE __ s:KW_ASSIGIN_EQUAL? __ ca:ident { - // => { type: 'collate'; symbol: '=' | null; value: ident; } + = KW_COLLATE __ ca:ident_type __ s:KW_ASSIGIN_EQUAL __ t:ident_type { + // => { type: 'collate'; keyword: 'collate'; collate: { symbol: '=' ; name: ident_type; value: ident_type; }} return { type: 'collate', - symbol: s, - value: ca, + keyword: 'collate', + collate: { + name: ca, + symbol: s, + value: t + } + } + } + / KW_COLLATE __ s:KW_ASSIGIN_EQUAL? __ ca:ident_type { + // => { type: 'collate'; keyword: 'collate'; collate: { symbol: '=' | null ; name: ident_type; }} + return { + type: 'collate', + keyword: 'collate', + collate: { + name: ca, + symbol: s, + } } } column_format diff --git a/pegjs/redshift.pegjs b/pegjs/redshift.pegjs index 07f0b3fb..53861bac 100644 --- a/pegjs/redshift.pegjs +++ b/pegjs/redshift.pegjs @@ -1232,12 +1232,25 @@ column_constraint } collate_expr - = KW_COLLATE __ s:KW_ASSIGIN_EQUAL? __ ca:ident { - // => { type: 'collate'; symbol: '=' | null; value: ident; } + = KW_COLLATE __ ca:ident_name __ s:KW_ASSIGIN_EQUAL __ t:ident { return { type: 'collate', - symbol: s, - value: ca, + keyword: 'collate', + collate: { + name: ca, + symbol: s, + value: t + } + } + } + / KW_COLLATE __ s:KW_ASSIGIN_EQUAL? __ ca:ident { + return { + type: 'collate', + keyword: 'collate', + collate: { + name: ca, + symbol: s, + } } } column_format diff --git a/pegjs/snowflake.pegjs b/pegjs/snowflake.pegjs index c6a4979c..1260e2a3 100644 --- a/pegjs/snowflake.pegjs +++ b/pegjs/snowflake.pegjs @@ -894,7 +894,6 @@ column_definition_opt return { reference_definition: re } } / t:create_option_character_set_kw __ s:KW_ASSIGIN_EQUAL? __ v:ident_without_kw_type { - // => { character_set: collate_expr } return { character_set: { type: t, value: v, symbol: s }} } @@ -970,12 +969,25 @@ column_constraint } collate_expr - = KW_COLLATE __ s:KW_ASSIGIN_EQUAL? __ ca:ident { - // => { type: 'collate'; symbol: '=' | null; value: ident; } + = KW_COLLATE __ ca:ident_name __ s:KW_ASSIGIN_EQUAL __ t:ident { return { type: 'collate', - symbol: s, - value: ca, + keyword: 'collate', + collate: { + name: ca, + symbol: s, + value: t + } + } + } + / KW_COLLATE __ s:KW_ASSIGIN_EQUAL? __ ca:ident { + return { + type: 'collate', + keyword: 'collate', + collate: { + name: ca, + symbol: s, + } } } column_format diff --git a/pegjs/sqlite.pegjs b/pegjs/sqlite.pegjs index f72885a3..f4d22390 100644 --- a/pegjs/sqlite.pegjs +++ b/pegjs/sqlite.pegjs @@ -561,11 +561,25 @@ create_column_definition } collate_expr - = KW_COLLATE __ s:KW_ASSIGIN_EQUAL? __ ca:ident_name { + = KW_COLLATE __ ca:ident_name __ s:KW_ASSIGIN_EQUAL __ t:ident { return { type: 'collate', - symbol: s, - value: ca, + keyword: 'collate', + collate: { + name: ca, + symbol: s, + value: t + } + } + } + / KW_COLLATE __ s:KW_ASSIGIN_EQUAL? __ ca:ident { + return { + type: 'collate', + keyword: 'collate', + collate: { + name: ca, + symbol: s, + } } } column_format @@ -2144,14 +2158,13 @@ unary_operator = '!' / '-' / '+' / '~' column_ref - = tbl:(ident __ DOT __)? col:column __ a:((DOUBLE_ARROW / SINGLE_ARROW) __ (literal_string / literal_numeric))+ __ ca:collate_expr? { + = tbl:(ident __ DOT __)? col:column __ a:((DOUBLE_ARROW / SINGLE_ARROW) __ (literal_string / literal_numeric))+ { const tableName = tbl && tbl[0] || null columnList.add(`select::${tableName}::${col}`); return { type: 'column_ref', table: tableName, column: col, - collate: ca, arrows: a.map(item => item[0]), properties: a.map(item => item[2]) }; diff --git a/pegjs/transactsql.pegjs b/pegjs/transactsql.pegjs index 320c58ca..bf31c6e8 100644 --- a/pegjs/transactsql.pegjs +++ b/pegjs/transactsql.pegjs @@ -555,11 +555,25 @@ identity_stmt } collate_expr - = KW_COLLATE __ s:KW_ASSIGIN_EQUAL? __ ca:ident_name { + = KW_COLLATE __ ca:ident_name __ s:KW_ASSIGIN_EQUAL __ t:ident { return { type: 'collate', - symbol: s, - value: ca, + keyword: 'collate', + collate: { + name: ca, + symbol: s, + value: t + } + } + } + / KW_COLLATE __ s:KW_ASSIGIN_EQUAL? __ ca:ident { + return { + type: 'collate', + keyword: 'collate', + collate: { + name: ca, + symbol: s, + } } } column_format diff --git a/pegjs/trino.pegjs b/pegjs/trino.pegjs index 06e0c25f..532b47cf 100644 --- a/pegjs/trino.pegjs +++ b/pegjs/trino.pegjs @@ -981,12 +981,25 @@ column_constraint } collate_expr - = KW_COLLATE __ s:KW_ASSIGIN_EQUAL? __ ca:ident { - // => { type: 'collate'; symbol: '=' | null; value: ident; } + = KW_COLLATE __ ca:ident_name __ s:KW_ASSIGIN_EQUAL __ t:ident { return { type: 'collate', - symbol: s, - value: ca, + keyword: 'collate', + collate: { + name: ca, + symbol: s, + value: t + } + } + } + / KW_COLLATE __ s:KW_ASSIGIN_EQUAL? __ ca:ident { + return { + type: 'collate', + keyword: 'collate', + collate: { + name: ca, + symbol: s, + } } } column_format diff --git a/src/binary.js b/src/binary.js index e510f459..62481900 100644 --- a/src/binary.js +++ b/src/binary.js @@ -1,5 +1,5 @@ import { exprToSQL } from './expr' -import { commonTypeValue, hasVal, toUpper } from './util' +import { hasVal, toUpper } from './util' function binaryToSQL(expr) { let operator = expr.operator || expr.op @@ -27,17 +27,7 @@ function binaryToSQL(expr) { const leftPart = Array.isArray(expr.left) ? expr.left.map(exprToSQL).join(', ') : exprToSQL(expr.left) const str = [leftPart, operator, rstr, toUpper(escape.type), exprToSQL(escape.value)].filter(hasVal).join(' ') const result = [expr.parentheses ? `(${str})` : str] - const { suffix } = expr - if (!suffix) return result.join(' ') - for (const suffixItem of suffix) { - const { type } = suffixItem - switch (type) { - case 'collate': - result.push(commonTypeValue(suffixItem).join(' ')) - break - } - } - return result.filter(hasVal).join(' ') + return result.join(' ') } export { diff --git a/src/collate.js b/src/collate.js new file mode 100644 index 00000000..ccd17197 --- /dev/null +++ b/src/collate.js @@ -0,0 +1,17 @@ +import { exprToSQL } from './expr' +import { hasVal, literalToSQL, toUpper } from './util' + +function collateToSQL(stmt) { + if (!stmt) return + const { keyword, collate: { name, symbol, value } } = stmt + const result = [toUpper(keyword)] + if (!value) result.push(symbol) + result.push(literalToSQL(name)) + if (value) result.push(symbol) + result.push(exprToSQL(value)) + return result.filter(hasVal).join(' ') +} + +export { + collateToSQL, +} diff --git a/src/column.js b/src/column.js index d90c5084..016c9fb1 100644 --- a/src/column.js +++ b/src/column.js @@ -35,7 +35,7 @@ function arrayIndexToSQL(arrayIndexList) { } function columnRefToSQL(expr) { const { - array_index, arrows = [], as, collate, column, db, isDual, notations = [], schema, table, parentheses, properties, + array_index, arrows = [], as, column, db, isDual, notations = [], schema, table, parentheses, properties, suffix, order_by, subFields = [], } = expr let str = column === '*' ? '*' : columnOffsetToSQL(column, isDual) @@ -54,7 +54,6 @@ function columnRefToSQL(expr) { commonOptionConnector('AS', exprToSQL, as), arrows.map((arrow, index) => commonOptionConnector(arrow, literalToSQL, properties[index])).join(' '), ] - if (collate) result.push(commonTypeValue(collate).join(' ')) result.push(toUpper(suffix)) result.push(toUpper(order_by)) const sql = result.filter(hasVal).join(' ') @@ -109,7 +108,7 @@ function columnOption(definition) { columnOpt.push(constraintDefinitionToSQL(check)) columnOpt.push(autoIncrementToSQL(autoIncrement), toUpper(primaryKey), toUpper(uniqueKey), commentToSQL(comment)) columnOpt.push(...commonTypeValue(characterSet)) - if (database !== 'sqlite') columnOpt.push(...commonTypeValue(collate)) + if (database !== 'sqlite') columnOpt.push(exprToSQL(collate)) columnOpt.push(...commonTypeValue(columnFormat)) columnOpt.push(...commonTypeValue(storage)) columnOpt.push(...columnReferenceDefinitionToSQL(referenceDefinition)) @@ -122,7 +121,7 @@ function columnOrderToSQL(columnOrder) { columnExpr.collate = null const result = [ exprToSQL(columnExpr), - commonOptionConnector(collate && collate.type, identifierToSql, collate && collate.value), + exprToSQL(collate), opclass, toUpper(order_by), toUpper(nulls), diff --git a/src/command.js b/src/command.js index cf8bd2f7..d6ca676e 100644 --- a/src/command.js +++ b/src/command.js @@ -1,6 +1,6 @@ import { columnDataType, columnRefToSQL } from './column' import { createDefinitionToSQL } from './create' -import { commonTypeValue, identifierToSql, hasVal, toUpper, literalToSQL } from './util' +import { identifierToSql, hasVal, toUpper, literalToSQL } from './util' import { exprToSQL } from './expr' import { tablesToSQL, tableToSQL } from './tables' import astToSQL from './sql' @@ -138,7 +138,7 @@ function declareToSQL(stmt) { const declareInfo = [[at, name].filter(hasVal).join(''), toUpper(as), toUpper(constant)] switch (keyword) { case 'variable': - declareInfo.push(columnDataType(datatype), ...commonTypeValue(dec.collate), toUpper(not_null)) + declareInfo.push(columnDataType(datatype), exprToSQL(dec.collate), toUpper(not_null)) if (definition) declareInfo.push(toUpper(definition.keyword), exprToSQL(definition.value)) break case 'cursor': diff --git a/src/create.js b/src/create.js index 42b896c3..38fff420 100644 --- a/src/create.js +++ b/src/create.js @@ -270,7 +270,7 @@ function createDomainToSQL(stmt) { const definitionType = definition.type switch (definitionType) { case 'collate': - definitionSQL.push(commonTypeValue(definition).join(' ')) + definitionSQL.push(exprToSQL(definition)) break case 'default': definitionSQL.push(toUpper(definitionType), exprToSQL(definition.value)) diff --git a/src/expr.js b/src/expr.js index 3db2a3d8..74262878 100644 --- a/src/expr.js +++ b/src/expr.js @@ -4,6 +4,7 @@ import { aggrToSQL } from './aggregation' import { assignToSQL } from './assign' import { binaryToSQL } from './binary' import { caseToSQL } from './case' +import { collateToSQL } from './collate' import { columnDefinitionToSQL, columnRefToSQL, fullTextSearchToSQL } from './column' import { anyValueFuncToSQL, castToSQL, extractFunToSQL, flattenFunToSQL, funcToSQL, jsonObjectArgToSQL, lambdaToSQL, tablefuncFunToSQL } from './func' import { intervalToSQL } from './interval' @@ -25,6 +26,7 @@ const exprToSQLConvertFn = { binary_expr : binaryToSQL, case : caseToSQL, cast : castToSQL, + collate : collateToSQL, column_ref : columnRefToSQL, column_definition : columnDefinitionToSQL, datatype : dataTypeToSQL, @@ -47,9 +49,8 @@ const exprToSQLConvertFn = { } function varToSQL(expr) { - const { prefix = '@', name, members, keyword, quoted, suffix } = expr + const { prefix = '@', name, members, quoted, suffix } = expr const val = [] - if (keyword) val.push(keyword) const varName = members && members.length > 0 ? `${name}.${members.join('.')}` : name let result = `${prefix || ''}${varName}` if (suffix) result += suffix diff --git a/src/func.js b/src/func.js index 680dd47f..8f7a6011 100644 --- a/src/func.js +++ b/src/func.js @@ -1,6 +1,6 @@ import { arrayIndexToSQL } from './column' import { exprToSQL } from './expr' -import { commonOptionConnector, commonTypeValue, hasVal, identifierToSql, literalToSQL, toUpper } from './util' +import { commonOptionConnector, hasVal, identifierToSql, literalToSQL, toUpper } from './util' import { overToSQL } from './over' function anyValueFuncToSQL(stmt) { @@ -24,7 +24,7 @@ function arrayDimensionToSymbol(target) { } function castToSQL(expr) { - const { arrows = [], collate, target, expr: expression, keyword, symbol, as: alias, parentheses: outParentheses, properties = [] } = expr + const { arrows = [], target, expr: expression, keyword, symbol, as: alias, parentheses: outParentheses, properties = [] } = expr const { length, dataType, parentheses, quoted, scale, suffix: dataTypeSuffix, expr: targetExpr } = target let str = targetExpr ? exprToSQL(targetExpr) : '' if (length != null) str = scale ? `${length}, ${scale}` : length @@ -40,7 +40,6 @@ function castToSQL(expr) { } suffix += arrows.map((arrow, index) => commonOptionConnector(arrow, literalToSQL, properties[index])).join(' ') if (alias) suffix += ` AS ${identifierToSql(alias)}` - if (collate) suffix += ` ${commonTypeValue(collate).join(' ')}` const arrayDimension = arrayDimensionToSymbol(target) const result = [prefix, symbolChar, quoted, dataType, quoted, arrayDimension, str, suffix].filter(hasVal).join('') return outParentheses ? `(${result})` : result @@ -76,8 +75,7 @@ function flattenFunToSQL(stmt) { } function funcToSQL(expr) { - const { args, array_index, name, args_parentheses, parentheses, over, collate, suffix } = expr - const collateStr = commonTypeValue(collate).join(' ') + const { args, array_index, name, args_parentheses, parentheses, over, suffix } = expr const overStr = overToSQL(over) const suffixStr = exprToSQL(suffix) const funcName = [literalToSQL(name.schema), name.name.map(literalToSQL).join('.')].filter(hasVal).join('.') @@ -90,7 +88,7 @@ function funcToSQL(expr) { if (args_parentheses !== false) str.push(')') str.push(arrayIndexToSQL(array_index)) str = [str.join(''), suffixStr].filter(hasVal).join(' ') - return [parentheses ? `(${str})` : str, collateStr, overStr].filter(hasVal).join(' ') + return [parentheses ? `(${str})` : str, overStr].filter(hasVal).join(' ') } function tablefuncFunToSQL(expr) { diff --git a/src/select.js b/src/select.js index f5b5412e..f5bda96e 100644 --- a/src/select.js +++ b/src/select.js @@ -4,6 +4,7 @@ import { limitToSQL } from './limit' import { withToSQL } from './with' import { tablesToSQL } from './tables' import { hasVal, commonOptionConnector, connector, identifierToSql, topToSQL, toUpper } from './util' +import { collateToSQL } from './collate' function distinctToSQL(distinct) { if (!distinct) return @@ -57,6 +58,7 @@ function selectToSQL(stmt) { const { as_struct_val: asStructVal, columns, + collate, distinct, for: forXml, from, @@ -96,6 +98,7 @@ function selectToSQL(stmt) { clauses.push(commonOptionConnector('QUALIFY', exprToSQL, qualify)) clauses.push(commonOptionConnector('WINDOW', exprToSQL, windowInfo)) clauses.push(orderOrPartitionByToSQL(orderby, 'order by')) + clauses.push(collateToSQL(collate)) clauses.push(limitToSQL(limit)) clauses.push(toUpper(lockingRead)) if (position === 'end') clauses.push(intoSQL) diff --git a/src/update.js b/src/update.js index 8f3766a3..a851a785 100644 --- a/src/update.js +++ b/src/update.js @@ -17,7 +17,7 @@ function setToSQL(sets) { const { value } = set for (const key in set) { if (key === 'value' || key === 'keyword') continue - if (Object.prototype.hasOwnProperty.call(set, key)) column[key] = set[key] + column[key] = set[key] } const str = columnRefToSQL(column) const setItem = [str] diff --git a/src/util.js b/src/util.js index fdc231e1..5864a5a8 100644 --- a/src/util.js +++ b/src/util.js @@ -1,4 +1,5 @@ import { columnToSQL, columnRefToSQL, columnOrderToSQL } from './column' +import { collateToSQL } from './collate' // const escapeMap = { // '\0' : '\\0', @@ -251,7 +252,7 @@ function literalToSQL(literal) { if (suffix) { if (typeof suffix === 'string') result.push(suffix) if (typeof suffix === 'object') { - if (suffix.collate) result.push([toUpper(suffix.collate.type), suffix.collate.symbol, toUpper(suffix.collate.value)].filter(hasVal).join(' ')) + if (suffix.collate) result.push(collateToSQL(suffix.collate)) else result.push(literalToSQL(suffix)) } } diff --git a/test/ast.spec.js b/test/ast.spec.js index c35635bc..ee364fca 100644 --- a/test/ast.spec.js +++ b/test/ast.spec.js @@ -202,7 +202,7 @@ describe('AST', () => { ], 'char casts': [ `SELECT CAST(test AS CHAR CHARACTER SET utf8mb4) COLLATE utf8mb4_bin;`, - 'SELECT CAST(`test` AS CHAR CHARACTER SET utf8mb4) COLLATE UTF8MB4_BIN' + 'SELECT CAST(`test` AS CHAR CHARACTER SET utf8mb4) COLLATE utf8mb4_bin' ], 'time casts': [ "SELECT CAST('12:31:41.8418443' AS TIME(6)) AS `time`;", diff --git a/test/create.spec.js b/test/create.spec.js index 9a6881a9..cc56280a 100644 --- a/test/create.spec.js +++ b/test/create.spec.js @@ -55,7 +55,7 @@ describe('create', () => { compCode varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '我是comment', compCode2 varchar(255) CHARACTER SET = utf8 COLLATE = utf8_general_ci NOT NULL COMMENT '我是comment' ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '我是comment' ROW_FORMAT = Dynamic`)) - .to.equal("CREATE TABLE `comp` (`id` INT(11) NOT NULL AUTO_INCREMENT, `compCode` VARCHAR(255) NOT NULL COMMENT '我是comment' CHARACTER SET utf8 COLLATE UTF8_GENERAL_CI, `compCode2` VARCHAR(255) NOT NULL COMMENT '我是comment' CHARACTER SET = utf8 COLLATE = UTF8_GENERAL_CI) ENGINE = INNODB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '我是comment' ROW_FORMAT = DYNAMIC"); + .to.equal("CREATE TABLE `comp` (`id` INT(11) NOT NULL AUTO_INCREMENT, `compCode` VARCHAR(255) NOT NULL COMMENT '我是comment' CHARACTER SET utf8 COLLATE utf8_general_ci, `compCode2` VARCHAR(255) NOT NULL COMMENT '我是comment' CHARACTER SET = utf8 COLLATE = utf8_general_ci) ENGINE = INNODB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '我是comment' ROW_FORMAT = DYNAMIC"); }) it('should support create temporary table', () => { @@ -91,27 +91,27 @@ describe('create', () => { it('should support create table with collate', () => { expect(getParsedSql(`create temporary table if not exists dbname.tableName (id INT(11) auto_increment primary key comment "id column" collate utf8_bin, name varchar(128) unique key comment "user name" collate utf8_bin) ENGINE = MEMORY`)) - .to.equal('CREATE TEMPORARY TABLE IF NOT EXISTS `dbname`.`tableName` (`id` INT(11) AUTO_INCREMENT PRIMARY KEY COMMENT "id column" COLLATE UTF8_BIN, `name` VARCHAR(128) UNIQUE KEY COMMENT "user name" COLLATE UTF8_BIN) ENGINE = MEMORY'); + .to.equal('CREATE TEMPORARY TABLE IF NOT EXISTS `dbname`.`tableName` (`id` INT(11) AUTO_INCREMENT PRIMARY KEY COMMENT "id column" COLLATE utf8_bin, `name` VARCHAR(128) UNIQUE KEY COMMENT "user name" COLLATE utf8_bin) ENGINE = MEMORY'); }) it('should support create table with collate', () => { expect(getParsedSql(`create temporary table if not exists dbname.tableName (id INT(11) auto_increment primary key comment "id column" collate utf8_bin, name varchar(128) unique key comment "user name" collate utf8_bin) ENGINE = MEMORY`)) - .to.equal('CREATE TEMPORARY TABLE IF NOT EXISTS `dbname`.`tableName` (`id` INT(11) AUTO_INCREMENT PRIMARY KEY COMMENT "id column" COLLATE UTF8_BIN, `name` VARCHAR(128) UNIQUE KEY COMMENT "user name" COLLATE UTF8_BIN) ENGINE = MEMORY'); + .to.equal('CREATE TEMPORARY TABLE IF NOT EXISTS `dbname`.`tableName` (`id` INT(11) AUTO_INCREMENT PRIMARY KEY COMMENT "id column" COLLATE utf8_bin, `name` VARCHAR(128) UNIQUE KEY COMMENT "user name" COLLATE utf8_bin) ENGINE = MEMORY'); }) it('should support create table with column_format', () => { expect(getParsedSql(`create temporary table if not exists dbname.tableName (id INT(11) auto_increment primary key comment "id column" collate utf8_bin column_format fixed, name varchar(128) unique key comment "user name" collate utf8_bin column_format dynamic) ENGINE = MEMORY`)) - .to.equal('CREATE TEMPORARY TABLE IF NOT EXISTS `dbname`.`tableName` (`id` INT(11) AUTO_INCREMENT PRIMARY KEY COMMENT "id column" COLLATE UTF8_BIN COLUMN_FORMAT FIXED, `name` VARCHAR(128) UNIQUE KEY COMMENT "user name" COLLATE UTF8_BIN COLUMN_FORMAT DYNAMIC) ENGINE = MEMORY'); + .to.equal('CREATE TEMPORARY TABLE IF NOT EXISTS `dbname`.`tableName` (`id` INT(11) AUTO_INCREMENT PRIMARY KEY COMMENT "id column" COLLATE utf8_bin COLUMN_FORMAT FIXED, `name` VARCHAR(128) UNIQUE KEY COMMENT "user name" COLLATE utf8_bin COLUMN_FORMAT DYNAMIC) ENGINE = MEMORY'); }) it('should support create table with storage', () => { expect(getParsedSql(`create temporary table if not exists dbname.tableName (id INT(11) auto_increment primary key comment "id column" collate utf8_bin column_format fixed storage disk, name varchar(128) unique key comment "user name" collate utf8_bin column_format dynamic storage memory) ENGINE = MEMORY`)) - .to.equal('CREATE TEMPORARY TABLE IF NOT EXISTS `dbname`.`tableName` (`id` INT(11) AUTO_INCREMENT PRIMARY KEY COMMENT "id column" COLLATE UTF8_BIN COLUMN_FORMAT FIXED STORAGE DISK, `name` VARCHAR(128) UNIQUE KEY COMMENT "user name" COLLATE UTF8_BIN COLUMN_FORMAT DYNAMIC STORAGE MEMORY) ENGINE = MEMORY'); + .to.equal('CREATE TEMPORARY TABLE IF NOT EXISTS `dbname`.`tableName` (`id` INT(11) AUTO_INCREMENT PRIMARY KEY COMMENT "id column" COLLATE utf8_bin COLUMN_FORMAT FIXED STORAGE DISK, `name` VARCHAR(128) UNIQUE KEY COMMENT "user name" COLLATE utf8_bin COLUMN_FORMAT DYNAMIC STORAGE MEMORY) ENGINE = MEMORY'); }) it('should support create table with reference definition', () => { expect(getParsedSql(`create temporary table if not exists dbname.tableName (id INT(11) auto_increment primary key comment "id column" collate utf8_bin column_format fixed storage disk references rdb.rta(id) match full on delete cascade on update restrict, name varchar(128) unique key comment "user name" collate utf8_bin column_format dynamic storage memory references rdb.rtb(name) match simple on delete set null on update set default) ENGINE = MEMORY`)) - .to.equal('CREATE TEMPORARY TABLE IF NOT EXISTS `dbname`.`tableName` (`id` INT(11) AUTO_INCREMENT PRIMARY KEY COMMENT "id column" COLLATE UTF8_BIN COLUMN_FORMAT FIXED STORAGE DISK REFERENCES `rdb`.`rta` (`id`) MATCH FULL ON DELETE CASCADE ON UPDATE RESTRICT, `name` VARCHAR(128) UNIQUE KEY COMMENT "user name" COLLATE UTF8_BIN COLUMN_FORMAT DYNAMIC STORAGE MEMORY REFERENCES `rdb`.`rtb` (`name`) MATCH SIMPLE ON DELETE SET NULL ON UPDATE SET DEFAULT) ENGINE = MEMORY'); + .to.equal('CREATE TEMPORARY TABLE IF NOT EXISTS `dbname`.`tableName` (`id` INT(11) AUTO_INCREMENT PRIMARY KEY COMMENT "id column" COLLATE utf8_bin COLUMN_FORMAT FIXED STORAGE DISK REFERENCES `rdb`.`rta` (`id`) MATCH FULL ON DELETE CASCADE ON UPDATE RESTRICT, `name` VARCHAR(128) UNIQUE KEY COMMENT "user name" COLLATE utf8_bin COLUMN_FORMAT DYNAMIC STORAGE MEMORY REFERENCES `rdb`.`rtb` (`name`) MATCH SIMPLE ON DELETE SET NULL ON UPDATE SET DEFAULT) ENGINE = MEMORY'); }) it('should support create table with column check', () => { @@ -133,7 +133,7 @@ describe('create', () => { it('should support generated columns', () =>{ expect(getParsedSql(`CREATE TABLE contacts (id INT KEY, first_name VARCHAR(50) NOT NULL, last_name VARCHAR(50) NOT NULL, fullname varchar(101) CHARACTER SET latin1 COLLATE latin1_general_cs AS (CONCAT(first_name,' ',last_name)) STORED NOT NULL);`)) - .to.equal("CREATE TABLE `contacts` (`id` INT KEY, `first_name` VARCHAR(50) NOT NULL, `last_name` VARCHAR(50) NOT NULL, `fullname` VARCHAR(101) NOT NULL CHARACTER SET latin1 COLLATE LATIN1_GENERAL_CS AS (CONCAT(`first_name`, ' ', `last_name`)) STORED)"); + .to.equal("CREATE TABLE `contacts` (`id` INT KEY, `first_name` VARCHAR(50) NOT NULL, `last_name` VARCHAR(50) NOT NULL, `fullname` VARCHAR(101) NOT NULL CHARACTER SET latin1 COLLATE latin1_general_cs AS (CONCAT(`first_name`, ' ', `last_name`)) STORED)"); expect(getParsedSql(`CREATE TABLE contacts (id INT KEY, first_name VARCHAR(50) NOT NULL, last_name VARCHAR(50) NOT NULL, fullname varchar(101) AS (CONCAT(first_name,' ',last_name)) STORED);`)) .to.equal("CREATE TABLE `contacts` (`id` INT KEY, `first_name` VARCHAR(50) NOT NULL, `last_name` VARCHAR(50) NOT NULL, `fullname` VARCHAR(101) AS (CONCAT(`first_name`, ' ', `last_name`)) STORED)"); @@ -144,7 +144,7 @@ describe('create', () => { it('should support generated columns with generated always', () =>{ expect(getParsedSql(`CREATE TABLE contacts (id INT KEY, first_name VARCHAR(50) NOT NULL, last_name VARCHAR(50) NOT NULL, fullname varchar(101) CHARACTER SET latin1 COLLATE latin1_general_cs GENERATED ALWAYS AS (CONCAT(first_name,' ',last_name)) STORED NOT NULL);`)) - .to.equal("CREATE TABLE `contacts` (`id` INT KEY, `first_name` VARCHAR(50) NOT NULL, `last_name` VARCHAR(50) NOT NULL, `fullname` VARCHAR(101) NOT NULL CHARACTER SET latin1 COLLATE LATIN1_GENERAL_CS GENERATED ALWAYS AS (CONCAT(`first_name`, ' ', `last_name`)) STORED)"); + .to.equal("CREATE TABLE `contacts` (`id` INT KEY, `first_name` VARCHAR(50) NOT NULL, `last_name` VARCHAR(50) NOT NULL, `fullname` VARCHAR(101) NOT NULL CHARACTER SET latin1 COLLATE latin1_general_cs GENERATED ALWAYS AS (CONCAT(`first_name`, ' ', `last_name`)) STORED)"); expect(getParsedSql(`CREATE TABLE contacts (id INT KEY, first_name VARCHAR(50) NOT NULL, last_name VARCHAR(50) NOT NULL, fullname varchar(101) GENERATED ALWAYS AS (CONCAT(first_name,' ',last_name)) VIRTUAL);`)) .to.equal("CREATE TABLE `contacts` (`id` INT KEY, `first_name` VARCHAR(50) NOT NULL, `last_name` VARCHAR(50) NOT NULL, `fullname` VARCHAR(101) GENERATED ALWAYS AS (CONCAT(`first_name`, ' ', `last_name`)) VIRTUAL)"); diff --git a/test/db2.spec.js b/test/db2.spec.js new file mode 100644 index 00000000..f0e7a1e8 --- /dev/null +++ b/test/db2.spec.js @@ -0,0 +1,17 @@ +const { expect } = require('chai'); +const Parser = require('../src/parser').default + +describe('db2', () => { + const parser = new Parser(); + const DEFAULT_OPT = { database: 'db2' } + + function getParsedSql(sql, opt = DEFAULT_OPT) { + const ast = parser.astify(sql, opt); + return parser.sqlify(ast, opt); + } + + it('should support basic db2', () => { + let sql = 'SELECT id as test FROM customers' + expect(getParsedSql(sql)).to.be.equal('SELECT id AS test FROM customers') + }) +}) \ No newline at end of file diff --git a/test/flink.spec.js b/test/flink.spec.js index 112114ed..a9ee596b 100644 --- a/test/flink.spec.js +++ b/test/flink.spec.js @@ -405,11 +405,11 @@ describe('Flink', () => { json_object( 'risk-tag' value risk_tag, 'abc' VALUE (10 * 2), - 'user-agent' value JSON_OBJECT('city' VALUE 'New York', 'postalCode' VALUE '10001') + 'user-agent' value JSON_OBJECT('city' VALUE 'New York' on null null, 'postalCode' VALUE '10001' on null absent) ) as eventDetail from check_risk );`, - "SELECT `name`, `eventTime`, `eventDetail` FROM (SELECT concat('AK中文信息') AS `name`, CAST(`event_time` AS VARCHAR) AS `eventTime`, JSON_OBJECT('risk-tag' VALUE `risk_tag`, 'abc' VALUE (10 * 2), 'user-agent' VALUE JSON_OBJECT('city' VALUE 'New York', 'postalCode' VALUE '10001')) AS `eventDetail` FROM `check_risk`)" + "SELECT `name`, `eventTime`, `eventDetail` FROM (SELECT concat('AK中文信息') AS `name`, CAST(`event_time` AS VARCHAR) AS `eventTime`, JSON_OBJECT('risk-tag' VALUE `risk_tag`, 'abc' VALUE (10 * 2), 'user-agent' VALUE JSON_OBJECT('city' VALUE 'New York' ON NULL NULL, 'postalCode' VALUE '10001' ON NULL ABSENT)) AS `eventDetail` FROM `check_risk`)" ] }, ]; diff --git a/test/mysql-mariadb.spec.js b/test/mysql-mariadb.spec.js index a2a5fb42..f12c8dac 100644 --- a/test/mysql-mariadb.spec.js +++ b/test/mysql-mariadb.spec.js @@ -65,7 +65,7 @@ describe('mysql', () => { title: 'regexp_like with collate', sql: [ "SELECT REGEXP_LIKE('CamelCase', 'CAMELCASE' COLLATE utf8mb4_0900_as_cs);", - "SELECT REGEXP_LIKE('CamelCase', 'CAMELCASE' COLLATE UTF8MB4_0900_AS_CS)" + "SELECT REGEXP_LIKE('CamelCase', 'CAMELCASE' COLLATE utf8mb4_0900_as_cs)" ] }, { @@ -551,7 +551,7 @@ describe('mysql', () => { title: 'index column length', sql: [ 'CREATE TABLE `Translation` (`id` char(36) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,`en-GB` text,PRIMARY KEY (`id`),KEY `Translation_en-GB_btree_idx` (`en-GB`(768))) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci', - 'CREATE TABLE `Translation` (`id` CHAR(36) NOT NULL CHARACTER SET ascii COLLATE ASCII_BIN, `en-GB` TEXT, PRIMARY KEY (`id`), KEY Translation_en-GB_btree_idx (`en-GB` (768))) ENGINE = INNODB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci', + 'CREATE TABLE `Translation` (`id` CHAR(36) NOT NULL CHARACTER SET ascii COLLATE ascii_bin, `en-GB` TEXT, PRIMARY KEY (`id`), KEY Translation_en-GB_btree_idx (`en-GB` (768))) ENGINE = INNODB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci', ] }, { @@ -883,7 +883,7 @@ describe('mysql', () => { test WHERE name LIKE :pattern COLLATE utf8mb4_general_ci`, - 'SELECT * FROM `test` WHERE `name` LIKE :pattern COLLATE UTF8MB4_GENERAL_CI' + 'SELECT * FROM `test` WHERE `name` LIKE :pattern COLLATE utf8mb4_general_ci' ] }, { @@ -954,7 +954,7 @@ describe('mysql', () => { title: 'collate in where clause include parentheses', sql: [ "SELECT * FROM product WHERE (id = '1' OR id = '2') COLLATE utf8mb4_general_ci;", - "SELECT * FROM `product` WHERE (`id` = '1' OR `id` = '2') COLLATE UTF8MB4_GENERAL_CI" + "SELECT * FROM `product` WHERE (`id` = '1' OR `id` = '2') COLLATE utf8mb4_general_ci" ] }, { @@ -1056,6 +1056,20 @@ describe('mysql', () => { 'CREATE TABLE `Table` (`TableID` INTEGER PRIMARY KEY, `IsAbstract` BOOLEAN, `HasOpenColumns` BOOLEAN, `HasOpenRows` BOOLEAN, `HasOpenSheets` BOOLEAN, `IsNormalised` BOOLEAN, `IsFlat` BOOLEAN, `RowGUID` VARCHAR(16)) CHARACTER SET \'UTF8\'' ] }, + { + title: 'collate', + sql: [ + 'select * from test order by id COLLATE utf8mb4_unicode_ci', + 'SELECT * FROM `test` ORDER BY `id` ASC COLLATE utf8mb4_unicode_ci' + ] + }, + { + title: 'collate2', + sql: [ + 'select * from test where id COLLATE utf8mb4_unicode_ci = abc', + 'SELECT * FROM `test` WHERE `id` COLLATE utf8mb4_unicode_ci = abc' + ] + }, ] SQL_LIST.forEach(sqlInfo => { const { title, sql } = sqlInfo diff --git a/test/postgres.spec.js b/test/postgres.spec.js index 9d3acb70..0b5acf2c 100644 --- a/test/postgres.spec.js +++ b/test/postgres.spec.js @@ -1048,7 +1048,7 @@ describe('Postgres', () => { title: 'create domain with full definition', sql: [ 'CREATE DOMAIN public.year AS integer collate utf8mb4_bin default 0 CONSTRAINT year_check CHECK (((VALUE >= 1901) AND (VALUE <= 2155)));', - 'CREATE DOMAIN "public"."year" AS INTEGER COLLATE UTF8MB4_BIN DEFAULT 0 CONSTRAINT "year_check" CHECK (((VALUE >= 1901) AND (VALUE <= 2155)))', + 'CREATE DOMAIN "public"."year" AS INTEGER COLLATE utf8mb4_bin DEFAULT 0 CONSTRAINT "year_check" CHECK (((VALUE >= 1901) AND (VALUE <= 2155)))', ] }, { diff --git a/test/select.spec.js b/test/select.spec.js index 96f8076c..9a560d50 100644 --- a/test/select.spec.js +++ b/test/select.spec.js @@ -180,7 +180,7 @@ describe('select', () => { }); it('should parse json column query expressions with collate', () => { const ast = parser.astify("SELECT item.jsonCol->>'$.test.path' collate utf8mb4_unicode_ci from 'items'"); - expect(parser.sqlify(ast)).to.be.equal("SELECT `item`.`jsonCol` ->> '$.test.path' COLLATE UTF8MB4_UNICODE_CI FROM `items`") + expect(parser.sqlify(ast)).to.be.equal("SELECT `item`.`jsonCol` ->> '$.test.path' COLLATE utf8mb4_unicode_ci FROM `items`") }); @@ -445,6 +445,7 @@ describe('select', () => { groupby: null, having: null, orderby: null, + collate: null, limit: null, window: null, }, @@ -514,6 +515,7 @@ describe('select', () => { groupby: null, having: null, orderby: null, + collate: null, limit: null, window: null, }, @@ -686,6 +688,7 @@ describe('select', () => { groupby: null, having: null, orderby: null, + collate: null, limit: null, window: null, }, @@ -754,7 +757,7 @@ describe('select', () => { expect(getParsedSql(`SELECT CONVERT('test', CHAR(10) CHARACTER SET utf8mb4);`)) .to.equal("SELECT CONVERT('test', CHAR(10) CHARACTER SET utf8mb4)") expect(getParsedSql(`SELECT CONVERT('test' USING utf8mb4) COLLATE utf8mb4_bin;`)) - .to.equal("SELECT CONVERT('test' USING UTF8MB4) COLLATE UTF8MB4_BIN") + .to.equal("SELECT CONVERT('test' USING UTF8MB4) COLLATE utf8mb4_bin") expect(getParsedSql(`select TYPE,taxpayer_Type,CONVERT(tax_Amount, DECIMAL(12,2)) AS tax_amount,CAST(tax_currency AS DECIMAL(12,2)) tax_currency from rs_order_tax where billno="{{billno}}" and Business_Type="order";`)) .to.equal('SELECT `TYPE`, `taxpayer_Type`, CONVERT(`tax_Amount`, DECIMAL(12, 2)) AS `tax_amount`, CAST(`tax_currency` AS DECIMAL(12, 2)) AS `tax_currency` FROM `rs_order_tax` WHERE `billno` = "{{billno}}" AND `Business_Type` = "order"') expect(getParsedSql(`SELECT CONVERT('test', INT(11) unsigned);`)) diff --git a/test/util.spec.js b/test/util.spec.js index 4c40104b..77a26608 100644 --- a/test/util.spec.js +++ b/test/util.spec.js @@ -1,4 +1,5 @@ const { expect } = require('chai') +const Parser = require('../src/parser').default const { columnIdentifierToSql, createValueExpr, @@ -11,6 +12,8 @@ const { const { overToSQL } = require('../src/over') describe('util function test', () => { + const parser = new Parser() + it('should throw error when type is unkonwn', () => { expect(createValueExpr.bind(null, {})).to.throw('Cannot convert value "object" to SQL') }) @@ -45,6 +48,21 @@ describe('util function test', () => { it('should support columnIdentifierToSql without ident', () => { expect(columnIdentifierToSql()).to.be.undefined + setParserOpt({"database": "db2"}) + expect(columnIdentifierToSql("id")).to.be.equal('"id"') + }) + + it.only('should support trim query option', () => { + const opt = { + "database": "mysql", + "parseOptions": { + "includeLocations": false + }, + "trimQuery": false + } + const sql = "select id from tableName " + const ast = parser.astify(sql, opt) + expect(parser.sqlify(ast, opt)).to.be.equal('SELECT `id` FROM `tableName`') }) it('should sqlify backticks_quote_string', () => {