pogi 2.10.2 → 3.0.0-beta2
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/.vscode/launch.json +47 -15
- package/CHANGELOG.md +11 -0
- package/docs/API/PgDb.md +25 -0
- package/docs/notification.md +19 -0
- package/jest.config.js +23 -0
- package/lib/bin/generateInterface.js +3 -3
- package/lib/bin/generateInterface.js.map +1 -1
- package/lib/connectionOptions.d.ts +10 -0
- package/lib/index.d.ts +1 -1
- package/lib/pgConverters.d.ts +9 -8
- package/lib/pgConverters.js +46 -32
- package/lib/pgConverters.js.map +1 -1
- package/lib/pgConverters.test.d.ts +1 -0
- package/lib/pgConverters.test.js +13 -0
- package/lib/pgConverters.test.js.map +1 -0
- package/lib/pgDb.d.ts +27 -27
- package/lib/pgDb.js +293 -100
- package/lib/pgDb.js.map +1 -1
- package/lib/pgDb.test.d.ts +1 -0
- package/lib/pgDb.test.js +1126 -0
- package/lib/pgDb.test.js.map +1 -0
- package/lib/pgDbInterface.d.ts +53 -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/pgDbOperators.test.d.ts +1 -0
- package/lib/pgDbOperators.test.js +313 -0
- package/lib/pgDbOperators.test.js.map +1 -0
- package/lib/pgSchema.d.ts +10 -9
- package/lib/pgSchema.js.map +1 -1
- package/lib/pgSchemaInterface.d.ts +12 -0
- package/lib/pgSchemaInterface.js +3 -0
- package/lib/pgSchemaInterface.js.map +1 -0
- package/lib/pgTable.d.ts +15 -40
- package/lib/pgTable.js +54 -54
- package/lib/pgTable.js.map +1 -1
- package/lib/pgTableInterface.d.ts +102 -0
- package/lib/pgTableInterface.js +4 -0
- package/lib/pgTableInterface.js.map +1 -0
- package/lib/pgUtils.d.ts +16 -6
- package/lib/pgUtils.js +162 -31
- package/lib/pgUtils.js.map +1 -1
- package/lib/queryAble.d.ts +20 -53
- package/lib/queryAble.js +149 -80
- package/lib/queryAble.js.map +1 -1
- package/lib/queryAbleInterface.d.ts +55 -0
- package/lib/queryAbleInterface.js +7 -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/mkdocs.yml +1 -0
- package/package.json +21 -11
- package/src/bin/generateInterface.ts +2 -2
- package/src/connectionOptions.ts +48 -13
- package/src/index.d.ts +7 -0
- package/src/index.ts +1 -1
- package/src/pgConverters.test.ts +10 -0
- package/src/pgConverters.ts +34 -22
- package/src/pgDb.test.ts +1324 -0
- package/src/pgDb.ts +318 -122
- package/src/pgDbInterface.ts +57 -0
- package/src/pgDbOperators.test.ts +478 -0
- package/src/pgDbOperators.ts +45 -22
- package/src/pgSchema.ts +10 -9
- package/src/pgSchemaInterface.ts +12 -0
- package/src/pgTable.ts +66 -98
- package/src/pgTableInterface.ts +131 -0
- package/src/pgUtils.ts +166 -42
- package/src/queryAble.ts +167 -125
- package/src/queryAbleInterface.ts +104 -0
- package/src/queryWhere.ts +42 -43
- package/{spec/resources → src/test}/init.sql +23 -0
- package/src/test/pgServiceRestartTest.ts +1500 -0
- package/{spec/resources → src/test}/throw_exception.sql +0 -0
- package/{spec/resources → src/test}/tricky.sql +0 -0
- package/{src/tsconfig.json → tsconfig.json} +12 -11
- package/spec/run.js +0 -5
- package/spec/support/jasmine.json +0 -9
- package/src/test/pgDbOperatorSpec.ts +0 -492
- package/src/test/pgDbSpec.ts +0 -994
package/src/pgUtils.ts
CHANGED
|
@@ -1,28 +1,96 @@
|
|
|
1
|
-
import {QueryOptions,
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
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";
|
|
4
6
|
import * as _ from 'lodash';
|
|
7
|
+
import util = require('util');
|
|
8
|
+
import * as pg from 'pg';
|
|
9
|
+
import { ForceEscapeColumnsOptions } from "./connectionOptions";
|
|
5
10
|
|
|
6
|
-
const util = require('util');
|
|
7
11
|
const NAMED_PARAMS_REGEXP = /(?:^|[^:]):(!?[a-zA-Z0-9_]+)/g; // do not convert "::type cast"
|
|
8
|
-
const ASC_DESC_REGEXP =
|
|
12
|
+
const ASC_DESC_REGEXP = /^\s*(.+?)(?:\s+(asc|desc))?\s*$/i;
|
|
9
13
|
|
|
10
14
|
export let pgUtils = {
|
|
11
15
|
|
|
12
|
-
logError(logger: PgDbLogger, options: { error?: string|Error, sql: string, params: any, connection }) {
|
|
16
|
+
logError(logger: PgDbLogger, options: { error?: string | Error, sql: string, params: any, connection?: pg.PoolClient | null }) {
|
|
13
17
|
let { error, sql, params, connection } = options;
|
|
14
18
|
logger.error(error, sql, util.inspect(logger.paramSanitizer ? logger.paramSanitizer(params) : params, false, null), connection ? connection.processID : null);
|
|
15
19
|
},
|
|
16
20
|
|
|
17
|
-
|
|
21
|
+
quoteFieldNameInsecure(f: string) {
|
|
18
22
|
return f.indexOf('"') == -1 && f.indexOf('(') == -1 ? '"' + f + '"' : f;
|
|
19
23
|
},
|
|
20
24
|
|
|
21
|
-
|
|
25
|
+
quoteFieldName(f: string) {
|
|
26
|
+
if (typeof f === 'string' && f.length) {
|
|
27
|
+
return `"${f
|
|
28
|
+
.replace(/^\s*"*/, '') // trim "
|
|
29
|
+
.replace(/"*\s*$/, '')
|
|
30
|
+
.replace(/"/g, '""')}"`;
|
|
31
|
+
} else {
|
|
32
|
+
throw new Error(`Invalid field: ${f}`);
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
quoteFieldNameOrPositionInsecure(f: string | number): string {
|
|
37
|
+
if (Number.isInteger(+f)) {
|
|
38
|
+
if (!Number.isInteger(+f) || +f < 1) throw new Error(`Invalid field: ${f}`);
|
|
39
|
+
return '' + f;
|
|
40
|
+
} else if (typeof f === 'string' && f.length) {
|
|
41
|
+
return f.indexOf('"') == -1 && f.indexOf('(') == -1 ? '"' + f + '"' : f;
|
|
42
|
+
} else {
|
|
43
|
+
throw new Error(`Invalid field: ${f}`);
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
/** ex. for order by column position can be use, which needs no quote */
|
|
48
|
+
quoteFieldNameOrPosition(f: string | number): string {
|
|
49
|
+
if (Number.isInteger(+f)) {
|
|
50
|
+
if (!Number.isInteger(+f) || +f < 1) throw new Error(`Invalid field: ${f}`);
|
|
51
|
+
return '' + f;
|
|
52
|
+
} else if (typeof f === 'string' && f.length) {
|
|
53
|
+
return `"${f
|
|
54
|
+
.replace(/^\s*"*\s*/, '') // trim "
|
|
55
|
+
.replace(/\s*"*\s*$/, '')
|
|
56
|
+
.replace(/"/g, '""')}"`;
|
|
57
|
+
} else {
|
|
58
|
+
throw new Error(`Invalid field: ${f}`);
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
/**
|
|
62
|
+
* https://www.postgresql.org/docs/current/functions-json.html
|
|
63
|
+
* column->'a' ,
|
|
64
|
+
* column -> 3
|
|
65
|
+
*/
|
|
66
|
+
quoteFieldNameJsonbOrPosition(f: string | number): string {
|
|
67
|
+
// treat numeric json keys as array indices, otherwise quote it
|
|
68
|
+
if (Number.isInteger(+f)) {
|
|
69
|
+
return '' + f;
|
|
70
|
+
} if (typeof f === 'string' && f.length) {
|
|
71
|
+
return `'${f
|
|
72
|
+
.replace(/^\s*'*/, '') // trim "
|
|
73
|
+
.replace(/'*\s*$/, '')
|
|
74
|
+
.replace(/'/g, "''")}'`;
|
|
75
|
+
} else {
|
|
76
|
+
throw new Error(`Invalid field: ${f}`);
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
processQueryFields<T>(options: QueryOptions, pgTable?: IPgTable<T>): string {
|
|
81
|
+
let escapeColumns = (
|
|
82
|
+
(pgTable?.db.config.forceEscapeColumns === true || (pgTable?.db.config.forceEscapeColumns as ForceEscapeColumnsOptions)?.select === true)
|
|
83
|
+
&& options.forceEscapeColumns !== false && (options.forceEscapeColumns as ForceEscapeColumnsOptions)?.select !== false)
|
|
84
|
+
|| options.forceEscapeColumns === true
|
|
85
|
+
|| (options.forceEscapeColumns as ForceEscapeColumnsOptions)?.select;
|
|
86
|
+
|
|
22
87
|
let s = options && options.distinct ? ' DISTINCT ' : ' ';
|
|
23
88
|
if (options && options.fields) {
|
|
24
89
|
if (Array.isArray(options.fields)) {
|
|
25
|
-
|
|
90
|
+
if (escapeColumns) {
|
|
91
|
+
return s + options.fields.map(pgUtils.quoteFieldName).join(', ');
|
|
92
|
+
}
|
|
93
|
+
return s + options.fields.map(pgUtils.quoteFieldNameInsecure).join(', ');
|
|
26
94
|
} else {
|
|
27
95
|
return s + options.fields;
|
|
28
96
|
}
|
|
@@ -56,7 +124,7 @@ export let pgUtils = {
|
|
|
56
124
|
sql2.push(sql.slice(lastIndex, NAMED_PARAMS_REGEXP.lastIndex - p[1].length - 1));
|
|
57
125
|
|
|
58
126
|
if (ddl) {
|
|
59
|
-
sql2.push(
|
|
127
|
+
sql2.push(pgUtils.quoteFieldName(params[name]));
|
|
60
128
|
} else {
|
|
61
129
|
params2.push(params[name]);
|
|
62
130
|
sql2.push('$' + params2.length);
|
|
@@ -64,7 +132,7 @@ export let pgUtils = {
|
|
|
64
132
|
lastIndex = NAMED_PARAMS_REGEXP.lastIndex;
|
|
65
133
|
p = NAMED_PARAMS_REGEXP.exec(sql);
|
|
66
134
|
}
|
|
67
|
-
sql2.push(sql.
|
|
135
|
+
sql2.push(sql.slice(lastIndex));
|
|
68
136
|
|
|
69
137
|
return {
|
|
70
138
|
sql: sql2.join(''),
|
|
@@ -72,40 +140,93 @@ export let pgUtils = {
|
|
|
72
140
|
}
|
|
73
141
|
},
|
|
74
142
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
let
|
|
143
|
+
handleColumnEscapeGroupBy<T>(options: QueryOptions, pgTable?: IPgTable<T>): string {
|
|
144
|
+
if (!options.groupBy) return '';
|
|
145
|
+
let escapeColumns = (
|
|
146
|
+
(pgTable?.db.config.forceEscapeColumns === true || (pgTable?.db.config.forceEscapeColumns as ForceEscapeColumnsOptions)?.groupBy === true)
|
|
147
|
+
&& options.forceEscapeColumns !== false && (options.forceEscapeColumns as ForceEscapeColumnsOptions)?.groupBy !== false)
|
|
148
|
+
|| options.forceEscapeColumns === true
|
|
149
|
+
|| (options.forceEscapeColumns as ForceEscapeColumnsOptions)?.groupBy;
|
|
78
150
|
|
|
79
|
-
if (
|
|
151
|
+
if (escapeColumns) {
|
|
80
152
|
if (Array.isArray(options.groupBy)) {
|
|
81
|
-
|
|
153
|
+
return ' GROUP BY ' + options.groupBy.map(pgUtils.quoteFieldNameOrPosition).join(',');
|
|
82
154
|
} else {
|
|
83
|
-
|
|
155
|
+
return ' GROUP BY ' + pgUtils.quoteFieldNameOrPosition(options.groupBy);
|
|
84
156
|
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
157
|
+
} else {
|
|
158
|
+
if (Array.isArray(options.groupBy)) {
|
|
159
|
+
return ' GROUP BY ' + options.groupBy.map(pgUtils.quoteFieldNameOrPositionInsecure).join(',');
|
|
160
|
+
} else {
|
|
161
|
+
return ' GROUP BY ' + pgUtils.quoteFieldNameOrPositionInsecure(options.groupBy);
|
|
89
162
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
handleColumnEscapeOrderBy<T>(options: QueryOptions, pgTable: IPgTable<T>): string {
|
|
167
|
+
if (!options.orderBy) return '';
|
|
168
|
+
let sql = '';
|
|
169
|
+
let escapeColumns = (
|
|
170
|
+
(pgTable?.db.config.forceEscapeColumns === true || (pgTable?.db.config.forceEscapeColumns as ForceEscapeColumnsOptions)?.orderBy === true)
|
|
171
|
+
&& options.forceEscapeColumns !== false && (options.forceEscapeColumns as ForceEscapeColumnsOptions)?.orderBy !== false)
|
|
172
|
+
|| options.forceEscapeColumns === true
|
|
173
|
+
|| (options.forceEscapeColumns as ForceEscapeColumnsOptions)?.orderBy;
|
|
174
|
+
|
|
175
|
+
let orderBy = typeof options.orderBy === 'string' ? options.orderBy.split(',') : options.orderBy;
|
|
176
|
+
if (Array.isArray(orderBy)) {
|
|
177
|
+
let orderBy2: string[];
|
|
178
|
+
|
|
179
|
+
if (escapeColumns) {
|
|
180
|
+
orderBy2 = orderBy.map(v => {
|
|
181
|
+
if (typeof v === 'number') return pgUtils.quoteFieldNameOrPosition(v);
|
|
182
|
+
else if (typeof v !== 'string' || !v.length) throw new Error(`Invalid orderBy: ${v}`);
|
|
183
|
+
if (v[0] == '+') return pgUtils.quoteFieldNameOrPosition(v.slice(1));
|
|
184
|
+
if (v[0] == '-') return pgUtils.quoteFieldNameOrPosition(v.slice(1)) + ' desc';
|
|
185
|
+
let o = ASC_DESC_REGEXP.exec(v);
|
|
186
|
+
if (!o) throw new Error(`Invalid orderBy: ${v}`);
|
|
187
|
+
return `${pgUtils.quoteFieldNameOrPosition(o[1])} ${o[2] ?? ''}`;
|
|
188
|
+
});
|
|
96
189
|
} else {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
190
|
+
orderBy2 = orderBy.map(v => {
|
|
191
|
+
if (typeof v === 'number') return pgUtils.quoteFieldNameOrPositionInsecure(v);
|
|
192
|
+
else if (typeof v !== 'string' || !v.length) throw new Error(`Invalid orderBy: ${v}`);
|
|
193
|
+
if (v[0] == '+') return pgUtils.quoteFieldNameOrPositionInsecure(v.slice(1));
|
|
194
|
+
if (v[0] == '-') return pgUtils.quoteFieldNameOrPositionInsecure(v.slice(1)) + ' desc';
|
|
195
|
+
let o = ASC_DESC_REGEXP.exec(v);
|
|
196
|
+
if (!o) throw new Error(`Invalid orderBy: ${v}`);
|
|
197
|
+
return `${pgUtils.quoteFieldNameOrPositionInsecure(o[1])} ${o[2] ?? ''}`;
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
sql += ' ORDER BY ' + orderBy2.join(',');
|
|
201
|
+
} else {
|
|
202
|
+
throw new Error(`Invalid orderBy: ${options.orderBy}`);
|
|
203
|
+
}
|
|
204
|
+
return sql;
|
|
205
|
+
},
|
|
206
|
+
|
|
207
|
+
processQueryOptions<T>(options: QueryOptions, pgTable: IPgTable<T>): string {
|
|
208
|
+
options = options || {};
|
|
209
|
+
let sql = '';
|
|
210
|
+
|
|
211
|
+
if (options.groupBy) {
|
|
212
|
+
sql += pgUtils.handleColumnEscapeGroupBy(options, pgTable);
|
|
213
|
+
}
|
|
214
|
+
if (options.orderBy) {
|
|
215
|
+
sql += pgUtils.handleColumnEscapeOrderBy(options, pgTable);
|
|
216
|
+
|
|
217
|
+
if (options.orderByNullsFirst != null) {
|
|
218
|
+
sql += ' NULLS ' + options.orderByNullsFirst ? 'FIRST' : 'LAST';
|
|
100
219
|
}
|
|
101
220
|
}
|
|
102
221
|
if (options.limit) {
|
|
103
|
-
|
|
222
|
+
if (!Number.isInteger(options.limit) || options.limit < 0) throw new Error(`Invalid limit: ${options.limit}`);
|
|
223
|
+
sql += ` LIMIT ${options.limit}`;
|
|
104
224
|
}
|
|
105
225
|
if (options.offset) {
|
|
106
|
-
|
|
226
|
+
if (!Number.isInteger(options.offset) || options.offset < 0) throw new Error(`Invalid offset: ${options.offset}`);
|
|
227
|
+
sql += ` OFFSET ${options.offset}`;
|
|
107
228
|
}
|
|
108
|
-
if (options.forUpdate){
|
|
229
|
+
if (options.forUpdate) {
|
|
109
230
|
sql += ' FOR UPDATE';
|
|
110
231
|
}
|
|
111
232
|
return sql;
|
|
@@ -122,18 +243,18 @@ export let pgUtils = {
|
|
|
122
243
|
(param != null && fieldType == FieldType.TIME && !(param instanceof Date)) ? new Date(param) : param;
|
|
123
244
|
},
|
|
124
245
|
|
|
125
|
-
postProcessResult(res: any[], fields: ResultFieldType[], pgdbTypeParsers: { [oid: number]: (s:string) => any }) {
|
|
246
|
+
postProcessResult(res: any[], fields: ResultFieldType[], pgdbTypeParsers: { [oid: number]: (s: string) => any }) {
|
|
126
247
|
if (res) {
|
|
127
248
|
if (res[0] && !Array.isArray(res[0])) {
|
|
128
249
|
if (Object.keys(res[0]).length != fields.length) {
|
|
129
|
-
throw Error("Name collision for the query, two or more fields have the same name.");
|
|
250
|
+
throw new Error("Name collision for the query, two or more fields have the same name.");
|
|
130
251
|
}
|
|
131
252
|
}
|
|
132
253
|
pgUtils.convertTypes(res, fields, pgdbTypeParsers);
|
|
133
254
|
}
|
|
134
255
|
},
|
|
135
256
|
|
|
136
|
-
convertTypes(res: any[], fields: ResultFieldType[], pgdbTypeParsers: { [oid: number]: (s:string) => any }) {
|
|
257
|
+
convertTypes(res: any[], fields: ResultFieldType[], pgdbTypeParsers: { [oid: number]: (s: string) => any }) {
|
|
137
258
|
let isArrayMode = Array.isArray(res[0]);
|
|
138
259
|
fields.forEach((field, i) => {
|
|
139
260
|
if (pgdbTypeParsers[field.dataTypeID]) {
|
|
@@ -146,10 +267,10 @@ export let pgUtils = {
|
|
|
146
267
|
});
|
|
147
268
|
},
|
|
148
269
|
|
|
149
|
-
createFunctionCaller(q:
|
|
150
|
-
return async (...args) => {
|
|
151
|
-
let placeHolders = [];
|
|
152
|
-
let params = [];
|
|
270
|
+
createFunctionCaller(q: IQueryAble, fn: { schema: string, name: string, return_single_row: boolean, return_single_value: boolean }) {
|
|
271
|
+
return async (...args: any[]) => {
|
|
272
|
+
let placeHolders: string[] = [];
|
|
273
|
+
let params: any[] = [];
|
|
153
274
|
args.forEach((arg) => {
|
|
154
275
|
placeHolders.push('$' + (placeHolders.length + 1));
|
|
155
276
|
params.push(arg);
|
|
@@ -159,18 +280,21 @@ export let pgUtils = {
|
|
|
159
280
|
if (fn.return_single_value) {
|
|
160
281
|
let keys = res[0] ? Object.keys(res[0]) : [];
|
|
161
282
|
if (keys.length != 1) {
|
|
162
|
-
throw Error(`Return type error. schema: ${fn.schema} fn: ${fn.name} expected return type: single value, current value:` + JSON.stringify(res))
|
|
283
|
+
throw new Error(`Return type error. schema: ${fn.schema} fn: ${fn.name} expected return type: single value, current value:` + JSON.stringify(res))
|
|
163
284
|
}
|
|
164
285
|
res = res.map((r) => r[keys[0]]);
|
|
165
286
|
}
|
|
166
287
|
if (fn.return_single_row) {
|
|
167
288
|
if (res.length != 1) {
|
|
168
|
-
throw Error(`Return type error. schema: ${fn.schema} fn: ${fn.name} expected return type: single value, current value:` + JSON.stringify(res))
|
|
289
|
+
throw new Error(`Return type error. schema: ${fn.schema} fn: ${fn.name} expected return type: single value, current value:` + JSON.stringify(res))
|
|
169
290
|
}
|
|
170
291
|
return res[0];
|
|
171
292
|
} else {
|
|
172
293
|
return res;
|
|
173
294
|
}
|
|
174
295
|
}
|
|
296
|
+
},
|
|
297
|
+
escapeForLike(s: string): string {
|
|
298
|
+
return s.replace(/([\\%_])/g, '\\$1');
|
|
175
299
|
}
|
|
176
300
|
};
|