1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | 'use strict';
|
19 |
|
20 | const {
|
21 | CHARS_GLOBAL_REGEXP,
|
22 | escapeSeries,
|
23 | isSeries,
|
24 | isSqlFragment,
|
25 | makeEscaper,
|
26 | } = require('./escapers.js');
|
27 |
|
28 | const { toString: bufferProtoToString } = Buffer.prototype;
|
29 | const { isBuffer } = Buffer;
|
30 | const { apply } = Reflect;
|
31 |
|
32 | const BT_GLOBAL_REGEXP = /`/g;
|
33 | const QUAL_GLOBAL_REGEXP = /\./g;
|
34 | const MYSQL_ID_REGEXP = /^`(?:[^`]|``)+`$/;
|
35 | const MYSQL_QUAL_ID_REGEXP = /^`(?:[^`]|``)+`(?:[.]`(?:[^`]|``)+`)*$/;
|
36 |
|
37 | const MYSQL_CHARS_ESCAPE_MAP = {
|
38 | __proto__: null,
|
39 | '\0': '\\0',
|
40 | '\b': '\\b',
|
41 | '\t': '\\t',
|
42 | '\n': '\\n',
|
43 | '\r': '\\r',
|
44 |
|
45 | '\x1a': '\\Z',
|
46 | '"': '\\"',
|
47 | '$': '\\$',
|
48 | '\'': '\\\'',
|
49 | '\\': '\\\\',
|
50 | };
|
51 |
|
52 |
|
53 | function mysqlEscapeId(val, forbidQualified) {
|
54 | if (isSqlFragment(val)) {
|
55 | const { content } = val;
|
56 | if ((forbidQualified ? MYSQL_ID_REGEXP : MYSQL_QUAL_ID_REGEXP).test(content)) {
|
57 | return content;
|
58 | }
|
59 | throw new Error(`Expected id, got ${ content }`);
|
60 | }
|
61 | if (isSeries(val)) {
|
62 | return escapeSeries(val, (element) => mysqlEscapeId(element, forbidQualified), false);
|
63 | }
|
64 | if (forbidQualified) {
|
65 | return `\`${ String(val).replace(BT_GLOBAL_REGEXP, '``') }\``;
|
66 | }
|
67 | return `\`${ String(val).replace(BT_GLOBAL_REGEXP, '``').replace(QUAL_GLOBAL_REGEXP, '`.`') }\``;
|
68 | }
|
69 |
|
70 | function mysqlEscapeString(val) {
|
71 | const str = `${ val }`;
|
72 |
|
73 | let chunkIndex = 0;
|
74 | let escapedVal = '';
|
75 |
|
76 | CHARS_GLOBAL_REGEXP.lastIndex = 0;
|
77 | for (let match; (match = CHARS_GLOBAL_REGEXP.exec(str));) {
|
78 | escapedVal += str.substring(chunkIndex, match.index) + MYSQL_CHARS_ESCAPE_MAP[match[0]];
|
79 | chunkIndex = CHARS_GLOBAL_REGEXP.lastIndex;
|
80 | }
|
81 |
|
82 | if (chunkIndex === 0) {
|
83 |
|
84 | return `'${ str }'`;
|
85 | }
|
86 |
|
87 | if (chunkIndex < str.length) {
|
88 | return `'${ escapedVal }${ str.substring(chunkIndex) }'`;
|
89 | }
|
90 |
|
91 | return `'${ escapedVal }'`;
|
92 | }
|
93 |
|
94 | const mysqlEscape = makeEscaper(mysqlEscapeId, mysqlEscapeString);
|
95 |
|
96 | function mysqlEscapeDelimited(value, delimiter, timeZone, forbidQualified) {
|
97 | if (delimiter === '`') {
|
98 | return mysqlEscapeId(value, forbidQualified).replace(/^`|`$/g, '');
|
99 | }
|
100 | if (isBuffer(value)) {
|
101 | value = apply(bufferProtoToString, value, [ 'binary' ]);
|
102 | }
|
103 | const escaped = mysqlEscape(String(value), true, timeZone);
|
104 | return escaped.substring(1, escaped.length - 1);
|
105 | }
|
106 |
|
107 | module.exports = Object.freeze({
|
108 | escape: mysqlEscape,
|
109 | escapeId: mysqlEscapeId,
|
110 | escapeDelimited: mysqlEscapeDelimited,
|
111 | });
|