node-sql-parser
Version:
simple node sql parser
279 lines (277 loc) • 10.5 kB
JavaScript
(function (global, factory) {
if (typeof define === "function" && define.amd) {
define(["exports", "./binary", "./column", "./expr", "./insert", "./interval", "./util"], factory);
} else if (typeof exports !== "undefined") {
factory(exports, require("./binary"), require("./column"), require("./expr"), require("./insert"), require("./interval"), require("./util"));
} else {
var mod = {
exports: {}
};
factory(mod.exports, global.binary, global.column, global.expr, global.insert, global.interval, global.util);
global.tables = mod.exports;
}
})(typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : this, function (_exports, _binary, _column, _expr, _insert, _interval, _util) {
"use strict";
Object.defineProperty(_exports, "__esModule", {
value: true
});
_exports.operatorToSQL = operatorToSQL;
_exports.tableHintToSQL = tableHintToSQL;
_exports.tableOptionToSQL = tableOptionToSQL;
_exports.tableToSQL = tableToSQL;
_exports.tableTumbleToSQL = tableTumbleToSQL;
_exports.tablesToSQL = tablesToSQL;
_exports.unnestToSQL = unnestToSQL;
function unnestToSQL(unnestExpr) {
const {
type,
as,
expr,
with_offset: withOffset
} = unnestExpr;
const result = [`${(0, _util.toUpper)(type)}(${expr && (0, _expr.exprToSQL)(expr) || ''})`, (0, _util.commonOptionConnector)('AS', typeof as === 'string' ? _util.identifierToSql : _expr.exprToSQL, as), (0, _util.commonOptionConnector)((0, _util.toUpper)(withOffset && withOffset.keyword), _util.identifierToSql, withOffset && withOffset.as)];
return result.filter(_util.hasVal).join(' ');
}
function pivotOperatorToSQL(operator) {
const {
as,
column,
expr,
in_expr,
type
} = operator;
const result = [(0, _expr.exprToSQL)(expr), 'FOR', (0, _column.columnRefToSQL)(column), (0, _binary.binaryToSQL)(in_expr)];
const sql = [`${(0, _util.toUpper)(type)}(${result.join(' ')})`];
if (as) sql.push('AS', (0, _util.identifierToSql)(as));
return sql.join(' ');
}
function operatorToSQL(operator) {
if (!operator) return;
const {
type
} = operator;
switch (type) {
case 'pivot':
case 'unpivot':
return pivotOperatorToSQL(operator);
default:
return '';
}
}
function tableHintToSQL(tableHintExpr) {
if (!tableHintExpr) return;
const {
keyword,
expr,
index,
index_columns,
parentheses,
prefix
} = tableHintExpr;
const result = [];
switch (keyword.toLowerCase()) {
case 'forceseek':
result.push((0, _util.toUpper)(keyword), `(${(0, _util.identifierToSql)(index)}`, `(${index_columns.map(_expr.exprToSQL).filter(_util.hasVal).join(', ')}))`);
break;
case 'spatial_window_max_cells':
result.push((0, _util.toUpper)(keyword), '=', (0, _expr.exprToSQL)(expr));
break;
case 'index':
result.push((0, _util.toUpper)(prefix), (0, _util.toUpper)(keyword), parentheses ? `(${expr.map(indexItem => (0, _util.identifierToSql)(indexItem)).join(', ')})` : `= ${(0, _util.identifierToSql)(expr)}`);
break;
default:
result.push((0, _expr.exprToSQL)(expr));
}
return result.filter(_util.hasVal).join(' ');
}
function tableTumbleArgsToSQL(param, expr) {
const {
name,
symbol
} = param;
return [(0, _util.toUpper)(name), symbol, expr].filter(_util.hasVal).join(' ');
}
function tableTumbleToSQL(tumble) {
if (!tumble) return '';
const {
data: tableInfo,
timecol,
offset,
size
} = tumble;
const fullTableName = [(0, _util.identifierToSql)(tableInfo.expr.db), (0, _util.identifierToSql)(tableInfo.expr.schema), (0, _util.identifierToSql)(tableInfo.expr.table)].filter(_util.hasVal).join('.');
const timeColSQL = `DESCRIPTOR(${(0, _column.columnRefToSQL)(timecol.expr)})`;
const result = [`TABLE(TUMBLE(TABLE ${tableTumbleArgsToSQL(tableInfo, fullTableName)}`, tableTumbleArgsToSQL(timecol, timeColSQL)];
const sizeSQL = tableTumbleArgsToSQL(size, (0, _interval.intervalToSQL)(size.expr));
if (offset && offset.expr) result.push(sizeSQL, `${tableTumbleArgsToSQL(offset, (0, _interval.intervalToSQL)(offset.expr))}))`);else result.push(`${sizeSQL}))`);
return result.filter(_util.hasVal).join(', ');
}
function temporalTableOptionToSQL(stmt) {
const {
keyword
} = stmt;
const result = [];
switch (keyword) {
case 'as':
result.push('AS', 'OF', (0, _expr.exprToSQL)(stmt.of));
break;
case 'from_to':
result.push('FROM', (0, _expr.exprToSQL)(stmt.from), 'TO', (0, _expr.exprToSQL)(stmt.to));
break;
case 'between_and':
result.push('BETWEEN', (0, _expr.exprToSQL)(stmt.between), 'AND', (0, _expr.exprToSQL)(stmt.and));
break;
case 'contained':
result.push('CONTAINED', 'IN', (0, _expr.exprToSQL)(stmt.in));
break;
}
return result.filter(_util.hasVal).join(' ');
}
function temporalTableToSQL(stmt) {
if (!stmt) return;
const {
keyword,
expr
} = stmt;
return [(0, _util.toUpper)(keyword), temporalTableOptionToSQL(expr)].filter(_util.hasVal).join(' ');
}
function generateVirtualTable(stmt) {
const {
keyword,
type,
generators
} = stmt;
const generatorSQL = generators.map(generator => (0, _util.commonTypeValue)(generator).join(' ')).join(', ');
return `${(0, _util.toUpper)(keyword)}(${(0, _util.toUpper)(type)}(${generatorSQL}))`;
}
function tableToSQL(tableInfo) {
if ((0, _util.toUpper)(tableInfo.type) === 'UNNEST') return unnestToSQL(tableInfo);
const {
table,
db,
as,
expr,
operator,
prefix: prefixStr,
schema,
server,
suffix,
tablesample,
temporal_table,
table_hint,
surround = {}
} = tableInfo;
const serverName = (0, _util.identifierToSql)(server, false, surround.server);
const database = (0, _util.identifierToSql)(db, false, surround.db);
const schemaStr = (0, _util.identifierToSql)(schema, false, surround.schema);
let tableName = table && (0, _util.identifierToSql)(table, false, surround.table);
if (expr) {
const exprType = expr.type;
switch (exprType) {
case 'values':
const {
parentheses,
values,
prefix
} = expr;
const valueSQL = [parentheses && '(', '', parentheses && ')'];
let valuesExpr = (0, _insert.valuesToSQL)(values);
if (prefix) valuesExpr = valuesExpr.split('(').slice(1).map(val => `${(0, _util.toUpper)(prefix)}(${val}`).join('');
valueSQL[1] = `VALUES ${valuesExpr}`;
tableName = valueSQL.filter(_util.hasVal).join('');
break;
case 'tumble':
tableName = tableTumbleToSQL(expr);
break;
case 'generator':
tableName = generateVirtualTable(expr);
break;
default:
tableName = (0, _expr.exprToSQL)(expr);
}
}
tableName = [(0, _util.toUpper)(prefixStr), tableName, (0, _util.toUpper)(suffix)].filter(_util.hasVal).join(' ');
const str = [serverName, database, schemaStr, tableName].filter(_util.hasVal).join('.');
const result = [str];
if (tablesample) {
const tableSampleSQL = ['TABLESAMPLE', (0, _expr.exprToSQL)(tablesample.expr), (0, _util.literalToSQL)(tablesample.repeatable)].filter(_util.hasVal).join(' ');
result.push(tableSampleSQL);
}
result.push(temporalTableToSQL(temporal_table), (0, _util.commonOptionConnector)('AS', typeof as === 'string' ? _util.identifierToSql : _expr.exprToSQL, as), operatorToSQL(operator));
if (table_hint) result.push((0, _util.toUpper)(table_hint.keyword), `(${table_hint.expr.map(tableHintToSQL).filter(_util.hasVal).join(', ')})`);
const tableSQL = result.filter(_util.hasVal).join(' ');
return tableInfo.parentheses ? `(${tableSQL})` : tableSQL;
}
/**
* @param {Array} tables
* @return {string}
*/
function tablesToSQL(tables) {
if (!tables) return '';
if (!Array.isArray(tables)) {
const {
expr,
parentheses,
joins
} = tables;
const sql = tablesToSQL(expr);
if (parentheses) {
const leftParentheses = [];
const rightParentheses = [];
const parenthesesNumber = parentheses === true ? 1 : parentheses.length;
let i = 0;
while (i++ < parenthesesNumber) {
leftParentheses.push('(');
rightParentheses.push(')');
}
const joinsSQL = joins && joins.length > 0 ? tablesToSQL(['', ...joins]) : '';
return leftParentheses.join('') + sql + rightParentheses.join('') + joinsSQL;
}
return sql;
}
const baseTable = tables[0];
const clauses = [];
if (baseTable.type === 'dual') return 'DUAL';
clauses.push(tableToSQL(baseTable));
for (let i = 1; i < tables.length; ++i) {
const joinExpr = tables[i];
const {
on,
using,
join
} = joinExpr;
const str = [];
const isTables = Array.isArray(joinExpr) || Object.hasOwnProperty.call(joinExpr, 'joins');
str.push(join ? ` ${(0, _util.toUpper)(join)}` : ',');
str.push(isTables ? tablesToSQL(joinExpr) : tableToSQL(joinExpr));
str.push((0, _util.commonOptionConnector)('ON', _expr.exprToSQL, on));
if (using) str.push(`USING (${using.map(_util.literalToSQL).join(', ')})`);
clauses.push(str.filter(_util.hasVal).join(' '));
}
return clauses.filter(_util.hasVal).join('');
}
function tableOptionToSQL(tableOption) {
const {
keyword,
symbol,
value
} = tableOption;
const sql = [keyword.toUpperCase()];
if (symbol) sql.push(symbol);
let val = (0, _util.literalToSQL)(value);
switch (keyword) {
case 'partition by':
case 'default collate':
val = (0, _expr.exprToSQL)(value);
break;
case 'options':
val = `(${value.map(tableOptionItem => [tableOptionItem.keyword, tableOptionItem.symbol, (0, _expr.exprToSQL)(tableOptionItem.value)].join(' ')).join(', ')})`;
break;
case 'cluster by':
val = value.map(_expr.exprToSQL).join(', ');
break;
}
sql.push(val);
return sql.filter(_util.hasVal).join(' ');
}
});