完成3.0版的转变
							parent
							
								
									f289d2f3c2
								
							
						
					
					
						commit
						3340e93509
					
				|  | @ -1,12 +1,12 @@ | |||
| 3.0.0 / 2018-04-16 | ||||
| ================== | ||||
| > 这是一个全新的大版本, 不向下兼容2.x, 使用typescript重构。 | ||||
| > 这是一个全新的大版本, 不向下兼容2.x。 | ||||
| 
 | ||||
|   * 重构API, 使用链式操作, 逻辑更加清晰 | ||||
|   * 支持完整的pool option | ||||
|   * 新增drop方法,支持删除数据库/数据表 | ||||
|   * 新增tableCreate/dbCreate方法, 支持创建数据库/数据表 | ||||
|   * 新增indexCreate/indexRename/indexDrop/indexList, 支持对索引的增删改查 | ||||
|   * 新增indexCreate/indexDrop/indexList, 支持对索引的增删改查 | ||||
| 
 | ||||
| 
 | ||||
| 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' | ||||
| import 'es.shim' | ||||
| require('es.shim') | ||||
| const mysql = require('mysql') | ||||
| import Api from './lib/api' | ||||
| const Api = require('./lib/api') | ||||
| 
 | ||||
| class Mysqli { | ||||
|   useSlaveDB: boolean | ||||
|   pool: any | ||||
|   /** | ||||
|    * [constructor 构造数据库连接池] | ||||
|    */ | ||||
|   constructor(config: any) { | ||||
|   constructor(config) { | ||||
|     if (!Array.isArray(config)) { | ||||
|       config = [config] | ||||
|     } | ||||
|  | @ -27,7 +25,7 @@ class Mysqli { | |||
|       restoreNodeTimeout: 10000 | ||||
|     }) | ||||
| 
 | ||||
|     config.forEach((item: { [prop: string]: any }, i: number) => { | ||||
|     config.forEach((item, i) => { | ||||
|       let { | ||||
|         host, | ||||
|         port, | ||||
|  | @ -66,11 +64,11 @@ class Mysqli { | |||
|   } | ||||
| 
 | ||||
|   //对外的escape方法
 | ||||
|   static escape(val: any) { | ||||
|   static escape(val) { | ||||
|     return mysql.escape(val) | ||||
|   } | ||||
| 
 | ||||
|   emit(fromSlave = false, db: string = '') { | ||||
|   emit(fromSlave = false, db = '') { | ||||
|     let slave = fromSlave && this.useSlaveDB ? 'SLAVE*' : 'MASTER' | ||||
|     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$ | ||||
|  */ | ||||
| 
 | ||||
| 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 { | ||||
|   pool: any | ||||
|   slave: string | ||||
|   db: string | ||||
|   cache: { [prop: string]: any } = {} | ||||
| 
 | ||||
|   constructor(pool: object, slave: string, db: string, table: string) { | ||||
|   constructor(pool, slave, db, table) { | ||||
|     this.pool = pool | ||||
|     this.slave = slave | ||||
|     this.db = db | ||||
|     this.cache = { table } | ||||
|   } | ||||
| 
 | ||||
|   private connect() { | ||||
|     let out = defer() | ||||
|     this.pool.getConnection(this.slave, (err: Error, conn: Conn) => { | ||||
|   connect() { | ||||
|     let defer = Promise.defer() | ||||
|     this.pool.getConnection(this.slave, (err, conn) => { | ||||
|       if (err) { | ||||
|         return out.reject(new SqlErr(`MySQL connect ${err}`)) | ||||
|         return defer.reject(new SqlErr(`MySQL connect ${err}`)) | ||||
|       } | ||||
|       if (this.db) { | ||||
|         conn.query('USE ' + this.db, (err: Error) => { | ||||
|         conn.query('USE ' + this.db, 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 { | ||||
|         out.resolve(conn) | ||||
|         defer.resolve(conn) | ||||
|       } | ||||
|     }) | ||||
|     return out.promise | ||||
|     return defer.promise | ||||
|   } | ||||
| 
 | ||||
|   leftJoin(tables: any[]) { | ||||
|   leftJoin(tables) { | ||||
|     this.cache.leftJoin = tables | ||||
|     return this | ||||
|   } | ||||
| 
 | ||||
|   rightJoin(tables: any[]) { | ||||
|   rightJoin(tables) { | ||||
|     this.cache.rightJoin = tables | ||||
|     return this | ||||
|   } | ||||
| 
 | ||||
|   join(tables: any[]) { | ||||
|   join(tables) { | ||||
|     this.cache.join = tables | ||||
|     return this | ||||
|   } | ||||
|  | @ -71,7 +61,7 @@ class Method { | |||
|    *     }) | ||||
|    * obj形式的过滤, 支持多种多样, 详细请看Readme介绍 | ||||
|    */ | ||||
|   filter(val: any) { | ||||
|   filter(val) { | ||||
|     this.cache.filter = val | ||||
|     return this | ||||
|   } | ||||
|  | @ -81,25 +71,25 @@ class Method { | |||
|    * @param {number }} keys [以对象形式传入值] | ||||
|    * 如: {name: 1, age: -1} 1代表顺序, -1代表逆序 | ||||
|    */ | ||||
|   sort(keys: { [prop: string]: number }) { | ||||
|   sort(keys) { | ||||
|     this.cache.sort = keys | ||||
|     return this | ||||
|   } | ||||
| 
 | ||||
|   // 从第几条记录开始返回, 必须搭配limit使用,否则会被忽略
 | ||||
|   skip(skip: number) { | ||||
|   skip(skip) { | ||||
|     this.cache.skip = skip | ||||
|     return this | ||||
|   } | ||||
| 
 | ||||
|   // 返回指定数量的记录
 | ||||
|   limit(size: number) { | ||||
|   limit(size) { | ||||
|     this.cache.size = size | ||||
|     return this | ||||
|   } | ||||
| 
 | ||||
|   // 截取指定范围内的记录
 | ||||
|   slice(start: number, end: number) { | ||||
|   slice(start, end) { | ||||
|     this.cache.limit = [start, end - start] | ||||
|     return this | ||||
|   } | ||||
|  | @ -108,7 +98,7 @@ class Method { | |||
|    * [withFields 选取指定的字段] | ||||
|    * @param {string[]} fields [以数组形式传入] | ||||
|    */ | ||||
|   withFields(fields: string[]) { | ||||
|   withFields(fields) { | ||||
|     this.cache.fields = fields | ||||
|     return this | ||||
|   } | ||||
|  | @ -121,10 +111,14 @@ class Method { | |||
|    * [getAll 获取所有记录] | ||||
|    * @param {any[]} ids [description] | ||||
|    */ | ||||
|   getAll(ids?: any[]) { | ||||
|   getAll(ids) { | ||||
|     if (!this.cache.filter && ids) { | ||||
|       if (ids.length === 1) { | ||||
|         this.cache.filter = { id: ids[0] } | ||||
|       } else { | ||||
|         this.cache.filter = { id: { $in: ids } } | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     let { | ||||
|       table, | ||||
|  | @ -149,8 +143,7 @@ class Method { | |||
|       } | ||||
|     } | ||||
| 
 | ||||
|     let sql: string | ||||
|     sql = parser.select(fields) | ||||
|     let sql = parser.select(fields) | ||||
|     sql += `FROM ${table} ` | ||||
|     if (leftJoin) { | ||||
|       sql += parser.leftJoin(leftJoin) | ||||
|  | @ -174,17 +167,17 @@ class Method { | |||
|       sql += parser.limit(limit) | ||||
|     } | ||||
| 
 | ||||
|     return this.connect().then((conn: Conn) => { | ||||
|       let out = defer() | ||||
|     return this.connect().then(conn => { | ||||
|       let defer = Promise.defer() | ||||
| 
 | ||||
|       conn.query(sql, (err: Error, result: any[]) => { | ||||
|       conn.query(sql, (err, result) => { | ||||
|         conn.release() | ||||
|         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 获取单条记录详细] | ||||
|    * @param {any} id [取主键值为id的记录, 当且仅当没设置过滤条件时有效] | ||||
|    */ | ||||
|   get(id?: any) { | ||||
|     return this.getAll(id ? [id] : null).then((list: any[]) => { | ||||
|   get(id) { | ||||
|     return this.getAll(id ? [id] : null).then(list => { | ||||
|       return list[0] | ||||
|     }) | ||||
|   } | ||||
|  | @ -202,8 +195,8 @@ class Method { | |||
|    * [count 获取记录总数] | ||||
|    * @return {number} [description] | ||||
|    */ | ||||
|   count(): number { | ||||
|     return this.getAll().then((list: any[]) => { | ||||
|   count() { | ||||
|     return this.getAll().then(list => { | ||||
|       return list.length | ||||
|     }) | ||||
|   } | ||||
|  | @ -212,7 +205,7 @@ class Method { | |||
|    * [insert 插入单条文档, 返回当前插入的文档的ID(如果是自增)] | ||||
|    * @param {any }} doc [文档object] | ||||
|    */ | ||||
|   insert(doc: { [prop: string]: any }) { | ||||
|   insert(doc) { | ||||
|     if (!doc) { | ||||
|       return Promise.reject(new SqlErr('Insert Error: empty document')) | ||||
|     } | ||||
|  | @ -227,19 +220,19 @@ class Method { | |||
|     } | ||||
|     sql += `(${keys.join(',')}) VALUES (${vals.join(',')})` | ||||
| 
 | ||||
|     return this.connect().then((conn: Conn) => { | ||||
|       const out = defer() | ||||
|     return this.connect().then(conn => { | ||||
|       const defer = Promise.defer() | ||||
| 
 | ||||
|       conn.query(sql, (err: Error, result: any) => { | ||||
|       conn.query(sql, (err, result) => { | ||||
|         conn.release() | ||||
|         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过滤条件 | ||||
|    * @param {any }} doc [要更新的字段] | ||||
|    */ | ||||
|   update(doc: { [prop: string]: any }) { | ||||
|   update(doc) { | ||||
|     if (!doc) { | ||||
|       return Promise.reject(new SqlErr('Update Error: empty document')) | ||||
|     } | ||||
|  | @ -267,19 +260,19 @@ class Method { | |||
|     sql += fields.join(',') | ||||
|     sql += parser.filter(filter) | ||||
| 
 | ||||
|     return this.connect().then((conn: Conn) => { | ||||
|       const out = defer() | ||||
|     return this.connect().then(conn => { | ||||
|       const defer = Promise.defer() | ||||
| 
 | ||||
|       conn.query(sql, (err: Error, result: any) => { | ||||
|       conn.query(sql, (err, result) => { | ||||
|         conn.release() | ||||
|         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} ` | ||||
|     sql += parser.filter(filter) | ||||
| 
 | ||||
|     return this.connect().then((conn: Conn) => { | ||||
|       const out = defer() | ||||
|     return this.connect().then(conn => { | ||||
|       const defer = Promise.defer() | ||||
| 
 | ||||
|       conn.query(sql, (err: Error, result: any) => { | ||||
|       conn.query(sql, (err, result) => { | ||||
|         conn.release() | ||||
|         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') | ||||
| 
 | ||||
| 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) { | ||||
|     return String(val) | ||||
|   } | ||||
|  | @ -15,7 +24,7 @@ function getType(val: any): string { | |||
|     .slice(8, -1) | ||||
|     .toLowerCase() | ||||
| } | ||||
| function parse$or(arr: any[]) { | ||||
| function parse$or(arr) { | ||||
|   let sql = '' | ||||
|   for (let it of arr) { | ||||
|     sql += '(' | ||||
|  | @ -29,7 +38,7 @@ function parse$or(arr: any[]) { | |||
|   sql = sql.slice(0, -3) | ||||
|   return sql | ||||
| } | ||||
| function parse$and(arr: any[]) { | ||||
| function parse$and(arr) { | ||||
|   let sql = '' | ||||
|   for (let it of arr) { | ||||
|     sql += '(' | ||||
|  | @ -44,7 +53,7 @@ function parse$and(arr: any[]) { | |||
|   return sql | ||||
| } | ||||
| 
 | ||||
| function parse$opt(opt: { [prop: string]: any }) { | ||||
| function parse$opt(opt) { | ||||
|   let sql = '' | ||||
|   for (let k in opt) { | ||||
|     let tmp = opt[k] | ||||
|  | @ -60,7 +69,7 @@ function parse$opt(opt: { [prop: string]: any }) { | |||
|         } | ||||
| 
 | ||||
|         if (tmp.$in) { | ||||
|           let list = tmp.$in.map((it: any) => { | ||||
|           let list = tmp.$in.map(it => { | ||||
|             return escape(it) | ||||
|           }) | ||||
|           sql += ` ${k} IN (${list.join(',')}) ` | ||||
|  | @ -70,7 +79,7 @@ function parse$opt(opt: { [prop: string]: any }) { | |||
|           if (tmp.$between.length < 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) | ||||
|           }) | ||||
|           sql += ` ${k} BETWEEN ${list[0]} AND ${list[1]} ` | ||||
|  | @ -102,32 +111,45 @@ function parse$opt(opt: { [prop: string]: any }) { | |||
|   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 = '' | ||||
|     for (let it of tables) { | ||||
|       it.table = fixtable(it.table) | ||||
|       sql += ` LEFT JOIN ${it.table} ON ${it.on} ` | ||||
|     } | ||||
|     return sql | ||||
|   }, | ||||
| 
 | ||||
|   rightJoin(tables: any[]) { | ||||
|   rightJoin(tables = []) { | ||||
|     let sql = '' | ||||
|     for (let it of tables) { | ||||
|       it.table = fixtable(it.table) | ||||
|       sql += ` RIGHT JOIN ${it.table} ON ${it.on} ` | ||||
|     } | ||||
|     return sql | ||||
|   }, | ||||
| 
 | ||||
|   join(tables: any[]) { | ||||
|   join(tables = []) { | ||||
|     let sql = '' | ||||
|     for (let it of tables) { | ||||
|       it.table = fixtable(it.table) | ||||
|       sql += ` JOIN ${it[0]} ON ${it.on} ` | ||||
|     } | ||||
|     return sql | ||||
|   }, | ||||
| 
 | ||||
|   filter(opt: any) { | ||||
|   filter(opt) { | ||||
|     if (typeof opt === 'string') { | ||||
|       return ` WHERE ${opt} ` | ||||
|     } | ||||
|  | @ -146,12 +168,12 @@ export const parser = { | |||
|     return ' ' | ||||
|   }, | ||||
| 
 | ||||
|   select(arr: string[] = ['*']) { | ||||
|   select(arr = ['*']) { | ||||
|     return `SELECT ${arr.join(',')} ` | ||||
|   }, | ||||
| 
 | ||||
|   // 排序 ----------------------------------
 | ||||
|   sort(obj: { [propName: string]: number } = {}) { | ||||
|   sort(obj = {}) { | ||||
|     let sort = '' | ||||
|     for (let i in obj) { | ||||
|       let c = '' | ||||
|  | @ -167,28 +189,91 @@ export const parser = { | |||
|     } | ||||
|   }, | ||||
| 
 | ||||
|   limit(...args: number[]) { | ||||
|   limit(...args) { | ||||
|     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 { | ||||
|   sql: string | ||||
|   constructor(msg: string = '', sql: string = '') { | ||||
|     super(msg) | ||||
|       // 这3种时间类型,不允许设置默认值为 当前时间戳
 | ||||
|       if (['TIME', 'DATE', 'YEAR'].includes(it.type)) { | ||||
|         if (it.default.toUpperCase() === 'CURRENT_TIMESTAMP') { | ||||
|           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 | ||||
|     hideProperty(this, 'stack', msg) | ||||
|   } | ||||
| 
 | ||||
|   toString() { | ||||
|     return this.message | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export const defer = () => { | ||||
|   let obj: { [prop: string]: any } = {} | ||||
|   obj.promise = new Promise((yes, no) => { | ||||
|     obj.resolve = yes | ||||
|     obj.reject = no | ||||
|   }) | ||||
|   return obj | ||||
| } | ||||
| 
 | ||||
| export { escape } | ||||
| 
 | ||||
| export default escape | ||||
| exports.SqlErr = SqlErr | ||||
| exports.parser = parser | ||||
| exports.escape = escape | ||||
| exports.fixtable = fixtable | ||||
|  | @ -2,9 +2,9 @@ | |||
|   "name": "mysqli", | ||||
|   "version": "3.0.0", | ||||
|   "description": "MySQL tool", | ||||
|   "main": "dist/index.js", | ||||
|   "main": "index.js", | ||||
|   "dependencies": { | ||||
|     "es.shim": "^0.0.3", | ||||
|     "es.shim": "^1.0.1", | ||||
|     "mysql": "^2.13.0" | ||||
|   }, | ||||
|   "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
	
	 宇天
						宇天