UNPKG

node-sqlparser

Version:

a sql parser for node.js

442 lines (396 loc) 8.93 kB
// (C) 2011-2013 Alibaba Group Holding Limited. // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // version 2 as published by the Free Software Foundation. // Author :windyrobin <windyrobin@Gmail.com> //http://dev.mysql.com/doc/refman/5.0/en/string-literals.html // for mysql only var escapeMap = { '\0' : '\\0', '\'' : '\\\'', '\"' : '\\\"', '\b' : '\\b', '\n' : '\\n', '\r' : '\\r', '\t' : '\\t', '\x1a': '\\Z', /**< EOF */ '\\' : '\\\\', // '%' : '\\%', // '_' : '\_' }; function escape(str) { var res = []; var c, e; for (var i = 0 ;i < str.length; i++) { c = str[i]; e = escapeMap[c]; if (e) { c = e; } res.push(c); } return res.join(''); } function literalToSQL(l) { var t = l.type; var v = l.value; // if t === number, do nothing if (t === 'string') { v = '\'' + escape(v) + '\''; //v = '"' + v + '"'; } else if (t === 'bool') { v = v ? 'TRUE' : 'FALSE'; } else if (t === 'null') { v = 'NULL'; } else if (t === 'star') { v = '*'; } if (l.paren) { return '(' + v + ')'; } else { return v; } } function unaryToSQL(e) { var str = e.operator + ' ' + exprToSQL(e.expr); if (e.paren) { return '(' + str + ')'; } else { return str; } } function getExprListSQL(l) { var es = []; for (var i = 0; i < l.length; i++) { es.push(exprToSQL(l[i])); } return es; } function binaryToSQL(e) { var op = e.operator; var left = e.left; var right = e.right; var lstr = exprToSQL(left); var rstr = exprToSQL(right); if (Array.isArray(rstr)) { if (op === '=') { op = 'IN'; } if (op === 'BETWEEN') { rstr = rstr[0] + ' AND ' + rstr[1]; } else { rstr = '(' + rstr.join(', ') + ')'; } } var str = lstr + ' ' + op + ' ' + rstr; if (e.paren) { return '(' + str + ')'; } else { return str; } } function aggrToSQL(e) { var args = e.args; var str = exprToSQL(args.expr); var name = e.name; if (name === 'COUNT') { //inspect(args); var distinct = args.distinct; if (distinct) { str = 'DISTINCT ' + str; } } //inspect(args); return name + '(' + str + ')'; } function funcToSQL(e) { //var es = getExprListSQL(e.args.value); var es = exprToSQL(e.args); var str = e.name + '(' + es.join(', ') + ')'; if (e.paren) { return '(' + str + ')'; } else { return str; } } function columnRefToSQL(e) { var str = e.column; if (e.table) { str = e.table + '.' + str; } if (e.paren) { return '(' + str + ')'; } else { return str; } } function exprToSQL(e) { var t = e.type; var res ; switch (t) { case 'unary_expr' : res = unaryToSQL(e); break; case 'binary_expr' : res = binaryToSQL(e); break; case 'aggr_func' : res = aggrToSQL(e); break; case 'function' : res = funcToSQL(e); break; case 'column_ref' : res = columnRefToSQL(e); break; case 'expr_list' : res = getExprListSQL(e.value); break; case 'var' : // res = varToSQL(e); throw new Error('unsupported type `var`'); case 'param': res = paramToSql(e); break; default: res = literalToSQL(e); } return res; } function paramToSql(e) { return e.value === '?' ? '?' : ':' + e.value; } function js2nSQLExpr(val) { var type = typeof val; var obj = {}; switch (type) { case 'string': case 'number': obj.type = type; obj.value = val; break; case 'boolean': obj.type = 'bool'; obj.value = val; break; case 'object': if (val === null) { obj.type = 'null'; obj.value = val; } else if (Array.isArray(val)) { obj.type = 'expr_list'; var arr = []; for (var i = 0; i < val.length; i++) { arr.push(js2nSQLExpr(val[i])); } obj.value = arr; } else { //TODO ,`object` type not supported now obj.type = 'object'; obj.value = val; } break; default: obj.type = 'unknown'; } return obj; } /* function varToSQL(e) { var val = Context.getBindVar(e); var expr = js2nSQLExpr(val); return exprToSQL(expr); } */ function unionToSQL(s, options) { var str = selectToSQL(s, options); var res = []; res.push(str); while (s._next) { str = selectToSQL(s._next, options); res.push('UNION'); res.push(str); s = s._next; } return res.join(' '); } function selectToSQL(s, options) { var distinct = s.distinct; var columns = s.columns; var from = s.from; var where = s.where; var groupby = s.groupby; var orderby = s.orderby; var limit = s.limit; var clauses = []; var i, str, cs; options = options || {}; clauses.push('SELECT'); //distinct if (distinct) { clauses.push(distinct); } //column clause //inspect(columns); if (columns === '*') { clauses.push('*'); } else { cs = []; for (i = 0; i < columns.length; i++) { var ea = columns[i]; str = exprToSQL(ea.expr); if (ea.as) { str += (' AS ' + ea.as); } cs.push(str); } clauses.push(cs.join(', ')); } //from clause if (Array.isArray(from)) { var tbase = from[0]; clauses.push('FROM'); cs = []; str = tbase.table; if (options.keep_db !== false && tbase.db) { str = tbase.db + '.' + str; } if (tbase.as) { str += ' AS ' + tbase.as; } cs.push(str); for (i = 1; i < from.length; i++) { var tref = from[i]; if (tref.join) { str = ' ' + tref.join + ' '; } else { str = ', '; } if (options.keep_db !== false && tref.db) { str += (tref.db + '.'); } str += tref.table; if (tref.as) { str += ' AS ' + tref.as; } if (tref.on) { str += ' ON ' + exprToSQL(tref.on); } cs.push(str); } clauses.push(cs.join('')); } //where clause if (where) { clauses.push('WHERE ' + exprToSQL(where)); } if (Array.isArray(groupby)) { var l = getExprListSQL(groupby); clauses.push('GROUP BY ' + l.join(', ')); } if (Array.isArray(orderby)) { cs = []; for (var i = 0; i < orderby.length; i++) { var o = orderby[i]; str = exprToSQL(o.expr); str += ' ' + o.type; cs.push(str); } clauses.push('ORDER BY ' + cs.join(', ')); } //TODO, use exprToSQL instead //limit is [number, number] if (Array.isArray(limit)) { if (options.offset !== false) { str = 'LIMIT ' + limit[0].value + ', ' + limit[1].value; } else { str = 'LIMIT ' + (limit[0].value + limit[1].value); } clauses.push(str); } return clauses.join(' '); } function updateToSQL(stmt, options) { var res = ['UPDATE']; options = options || {}; if (options.keep_db === false) { res.push(stmt.table); } else { res.push(stmt.db + '.' + stmt.table); } res.push('SET'); var cs = []; var sets = stmt.set; var i, str; for (i = 0; i < sets.length; i++) { str = sets[i].column + ' = ' + exprToSQL(sets[i].value); cs.push(str); } res.push(cs.join(', ')); if (stmt.where) { str = 'WHERE '; str += exprToSQL(stmt.where); res.push(str); } return res.join(' '); } function replace_insertToSQL(stmt, options) { options = options || {}; var res = []; res.push(stmt.type.toUpperCase()); res.push('INTO'); if (options.keep_db === false) { res.push(stmt.table); } else { res.push(stmt.db + '.' + stmt.table); } res.push('(' + stmt.columns.join(', ') + ')'); res.push('VALUES'); var i; var cs = []; var vs = stmt.values; for (i = 0; i < vs.length; i++) { var es = vs[i].value; var rs = []; for (var j = 0; j < es.length; j++) { rs.push(exprToSQL(es[j])); } cs.push('(' + rs.join(', ') + ')'); } res.push(cs.join(', ')); return res.join(' '); } /** * stringify the ast to sql string * @param {AST} ast ast * @param {Object} opt options * @return {[type]} [description] */ module.exports = function (ast, opt) { var options = { keep_db: true, offset: false }; for (var k in opt) { options[k] = opt[k]; } var res ; switch (ast.type) { case 'select' : res = unionToSQL(ast, options); break; case 'update' : res = updateToSQL(ast, options); break; case 'insert' : case 'replace': res = replace_insertToSQL(ast, options); break; case 'delete' : throw new Error('ERROR TYPE :' + ast.type + ', NOT SUPPORTED'); default : throw new Error('ERROR TYPE :' + ast.type + ', NOT SUPPORTED'); } return res; }; module.exports.exprToSQL = exprToSQL;