pogi 2.11.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 (82) hide show
  1. package/.vscode/launch.json +35 -0
  2. package/CHANGELOG.md +277 -0
  3. package/LICENSE +21 -0
  4. package/README.md +85 -0
  5. package/docs/API/PgDb.md +218 -0
  6. package/docs/API/PgSchema.md +91 -0
  7. package/docs/API/PgTable.md +365 -0
  8. package/docs/API/QueryOptions.md +77 -0
  9. package/docs/API/condition.md +133 -0
  10. package/docs/connection.md +91 -0
  11. package/docs/css/docs.css +164 -0
  12. package/docs/executingSqlFile.md +44 -0
  13. package/docs/faq.md +15 -0
  14. package/docs/functions.md +19 -0
  15. package/docs/generatingInterfaceForTables.md +35 -0
  16. package/docs/index.md +48 -0
  17. package/docs/logger.md +40 -0
  18. package/docs/mappingDatabaseTypes.md +89 -0
  19. package/docs/notification.md +19 -0
  20. package/docs/pitfalls.md +73 -0
  21. package/docs/streams.md +68 -0
  22. package/docs/transaction.md +65 -0
  23. package/lib/bin/generateInterface.d.ts +1 -0
  24. package/lib/bin/generateInterface.js +53 -0
  25. package/lib/bin/generateInterface.js.map +1 -0
  26. package/lib/connectionOptions.d.ts +25 -0
  27. package/lib/connectionOptions.js +3 -0
  28. package/lib/connectionOptions.js.map +1 -0
  29. package/lib/index.d.ts +6 -0
  30. package/lib/index.js +10 -0
  31. package/lib/index.js.map +1 -0
  32. package/lib/pgConverters.d.ts +10 -0
  33. package/lib/pgConverters.js +66 -0
  34. package/lib/pgConverters.js.map +1 -0
  35. package/lib/pgDb.d.ts +86 -0
  36. package/lib/pgDb.js +745 -0
  37. package/lib/pgDb.js.map +1 -0
  38. package/lib/pgDbLogger.d.ts +5 -0
  39. package/lib/pgDbLogger.js +3 -0
  40. package/lib/pgDbLogger.js.map +1 -0
  41. package/lib/pgDbOperators.d.ts +113 -0
  42. package/lib/pgDbOperators.js +44 -0
  43. package/lib/pgDbOperators.js.map +1 -0
  44. package/lib/pgSchema.d.ts +16 -0
  45. package/lib/pgSchema.js +16 -0
  46. package/lib/pgSchema.js.map +1 -0
  47. package/lib/pgTable.d.ts +131 -0
  48. package/lib/pgTable.js +322 -0
  49. package/lib/pgTable.js.map +1 -0
  50. package/lib/pgUtils.d.ts +31 -0
  51. package/lib/pgUtils.js +157 -0
  52. package/lib/pgUtils.js.map +1 -0
  53. package/lib/queryAble.d.ts +76 -0
  54. package/lib/queryAble.js +330 -0
  55. package/lib/queryAble.js.map +1 -0
  56. package/lib/queryWhere.d.ts +8 -0
  57. package/lib/queryWhere.js +249 -0
  58. package/lib/queryWhere.js.map +1 -0
  59. package/mkdocs.yml +25 -0
  60. package/package.json +65 -0
  61. package/spec/resources/init.sql +122 -0
  62. package/spec/resources/throw_exception.sql +5 -0
  63. package/spec/resources/tricky.sql +13 -0
  64. package/spec/run.js +5 -0
  65. package/spec/support/jasmine.json +9 -0
  66. package/src/bin/generateInterface.ts +54 -0
  67. package/src/connectionOptions.ts +42 -0
  68. package/src/index.ts +6 -0
  69. package/src/pgConverters.ts +55 -0
  70. package/src/pgDb.ts +820 -0
  71. package/src/pgDbLogger.ts +13 -0
  72. package/src/pgDbOperators.ts +62 -0
  73. package/src/pgSchema.ts +15 -0
  74. package/src/pgTable.ts +401 -0
  75. package/src/pgUtils.ts +176 -0
  76. package/src/queryAble.ts +393 -0
  77. package/src/queryWhere.ts +326 -0
  78. package/src/test/pgDbOperatorSpec.ts +492 -0
  79. package/src/test/pgDbSpec.ts +1339 -0
  80. package/src/test/pgServiceRestartTest.ts +1500 -0
  81. package/src/tsconfig.json +33 -0
  82. package/utils_sql/lower.sql +4 -0
@@ -0,0 +1,13 @@
1
+ /**
2
+ * log will get 3 parameters:
3
+ * sql -> the query
4
+ * parameters -> parameters for the query
5
+ * poolId -> the id of the connection
6
+ *
7
+ * paramSanitizer - optional function to remove parameters from the query for security reason (e.g. email / password or similar)
8
+ */
9
+ export interface PgDbLogger {
10
+ log: Function;
11
+ error: Function;
12
+ paramSanitizer?: Function;
13
+ }
@@ -0,0 +1,62 @@
1
+ const util = require("util");
2
+
3
+ function escapeForLike(s) {
4
+ return s.replace(/([\\%_])/g,'\\$1');
5
+ }
6
+
7
+ export default {
8
+ // lowercase comparison
9
+ '=*': {operator: '=', mutator: (s:string) => s.toLocaleLowerCase(), fieldMutator: s => util.format('LOWER("%s")',s)},
10
+
11
+ // caseless contains for string
12
+ 'icontains': {operator: 'ILIKE', mutator: s => '%' + escapeForLike(s) + '%'},
13
+
14
+ // contains for array
15
+ //'acontains': value = ANY("columnName")
16
+
17
+ // basic comparison
18
+ '=': {operator: '='},
19
+ '!': {operator: '<>'},
20
+ '>': {operator: '>'},
21
+ '<': {operator: '<'},
22
+ '>=': {operator: '>='},
23
+ '<=': {operator: '<='},
24
+ '!=': {operator: '<>'},
25
+ '<>': {operator: '<>'},
26
+ 'is not': {operator: 'IS NOT'},
27
+
28
+ // free text search
29
+ '@@': {operator: '@@'}, //value can be {lang:string, query:string} or simply string (defaults to english)
30
+
31
+ // jsonb / array
32
+ '@>': {operator: '@>'}, //contains ARRAY[1,4,3] @> ARRAY[3,1] => true
33
+ '<@': {operator: '<@'}, //is contained by ARRAY[2,7] <@ ARRAY[1,7,4,2,6] => true
34
+ '&&': {operator: '&&'}, //overlap (have elements in common) ARRAY[1,4,3] && ARRAY[2,1] => true
35
+ '&&*': {operator: '&&', mutator: (s:string) => s.toLocaleLowerCase(), fieldMutator: f => util.format('LOWER("%s")', f)},
36
+
37
+ // jsonb
38
+ '?': {operator: '?'}, //exists key
39
+ '?|': {operator: '?|'}, //exists any keys
40
+ '?&': {operator: '?&'}, //exists all keys
41
+
42
+
43
+ // pattern matching
44
+ '~~': {operator: 'LIKE'},
45
+ 'like': {operator: 'LIKE'},
46
+ '!~~': {operator: 'NOT LIKE'},
47
+ 'not like': {operator: 'NOT LIKE'},
48
+ '~~*': {operator: 'ILIKE'},
49
+ 'ilike': {operator: 'ILIKE'},
50
+ '!~~*': {operator: 'NOT ILIKE'},
51
+ 'not ilike': {operator: 'NOT ILIKE'},
52
+ 'similar to': {operator: 'SIMILAR TO'},
53
+ 'not similar to': {operator: 'NOT SIMILAR TO'},
54
+ // regexp
55
+ '~': {operator: '~'},
56
+ '!~': {operator: '!~'},
57
+ '~*': {operator: '~*'}, //Matches regular expression, case insensitive
58
+ '!~*': {operator: '!~*'},
59
+ // distinct
60
+ 'is distinct from': {operator: 'IS DISTINCT FROM'},
61
+ 'is not distinct from': {operator: 'IS NOT DISTINCT FROM'}
62
+ };
@@ -0,0 +1,15 @@
1
+ import {QueryAble} from "./queryAble";
2
+ import {PgDb} from "./pgDb";
3
+ import {PgTable} from "./pgTable";
4
+
5
+ export class PgSchema extends QueryAble {
6
+ schema:PgSchema;
7
+ tables:{[name:string]:PgTable<any>} = {};
8
+ fn: {[name:string]:(...any)=>any} = {};
9
+ [name:string]:any|PgTable<any>;
10
+
11
+ constructor(public db:PgDb, public schemaName:string) {
12
+ super();
13
+ this.schema = this;
14
+ }
15
+ }
package/src/pgTable.ts ADDED
@@ -0,0 +1,401 @@
1
+ import {QueryAble, QueryOptions} from "./queryAble";
2
+ import {PgDb, FieldType} from "./pgDb";
3
+ import {PgDbLogger} from "./pgDbLogger"
4
+ import generateWhere from "./queryWhere";
5
+ import {PgSchema} from "./pgSchema";
6
+ import {pgUtils} from "./pgUtils";
7
+ import * as _ from 'lodash';
8
+ import * as stream from "stream";
9
+
10
+ const util = require('util');
11
+
12
+ export interface InsertOption {
13
+ logger?: PgDbLogger;
14
+ }
15
+
16
+ export interface Return {
17
+ return?: string[] | '*';
18
+ }
19
+
20
+ export interface UpdateDeleteOption {
21
+ skipUndefined?: boolean;
22
+ logger?: PgDbLogger;
23
+ }
24
+
25
+ export interface UpsertOption {
26
+ constraint?: string,
27
+ columns?: string[],
28
+ logger?: PgDbLogger;
29
+ }
30
+
31
+ export interface CountOption {
32
+ skipUndefined?: boolean;
33
+ logger?: PgDbLogger;
34
+ }
35
+
36
+ export interface Stream {
37
+ stream: true;
38
+ }
39
+
40
+ export interface TruncateOptions {
41
+ restartIdentity?: boolean,
42
+ cascade?: boolean,
43
+ logger?: PgDbLogger;
44
+ }
45
+
46
+ export class PgTable<T> extends QueryAble {
47
+ qualifiedName: string;
48
+ pkey: string;
49
+ db: PgDb;
50
+ fieldTypes: { [index: string]: FieldType }; //written directly
51
+
52
+ constructor(public schema: PgSchema, protected desc: { name: string, pkey?:string, schema: string }, fieldTypes = {}) {
53
+ super();
54
+ this.db = schema.db;
55
+ this.qualifiedName = util.format('"%s"."%s"', desc.schema, desc.name);
56
+ this.pkey = desc.pkey || desc.name + "_pkey"; //poor man's pkey (could be queried by why?)
57
+ this.fieldTypes = fieldTypes;
58
+ }
59
+
60
+ toString() {
61
+ return this.qualifiedName;
62
+ }
63
+
64
+ /**
65
+ * If you dont want to use the result set the options.return to false
66
+ * by default it is true. Also can set it to the fields that need to be returned,
67
+ * e.g.:
68
+ *
69
+ * let res = await table.insert([{username:'anonymous'},{username:'anonymous2'}], {return:['id']})
70
+ * res; // [{id:1},{id:2}]
71
+ *
72
+ * let res = await table.insert({username:'anonymous'}, {return:false})
73
+ * res; // void
74
+ *
75
+ * let res = await table.insert({username:'anonymous'})
76
+ * res; // {id:1, name:'anonymous', created:'...'}
77
+ *
78
+ */
79
+ async insert(records: T[], options?: InsertOption): Promise<number>
80
+ async insert(records: T, options?: InsertOption): Promise<number>
81
+ async insert(records: any, options?: any): Promise<any> {
82
+ options = options || {};
83
+
84
+ if (!records) {
85
+ throw new Error("insert should be called with data");
86
+ } else if (!Array.isArray(records)) {
87
+ records = [records];
88
+ } else if (records.length === 0) {
89
+ return 0; // just return empty arrays so bulk inserting variable-length lists is more friendly
90
+ }
91
+
92
+ let {sql, parameters} = this.getInsertQuery(records);
93
+ sql = "WITH __RESULT as ( " + sql + " RETURNING 1) SELECT SUM(1) FROM __RESULT";
94
+ let result = await this.query(sql, parameters, {logger: options.logger});
95
+ return result[0].sum;
96
+ }
97
+
98
+ async insertAndGet(records: T[], options?: InsertOption & Return): Promise<T[]>
99
+ async insertAndGet(records: T, options?: InsertOption & Return): Promise<T>
100
+ async insertAndGet(records: any, options?: InsertOption & Return): Promise<any> {
101
+ let returnSingle = false;
102
+ options = options || {};
103
+
104
+ if (!records) {
105
+ throw new Error("insert should be called with data");
106
+ } else if (!Array.isArray(records)) {
107
+ returnSingle = true;
108
+ records = [records];
109
+ } else if (records.length === 0) {
110
+ return []; // just return empty arrays so bulk inserting variable-length lists is more friendly
111
+ }
112
+
113
+ let {sql, parameters} = this.getInsertQuery(records);
114
+
115
+ sql += " RETURNING " + (options && options.return && Array.isArray(options.return) ? options.return.map(pgUtils.quoteField).join(',') : '*');
116
+
117
+ let result = await this.query(sql, parameters, {logger: options.logger});
118
+ if (options.return && options.return.length == 0) {
119
+ return new Array(returnSingle ? 1 : records.length).fill({});
120
+ }
121
+ if (returnSingle) {
122
+ return result[0];
123
+ } else {
124
+ return result;
125
+ }
126
+ };
127
+
128
+ async updateOne(conditions: { [k: string]: any }, fields: { [k: string]: any }, options?: UpdateDeleteOption): Promise<number> {
129
+ let affected = await this.update(conditions, fields, options);
130
+ if (affected > 1) {
131
+ throw new Error('More then one record has been updated!');
132
+ }
133
+ return affected;
134
+ }
135
+
136
+ async updateAndGetOne(conditions: { [k: string]: any }, fields: { [k: string]: any }, options?: UpdateDeleteOption & Return): Promise<T> {
137
+ let result = await this.updateAndGet(conditions, fields, options);
138
+ if (result.length > 1) {
139
+ throw new Error('More then one record has been updated!');
140
+ }
141
+ return result[0];
142
+ }
143
+
144
+ async update(conditions: { [k: string]: any }, fields: { [k: string]: any }, options?: UpdateDeleteOption): Promise<number> {
145
+ let {sql, parameters} = this.getUpdateQuery(conditions, fields, options);
146
+ sql = "WITH __RESULT as ( " + sql + " RETURNING 1) SELECT SUM(1) FROM __RESULT";
147
+ let res = await this.query(sql, parameters, options);
148
+ return res[0].sum;
149
+ };
150
+
151
+ async updateAndGet(conditions: { [k: string]: any }, fields: { [k: string]: any }, options?: UpdateDeleteOption & Return): Promise<T[]> {
152
+ let {sql, parameters} = this.getUpdateQuery(conditions, fields, options);
153
+ sql += " RETURNING " + (options && options.return && Array.isArray(options.return) ? options.return.map(pgUtils.quoteField).join(',') : '*');
154
+ return this.query(sql, parameters, options);
155
+ };
156
+
157
+ /**
158
+ * columnsOrConstraintName is by default the primary key
159
+ */
160
+ async upsert(record: T, options?: UpsertOption): Promise<number> {
161
+ options = options || {};
162
+ if (!record) {
163
+ throw new Error("insert should be called with data");
164
+ }
165
+
166
+ let {sql, parameters} = this.getUpsertQuery(record, options);
167
+ sql = "WITH __RESULT as ( " + sql + " RETURNING 1) SELECT SUM(1) FROM __RESULT";
168
+ let result = await this.query(sql, parameters, {logger: options.logger});
169
+ return result[0].sum;
170
+ };
171
+
172
+ /**
173
+ * columnsOrConstraintName is by default the primary key
174
+ */
175
+ async upsertAndGet(record: T, options?: UpsertOption & Return): Promise<T> {
176
+ options = options || {};
177
+ if (!record) {
178
+ throw new Error("insert should be called with data");
179
+ }
180
+
181
+ let {sql, parameters} = this.getUpsertQuery(record, options);
182
+ sql += " RETURNING " + (options && options.return && Array.isArray(options.return) ? options.return.map(pgUtils.quoteField).join(',') : '*');
183
+
184
+ let result = await this.query(sql, parameters, {logger: options.logger});
185
+
186
+ if (options.return && options.return.length == 0) {
187
+ return <T>{};
188
+ }
189
+ return result[0];
190
+ };
191
+
192
+ async delete(conditions: { [k: string]: any }, options?: UpdateDeleteOption): Promise<number> {
193
+ let {sql, parameters} = this.getDeleteQuery(conditions, options);
194
+ sql = "WITH __RESULT as ( " + sql + " RETURNING 1) SELECT SUM(1) FROM __RESULT";
195
+ let res = await this.query(sql, parameters, options);
196
+ return res[0].sum;
197
+ }
198
+
199
+ async deleteOne(conditions: { [k: string]: any }, options?: UpdateDeleteOption): Promise<number> {
200
+ let affected = await this.delete(conditions, options);
201
+ if (affected > 1) {
202
+ throw new Error('More then one record has been deleted!');
203
+ }
204
+ return affected;
205
+ }
206
+
207
+ async deleteAndGet(conditions: { [k: string]: any }, options?: UpdateDeleteOption & Return): Promise<any[]> {
208
+ options = options || {};
209
+ let {sql, parameters} = this.getDeleteQuery(conditions, options);
210
+ sql += " RETURNING " + (options && options.return && Array.isArray(options.return) ? options.return.map(pgUtils.quoteField).join(',') : '*');
211
+ return this.query(sql, parameters);
212
+ }
213
+
214
+ async deleteAndGetOne(conditions: { [k: string]: any }, options?: UpdateDeleteOption & Return): Promise<any> {
215
+ let result = await this.deleteAndGet(conditions, options);
216
+ if (result.length > 1) {
217
+ throw new Error('More then one record has been deleted!');
218
+ }
219
+ return result[0];
220
+ }
221
+
222
+ // async deleteAll(options?:UpdateDeleteOption):Promise<number> {
223
+ // let sql = util.format("DELETE FROM %s ", this.qualifiedName);
224
+ // sql = "WITH __RESULT as ( " + sql + " RETURNING 1) SELECT SUM(1) FROM __RESULT";
225
+ // let res = await this.query(sql, {logger:options.logger});
226
+ // return res[0].sum;
227
+ // }
228
+
229
+ async truncate(options?: TruncateOptions): Promise<void> {
230
+ let sql = `TRUNCATE ${this.qualifiedName}`;
231
+ if (options && options.restartIdentity) {
232
+ sql += ' RESTART IDENTITY';
233
+ }
234
+ if (options && options.cascade) {
235
+ sql += ' CASCADE';
236
+ }
237
+ await this.query(sql, null, options);
238
+ }
239
+
240
+ async find(conditions: { [k: string]: any }, options?: QueryOptions): Promise<T[]>
241
+ async find(conditions: { [k: string]: any }, options?: QueryOptions & Stream): Promise<stream.Readable>
242
+ async find(conditions: { [k: string]: any }, options?: any): Promise<any> {
243
+ options = options || {};
244
+ options.skipUndefined = options.skipUndefined === true || (options.skipUndefined === undefined && ['all', 'select'].indexOf(this.db.config.skipUndefined) > -1);
245
+ let where = _.isEmpty(conditions) ? {
246
+ where: " ",
247
+ params: null
248
+ } : generateWhere(conditions, this.fieldTypes, this.qualifiedName, 0, options.skipUndefined);
249
+ let sql = `SELECT ${pgUtils.processQueryFields(options)} FROM ${this.qualifiedName} ${where.where} ${pgUtils.processQueryOptions(options)}`;
250
+ return options.stream ? this.queryAsStream(sql, where.params, options) : this.query(sql, where.params, options);
251
+ }
252
+
253
+
254
+ async findWhere(where: string, params: any[] | {}, options?: QueryOptions): Promise<T[]>
255
+ async findWhere(where: string, params: any[] | {}, options?: QueryOptions & Stream): Promise<stream.Readable>
256
+ async findWhere(where: string, params: any, options?: any): Promise<any> {
257
+ options = options || {};
258
+ let sql = `SELECT ${pgUtils.processQueryFields(options)} FROM ${this.qualifiedName} WHERE ${where} ${pgUtils.processQueryOptions(options)}`;
259
+ return options.stream ? this.queryAsStream(sql, params, options) : this.query(sql, params, options);
260
+ }
261
+
262
+ public async findAll(options?: QueryOptions): Promise<T[]>
263
+ public async findAll(options?: QueryOptions & Stream): Promise<stream.Readable>
264
+ public async findAll(options?: any): Promise<any> {
265
+ options = options || {};
266
+ let sql = `SELECT ${pgUtils.processQueryFields(options)} FROM ${this.qualifiedName} ${pgUtils.processQueryOptions(options)}`;
267
+ return options.stream ? this.queryAsStream(sql, null, options) : this.query(sql, null, options);
268
+ }
269
+
270
+ async findOne(conditions, options?: QueryOptions): Promise<T> {
271
+ let res = await this.find(conditions, options);
272
+ if (res.length > 1) {
273
+ let logger = (options && options.logger || this.getLogger(false));
274
+ let error = new Error('More then one rows exists');
275
+ pgUtils.logError(logger, { error, sql:this.qualifiedName, params: conditions, connection: this.db.connection });
276
+ throw error;
277
+ }
278
+ return res[0];
279
+ }
280
+
281
+ async findFirst(conditions, options?: QueryOptions): Promise<T> {
282
+ options = options || {};
283
+ options.limit = 1;
284
+ let res = await this.find(conditions, options);
285
+ return res[0];
286
+ }
287
+
288
+
289
+ async count(conditions?: {}, options?: CountOption): Promise<number> {
290
+ options = options || {};
291
+ options.skipUndefined = options.skipUndefined === true || (options.skipUndefined === undefined && ['all', 'select'].indexOf(this.db.config.skipUndefined) > -1);
292
+
293
+ let where = _.isEmpty(conditions) ? {
294
+ where: " ",
295
+ params: null
296
+ } : generateWhere(conditions, this.fieldTypes, this.qualifiedName, 0, options.skipUndefined);
297
+ let sql = `SELECT COUNT(*) c FROM ${this.qualifiedName} ${where.where}`;
298
+ return (await this.queryOneField(sql, where.params));
299
+ }
300
+
301
+ async findOneFieldOnly(conditions, field: string, options?: QueryOptions): Promise<any> {
302
+ options = options || {};
303
+ options.fields = [field];
304
+ let res = await this.findOne(conditions, options);
305
+ return res ? res[field] : null;
306
+ }
307
+
308
+
309
+ private getInsertQuery(records: T[]) {
310
+ let columnsMap = {};
311
+ records.forEach(rec => {
312
+ for (let field in <Object>rec) {
313
+ columnsMap[field] = true;
314
+ }
315
+ });
316
+ let columns = Object.keys(columnsMap);
317
+ let sql = util.format("INSERT INTO %s (%s) VALUES\n", this.qualifiedName, columns.map(pgUtils.quoteField).join(", "));
318
+ let parameters = [];
319
+ let placeholders = [];
320
+
321
+ for (let i = 0, seed = 0; i < records.length; i++) {
322
+ placeholders.push('(' + columns.map(c => "$" + (++seed)).join(', ') + ')');
323
+ parameters.push.apply(parameters, columns.map(c => pgUtils.transformInsertUpdateParams(records[i][c], this.fieldTypes[c])));
324
+ }
325
+ sql += placeholders.join(",\n");
326
+
327
+ return {sql, parameters};
328
+
329
+ }
330
+
331
+ protected getUpdateSetSnipplet(fields: { [k: string]: any }, parameters?:any[] ): { snipplet: string, parameters: any[] } {
332
+ let params = parameters || [];
333
+ let f = [];
334
+ let seed = params.length;
335
+
336
+ _.each(fields, (value, fieldName) => {
337
+ if (value === undefined) return;
338
+
339
+ f.push(util.format('%s = $%s', pgUtils.quoteField(fieldName), (++seed)));
340
+ params.push(pgUtils.transformInsertUpdateParams(value, this.fieldTypes[fieldName]));
341
+ });
342
+
343
+ return {snipplet: f.join(', '), parameters: params};
344
+ }
345
+
346
+ protected getUpdateQuery(conditions: { [k: string]: any }, fields: { [k: string]: any }, options?: UpdateDeleteOption): { sql: string, parameters: any[] } {
347
+ options = options || {};
348
+ options.skipUndefined = options.skipUndefined === true || (options.skipUndefined === undefined && this.db.config.skipUndefined === 'all');
349
+
350
+ let hasConditions = true;
351
+
352
+ if (_.isEmpty(fields)) {
353
+ throw new Error('Missing fields for update');
354
+ }
355
+
356
+ let {snipplet, parameters} = this.getUpdateSetSnipplet(fields);
357
+ let sql = util.format("UPDATE %s SET %s", this.qualifiedName, snipplet);
358
+
359
+ if (!hasConditions || !_.isEmpty(conditions)) {
360
+ let parsedWhere = generateWhere(conditions, this.fieldTypes, this.qualifiedName, parameters.length, options.skipUndefined);
361
+ sql += parsedWhere.where;
362
+ parameters = parameters.concat(parsedWhere.params);
363
+ }
364
+ return {sql, parameters};
365
+ }
366
+
367
+ protected getUpsertQuery(record: T, options?: UpsertOption): { sql: string, parameters: any[] } {
368
+ options = options || {};
369
+
370
+ if (_.isEmpty(record)) {
371
+ throw new Error('Missing fields for upsert');
372
+ }
373
+
374
+ let insert = this.getInsertQuery([record]);
375
+ let {snipplet, parameters} = this.getUpdateSetSnipplet(record, insert.parameters);
376
+ let sql = insert.sql;
377
+
378
+ if (options.columns) {
379
+ sql += " ON CONFLICT (" + options.columns.map(c=>pgUtils.quoteField(c)).join(', ') + ") DO UPDATE SET " + snipplet;
380
+ } else {
381
+ let constraint = options.constraint || this.pkey;
382
+ sql += " ON CONFLICT ON CONSTRAINT " + util.format('"%s"', constraint) + " DO UPDATE SET " + snipplet;
383
+ }
384
+
385
+ return {sql, parameters};
386
+ }
387
+
388
+ protected getDeleteQuery(conditions: { [k: string]: any }, options?: UpdateDeleteOption): { sql: string, parameters: any[] } {
389
+ options = options || {};
390
+ options.skipUndefined = options.skipUndefined === true || (options.skipUndefined === undefined && this.db.config.skipUndefined === 'all');
391
+
392
+ let sql = util.format("DELETE FROM %s ", this.qualifiedName);
393
+
394
+ let parsedWhere;
395
+ if (!_.isEmpty(conditions)) {
396
+ parsedWhere = generateWhere(conditions, this.fieldTypes, this.qualifiedName, 0, options.skipUndefined);
397
+ sql += parsedWhere.where;
398
+ }
399
+ return {sql, parameters: parsedWhere && parsedWhere.params || []}
400
+ }
401
+ }