@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,30 @@
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 specialQueryType = {
11
+ result: 0,
12
+ multiResult: 1,
13
+ stream: 2
14
+ };
15
+
16
+ class SpecialQuery {
17
+ constructor(type) {
18
+ this.isResult = type === specialQueryType.result; // type used implicitly
19
+ this.isStream = type === specialQueryType.stream;
20
+ this.isMultiResult = type === specialQueryType.multiResult;
21
+ }
22
+ }
23
+
24
+ const cache = {
25
+ resultQuery: new SpecialQuery(specialQueryType.result),
26
+ multiResultQuery: new SpecialQuery(specialQueryType.multiResult),
27
+ streamQuery: new SpecialQuery(specialQueryType.stream)
28
+ };
29
+
30
+ module.exports = {SpecialQuery, ...cache};
package/lib/stream.js ADDED
@@ -0,0 +1,125 @@
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
+
12
+ const npm = {
13
+ utils: require('./utils'),
14
+ text: require('./text')
15
+ };
16
+
17
+ ////////////////////////////////////////////
18
+ // Streams query data into any destination,
19
+ // with the help of pg-query-stream library.
20
+ function $stream(ctx, qs, initCB) {
21
+
22
+ // istanbul ignore next:
23
+ // we do not provide code coverage for the Native Bindings specifics
24
+ if (ctx.options.pgNative) {
25
+ return Promise.reject(new Error(npm.text.nativeStreaming));
26
+ }
27
+ // Stream class was renamed again, see the following issue:
28
+ // https://github.com/brianc/node-postgres/issues/2412
29
+ if (!qs || !qs.constructor || qs.constructor.name !== 'QueryStream') {
30
+ // invalid or missing stream object;
31
+ return Promise.reject(new TypeError(npm.text.invalidStream));
32
+ }
33
+ if (qs._reading || qs._closed) {
34
+ // stream object is in the wrong state;
35
+ return Promise.reject(new Error(npm.text.invalidStreamState));
36
+ }
37
+ if (typeof initCB !== 'function') {
38
+ // parameter `initCB` must be passed as the initialization callback;
39
+ return Promise.reject(new TypeError(npm.text.invalidStreamCB));
40
+ }
41
+
42
+ let error = Events.query(ctx.options, getContext());
43
+
44
+ if (error) {
45
+ error = getError(error);
46
+ Events.error(ctx.options, error, getContext());
47
+ return Promise.reject(error);
48
+ }
49
+
50
+ const stream = ctx.db.client.query(qs);
51
+
52
+ stream.on('data', onData);
53
+ stream.on('error', onError);
54
+ stream.on('end', onEnd);
55
+
56
+ try {
57
+ initCB.call(this, stream); // the stream must be initialized during the call;
58
+ } catch (e) {
59
+ release();
60
+ error = getError(e);
61
+ Events.error(ctx.options, error, getContext());
62
+ return Promise.reject(error);
63
+ }
64
+
65
+ const start = Date.now();
66
+ let resolve, reject, nRows = 0;
67
+
68
+ function onData(data) {
69
+ nRows++;
70
+ error = Events.receive(ctx.options, [data], undefined, getContext());
71
+ if (error) {
72
+ onError(error);
73
+ }
74
+ }
75
+
76
+ function onError(e) {
77
+ release();
78
+ stream.destroy();
79
+ e = getError(e);
80
+ Events.error(ctx.options, e, getContext());
81
+ reject(e);
82
+ }
83
+
84
+ function onEnd() {
85
+ release();
86
+ resolve({
87
+ processed: nRows, // total number of rows processed;
88
+ duration: Date.now() - start // duration, in milliseconds;
89
+ });
90
+ }
91
+
92
+ function release() {
93
+ stream.removeListener('data', onData);
94
+ stream.removeListener('error', onError);
95
+ stream.removeListener('end', onEnd);
96
+ }
97
+
98
+ function getError(e) {
99
+ return e instanceof npm.utils.InternalError ? e.error : e;
100
+ }
101
+
102
+ function getContext() {
103
+ let client;
104
+ if (ctx.db) {
105
+ client = ctx.db.client;
106
+ } else {
107
+ error = new Error(npm.text.looseQuery);
108
+ }
109
+ return {
110
+ client,
111
+ dc: ctx.dc,
112
+ query: qs.cursor.text,
113
+ params: qs.cursor.values,
114
+ ctx: ctx.ctx
115
+ };
116
+ }
117
+
118
+ return new Promise((res, rej) => {
119
+ resolve = res;
120
+ reject = rej;
121
+ });
122
+
123
+ }
124
+
125
+ module.exports = {stream: $stream};
package/lib/task.js ADDED
@@ -0,0 +1,404 @@
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
+
12
+ const npm = {
13
+ spex: require('spex'),
14
+ utils: require('./utils'),
15
+ mode: require('./tx-mode'),
16
+ query: require('./query').query,
17
+ text: require('./text')
18
+ };
19
+
20
+ /**
21
+ * @interface Task
22
+ * @description
23
+ * Extends {@link Database} for an automatic connection session, with methods for executing multiple database queries.
24
+ *
25
+ * The type isn't available directly, it can only be created via methods {@link Database#task Database.task}, {@link Database#tx Database.tx},
26
+ * or their derivations.
27
+ *
28
+ * When executing more than one request at a time, one should allocate and release the connection only once,
29
+ * while executing all the required queries within the same connection session. More importantly, a transaction
30
+ * can only work within a single connection.
31
+ *
32
+ * This is an interface for tasks/transactions to implement a connection session, during which you can
33
+ * execute multiple queries against the same connection that's released automatically when the task/transaction is finished.
34
+ *
35
+ * Each task/transaction manages the connection automatically. When executed on the root {@link Database} object, the connection
36
+ * is allocated from the pool, and once the method's callback has finished, the connection is released back to the pool.
37
+ * However, when invoked inside another task or transaction, the method reuses the parent connection.
38
+ *
39
+ * @see
40
+ * {@link Task#ctx ctx},
41
+ * {@link Task#batch batch},
42
+ * {@link Task#sequence sequence},
43
+ * {@link Task#page page}
44
+ *
45
+ * @example
46
+ * db.task(t => {
47
+ * // t = task protocol context;
48
+ * // t.ctx = Task Context;
49
+ * return t.one('select * from users where id=$1', 123)
50
+ * .then(user => {
51
+ * return t.any('select * from events where login=$1', user.name);
52
+ * });
53
+ * })
54
+ * .then(events => {
55
+ * // success;
56
+ * })
57
+ * .catch(error => {
58
+ * // error;
59
+ * });
60
+ *
61
+ */
62
+ function Task(ctx, tag, isTX, config) {
63
+
64
+ /**
65
+ * @member {TaskContext} Task#ctx
66
+ * @readonly
67
+ * @description
68
+ * Task/Transaction Context object - contains individual properties for each task/transaction.
69
+ *
70
+ * @see event {@link event:query query}
71
+ *
72
+ * @example
73
+ *
74
+ * db.task(t => {
75
+ * return t.ctx; // task context object
76
+ * })
77
+ * .then(ctx => {
78
+ * console.log('Task Duration:', ctx.duration);
79
+ * });
80
+ *
81
+ * @example
82
+ *
83
+ * db.tx(t => {
84
+ * return t.ctx; // transaction context object
85
+ * })
86
+ * .then(ctx => {
87
+ * console.log('Transaction Duration:', ctx.duration);
88
+ * });
89
+ */
90
+ this.ctx = ctx.ctx = {}; // task context object;
91
+
92
+ npm.utils.addReadProp(this.ctx, 'isTX', isTX);
93
+
94
+ if ('context' in ctx) {
95
+ npm.utils.addReadProp(this.ctx, 'context', ctx.context);
96
+ }
97
+
98
+ npm.utils.addReadProp(this.ctx, 'connected', !ctx.db);
99
+ npm.utils.addReadProp(this.ctx, 'tag', tag);
100
+ npm.utils.addReadProp(this.ctx, 'dc', ctx.dc);
101
+ npm.utils.addReadProp(this.ctx, 'level', ctx.level);
102
+ npm.utils.addReadProp(this.ctx, 'inTransaction', ctx.inTransaction);
103
+
104
+ if (isTX) {
105
+ npm.utils.addReadProp(this.ctx, 'txLevel', ctx.txLevel);
106
+ }
107
+
108
+ npm.utils.addReadProp(this.ctx, 'parent', ctx.parentCtx);
109
+
110
+ // generic query method;
111
+ this.query = function (query, values, qrm) {
112
+ if (!ctx.db) {
113
+ return Promise.reject(new Error(npm.text.looseQuery));
114
+ }
115
+ return config.$npm.query.call(this, ctx, query, values, qrm);
116
+ };
117
+
118
+ /**
119
+ * @method Task#batch
120
+ * @description
121
+ * Settles a predefined array of mixed values by redirecting to method $[spex.batch].
122
+ *
123
+ * **NOTE:**
124
+ * Consider using `async/await` syntax instead, or if you must have
125
+ * pre-generated promises, then $[Promise.allSettled].
126
+ *
127
+ * @param {array} values
128
+ * @param {Object} [options]
129
+ * Optional Parameters.
130
+ * @param {function} [options.cb]
131
+ *
132
+ * @returns {external:Promise}
133
+ */
134
+ this.batch = function (values, options) {
135
+ return config.$npm.spex.batch.call(this, values, options);
136
+ };
137
+
138
+ /**
139
+ * @method Task#page
140
+ * @description
141
+ * Resolves a dynamic sequence of arrays/pages with mixed values, by redirecting to method $[spex.page].
142
+ *
143
+ * For complete method documentation see $[spex.page].
144
+ *
145
+ * @param {function} source
146
+ * @param {Object} [options]
147
+ * Optional Parameters.
148
+ * @param {function} [options.dest]
149
+ * @param {number} [options.limit=0]
150
+ *
151
+ * @returns {external:Promise}
152
+ */
153
+ this.page = function (source, options) {
154
+ return config.$npm.spex.page.call(this, source, options);
155
+ };
156
+
157
+ /**
158
+ * @method Task#sequence
159
+ * @description
160
+ * Resolves a dynamic sequence of mixed values by redirecting to method $[spex.sequence].
161
+ *
162
+ * For complete method documentation see $[spex.sequence].
163
+ *
164
+ * @param {function} source
165
+ * @param {Object} [options]
166
+ * Optional Parameters.
167
+ * @param {function} [options.dest]
168
+ * @param {number} [options.limit=0]
169
+ * @param {boolean} [options.track=false]
170
+ *
171
+ * @returns {external:Promise}
172
+ */
173
+ this.sequence = function (source, options) {
174
+ return config.$npm.spex.sequence.call(this, source, options);
175
+ };
176
+
177
+ }
178
+
179
+ /**
180
+ * @private
181
+ * @method Task.callback
182
+ * Callback invocation helper.
183
+ *
184
+ * @param ctx
185
+ * @param obj
186
+ * @param cb
187
+ * @param config
188
+ * @returns {Promise.<TResult>}
189
+ */
190
+ const callback = (ctx, obj, cb) => {
191
+
192
+ let result;
193
+
194
+ try {
195
+ if (cb.constructor.name === 'GeneratorFunction') {
196
+ throw new TypeError('ES6 generator functions are not supported!');
197
+ }
198
+ result = cb.call(obj, obj); // invoking the callback function;
199
+ } catch (err) {
200
+ Events.error(ctx.options, err, {
201
+ client: ctx.db && ctx.db.client, // the error can be due to loss of connectivity
202
+ dc: ctx.dc,
203
+ ctx: ctx.ctx
204
+ });
205
+ return Promise.reject(err); // reject with the error;
206
+ }
207
+ if (result instanceof Promise) {
208
+ return result; // result is a valid promise object;
209
+ }
210
+ return Promise.resolve(result);
211
+ };
212
+
213
+ /**
214
+ * @private
215
+ * @method Task.execute
216
+ * Executes a task.
217
+ *
218
+ * @param ctx
219
+ * @param obj
220
+ * @param isTX
221
+ * @param config
222
+ * @returns {Promise.<TResult>}
223
+ */
224
+ const execute = (ctx, obj, isTX, config) => {
225
+
226
+ // updates the task context and notifies the client;
227
+ function update(start, success, result) {
228
+ const c = ctx.ctx;
229
+ if (start) {
230
+ npm.utils.addReadProp(c, 'start', new Date());
231
+ } else {
232
+ c.finish = new Date();
233
+ c.success = success;
234
+ c.result = result;
235
+ c.duration = c.finish - c.start;
236
+ }
237
+ (isTX ? Events.transact : Events.task)(ctx.options, {
238
+ client: ctx.db && ctx.db.client, // loss of connectivity is possible at this point
239
+ dc: ctx.dc,
240
+ ctx: c
241
+ });
242
+ }
243
+
244
+ let cbData, cbReason, success,
245
+ spName; // Save-Point Name;
246
+
247
+ const capSQL = ctx.options.capSQL; // capitalize sql;
248
+
249
+ update(true);
250
+
251
+ if (isTX) {
252
+ // executing a transaction;
253
+ spName = `sp_${ctx.txLevel}_${ctx.nextTxCount}`;
254
+ return begin()
255
+ .then(() => callback(ctx, obj, ctx.cb, config)
256
+ .then(data => {
257
+ cbData = data; // save callback data;
258
+ success = true;
259
+ return commit();
260
+ }, err => {
261
+ cbReason = err; // save callback failure reason;
262
+ return rollback();
263
+ })
264
+ .then(() => {
265
+ if (success) {
266
+ update(false, true, cbData);
267
+ return cbData;
268
+ }
269
+ update(false, false, cbReason);
270
+ return Promise.reject(cbReason);
271
+ },
272
+ err => {
273
+ // either COMMIT or ROLLBACK has failed, which is impossible
274
+ // to replicate in a test environment, so skipping from the test;
275
+ // istanbul ignore next:
276
+ update(false, false, err);
277
+ // istanbul ignore next:
278
+ return Promise.reject(err);
279
+ }),
280
+ err => {
281
+ // BEGIN has failed, which is impossible to replicate in a test
282
+ // environment, so skipping the whole block from the test;
283
+ // istanbul ignore next:
284
+ update(false, false, err);
285
+ // istanbul ignore next:
286
+ return Promise.reject(err);
287
+ });
288
+ }
289
+
290
+ function begin() {
291
+ if (!ctx.txLevel && ctx.mode instanceof npm.mode.TransactionMode) {
292
+ return exec(ctx.mode.begin(capSQL), 'savepoint');
293
+ }
294
+ return exec('begin', 'savepoint');
295
+ }
296
+
297
+ function commit() {
298
+ return exec('commit', 'release savepoint');
299
+ }
300
+
301
+ function rollback() {
302
+ return exec('rollback', 'rollback to savepoint');
303
+ }
304
+
305
+ function exec(top, nested) {
306
+ if (ctx.txLevel) {
307
+ return obj.none((capSQL ? nested.toUpperCase() : nested) + ' ' + spName);
308
+ }
309
+ return obj.none(capSQL ? top.toUpperCase() : top);
310
+ }
311
+
312
+ // executing a task;
313
+ return callback(ctx, obj, ctx.cb, config)
314
+ .then(data => {
315
+ update(false, true, data);
316
+ return data;
317
+ })
318
+ .catch(error => {
319
+ update(false, false, error);
320
+ return Promise.reject(error);
321
+ });
322
+ };
323
+
324
+ module.exports = config => {
325
+ const npmLocal = config.$npm;
326
+ npmLocal.query = npmLocal.query || npm.query;
327
+ npmLocal.spex = npmLocal.spex || npm.spex;
328
+ return {
329
+ Task, execute, callback
330
+ };
331
+ };
332
+
333
+ /**
334
+ * @typedef TaskContext
335
+ * @description
336
+ * Task/Transaction Context used via property {@link Task#ctx ctx} inside tasks (methods {@link Database#task Database.task} and {@link Database#taskIf Database.taskIf})
337
+ * and transactions (methods {@link Database#tx Database.tx} and {@link Database#txIf Database.txIf}).
338
+ *
339
+ * Properties `context`, `connected`, `parent`, `level`, `dc`, `isTX`, `tag`, `start`, `useCount` and `serverVersion` are set just before the operation has started,
340
+ * while properties `finish`, `duration`, `success` and `result` are set immediately after the operation has finished.
341
+ *
342
+ * @property {*} context
343
+ * If the operation was invoked with a calling context - `task.call(context,...)` or `tx.call(context,...)`,
344
+ * this property is set with the context that was passed in. Otherwise, the property doesn't exist.
345
+ *
346
+ * @property {*} dc
347
+ * _Database Context_ that was passed into the {@link Database} object during construction.
348
+ *
349
+ * @property {boolean} isTX
350
+ * Indicates whether this operation is a transaction (as opposed to a regular task).
351
+ *
352
+ * @property {number} duration
353
+ * Number of milliseconds consumed by the operation.
354
+ *
355
+ * Set after the operation has finished, it is simply a shortcut for `finish - start`.
356
+ *
357
+ * @property {number} level
358
+ * Task nesting level, starting from 0, counting both regular tasks and transactions.
359
+ *
360
+ * @property {number} txLevel
361
+ * Transaction nesting level, starting from 0. Transactions on level 0 use `BEGIN/COMMIT/ROLLBACK`,
362
+ * while transactions on nested levels use the corresponding `SAVEPOINT` commands.
363
+ *
364
+ * This property exists only within the context of a transaction (`isTX = true`).
365
+ *
366
+ * @property {boolean} inTransaction
367
+ * Available in both tasks and transactions, it simplifies checking when there is a transaction
368
+ * going on either on this level or above.
369
+ *
370
+ * For example, when you want to check for a containing transaction while inside a task, and
371
+ * only start a transaction when there is none yet.
372
+ *
373
+ * @property {TaskContext} parent
374
+ * Parent task/transaction context, or `null` when it is top-level.
375
+ *
376
+ * @property {boolean} connected
377
+ * Indicates when the task/transaction acquired the connection on its own (`connected = true`), and will release it once
378
+ * the operation has finished. When the value is `false`, the operation is reusing an existing connection.
379
+ *
380
+ * @property {*} tag
381
+ * Tag value as it was passed into the task. See methods {@link Database#task task} and {@link Database#tx tx}.
382
+ *
383
+ * @property {Date} start
384
+ * Date/Time of when this operation started the execution.
385
+ *
386
+ * @property {number} useCount
387
+ * Number of times the connection has been previously used, starting with 0 for a freshly
388
+ * allocated physical connection.
389
+ *
390
+ * @property {string} serverVersion
391
+ * Version of the PostgreSQL server to which we are connected.
392
+ * Not available with $[Native Bindings].
393
+ *
394
+ * @property {Date} finish
395
+ * Once the operation has finished, this property is set to the Data/Time of when it happened.
396
+ *
397
+ * @property {boolean} success
398
+ * Once the operation has finished, this property indicates whether it was successful.
399
+ *
400
+ * @property {*} result
401
+ * Once the operation has finished, this property contains the result, depending on property `success`:
402
+ * - data resolved by the operation, if `success = true`
403
+ * - error / rejection reason, if `success = false`
404
+ */
package/lib/text.js ADDED
@@ -0,0 +1,40 @@
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
+ /* All error messages used in the module */
11
+
12
+ const streamVersion = require('../package.json')
13
+ .devDependencies['pg-query-stream'];
14
+
15
+ module.exports = {
16
+ nativeError: 'Failed to initialize Native Bindings.',
17
+
18
+ /* Database errors */
19
+ queryDisconnected: 'Cannot execute a query on a disconnected client.',
20
+ invalidQuery: 'Invalid query format.',
21
+ invalidFunction: 'Invalid function name.',
22
+ invalidProc: 'Invalid procedure name.',
23
+ invalidMask: 'Invalid Query Result Mask specified.',
24
+ looseQuery: 'Querying against a released or lost connection.',
25
+
26
+ /* result errors */
27
+ notEmpty: 'No return data was expected.',
28
+ noData: 'No data returned from the query.',
29
+ multiple: 'Multiple rows were not expected.',
30
+
31
+ /* streaming support */
32
+ nativeStreaming: 'Streaming doesn\'t work with Native Bindings.',
33
+ invalidStream: `Invalid or missing stream object: pg-query-stream >= v${streamVersion} was expected`,
34
+ invalidStreamState: 'Invalid stream state.',
35
+ invalidStreamCB: 'Invalid or missing stream initialization callback.',
36
+
37
+ /* connection errors */
38
+ poolDestroyed: 'Connection pool of the database object has been destroyed.',
39
+ clientEnd: 'Abnormal client.end() call, due to invalid code or failed server connection.'
40
+ };