utils.js 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. 'use strict';
  2. const Long = require('long');
  3. const hexArray = '0123456789ABCDEF'.split('');
  4. const Errors = require('../misc/errors');
  5. const CommonText = require('../cmd/common-text-cmd');
  6. const Iconv = require('iconv-lite');
  7. /**
  8. * Write bytes/hexadecimal value of a byte array to a string.
  9. * String output example :
  10. * 38 00 00 00 03 63 72 65 61 74 65 20 74 61 62 6C 8....create tabl
  11. * 65 20 42 6C 6F 62 54 65 73 74 63 6C 6F 62 74 65 e BlobTestclobte
  12. * 73 74 32 20 28 73 74 72 6D 20 74 65 78 74 29 20 st2 (strm text)
  13. * 43 48 41 52 53 45 54 20 75 74 66 38 CHARSET utf8
  14. */
  15. module.exports.log = function (opts, buf, off, end, header) {
  16. let out = [];
  17. if (!buf) return '';
  18. if (off === undefined || off === null) off = 0;
  19. if (end === undefined || end === null) end = buf.length;
  20. let asciiValue = new Array(16);
  21. asciiValue[8] = ' ';
  22. let useHeader = header !== undefined;
  23. let offset = off || 0;
  24. const maxLgh = Math.min(useHeader ? opts.debugLen - header.length : opts.debugLen, end - offset);
  25. const isLimited = end - offset > maxLgh;
  26. let byteValue;
  27. let posHexa = 0;
  28. let pos = 0;
  29. out.push(
  30. '+--------------------------------------------------+\n' +
  31. '| 0 1 2 3 4 5 6 7 8 9 a b c d e f |\n' +
  32. '+--------------------------------------------------+------------------+\n'
  33. );
  34. if (useHeader) {
  35. while (pos < header.length) {
  36. if (posHexa === 0) out.push('| ');
  37. byteValue = header[pos++] & 0xff;
  38. out.push(hexArray[byteValue >>> 4], hexArray[byteValue & 0x0f], ' ');
  39. asciiValue[posHexa++] =
  40. byteValue > 31 && byteValue < 127 ? String.fromCharCode(byteValue) : '.';
  41. if (posHexa === 8) out.push(' ');
  42. }
  43. }
  44. pos = offset;
  45. while (pos < maxLgh + offset) {
  46. if (posHexa === 0) out.push('| ');
  47. byteValue = buf[pos] & 0xff;
  48. out.push(hexArray[byteValue >>> 4], hexArray[byteValue & 0x0f], ' ');
  49. asciiValue[posHexa++] =
  50. byteValue > 31 && byteValue < 127 ? String.fromCharCode(byteValue) : '.';
  51. if (posHexa === 8) out.push(' ');
  52. if (posHexa === 16) {
  53. out.push('| ', asciiValue.join(''), ' |\n');
  54. posHexa = 0;
  55. }
  56. pos++;
  57. }
  58. let remaining = posHexa;
  59. if (remaining > 0) {
  60. if (remaining < 8) {
  61. for (; remaining < 8; remaining++) {
  62. out.push(' ');
  63. asciiValue[posHexa++] = ' ';
  64. }
  65. out.push(' ');
  66. }
  67. for (; remaining < 16; remaining++) {
  68. out.push(' ');
  69. asciiValue[posHexa++] = ' ';
  70. }
  71. out.push('| ', asciiValue.join(''), isLimited ? ' |...\n' : ' |\n');
  72. } else if (isLimited) {
  73. out[out.length - 1] = ' |...\n';
  74. }
  75. out.push('+--------------------------------------------------+------------------+\n');
  76. return out.join('');
  77. };
  78. module.exports.escapeId = (opts, info, value) => {
  79. if (!value || value === '') {
  80. throw Errors.createError(
  81. 'Cannot escape empty ID value',
  82. false,
  83. info,
  84. '0A000',
  85. Errors.ER_NULL_ESCAPEID
  86. );
  87. }
  88. if (value.includes('\u0000')) {
  89. throw Errors.createError(
  90. 'Cannot escape ID with null character (u0000)',
  91. false,
  92. info,
  93. '0A000',
  94. Errors.ER_NULL_CHAR_ESCAPEID
  95. );
  96. }
  97. // always return escaped value, event when there is no special characters
  98. // to permit working with reserved words
  99. if (value.match(/^`.+`$/g)) {
  100. // already escaped
  101. return value;
  102. }
  103. return '`' + value.replace(/`/g, '``') + '`';
  104. };
  105. module.exports.escape = (opts, info, value) => {
  106. if (value === undefined || value === null) return 'NULL';
  107. switch (typeof value) {
  108. case 'boolean':
  109. return value ? 'true' : 'false';
  110. case 'bigint':
  111. case 'number':
  112. return '' + value;
  113. case 'object':
  114. if (Object.prototype.toString.call(value) === '[object Date]') {
  115. return opts.tz
  116. ? opts.tz === 'Etc/UTC'
  117. ? CommonText.getUtcDate(value, opts)
  118. : CommonText.getTimezoneDate(value, opts)
  119. : CommonText.getLocalDate(value, opts);
  120. } else if (Buffer.isBuffer(value)) {
  121. let stValue;
  122. if (Buffer.isEncoding(opts.collation.charset)) {
  123. stValue = value.toString(opts.collation.charset, 0, value.length);
  124. } else {
  125. stValue = Iconv.decode(value, opts.collation.charset);
  126. }
  127. return "_binary'" + escapeString(stValue) + "'";
  128. } else if (typeof value.toSqlString === 'function') {
  129. return "'" + escapeString(String(value.toSqlString())) + "'";
  130. } else if (Long.isLong(value)) {
  131. return value.toString();
  132. } else if (Array.isArray(value)) {
  133. let out = opts.arrayParenthesis ? '(' : '';
  134. for (let i = 0; i < value.length; i++) {
  135. if (i !== 0) out += ',';
  136. out += this.escape(opts, info, value[i]);
  137. }
  138. if (opts.arrayParenthesis) out += ')';
  139. return out;
  140. } else {
  141. if (
  142. value.type != null &&
  143. [
  144. 'Point',
  145. 'LineString',
  146. 'Polygon',
  147. 'MultiPoint',
  148. 'MultiLineString',
  149. 'MultiPolygon',
  150. 'GeometryCollection'
  151. ].includes(value.type)
  152. ) {
  153. //GeoJSON format.
  154. let prefix =
  155. info &&
  156. ((info.isMariaDB() && info.hasMinVersion(10, 1, 4)) ||
  157. (!info.isMariaDB() && info.hasMinVersion(5, 7, 6)))
  158. ? 'ST_'
  159. : '';
  160. switch (value.type) {
  161. case 'Point':
  162. return (
  163. prefix +
  164. "PointFromText('POINT(" +
  165. CommonText.geoPointToString(value.coordinates) +
  166. ")')"
  167. );
  168. case 'LineString':
  169. return (
  170. prefix +
  171. "LineFromText('LINESTRING(" +
  172. CommonText.geoArrayPointToString(value.coordinates) +
  173. ")')"
  174. );
  175. case 'Polygon':
  176. return (
  177. prefix +
  178. "PolygonFromText('POLYGON(" +
  179. CommonText.geoMultiArrayPointToString(value.coordinates) +
  180. ")')"
  181. );
  182. case 'MultiPoint':
  183. return (
  184. prefix +
  185. "MULTIPOINTFROMTEXT('MULTIPOINT(" +
  186. CommonText.geoArrayPointToString(value.coordinates) +
  187. ")')"
  188. );
  189. case 'MultiLineString':
  190. return (
  191. prefix +
  192. "MLineFromText('MULTILINESTRING(" +
  193. CommonText.geoMultiArrayPointToString(value.coordinates) +
  194. ")')"
  195. );
  196. case 'MultiPolygon':
  197. return (
  198. prefix +
  199. "MPolyFromText('MULTIPOLYGON(" +
  200. CommonText.geoMultiPolygonToString(value.coordinates) +
  201. ")')"
  202. );
  203. case 'GeometryCollection':
  204. return (
  205. prefix +
  206. "GeomCollFromText('GEOMETRYCOLLECTION(" +
  207. CommonText.geometricCollectionToString(value.geometries) +
  208. ")')"
  209. );
  210. }
  211. } else {
  212. if (opts.permitSetMultiParamEntries) {
  213. let out = '';
  214. let first = true;
  215. for (let key in value) {
  216. const val = value[key];
  217. if (typeof val === 'function') continue;
  218. if (first) {
  219. first = false;
  220. } else {
  221. out += ',';
  222. }
  223. out += '`' + key + '`=';
  224. out += this.escape(opts, info, val);
  225. }
  226. if (out === '') return "'" + escapeString(JSON.stringify(value)) + "'";
  227. return out;
  228. } else {
  229. return "'" + escapeString(JSON.stringify(value)) + "'";
  230. }
  231. }
  232. }
  233. default:
  234. return "'" + escapeString(value) + "'";
  235. }
  236. };
  237. // see https://mariadb.com/kb/en/library/string-literals/
  238. const LITTERAL_ESCAPE = {
  239. '\u0000': '\\0',
  240. "'": "\\'",
  241. '"': '\\"',
  242. '\b': '\\b',
  243. '\n': '\\n',
  244. '\r': '\\r',
  245. '\t': '\\t',
  246. '\u001A': '\\Z',
  247. '\\': '\\\\'
  248. };
  249. const escapeString = (val) => {
  250. const pattern = /[\u0000'"\b\n\r\t\u001A\\]/g;
  251. let offset = 0;
  252. let escaped = '';
  253. let match;
  254. while ((match = pattern.exec(val))) {
  255. escaped += val.substring(offset, match.index);
  256. escaped += LITTERAL_ESCAPE[match[0]];
  257. offset = pattern.lastIndex;
  258. }
  259. if (offset === 0) {
  260. return val;
  261. }
  262. if (offset < val.length) {
  263. escaped += val.substring(offset);
  264. }
  265. return escaped;
  266. };