2014-04-01 19:12:34 +08:00

805 lines
28 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

var url = require("url");
var querystring = require("querystring");
/**
* 数据库基类
* @return {[type]} [description]
*/
var Db = module.exports = Class(function(){
"use strict";
return {
/**
* where条件里的表达式
* @type {Object}
*/
comparison: {
'eq': '=',
'neq': '<>',
'gt': '>',
'egt': '>=',
'lt': '<',
'elt': '<=',
'notlike': 'NOT LIKE',
'like': 'LIKE',
'in': 'IN',
'notin': 'NOT IN'
},
/**
* 用于查询的sql语句所有select语句根据该语句解析
* @type {String}
*/
selectSql: 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%COMMENT%',
/**
* 初始化一些属性,不能直接放在原型上
* @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){
var whereStr = "";
var data;
if (isArray(val)) {
if (isString(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[val[0].toLowerCase()] + " " + this.parseValue(val[1]);
}else if (reg1.test(val[0])) { // 模糊查找
if (isArray(val[1])) {
var likeLogic = (val[2] ? val[2] : "OR").toUpperCase();
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 (isString(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运算
data = isString(val[1]) ? val[1].split(",") : val[1];
if (!isArray(data)) {
data = [val[1], val[2]];
}
whereStr += " (" + key + " " + val[0].toUpperCase() + " " + 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] || "";
var ruleList = ['AND','OR','XOR'];
if (rule && ruleList.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]) ? this.comparison[val[i][0].toLowerCase()] : "=";
whereStr += "(" + key + " " + op + " " + this.parseValue(data) + ") " + rule + " ";
}
}
whereStr = whereStr.substr(0, whereStr.length - 4);
}
}else{
//对字符串类型字段采用模糊匹配
if (C('db_like_fields') && (new RegExp(C('db_like_fields'), "i")).test(key)) {
whereStr += key + " LIKE " + this.parseValue("%" + val + "%");
}else{
whereStr = key + " = " + this.parseValue(val);
}
}
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 orderStr = "";
var self = this;
if (isArray(order)) {
orderStr = 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);
}
orderStr = 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 " + 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(this.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;
//获取数据
function queryData(){
return self.query(sql).then(function(data){
if (cache) {
S(key, data, cache);
}
return data;
});
}
if (!isEmpty(cache) && C('db_cache_on')) {
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,
"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;
};