238 lines
5.7 KiB
JavaScript
238 lines
5.7 KiB
JavaScript
|
var SqlString = exports;
|
||
|
|
||
|
var ID_GLOBAL_REGEXP = /`/g;
|
||
|
var QUAL_GLOBAL_REGEXP = /\./g;
|
||
|
var CHARS_GLOBAL_REGEXP = /[\0\b\t\n\r\x1a\"\'\\]/g; // eslint-disable-line no-control-regex
|
||
|
var CHARS_ESCAPE_MAP = {
|
||
|
'\0' : '\\0',
|
||
|
'\b' : '\\b',
|
||
|
'\t' : '\\t',
|
||
|
'\n' : '\\n',
|
||
|
'\r' : '\\r',
|
||
|
'\x1a' : '\\Z',
|
||
|
'"' : '\\"',
|
||
|
'\'' : '\\\'',
|
||
|
'\\' : '\\\\'
|
||
|
};
|
||
|
|
||
|
SqlString.escapeId = function escapeId(val, forbidQualified) {
|
||
|
if (Array.isArray(val)) {
|
||
|
var sql = '';
|
||
|
|
||
|
for (var i = 0; i < val.length; i++) {
|
||
|
sql += (i === 0 ? '' : ', ') + SqlString.escapeId(val[i], forbidQualified);
|
||
|
}
|
||
|
|
||
|
return sql;
|
||
|
} else if (forbidQualified) {
|
||
|
return '`' + String(val).replace(ID_GLOBAL_REGEXP, '``') + '`';
|
||
|
} else {
|
||
|
return '`' + String(val).replace(ID_GLOBAL_REGEXP, '``').replace(QUAL_GLOBAL_REGEXP, '`.`') + '`';
|
||
|
}
|
||
|
};
|
||
|
|
||
|
SqlString.escape = function escape(val, stringifyObjects, timeZone) {
|
||
|
if (val === undefined || val === null) {
|
||
|
return 'NULL';
|
||
|
}
|
||
|
|
||
|
switch (typeof val) {
|
||
|
case 'boolean': return (val) ? 'true' : 'false';
|
||
|
case 'number': return val + '';
|
||
|
case 'object':
|
||
|
if (Object.prototype.toString.call(val) === '[object Date]') {
|
||
|
return SqlString.dateToString(val, timeZone || 'local');
|
||
|
} else if (Array.isArray(val)) {
|
||
|
return SqlString.arrayToList(val, timeZone);
|
||
|
} else if (Buffer.isBuffer(val)) {
|
||
|
return SqlString.bufferToString(val);
|
||
|
} else if (typeof val.toSqlString === 'function') {
|
||
|
return String(val.toSqlString());
|
||
|
} else if (stringifyObjects) {
|
||
|
return escapeString(val.toString());
|
||
|
} else {
|
||
|
return SqlString.objectToValues(val, timeZone);
|
||
|
}
|
||
|
default: return escapeString(val);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
SqlString.arrayToList = function arrayToList(array, timeZone) {
|
||
|
var sql = '';
|
||
|
|
||
|
for (var i = 0; i < array.length; i++) {
|
||
|
var val = array[i];
|
||
|
|
||
|
if (Array.isArray(val)) {
|
||
|
sql += (i === 0 ? '' : ', ') + '(' + SqlString.arrayToList(val, timeZone) + ')';
|
||
|
} else {
|
||
|
sql += (i === 0 ? '' : ', ') + SqlString.escape(val, true, timeZone);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return sql;
|
||
|
};
|
||
|
|
||
|
SqlString.format = function format(sql, values, stringifyObjects, timeZone) {
|
||
|
if (values == null) {
|
||
|
return sql;
|
||
|
}
|
||
|
|
||
|
if (!Array.isArray(values)) {
|
||
|
values = [values];
|
||
|
}
|
||
|
|
||
|
var chunkIndex = 0;
|
||
|
var placeholdersRegex = /\?+/g;
|
||
|
var result = '';
|
||
|
var valuesIndex = 0;
|
||
|
var match;
|
||
|
|
||
|
while (valuesIndex < values.length && (match = placeholdersRegex.exec(sql))) {
|
||
|
var len = match[0].length;
|
||
|
|
||
|
if (len > 2) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
var value = len === 2
|
||
|
? SqlString.escapeId(values[valuesIndex])
|
||
|
: SqlString.escape(values[valuesIndex], stringifyObjects, timeZone);
|
||
|
|
||
|
result += sql.slice(chunkIndex, match.index) + value;
|
||
|
chunkIndex = placeholdersRegex.lastIndex;
|
||
|
valuesIndex++;
|
||
|
}
|
||
|
|
||
|
if (chunkIndex === 0) {
|
||
|
// Nothing was replaced
|
||
|
return sql;
|
||
|
}
|
||
|
|
||
|
if (chunkIndex < sql.length) {
|
||
|
return result + sql.slice(chunkIndex);
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
};
|
||
|
|
||
|
SqlString.dateToString = function dateToString(date, timeZone) {
|
||
|
var dt = new Date(date);
|
||
|
|
||
|
if (isNaN(dt.getTime())) {
|
||
|
return 'NULL';
|
||
|
}
|
||
|
|
||
|
var year;
|
||
|
var month;
|
||
|
var day;
|
||
|
var hour;
|
||
|
var minute;
|
||
|
var second;
|
||
|
var millisecond;
|
||
|
|
||
|
if (timeZone === 'local') {
|
||
|
year = dt.getFullYear();
|
||
|
month = dt.getMonth() + 1;
|
||
|
day = dt.getDate();
|
||
|
hour = dt.getHours();
|
||
|
minute = dt.getMinutes();
|
||
|
second = dt.getSeconds();
|
||
|
millisecond = dt.getMilliseconds();
|
||
|
} else {
|
||
|
var tz = convertTimezone(timeZone);
|
||
|
|
||
|
if (tz !== false && tz !== 0) {
|
||
|
dt.setTime(dt.getTime() + (tz * 60000));
|
||
|
}
|
||
|
|
||
|
year = dt.getUTCFullYear();
|
||
|
month = dt.getUTCMonth() + 1;
|
||
|
day = dt.getUTCDate();
|
||
|
hour = dt.getUTCHours();
|
||
|
minute = dt.getUTCMinutes();
|
||
|
second = dt.getUTCSeconds();
|
||
|
millisecond = dt.getUTCMilliseconds();
|
||
|
}
|
||
|
|
||
|
// YYYY-MM-DD HH:mm:ss.mmm
|
||
|
var str = zeroPad(year, 4) + '-' + zeroPad(month, 2) + '-' + zeroPad(day, 2) + ' ' +
|
||
|
zeroPad(hour, 2) + ':' + zeroPad(minute, 2) + ':' + zeroPad(second, 2) + '.' +
|
||
|
zeroPad(millisecond, 3);
|
||
|
|
||
|
return escapeString(str);
|
||
|
};
|
||
|
|
||
|
SqlString.bufferToString = function bufferToString(buffer) {
|
||
|
return 'X' + escapeString(buffer.toString('hex'));
|
||
|
};
|
||
|
|
||
|
SqlString.objectToValues = function objectToValues(object, timeZone) {
|
||
|
var sql = '';
|
||
|
|
||
|
for (var key in object) {
|
||
|
var val = object[key];
|
||
|
|
||
|
if (typeof val === 'function') {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
sql += (sql.length === 0 ? '' : ', ') + SqlString.escapeId(key) + ' = ' + SqlString.escape(val, true, timeZone);
|
||
|
}
|
||
|
|
||
|
return sql;
|
||
|
};
|
||
|
|
||
|
SqlString.raw = function raw(sql) {
|
||
|
if (typeof sql !== 'string') {
|
||
|
throw new TypeError('argument sql must be a string');
|
||
|
}
|
||
|
|
||
|
return {
|
||
|
toSqlString: function toSqlString() { return sql; }
|
||
|
};
|
||
|
};
|
||
|
|
||
|
function escapeString(val) {
|
||
|
var chunkIndex = CHARS_GLOBAL_REGEXP.lastIndex = 0;
|
||
|
var escapedVal = '';
|
||
|
var match;
|
||
|
|
||
|
while ((match = CHARS_GLOBAL_REGEXP.exec(val))) {
|
||
|
escapedVal += val.slice(chunkIndex, match.index) + CHARS_ESCAPE_MAP[match[0]];
|
||
|
chunkIndex = CHARS_GLOBAL_REGEXP.lastIndex;
|
||
|
}
|
||
|
|
||
|
if (chunkIndex === 0) {
|
||
|
// Nothing was escaped
|
||
|
return "'" + val + "'";
|
||
|
}
|
||
|
|
||
|
if (chunkIndex < val.length) {
|
||
|
return "'" + escapedVal + val.slice(chunkIndex) + "'";
|
||
|
}
|
||
|
|
||
|
return "'" + escapedVal + "'";
|
||
|
}
|
||
|
|
||
|
function zeroPad(number, length) {
|
||
|
number = number.toString();
|
||
|
while (number.length < length) {
|
||
|
number = '0' + number;
|
||
|
}
|
||
|
|
||
|
return number;
|
||
|
}
|
||
|
|
||
|
function convertTimezone(tz) {
|
||
|
if (tz === 'Z') {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
var m = tz.match(/([\+\-\s])(\d\d):?(\d\d)?/);
|
||
|
if (m) {
|
||
|
return (m[1] === '-' ? -1 : 1) * (parseInt(m[2], 10) + ((m[3] ? parseInt(m[3], 10) : 0) / 60)) * 60;
|
||
|
}
|
||
|
return false;
|
||
|
}
|