UNPKG

liteq

Version:

QueryBuilder for JavaScript. Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle and more. Works in NodeJS.

425 lines (398 loc) 12.2 kB
/** * @Author: richen * @Date: 2018-02-22 16:15:54 * @Copyright (c) - <richenlin(at)gmail.com> * @Last Modified by: richen * @Last Modified time: 2019-02-28 10:47:38 */ const helper = require('./helper.js'); const logger = require('think_logger'); const supportType = ['mysql', 'postgresql', 'sqlite3', 'oracle', 'mssql']; const __LiteQInstances = {}; module.exports = class Adapter { /** *Creates an instance of Adapter. * @param {*} config */ constructor(config) { this.pool = null; this.config = config; this.knexClient = null; this.parser = null; this.transTimes = 0; //transaction times this.init(config); if (this.knexClient) { config = this.config; this.knexClient.on('query-response', function (response, obj, builder) { builder.toString && config.db_ext_config && config.db_ext_config.db_log_sql && logger.custom('INFO', 'blue', builder.toString()); }); this.knexClient.on('query-error', function (error, obj) { obj.sql && logger.error(['<bindings>:', obj.bindings || [], '\r\n', '<sql>:', obj.sql]); throw Error(error); }); if (this.knexClient.client && this.knexClient.client.pool) { this.pool = this.knexClient.client.pool; } } } /** * 初始化构造方法 * */ init() { } /** * * */ async close() { try { if (this.pool) { await this.pool.destroy(); } this.pool = null; return null; } catch (e) { logger.error(e); return null; } } /** * 生成sqlString * * @param {*} options * {method: find | select | add | update | count | sum | decrement | increment} * @param {*} [data={}] * @returns */ sql(options, data = {}) { switch (options.method) { case 'find': options.limit = [0, 1]; options.method = 'SELECT'; break; case 'select': options.method = 'SELECT'; break; case 'add': options.method = 'ADD'; break; case 'update': options.method = 'UPDATE'; break; case 'count': options.method = 'COUNT'; break; case 'sum': options.method = 'SUM'; break; case 'decrement': options.method = 'DECREMENT'; break; case 'increment': options.method = 'INCREMENT'; break; default: options.method = 'SELECT'; break; } if (['ADD', 'UPDATE'].includes(options.method) && helper.isEmpty(data)) { return Promise.reject('paramer data is empty'); } if (['COUNT', 'SUM', 'DECREMENT', 'INCREMENT'].includes(options.method) && helper.isEmpty(data)) { return Promise.reject('paramer field is empty'); } return this.parser.buildSql(this.config, data, options, this.knexClient).catch(err => { return Promise.reject(err); }); } /** * 执行查询 * * @param {any} options * @param {number} [times=0] * @returns */ query(options, times = 0) { return this.parser.querySql(this.config, {}, options, this.knexClient).catch(err => { // if server close connection, then retry it if (times < 3 && err.message && err.message.indexOf('PROTOCOL') > -1) { return this.query(options, times + 1); } return Promise.reject(err); }); } /** * 执行修改 * * @param {any} data * @param {any} options * @param {number} [times=0] * @returns */ execute(data, options, times = 0) { return this.parser.querySql(this.config, data, options, this.knexClient).catch(err => { // if server close connection, then retry it if (times < 3 && err.message && err.message.indexOf('PROTOCOL') > -1) { return this.query(options, times + 1); } return Promise.reject(err); }); } /** * 执行原生语句 * * @param {*} sqlStr * @param {*} bind * @param {number} [times=0] * @returns */ native(sqlStr, bind, times = 0) { if (helper.isEmpty(sqlStr)) { return Promise.reject('SQL String is null.'); } return this.knexClient.raw(sqlStr, bind).then(rowData => { if (rowData.rows) { return rowData.rows || []; } else { return rowData[0] || []; } }).catch(err => { // if server close connection, then retry it if (times < 3 && err.message && err.message.indexOf('PROTOCOL') > -1) { return this.native(sqlStr, bind, times + 1); } return Promise.reject(err); }); } /** * 数据迁移 * * @param {*} schema * @param {string} [sqlStr=''] * @returns */ async migrate(schema, sqlStr = '') { if (helper.isEmpty(sqlStr)) { try { let options = { method: 'MIGRATE', schema: schema }; const ex = await this.knexClient.schema.hasTable(schema.table).catch(() => false); if (!ex) { //直接使用this.parser.querySql会导致create table多次执行,目前原因未知。暂时使用buildSql // sqlStr = await this.parser.buildSql(this.config, {}, options, this.knexClient); // const sqlArr = sqlStr.split(';'); // // 串行 // for (const item of sqlArr) { // await this.knexClient.raw(item, []); // } await this.parser.querySql(this.config, {}, options, this.knexClient); } else { logger.error(`Table ${schema.table} already exists`); } return Promise.resolve(); } catch (err) { return Promise.reject(err); } } else { return this.knexClient.raw(sqlStr, []); } } /** * 增加数据 * * @param {any} data * @param {any} [options={}] * @returns */ add(data, options = {}) { options.method = 'ADD'; options.alias = undefined; return this.execute(data, options); } /** * 批量增加数据 * * @param {*} data * @param {*} [options={}] * @returns */ batchAdd(data, options = {}) { options.method = 'BATCHADD'; options.alias = undefined; return this.execute(data, options).then(res => { if (helper.isArray(res)) { for (let i = 1; i < data.length; i++) { res.push(res[0] + i); } } return res; }); } /** * 删除数据 * * @param {any} [options={}] * @returns */ delete(options = {}) { options.method = 'DELETE'; options.alias = undefined; return this.execute(options); } /** * 更新数据 * * @param {any} data * @param {any} [options={}] * @returns */ update(data, options = {}) { options.method = 'UPDATE'; options.alias = undefined; return this.execute(data, options); } /** * 字段自增 * * @param {any} data * @param {any} field * @param {any} [options={}] * @returns */ increment(data, field, options = {}) { options.method = 'INCREMENT'; options.alias = undefined; options.targetField = field; return this.execute(data, options); } /** * 字段自减 * * @param {any} data * @param {any} field * @param {any} [options={}] * @returns */ decrement(data, field, options = {}) { options.method = 'DECREMENT'; options.alias = undefined; options.targetField = field; return this.execute(data, options); } /** * 统计数据条数 * * @param {any} field * @param {any} [options={}] * @returns */ count(field, options = {}) { options.method = 'COUNT'; options.limit = [0, 1]; options.targetField = field; return this.query(options).then(result => { if (helper.isArray(result)) { if (!helper.isEmpty(options.group)) { return result.length; } else { return result[0].count ? (helper.toInt(result[0].count) || 0) : 0; } } else { return result.count || 0; } }); } /** * 统计字段求和 * * @param {any} field * @param {any} [options={}] * @returns */ sum(field, options = {}) { options.method = 'SUM'; options.limit = [0, 1]; options.targetField = field; return this.query(options).then(result => { if (helper.isArray(result)) { if (!helper.isEmpty(options.group)) { let res = 0; result.map(it => { const iSum = helper.toInt(it.sum); if (iSum) { res += iSum; } }); return res; } else { return result[0].sum ? (helper.toInt(result[0].sum) || 0) : 0; } } else { return result.sum || 0; } }); } /** * 查询单条数据 * * @param {any} [options={}] * @returns */ find(options = {}) { options.method = 'SELECT'; options.limit = [0, 1]; return this.query(options).then(result => { return helper.isArray(result) ? result[0] : result; }); } /** * 查询多条数据 * * @param {any} [options={}] * @returns */ select(options = {}) { options.method = 'SELECT'; return this.query(options); } /** * Singleton * * @static * @param {*} config * @param {*} forceNew * @returns */ static async getInstance(config, forceNew) { let key = `${config.db_type}_${config.db_host}_${config.db_port}_${config.db_name}`; if (__LiteQInstances[key] && !forceNew) { return Promise.resolve(__LiteQInstances[key]); } let dbType = helper.isEmpty(config.db_type) ? 'mysql' : (config.db_type).toLowerCase(); if (dbType === 'mariadb' || dbType.indexOf('mysql') > -1) { dbType = 'mysql'; } if (supportType.indexOf(dbType) < 0) { throw Error('This database type is not supported.'); } //Test cases are not completely covered, there may be errors, do not use in production environments // if (['sqlite3', 'oracle', 'mssql'].indexOf(dbType) > -1) { // logger.warn('Test cases are not completely covered, there may be errors, do not use in production environments!!'); // } const adapter = require(`./adapters/${dbType}.js`); if (forceNew) { config = helper.extend(config, { db_ext_config: { db_pool_size: 1 } }); } const cls = new adapter(config); if (!forceNew) { __LiteQInstances[key] = cls; } return cls; } };