@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.
- package/LICENSE +21 -0
- package/README.md +25 -0
- package/lib/assert.js +10 -0
- package/lib/connect.js +178 -0
- package/lib/context.js +93 -0
- package/lib/database-pool.js +115 -0
- package/lib/database.js +1643 -0
- package/lib/errors/README.md +13 -0
- package/lib/errors/index.js +51 -0
- package/lib/errors/parameterized-query-error.js +71 -0
- package/lib/errors/prepared-statement-error.js +71 -0
- package/lib/errors/query-file-error.js +75 -0
- package/lib/errors/query-result-error.js +157 -0
- package/lib/events.js +543 -0
- package/lib/formatting.js +932 -0
- package/lib/helpers/README.md +10 -0
- package/lib/helpers/column-set.js +614 -0
- package/lib/helpers/column.js +406 -0
- package/lib/helpers/index.js +75 -0
- package/lib/helpers/methods/concat.js +103 -0
- package/lib/helpers/methods/index.js +13 -0
- package/lib/helpers/methods/insert.js +151 -0
- package/lib/helpers/methods/sets.js +81 -0
- package/lib/helpers/methods/update.js +248 -0
- package/lib/helpers/methods/values.js +116 -0
- package/lib/helpers/table-name.js +175 -0
- package/lib/index.js +29 -0
- package/lib/inner-state.js +39 -0
- package/lib/main.js +394 -0
- package/lib/patterns.js +43 -0
- package/lib/query-file.js +379 -0
- package/lib/query-result.js +39 -0
- package/lib/query.js +273 -0
- package/lib/special-query.js +30 -0
- package/lib/stream.js +125 -0
- package/lib/task.js +404 -0
- package/lib/text.js +40 -0
- package/lib/tx-mode.js +194 -0
- package/lib/types/index.js +18 -0
- package/lib/types/parameterized-query.js +247 -0
- package/lib/types/prepared-statement.js +298 -0
- package/lib/types/server-formatting.js +92 -0
- package/lib/utils/README.md +13 -0
- package/lib/utils/color.js +68 -0
- package/lib/utils/index.js +199 -0
- package/lib/utils/public.js +312 -0
- package/package.json +77 -0
- package/typescript/README.md +63 -0
- package/typescript/pg-promise.d.ts +728 -0
- package/typescript/pg-subset.d.ts +359 -0
- package/typescript/tslint.json +21 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
### `helpers` namespace
|
|
2
|
+
|
|
3
|
+
This folder contains everything that's available via the [helpers] namespace, after initializing the library:
|
|
4
|
+
|
|
5
|
+
```js
|
|
6
|
+
const pgp = require('pg-promise')(/*initialization options*/);
|
|
7
|
+
const helpers = pgp.helpers; // `helpers` namespace
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
[helpers]:http://vitaly-t.github.io/pg-promise/helpers.html
|
|
@@ -0,0 +1,614 @@
|
|
|
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 {assert} = require('../assert');
|
|
12
|
+
const {TableName} = require('./table-name');
|
|
13
|
+
const {Column} = require('./column');
|
|
14
|
+
|
|
15
|
+
const npm = {
|
|
16
|
+
os: require('os'),
|
|
17
|
+
utils: require('../utils'),
|
|
18
|
+
formatting: require('../formatting')
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @class helpers.ColumnSet
|
|
23
|
+
* @description
|
|
24
|
+
* Performance-optimized, read-only structure with query-formatting columns.
|
|
25
|
+
*
|
|
26
|
+
* In order to avail from performance optimization provided by this class, it should be created
|
|
27
|
+
* only once, statically, and then reused.
|
|
28
|
+
*
|
|
29
|
+
* @param {object|helpers.Column|array} columns
|
|
30
|
+
* Columns information object, depending on the type:
|
|
31
|
+
*
|
|
32
|
+
* - When it is a simple object, its properties are enumerated to represent both column names and property names
|
|
33
|
+
* within the source objects. See also option `inherit` that's applicable in this case.
|
|
34
|
+
*
|
|
35
|
+
* - When it is a single {@link helpers.Column Column} object, property {@link helpers.ColumnSet#columns columns} is initialized with
|
|
36
|
+
* just a single column. It is not a unique situation when only a single column is required for an update operation.
|
|
37
|
+
*
|
|
38
|
+
* - When it is an array, each element is assumed to represent details for a column. If the element is already of type {@link helpers.Column Column},
|
|
39
|
+
* it is used directly; otherwise the element is passed into {@link helpers.Column Column} constructor for initialization.
|
|
40
|
+
* On any duplicate column name (case-sensitive) it will throw {@link external:Error Error} = `Duplicate column name "name".`
|
|
41
|
+
*
|
|
42
|
+
* - When it is none of the above, it will throw {@link external:TypeError TypeError} = `Invalid parameter 'columns' specified.`
|
|
43
|
+
*
|
|
44
|
+
* @param {object} [options]
|
|
45
|
+
*
|
|
46
|
+
* @param {helpers.TableName|Table|string} [options.table]
|
|
47
|
+
* Table details.
|
|
48
|
+
*
|
|
49
|
+
* When it is a non-null value, and not a {@link helpers.TableName TableName} object, a new {@link helpers.TableName TableName} is constructed from the value.
|
|
50
|
+
*
|
|
51
|
+
* It can be used as the default for methods {@link helpers.insert insert} and {@link helpers.update update} when their parameter
|
|
52
|
+
* `table` is omitted, and for logging purposes.
|
|
53
|
+
*
|
|
54
|
+
* @param {boolean} [options.inherit = false]
|
|
55
|
+
* Use inherited properties in addition to the object's own properties.
|
|
56
|
+
*
|
|
57
|
+
* By default, only the object's own properties are enumerated for column names.
|
|
58
|
+
*
|
|
59
|
+
* @param {'error' | 'skip' | 'replace'} [options.duplicate = 'error']
|
|
60
|
+
* Strategy for processing duplicate columns:
|
|
61
|
+
* - `'error'` - throws {@link external:Error Error} = `Duplicate column name "name".` (Default)
|
|
62
|
+
* - `'skip'` - skips repeated columns (only the first will be used)
|
|
63
|
+
* - `'replace'` - replaces repeated columns (the last one will be used)
|
|
64
|
+
*
|
|
65
|
+
* @returns {helpers.ColumnSet}
|
|
66
|
+
*
|
|
67
|
+
* @see
|
|
68
|
+
*
|
|
69
|
+
* {@link helpers.ColumnSet#columns columns},
|
|
70
|
+
* {@link helpers.ColumnSet#names names},
|
|
71
|
+
* {@link helpers.ColumnSet#table table},
|
|
72
|
+
* {@link helpers.ColumnSet#variables variables} |
|
|
73
|
+
* {@link helpers.ColumnSet#assign assign},
|
|
74
|
+
* {@link helpers.ColumnSet#assignColumns assignColumns},
|
|
75
|
+
* {@link helpers.ColumnSet#extend extend},
|
|
76
|
+
* {@link helpers.ColumnSet#merge merge},
|
|
77
|
+
* {@link helpers.ColumnSet#prepare prepare}
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
*
|
|
81
|
+
* // A complex insert/update object scenario for table 'purchases' in schema 'fiscal'.
|
|
82
|
+
* // For a good performance, you should declare such objects once and then reuse them.
|
|
83
|
+
* //
|
|
84
|
+
* // Column Requirements:
|
|
85
|
+
* //
|
|
86
|
+
* // 1. Property 'id' is only to be used for a WHERE condition in updates
|
|
87
|
+
* // 2. Property 'list' needs to be formatted as a csv
|
|
88
|
+
* // 3. Property 'code' is to be used as raw text, and to be defaulted to 0 when the
|
|
89
|
+
* // property is missing in the source object
|
|
90
|
+
* // 4. Property 'log' is a JSON object with 'log-entry' for the column name
|
|
91
|
+
* // 5. Property 'data' requires SQL type casting '::int[]'
|
|
92
|
+
* // 6. Property 'amount' needs to be set to 100, if it is 0
|
|
93
|
+
* // 7. Property 'total' must be skipped during updates, if 'amount' was 0, plus its
|
|
94
|
+
* // column name is 'total-val'
|
|
95
|
+
* const {ColumnSet} = pgp.helpers;
|
|
96
|
+
*
|
|
97
|
+
* const cs = new ColumnSet([
|
|
98
|
+
* '?id', // ColumnConfig equivalent: {name: 'id', cnd: true}
|
|
99
|
+
* 'list:csv', // ColumnConfig equivalent: {name: 'list', mod: ':csv'}
|
|
100
|
+
* {
|
|
101
|
+
* name: 'code',
|
|
102
|
+
* mod: '^', // format as raw text
|
|
103
|
+
* def: 0 // default to 0 when the property doesn't exist
|
|
104
|
+
* },
|
|
105
|
+
* {
|
|
106
|
+
* name: 'log-entry',
|
|
107
|
+
* prop: 'log',
|
|
108
|
+
* mod: ':json' // format as JSON
|
|
109
|
+
* },
|
|
110
|
+
* {
|
|
111
|
+
* name: 'data',
|
|
112
|
+
* cast: 'int[]' // use SQL type casting '::int[]'
|
|
113
|
+
* },
|
|
114
|
+
* {
|
|
115
|
+
* name: 'amount',
|
|
116
|
+
* init(col) {
|
|
117
|
+
* // set to 100, if the value is 0:
|
|
118
|
+
* return col.value === 0 ? 100 : col.value;
|
|
119
|
+
* }
|
|
120
|
+
* },
|
|
121
|
+
* {
|
|
122
|
+
* name: 'total-val',
|
|
123
|
+
* prop: 'total',
|
|
124
|
+
* skip(col) {
|
|
125
|
+
* // skip from updates, if 'amount' is 0:
|
|
126
|
+
* return col.source.amount === 0;
|
|
127
|
+
* }
|
|
128
|
+
* }
|
|
129
|
+
* ], {table: {table: 'purchases', schema: 'fiscal'}});
|
|
130
|
+
*
|
|
131
|
+
* // Alternatively, you could take the table declaration out:
|
|
132
|
+
* // const table = new pgp.helpers.TableName('purchases', 'fiscal');
|
|
133
|
+
*
|
|
134
|
+
* console.log(cs); // console output for the object:
|
|
135
|
+
* //=>
|
|
136
|
+
* // ColumnSet {
|
|
137
|
+
* // table: "fiscal"."purchases"
|
|
138
|
+
* // columns: [
|
|
139
|
+
* // Column {
|
|
140
|
+
* // name: "id"
|
|
141
|
+
* // cnd: true
|
|
142
|
+
* // }
|
|
143
|
+
* // Column {
|
|
144
|
+
* // name: "list"
|
|
145
|
+
* // mod: ":csv"
|
|
146
|
+
* // }
|
|
147
|
+
* // Column {
|
|
148
|
+
* // name: "code"
|
|
149
|
+
* // mod: "^"
|
|
150
|
+
* // def: 0
|
|
151
|
+
* // }
|
|
152
|
+
* // Column {
|
|
153
|
+
* // name: "log-entry"
|
|
154
|
+
* // prop: "log"
|
|
155
|
+
* // mod: ":json"
|
|
156
|
+
* // }
|
|
157
|
+
* // Column {
|
|
158
|
+
* // name: "data"
|
|
159
|
+
* // cast: "int[]"
|
|
160
|
+
* // }
|
|
161
|
+
* // Column {
|
|
162
|
+
* // name: "amount"
|
|
163
|
+
* // init: [Function]
|
|
164
|
+
* // }
|
|
165
|
+
* // Column {
|
|
166
|
+
* // name: "total-val"
|
|
167
|
+
* // prop: "total"
|
|
168
|
+
* // skip: [Function]
|
|
169
|
+
* // }
|
|
170
|
+
* // ]
|
|
171
|
+
* // }
|
|
172
|
+
*/
|
|
173
|
+
class ColumnSet extends InnerState {
|
|
174
|
+
|
|
175
|
+
constructor(columns, options) {
|
|
176
|
+
super();
|
|
177
|
+
|
|
178
|
+
if (!columns || typeof columns !== 'object') {
|
|
179
|
+
throw new TypeError('Invalid parameter \'columns\' specified.');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
options = assert(options, ['table', 'inherit', 'duplicate']);
|
|
183
|
+
|
|
184
|
+
if (!npm.utils.isNull(options.table)) {
|
|
185
|
+
this.table = (options.table instanceof TableName) ? options.table : new TableName(options.table);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* @name helpers.ColumnSet#table
|
|
190
|
+
* @type {helpers.TableName}
|
|
191
|
+
* @readonly
|
|
192
|
+
* @description
|
|
193
|
+
* Destination table. It can be specified for two purposes:
|
|
194
|
+
*
|
|
195
|
+
* - **primary:** to be used as the default table when it is omitted during a call into methods {@link helpers.insert insert} and {@link helpers.update update}
|
|
196
|
+
* - **secondary:** to be automatically written into the console (for logging purposes).
|
|
197
|
+
*/
|
|
198
|
+
|
|
199
|
+
this.columns = [];
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* @name helpers.ColumnSet#columns
|
|
203
|
+
* @type helpers.Column[]
|
|
204
|
+
* @readonly
|
|
205
|
+
* @description
|
|
206
|
+
* Array of {@link helpers.Column Column} objects.
|
|
207
|
+
*/
|
|
208
|
+
if (Array.isArray(columns)) {
|
|
209
|
+
const duplicate = options.duplicate || 'error';
|
|
210
|
+
columns.forEach(c => {
|
|
211
|
+
const col = (c instanceof Column) ? c : new Column(c);
|
|
212
|
+
const idx = this.columns.findIndex(a => a.name === col.name);
|
|
213
|
+
if (idx === -1) {
|
|
214
|
+
this.columns.push(col);
|
|
215
|
+
} else {
|
|
216
|
+
if (duplicate === 'replace') {
|
|
217
|
+
this.columns[idx] = col;
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
if (duplicate !== 'skip') {
|
|
221
|
+
throw new Error(`Duplicate column name "${col.name}".`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
} else {
|
|
226
|
+
if (columns instanceof Column) {
|
|
227
|
+
this.columns = [columns];
|
|
228
|
+
} else {
|
|
229
|
+
for (const name in columns) {
|
|
230
|
+
if (options.inherit || Object.prototype.hasOwnProperty.call(columns, name)) {
|
|
231
|
+
this.columns.push(new Column(name));
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
Object.freeze(this.columns);
|
|
238
|
+
Object.freeze(this);
|
|
239
|
+
|
|
240
|
+
this.extendState({
|
|
241
|
+
names: undefined,
|
|
242
|
+
variables: undefined,
|
|
243
|
+
updates: undefined,
|
|
244
|
+
isSimple: true
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
for (let i = 0; i < this.columns.length; i++) {
|
|
248
|
+
const c = this.columns[i];
|
|
249
|
+
// ColumnSet is simple when the source objects require no preparation,
|
|
250
|
+
// and should be used directly:
|
|
251
|
+
if (c.prop || c.init || 'def' in c) {
|
|
252
|
+
this._inner.isSimple = false;
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* @name helpers.ColumnSet#names
|
|
260
|
+
* @type string
|
|
261
|
+
* @readonly
|
|
262
|
+
* @description
|
|
263
|
+
* Returns a string - comma-separated list of all column names, properly escaped.
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* const cs = new ColumnSet(['id^', {name: 'cells', cast: 'int[]'}, 'doc:json']);
|
|
267
|
+
* console.log(cs.names);
|
|
268
|
+
* //=> "id","cells","doc"
|
|
269
|
+
*/
|
|
270
|
+
get names() {
|
|
271
|
+
const _i = this._inner;
|
|
272
|
+
if (!_i.names) {
|
|
273
|
+
_i.names = this.columns.map(c => c.escapedName).join();
|
|
274
|
+
}
|
|
275
|
+
return _i.names;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* @name helpers.ColumnSet#variables
|
|
280
|
+
* @type string
|
|
281
|
+
* @readonly
|
|
282
|
+
* @description
|
|
283
|
+
* Returns a string - formatting template for all column values.
|
|
284
|
+
*
|
|
285
|
+
* @see {@link helpers.ColumnSet#assign assign}
|
|
286
|
+
*
|
|
287
|
+
* @example
|
|
288
|
+
* const cs = new ColumnSet(['id^', {name: 'cells', cast: 'int[]'}, 'doc:json']);
|
|
289
|
+
* console.log(cs.variables);
|
|
290
|
+
* //=> ${id^},${cells}::int[],${doc:json}
|
|
291
|
+
*/
|
|
292
|
+
get variables() {
|
|
293
|
+
const _i = this._inner;
|
|
294
|
+
if (!_i.variables) {
|
|
295
|
+
_i.variables = this.columns.map(c => c.variable + c.castText).join();
|
|
296
|
+
}
|
|
297
|
+
return _i.variables;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* @method helpers.ColumnSet#assign
|
|
304
|
+
* @description
|
|
305
|
+
* Returns a formatting template of SET assignments, either generic or for a single object.
|
|
306
|
+
*
|
|
307
|
+
* The method is optimized to cache the output string when there are no columns that can be skipped dynamically.
|
|
308
|
+
*
|
|
309
|
+
* This method is primarily for internal use, that's why it does not validate the input.
|
|
310
|
+
*
|
|
311
|
+
* @param {object} [options]
|
|
312
|
+
* Assignment/formatting options.
|
|
313
|
+
*
|
|
314
|
+
* @param {object} [options.source]
|
|
315
|
+
* Source - a single object that contains values for columns.
|
|
316
|
+
*
|
|
317
|
+
* The object is only necessary to correctly apply the logic of skipping columns dynamically, based on the source data
|
|
318
|
+
* and the rules defined in the {@link helpers.ColumnSet ColumnSet}. If, however, you do not care about that, then you do not need to specify any object.
|
|
319
|
+
*
|
|
320
|
+
* Note that even if you do not specify the object, the columns marked as conditional (`cnd: true`) will always be skipped.
|
|
321
|
+
*
|
|
322
|
+
* @param {string} [options.prefix]
|
|
323
|
+
* In cases where needed, an alias prefix to be added before each column.
|
|
324
|
+
*
|
|
325
|
+
* @returns {string}
|
|
326
|
+
* Comma-separated list of variable-to-column assignments.
|
|
327
|
+
*
|
|
328
|
+
* @see {@link helpers.ColumnSet#variables variables}
|
|
329
|
+
*
|
|
330
|
+
* @example
|
|
331
|
+
*
|
|
332
|
+
* const cs = new pgp.helpers.ColumnSet([
|
|
333
|
+
* '?first', // = {name: 'first', cnd: true}
|
|
334
|
+
* 'second:json',
|
|
335
|
+
* {name: 'third', mod: ':raw', cast: 'text'}
|
|
336
|
+
* ]);
|
|
337
|
+
*
|
|
338
|
+
* cs.assign();
|
|
339
|
+
* //=> "second"=${second:json},"third"=${third:raw}::text
|
|
340
|
+
*
|
|
341
|
+
* cs.assign({prefix: 'a b c'});
|
|
342
|
+
* //=> "a b c"."second"=${second:json},"a b c"."third"=${third:raw}::text
|
|
343
|
+
*/
|
|
344
|
+
ColumnSet.prototype.assign = function (options) {
|
|
345
|
+
const _i = this._inner;
|
|
346
|
+
const hasPrefix = options && options.prefix && typeof options.prefix === 'string';
|
|
347
|
+
if (_i.updates && !hasPrefix) {
|
|
348
|
+
return _i.updates;
|
|
349
|
+
}
|
|
350
|
+
let dynamic = hasPrefix;
|
|
351
|
+
const hasSource = options && options.source && typeof options.source === 'object';
|
|
352
|
+
let list = this.columns.filter(c => {
|
|
353
|
+
if (c.cnd) {
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
if (c.skip) {
|
|
357
|
+
dynamic = true;
|
|
358
|
+
if (hasSource) {
|
|
359
|
+
const a = colDesc(c, options.source);
|
|
360
|
+
if (c.skip.call(options.source, a)) {
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return true;
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
const prefix = hasPrefix ? npm.formatting.as.alias(options.prefix) + '.' : '';
|
|
369
|
+
list = list.map(c => prefix + c.escapedName + '=' + c.variable + c.castText).join();
|
|
370
|
+
|
|
371
|
+
if (!dynamic) {
|
|
372
|
+
_i.updates = list;
|
|
373
|
+
}
|
|
374
|
+
return list;
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* @method helpers.ColumnSet#assignColumns
|
|
379
|
+
* @description
|
|
380
|
+
* Generates assignments for all columns in the set, with support for aliases and column-skipping logic.
|
|
381
|
+
* Aliases are set by using method {@link formatting.alias as.alias}.
|
|
382
|
+
*
|
|
383
|
+
* @param {{}} [options]
|
|
384
|
+
* Optional Parameters.
|
|
385
|
+
*
|
|
386
|
+
* @param {string} [options.from]
|
|
387
|
+
* Alias for the source columns.
|
|
388
|
+
*
|
|
389
|
+
* @param {string} [options.to]
|
|
390
|
+
* Alias for the destination columns.
|
|
391
|
+
*
|
|
392
|
+
* @param {string | Array<string> | function} [options.skip]
|
|
393
|
+
* Name(s) of the column(s) to be skipped (case-sensitive). It can be either a single string or an array of strings.
|
|
394
|
+
*
|
|
395
|
+
* It can also be a function - iterator, to be called for every column, passing in {@link helpers.Column Column} as
|
|
396
|
+
* `this` context, and plus as a single parameter. The function would return a truthy value for every column that needs to be skipped.
|
|
397
|
+
*
|
|
398
|
+
* @returns {string}
|
|
399
|
+
* A string of comma-separated column assignments.
|
|
400
|
+
*
|
|
401
|
+
* @example
|
|
402
|
+
*
|
|
403
|
+
* const cs = new ColumnSet(['id', 'city', 'street']);
|
|
404
|
+
*
|
|
405
|
+
* cs.assignColumns({from: 'EXCLUDED', skip: 'id'})
|
|
406
|
+
* //=> "city"=EXCLUDED."city","street"=EXCLUDED."street"
|
|
407
|
+
*
|
|
408
|
+
* @example
|
|
409
|
+
*
|
|
410
|
+
* const cs = new ColumnSet(['?id', 'city', 'street']);
|
|
411
|
+
*
|
|
412
|
+
* cs.assignColumns({from: 'source', to: 'target', skip: c => c.cnd})
|
|
413
|
+
* //=> target."city"=source."city",target."street"=source."street"
|
|
414
|
+
*
|
|
415
|
+
*/
|
|
416
|
+
ColumnSet.prototype.assignColumns = function (options) {
|
|
417
|
+
options = assert(options, ['from', 'to', 'skip']);
|
|
418
|
+
const skip = (typeof options.skip === 'string' && [options.skip]) || ((Array.isArray(options.skip) || typeof options.skip === 'function') && options.skip);
|
|
419
|
+
const from = (typeof options.from === 'string' && options.from && (npm.formatting.as.alias(options.from) + '.')) || '';
|
|
420
|
+
const to = (typeof options.to === 'string' && options.to && (npm.formatting.as.alias(options.to) + '.')) || '';
|
|
421
|
+
const iterator = typeof skip === 'function' ? c => !skip.call(c, c) : c => skip.indexOf(c.name) === -1;
|
|
422
|
+
const cols = skip ? this.columns.filter(iterator) : this.columns;
|
|
423
|
+
return cols.map(c => to + c.escapedName + '=' + from + c.escapedName).join();
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* @method helpers.ColumnSet#extend
|
|
428
|
+
* @description
|
|
429
|
+
* Creates a new {@link helpers.ColumnSet ColumnSet}, by joining the two sets of columns.
|
|
430
|
+
*
|
|
431
|
+
* If the two sets contain a column with the same `name` (case-sensitive), an error is thrown (unless `options.skip` is set to `true`).
|
|
432
|
+
*
|
|
433
|
+
* @param {helpers.Column|helpers.ColumnSet|array} columns
|
|
434
|
+
* Columns to be appended, of the same type as parameter `columns` during {@link helpers.ColumnSet ColumnSet} construction, except:
|
|
435
|
+
* - it can also be of type {@link helpers.ColumnSet ColumnSet}
|
|
436
|
+
* - it cannot be a simple object (properties enumeration is not supported here)
|
|
437
|
+
*
|
|
438
|
+
* @param {object} [options]
|
|
439
|
+
*
|
|
440
|
+
* @param {boolean} [options.skip = false]
|
|
441
|
+
* When set to `true`, it skips duplicate columns (by default, it throws an error).
|
|
442
|
+
*
|
|
443
|
+
* @returns {helpers.ColumnSet}
|
|
444
|
+
* New {@link helpers.ColumnSet ColumnSet} object with the extended/concatenated list of columns.
|
|
445
|
+
*
|
|
446
|
+
* @see
|
|
447
|
+
* {@link helpers.Column Column},
|
|
448
|
+
* {@link helpers.ColumnSet#merge merge}
|
|
449
|
+
*
|
|
450
|
+
* @example
|
|
451
|
+
*
|
|
452
|
+
* const pgp = require('pg-promise')();
|
|
453
|
+
*
|
|
454
|
+
* const cs = new pgp.helpers.ColumnSet(['one', 'two'], {table: 'my-table'});
|
|
455
|
+
* console.log(cs);
|
|
456
|
+
* //=>
|
|
457
|
+
* // ColumnSet {
|
|
458
|
+
* // table: "my-table"
|
|
459
|
+
* // columns: [
|
|
460
|
+
* // Column {
|
|
461
|
+
* // name: "one"
|
|
462
|
+
* // }
|
|
463
|
+
* // Column {
|
|
464
|
+
* // name: "two"
|
|
465
|
+
* // }
|
|
466
|
+
* // ]
|
|
467
|
+
* // }
|
|
468
|
+
* const csExtended = cs.extend(['three']);
|
|
469
|
+
* console.log(csExtended);
|
|
470
|
+
* //=>
|
|
471
|
+
* // ColumnSet {
|
|
472
|
+
* // table: "my-table"
|
|
473
|
+
* // columns: [
|
|
474
|
+
* // Column {
|
|
475
|
+
* // name: "one"
|
|
476
|
+
* // }
|
|
477
|
+
* // Column {
|
|
478
|
+
* // name: "two"
|
|
479
|
+
* // }
|
|
480
|
+
* // Column {
|
|
481
|
+
* // name: "three"
|
|
482
|
+
* // }
|
|
483
|
+
* // ]
|
|
484
|
+
* // }
|
|
485
|
+
*/
|
|
486
|
+
ColumnSet.prototype.extend = function (columns, options) {
|
|
487
|
+
options = assert(options, ['skip']);
|
|
488
|
+
const duplicate = options.skip ? 'skip' : 'error';
|
|
489
|
+
const cs = columns instanceof ColumnSet ? columns : new ColumnSet(columns, {duplicate});
|
|
490
|
+
const joinedCols = this.columns.concat(...cs.columns);
|
|
491
|
+
return new ColumnSet(joinedCols, {table: this.table, duplicate});
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* @method helpers.ColumnSet#merge
|
|
496
|
+
* @description
|
|
497
|
+
* Creates a new {@link helpers.ColumnSet ColumnSet}, by joining the two sets of columns.
|
|
498
|
+
*
|
|
499
|
+
* Items in `columns` with the same `name` (case-sensitive) override the original columns.
|
|
500
|
+
*
|
|
501
|
+
* @param {helpers.Column|helpers.ColumnSet|array} columns
|
|
502
|
+
* Columns to be appended, of the same type as parameter `columns` during {@link helpers.ColumnSet ColumnSet} construction, except:
|
|
503
|
+
* - it can also be of type {@link helpers.ColumnSet ColumnSet}
|
|
504
|
+
* - it cannot be a simple object (properties enumeration is not supported here)
|
|
505
|
+
*
|
|
506
|
+
* @see
|
|
507
|
+
* {@link helpers.Column Column},
|
|
508
|
+
* {@link helpers.ColumnSet#extend extend}
|
|
509
|
+
*
|
|
510
|
+
* @returns {helpers.ColumnSet}
|
|
511
|
+
* New {@link helpers.ColumnSet ColumnSet} object with the merged list of columns.
|
|
512
|
+
*
|
|
513
|
+
* @example
|
|
514
|
+
*
|
|
515
|
+
* const pgp = require('pg-promise')();
|
|
516
|
+
* const {ColumnSet} = pgp.helpers;
|
|
517
|
+
*
|
|
518
|
+
* const cs = new ColumnSet(['?one', 'two:json'], {table: 'my-table'});
|
|
519
|
+
* console.log(cs);
|
|
520
|
+
* //=>
|
|
521
|
+
* // ColumnSet {
|
|
522
|
+
* // table: "my-table"
|
|
523
|
+
* // columns: [
|
|
524
|
+
* // Column {
|
|
525
|
+
* // name: "one"
|
|
526
|
+
* // cnd: true
|
|
527
|
+
* // }
|
|
528
|
+
* // Column {
|
|
529
|
+
* // name: "two"
|
|
530
|
+
* // mod: ":json"
|
|
531
|
+
* // }
|
|
532
|
+
* // ]
|
|
533
|
+
* // }
|
|
534
|
+
* const csMerged = cs.merge(['two', 'three^']);
|
|
535
|
+
* console.log(csMerged);
|
|
536
|
+
* //=>
|
|
537
|
+
* // ColumnSet {
|
|
538
|
+
* // table: "my-table"
|
|
539
|
+
* // columns: [
|
|
540
|
+
* // Column {
|
|
541
|
+
* // name: "one"
|
|
542
|
+
* // cnd: true
|
|
543
|
+
* // }
|
|
544
|
+
* // Column {
|
|
545
|
+
* // name: "two"
|
|
546
|
+
* // }
|
|
547
|
+
* // Column {
|
|
548
|
+
* // name: "three"
|
|
549
|
+
* // mod: "^"
|
|
550
|
+
* // }
|
|
551
|
+
* // ]
|
|
552
|
+
* // }
|
|
553
|
+
*
|
|
554
|
+
*/
|
|
555
|
+
ColumnSet.prototype.merge = function (columns) {
|
|
556
|
+
const duplicate = 'replace';
|
|
557
|
+
const cs = columns instanceof ColumnSet ? columns : new ColumnSet(columns, {duplicate});
|
|
558
|
+
const joinedCols = this.columns.concat(...cs.columns);
|
|
559
|
+
return new ColumnSet(joinedCols, {table: this.table, duplicate});
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* @method helpers.ColumnSet#prepare
|
|
564
|
+
* @description
|
|
565
|
+
* Prepares a source object to be formatted, by cloning it and applying the rules as set by the
|
|
566
|
+
* columns configuration.
|
|
567
|
+
*
|
|
568
|
+
* This method is primarily for internal use, that's why it does not validate the input parameters.
|
|
569
|
+
*
|
|
570
|
+
* @param {object} source
|
|
571
|
+
* The source object to be prepared, if required.
|
|
572
|
+
*
|
|
573
|
+
* It must be a non-`null` object, which the method does not validate, as it is
|
|
574
|
+
* intended primarily for internal use by the library.
|
|
575
|
+
*
|
|
576
|
+
* @returns {object}
|
|
577
|
+
* When the object needs to be prepared, the method returns a clone of the source object,
|
|
578
|
+
* with all properties and values set according to the columns configuration.
|
|
579
|
+
*
|
|
580
|
+
* When the object does not need to be prepared, the original object is returned.
|
|
581
|
+
*/
|
|
582
|
+
ColumnSet.prototype.prepare = function (source) {
|
|
583
|
+
if (this._inner.isSimple) {
|
|
584
|
+
return source; // a simple ColumnSet requires no object preparation;
|
|
585
|
+
}
|
|
586
|
+
const target = {};
|
|
587
|
+
this.columns.forEach(c => {
|
|
588
|
+
const a = colDesc(c, source);
|
|
589
|
+
if (c.init) {
|
|
590
|
+
target[a.name] = c.init.call(source, a);
|
|
591
|
+
} else {
|
|
592
|
+
if (a.exists || 'def' in c) {
|
|
593
|
+
target[a.name] = a.value;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
});
|
|
597
|
+
return target;
|
|
598
|
+
};
|
|
599
|
+
|
|
600
|
+
function colDesc(column, source) {
|
|
601
|
+
const a = {
|
|
602
|
+
source,
|
|
603
|
+
name: column.prop || column.name
|
|
604
|
+
};
|
|
605
|
+
a.exists = a.name in source;
|
|
606
|
+
if (a.exists) {
|
|
607
|
+
a.value = source[a.name];
|
|
608
|
+
} else {
|
|
609
|
+
a.value = 'def' in column ? column.def : undefined;
|
|
610
|
+
}
|
|
611
|
+
return a;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
module.exports = {ColumnSet};
|