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/queryAble.ts
CHANGED
|
@@ -1,66 +1,26 @@
|
|
|
1
1
|
import { PgDbLogger } from "./pgDbLogger";
|
|
2
2
|
import { pgUtils } from "./pgUtils";
|
|
3
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"
|
|
4
11
|
|
|
5
|
-
const util = require('util');
|
|
6
|
-
const QueryStream = require('pg-query-stream');
|
|
7
|
-
const through = require('through');
|
|
8
|
-
|
|
9
|
-
export interface QueryOptions {
|
|
10
|
-
limit?: number;
|
|
11
|
-
offset?: number;
|
|
12
|
-
orderBy?: string | string[] | { [fieldName: string]: 'asc' | 'desc' };//free text or column list
|
|
13
|
-
groupBy?: string | string[];//free text or column list
|
|
14
|
-
fields?: string | string[];//free text or column list
|
|
15
|
-
logger?: PgDbLogger;
|
|
16
|
-
forUpdate?: boolean;
|
|
17
|
-
distinct?: boolean;
|
|
18
|
-
skipUndefined?: boolean;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface SqlQueryOptions {
|
|
22
|
-
logger?: PgDbLogger;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface ResultFieldType {
|
|
26
|
-
name: string,
|
|
27
|
-
tableID: number,
|
|
28
|
-
columnID: number,
|
|
29
|
-
dataTypeID: number,
|
|
30
|
-
dataTypeSize: number,
|
|
31
|
-
dataTypeModifier: number,
|
|
32
|
-
format: string
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface ResultType {
|
|
36
|
-
command: 'SELECT' | 'UPDATE' | 'DELETE',
|
|
37
|
-
rowCount: number,
|
|
38
|
-
oid: number,
|
|
39
|
-
rows: any[],
|
|
40
|
-
fields: ResultFieldType[],
|
|
41
|
-
_parsers: Function[][],
|
|
42
|
-
RowCtor: Function[],
|
|
43
|
-
rowsAsArray: boolean,
|
|
44
|
-
_getTypeParser: Function[]
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export interface PgRowResult {
|
|
48
|
-
columns: string[],
|
|
49
|
-
rows: any[]
|
|
50
|
-
}
|
|
51
12
|
|
|
52
13
|
let defaultLogger = {
|
|
53
14
|
log: () => { },
|
|
54
15
|
error: () => { }
|
|
55
16
|
};
|
|
56
17
|
|
|
57
|
-
export class QueryAble {
|
|
58
|
-
db;
|
|
59
|
-
schema;
|
|
60
|
-
protected logger
|
|
18
|
+
export abstract class QueryAble implements IQueryAble {
|
|
19
|
+
db!: IPgDb & IQueryAble; // assigned in async init
|
|
20
|
+
schema!: IPgSchema;
|
|
21
|
+
/*protected*/ logger!: PgDbLogger;
|
|
61
22
|
|
|
62
|
-
|
|
63
|
-
}
|
|
23
|
+
public static connectionErrorListener = () => { }
|
|
64
24
|
|
|
65
25
|
setLogger(logger: PgDbLogger) {
|
|
66
26
|
this.logger = logger;
|
|
@@ -84,15 +44,18 @@ export class QueryAble {
|
|
|
84
44
|
* e.g. query('select * from a.b where id=$1;',['the_stage_is_set']);
|
|
85
45
|
* e.g. query('select * from :!schema.:!table where id=:id;',{schema:'a',table:'b', id:'the_stage_is_set'});
|
|
86
46
|
*/
|
|
87
|
-
async query(sql: string, params?: any[] | {}, options?: SqlQueryOptions): Promise<any[]> {
|
|
47
|
+
async query(sql: string, params?: any[] | {} | null, options?: SqlQueryOptions): Promise<any[]> {
|
|
88
48
|
let connection = this.db.connection;
|
|
89
49
|
let logger = (options && options.logger || this.getLogger(false));
|
|
90
50
|
return this.internalQuery({ connection, sql, params, logger });
|
|
91
51
|
}
|
|
92
52
|
|
|
93
|
-
protected async internalQuery(options: { connection, sql: string, params?: any, logger
|
|
94
|
-
protected async internalQuery(options: { connection, sql: string, params?: any, logger
|
|
95
|
-
protected async internalQuery(options: { connection, sql: string, params?: any, logger
|
|
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
|
+
}
|
|
96
59
|
let { connection, sql, params, logger } = options;
|
|
97
60
|
logger = logger || this.getLogger(false);
|
|
98
61
|
|
|
@@ -102,40 +65,38 @@ export class QueryAble {
|
|
|
102
65
|
sql = p.sql;
|
|
103
66
|
params = p.params;
|
|
104
67
|
}
|
|
105
|
-
|
|
68
|
+
let query = options?.rowMode ? { text: sql, values: params, rowMode: 'array' } : { text: sql, values: params };
|
|
69
|
+
let res;
|
|
106
70
|
if (connection) {
|
|
107
71
|
logger.log('reused connection', sql, util.inspect(params, false, null), connection.processID);
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if (this.db.postProcessResult) this.db.postProcessResult(res.rows, res.fields, logger);
|
|
111
|
-
|
|
112
|
-
return options?.rowMode ? { columns: (res.fields || []).map(f => f.name), rows: res.rows || [] } : res.rows;
|
|
72
|
+
res = await connection.query(query);
|
|
73
|
+
await this.checkAndFixOids(connection, res.fields);
|
|
113
74
|
} else {
|
|
114
75
|
connection = await this.db.pool.connect();
|
|
115
76
|
logger.log('new connection', sql, util.inspect(params, false, null), connection.processID);
|
|
116
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) {
|
|
117
91
|
try {
|
|
118
|
-
|
|
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
|
|
119
95
|
connection.release();
|
|
120
|
-
connection = null;
|
|
121
|
-
pgUtils.postProcessResult(res.rows, res.fields, this.db.pgdbTypeParsers);
|
|
122
|
-
if (this.db.postProcessResult) this.db.postProcessResult(res.rows, res.fields, logger);
|
|
123
|
-
|
|
124
|
-
return options?.rowMode ? { columns: (res.fields || []).map(f => f.name), rows: res.rows || [] } : res.rows;
|
|
125
96
|
} catch (e) {
|
|
126
|
-
|
|
127
|
-
try {
|
|
128
|
-
if (connection)
|
|
129
|
-
connection.release();
|
|
130
|
-
} catch (e) {
|
|
131
|
-
logger.error('connection error2', e.message);
|
|
132
|
-
}
|
|
133
|
-
connection = null;
|
|
134
|
-
throw e;
|
|
97
|
+
logger.error('connection error', (<Error>e).message);
|
|
135
98
|
}
|
|
136
99
|
}
|
|
137
|
-
} catch (e) {
|
|
138
|
-
pgUtils.logError(logger, { error: e, sql, params, connection });
|
|
139
100
|
throw e;
|
|
140
101
|
}
|
|
141
102
|
}
|
|
@@ -153,128 +114,191 @@ export class QueryAble {
|
|
|
153
114
|
/**
|
|
154
115
|
* If the callback function return true, the connection will be closed.
|
|
155
116
|
*/
|
|
156
|
-
async queryWithOnCursorCallback(sql: string, params: any[] |
|
|
157
|
-
|
|
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;
|
|
158
124
|
|
|
159
125
|
try {
|
|
160
126
|
if (params && !Array.isArray(params)) {
|
|
161
127
|
let p = pgUtils.processNamedParams(sql, params);
|
|
162
128
|
sql = p.sql;
|
|
163
129
|
params = p.params;
|
|
130
|
+
} else {
|
|
131
|
+
positionedParams = params;
|
|
164
132
|
}
|
|
165
133
|
|
|
166
|
-
|
|
167
|
-
this.getLogger(false).log(sql, util.inspect(
|
|
168
|
-
let
|
|
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);
|
|
169
140
|
let stream = connection.query(query);
|
|
170
141
|
await new Promise((resolve, reject) => {
|
|
171
|
-
|
|
142
|
+
query.handleError = reject;
|
|
143
|
+
stream.on('data', (res: any) => {
|
|
172
144
|
try {
|
|
173
145
|
let fields = stream._result && stream._result.fields || stream.cursor._result && stream.cursor._result.fields;
|
|
174
|
-
|
|
175
|
-
|
|
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));
|
|
176
155
|
|
|
177
156
|
if (callback(res)) {
|
|
178
|
-
stream.
|
|
157
|
+
stream.destroy();
|
|
179
158
|
}
|
|
180
159
|
} catch (e) {
|
|
181
160
|
reject(e);
|
|
182
161
|
}
|
|
183
162
|
});
|
|
184
163
|
|
|
185
|
-
stream.on('
|
|
164
|
+
stream.on('close', resolve);
|
|
186
165
|
stream.on('error', reject);
|
|
187
166
|
});
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
this.getLogger(false).log(sql, util.inspect(params, false, null), connection.processID);
|
|
193
|
-
let query = new QueryStream(sql, params);
|
|
194
|
-
let stream = connection.query(query);
|
|
167
|
+
if (fieldsToFix) {
|
|
168
|
+
await this.checkAndFixOids(connection, fieldsToFix);
|
|
169
|
+
query = new QueryStream(sql, positionedParams);
|
|
170
|
+
stream = connection.query(query);
|
|
195
171
|
await new Promise((resolve, reject) => {
|
|
196
|
-
|
|
172
|
+
query.handleError = reject;
|
|
173
|
+
stream.on('data', (res: any) => {
|
|
197
174
|
try {
|
|
198
175
|
let fields = stream._result && stream._result.fields || stream.cursor._result && stream.cursor._result.fields;
|
|
199
|
-
|
|
200
|
-
if (this.db.postProcessResult) this.db.postProcessResult([res], fields, this.getLogger(false));
|
|
176
|
+
this.postProcessFields([res], fields, this.getLogger(false));
|
|
201
177
|
|
|
202
178
|
if (callback(res)) {
|
|
203
|
-
stream.
|
|
179
|
+
stream.destroy();
|
|
204
180
|
}
|
|
205
181
|
} catch (e) {
|
|
206
182
|
reject(e);
|
|
207
183
|
}
|
|
208
184
|
});
|
|
209
185
|
|
|
210
|
-
stream.on('
|
|
186
|
+
stream.on('close', resolve);
|
|
211
187
|
stream.on('error', reject);
|
|
212
188
|
});
|
|
213
|
-
} finally {
|
|
214
|
-
try {
|
|
215
|
-
connection.release();
|
|
216
|
-
} catch (e) {
|
|
217
|
-
this.getLogger(true).error('connection error', e.message);
|
|
218
|
-
}
|
|
219
189
|
}
|
|
220
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
|
+
}
|
|
221
203
|
} catch (e) {
|
|
222
|
-
|
|
223
|
-
|
|
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
|
+
}
|
|
224
212
|
throw e;
|
|
225
213
|
}
|
|
226
214
|
}
|
|
227
215
|
|
|
228
|
-
async queryAsStream(sql: string, params?: any[] |
|
|
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
|
+
}
|
|
229
220
|
let connection = this.db.connection;
|
|
230
221
|
let logger = (options && options.logger || this.getLogger(false));
|
|
231
|
-
let pgStream;
|
|
232
|
-
let
|
|
233
|
-
let
|
|
222
|
+
let pgStream: any;
|
|
223
|
+
let queriable = this;
|
|
224
|
+
let isFirst = true;
|
|
225
|
+
let convertTypeFilter = through(function (this: stream, data) {
|
|
234
226
|
try {
|
|
235
227
|
let fields = pgStream._result && pgStream._result.fields || pgStream.cursor._result && pgStream.cursor._result.fields;
|
|
236
|
-
|
|
237
|
-
|
|
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);
|
|
238
235
|
|
|
239
236
|
this.emit('data', data);
|
|
240
237
|
} catch (err) {
|
|
241
238
|
this.emit('error', err);
|
|
242
239
|
}
|
|
243
240
|
});
|
|
244
|
-
convertTypeFilter.on('error', (e) => {
|
|
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;
|
|
245
250
|
pgUtils.logError(logger, { error: e, sql, params, connection });
|
|
246
251
|
});
|
|
247
252
|
|
|
253
|
+
let positionedParams: any[] | undefined | null;
|
|
254
|
+
|
|
248
255
|
try {
|
|
249
256
|
if (params && !Array.isArray(params)) {
|
|
250
257
|
let p = pgUtils.processNamedParams(sql, params);
|
|
251
258
|
sql = p.sql;
|
|
252
259
|
params = p.params;
|
|
260
|
+
} else {
|
|
261
|
+
positionedParams = params;
|
|
253
262
|
}
|
|
254
263
|
|
|
255
264
|
if (connection) {
|
|
256
|
-
logger.log(sql, util.inspect(
|
|
257
|
-
let query = new QueryStream(sql,
|
|
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
|
+
};
|
|
258
270
|
pgStream = connection.query(query);
|
|
259
271
|
return pgStream.pipe(convertTypeFilter);
|
|
260
272
|
} else {
|
|
261
273
|
connection = await this.db.pool.connect();
|
|
262
|
-
logger.log(sql, util.inspect(
|
|
263
|
-
|
|
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
|
+
};
|
|
264
281
|
pgStream = connection.query(query);
|
|
265
282
|
pgStream.on('close', () => {
|
|
266
|
-
if (connection)
|
|
283
|
+
if (connection) {
|
|
284
|
+
connection.off('error', QueryAble.connectionErrorListener);
|
|
285
|
+
connection.release();
|
|
286
|
+
}
|
|
267
287
|
connection = null;
|
|
268
288
|
});
|
|
269
|
-
pgStream.on('error', (e) => {
|
|
270
|
-
pgUtils.logError(logger, { error: e, sql, params, connection });
|
|
271
|
-
|
|
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
|
+
}
|
|
272
296
|
connection = null;
|
|
273
297
|
});
|
|
274
298
|
return pgStream.pipe(convertTypeFilter);
|
|
275
299
|
}
|
|
276
300
|
} catch (e) {
|
|
277
|
-
pgUtils.logError(logger, { error: e, sql, params, connection });
|
|
301
|
+
pgUtils.logError(logger, { error: <Error>e, sql, params: positionedParams, connection });
|
|
278
302
|
throw e;
|
|
279
303
|
}
|
|
280
304
|
}
|
|
@@ -320,4 +344,22 @@ export class QueryAble {
|
|
|
320
344
|
let fieldName = Object.keys(res[0])[0];
|
|
321
345
|
return res.map(r => r[fieldName]);
|
|
322
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
|
+
}
|
|
323
365
|
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { PgDbLogger } from "./pgDbLogger";
|
|
2
|
+
import { IPgSchema } from "./pgSchemaInterface";
|
|
3
|
+
import * as stream from "stream";
|
|
4
|
+
import { ResultFieldType, IPgDb } from "./pgDbInterface";
|
|
5
|
+
import { ForceEscapeColumnsOptions } from "./connectionOptions";
|
|
6
|
+
|
|
7
|
+
export interface QueryOptions {
|
|
8
|
+
limit?: number;
|
|
9
|
+
offset?: number;
|
|
10
|
+
orderBy?: string | string[] | { [fieldName: string]: 'asc' | 'desc' };//free text or column list
|
|
11
|
+
/**
|
|
12
|
+
* only used with orderBy
|
|
13
|
+
* true -> nulls first,
|
|
14
|
+
* false -> nulls last
|
|
15
|
+
*/
|
|
16
|
+
orderByNullsFirst?: boolean;
|
|
17
|
+
groupBy?: string | string[];//free text or column list
|
|
18
|
+
fields?: string | string[];//free text or column list
|
|
19
|
+
logger?: PgDbLogger;
|
|
20
|
+
forUpdate?: boolean;
|
|
21
|
+
distinct?: boolean;
|
|
22
|
+
skipUndefined?: boolean;
|
|
23
|
+
forceEscapeColumns?: boolean | ForceEscapeColumnsOptions
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface SqlQueryOptions {
|
|
27
|
+
logger?: PgDbLogger;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
export interface ResultType {
|
|
33
|
+
command: 'SELECT' | 'UPDATE' | 'DELETE',
|
|
34
|
+
rowCount: number,
|
|
35
|
+
oid: number,
|
|
36
|
+
rows: any[],
|
|
37
|
+
fields: ResultFieldType[],
|
|
38
|
+
_parsers: Function[][],
|
|
39
|
+
RowCtor: Function[],
|
|
40
|
+
rowsAsArray: boolean,
|
|
41
|
+
_getTypeParser: Function[]
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface PgRowResult {
|
|
45
|
+
columns: string[],
|
|
46
|
+
rows: any[]
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let defaultLogger = {
|
|
50
|
+
log: () => { },
|
|
51
|
+
error: () => { }
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
export interface IQueryAble {
|
|
57
|
+
db: IPgDb & IQueryAble; // assigned in async init
|
|
58
|
+
schema: IPgSchema;
|
|
59
|
+
logger: PgDbLogger;
|
|
60
|
+
|
|
61
|
+
/*connectionErrorListener : () => { }*/
|
|
62
|
+
|
|
63
|
+
setLogger(logger: PgDbLogger): void
|
|
64
|
+
|
|
65
|
+
getLogger(useConsoleAsDefault: boolean): PgDbLogger
|
|
66
|
+
|
|
67
|
+
/** alias to {@link query} */
|
|
68
|
+
run(sql: string, params?: any[] | {}, options?: SqlQueryOptions): Promise<any[]>
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Params can be
|
|
72
|
+
* 1) array, then sql should have $1 $2 for placeholders
|
|
73
|
+
* 2) object, then sql should have:
|
|
74
|
+
* :example -> for params in statements (set/where), will be transformed to $1 $2 ...
|
|
75
|
+
* :!example -> for DDL names (schema, table, column), will be replaced in the query
|
|
76
|
+
* e.g. query('select * from a.b where id=$1;',['the_stage_is_set']);
|
|
77
|
+
* e.g. query('select * from :!schema.:!table where id=:id;',{schema:'a',table:'b', id:'the_stage_is_set'});
|
|
78
|
+
*/
|
|
79
|
+
query(sql: string, params?: any[] | {} | null, options?: SqlQueryOptions): Promise<any[]>
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Same as query but response is two array: columns and rows and rows are arrays also not objects
|
|
83
|
+
* This is useful for queries which have colliding column names
|
|
84
|
+
*/
|
|
85
|
+
queryAsRows(sql: string, params?: any[] | {}, options?: SqlQueryOptions): Promise<PgRowResult>
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* If the callback function return true, the connection will be closed.
|
|
89
|
+
*/
|
|
90
|
+
queryWithOnCursorCallback(sql: string, params: any[] | Record<string, any> | null, options: SqlQueryOptions | null, callback: (res: any) => any): Promise<void>
|
|
91
|
+
|
|
92
|
+
queryAsStream(sql: string, params?: any[] | Record<string, any> | null, options?: SqlQueryOptions | null): Promise<stream.Readable>
|
|
93
|
+
|
|
94
|
+
queryOne(sql: string, params?: any[] | {}, options?: SqlQueryOptions): Promise<any>
|
|
95
|
+
|
|
96
|
+
queryFirst(sql: string, params?: any[] | {}, options?: SqlQueryOptions): Promise<any>
|
|
97
|
+
|
|
98
|
+
/** @return one record's one field */
|
|
99
|
+
queryOneField(sql: string, params?: any[] | {}, options?: SqlQueryOptions): Promise<any>
|
|
100
|
+
|
|
101
|
+
/** @return one column for the matching records */
|
|
102
|
+
queryOneColumn(sql: string, params?: any[] | {}, options?: SqlQueryOptions): Promise<any[]>
|
|
103
|
+
|
|
104
|
+
}
|