完成3.0版的转变
parent
f289d2f3c2
commit
3340e93509
|
@ -1,12 +1,12 @@
|
||||||
3.0.0 / 2018-04-16
|
3.0.0 / 2018-04-16
|
||||||
==================
|
==================
|
||||||
> 这是一个全新的大版本, 不向下兼容2.x, 使用typescript重构。
|
> 这是一个全新的大版本, 不向下兼容2.x。
|
||||||
|
|
||||||
* 重构API, 使用链式操作, 逻辑更加清晰
|
* 重构API, 使用链式操作, 逻辑更加清晰
|
||||||
* 支持完整的pool option
|
* 支持完整的pool option
|
||||||
* 新增drop方法,支持删除数据库/数据表
|
* 新增drop方法,支持删除数据库/数据表
|
||||||
* 新增tableCreate/dbCreate方法, 支持创建数据库/数据表
|
* 新增tableCreate/dbCreate方法, 支持创建数据库/数据表
|
||||||
* 新增indexCreate/indexRename/indexDrop/indexList, 支持对索引的增删改查
|
* 新增indexCreate/indexDrop/indexList, 支持对索引的增删改查
|
||||||
|
|
||||||
|
|
||||||
2.2.2 / 2018-03-22
|
2.2.2 / 2018-03-22
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2018
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -5,17 +5,15 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
'use strict'
|
'use strict'
|
||||||
import 'es.shim'
|
require('es.shim')
|
||||||
const mysql = require('mysql')
|
const mysql = require('mysql')
|
||||||
import Api from './lib/api'
|
const Api = require('./lib/api')
|
||||||
|
|
||||||
class Mysqli {
|
class Mysqli {
|
||||||
useSlaveDB: boolean
|
|
||||||
pool: any
|
|
||||||
/**
|
/**
|
||||||
* [constructor 构造数据库连接池]
|
* [constructor 构造数据库连接池]
|
||||||
*/
|
*/
|
||||||
constructor(config: any) {
|
constructor(config) {
|
||||||
if (!Array.isArray(config)) {
|
if (!Array.isArray(config)) {
|
||||||
config = [config]
|
config = [config]
|
||||||
}
|
}
|
||||||
|
@ -27,7 +25,7 @@ class Mysqli {
|
||||||
restoreNodeTimeout: 10000
|
restoreNodeTimeout: 10000
|
||||||
})
|
})
|
||||||
|
|
||||||
config.forEach((item: { [prop: string]: any }, i: number) => {
|
config.forEach((item, i) => {
|
||||||
let {
|
let {
|
||||||
host,
|
host,
|
||||||
port,
|
port,
|
||||||
|
@ -66,11 +64,11 @@ class Mysqli {
|
||||||
}
|
}
|
||||||
|
|
||||||
//对外的escape方法
|
//对外的escape方法
|
||||||
static escape(val: any) {
|
static escape(val) {
|
||||||
return mysql.escape(val)
|
return mysql.escape(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
emit(fromSlave = false, db: string = '') {
|
emit(fromSlave = false, db = '') {
|
||||||
let slave = fromSlave && this.useSlaveDB ? 'SLAVE*' : 'MASTER'
|
let slave = fromSlave && this.useSlaveDB ? 'SLAVE*' : 'MASTER'
|
||||||
return new Api(this.pool, slave, db)
|
return new Api(this.pool, slave, db)
|
||||||
}
|
}
|
|
@ -0,0 +1,180 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @authors yutent (yutent@doui.cc)
|
||||||
|
* @date 2018-04-13 14:30:49
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { SqlErr, parser, fixtable } = require('./utils')
|
||||||
|
const Method = require('./method')
|
||||||
|
|
||||||
|
class Api {
|
||||||
|
constructor(pool, slave = 'MASTER', db = '') {
|
||||||
|
this.pool = pool
|
||||||
|
this.slave = slave
|
||||||
|
this.db = db ? '`' + db + '`' : null
|
||||||
|
}
|
||||||
|
|
||||||
|
connect() {
|
||||||
|
let defer = Promise.defer()
|
||||||
|
this.pool.getConnection(this.slave, (err, conn) => {
|
||||||
|
if (err) {
|
||||||
|
return defer.reject(new SqlErr(`MySQL connect ${err}`))
|
||||||
|
}
|
||||||
|
if (this.db) {
|
||||||
|
conn.query('USE ' + this.db, err => {
|
||||||
|
if (err) {
|
||||||
|
return defer.reject(new SqlErr('Use DB ' + err))
|
||||||
|
}
|
||||||
|
defer.resolve(conn)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
defer.resolve(conn)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return defer.promise
|
||||||
|
}
|
||||||
|
|
||||||
|
table(name) {
|
||||||
|
if (!name) {
|
||||||
|
throw new SqlErr('Query Error: empty table')
|
||||||
|
}
|
||||||
|
name = fixtable(name)
|
||||||
|
return new Method(this.pool, this.slave, this.db, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [query sql语句执行]
|
||||||
|
* @param {[type]} sql [sql语句]
|
||||||
|
*/
|
||||||
|
query(sql) {
|
||||||
|
if (typeof sql !== 'string') {
|
||||||
|
return Promise.reject(
|
||||||
|
new SqlErr(
|
||||||
|
`Query error, argument sql must be string. ${typeof sql} given`,
|
||||||
|
sql
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.connect().then(conn => {
|
||||||
|
let defer = Promise.defer()
|
||||||
|
|
||||||
|
conn.query(sql, (err, result) => {
|
||||||
|
conn.release()
|
||||||
|
if (err) {
|
||||||
|
return defer.reject(new SqlErr(`Query ${err}`, sql))
|
||||||
|
}
|
||||||
|
defer.resolve(result)
|
||||||
|
})
|
||||||
|
return defer.promise
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
drop(db) {
|
||||||
|
if (!this.db && db) {
|
||||||
|
return Promise.reject('No database selected.')
|
||||||
|
}
|
||||||
|
this.db = db || this.db
|
||||||
|
let defer = Promise.defer()
|
||||||
|
|
||||||
|
this.connect().then(conn => {
|
||||||
|
conn.query(`DROP DATABASE ${db || this.db}`, (err, result) => {
|
||||||
|
conn.release()
|
||||||
|
if (err) {
|
||||||
|
return defer.reject(new SqlErr(`Drop database ${err}`))
|
||||||
|
}
|
||||||
|
defer.resolve(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return defer.promise
|
||||||
|
}
|
||||||
|
|
||||||
|
dbList() {
|
||||||
|
return this.connect().then(conn => {
|
||||||
|
let defer = Promise.defer()
|
||||||
|
|
||||||
|
conn.query('SHOW DATABASES', (err, row) => {
|
||||||
|
conn.release()
|
||||||
|
if (err) {
|
||||||
|
return defer.reject(new SqlErr('Show databases ' + err))
|
||||||
|
}
|
||||||
|
defer.resolve(row.map(it => it.Database))
|
||||||
|
})
|
||||||
|
|
||||||
|
return defer.promise
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//返回数据表
|
||||||
|
tableList() {
|
||||||
|
return this.connect().then(conn => {
|
||||||
|
let defer = Promise.defer()
|
||||||
|
|
||||||
|
conn.query('SHOW TABLES', (err, row) => {
|
||||||
|
conn.release()
|
||||||
|
if (err) {
|
||||||
|
return defer.reject(new SqlErr('Show tables ' + err))
|
||||||
|
}
|
||||||
|
defer.resolve(row.map(it => it[Object.keys(it)[0]]))
|
||||||
|
})
|
||||||
|
|
||||||
|
return defer.promise
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建新的数据库
|
||||||
|
dbCreate(name, { charset = 'utf8' }) {
|
||||||
|
if (!name) {
|
||||||
|
return Promise.reject('Empty database name.')
|
||||||
|
}
|
||||||
|
|
||||||
|
let sql = `CREATE DATABASE \`${name}\` DEFAULT CHARACTER SET = \`${charset}\``
|
||||||
|
return this.connect().then(conn => {
|
||||||
|
let defer = Promise.defer()
|
||||||
|
|
||||||
|
conn.query(sql, (err, result) => {
|
||||||
|
conn.release()
|
||||||
|
if (err) {
|
||||||
|
return defer.reject(new SqlErr('Create database ' + err))
|
||||||
|
}
|
||||||
|
defer.resolve(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
return defer.promise
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建新的表,
|
||||||
|
tableCreate(name, fields, { charset = 'utf8', engine = 'InnoDB' }) {
|
||||||
|
if (!name) {
|
||||||
|
return Promise.reject('Empty database name.')
|
||||||
|
}
|
||||||
|
|
||||||
|
let sql = `CREATE TABLE \`${name}\` `
|
||||||
|
|
||||||
|
try {
|
||||||
|
sql += parser.field(fields)
|
||||||
|
} catch (err) {
|
||||||
|
return Promise.reject(err + '')
|
||||||
|
}
|
||||||
|
|
||||||
|
sql += ` ENGINE=${engine} DEFAULT CHARSET=${charset}`
|
||||||
|
|
||||||
|
return this.connect().then(conn => {
|
||||||
|
let defer = Promise.defer()
|
||||||
|
|
||||||
|
conn.query(sql, (err, result) => {
|
||||||
|
conn.release()
|
||||||
|
if (err) {
|
||||||
|
return defer.reject(new SqlErr('Create table ' + err, sql))
|
||||||
|
}
|
||||||
|
defer.resolve(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
return defer.promise
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Api
|
|
@ -5,57 +5,47 @@
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { defer, SqlErr, parser, escape } from './utils'
|
const { SqlErr, parser, escape, fixtable } = require('./utils')
|
||||||
|
|
||||||
console.log(escape(new Date()))
|
|
||||||
interface Conn {
|
|
||||||
query(sql: string, cb?: any): void
|
|
||||||
release(): void
|
|
||||||
}
|
|
||||||
class Method {
|
class Method {
|
||||||
pool: any
|
constructor(pool, slave, db, table) {
|
||||||
slave: string
|
|
||||||
db: string
|
|
||||||
cache: { [prop: string]: any } = {}
|
|
||||||
|
|
||||||
constructor(pool: object, slave: string, db: string, table: string) {
|
|
||||||
this.pool = pool
|
this.pool = pool
|
||||||
this.slave = slave
|
this.slave = slave
|
||||||
this.db = db
|
this.db = db
|
||||||
this.cache = { table }
|
this.cache = { table }
|
||||||
}
|
}
|
||||||
|
|
||||||
private connect() {
|
connect() {
|
||||||
let out = defer()
|
let defer = Promise.defer()
|
||||||
this.pool.getConnection(this.slave, (err: Error, conn: Conn) => {
|
this.pool.getConnection(this.slave, (err, conn) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
return out.reject(new SqlErr(`MySQL connect ${err}`))
|
return defer.reject(new SqlErr(`MySQL connect ${err}`))
|
||||||
}
|
}
|
||||||
if (this.db) {
|
if (this.db) {
|
||||||
conn.query('USE ' + this.db, (err: Error) => {
|
conn.query('USE ' + this.db, err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
return out.reject(new SqlErr('Select DB ' + err))
|
return defer.reject(new SqlErr('Use DB ' + err))
|
||||||
}
|
}
|
||||||
out.resolve(conn)
|
defer.resolve(conn)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
out.resolve(conn)
|
defer.resolve(conn)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return out.promise
|
return defer.promise
|
||||||
}
|
}
|
||||||
|
|
||||||
leftJoin(tables: any[]) {
|
leftJoin(tables) {
|
||||||
this.cache.leftJoin = tables
|
this.cache.leftJoin = tables
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
rightJoin(tables: any[]) {
|
rightJoin(tables) {
|
||||||
this.cache.rightJoin = tables
|
this.cache.rightJoin = tables
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
join(tables: any[]) {
|
join(tables) {
|
||||||
this.cache.join = tables
|
this.cache.join = tables
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
@ -71,7 +61,7 @@ class Method {
|
||||||
* })
|
* })
|
||||||
* obj形式的过滤, 支持多种多样, 详细请看Readme介绍
|
* obj形式的过滤, 支持多种多样, 详细请看Readme介绍
|
||||||
*/
|
*/
|
||||||
filter(val: any) {
|
filter(val) {
|
||||||
this.cache.filter = val
|
this.cache.filter = val
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
@ -81,25 +71,25 @@ class Method {
|
||||||
* @param {number }} keys [以对象形式传入值]
|
* @param {number }} keys [以对象形式传入值]
|
||||||
* 如: {name: 1, age: -1} 1代表顺序, -1代表逆序
|
* 如: {name: 1, age: -1} 1代表顺序, -1代表逆序
|
||||||
*/
|
*/
|
||||||
sort(keys: { [prop: string]: number }) {
|
sort(keys) {
|
||||||
this.cache.sort = keys
|
this.cache.sort = keys
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从第几条记录开始返回, 必须搭配limit使用,否则会被忽略
|
// 从第几条记录开始返回, 必须搭配limit使用,否则会被忽略
|
||||||
skip(skip: number) {
|
skip(skip) {
|
||||||
this.cache.skip = skip
|
this.cache.skip = skip
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
// 返回指定数量的记录
|
// 返回指定数量的记录
|
||||||
limit(size: number) {
|
limit(size) {
|
||||||
this.cache.size = size
|
this.cache.size = size
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
// 截取指定范围内的记录
|
// 截取指定范围内的记录
|
||||||
slice(start: number, end: number) {
|
slice(start, end) {
|
||||||
this.cache.limit = [start, end - start]
|
this.cache.limit = [start, end - start]
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
@ -108,7 +98,7 @@ class Method {
|
||||||
* [withFields 选取指定的字段]
|
* [withFields 选取指定的字段]
|
||||||
* @param {string[]} fields [以数组形式传入]
|
* @param {string[]} fields [以数组形式传入]
|
||||||
*/
|
*/
|
||||||
withFields(fields: string[]) {
|
withFields(fields) {
|
||||||
this.cache.fields = fields
|
this.cache.fields = fields
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
@ -121,10 +111,14 @@ class Method {
|
||||||
* [getAll 获取所有记录]
|
* [getAll 获取所有记录]
|
||||||
* @param {any[]} ids [description]
|
* @param {any[]} ids [description]
|
||||||
*/
|
*/
|
||||||
getAll(ids?: any[]) {
|
getAll(ids) {
|
||||||
if (!this.cache.filter && ids) {
|
if (!this.cache.filter && ids) {
|
||||||
|
if (ids.length === 1) {
|
||||||
|
this.cache.filter = { id: ids[0] }
|
||||||
|
} else {
|
||||||
this.cache.filter = { id: { $in: ids } }
|
this.cache.filter = { id: { $in: ids } }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
table,
|
table,
|
||||||
|
@ -149,8 +143,7 @@ class Method {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let sql: string
|
let sql = parser.select(fields)
|
||||||
sql = parser.select(fields)
|
|
||||||
sql += `FROM ${table} `
|
sql += `FROM ${table} `
|
||||||
if (leftJoin) {
|
if (leftJoin) {
|
||||||
sql += parser.leftJoin(leftJoin)
|
sql += parser.leftJoin(leftJoin)
|
||||||
|
@ -174,17 +167,17 @@ class Method {
|
||||||
sql += parser.limit(limit)
|
sql += parser.limit(limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.connect().then((conn: Conn) => {
|
return this.connect().then(conn => {
|
||||||
let out = defer()
|
let defer = Promise.defer()
|
||||||
|
|
||||||
conn.query(sql, (err: Error, result: any[]) => {
|
conn.query(sql, (err, result) => {
|
||||||
conn.release()
|
conn.release()
|
||||||
if (err) {
|
if (err) {
|
||||||
return out.reject(new SqlErr(`Find ${err}`, sql))
|
return defer.reject(new SqlErr(`Find ${err}`, sql))
|
||||||
}
|
}
|
||||||
out.resolve(result)
|
defer.resolve(result)
|
||||||
})
|
})
|
||||||
return out.promise
|
return defer.promise
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,8 +185,8 @@ class Method {
|
||||||
* [get 获取单条记录详细]
|
* [get 获取单条记录详细]
|
||||||
* @param {any} id [取主键值为id的记录, 当且仅当没设置过滤条件时有效]
|
* @param {any} id [取主键值为id的记录, 当且仅当没设置过滤条件时有效]
|
||||||
*/
|
*/
|
||||||
get(id?: any) {
|
get(id) {
|
||||||
return this.getAll(id ? [id] : null).then((list: any[]) => {
|
return this.getAll(id ? [id] : null).then(list => {
|
||||||
return list[0]
|
return list[0]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -202,8 +195,8 @@ class Method {
|
||||||
* [count 获取记录总数]
|
* [count 获取记录总数]
|
||||||
* @return {number} [description]
|
* @return {number} [description]
|
||||||
*/
|
*/
|
||||||
count(): number {
|
count() {
|
||||||
return this.getAll().then((list: any[]) => {
|
return this.getAll().then(list => {
|
||||||
return list.length
|
return list.length
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -212,7 +205,7 @@ class Method {
|
||||||
* [insert 插入单条文档, 返回当前插入的文档的ID(如果是自增)]
|
* [insert 插入单条文档, 返回当前插入的文档的ID(如果是自增)]
|
||||||
* @param {any }} doc [文档object]
|
* @param {any }} doc [文档object]
|
||||||
*/
|
*/
|
||||||
insert(doc: { [prop: string]: any }) {
|
insert(doc) {
|
||||||
if (!doc) {
|
if (!doc) {
|
||||||
return Promise.reject(new SqlErr('Insert Error: empty document'))
|
return Promise.reject(new SqlErr('Insert Error: empty document'))
|
||||||
}
|
}
|
||||||
|
@ -227,19 +220,19 @@ class Method {
|
||||||
}
|
}
|
||||||
sql += `(${keys.join(',')}) VALUES (${vals.join(',')})`
|
sql += `(${keys.join(',')}) VALUES (${vals.join(',')})`
|
||||||
|
|
||||||
return this.connect().then((conn: Conn) => {
|
return this.connect().then(conn => {
|
||||||
const out = defer()
|
const defer = Promise.defer()
|
||||||
|
|
||||||
conn.query(sql, (err: Error, result: any) => {
|
conn.query(sql, (err, result) => {
|
||||||
conn.release()
|
conn.release()
|
||||||
if (err) {
|
if (err) {
|
||||||
return out.reject(new SqlErr(`Insert ${err}`, sql))
|
return defer.reject(new SqlErr(`Insert ${err}`, sql))
|
||||||
}
|
}
|
||||||
|
|
||||||
out.resolve(result.insertId)
|
defer.resolve(result.insertId)
|
||||||
})
|
})
|
||||||
|
|
||||||
return out.promise
|
return defer.promise
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,7 +241,7 @@ class Method {
|
||||||
* 可以使用filter过滤条件
|
* 可以使用filter过滤条件
|
||||||
* @param {any }} doc [要更新的字段]
|
* @param {any }} doc [要更新的字段]
|
||||||
*/
|
*/
|
||||||
update(doc: { [prop: string]: any }) {
|
update(doc) {
|
||||||
if (!doc) {
|
if (!doc) {
|
||||||
return Promise.reject(new SqlErr('Update Error: empty document'))
|
return Promise.reject(new SqlErr('Update Error: empty document'))
|
||||||
}
|
}
|
||||||
|
@ -267,19 +260,19 @@ class Method {
|
||||||
sql += fields.join(',')
|
sql += fields.join(',')
|
||||||
sql += parser.filter(filter)
|
sql += parser.filter(filter)
|
||||||
|
|
||||||
return this.connect().then((conn: Conn) => {
|
return this.connect().then(conn => {
|
||||||
const out = defer()
|
const defer = Promise.defer()
|
||||||
|
|
||||||
conn.query(sql, (err: Error, result: any) => {
|
conn.query(sql, (err, result) => {
|
||||||
conn.release()
|
conn.release()
|
||||||
if (err) {
|
if (err) {
|
||||||
return out.reject(new SqlErr(`Update ${err}`, sql))
|
return defer.reject(new SqlErr(`Update ${err}`, sql))
|
||||||
}
|
}
|
||||||
|
|
||||||
out.resolve(result.affectedRows)
|
defer.resolve(result.affectedRows)
|
||||||
})
|
})
|
||||||
|
|
||||||
return out.promise
|
return defer.promise
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,21 +285,125 @@ class Method {
|
||||||
let sql = `DELETE FROM ${table} `
|
let sql = `DELETE FROM ${table} `
|
||||||
sql += parser.filter(filter)
|
sql += parser.filter(filter)
|
||||||
|
|
||||||
return this.connect().then((conn: Conn) => {
|
return this.connect().then(conn => {
|
||||||
const out = defer()
|
const defer = Promise.defer()
|
||||||
|
|
||||||
conn.query(sql, (err: Error, result: any) => {
|
conn.query(sql, (err, result) => {
|
||||||
conn.release()
|
conn.release()
|
||||||
if (err) {
|
if (err) {
|
||||||
return out.reject(new SqlErr(`Remove ${err}`, sql))
|
return defer.reject(new SqlErr(`Remove ${err}`, sql))
|
||||||
}
|
}
|
||||||
|
|
||||||
out.resolve(result.affectedRows)
|
defer.resolve(result.affectedRows)
|
||||||
})
|
})
|
||||||
|
|
||||||
return out.promise
|
return defer.promise
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重命名表
|
||||||
|
renameTo(name) {
|
||||||
|
return this.connect().then(conn => {
|
||||||
|
const defer = Promise.defer()
|
||||||
|
|
||||||
|
let sql = `RENAME TABLE ${this.cache.table} TO ${fixtable(name)}`
|
||||||
|
conn.query(sql, (err, result) => {
|
||||||
|
conn.release()
|
||||||
|
if (err) {
|
||||||
|
return defer.reject(new SqlErr(`List index ${err}`, sql))
|
||||||
|
}
|
||||||
|
|
||||||
|
defer.resolve(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
return defer.promise
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回索引列表
|
||||||
|
indexList() {
|
||||||
|
return this.connect().then(conn => {
|
||||||
|
const defer = Promise.defer()
|
||||||
|
|
||||||
|
let sql = `SHOW INDEX FROM ${this.cache.table}`
|
||||||
|
conn.query(sql, (err, result) => {
|
||||||
|
conn.release()
|
||||||
|
if (err) {
|
||||||
|
return defer.reject(new SqlErr(`List index ${err}`, sql))
|
||||||
|
}
|
||||||
|
let list = result.map(it => {
|
||||||
|
return {
|
||||||
|
name: it.Key_name,
|
||||||
|
column: it.Column_name,
|
||||||
|
unique: !it.Non_unique,
|
||||||
|
cardinality: it.Cardinality,
|
||||||
|
collation: it.Collation,
|
||||||
|
index: it.Seq_in_index
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
defer.resolve(list)
|
||||||
|
})
|
||||||
|
|
||||||
|
return defer.promise
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除指定索引
|
||||||
|
indexDrop(name) {
|
||||||
|
if (!name) {
|
||||||
|
return Promise.reject('Empty index name')
|
||||||
|
}
|
||||||
|
return this.connect().then(conn => {
|
||||||
|
const defer = Promise.defer()
|
||||||
|
|
||||||
|
let sql = `ALTER TABLE ${this.cache.table} DROP INDEX \`${name}\``
|
||||||
|
conn.query(sql, (err, result) => {
|
||||||
|
conn.release()
|
||||||
|
if (err) {
|
||||||
|
return defer.reject(new SqlErr(`Drop index ${err}`, sql))
|
||||||
|
}
|
||||||
|
|
||||||
|
defer.resolve(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
return defer.promise
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除指定索引
|
||||||
|
indexCreate(name, opt = {}) {
|
||||||
|
if (!name) {
|
||||||
|
return Promise.reject('Empty index name')
|
||||||
|
}
|
||||||
|
if (!opt.field) {
|
||||||
|
return Promise.reject('Empty field name')
|
||||||
|
}
|
||||||
|
let unique = ''
|
||||||
|
opt.field = '`' + opt.field + '`'
|
||||||
|
if (opt.unique) {
|
||||||
|
unique = 'UNIQUE'
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.connect().then(conn => {
|
||||||
|
const defer = Promise.defer()
|
||||||
|
|
||||||
|
let sql = `ALTER TABLE ${
|
||||||
|
this.cache.table
|
||||||
|
} ADD ${unique} INDEX \`${name}\` (${opt.field})`
|
||||||
|
|
||||||
|
conn.query(sql, (err, result) => {
|
||||||
|
conn.release()
|
||||||
|
if (err) {
|
||||||
|
return defer.reject(new SqlErr(`Drop index ${err}`, sql))
|
||||||
|
}
|
||||||
|
|
||||||
|
defer.resolve(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
return defer.promise
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Method
|
module.exports = Method
|
|
@ -6,7 +6,16 @@
|
||||||
*/
|
*/
|
||||||
const { escape } = require('mysql')
|
const { escape } = require('mysql')
|
||||||
|
|
||||||
function getType(val: any): string {
|
function hideProperty(host, name, value) {
|
||||||
|
Object.defineProperty(host, name, {
|
||||||
|
value: value,
|
||||||
|
writable: true,
|
||||||
|
enumerable: false,
|
||||||
|
configurable: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getType(val) {
|
||||||
if (val === null) {
|
if (val === null) {
|
||||||
return String(val)
|
return String(val)
|
||||||
}
|
}
|
||||||
|
@ -15,7 +24,7 @@ function getType(val: any): string {
|
||||||
.slice(8, -1)
|
.slice(8, -1)
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
}
|
}
|
||||||
function parse$or(arr: any[]) {
|
function parse$or(arr) {
|
||||||
let sql = ''
|
let sql = ''
|
||||||
for (let it of arr) {
|
for (let it of arr) {
|
||||||
sql += '('
|
sql += '('
|
||||||
|
@ -29,7 +38,7 @@ function parse$or(arr: any[]) {
|
||||||
sql = sql.slice(0, -3)
|
sql = sql.slice(0, -3)
|
||||||
return sql
|
return sql
|
||||||
}
|
}
|
||||||
function parse$and(arr: any[]) {
|
function parse$and(arr) {
|
||||||
let sql = ''
|
let sql = ''
|
||||||
for (let it of arr) {
|
for (let it of arr) {
|
||||||
sql += '('
|
sql += '('
|
||||||
|
@ -44,7 +53,7 @@ function parse$and(arr: any[]) {
|
||||||
return sql
|
return sql
|
||||||
}
|
}
|
||||||
|
|
||||||
function parse$opt(opt: { [prop: string]: any }) {
|
function parse$opt(opt) {
|
||||||
let sql = ''
|
let sql = ''
|
||||||
for (let k in opt) {
|
for (let k in opt) {
|
||||||
let tmp = opt[k]
|
let tmp = opt[k]
|
||||||
|
@ -60,7 +69,7 @@ function parse$opt(opt: { [prop: string]: any }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tmp.$in) {
|
if (tmp.$in) {
|
||||||
let list = tmp.$in.map((it: any) => {
|
let list = tmp.$in.map(it => {
|
||||||
return escape(it)
|
return escape(it)
|
||||||
})
|
})
|
||||||
sql += ` ${k} IN (${list.join(',')}) `
|
sql += ` ${k} IN (${list.join(',')}) `
|
||||||
|
@ -70,7 +79,7 @@ function parse$opt(opt: { [prop: string]: any }) {
|
||||||
if (tmp.$between.length < 2) {
|
if (tmp.$between.length < 2) {
|
||||||
throw new Error(`Array $between's length must be 2.`)
|
throw new Error(`Array $between's length must be 2.`)
|
||||||
}
|
}
|
||||||
let list = tmp.$between.map((it: any) => {
|
let list = tmp.$between.map(it => {
|
||||||
return escape(it)
|
return escape(it)
|
||||||
})
|
})
|
||||||
sql += ` ${k} BETWEEN ${list[0]} AND ${list[1]} `
|
sql += ` ${k} BETWEEN ${list[0]} AND ${list[1]} `
|
||||||
|
@ -102,32 +111,45 @@ function parse$opt(opt: { [prop: string]: any }) {
|
||||||
return sql
|
return sql
|
||||||
}
|
}
|
||||||
|
|
||||||
export const parser = {
|
// 格式化表名
|
||||||
leftJoin(tables: any[]) {
|
function fixtable(name) {
|
||||||
|
return name
|
||||||
|
.split('.')
|
||||||
|
.map(it => {
|
||||||
|
return '`' + it + '`'
|
||||||
|
})
|
||||||
|
.join('.')
|
||||||
|
}
|
||||||
|
|
||||||
|
const parser = {
|
||||||
|
leftJoin(tables = []) {
|
||||||
let sql = ''
|
let sql = ''
|
||||||
for (let it of tables) {
|
for (let it of tables) {
|
||||||
|
it.table = fixtable(it.table)
|
||||||
sql += ` LEFT JOIN ${it.table} ON ${it.on} `
|
sql += ` LEFT JOIN ${it.table} ON ${it.on} `
|
||||||
}
|
}
|
||||||
return sql
|
return sql
|
||||||
},
|
},
|
||||||
|
|
||||||
rightJoin(tables: any[]) {
|
rightJoin(tables = []) {
|
||||||
let sql = ''
|
let sql = ''
|
||||||
for (let it of tables) {
|
for (let it of tables) {
|
||||||
|
it.table = fixtable(it.table)
|
||||||
sql += ` RIGHT JOIN ${it.table} ON ${it.on} `
|
sql += ` RIGHT JOIN ${it.table} ON ${it.on} `
|
||||||
}
|
}
|
||||||
return sql
|
return sql
|
||||||
},
|
},
|
||||||
|
|
||||||
join(tables: any[]) {
|
join(tables = []) {
|
||||||
let sql = ''
|
let sql = ''
|
||||||
for (let it of tables) {
|
for (let it of tables) {
|
||||||
|
it.table = fixtable(it.table)
|
||||||
sql += ` JOIN ${it[0]} ON ${it.on} `
|
sql += ` JOIN ${it[0]} ON ${it.on} `
|
||||||
}
|
}
|
||||||
return sql
|
return sql
|
||||||
},
|
},
|
||||||
|
|
||||||
filter(opt: any) {
|
filter(opt) {
|
||||||
if (typeof opt === 'string') {
|
if (typeof opt === 'string') {
|
||||||
return ` WHERE ${opt} `
|
return ` WHERE ${opt} `
|
||||||
}
|
}
|
||||||
|
@ -146,12 +168,12 @@ export const parser = {
|
||||||
return ' '
|
return ' '
|
||||||
},
|
},
|
||||||
|
|
||||||
select(arr: string[] = ['*']) {
|
select(arr = ['*']) {
|
||||||
return `SELECT ${arr.join(',')} `
|
return `SELECT ${arr.join(',')} `
|
||||||
},
|
},
|
||||||
|
|
||||||
// 排序 ----------------------------------
|
// 排序 ----------------------------------
|
||||||
sort(obj: { [propName: string]: number } = {}) {
|
sort(obj = {}) {
|
||||||
let sort = ''
|
let sort = ''
|
||||||
for (let i in obj) {
|
for (let i in obj) {
|
||||||
let c = ''
|
let c = ''
|
||||||
|
@ -167,28 +189,91 @@ export const parser = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
limit(...args: number[]) {
|
limit(...args) {
|
||||||
return ` LIMIT ${args.join(',')} `
|
return ` LIMIT ${args.join(',')} `
|
||||||
|
},
|
||||||
|
|
||||||
|
// 解析数据表的配置(新建表时)
|
||||||
|
field(fields = []) {
|
||||||
|
let primary = null
|
||||||
|
let indexes = []
|
||||||
|
let sql = `(\n`
|
||||||
|
for (let it of fields) {
|
||||||
|
it.type = it.type.toUpperCase()
|
||||||
|
let inc = '' // 自增
|
||||||
|
let autoUpdate = '' // 自动更新时间戳(仅 DATETIME & TIMESTAMP)
|
||||||
|
let defaultVal = ''
|
||||||
|
// 只处理1个主键, 其他的忽略, 且作为主键,必须 NOT NULL
|
||||||
|
if (!primary && it.primary) {
|
||||||
|
primary = `PRIMARY KEY (\`${it.name}\`),\n`
|
||||||
|
it.notnull = true
|
||||||
|
if (it.inc) {
|
||||||
|
inc = 'AUTO_INCREMENT'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let notnull = it.notnull ? 'NOT NULL' : 'NULL'
|
||||||
|
|
||||||
|
if (/CHAR/.test(it.type)) {
|
||||||
|
it.default = it.default ? escape(it.default) : ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// 这几种类型,不允许设置默认值
|
||||||
|
if (['TEXT', 'BLOB', 'JSON', 'GEOMETRY'].includes(it.type)) {
|
||||||
|
notnull = 'NULL'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 这2种类型,如果设置了自动更新时间戳, 则默认值自动改为当前时间戳
|
||||||
|
if (['TIMESTAMP', 'DATETIME'].includes(it.type)) {
|
||||||
|
if (it.update) {
|
||||||
|
autoUpdate = 'ON UPDATE CURRENT_TIMESTAMP'
|
||||||
|
it.default = 'CURRENT_TIMESTAMP'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SqlErr extends Error {
|
// 这3种时间类型,不允许设置默认值为 当前时间戳
|
||||||
sql: string
|
if (['TIME', 'DATE', 'YEAR'].includes(it.type)) {
|
||||||
constructor(msg: string = '', sql: string = '') {
|
if (it.default.toUpperCase() === 'CURRENT_TIMESTAMP') {
|
||||||
super(msg)
|
it.default = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defaultVal = it.default ? `DEFAULT ${it.default}` : ''
|
||||||
|
|
||||||
|
// 非主键下, 设置了unique & index时,都为索引
|
||||||
|
if (!it.primary) {
|
||||||
|
if (it.index || it.unique) {
|
||||||
|
let idx = `INDEX \`${it.name}_idx\` (\`${it.name}\`)`
|
||||||
|
if (it.unique) {
|
||||||
|
idx = 'UNIQUE ' + idx
|
||||||
|
}
|
||||||
|
indexes.push(idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sql += `\`${it.name}\` ${
|
||||||
|
it.type
|
||||||
|
} ${notnull} ${defaultVal} ${inc} ${autoUpdate},\n`
|
||||||
|
}
|
||||||
|
if (!primary) {
|
||||||
|
throw new Error('Can not create table without primary key.')
|
||||||
|
}
|
||||||
|
sql += primary
|
||||||
|
sql += indexes.join(', \n') + ')'
|
||||||
|
return sql
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SqlErr {
|
||||||
|
constructor(msg = '', sql = '') {
|
||||||
|
this.message = msg
|
||||||
this.sql = sql
|
this.sql = sql
|
||||||
|
hideProperty(this, 'stack', msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
toString() {
|
||||||
|
return this.message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defer = () => {
|
exports.SqlErr = SqlErr
|
||||||
let obj: { [prop: string]: any } = {}
|
exports.parser = parser
|
||||||
obj.promise = new Promise((yes, no) => {
|
exports.escape = escape
|
||||||
obj.resolve = yes
|
exports.fixtable = fixtable
|
||||||
obj.reject = no
|
|
||||||
})
|
|
||||||
return obj
|
|
||||||
}
|
|
||||||
|
|
||||||
export { escape }
|
|
||||||
|
|
||||||
export default escape
|
|
|
@ -2,9 +2,9 @@
|
||||||
"name": "mysqli",
|
"name": "mysqli",
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"description": "MySQL tool",
|
"description": "MySQL tool",
|
||||||
"main": "dist/index.js",
|
"main": "index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es.shim": "^0.0.3",
|
"es.shim": "^1.0.1",
|
||||||
"mysql": "^2.13.0"
|
"mysql": "^2.13.0"
|
||||||
},
|
},
|
||||||
"repository": "https://github.com/yutent/mysqli.git",
|
"repository": "https://github.com/yutent/mysqli.git",
|
||||||
|
|
123
src/lib/api.ts
123
src/lib/api.ts
|
@ -1,123 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @authors yutent (yutent@doui.cc)
|
|
||||||
* @date 2018-04-13 14:30:49
|
|
||||||
* @version $Id$
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { defer, SqlErr } from './utils'
|
|
||||||
import Method from './method'
|
|
||||||
interface Conn {
|
|
||||||
query(sql: string, cb?: any): void
|
|
||||||
release(): void
|
|
||||||
}
|
|
||||||
class Api {
|
|
||||||
pool: any
|
|
||||||
slave: string
|
|
||||||
db: string
|
|
||||||
constructor(pool: object, slave: string = 'MASTER', db: string = '') {
|
|
||||||
this.pool = pool
|
|
||||||
this.slave = slave
|
|
||||||
this.db = db
|
|
||||||
}
|
|
||||||
|
|
||||||
connect() {
|
|
||||||
let out = defer()
|
|
||||||
this.pool.getConnection(this.slave, (err: Error, conn: Conn) => {
|
|
||||||
if (err) {
|
|
||||||
return out.reject(new SqlErr(`MySQL connect ${err}`))
|
|
||||||
}
|
|
||||||
if (this.db) {
|
|
||||||
conn.query('USE ' + this.db, (err: Error) => {
|
|
||||||
if (err) {
|
|
||||||
return out.reject(new SqlErr('Select DB ' + err))
|
|
||||||
}
|
|
||||||
out.resolve(conn)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
out.resolve(conn)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return out.promise
|
|
||||||
}
|
|
||||||
|
|
||||||
table(name: string) {
|
|
||||||
if (!name) {
|
|
||||||
throw new SqlErr('Query Error: empty table')
|
|
||||||
}
|
|
||||||
return new Method(this.pool, this.slave, this.db, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* [query sql语句执行]
|
|
||||||
* @param {[type]} sql [sql语句]
|
|
||||||
*/
|
|
||||||
query(sql: string) {
|
|
||||||
if (typeof sql !== 'string') {
|
|
||||||
return Promise.reject(
|
|
||||||
new SqlErr(
|
|
||||||
`Query error, argument sql must be string. ${typeof sql} given`,
|
|
||||||
sql
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.connect().then((conn: Conn) => {
|
|
||||||
let out = defer()
|
|
||||||
|
|
||||||
conn.query(sql, (err: Error, result: any) => {
|
|
||||||
conn.release()
|
|
||||||
if (err) {
|
|
||||||
return out.reject(new SqlErr(`Query ${err}`, sql))
|
|
||||||
}
|
|
||||||
out.resolve(result)
|
|
||||||
})
|
|
||||||
return out.promise
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
drop() {
|
|
||||||
if (!this.db) {
|
|
||||||
return Promise.reject('')
|
|
||||||
}
|
|
||||||
|
|
||||||
this.connect().then((conn: Conn) => {
|
|
||||||
conn.query('')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
dbList() {
|
|
||||||
return this.connect().then((conn: Conn) => {
|
|
||||||
let out = defer()
|
|
||||||
|
|
||||||
conn.query('SHOW DATABASES', (err: Error, row: any) => {
|
|
||||||
conn.release()
|
|
||||||
if (err) {
|
|
||||||
return out.reject(new SqlErr('SHOW DATABASES ' + err))
|
|
||||||
}
|
|
||||||
out.resolve(row.map((it: any) => it.Database))
|
|
||||||
})
|
|
||||||
|
|
||||||
return out.promise
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
//返回数据表
|
|
||||||
tableList() {
|
|
||||||
return this.connect().then((conn: Conn) => {
|
|
||||||
const out = defer()
|
|
||||||
|
|
||||||
conn.query('SHOW TABLES', (err: Error, row: any) => {
|
|
||||||
conn.release()
|
|
||||||
if (err) {
|
|
||||||
return out.reject(new SqlErr('SHOW TABLES ' + err))
|
|
||||||
}
|
|
||||||
out.resolve(row.map((it: any) => it[Object.keys(it)[0]]))
|
|
||||||
})
|
|
||||||
|
|
||||||
return out.promise
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Api
|
|
Loading…
Reference in New Issue