SqlString.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. var SqlString = exports;
  2. var ID_GLOBAL_REGEXP = /`/g;
  3. var QUAL_GLOBAL_REGEXP = /\./g;
  4. var CHARS_GLOBAL_REGEXP = /[\0\b\t\n\r\x1a\"\'\\]/g; // eslint-disable-line no-control-regex
  5. var CHARS_ESCAPE_MAP = {
  6. '\0' : '\\0',
  7. '\b' : '\\b',
  8. '\t' : '\\t',
  9. '\n' : '\\n',
  10. '\r' : '\\r',
  11. '\x1a' : '\\Z',
  12. '"' : '\\"',
  13. '\'' : '\\\'',
  14. '\\' : '\\\\'
  15. };
  16. SqlString.escapeId = function escapeId(val, forbidQualified) {
  17. if (Array.isArray(val)) {
  18. var sql = '';
  19. for (var i = 0; i < val.length; i++) {
  20. sql += (i === 0 ? '' : ', ') + SqlString.escapeId(val[i], forbidQualified);
  21. }
  22. return sql;
  23. } else if (forbidQualified) {
  24. return '`' + String(val).replace(ID_GLOBAL_REGEXP, '``') + '`';
  25. } else {
  26. return '`' + String(val).replace(ID_GLOBAL_REGEXP, '``').replace(QUAL_GLOBAL_REGEXP, '`.`') + '`';
  27. }
  28. };
  29. SqlString.escape = function escape(val, stringifyObjects, timeZone) {
  30. if (val === undefined || val === null) {
  31. return 'NULL';
  32. }
  33. switch (typeof val) {
  34. case 'boolean': return (val) ? 'true' : 'false';
  35. case 'number': return val + '';
  36. case 'object':
  37. if (val instanceof Date) {
  38. return SqlString.dateToString(val, timeZone || 'local');
  39. } else if (Array.isArray(val)) {
  40. return SqlString.arrayToList(val, timeZone);
  41. } else if (Buffer.isBuffer(val)) {
  42. return SqlString.bufferToString(val);
  43. } else if (typeof val.toSqlString === 'function') {
  44. return String(val.toSqlString());
  45. } else if (stringifyObjects) {
  46. return escapeString(val.toString());
  47. } else {
  48. return SqlString.objectToValues(val, timeZone);
  49. }
  50. default: return escapeString(val);
  51. }
  52. };
  53. SqlString.arrayToList = function arrayToList(array, timeZone) {
  54. var sql = '';
  55. for (var i = 0; i < array.length; i++) {
  56. var val = array[i];
  57. if (Array.isArray(val)) {
  58. sql += (i === 0 ? '' : ', ') + '(' + SqlString.arrayToList(val, timeZone) + ')';
  59. } else {
  60. sql += (i === 0 ? '' : ', ') + SqlString.escape(val, true, timeZone);
  61. }
  62. }
  63. return sql;
  64. };
  65. SqlString.format = function format(sql, values, stringifyObjects, timeZone) {
  66. if (values == null) {
  67. return sql;
  68. }
  69. if (!(values instanceof Array || Array.isArray(values))) {
  70. values = [values];
  71. }
  72. var chunkIndex = 0;
  73. var placeholdersRegex = /\?+/g;
  74. var result = '';
  75. var valuesIndex = 0;
  76. var match;
  77. while (valuesIndex < values.length && (match = placeholdersRegex.exec(sql))) {
  78. var len = match[0].length;
  79. if (len > 2) {
  80. continue;
  81. }
  82. var value = len === 2
  83. ? SqlString.escapeId(values[valuesIndex])
  84. : SqlString.escape(values[valuesIndex], stringifyObjects, timeZone);
  85. result += sql.slice(chunkIndex, match.index) + value;
  86. chunkIndex = placeholdersRegex.lastIndex;
  87. valuesIndex++;
  88. }
  89. if (chunkIndex === 0) {
  90. // Nothing was replaced
  91. return sql;
  92. }
  93. if (chunkIndex < sql.length) {
  94. return result + sql.slice(chunkIndex);
  95. }
  96. return result;
  97. };
  98. SqlString.dateToString = function dateToString(date, timeZone) {
  99. var dt = new Date(date);
  100. if (isNaN(dt.getTime())) {
  101. return 'NULL';
  102. }
  103. var year;
  104. var month;
  105. var day;
  106. var hour;
  107. var minute;
  108. var second;
  109. var millisecond;
  110. if (timeZone === 'local') {
  111. year = dt.getFullYear();
  112. month = dt.getMonth() + 1;
  113. day = dt.getDate();
  114. hour = dt.getHours();
  115. minute = dt.getMinutes();
  116. second = dt.getSeconds();
  117. millisecond = dt.getMilliseconds();
  118. } else {
  119. var tz = convertTimezone(timeZone);
  120. if (tz !== false && tz !== 0) {
  121. dt.setTime(dt.getTime() + (tz * 60000));
  122. }
  123. year = dt.getUTCFullYear();
  124. month = dt.getUTCMonth() + 1;
  125. day = dt.getUTCDate();
  126. hour = dt.getUTCHours();
  127. minute = dt.getUTCMinutes();
  128. second = dt.getUTCSeconds();
  129. millisecond = dt.getUTCMilliseconds();
  130. }
  131. // YYYY-MM-DD HH:mm:ss.mmm
  132. var str = zeroPad(year, 4) + '-' + zeroPad(month, 2) + '-' + zeroPad(day, 2) + ' ' +
  133. zeroPad(hour, 2) + ':' + zeroPad(minute, 2) + ':' + zeroPad(second, 2) + '.' +
  134. zeroPad(millisecond, 3);
  135. return escapeString(str);
  136. };
  137. SqlString.bufferToString = function bufferToString(buffer) {
  138. return 'X' + escapeString(buffer.toString('hex'));
  139. };
  140. SqlString.objectToValues = function objectToValues(object, timeZone) {
  141. var sql = '';
  142. for (var key in object) {
  143. var val = object[key];
  144. if (typeof val === 'function') {
  145. continue;
  146. }
  147. sql += (sql.length === 0 ? '' : ', ') + SqlString.escapeId(key) + ' = ' + SqlString.escape(val, true, timeZone);
  148. }
  149. return sql;
  150. };
  151. SqlString.raw = function raw(sql) {
  152. if (typeof sql !== 'string') {
  153. throw new TypeError('argument sql must be a string');
  154. }
  155. return {
  156. toSqlString: function toSqlString() { return sql; }
  157. };
  158. };
  159. function escapeString(val) {
  160. var chunkIndex = CHARS_GLOBAL_REGEXP.lastIndex = 0;
  161. var escapedVal = '';
  162. var match;
  163. while ((match = CHARS_GLOBAL_REGEXP.exec(val))) {
  164. escapedVal += val.slice(chunkIndex, match.index) + CHARS_ESCAPE_MAP[match[0]];
  165. chunkIndex = CHARS_GLOBAL_REGEXP.lastIndex;
  166. }
  167. if (chunkIndex === 0) {
  168. // Nothing was escaped
  169. return "'" + val + "'";
  170. }
  171. if (chunkIndex < val.length) {
  172. return "'" + escapedVal + val.slice(chunkIndex) + "'";
  173. }
  174. return "'" + escapedVal + "'";
  175. }
  176. function zeroPad(number, length) {
  177. number = number.toString();
  178. while (number.length < length) {
  179. number = '0' + number;
  180. }
  181. return number;
  182. }
  183. function convertTimezone(tz) {
  184. if (tz === 'Z') {
  185. return 0;
  186. }
  187. var m = tz.match(/([\+\-\s])(\d\d):?(\d\d)?/);
  188. if (m) {
  189. return (m[1] === '-' ? -1 : 1) * (parseInt(m[2], 10) + ((m[3] ? parseInt(m[3], 10) : 0) / 60)) * 60;
  190. }
  191. return false;
  192. }