jsharmony-db-sqlite
Version:
jsHarmony Database Connector for SQLite
601 lines (548 loc) • 25.3 kB
JavaScript
/*
Copyright 2017 apHarmony
This file is part of jsHarmony.
jsHarmony is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
jsHarmony is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this package. If not, see <http://www.gnu.org/licenses/>.
*/
/*
Notes:
1. Don't use ";" in any queries except as a statement terminator.
For example, don't use a raw ";" in any strings.
If ";" is used, it must be escaped as \;
The escape function automatically handles escaping ; characters
2. Errors are generated by updating jsharmony_meta(errcode, errmsg)
Notices have an errcode of -1
Warnings have an errcode of -2
*/
var DB = require('jsharmony-db');
var types = DB.types;
var sqlite3 = require('sqlite3');
var _ = require('lodash');
var async = require('async');
var moment = require('moment');
var crypto = require('crypto');
var typeHandler = require('./DB.sqlite.types.js');
var path = require('path');
//Locks
//usage: lock('lockname',function(release){ /* actions */ release(); });
var _locks = {};
function lock(id, f){
if(id in _locks){ setTimeout(function(){ lock(id, f); }, 1); }
else {
_locks[id] = true;
f(function(){ delete _locks[id]; });
}
}
var _INIT_COMPLETE = '%%%INITCOMPLETE%%%';
function DBdriver() {
this.name = 'sqlite';
this.sql = require('./DB.sqlite.sql.js');
this.meta = require('./DB.sqlite.meta.js');
this.connectsql = "drop table if exists jsharmony_meta;";
this.initsql = "\
pragma foreign_keys = ON; \
create table if not exists jsharmony_meta as select 'USystem' context,0 errcode,'' errmsg,'' jsexec,null as audit_seq,0 extra_changes, null last_insert_rowid_override; \
"+_INIT_COMPLETE+";\
";
this.dbpool = {};
this.timeout = 10000;
this.silent = false;
//Initialize platform
this.platform = {
Log: function(msg){ console.log(msg); }, // eslint-disable-line no-console
Config: {
debug_params: {
db_log_level: 6, //Bitmask: 2 = WARNING, 4 = NOTICES :: Database messages logged to the console / log
db_error_sql_state: false //Log SQL state during DB error
}
}
};
this.platform.Log.info = function(msg){ console.log(msg); }; // eslint-disable-line no-console
this.platform.Log.warning = function(msg){ console.log(msg); }; // eslint-disable-line no-console
this.platform.Log.error = function(msg){ console.log(msg); }; // eslint-disable-line no-console
}
DBdriver.prototype.getDefaultSchema = function(){
return '';
};
DBdriver.prototype.logRawSQL = function(sql){
if (this.platform.Config.debug_params && this.platform.Config.debug_params.db_raw_sql && this.platform.Log) {
this.platform.Log.info(sql, { source: 'database_raw_sql' });
}
};
DBdriver.prototype.Init = function (cb) { if(cb) return cb(); };
DBdriver.prototype.Close = function(onClosed, options){
options = _.extend({ force: false }, options);
var _this = this;
//If no remaining connections, run the callback
if(_.isEmpty(_this.dbpool)){ if(onClosed) onClosed(); }
//Close the connections, one at a time
for(var conid in _this.dbpool){
_this.dbpool[conid].DeferClose({ clearTimeout: true });
_this.dbpool[conid].close(function(){
//Remove the connection from the pool
delete _this.dbpool[conid];
//Rerun the Close function to target the next connection
_this.Close(onClosed, options);
});
break;
}
};
DBdriver.prototype.getDBParam = function (dbtype, val) {
var _this = this;
if (!dbtype) throw new Error('Cannot get dbtype of null object');
if (val === null) return 'NULL';
if (typeof val === 'undefined') return 'NULL';
if ((dbtype.name == 'VarChar') || (dbtype.name == 'Char')) {
var valstr = val.toString();
if ((dbtype.length == types.MAX) || (dbtype.length == -1)) return "cast('" + _this.escape(valstr) + "' as text)";
return "cast('" + _this.escape(valstr.substring(0, dbtype.length)) + "' as text)";
}
else if (dbtype.name == 'VarBinary') {
var valbin = null;
if (val instanceof Buffer) valbin = val;
else valbin = new Buffer(val.toString());
if (valbin.length == 0) return "NULL";
return "X'" + valbin.toString('hex').toLowerCase() + "'";
}
else if ((dbtype.name == 'BigInt') || (dbtype.name == 'Int') || (dbtype.name == 'SmallInt') || (dbtype.name == 'TinyInt')) {
var valint = parseInt(val);
if (isNaN(valint)) { return "NULL"; }
return valint.toString();
}
else if (dbtype.name == 'Boolean') {
if((val==='')||(typeof val == 'undefined')) return "NULL";
if(typeHandler.boolParser(val)) return '1';
return '0';
}
else if (dbtype.name == 'Decimal') {
let valfloat = parseFloat(val);
if (isNaN(valfloat)) { return "NULL"; }
return "cast('" + _this.escape(val.toString()) + "' as numeric)";
}
else if (dbtype.name == 'Float') {
let valfloat = parseFloat(val);
if (isNaN(valfloat)) { return "NULL"; }
return "cast('" + _this.escape(val.toString()) + "' as real)";
}
else if ((dbtype.name == 'Date') || (dbtype.name == 'Time') || (dbtype.name == 'DateTime')) {
var suffix = '';
var valdt = null;
if (val instanceof Date) { valdt = val; }
else if(_.isNumber(val) && !isNaN(val)){
valdt = moment(moment.utc(val).format('YYYY-MM-DDTHH:mm:ss.SSS'), "YYYY-MM-DDTHH:mm:ss.SSS").toDate();
}
else {
if (isNaN(Date.parse(val))) return "NULL";
valdt = new Date(val);
}
var mdate = moment(valdt);
if (!mdate.isValid()) return "NULL";
if(!_.isNumber(val)){
//Convert to local on timestamptz and timetz
if('jsh_utcOffset' in val){
//Time is in UTC, Offset specifies amount and timezone
var neg = false;
if(val.jsh_utcOffset < 0){ neg = true; }
suffix = moment.utc(new Date(val.jsh_utcOffset*(neg?-1:1)*60*1000)).format('HH:mm');
//Reverse offset
suffix = ' '+(neg?'+':'-')+suffix;
mdate = moment.utc(valdt);
mdate = mdate.add(val.jsh_utcOffset*-1, 'minutes');
}
if('jsh_microseconds' in val){
var ms_str = "000"+(Math.round(val.jsh_microseconds)).toString();
ms_str = ms_str.slice(-3);
suffix = ms_str.replace(/0+$/,'') + suffix;
}
}
var rslt = '';
if (dbtype.name == 'Date') rslt = "'" + mdate.format('YYYY-MM-DD') + "'";
else if (dbtype.name == 'Time') rslt = "'" + mdate.format('HH:mm:ss.SSS') + suffix + "'";
else rslt = "'" + mdate.format('YYYY-MM-DD HH:mm:ss.SSS') + suffix + "'";
return rslt;
}
throw new Error('Invalid datatype: ' + JSON.stringify(dbtype));
};
DBdriver.prototype.ExecSession = function (dbtrans, dbconfig, session) {
var _this = this;
if (dbtrans) {
session(null, dbtrans.con, '', function () { /* Do nothing */ });
}
else {
if(!dbconfig) throw new Error('dbconfig is required');
var onRelease = function(con, release){
return function () {
//Do not close in-memory databases
if(dbconfig.database == ':memory:') return release();
con.DeferClose();
return release();
};
};
if(path.basename(dbconfig.database)=='___DB_NAME___'){
_this.platform.Log.error('Unconfigured database');
throw new Error('Unconfigured database');
}
lock(dbconfig.database, function(release){
if(dbconfig.database in _this.dbpool){
let con = _this.dbpool[dbconfig.database];
con.DeferClose({ clearTimeout: true });
return session(null, con, _this.initsql + (dbconfig._presql || ''), onRelease(con, release));
}
else {
let con = new sqlite3.Database(dbconfig.database, sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, function(err){
_this.logRawSQL(_this.connectsql);
if (err) { setTimeout(function(){ con.close(); release(); },1); return _this.ExecError(err, session, "DB Connect Error: "+err.toString()); }
con.run(_this.connectsql, function(err, stmt_rslt){
if (err) { setTimeout(function(){ con.close(); release(); },1); return _this.ExecError(err, session, "DB Connect Error: "+err.toString()); }
_this.dbpool[dbconfig.database] = con;
con.DeferClose = DB.util.waitDefer(function(){
con.close(function(){
delete _this.dbpool[dbconfig.database];
});
}, _this.timeout);
session(null, con, _this.initsql + (dbconfig._presql || ''), onRelease(con, release));
});
});
}
});
}
};
DBdriver.prototype.ExecError = function(err, callback, errprefix) {
var errMsg = '';
if(err){
if(err.message && (err.message.indexOf('SQLITE_CONSTRAINT: Application Error')==0)) err.message = err.message.substr(19);
else if(err.message && (err.message.indexOf('SQLITE_CONSTRAINT: Application Warning')==0)) err.message = err.message.substr(19);
else if(err.message && (err.message.indexOf('SQLITE_CONSTRAINT: Execute Form')==0)) err.message = err.message.substr(19);
if(err.number && err.message) errMsg = 'Error '+err.number + ': ' + err.message;
else errMsg = err.toString();
}
if (this.platform.Config.debug_params.db_error_sql_state && !this.silent) this.platform.Log((errprefix || '') + errMsg, { source: 'database' });
if (callback) return callback(err, null);
else throw err;
};
function splitSQL(fsql){
var sql = [];
var lastidx=fsql.lastIndexOf('%%%JSEXEC_ESCAPE(');
//Escape JSEXEC expressions
while(lastidx >= 0){
var endPos = fsql.indexOf(')%%%',lastidx);
if(endPos >= 0){
var match = fsql.substr(lastidx,endPos-lastidx+4);
var expr = match.substr(17);
expr = expr.substr(0,expr.length-4);
expr = expr.replace(/'/g,"''").replace(/\\;/g,"\\\\\\;").replace(/\r/g," ").replace(/\n/g,"\\n ");
fsql = fsql.substr(0,lastidx) + expr + fsql.substr(lastidx+match.length);
}
if(lastidx == 0) lastidx = -1;
else lastidx=fsql.lastIndexOf('%%%JSEXEC_ESCAPE(',lastidx-1);
}
while(fsql){
var nexts = fsql.indexOf(';');
while((nexts > 0) && (fsql[nexts-1]=="\\")) nexts = fsql.indexOf(';', nexts+1);
if(nexts < 0){ sql.push(fsql.trim()); fsql = ''; }
else if(nexts==0) fsql = fsql.substr(1);
else{ sql.push(fsql.substr(0,nexts).trim()); fsql = fsql.substr(nexts+1); }
}
for(var i=0;i<sql.length;i++){
var stmt = sql[i].trim();
//Remove starting comments
while((stmt.indexOf('/*')==0)||(stmt.indexOf('//')==0)||(stmt.indexOf('--')==0)){
if((stmt.indexOf('//')==0)||(stmt.indexOf('--')==0)){
var eolpos = stmt.indexOf('\n');
if(eolpos >= 0) stmt = stmt.substr(eolpos+1);
else stmt = '';
}
else if(stmt.indexOf('/*')==0){
var eoc = stmt.indexOf('*/');
if(eoc >= 0) stmt = stmt.substr(eoc+2);
else stmt = '';
}
stmt = stmt.trim();
}
//Remove empty statements
var is_empty = stmt.match(/^(\s)*$/);
var is_comment = stmt.match(/^(\s)*\/\//);
is_comment = is_comment || stmt.match(/^(\s)*--/);
if(is_empty || is_comment){
sql.splice(i,1);
i--;
continue;
}
stmt = DB.util.ReplaceAll(stmt, "\\;", ';');
sql[i] = stmt;
}
return sql;
}
DBdriver.prototype.ExecConSQL = function(con, sql, cb){
this.logRawSQL(sql);
return con.all(sql, cb);
};
function getSalt(len){
var rslt = '';
var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=][}{|~,.<>?';
for(var i=0;i<len;i++) rslt += chars.charAt(Math.floor(Math.random()*chars.length));
return rslt;
}
DBdriver.prototype.ExecQuery = function(con, sql, conComplete, callback, processor) {
var _this = this;
//Split SQL into an array
sql = splitSQL(sql);
//Set start_idx
var start_idx = 0;
for(var i=0;i<sql.length;i++){
if(!start_idx && (sql[i] == _INIT_COMPLETE)){
start_idx = i;
sql.splice(i,1);
i--;
}
}
//console.log(sql); //Log all SQL statements for debugging
//Run SQL
var notices = [];
//var startTime = Date.now();
var rslt = [];
var idx = 0;
var last_stmt = '';
async.eachSeries(sql, function(stmt, stmt_cb){
idx++;
var is_select = stmt.match(/^(\s)*(select|with)/gi);
var is_pragma = stmt.match(/^(\s)*(pragma)/gi);
last_stmt = stmt;
//var startdt = Date.now();
_this.ExecConSQL(con, stmt, function (err, stmt_rslt) {
//Statement Execution Monitoring
//console.log('Statement '+idx+': '+(Date.now()-startdt));
//if((Date.now()-startdt)>25) console.log(stmt);
if(idx > start_idx){
if(is_select) rslt.push(stmt_rslt);
else if(is_pragma && stmt_rslt && stmt_rslt.length) rslt.push(stmt_rslt);
}
if(err) { /* Do nothing */ } //Don't check for jsharmony_meta(errcode) if err was generated by statement
else if(idx <= start_idx) { /* Do nothing */ } //Don't check for jsharmony_meta(errcode) before jsharmony_meta is initialized
else return _this.ExecConSQL(con, "select errcode, errmsg, jsexec from jsharmony_meta", function(err, stmt_rslt){
idx++;
if(err) return stmt_cb(err);
else if(!stmt_rslt || !stmt_rslt.length) return stmt_cb(new Error('No data returned from jsharmony_meta'));
else if(stmt_rslt[0].errcode && (stmt_rslt[0].errcode==-1)){ notices.push(new DB.Message(DB.Message.NOTICE, stmt_rslt[0].errmsg)); }
else if(stmt_rslt[0].errcode && (stmt_rslt[0].errcode==-2)){ notices.push(new DB.Message(DB.Message.WARNING, stmt_rslt[0].errmsg)); }
else if(stmt_rslt[0].errcode) return stmt_cb({ number: stmt_rslt[0].errcode, message: stmt_rslt[0].errmsg });
if(!stmt_rslt[0].jsexec) return stmt_cb(null);
//Execute jsexec function
var strexec = stmt_rslt[0].jsexec.toString().trim();
if(strexec.substr(strexec.length-1,1)==',') strexec = strexec.substr(0,strexec.length-1);
strexec = '['+strexec+']';
var jsexec = null;
try{
jsexec = JSON.parse(strexec);
}
catch(ex){
return stmt_cb(new Error('Error parsing jsharmony_meta jsexec command: '+strexec));
}
async.eachSeries(jsexec, function(jscmd, jsexec_cb){
if(!jscmd) return jsexec_cb(null);
var jscmdstr = JSON.stringify(jscmd);
if(!jscmd.function) return jsexec_cb(new Error('jsharmony_meta jsexec command missing function: '+jscmdstr));
var f = jscmd.function;
if((f=='sha1')||(f=='sha256')){
//{ "function": "sha1", "table": "jsharmony_pe", "rowid": '||NEW.rowid||', "source":"pe_id||pe_pw1||(select pp_val from jsharmony_v_pp where PP_PROCESS=''USERS'' and PP_ATTRIB=''HASH_SEED_S'')", "dest":"pe_hash" }
//{ "function": "sha256", "table": "jsharmony_pe", "rowid": '||NEW.rowid||', "source":"pe_id||pe_pw1||(select pp_val from jsharmony_v_pp where PP_PROCESS=''USERS'' and PP_ATTRIB=''HASH_SEED_S'')", "dest":"pe_hash" }
if(!jscmd.table) return jsexec_cb(new Error('jsharmony_meta jsexec command missing "table" parameter: '+jscmdstr));
if(!jscmd.rowid) return jsexec_cb(new Error('jsharmony_meta jsexec command missing "rowid" parameter: '+jscmdstr));
if(!jscmd.source && !jscmd.random) return jsexec_cb(new Error('jsharmony_meta jsexec command missing "source" parameter: '+jscmdstr));
if(!jscmd.dest) return jsexec_cb(new Error('jsharmony_meta jsexec command missing "dest" parameter: '+jscmdstr));
var getHash = function(seed){
var rslt = crypto.createHash(f).update((seed||'').toString()).digest();
if(jscmd.substring && parseInt(jscmd.substring)) rslt = rslt.slice(0,parseInt(jscmd.substring));
return rslt;
};
var getSource = function(source_cb){
_this.ExecConSQL(con, "select ("+jscmd.source+") as hash from "+jscmd.table+" where rowid="+_this.getDBParam(types.BigInt,jscmd.rowid), function(err, cmd_rslt){
if(err) return jsexec_cb(err);
if(!cmd_rslt.length) return jsexec_cb(new Error('jsharmony_meta jsexec No results for hash source'));
var hash = getHash(cmd_rslt[0]['hash']||'');
return source_cb(hash);
});
};
if(jscmd.random){
getSource = function(source_cb){
var hash = getHash(getSalt(256));
//Check if hash exists
_this.ExecConSQL(con, "select ("+jscmd.dest+") as hash from "+jscmd.table+" where "+jscmd.dest+"="+_this.getDBParam(types.VarBinary(types.MAX),hash), function(err, cmd_rslt){
if(err) return jsexec_cb(err);
//If hash collision, retry
if(cmd_rslt.length) return getSource(source_cb);
return source_cb(hash);
});
};
}
getSource(function(hash){
_this.ExecConSQL(con, "update "+jscmd.table+" set "+jscmd.dest+"="+_this.getDBParam(types.VarBinary(types.MAX),hash)+" where rowid="+_this.getDBParam(types.BigInt,jscmd.rowid), jsexec_cb);
});
}
else if(f=='soundex'){
//{ "function": "soundex", "source": "(select c_name from c where c_id='||NEW.c_id||')", "dest": "insert into sdx(table_name,field_name,table_id,sdx_word,sdx_val) values(''c'',''c_name'','||NEW.c_id||',(select c_name from c where c_id='||NEW.c_id||'),%%%SOUNDEX%%%)" }
if(!jscmd.source) return jsexec_cb(new Error('jsharmony_meta jsexec command missing "source" parameter: '+jscmdstr));
if(!jscmd.dest) return jsexec_cb(new Error('jsharmony_meta jsexec command missing "dest" parameter: '+jscmdstr));
_this.ExecConSQL(con, "select ("+jscmd.source+") as soundex", function(err, cmd_rslt){
if(err) return jsexec_cb(err);
if(!cmd_rslt.length) return jsexec_cb(new Error('jsharmony_meta jsexec No results for soundex source'));
var soundex = DB.util.Soundex(cmd_rslt[0]['soundex']||'');
_this.ExecConSQL(con, DB.util.ReplaceAll(jscmd.dest, '%%%SOUNDEX%%%', _this.getDBParam(types.VarChar(types.MAX),soundex)), jsexec_cb);
});
}
else if(f=='exec'){
//{ "function": "exec", "sql": "update jsharmony_pe set pe_pw1=null,pe_pw2=null where rowid='||NEW.rowid||'" }
if(!jscmd.sql) return jsexec_cb(new Error('jsharmony_meta jsexec command missing "sql" parameter: '+jscmdstr));
var fsql = splitSQL(jscmd.sql);
async.eachSeries(fsql, function(fsql_stmt,fsql_cb){
last_stmt = fsql_stmt;
_this.ExecConSQL(con, fsql_stmt, fsql_cb);
}, jsexec_cb);
}
else return jsexec_cb(new Error('jsharmony_meta jsexec invalid function: '+jscmdstr));
}, function(err){
if(err) return stmt_cb(err);
_this.ExecConSQL(con, "update jsharmony_meta set jsexec=''", stmt_cb);
});
});
stmt_cb(err);
});
},function(err){
//var totTime = Date.now()-startTime;
//console.log('Executed '+idx+' statements');
//console.log('Time: '+totTime+'ms');
//console.log(Math.round(totTime/idx) + 'ms per statement');
//console.log(sql);
//console.log(rslt); //Log all SQL results for debugging
conComplete();
if (err) { return _this.ExecError(err, callback, 'SQL Error: ' + last_stmt + ' :: '); }
_this.postProcessDataTypes(rslt);
processor(rslt, notices);
});
};
DBdriver.prototype.postProcessDataTypes = function(dbrslt){
if(!dbrslt || !dbrslt.length) return;
for(var i=0;i<dbrslt.length;i++){
var dbtbl = dbrslt[i];
if(!dbtbl || !dbtbl.length) continue;
for(var j=0;j<dbtbl.length;j++){
var row = dbtbl[j];
if(!row) continue;
for(var col in row){
//boolean
if(col.endsWith('__cast_as_boolean')){
var colbase = col.substr(0,col.length-17);
var val = typeHandler.boolParser(row[col]);
row[colbase] = val;
delete row[col];
}
}
}
}
};
DBdriver.prototype.Exec = function (dbtrans, context, return_type, sql, ptypes, params, callback, dbconfig) {
if(!dbconfig) throw new Error('dbconfig is required');
var _this = this;
_this.ExecSession(dbtrans, dbconfig, function (err, con, presql, conComplete) {
if(dbtrans && (dbtrans.dbconfig != dbconfig)) err = new Error('Transaction cannot span multiple database connections');
if(err) {
if (callback != null) callback(err, null);
else throw err;
return;
}
var execsql = presql + _this.getContextSQL(context) + sql;
execsql = _this.applySQLParams(execsql, ptypes, params);
//_this.platform.Log(execsql);
//console.log(params);
//console.log(ptypes);
//Execute sql
_this.ExecQuery(con, execsql, conComplete, callback, function (rslt, notices) {
var dbrslt = null;
if (return_type == 'row') { if (rslt.length && rslt[0].length) dbrslt = rslt[0][0]; }
else if (return_type == 'recordset'){ if(rslt.length) dbrslt = rslt[0]; }
else if (return_type == 'multirecordset') { dbrslt = rslt; }
else if (return_type == 'scalar') {
if (rslt.length && rslt[0].length) {
var row = rslt[0][0];
for (var key in row) if (row.hasOwnProperty(key)) dbrslt = row[key];
}
}
var warnings = [];
for(var i=0;i<notices.length;i++){
if(notices[i].severity=='WARNING'){
warnings.push(notices[i]);
notices.splice(i,1);
i--;
}
}
DB.util.LogDBResult(_this.platform, { sql: execsql, dbrslt: dbrslt, notices: notices, warnings: warnings });
if (callback) callback(null, dbrslt, { notices: notices, warnings: warnings });
});
});
};
DBdriver.prototype.ExecTransTasks = function (execTasks, callback, dbconfig) {
if(!dbconfig) throw new Error('dbconfig is required');
var _this = this;
_this.ExecSession(null, dbconfig, function (err, con, presql, conComplete) {
if(err) return callback(err, null);
//Begin transaction
_this.ExecQuery(con, presql + "begin transaction", function () { }, callback, function () {
var trans = new DB.TransactionConnection(con,dbconfig);
execTasks(trans, function (dberr, rslt) {
if (dberr != null) {
//Rollback transaction
_this.ExecQuery(con, "rollback transaction", conComplete, callback, function () {
callback(dberr, null);
});
}
else {
//Commit transaction
_this.ExecQuery(con, "commit transaction", conComplete, callback, function () {
callback(null, rslt);
});
}
});
});
});
};
DBdriver.prototype.escape = function(val){ return this.sql.escape(val); };
DBdriver.prototype.getContextSQL = function(context) {
var sqlContext = (context?this.escape(context):'USystem');
var ignoreContext = false;
if(sqlContext=='login') ignoreContext = true;
var rslt = "update jsharmony_meta set errcode=0,errmsg=''"+(!ignoreContext?",context='"+sqlContext+"'":"")+",jsexec='',audit_seq=null,extra_changes=0,last_insert_rowid_override=null \
where (errcode<>0) or (errmsg<>'')"+(!ignoreContext?" or (context<>'"+sqlContext+"')":"")+" or (jsexec<>'') or (audit_seq is not null) or (extra_changes <> 0) or (last_insert_rowid_override is not null);";
return rslt;
};
DBdriver.prototype.applySQLParams = function (sql, ptypes, params) {
var _this = this;
//Apply ptypes, params to SQL
var ptypes_ref = {};
if(ptypes){
let i = 0;
for (let p in params) {
ptypes_ref[p] = ptypes[i];
i++;
}
}
//Sort params by length
var param_keys = _.keys(params);
param_keys.sort(function (a, b) { return b.length - a.length; });
//Replace params in SQL statement
for (let i = 0; i < param_keys.length; i++) {
let p = param_keys[i];
var val = params[p];
if (val === '') val = null;
sql = DB.util.ReplaceAll(sql, '@' + p, _this.getDBParam(ptypes ? ptypes_ref[p] : types.fromValue(val), val));
}
return sql;
};
exports = module.exports = DBdriver;