@teamnet/ic-orm
Version:
Database Management System
2,171 lines (1,795 loc) • 73.3 kB
JavaScript
const Fs = require('fs');
const Url = require('url');
const Qs = require('querystring');
const CONN = {};
const CACHE = {};
/**
* Elimina un parámetro de una URL
* @param {string} url - URL de conexión
* @param {string} paramName - Nombre del parámetro a eliminar
* @returns {string} - URL sin el parámetro especificado
*/
function removeParamFromURL(url, paramName) {
var parsedUrl = Url.parse(url, true);
if (parsedUrl.search) {
// Eliminar el parámetro del objeto query
delete parsedUrl.query[paramName];
// Reconstruir la cadena de consulta
parsedUrl.search = '?' + Qs.stringify(parsedUrl.query);
}
// Formato estándar: eliminar la propiedad search para que se use query al formatear
delete parsedUrl.search;
// Reconstruir la URL
return Url.format(parsedUrl);
}
const COMPARE = { '<': '<', '>': '>', '>=': '>=', '=>': '>=', '=<': '<=', '<=': '<=', '==': '=', '===': '=', '!=': '!=', '<>': '!=', '=': '=' };
const MODIFY = { insert: 1, update: 1, modify: 1 };
const TEMPLATES = {};
const REG_FIELDS_CLEANER = /"|`|\||'|\s/g;
const CACHEBLACKLIST = { insert: 1, modify: 1, update: 1, remove: 1 };
// A temporary cache for fields (it's cleaning each 10 minutes)
var FIELDS = {};
var auditwriter;
function promise(fn) {
var self = this;
var $;
if (fn && typeof(fn) === 'object') {
$ = fn;
fn = null;
}
return new Promise(function(resolve, reject) {
self.callback(function(err, result) {
if (err) {
if ($)
$.invalid(err);
else
reject(err);
} else
resolve(fn ? fn(result) : result);
});
});
}
var logger;
function DBMS(errbuilder) {
var self = this;
self.$conn = {};
self.$commands = [];
self.$output = {};
self.response = self.$outputall = {};
self.$eb = global.ErrorBuilder != null;
self.$errors = errbuilder || (global.ErrorBuilder ? new global.ErrorBuilder() : []);
// self.$log;
// self.$lastoutput;
self.$next = function(err) {
err && self.$errors.push(err);
self.next();
};
}
const DP = DBMS.prototype;
DP.promise = promise;
DP.cache = function(key, expire) {
var self = this;
var f = expire[0];
if (f === 'c' || f === 'r') {
exports.cache_set(key, expire);
} else {
self.$cachekey = key;
self.$cacheexpire = expire;
}
return self;
};
DP.blob = function(table) {
if (!table)
table = 'default';
var cache = CACHE[table];
if (!cache) {
var tmp = table.split('/');
cache = { db: tmp.length > 1 ? tmp[0] : 'default', table: tmp.length > 1 ? tmp[1] : tmp[0] };
CACHE[table] = cache;
}
var conn = CONN[cache.db];
var driver = require('./' + conn.db);
return {
write: function(stream, filename, callback) {
if (stream instanceof Buffer) {
if (typeof(filename) === 'function') {
callback = filename;
filename = null;
}
// Creates a temporary file
var tmpfile = PATH.temp(Math.random().toString(16).substring(3) + '.icorm');
Fs.writeFile(tmpfile, stream, function(err) {
if (err) {
callback(err);
return;
}
stream = Fs.createReadStream(tmpfile);
driver.blob_write(conn, stream, filename, callback, cache);
stream.on('close', () => Fs.unlink(tmpfile, NOOP));
});
} else
driver.blob_write(conn, stream, filename, callback, cache);
},
read: function(id, callback) {
driver.blob_read(conn, id, callback, cache);
},
remove: function(id, callback) {
driver.blob_remove(conn, id, callback, cache);
}
};
};
DP.output = function(val) {
this.$output = val;
return this;
};
function debug(val) {
console.log('ICORM --->', val);
}
DP.debug = function() {
this.$debug = debug;
return this;
};
DP.log = DP.audit = function() {
var arg = [];
var self = this;
for (var i = 0; i < arguments.length; i++)
arg.push(arguments[i]);
self.$commands.push({ type: 'audit', arg: arg });
if (!self.busy) {
self.$op && clearImmediate(self.$op);
self.$op = setImmediate(self.$next);
}
return self;
};
DP.invalid = function(name, err) {
var self = this;
self.$errors.push(name, err);
return self;
};
DP.kill = function(reason) {
var self = this;
self.$commands.length = 0;
reason && self.$errors.push(reason);
return self;
};
DP.done = function($, callback, param) {
this.$callback = function(err, response) {
if (err)
$.invalid(err);
else
callback(response, param);
};
return this;
};
DP.callback = function(fn) {
var self = this;
if (typeof(fn) === 'function') {
self.$callback = fn;
return self;
} else {
self.$ = fn;
return new Promise(function(resolve, reject) {
self.$resolve = resolve;
self.$reject = reject;
});
}
};
DP.data = function(fn, param) {
var self = this;
self.$callbackok = fn;
self.$callbackokparam = param;
return self;
};
DP.fail = function(fn, param) {
var self = this;
self.$callbackno = fn;
self.$callbacknoparam = param;
return self;
};
DP.get = function(path) {
var self = this;
path = path.split('.');
return function() {
var data = self.$outputall;
var p, tmp;
for (var i = 0; i < path.length - 1; i++) {
p = path[i];
if (data && data[p] != null) {
data = data[p];
} else {
data = null;
break;
}
}
p = path[path.length - 1];
if (data instanceof Array) {
tmp = [];
for (var i = 0; i < data.length; i++) {
var val = data[i][p];
if (val != null)
tmp.push(val);
}
return tmp;
} else
return data ? data[p] : null;
};
};
DP.next = function() {
var self = this;
if (self.$skip)
return;
var cmd = self.$commands.shift();
logger && loggerend(self);
if (self.$op) {
clearImmediate(self.$op);
self.$op = null;
}
var stop = false;
if (cmd) {
if (cmd.builder && self.prev && self.prev.builder) {
if (cmd.builder.$prevfilter) {
for (var i = 0; i < self.prev.builder.$commands.length; i++)
cmd.builder.$commands.push(self.prev.builder.$commands[i]);
}
if (cmd.builder.$prevfields && self.prev.builder.options.fields)
cmd.builder.options.fields = self.prev.builder.options.fields;
}
if (cmd.builder && cmd.builder.$joinmeta) {
if (!cmd.builder.$joinmeta.can) {
self.$commands.push(cmd);
setImmediate(self.$next);
return;
}
}
if (cmd.builder && cmd.builder.disabled) {
setImmediate(self.$next);
return;
}
if (cmd.type === 'audit') {
auditwriter && auditwriter.apply(self, cmd.arg);
self.$op && clearImmediate(self.$op);
self.$op = setImmediate(self.$next);
} else if (cmd.type === 'task') {
cmd.value.call(self, self.$outputall, self.$lastoutput);
if (self.$errors.length) {
self.$commands = null;
if (self.$) {
self.$.invalid(self.$errors);
self.$ = null;
}
if (self.$callback) {
try {
self.$callback(self.$errors, null);
self.$callback = null;
} catch (e) {
self.unexpected(e);
}
}
if (self.$callbackno) {
try {
self.$callbackno(self.$errors, self.$callbacknoparam);
self.$callbacknoparam = self.$callbackno = null;
} catch (e) {
self.unexpected(e);
}
}
self.forcekill();
} else {
self.$op && clearImmediate(self.$op);
self.$op = setImmediate(self.$next);
}
} else if (cmd.type === 'validate') {
if (cmd.value == null) {
if (self.$lasterror)
stop = true;
} else {
var type = typeof(cmd.value);
switch (type) {
case 'function':
var val = cmd.value(self.$lastoutput, self.$output);
if (typeof(val) === 'string') {
stop = true;
self.$errors.push(val);
}
break;
case 'string':
case 'number':
if (type === 'number')
cmd.value += '';
if (self.$lastoutput instanceof Array) {
if (cmd.reverse) {
if (self.$lastoutput.length) {
self.$errors.push(cmd.value);
stop = true;
}
} else {
if (!self.$lastoutput.length) {
self.$errors.push(cmd.value);
stop = true;
}
}
} else {
if (cmd.reverse) {
if (self.$lastoutput) {
self.$errors.push(cmd.value);
stop = true;
}
} else {
if (!self.$lastoutput) {
self.$errors.push(cmd.value);
stop = true;
}
}
}
break;
}
}
if (stop) {
self.$commands = null;
if (self.$) {
self.$.invalid(self.$errors);
self.$ = null;
}
if (self.$callback) {
try {
self.$callback(self.$errors, null);
self.$callback = null;
} catch (e) {
self.unexpected(e);
}
}
if (self.$callbackno) {
try {
self.$callbackno(self.$errors, self.$callbacknoparam);
self.$callbacknoparam = self.$callbackno = null;
} catch (e) {
self.unexpected(e);
}
}
self.forcekill();
} else
setImmediate(self.$next);
} else {
if (MODIFY[cmd.type] && cmd.value && typeof(cmd.value.$clean) === 'function')
cmd.value = cmd.value.$clean();
var conn = CONN[cmd.conn || cmd.builder.options.db];
// Due to TextDB.query()
if (!conn)
conn = CONN.default;
if (conn) {
if (self.$cachekey) {
exports.cache_get(self.$cachekey, cmd.builder.options.assign || 'default', function(err, cache) {
if (cache) {
cmd.builder.$callback(err, cache.response, cache.count, true);
} else {
logger && loggerbeg(self, cmd);
require('./' + conn.db).run(conn, self, cmd);
}
});
} else {
logger && loggerbeg(self, cmd);
require('./' + conn.db).run(conn, self, cmd);
}
} else {
var err = new Error('Connection string "' + (cmd.conn || cmd.builder.options.db) + '" is not initialized.');
if (cmd.builder)
cmd.builder.$callback(err);
else
cmd.db.$next(err);
}
}
self.prev = cmd;
} else {
self.forcekill();
var err = self.$eb ? self.$errors.items.length > 0 ? self.$errors : null : self.$errors.length > 0 ? self.$errors : null;
if (self.$) {
if (err)
self.$.invalid(err);
else {
try {
self.$resolve(self.$output);
} catch (e) {
self.unexpected(e);
}
}
self.$reject = self.$resolve = null;
self.$ = null;
}
if (self.$callback) {
try {
self.$callback(err, self.$output);
self.$callback = null;
} catch (e) {
self.unexpected(e);
}
}
if (err) {
if (self.$callbackno) {
self.$callbackno(err, self.$callbacknoparam);
self.$callbacknoparam = self.$callbackno = null;
}
} else {
if (self.$callbackok) {
self.$callbackok(self.$output, self.$callbackokparam);
self.$callbackokparam = self.$callbackok = null;
}
}
}
return self;
};
DP.unexpected = function(e) {
this.forcekill();
throw e;
};
DP.forcekill = function() {
var self = this;
if (self.$conn) {
self.closed = true;
for (var key in self.$conn) {
var item = self.$conn[key];
if (item) {
item.$$destroy && item.$$destroy(item);
self.$conn[key] = null;
}
}
}
};
function loggerbeg(self, cmd) {
cmd.ts = new Date();
self.$logger = cmd;
}
function loggerend(self) {
if (self.$logger) {
var ln = (self.$logger.builder.options.db === 'default' ? '' : (self.$logger.builder.options.db + '/')) + (self.$logger.builder.options.table || '');
NOW = new Date();
logger(NOW.format('yyyy-MM-dd HH:mm:ss'), 'ICORM logger: ' + (ln ? (ln + '.') : '') + self.$logger.type + '()', self.$logger.builder.$count + 'x', ((NOW - self.$logger.ts) / 1000) + 's');
self.$logger = null;
}
}
DP.make = function(fn) {
var self = this;
fn.call(self, self);
return self;
};
DP.all = DP.find = function(table) {
var self = this;
var builder = new QueryBuilder(self, 'find');
builder.table(table);
self.$commands.push({ type: 'find', builder: builder });
if (!self.$joinmeta && !self.busy) {
self.$op && clearImmediate(self.$op);
self.$op = setImmediate(self.$next);
}
return builder;
};
DP.bigFind = function(table) {
var self = this;
var builder = new QueryBuilder(self, 'bigFind');
builder.table(table);
self.$commands.push({ type: 'bigFind', builder: builder });
if (!self.$joinmeta && !self.busy) {
self.$op && clearImmediate(self.$op);
self.$op = setImmediate(self.$next);
}
return builder;
};
DP.dif = DP.diff = function(table, form, prop) {
var self = this;
var builder = new QueryBuilder(self, 'diff');
builder.table(table);
self.$commands.push({ type: 'diff', builder: builder, form: form, key: prop || 'id' });
if (!self.busy) {
self.$op && clearImmediate(self.$op);
self.$op = setImmediate(self.$next);
}
return builder;
};
DP.task = function(fn) {
this.$commands.push({ type: 'task', value: fn });
return this;
};
DP.list = DP.listing = function(table, improved) {
var self = this;
var builder = new QueryBuilder(self, 'list');
builder.table(table);
//builder.options.take = 100;
self.$commands.push({ type: 'list', builder: builder, improved: improved });
if (!self.busy) {
self.$op && clearImmediate(self.$op);
self.$op = setImmediate(self.$next);
}
return builder;
};
DP.listCollections = function(db) {
var self = this;
var builder = new QueryBuilder(self, 'listCollections');
self.$commands.push({ type: 'listCollections', builder: builder });
if (!self.busy) {
self.$op && clearImmediate(self.$op);
self.$op = setImmediate(self.$next);
}
return builder;
};
DP.listDatabases = function() {
var self = this;
var builder = new QueryBuilder(self, 'listDatabases');
self.$commands.push({ type: 'listDatabases', builder: builder });
if (!self.busy) {
self.$op && clearImmediate(self.$op);
self.$op = setImmediate(self.$next);
}
return builder;
};
DP.pipeLines = function(table, improved) {
var self = this;
var builder = new QueryBuilder(self, 'pipeLines');
builder.table(table);
self.$commands.push({ type: 'pipeLines', builder: builder, improved: improved });
if (!self.busy) {
self.$op && clearImmediate(self.$op);
self.$op = setImmediate(self.$next);
}
return builder;
};
DP.read = DP.one = function(table) {
var self = this;
var builder = new QueryBuilder(self, 'read');
builder.table(table);
builder.options.first = true;
builder.options.take = 1;
self.$commands.push({ type: 'read', builder: builder });
if (!self.busy) {
self.$op && clearImmediate(self.$op);
self.$op = setImmediate(self.$next);
}
return builder;
};
DP.check = function(table) {
var self = this;
var builder = new QueryBuilder(self, 'check');
builder.table(table);
builder.options.first = true;
builder.options.take = 1;
self.$commands.push({ type: 'check', builder: builder });
if (!self.busy) {
self.$op && clearImmediate(self.$op);
self.$op = setImmediate(self.$next);
}
return builder;
};
DP.stream = function(table, limit, callback, done) {
var self = this;
var builder = new QueryBuilder(self, 'find');
builder.table(table);
builder.options.take = limit;
builder.options.skip = 0;
var count = 0;
var page = 1;
var cb = function(err, response) {
if (!response || response.length === 0) {
// done
done && done(null, count);
return;
}
callback(response, function(stop) {
if (stop) {
done && done(null, count);
return;
}
builder.db.forcekill();
builder.options.skip = limit * (page++);
var db = new DBMS(builder.$errors);
builder.db = db;
db.$commands.push({ type: 'find', builder: builder });
db.$op && clearImmediate(db.$op);
db.$op = setImmediate(db.$next);
});
};
builder.callback(cb);
self.$commands.push({ type: 'find', builder: builder });
if (!self.busy) {
self.$op && clearImmediate(self.$op);
self.$op = setImmediate(function() {
var is = false;
for (var i = 0; i < builder.$commands.length; i++) {
var cmd = builder.$commands[i];
if (cmd.type === 'sort') {
is = true;
break;
}
}
!is && builder.sort('1');
self.$next();
});
}
return builder;
};
DP.scalar = function(table, type, name, field) {
// type: avg
// type: count
// type: group
// type: max
// type: min
// type: sum
var self = this;
var builder = new QueryBuilder(self, 'scalar');
builder.table(table);
self.$commands.push({ type: 'scalar', builder: builder, scalar: type, name: name, field: field });
if (!self.busy) {
self.$op && clearImmediate(self.$op);
self.$op = setImmediate(self.$next);
}
return builder;
};
DP.count = function(table) {
return this.scalar(table, 'count');
};
DP.max = function(table, prop) {
return this.scalar(table, 'max', prop);
};
DP.min = function(table, prop) {
return this.scalar(table, 'min', prop);
};
DP.avg = function(table, prop) {
return this.scalar(table, 'avg', prop);
};
DP.sum = function(table, prop) {
return this.scalar(table, 'sum', prop);
};
DP.group = function(table, prop) {
return this.scalar(table, 'group', prop);
};
DP.begin = function(conn) {
var self = this;
self.$commands.push({ type: 'transaction', db: self, conn: conn || 'default' });
return self;
};
DP.commit = function(conn) {
var self = this;
self.$commands.push({ type: 'commit', db: self, conn: conn || 'default' });
return self;
};
DP.end = function(conn) {
var self = this;
self.$commands.push({ type: 'end', db: self, conn: conn || 'default' });
return self;
};
DP.rollback = DP.abort = function(conn) {
var self = this;
self.$commands.push({ type: 'rollback', db: self, conn: conn || 'default' });
return self;
};
DP.save = function(table, isUpdate, obj, fn) {
if (obj == null || typeof(obj) === 'function') {
fn = obj;
obj = isUpdate;
isUpdate = !!obj.id;
}
var builder = isUpdate ? this.modify(table, obj) : this.insert(table, obj);
fn && fn.call(builder, builder, isUpdate, builder.value);
return builder;
};
DP.add = DP.ins = DP.insert = function(table, value, unique) {
var self = this;
var builder = new QueryBuilder(self, 'insert');
builder.table(table);
builder.value = value || {};
if (unique) {
builder.options.first = true;
builder.options.take = 1;
}
// Total.js schemas
if (builder.value.$clean)
builder.value = builder.value.$clean();
builder.$commandindex = self.$commands.push({ type: 'insert', builder: builder, unique: unique }) - 1;
if (!self.busy) {
self.$op && clearImmediate(self.$op);
self.$op = setImmediate(self.$next);
}
return builder;
};
DP.upd = DP.update = function(table, value, insert) {
var self = this;
var builder = new QueryBuilder(self, 'update');
builder.table(table);
builder.value = value || {};
// Total.js schemas
if (builder.value.$clean)
builder.value = builder.value.$clean();
builder.$commandindex = self.$commands.push({ type: 'update', builder: builder, insert: insert }) - 1;
if (!self.busy) {
self.$op && clearImmediate(self.$op);
self.$op = setImmediate(self.$next);
}
return builder;
};
DP.mod = DP.modify = function(table, value, insert) {
var self = this;
var builder;
if (typeof(value) === 'function') {
builder = new QueryBuilder(self, 'read');
builder.table(table);
builder.options.first = true;
builder.options.take = 1;
builder.$commandindex = self.$commands.push({ type: 'modify2', builder: builder, fn: value, insert: insert }) - 1;
} else {
builder = new QueryBuilder(self, 'modify');
builder.table(table);
builder.value = value || {};
// Total.js schemas
if (builder.value.$clean)
builder.value = builder.value.$clean();
builder.$commandindex = self.$commands.push({ type: 'modify', builder: builder, insert: insert }) - 1;
}
if (!self.busy) {
self.$op && clearImmediate(self.$op);
self.$op = setImmediate(self.$next);
}
return builder;
};
DP.que = DP.query = function(conn, query, value) {
if (query == null || typeof(query) === 'object') {
value = query;
query = conn;
conn = null;
}
var self = this;
var builder = new QueryBuilder(self, 'query');
builder.options.db = conn || 'default';
self.$commands.push({ type: 'query', builder: builder, query: query, value: value });
value && (builder.options.params = true);
if (!self.busy) {
self.$op && clearImmediate(self.$op);
self.$op = setImmediate(self.$next);
}
return builder;
};
DP.rem = DP.remove = function(table) {
var self = this;
var builder = new QueryBuilder(self, 'remove');
builder.table(table);
self.$commands.push({ type: 'remove', builder: builder });
if (!self.busy) {
self.$op && clearImmediate(self.$op);
self.$op = setImmediate(self.$next);
}
return builder;
};
DP.err = DP.error = DP.must = DP.validate = function(err, reverse) {
var self = this;
self.$commands.push({ type: 'validate', value: typeof(err) === 'number' ? (err + '') : err, reverse: reverse });
return self;
};
function QueryBuilder(db, type) {
var self = this;
// cloning
if (db instanceof QueryBuilder) {
self.db = db.db;
self.$ormprimary = db.$ormprimary;
self.$ormprimaryremove = db.$ormprimaryremove;
self.$primarykey = db.$primarykey;
self.$commands = db.$commands.slice(0);
self.options = clone(db.options);
} else {
self.db = db;
self.$commands = [];
self.options = { db: 'default', type: type, take: 0, skip: 0, first: false, fields: null, dynamic: false };
}
}
function clone(obj) {
var keys = Object.keys(obj);
var o = {};
for (var i = 0; i < keys.length; i++)
o[keys[i]] = obj[keys[i]];
return o;
}
const QB = QueryBuilder.prototype;
const NOOP = function(){};
QB.promise = promise;
QB.cache = function(key, expire) {
var self = this;
self.db.cache(key, expire);
return self;
};
QB.primarykey = function(key) {
var self = this;
self.$primarykey = key;
return self;
};
QB.prevfilter = function() {
var self = this;
self.$prevfilter = 1;
return self;
};
QB.custom = function(fn) {
this.$commands.push({ type: 'custom', fn: fn });
return this;
};
QB.prevfields = function() {
var self = this;
self.$prevfields = 1;
return self;
};
QB.use = function(name, arg) {
if (TEMPLATES[name])
TEMPLATES[name](this, arg);
return this;
};
QB.get = function(path) {
return this.db.get(path);
};
QB.log = function(msg, user) {
var self = this;
if (auditwriter) {
self.db.log.apply(self.db, arguments);
return self;
}
if (msg) {
NOW = new Date();
self.$log = (self.$log ? self.$log : '') + NOW.format('yyyy-MM-dd HH:mm:ss') + ' | ' + self.options.table.padRight(25) + ': ' + (user ? '[' + user.padRight(20) + '] ' : '') + msg + '\n';
} else if (self.$log) {
Fs.appendFile(F.path.logs('icorm.log'), self.$log, NOOP);
self.$log = null;
}
return self;
};
QB.table = function(table) {
var self = this;
var cache = CACHE[table];
if (!cache) {
var tmp = table.split('/');
cache = { db: tmp.length > 1 ? tmp[0] : 'default', table: tmp.length > 1 ? tmp[1] : tmp[0] };
cache.type = CONN[cache.db] ? CONN[cache.db].type : '';
CACHE[table] = cache;
}
self.options.path = table;
self.options.db = cache.db;
self.options.table = cache.table;
self.options.dbname = cache.type;
return self;
};
QB.conn = function(name) {
var self = this;
self.options.db = name;
return self;
};
QB.orm = function(primary) {
var self = this;
self.$orm = 1;
self.$ormprimary = primary || '';
if (primary && self.options.fields && self.options.fields.indexOf(primary) === -1) {
self.options.fields.push(primary);
self.$ormprimaryremove = 1;
}
return self;
};
QB.$callback = function(err, value, count, iscache) {
var self = this;
var opt = self.options;
self.$log && self.log();
if (logger)
self.$count = value instanceof Array ? value.length : value != null ? 1 : 0;
if (opt.type === 'list') {
value = { items: value, count: count };
value.page = (opt.skip / opt.take) + 1;
value.limit = opt.take;
value.pages = Math.ceil(count / value.limit);
}
if (value) {
if (self.$orm) {
self.$orm = 2;
if (value instanceof Array) {
for (var i = 0; i < value.length; i++) {
if (opt.fieldsrem) {
for (var j = 0; j < opt.fieldsrem.length; j++)
value[i][opt.fieldsrem[j]] = undefined;
}
value[i].dbms = new QueryBuilder(self);
value[i].dbms.value = value[i];
}
} else {
if (opt.fieldsrem) {
for (var j = 0; j < opt.fieldsrem.length; j++)
value[opt.fieldsrem[j]] = undefined;
}
value.dbms = new QueryBuilder(self);
value.dbms.value = value;
}
} else if (opt.fieldsrem) {
if (value instanceof Array) {
for (var i = 0; i < value.length; i++) {
for (var j = 0; j < opt.fieldsrem.length; j++)
value[i][opt.fieldsrem[j]] = undefined;
}
} else {
for (var j = 0; j < opt.fieldsrem.length; j++)
value[opt.fieldsrem[j]] = undefined;
}
}
}
if (err) {
if (!self.$joinmeta && self.$) {
self.$.invalid(err);
self.$ = null;
}
try {
opt.callback && opt.callback(err, value, count);
} catch (e) {
self.db.unexpected(e);
}
self.db.$errors.push(err);
self.db.$lastoutput = null;
self.db.$outputall[opt.table] = null;
if (opt.callbackno) {
try {
opt.callbackno(err, opt.callbacknoparam);
} catch (e) {
self.db.unexpected(e);
}
opt.callbacknoparam = opt.callbackno = null;
}
self.db.$lasterror = err;
} else {
if (!self.$joinmeta) {
self.db.$outputall[opt.table] = self.db.$lastoutput = value;
if (opt.assign) {
if (!opt.nobind) {
if (self.db.$output == null)
self.db.$output = {};
self.db.$output[opt.assign] = value;
}
self.db.$outputall[opt.assign] = value;
} else if (!opt.nobind)
self.db.$output = value;
var ok = true;
if (opt.validate) {
if (opt.validatereverse) {
if (value instanceof Array) {
if (value.length) {
self.db.$errors.push(opt.validate);
ok = false;
}
} else if (value) {
self.db.$errors.push(opt.validate);
ok = false;
}
} else {
if (value instanceof Array) {
if (!value.length) {
self.db.$errors.push(opt.validate);
ok = false;
}
} else if (!value) {
self.db.$errors.push(opt.validate);
ok = false;
}
}
}
}
if (!self.$joinmeta && self.$) {
err = ok ? null : opt.validate;
if (err)
self.$.invalid(err);
else {
try {
self.$resolve(value);
} catch (e) {
self.db.unexpected(e);
}
}
self.$reject = self.$resolve = null;
}
if (opt.callback) {
try {
opt.callback(ok ? null : opt.validate, value, count);
} catch (e) {
self.db.unexpected(e);
}
}
if (ok) {
if (opt.callbackok) {
try {
opt.callbackok(value, opt.callbackokparam);
} catch (e) {
self.db.unexpected(e);
}
}
} else if (opt.callbackno) {
try {
opt.callbackno(opt.validate, opt.callbacknoparam);
} catch (e) {
self.db.unexpected(e);
}
opt.callbackno = opt.callbacknoparam = null;
}
}
if (self.db.$cachekey && !iscache && !CACHEBLACKLIST[opt.type])
exports.cache_set(self.db.$cachekey, self.db.$cacheexpire, opt.assign || 'default', { response: value, count: count });
if (self.$orm)
opt.callbacknoparam = opt.callbackokparam = opt.callbackok = opt.callbackno = opt.callback = undefined;
if (!self.busy) {
self.db.$op && clearImmediate(self.db.$op);
self.db.$op = setImmediate(self.db.$next);
}
};
QB.nobind = QB.unbind = function() {
this.options.nobind = true;
return this;
};
QB.make = function(fn) {
var self = this;
fn.call(self, self);
return self.db;
};
QB.inc = function(prop, value) {
var self = this;
if (self.$commandindex == null)
throw new Error('This QueryBuilder.inc() is supported for INSERT/UPDATE/MODIFY operations.');
var cmd = self.db.$commands[self.$commandindex];
if (value > 0)
prop = '+' + prop;
else {
prop = '-' + prop;
value = value * -1;
}
if (cmd.value[prop])
cmd.value += value;
else
cmd.value[prop] = value;
return self;
};
QB.set = QB.upd = function(prop, value) {
var self = this;
if (value === undefined) {
self.options.assign = prop == null ? '' : prop;
return self;
}
if (self.$commandindex == null)
throw new Error('This QueryBuilder.inc() is supported for INSERT/UPDATE/MODIFY operations.');
self.model[prop] = value;
return self;
};
// Is same as `set()` without `value`
QB.assign = function(prop) {
var self = this;
self.options.assign = prop == null ? '' : prop;
return self;
};
QB.eq = function() {
var model = arguments[arguments.length - 1];
for (var i = 0; i < arguments.length - 1; i++)
this.where(arguments[i], model[arguments[i]]);
return this;
};
QB.inarray = function(name, value, ornull) {
var self = this;
if (!(value instanceof Array))
value = [value];
self.query((ornull ? ('array_length(' + name + ',1) IS NULL OR ') : '') + name + ' && $1', [value]);
return self;
};
QB.where = function(name, compare, value) {
if (value === undefined) {
value = compare;
compare = '=';
} else {
compare = COMPARE[compare];
if (compare == null)
throw new Error('DBMS: comparer "' + compare + '" is not supported for QueryBuilder.');
}
var self = this;
self.$commands.push({ type: 'where', name: name, value: value, compare: compare });
return self;
};
QB.owner = function(name, value, member, condition) {
var self = this;
self.$commands.push({ type: 'owner', name: name, value: value && value instanceof Object ? (value.user ? value.user.id : value.id) : value, member: member, condition: condition });
return self;
};
QB.permit = function(name, type, value, useridfield, userid, must) {
// type: R read
// type: W write
// type: D delete
var self = this;
var arr = [];
for (var i = 0; i < value.length; i++)
arr.push(type + value[i]);
self.$commands.push({ type: 'permit', name: name, value: arr, useridfield: useridfield, userid: userid, must: must });
return self;
};
QB.id = function(value, field) {
var self = this;
if (value instanceof Array)
self.$commands.push({ type: 'in', name: 'id', value: value, field: field });
else
self.$commands.push({ type: 'where', name: 'id', value: value, compare: '=' });
return self;
};
QB.userid = function(value) {
this.$commands.push({ type: 'where', name: 'userid', value: value, compare: '=' });
return this;
};
QB.undeleted = function() {
this.$commands.push({ type: 'where', name: 'isremoved=FALSE', compare: '=' });
return this;
};
QB.in = function(name, value, field) {
var self = this;
self.$commands.push({ type: 'in', name: name, value: value, field: field });
return self;
};
QB.notin = function(name, value) {
var self = this;
self.$commands.push({ type: 'notin', name: name, value: value });
return self;
};
QB.between = function(name, a, b) {
var self = this;
self.$commands.push({ type: 'between', name: name, a: a, b: b });
return self;
};
QB.search = function(name, value, compare) {
var self = this;
self.$commands.push({ type: 'search', name: name, value: value, compare: compare == null ? '*' : compare });
return self;
};
QB.searchfull = function(value) {
var self = this;
self.$commands.push({ type: 'searchfull', value: value });
return self;
};
QB.searchall = function(name, value) {
var self = this;
if (!(value instanceof Array))
value = value.split(' ');
self.$commands.push({ type: 'searchall', name: name, value: value });
return self;
};
QB.fulltext = function(name, value, weight) {
var self = this;
self.$commands.push({ type: 'fulltext', name: name, value: value, weight: weight });
return self;
};
QB.regexp = function(name, value) {
var self = this;
self.$commands.push({ type: 'regexp', name: name, value: value });
return self;
};
QB.contains = function(name) {
var self = this;
self.$commands.push({ type: 'contains', name: name });
return self;
};
QB.empty = function(name) {
var self = this;
self.$commands.push({ type: 'empty', name: name });
return self;
};
QB.first = function() {
var self = this;
self.options.first = true;
self.take(1);
return self;
};
QB.sort = function(name, desc) {
var self = this;
self.$commands.push({ type: 'sort', name: name, desc: desc == true || desc === 'desc' });
return self;
};
QB.take = function(value) {
var self = this;
self.options.take = value;
return self;
};
QB.skip = function(value) {
var self = this;
self.options.skip = value;
return self;
};
QB.equal = function(val, model) {
var self = this;
var keys = val.split(',');
if (!model)
self.options.equal = [];
for (var i = 0; i < keys.length; i++) {
var k = keys[i][0] === ' ' ? keys[i].substring(1) : keys[i];
if (model)
self.where(k, model[k]);
else
self.options.equal.push(k);
}
return self;
};
QB.limit = function(value) {
var self = this;
self.options.take = value;
return self;
};
QB.page = function(page, limit) {
var self = this;
if (limit)
self.options.take = limit;
self.options.skip = (page - 1) * self.options.take;
return self;
};
QB.paginate = function(page, limit, maxlimit) {
var self = this;
var limit2 = +(limit || 0);
var page2 = (+(page || 0)) - 1;
if (page2 < 0 || !page2)
page2 = 0;
if (maxlimit && limit2 > maxlimit)
limit2 = maxlimit;
if (!limit2)
limit2 = maxlimit;
self.options.skip = page2 * limit2;
self.options.take = limit2;
return self;
};
QB.callback = function(callback) {
var self = this;
// Because of JOINS
if (self.$ && self.$joinmeta && self.$joinmeta.owner && !self.$joinmeta.promise) {
self.$joinmeta.promise = true;
self.$joinmeta.owner.$ = self.$;
self.$joinmeta.owner.$resolve = self.$resolve;
self.$joinmeta.owner.$reject = self.$reject;
}
if (self.options.callback && self.$joinmeta && self.$joinmeta.owner && !self.$joinmeta.callback) {
self.$joinmeta.callback = true;
self.$joinmeta.owner.options.callback = self.options.callback;
}
if (typeof(callback) === 'function') {
self.options.callback = callback;
return self;
}
self.$ = callback;
return new Promise(function(resolve, reject) {
self.$resolve = resolve;
self.$reject = reject;
});
};
QB.done = function($, callback, param) {
return this.callback(function(err, response) {
if (err)
$.invalid(err);
else
callback(response, param);
});
};
QB.debug = function() {
this.db.$debug = debug;
return this;
};
QB.data = function(fn, param) {
var self = this;
// Because of JOINS
if (self.$joinmeta && self.$joinmeta.owner) {
self.$joinmeta.owner.options.callbackok = fn;
self.$joinmeta.owner.options.callbackokparam = param;
} else {
self.options.callbackok = fn;
self.options.callbackokparam = param;
}
return self;
};
QB.fail = function(fn, param) {
var self = this;
// Because of JOINS
if (self.$joinmeta && self.$joinmeta.owner) {
self.$joinmeta.owner.options.callbackno = fn;
self.$joinmeta.owner.options.callbacknoparam = param;
} else {
self.options.callbackno = fn;
self.options.callbacknoparam = param;
}
return self;
};
QB.on = function(a, b) {
var self = this;
self.$joinmeta.a = a;
self.$joinmeta.b = b;
return self;
};
QB.join = function(field, table) {
if (table == null)
table = field;
var self = this;
var builder = new QueryBuilder(self.db, 'find');
builder.table(table);
builder.$joinmeta = { unique: new Set(), field: field, a: '', b: '', owner: self.$joinmeta ? self.$joinmeta.owner : self };
if (self.$joins)
self.$joins.push(builder);
else
self.$joins = [builder];
self.db.$commands.push({ type: 'find', builder: builder });
return builder;
};
QB.err = QB.error = QB.must = QB.validate = function(err, reverse) {
var self = this;
self.options.validate = (typeof(err) === 'number' ? (err + '') : err) || 'unhandled exception';
self.options.validatereverse = reverse;
return self;
};
QB.insert = function(callback, params) {
var self = this;
self.options.insert = callback;
self.options.insertparams = params;
return self;
};
QB.code = QB.query = function(q, value) {
var self = this;
if (!self.options.params && !!value)
self.options.params = true;
self.$commands.push({ type: 'query', query: q, value: value });
return self;
};
QB.or = function(fn) {
var self = this;
var beg = self.$commands.push({ type: 'or' });
fn.call(self, self);
var end = self.$commands.push({ type: 'end' });
if ((end - beg) === 1) {
self.$commands.pop();
self.$commands.pop();
}
return self;
};
QB.subquery = function(name, query) {
if (query == null) {
query = name;
name = null;
}
var self = this;
if (!self.options.subquery)
self.options.subquery = [];
self.options.subquery.push({ name: name, query: query });
return self;
};
QB.fields = function(fields) {
var self = this;
var arr = arguments;
var is = false;
if (arr.length === 1) {
if (FIELDS[fields]) {
self.options.fields = FIELDS[fields];
return self;
}
if (fields.indexOf(',') !== -1) {
arr = fields.split(',');
is = true;
}
}
if (!self.options.fields)
self.options.fields = [];
for (var i = 0; i < arr.length; i++) {
var field = arr[i][0] === ' ' ? arr[i].trim() : arr[i];
self.options.fields.push(field);
}
if (is)
FIELDS[fields] = self.options.fields;
return self;
};
QB.language = function(language, prefix, skip) {
var self = this;
if (skip && language && language === skip)
language = null;
self.options.language = (language ? ((prefix == null ? global.DBMS.languageprefix : (prefix || '')) + language) : '');
self.options.islanguage = true;
return self;
};
QB.year = function(name, compare, value) {
if (value === undefined) {
value = compare;
compare = '=';
} else {
compare = COMPARE[compare];
if (compare == null)
throw new Error('DBMS: comparer "' + compare + '" is not supported for QueryBuilder.');
}
var self = this;
self.$commands.push({ type: 'year', name: name, value: value, compare: compare });
return self;
};
QB.month = function(name, compare, value) {
if (value === undefined) {
value = compare;
compare = '=';
} else {
compare = COMPARE[compare];
if (compare == null)
throw new Error('DBMS: comparer "' + compare + '" is not supported for QueryBuilder.');
}
var self = this;
self.$commands.push({ type: 'month', name: name, value: value, compare: compare });
return self;
};
QB.day = function(name, compare, value) {
if (value === undefined) {
value = compare;
compare = '=';
} else {
compare = COMPARE[compare];
if (compare == null)
throw new Error('DBMS: comparer "' + compare + '" is not supported for QueryBuilder.');
}
var self = this;
self.$commands.push({ type: 'day', name: name, value: value, compare: compare });
return self;
};
QB.hour = function(name, compare, value) {
if (value === undefined) {
value = compare;
compare = '=';
} else {
compare = COMPARE[compare];
if (compare == null)
throw new Error('DBMS: comparer "' + compare + '" is not supported for QueryBuilder.');
}
var self = this;
self.$commands.push({ type: 'hour', name: name, value: value, compare: compare });
return self;
};
QB.minute = function(name, compare, value) {
if (value === undefined) {
value = compare;
compare = '=';
} else {
compare = COMPARE[compare];
if (compare == null)
throw new Error('DBMS: comparer "' + compare + '" is not supported for QueryBuilder.');
}
var self = this;
self.$commands.push({ type: 'minute', name: name, value: value, compare: compare });
return self;
};
QB.date = function(name, compare, value) {
if (value === undefined) {
value = compare;
compare = '=';
} else {
compare = COMPARE[compare];
if (compare == null)
throw new Error('DBMS: comparer "' + compare + '" is not supported for QueryBuilder.');
}
var self = this;
self.$commands.push({ type: 'date', name: name, value: value, compare: compare });
return self;
};
// ORM
QB.wait = function() {
var self = this;
self.db.$skip = true;
self.db.$op && clearImmediate(self.db.$op);
return self;
};
// ORM
QB.remove = function(callback) {
var self = this;
var isnew = false;
if (!self.db || self.db.closed) {
isnew = true;
self.db = new DBMS();
}
self.$orm = 0;
if (self.$ormprimary) {
self.where(self.$ormprimary, self.value[self.$ormprimary] || null);
if (self.$ormprimaryremove)
self.value[self.$ormprimary] = undefined;
}
if (self.options.callbackok)
self.options.callbackok = null;
if (self.options.callbackno)
self.options.callbackno = null;
self.options.callback = callback ? callback : null;
self.db.$commands.push({ type: 'remove', builder: self });
// "next" command is performend when the DBMS instance is new
if (self.db.$skip || isnew) {
self.db.$skip = false;
self.db.$op && clearImmediate(self.db.$op);
self.db.$op = setImmediate(self.db.$next);
}
return self;
};
// ORM
QB.continue = function() {
var self = this;
self.db.$skip = false;
self.db.$op && clearImmediate(self.db.$op);
self.db.$op = setImmediate(self.db.$next);
return self;
};
// ORM
QB.copy = function(val, existing) {
var self = this;
var keys = Object.keys(val);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (key !== 'dbms' && key !== self.$ormprimary && (!existing || self.value[key] !== undefined))
self.value[key] = val[key];
}
return self;
};
// ORM
QB.modified = function(val) {
var self = this;
var keys = Object.keys(self.value);
var data;
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var a = val[key];
if (a === undefined)
continue;
var b = self.value[key];
if (a === b)
continue;
if ((a instanceof Date) && (b instanceof Date)) {
if (a.getTime() === b.getTime())
continue;
} if (a && b && a instanceof Object && b instanceof Object) {
// array or object
if (JSON.stringify(a) === JSON.stringify(b))
continue;
}
if (!data)
data = {};
data[key] = a;
}
if (data) {
data[self.$ormprimary] = self.value[self.$ormprimary];
self.value = data;
} else
self.value = null;
return !!data;
};
QB.replace = function(val) {
var self = this;
var id = self.$ormprimary ? self.value[self.$ormprimary] : null;
self.value = val;
if (self.$ormprimary)
self.value[self.$ormprimary] = id;
return self;
};
// ORM
QB.save = function(callback) {
var self = this;
if (!self.value) {
callback(null, 0);
return;
}
var isnew = false;
if (!self.db || self.db.closed) {
isnew = true;
self.db = new DBMS();
}
self.$orm = 0;
self.db.$commandindex = self.db.$commands.push({ type: 'modify', builder: self }) - 1;
self.options.fields = null;
if (self.options.callbackok)
self.options.callbackok = null;
if (self.options.callbackno)
self.options.callbackno = null;
if (self.$ormprimary) {
self.where(self.$ormprimary, self.value[self.$ormprimary] || null);
if (self.$ormprimaryremove)
self.value[self.$ormprimary] = undefined;
}
self.options.callback = callback ? callback : null;
// "next" command is performed when the DBMS instance is new
if (self.db.$skip || isnew) {
self.db.$skip = false;
self.db.$op && clearImmediate(self.db.$op);
self.db.$op = setImmediate(self.db.$next);
}
return self;
};
exports.QueryBuilder = QueryBuilder;
exports.DBMS = DBMS;
exports.make = function(fn) {
var self = new DBMS();
fn.call(self, self);
return self;
};
exports.init = function(name, connection, onerror) {
if (connection == null) {
connection = name;
name = 'default';
}
if ((onerror === true || connection === true) && global.F) {
onerror = function(err, sql, builder) {
F.error(new Error(err.toString() + ': ' + sql), 'DBMS');
};
}
if (connection === true || typeof(connection) === 'function') {
if (!onerror)
onerror = connection;
connection = name;
name = 'default';
var onerror2 = onerror;
onerror = function(err, sql, builder) {
onerror2(new Error(err.toString() + ': ' + sql), builder);
};
}
// Total.js
if (connection === 'nosql' || connection === 'table') {
CONN[name] = { id: name, db: 'total', type: connection };
return exports;
}
if (connection === 'textdb' || connection === 'inmemory') {
CONN[name] = { db: connection };
return exports;
}
var opt = Url.parse(connection);
var q = Qs.parse(opt.query);
var tmp = {};
var arr;
var pooling = q.pooling ? q.pooling !== '0' && q.pooling !== 'false' && q.pooling !== 'off' : true;
var native = q.native === '1' || q.native === 'true' || q.native === 'on';
switch (opt.protocol) {
case 'postgresql:':
case 'postgres:':
case 'postgre:':
case 'pg:':
if (opt.auth) {
arr = opt.auth.split(':');
tmp.user = arr[0] || '';
tmp.password = arr[1] || '';
}
tmp.host = opt.hostname;
tmp.port = opt.port;
tmp.database = opt.pathname.split('/')[1];
tmp.ssl = q.ssl === '1' || q.ssl === 'true' || q.ssl === 'on';
tmp.max = +(q.max || '4');
tmp.min = +(q.min || '2');
tmp.idleTimeoutMillis = +(q.timeout || '1000');
tmp.native = native;
tmp.pooling = pooling;
CONN[name] = { id: name, db: 'pg', options: tmp, onerror: onerror, type: 'pg' };
// Due to PG_ESCAPE
require('./pg');
break;
case 'mysql:':
case 'mariadb:':
if (opt.auth) {
arr = opt.auth.split(':');
tmp.user = arr[0] || '';
tmp.password = arr[1] || '';
}
tmp.host = opt.hostname;
tmp.port = opt.port;
tmp.database = opt.pathname.split('/')[1];
tmp.ssl = q.ssl === '1' || q.ssl === 'true' || q.ssl === 'on';
tmp.connectionLimit = +(q.max || '4');
// tmp.min = +(q.min || '2');
tmp.waitForConnections = true;
tmp.pooling = pooling;
CONN[name] = { id: name, db: 'mysql', options: tmp, onerror: onerror, type: 'mysql' };
// Due to PG_ESCAPE
require('./mysql');
break;
case 'mongodb+srv:':
case 'mongodb:':
case 'mongo:':
// Para MongoDB, no usamos max/maxPoolSize en la URL de conexión
// Lo aplicamos directamente en las opciones del cliente
// Limpiar la URL de conexión para eliminar parámetros no soportados
var mongoURI = connection;
var maxPoolSize = 10; // valor por defecto
// Extraer el nombre de la base de datos correctamente
// En una URL de replicaset: mongodb://user:pass@host1,host2,host3/dbname?options
// El nombre de la BD está después de la última '/' y antes del '?'
var dbName = '';
var fullUrl = connection;
// Encontrar la última ocurrencia de '/' antes de '?'
var lastSlashIndex = fullUrl.lastIndexOf('/');
var questionMarkIndex = fullUrl.indexOf('?', lastSlashIndex);
if (lastSlashIndex !== -1) {
if (questionMarkIndex !== -1) {
// Hay un '?' después de la última '/'
dbName = fullUrl.substring(lastSlashIndex + 1, questionMarkIndex);
} else {
// No hay '?' después de la última '/'
dbName = fullUrl.substring(lastSlashIndex + 1);
}
}
// Validar que el nombre de la base de datos no contenga caracteres ilegales
if (dbName && dbName.indexOf('.') !== -1) {
console.error('Error: El nombre de la base de datos no puede contener puntos (.):', dbName);
// Eliminar los puntos del nombre de la base de datos
dbName = dbName.replace(/\./g, '_');
console.log('Nombre de base de datos corregido:', dbName);
}
// Extraer maxPoolSize si existe en los parámetros
if (q.max) {
maxPoolSize = parseInt(q.max, 10);
// Crear una nueva URL sin el parámetro max
mongoURI = removeParamFromURL(connection, 'max');
} else if (q.maxPoolSize) {
maxPoolSize = parseInt(q.maxPoolSize, 10);
// Crear una nueva URL sin el parámetro maxPoolSize
mongoURI = removeParamFromURL(connection, 'maxPoolSize');
}
CONN[name] = {
id: name,
db: 'mongo',
options: mongoURI,
database: dbName, // Usar el nombre de base de datos extraído correctamente
onerror: onerror,
type: 'mongodb',
maxPoolSize: maxPoolSize // Configuración de maxPoolSize separada
};
break;
case 'inmemory':
CONN[name] = { id: name, db: 'inmemory', options: connection, table: opt.host, database: opt.host, onerror: onerror, type: 'textdb' };
break;
case 'textdb:':
CONN[name] = { id: name, db: 'textdb', options: connection, table: opt.host, database: opt.host, onerror: onerror, type: 'textdb' };
break;
case 'opendb:':
CONN[name] = { id: name, db: 'opendb', options: connection, table: opt.host, url: connection.replace('opendb:', q.ssl === '1' || q.ssl === 'true' || q.ssl === 'on' ? 'wss:' : 'ws:'), database: opt.host, onerror: onerror, type: 'opendb' };
break;
}
return exports;
};
global.DBMS = function(err) {
if (typeof(err) === 'function') {
var db = new exports.DBMS();
err.call(db, db);
} else
return new exports.DBMS(err);
};
global.DBMS.languageprefix = '_';
global.DBMS.audit = function(fn) {
auditwriter = fn;
};
global.DBMS.logger = function(fn) {
if (fn === undefined)
logger = console.log;
else
logger = fn;
};
global.DBMS.template = function(name, fn) {
TEMPLATES[name] = fn;
};
global.DBMS.measure = function(callback, file) {
if (typeof(callback) === 'boolean') {
var tmp = file;
file = callback;
callback = tmp;
}
var stats = { insert: 0, inserttotal: 0, insertidle: 0, select: 0, selecttotal: 0, selectidle: 0, update: 0, updatetotal: 0, updateidle: 0, query: 0, querytotal: 0, queryidle: 0, delete: 0, deletetotal: 0, deleteidle: 0, count: 0, total: 0, ticks: 0 };
var usage = {};
ON('dbms', function(type, table, db) {
var now = Date.now();
stats.idle = stats.ticks ? (now - stats.ticks) : null;
switch (type) {
case 'insert':
stats.insert++;
stats.inserttotal++;
stats.count++;
stats.total++;
stats.insertidle = stats.insertticks ? (now - stats.insertticks) : null;
stats.insertticks = now;
break;
case 'select':
stats.select++;
stats.selecttotal++;
stats.count++;
stats.total++;
stats.selectidle = stats.selectticks ? (now - stats.selectticks) : null;
stats.selectticks = now;
break;
case 'query':
stats.query++;
stats.querytotal++;
stats.count++;
stats.total++;
stats.queryidle = stats.queryticks ? (now - stats.queryticks) : null;
stats.queryticks = now;
table = table.substring(0, 30);
break;
case 'udpate':
stats.update++;
stats.updatetotal++;
stats.count++;
stats.total++;