febs-db
Version:
febs-db is a ORM
1,170 lines (1,030 loc) • 33.8 kB
JavaScript
;
/**
* Copyright (c) 2016 Copyright citongs All Rights Reserved.
* Author: lipengxiang
*/
var debug = require('debug')('febs-db');
var mssql = require('mssql');
var febs = require('febs');
var assert = require('assert');
var copy = require('copy-to');
var BigNumber = require('febs').BigNumber;
var exception = require('../exception');
var dataType = require('../dataType');
var table_helper = require('./table_helper');
var type_cast = require('./type_cast');
var handle_err = require('./handle_err');
var isolationLevel = require('../isolationLevel');
var procedureParams = require('../procedureParams');
var PromiseLib = Promise;
var g_opt;
module.exports = {
escape_like,
createPool,
errHandle,
exec,
execProcedure,
transaction,
type_cast,
table_helper,
opt_cvt,
ret_data_cvt,
add,
remove,
update,
selectLockRow,
select,
count,
exist,
sql_add,
sql_count,
sql_remove,
sql_select,
sql_selectLockRow,
sql_update,
}
/**
* @desc: 转换查询结果.
* @return:
*/
function ret_cvt(ret) {
let cret = {};
if (!ret) {
cret.rows = [];
cret.rowsAffected = 0;
return cret;
}
if (ret.rowsAffected && ret.rowsAffected.length > 0) {
cret.rowsAffected = ret.rowsAffected[0];
}
if (ret.recordset && ret.recordset.length > 0) {
//cret.rowsAffected = ret.recordset.length;
cret.rows = ret.recordset;
if (ret.recordset[0].identity) {
cret.insertId = ret.recordset[0].identity;
cret.insertId = dataType.getValueBigInt(cret.insertId);
}
} else {
cret.rows = [];
}
return cret;
}
/**
* @desc: 转换查询结果中的数据,按类型转换.
* @param alreadyCvt: 用于防止重复转换.
* @return:
*/
function ret_data_cvt(rows, table, alreadyCvt = null) {
for (let i = 0; i < rows.length; i++) {
if (alreadyCvt) {
if (!alreadyCvt[i]) alreadyCvt[i] = {};
}
for (let k in rows[i]) {
delete rows[i]['RowNumber'];
let rname = table.getLogicColName(k);
if (!rname) continue;
rname = rname.split('.');
rname = rname[rname.length-1];
let kk = table.model[rname];
if (kk) {
// 转换过不再处理.
if (alreadyCvt) {
if (alreadyCvt[i][kk.map])
continue;
alreadyCvt[i][kk.map] = true;
}
let t = dataType.getType(kk.type);
// fix bigint col.
if (typeof rows[i][kk.map] === 'string' && t === dataType.BigInt().type) {
rows[i][kk.map] = dataType.getValueBigInt(rows[i][kk.map]);
}
// fix datetime.
else if (t == dataType.DateTime().type) {
if (rows[i][kk.map] instanceof Date) {
if (rows[i][kk.map] == 'Invalid Date')
rows[i][kk.map] = null;
// else {
// let localDate = Date.UTC(
// rows[i][kk.map].getFullYear(),
// rows[i][kk.map].getMonth(),
// rows[i][kk.map].getDate(),
// rows[i][kk.map].getHours(),
// rows[i][kk.map].getMinutes(),
// rows[i][kk.map].getSeconds(),
// rows[i][kk.map].getMilliseconds(),
// );
// rows[i][kk.map] = new Date(localDate);
// }
} else { // string.
if (!rows[i][kk.map]) {
rows[i][kk.map] = null;
continue;
}
let ttt = new Date(rows[i][kk.map]);
if (ttt == 'Invalid Date')
rows[i][kk.map] = null;
// else
// rows[i][kk.map] = new Date(Date.UTC(ttt.getFullYear(), ttt.getMonth(), ttt.getDate(), ttt.getHours(), ttt.getMinutes(), ttt.getSeconds(), ttt.getMilliseconds()));
}
}
// // fix boolean col.
// else if (t === dataType.Bit().type) {
// rows[i][kk.map] = dataType.getValueBit(rows[i][kk.map]);
// }
}
if (rname != kk.map) {
rows[i][rname] = rows[i][kk.map];
delete rows[i][kk.map];
}
}
}
return rows;
}
/**
* @desc: get mssql data type.
* @return:
*/
function data_type_cvt(type) {
if (type.type === dataType.VarChar().type)
return mssql.VarChar(type.length);
else if (type.type === dataType.NVarChar().type)
return mssql.NVarChar(type.length);
else if (type.type === dataType.Text().type)
return mssql.Text;
else if (type.type === dataType.NText().type)
return mssql.NText;
else if (type.type === dataType.Char().type)
return mssql.Char(type.length);
else if (type.type === dataType.NChar().type)
return mssql.NChar(type.length);
else if (type.type === dataType.Bit().type)
return mssql.Bit;
else if (type.type === dataType.BigInt().type)
return mssql.BigInt;
else if (type.type === dataType.TinyInt().type)
return mssql.TinyInt;
else if (type.type === dataType.SmallInt().type)
return mssql.SmallInt;
else if (type.type === dataType.Int().type)
return mssql.Int;
else if (type.type === dataType.Float().type)
return mssql.Float;
else if (type.type === dataType.Numeric().type)
return mssql.Numeric(type.precision, type.scale);
else if (type.type === dataType.Decimal().type)
return mssql.Decimal(type.precision, type.scale);
else if (type.type === dataType.Real().type)
return mssql.Real;
else if (type.type === dataType.DateTime().type)
return mssql.SmallDateTime;
else if (type.type === dataType.Binary().type)
return mssql.Binary;
else if (type.type === dataType.VarBinary().type)
return mssql.VarBinary(type.length);
else
throw new exception(febs.exception.PARAM, febs.exception.PARAM, __filename, __line);
}
/**
* @desc: 执行事务过程.
* @return:
*/
async function do_transaction(db, fooCB, preCB, commitCB, rollbackCB, resolve, reject, rejectCb, conn, needCommit) {
try {
if (preCB)
await preCB(db);
}
catch (e) {
let db1 = db.__db || db;
if (!db1.__rolledBack) {
conn.rollback((err)=>{
debug('[ROLLBACK] ' + (err?"err":"ok"));
try {
if (db.sqlLogCallback) db.sqlLogCallback(err, 'ROLLBACK TRAN', true);
} catch (e) { console.log('[error in sqlLogCallback]'); console.log(e); }
if (err) {
db._destroy();
reject(handle_err('ROLLBACK', e, __filename, __line));
rejectCb && rejectCb(e);
} else {
db._destroy();
reject(handle_err('ROLLBACK', e, __filename, __line));
rejectCb && rejectCb(e);
}
});
} else {
db._destroy();
reject(handle_err('ROLLBACK', e, __filename, __line));
rejectCb && rejectCb(e);
}
return;
}
try {
let r = await fooCB(db);
if (r === true) {
if (commitCB) r = await commitCB(db);
}
if (r === true) {
if (needCommit) {
conn.commit(err => {
debug('[COMMIT] ' + (err?"err":"ok"));
try {
if (db.sqlLogCallback) db.sqlLogCallback(err, 'COMMIT TRAN', true);
} catch (e) { console.log('[error in sqlLogCallback]'); console.log(e); }
if (err) {
reject(handle_err('COMMIT', err, __filename, __line));
rejectCb && rejectCb(err);
} else {
resolve(true);
}
});
}
else {
resolve(true);
}
}
else {
if (rollbackCB) await rollbackCB(db);
let db1 = db.__db || db;
if (!db1.__rolledBack)
{
conn.rollback(err=>{
debug('[ROLLBACK] ' + (err?"err":"ok"));
try {
if (db.sqlLogCallback) db.sqlLogCallback(err, 'ROLLBACK TRAN', true);
} catch (e) { console.log('[error in sqlLogCallback]'); console.log(e); }
if (err) {
reject(handle_err('ROLLBACK', err, __filename, __line));
rejectCb && rejectCb(err);
} else {
resolve(false);
}
});
} else {
reject(handle_err('ROLLBACK', 'rollback', __filename, __line));
}
if (needCommit)
db._destroy();
} // if..else.
}
catch (e) {
if (rollbackCB) await rollbackCB(db);
let db1 = db.__db || db;
if (!db1.__rolledBack) {
conn.rollback((err)=>{
debug('[ROLLBACK] ' + (err?"err":"ok"));
try {
if (db.sqlLogCallback) db.sqlLogCallback(err, 'ROLLBACK TRAN', true);
} catch (e) { console.log('[error in sqlLogCallback]'); console.log(e); }
if (err) {
db._destroy();
reject(handle_err('ROLLBACK', e, __filename, __line));
rejectCb && rejectCb(e);
} else {
db._destroy();
reject(handle_err('ROLLBACK', e, __filename, __line));
rejectCb && rejectCb(e);
}
});
} else {
db._destroy();
reject(handle_err('ROLLBACK', e, __filename, __line));
rejectCb && rejectCb(e);
}
}
}
/**
* @desc: 执行存储过程
* @return:
*/
function do_procedure(db, req, name, params, resolve, reject, rejectCb, filename, line) {
try {
for (let i = 0; i < params.params.length; i++) {
let pi = params.params[i];
if (pi.in) {
req = req.input(pi.name, data_type_cvt(pi.type), pi.value);
} else {
req = req.output(pi.name, data_type_cvt(pi.type));
}
} // for.
} catch (err) {
reject(handle_err('procedurce ' + name + ' params is error', err, filename, line));
rejectCb && rejectCb(err);
return;
}
try {
req.execute(name, (err, result) => {
try {
if (db.sqlLogCallback) db.sqlLogCallback(err, 'exec ' + name + ' ...');
} catch (e) {
console.log('[error in sqlLogCallback]'); console.log(e);
}
if (err) { reject(handle_err('exec ' + name + ' ...', err, filename, line)); (rejectCb && rejectCb(err)); return; }
let cret = ret_cvt(result);
cret.out = result.output;
resolve(cret);
});
} catch (err) {
reject(handle_err('procedurce ' + name + ' execute error', err, filename, line));
rejectCb && rejectCb(err);
return;
}
}
/**
* @desc: convert opt to mssql opt.
* @return:
*/
function opt_cvt(opt) {
opt.connectionTimeout = opt.hasOwnProperty('connectTimeout') ? opt.connectTimeout : 5000;
opt.requestTimeout = opt.hasOwnProperty('queryTimeout') ? opt.queryTimeout : 5000;
opt.pool = {
acquireTimeoutMillis : opt.hasOwnProperty('acquireTimeout') ? opt.acquireTimeout : 5000,
maxWaitingClients : opt.hasOwnProperty('queueLimit') ? opt.queueLimit : 200,
max : opt.hasOwnProperty('connectionLimit') ? opt.connectionLimit : 10,
min : 8,
idleTimeoutMillis : opt.hasOwnProperty('idleTimeoutMillis') ? opt.idleTimeoutMillis : 600000,
};
opt.server = opt.host;
opt.port = opt.hasOwnProperty('port') ? opt.port : 3306;
opt.database = opt.database;
opt.user = opt.user;
opt.password = opt.password;
if (opt.encrypt) {
opt.options = {encrypt:true};
}
return opt;
}
function escape_like(str) {
// str = febs.string.replace(str, "\\", "\\\\");
str = febs.string.replace(str, "'", "''");
str = febs.string.replace(str, "[", "[[]");
str = febs.string.replace(str, "%", "[%]");
return febs.string.replace(str, "_", "[_]");
}
/**
* @desc: create connection pool.
* @return:
*/
function createPool(opt) {
g_opt = opt;
const pool1 = new mssql.ConnectionPool(opt);
return pool1;
}
// /**
// * @desc: 判断是否需要删除连接.
// * @return: boolean.
// */
// function needReleaseConnection(err) {
// if (err.name == 'TimeoutError' && err.message == 'ResourceRequest timed out') {
// return true;
// } else {
// return false;
// }
// }
/**
* @desc: 进行db的错误处理.
* @return:
*/
function errHandle(db, pool) {
pool.on('error', err=>{
if (err) {
if (err.name == 'TransactionError') {
if (1) {
console.log('[mssql error]');
console.log(err);
}
}
if (err.name == 'RequestError') {
if (err.code != 'ECANCEL' || err.code != 'ETIMEOUT') {
console.log('[mssql error]');
console.log(err);
}
}
if (err.name == 'PreparedStatementError') {
if (err.code == 'EARGS' || err.code == 'EINJECT') {
console.log('[mssql error]');
console.log(err);
}
}
} // if.
})
}
/**
* @desc: 执行sql语句.
* @return: (Promise) 正确返回结果.
*/
function exec(db, pool, sql, conn) {
assert(!febs.string.isEmpty(sql));
return new PromiseLib((resolve, reject)=>{
try {
let db1 = db.__db || db;
if (db1.__rolledBack) {
reject(handle_err(sql, 'already rollbacked', __filename, __line));
return;
}
// use new conn.
if (!conn) {
// other is connecting.
if (pool.connecting) {
// db1.__waitExecArray = db1.__waitExecArray || [];
// db1.__waitExecArray.push({type:'s',sql,resolve,reject});
reject(handle_err(sql, 'db is connecting', __filename, __line));
return;
}
// to connect pool.
else if (!pool.connected) {
try {
pool.connect(err=>{
if (err) {
// 执行等待exec列表.
// reject_wait(db1, err, __filename, __line);
reject(handle_err(sql, err, __filename, __line));
return;
}
// 执行等待exec列表.
// do_wait(db1, pool, __filename, __line);
try {
let req = new mssql.Request(pool); // or: new sql.Request(pool1)
req.query(sql, (err, result) => {
try {
if (db.sqlLogCallback) db.sqlLogCallback(err, sql);
} catch (err) { console.log('[error in sqlLogCallback]'); console.log(err); }
if (err) {
reject(handle_err(sql, err, __filename, __line));
return;
}
resolve(ret_cvt(result));
});
} catch (err) {
reject(handle_err(sql, err, __filename, __line));
}
});
} catch (e) {
reject(handle_err('', 'connect err: ' + e.toString(), __filename, __line));
}
}
// query.
else {
//!!!: 执行等待exec列表.
// do_wait(db1, pool, __filename, __line);
let req = new mssql.Request(pool); // or: new sql.Request(pool1)
req.query(sql, (err, result) => {
try {
if (db.sqlLogCallback) db.sqlLogCallback(err, sql);
} catch (err) { console.log('[error in sqlLogCallback]'); console.log(err); }
if (err) {
reject(handle_err(sql, err, __filename, __line));
return;
}
resolve(ret_cvt(result));
});
}
}
else {
//!!!: 执行等待exec列表.
// do_wait(db1, pool, __filename, __line);
let req = new mssql.Request(conn);
req.query(sql, (err, result)=>{
try {
if (db.sqlLogCallback) db.sqlLogCallback(err, sql);
} catch (err) { console.log('[error in sqlLogCallback]'); console.log(err); }
if (err) {
reject(handle_err(sql, err, __filename, __line));
return;
}
resolve(ret_cvt(result));
});
}
} catch (e) {
reject(handle_err(sql, e, __filename, __line));
}
});
}
/**
* @desc:
* @return: (Promise) 正确返回结果.
*/
function execProcedure(db, pool, name, params, conn) {
assert(!febs.string.isEmpty(name));
return new PromiseLib((resolve, reject)=>{
if (!(params instanceof procedureParams)) {
reject(handle_err('', new exception(febs.exception.PARAM, febs.exception.PARAM, __filename, __line))); return;
}
let db1 = db.__db || db;
if (db1.__rolledBack) {
reject(handle_err(sql, 'already rollbacked', __filename, __line)); return;
}
let sql;
try {
// use new conn.
if (!conn) {
// other is connecting.
if (pool.connecting) {
// let params1 = {};
// copy(params).to(params1);
// db1.__waitExecArray = db1.__waitExecArray || [];
// db1.__waitExecArray.push({type:'p',name, params:params1,resolve,reject});
reject(handle_err(sql, 'db is connecting', __filename, __line));
return;
}
// to connect pool.
else if (!pool.connected) {
try {
pool.connect(err=>{
if (err) {
// 执行等待exec列表.
// reject_wait(db1, err, __filename, __line);
reject(handle_err(sql, err, __filename, __line));
return;
}
// 执行等待exec列表.
// do_wait(db1, pool, __filename, __line);
let req = new mssql.Request(pool); // or: new sql.Request(pool1)
do_procedure(db1, req, name, params, resolve, reject, err=>{
}, __filename, __line);
});
} catch (e) {
reject(handle_err('', 'connect err: ' + e.toString(), __filename, __line));
}
}
// query.
else {
//!!!: 执行等待exec列表.
// do_wait(db1, pool, __filename, __line);
let req = new mssql.Request(pool); // or: new sql.Request(pool1)
do_procedure(db1, req, name, params, resolve, reject, err=>{
}, __filename, __line);
}
} else {
//!!!: 执行等待exec列表.
// do_wait(db1, pool, __filename, __line);
let req = new mssql.Request(conn); // or: new sql.Request(pool1)
do_procedure(db1, req, name, params, resolve, reject, err=>{
}, __filename, __line);
}
} catch (e) {
reject(handle_err(sql, e, __filename, __line));
}
});
}
/**
* @desc: transaction.
* @param fooCB: async function(database):boolean {}, 返回false则rollback, 返回true则commit.
* @return: (Promise).
*/
function transaction(db, isolationLevel1, fooCB, preCB, commitCB, rollbackCB) {
assert(db);
return new PromiseLib((resolve, reject)=>{
if (!fooCB || (typeof fooCB !== 'function')) {
reject(new exception(febs.exception.PARAM, febs.exception.PARAM, __filename, __line));
return;
}
if (db._transactionConn) {
assert(!db.pool);
debug('[Recursion Transaction]');
do_transaction(db, fooCB, preCB, commitCB, rollbackCB, resolve, reject, null, db._transactionConn, false);
}
else {
let sqlLevel;
let sqlLevelSql;
if (isolationLevel1 == isolationLevel.Read_committed) {
sqlLevel = mssql.ISOLATION_LEVEL.READ_COMMITTED;
sqlLevelSql = 'READ COMMITTED';
} else if (isolationLevel1 == isolationLevel.Read_uncommitted) {
sqlLevel = mssql.ISOLATION_LEVEL.READ_UNCOMMITTED;
sqlLevelSql = 'READ UNCOMMITTED';
} else if (isolationLevel1 == isolationLevel.Repeatable_read) {
sqlLevel = mssql.ISOLATION_LEVEL.REPEATABLE_READ;
sqlLevelSql = 'REPEATABLE READ';
} else if (isolationLevel1 == isolationLevel.Serializable) {
sqlLevel = mssql.ISOLATION_LEVEL.SERIALIZABLE;
sqlLevelSql = 'SERIALIZABLE';
} else {
reject(new exception(febs.exception.PARAM, febs.exception.PARAM, __filename, __line));
return;
}
assert(db.pool);
let pool = db.pool;
try {
// other is connecting.
if (pool.connecting) {
db = db.__db || db;
// db.__waitExecArray = db.__waitExecArray || [];
// db.__waitExecArray.push({type:'t',db, sqlLevel, fooCB, resolve, reject});
reject(handle_err('getConnection', 'db is connecting', __filename, __line));
return;
}
// to connect pool.
else if (!pool.connected) {
try {
pool.connect(err=>{
if (err) {
// 执行等待exec列表.
// reject_wait((db.__db || db), err, __filename, __line);
reject(handle_err('connect', err, __filename, __line));
return;
}
// 执行等待exec列表.
// do_wait((db.__db || db), pool, __filename, __line);
const trans_conn = new mssql.Transaction(pool);
trans_conn.begin(sqlLevel, err=>{
if (err) {
reject(handle_err('begin tran', err, __filename, __line));
return;
}
debug('[Transaction] begin ' + sqlLevelSql);
try {
if (db.sqlLogCallback) db.sqlLogCallback(err, 'SET TRANSACTION ISOLATION LEVEL '+sqlLevelSql + ' BEGIN TRAN', true);
} catch (e) { console.log('[error in sqlLogCallback]'); console.log(e); }
let db1 = db._cloneWithConn(trans_conn);
db1.__db = db;
trans_conn.on('rollback', aborted => {
// emited with aborted === true
try {
if (db1.sqlLogCallback) db1.sqlLogCallback(null, 'ROLLBACK TRAN', true);
} catch (err) { console.log('[error in sqlLogCallback]'); console.log(err); }
db1.__rolledBack = true;
})
do_transaction(db1, fooCB, preCB, commitCB, rollbackCB, resolve, reject, err=>{
}, trans_conn, true);
});
});
} catch (e) {
reject(handle_err('', 'connect err: ' + e.toString(), __filename, __line));
}
}
// query.
else {
// do_wait(db, pool, __filename, __line);
const trans_conn = new mssql.Transaction(pool);
trans_conn.begin(sqlLevel, err=>{
if (err) {
reject(handle_err('begin tran', err, __filename, __line));
return;
}
debug('[Transaction] begin ' + sqlLevelSql);
try {
if (db.sqlLogCallback) db.sqlLogCallback(err, 'SET TRANSACTION ISOLATION LEVEL '+sqlLevelSql + ' BEGIN TRAN', true);
} catch (e) { console.log('[error in sqlLogCallback]'); console.log(e); }
let db1 = db._cloneWithConn(trans_conn);
db1.__db = db;
trans_conn.on('rollback', aborted => {
// emited with aborted === true
try {
if (db1.sqlLogCallback) db1.sqlLogCallback(null, 'ROLLBACK TRAN', true);
} catch (err) { console.log('[error in sqlLogCallback]'); console.log(err); }
db1.__rolledBack = true;
})
do_transaction(db1, fooCB, preCB, commitCB, rollbackCB, resolve, reject, null, trans_conn, true);
});
} // if..else.
} catch (err) {
reject(handle_err('tran', err, __filename, __line));
}
}
});
}
/**
* @desc: add
* @return: Promise.
* @resolve:
* ret - boolean.
*/
function add( table, item ) {
return table.db.exec(sql_add(table, item))
.then(ret=>{
if (table.idKeyNameAutoInc && ret.insertId) {
item[table.idKeyNameAutoInc] = ret.insertId;
}
return (ret.rowsAffected > 0) ? true : false;
});
}
/**
* @desc: remove
* @return: Promise.
* @resolve:
* ret - boolean.
*/
function remove(table, where) {
return table.db.exec(sql_remove(table, where))
.then(ret=>{
return ret.rowsAffected > 0;
});
}
/**
* @desc: update; where id = item.id.
* if item.id is existed, sql condition is: 'id=value' AND (where)
* otherwise sql condition is: where
* @param item, where.
* @return: Promise.
* @resolve:
* ret - boolean.
*/
function update( table, item, where = null ) {
return table.db.exec(sql_update(table, item, where))
.then(ret=>{
return ret.rowsAffected > 0;
});
}
/**
* @desc: query by id and lock row for update (use in transaction).
* id is Object if table is combined primary.
* @param id: 主鍵值.
* @param query_cols:
* [col1, col2, ...]需要查询出的字段名称数组,
* 其中, 如果未指定表名, 则默认为第一个表的元素.
* @return: Promise.
* @resolve:
* ret - mod.
*/
function selectLockRow( table, id, query_cols = null, alias = null ) {
if (!table.db._transactionConn) {
throw new exception('selectLockRow only call in transaction', exception.DB_CommonException, __filename, __line);
}
return table.db.exec(sql_selectLockRow(table, id, query_cols, alias))
.then(ret=>{
return ret_data_cvt(ret.rows, table)[0];
});
}
/**
* @desc: query
* sql的连接顺序为: SELECT query_cols FROM where groupSql orderby pageInfo.
* @param where:
* 查询条件,不会对此字符串验证. 使用 condition 对象进行构建.
* @param opt: 查询选项. 可以包含以下键值.
* - cols: 需要查询出的字段名称数组; 例如: [col1, col2, ...]; 不指定则为查询全部.
* - groupSql: group by子句, 不会对内容进行验证; 应包含group by关键字.
* - orderby: orderby by子句, 例如: {key:true/false} true-means asc, false-means desc..
* - offset: 分页查询起始位置.
* - limit: 分页查询查询行数.
* @param alias:
* 别名
* @return: Promise.
* @resolve:
* ret - mod array.
*/
function select( table, where, opt = null, alias = null ) {
opt = opt || {};
let query_cols = opt.cols;
let groupSql = opt.groupSql;
let orderby = opt.orderby;
let pageinfo = {offset:opt.offset, limit:opt.limit};
return table.db.exec(sql_select(table, where, query_cols, groupSql, orderby, pageinfo, alias))
.then(ret1=>{
return ret_data_cvt(ret1.rows, table);
});
}
/**
* @desc: count
* @param: where
* @return: Promise.
* @resolve:
* ret - number.
*/
function count(table, where = null, alias = null) {
return table.db.exec(sql_count(table, where, alias))
.then(ret=>{
return (ret && ret.rows.length >= 1) ? ret.rows[0].a : -1;
});
}
/**
* @desc: exist
* id is Object if table is combined primary.
* @return: Promise.
* @resolve:
* ret - boolean.
*/
function exist( table, id, alias = null ) {
assert(id != null && id != undefined);
if (table.idKeyIsCombined && !(id instanceof Object)) {
throw new exception('params id is error in exist', febs.exception.PARAM, __filename, __line);
}
// combined key.
let sqlprimary = '';
if (table.idKeyIsCombined) {
for (let i = 0; i < table.idKeyName.length; i++) {
let id2 = id[table.idKeyName[i]];
if (id2 === undefined || id2 === null) {
sqlprimary = null;
break;
} else {
if (i > 0)
sqlprimary += ' AND ';
sqlprimary += table.idKeyName[i] + "=" + type_cast(id2, table.model[table.idKeyName[i]].type, table.idKeyName[i]);
}
}
} else {
let id2 = id;
if (id2 !== undefined && id2 !== null) {
sqlprimary += table.idKeyName + "=" + type_cast(id2, table.model[table.idKeyName].type, table.idKeyName);
} else {
sqlprimary = null;
}
}
if (!sqlprimary) {
throw new exception('params id is error in isExist', exception.DB_ERROR_SQL, __filename, __line);
}
return table.db.exec(sql_count(table, sqlprimary, alias))
.then(ret=>{
return (ret && ret.rows.length >= 1) ? ret.rows[0].a > 0 : false;
});
}
/**
* @desc: add
* @return: sql string.
*/
function sql_add( table, item ) {
assert(item != null && item != undefined);
let sqlv = table_helper.makeColsKeysAndValuesForAdd(item, table);
let sql = "INSERT INTO " + table.tablename + " (" + sqlv[0] + ") VALUES(" + sqlv[1] + ")";
if (table.idKeyNameAutoInc) {
sql += ';select @@IDENTITY AS \'identity\'';
}
return sql;
}
/**
* @desc: remove
* @return: sql string.
*/
function sql_remove( table, where ) {
assert(where != null && where != undefined && where.length > 0);
let sql = "DELETE FROM " + table.tablename + " WHERE " + (where);
return sql;
}
/**
* @desc: update; where id = item.id.
* if item.id is existed, sql condition is: 'id=value' AND (where)
* otherwise sql condition is: where
* @param item, where.
* @return: sql string.
*/
function sql_update( table, item, where = null ) {
assert(item != null && item != undefined);
let sqlv = table_helper.makeColsKeyPairsForUpdate(item, table);
let sql = "UPDATE " + table.tablename + " SET " + sqlv + " WHERE ";
// combined key.
let sqlprimary = '';
if (table.idKeyIsCombined) {
for (let i = 0; i < table.idKeyName.length; i++) {
let name = table.idKeyName[i];
let id = item[name];
if (id === undefined || id === null) {
sqlprimary = null;
break;
} else {
if (i > 0)
sqlprimary += ' AND ';
sqlprimary += table.idKeyName[i] + "=" + type_cast(id, table.model[name].type, name);
}
}
} else {
let id = item[table.idKeyName];
if (id !== undefined && id !== null) {
sqlprimary += table.idKeyName + "=" + type_cast(id, table.model[table.idKeyName].type, table.idKeyName);
}
}
if (sqlprimary)
{
sql += sqlprimary;
if (!febs.string.isEmpty(where))
{
sql += ' AND (' + where + ')';
}
}
else {
if (!febs.string.isEmpty(where))
{
sql += where;
}
else {
throw new exception('no item.id and no where', exception.DB_SqlException, __filename, __line);
}
}
return sql;
}
/**
* @desc: query by id and lock row for update (use in transaction).
* id is Object if table is combined primary.
* @param id: 主鍵值.
* @param query_cols:
* [col1, col2, ...]需要查询出的字段名称数组,
* 其中, 如果未指定表名, 则默认为第一个表的元素.
* @return: sql string.
*/
function sql_selectLockRow( table, id, query_cols = null, alias = null ) {
assert(id != null && id != undefined);
let sqlprimary = table_helper.makePrimaryValues(id, table, alias);
let query_str = table_helper.makeCols(query_cols, table, alias);
let alis;
if (alias) {
if (alias.indexOf(' ') >= 0) alis = '('+alias+')';
else alis = table.tablename + ' AS '+alias;
}
else {
alis = table.tablename;
}
let sql = "SELECT " + query_str + " FROM " + alis + " with (rowlock,UpdLock) WHERE " + sqlprimary;
return sql;
}
/**
* @desc: query
* sql的连接顺序为: SELECT query_cols FROM where groupSql orderby pageInfo.
*
* @param where:
* 查询条件,不会对此字符串验证.
* @param query_cols:
* [col1, col2, ...]需要查询出的字段名称数组,
* 其中, 如果未指定表名, 则默认为第一个表的元素.
* @param groupSql:
* group by子句, 不会对内容进行验证; 应包含group by关键字.
* @param orderby:
* orderby by子句, {key:true/false} true-means asc, false-means desc.
* @param pageinfo:
* 分页信息; {offset:0, limit:100}; 默认为 offset=0, limit=100;
* @param alias:
* 别名
* @return: sql string.
*/
function sql_select( table, where, query_cols = null, groupSql = null, orderby = null, pageinfo = null, alias = null ) {
let index;
let offset = (pageinfo ? pageinfo.offset : 0) || 0;
let limit = (pageinfo ? pageinfo.limit : 10000) || 10000;
query_cols = query_cols ? table_helper.makeCols(query_cols, table, alias) : '*';
let alis;
let alip;
if (alias) {
if (alias.indexOf(' ') >= 0) { alis = '('+alias+')'; alip = ''; }
else { alis = table.tablename + ' AS '+alias; alip = `${alias}.`; }
}
else {
alis = table.tablename;
alip = '';
}
// SELECT TOP 20 *
// FROM
// (
// SELECT ROW_NUMBER() OVER (ORDER BY id) AS RowNumber,* FROM table1
// ) as A
// WHERE RowNumber > 20*(10-1)
// orderby.
let sqlOrderby = '';
if (orderby)
{
let first = true;
for (let k in orderby) {
let v = table.model[k];
let mapName;
if (v) {
mapName = v.map;
} else {
mapName = k;
}
if (first)
first = false;
else
sqlOrderby += ',';
sqlOrderby += alip + mapName + ' ';
if (!orderby[k])
sqlOrderby += 'DESC';
}
}
if (sqlOrderby.length <= 0) {
sqlOrderby = ((table._idKeyNameMap instanceof Array) ? table._idKeyNameMap[0] : table._idKeyNameMap);
}
if (sqlOrderby.length <= 0) {
for (let key in table.model) {
sqlOrderby = table.model[key].map;
break;
}
}
let sql = "SELECT TOP " + limit + ' * FROM (';
sql += "SELECT ROW_NUMBER() OVER (ORDER BY " + sqlOrderby + ') AS RowNumber,';
sql += query_cols + " FROM " + alis;
// where.
if (where != null && where != undefined && where.length > 0)
sql += " WHERE " + (where);
// group.
if (groupSql)
sql += ' ' + groupSql;
// limit.
sql += ') AS A WHERE RowNumber > ' + offset;
return sql;
}
/**
* @desc: count
* @param: where
* @return: sql string.
*/
function sql_count(table, where = null, alias = null) {
let alis;
if (alias) {
if (alias.indexOf(' ') >= 0) alis = '('+alias+')';
else alis = table.tablename + ' AS '+alias;
}
else {
alis = table.tablename;
}
let sql = "SELECT COUNT(*) AS a FROM " + alis;
if (!febs.string.isEmpty(where))
sql += " WHERE " + (where);
return sql;
}