UNPKG

3.02 kBJavaScriptView Raw
1/**
2 * @license
3 * Copyright 2018 Google LLC
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18'use strict';
19
20const {
21 CHARS_GLOBAL_REGEXP,
22 escapeSeries,
23 isSeries,
24 isSqlFragment,
25 makeEscaper,
26} = require('./escapers.js');
27
28const { toString: bufferProtoToString } = Buffer.prototype;
29const { isBuffer } = Buffer;
30const { apply } = Reflect;
31
32const BT_GLOBAL_REGEXP = /`/g;
33const QUAL_GLOBAL_REGEXP = /\./g;
34const MYSQL_ID_REGEXP = /^`(?:[^`]|``)+`$/;
35const MYSQL_QUAL_ID_REGEXP = /^`(?:[^`]|``)+`(?:[.]`(?:[^`]|``)+`)*$/;
36
37const MYSQL_CHARS_ESCAPE_MAP = {
38 __proto__: null,
39 '\0': '\\0',
40 '\b': '\\b',
41 '\t': '\\t',
42 '\n': '\\n',
43 '\r': '\\r',
44 // Windows end-of-file
45 '\x1a': '\\Z',
46 '"': '\\"',
47 '$': '\\$',
48 '\'': '\\\'',
49 '\\': '\\\\',
50};
51
52
53function 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
70function 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 // Nothing was escaped
84 return `'${ str }'`;
85 }
86
87 if (chunkIndex < str.length) {
88 return `'${ escapedVal }${ str.substring(chunkIndex) }'`;
89 }
90
91 return `'${ escapedVal }'`;
92}
93
94const mysqlEscape = makeEscaper(mysqlEscapeId, mysqlEscapeString);
95
96function 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
107module.exports = Object.freeze({
108 escape: mysqlEscape,
109 escapeId: mysqlEscapeId,
110 escapeDelimited: mysqlEscapeDelimited,
111});