yoyosqlite
Version:
一个链式连接sqlite数据库的扩展包
673 lines (672 loc) • 23.6 kB
JavaScript
"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');
}
}
}