@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
package/lib/events.js ADDED
@@ -0,0 +1,543 @@
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 {ColorConsole} = require('./utils/color');
11
+
12
+ const npm = {
13
+ main: require('./'),
14
+ utils: require('./utils')
15
+ };
16
+
17
+ /////////////////////////////////
18
+ // Client notification helpers;
19
+ class Events {
20
+
21
+ /**
22
+ * @event connect
23
+ * @description
24
+ * Global notification of acquiring a new database connection from the connection pool, i.e. a virtual connection.
25
+ *
26
+ * However, for direct calls to method {@link Database#connect Database.connect} with parameter `{direct: true}`,
27
+ * this event represents a physical connection.
28
+ *
29
+ * The library will suppress any error thrown by the handler and write it into the console.
30
+ *
31
+ * @param {{}} e Event Properties
32
+ *
33
+ * @param {external:Client} e.client
34
+ * $[pg.Client] object that represents the connection.
35
+ *
36
+ * @param {*} e.dc
37
+ * Database Context that was used when creating the database object (see {@link Database}).
38
+ *
39
+ * @param {number} e.useCount
40
+ * Number of times the connection has been previously used, starting with 0, for a freshly
41
+ * allocated physical connection.
42
+ *
43
+ * This parameter is always 0 for direct connections (created by calling {@link Database#connect Database.connect}
44
+ * with parameter `{direct: true}`).
45
+ *
46
+ * @example
47
+ *
48
+ * const initOptions = {
49
+ *
50
+ * // pg-promise initialization options...
51
+ *
52
+ * connect(e) {
53
+ * const cp = e.client.connectionParameters;
54
+ * console.log('Connected to database:', cp.database);
55
+ * }
56
+ *
57
+ * };
58
+ */
59
+ static connect(ctx, client, useCount) {
60
+ if (typeof ctx.options.connect === 'function') {
61
+ try {
62
+ ctx.options.connect({client, dc: ctx.dc, useCount});
63
+ } catch (e) {
64
+ // have to silence errors here;
65
+ // cannot allow unhandled errors while connecting to the database,
66
+ // as it will break the connection logic;
67
+ Events.unexpected('connect', e);
68
+ }
69
+ }
70
+ }
71
+
72
+ /**
73
+ * @event disconnect
74
+ * @description
75
+ * Global notification of releasing a database connection back to the connection pool, i.e. releasing the virtual connection.
76
+ *
77
+ * However, when releasing a direct connection (created by calling {@link Database#connect Database.connect} with parameter
78
+ * `{direct: true}`), this event represents a physical disconnection.
79
+ *
80
+ * The library will suppress any error thrown by the handler and write it into the console.
81
+ *
82
+ * @param {{}} e Event Properties
83
+ *
84
+ * @param {external:Client} e.client - $[pg.Client] object that represents connection with the database.
85
+ *
86
+ * @param {*} e.dc - Database Context that was used when creating the database object (see {@link Database}).
87
+ *
88
+ * @example
89
+ *
90
+ * const initOptions = {
91
+ *
92
+ * // pg-promise initialization options...
93
+ *
94
+ * disconnect(e) {
95
+ * const cp = e.client.connectionParameters;
96
+ * console.log('Disconnecting from database:', cp.database);
97
+ * }
98
+ *
99
+ * };
100
+ */
101
+ static disconnect(ctx, client) {
102
+ if (typeof ctx.options.disconnect === 'function') {
103
+ try {
104
+ ctx.options.disconnect({client, dc: ctx.dc});
105
+ } catch (e) {
106
+ // have to silence errors here;
107
+ // cannot allow unhandled errors while disconnecting from the database,
108
+ // as it will break the disconnection logic;
109
+ Events.unexpected('disconnect', e);
110
+ }
111
+ }
112
+ }
113
+
114
+ /**
115
+ * @event query
116
+ * @description
117
+ *
118
+ * Global notification of a query that's about to execute.
119
+ *
120
+ * Notification happens just before the query execution. And if the handler throws an error, the query execution
121
+ * will be rejected with that error.
122
+ *
123
+ * @param {EventContext} e
124
+ * Event Context Object.
125
+ *
126
+ * @example
127
+ *
128
+ * const initOptions = {
129
+ *
130
+ * // pg-promise initialization options...
131
+ *
132
+ * query(e) {
133
+ * console.log('QUERY:', e.query);
134
+ * }
135
+ * };
136
+ */
137
+ static query(options, context) {
138
+ if (typeof options.query === 'function') {
139
+ try {
140
+ options.query(context);
141
+ } catch (e) {
142
+ // throwing an error during event 'query'
143
+ // will result in a reject for the request.
144
+ return e instanceof Error ? e : new npm.utils.InternalError(e);
145
+ }
146
+ }
147
+ }
148
+
149
+ /**
150
+ * @event receive
151
+ * @description
152
+ * Global notification of any data received from the database, coming from a regular query or from a stream.
153
+ *
154
+ * The event is fired before the data reaches the client, and it serves two purposes:
155
+ * - Providing selective data logging for debugging;
156
+ * - Pre-processing data before it reaches the client.
157
+ *
158
+ * **NOTES:**
159
+ * - If you alter the size of `data` directly or through the `result` object, it may affect `QueryResultMask`
160
+ * validation for regular queries, which is executed right after.
161
+ * - Any data pre-processing needs to be fast here, to avoid performance penalties.
162
+ * - If the event handler throws an error, the original request will be rejected with that error.
163
+ *
164
+ * For methods {@link Database#multi Database.multi} and {@link Database#multiResult Database.multiResult},
165
+ * this event is called for every result that's returned. And for method {@link Database#stream Database.stream},
166
+ * the event occurs for every record.
167
+ *
168
+ * @param {{}} e Event Properties
169
+ *
170
+ * @param {Array<Object>} e.data
171
+ * Array of received objects/rows.
172
+ *
173
+ * If any of those objects are modified during notification, the client will receive the modified data.
174
+ *
175
+ * @param {external:Result} e.result
176
+ * - Original $[Result] object, if the data is from a non-stream query, in which case `data = result.rows`.
177
+ * For single-query requests, $[Result] object is extended with property `duration` - number of milliseconds
178
+ * it took to send the query, execute it and get the result back.
179
+ * - It is `undefined` when the data comes from a stream (method {@link Database#stream Database.stream}).
180
+ *
181
+ * @param {EventContext} e.ctx
182
+ * Event Context Object.
183
+ *
184
+ * @example
185
+ *
186
+ * // Example below shows the fastest way to camelize all column names.
187
+ * // NOTE: The example does not do processing for nested JSON objects.
188
+ *
189
+ * const initOptions = {
190
+ *
191
+ * // pg-promise initialization options...
192
+ *
193
+ * receive(e) {
194
+ * camelizeColumns(e.data);
195
+ * }
196
+ * };
197
+ *
198
+ * function camelizeColumns(data) {
199
+ * const tmp = data[0];
200
+ * for (const prop in tmp) {
201
+ * const camel = pgp.utils.camelize(prop);
202
+ * if (!(camel in tmp)) {
203
+ * for (let i = 0; i < data.length; i++) {
204
+ * const d = data[i];
205
+ * d[camel] = d[prop];
206
+ * delete d[prop];
207
+ * }
208
+ * }
209
+ * }
210
+ * }
211
+ */
212
+ static receive(options, data, result, ctx) {
213
+ if (typeof options.receive === 'function') {
214
+ try {
215
+ options.receive({data, result, ctx});
216
+ } catch (e) {
217
+ // throwing an error during event 'receive'
218
+ // will result in a reject for the request.
219
+ return e instanceof Error ? e : new npm.utils.InternalError(e);
220
+ }
221
+ }
222
+ }
223
+
224
+ /**
225
+ * @event task
226
+ * @description
227
+ * Global notification of a task start / finish events, as executed via
228
+ * {@link Database#task Database.task} or {@link Database#taskIf Database.taskIf}.
229
+ *
230
+ * The library will suppress any error thrown by the handler and write it into the console.
231
+ *
232
+ * @param {EventContext} e
233
+ * Event Context Object.
234
+ *
235
+ * @example
236
+ *
237
+ * const initOptions = {
238
+ *
239
+ * // pg-promise initialization options...
240
+ *
241
+ * task(e) {
242
+ * if (e.ctx.finish) {
243
+ * // this is a task->finish event;
244
+ * console.log('Duration:', e.ctx.duration);
245
+ * if (e.ctx.success) {
246
+ * // e.ctx.result = resolved data;
247
+ * } else {
248
+ * // e.ctx.result = error/rejection reason;
249
+ * }
250
+ * } else {
251
+ * // this is a task->start event;
252
+ * console.log('Start Time:', e.ctx.start);
253
+ * }
254
+ * }
255
+ * };
256
+ *
257
+ */
258
+ static task(options, context) {
259
+ if (typeof options.task === 'function') {
260
+ try {
261
+ options.task(context);
262
+ } catch (e) {
263
+ // silencing the error, to avoid breaking the task;
264
+ Events.unexpected('task', e);
265
+ }
266
+ }
267
+ }
268
+
269
+ /**
270
+ * @event transact
271
+ * @description
272
+ * Global notification of a transaction start / finish events, as executed via {@link Database#tx Database.tx}
273
+ * or {@link Database#txIf Database.txIf}.
274
+ *
275
+ * The library will suppress any error thrown by the handler and write it into the console.
276
+ *
277
+ * @param {EventContext} e
278
+ * Event Context Object.
279
+ *
280
+ * @example
281
+ *
282
+ * const initOptions = {
283
+ *
284
+ * // pg-promise initialization options...
285
+ *
286
+ * transact(e) {
287
+ * if (e.ctx.finish) {
288
+ * // this is a transaction->finish event;
289
+ * console.log('Duration:', e.ctx.duration);
290
+ * if (e.ctx.success) {
291
+ * // e.ctx.result = resolved data;
292
+ * } else {
293
+ * // e.ctx.result = error/rejection reason;
294
+ * }
295
+ * } else {
296
+ * // this is a transaction->start event;
297
+ * console.log('Start Time:', e.ctx.start);
298
+ * }
299
+ * }
300
+ * };
301
+ *
302
+ */
303
+ static transact(options, context) {
304
+ if (typeof options.transact === 'function') {
305
+ try {
306
+ options.transact(context);
307
+ } catch (e) {
308
+ // silencing the error, to avoid breaking the transaction;
309
+ Events.unexpected('transact', e);
310
+ }
311
+ }
312
+ }
313
+
314
+ /**
315
+ * @event error
316
+ * @description
317
+ * Global notification of every error encountered by this library.
318
+ *
319
+ * The library will suppress any error thrown by the handler and write it into the console.
320
+ *
321
+ * @param {*} err
322
+ * The error encountered, of the same value and type as it was reported.
323
+ *
324
+ * @param {EventContext} e
325
+ * Event Context Object.
326
+ *
327
+ * @example
328
+ * const initOptions = {
329
+ *
330
+ * // pg-promise initialization options...
331
+ *
332
+ * error(err, e) {
333
+ *
334
+ * if (e.cn) {
335
+ * // this is a connection-related error
336
+ * // cn = safe connection details passed into the library:
337
+ * // if password is present, it is masked by #
338
+ * }
339
+ *
340
+ * if (e.query) {
341
+ * // query string is available
342
+ * if (e.params) {
343
+ * // query parameters are available
344
+ * }
345
+ * }
346
+ *
347
+ * if (e.ctx) {
348
+ * // occurred inside a task or transaction
349
+ * }
350
+ * }
351
+ * };
352
+ *
353
+ */
354
+ static error(options, err, context) {
355
+ if (typeof options.error === 'function') {
356
+ try {
357
+ options.error(err, context);
358
+ } catch (e) {
359
+ // have to silence errors here;
360
+ // throwing unhandled errors while handling an error
361
+ // notification is simply not acceptable.
362
+ Events.unexpected('error', e);
363
+ }
364
+ }
365
+ }
366
+
367
+ /**
368
+ * @event extend
369
+ * @description
370
+ * Extends {@link Database} protocol with custom methods and properties.
371
+ *
372
+ * Override this event to extend the existing access layer with your own functions and
373
+ * properties best suited for your application.
374
+ *
375
+ * The extension thus becomes available across all access layers:
376
+ *
377
+ * - Within the root/default database protocol;
378
+ * - Inside transactions, including nested ones;
379
+ * - Inside tasks, including nested ones.
380
+ *
381
+ * All pre-defined methods and properties are read-only, so you will get an error,
382
+ * if you try overriding them.
383
+ *
384
+ * The library will suppress any error thrown by the handler and write it into the console.
385
+ *
386
+ * @param {object} obj - Protocol object to be extended.
387
+ *
388
+ * @param {*} dc - Database Context that was used when creating the {@link Database} object.
389
+ *
390
+ * @see $[pg-promise-demo]
391
+ *
392
+ * @example
393
+ *
394
+ * // In the example below we extend the protocol with function `addImage`
395
+ * // that will insert one binary image and resolve with the new record id.
396
+ *
397
+ * const initOptions = {
398
+ *
399
+ * // pg-promise initialization options...
400
+ *
401
+ * extend(obj, dc) {
402
+ * // dc = database context;
403
+ * obj.addImage = data => {
404
+ * // adds a new image and resolves with its record id:
405
+ * return obj.one('INSERT INTO images(data) VALUES($1) RETURNING id', data, a => a.id);
406
+ * }
407
+ * }
408
+ * };
409
+ *
410
+ * @example
411
+ *
412
+ * // It is best to extend the protocol by adding whole entity repositories to it as shown in the following example.
413
+ * // For a comprehensive example see https://github.com/vitaly-t/pg-promise-demo
414
+ *
415
+ * class UsersRepository {
416
+ * constructor(rep, pgp) {
417
+ * this.rep = rep;
418
+ * this.pgp = pgp;
419
+ * }
420
+ *
421
+ * add(name) {
422
+ * return this.rep.one('INSERT INTO users(name) VALUES($1) RETURNING id', name, a => a.id);
423
+ * }
424
+ *
425
+ * remove(id) {
426
+ * return this.rep.none('DELETE FROM users WHERE id = $1', id);
427
+ * }
428
+ * }
429
+ *
430
+ * // Overriding 'extend' event;
431
+ * const initOptions = {
432
+ *
433
+ * // pg-promise initialization options...
434
+ *
435
+ * extend(obj, dc) {
436
+ * // dc = database context;
437
+ * obj.users = new UsersRepository(obj, pgp);
438
+ * // You can set different repositories based on `dc`
439
+ * }
440
+ * };
441
+ *
442
+ * // Usage example:
443
+ * db.users.add('John', true)
444
+ * .then(id => {
445
+ * // user added successfully, id = new user's id
446
+ * })
447
+ * .catch(error => {
448
+ * // failed to add the user;
449
+ * });
450
+ *
451
+ */
452
+ static extend(options, obj, dc) {
453
+ if (typeof options.extend === 'function') {
454
+ try {
455
+ options.extend.call(obj, obj, dc);
456
+ } catch (e) {
457
+ // have to silence errors here;
458
+ // the result of throwing unhandled errors while
459
+ // extending the protocol would be unpredictable.
460
+ Events.unexpected('extend', e);
461
+ }
462
+ }
463
+ }
464
+
465
+ /**
466
+ * @event unexpected
467
+ * @param {string} event - unhandled event name.
468
+ * @param {string|Error} e - unhandled error.
469
+ * @private
470
+ */
471
+ static unexpected(event, e) {
472
+ // If you should ever get here, your app is definitely broken, and you need to fix
473
+ // your event handler to prevent unhandled errors during event notifications.
474
+ //
475
+ // Console output is suppressed when running tests, to avoid polluting test output
476
+ // with error messages that are intentional and of no value to the test.
477
+
478
+ /* istanbul ignore if */
479
+ if (!npm.main.suppressErrors) {
480
+ const stack = e instanceof Error ? e.stack : new Error().stack;
481
+ ColorConsole.error(`Unexpected error in '${event}' event handler.\n${stack}\n`);
482
+ }
483
+ }
484
+ }
485
+
486
+ module.exports = {Events};
487
+
488
+ /**
489
+ * @typedef EventContext
490
+ * @description
491
+ * This common type is used for the following events: {@link event:query query}, {@link event:receive receive},
492
+ * {@link event:error error}, {@link event:task task} and {@link event:transact transact}.
493
+ *
494
+ * @property {string|object} cn
495
+ *
496
+ * Set only for event {@link event:error error}, and only when the error is connection-related.
497
+ *
498
+ * It is a safe copy of the connection string/object that was used when initializing `db` - the database instance.
499
+ *
500
+ * If the original connection contains a password, the safe copy contains it masked with symbol `#`, so the connection
501
+ * can be logged safely, without exposing the password.
502
+ *
503
+ * @property {*} dc
504
+ * Database Context that was used when creating the database object (see {@link Database}). It is set for all events.
505
+ *
506
+ * @property {string|object} query
507
+ *
508
+ * Query string/object that was passed into the query method. This property is only set during events {@link event:query query},
509
+ * {@link event:receive receive} and {@link event:error error} (only when the error is query-related).
510
+ *
511
+ * @property {external:Client} client
512
+ *
513
+ * $[pg.Client] object that represents the connection. It is set for all events, except for event {@link event:error error}
514
+ * when it is connection-related. Note that sometimes the value may be unset when the connection is lost.
515
+ *
516
+ * @property {*} params - Formatting parameters for the query.
517
+ *
518
+ * It is set only for events {@link event:query query}, {@link event:receive receive} and {@link event:error error}, and only
519
+ * when it is needed for logging. This library takes an extra step in figuring out when formatting parameters are of any value
520
+ * to the event logging:
521
+ * - when an error occurs related to the query formatting, event {@link event:error error} is sent with the property set.
522
+ * - when initialization parameter `pgFormat` is used, and all query formatting is done within the $[PG] library, events
523
+ * {@link event:query query} and {@link event:receive receive} will have this property set also, since this library no longer
524
+ * handles the query formatting.
525
+ *
526
+ * When this parameter is not set, it means one of the two things:
527
+ * - there were no parameters passed into the query method;
528
+ * - property `query` of this object already contains all the formatting values in it, so logging only the query is sufficient.
529
+ *
530
+ * @property {*} values - The original parameter values passed into the query method.
531
+ * It is set for all events.
532
+ *
533
+ * @property {string|undefined} queryFilePath
534
+ * File path of a {@link QueryFile}, if the query comes from an external SQL file.
535
+ * This is set only when the query originates from a {@link QueryFile} and only for events {@link event:query query} and {@link event:error error}.
536
+ *
537
+ * @property {TaskContext} ctx
538
+ * _Task/Transaction Context_ object.
539
+ *
540
+ * This property is always set for events {@link event:task task} and {@link event:transact transact}, while for events
541
+ * {@link event:query query}, {@link event:receive receive} and {@link event:error error} it is only set when they occur
542
+ * inside a task or transaction.
543
+ */