@m2k-5f/pgtx 2.7.2 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +65 -37
  2. package/dist/batch.d.ts +4 -1
  3. package/dist/batch.d.ts.map +1 -1
  4. package/dist/batch.js +14 -7
  5. package/dist/clauses/fragment.clause.js +1 -1
  6. package/dist/connection.d.ts +17 -16
  7. package/dist/connection.d.ts.map +1 -1
  8. package/dist/connection.js +231 -175
  9. package/dist/error.d.ts +6 -1
  10. package/dist/error.d.ts.map +1 -1
  11. package/dist/error.js +26 -7
  12. package/dist/pool.d.ts +1 -1
  13. package/dist/pool.d.ts.map +1 -1
  14. package/dist/pool.js +5 -3
  15. package/dist/protocol/connection-request-writer.d.ts +107 -11
  16. package/dist/protocol/connection-request-writer.d.ts.map +1 -1
  17. package/dist/protocol/connection-request-writer.js +442 -48
  18. package/dist/protocol/connection-response-reader.d.ts +75 -30
  19. package/dist/protocol/connection-response-reader.d.ts.map +1 -1
  20. package/dist/protocol/connection-response-reader.js +285 -289
  21. package/dist/protocol/constants.d.ts +37 -30
  22. package/dist/protocol/constants.d.ts.map +1 -1
  23. package/dist/protocol/constants.js +43 -37
  24. package/dist/protocol/socket-authorization.d.ts.map +1 -1
  25. package/dist/protocol/socket-authorization.js +3 -3
  26. package/dist/protocol/socket-connector.d.ts +4 -4
  27. package/dist/protocol/socket-connector.d.ts.map +1 -1
  28. package/dist/protocol/socket-connector.js +2 -2
  29. package/dist/protocol/types.d.ts +14 -0
  30. package/dist/protocol/types.d.ts.map +1 -0
  31. package/dist/protocol/types.js +264 -0
  32. package/dist/query.d.ts +34 -24
  33. package/dist/query.d.ts.map +1 -1
  34. package/dist/query.js +45 -30
  35. package/dist/queue.d.ts +1 -0
  36. package/dist/queue.d.ts.map +1 -1
  37. package/dist/queue.js +1 -0
  38. package/dist/types.d.ts +3 -1
  39. package/dist/types.d.ts.map +1 -1
  40. package/dist/utils/template-compiler.d.ts +5 -2
  41. package/dist/utils/template-compiler.d.ts.map +1 -1
  42. package/dist/utils/template-compiler.js +4 -1
  43. package/dist/utils.d.ts +8 -0
  44. package/dist/utils.d.ts.map +1 -0
  45. package/dist/utils.js +73 -0
  46. package/package.json +2 -2
@@ -1,16 +1,15 @@
1
- import { ConnectionRequestWriter } from "./protocol/connection-request-writer";
2
- import { ResponseTypes } from "./protocol/constants";
3
- import { compileSqlTemplate } from "./utils/template-compiler";
1
+ import { DescribeType, EMPTY_ARRAY, ResponseTypes } from "./protocol/constants";
2
+ import { compileSqlTemplate } from "./utils";
4
3
  import { Transaction } from "./transaction";
5
4
  import { SocketConnector } from "./protocol/socket-connector";
6
5
  import { Queue } from "./queue";
7
- import { CollectQuery, StreamQuery, ExecuteQuery } from "./query";
6
+ import { CollectQuery, StreamQuery, ExecuteQuery, ParseQuery } from "./query";
8
7
  import { sql } from ".";
9
8
  import { Begin, Future, Ok } from 'fluent-future';
10
9
  import { ErrConnectionClosed, ErrConnectionReconnecting } from "./error";
11
- import { Batch } from "./batch";
12
10
  import { nextTick } from "process";
13
11
  import { authorizeSocket, createSocket, upgradeSocket } from "./protocol/socket-authorization";
12
+ import { ConnectionRequestBuffer } from "./protocol/connection-request-writer";
14
13
  const shedule = {
15
14
  Immediate: setImmediate,
16
15
  afterMicrotask: setTimeout,
@@ -30,15 +29,50 @@ export class Connection {
30
29
  _nextStatement() {
31
30
  return `s-${this._stmtCounter++}`;
32
31
  }
32
+ _registerShedule() {
33
+ if (!this._sheduled) {
34
+ this._sheduled = true;
35
+ this._writer.clear();
36
+ shedule[this.config.syncShedule](() => this._shedule());
37
+ }
38
+ }
39
+ _shedule() {
40
+ if (this._reconnecting)
41
+ return;
42
+ this._writer.hasMore && this._socket.write(this._writer);
43
+ this._writer.clear();
44
+ this._sheduled = false;
45
+ }
46
+ _registerQuery(query) {
47
+ this._registerShedule();
48
+ if (query instanceof ParseQuery) {
49
+ this._writer
50
+ .writeParse(query.meta.statement, query.text)
51
+ .writeDescribe(DescribeType.Statement, query.meta.statement)
52
+ .writeSync();
53
+ this._queue.push(query);
54
+ return null;
55
+ }
56
+ const err = this._writer
57
+ .writeBind("", query.meta, query.args);
58
+ if (err)
59
+ return err;
60
+ this._writer
61
+ .writeExecute("")
62
+ .writeSync();
63
+ this._queue.push(query);
64
+ return null;
65
+ }
33
66
  constructor(socket, config) {
34
- this._activeBatch = null;
67
+ this._writer = ConnectionRequestBuffer.new(65536);
68
+ this._sheduled = false;
69
+ this._queue = new Queue();
70
+ this._executingCounter = 0;
35
71
  this._closing = null;
36
72
  this._closed = false;
37
73
  this._reconnecting = null;
38
- this._cachedBuffer = ConnectionRequestWriter.new();
39
- this._batchQueue = new Queue();
40
- this._parsed = new Map();
41
- this._parsing = new Map();
74
+ this._parsed = {};
75
+ this._parsing = {};
42
76
  this._listeningCallbacks = new Map();
43
77
  this._stmtCounter = 0;
44
78
  this.config = config;
@@ -54,7 +88,7 @@ export class Connection {
54
88
  logLevel: config.logLevel || 'error',
55
89
  int8toBigint: config.int8toBigint || false,
56
90
  queryTimeout: config.queryTimeout || 30000,
57
- syncShedule: config.syncShedule || 'afterMicrotask',
91
+ syncShedule: config.syncShedule || 'Immediate',
58
92
  ssl: config.caPath ? 'require' : (config.ssl || 'prefer')
59
93
  };
60
94
  return createSocket(conf)
@@ -62,22 +96,6 @@ export class Connection {
62
96
  .andThen(socket => authorizeSocket(socket, conf))
63
97
  .andThen(socket => Ok(new Connection(socket, conf)));
64
98
  }
65
- _registerBatch() {
66
- if (!this._activeBatch) {
67
- const batch = new Batch(this._cachedBuffer.clear());
68
- this._activeBatch = batch;
69
- shedule[this.config.syncShedule](() => this._sync(batch));
70
- return batch;
71
- }
72
- return this._activeBatch;
73
- }
74
- _sync(batch) {
75
- if (this._reconnecting)
76
- return;
77
- this._socket.write(batch.end());
78
- this._activeBatch = null;
79
- this._batchQueue.push(batch);
80
- }
81
99
  /**
82
100
  * Runs a query, binding template values as `$1, $2, ...`.
83
101
  * Parameterized queries are cached as prepared statements.
@@ -88,32 +106,47 @@ export class Connection {
88
106
  query(templates, ...params) {
89
107
  if (this.isClosed)
90
108
  return Future.reject(ErrConnectionClosed);
109
+ const { text, args } = compileSqlTemplate(templates, params);
110
+ const resolvers = Future.withResolvers();
91
111
  if (this._reconnecting)
92
- return this._reconnecting.andThen(() => this._performQuery(templates, ...params));
93
- return this._performQuery(templates, ...params);
112
+ return this._reconnecting.andThen(() => this._performQuery(text, args, resolvers));
113
+ return this._performQuery(text, args, resolvers);
94
114
  }
95
- _performQuery(templates, ...params) {
96
- const { text, args } = compileSqlTemplate(templates, params);
115
+ _performQuery(text, args, resolvers) {
97
116
  if (this.config.logLevel === 'query') {
98
- console.log(`\nQUERY: ${text}\n${args.length !== 0 ? `ARGUMENTS: [${args}]\n` : ""}`);
117
+ console.log(`\n\x1b[36m┌─ QUERY ─────────────────────────────────────────\x1b[0m\n`
118
+ + `\x1b[36m│\x1b[0m ${text}\n`
119
+ + `${args.length !== 0 ? `\x1b[36m│\x1b[0m \x1b[90mArguments:\x1b[0m [${args}]\n` : ''}`
120
+ + `\x1b[36m└────────────────────────────────────────────────\x1b[0m`);
99
121
  }
100
- if (this._parsed.has(text)) {
101
- const meta = this._parsed.get(text);
102
- const query = new CollectQuery(meta.statement, text, args, meta.columns, this.config.queryTimeout);
103
- this._registerBatch().registerQuery(query);
104
- return query.future;
122
+ this._executingCounter++;
123
+ const parsed = this._parsed[text];
124
+ if (parsed) {
125
+ const query = new CollectQuery(parsed, text, args, parsed.columns, resolvers, this.config.queryTimeout);
126
+ const err = this._registerQuery(query);
127
+ if (err) {
128
+ this._executingCounter--;
129
+ query.error(err);
130
+ return query.resolvers.future;
131
+ }
132
+ return query.resolvers.future;
105
133
  }
106
- if (this._parsing.has(text)) {
107
- const statement = this._parsing.get(text);
108
- const query = new CollectQuery(statement, text, args, null, this.config.queryTimeout);
109
- this._registerBatch().registerQuery(query);
110
- return query.future;
134
+ if (!this._parsing[text]) {
135
+ const parseQuery = new ParseQuery({ statement: this._nextStatement(), columns: EMPTY_ARRAY, parameters: EMPTY_ARRAY }, text, Future.withResolvers(), this.config.queryTimeout);
136
+ this._registerQuery(parseQuery);
137
+ this._parsing[text] = parseQuery.resolvers.future;
111
138
  }
112
- const query = new CollectQuery(this._nextStatement(), text, args, null, this.config.queryTimeout);
113
- this._parsing.set(text, query.statement);
114
- this._registerBatch().registerParse(query);
115
- this._registerBatch().registerQuery(query);
116
- return query.future;
139
+ const parsing = this._parsing[text];
140
+ return parsing.andThen(meta => {
141
+ const query = new CollectQuery(meta, text, args, meta.columns, resolvers, this.config.queryTimeout);
142
+ const err = this._registerQuery(query);
143
+ if (err) {
144
+ this._executingCounter--;
145
+ query.error(err);
146
+ return query.resolvers.future;
147
+ }
148
+ return query.resolvers.future;
149
+ });
117
150
  }
118
151
  /**
119
152
  * Like {@link query}, but for statements that don't return rows (INSERT/UPDATE/DDL/etc).
@@ -124,32 +157,111 @@ export class Connection {
124
157
  execute(templates, ...params) {
125
158
  if (this.isClosed)
126
159
  return Future.reject(ErrConnectionClosed);
160
+ const { text, args } = compileSqlTemplate(templates, params);
161
+ const resolvers = Future.withResolvers();
127
162
  if (this._reconnecting)
128
- return this._reconnecting.andThen(() => this._performExecute(templates, ...params));
129
- return this._performExecute(templates, ...params);
163
+ return this._reconnecting.andThen(() => this._performExecute(text, args, resolvers));
164
+ return this._performExecute(text, args, resolvers);
165
+ }
166
+ _performExecute(text, args, resolvers) {
167
+ if (this.config.logLevel === 'query') {
168
+ console.log(`\n\x1b[35m┌─ EXECUTE ──────────────────────────────────────\x1b[0m\n`
169
+ + `\x1b[35m│\x1b[0m ${text}\n`
170
+ + `${args.length !== 0 ? `\x1b[35m│\x1b[0m \x1b[90mArguments:\x1b[0m [${args}]\n` : ''}`
171
+ + `\x1b[35m└────────────────────────────────────────────────\x1b[0m`);
172
+ }
173
+ this._executingCounter++;
174
+ const parsed = this._parsed[text];
175
+ if (parsed) {
176
+ const query = new ExecuteQuery(parsed, text, args, resolvers, this.config.queryTimeout);
177
+ const err = this._registerQuery(query);
178
+ if (err) {
179
+ this._executingCounter--;
180
+ query.error(err);
181
+ return query.resolvers.future;
182
+ }
183
+ return query.resolvers.future;
184
+ }
185
+ if (!this._parsing[text]) {
186
+ const parseQuery = new ParseQuery({ statement: this._nextStatement(), columns: EMPTY_ARRAY, parameters: EMPTY_ARRAY }, text, Future.withResolvers(), this.config.queryTimeout);
187
+ this._registerQuery(parseQuery);
188
+ this._parsing[text] = parseQuery.resolvers.future;
189
+ }
190
+ const parsing = this._parsing[text];
191
+ return parsing.andThen(meta => {
192
+ const query = new ExecuteQuery(meta, text, args, resolvers, this.config.queryTimeout);
193
+ const err = this._registerQuery(query);
194
+ if (err) {
195
+ this._executingCounter--;
196
+ query.error(err);
197
+ return query.resolvers.future;
198
+ }
199
+ return query.resolvers.future;
200
+ });
130
201
  }
131
- _performExecute(templates, ...params) {
202
+ /**
203
+ * Streams query results as a `ReadableStream`, without buffering rows in memory.
204
+ * Ideal for large result sets or piping straight into an HTTP response.
205
+ *
206
+ * @example
207
+ * for await (const row of conn.stream<User>`SELECT * FROM orders`) { ... }
208
+ */
209
+ stream(templates, ...params) {
210
+ if (this.isClosed)
211
+ throw ErrConnectionClosed;
132
212
  const { text, args } = compileSqlTemplate(templates, params);
213
+ let controller;
214
+ const stream = new ReadableStream({
215
+ start: c => {
216
+ controller = c;
217
+ }
218
+ });
219
+ if (this._reconnecting) {
220
+ this._reconnecting
221
+ .tap(() => this._performStream(text, args, controller))
222
+ .tapErr(err => controller.error(err));
223
+ return stream;
224
+ }
225
+ this._performStream(text, args, controller);
226
+ return stream;
227
+ }
228
+ _performStream(text, args, controller) {
133
229
  if (this.config.logLevel === 'query') {
134
- console.log(`\nQUERY: ${text}\n${args.length !== 0 ? `ARGUMENTS: [${args}]\n` : ""}`);
230
+ console.log(`\n\x1b[34m┌─ STREAM ───────────────────────────────────────\x1b[0m\n`
231
+ + `\x1b[34m│\x1b[0m ${text}\n`
232
+ + `${args.length !== 0 ? `\x1b[34m│\x1b[0m \x1b[90mArguments:\x1b[0m [${args}]\n` : ''}`
233
+ + `\x1b[34m└────────────────────────────────────────────────\x1b[0m`);
135
234
  }
136
- if (this._parsed.has(text)) {
137
- const meta = this._parsed.get(text);
138
- const query = new ExecuteQuery(meta.statement, text, args, this.config.queryTimeout);
139
- this._registerBatch().registerQuery(query);
140
- return query.future;
235
+ this._executingCounter++;
236
+ const parsed = this._parsed[text];
237
+ if (parsed) {
238
+ const query = new StreamQuery(parsed, text, args, controller, parsed.columns, this.config.queryTimeout);
239
+ const err = this._registerQuery(query);
240
+ if (err) {
241
+ this._executingCounter--;
242
+ controller.error(err);
243
+ }
244
+ return;
141
245
  }
142
- if (this._parsing.has(text)) {
143
- const statement = this._parsing.get(text);
144
- const query = new ExecuteQuery(statement, text, args, this.config.queryTimeout);
145
- this._registerBatch().registerQuery(query);
146
- return query.future;
246
+ if (!this._parsing[text]) {
247
+ const parseQuery = new ParseQuery({ statement: this._nextStatement(), columns: EMPTY_ARRAY, parameters: EMPTY_ARRAY }, text, Future.withResolvers(), this.config.queryTimeout);
248
+ this._registerQuery(parseQuery);
249
+ this._parsing[text] = parseQuery.resolvers.future;
147
250
  }
148
- const query = new ExecuteQuery(this._nextStatement(), text, args, this.config.queryTimeout);
149
- this._parsing.set(text, query.statement);
150
- this._registerBatch().registerParse(query);
151
- this._registerBatch().registerQuery(query);
152
- return query.future;
251
+ const parsing = this._parsing[text];
252
+ parsing
253
+ .tap(meta => {
254
+ const query = new StreamQuery(meta, text, args, controller, null, this.config.queryTimeout);
255
+ const err = this._registerQuery(query);
256
+ if (err) {
257
+ this._executingCounter--;
258
+ controller.error(err);
259
+ }
260
+ })
261
+ .tapErr(err => {
262
+ controller.error(err);
263
+ })
264
+ .recover();
153
265
  }
154
266
  /**
155
267
  * Runs `txCallback` inside `BEGIN`/`COMMIT`, rolling back on error.
@@ -209,53 +321,6 @@ export class Connection {
209
321
  }
210
322
  return Ok();
211
323
  }
212
- /**
213
- * Streams query results as a `ReadableStream`, without buffering rows in memory.
214
- * Ideal for large result sets or piping straight into an HTTP response.
215
- *
216
- * @example
217
- * for await (const row of conn.stream<User>`SELECT * FROM orders`) { ... }
218
- */
219
- stream(templates, ...params) {
220
- if (this.isClosed)
221
- throw ErrConnectionClosed;
222
- let controller;
223
- const stream = new ReadableStream({
224
- start: c => {
225
- controller = c;
226
- }
227
- });
228
- if (this._reconnecting) {
229
- this._reconnecting
230
- .tap(() => this._performStream(templates, params, controller))
231
- .tapErr(err => controller.error(err));
232
- return stream;
233
- }
234
- this._performStream(templates, params, controller);
235
- return stream;
236
- }
237
- _performStream(templates, params, controller) {
238
- const { text, args } = compileSqlTemplate(templates, params);
239
- if (this.config.logLevel === 'query') {
240
- console.log(`\nQUERY: ${text}\n${args.length !== 0 ? `ARGUMENTS: [${args}]\n` : ""}`);
241
- }
242
- if (this._parsed.has(text)) {
243
- const meta = this._parsed.get(text);
244
- const query = new StreamQuery(meta.statement, text, args, controller, meta.columns, this.config.queryTimeout);
245
- this._registerBatch().registerQuery(query);
246
- return;
247
- }
248
- if (this._parsing.has(text)) {
249
- const statement = this._parsing.get(text);
250
- const query = new StreamQuery(statement, text, args, controller, null, this.config.queryTimeout);
251
- this._registerBatch().registerQuery(query);
252
- return;
253
- }
254
- const query = new StreamQuery(this._nextStatement(), text, args, controller, null, this.config.queryTimeout);
255
- this._parsing.set(text, query.statement);
256
- this._registerBatch().registerParse(query);
257
- this._registerBatch().registerQuery(query);
258
- }
259
324
  _reconnect() {
260
325
  if (this.isClosed)
261
326
  return;
@@ -272,13 +337,14 @@ export class Connection {
272
337
  }
273
338
  _performReconnect() {
274
339
  this._socket.destroy();
275
- this._parsed.clear();
276
- this._parsing.clear();
277
- this._activeBatch?.reject(ErrConnectionReconnecting);
278
- this._activeBatch = null;
279
- while (this._batchQueue.hasMore) {
280
- this._batchQueue.shift.reject(ErrConnectionReconnecting);
340
+ this._parsed = {};
341
+ this._parsing = {};
342
+ this._sheduled = false;
343
+ this._executingCounter = 0;
344
+ while (this._queue.hasMore) {
345
+ this._queue.shift.error(ErrConnectionReconnecting);
281
346
  }
347
+ this._writer.clear();
282
348
  return createSocket(this.config)
283
349
  .andThen(socket => upgradeSocket(socket, this.config))
284
350
  .andThen(socket => authorizeSocket(socket, this.config))
@@ -296,98 +362,87 @@ export class Connection {
296
362
  });
297
363
  return Future.all(futures).map(() => { });
298
364
  }
299
- _getCurrentQuery() {
300
- return this._batchQueue.current.current;
365
+ get _currentQuery() {
366
+ return this._queue.current;
301
367
  }
302
368
  _handlePacket(type, length, reader) {
303
369
  switch (type) {
304
370
  case ResponseTypes.ParseComplete:
305
- {
306
- reader.readParseComplete();
307
- }
308
- break;
309
371
  case ResponseTypes.BindComplete:
310
- {
311
- reader.readBindComplete();
312
- const query = this._getCurrentQuery();
313
- if (query instanceof ExecuteQuery) {
314
- break;
315
- }
316
- if (!query.columns) {
317
- query.columns = this._parsed.get(query.text).columns;
318
- }
319
- }
320
- break;
321
- case ResponseTypes.CloseComplete:
322
- {
323
- reader.readBindComplete();
324
- }
325
- break;
372
+ case ResponseTypes.CloseComplete: break;
326
373
  case ResponseTypes.ParameterDescription:
327
374
  {
328
- reader.readParameterDescription();
375
+ const query = this._currentQuery;
376
+ const parameters = reader.readParameterDescription();
377
+ query.meta.parameters = parameters;
329
378
  }
330
379
  break;
331
- case ResponseTypes.NoData:
380
+ case ResponseTypes.RowDescription:
332
381
  {
333
- reader.readNoData();
334
- const query = this._getCurrentQuery();
335
- const meta = { statement: query.statement, columns: [] };
336
- this._parsing.delete(query.text);
337
- this._parsed.set(query.text, meta);
382
+ const query = this._currentQuery;
383
+ const columns = reader.readRowDescription();
384
+ query.meta.columns = columns;
385
+ delete this._parsing[query.text];
386
+ this._parsed[query.text] = query.meta;
387
+ query.complete();
338
388
  }
339
389
  break;
340
- case ResponseTypes.RowDescription:
390
+ case ResponseTypes.NoData:
341
391
  {
342
- const columns = reader.readRowDescription();
343
- const query = this._getCurrentQuery();
344
- const meta = {
345
- statement: query.statement, columns
346
- };
347
- this._parsing.delete(query.text);
348
- this._parsed.set(query.text, meta);
392
+ const query = this._currentQuery;
393
+ delete this._parsing[query.text];
394
+ this._parsed[query.text] = query.meta;
395
+ query.complete();
349
396
  }
350
397
  break;
351
398
  case ResponseTypes.DataRow:
352
399
  {
353
- let query = this._getCurrentQuery();
400
+ let query = this._currentQuery;
354
401
  if (query instanceof ExecuteQuery) {
355
- reader.skip(length - 4);
402
+ reader.skipBytes(length);
356
403
  break;
357
404
  }
358
- query.push(reader.readDataRow(query.columns, this.config.int8toBigint));
405
+ query.push(reader.readDataRow(query.meta.columns, this.config.int8toBigint));
359
406
  }
360
407
  break;
361
408
  case ResponseTypes.ComandComplete:
362
409
  {
363
- reader.readCommandComplete();
364
- const query = this._getCurrentQuery();
365
- query.resolve();
366
- this._batchQueue.current.next();
410
+ reader.skipBytes(length);
411
+ const query = this._currentQuery;
412
+ query.complete();
413
+ this._executingCounter--;
367
414
  }
368
415
  break;
369
416
  case ResponseTypes.ErrorResponse:
370
417
  {
371
418
  const error = reader.readErrorResponse();
372
419
  if (this.config.logLevel === 'error' || this.config.logLevel === 'notice' || this.config.logLevel === 'query') {
373
- console.log(`\nError: ${error}\n`);
420
+ console.log(`\n\x1b[31m┌─ ERROR ────────────────────────────────────────\x1b[0m\n`
421
+ + `\x1b[31m│\x1b[0m ${error}\n`
422
+ + `\x1b[31m└────────────────────────────────────────────────\x1b[0m\n`);
423
+ }
424
+ const query = this._currentQuery;
425
+ if (error.isParseError) {
426
+ delete this._parsing[query.text];
374
427
  }
375
- this._parsing.clear();
376
- this._batchQueue.current.reject(error);
428
+ query.error(error);
429
+ this._executingCounter--;
377
430
  }
378
431
  break;
379
432
  case ResponseTypes.ReadyForQuery:
380
433
  {
381
- reader.readReadyForQuery();
382
- this._batchQueue.next();
383
- this._closing && !this._hasQueries && this._closing.resolve();
434
+ reader.skipBytes(length);
435
+ this._queue.next();
436
+ this._closing && !this._executingCounter && this._closing.resolve();
384
437
  }
385
438
  break;
386
439
  case ResponseTypes.Notice:
387
440
  {
388
441
  const message = reader.readErrorResponse();
389
442
  if (this.config.logLevel === 'notice' || this.config.logLevel === 'query') {
390
- console.log(`\nError: ${message}\n`);
443
+ console.log(`\n\x1b[33m┌─ NOTICE ───────────────────────────────────────\x1b[0m\n`
444
+ + `\x1b[33m│\x1b[0m ${message}\n`
445
+ + `\x1b[33m└────────────────────────────────────────────────\x1b[0m\n`);
391
446
  }
392
447
  }
393
448
  break;
@@ -400,12 +455,13 @@ export class Connection {
400
455
  callbackSet.forEach(cb => cb(payload));
401
456
  }
402
457
  break;
403
- default: console.log('Undeclared response type: ', type);
458
+ default:
459
+ {
460
+ reader.skipBytes(length);
461
+ }
462
+ break;
404
463
  }
405
464
  }
406
- get _hasQueries() {
407
- return this._batchQueue.hasMore || this._activeBatch;
408
- }
409
465
  /** Whether the connection is alive and usable. */
410
466
  get isOpened() {
411
467
  return !this._closing && !this._closed;
@@ -423,7 +479,7 @@ export class Connection {
423
479
  if (this._closing) {
424
480
  return this._closing.future;
425
481
  }
426
- if (!this._hasQueries) {
482
+ if (!this._executingCounter) {
427
483
  this._closed = true;
428
484
  this._socket.destroy();
429
485
  return Future.resolve();
package/dist/error.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { DataTypeOid } from "./protocol/constants";
1
2
  export declare class PostgresError extends Error {
2
3
  message: string;
3
4
  code: string;
@@ -10,8 +11,11 @@ export declare class PostgresError extends Error {
10
11
  constraint: string;
11
12
  constructor(message: string, code?: string, detail?: string, severity?: string, where?: string, hint?: string, position?: string, dataType?: string, constraint?: string);
12
13
  get isParseError(): boolean;
13
- get isDeadlock(): boolean;
14
+ get isBindError(): boolean;
15
+ get isPlanInvalidationError(): boolean;
16
+ get shouldInvalidateStatementCache(): boolean;
14
17
  get isConstraintViolation(): boolean;
18
+ get isDeadlock(): boolean;
15
19
  get isTimeout(): boolean;
16
20
  get isConnectionFailure(): boolean;
17
21
  }
@@ -27,4 +31,5 @@ export declare const ErrSSLDenied: PostgresError;
27
31
  export declare const ErrDatabaseNotFound: PostgresError;
28
32
  export declare const ErrUntrustedCertificate: PostgresError;
29
33
  export declare const ErrCertificateFileNotFound: PostgresError;
34
+ export declare function createBindTypeError(index: number, expectedType: DataTypeOid, actualValue: unknown): PostgresError;
30
35
  //# sourceMappingURL=error.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA,qBAAa,aAAc,SAAQ,KAAK;IAEhB,OAAO,EAAE,MAAM;IACxB,IAAI,EAAE,MAAM;IACZ,MAAM,EAAE,MAAM;IACd,QAAQ,EAAE,MAAM;IAChB,KAAK,EAAE,MAAM;IACb,IAAI,EAAE,MAAM;IACZ,QAAQ,EAAE,MAAM;IAChB,QAAQ,EAAE,MAAM;IAChB,UAAU,EAAE,MAAM;gBART,OAAO,EAAE,MAAM,EACxB,IAAI,GAAE,MAAqB,EAC3B,MAAM,GAAE,MAAW,EACnB,QAAQ,GAAE,MAAW,EACrB,KAAK,GAAE,MAAW,EAClB,IAAI,GAAE,MAAW,EACjB,QAAQ,GAAE,MAAW,EACrB,QAAQ,GAAE,MAAW,EACrB,UAAU,GAAE,MAAW;IAQlC,IAAI,YAAY,IAAI,OAAO,CAE1B;IAID,IAAI,UAAU,IAAI,OAAO,CAExB;IAID,IAAI,qBAAqB,IAAI,OAAO,CAEnC;IAGD,IAAI,SAAS,IAAI,OAAO,CAEvB;IAGD,IAAI,mBAAmB,IAAI,OAAO,CAEjC;CACJ;AAED,eAAO,MAAM,gBAAgB,eAAmF,CAAA;AAChH,eAAO,MAAM,mBAAmB,eAAqE,CAAA;AACrG,eAAO,MAAM,yBAAyB,eAAiD,CAAA;AAEvF,eAAO,MAAM,eAAe,eAA8C,CAAA;AAE1E,eAAO,MAAM,aAAa,eAAkE,CAAA;AAE5F,eAAO,MAAM,oBAAoB,eAA6E,CAAA;AAE9G,eAAO,MAAM,mBAAmB,eAA8E,CAAA;AAC9G,eAAO,MAAM,yBAAyB,eAA2F,CAAA;AACjI,eAAO,MAAM,YAAY,eAA4D,CAAA;AACrF,eAAO,MAAM,mBAAmB,eAAwD,CAAA;AACxF,eAAO,MAAM,uBAAuB,eAA4E,CAAA;AAChH,eAAO,MAAM,0BAA0B,eAAkF,CAAA"}
1
+ {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAgB,MAAM,sBAAsB,CAAA;AAEhE,qBAAa,aAAc,SAAQ,KAAK;IAEhB,OAAO,EAAE,MAAM;IACxB,IAAI,EAAE,MAAM;IACZ,MAAM,EAAE,MAAM;IACd,QAAQ,EAAE,MAAM;IAChB,KAAK,EAAE,MAAM;IACb,IAAI,EAAE,MAAM;IACZ,QAAQ,EAAE,MAAM;IAChB,QAAQ,EAAE,MAAM;IAChB,UAAU,EAAE,MAAM;gBART,OAAO,EAAE,MAAM,EACxB,IAAI,GAAE,MAAqB,EAC3B,MAAM,GAAE,MAAW,EACnB,QAAQ,GAAE,MAAW,EACrB,KAAK,GAAE,MAAW,EAClB,IAAI,GAAE,MAAW,EACjB,QAAQ,GAAE,MAAW,EACrB,QAAQ,GAAE,MAAW,EACrB,UAAU,GAAE,MAAW;IAMlC,IAAI,YAAY,IAAI,OAAO,CAK1B;IAED,IAAI,WAAW,IAAI,OAAO,CAKzB;IAED,IAAI,uBAAuB,IAAI,OAAO,CAErC;IAED,IAAI,8BAA8B,IAAI,OAAO,CAE5C;IAED,IAAI,qBAAqB,IAAI,OAAO,CAEnC;IAED,IAAI,UAAU,IAAI,OAAO,CAExB;IAED,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,IAAI,mBAAmB,IAAI,OAAO,CAEjC;CACJ;AAGD,eAAO,MAAM,gBAAgB,eAAmF,CAAA;AAChH,eAAO,MAAM,mBAAmB,eAAqE,CAAA;AACrG,eAAO,MAAM,yBAAyB,eAAiD,CAAA;AAEvF,eAAO,MAAM,eAAe,eAA8C,CAAA;AAE1E,eAAO,MAAM,aAAa,eAAkE,CAAA;AAE5F,eAAO,MAAM,oBAAoB,eAA6E,CAAA;AAE9G,eAAO,MAAM,mBAAmB,eAA8E,CAAA;AAC9G,eAAO,MAAM,yBAAyB,eAA2F,CAAA;AACjI,eAAO,MAAM,YAAY,eAA4D,CAAA;AACrF,eAAO,MAAM,mBAAmB,eAAwD,CAAA;AACxF,eAAO,MAAM,uBAAuB,eAA4E,CAAA;AAChH,eAAO,MAAM,0BAA0B,eAAkF,CAAA;AAEzH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,GAAG,aAAa,CAcjH"}
package/dist/error.js CHANGED
@@ -12,23 +12,31 @@ export class PostgresError extends Error {
12
12
  this.constraint = constraint;
13
13
  this.name = "PostgresError";
14
14
  }
15
- // Parse error
16
15
  get isParseError() {
17
- return this.code.startsWith('42');
16
+ return (this.code.startsWith('42') ||
17
+ this.code === '26000');
18
18
  }
19
- // Execute error
20
- get isDeadlock() {
21
- return this.code === '40P01';
19
+ get isBindError() {
20
+ return (this.code.startsWith('22') ||
21
+ this.code === '0A000');
22
+ }
23
+ get isPlanInvalidationError() {
24
+ return this.code === '0A000' || this.code === '42P05';
25
+ }
26
+ get shouldInvalidateStatementCache() {
27
+ return this.isParseError || this.isBindError || this.isPlanInvalidationError;
22
28
  }
23
- // Execute error
24
29
  get isConstraintViolation() {
25
30
  return this.code.startsWith('23');
26
31
  }
32
+ get isDeadlock() {
33
+ return this.code === '40P01';
34
+ }
27
35
  get isTimeout() {
28
36
  return this.code === '57014';
29
37
  }
30
38
  get isConnectionFailure() {
31
- return this.code.startsWith('08') || this.code.startsWith('57') && this.code !== '57014';
39
+ return this.code.startsWith('08') || (this.code.startsWith('57') && this.code !== '57014');
32
40
  }
33
41
  }
34
42
  export const ErrNonceMismatch = new PostgresError("Protocol violation: server nonce doesn't match client nonce");
@@ -43,3 +51,14 @@ export const ErrSSLDenied = new PostgresError("SSL is required but server denied
43
51
  export const ErrDatabaseNotFound = new PostgresError('Database with that dsn not found');
44
52
  export const ErrUntrustedCertificate = new PostgresError("Database SSL certificate is untrusted or self-signed");
45
53
  export const ErrCertificateFileNotFound = new PostgresError("The SSL certificate file specified in caPath was not found");
54
+ export function createBindTypeError(index, expectedType, actualValue) {
55
+ const position = (index + 1).toString();
56
+ const actualType = actualValue === null ? 'null' : typeof actualValue;
57
+ const message = `Bind error: Parameter $${position} type mismatch. Expected ${expectedType}, received "${actualType}(${actualValue})".`;
58
+ return new PostgresError(message, '22000', // code
59
+ `The parameter at position $${position} failed client-side binary validation.`, // detail
60
+ 'ERROR', // severity
61
+ 'writeBind() inside driver', // where
62
+ `Ensure that the argument passed as $${position} matches the PostgreSQL schema requirements.` // hint
63
+ );
64
+ }
package/dist/pool.d.ts CHANGED
@@ -57,7 +57,7 @@ export declare class Pool {
57
57
  * @example
58
58
  * for await (const row of pool.stream<User>`SELECT * FROM orders`) { ... }
59
59
  */
60
- stream<T extends Row>(templates: TemplateStringsArray, ...args: any[]): ReadableStream<T>;
60
+ stream<T extends Row>(templates: TemplateStringsArray, ...params: any[]): ReadableStream<T>;
61
61
  /** Sends a `pg_notify` message on `channelName` (payload ≤ 8000 bytes). */
62
62
  notify(channelName: string, payload?: string): Future<void, PostgresError>;
63
63
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAE3C,OAAO,EAAS,MAAM,EAAM,MAAM,eAAe,CAAC;AAClD,OAAO,EAAiB,aAAa,EAAE,MAAM,SAAS,CAAC;AACvD,OAAO,EAAc,iBAAiB,EAAE,GAAG,EAAU,MAAM,SAAS,CAAC;AAGrE;;;;;;;;GAQG;AACH,qBAAa,IAAI;IACb,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,MAAM,CAAI;IAClB,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,SAAS,CAAO;gBAEZ,MAAM,EAAE,iBAAiB;IAOrC;;;OAGG;IACH,OAAO;IA2BP;;;;;OAKG;IACH,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC;IAUnD,wFAAwF;IACxF,OAAO,CAAC,IAAI,EAAE,UAAU;IAoBxB;;;;;;;OAOG;IACH,KAAK,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,WAAW,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC;IAU7D;;;;;OAKG;IACH,KAAK,CAAC,CAAC,SAAS,GAAG,EAAE,SAAS,EAAE,oBAAoB,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE;IAuBpE,qEAAqE;IACrE,OAAO,CAAC,SAAS,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE;IAuBzD;;;;;OAKG;IACH,MAAM,CAAC,CAAC,SAAS,GAAG,EAAE,SAAS,EAAE,oBAAoB,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,cAAc,CAAC,CAAC,CAAC;IAoCzF,2EAA2E;IAC3E,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,MAAW;IAQhD;;;;;;;OAOG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI;IAY3D,kCAAkC;IAClC,IAAI,IAAI,WAEP;IAGD,iDAAiD;IACjD,IAAI,KAAK,WAER;IAGD;;OAEG;IACH,KAAK;CAiBR"}
1
+ {"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAE3C,OAAO,EAAS,MAAM,EAAM,MAAM,eAAe,CAAC;AAClD,OAAO,EAAiB,aAAa,EAAE,MAAM,SAAS,CAAC;AACvD,OAAO,EAAc,iBAAiB,EAAE,GAAG,EAAU,MAAM,SAAS,CAAC;AAIrE;;;;;;;;GAQG;AACH,qBAAa,IAAI;IACb,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,MAAM,CAAI;IAClB,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,SAAS,CAAO;gBAEZ,MAAM,EAAE,iBAAiB;IAOrC;;;OAGG;IACH,OAAO;IA2BP;;;;;OAKG;IACH,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC;IAUnD,wFAAwF;IACxF,OAAO,CAAC,IAAI,EAAE,UAAU;IAoBxB;;;;;;;OAOG;IACH,KAAK,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,WAAW,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC;IAU7D;;;;;OAKG;IACH,KAAK,CAAC,CAAC,SAAS,GAAG,EAAE,SAAS,EAAE,oBAAoB,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE;IAuBpE,qEAAqE;IACrE,OAAO,CAAC,SAAS,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE;IAuBzD;;;;;OAKG;IACH,MAAM,CAAC,CAAC,SAAS,GAAG,EAAE,SAAS,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE,GAAG,cAAc,CAAC,CAAC,CAAC;IAsC3F,2EAA2E;IAC3E,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,MAAW;IAQhD;;;;;;;OAOG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI;IAY3D,kCAAkC;IAClC,IAAI,IAAI,WAEP;IAGD,iDAAiD;IACjD,IAAI,KAAK,WAER;IAGD;;OAEG;IACH,KAAK;CAiBR"}