node-querybuilder
Version:
Modeled after Codeigniter's QueryBuilder. Build and execute queries in a safe and database-agnostic way.
259 lines (218 loc) • 9.43 kB
JavaScript
const Request = require('tedious').Request;
const QueryBuilder = require('./query_builder.js');
const ERROR = require("../QueryExecError");
const WrapperPromise = require("../WrapperPromise");
// ****************************************************************************
// QueryBuilder "Query Execution" methods.
// ****************************************************************************
class QueryExec extends QueryBuilder {
constructor() {
super();
}
_exec(sql, cb) {
if (Object.prototype.toString.call(this._connection) === Object.prototype.toString.call({})) {
const request = new Request(sql, (err, count, results) => {
// Standardize some important properties
if (!err && results && Array.isArray(results)) {
if (results.length === 0) {
results = {insert_id: null, affected_rows: 0, changed_rows: 0};
// Affected & Changed Rows
if (count) {
results.affected_rows = count;
// Only set changed rows on an update
if (/^update\s/i.test(sql)) results.changed_rows = count;
}
} else {
const data = results.map(row => row.map(col => ({[col.metadata.colName]: col.value})).reduce((l,r) => Object.assign(l,r)))
if (/^insert\s/i.test(sql)) {
results = {insert_id: data, affected_rows: (count ? count : 0), changed_rows: 0};
} else {
results = data;
}
}
}
cb(err, results);
});
if (!this._connection) {
this._connection.connect(err => {
if (err) return cb(err, null);
this._connection.execSql(request);
});
} else {
//console.log("connection object2: ", this._connection);
this._connection.execSql(request);
}
} else {
throw new Error("No connection object supplied to the Query Exec Library!");
}
}
query(sql, cb) {
if (!cb || typeof cb !== "function") return new WrapperPromise(sql, this._exec.bind(this)).promisify();
this._exec(sql, cb);
}
count(table, cb) {
if (typeof table === 'function' && typeof cb !== 'function') {
table = null;
cb = table;
}
const sql = this._count(table);
this.reset_query(sql);
const handler = (err, row) => {
if (!err) {
if (typeof callback !== "function") {
this.resolve(row[0].numrows);
return;
}
cb(err, row[0].numrows);
}
else {
if (typeof callback !== "function") {
this.reject(err);
return;
}
cb(err, row);
}
}
if (typeof cb !== "function") return new WrapperPromise(sql, this._exec.bind(this), handler).promisify();
this._exec(sql, handler);
}
get(table,cb,conn) {
// The table parameter is optional, it could be the cb...
if (typeof table === 'function' && typeof cb !== 'function') {
cb = table;
}
const sql = this._get(table);
this.reset_query(sql);
if (typeof cb !== "function") return new WrapperPromise(sql, this._exec.bind(this)).promisify();
this._exec(sql, cb);
}
get_where(table,where,cb) {
if (typeof table !== 'string' && !Array.isArray(table)) {
throw ERROR.FIRST_PARAM_OF_GET_WHERE_ERR;
}
if (Object.prototype.toString.call(where) !== Object.prototype.toString.call({})) {
throw ERROR.SECOND_PARAM_OF_GET_WHERE_ERR;
}
const sql = this._get_where(table, where);
this.reset_query(sql);
if (typeof cb !== "function") return new WrapperPromise(sql, this._exec.bind(this)).promisify();
this._exec(sql, cb);
}
insert(table, set, cb, ignore, suffix) {
const sql = this._insert(table, set, ignore, suffix);
this.reset_query(sql);
if (typeof cb !== "function") return new WrapperPromise(sql, this._exec.bind(this)).promisify();
this._exec(sql, cb);
}
insert_ignore(table, set, on_dupe, cb) {
throw new Error("insert_ignore(): This feature is currently unsupported in the MSSQL driver.");
// if (typeof on_dupe === 'function') {
// cb = on_dupe;
// on_dupe = null;
// }
// const sql = this._insert_ignore(table,set,on_dupe);
// this.reset_query(sql);
// this._exec(sql,cb);
}
insert_batch(table, set, ignore, on_dupe, cb) {
if (typeof ignore === 'function') {
cb = ignore;
ignore = null;
}
else if (typeof on_dupe === 'function') {
cb = on_dupe;
on_dupe = null;
}
if (ignore) throw new Error("QE insert_batch(): INSERT IGNORE is currently unsupported on the MSSQL driver.");
if (suffix) throw new Error("QE insert_batch(): 'on_dupe' string (4th parameter) is currently unsupported on the MSSQL driver.");
const sql = this._insert_batch(table, set);
this.reset_query(sql);
if (typeof cb !== "function") return new WrapperPromise(sql, this._exec.bind(this)).promisify();
this._exec(sql, cb);
}
update(table,set,where,cb) {
// The where parameter is optional, it could be the cb...
if (typeof where === 'function' && typeof cb !== 'function') {
cb = where;
where = null;
}
else if (typeof where === 'undefined' || where === false || (where !== null && typeof where === 'object' && Object.keys(where).length === 0)) {
where = null;
}
const sql = this._update(table,set,where);
this.reset_query(sql);
if (typeof cb !== "function") return new WrapperPromise(sql, this._exec.bind(this)).promisify();
this._exec(sql, cb);
}
// TODO: Write this complicated-ass function
update_batch(table,set,index,where,cb) {
// The where parameter is optional, it could be the cb...
if (typeof where === 'function' && typeof cb !== 'function') {
cb = where;
where = null;
}
else if (typeof where === 'undefined' || where === false || (where !== null && typeof where === 'object' && Object.keys(where).length === 0)) {
where = null;
}
const sqls = this._update_batch(table, set, index, where);
const results = null;
const errors = [];
// Execute each batch of (at least) 100
const handler = (resolve, reject) => {
(function next_batch() {
const sql = sqls.shift();
this.reset_query(sql);
this._exec(sql, (err, res) => {
if (!err) {
if (null === results) {
results = res;
} else {
results.affected_rows += res.affected_rows;
results.changed_rows += res.changed_rows;
}
} else {
errors.push(err);
}
if (sqls.length > 0) {
setTimeout(next_batch,0);
} else {
return cb(errors, results);
}
});
})();
};
if (!cb || cb !== 'function') {
return new Promise(handler);
} else {
handler();
}
}
delete(table, where, cb) {
if (typeof where === 'function' && typeof cb !== 'function') {
cb = where;
where = undefined;
}
if (typeof table === 'function' && typeof cb !== 'function') {
cb = table;
table = undefined;
where = undefined;
}
const sql = this._delete(table, where);
this.reset_query(sql);
if (typeof cb !== "function") return new WrapperPromise(sql, this._exec.bind(this)).promisify();
this._exec(sql, cb);
}
empty_table(table, cb) {
const sql = this._empty_table(table,cb);
this.reset_query(sql);
if (typeof cb !== "function") return new WrapperPromise(sql, this._exec.bind(this)).promisify();
this._exec(sql, cb);
}
truncate(table, cb) {
const sql = this._truncate(table,cb);
this.reset_query(sql);
if (typeof cb !== "function") return new WrapperPromise(sql, this._exec.bind(this)).promisify();
this._exec(sql, cb);
}
}
module.exports = QueryExec;