1 |
|
2 |
|
3 | const charsRegex = /[\0\b\t\n\r\x1a"'\\]/g;
|
4 | const charsMap = {
|
5 | '\0': '\\0',
|
6 | '\b': '\\b',
|
7 | '\t': '\\t',
|
8 | '\n': '\\n',
|
9 | '\r': '\\r',
|
10 | '\x1a': '\\Z',
|
11 | '"': '\\"',
|
12 | "'": "\\'",
|
13 | '\\': '\\\\',
|
14 | };
|
15 |
|
16 | function wrapEscape(escapeFn) {
|
17 | return function finalEscape(val, ctx = {}) {
|
18 | return escapeFn(val, finalEscape, ctx);
|
19 | };
|
20 | }
|
21 |
|
22 | function makeEscape(config = {}) {
|
23 | const finalEscapeDate = config.escapeDate || dateToString;
|
24 | const finalEscapeArray = config.escapeArray || arrayToList;
|
25 | const finalEscapeBuffer = config.escapeBuffer || bufferToString;
|
26 | const finalEscapeString = config.escapeString || escapeString;
|
27 | const finalEscapeObject = config.escapeObject || escapeObject;
|
28 | const finalWrap = config.wrap || wrapEscape;
|
29 |
|
30 | function escapeFn(val, finalEscape, ctx) {
|
31 | if (val === undefined || val === null) {
|
32 | return 'NULL';
|
33 | }
|
34 | switch (typeof val) {
|
35 | case 'boolean':
|
36 | return val ? 'true' : 'false';
|
37 | case 'number':
|
38 | return val + '';
|
39 | case 'object':
|
40 | if (val instanceof Date) {
|
41 | val = finalEscapeDate(val, finalEscape, ctx);
|
42 | } else if (Array.isArray(val)) {
|
43 | return finalEscapeArray(val, finalEscape, ctx);
|
44 | } else if (Buffer.isBuffer(val)) {
|
45 | return finalEscapeBuffer(val, finalEscape, ctx);
|
46 | } else {
|
47 | return finalEscapeObject(val, finalEscape, ctx);
|
48 | }
|
49 | }
|
50 | return finalEscapeString(val, finalEscape, ctx);
|
51 | }
|
52 |
|
53 | return finalWrap ? finalWrap(escapeFn) : escapeFn;
|
54 | }
|
55 |
|
56 | function escapeObject(val, finalEscape, ctx) {
|
57 | if (typeof val.toSQL === 'function') {
|
58 | return val.toSQL(ctx);
|
59 | } else {
|
60 | return JSON.stringify(val);
|
61 | }
|
62 | }
|
63 |
|
64 | function arrayToList(array, finalEscape, ctx) {
|
65 | let sql = '';
|
66 | for (let i = 0; i < array.length; i++) {
|
67 | const val = array[i];
|
68 | if (Array.isArray(val)) {
|
69 | sql +=
|
70 | (i === 0 ? '' : ', ') + '(' + arrayToList(val, finalEscape, ctx) + ')';
|
71 | } else {
|
72 | sql += (i === 0 ? '' : ', ') + finalEscape(val, ctx);
|
73 | }
|
74 | }
|
75 | return sql;
|
76 | }
|
77 |
|
78 | function bufferToString(buffer) {
|
79 | return 'X' + escapeString(buffer.toString('hex'));
|
80 | }
|
81 |
|
82 | function escapeString(val, finalEscape, ctx) {
|
83 | let chunkIndex = (charsRegex.lastIndex = 0);
|
84 | let escapedVal = '';
|
85 | let match;
|
86 |
|
87 | while ((match = charsRegex.exec(val))) {
|
88 | escapedVal += val.slice(chunkIndex, match.index) + charsMap[match[0]];
|
89 | chunkIndex = charsRegex.lastIndex;
|
90 | }
|
91 |
|
92 | if (chunkIndex === 0) {
|
93 |
|
94 | return "'" + val + "'";
|
95 | }
|
96 |
|
97 | if (chunkIndex < val.length) {
|
98 | return "'" + escapedVal + val.slice(chunkIndex) + "'";
|
99 | }
|
100 |
|
101 | return "'" + escapedVal + "'";
|
102 | }
|
103 |
|
104 | function dateToString(date, finalEscape, ctx) {
|
105 | const timeZone = ctx.timeZone || 'local';
|
106 |
|
107 | const dt = new Date(date);
|
108 | let year;
|
109 | let month;
|
110 | let day;
|
111 | let hour;
|
112 | let minute;
|
113 | let second;
|
114 | let millisecond;
|
115 |
|
116 | if (timeZone === 'local') {
|
117 | year = dt.getFullYear();
|
118 | month = dt.getMonth() + 1;
|
119 | day = dt.getDate();
|
120 | hour = dt.getHours();
|
121 | minute = dt.getMinutes();
|
122 | second = dt.getSeconds();
|
123 | millisecond = dt.getMilliseconds();
|
124 | } else {
|
125 | const tz = convertTimezone(timeZone);
|
126 |
|
127 | if (tz !== false && tz !== 0) {
|
128 | dt.setTime(dt.getTime() + tz * 60000);
|
129 | }
|
130 |
|
131 | year = dt.getUTCFullYear();
|
132 | month = dt.getUTCMonth() + 1;
|
133 | day = dt.getUTCDate();
|
134 | hour = dt.getUTCHours();
|
135 | minute = dt.getUTCMinutes();
|
136 | second = dt.getUTCSeconds();
|
137 | millisecond = dt.getUTCMilliseconds();
|
138 | }
|
139 |
|
140 |
|
141 | return (
|
142 | zeroPad(year, 4) +
|
143 | '-' +
|
144 | zeroPad(month, 2) +
|
145 | '-' +
|
146 | zeroPad(day, 2) +
|
147 | ' ' +
|
148 | zeroPad(hour, 2) +
|
149 | ':' +
|
150 | zeroPad(minute, 2) +
|
151 | ':' +
|
152 | zeroPad(second, 2) +
|
153 | '.' +
|
154 | zeroPad(millisecond, 3)
|
155 | );
|
156 | }
|
157 |
|
158 | function zeroPad(number, length) {
|
159 | number = number.toString();
|
160 | while (number.length < length) {
|
161 | number = '0' + number;
|
162 | }
|
163 | return number;
|
164 | }
|
165 |
|
166 | function convertTimezone(tz) {
|
167 | if (tz === 'Z') {
|
168 | return 0;
|
169 | }
|
170 | const m = tz.match(/([+\-\s])(\d\d):?(\d\d)?/);
|
171 | if (m) {
|
172 | return (
|
173 | (m[1] == '-' ? -1 : 1) *
|
174 | (parseInt(m[2], 10) + (m[3] ? parseInt(m[3], 10) : 0) / 60) *
|
175 | 60
|
176 | );
|
177 | }
|
178 | return false;
|
179 | }
|
180 |
|
181 | module.exports = {
|
182 | arrayToList,
|
183 | bufferToString,
|
184 | dateToString,
|
185 | escapeString,
|
186 | charsRegex,
|
187 | charsMap,
|
188 | escapeObject,
|
189 | makeEscape,
|
190 | };
|