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 { /** * 初始化一些属性,不能直接放在原型上 * @return {[type]} [description] */ initAttr: function(){ // 数据库类型 this.dbType = null; // 当前操作所属的模型名 this.model = "_think_"; // 当前SQL指令 this.queryStr = ""; // 操作的sql列表 this.modelSql = []; // 数据库连接ID 支持多个连接 this.linkIds = []; // 当前连接ID this.linkId = null; // 是否已经连接数据库 this.connected = false; // 数据库连接参数配置 this.config = ''; }, /** * 初始化,类似于constrcut,类实例化时自动调用 * @return {[type]} [description] */ init: function(){ this.initAttr(); }, /** * 连接数据库 * @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 fn1 = function(item, i){ var v = multi ? val[i] : val; return "(" + self.parseWhereItem(self.parseKey(item), v) + ")"; }; var fn2 = 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(fn1).join(" OR "); }else if (key.indexOf("&") > -1) { arr = key.split("&"); whereStr += arr.map(fn2).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(","); } whereStr += key + " " + val0 + " (" + this.parseValue(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]) + " 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){ var whereStr = ""; 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); } whereStr = arr.join(op); return whereStr; 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; };