UNPKG

yoyosqlite

Version:

一个链式连接sqlite数据库的扩展包

673 lines (672 loc) 23.6 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const sqlite3_1 = __importDefault(require("sqlite3")); function isOperator(value) { const operators = ['=', '<>', '<', '<=', '>', '>=', 'LIKE', 'IN', 'NOT IN', 'IS NULL', 'IS NOT NULL', 'BETWEEN', 'NOT BETWEEN', 'REGEXP', 'NOT REGEXP', 'NOT LIKE']; value = value.toUpperCase(); return operators.includes(value); } function formatValue(value) { if (typeof value === 'string') { return `'${value}'`; } else if (typeof value === 'number') { return value; } throw new Error('The value passed in is of the wrong type and can only be a string or a number'); } function fillStr(arr, str = '?', decollator = ',') { return Array(arr.length).fill(str).join(decollator); } function isTypeof(data) { let type = Object.prototype.toString.call(data); return type.replace(/^\[object (.+)\]$/, '$1').toLowerCase(); } function isObject(data) { return typeof data === 'object' && data !== null && !Array.isArray(data); } function isTwoArray(arr) { return Array.isArray(arr) && arr.every(Array.isArray); } function isArrayObj(arr) { return Array.isArray(arr) && arr.every(item => isObject(item)); } function isArrayData(arr) { return Array.isArray(arr) && arr.every(item => typeof item !== 'object'); } class YoyoMysql { // 创建数据库连接 static createDatabase(dbPath) { if (YoyoMysql._DB) return YoyoMysql; YoyoMysql._DB = new sqlite3_1.default.Database(dbPath); return YoyoMysql; } // 设置表名 static table(table) { return new YoyoMysqlClass(YoyoMysql._DB).table(table); } // 开启事务 static beginTransaction(callback) { return __awaiter(this, void 0, void 0, function* () { YoyoMysql._DB.serialize(() => __awaiter(this, void 0, void 0, function* () { YoyoMysql._DB.run('BEGIN TRANSACTION'); try { yield callback({ table: (table) => new YoyoMysqlClass(YoyoMysql._DB).table(table), query: (sql, values) => new Promise((resolve, reject) => { YoyoMysql._DB.all(sql, values, (err, rows) => { if (err) reject(err); else resolve([rows, []]); }); }), execute: (sql, values) => new Promise((resolve, reject) => { YoyoMysql._DB.run(sql, values, function (err) { if (err) reject(err); else resolve([this, []]); }); }) }); YoyoMysql._DB.run('COMMIT'); } catch (error) { YoyoMysql._DB.run('ROLLBACK'); throw error; } })); }); } // 执行查询语句 static query(sql, values) { return new Promise((resolve, reject) => { YoyoMysql._DB.all(sql, values, (err, rows) => { if (err) reject(err); else resolve([rows, []]); }); }); } // 执行非查询语句 static execute(sql, values) { return new Promise((resolve, reject) => { YoyoMysql._DB.run(sql, values, function (err) { if (err) reject(err); else resolve([this, []]); }); }); } } exports.default = YoyoMysql; class YoyoMysqlClass { constructor(link) { this._fields = []; this._clogic = 'AND'; this._where = []; this._group = new Set([]); this._having = ''; this._order = []; this._join = []; this._link = link; } // 设置表名 table(table) { this._table = `\`${table}\``; return this; } field(field) { if (Array.isArray(field)) { field.forEach((f) => this._fields.push(f)); return this; } this._fields.push(field); return this; } order(...args) { var _a; if (args.length === 1) { if (typeof args[0] === 'object') { for (const f in args[0]) { const order = args[0][f]; this._order.push({ field: f, order }); } return this; } } if (args.length === 2) { if (typeof args[0] === 'string') { args[1] = (_a = args[1]) !== null && _a !== void 0 ? _a : 'ASC'; this._order.push({ field: args[0], order: args[1] }); return this; } } throw new Error('The order method can only accept one or two parameters'); } group(args) { if (Array.isArray(args)) { args.forEach((field) => { this._group.add(field); }); return this; } this._group.add(args); return this; } // 过滤由 GROUP BY 产生的结果集中的重复记录 having(where) { if (this._group.size === 0) throw new Error('The group method must be called before the having method'); this._having = where; return this; } limit(...args) { if (args.length === 1) { this._limit = args[0]; return this; } if (args.length === 2) { this._limit = [args[0], args[1]]; return this; } throw new Error('The limit method can only accept one or two parameters'); } // 多表联查[左查询](LEFT%20JOIN) leftJoin(table, where) { table = `\`${table}\``; this._join.push({ type: 'LEFT', table, where }); return this; } // 多表联查[右查询](RIGHT%20JOIN) rightJoin(table, where) { table = `\`${table}\``; this._join.push({ type: 'RIGHT', table, where }); return this; } // 多表联查[等值查询](INNER%20JOIN) join(table, where) { table = `\`${table}\``; this._join.push({ type: 'INNER', table, where }); return this; } // 多表联查[一个匹配填充null返回](FULL%20JOIN) fullJoin(table, where) { table = `\`${table}\``; this._join.push({ type: 'FULL', table, where }); return this; } _whereComposite(field, ...args) { if (Array.isArray(field)) { field.forEach((item) => { this._whereComposite(item[0], item[1], item[2]); }); return this; } else if (typeof field === 'string' && typeof args[0] === 'string' && typeof args[1] !== 'undefined') { args[0] = args[0].toUpperCase(); if (!isOperator(args[0])) throw new Error('operator error'); if (!/\./.test(field)) field = `\`${field}\``; const _where = { type: this._clogic, data: { subquery: false, field, operator: args[0], value: args[1], param: [] } }; if (Array.isArray(args[1])) { if (args[0] !== 'IN' && args[0] !== 'NOT IN') throw new Error('The operator should be IN or NOT IN when the array is passed.'); _where.data.value = `(${fillStr(args[1])})`; _where.data.param = args[1]; } this._where.push(_where); return this; } else if (typeof field === 'string' && (typeof args[0] === 'string' || typeof args[0] === 'number' || typeof args[0] === 'object') && typeof args[1] === 'undefined') { if (typeof args[0] === 'object') { for (const key in args[0]) { const v = args[0][key]; this._whereComposite(key, '=', v); } return this; } return this._whereComposite(field, '=', args[0]); } else if (typeof field === 'string' && typeof args[0] === 'function') { const result = args[0](); if (!(result instanceof YoyoMysqlClass)) { throw new Error('where callback must return YoyoMysql'); } const resultSql = result.buildSql('select'); if (!/\./.test(field)) field = `\`${field}\``; this._where.push({ type: this._clogic, data: { subquery: true, field, operator: '=', value: `${resultSql.sql}`, param: resultSql.params } }); return this; } throw new Error('where params error'); } // 添加一条IN条件(子查询) whereIn(field, callback) { const result = callback(); if (!(result instanceof YoyoMysqlClass)) { throw new Error('whereIn callback must return YoyoMysql'); } const resultSql = result.buildSql(); if (!/\./.test(field)) field = `\`${field}\``; this._where.push({ type: this._clogic, data: { subquery: true, field, operator: 'IN', value: `${resultSql.sql}`, param: resultSql.params } }); return this; } where(field, ...args) { this._clogic = 'AND'; return this._whereComposite(field, ...args); } whereOr(field, ...args) { this._clogic = 'OR'; return this._whereComposite(field, ...args); } // 查询指定条件的单条数据 find() { this._limit = 1; const { sql, params } = this.buildSelect(); return new Promise((resolve, reject) => { this._link.all(sql, params, (err, rows) => { if (err) reject(err); else resolve(rows[0] || null); }); }); } // 查询指定条件的数据 select() { const { sql, params } = this.buildSelect(); return new Promise((resolve, reject) => { this._link.all(sql, params, (err, rows) => { if (err) reject(err); else resolve({ data: rows, count: rows.length, maxCount: rows.length, fields: [] }); }); }); } // 查询指定条件的数据(分页) pages(page = 1, count = 10) { if (page < 1) page = 1; if (count < 1) count = 1; const cache_fields = JSON.parse(JSON.stringify(this._fields)); this._fields = ['COUNT(*) as count']; this._limit = 1; const { sql, params } = this.buildSelect(); return new Promise((resolve, reject) => { this._link.all(sql, params, (err, rows) => { if (err) reject(err); else { const countData = rows[0] || { count: 0 }; const selectSite = { site: 0, maxPage: Math.ceil(countData.count / count) || 1 }; selectSite.site = (page - 1) * count; this._fields = cache_fields; this._limit = [selectSite.site, count]; const newSql = this.buildSelect(); this._link.all(newSql.sql, newSql.params, (err, rows) => { if (err) reject(err); else resolve({ data: rows, fields: [], count: rows.length, maxCount: countData.count, maxPage: selectSite.maxPage, }); }); } }); }); } // 删除指定条件的数据 delete() { const { sql, params } = this.buildDelete(); return new Promise((resolve, reject) => { this._link.run(sql, params, function (err) { if (err) reject(err); else resolve(this); }); }); } // 插入一条或者多条数据 insert(data) { const { sql, params } = this.buildInsert(data); return new Promise((resolve, reject) => { this._link.run(sql, params, function (err) { if (err) reject(err); else resolve(this); }); }); } // 修改指定条件的所有数据 update(data) { const { sql, params } = this.buildUpdate(data); return new Promise((resolve, reject) => { this._link.run(sql, params, function (err) { if (err) reject(err); else resolve(this); }); }); } buildWhere() { const _where = this._where; const whereGroup = []; let minGroup = []; if (_where.length == 0) return null; for (const index in _where) { const eachWhere = _where[index]; const minWhereLast = minGroup.at(-1); if (typeof minWhereLast === 'undefined') { minGroup.push(eachWhere); continue; } if (minWhereLast.type === eachWhere.type) { minGroup.push(eachWhere); continue; } if (Number(index) == _where.length - 1) { minGroup.push(eachWhere); } else { whereGroup.push(minGroup); minGroup = [eachWhere]; } } whereGroup.push(minGroup); const ultimately = { where: '', params: [], }; for (const index in whereGroup) { const eachGroupWhere = whereGroup[index]; const whereMinGroupSqlArr = []; const whereMinGroupValArr = []; for (const eachIndex in eachGroupWhere) { const eachWhere = eachGroupWhere[eachIndex]; if (eachWhere.data.subquery) { whereMinGroupSqlArr.push(`${eachWhere.data.field} ${eachWhere.data.operator} (${eachWhere.data.value})`); whereMinGroupValArr.push(...eachWhere.data.param); continue; } if (eachWhere.data.param.length > 0) { whereMinGroupSqlArr.push(`${eachWhere.data.field} ${eachWhere.data.operator} (${fillStr(eachWhere.data.param)})`); whereMinGroupValArr.push(...eachWhere.data.param); continue; } whereMinGroupSqlArr.push(`${Number(eachIndex) > 0 ? ` ${eachWhere.type} ` : ''}${eachWhere.data.field} ${eachWhere.data.operator} ?`); whereMinGroupValArr.push(eachWhere.data.value); } if (Number(index) > 0) ultimately.where += ` ${eachGroupWhere[0].type} `; ultimately.where += `(${whereMinGroupSqlArr.join('')})`; ultimately.params = [...ultimately.params, ...whereMinGroupValArr]; } return ultimately; } buildSelect() { const table = this._table; const field = this._fields; const params = []; const sqlArr = ['SELECT']; if (field.length > 0) { sqlArr.push(field.join(',')); } else { sqlArr.push('*'); } sqlArr.push(`FROM ${table}`); if (this._join.length > 0) { const joinArr = this._join.map(j => `${j.type} JOIN ${j.table} ON ${j.where}`); sqlArr.push(joinArr.join(' ')); } const resultWhere = this.buildWhere(); if (resultWhere) { sqlArr.push('WHERE', resultWhere.where); params.push(...resultWhere.params); } if (this._group.size > 0) { sqlArr.push(`GROUP BY ${Array.from(this._group).join(',')}`); } if (this._having) { sqlArr.push(`HAVING ${this._having}`); } if (this._order.length > 0) { const orderArr = this._order.map(o => `\`${o.field}\` ${o.order}`); sqlArr.push(`ORDER BY ${orderArr.join(',')}`); } if (typeof this._limit !== 'undefined') { let limitStr = ''; if (Array.isArray(this._limit)) { limitStr = `LIMIT ${this._limit[1]} OFFSET ${this._limit[0]}`; } else { limitStr = `LIMIT ${this._limit}`; } sqlArr.push(limitStr); } return { sql: sqlArr.join(' '), params, }; } buildDelete() { const table = this._table; const params = []; const sqlArr = ['DELETE']; sqlArr.push(`FROM ${table}`); if (this._join.length > 0) { const joinArr = this._join.map(j => `${j.type} JOIN ${j.table} ON ${j.where}`); sqlArr.push(joinArr.join(' ')); } const resultWhere = this.buildWhere(); if (resultWhere) { sqlArr.push('WHERE', resultWhere.where); params.push(...resultWhere.params); } if (this._order.length > 0) { const orderArr = this._order.map(o => `\`${o.field}\` ${o.order}`); sqlArr.push(`ORDER BY ${orderArr.join(',')}`); } if (typeof this._limit !== 'undefined') { let limitStr = ''; if (Array.isArray(this._limit)) { limitStr = `LIMIT ${this._limit[1]} OFFSET ${this._limit[0]}`; } else { limitStr = `LIMIT ${this._limit}`; } sqlArr.push(limitStr); } return { sql: sqlArr.join(' '), params, }; } buildInsert(data) { const table = this._table; let field = this._fields; const params = []; const sqlArr = ['INSERT']; sqlArr.push(`INTO ${table}`); const processingData = (data) => { if (isObject(data)) { field = Object.keys(data).map(f => `\`${f}\``); params.push([...Object.values(data)]); return; } if (!Array.isArray(data)) throw new Error("The inserted data must be an object or array with a value"); if (data.length <= 0) throw new Error("The data you submit is empty"); if (isTwoArray(data)) { params.push(...Object.values(data)); return; } if (isArrayObj(data)) { field = Object.keys(data[0]).map(f => `\`${f}\``); params.push(...data.map(d => Object.values(d))); return; } if (isArrayData(data)) { params.push(data); return; } throw new Error("The inserted data must be an object or array with a value"); }; processingData(data); if (field.length <= 0) { throw new Error("The data you submit has no fields"); } sqlArr.push(`(${field.join(',')})`); sqlArr.push('VALUES'); sqlArr.push(params.map(p => `(${p.map(v => '?').join(',')})`).join(',')); return { sql: sqlArr.join(' '), params: params.flat(), }; } buildUpdate(data) { const table = this._table; const params = []; const sqlArr = ['UPDATE']; sqlArr.push(`${table}`); if (this._join.length > 0) { const joinArr = this._join.map(j => `${j.type} JOIN ${j.table} ON ${j.where}`); sqlArr.push(joinArr.join(' ')); } sqlArr.push(`SET`); const checkSet = () => { if (isObject(data)) { const arr = []; for (const field in data) { arr.push(`${field} = ?`); params.push(data[field]); } sqlArr.push(arr.join(',')); return; } if (isTwoArray(data)) { const arr = []; data.forEach(d => { arr.push(`${d[0]} = ${d[1]}`); }); sqlArr.push(arr.join(',')); return; } if (isArrayData(data) && data.length == 3) { sqlArr.push(`${data[0]} ${data[1]}`); params.push(data[2]); return; } throw new Error("You can only submit data in the form of arrays, two-dimensional arrays, and objects"); }; checkSet(); const resultWhere = this.buildWhere(); if (resultWhere) { sqlArr.push('WHERE', resultWhere.where); params.push(...resultWhere.params); } if (this._order.length > 0) { const orderArr = this._order.map(o => `\`${o.field}\` ${o.order}`); sqlArr.push(`ORDER BY ${orderArr.join(',')}`); } return { sql: sqlArr.join(' '), params, }; } buildSql(type = 'select', data) { switch (type) { case 'select': return this.buildSelect(); case 'insert': return this.buildInsert(data); case 'update': return this.buildUpdate(data); case 'delete': return this.buildDelete(); default: throw new Error('Unknown type'); } } }