@depup/pg-promise 12.6.2-depup.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +25 -0
  3. package/lib/assert.js +10 -0
  4. package/lib/connect.js +178 -0
  5. package/lib/context.js +93 -0
  6. package/lib/database-pool.js +115 -0
  7. package/lib/database.js +1643 -0
  8. package/lib/errors/README.md +13 -0
  9. package/lib/errors/index.js +51 -0
  10. package/lib/errors/parameterized-query-error.js +71 -0
  11. package/lib/errors/prepared-statement-error.js +71 -0
  12. package/lib/errors/query-file-error.js +75 -0
  13. package/lib/errors/query-result-error.js +157 -0
  14. package/lib/events.js +543 -0
  15. package/lib/formatting.js +932 -0
  16. package/lib/helpers/README.md +10 -0
  17. package/lib/helpers/column-set.js +614 -0
  18. package/lib/helpers/column.js +406 -0
  19. package/lib/helpers/index.js +75 -0
  20. package/lib/helpers/methods/concat.js +103 -0
  21. package/lib/helpers/methods/index.js +13 -0
  22. package/lib/helpers/methods/insert.js +151 -0
  23. package/lib/helpers/methods/sets.js +81 -0
  24. package/lib/helpers/methods/update.js +248 -0
  25. package/lib/helpers/methods/values.js +116 -0
  26. package/lib/helpers/table-name.js +175 -0
  27. package/lib/index.js +29 -0
  28. package/lib/inner-state.js +39 -0
  29. package/lib/main.js +394 -0
  30. package/lib/patterns.js +43 -0
  31. package/lib/query-file.js +379 -0
  32. package/lib/query-result.js +39 -0
  33. package/lib/query.js +273 -0
  34. package/lib/special-query.js +30 -0
  35. package/lib/stream.js +125 -0
  36. package/lib/task.js +404 -0
  37. package/lib/text.js +40 -0
  38. package/lib/tx-mode.js +194 -0
  39. package/lib/types/index.js +18 -0
  40. package/lib/types/parameterized-query.js +247 -0
  41. package/lib/types/prepared-statement.js +298 -0
  42. package/lib/types/server-formatting.js +92 -0
  43. package/lib/utils/README.md +13 -0
  44. package/lib/utils/color.js +68 -0
  45. package/lib/utils/index.js +199 -0
  46. package/lib/utils/public.js +312 -0
  47. package/package.json +77 -0
  48. package/typescript/README.md +63 -0
  49. package/typescript/pg-promise.d.ts +728 -0
  50. package/typescript/pg-subset.d.ts +359 -0
  51. package/typescript/tslint.json +21 -0
@@ -0,0 +1,379 @@
1
+ /*
2
+ * Copyright (c) 2015-present, Vitaly Tomilov
3
+ *
4
+ * See the LICENSE file at the top-level directory of this distribution
5
+ * for licensing information.
6
+ *
7
+ * Removal or modification of this copyright notice is prohibited.
8
+ */
9
+
10
+ const {InnerState} = require('./inner-state');
11
+ const {QueryFileError} = require('./errors');
12
+ const {assert} = require('./assert');
13
+ const {ColorConsole} = require('./utils/color');
14
+
15
+ const npm = {
16
+ fs: require('fs'),
17
+ os: require('os'),
18
+ path: require('path'),
19
+ minify: require('pg-minify'),
20
+ utils: require('./utils'),
21
+ formatting: require('./formatting'),
22
+ inspect: require('util').inspect
23
+ };
24
+
25
+ const file$query = Symbol('QueryFile.query');
26
+
27
+ /**
28
+ * @class QueryFile
29
+ * @description
30
+ *
31
+ * Represents an external SQL file. The type is available from the library's root: `pgp.QueryFile`.
32
+ *
33
+ * Reads a file with SQL and prepares it for execution, also parses and minifies it, if required.
34
+ * The SQL can be of any complexity, with both single and multi-line comments.
35
+ *
36
+ * The type can be used in place of the `query` parameter, with any query method directly, plus as `text` in {@link PreparedStatement}
37
+ * and {@link ParameterizedQuery}.
38
+ *
39
+ * It never throws any error, leaving it for query methods to reject with {@link errors.QueryFileError QueryFileError}.
40
+ *
41
+ * **IMPORTANT:** You should only create a single reusable object per file, in order to avoid repeated file reads,
42
+ * as the IO is a very expensive resource. If you do not follow it, you will be seeing the following warning:
43
+ * `Creating a duplicate QueryFile object for the same file`, which signals a bad-use pattern.
44
+ *
45
+ * @param {string} file
46
+ * Path to the SQL file with the query, either absolute or relative to the application's entry point file.
47
+ *
48
+ * If there is any problem reading the file, it will be reported when executing the query.
49
+ *
50
+ * @param {QueryFile.Options} [options]
51
+ * Set of configuration options, as documented by {@link QueryFile.Options}.
52
+ *
53
+ * @returns {QueryFile}
54
+ *
55
+ * @see
56
+ * {@link errors.QueryFileError QueryFileError},
57
+ * {@link QueryFile#toPostgres toPostgres}
58
+ *
59
+ * @example
60
+ * // File sql.js
61
+ *
62
+ * // Proper way to organize an sql provider:
63
+ * //
64
+ * // - have all sql files for Users in ./sql/users
65
+ * // - have all sql files for Products in ./sql/products
66
+ * // - have your sql provider module as ./sql/index.js
67
+ *
68
+ * const {QueryFile} = require('pg-promise');
69
+ * const {join: joinPath} = require('path');
70
+ *
71
+ * // Helper for linking to external query files:
72
+ * function sql(file) {
73
+ * const fullPath = joinPath(__dirname, file); // generating full path;
74
+ * return new QueryFile(fullPath, {minify: true});
75
+ * }
76
+ *
77
+ * module.exports = {
78
+ * // external queries for Users:
79
+ * users: {
80
+ * add: sql('users/create.sql'),
81
+ * search: sql('users/search.sql'),
82
+ * report: sql('users/report.sql'),
83
+ * },
84
+ * // external queries for Products:
85
+ * products: {
86
+ * add: sql('products/add.sql'),
87
+ * quote: sql('products/quote.sql'),
88
+ * search: sql('products/search.sql'),
89
+ * }
90
+ * };
91
+ *
92
+ * @example
93
+ * // Testing our SQL provider
94
+ *
95
+ * const db = require('./db'); // our database module;
96
+ * const {users: sql} = require('./sql'); // sql for users;
97
+ *
98
+ * module.exports = {
99
+ * addUser: (name, age) => db.none(sql.add, [name, age]),
100
+ * findUser: name => db.any(sql.search, name)
101
+ * };
102
+ *
103
+ */
104
+ class QueryFile extends InnerState {
105
+
106
+ constructor(file, options) {
107
+
108
+ let filePath = file;
109
+
110
+ options = assert(options, {
111
+ debug: npm.utils.isDev(),
112
+ minify: (options && options.compress && options.minify === undefined) ? true : undefined,
113
+ compress: undefined,
114
+ params: undefined,
115
+ noWarnings: undefined
116
+ });
117
+
118
+ if (npm.utils.isText(filePath) && !npm.path.isAbsolute(filePath)) {
119
+ filePath = npm.path.join(npm.utils.startDir, filePath);
120
+ }
121
+
122
+ const {usedPath} = QueryFile.instance;
123
+
124
+ // istanbul ignore next:
125
+ if (!options.noWarnings) {
126
+ if (filePath in usedPath) {
127
+ usedPath[filePath]++;
128
+ ColorConsole.warn(`WARNING: Creating a duplicate QueryFile object for the same file - \n ${filePath}\n${npm.utils.getLocalStack(2, 3)}\n`);
129
+ } else {
130
+ usedPath[filePath] = 0;
131
+ }
132
+ }
133
+
134
+ const _inner = {
135
+ file,
136
+ filePath,
137
+ options,
138
+ sql: undefined,
139
+ error: undefined,
140
+ ready: undefined,
141
+ modTime: undefined
142
+ };
143
+
144
+ super(_inner);
145
+
146
+ this.prepare();
147
+ }
148
+
149
+ /**
150
+ * Global instance of the file-path repository.
151
+ *
152
+ * @return {{usedPath: {}}}
153
+ */
154
+ static get instance() {
155
+ const s = Symbol.for('pgPromiseQueryFile');
156
+ let scope = global[s];
157
+ if (!scope) {
158
+ scope = {
159
+ usedPath: {} // used-path look-up dictionary
160
+ };
161
+ global[s] = scope;
162
+ }
163
+ return scope;
164
+ }
165
+
166
+ /**
167
+ * @name QueryFile#Symbol(QueryFile.$query)
168
+ * @type {string}
169
+ * @default undefined
170
+ * @readonly
171
+ * @private
172
+ * @summary Prepared query string.
173
+ * @description
174
+ * When property {@link QueryFile#error error} is set, the query is `undefined`.
175
+ *
176
+ * **IMPORTANT:** This property is for internal use by the library only, never use this
177
+ * property directly from your code.
178
+ */
179
+ get [file$query]() {
180
+ return this._inner.sql;
181
+ }
182
+
183
+ /**
184
+ * @name QueryFile#error
185
+ * @type {errors.QueryFileError}
186
+ * @default undefined
187
+ * @readonly
188
+ * @description
189
+ * When in an error state, it is set to a {@link errors.QueryFileError QueryFileError} object. Otherwise, it is `undefined`.
190
+ */
191
+ get error() {
192
+ return this._inner.error;
193
+ }
194
+
195
+ /**
196
+ * @name QueryFile#file
197
+ * @type {string}
198
+ * @readonly
199
+ * @description
200
+ * File name that was passed into the constructor.
201
+ *
202
+ * This property is primarily for internal use by the library.
203
+ */
204
+ get file() {
205
+ return this._inner.file;
206
+ }
207
+
208
+ /**
209
+ * @name QueryFile#options
210
+ * @type {QueryFile.Options}
211
+ * @readonly
212
+ * @description
213
+ * Set of options, as configured during the object's construction.
214
+ *
215
+ * This property is primarily for internal use by the library.
216
+ */
217
+ get options() {
218
+ return this._inner.options;
219
+ }
220
+
221
+ /**
222
+ * @summary Prepares the query for execution.
223
+ * @description
224
+ * If the query hasn't been prepared yet, it will read the file and process the content according
225
+ * to the parameters passed into the constructor.
226
+ *
227
+ * This method is primarily for internal use by the library.
228
+ *
229
+ * @param {boolean} [throwErrors=false]
230
+ * Throw any error encountered.
231
+ */
232
+ prepare(throwErrors) {
233
+ const i = this._inner, options = i.options;
234
+ let lastMod;
235
+ if (options.debug && i.ready) {
236
+ try {
237
+ lastMod = npm.fs.statSync(i.filePath).mtime.getTime();
238
+ // istanbul ignore if;
239
+ if (lastMod === i.modTime) {
240
+ return;
241
+ }
242
+ i.ready = false;
243
+ } catch (e) {
244
+ i.sql = undefined;
245
+ i.ready = false;
246
+ i.error = e;
247
+ if (throwErrors) {
248
+ throw i.error;
249
+ }
250
+ return;
251
+ }
252
+ }
253
+ if (i.ready) {
254
+ return;
255
+ }
256
+ try {
257
+ i.sql = npm.fs.readFileSync(i.filePath, 'utf8');
258
+ i.modTime = lastMod || npm.fs.statSync(i.filePath).mtime.getTime();
259
+ if (options.minify && options.minify !== 'after') {
260
+ i.sql = npm.minify(i.sql, {compress: options.compress});
261
+ }
262
+ if (options.params !== undefined) {
263
+ i.sql = npm.formatting.as.format(i.sql, options.params, {partial: true});
264
+ }
265
+ if (options.minify && options.minify === 'after') {
266
+ i.sql = npm.minify(i.sql, {compress: options.compress});
267
+ }
268
+ i.ready = true;
269
+ i.error = undefined;
270
+ } catch (e) {
271
+ i.sql = undefined;
272
+ i.error = new QueryFileError(e, this);
273
+ if (throwErrors) {
274
+ throw i.error;
275
+ }
276
+ }
277
+ }
278
+
279
+ }
280
+
281
+ // Hiding the query as a symbol within the type,
282
+ // to make it even more difficult to misuse it:
283
+ QueryFile.$query = file$query;
284
+
285
+ /**
286
+ * @method QueryFile#toPostgres
287
+ * @description
288
+ * $[Custom Type Formatting], based on $[Symbolic CTF], i.e. the actual method is available only via {@link external:Symbol Symbol}:
289
+ *
290
+ * ```js
291
+ * const {toPostgres} = pgp.as.ctf; // Custom Type Formatting symbols namespace
292
+ * const query = qf[toPostgres](); // qf = an object of type QueryFile
293
+ * ```
294
+ *
295
+ * This is a raw formatting type (`rawType = true`), i.e. when used as a query-formatting parameter, type `QueryFile` injects SQL as raw text.
296
+ *
297
+ * If you need to support type `QueryFile` outside of query methods, this is the only safe way to get the most current SQL.
298
+ * And you would want to use this method dynamically, as it reloads the SQL automatically, if option `debug` is set.
299
+ * See {@link QueryFile.Options Options}.
300
+ *
301
+ * @param {QueryFile} [self]
302
+ * Optional self-reference, for ES6 arrow functions.
303
+ *
304
+ * @returns {string}
305
+ * SQL string from the file, according to the {@link QueryFile.Options options} specified.
306
+ *
307
+ */
308
+ QueryFile.prototype[npm.formatting.as.ctf.toPostgres] = function (self) {
309
+ self = this instanceof QueryFile && this || self;
310
+ self.prepare(true);
311
+ return self[QueryFile.$query];
312
+ };
313
+
314
+ QueryFile.prototype[npm.formatting.as.ctf.rawType] = true; // use as pre-formatted
315
+
316
+ npm.utils.addInspection(QueryFile, function () {
317
+ const s = 'QueryFile ';
318
+ const obj = {
319
+ file: this.file,
320
+ options: this.options
321
+ };
322
+ this.prepare();
323
+ if (this.error) {
324
+ obj.error = this.error;
325
+ }
326
+ obj.query = this[QueryFile.$query];
327
+ return s + npm.inspect(obj, {breakLength: 100, depth: 5, colors: true});
328
+ });
329
+
330
+ module.exports = {QueryFile};
331
+
332
+ /**
333
+ * @typedef QueryFile.Options
334
+ * @description
335
+ * A set of configuration options as passed into the {@link QueryFile} constructor.
336
+ *
337
+ * @property {boolean} debug
338
+ * When in debug mode, the query file is checked for its last modification time on every query request,
339
+ * so if it changes, the file is read afresh.
340
+ *
341
+ * The default for this property is `true` when `NODE_ENV` = `development`,
342
+ * or `false` otherwise.
343
+ *
344
+ * @property {boolean|string} minify=false
345
+ * Parses and minifies the SQL using $[pg-minify]:
346
+ * - `false` - do not use $[pg-minify]
347
+ * - `true` - use $[pg-minify] to parse and minify SQL
348
+ * - `'after'` - use $[pg-minify] after applying static formatting parameters
349
+ * (option `params`), as opposed to before it (default)
350
+ *
351
+ * If option `compress` is set, then the default for `minify` is `true`.
352
+ *
353
+ * Failure to parse SQL will result in $[SQLParsingError].
354
+ *
355
+ * @property {boolean} compress=false
356
+ * Sets option `compress` as supported by $[pg-minify], to uglify the SQL:
357
+ * - `false` - no compression to be applied, keep minimum spaces for easier read
358
+ * - `true` - remove all unnecessary spaces from SQL
359
+ *
360
+ * This option has no meaning, if `minify` is explicitly set to `false`. However, if `minify` is not
361
+ * specified and `compress` is specified as `true`, then `minify` defaults to `true`.
362
+ *
363
+ * @property {array|object|value} params
364
+ *
365
+ * Static formatting parameters to be applied to the SQL, using the same method {@link formatting.format as.format},
366
+ * but with option `partial` = `true`.
367
+ *
368
+ * Most of the time query formatting is fully dynamic, and applied just before executing the query.
369
+ * In some cases though you may need to pre-format SQL with static values. Examples of it can be a
370
+ * schema name, or a configurable table name.
371
+ *
372
+ * This option makes two-step SQL formatting easy: you can pre-format the SQL initially, and then
373
+ * apply the second-step dynamic formatting when executing the query.
374
+ *
375
+ * @property {boolean} noWarnings=false
376
+ * Suppresses all warnings produced by the class. It is not recommended for general use, only in specific tests
377
+ * that may require it.
378
+ *
379
+ */
@@ -0,0 +1,39 @@
1
+ /*
2
+ * Copyright (c) 2015-present, Vitaly Tomilov
3
+ *
4
+ * See the LICENSE file at the top-level directory of this distribution
5
+ * for licensing information.
6
+ *
7
+ * Removal or modification of this copyright notice is prohibited.
8
+ */
9
+
10
+ /**
11
+ * @enum {number}
12
+ * @alias queryResult
13
+ * @readonly
14
+ * @description
15
+ * **Query Result Mask**
16
+ *
17
+ * Binary mask that represents the number of rows expected from a query method,
18
+ * used by generic {@link Database#query query} method, plus {@link Database#func func}.
19
+ *
20
+ * The mask is always the last optional parameter, which defaults to `queryResult.any`.
21
+ *
22
+ * Any combination of flags is supported, except for `one + many`.
23
+ *
24
+ * The type is available from the library's root: `pgp.queryResult`.
25
+ *
26
+ * @see {@link Database#query Database.query}, {@link Database#func Database.func}
27
+ */
28
+ const queryResult = {
29
+ /** Single row is expected, to be resolved as a single row-object. */
30
+ one: 1,
31
+ /** One or more rows expected, to be resolved as an array, with at least 1 row-object. */
32
+ many: 2,
33
+ /** Expecting no rows, to be resolved with `null`. */
34
+ none: 4,
35
+ /** `many|none` - any result is expected, to be resolved with an array of rows-objects. */
36
+ any: 6
37
+ };
38
+
39
+ module.exports = {queryResult};
package/lib/query.js ADDED
@@ -0,0 +1,273 @@
1
+ /*
2
+ * Copyright (c) 2015-present, Vitaly Tomilov
3
+ *
4
+ * See the LICENSE file at the top-level directory of this distribution
5
+ * for licensing information.
6
+ *
7
+ * Removal or modification of this copyright notice is prohibited.
8
+ */
9
+
10
+ const {Events} = require('./events');
11
+ const {QueryFile} = require('./query-file');
12
+ const {ServerFormatting, PreparedStatement, ParameterizedQuery} = require('./types');
13
+ const {SpecialQuery} = require('./special-query');
14
+ const {queryResult} = require('./query-result');
15
+
16
+ const npm = {
17
+ util: require('util'),
18
+ utils: require('./utils'),
19
+ formatting: require('./formatting'),
20
+ errors: require('./errors'),
21
+ stream: require('./stream').stream,
22
+ text: require('./text')
23
+ };
24
+
25
+ const QueryResultError = npm.errors.QueryResultError,
26
+ InternalError = npm.utils.InternalError,
27
+ qrec = npm.errors.queryResultErrorCode;
28
+
29
+ const badMask = queryResult.one | queryResult.many; // unsupported combination bit-mask;
30
+
31
+ //////////////////////////////
32
+ // Generic query method;
33
+ function $query(ctx, query, values, qrm) {
34
+
35
+ const special = qrm instanceof SpecialQuery && qrm;
36
+
37
+ if (special && special.isStream) {
38
+ return npm.stream.call(this, ctx, query, values);
39
+ }
40
+
41
+ const opt = ctx.options,
42
+ capSQL = opt.capSQL;
43
+
44
+ let error, entityType, queryFilePath,
45
+ pgFormatting = opt.pgFormatting,
46
+ params = pgFormatting ? values : undefined;
47
+
48
+ if (typeof query === 'function') {
49
+ try {
50
+ query = npm.formatting.resolveFunc(query, values);
51
+ } catch (e) {
52
+ error = e;
53
+ params = values;
54
+ query = npm.util.inspect(query);
55
+ }
56
+ }
57
+
58
+ if (!error && !query) {
59
+ error = new TypeError(npm.text.invalidQuery);
60
+ }
61
+
62
+ if (!error && typeof query === 'object') {
63
+ if (query instanceof QueryFile) {
64
+ query.prepare();
65
+ if (query.error) {
66
+ error = query.error;
67
+ query = query.file;
68
+ } else {
69
+ queryFilePath = query.file;
70
+ query = query[QueryFile.$query];
71
+ }
72
+ } else {
73
+ if ('entity' in query) {
74
+ entityType = query.type;
75
+ query = query.entity; // query is a function name;
76
+ } else {
77
+ if (query instanceof ServerFormatting) {
78
+ pgFormatting = true;
79
+ } else {
80
+ if ('name' in query) {
81
+ query = new PreparedStatement(query);
82
+ pgFormatting = true;
83
+ } else {
84
+ if ('text' in query) {
85
+ query = new ParameterizedQuery(query);
86
+ pgFormatting = true;
87
+ }
88
+ }
89
+ }
90
+ if (query instanceof ServerFormatting && !npm.utils.isNull(values)) {
91
+ query.values = values;
92
+ }
93
+ }
94
+ }
95
+ }
96
+
97
+ if (!error) {
98
+ if (!pgFormatting && !npm.utils.isText(query)) {
99
+ const errTxt = entityType ? (entityType === 'func' ? npm.text.invalidFunction : npm.text.invalidProc) : npm.text.invalidQuery;
100
+ error = new TypeError(errTxt);
101
+ }
102
+ if (query instanceof ServerFormatting) {
103
+ const qp = query.parse();
104
+ if (qp instanceof Error) {
105
+ error = qp;
106
+ } else {
107
+ query = qp;
108
+ }
109
+ }
110
+ }
111
+
112
+ if (!error && !special) {
113
+ if (npm.utils.isNull(qrm)) {
114
+ qrm = queryResult.any; // default query result;
115
+ } else {
116
+ if (qrm !== parseInt(qrm) || (qrm & badMask) === badMask || qrm < 1 || qrm > 6) {
117
+ error = new TypeError(npm.text.invalidMask);
118
+ }
119
+ }
120
+ }
121
+
122
+ if (!error && (!pgFormatting || entityType)) {
123
+ try {
124
+ // use 'pg-promise' implementation of value formatting;
125
+ if (entityType) {
126
+ params = undefined;
127
+ query = npm.formatting.formatEntity(query, values, {capSQL, type: entityType});
128
+ } else {
129
+ query = npm.formatting.formatQuery(query, values);
130
+ }
131
+ } catch (e) {
132
+ if (entityType) {
133
+ let prefix = entityType === 'func' ? 'select * from' : 'call';
134
+ if (capSQL) {
135
+ prefix = prefix.toUpperCase();
136
+ }
137
+ query = prefix + ' ' + query + '(...)';
138
+ } else {
139
+ params = values;
140
+ }
141
+ error = e instanceof Error ? e : new npm.utils.InternalError(e);
142
+ }
143
+ }
144
+
145
+ return new Promise((resolve, reject) => {
146
+
147
+ if (notifyReject()) {
148
+ return;
149
+ }
150
+ error = Events.query(opt, getContext());
151
+ if (notifyReject()) {
152
+ return;
153
+ }
154
+ try {
155
+ const start = Date.now();
156
+ ctx.db.client.query(query, params, (err, result) => {
157
+ let data, multiResult, lastResult = result;
158
+ if (err) {
159
+ // istanbul ignore if (auto-testing connectivity issues is too problematic)
160
+ if (npm.utils.isConnectivityError(err)) {
161
+ ctx.db.client.$connectionError = err;
162
+ }
163
+ err.query = err.query || query;
164
+ err.params = err.params || params;
165
+ error = err;
166
+ } else {
167
+ multiResult = Array.isArray(result);
168
+ if (multiResult) {
169
+ lastResult = result[result.length - 1];
170
+ for (let i = 0; i < result.length; i++) {
171
+ const r = result[i];
172
+ makeIterable(r);
173
+ error = Events.receive(opt, r.rows, r, getContext());
174
+ if (error) {
175
+ break;
176
+ }
177
+ }
178
+ } else {
179
+ makeIterable(result);
180
+ result.duration = Date.now() - start;
181
+ error = Events.receive(opt, result.rows, result, getContext());
182
+ }
183
+ }
184
+ if (!error) {
185
+ data = lastResult;
186
+ if (special) {
187
+ if (special.isMultiResult) {
188
+ data = multiResult ? result : [result]; // method .multiResult() is called
189
+ }
190
+ // else, method .result() is called
191
+ } else {
192
+ data = data.rows;
193
+ const len = data.length;
194
+ if (len) {
195
+ if (len > 1 && qrm & queryResult.one) {
196
+ // one row was expected but returned multiple;
197
+ error = new QueryResultError(qrec.multiple, lastResult, query, params);
198
+ } else {
199
+ if (!(qrm & (queryResult.one | queryResult.many))) {
200
+ // no data should have been returned;
201
+ error = new QueryResultError(qrec.notEmpty, lastResult, query, params);
202
+ } else {
203
+ if (!(qrm & queryResult.many)) {
204
+ data = data[0];
205
+ }
206
+ }
207
+ }
208
+ } else {
209
+ // no data returned;
210
+ if (qrm & queryResult.none) {
211
+ if (qrm & queryResult.one) {
212
+ data = null;
213
+ } else {
214
+ data = qrm & queryResult.many ? data : null;
215
+ }
216
+ } else {
217
+ error = new QueryResultError(qrec.noData, lastResult, query, params);
218
+ }
219
+ }
220
+ }
221
+ }
222
+
223
+ if (!notifyReject()) {
224
+ resolve(data);
225
+ }
226
+ });
227
+ } catch (e) {
228
+ // this can only happen as a result of an internal failure within node-postgres,
229
+ // like during a sudden loss of communications, which is impossible to reproduce
230
+ // automatically, so removing it from the test coverage:
231
+ // istanbul ignore next
232
+ error = e;
233
+ }
234
+
235
+ function getContext() {
236
+ let client;
237
+ if (ctx.db) {
238
+ client = ctx.db.client;
239
+ } else {
240
+ error = new Error(npm.text.looseQuery);
241
+ }
242
+ return {
243
+ client, query, params, queryFilePath, values,
244
+ dc: ctx.dc,
245
+ ctx: ctx.ctx
246
+ };
247
+ }
248
+
249
+ notifyReject();
250
+
251
+ function notifyReject() {
252
+ const context = getContext();
253
+ if (error) {
254
+ if (error instanceof InternalError) {
255
+ error = error.error;
256
+ }
257
+ Events.error(opt, error, context);
258
+ reject(error);
259
+ return true;
260
+ }
261
+ }
262
+ });
263
+ }
264
+
265
+ // Extends Result to provide iterable for the rows;
266
+ // See: https://github.com/brianc/node-postgres/pull/2861
267
+ function makeIterable(r) {
268
+ r[Symbol.iterator] = function () {
269
+ return this.rows.values();
270
+ };
271
+ }
272
+
273
+ module.exports = {query: $query};