pogi 3.0.0-beta → 3.0.0-beta5
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/.env +5 -0
- package/lib/bin/generateInterface.js +1 -1
- package/lib/connectionOptions.d.ts +7 -0
- package/lib/index.d.ts +1 -1
- package/lib/pgConverters.d.ts +9 -10
- package/lib/pgConverters.js +44 -39
- package/lib/pgConverters.js.map +1 -1
- package/lib/{src/pgConverters.test.d.ts → pgConverters.test.d.ts} +0 -0
- package/lib/{src/pgConverters.test.js → pgConverters.test.js} +0 -0
- package/lib/pgConverters.test.js.map +1 -0
- package/lib/pgDb.d.ts +24 -31
- package/lib/pgDb.js +66 -69
- package/lib/pgDb.js.map +1 -1
- package/lib/{src/pgDb.test.d.ts → pgDb.test.d.ts} +0 -0
- package/lib/{src/pgDb.test.js → pgDb.test.js} +6 -0
- package/lib/pgDb.test.js.map +1 -0
- package/lib/pgDbInterface.d.ts +22 -0
- package/lib/pgDbInterface.js +11 -0
- package/lib/pgDbInterface.js.map +1 -0
- package/lib/pgDbOperators.d.ts +3 -3
- package/lib/pgDbOperators.js +4 -7
- package/lib/pgDbOperators.js.map +1 -1
- package/lib/{src/pgDbOperators.test.d.ts → pgDbOperators.test.d.ts} +0 -0
- package/lib/{src/pgDbOperators.test.js → pgDbOperators.test.js} +0 -0
- package/lib/pgDbOperators.test.js.map +1 -0
- package/lib/pgSchema.d.ts +2 -3
- package/lib/pgSchema.js.map +1 -1
- package/lib/pgSchemaInterface.d.ts +0 -0
- package/lib/pgSchemaInterface.js +2 -0
- package/lib/pgSchemaInterface.js.map +1 -0
- package/lib/pgTable.d.ts +13 -38
- package/lib/pgTable.js +54 -54
- package/lib/pgTable.js.map +1 -1
- package/lib/pgTableInterface.d.ts +28 -0
- package/lib/{src/pgTableInterface.js → pgTableInterface.js} +0 -0
- package/lib/pgTableInterface.js.map +1 -0
- package/lib/pgUtils.d.ts +15 -5
- package/lib/pgUtils.js +118 -24
- package/lib/pgUtils.js.map +1 -1
- package/lib/queryAble.d.ts +15 -64
- package/lib/queryAble.js +46 -45
- package/lib/queryAble.js.map +1 -1
- package/lib/{src/queryAbleInterface.d.ts → queryAbleInterface.d.ts} +6 -10
- package/lib/{src/queryAbleInterface.js → queryAbleInterface.js} +0 -0
- package/lib/queryAbleInterface.js.map +1 -0
- package/lib/queryWhere.d.ts +2 -2
- package/lib/queryWhere.js +19 -23
- package/lib/queryWhere.js.map +1 -1
- package/lib/{src/bin/generateInterface.d.ts → test/pgDbOperatorSpec.d.ts} +0 -0
- package/lib/test/pgDbOperatorSpec.js +326 -0
- package/lib/test/pgDbOperatorSpec.js.map +1 -0
- package/lib/test/pgDbSpec.d.ts +1 -0
- package/lib/test/pgDbSpec.js +1139 -0
- package/lib/test/pgDbSpec.js.map +1 -0
- package/lib/test/pgServiceRestartTest.d.ts +1 -0
- package/lib/test/pgServiceRestartTest.js +1532 -0
- package/lib/test/pgServiceRestartTest.js.map +1 -0
- package/package.json +2 -1
- package/tsconfig.json +3 -5
- package/lib/src/bin/generateInterface.js +0 -53
- package/lib/src/bin/generateInterface.js.map +0 -1
- package/lib/src/connectionOptions.d.ts +0 -34
- package/lib/src/connectionOptions.js +0 -3
- package/lib/src/connectionOptions.js.map +0 -1
- package/lib/src/index.d.ts +0 -6
- package/lib/src/index.js +0 -12
- package/lib/src/index.js.map +0 -1
- package/lib/src/pgConverters.d.ts +0 -9
- package/lib/src/pgConverters.js +0 -71
- package/lib/src/pgConverters.js.map +0 -1
- package/lib/src/pgConverters.test.js.map +0 -1
- package/lib/src/pgDb.d.ts +0 -79
- package/lib/src/pgDb.js +0 -764
- package/lib/src/pgDb.js.map +0 -1
- package/lib/src/pgDb.test.js.map +0 -1
- package/lib/src/pgDbInterface.js +0 -11
- package/lib/src/pgDbInterface.js.map +0 -1
- package/lib/src/pgDbLogger.d.ts +0 -5
- package/lib/src/pgDbLogger.js +0 -3
- package/lib/src/pgDbLogger.js.map +0 -1
- package/lib/src/pgDbOperators.d.ts +0 -113
- package/lib/src/pgDbOperators.js +0 -41
- package/lib/src/pgDbOperators.js.map +0 -1
- package/lib/src/pgDbOperators.test.js.map +0 -1
- package/lib/src/pgSchema.d.ts +0 -17
- package/lib/src/pgSchema.js +0 -16
- package/lib/src/pgSchema.js.map +0 -1
- package/lib/src/pgSchemaInterface.d.ts +0 -12
- package/lib/src/pgSchemaInterface.js +0 -3
- package/lib/src/pgSchemaInterface.js.map +0 -1
- package/lib/src/pgTable.d.ts +0 -105
- package/lib/src/pgTable.js +0 -322
- package/lib/src/pgTable.js.map +0 -1
- package/lib/src/pgTableInterface.d.ts +0 -102
- package/lib/src/pgTableInterface.js.map +0 -1
- package/lib/src/pgUtils.d.ts +0 -41
- package/lib/src/pgUtils.js +0 -282
- package/lib/src/pgUtils.js.map +0 -1
- package/lib/src/queryAble.d.ts +0 -40
- package/lib/src/queryAble.js +0 -338
- package/lib/src/queryAble.js.map +0 -1
- package/lib/src/queryAbleInterface.js.map +0 -1
- package/lib/src/queryWhere.d.ts +0 -8
- package/lib/src/queryWhere.js +0 -245
- package/lib/src/queryWhere.js.map +0 -1
- package/src/bin/generateInterface.ts +0 -54
- package/src/connectionOptions.ts +0 -75
- package/src/index.d.ts +0 -7
- package/src/index.ts +0 -6
- package/src/pgConverters.test.ts +0 -10
- package/src/pgConverters.ts +0 -59
- package/src/pgDb.test.ts +0 -1324
- package/src/pgDb.ts +0 -842
- package/src/pgDbInterface.ts +0 -57
- package/src/pgDbLogger.ts +0 -13
- package/src/pgDbOperators.test.ts +0 -478
- package/src/pgDbOperators.ts +0 -85
- package/src/pgSchema.ts +0 -16
- package/src/pgSchemaInterface.ts +0 -12
- package/src/pgTable.ts +0 -369
- package/src/pgTableInterface.ts +0 -131
- package/src/pgUtils.ts +0 -290
- package/src/queryAble.ts +0 -365
- package/src/queryAbleInterface.ts +0 -108
- package/src/queryWhere.ts +0 -325
- package/src/test/init.sql +0 -122
- package/src/test/pgServiceRestartTest.ts +0 -1500
- package/src/test/throw_exception.sql +0 -5
- package/src/test/tricky.sql +0 -13
package/src/pgUtils.ts
DELETED
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
import { QueryOptions, IQueryAble } from "./queryAbleInterface";
|
|
2
|
-
import { ResultFieldType } from "./pgDbInterface";
|
|
3
|
-
import { FieldType } from "./pgDb";
|
|
4
|
-
import { PgDbLogger } from "./pgDbLogger";
|
|
5
|
-
import { IPgTable } from "./pgTableInterface";
|
|
6
|
-
import * as _ from 'lodash';
|
|
7
|
-
import util = require('util');
|
|
8
|
-
import * as pg from 'pg';
|
|
9
|
-
|
|
10
|
-
const NAMED_PARAMS_REGEXP = /(?:^|[^:]):(!?[a-zA-Z0-9_]+)/g; // do not convert "::type cast"
|
|
11
|
-
const ASC_DESC_REGEXP = /^\s*(.+?)(?:\s+(asc|desc))?\s*$/i;
|
|
12
|
-
|
|
13
|
-
export let pgUtils = {
|
|
14
|
-
|
|
15
|
-
logError(logger: PgDbLogger, options: { error?: string | Error, sql: string, params: any, connection?: pg.PoolClient | null }) {
|
|
16
|
-
let { error, sql, params, connection } = options;
|
|
17
|
-
logger.error(error, sql, util.inspect(logger.paramSanitizer ? logger.paramSanitizer(params) : params, false, null), connection ? connection.processID : null);
|
|
18
|
-
},
|
|
19
|
-
|
|
20
|
-
quoteFieldNameInsecure(f: string) {
|
|
21
|
-
return f.indexOf('"') == -1 && f.indexOf('(') == -1 ? '"' + f + '"' : f;
|
|
22
|
-
},
|
|
23
|
-
|
|
24
|
-
quoteFieldName(f: string) {
|
|
25
|
-
if (typeof f === 'string' && f.length) {
|
|
26
|
-
return `"${f
|
|
27
|
-
.replace(/^\s*"*/, '') // trim "
|
|
28
|
-
.replace(/"*\s*$/, '')
|
|
29
|
-
.replace(/"/g, '""')}"`;
|
|
30
|
-
} else {
|
|
31
|
-
throw new Error(`Invalid field: ${f}`);
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
|
-
|
|
35
|
-
quoteFieldNameOrPositionInsecure(f: string | number): string {
|
|
36
|
-
if (Number.isInteger(+f)) {
|
|
37
|
-
if (!Number.isInteger(+f) || +f < 1) throw new Error(`Invalid field: ${f}`);
|
|
38
|
-
return '' + f;
|
|
39
|
-
} else if (typeof f === 'string' && f.length) {
|
|
40
|
-
return f.indexOf('"') == -1 && f.indexOf('(') == -1 ? '"' + f + '"' : f;
|
|
41
|
-
} else {
|
|
42
|
-
throw new Error(`Invalid field: ${f}`);
|
|
43
|
-
}
|
|
44
|
-
},
|
|
45
|
-
|
|
46
|
-
/** ex. for order by column position can be use, which needs no quote */
|
|
47
|
-
quoteFieldNameOrPosition(f: string | number): string {
|
|
48
|
-
if (Number.isInteger(+f)) {
|
|
49
|
-
if (!Number.isInteger(+f) || +f < 1) throw new Error(`Invalid field: ${f}`);
|
|
50
|
-
return '' + f;
|
|
51
|
-
} else if (typeof f === 'string' && f.length) {
|
|
52
|
-
return `"${f
|
|
53
|
-
.replace(/^\s*"*\s*/, '') // trim "
|
|
54
|
-
.replace(/\s*"*\s*$/, '')
|
|
55
|
-
.replace(/"/g, '""')}"`;
|
|
56
|
-
} else {
|
|
57
|
-
throw new Error(`Invalid field: ${f}`);
|
|
58
|
-
}
|
|
59
|
-
},
|
|
60
|
-
/**
|
|
61
|
-
* https://www.postgresql.org/docs/current/functions-json.html
|
|
62
|
-
* column->'a' ,
|
|
63
|
-
* column -> 3
|
|
64
|
-
*/
|
|
65
|
-
quoteFieldNameJsonbOrPosition(f: string | number): string {
|
|
66
|
-
// treat numeric json keys as array indices, otherwise quote it
|
|
67
|
-
if (Number.isInteger(+f)) {
|
|
68
|
-
return '' + f;
|
|
69
|
-
} if (typeof f === 'string' && f.length) {
|
|
70
|
-
return `'${f
|
|
71
|
-
.replace(/^\s*'*/, '') // trim "
|
|
72
|
-
.replace(/'*\s*$/, '')
|
|
73
|
-
.replace(/'/g, "''")}'`;
|
|
74
|
-
} else {
|
|
75
|
-
throw new Error(`Invalid field: ${f}`);
|
|
76
|
-
}
|
|
77
|
-
},
|
|
78
|
-
|
|
79
|
-
processQueryFields<T>(options: QueryOptions, pgTable?: IPgTable<T>): string {
|
|
80
|
-
let escapeColumns = ((pgTable?.db.config.forceEscapeColumns === true || pgTable?.db.config.forceEscapeColumns?.select === true) && options.forceEscapeColumns?.select !== false && options.forceEscapeColumns !== false) ||
|
|
81
|
-
options.forceEscapeColumns === true || options.forceEscapeColumns?.select;
|
|
82
|
-
|
|
83
|
-
let s = options && options.distinct ? ' DISTINCT ' : ' ';
|
|
84
|
-
if (options && options.fields) {
|
|
85
|
-
if (Array.isArray(options.fields)) {
|
|
86
|
-
if (escapeColumns) {
|
|
87
|
-
return s + options.fields.map(pgUtils.quoteFieldName).join(', ');
|
|
88
|
-
}
|
|
89
|
-
return s + options.fields.map(pgUtils.quoteFieldNameInsecure).join(', ');
|
|
90
|
-
} else {
|
|
91
|
-
return s + options.fields;
|
|
92
|
-
}
|
|
93
|
-
} else {
|
|
94
|
-
return s + ' *';
|
|
95
|
-
}
|
|
96
|
-
},
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* :named -> $1 (not works with DDL (schema, table, column))
|
|
100
|
-
* :!named -> "value" (for DDL (schema, table, column))
|
|
101
|
-
* do not touch ::type cast
|
|
102
|
-
*/
|
|
103
|
-
processNamedParams(sql: string, params: Object) {
|
|
104
|
-
let sql2 = [];
|
|
105
|
-
let params2 = [];
|
|
106
|
-
|
|
107
|
-
let p = NAMED_PARAMS_REGEXP.exec(sql);
|
|
108
|
-
let lastIndex = 0;
|
|
109
|
-
while (p) {
|
|
110
|
-
let ddl = false;
|
|
111
|
-
let name = p[1];
|
|
112
|
-
if (name[0] == '!') {
|
|
113
|
-
name = name.slice(1);
|
|
114
|
-
ddl = true;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (!(name in params)) {
|
|
118
|
-
throw new Error(`No ${p[1]} in params (keys: ${Object.keys(params)})`);
|
|
119
|
-
}
|
|
120
|
-
sql2.push(sql.slice(lastIndex, NAMED_PARAMS_REGEXP.lastIndex - p[1].length - 1));
|
|
121
|
-
|
|
122
|
-
if (ddl) {
|
|
123
|
-
sql2.push(pgUtils.quoteFieldName(params[name]));
|
|
124
|
-
} else {
|
|
125
|
-
params2.push(params[name]);
|
|
126
|
-
sql2.push('$' + params2.length);
|
|
127
|
-
}
|
|
128
|
-
lastIndex = NAMED_PARAMS_REGEXP.lastIndex;
|
|
129
|
-
p = NAMED_PARAMS_REGEXP.exec(sql);
|
|
130
|
-
}
|
|
131
|
-
sql2.push(sql.slice(lastIndex));
|
|
132
|
-
|
|
133
|
-
return {
|
|
134
|
-
sql: sql2.join(''),
|
|
135
|
-
params: params2
|
|
136
|
-
}
|
|
137
|
-
},
|
|
138
|
-
|
|
139
|
-
handleColumnEscapeGroupBy<T>(options: QueryOptions, pgTable?: IPgTable<T>): string {
|
|
140
|
-
if (!options.groupBy) return '';
|
|
141
|
-
let escapeColumns = ((pgTable?.db.config.forceEscapeColumns === true || pgTable?.db.config.forceEscapeColumns?.groupBy === true) && options.forceEscapeColumns?.groupBy !== false && options.forceEscapeColumns !== false) ||
|
|
142
|
-
options.forceEscapeColumns === true || options.forceEscapeColumns?.groupBy;
|
|
143
|
-
|
|
144
|
-
if (escapeColumns) {
|
|
145
|
-
if (Array.isArray(options.groupBy)) {
|
|
146
|
-
return ' GROUP BY ' + options.groupBy.map(pgUtils.quoteFieldNameOrPosition).join(',');
|
|
147
|
-
} else {
|
|
148
|
-
return ' GROUP BY ' + pgUtils.quoteFieldNameOrPosition(options.groupBy);
|
|
149
|
-
}
|
|
150
|
-
} else {
|
|
151
|
-
if (Array.isArray(options.groupBy)) {
|
|
152
|
-
return ' GROUP BY ' + options.groupBy.map(pgUtils.quoteFieldNameOrPositionInsecure).join(',');
|
|
153
|
-
} else {
|
|
154
|
-
return ' GROUP BY ' + pgUtils.quoteFieldNameOrPositionInsecure(options.groupBy);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
},
|
|
158
|
-
|
|
159
|
-
handleColumnEscapeOrderBy<T>(options: QueryOptions, pgTable: IPgTable<T>): string {
|
|
160
|
-
if (!options.orderBy) return '';
|
|
161
|
-
let sql = '';
|
|
162
|
-
let escapeColumns = ((pgTable?.db.config.forceEscapeColumns === true || pgTable?.db.config.forceEscapeColumns?.orderBy === true) && options.forceEscapeColumns?.orderBy !== false && options.forceEscapeColumns !== false) ||
|
|
163
|
-
options.forceEscapeColumns === true || options.forceEscapeColumns?.orderBy;
|
|
164
|
-
|
|
165
|
-
let orderBy = typeof options.orderBy === 'string' ? options.orderBy.split(',') : options.orderBy;
|
|
166
|
-
if (Array.isArray(orderBy)) {
|
|
167
|
-
let orderBy2: string[];
|
|
168
|
-
|
|
169
|
-
if (escapeColumns) {
|
|
170
|
-
orderBy2 = orderBy.map(v => {
|
|
171
|
-
if (typeof v === 'number') return pgUtils.quoteFieldNameOrPosition(v);
|
|
172
|
-
else if (typeof v !== 'string' || !v.length) throw new Error(`Invalid orderBy: ${v}`);
|
|
173
|
-
if (v[0] == '+') return pgUtils.quoteFieldNameOrPosition(v.slice(1));
|
|
174
|
-
if (v[0] == '-') return pgUtils.quoteFieldNameOrPosition(v.slice(1)) + ' desc';
|
|
175
|
-
let o = ASC_DESC_REGEXP.exec(v);
|
|
176
|
-
if (!o) throw new Error(`Invalid orderBy: ${v}`);
|
|
177
|
-
return `${pgUtils.quoteFieldNameOrPosition(o[1])} ${o[2] ?? ''}`;
|
|
178
|
-
});
|
|
179
|
-
} else {
|
|
180
|
-
orderBy2 = orderBy.map(v => {
|
|
181
|
-
if (typeof v === 'number') return pgUtils.quoteFieldNameOrPositionInsecure(v);
|
|
182
|
-
else if (typeof v !== 'string' || !v.length) throw new Error(`Invalid orderBy: ${v}`);
|
|
183
|
-
if (v[0] == '+') return pgUtils.quoteFieldNameOrPositionInsecure(v.slice(1));
|
|
184
|
-
if (v[0] == '-') return pgUtils.quoteFieldNameOrPositionInsecure(v.slice(1)) + ' desc';
|
|
185
|
-
let o = ASC_DESC_REGEXP.exec(v);
|
|
186
|
-
if (!o) throw new Error(`Invalid orderBy: ${v}`);
|
|
187
|
-
return `${pgUtils.quoteFieldNameOrPositionInsecure(o[1])} ${o[2] ?? ''}`;
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
sql += ' ORDER BY ' + orderBy2.join(',');
|
|
191
|
-
} else {
|
|
192
|
-
throw new Error(`Invalid orderBy: ${options.orderBy}`);
|
|
193
|
-
}
|
|
194
|
-
return sql;
|
|
195
|
-
},
|
|
196
|
-
|
|
197
|
-
processQueryOptions<T>(options: QueryOptions, pgTable: IPgTable<T>): string {
|
|
198
|
-
options = options || {};
|
|
199
|
-
let sql = '';
|
|
200
|
-
|
|
201
|
-
if (options.groupBy) {
|
|
202
|
-
sql += pgUtils.handleColumnEscapeGroupBy(options, pgTable);
|
|
203
|
-
}
|
|
204
|
-
if (options.orderBy) {
|
|
205
|
-
sql += pgUtils.handleColumnEscapeOrderBy(options, pgTable);
|
|
206
|
-
|
|
207
|
-
if (options.orderByNullsFirst != null) {
|
|
208
|
-
sql += ' NULLS ' + options.orderByNullsFirst ? 'FIRST' : 'LAST';
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
if (options.limit) {
|
|
212
|
-
if (!Number.isInteger(options.limit) || options.limit < 0) throw new Error(`Invalid limit: ${options.limit}`);
|
|
213
|
-
sql += ` LIMIT ${options.limit}`;
|
|
214
|
-
}
|
|
215
|
-
if (options.offset) {
|
|
216
|
-
if (!Number.isInteger(options.offset) || options.offset < 0) throw new Error(`Invalid offset: ${options.offset}`);
|
|
217
|
-
sql += ` OFFSET ${options.offset}`;
|
|
218
|
-
}
|
|
219
|
-
if (options.forUpdate) {
|
|
220
|
-
sql += ' FOR UPDATE';
|
|
221
|
-
}
|
|
222
|
-
return sql;
|
|
223
|
-
},
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* NOTE-DATE: there are 2 approaches to keep tz (the time correctly):
|
|
227
|
-
* 1) use Date.toISOString() function, but then the $x placeholder should be TIMESTAMP WITH TIME ZONE $x
|
|
228
|
-
* 2) use Date, and then no need to change the placeholder $x
|
|
229
|
-
* lets use 2)
|
|
230
|
-
*/
|
|
231
|
-
transformInsertUpdateParams(param: any, fieldType: FieldType) {
|
|
232
|
-
return (param != null && fieldType == FieldType.JSON) ? JSON.stringify(param) :
|
|
233
|
-
(param != null && fieldType == FieldType.TIME && !(param instanceof Date)) ? new Date(param) : param;
|
|
234
|
-
},
|
|
235
|
-
|
|
236
|
-
postProcessResult(res: any[], fields: ResultFieldType[], pgdbTypeParsers: { [oid: number]: (s: string) => any }) {
|
|
237
|
-
if (res) {
|
|
238
|
-
if (res[0] && !Array.isArray(res[0])) {
|
|
239
|
-
if (Object.keys(res[0]).length != fields.length) {
|
|
240
|
-
throw new Error("Name collision for the query, two or more fields have the same name.");
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
pgUtils.convertTypes(res, fields, pgdbTypeParsers);
|
|
244
|
-
}
|
|
245
|
-
},
|
|
246
|
-
|
|
247
|
-
convertTypes(res: any[], fields: ResultFieldType[], pgdbTypeParsers: { [oid: number]: (s: string) => any }) {
|
|
248
|
-
let isArrayMode = Array.isArray(res[0]);
|
|
249
|
-
fields.forEach((field, i) => {
|
|
250
|
-
if (pgdbTypeParsers[field.dataTypeID]) {
|
|
251
|
-
if (isArrayMode) {
|
|
252
|
-
res.forEach(e => e[i] = e[i] == null ? null : pgdbTypeParsers[field.dataTypeID](e[i]));
|
|
253
|
-
} else {
|
|
254
|
-
res.forEach(e => e[field.name] = e[field.name] == null ? null : pgdbTypeParsers[field.dataTypeID](e[field.name]));
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
});
|
|
258
|
-
},
|
|
259
|
-
|
|
260
|
-
createFunctionCaller(q: IQueryAble, fn: { schema: string, name: string, return_single_row: boolean, return_single_value: boolean }) {
|
|
261
|
-
return async (...args: any[]) => {
|
|
262
|
-
let placeHolders: string[] = [];
|
|
263
|
-
let params: any[] = [];
|
|
264
|
-
args.forEach((arg) => {
|
|
265
|
-
placeHolders.push('$' + (placeHolders.length + 1));
|
|
266
|
-
params.push(arg);
|
|
267
|
-
});
|
|
268
|
-
let res = await q.query(`SELECT "${fn.schema}"."${fn.name}"(${placeHolders.join(',')})`, params);
|
|
269
|
-
|
|
270
|
-
if (fn.return_single_value) {
|
|
271
|
-
let keys = res[0] ? Object.keys(res[0]) : [];
|
|
272
|
-
if (keys.length != 1) {
|
|
273
|
-
throw new Error(`Return type error. schema: ${fn.schema} fn: ${fn.name} expected return type: single value, current value:` + JSON.stringify(res))
|
|
274
|
-
}
|
|
275
|
-
res = res.map((r) => r[keys[0]]);
|
|
276
|
-
}
|
|
277
|
-
if (fn.return_single_row) {
|
|
278
|
-
if (res.length != 1) {
|
|
279
|
-
throw new Error(`Return type error. schema: ${fn.schema} fn: ${fn.name} expected return type: single value, current value:` + JSON.stringify(res))
|
|
280
|
-
}
|
|
281
|
-
return res[0];
|
|
282
|
-
} else {
|
|
283
|
-
return res;
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
},
|
|
287
|
-
escapeForLike(s: string): string {
|
|
288
|
-
return s.replace(/([\\%_])/g, '\\$1');
|
|
289
|
-
}
|
|
290
|
-
};
|
package/src/queryAble.ts
DELETED
|
@@ -1,365 +0,0 @@
|
|
|
1
|
-
import { PgDbLogger } from "./pgDbLogger";
|
|
2
|
-
import { pgUtils } from "./pgUtils";
|
|
3
|
-
import * as stream from "stream";
|
|
4
|
-
import { IPgSchema } from "./pgSchemaInterface";
|
|
5
|
-
import * as pg from 'pg';
|
|
6
|
-
import util = require('util');
|
|
7
|
-
import QueryStream = require('pg-query-stream');
|
|
8
|
-
import through = require('through');
|
|
9
|
-
import {ResultFieldType, IPgDb} from "./pgDbInterface";
|
|
10
|
-
import {SqlQueryOptions, IQueryAble, PgRowResult } from "./queryAbleInterface"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
let defaultLogger = {
|
|
14
|
-
log: () => { },
|
|
15
|
-
error: () => { }
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export abstract class QueryAble implements IQueryAble {
|
|
19
|
-
db!: IPgDb & IQueryAble; // assigned in async init
|
|
20
|
-
schema!: IPgSchema;
|
|
21
|
-
/*protected*/ logger!: PgDbLogger;
|
|
22
|
-
|
|
23
|
-
public static connectionErrorListener = () => { }
|
|
24
|
-
|
|
25
|
-
setLogger(logger: PgDbLogger) {
|
|
26
|
-
this.logger = logger;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
getLogger(useConsoleAsDefault = false) {
|
|
30
|
-
return this.logger || this.schema && this.schema.logger || this.db.logger || (useConsoleAsDefault ? console : defaultLogger);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** alias to {@link query} */
|
|
34
|
-
async run(sql: string, params?: any[] | {}, options?: SqlQueryOptions): Promise<any[]> {
|
|
35
|
-
return this.query(sql, params, options);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Params can be
|
|
40
|
-
* 1) array, then sql should have $1 $2 for placeholders
|
|
41
|
-
* 2) object, then sql should have:
|
|
42
|
-
* :example -> for params in statements (set/where), will be transformed to $1 $2 ...
|
|
43
|
-
* :!example -> for DDL names (schema, table, column), will be replaced in the query
|
|
44
|
-
* e.g. query('select * from a.b where id=$1;',['the_stage_is_set']);
|
|
45
|
-
* e.g. query('select * from :!schema.:!table where id=:id;',{schema:'a',table:'b', id:'the_stage_is_set'});
|
|
46
|
-
*/
|
|
47
|
-
async query(sql: string, params?: any[] | {} | null, options?: SqlQueryOptions): Promise<any[]> {
|
|
48
|
-
let connection = this.db.connection;
|
|
49
|
-
let logger = (options && options.logger || this.getLogger(false));
|
|
50
|
-
return this.internalQuery({ connection, sql, params, logger });
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
protected async internalQuery(options: { connection: pg.PoolClient | null, sql: string, params?: any, logger?: PgDbLogger }): Promise<any[]>;
|
|
54
|
-
protected async internalQuery(options: { connection: pg.PoolClient | null, sql: string, params?: any, logger?: PgDbLogger, rowMode: true }): Promise<PgRowResult>;
|
|
55
|
-
protected async internalQuery(options: { connection: pg.PoolClient | null, sql: string, params?: any, logger?: PgDbLogger, rowMode?: boolean }): Promise<any[] | PgRowResult> {
|
|
56
|
-
if (this.db.needToFixConnectionForListen()) {
|
|
57
|
-
await this.db.runRestartConnectionForListen();
|
|
58
|
-
}
|
|
59
|
-
let { connection, sql, params, logger } = options;
|
|
60
|
-
logger = logger || this.getLogger(false);
|
|
61
|
-
|
|
62
|
-
try {
|
|
63
|
-
if (params && !Array.isArray(params)) {
|
|
64
|
-
let p = pgUtils.processNamedParams(sql, params);
|
|
65
|
-
sql = p.sql;
|
|
66
|
-
params = p.params;
|
|
67
|
-
}
|
|
68
|
-
let query = options?.rowMode ? { text: sql, values: params, rowMode: 'array' } : { text: sql, values: params };
|
|
69
|
-
let res;
|
|
70
|
-
if (connection) {
|
|
71
|
-
logger.log('reused connection', sql, util.inspect(params, false, null), connection.processID);
|
|
72
|
-
res = await connection.query(query);
|
|
73
|
-
await this.checkAndFixOids(connection, res.fields);
|
|
74
|
-
} else {
|
|
75
|
-
connection = await this.db.pool.connect();
|
|
76
|
-
logger.log('new connection', sql, util.inspect(params, false, null), connection.processID);
|
|
77
|
-
|
|
78
|
-
connection.on('error', QueryAble.connectionErrorListener);
|
|
79
|
-
res = await connection.query(query);
|
|
80
|
-
await this.checkAndFixOids(connection, res.fields);
|
|
81
|
-
|
|
82
|
-
connection.off('error', QueryAble.connectionErrorListener);
|
|
83
|
-
connection.release();
|
|
84
|
-
connection = null;
|
|
85
|
-
}
|
|
86
|
-
this.postProcessFields(res.rows, res.fields, logger);
|
|
87
|
-
return options?.rowMode ? { columns: (res.fields || []).map(f => f.name), rows: res.rows || [] } : res.rows;
|
|
88
|
-
} catch (e) {
|
|
89
|
-
pgUtils.logError(logger, { error: <Error>e, sql, params, connection });
|
|
90
|
-
if (connection) {
|
|
91
|
-
try {
|
|
92
|
-
//If any problem has happened in a dedicated connection, (wrong sql format or non-accessible postgres server)
|
|
93
|
-
//close the connection to be a free connection in the pool,
|
|
94
|
-
//but keep the db.connection member non - null to crash in all of the following commands
|
|
95
|
-
connection.release();
|
|
96
|
-
} catch (e) {
|
|
97
|
-
logger.error('connection error', (<Error>e).message);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
throw e;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Same as query but response is two array: columns and rows and rows are arrays also not objects
|
|
106
|
-
* This is useful for queries which have colliding column names
|
|
107
|
-
*/
|
|
108
|
-
async queryAsRows(sql: string, params?: any[] | {}, options?: SqlQueryOptions): Promise<PgRowResult> {
|
|
109
|
-
let connection = this.db.connection;
|
|
110
|
-
let logger = (options && options.logger || this.getLogger(false));
|
|
111
|
-
return this.internalQuery({ connection, sql, params, logger, rowMode: true });
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* If the callback function return true, the connection will be closed.
|
|
116
|
-
*/
|
|
117
|
-
async queryWithOnCursorCallback(sql: string, params: any[] | Record<string, any> | null, options: SqlQueryOptions | null, callback: (res: any) => any): Promise<void> {
|
|
118
|
-
if (this.db.needToFixConnectionForListen()) {
|
|
119
|
-
await this.db.runRestartConnectionForListen();
|
|
120
|
-
}
|
|
121
|
-
let connection = this.db.connection!;
|
|
122
|
-
let logger = this.getLogger(true);
|
|
123
|
-
let positionedParams: any[] | null | undefined;
|
|
124
|
-
|
|
125
|
-
try {
|
|
126
|
-
if (params && !Array.isArray(params)) {
|
|
127
|
-
let p = pgUtils.processNamedParams(sql, params);
|
|
128
|
-
sql = p.sql;
|
|
129
|
-
params = p.params;
|
|
130
|
-
} else {
|
|
131
|
-
positionedParams = params;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
let queryInternal = async () => {
|
|
135
|
-
this.getLogger(false).log(sql, util.inspect(positionedParams, false, null), connection.processID);
|
|
136
|
-
let fieldsToFix: ResultFieldType[] | undefined;
|
|
137
|
-
let isFirst = true;
|
|
138
|
-
|
|
139
|
-
let query = new QueryStream(sql, positionedParams);
|
|
140
|
-
let stream = connection.query(query);
|
|
141
|
-
await new Promise((resolve, reject) => {
|
|
142
|
-
query.handleError = reject;
|
|
143
|
-
stream.on('data', (res: any) => {
|
|
144
|
-
try {
|
|
145
|
-
let fields = stream._result && stream._result.fields || stream.cursor._result && stream.cursor._result.fields;
|
|
146
|
-
if (isFirst) {
|
|
147
|
-
if (this.hasUnknownOids(fields)) {
|
|
148
|
-
fieldsToFix = fields;
|
|
149
|
-
stream.destroy();
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
isFirst = false;
|
|
153
|
-
}
|
|
154
|
-
this.postProcessFields([res], fields, this.getLogger(false));
|
|
155
|
-
|
|
156
|
-
if (callback(res)) {
|
|
157
|
-
stream.destroy();
|
|
158
|
-
}
|
|
159
|
-
} catch (e) {
|
|
160
|
-
reject(e);
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
stream.on('close', resolve);
|
|
165
|
-
stream.on('error', reject);
|
|
166
|
-
});
|
|
167
|
-
if (fieldsToFix) {
|
|
168
|
-
await this.checkAndFixOids(connection, fieldsToFix);
|
|
169
|
-
query = new QueryStream(sql, positionedParams);
|
|
170
|
-
stream = connection.query(query);
|
|
171
|
-
await new Promise((resolve, reject) => {
|
|
172
|
-
query.handleError = reject;
|
|
173
|
-
stream.on('data', (res: any) => {
|
|
174
|
-
try {
|
|
175
|
-
let fields = stream._result && stream._result.fields || stream.cursor._result && stream.cursor._result.fields;
|
|
176
|
-
this.postProcessFields([res], fields, this.getLogger(false));
|
|
177
|
-
|
|
178
|
-
if (callback(res)) {
|
|
179
|
-
stream.destroy();
|
|
180
|
-
}
|
|
181
|
-
} catch (e) {
|
|
182
|
-
reject(e);
|
|
183
|
-
}
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
stream.on('close', resolve);
|
|
187
|
-
stream.on('error', reject);
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
if (connection) {
|
|
193
|
-
await queryInternal();
|
|
194
|
-
} else {
|
|
195
|
-
connection = await this.db.pool.connect();
|
|
196
|
-
logger.log('new connection', sql, util.inspect(positionedParams, false, null), connection.processID);
|
|
197
|
-
connection.on('error', QueryAble.connectionErrorListener);
|
|
198
|
-
await queryInternal();
|
|
199
|
-
|
|
200
|
-
connection.off('error', QueryAble.connectionErrorListener);
|
|
201
|
-
connection.release();
|
|
202
|
-
}
|
|
203
|
-
} catch (e) {
|
|
204
|
-
pgUtils.logError(logger, { error: <Error>e, sql, params, connection });
|
|
205
|
-
if (connection) {
|
|
206
|
-
try {
|
|
207
|
-
connection.release();
|
|
208
|
-
} catch (e) {
|
|
209
|
-
logger.error('connection error', (<Error>e).message);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
throw e;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
async queryAsStream(sql: string, params?: any[] | Record<string, any> | null, options?: SqlQueryOptions | null): Promise<stream.Readable> {
|
|
217
|
-
if (this.db.needToFixConnectionForListen()) {
|
|
218
|
-
await this.db.runRestartConnectionForListen();
|
|
219
|
-
}
|
|
220
|
-
let connection = this.db.connection;
|
|
221
|
-
let logger = (options && options.logger || this.getLogger(false));
|
|
222
|
-
let pgStream: any;
|
|
223
|
-
let queriable = this;
|
|
224
|
-
let isFirst = true;
|
|
225
|
-
let convertTypeFilter = through(function (this: stream, data) {
|
|
226
|
-
try {
|
|
227
|
-
let fields = pgStream._result && pgStream._result.fields || pgStream.cursor._result && pgStream.cursor._result.fields;
|
|
228
|
-
if (isFirst) {
|
|
229
|
-
if (queriable.hasUnknownOids(fields)) {
|
|
230
|
-
throw new Error('[337] Query returns fields with unknown oid.');
|
|
231
|
-
}
|
|
232
|
-
isFirst = false;
|
|
233
|
-
}
|
|
234
|
-
queriable.postProcessFields([data], fields, queriable.db.pgdbTypeParsers);
|
|
235
|
-
|
|
236
|
-
this.emit('data', data);
|
|
237
|
-
} catch (err) {
|
|
238
|
-
this.emit('error', err);
|
|
239
|
-
}
|
|
240
|
-
});
|
|
241
|
-
convertTypeFilter.on('error', (e: Error) => {
|
|
242
|
-
if (connection) {
|
|
243
|
-
try {
|
|
244
|
-
connection.release();
|
|
245
|
-
} catch (e) {
|
|
246
|
-
logger.error('connection error', (<Error>e).message);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
connection = null;
|
|
250
|
-
pgUtils.logError(logger, { error: e, sql, params, connection });
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
let positionedParams: any[] | undefined | null;
|
|
254
|
-
|
|
255
|
-
try {
|
|
256
|
-
if (params && !Array.isArray(params)) {
|
|
257
|
-
let p = pgUtils.processNamedParams(sql, params);
|
|
258
|
-
sql = p.sql;
|
|
259
|
-
params = p.params;
|
|
260
|
-
} else {
|
|
261
|
-
positionedParams = params;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
if (connection) {
|
|
265
|
-
logger.log(sql, util.inspect(positionedParams, false, null), connection.processID);
|
|
266
|
-
let query = new QueryStream(sql, positionedParams);
|
|
267
|
-
query.handleError = (err: Error) => {
|
|
268
|
-
convertTypeFilter.emit('error', err);
|
|
269
|
-
};
|
|
270
|
-
pgStream = connection.query(query);
|
|
271
|
-
return pgStream.pipe(convertTypeFilter);
|
|
272
|
-
} else {
|
|
273
|
-
connection = await this.db.pool.connect();
|
|
274
|
-
logger.log('new connection', sql, util.inspect(positionedParams, false, null), connection.processID);
|
|
275
|
-
connection.on('error', QueryAble.connectionErrorListener);
|
|
276
|
-
|
|
277
|
-
let query = new QueryStream(sql, positionedParams);
|
|
278
|
-
query.handleError = (err: Error, _connection: pg.PoolClient) => {
|
|
279
|
-
convertTypeFilter.emit('error', err);
|
|
280
|
-
};
|
|
281
|
-
pgStream = connection.query(query);
|
|
282
|
-
pgStream.on('close', () => {
|
|
283
|
-
if (connection) {
|
|
284
|
-
connection.off('error', QueryAble.connectionErrorListener);
|
|
285
|
-
connection.release();
|
|
286
|
-
}
|
|
287
|
-
connection = null;
|
|
288
|
-
});
|
|
289
|
-
pgStream.on('error', (e: Error) => {
|
|
290
|
-
pgUtils.logError(logger, { error: e, sql, params: positionedParams, connection });
|
|
291
|
-
|
|
292
|
-
if (connection) {
|
|
293
|
-
connection.off('error', QueryAble.connectionErrorListener);
|
|
294
|
-
connection.release();
|
|
295
|
-
}
|
|
296
|
-
connection = null;
|
|
297
|
-
});
|
|
298
|
-
return pgStream.pipe(convertTypeFilter);
|
|
299
|
-
}
|
|
300
|
-
} catch (e) {
|
|
301
|
-
pgUtils.logError(logger, { error: <Error>e, sql, params: positionedParams, connection });
|
|
302
|
-
throw e;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
async queryOne(sql: string, params?: any[] | {}, options?: SqlQueryOptions): Promise<any> {
|
|
307
|
-
let res = await this.query(sql, params, options);
|
|
308
|
-
if (res.length > 1) {
|
|
309
|
-
let logger = (options && options.logger || this.getLogger(false));
|
|
310
|
-
let error = Error('More then one rows exists');
|
|
311
|
-
pgUtils.logError(logger, { error, sql, params, connection: this.db.connection });
|
|
312
|
-
throw error;
|
|
313
|
-
}
|
|
314
|
-
return res[0];
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
async queryFirst(sql: string, params?: any[] | {}, options?: SqlQueryOptions): Promise<any> {
|
|
318
|
-
let res = await this.query(sql, params, options);
|
|
319
|
-
return res[0];
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
/** @return one record's one field */
|
|
323
|
-
async queryOneField(sql: string, params?: any[] | {}, options?: SqlQueryOptions): Promise<any> {
|
|
324
|
-
let res = await this.query(sql, params, options);
|
|
325
|
-
if (!res.length) {
|
|
326
|
-
return null;
|
|
327
|
-
}
|
|
328
|
-
let fieldName = Object.keys(res[0])[0];
|
|
329
|
-
if (res.length > 1) {
|
|
330
|
-
let logger = (options && options.logger || this.getLogger(false));
|
|
331
|
-
let error = Error('More then one field exists!');
|
|
332
|
-
pgUtils.logError(logger, { error, sql, params, connection: this.db.connection });
|
|
333
|
-
throw error;
|
|
334
|
-
}
|
|
335
|
-
return res.length == 1 ? res[0][fieldName] : null;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
/** @return one column for the matching records */
|
|
339
|
-
async queryOneColumn(sql: string, params?: any[] | {}, options?: SqlQueryOptions): Promise<any[]> {
|
|
340
|
-
let res = await this.query(sql, params, options);
|
|
341
|
-
if (!res.length) {
|
|
342
|
-
return [];
|
|
343
|
-
}
|
|
344
|
-
let fieldName = Object.keys(res[0])[0];
|
|
345
|
-
return res.map(r => r[fieldName]);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
private postProcessFields(rows: any[], fields: ResultFieldType[], logger: PgDbLogger) {
|
|
349
|
-
pgUtils.postProcessResult(rows, fields, this.db.pgdbTypeParsers);
|
|
350
|
-
if (this.db.postProcessResult) this.db.postProcessResult(rows, fields, logger);
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
private async checkAndFixOids(connection: pg.PoolClient, fields: ResultFieldType[]) {
|
|
354
|
-
if (fields) {
|
|
355
|
-
let oidList = fields.map(field => field.dataTypeID);
|
|
356
|
-
return this.db.resetMissingParsers(connection, oidList);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
private hasUnknownOids(fields: ResultFieldType[]): boolean {
|
|
361
|
-
let oidList = fields.map(field => field.dataTypeID);
|
|
362
|
-
let unknownOids = oidList.filter(oid => !this.db.knownOids[oid]);
|
|
363
|
-
return !!unknownOids.length;
|
|
364
|
-
}
|
|
365
|
-
}
|