UNPKG

node-sql-parser

Version:
456 lines (451 loc) 12.5 kB
(function (global, factory) { if (typeof define === "function" && define.amd) { define(["exports", "./column", "./collate"], factory); } else if (typeof exports !== "undefined") { factory(exports, require("./column"), require("./collate")); } else { var mod = { exports: {} }; factory(mod.exports, global.column, global.collate); global.util = mod.exports; } })(typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : this, function (_exports, _column, _collate) { "use strict"; Object.defineProperty(_exports, "__esModule", { value: true }); _exports.DEFAULT_OPT = void 0; _exports.arrayStructTypeToSQL = arrayStructTypeToSQL; _exports.autoIncrementToSQL = autoIncrementToSQL; _exports.columnIdentifierToSql = columnIdentifierToSql; _exports.columnOrderListToSQL = columnOrderListToSQL; _exports.commentToSQL = commentToSQL; _exports.commonKeywordArgsToSQL = commonKeywordArgsToSQL; _exports.commonOptionConnector = commonOptionConnector; _exports.commonTypeValue = commonTypeValue; _exports.connector = connector; _exports.createBinaryExpr = createBinaryExpr; _exports.createValueExpr = createValueExpr; _exports.dataTypeToSQL = dataTypeToSQL; _exports.escape = escape; _exports.getParserOpt = getParserOpt; _exports.hasVal = hasVal; _exports.identifierToSql = identifierToSql; _exports.literalToSQL = literalToSQL; _exports.onPartitionsToSQL = onPartitionsToSQL; _exports.replaceParams = replaceParams; _exports.returningToSQL = returningToSQL; _exports.setParserOpt = setParserOpt; _exports.toUpper = toUpper; _exports.topToSQL = topToSQL; _exports.triggerEventToSQL = triggerEventToSQL; // const escapeMap = { // '\0' : '\\0', // '\'' : '\\\'', // '"' : '\\"', // '\b' : '\\b', // '\n' : '\\n', // '\r' : '\\r', // '\t' : '\\t', // '\x1a' : '\\Z', // // '\\' : '\\\\', // } const DEFAULT_OPT = _exports.DEFAULT_OPT = { database: PARSER_NAME || 'mysql', type: 'table', trimQuery: true, parseOptions: { includeLocations: false } }; let parserOpt = DEFAULT_OPT; function commonOptionConnector(keyword, action, opt) { if (!opt) return; if (!keyword) return action(opt); return `${keyword.toUpperCase()} ${action(opt)}`; } function connector(keyword, str) { if (!str) return; return `${keyword.toUpperCase()} ${str}`; } /** * @param {(Array|boolean|string|number|null)} value * @return {Object} */ function createValueExpr(value) { const type = typeof value; if (Array.isArray(value)) return { type: 'expr_list', value: value.map(createValueExpr) }; if (value === null) return { type: 'null', value: null }; switch (type) { case 'boolean': return { type: 'bool', value }; case 'string': return { type: 'string', value }; case 'number': return { type: 'number', value }; default: throw new Error(`Cannot convert value "${type}" to SQL`); } } /** * @param operator * @param left * @param right * @return {Object} */ function createBinaryExpr(operator, left, right) { const expr = { operator, type: 'binary_expr' }; expr.left = left.type ? left : createValueExpr(left); if (operator === 'BETWEEN' || operator === 'NOT BETWEEN') { expr.right = { type: 'expr_list', value: [createValueExpr(right[0]), createValueExpr(right[1])] }; return expr; } expr.right = right.type ? right : createValueExpr(right); return expr; } /** * Replace param expressions * * @param {Object} ast - AST object * @param {Object} keys - Keys = parameter names, values = parameter values * @return {Object} - Newly created AST object */ function replaceParamsInner(ast, keys) { Object.keys(ast).filter(key => { const value = ast[key]; return Array.isArray(value) || typeof value === 'object' && value !== null; }).forEach(key => { const expr = ast[key]; if (!(typeof expr === 'object' && expr.type === 'param')) return replaceParamsInner(expr, keys); if (typeof keys[expr.value] === 'undefined') throw new Error(`no value for parameter :${expr.value} found`); ast[key] = createValueExpr(keys[expr.value]); return null; }); return ast; } function escape(str) { return str; // const res = [] // for (let i = 0, len = str.length; i < len; ++i) { // let char = str[i] // const escaped = escapeMap[char] // if (escaped) char = escaped // res.push(char) // } // return res.join('') } function getParserOpt() { return parserOpt; } function setParserOpt(opt) { parserOpt = opt; } function topToSQL(opt) { if (!opt) return; const { value, percent, parentheses } = opt; const val = parentheses ? `(${value})` : value; const prefix = `TOP ${val}`; if (!percent) return prefix; return `${prefix} ${percent.toUpperCase()}`; } function columnIdentifierToSql(ident) { const { database } = getParserOpt(); if (!ident) return; switch (database && database.toLowerCase()) { case 'db2': case 'postgresql': case 'redshift': case 'snowflake': case 'noql': case 'trino': case 'sqlite': return `"${ident}"`; case 'transactsql': return `[${ident}]`; case 'mysql': case 'mariadb': case 'bigquery': default: return `\`${ident}\``; } } function identifierToSql(ident, isDual) { const { database } = getParserOpt(); if (isDual === true) return `'${ident}'`; if (!ident) return; if (ident === '*') return ident; switch (database && database.toLowerCase()) { case 'mysql': case 'mariadb': return `\`${ident}\``; case 'postgresql': case 'redshift': case 'snowflake': case 'trino': case 'noql': case 'sqlite': return `"${ident}"`; case 'transactsql': return `[${ident}]`; case 'bigquery': case 'db2': return ident; default: return `\`${ident}\``; } } function toUpper(val) { if (!val) return; return val.toUpperCase(); } function hasVal(val) { return val; } function literalToSQL(literal) { if (!literal) return; let { prefix } = literal; const { type, parentheses, suffix, value } = literal; let str = typeof literal === 'object' ? value : literal; switch (type) { case 'backticks_quote_string': str = `\`${escape(value)}\``; break; case 'string': str = `'${escape(value)}'`; break; case 'regex_string': str = `r"${escape(value)}"`; break; case 'hex_string': str = `X'${escape(value)}'`; break; case 'full_hex_string': str = `0x${escape(value)}`; break; case 'natural_string': str = `N'${escape(value)}'`; break; case 'bit_string': str = `b'${escape(value)}'`; break; case 'double_quote_string': str = `"${escape(value)}"`; break; case 'single_quote_string': str = `'${value}'`; break; case 'boolean': case 'bool': str = value ? 'TRUE' : 'FALSE'; break; case 'null': str = 'NULL'; break; case 'star': str = '*'; break; case 'param': str = `${prefix || ':'}${value}`; prefix = null; break; case 'origin': str = value.toUpperCase(); break; case 'date': case 'datetime': case 'time': case 'timestamp': str = `${type.toUpperCase()} '${value}'`; break; case 'var_string': str = `N'${escape(value)}'`; break; case 'unicode_string': str = `U&'${escape(value)}'`; break; default: break; } const result = []; if (prefix) result.push(toUpper(prefix)); result.push(str); if (suffix) { if (typeof suffix === 'string') result.push(suffix); if (typeof suffix === 'object') { if (suffix.collate) result.push((0, _collate.collateToSQL)(suffix.collate));else result.push(literalToSQL(suffix)); } } str = result.join(' '); return parentheses ? `(${str})` : str; } function commonTypeValue(opt) { if (!opt) return []; const { type, symbol, value } = opt; return [type.toUpperCase(), symbol, typeof value === 'string' ? value.toUpperCase() : literalToSQL(value)].filter(hasVal); } function replaceParams(ast, params) { return replaceParamsInner(JSON.parse(JSON.stringify(ast)), params); } function onPartitionsToSQL(expr) { const { type, partitions } = expr; const result = [toUpper(type), `(${partitions.map(partition => { const { type: partitionType } = partition; if (!(partitionType === 'range')) return literalToSQL(partition); const { start, end, symbol } = partition; return `${literalToSQL(start)} ${toUpper(symbol)} ${literalToSQL(end)}`; }).join(', ')})`]; return result.join(' '); } function dataTypeToSQL(expr) { const { dataType, length, parentheses, scale, suffix } = expr; let str = ''; if (length != null) str = scale ? `${length}, ${scale}` : length; if (parentheses) str = `(${str})`; if (suffix && suffix.length) str += ` ${suffix.join(' ')}`; return `${dataType}${str}`; } function arrayStructTypeToSQL(expr) { if (!expr) return; const { dataType, definition, anglebracket } = expr; const dataTypeUpper = toUpper(dataType); const isNotArrayOrStruct = dataTypeUpper !== 'ARRAY' && dataTypeUpper !== 'STRUCT'; if (isNotArrayOrStruct) return dataTypeUpper; const result = definition && definition.map(field => { const { field_name: fieldName, field_type: fieldType } = field; const fieldResult = [fieldName, arrayStructTypeToSQL(fieldType)]; return fieldResult.filter(hasVal).join(' '); }).join(', '); return anglebracket ? `${dataTypeUpper}<${result}>` : `${dataTypeUpper} ${result}`; } function commentToSQL(comment) { if (!comment) return; const result = []; const { keyword, symbol, value } = comment; result.push(keyword.toUpperCase()); if (symbol) result.push(symbol); result.push(literalToSQL(value)); return result.join(' '); } function triggerEventToSQL(events) { return events.map(event => { const { keyword: eventKw, args } = event; const result = [toUpper(eventKw)]; if (args) { const { keyword: kwArgs, columns } = args; result.push(toUpper(kwArgs), columns.map(_column.columnRefToSQL).join(', ')); } return result.join(' '); }).join(' OR '); } function returningToSQL(returning) { if (!returning) return ''; const { columns } = returning; return ['RETURNING', columns.map(_column.columnToSQL).filter(hasVal).join(', ')].join(' '); } function commonKeywordArgsToSQL(kwArgs) { if (!kwArgs) return []; return [toUpper(kwArgs.keyword), toUpper(kwArgs.args)]; } function autoIncrementToSQL(autoIncrement) { if (!autoIncrement) return; if (typeof autoIncrement === 'string') { const { database } = getParserOpt(); switch (database && database.toLowerCase()) { case 'sqlite': return 'AUTOINCREMENT'; default: return 'AUTO_INCREMENT'; } } const { keyword, seed, increment, parentheses } = autoIncrement; let result = toUpper(keyword); if (parentheses) result += `(${literalToSQL(seed)}, ${literalToSQL(increment)})`; return result; } function columnOrderListToSQL(columnOrderList) { if (!columnOrderList) return; return columnOrderList.map(_column.columnOrderToSQL).filter(hasVal).join(', '); } });