thinkjs/lib/Lib/Core/Db.class.js
2013-11-19 06:56:03 +08:00

628 lines
23 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(){
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%',
initProp: function(){
// 数据库类型
this.dbType = null;
// 是否自动释放查询结果
this.autoFree = false;
// 当前操作所属的模型名
this.model = "_think_";
// 是否使用永久连接
this.pconnect = false;
// 当前SQL指令
this.queryStr = "";
this.modelSql = [];
// 最后插入ID
this.lastInsID = null;
// 返回或者影响记录数
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.initProp();
},
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(",");
},
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分析
* @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(",");
}
},
/**
* 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<length; i++){
var data = is_array(val[i]) ? val[i][1] : val[i];
var exp = ((is_array(val[i]) && val[i][0]) + "").toLowerCase();
if (exp == 'exp') {
whereStr += "(" + key + " " + data + ") " + rule + " ";
}else{
var op = is_array(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;
},
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){
var val = where[name];
val = this.parseKey(name) + " = " + this.parseValue(val);
arr.push(val);
}
whereStr = arr.join(op);
return whereStr;
default:
return "";
}
return "";
},
parseLimit: function(limit){
return limit ? (" LIMIT " + limit) : "";
},
parseJoin: function(join){
var joinStr = "";
if (join) {
if (is_object(join)) {
for(var key in join){
var val = join[key];
if (val.toLowerCase().indexOf("join") > -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;
},
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 ";
},
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 || 20;
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;
},
parseSql: function(sql, options){
options = options || {};
var self = this;
return sql.replace(/\%([A-Z]+)\%/g, function(a, type){
return self[ucfirst(type)](options[type.toLowerCase()] || "");
})
},
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);
},
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);
},
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 value;
};
};
var result = this.query(sql);
if (cache && result !== false) {
S(key, result, cache);
};
return result;
},
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;
},
getLastInsID: function(){
return this.lastInsID;
},
getError: function(){
return this.error;
},
query: function(){
},
execute: function(){
},
close: function(){
}
}
});
/**
* 解析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;
}