pimatic
Version:
A home automation server and framework for the Raspberry PI running on node.js
274 lines (232 loc) • 9.82 kB
JavaScript
'use strict';
var _ = require('lodash');
var inherits = require('inherits');
var Oracle_Compiler = require('../../oracle/query/compiler');
var ReturningHelper = require('../utils').ReturningHelper;
var BlobHelper = require('../utils').BlobHelper;
function Oracledb_Compiler(client, builder) {
Oracle_Compiler.call(this, client, builder);
}
inherits(Oracledb_Compiler, Oracle_Compiler);
_.assign(Oracledb_Compiler.prototype, {
// Compiles an "insert" query, allowing for multiple
// inserts using a single query statement.
insert: function insert() {
var self = this;
var outBindPrep = this._prepOutbindings(this.single.insert, this.single.returning);
var outBinding = outBindPrep.outBinding;
var returning = outBindPrep.returning;
var insertValues = outBindPrep.values;
if (Array.isArray(insertValues) && insertValues.length === 1 && _.isEmpty(insertValues[0])) {
return this._addReturningToSqlAndConvert('insert into ' + this.tableName + ' (' + this.formatter.wrap(this.single.returning) + ') values (default)', outBinding[0], this.tableName, returning);
}
if (_.isEmpty(this.single.insert) && typeof this.single.insert !== 'function') {
return '';
}
var insertData = this._prepInsert(insertValues);
var sql = {};
if (_.isString(insertData)) {
return this._addReturningToSqlAndConvert('insert into ' + this.tableName + ' ' + insertData, outBinding[0], this.tableName, returning);
}
if (insertData.values.length === 1) {
return this._addReturningToSqlAndConvert('insert into ' + this.tableName + ' (' + this.formatter.columnize(insertData.columns) + ') values (' + this.formatter.parameterize(insertData.values[0]) + ')', outBinding[0], this.tableName, returning);
}
var insertDefaultsOnly = insertData.columns.length === 0;
sql.returning = returning;
sql.sql = 'begin ' + _.map(insertData.values, function (value, index) {
var parameterizedValues = !insertDefaultsOnly ? self.formatter.parameterize(value, self.client.valueForUndefined) : '';
var subSql = 'insert into ' + self.tableName;
if (insertDefaultsOnly) {
// No columns given so only the default value
subSql += ' (' + self.formatter.wrap(self.single.returning) + ') values (default)';
} else {
subSql += ' (' + self.formatter.columnize(insertData.columns) + ') values (' + parameterizedValues + ')';
}
var returningClause = '';
var intoClause = '';
var usingClause = '';
var outClause = '';
_.each(value, function (val) {
if (!(val instanceof BlobHelper)) {
usingClause += ' ?,';
}
});
usingClause = usingClause.slice(0, -1);
// Build returning and into clauses
_.each(outBinding[index], function (ret) {
var columnName = ret.columnName || ret;
returningClause += '"' + columnName + '",';
intoClause += ' ?,';
outClause += ' out ?,';
// Add Helpers to bindings
if (ret instanceof BlobHelper) {
return self.formatter.bindings.push(ret);
}
self.formatter.bindings.push(new ReturningHelper(columnName));
});
// Strip last comma
returningClause = returningClause.slice(0, -1);
intoClause = intoClause.slice(0, -1);
outClause = outClause.slice(0, -1);
if (returningClause && intoClause) {
subSql += ' returning ' + returningClause + ' into' + intoClause;
}
// Pre bind position because subSql is an execute immediate parameter
// later position binding will only convert the ? params
subSql = self.formatter.client.positionBindings(subSql);
var parameterizedValuesWithoutDefaultAndBlob = parameterizedValues.replace('DEFAULT, ', '').replace(', DEFAULT', '').replace('EMPTY_BLOB(), ', '').replace(', EMPTY_BLOB()', '');
return 'execute immediate \'' + subSql.replace(/'/g, "''") + (parameterizedValuesWithoutDefaultAndBlob || value ? '\' using ' : '') + parameterizedValuesWithoutDefaultAndBlob + (parameterizedValuesWithoutDefaultAndBlob && outClause ? ',' : '') + outClause + ';';
}).join(' ') + 'end;';
sql.outBinding = outBinding;
if (returning[0] === '*') {
returning = returning.slice(0, -1);
// Generate select statement with special order by
// to keep the order because 'in (..)' may change the order
sql.returningSql = function () {
return 'select * from ' + self.tableName + ' where ROWID in (' + this.outBinding.map(function (v, i) {
return ':' + (i + 1);
}).join(', ') + ')' + ' order by case ROWID ' + this.outBinding.map(function (v, i) {
return 'when CHARTOROWID(:' + (i + 1) + ') then ' + i;
}).join(' ') + ' end';
};
}
return sql;
},
_addReturningToSqlAndConvert: function _addReturningToSqlAndConvert(sql, outBinding, tableName, returning) {
var self = this;
var res = {
sql: sql
};
if (!outBinding) {
return res;
}
var returningValues = Array.isArray(outBinding) ? outBinding : [outBinding];
var returningClause = '';
var intoClause = '';
// Build returning and into clauses
_.each(returningValues, function (ret) {
var columnName = ret.columnName || ret;
returningClause += '"' + columnName + '",';
intoClause += '?,';
// Add Helpers to bindings
if (ret instanceof BlobHelper) {
return self.formatter.bindings.push(ret);
}
self.formatter.bindings.push(new ReturningHelper(columnName));
});
res.sql = sql;
// Strip last comma
returningClause = returningClause.slice(0, -1);
intoClause = intoClause.slice(0, -1);
if (returningClause && intoClause) {
res.sql += ' returning ' + returningClause + ' into ' + intoClause;
}
res.outBinding = [outBinding];
if (returning[0] === '*') {
res.returningSql = function () {
return 'select * from ' + self.tableName + ' where ROWID = :1';
};
}
res.returning = returning;
return res;
},
_prepOutbindings: function _prepOutbindings(paramValues, paramReturning) {
var result = {};
var params = paramValues || [];
var returning = paramReturning || [];
if (!Array.isArray(params) && _.isPlainObject(paramValues)) {
params = [params];
}
// Always wrap returning argument in array
if (returning && !Array.isArray(returning)) {
returning = [returning];
}
var outBinding = [];
// Handle Buffer value as Blob
_.each(params, function (values, index) {
if (returning[0] === '*') {
outBinding[index] = ['ROWID'];
} else {
outBinding[index] = _.clone(returning);
}
_.each(values, function (value, key) {
if (value instanceof Buffer) {
values[key] = new BlobHelper(key, value);
// Delete blob duplicate in returning
var blobIndex = outBinding[index].indexOf(key);
if (blobIndex >= 0) {
outBinding[index].splice(blobIndex, 1);
values[key].returning = true;
}
outBinding[index].push(values[key]);
}
if (_.isUndefined(value)) {
delete params[index][key];
}
});
});
result.returning = returning;
result.outBinding = outBinding;
result.values = params;
return result;
},
update: function update() {
var self = this;
var sql = {};
var outBindPrep = this._prepOutbindings(this.single.update, this.single.returning);
var outBinding = outBindPrep.outBinding;
var returning = outBindPrep.returning;
var updates = this._prepUpdate(this.single.update);
var where = this.where();
var returningClause = '';
var intoClause = '';
if (_.isEmpty(this.single.update) && typeof this.single.update !== 'function') {
return '';
}
// Build returning and into clauses
_.each(outBinding, function (out) {
_.each(out, function (ret) {
var columnName = ret.columnName || ret;
returningClause += '"' + columnName + '",';
intoClause += ' ?,';
// Add Helpers to bindings
if (ret instanceof BlobHelper) {
return self.formatter.bindings.push(ret);
}
self.formatter.bindings.push(new ReturningHelper(columnName));
});
});
// Strip last comma
returningClause = returningClause.slice(0, -1);
intoClause = intoClause.slice(0, -1);
sql.outBinding = outBinding;
sql.returning = returning;
sql.sql = 'update ' + this.tableName + ' set ' + updates.join(', ') + (where ? ' ' + where : '');
if (outBinding.length && !_.isEmpty(outBinding[0])) {
sql.sql += ' returning ' + returningClause + ' into' + intoClause;
}
if (returning[0] === '*') {
sql.returningSql = function () {
var sql = 'select * from ' + self.tableName;
var modifiedRowsCount = this.rowsAffected.length || this.rowsAffected;
var returningSqlIn = ' where ROWID in (';
var returningSqlOrderBy = ') order by case ROWID ';
// Needs special order by because in(...) change result order
for (var i = 0; i < modifiedRowsCount; i++) {
if (this.returning[0] === '*') {
returningSqlIn += ':' + (i + 1) + ', ';
returningSqlOrderBy += 'when CHARTOROWID(:' + (i + 1) + ') then ' + i + ' ';
}
}
if (this.returning[0] === '*') {
this.returning = this.returning.slice(0, -1);
returningSqlIn = returningSqlIn.slice(0, -2);
returningSqlOrderBy = returningSqlOrderBy.slice(0, -1);
}
return sql += returningSqlIn + returningSqlOrderBy + ' end';
};
}
return sql;
}
});
module.exports = Oracledb_Compiler;