var url = require("url"); var querystring = require("querystring"); /** * 数据库基类 * @return {[type]} [description] */ var db = module.exports = Class(function(){ return { comparison: { 'eq': '=', 'neq': '<>', 'gt': '>', 'egt': '>=', 'lt': '<', 'elt': '<=', 'notlike': 'NOT LIKE', 'like': 'LIKE', 'in': 'IN', 'notin': 'NOT IN' }, selectSql: 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%COMMENT%', initAttr: function(){ // 数据库类型 this.dbType = null; // 是否自动释放查询结果 //this.autoFree = false; // 当前操作所属的模型名 this.model = "_think_"; // 是否使用永久连接 this.pconnect = false; // 当前SQL指令 this.queryStr = ""; this.modelSql = []; // 返回或者影响记录数 //this.numRows = 0; // 返回字段数 //this.numCols = 0; // 事务指令数 //this.transTimes = 0; // 错误信息 this.error = ""; // 数据库连接ID 支持多个连接 this.linkID = []; // 当前连接ID this._linkID = null; // 当前查询ID //this.queryId = null; // 是否已经连接数据库 this.connected = false; // 数据库连接参数配置 this.config = ''; // 数据库表达式 }, 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 (is_scalar(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 (is_string(value)) { value = '\'' + this.escapeString(value) + '\''; }else if(is_array(value)){ if ((value[0] + "").toLowerCase() == 'exp') { value = this.escapeString(value[1]); }else{ value = value.map(this.parseValue); } }else if(is_boolean(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 (is_string(fields) && fields.indexOf(',') > -1) { fields = fields.split(","); }; if (is_array(fields)) { var self = this; return fields.map(function(item){ return self.parseKey(item); }).join(","); }else if(is_object(fields)){ var data = []; for(var key in fields){ data.push(this.parseKey(key) + " AS " + this.parseKey(fields[key])); } return data.join(","); }else if(is_string(fields) && fields){ return this.parseKey(fields); } return "*"; }, /** * table别名分析 * @param {[type]} tables [description] * @return {[type]} [description] */ parseTable: function(tables){ if (is_string(tables)) { tables = tables.split(","); }; if (is_array(tables)) { var self = this; return tables.map(function(item){ return self.parseKey(item); }).join(","); }else if (is_object(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; if (is_string(where)) { whereStr = where; }else{ where = where || {}; var operate = (where["_logic"] + "").toUpperCase(); var oList = ["AND", "OR", "XOR"]; if (oList.indexOf(operate) > -1) { // 定义逻辑运算规则 例如 OR XOR AND NOT operate = " " + operate + " "; delete where["_logic"]; }else{ operate = " AND "; } //key值的安全检测正则 var keySafeRegExp = /^[A-Z_\|\&\-.a-z0-9\(\)\,]+$/; for(var key in where){ var val = where[key]; whereStr += "( "; if (key.indexOf("_") === 0) { // 解析特殊条件表达式 whereStr += this.parseThinkWhere(key, val); }else{ if (!keySafeRegExp.test(key.trim())) { throw_error(key + " is not safe"); }; var multi = where && where['_multi']; key = key.trim(); // 支持 name|title|nickname 方式定义查询字段 if (key.indexOf('|') > -1) { var arr = key.split("|"); whereStr += arr.map(function(item, i){ var v = multi ? val[i] : val; return "(" + self.parseWhereItem(self.parseKey(item, v)) + ")"; }).join(" OR "); }else if (key.indexOf("&") > -1) { var arr = key.split("&"); whereStr += arr.map(function(item, i){ var v = multi ? val[i] : val; return "(" + self.parseWhereItem(self.parseKey(item, v)) + ")"; }).join(" AND "); }else{ whereStr += this.parseWhereItem(this.parseKey(key), val); } } whereStr += ")" + operate; } whereStr = whereStr.substr(0, whereStr.length - operate.length); } return whereStr ? (" WHERE " + whereStr) : ""; }, parseWhereItem: function(key, val){ var whereStr = ""; if (is_array(val)) { if (is_string(val[0])) { var reg = /^(EQ|NEQ|GT|EGT|LT|ELT)$/i; var reg1 = /^(NOTLIKE|LIKE)$/i; if (reg.test(val[0])) { // 比较运算 whereStr += key + " " + this.comparison[key.toLowerCase()] + " " + this.parseValue(val[1]); }else if (reg1.test(val[0])) { // 模糊查找 if (is_array(val[1])) { var likeLogic = val[2] ? val[2] : "OR"; var likesLogic = ['AND','OR','XOR']; if (likesLogic.indexOf(likeLogic) > -1) { var likeStr = this.comparison[val[0].toLowerCase()]; var like = val[1].map(function(item){ return key + " " + likeStr + " " + this.parseValue(item); }).join(likeLogic); whereStr += "(" + like + ")"; }; }else{ whereStr += key + " " + this.comparison[val[0].toLowerCase()] + " " + this.parseValue(val[1]); } }else if(val[0].toLowerCase() == 'exp'){ // 使用表达式 whereStr += "(" + key + " " + val[1] + ")"; }else if(/IN/i.test(val[0])){ // IN 运算 if (val[2] == 'exp') { whereStr += key + " " + val[0].toUpperCase() + " " + val[1]; }else{ if (is_string(val[1])) { val[1] = val[1].split(","); }; var zone = this.parseValue(val[1]).join(","); whereStr += key + " " + val[0].toUpperCase() + " (" + zone + ")"; } }else if(/BETWEEN/i.test(val[0])){ // BETWEEN运算 var data = is_string(val[1]) ? val[1].split(",") : val[1]; whereStr += " (" + key + " " + val[0].toUpperCase() + " " + this.parseValue(data[0]) + " AND " + this.parseValue(data[1]) + ")"; }else{ throw_error("_EXPRESS_ERROR_" + key + val); } }else{ var length = val.length; var rule = val[val.length - 1] || ""; var ruleList = ['AND','OR','XOR']; if (rule && ruleList.indexOf(rule) > -1) { length = length - 1; }else{ rule = "AND"; } for(var i=0; i -1) { joinStr += val; }else{ joinStr += " LEFT JOIN " + val; } } }else{ joinStr += " LEFT JOIN " + join; } }; //将__TABLE_NAME__这样的字符串替换成正规的表名,并且带上前缀和后缀 joinStr = joinStr.replace(/__([A-Z_-]+)__/g, function(a, b){ return C('db_prefix') + b.toLowerCase(); }); return joinStr; }, /** * 解析order * @param {[type]} order [description] * @return {[type]} [description] */ parseOrder: function(order){ var orderStr = ""; var self = this; if (is_array(order)) { orderStr = order.map(function(item){ return self.parseKey(item); }).join(","); }else if (is_object(order)) { var arr = []; for(var key in order){ var val = order[key]; val = this.parseKey(key) + " " + val; arr.push(val); } orderStr = arr.join(","); }; return order ? (" ORDER BY " + order) : ""; }, parseGroup: function(group){ return group ? (" GROUP BY " + group) : ""; }, parseHaving: function(having){ return having ? (" HAVING " + having) : ""; }, parseComment: function(comment){ return comment ? (" /* " + comment + "*/") : ""; }, parseDistinct: function(distinct){ return distinct ? (" Distinct " + distinct) : ""; }, 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 + (is_array(val) ? this.buildSelectSql(val) : val); sql.push(sql); } return sql.join(" "); }, parseLock: function(lock){ if (!lock) { return ""; }; return " FOR UPDATE "; }, /** * 拼接select查询语句 * @param {[type]} options [description] * @return {[type]} [description] */ buildSelectSql: function(options){ options = options || {}; if ("page" in options) { // 根据页数计算limit var page = options.page || ""; var listRows = 0; if (page.indexOf(",") > -1) { page = page.split(","); page = page[0]; listRows = page[1]; } page = parseInt(page, 10) || 1; listRows = listRows || options.limit || C('db_nums_per_page'); var offset = listRows * (page - 1); options.limit = offset + "," + listRows; }; // SQL创建缓存 var key = ""; if (C('db_sql_build_cache')) { key = md5(JSON.stringify(options)); //使用默认对象的缓存方式,这样是直接获取 value = S(key, undefined, true); if (value !== false) { return value; }; }; var sql = this.parseSql(this.selectSql, options); sql += this.parseLock(options.lock); if (key) { S(key, sql, { type: true, expire: 0 }); }; 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] || ""); }) }, /** * 插入一条记录 * @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 (is_scalar(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 (is_scalar(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 (is_string(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){ options = options || {}; this.model = options.model; var sql = this.buildSelectSql(options); var cache = options.cache; if (cache) { var key = cache.key || md5(sql); var value = S(key, undefined, cache); if (value !== false) { return get_promise(value); }; }; return this.query(sql).then(function(data){ if (cache && data !== false) { S(key, data, cache); }; return data; }); }, /** * 转义字符 * @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; } }); }, setModel: function(model){ this.model = model; }, getLastSql: function(model){ return model ? this.modelSql[model] : this.queryStr; } } }); /** * 解析dsn * 格式: mysql://username:passwd@localhost:3306/DbName * @param string dsn [description] * @return {[type]} [description] */ db.parseDSN = function(dsn){ 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){ if (config && is_string(config)) { return this.parseDSN(config); }else if(is_object(config)){ return { "dbms": config.db_type, "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){ config = this.parseConfig(config); if (!config.dbms) { return throw_error("no dbms config"); }; //数据库类型 var dbType = config.dbms.toLowerCase(); dbType = dbType.substr(0, 1).toUpperCase() + dbType.substr(1); var instance = think_require(dbType + "Db")(config); instance.dbType = dbType.toUpperCase(); return instance; }