UNPKG

5.72 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.escapeLiteral = exports.escapeIdentifier = exports.SqlFragment = undefined;
7exports.sqlStr = sqlStr;
8exports.identifier = identifier;
9exports.literal = literal;
10exports.insert_object = insert_object;
11
12var _util = require('util');
13
14var _pg = require('pg');
15
16var _pg2 = _interopRequireDefault(_pg);
17
18var _utils = require('pg/lib/utils');
19
20function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
21
22let SQL;
23
24class SqlFragment {
25
26 // SQL`insert into $quote${tableName} $values${keyValue}`;
27 constructor(templateParts, templateValues) {
28 this._parts = [];
29 this.values = [];
30
31 const length = templateValues.length;
32 const text = [];
33 let currentFragment = [];
34 let argIndex = 1;
35
36 let i = 0;
37
38 const addText = str => {
39 currentFragment.push(str);
40 };
41
42 const flushText = () => {
43 const fragment = currentFragment.join('');
44 currentFragment = [];
45 this._parts.push(fragment);
46 text.push(fragment);
47 };
48
49 const addValue = value => {
50 flushText();
51 this.values.push(value);
52 text.push('$', argIndex++);
53 };
54
55 while (i < length) {
56 const parts = templateParts[i].split('$');
57 let value = templateValues.shift();
58 if (typeof value === 'undefined') throw new Error(`Expected something, but got undefined. ` + `Value after SQL fragment: ${ text.join('') }${ templateParts[i] }`);
59
60 while (parts.length > 1) value = SQL.transform(parts.pop(), value);
61
62 addText(parts[0]);
63
64 if (value && value.toSQL) value = value.toSQL();
65
66 if (value instanceof SqlFragment) {
67 const nestedValuesLength = value.values.length;
68 let valueIndex = 0;
69 while (valueIndex < nestedValuesLength) {
70 addText(value._parts[valueIndex]);
71 addValue(value.values[valueIndex]);
72 valueIndex++;
73 }
74 addText(value._parts[valueIndex]);
75 } else {
76 addValue(value);
77 }
78 i++;
79 }
80 // last part is alone, without value
81 addText(templateParts[i]);
82 flushText();
83
84 this.text = text.join('');
85 }
86
87 // this is for log/debugging only
88 toString() {
89 if (this._asString) return this._asString;
90
91 const text = [];
92 const length = this.values.length;
93 let i = 0;
94 while (i < length) {
95 text.push(this._parts[i]);
96 const value = literal(this.values[i]);
97 text.push(value);
98 i++;
99 }
100 text.push(this._parts[i]);
101 this._asString = text.join('');
102 return this._asString;
103 }
104}
105
106exports.SqlFragment = SqlFragment;
107SQL = function SQL(parts) {
108 for (var _len = arguments.length, values = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
109 values[_key - 1] = arguments[_key];
110 }
111
112 // eslint-disable-line prefer-const
113 // only one argument, called manually
114 if (!Array.isArray(parts) && values.length === 0) {
115 if (parts instanceof SqlFragment) return parts;
116
117 parts = [parts];
118 }
119
120 return new SqlFragment(parts, values);
121};
122
123SQL.NULL = SQL('NULL');
124SQL.DEFAULT = SQL('DEFAULT');
125
126SQL._transforms = {};
127SQL.registerTransform = function () {
128 for (var _len2 = arguments.length, names = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
129 names[_key2] = arguments[_key2];
130 }
131
132 const transform = names.pop();
133 if (typeof transform !== 'function') throw new Error('Last argument must be a function');
134
135 const transforms = SQL._transforms;
136 for (let i = 0; i < names.length; i++) {
137 const name = names[i].trim().toLowerCase();
138 if (transforms[name] && transforms[name] !== transform) throw new Error(`Transform ${ name } already registered`);
139 transforms[names[i].toLowerCase()] = transform;
140 }
141};
142
143SQL.transform = (name, value) => {
144 name = name.trim().toLowerCase();
145 const transform = SQL._transforms[name];
146 if (!transform) throw new Error(`Unknown transform: "${ name }"`);
147 return transform(value);
148};
149
150const escapeIdentifier = exports.escapeIdentifier = _pg2.default.Client.prototype.escapeIdentifier;
151const escapeLiteral = exports.escapeLiteral = _pg2.default.Client.prototype.escapeLiteral;
152
153function sqlStr(str) {
154 if (typeof str !== 'string') throw new Error(`Expected string, got ${ (0, _util.inspect)(str) }`);
155
156 return SQL(str);
157}
158
159// returns quoted identifier
160function identifier(name) {
161 if (!name) throw new Error(`Expected nonempty string, got ${ (0, _util.inspect)(name) }`);
162
163 return SQL(escapeIdentifier(name));
164}
165
166// returns quoted literal
167function literal(value) {
168 if (value instanceof SqlFragment) return value;
169
170 if (typeof value === 'undefined') throw new Error(`Expected something, but got undefined.`);
171
172 if (value === null) return SQL.NULL;
173
174 if (value.toPostgres) return SQL(escapeLiteral(value.toPostgres(_utils.prepareValue)));
175
176 if (value.toSQL) return SQL(value.toSQL());
177
178 return SQL(escapeLiteral(value.toString()));
179}
180
181function insert_object(data) {
182 const keys = Object.keys(data);
183 const length = keys.length;
184 const sqlFragments = new Array(length);
185 const values = new Array(length - 1);
186 const sb = [];
187
188 sb.push('(');
189 let i = 0;
190 while (i < length) {
191 const column = keys[i];
192 values[i] = data[column];
193 i++;
194 sb.push(escapeIdentifier(column), ',');
195 sqlFragments[i] = ',';
196 }
197 sb[sb.length - 1] = ') VALUES (';
198 sqlFragments[0] = sb.join('');
199 sqlFragments[i] = ')';
200
201 return new SqlFragment(sqlFragments, values);
202}
203
204SQL.registerTransform('id', 'ident', 'identifier', 'name', identifier);
205SQL.registerTransform('', 'literal', literal);
206SQL.registerTransform('!', 'raw', sqlStr);
207SQL.registerTransform('insert_object', 'insert', insert_object);
208
209exports.default = SQL;
\No newline at end of file