var url = require('url'); var querystring = require('querystring'); /** * 数据库基类 * @return {[type]} [description] */ var Db = module.exports = Class(function(){ 'use strict'; //用于查询的sql语句,所有select语句根据该语句解析 var selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%COMMENT%'; //where条件里的表达式 var comparison = { 'EQ': '=', 'NEQ': '!=', '<>': '!=', 'GT': '>', 'EGT': '>=', 'LT': '<', 'ELT': '<=', 'NOTLIKE': 'NOT LIKE', 'LIKE': 'LIKE', 'IN': 'IN', 'NOTIN': 'NOT IN' }; return { // 数据库类型 dbType: null, // 当前操作所属的模型名 model: '_think_', // 当前SQL指令 queryStr: '', // 操作的sql列表 modelSql: [], // 数据库连接ID 支持多个连接 linkIds: [], // 当前连接ID linkId: null, // 是否已经连接数据库 connected: false, // 数据库连接参数配置 config: '', /** * 初始化,类似于constrcut,类实例化时自动调用 * @return {[type]} [description] */ init: function(){ }, /** * 连接数据库 * @return {[type]} [description] */ initConnect: function(){ if (!this.connected) { this.linkId = this.connect(); } }, /** * 解析set集合 * @param {[type]} data [description] * @return {[type]} [description] */ parseSet: function(data){ data = data || {}; var set = []; for(var key in data){ var value = this.parseValue(data[key]); if (isScalar(value)) { set.push(this.parseKey(key) + '=' + value); } } return ' SET ' + set.join(','); }, /** * 解析字段名,具体的数据库里实现 * @param {[type]} key [description] * @return {[type]} [description] */ parseKey: function(key){ return key; }, /** * value分析 * @param {[type]} value [description] * @return {[type]} [description] */ parseValue: function(value){ if (isString(value)) { value = '\'' + this.escapeString(value) + '\''; }else if(isArray(value)){ if ((value[0] + '').toLowerCase() === 'exp') { value = this.escapeString(value[1]); }else{ var self = this; value = value.map(function(item){ return self.parseValue(item); }); } }else if(isBoolean(value)){ value = value ? '1' : '0'; }else if (value === null) { value = 'null'; } return value; }, /** * field分析 * parseField('name'); * parseField('name,email'); * parseField({ * xx_name: 'name', * xx_email: 'email' * }) * @return {[type]} [description] */ parseField: function(fields){ if (isString(fields) && fields.indexOf(',') > -1) { fields = fields.split(','); } if (isArray(fields)) { var self = this; return fields.map(function(item){ return self.parseKey(item); }).join(','); }else if(isObject(fields)){ var data = []; for(var key in fields){ data.push(this.parseKey(key) + ' AS ' + this.parseKey(fields[key])); } return data.join(','); }else if(isString(fields) && fields){ return this.parseKey(fields); } return '*'; }, /** * table别名分析 * @param {[type]} tables [description] * @return {[type]} [description] */ parseTable: function(tables){ if (isString(tables)) { tables = tables.split(','); } if (isArray(tables)) { var self = this; return tables.map(function(item){ return self.parseKey(item); }).join(','); }else if (isObject(tables)) { var data = []; for(var key in tables){ data.push(this.parseKey(key) + ' AS ' + this.parseKey(tables[key])); } return data.join(','); } return ''; }, /** * where条件分析 * @param {[type]} where [description] * @return {[type]} [description] */ parseWhere: function(where){ var whereStr = ''; var self = this; where = where || {}; if (isString(where)) { whereStr = where; }else{ // 定义逻辑运算规则 例如 OR XOR AND NOT var oList = ['AND', 'OR', 'XOR']; var operate = (where._logic + '').toUpperCase(); delete where._logic; operate = oList.indexOf(operate) > -1 ? ' ' + operate + ' ' : ' AND '; //key值的安全检测正则 var keySafeRegExp = /^[\w\|\&\-\.\(\)\,]+$/; var multi = where._multi; delete where._multi; var val; var fn = function(item, i){ var v = multi ? val[i] : val; return '(' + self.parseWhereItem(self.parseKey(item), v) + ')'; }; for(var key in where){ key = key.trim(); val = where[key]; whereStr += '( '; if (key.indexOf('_') === 0) { // 解析特殊条件表达式 whereStr += this.parseThinkWhere(key, val); }else{ if (!keySafeRegExp.test(key)) { console.log(key + ' is not safe'); continue; } var arr; // 支持 name|title|nickname 方式定义查询字段 if (key.indexOf('|') > -1) { arr = key.split('|'); whereStr += arr.map(fn).join(' OR '); }else if (key.indexOf('&') > -1) { arr = key.split('&'); whereStr += arr.map(fn).join(' AND '); }else{ whereStr += this.parseWhereItem(this.parseKey(key), val); } } whereStr += ' )' + operate; } whereStr = whereStr.substr(0, whereStr.length - operate.length); } return whereStr ? (' WHERE ' + whereStr) : ''; }, /** * 解析单个查询条件 * @param {[type]} key [description] * @param {[type]} val [description] * @return {[type]} [description] */ parseWhereItem: function(key, val){ if (!isArray(val)) { //对字符串类型字段采用模糊匹配 if (C('db_like_fields').indexOf(key) > -1) { return key + ' LIKE ' + this.parseValue('%' + val + '%'); }else{ return key + ' = ' + this.parseValue(val); } } var whereStr = ''; var data; if (isString(val[0])) { var val0 = val[0].toUpperCase(); val0 = comparison[val0] || val0; if (/^(=|!=|>|>=|<|<=)$/.test(val0)) { // 比较运算 whereStr += key + ' ' + val0 + ' ' + this.parseValue(val[1]); }else if (/^(NOT\s+LIKE|LIKE)$/.test(val0)) { // 模糊查找 if (isArray(val[1])) { var likeLogic = (val[2] ? val[2] : 'OR').toUpperCase(); var likesLogic = ['AND','OR','XOR']; if (likesLogic.indexOf(likeLogic) > -1) { var like = val[1].map(function(item){ return key + ' ' + val0 + ' ' + this.parseValue(item); }).join(likeLogic); whereStr += '(' + like + ')'; } }else{ whereStr += key + ' ' + val0 + ' ' + this.parseValue(val[1]); } }else if(val0 === 'EXP'){ // 使用表达式 whereStr += '(' + key + ' ' + val[1] + ')'; }else if(val0 === 'IN' || val0 === 'NOT IN'){ // IN 运算 if (val[2] === 'exp') { whereStr += key + ' ' + val0 + ' ' + val[1]; }else{ if (isString(val[1])) { val[1] = val[1].split(','); } val[1] = this.parseValue(val[1]); //如果只有一个值,那么变成=或者!= if (val[1].length === 1) { whereStr += key + (val0 === 'IN' ? ' = ' : ' != ') + val[1]; }else{ whereStr += key + ' ' + val0 + ' (' + val[1].join(',') + ')'; } } }else if(val0 === 'BETWEEN'){ // BETWEEN运算 data = isString(val[1]) ? val[1].split(',') : val[1]; if (!isArray(data)) { data = [val[1], val[2]]; } whereStr += ' (' + key + ' ' + val0 + ' ' + this.parseValue(data[0]); whereStr += ' AND ' + this.parseValue(data[1]) + ')'; }else{ console.log('_EXPRESS_ERROR_', key, val); return ''; } }else{ var length = val.length; var rule = val[val.length - 1] || ''; if (rule && ['AND','OR','XOR'].indexOf(rule) > -1) { length = length - 1; }else{ rule = 'AND'; } for(var i = 0; i < length; i++){ data = isArray(val[i]) ? val[i][1] : val[i]; var exp = ((isArray(val[i]) && val[i][0]) + '').toLowerCase(); if (exp === 'exp') { whereStr += '(' + key + ' ' + data + ') ' + rule + ' '; }else{ var op = isArray(val[i]) ? comparison[val[i][0].toUpperCase()] : '='; whereStr += '(' + key + ' ' + op + ' ' + this.parseValue(data) + ') ' + rule + ' '; } } whereStr = whereStr.substr(0, whereStr.length - 4); } return whereStr; }, /** * 解析一些特殊的where条件 * @param {[type]} key [description] * @param {[type]} val [description] * @return {[type]} [description] */ parseThinkWhere: function(key, val){ switch(key){ // 字符串模式查询条件 case '_string': return val; // 复合查询条件 case '_complex': return this.parseWhere(val).substr(6); // 字符串模式查询条件 case '_query': var where = querystring.parse(val); var op = ' AND '; if ('_logic' in where) { op = ' ' + where._logic.toLowerCase() + ' '; delete where._logic; } var arr = []; for(var name in where){ val = where[name]; val = this.parseKey(name) + ' = ' + this.parseValue(val); arr.push(val); } return arr.join(op); default: return ''; } return ''; }, /** * 解析limit,对非法的limit进行过滤 * @param {[type]} limit [description] * @return {[type]} [description] */ parseLimit: function(limit){ limit = (limit + '').split(',').slice(0, 2); var flag = limit.every(function(item){ return isNumberString(item); }); if (!flag) { return ''; } limit = limit.join(','); return limit ? (' LIMIT ' + limit) : ''; }, /** * 解析join * @param {[type]} join [description] * @return {[type]} [description] */ parseJoin: function(join){ var joinStr = ''; if (!join) { return ''; } if (isArray(join)) { join.forEach(function(val){ if (val.toLowerCase().indexOf('join') > -1) { joinStr += val; }else{ joinStr += ' LEFT JOIN ' + val; } }); }else{ joinStr += ' LEFT JOIN ' + join; } return joinStr; }, /** * 解析order * @param {[type]} order [description] * @return {[type]} [description] */ parseOrder: function(order){ var self = this; if (isArray(order)) { order = order.map(function(item){ return self.parseKey(item); }).join(','); }else if (isObject(order)) { var arr = []; for(var key in order){ var val = order[key]; val = this.parseKey(key) + ' ' + val; arr.push(val); } order = arr.join(','); } return order ? (' ORDER BY ' + order) : ''; }, /** * 解析group * @param {[type]} group [description] * @return {[type]} [description] */ parseGroup: function(group){ return group ? (' GROUP BY ' + group) : ''; }, /** * 解析having * @param {[type]} having [description] * @return {[type]} [description] */ parseHaving: function(having){ return having ? (' HAVING ' + having) : ''; }, /** * 解析注释,一般情况下用不到 * @param {[type]} comment [description] * @return {[type]} [description] */ parseComment: function(comment){ return comment ? (' /* ' + comment + '*/') : ''; }, /** * 解析Distinct * @param {[type]} distinct [description] * @return {[type]} [description] */ parseDistinct: function(distinct){ return distinct ? ' Distinct ' : ''; }, /** * 解析Union * @param {[type]} union [description] * @return {[type]} [description] */ parseUnion: function(union){ if (!union) { return ''; } var str = ''; if ('_all' in union) { str = 'UNION ALL '; delete union._all; }else{ str = 'UNION '; } var sql = []; for(var key in union){ var val = union[key]; val = str + (isArray(val) ? this.buildSelectSql(val) : val); sql.push(sql); } return sql.join(' '); }, /** * 解析Lock * @param {[type]} lock [description] * @return {[type]} [description] */ parseLock: function(lock){ if (!lock) { return ''; } return ' FOR UPDATE '; }, /** * 将page转化为sql里的limit * @return {[type]} [description] */ pageToLimit: function(options){ options = options || {}; //根据page生成limit if ('page' in options) { var page = options.page + ''; var listRows = 0; if (page.indexOf(',') > -1) { page = page.split(','); listRows = page[1]; page = page[0]; } page = parseInt(page, 10) || 1; if (!listRows) { listRows = isNumberString(options.limit) ? options.limit : C('db_nums_per_page'); } var offset = listRows * (page - 1); options.limit = offset + ',' + listRows; } return options; }, /** * 拼接select查询语句 * @param {[type]} options [description] * @return {[type]} [description] */ buildSelectSql: function(options){ options = this.pageToLimit(options); var sql = this.parseSql(selectSql, options); sql += this.parseLock(options.lock); return sql; }, /** * 解析sql语句 * @param {[type]} sql [description] * @param {[type]} options [description] * @return {[type]} [description] */ parseSql: function(sql, options){ options = options || {}; var self = this; return sql.replace(/\%([A-Z]+)\%/g, function(a, type){ type = type.toLowerCase(); return self['parse' + ucfirst(type)](options[type] || ''); }).replace(/__([A-Z_-]+)__/g, function(a, b){ return '`' + C('db_prefix') + b.toLowerCase() + '`'; }); }, /** * 插入一条记录 * @param {[type]} data [description] * @param {[type]} options [description] * @param {[type]} replace [description] * @return {[type]} [description] */ insert: function(data, options, replace){ data = data || {}; options = options || {}; var values = []; var fields = []; this.model = options.model; for(var key in data){ var val = data[key]; val = this.parseValue(val); if (isScalar(val)) { values.push(val); fields.push(this.parseKey(key)); } } var sql = (replace ? 'REPLACE' : 'INSERT') + ' INTO '; sql += this.parseTable(options.table) + ' (' + fields.join(',') + ') '; sql += 'VALUES(' + values.join(',') + ')'; sql += this.parseLock(options.lock) + this.parseComment(options.comment); return this.execute(sql); }, /** * 插入多条记录 * @param {[type]} data [description] * @param {[type]} options [description] * @param {[type]} replace [description] * @return {[type]} [description] */ insertAll: function(data, options, replace){ var fields = Object.keys(data[0]); var self = this; fields = fields.map(function(item){ return self.parseKey(item); }).join(','); var values = data.map(function(item){ var value = []; for(var key in item){ var val = item[key]; val = self.parseValue(val); if (isScalar(val)) { value.push(val); } } return '(' + value.join(',') + ')'; }).join(','); var sql = replace ? 'REPLACE' : 'INSERT'; sql += ' INTO ' + this.parseTable(options.table) + '(' + fields + ') VALUES ' + values; return this.execute(sql); }, /** * 从一个选择条件的结果插入记录 * @param {[type]} fields [description] * @param {[type]} table [description] * @param {[type]} options [description] * @return {[type]} [description] */ selectInsert: function(fields, table, options){ options = options || {}; this.model = options.model; if (isString(fields)) { fields = fields.split(','); } var self = this; fields = fields.map(function(item){ return self.parseKey(item); }); var sql = 'INSERT INTO ' + this.parseTable(options.table) + ' (' + fields.join(',') + ')'; sql += this.buildSelectSql(options); return this.execute(sql); }, /** * 删除记录 * @param {[type]} options [description] * @return {[type]} [description] */ delete: function(options){ options = options || {}; this.model = options.model; var sql = [ 'DELETE FROM ', this.parseTable(options.table), this.parseWhere(options.where), this.parseOrder(options.order), this.parseLimit(options.limit), this.parseLock(options.lock), this.parseComment(options.comment) ].join(''); return this.execute(sql); }, /** * 更新数据 * @param {[type]} data [description] * @param {[type]} options [description] * @return {[type]} [description] */ update: function(data, options){ options = options || {}; this.model = options.model; var sql = [ 'UPDATE ', this.parseTable(options.table), this.parseSet(data), this.parseWhere(options.where), this.parseOrder(options.order), this.parseLimit(options.limit), this.parseLock(options.lock), this.parseComment(options.comment) ].join(''); return this.execute(sql); }, /** * 数据查询 * @todo 返回是个promise,缓存调用需要修改 * @param {[type]} options [description] * @return {[type]} [description] */ select: function(options){ var sql, cache; if (isString(options) && options.indexOf('SELECT') > -1) { sql = options; cache = arguments[1]; }else{ options = options || {}; this.model = options.model; sql = this.buildSelectSql(options); cache = options.cache; } var self = this; var cacheOn = !isEmpty(cache) && C('db_cache_on'); //获取数据 function queryData(){ return self.query(sql).then(function(data){ if (cacheOn) { S(key, data, cache); } return data; }); } if (cacheOn) { var key = isString(cache.key) && cache.key ? cache.key : md5(sql); return S(key, undefined, cache).then(function(value){ return value || queryData(); }); } return queryData(); }, /** * 转义字符 * @param {[type]} str [description] * @return {[type]} [description] */ escapeString: function(str){ if (!str) { return ''; } return str.replace(/[\0\n\r\b\t\\\'\"\x1a]/g, function(s) { switch(s) { case '\0': return '\\0'; case '\n': return '\\n'; case '\r': return '\\r'; case '\b': return '\\b'; case '\t': return '\\t'; case '\x1a': return '\\Z'; default: return '\\'+s; } }); }, /** * 获取上次的sql语句 * @param {[type]} model [description] * @return {[type]} [description] */ getLastSql: function(model){ return model ? this.modelSql[model] : this.queryStr; }, /** * 设置模型 * @param {[type]} model [description] */ setModel: function(model){ this.model = model; return this; } }; }); /** * 解析dsn * 格式: mysql://username:passwd@localhost:3306/DbName * @param string dsn [description] * @return {[type]} [description] */ Db.parseDSN = function(dsn){ 'use strict'; if (!dsn) { return false; } var info = url.parse(dsn); var auth = (info.auth || '').split(':'); return { 'dbms': info.protocol, 'username': auth[0] || '', 'password': auth[1] || '', 'hostname': info.hostname || '', 'hostport': info.port || '', 'database': (info.pathname || '').substr(1), 'dsn': '' }; }; /** * 解析配置 * @param {[type]} config [description] * @return {[type]} [description] */ Db.parseConfig = function(config){ 'use strict'; if (config && isString(config)) { return this.parseDSN(config); }else if(isObject(config)){ return { 'dbms': config.db_type || 'mysql', 'username': config.db_user, 'password': config.db_pwd, 'hostname': config.db_host, 'hostport': config.db_port, 'database': config.db_name, 'dsn': config.db_dsn, 'params': config.db_params }; }else if(!config){ if (C('db_dsn')) { return this.parseDSN(C('db_dsn')); } return { 'dbms' : C('db_type'), 'username' : C('db_user'), 'password' : C('db_pwd'), 'hostname' : C('db_host'), 'hostport' : C('db_port'), 'database' : C('db_name'), 'dsn' : C('db_dsn'), 'params' : C('db_params'), }; } return config; }; /** * 根据配置获取对应的数据库实例 * @param {[type]} config [description] * @return {[type]} [description] */ Db.getInstance = function(config){ 'use strict'; config = this.parseConfig(config); if (!config.dbms) { console.log('no dbms config'); return false; } //数据库类型 var dbType = config.dbms.toLowerCase(); dbType = dbType.substr(0, 1).toUpperCase() + dbType.substr(1); var instance = thinkRequire(dbType + 'Db')(config); instance.dbType = dbType.toUpperCase(); return instance; };