2014-07-03 15:06:57 +08:00

836 lines
25 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';
//用于查询的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
linkId: null,
// 数据库连接参数配置
config: '',
/**
* 初始化
* @return {[type]} [description]
*/
init: function(){
},
/**
* 解析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 (isObject(val)) { // {id: {'<': 10, '>': 1}}
var logic = (val._logic || 'AND').toUpperCase();
delete val._logic;
var result = [];
for(var opr in val){
var nop = opr.toUpperCase();
nop = comparison[nop] || nop;
result.push(key + ' ' + nop + ' ' + this.parseValue(val[opr]));
}
return result.join(' ' + logic + ' ');
}else 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])) { //多个like
var likeLogic = (val[2] || 'OR').toUpperCase();
var likesLogic = ['AND','OR','XOR'];
var self = this;
if (likesLogic.indexOf(likeLogic) > -1) {
var like = val[1].map(function(item){
return key + ' ' + val0 + ' ' + self.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(',');
}
//如果不是数组,自动转为数组
if (!isArray(val[1])) {
val[1] = [val[1]];
}
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 = 'AND';
if (isString(val[length - 1])) {
var last = val[length - 1].toUpperCase();
if (last && ['AND', 'OR', 'XOR'].indexOf(last) > -1) {
rule = last;
length--;
}
}
for(var i = 0; i < length; i++){
var isArr = isArray(val[i]);
data = isArr ? val[i][1] : val[i];
var exp = ((isArr ? val[i][0] : '') + '').toUpperCase();
if (exp === 'EXP') {
whereStr += '(' + key + ' ' + data + ') ' + rule + ' ';
}else{
var op = isArr ? (comparison[val[i][0].toUpperCase()] || val[i][0]) : '=';
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 = isString(val) ? querystring.parse(val) : val;
var op = ' AND ';
if ('_logic' in where) {
op = ' ' + where._logic.toUpperCase() + ' ';
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){
if (!limit) {
return '';
}
limit = (limit + '').split(',');
var data = [];
for(var i = 0; i < Math.min(2, limit.length); i++){
data[i] = limit[i] | 0;
}
return ' LIMIT ' + data.join(',');
},
/**
* 解析join
* @param {[type]} join [description]
* @return {[type]} [description]
*/
parseJoin: function(join, options){
if (!join) {
return '';
}
var joinStr = '';
var defaultJoin = ' LEFT JOIN ';
if (isArray(join)) {
var joins = {
'left': ' LEFT JOIN ',
'right': ' RIGHT JOIN ',
'inner': ' INNER JOIN '
};
join.forEach(function(val){
if (isString(val)) {//字符串,直接拼接
var hasJoin = val.toLowerCase().indexOf(' join ') > -1;
joinStr += (hasJoin ? ' ' : defaultJoin) + val;
}else if (isObject(val)) {
var ret = [];
if (!('on' in val)) {
for(var key in val){
var v = val[key];
v.table = key;
ret.push(v);
}
}else{
ret.push(val);
}
ret.forEach(function(item){
var joinType = joins[item.join] || item.join || defaultJoin;
var table = options.tablePrefix + item.table;
joinStr += joinType + '`' + table + '`';
if (item.as) {
joinStr += ' AS ' + item.as;
}
//ON条件
if (item.on) {
var mTable = options.alias || options.table;
var jTable = item.as || table;
//多个=条件
if (isObject(item.on)) {
var where = [];
for(var key in item.on){
where.push(mTable + '.`' + key + '`' + '=' + jTable + '.`' + item.on[key] + '`');
}
joinStr += ' ON (' + where.join(' AND ') + ')';
}else{
if (isString(item.on)) {
item.on = item.on.split(/\s*,\s*/);
}
joinStr += ' ON ' + mTable + '.`' + item.on[0] + '`';
joinStr += '=' + jTable + '.`' + item.on[1] + '`';
}
}
})
}
});
}else{
joinStr += defaultJoin + 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 '';
}
if (isArray(union)) {
var self = this;
var sql = '';
union.forEach(function(item){
sql += item.all ? 'UNION ALL ' : 'UNION ';
sql += '(' + (isObject(item.union) ? self.buildSelectSql(item.union).trim() : item.union) + ') ';
})
return sql;
}else{
return 'UNION (' + (isObject(union) ? this.buildSelectSql(union).trim() : union) + ') ';
}
},
/**
* 解析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] | 0;
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] || '', options);
}).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;
};