UNPKG

database-utils

Version:

Database utils for node.js

615 lines (561 loc) 14.9 kB
"use strict"; module.exports = SqlDriver; var isArray = require("util").isArray; var isNumeric = require("useful-functions.js").isNumeric; var inArray = require("useful-functions.js").inArray; var isString = require("useful-functions.js").isString; var varType = require("useful-functions.js").varType; var stringEndsWith = require("useful-functions.js").stringEndsWith; var SqlParameters = require("./sqlparameters.js"); function SqlDriver() { var _get = ""; var _selects = []; var _froms = []; var _distinct = false; var _wheres = []; var _whereConcat = "and"; var _whereConcatDefault = "and"; var _sets = []; var _limit; var _offset; var _orderBys = []; var _joins = []; var _groupBys = []; var _whereGroupCount = 0; var _openWhereGroupCount = 0; var _havings = []; var _isParametrized = false; var _sqlParameters = new SqlParameters(); var _returnParameters = false; SqlDriver.prototype.reset = function() { _get = ""; _selects = []; _froms = []; _distinct = false; _wheres = []; _whereConcat = "and"; _whereConcatDefault = "and"; _sets = []; _limit = undefined; _offset = undefined; _orderBys = []; _joins = []; _groupBys = []; _whereGroupCount = 0; _openWhereGroupCount = 0; _havings = []; _isParametrized = false; return this; } SqlDriver.prototype.parameters = function() { if (_sqlParameters.count() > 0) { var result = _sqlParameters.values(); _sqlParameters.reset(); _isParametrized = false; return result; } else { _isParametrized = true; return this; } } SqlDriver.prototype.selectcase = function(field, options, alias) { _get = "Select"; var caseOptions = ""; for (var key in options) { var value = _wrap(options[key]); if (key == "") { caseOptions += " else " + value; } else { caseOptions += " when " + key + " then " + value; } } _selects.push({ "field": field, "alias": alias, "caseOptions": caseOptions }); return this; } SqlDriver.prototype.between = function(field, value, otherValue) { if (otherValue === undefined) { var split = (""+value).split(".."); if (split.length > 1) { value = split[0]; otherValue = split[1]; } } var sql = field + " between " + _wrap(value) + " and " + _wrap(otherValue); _where(sql); return this; } SqlDriver.prototype.notlike = function(field, match, side) { return this.like(field, match, side, "not like"); } SqlDriver.prototype.like = function(field, match, side, op) { if (op === undefined) op = "like"; if (side === undefined) side = "both"; if (match === undefined) match = ""; switch (side) { case "left": match = "%" + match; break; case "right": match += "%"; break; case "both": { if (typeof match == "string" && match.length == 0) { match = "%"; } else { match = "%" + match + "%"; } } break; default: throw new Error("Unknown side " + side + "."); } var sql = field + " " + op + " " + _wrap(match); _where(sql); return this; } SqlDriver.prototype.groupby = function(fields) { if (!isArray(fields)) { fields = fields.toString().split(","); } for (var i in fields) { var field = fields[i].trim(); if (field != "") { _groupBys[_groupBys.length] = field; } } return this; } SqlDriver.prototype.wherenotin = function(field, values) { return this.wherein(field, values, "not in"); } SqlDriver.prototype.wherein = function(field, values, op) { if (op === undefined) { op = "in"; } if (!isArray(values)) { values = ["" + values]; } values = values.map(function(value) { return _wrap(value); }); var value = "(" + values.join(",") + ")"; var where = field + " " + op + " " + value; _where(where); return this; } SqlDriver.prototype.having = function(field, value) { var expr = _conditionExpr(field, value); var sql = expr.join(" "); _havings[_havings.length] = sql; return this; } SqlDriver.prototype.getCount = function(table, where) { // TODO: [hold] this.reset(); if (table) { this.from(table); } if (where) { this.where(where); } this.select("count", "*", "RowCount") } SqlDriver.prototype.join = function(table, on, join) { join = String(join); if (!inArray(join, ["inner", "outer", "left", "right", "left outer", "right outer"])) { join = ""; } _joins[_joins.length] = (join + " join " + table + " on " + on).trimLeft(); return this; } SqlDriver.prototype.leftjoin = function(table, on) { return this.join(table, on, "left"); } SqlDriver.prototype.orderby = function(field, direction) { direction = String(direction).toLowerCase(); if (direction != "asc") { direction = "desc"; } var orderby = field + " " + direction; _orderBys[_orderBys.length] = orderby; return this; } SqlDriver.prototype.executeScalar = function() { // TODO: return first column of first row. return this.execute(); } SqlDriver.prototype.execute = function() { var query = this.get(); // TODO: These should do insert, update, delete, select. Get() returns the query. } SqlDriver.prototype.delete = function(table) { _get = "Delete"; if (table !== undefined) { _froms[_froms.length] = table; } return this; } SqlDriver.prototype.getDelete = function() { var table = _froms[0]; var result = "delete " + table; if (_wheres.length > 0) { result += "\n" + "where " + _wheres.join("\n"); } this.reset(); return result; } SqlDriver.prototype.getUpdate = function() { this._endQuery(); var table = _froms[0]; var result = "update " + table + "\n" + "set "; for (var i = 0, count = _sets.length; i < count; ++i) { if (i > 0) { result += ", "; } var value = _sets[i].value; if (_sets[i].wrapValue) { value = _wrap(value); } result += _sets[i].name + " = " + value; } if (_wheres.length > 0) { result += "\n" + "where " + _wheres.join("\n"); } this.reset(); return result; } SqlDriver.prototype.update = function(table) { _get = "Update"; if (table !== undefined) { _froms[_froms.length] = table; } return this; } SqlDriver.prototype.insert = function() { _get = "Insert"; return this; } SqlDriver.prototype.set = function(name, value, wrapValue) { if (arguments.length == 1) { for (var i in name) { this.set(i, name[i], true); } return this; } else { if (arguments[2] === undefined) { wrapValue = true; } if (value === null || value === undefined) { value = "null"; wrapValue = false; } _sets.push({ name: name, value: value, wrapValue: wrapValue }); } return this; } SqlDriver.prototype.getInsert = function() { // this.endQuery(); var table = _froms[0]; var result = "insert into " + table; var values = []; var names = []; for (var i = 0, count = _sets.length; i < count; ++i) { names[names.length] = _sets[i].name; var value = _sets[i].value; if (_sets[i].wrapValue) { value = _wrap(value); } values[values.length] = value; } result += "(" + names.join(", ") + ") values(" + values.join(", ") + ")"; this.reset(); return result; } SqlDriver.prototype.select = function(select, alias, func) { _get = "Select"; if (arguments.length == 0) { _selects.push({ "field": "*" }); } else if (arguments.length == 1) { if (isString(select)) { select = select.split(","); } for (var i = 0, count = select.length; i < count; ++i) { var field = select[i].toString().trim(); if (field === "") continue; var split = field.split(/\s*as\s*/i); if (split.length > 1) { field = split[0]; alias = split[1]; } _selects.push({ "field": field, "alias": alias }); } } else if (arguments.length == 2) { _selects.push({ "field": select, "alias": alias }); } else { var args = Array.prototype.slice.call(arguments); func = args.shift(); alias = args.pop(); // Do flatten array. for (var i = 0, count = args.length; i < count; ++i) { if (isArray(args[i])) { args[i] = args[i].join(", "); } } _selects.push({ "func": func, "field": args.join(", "), "alias": alias }); } return this; } SqlDriver.prototype.from = function(from) { parse: { if (isArray(from)) break parse; if (isString(from)) { from = from.split(","); } } for (var i = 0, count = from.length; i < count; ++i) { var table = from[i].toString().trim(); if (table === "") continue; _froms.push(table); } return this; } SqlDriver.prototype.distinct = function(value) { if (typeof value == "boolean") { _distinct = value; } else { _distinct = true; } return this; } SqlDriver.prototype.subQuery = function() { var sql = this.get(); return "(" + sql + ")"; } SqlDriver.prototype.getSelect = function() { this._endQuery(); var result = "select "; if (_distinct) { result += "distinct "; } var selects = ""; for (var i = 0, count = _selects.length; i < count; ++i) { var item = _selects[i]; var field = item.field; if (item.func) { field = item.func + "(" + field + ")"; } else if ("caseOptions" in item) { field = "case " + field + item.caseOptions + " end"; } if (item.alias) { field = field + " as " + item.alias; } if (i > 0) field = ", " + field; selects += field; } if (selects == "") { selects = "*"; } result += selects; if (_froms.length > 0) { result += "\n" + "from " + _froms.join(", "); } if (_joins.length > 0) { result += "\n" + _joins.join("\n"); } if (_wheres.length > 0) { result += "\n" + "where " + _wheres.join("\n"); } if (_groupBys.length > 0) { result += "\n" + "group by " + _groupBys.join(", "); } if (_havings.length > 0) { result += "\n" + "having " + _havings.join("\n"); } if (_orderBys.length > 0) { result += "\n" + "order by " + _orderBys.join(", "); } if (isNumeric(_limit)) { result += "\n"; result = this.getLimit(result, _limit, _offset); } this.reset(); return result; } var _conditionExpr = (function() { var operators = [">=", "<=", "!=", "<>", ">", "<", "!@", "@", "%$", "^%", "%", "like", "not like", "is null", "is not null"]; return function(field, value) { var operator; for (var count = operators.length, i = 0; i < count; ++i) { var op = operators[i]; if (stringEndsWith(field, op)) { field = field.slice(0, -op.length).trim(); operator = op; break; } } if (operator === undefined) { operator = "="; } var wrapValue = true; if (value === null) { value = "@null"; } if (isString(value)) { if (value.substr(0, 1) == "@") { wrapValue = false; value = value.substr(1); } } var result = [field, operator]; if (value !== undefined) { if (wrapValue) { value = _wrap(value); } result[result.length] = value; } return result; } })(); SqlDriver.prototype.where = function(field, value) { if (typeof field == "object") { for (var i in field) { this.where(i, field[i]); } return this; } var expr = _conditionExpr(field, value); var operator = expr[1]; switch (operator) { case "@": return this.wherein(field.slice(0, -1), value); case "!@": return this.wherenotin(field.slice(0, -2), value); case "!%": return this.notlike(field.slice(0, -2), value, "both"); case "%": return this.like(field.slice(0, -1), value, "both"); case "^%": return this.like(field.slice(0, -2), value, "right"); case "%$": return this.like(field.slice(0, -2), value, "left"); } var sql = expr.join(" "); _where(sql); return this; } var _where = function(sql) { var concat = ""; if (_wheres.length > 0) { concat = (new Array(_whereGroupCount + 2)).join(" ") + _whereConcat + " "; } while (_openWhereGroupCount > 0) { concat += "("; _openWhereGroupCount--; } _whereConcat = _whereConcatDefault; _wheres.push(concat + sql); } SqlDriver.prototype.limit = function(limit, offset) { _limit = limit; if (offset !== undefined) { _offset = offset; } return this; } SqlDriver.prototype.offset = function(offset) { _offset = offset; return this; } SqlDriver.prototype.getLimit = function(sql, limit, offset) { throw "getLimit() not supported."; } SqlDriver.prototype.endQuery = function() { // TODO: endQuery function. } SqlDriver.prototype.query = function() { throw "Database engine is not defined."; } SqlDriver.prototype.get = function() { if (!_get) { throw new Error("_get is not set."); } var f = "get" + _get; if (typeof this[f] != "function") { throw "Error while trying to call '"+f+"' method."; } return this[f].call(this); } SqlDriver.prototype.getQuery = function() { var sql = this.get(); var parameters = this.parameters(); return [sql, parameters]; } var _wrap = function(value) { if (_isParametrized) { _sqlParameters.push({value: value}); return "?"; } if (!isNumeric(value)) { value = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') .replace(/'/g, "\\'") .replace(/\\"/g, '"') + '\''; } return value; } var _quote = function (value, wrapInQuotes) { if (isNumeric(value)) { return value; } value = value .replace("\\", "\\\\") .replace("\0", "\\0") .replace("\n", "\\n") .replace("\r", "\\r") .replace("'", "\\'") .replace("\"", "\\\"") .replace("\x1a", "\\Z"); if (wrapInQuotes === true) { value = "'" + value + "'"; } return value; } SqlDriver.prototype.andop = function() { _whereConcat = "and"; return this; } SqlDriver.prototype.orop = function() { _whereConcat = "or"; return this; } SqlDriver.prototype.beginwheregroup = function() { _whereGroupCount++; _openWhereGroupCount++; return this; } SqlDriver.prototype.endwheregroup = function() { if (_whereGroupCount > 0) { var whereCount = _wheres.length; if (_openWhereGroupCount >= _whereGroupCount) { _openWhereGroupCount--; } else if (whereCount > 0) { _wheres[whereCount-1] += ")"; } _whereGroupCount--; } return this; } SqlDriver.prototype._endQuery = function() { while (_whereGroupCount > 0) { this.endwheregroup(); } } }