pimatic
Version:
A home automation server and framework for the Raspberry PI running on node.js
663 lines (555 loc) • 21.6 kB
JavaScript
'use strict';
exports.__esModule = true;
var _keys = require('babel-runtime/core-js/object/keys');
var _keys2 = _interopRequireDefault(_keys);
var _typeof2 = require('babel-runtime/helpers/typeof');
var _typeof3 = _interopRequireDefault(_typeof2);
var _reduce2 = require('lodash/reduce');
var _reduce3 = _interopRequireDefault(_reduce2);
var _omitBy2 = require('lodash/omitBy');
var _omitBy3 = _interopRequireDefault(_omitBy2);
var _map2 = require('lodash/map');
var _map3 = _interopRequireDefault(_map2);
var _isUndefined2 = require('lodash/isUndefined');
var _isUndefined3 = _interopRequireDefault(_isUndefined2);
var _isString2 = require('lodash/isString');
var _isString3 = _interopRequireDefault(_isString2);
var _isEmpty2 = require('lodash/isEmpty');
var _isEmpty3 = _interopRequireDefault(_isEmpty2);
var _groupBy2 = require('lodash/groupBy');
var _groupBy3 = _interopRequireDefault(_groupBy2);
var _compact2 = require('lodash/compact');
var _compact3 = _interopRequireDefault(_compact2);
var _bind2 = require('lodash/bind');
var _bind3 = _interopRequireDefault(_bind2);
var _assign2 = require('lodash/assign');
var _assign3 = _interopRequireDefault(_assign2);
var _helpers = require('../helpers');
var helpers = _interopRequireWildcard(_helpers);
var _raw = require('../raw');
var _raw2 = _interopRequireDefault(_raw);
var _joinclause = require('./joinclause');
var _joinclause2 = _interopRequireDefault(_joinclause);
var _debug = require('debug');
var _debug2 = _interopRequireDefault(_debug);
var _uuid = require('uuid');
var _uuid2 = _interopRequireDefault(_uuid);
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var debugBindings = (0, _debug2.default)('knex:bindings');
// The "QueryCompiler" takes all of the query statements which
// have been gathered in the "QueryBuilder" and turns them into a
// properly formatted / bound query string.
// Query Compiler
// -------
function QueryCompiler(client, builder) {
this.client = client;
this.method = builder._method || 'select';
this.options = builder._options;
this.single = builder._single;
this.timeout = builder._timeout || false;
this.cancelOnTimeout = builder._cancelOnTimeout || false;
this.grouped = (0, _groupBy3.default)(builder._statements, 'grouping');
this.formatter = client.formatter();
}
var components = ['columns', 'join', 'where', 'union', 'group', 'having', 'order', 'limit', 'offset', 'lock'];
(0, _assign3.default)(QueryCompiler.prototype, {
// Used when the insert call is empty.
_emptyInsertValue: 'default values',
// Collapse the builder into a single object
toSQL: function toSQL(method, tz) {
this._undefinedInWhereClause = false;
method = method || this.method;
var val = this[method]();
var defaults = {
method: method,
options: (0, _reduce3.default)(this.options, _assign3.default, {}),
timeout: this.timeout,
cancelOnTimeout: this.cancelOnTimeout,
bindings: this.formatter.bindings,
__knexQueryUid: _uuid2.default.v4()
};
if ((0, _isString3.default)(val)) {
val = { sql: val };
}
defaults.bindings = defaults.bindings || [];
if (method === 'select' || method === 'first') {
if (this.single.as) {
defaults.as = this.single.as;
}
}
if (this._undefinedInWhereClause) {
debugBindings(defaults.bindings);
throw new Error('Undefined binding(s) detected when compiling ' + (method.toUpperCase() + ' query: ' + val.sql));
}
return (0, _assign3.default)(defaults, val);
},
// Compiles the `select` statement, or nested sub-selects by calling each of
// the component compilers, trimming out the empties, and returning a
// generated query string.
select: function select() {
var _this = this;
var sql = this.with();
var statements = components.map(function (component) {
return _this[component](_this);
});
sql += (0, _compact3.default)(statements).join(' ');
return sql;
},
pluck: function pluck() {
var toPluck = this.single.pluck;
if (toPluck.indexOf('.') !== -1) {
toPluck = toPluck.split('.').slice(-1)[0];
}
return {
sql: this.select(),
pluck: toPluck
};
},
// Compiles an "insert" query, allowing for multiple
// inserts using a single query statement.
insert: function insert() {
var insertValues = this.single.insert || [];
var sql = this.with() + ('insert into ' + this.tableName + ' ');
if (Array.isArray(insertValues)) {
if (insertValues.length === 0) {
return '';
}
} else if ((typeof insertValues === 'undefined' ? 'undefined' : (0, _typeof3.default)(insertValues)) === 'object' && (0, _isEmpty3.default)(insertValues)) {
return sql + this._emptyInsertValue;
}
var insertData = this._prepInsert(insertValues);
if (typeof insertData === 'string') {
sql += insertData;
} else {
if (insertData.columns.length) {
sql += '(' + this.formatter.columnize(insertData.columns);
sql += ') values (';
var i = -1;
while (++i < insertData.values.length) {
if (i !== 0) sql += '), (';
sql += this.formatter.parameterize(insertData.values[i], this.client.valueForUndefined);
}
sql += ')';
} else if (insertValues.length === 1 && insertValues[0]) {
sql += this._emptyInsertValue;
} else {
sql = '';
}
}
return sql;
},
// Compiles the "update" query.
update: function update() {
// Make sure tableName is processed by the formatter first.
var tableName = this.tableName;
var updateData = this._prepUpdate(this.single.update);
var wheres = this.where();
return this.with() + ('update ' + (this.single.only ? 'only ' : '') + tableName) + ' set ' + updateData.join(', ') + (wheres ? ' ' + wheres : '');
},
// Compiles the columns in the query, specifying if an item was distinct.
columns: function columns() {
var distinct = false;
if (this.onlyUnions()) return '';
var columns = this.grouped.columns || [];
var i = -1,
sql = [];
if (columns) {
while (++i < columns.length) {
var stmt = columns[i];
if (stmt.distinct) distinct = true;
if (stmt.type === 'aggregate') {
sql.push(this.aggregate(stmt));
} else if (stmt.value && stmt.value.length > 0) {
sql.push(this.formatter.columnize(stmt.value));
}
}
}
if (sql.length === 0) sql = ['*'];
return 'select ' + (distinct ? 'distinct ' : '') + sql.join(', ') + (this.tableName ? ' from ' + (this.single.only ? 'only ' : '') + this.tableName : '');
},
aggregate: function aggregate(stmt) {
var val = stmt.value;
var splitOn = val.toLowerCase().indexOf(' as ');
var distinct = stmt.aggregateDistinct ? 'distinct ' : '';
// Allows us to speciy an alias for the aggregate types.
if (splitOn !== -1) {
var col = val.slice(0, splitOn);
var alias = val.slice(splitOn + 4);
return stmt.method + '(' + (distinct + this.formatter.wrap(col)) + ') ' + ('as ' + this.formatter.wrap(alias));
}
return stmt.method + '(' + (distinct + this.formatter.wrap(val)) + ')';
},
// Compiles all each of the `join` clauses on the query,
// including any nested join queries.
join: function join() {
var sql = '';
var i = -1;
var joins = this.grouped.join;
if (!joins) return '';
while (++i < joins.length) {
var join = joins[i];
var table = join.schema ? join.schema + '.' + join.table : join.table;
if (i > 0) sql += ' ';
if (join.joinType === 'raw') {
sql += this.formatter.unwrapRaw(join.table);
} else {
sql += join.joinType + ' join ' + this.formatter.wrap(table);
var ii = -1;
while (++ii < join.clauses.length) {
var clause = join.clauses[ii];
if (ii > 0) {
sql += ' ' + clause.bool + ' ';
} else {
sql += ' ' + (clause.type === 'onUsing' ? 'using' : 'on') + ' ';
}
var val = this[clause.type].call(this, clause);
if (val) {
sql += val;
}
}
}
}
return sql;
},
onBetween: function onBetween(statement) {
return this.formatter.wrap(statement.column) + ' ' + this._not(statement, 'between') + ' ' + (0, _map3.default)(statement.value, (0, _bind3.default)(this.formatter.parameter, this.formatter)).join(' and ');
},
onNull: function onNull(statement) {
return this.formatter.wrap(statement.column) + ' is ' + this._not(statement, 'null');
},
onExists: function onExists(statement) {
return this._not(statement, 'exists') + ' (' + this.formatter.rawOrFn(statement.value) + ')';
},
onIn: function onIn(statement) {
if (Array.isArray(statement.column)) return this.multiOnIn(statement);
return this.formatter.wrap(statement.column) + ' ' + this._not(statement, 'in ') + this.wrap(this.formatter.parameterize(statement.value));
},
multiOnIn: function multiOnIn(statement) {
var i = -1,
sql = '(' + this.formatter.columnize(statement.column) + ') ';
sql += this._not(statement, 'in ') + '((';
while (++i < statement.value.length) {
if (i !== 0) sql += '),(';
sql += this.formatter.parameterize(statement.value[i]);
}
return sql + '))';
},
// Compiles all `where` statements on the query.
where: function where() {
var wheres = this.grouped.where;
if (!wheres) return;
var sql = [];
var i = -1;
while (++i < wheres.length) {
var stmt = wheres[i];
if (stmt.hasOwnProperty('value') && helpers.containsUndefined(stmt.value)) {
this._undefinedInWhereClause = true;
}
var val = this[stmt.type](stmt);
if (val) {
if (sql.length === 0) {
sql[0] = 'where';
} else {
sql.push(stmt.bool);
}
sql.push(val);
}
}
return sql.length > 1 ? sql.join(' ') : '';
},
group: function group() {
return this._groupsOrders('group');
},
order: function order() {
return this._groupsOrders('order');
},
// Compiles the `having` statements.
having: function having() {
var havings = this.grouped.having;
if (!havings) return '';
var sql = ['having'];
for (var i = 0, l = havings.length; i < l; i++) {
var s = havings[i];
var val = this[s.type](s);
if (val) {
if (sql.length === 0) {
sql[0] = 'where';
}
if (sql.length > 1 || sql.length === 1 && sql[0] !== 'having') {
sql.push(s.bool);
}
sql.push(val);
}
}
return sql.length > 1 ? sql.join(' ') : '';
},
havingRaw: function havingRaw(statement) {
return this._not(statement, '') + this.formatter.unwrapRaw(statement.value);
},
havingWrapped: function havingWrapped(statement) {
var val = this.formatter.rawOrFn(statement.value, 'where');
return val && this._not(statement, '') + '(' + val.slice(6) + ')' || '';
},
havingBasic: function havingBasic(statement) {
return this._not(statement, '') + this.formatter.wrap(statement.column) + ' ' + this.formatter.operator(statement.operator) + ' ' + this.formatter.parameter(statement.value);
},
havingNull: function havingNull(statement) {
return this.formatter.wrap(statement.column) + ' is ' + this._not(statement, 'null');
},
havingExists: function havingExists(statement) {
return this._not(statement, 'exists') + ' (' + this.formatter.rawOrFn(statement.value) + ')';
},
havingBetween: function havingBetween(statement) {
return this.formatter.wrap(statement.column) + ' ' + this._not(statement, 'between') + ' ' + (0, _map3.default)(statement.value, (0, _bind3.default)(this.formatter.parameter, this.formatter)).join(' and ');
},
havingIn: function havingIn(statement) {
if (Array.isArray(statement.column)) return this.multiHavingIn(statement);
return this.formatter.wrap(statement.column) + ' ' + this._not(statement, 'in ') + this.wrap(this.formatter.parameterize(statement.value));
},
multiHavingIn: function multiHavingIn(statement) {
var i = -1,
sql = '(' + this.formatter.columnize(statement.column) + ') ';
sql += this._not(statement, 'in ') + '((';
while (++i < statement.value.length) {
if (i !== 0) sql += '),(';
sql += this.formatter.parameterize(statement.value[i]);
}
return sql + '))';
},
// Compile the "union" queries attached to the main query.
union: function union() {
var onlyUnions = this.onlyUnions();
var unions = this.grouped.union;
if (!unions) return '';
var sql = '';
for (var i = 0, l = unions.length; i < l; i++) {
var union = unions[i];
if (i > 0) sql += ' ';
if (i > 0 || !onlyUnions) sql += union.clause + ' ';
var statement = this.formatter.rawOrFn(union.value);
if (statement) {
if (union.wrap) sql += '(';
sql += statement;
if (union.wrap) sql += ')';
}
}
return sql;
},
// If we haven't specified any columns or a `tableName`, we're assuming this
// is only being used for unions.
onlyUnions: function onlyUnions() {
return !this.grouped.columns && this.grouped.union && !this.tableName;
},
limit: function limit() {
var noLimit = !this.single.limit && this.single.limit !== 0;
if (noLimit) return '';
return 'limit ' + this.formatter.parameter(this.single.limit);
},
offset: function offset() {
if (!this.single.offset) return '';
return 'offset ' + this.formatter.parameter(this.single.offset);
},
// Compiles a `delete` query.
del: function del() {
// Make sure tableName is processed by the formatter first.
var tableName = this.tableName;
var wheres = this.where();
return this.with() + ('delete from ' + (this.single.only ? 'only ' : '') + tableName) + (wheres ? ' ' + wheres : '');
},
// Compiles a `truncate` query.
truncate: function truncate() {
return 'truncate ' + this.tableName;
},
// Compiles the "locks".
lock: function lock() {
if (this.single.lock) {
if (!this.client.transacting) {
helpers.warn('You are attempting to perform a "lock" command outside of a transaction.');
} else {
return this[this.single.lock]();
}
}
},
// Compile the "counter".
counter: function counter() {
var counter = this.single.counter;
var toUpdate = {};
toUpdate[counter.column] = this.client.raw(this.formatter.wrap(counter.column) + ' ' + (counter.symbol || '+') + ' ' + counter.amount);
this.single.update = toUpdate;
return this.update();
},
// On Clause
// ------
onWrapped: function onWrapped(clause) {
var self = this;
var wrapJoin = new _joinclause2.default();
clause.value.call(wrapJoin, wrapJoin);
var sql = '';
wrapJoin.clauses.forEach(function (wrapClause, ii) {
if (ii > 0) {
sql += ' ' + wrapClause.bool + ' ';
}
var val = self[wrapClause.type](wrapClause);
if (val) {
sql += val;
}
});
if (sql.length) {
return '(' + sql + ')';
}
return '';
},
onBasic: function onBasic(clause) {
return this.formatter.wrap(clause.column) + ' ' + this.formatter.operator(clause.operator) + ' ' + this.formatter.wrap(clause.value);
},
onRaw: function onRaw(clause) {
return this.formatter.unwrapRaw(clause.value);
},
onUsing: function onUsing(clause) {
return this.formatter.wrap(clause.column);
},
// Where Clause
// ------
whereIn: function whereIn(statement) {
if (Array.isArray(statement.column)) return this.multiWhereIn(statement);
return this.formatter.wrap(statement.column) + ' ' + this._not(statement, 'in ') + this.wrap(this.formatter.parameterize(statement.value));
},
multiWhereIn: function multiWhereIn(statement) {
var i = -1,
sql = '(' + this.formatter.columnize(statement.column) + ') ';
sql += this._not(statement, 'in ') + '((';
while (++i < statement.value.length) {
if (i !== 0) sql += '),(';
sql += this.formatter.parameterize(statement.value[i]);
}
return sql + '))';
},
whereNull: function whereNull(statement) {
return this.formatter.wrap(statement.column) + ' is ' + this._not(statement, 'null');
},
// Compiles a basic "where" clause.
whereBasic: function whereBasic(statement) {
return this._not(statement, '') + this.formatter.wrap(statement.column) + ' ' + this.formatter.operator(statement.operator) + ' ' + this.formatter.parameter(statement.value);
},
whereExists: function whereExists(statement) {
return this._not(statement, 'exists') + ' (' + this.formatter.rawOrFn(statement.value) + ')';
},
whereWrapped: function whereWrapped(statement) {
var val = this.formatter.rawOrFn(statement.value, 'where');
return val && this._not(statement, '') + '(' + val.slice(6) + ')' || '';
},
whereBetween: function whereBetween(statement) {
return this.formatter.wrap(statement.column) + ' ' + this._not(statement, 'between') + ' ' + (0, _map3.default)(statement.value, (0, _bind3.default)(this.formatter.parameter, this.formatter)).join(' and ');
},
// Compiles a "whereRaw" query.
whereRaw: function whereRaw(statement) {
return this._not(statement, '') + this.formatter.unwrapRaw(statement.value);
},
wrap: function wrap(str) {
if (str.charAt(0) !== '(') return '(' + str + ')';
return str;
},
// Compiles all `with` statements on the query.
with: function _with() {
if (!this.grouped.with || !this.grouped.with.length) {
return '';
}
var withs = this.grouped.with;
if (!withs) return;
var sql = [];
var i = -1;
while (++i < withs.length) {
var stmt = withs[i];
var val = this[stmt.type](stmt);
sql.push(val);
}
return 'with ' + sql.join(', ') + ' ';
},
withWrapped: function withWrapped(statement) {
var val = this.formatter.rawOrFn(statement.value);
return val && this.formatter.columnize(statement.alias) + ' as (' + val + ')' || '';
},
withRaw: function withRaw(statement) {
return this.formatter.columnize(statement.alias) + ' as (' + this.formatter.unwrapRaw(statement.value) + ')';
},
// Determines whether to add a "not" prefix to the where clause.
_not: function _not(statement, str) {
if (statement.not) return 'not ' + str;
return str;
},
_prepInsert: function _prepInsert(data) {
var isRaw = this.formatter.rawOrFn(data);
if (isRaw) return isRaw;
var columns = [];
var values = [];
if (!Array.isArray(data)) data = data ? [data] : [];
var i = -1;
while (++i < data.length) {
if (data[i] == null) break;
if (i === 0) columns = (0, _keys2.default)(data[i]).sort();
var row = new Array(columns.length);
var keys = (0, _keys2.default)(data[i]);
var j = -1;
while (++j < keys.length) {
var key = keys[j];
var idx = columns.indexOf(key);
if (idx === -1) {
columns = columns.concat(key).sort();
idx = columns.indexOf(key);
var k = -1;
while (++k < values.length) {
values[k].splice(idx, 0, undefined);
}
row.splice(idx, 0, undefined);
}
row[idx] = data[i][key];
}
values.push(row);
}
return {
columns: columns,
values: values
};
},
// "Preps" the update.
_prepUpdate: function _prepUpdate(data) {
data = (0, _omitBy3.default)(data, _isUndefined3.default);
var vals = [];
var sorted = (0, _keys2.default)(data).sort();
var i = -1;
while (++i < sorted.length) {
vals.push(this.formatter.wrap(sorted[i]) + ' = ' + this.formatter.parameter(data[sorted[i]]));
}
return vals;
},
// Compiles the `order by` statements.
_groupsOrders: function _groupsOrders(type) {
var items = this.grouped[type];
if (!items) return '';
var formatter = this.formatter;
var sql = items.map(function (item) {
var column = item.value instanceof _raw2.default ? formatter.unwrapRaw(item.value) : formatter.columnize(item.value);
var direction = type === 'order' && item.type !== 'orderByRaw' ? ' ' + formatter.direction(item.direction) : '';
return column + direction;
});
return sql.length ? type + ' by ' + sql.join(', ') : '';
}
});
QueryCompiler.prototype.first = QueryCompiler.prototype.select;
// Get the table name, wrapping it if necessary.
// Implemented as a property to prevent ordering issues as described in #704.
Object.defineProperty(QueryCompiler.prototype, 'tableName', {
get: function get() {
if (!this._tableName) {
// Only call this.formatter.wrap() the first time this property is accessed.
var tableName = this.single.table;
var schemaName = this.single.schema;
if (tableName && schemaName) tableName = schemaName + '.' + tableName;
this._tableName = tableName ? this.formatter.wrap(tableName) : '';
}
return this._tableName;
}
});
exports.default = QueryCompiler;
module.exports = exports['default'];