pogi 2.10.1 → 3.0.0-beta
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 +20 -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 +2 -2
- package/lib/bin/generateInterface.js.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/pgConverters.d.ts +2 -0
- package/lib/pgConverters.js +27 -10
- package/lib/pgConverters.js.map +1 -1
- package/lib/pgDb.d.ts +15 -6
- package/lib/pgDb.js +263 -67
- package/lib/pgDb.js.map +1 -1
- package/lib/pgTable.js +7 -7
- package/lib/pgTable.js.map +1 -1
- package/lib/pgUtils.d.ts +3 -1
- package/lib/pgUtils.js +61 -23
- package/lib/pgUtils.js.map +1 -1
- package/lib/queryAble.d.ts +16 -1
- package/lib/queryAble.js +124 -56
- package/lib/queryAble.js.map +1 -1
- package/lib/src/bin/generateInterface.d.ts +1 -0
- package/lib/src/bin/generateInterface.js +53 -0
- package/lib/src/bin/generateInterface.js.map +1 -0
- package/lib/src/connectionOptions.d.ts +34 -0
- package/lib/src/connectionOptions.js +3 -0
- package/lib/src/connectionOptions.js.map +1 -0
- package/lib/src/index.d.ts +6 -0
- package/lib/src/index.js +12 -0
- package/lib/src/index.js.map +1 -0
- package/lib/src/pgConverters.d.ts +9 -0
- package/lib/src/pgConverters.js +71 -0
- package/lib/src/pgConverters.js.map +1 -0
- package/lib/src/pgConverters.test.d.ts +1 -0
- package/lib/src/pgConverters.test.js +13 -0
- package/lib/src/pgConverters.test.js.map +1 -0
- package/lib/src/pgDb.d.ts +79 -0
- package/lib/src/pgDb.js +764 -0
- package/lib/src/pgDb.js.map +1 -0
- package/lib/src/pgDb.test.d.ts +1 -0
- package/lib/src/pgDb.test.js +1126 -0
- package/lib/src/pgDb.test.js.map +1 -0
- package/lib/src/pgDbInterface.js +11 -0
- package/lib/src/pgDbInterface.js.map +1 -0
- package/lib/src/pgDbLogger.d.ts +5 -0
- package/lib/src/pgDbLogger.js +3 -0
- package/lib/src/pgDbLogger.js.map +1 -0
- package/lib/src/pgDbOperators.d.ts +113 -0
- package/lib/src/pgDbOperators.js +41 -0
- package/lib/src/pgDbOperators.js.map +1 -0
- package/lib/src/pgDbOperators.test.d.ts +1 -0
- package/lib/src/pgDbOperators.test.js +313 -0
- package/lib/src/pgDbOperators.test.js.map +1 -0
- package/lib/src/pgSchema.d.ts +17 -0
- package/lib/src/pgSchema.js +16 -0
- package/lib/src/pgSchema.js.map +1 -0
- package/lib/src/pgSchemaInterface.d.ts +12 -0
- package/lib/src/pgSchemaInterface.js +3 -0
- package/lib/src/pgSchemaInterface.js.map +1 -0
- package/lib/src/pgTable.d.ts +105 -0
- package/lib/src/pgTable.js +322 -0
- package/lib/src/pgTable.js.map +1 -0
- package/lib/src/pgTableInterface.d.ts +102 -0
- package/lib/src/pgTableInterface.js +4 -0
- package/lib/src/pgTableInterface.js.map +1 -0
- package/lib/src/pgUtils.d.ts +41 -0
- package/lib/src/pgUtils.js +282 -0
- package/lib/src/pgUtils.js.map +1 -0
- package/lib/src/queryAble.d.ts +40 -0
- package/lib/src/queryAble.js +338 -0
- package/lib/src/queryAble.js.map +1 -0
- package/lib/src/queryAbleInterface.d.ts +59 -0
- package/lib/src/queryAbleInterface.js +7 -0
- package/lib/src/queryAbleInterface.js.map +1 -0
- package/lib/src/queryWhere.d.ts +8 -0
- package/lib/src/queryWhere.js +245 -0
- package/lib/src/queryWhere.js.map +1 -0
- package/mkdocs.yml +1 -0
- package/package.json +23 -14
- package/src/bin/generateInterface.ts +2 -2
- package/src/connectionOptions.ts +46 -13
- package/src/index.d.ts +7 -0
- package/src/pgConverters.test.ts +10 -0
- package/src/pgConverters.ts +34 -22
- package/src/pgDb.test.ts +1324 -0
- package/src/pgDb.ts +321 -125
- 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 +65 -97
- package/src/pgTableInterface.ts +131 -0
- package/src/pgUtils.ts +156 -42
- package/src/queryAble.ts +167 -125
- package/src/queryAbleInterface.ts +108 -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} +9 -6
- 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,92 @@
|
|
|
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';
|
|
5
9
|
|
|
6
|
-
const util = require('util');
|
|
7
10
|
const NAMED_PARAMS_REGEXP = /(?:^|[^:]):(!?[a-zA-Z0-9_]+)/g; // do not convert "::type cast"
|
|
8
|
-
const ASC_DESC_REGEXP =
|
|
11
|
+
const ASC_DESC_REGEXP = /^\s*(.+?)(?:\s+(asc|desc))?\s*$/i;
|
|
9
12
|
|
|
10
13
|
export let pgUtils = {
|
|
11
14
|
|
|
12
|
-
logError(logger: PgDbLogger, options: { error?: string|Error, sql: string, params: any, connection }) {
|
|
15
|
+
logError(logger: PgDbLogger, options: { error?: string | Error, sql: string, params: any, connection?: pg.PoolClient | null }) {
|
|
13
16
|
let { error, sql, params, connection } = options;
|
|
14
17
|
logger.error(error, sql, util.inspect(logger.paramSanitizer ? logger.paramSanitizer(params) : params, false, null), connection ? connection.processID : null);
|
|
15
18
|
},
|
|
16
19
|
|
|
17
|
-
|
|
20
|
+
quoteFieldNameInsecure(f: string) {
|
|
18
21
|
return f.indexOf('"') == -1 && f.indexOf('(') == -1 ? '"' + f + '"' : f;
|
|
19
22
|
},
|
|
20
23
|
|
|
21
|
-
|
|
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
|
+
|
|
22
83
|
let s = options && options.distinct ? ' DISTINCT ' : ' ';
|
|
23
84
|
if (options && options.fields) {
|
|
24
85
|
if (Array.isArray(options.fields)) {
|
|
25
|
-
|
|
86
|
+
if (escapeColumns) {
|
|
87
|
+
return s + options.fields.map(pgUtils.quoteFieldName).join(', ');
|
|
88
|
+
}
|
|
89
|
+
return s + options.fields.map(pgUtils.quoteFieldNameInsecure).join(', ');
|
|
26
90
|
} else {
|
|
27
91
|
return s + options.fields;
|
|
28
92
|
}
|
|
@@ -56,7 +120,7 @@ export let pgUtils = {
|
|
|
56
120
|
sql2.push(sql.slice(lastIndex, NAMED_PARAMS_REGEXP.lastIndex - p[1].length - 1));
|
|
57
121
|
|
|
58
122
|
if (ddl) {
|
|
59
|
-
sql2.push(
|
|
123
|
+
sql2.push(pgUtils.quoteFieldName(params[name]));
|
|
60
124
|
} else {
|
|
61
125
|
params2.push(params[name]);
|
|
62
126
|
sql2.push('$' + params2.length);
|
|
@@ -64,7 +128,7 @@ export let pgUtils = {
|
|
|
64
128
|
lastIndex = NAMED_PARAMS_REGEXP.lastIndex;
|
|
65
129
|
p = NAMED_PARAMS_REGEXP.exec(sql);
|
|
66
130
|
}
|
|
67
|
-
sql2.push(sql.
|
|
131
|
+
sql2.push(sql.slice(lastIndex));
|
|
68
132
|
|
|
69
133
|
return {
|
|
70
134
|
sql: sql2.join(''),
|
|
@@ -72,40 +136,87 @@ export let pgUtils = {
|
|
|
72
136
|
}
|
|
73
137
|
},
|
|
74
138
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
let
|
|
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;
|
|
78
143
|
|
|
79
|
-
if (
|
|
144
|
+
if (escapeColumns) {
|
|
80
145
|
if (Array.isArray(options.groupBy)) {
|
|
81
|
-
|
|
146
|
+
return ' GROUP BY ' + options.groupBy.map(pgUtils.quoteFieldNameOrPosition).join(',');
|
|
82
147
|
} else {
|
|
83
|
-
|
|
148
|
+
return ' GROUP BY ' + pgUtils.quoteFieldNameOrPosition(options.groupBy);
|
|
84
149
|
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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);
|
|
89
155
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
+
});
|
|
96
179
|
} else {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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';
|
|
100
209
|
}
|
|
101
210
|
}
|
|
102
211
|
if (options.limit) {
|
|
103
|
-
|
|
212
|
+
if (!Number.isInteger(options.limit) || options.limit < 0) throw new Error(`Invalid limit: ${options.limit}`);
|
|
213
|
+
sql += ` LIMIT ${options.limit}`;
|
|
104
214
|
}
|
|
105
215
|
if (options.offset) {
|
|
106
|
-
|
|
216
|
+
if (!Number.isInteger(options.offset) || options.offset < 0) throw new Error(`Invalid offset: ${options.offset}`);
|
|
217
|
+
sql += ` OFFSET ${options.offset}`;
|
|
107
218
|
}
|
|
108
|
-
if (options.forUpdate){
|
|
219
|
+
if (options.forUpdate) {
|
|
109
220
|
sql += ' FOR UPDATE';
|
|
110
221
|
}
|
|
111
222
|
return sql;
|
|
@@ -122,18 +233,18 @@ export let pgUtils = {
|
|
|
122
233
|
(param != null && fieldType == FieldType.TIME && !(param instanceof Date)) ? new Date(param) : param;
|
|
123
234
|
},
|
|
124
235
|
|
|
125
|
-
postProcessResult(res: any[], fields: ResultFieldType[], pgdbTypeParsers: { [oid: number]: (s:string) => any }) {
|
|
236
|
+
postProcessResult(res: any[], fields: ResultFieldType[], pgdbTypeParsers: { [oid: number]: (s: string) => any }) {
|
|
126
237
|
if (res) {
|
|
127
238
|
if (res[0] && !Array.isArray(res[0])) {
|
|
128
239
|
if (Object.keys(res[0]).length != fields.length) {
|
|
129
|
-
throw Error("Name collision for the query, two or more fields have the same name.");
|
|
240
|
+
throw new Error("Name collision for the query, two or more fields have the same name.");
|
|
130
241
|
}
|
|
131
242
|
}
|
|
132
243
|
pgUtils.convertTypes(res, fields, pgdbTypeParsers);
|
|
133
244
|
}
|
|
134
245
|
},
|
|
135
246
|
|
|
136
|
-
convertTypes(res: any[], fields: ResultFieldType[], pgdbTypeParsers: { [oid: number]: (s:string) => any }) {
|
|
247
|
+
convertTypes(res: any[], fields: ResultFieldType[], pgdbTypeParsers: { [oid: number]: (s: string) => any }) {
|
|
137
248
|
let isArrayMode = Array.isArray(res[0]);
|
|
138
249
|
fields.forEach((field, i) => {
|
|
139
250
|
if (pgdbTypeParsers[field.dataTypeID]) {
|
|
@@ -146,10 +257,10 @@ export let pgUtils = {
|
|
|
146
257
|
});
|
|
147
258
|
},
|
|
148
259
|
|
|
149
|
-
createFunctionCaller(q:
|
|
150
|
-
return async (...args) => {
|
|
151
|
-
let placeHolders = [];
|
|
152
|
-
let params = [];
|
|
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[] = [];
|
|
153
264
|
args.forEach((arg) => {
|
|
154
265
|
placeHolders.push('$' + (placeHolders.length + 1));
|
|
155
266
|
params.push(arg);
|
|
@@ -159,18 +270,21 @@ export let pgUtils = {
|
|
|
159
270
|
if (fn.return_single_value) {
|
|
160
271
|
let keys = res[0] ? Object.keys(res[0]) : [];
|
|
161
272
|
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))
|
|
273
|
+
throw new Error(`Return type error. schema: ${fn.schema} fn: ${fn.name} expected return type: single value, current value:` + JSON.stringify(res))
|
|
163
274
|
}
|
|
164
275
|
res = res.map((r) => r[keys[0]]);
|
|
165
276
|
}
|
|
166
277
|
if (fn.return_single_row) {
|
|
167
278
|
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))
|
|
279
|
+
throw new Error(`Return type error. schema: ${fn.schema} fn: ${fn.name} expected return type: single value, current value:` + JSON.stringify(res))
|
|
169
280
|
}
|
|
170
281
|
return res[0];
|
|
171
282
|
} else {
|
|
172
283
|
return res;
|
|
173
284
|
}
|
|
174
285
|
}
|
|
286
|
+
},
|
|
287
|
+
escapeForLike(s: string): string {
|
|
288
|
+
return s.replace(/([\\%_])/g, '\\$1');
|
|
175
289
|
}
|
|
176
290
|
};
|