psqlorm
Version:
orm for postgresql
963 lines (797 loc) • 26.3 kB
JavaScript
'use strict';
const process = require('node:process');
const randstring = require('./randstring.js');
const makeId = require('./makeId.js');
const makeTimestamp = require('./makeTimestamp.js')
const forbidColumns = require('./forbidColumns.js')
let modelErrorText = '\n\x1b[2;31;47mWarning: model执行后会自动释放模型,'
+ '若需要重复使用,请调用connect()持有当前model实例,并在执行完毕后调用free()释放。\n'
+ '你可能使用了中间变量保存模型实例并多次运行。Model.prototype.connect()可以让您持有此模型实例。'
+ 'Model.prototype.free()用于将模型实例释放到连接池。\x1b[0m\n\n'
+ '\x1b[1;35m程序仍然可以继续执行,因为使用了copy方法复制自身创建了新的model,'
+ '请尽可能修改代码保证更好的性能以及稳定运行!!\n'
+ '如果有必要,请在初始化数据连接后调用psqlorm.prototype.ignoreCopyWarning()方法忽略警告!!\x1b[0m\n';
let saltArr = [
'o', 'u', 'v', 'x', 'w', 'z',
'_', '_', '_', 'x', 'x', 'o',
'o', 'i', 'i', 'p', 'y', '_',
'x', 'x', '_', 'q', 'o', 'n',
'v', 'u', 'k', 'g', 't'
];
let commandTable = {
INSERT: '1',
INSERTS: '2',
GET: '3',
SELECT: '4',
UPDATE: '5',
DELETE: '6'
}
/*
let beforeEventName = {};
beforeEventName[ commandTable.INSERT ] = 'beforeInsert';
beforeEventName[ commandTable.INSERTS ] = 'beforeInsert';
beforeEventName[ commandTable.UPDATE ] = 'beforeUpdate';
beforeEventName[ commandTable.DELETE ] = 'beforeDelete';
*/
let eventName = {};
eventName[ commandTable.INSERT ] = 'insert';
eventName[ commandTable.INSERTS ] = 'insert';
eventName[ commandTable.UPDATE ] = 'update';
eventName[ commandTable.DELETE ] = 'delete';
let state = {
USING: 1,
FREE: 0
};
class Model {
constructor(db, tableName='', schema='public', myparent=null, trigger=null) {
this.odb = db;
this.db = db;
this.tableName = tableName;
this.__schema__ = schema || 'public';
this._schema = this.__schema__;
Object.defineProperty(this, 'parent', {
value: myparent,
enumerable: false,
configurable: false,
writable: false
});
this.makeId = makeId.serialId;
this.bigId = makeId.bigId;
this.tableTrigger = trigger;
//用于事务处理时的锁定。
this.__free_lock__ = false;
this.state = state;
this.__state__ = this.state.USING;
this.__fetch_sql__ = false;
this.__log_sql__ = null;
this.__validate__ = null;
this.__fmtfields__ = null;
this.stag = this.makeQuoteTag(5 + parseInt(Math.random() * 5));
this.lstag = this.stag.substring(0, this.stag.length - 1);
this.sqlUnit = {
command : 0,
values : '',
fields : '',
table : '',
where : '',
limit : '',
order : '',
offset : 0,
join : '',
group: '',
returning: '',
alias: '',
selectFor: ''
};
this.__id_len__ = 16;
this.__id_pre__ = '';
this.__auto_id__ = false;
this.__primary_key__ = 'id';
this.__pkey_type__ = 'v';
this.__insert_timestamp__ = null;
this.__update_timestamp__ = null;
//this.__trigger_before__ = false;
this.__trigger_after__ = false;
this.__trigger_commit__ = false;
this.__transaction__ = false;
this.commitTriggers = [];
}
//此方法的主要目的是如果检测到使用已经释放的model执行sql,则自动copy一个新的model去执行。
//需要注意的是:自动复制以后,需要执行init防止再次引用现在的model因为旧有的数据导致错误。
copy(autoInit=false) {
let m = new this.constructor(this.odb, this.tableName, this.__schema__, this.parent, this.tableTrigger);
//如果是事务操作,则新的model也是事务操作。
m.db = this.db;
m.__schema__ = this.__schema__;
m._schema = this._schema;
//复制以后也要init现在的model。
for (let k in this.sqlUnit) m.sqlUnit[k] = this.sqlUnit[k];
m.__id_len__ = this.__id_len__;
m.__id_pre__ = this.__id_pre__;
m.__auto_id__ = this.__auto_id__;
m.__primary_key__ = this.__primary_key__;
m.__transaction__ = this.__transaction__;
m.__trigger_commit__ = this.__trigger_commit__;
m.__trigger_after__ = this.__trigger_after__;
m.__pkey_type__ = this.__pkey_type__;
//m.__free_lock__ = this.__free_lock__;
m.__insert_timestamp__ = this.__insert_timestamp__;
m.__update_timestamp__ = this.__update_timestamp__;
/**
* 复制的新模型处于锁定状态,执行sql以后不会自动释放到连接池,开发者可以继续执行新的sql。
* 如果是false,则执行后会自动释放回连接池,此时如果开发者不了解内部设计,再次重复使用。
* 会导致再次创建新的model,如此频繁的操作会影响性能。
* 另一个原因是,避免放回连接池后,被其他引用导致无法预知的错误。
* */
m.__free_lock__ = true;
m.commitTriggers = this.commitTriggers;
m.__log_sql__ = this.__log_sql__;
m.__fetch_sql__ = this.__fetch_sql__;
autoInit && this.init();
m.__validate__ = this.__validate__;
m.__fmtfields__ = this.__fmtfields__;
return m;
}
init() {
this.sqlUnit.command = 0;
this.sqlUnit.values = '';
this.sqlUnit.table = '';
this.sqlUnit.alias = '';
this.sqlUnit.fields = '';
this.sqlUnit.where = '';
this.sqlUnit.limit = '';
this.sqlUnit.offset = 0;
this.sqlUnit.join = '';
this.sqlUnit.order = '';
this.sqlUnit.group = '';
this.sqlUnit.returning = '';
this.sqlUnit.selectFor = '';
//this.__trigger_before__ = false;
this.__trigger_after__ = false;
this.__trigger_commit__ = false;
this.__log_sql__ = null;
this.__pkey_type__ = 'v';
this.__insert_timestamp__ = null;
this.__update_timestamp__ = null;
this.__validate__ = null;
this.__fmtfields__ = null;
}
resetIdInfo() {
this.__auto_id__ = false;
this.__primary_key__ = 'id';
this.__id_len__ = 16;
this.__id_pre__ = '';
this.__pkey_type__ = 'v';
}
makeQuoteTag(len = 5) {
return '$_' + randstring(len, saltArr) + '_$';
}
trigger(on = true) {
if (this.__transaction__) {
this.__trigger_commit__ = on;
} else {
this.__trigger_after__ = on;
}
return this;
}
triggerCommit(on = true) {
this.__trigger_commit__ = on;
return this;
}
getSchema() {
return this.__schema__;
}
schema(name) {
name && (this.__schema__ = name);
return this;
}
autoId(b=true) {
this.__auto_id__ = b;
if (b === 'b' || b === 'v') this.__pkey_type__ = b;
return this;
}
setIdLen(ilen) {
if (typeof ilen === 'number' && ilen > 6) this.__id_len__ = ilen;
return this;
}
setIdPre(pre='') {
this.__id_pre__ = pre;
return this;
}
returningPrimary() {
if (this.__primary_key__) this.returning(th.__primary_key__);
return this;
}
primaryKey(k) {
this.__primary_key__ = k;
return this;
}
table(tableName='', schema=null) {
if (typeof tableName === 'string' && tableName.length > 0) {
this.tableName = tableName;
}
if (schema) {
this.__schema__ = schema;
}
return this;
}
resetSchema() {
this.__schema__ = this._schema;
return this;
}
alias(name) {
name && (this.sqlUnit.alias = name);
return this;
}
fetchSql(b=true) {
this.__fetch_sql__ = b;
return this;
}
logSql(callback=null) {
if (callback && typeof callback === 'function') {
this.__log_sql__ = callback;
}
return this;
}
/**
*
* @param {object} tobj
* - insert {array}
* - update {array}
* @returns this
*/
timestamp(tobj) {
if (tobj) {
(tobj.insert !== undefined) && (this.__insert_timestamp__ = tobj.insert);
(tobj.update !== undefined) && (this.__update_timestamp__ = tobj.update);
} else {
this.__insert_timestamp__ = null;
this.__update_timestamp__ = null;
}
return this;
}
quote(a) {
if (a === undefined) throw new Error('传递了undefined值,请检查');
if (typeof a === 'number') {
return a;
}
if (a.indexOf(this.lstag) >= 0) {
a = a.replaceAll(this.lstag, '');
}
return this.stag + a + this.stag;
}
whereIf(icond, cond, args=[]) {
if (icond) return this.where(cond, args);
return this;
}
/**
*
* @param {string | object} cond 条件,如果是字符串,args表示字符串中?要替换的参数
* @param {array} args
*/
where(cond, args=[]) {
let andstr = '';
if (typeof cond === 'string') {
let whstr = '';
let typ = typeof args;
let real_field = this.__fmtfields__ ? (this.__fmtfields__[cond]||cond) : cond;
if (args === undefined)
throw new Error(`${cond} 传递了undefined值,请检查参数输入。`);
switch (typ) {
case 'number':
whstr = real_field + '=' + args;
break;
case 'string':
whstr = real_field + '=' + this.quote(args);
break;
case 'object':
if (Array.isArray(args)) {
if (cond.indexOf('?') < 0) {
whstr = cond;
} else {
let carr = cond.split('?');
let condarr = [];
for (let i=0; i < args.length; i++) {
condarr.push(carr[i]);
condarr.push( this.quote(args[i]) );
}
condarr.push(carr[carr.length-1]);
whstr = condarr.join('');
carr = condarr = null;
}
} else if (args === null) {
whstr = real_field + ' is null';
}
break;
}
//end switch
if (this.sqlUnit.where && whstr) {
andstr = ' and ';
}
this.sqlUnit.where += andstr + whstr;
} else if (typeof cond === 'object' && cond !== null) {
let tmp = [];
let t = null;
let vals = [];
let fmt_k = '';
let cond_value = '';
for (let k in cond) {
if (k[0] === '[' && k.length > 1) {
this.where(k.substring(1, k.length-1), cond[k]);
continue;
}
cond_value = cond[k];
if (this.__fmtfields__) {
fmt_k = this.__fmtfields__[k];
fmt_k && (k = fmt_k);
}
if ( Array.isArray(cond_value) ) {
if (cond_value.length === 0) {
throw new Error(`${k} 传递了空数组。`);
}
vals = [];
for (let i = 0; i < cond_value.length; i++) {
vals.push(this.quote(cond_value[i]));
}
tmp.push(`${k} in (${vals.join(',')})`);
continue;
}
t = typeof cond_value;
if (t === 'number') {
tmp.push(`${k}=${cond_value}`);
} else if (t === 'string') {
tmp.push(`${k}=${this.quote(cond_value)}`);
} else if (t === 'object') {
if (cond_value === null) {
tmp.push(`${k} is null`);
continue;
}
for (let ks in cond_value) {
if (cond_value[ks] === null) {
tmp.push(`${k} ${ks} null`);
continue;
}
tmp.push(`${k} ${ks} ${this.quote(cond_value[ks])}`);
}
}
}
if (tmp.length > 0) {
if (this.sqlUnit.where) {
andstr = ' and ';
}
this.sqlUnit.where += andstr + tmp.join(' and ');
}
}
return this;
}
forUpdate(k='') {
if (!k) {
this.sqlUnit.selectFor = ' for update';
} else {
this.sqlUnit.selectFor = ' for no key update';
}
return this;
}
forShare(k='') {
if (!k) {
this.sqlUnit.selectFor = ' for share';
} else {
this.sqlUnit.selectFor = ' for key share';
}
return this;
}
join(table, on, join_type = 'inner') {
this.sqlUnit.join += ` ${join_type} join ${this.__schema__}.${table} on ${on} `;
return this;
}
leftJoin(table, on) {
return this.join(table, on, 'left');
}
rightJoin(table, on) {
return this.join(table, on, 'right');
}
group(colname) {
let real_field = this.__fmtfields__ ? (this.__fmtfields__[colname]||colname) : colname;
this.sqlUnit.group = `group by ${real_field} `;
return this;
}
order(str, otype='') {
return this.orderby(str, otype)
}
orderby(ostr, otype='') {
let real_field = this.__fmtfields__ ? (this.__fmtfields__[ostr]||ostr) : ostr;
if (this.sqlUnit.order) {
this.sqlUnit.order += `,${real_field} ${otype} `;
} else {
this.sqlUnit.order = `order by ${real_field} ${otype} `;
}
return this;
}
limit(count, offset = 0) {
if (count <= 0) {
this.sqlUnit.limit = `offset ${offset}`;
} else {
this.sqlUnit.limit = `limit ${count} offset ${offset}`;
}
return this;
}
returning(cols) {
let retstr;
if (Array.isArray(cols))
retstr = this.fmtFields(cols).join(',');
else if (typeof cols === 'string' && cols !== '')
retstr = cols;
else
retstr = '';
if (this.sqlUnit.returning) {
if (retstr) this.sqlUnit.returning += ',' + retstr;
} else {
this.sqlUnit.returning = ' returning ' + retstr;
}
return this;
}
psql() {
let sql = '';
let schemaTable = `${this.__schema__}.${this.tableName}`;
if (this.sqlUnit.alias) schemaTable += ` as ${this.sqlUnit.alias}`;
switch (this.sqlUnit.command) {
case commandTable.SELECT:
case commandTable.GET:
sql = `select ${this.sqlUnit.fields} from ${schemaTable}${this.sqlUnit.join}`
+ `${this.sqlUnit.where ? ' where ' : ''}${this.sqlUnit.where} `
+ `${this.sqlUnit.group}${this.sqlUnit.order}${this.sqlUnit.limit}${this.sqlUnit.selectFor};`;
break;
case commandTable.DELETE:
sql = `delete from ${schemaTable} ${this.sqlUnit.where ? 'where ' : ''}${this.sqlUnit.where}${this.sqlUnit.returning};`;
break;
case commandTable.UPDATE:
sql = `update ${schemaTable} set ${this.sqlUnit.values} ${this.sqlUnit.where ? ' where ' : ''} ${this.sqlUnit.where}${this.sqlUnit.returning};`;
break;
case commandTable.INSERT:
case commandTable.INSERTS:
sql = `insert into ${schemaTable} ${this.sqlUnit.fields} values ${this.sqlUnit.values}${this.sqlUnit.returning};`;
break;
}
return sql;
}
async exec() {
if (this.__state__ === this.state.FREE) {
!process.env.PSQLORM_IGNORE_COPY_WARNING && console.error(modelErrorText);
return this.copy(true).exec();
//throw new Error(`Model实例处于释放状态,重复使用可能会导致冲突。提示:connect()用于持有此模型实例,free()用于释放模型实例。\n`);
}
if (!this.tableName) {
throw new Error('你运行的是初始状态的Model,未指定table,请通过方法table(name)设定table名称再次执行。');
}
let sql = this.psql();
let comm = this.sqlUnit.command;
//let is_trigger_b = this.__trigger_before__;
let is_trigger = this.__trigger_after__;
let is_trigger_m = this.__trigger_commit__;
if (this.__log_sql__) {
try {
this.__log_sql__(sql);
} catch (err) {}
}
try {
let ename;
if (this.__fetch_sql__) return sql;
let r = await this.db.query(sql);
let rdata;
switch (comm) {
case commandTable.SELECT:
rdata = r.rows || [];
break;
case commandTable.GET:
rdata = r.rowCount > 0 ? r.rows[0] : null;
break;
case commandTable.INSERT:
rdata = r.rows.length > 0 ? r.rows[0] : r.rowCount;
break;
case commandTable.INSERTS:
rdata = r.rows.length > 0 ? r.rows : r.rowCount;
break;
case commandTable.DELETE:
case commandTable.UPDATE:
if (r.rows.length > 0) {
rdata = (r.rows.length === 1 ? r.rows[0] : r.rows);
} else {
rdata = r.rowCount;
}
break;
}
if ((is_trigger || is_trigger_m) && this.tableTrigger) {
ename = eventName[comm];
if (is_trigger_m) {
this.commitTriggers.push([
this.__schema__, this.tableName, ename, sql, rdata
]);
} else {
ename && this.tableTrigger.emit(ename, this.__schema__, this.tableName, ename, sql, rdata);
}
}
return rdata !== undefined ? rdata : r;
} catch (err) {
throw err;
} finally {
if (!this.__free_lock__) {
this.__free_lock__ = false;
this.parent.free(this);
} else {
//在事务操作中,尽管不释放状态,也要重置执行环境记录,避免继续执行新的操作出错。
this.init();
this.resetIdInfo();
}
}
}
fmtFields(fields) {
if (!this.__fmtfields__) return fields;
let newfields = [];
let fname;
for (let a of fields) {
fname = this.__fmtfields__[a];
newfields.push(fname || a);
}
return newfields;
}
async get(fields = '*') {
return this.select(Array.isArray(fields) ? this.fmtFields(fields) : fields, true);
}
async select(fields = '*', first = false) {
this.sqlUnit.command = first ? commandTable.GET : commandTable.SELECT;
if ( Array.isArray(fields) ) {
this.sqlUnit.fields = this.fmtFields(fields).join(',');
} else if (typeof fields === 'string') {
this.sqlUnit.fields = fields;
}
return this.exec();
}
async delete() {
this.sqlUnit.command = commandTable.DELETE;
return this.exec();
}
validate(data, throw_error = true) {
if (this.__validate__) {
for (let k in this.__validate__) {
if (data[k] !== undefined) {
if (this.__validate__[k](data[k]) === false) {
if (throw_error) {
throw new Error(`data validate: ${k}, 数据不符合要求`)
}
return {ok: false, column: k}
}
}
}
}
return true
}
async insert(data) {
if (Array.isArray(data)) {
return this.insertAll(data)
}
if (this.__auto_id__
&& this.__primary_key__ && typeof this.__primary_key__ === 'string'
&& data[this.__primary_key__] === undefined)
{
if (this.__pkey_type__ === 'v') {
data[this.__primary_key__] = this.makeId(this.__id_len__, this.__id_pre__);
} else {
data[this.__primary_key__] = this.bigId();
}
}
//检测是否自动创建时间戳
this.__insert_timestamp__ && makeTimestamp(data, this.__insert_timestamp__);
this.__update_timestamp__ && makeTimestamp(data, this.__update_timestamp__);
let fields = Object.keys(data);
this.sqlUnit.command = commandTable.INSERT;
this.sqlUnit.fields = `(${this.fmtFields(fields).join(',')})`;
let vals = [];
for (let i = 0; i < fields.length; i++) {
vals.push(this.quote(data[ fields[i] ]));
}
this.validate(data)
this.sqlUnit.values = `(${vals.join(',')})`;
return this.exec();
}
async insertAll(data) {
if (!Array.isArray(data) || data.length == 0) {
throw new Error('data must be array and length > 0');
}
let genid = this.makeId;
if (this.__pkey_type__ === 'b') genid = this.bigId;
if (this.__auto_id__ && this.__primary_key__ && typeof this.__primary_key__ === 'string') {
for (let i = 0; i < data.length; i++) {
if (data[i][this.__primary_key__] === undefined) {
data[i][this.__primary_key__] = genid(this.__id_len__, this.__id_pre__);
}
this.__insert_timestamp__ && makeTimestamp(data[i], this.__insert_timestamp__);
this.__update_timestamp__ && makeTimestamp(data[i], this.__update_timestamp__);
this.validate(data[i]);
}
}
this.sqlUnit.command = commandTable.INSERTS;
let fields = Object.keys(data[0]);
this.sqlUnit.fields = `(${this.fmtFields(fields).join(',')})`;
let vals = [];
let vallist = [];
let data_item;
for (let i=0; i < data.length; i++) {
vals = [];
data_item = data[i];
for (let i = 0; i < fields.length; i++) {
vals.push(this.quote(data_item[ fields[i] ]));
}
vallist.push(`(${vals.join(',')})`);
}
this.sqlUnit.values = vallist.join(',');
return this.exec();
}
async update(data) {
this.sqlUnit.command = commandTable.UPDATE;
if (typeof data === 'string') {
this.sqlUnit.values = data;
} else {
this.validate(data);
let vals = [];
this.__update_timestamp__ && makeTimestamp(data, this.__update_timestamp__);
let fmt_k;
let val;
for (let k in data) {
val = data[k];
if (k[0] === '@') {
k = k.substring(1);
fmt_k = this.__fmtfields__ && this.__fmtfields__[k] ? this.__fmtfields__[k] : k;
vals.push(`${fmt_k}=${val}`);
continue;
}
fmt_k = this.__fmtfields__ && this.__fmtfields__[k] ? this.__fmtfields__[k] : k;
vals.push(`${fmt_k}=${this.quote(val)}`);
}
this.sqlUnit.values = vals.join(',');
}
return this.exec();
}
async count(count_column = '*') {
let r = await this.get(`count(${count_column}) as total`);
if (this.__fetch_sql__) return r;
return r === null ? 0 : parseInt(r.total);
}
toValue(val, type, prec = 0) {
switch (type) {
case 'int':
return parseInt(val);
case 'float':
return parseFloat(val);
case 'fixed':
case 'fixed-float':
let a = parseFloat(val);
if (isNaN(a)) return val;
if (type === 'fixed') return a.toFixed(prec);
return parseFloat( a.toFixed(prec) );
}
return val;
}
async avg(field, to = '', prec = 1) {
let r = await this.get(`avg(${this.__fmtfields__ ? (this.__fmtfields__[field]||field) : field}) as average`);
if (this.__fetch_sql__) return r;
if (r === null) return null;
if (to) return this.toValue(r.average, to, prec);
return r.average;
}
async max(field, to = '', prec = 1) {
let r = await this.get(`max(${this.__fmtfields__ ? (this.__fmtfields__[field]||field) : field}) as m`);
if (this.__fetch_sql__) return r;
if (r === null) return null;
if (to) return this.toValue(r.m, to, prec);
return r.m;
}
async min(field, to = '', prec = 1) {
let r = await this.get(`min(${this.__fmtfields__ ? (this.__fmtfields__[field]||field) : field}) as m`);
if (this.__fetch_sql__) return r;
if (r === null) return null;
if (to) return this.toValue(r.m, to, prec);
return r.m;
}
async sum(field, to = '', prec = 1) {
let r = await this.get(`sum(${this.__fmtfields__ ? (this.__fmtfields__[field]||field) : field}) as sum_value`);
if (this.__fetch_sql__) return r;
if (r === null) return null;
if (to) return this.toValue(r.sum_value, to, prec);
return r.sum_value;
}
free() {
this.parent.free(this);
}
/**
* 锁定模型,不释放。
* @returns {this}
*/
connect() {
if (this.__state__ === this.state.FREE) {
let m = new this.constructor(this.odb,
this.tableName,
this.__schema__,
this.parent,
this.tableTrigger);
m.__free_lock__ = true;
return m;
//throw new Error(`无法连接模型实例,您可能进行了错误操作:先执行sql然后再次调用connect()`);
}
this.__free_lock__ = true;
return this;
}
/**
*
* @param {object} db 数据库连接的pg.Client客户端实例
* @returns {this}
*/
bind(db) {
if (db.constructor.name === 'Model' || (db.db && db.db.constructor.name === 'BoundPool') || (db instanceof Model))
{
this.db = db.db;
this.commitTriggers = db.commitTriggers;
//this.__trigger_commit__ = db.__trigger_commit__;
this.__free_lock__ = true;
this.__transaction__ = db.__transaction__;
} else if (db.constructor.name === 'BoundPool' || db.constructor.name === 'Client') {
this.db = db;
}
return this;
}
async transaction(callback) {
if (typeof callback !== 'function' || callback.constructor.name !== 'AsyncFunction') {
throw new Error('callback must be async function');
}
let finalRet = {
result : null,
ok : null,
message : '',
error: null,
throwFailed: (err = 'Transaction failed') => {
finalRet.ok = false;
let ty = typeof err;
if (ty !== 'object') throw new Error(err);
throw err;
},
failed: (errmsg = 'Transaction failed') => {
finalRet.ok = false;
finalRet.message = errmsg;
},
}
try {
this.db = await this.odb.connect();
//事务中,锁定释放。
this.__free_lock__ = true;
this.__transaction__ = true;
await this.db.query('begin');
let rval = await callback(this, finalRet);
if (finalRet.ok === false) {
throw new Error(finalRet.message);
}
await this.db.query('commit');
if (this.commitTriggers.length > 0 && this.tableTrigger) {
let tlen = this.commitTriggers.length;
let tmp;
for (let i = 0; i < tlen; i++) {
tmp = this.commitTriggers[i];
// 0:schema 1:table 2:evtname 3:sql 4:data
this.tableTrigger.emit(tmp[2], tmp[0], tmp[1], tmp[2], tmp[3], tmp[4]);
}
}
;(finalRet.ok === null) && (finalRet.ok = true);
;(rval !== undefined && finalRet.result === null) && (finalRet.result = rval);
} catch (err) {
this.db.query('rollback');
finalRet.message = err.message || 'Transaction failed';
finalRet.ok = false;
finalRet.error = err;
} finally {
this.db.release();
this.db = this.odb;
this.__free_lock__ = false;
this.parent.free(this);
}
return finalRet;
}
}
module.exports = Model