@m2k-5f/pgtx 2.8.0 → 2.8.2

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 +107 -47
  2. package/dist/clauses/array.clause.d.ts.map +1 -1
  3. package/dist/clauses/array.clause.js +0 -5
  4. package/dist/clauses/iden.caluse.d.ts.map +1 -1
  5. package/dist/clauses/iden.caluse.js +0 -2
  6. package/dist/clauses/insert.clause.d.ts.map +1 -1
  7. package/dist/clauses/insert.clause.js +5 -6
  8. package/dist/clauses/literal.clause.d.ts.map +1 -1
  9. package/dist/clauses/literal.clause.js +0 -3
  10. package/dist/clauses/update.clause.d.ts.map +1 -1
  11. package/dist/clauses/update.clause.js +0 -2
  12. package/dist/clauses/where.clause.d.ts.map +1 -1
  13. package/dist/clauses/where.clause.js +0 -2
  14. package/dist/connection.d.ts +7 -3
  15. package/dist/connection.d.ts.map +1 -1
  16. package/dist/connection.js +147 -116
  17. package/dist/error.d.ts +1 -0
  18. package/dist/error.d.ts.map +1 -1
  19. package/dist/error.js +40 -9
  20. package/dist/index.d.ts +30 -1
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +1 -1
  23. package/dist/pool.d.ts +2 -0
  24. package/dist/pool.d.ts.map +1 -1
  25. package/dist/pool.js +6 -0
  26. package/dist/protocol/connection-request-writer.d.ts +1 -1
  27. package/dist/protocol/connection-request-writer.d.ts.map +1 -1
  28. package/dist/protocol/connection-request-writer.js +1 -3
  29. package/dist/protocol/connection-response-reader.d.ts +1 -1
  30. package/dist/protocol/connection-response-reader.js +1 -1
  31. package/dist/protocol/constants.js +8 -8
  32. package/dist/protocol/types.d.ts +3 -1
  33. package/dist/protocol/types.d.ts.map +1 -1
  34. package/dist/protocol/types.js +91 -1
  35. package/dist/query.d.ts +9 -11
  36. package/dist/query.d.ts.map +1 -1
  37. package/dist/query.js +8 -10
  38. package/dist/transaction.d.ts +10 -2
  39. package/dist/transaction.d.ts.map +1 -1
  40. package/dist/transaction.js +78 -23
  41. package/dist/types.d.ts +2 -2
  42. package/dist/types.d.ts.map +1 -1
  43. package/dist/utils.d.ts +0 -2
  44. package/dist/utils.d.ts.map +1 -1
  45. package/dist/utils.js +0 -46
  46. package/package.json +1 -1
@@ -6,7 +6,7 @@ import { Queue } from "./queue";
6
6
  import { CollectQuery, StreamQuery, ExecuteQuery, ParseQuery } from "./query";
7
7
  import { sql } from ".";
8
8
  import { Begin, Future, Ok } from 'fluent-future';
9
- import { ErrConnectionClosed, ErrConnectionReconnecting } from "./error";
9
+ import { ErrConnectionClosed, ErrConnectionReconnecting, ErrTransactionInProgress } from "./error";
10
10
  import { nextTick } from "process";
11
11
  import { authorizeSocket, createSocket, upgradeSocket } from "./protocol/socket-authorization";
12
12
  import { ConnectionRequestBuffer } from "./protocol/connection-request-writer";
@@ -29,14 +29,36 @@ export class Connection {
29
29
  _nextStatement() {
30
30
  return `s-${this._stmtCounter++}`;
31
31
  }
32
- _registerShedule() {
32
+ _logError(error) {
33
+ if (this.config.logLevel === 'error' || this.config.logLevel === 'notice' || this.config.logLevel === 'query') {
34
+ console.log(`\n\x1b[31m┌─ ERROR ────────────────────────────────────────\x1b[0m\n`
35
+ + `\x1b[31m│\x1b[0m ${error}\n`
36
+ + `\x1b[31m└────────────────────────────────────────────────\x1b[0m\n`);
37
+ }
38
+ }
39
+ _logQuery(text, args) {
40
+ if (this.config.logLevel === 'query') {
41
+ console.log(`\n\x1b[36m┌─ QUERY ─────────────────────────────────────────\x1b[0m\n`
42
+ + `\x1b[36m│\x1b[0m ${text}\n`
43
+ + `${args.length !== 0 ? `\x1b[36m│\x1b[0m \x1b[90mArguments:\x1b[0m [${args}]\n` : ''}`
44
+ + `\x1b[36m└────────────────────────────────────────────────\x1b[0m`);
45
+ }
46
+ }
47
+ _logNotice(notice) {
48
+ if (this.config.logLevel === 'notice' || this.config.logLevel === 'query') {
49
+ console.log(`\n\x1b[33m┌─ NOTICE ───────────────────────────────────────\x1b[0m\n`
50
+ + `\x1b[33m│\x1b[0m ${notice}\n`
51
+ + `\x1b[33m└────────────────────────────────────────────────\x1b[0m\n`);
52
+ }
53
+ }
54
+ _registerSсhedule() {
33
55
  if (!this._sheduled) {
34
56
  this._sheduled = true;
35
57
  this._writer.clear();
36
- shedule[this.config.syncShedule](() => this._shedule());
58
+ shedule[this.config.syncSсhedule](() => this._sсhedule());
37
59
  }
38
60
  }
39
- _shedule() {
61
+ _sсhedule() {
40
62
  if (this._reconnecting)
41
63
  return;
42
64
  this._writer.hasMore && this._socket.write(this._writer);
@@ -44,7 +66,7 @@ export class Connection {
44
66
  this._sheduled = false;
45
67
  }
46
68
  _registerQuery(query) {
47
- this._registerShedule();
69
+ this._registerSсhedule();
48
70
  if (query instanceof ParseQuery) {
49
71
  this._writer
50
72
  .writeParse(query.meta.statement, query.text)
@@ -67,12 +89,12 @@ export class Connection {
67
89
  this._writer = ConnectionRequestBuffer.new(65536);
68
90
  this._sheduled = false;
69
91
  this._queue = new Queue();
70
- this._executingCounter = 0;
71
92
  this._closing = null;
72
93
  this._closed = false;
73
94
  this._reconnecting = null;
74
- this._parsed = {};
75
- this._parsing = {};
95
+ this._inTransaction = false;
96
+ this._parsed = new Map();
97
+ this._parsing = new Map();
76
98
  this._listeningCallbacks = new Map();
77
99
  this._stmtCounter = 0;
78
100
  this.config = config;
@@ -88,7 +110,7 @@ export class Connection {
88
110
  logLevel: config.logLevel || 'error',
89
111
  int8toBigint: config.int8toBigint || false,
90
112
  queryTimeout: config.queryTimeout || 30000,
91
- syncShedule: config.syncShedule || 'Immediate',
113
+ syncSсhedule: config.syncSсhedule || 'Immediate',
92
114
  ssl: config.caPath ? 'require' : (config.ssl || 'prefer')
93
115
  };
94
116
  return createSocket(conf)
@@ -104,46 +126,52 @@ export class Connection {
104
126
  * const users = await conn.query<User>`SELECT * FROM users WHERE id = ${1}`
105
127
  */
106
128
  query(templates, ...params) {
107
- if (this.isClosed)
129
+ if (this.isClosed) {
130
+ this._logError(ErrConnectionClosed);
108
131
  return Future.reject(ErrConnectionClosed);
132
+ }
133
+ if (this._inTransaction) {
134
+ this._logError(ErrTransactionInProgress);
135
+ return Future.reject(ErrTransactionInProgress);
136
+ }
109
137
  const { text, args } = compileSqlTemplate(templates, params);
110
138
  const resolvers = Future.withResolvers();
111
- if (this._reconnecting)
112
- return this._reconnecting.andThen(() => this._performQuery(text, args, resolvers));
139
+ if (this._reconnecting) {
140
+ return this._reconnecting
141
+ .tapErr(err => this._logError(err))
142
+ .andThen(() => this._performQuery(text, args, resolvers));
143
+ }
113
144
  return this._performQuery(text, args, resolvers);
114
145
  }
115
146
  _performQuery(text, args, resolvers) {
116
- if (this.config.logLevel === 'query') {
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`);
121
- }
122
- this._executingCounter++;
123
- const parsed = this._parsed[text];
147
+ this._logQuery(text, args);
148
+ const parsed = this._parsed.get(text);
124
149
  if (parsed) {
125
- const query = new CollectQuery(parsed, text, args, parsed.columns, resolvers, this.config.queryTimeout);
150
+ const query = new CollectQuery(text, args, parsed, resolvers, this.config.queryTimeout);
126
151
  const err = this._registerQuery(query);
127
152
  if (err) {
128
- this._executingCounter--;
129
153
  query.error(err);
130
- return query.resolvers.future;
154
+ this._logError(err);
131
155
  }
132
156
  return query.resolvers.future;
133
157
  }
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);
158
+ if (!this._parsing.has(text)) {
159
+ const newMeta = {
160
+ statement: this._nextStatement(),
161
+ columns: EMPTY_ARRAY,
162
+ parameters: EMPTY_ARRAY
163
+ };
164
+ const parseQuery = new ParseQuery(text, newMeta, Future.withResolvers(), this.config.queryTimeout);
136
165
  this._registerQuery(parseQuery);
137
- this._parsing[text] = parseQuery.resolvers.future;
166
+ this._parsing.set(text, parseQuery.resolvers.future);
138
167
  }
139
- const parsing = this._parsing[text];
168
+ const parsing = this._parsing.get(text);
140
169
  return parsing.andThen(meta => {
141
- const query = new CollectQuery(meta, text, args, meta.columns, resolvers, this.config.queryTimeout);
170
+ const query = new CollectQuery(text, args, meta, resolvers, this.config.queryTimeout);
142
171
  const err = this._registerQuery(query);
143
172
  if (err) {
144
- this._executingCounter--;
145
173
  query.error(err);
146
- return query.resolvers.future;
174
+ this._logError(err);
147
175
  }
148
176
  return query.resolvers.future;
149
177
  });
@@ -155,46 +183,52 @@ export class Connection {
155
183
  * await conn.execute`UPDATE users SET name = ${name} WHERE id = ${id}`
156
184
  */
157
185
  execute(templates, ...params) {
158
- if (this.isClosed)
186
+ if (this.isClosed) {
187
+ this._logError(ErrConnectionClosed);
159
188
  return Future.reject(ErrConnectionClosed);
189
+ }
190
+ if (this._inTransaction) {
191
+ this._logError(ErrTransactionInProgress);
192
+ return Future.reject(ErrTransactionInProgress);
193
+ }
160
194
  const { text, args } = compileSqlTemplate(templates, params);
161
195
  const resolvers = Future.withResolvers();
162
- if (this._reconnecting)
163
- return this._reconnecting.andThen(() => this._performExecute(text, args, resolvers));
196
+ if (this._reconnecting) {
197
+ return this._reconnecting
198
+ .tapErr(err => this._logError(err))
199
+ .andThen(() => this._performExecute(text, args, resolvers));
200
+ }
164
201
  return this._performExecute(text, args, resolvers);
165
202
  }
166
203
  _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];
204
+ this._logQuery(text, args);
205
+ const parsed = this._parsed.get(text);
175
206
  if (parsed) {
176
- const query = new ExecuteQuery(parsed, text, args, resolvers, this.config.queryTimeout);
207
+ const query = new ExecuteQuery(text, args, parsed, resolvers, this.config.queryTimeout);
177
208
  const err = this._registerQuery(query);
178
209
  if (err) {
179
- this._executingCounter--;
180
210
  query.error(err);
181
- return query.resolvers.future;
211
+ this._logError(err);
182
212
  }
183
213
  return query.resolvers.future;
184
214
  }
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);
215
+ if (!this._parsing.has(text)) {
216
+ const newMeta = {
217
+ statement: this._nextStatement(),
218
+ columns: EMPTY_ARRAY,
219
+ parameters: EMPTY_ARRAY
220
+ };
221
+ const parseQuery = new ParseQuery(text, newMeta, Future.withResolvers(), this.config.queryTimeout);
187
222
  this._registerQuery(parseQuery);
188
- this._parsing[text] = parseQuery.resolvers.future;
223
+ this._parsing.set(text, parseQuery.resolvers.future);
189
224
  }
190
- const parsing = this._parsing[text];
225
+ const parsing = this._parsing.get(text);
191
226
  return parsing.andThen(meta => {
192
- const query = new ExecuteQuery(meta, text, args, resolvers, this.config.queryTimeout);
227
+ const query = new ExecuteQuery(text, args, meta, resolvers, this.config.queryTimeout);
193
228
  const err = this._registerQuery(query);
194
229
  if (err) {
195
- this._executingCounter--;
196
230
  query.error(err);
197
- return query.resolvers.future;
231
+ this._logError(err);
198
232
  }
199
233
  return query.resolvers.future;
200
234
  });
@@ -207,8 +241,14 @@ export class Connection {
207
241
  * for await (const row of conn.stream<User>`SELECT * FROM orders`) { ... }
208
242
  */
209
243
  stream(templates, ...params) {
210
- if (this.isClosed)
244
+ if (this.isClosed) {
245
+ this._logError(ErrConnectionClosed);
211
246
  throw ErrConnectionClosed;
247
+ }
248
+ if (this._inTransaction) {
249
+ this._logError(ErrTransactionInProgress);
250
+ throw (ErrTransactionInProgress);
251
+ }
212
252
  const { text, args } = compileSqlTemplate(templates, params);
213
253
  let controller;
214
254
  const stream = new ReadableStream({
@@ -219,43 +259,42 @@ export class Connection {
219
259
  if (this._reconnecting) {
220
260
  this._reconnecting
221
261
  .tap(() => this._performStream(text, args, controller))
222
- .tapErr(err => controller.error(err));
262
+ .tapErr(err => {
263
+ this._logError(err);
264
+ controller.error(err);
265
+ })
266
+ .recover();
223
267
  return stream;
224
268
  }
225
269
  this._performStream(text, args, controller);
226
270
  return stream;
227
271
  }
228
272
  _performStream(text, args, controller) {
229
- if (this.config.logLevel === 'query') {
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`);
234
- }
235
- this._executingCounter++;
236
- const parsed = this._parsed[text];
273
+ this._logQuery(text, args);
274
+ const parsed = this._parsed.get(text);
237
275
  if (parsed) {
238
- const query = new StreamQuery(parsed, text, args, controller, parsed.columns, this.config.queryTimeout);
276
+ const query = new StreamQuery(text, args, parsed, controller, this.config.queryTimeout);
239
277
  const err = this._registerQuery(query);
240
278
  if (err) {
241
- this._executingCounter--;
242
279
  controller.error(err);
280
+ this._logError(err);
243
281
  }
244
282
  return;
245
283
  }
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);
284
+ if (!this._parsing.has(text)) {
285
+ const newMeta = { statement: this._nextStatement(), columns: EMPTY_ARRAY, parameters: EMPTY_ARRAY };
286
+ const parseQuery = new ParseQuery(text, newMeta, Future.withResolvers(), this.config.queryTimeout);
248
287
  this._registerQuery(parseQuery);
249
- this._parsing[text] = parseQuery.resolvers.future;
288
+ this._parsing.set(text, parseQuery.resolvers.future);
250
289
  }
251
- const parsing = this._parsing[text];
290
+ const parsing = this._parsing.get(text);
252
291
  parsing
253
292
  .tap(meta => {
254
- const query = new StreamQuery(meta, text, args, controller, null, this.config.queryTimeout);
293
+ const query = new StreamQuery(text, args, meta, controller, this.config.queryTimeout);
255
294
  const err = this._registerQuery(query);
256
295
  if (err) {
257
- this._executingCounter--;
258
296
  controller.error(err);
297
+ this._logError(err);
259
298
  }
260
299
  })
261
300
  .tapErr(err => {
@@ -272,13 +311,12 @@ export class Connection {
272
311
  * })
273
312
  */
274
313
  begin(txCallback) {
275
- if (this.isClosed)
276
- return Future.reject(ErrConnectionClosed);
277
314
  return Begin()
278
315
  .andThen(() => this.execute `begin`)
279
316
  .andThen(() => {
317
+ this._inTransaction = true;
280
318
  const tx = new Transaction(this);
281
- return Future.of(txCallback(tx))
319
+ return Future.of(() => txCallback(tx))
282
320
  .tap(() => {
283
321
  if (tx.isActive)
284
322
  return tx.commit();
@@ -287,29 +325,25 @@ export class Connection {
287
325
  if (tx.isActive)
288
326
  return tx.rollback();
289
327
  });
290
- });
328
+ })
329
+ .finally(() => this._inTransaction = false);
291
330
  }
292
331
  /** Sends a `pg_notify` message on `channelName` (payload ≤ 8000 bytes). */
293
332
  notify(channelName, payload = "") {
294
- if (this.isClosed)
295
- return Future.reject(ErrConnectionClosed);
296
- return this.execute `select pg_notify(${channelName}, ${payload})`.map(() => { });
333
+ return this.execute `select pg_notify(${channelName}, ${payload})`;
297
334
  }
298
335
  /** Subscribes `callback` to `channelName`, issuing `LISTEN` on first subscription. */
299
336
  listen(channelName, callback) {
300
- if (this.isClosed)
301
- return Future.reject(ErrConnectionClosed);
302
- if (!this._listeningCallbacks.has(channelName)) {
303
- this._listeningCallbacks.set(channelName, new Set());
304
- }
305
- const callbackSet = this._listeningCallbacks.get(channelName);
306
- callbackSet.add(callback);
307
- return this.execute `listen ${sql.ident(channelName)};`;
337
+ return this.execute `listen ${sql.ident(channelName)};`
338
+ .tap(() => {
339
+ if (!this._listeningCallbacks.has(channelName)) {
340
+ this._listeningCallbacks.set(channelName, new Set());
341
+ }
342
+ this._listeningCallbacks.get(channelName).add(callback);
343
+ });
308
344
  }
309
345
  /** Unsubscribes `callback`, issuing `UNLISTEN` once no callbacks remain. */
310
346
  unlisten(channelName, callback) {
311
- if (this.isClosed)
312
- return Future.reject(ErrConnectionClosed);
313
347
  if (!this._listeningCallbacks.has(channelName)) {
314
348
  return Ok();
315
349
  }
@@ -317,7 +351,7 @@ export class Connection {
317
351
  callbackSet.delete(callback);
318
352
  if (callbackSet.size === 0) {
319
353
  this._listeningCallbacks.delete(channelName);
320
- return this.execute `unlisten ${sql.ident(channelName)};`.map(() => { });
354
+ return this.execute `unlisten ${sql.ident(channelName)};`;
321
355
  }
322
356
  return Ok();
323
357
  }
@@ -337,10 +371,9 @@ export class Connection {
337
371
  }
338
372
  _performReconnect() {
339
373
  this._socket.destroy();
340
- this._parsed = {};
341
- this._parsing = {};
374
+ this._parsed.clear();
375
+ this._parsing.clear();
342
376
  this._sheduled = false;
343
- this._executingCounter = 0;
344
377
  while (this._queue.hasMore) {
345
378
  this._queue.shift.error(ErrConnectionReconnecting);
346
379
  }
@@ -382,16 +415,16 @@ export class Connection {
382
415
  const query = this._currentQuery;
383
416
  const columns = reader.readRowDescription();
384
417
  query.meta.columns = columns;
385
- delete this._parsing[query.text];
386
- this._parsed[query.text] = query.meta;
418
+ this._parsing.delete(query.text);
419
+ this._parsed.set(query.text, query.meta);
387
420
  query.complete();
388
421
  }
389
422
  break;
390
423
  case ResponseTypes.NoData:
391
424
  {
392
425
  const query = this._currentQuery;
393
- delete this._parsing[query.text];
394
- this._parsed[query.text] = query.meta;
426
+ this._parsing.delete(query.text);
427
+ this._parsed.set(query.text, query.meta);
395
428
  query.complete();
396
429
  }
397
430
  break;
@@ -410,40 +443,29 @@ export class Connection {
410
443
  reader.skipBytes(length);
411
444
  const query = this._currentQuery;
412
445
  query.complete();
413
- this._executingCounter--;
414
446
  }
415
447
  break;
416
448
  case ResponseTypes.ErrorResponse:
417
449
  {
418
450
  const error = reader.readErrorResponse();
419
- if (this.config.logLevel === 'error' || this.config.logLevel === 'notice' || this.config.logLevel === 'query') {
420
- console.log(`\n\x1b[31m┌─ ERROR ────────────────────────────────────────\x1b[0m\n`
421
- + `\x1b[31m│\x1b[0m ${error}\n`
422
- + `\x1b[31m└────────────────────────────────────────────────\x1b[0m\n`);
423
- }
451
+ this._logError(error);
424
452
  const query = this._currentQuery;
425
- if (error.isParseError) {
426
- delete this._parsing[query.text];
427
- }
453
+ if (query instanceof ParseQuery)
454
+ this._parsing.delete(query.text);
428
455
  query.error(error);
429
- this._executingCounter--;
430
456
  }
431
457
  break;
432
458
  case ResponseTypes.ReadyForQuery:
433
459
  {
434
460
  reader.skipBytes(length);
435
461
  this._queue.next();
436
- this._closing && !this._executingCounter && this._closing.resolve();
462
+ this._tryFinishClosing();
437
463
  }
438
464
  break;
439
465
  case ResponseTypes.Notice:
440
466
  {
441
- const message = reader.readErrorResponse();
442
- if (this.config.logLevel === 'notice' || this.config.logLevel === 'query') {
443
- console.log(`\n\x1b[33m┌─ NOTICE ───────────────────────────────────────\x1b[0m\n`
444
- + `\x1b[33m│\x1b[0m ${message}\n`
445
- + `\x1b[33m└────────────────────────────────────────────────\x1b[0m\n`);
446
- }
467
+ const notice = reader.readErrorResponse();
468
+ this._logNotice(notice);
447
469
  }
448
470
  break;
449
471
  case ResponseTypes.NotificationResponse:
@@ -470,6 +492,19 @@ export class Connection {
470
492
  get isClosed() {
471
493
  return this._closed || !!this._closing;
472
494
  }
495
+ _tryFinishClosing() {
496
+ if (!this._closing)
497
+ return;
498
+ if (this._queue.hasMore)
499
+ return;
500
+ setTimeout(() => {
501
+ if (!this._closing)
502
+ return;
503
+ if (!this._queue.hasMore) {
504
+ this._closing.resolve();
505
+ }
506
+ }, 0);
507
+ }
473
508
  /**
474
509
  * Closes the connection, awaiting for all pending queries. Not usable afterward.
475
510
  */
@@ -479,11 +514,6 @@ export class Connection {
479
514
  if (this._closing) {
480
515
  return this._closing.future;
481
516
  }
482
- if (!this._executingCounter) {
483
- this._closed = true;
484
- this._socket.destroy();
485
- return Future.resolve();
486
- }
487
517
  const closing = Future.withResolvers();
488
518
  this._closing = closing;
489
519
  closing.future.tap(() => {
@@ -491,6 +521,7 @@ export class Connection {
491
521
  this._closed = true;
492
522
  this._socket.destroy();
493
523
  });
524
+ this._tryFinishClosing();
494
525
  return closing.future;
495
526
  }
496
527
  }
package/dist/error.d.ts CHANGED
@@ -31,5 +31,6 @@ export declare const ErrSSLDenied: PostgresError;
31
31
  export declare const ErrDatabaseNotFound: PostgresError;
32
32
  export declare const ErrUntrustedCertificate: PostgresError;
33
33
  export declare const ErrCertificateFileNotFound: PostgresError;
34
+ export declare const ErrTransactionInProgress: PostgresError;
34
35
  export declare function createBindTypeError(index: number, expectedType: DataTypeOid, actualValue: unknown): PostgresError;
35
36
  //# sourceMappingURL=error.d.ts.map
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAgB,MAAM,sBAAsB,CAAA;AAGhE,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;AACzH,eAAO,MAAM,wBAAwB,eAGpC,CAAA;AA+BD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,GAAG,aAAa,CAqBjH"}
package/dist/error.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { handlers } from "./protocol/types";
1
2
  export class PostgresError extends Error {
2
3
  constructor(message, code = 'undeclared', detail = '', severity = '', where = '', hint = '', position = '', dataType = '', constraint = '') {
3
4
  super(message);
@@ -51,14 +52,44 @@ export const ErrSSLDenied = new PostgresError("SSL is required but server denied
51
52
  export const ErrDatabaseNotFound = new PostgresError('Database with that dsn not found');
52
53
  export const ErrUntrustedCertificate = new PostgresError("Database SSL certificate is untrusted or self-signed");
53
54
  export const ErrCertificateFileNotFound = new PostgresError("The SSL certificate file specified in caPath was not found");
55
+ export const ErrTransactionInProgress = new PostgresError("This connection has an active transaction — use the Transaction object passed into begin()'s callback instead of the Connection directly", 'transaction_in_progress', '', "ERROR");
56
+ function describeValue(value) {
57
+ if (value === null)
58
+ return 'null';
59
+ if (value === undefined)
60
+ return 'undefined';
61
+ if (typeof value === 'bigint')
62
+ return `${value}n`;
63
+ if (typeof value === 'string') {
64
+ return value.length > 60 ? `${JSON.stringify(value.slice(0, 60))}…` : JSON.stringify(value);
65
+ }
66
+ if (Buffer.isBuffer(value) || value instanceof Uint8Array) {
67
+ return `Uint8Array(length=${value.length})`;
68
+ }
69
+ if (value instanceof Date) {
70
+ return `Date(${isNaN(value.getTime()) ? 'Invalid Date' : value.toISOString()})`;
71
+ }
72
+ if (Array.isArray(value)) {
73
+ const preview = value.slice(0, 5).map(describeValue).join(', ');
74
+ return `[${preview}${value.length > 5 ? ', …' : ''}] (length=${value.length})`;
75
+ }
76
+ if (typeof value === 'object') {
77
+ try {
78
+ const json = JSON.stringify(value);
79
+ return json.length > 120 ? `${json.slice(0, 120)}…` : json;
80
+ }
81
+ catch {
82
+ return Object.prototype.toString.call(value);
83
+ }
84
+ }
85
+ return String(value);
86
+ }
54
87
  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
- );
88
+ const position = index + 1;
89
+ const info = handlers[expectedType];
90
+ const expectedPgType = info?.pgType ?? `oid ${expectedType}`;
91
+ const expectedJsShape = info?.jsShape ?? '(тип не описан в справочнике)';
92
+ const message = `Bind error: Parameter $${position} type mismatch. ` +
93
+ `Expected PostgreSQL type "${expectedPgType}" (${expectedJsShape}), received: ${describeValue(actualValue)}.`;
94
+ return new PostgresError(message, '22000', `Parameter $${position} failed client-side binary validation for pg type "${expectedPgType}".`, 'ERROR', 'writeBind() inside driver', `Pass a value matching ${expectedJsShape} for $${position}.`, '', expectedPgType);
64
95
  }
package/dist/index.d.ts CHANGED
@@ -2,6 +2,35 @@ import { Pool as PgtxPool } from "./pool";
2
2
  import { IdentifierClause, InsertClause, ArrayClause, UpdateClause, ExcludeUpdateClause, WhereClause, LiteralClause, FragmentClause } from './clauses';
3
3
  import { Connection } from "./connection";
4
4
  import { Transaction } from "./transaction";
5
+ export type PgPoint = {
6
+ x: number;
7
+ y: number;
8
+ };
9
+ export type PgLine = {
10
+ a: number;
11
+ b: number;
12
+ c: number;
13
+ };
14
+ export type PgLineSegment = {
15
+ a: PgPoint;
16
+ b: PgPoint;
17
+ };
18
+ export type PgBox = {
19
+ high: PgPoint;
20
+ low: PgPoint;
21
+ };
22
+ export type PgPath = {
23
+ closed: boolean;
24
+ points: PgPoint[];
25
+ };
26
+ export type PgPolygon = {
27
+ points: PgPoint[];
28
+ };
29
+ export type PgInterval = {
30
+ months: number;
31
+ days: number;
32
+ microseconds: number;
33
+ };
5
34
  /**
6
35
  * Core SQL tagging utility for Pgtx.
7
36
  * Provides type-safe helpers for building dynamic queries with recursive support.
@@ -16,7 +45,7 @@ export declare const sql: {
16
45
  * // Result: (name, age) VALUES ($1, $2)
17
46
  *
18
47
  * @example
19
- * sql.insert([{ id: 1 }, { id: 2 }])
48
+ * sql.insert({ id: 1 }, { id: 2 })
20
49
  * // Result: (id) VALUES ($1), ($2)
21
50
  */
22
51
  insert: typeof InsertClause.create;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAC1C,OAAO,EACH,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,mBAAmB,EAEnB,WAAW,EACX,aAAa,EACb,cAAc,EACjB,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C;;;GAGG;AACH,eAAO,MAAM,GAAG;IACZ;;;;;;;;;;;OAWG;;IAGH;;;;;;OAMG;;IAGH;;;;;;OAMG;;IAGH;;;;;;;;OAQG;;IAGH;;;;;;;;;;;KAWC;;IAGD;;;;;;;;;;OAUG;;IAGH;;;;;;;OAOG;;IAGH;;;;;;;;OAQG;;IAGH;;;;;;;;;;;;;;;;;;;OAmBG;;CAEN,CAAA;AAGD;;;GAGG;AAEH,OAAO,EAAE,UAAU,IAAI,UAAU,EAAE,CAAA;AAEnC,OAAO,EAAE,WAAW,IAAI,WAAW,EAAE,CAAA;AAErC,OAAO,EAAE,QAAQ,IAAI,IAAI,EAAE,CAAA;AAG3B,cAAc,WAAW,CAAA;AACzB,cAAc,SAAS,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAC1C,OAAO,EACH,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,mBAAmB,EAEnB,WAAW,EACX,aAAa,EACb,cAAc,EACjB,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAG5C,MAAM,MAAM,OAAO,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAC9C,MAAM,MAAM,MAAM,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AACxD,MAAM,MAAM,aAAa,GAAG;IAAE,CAAC,EAAE,OAAO,CAAC;IAAC,CAAC,EAAE,OAAO,CAAA;CAAE,CAAA;AACtD,MAAM,MAAM,KAAK,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,OAAO,CAAA;CAAE,CAAA;AACnD,MAAM,MAAM,MAAM,GAAG;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,OAAO,EAAE,CAAA;CAAE,CAAA;AAC3D,MAAM,MAAM,SAAS,GAAG;IAAE,MAAM,EAAE,OAAO,EAAE,CAAA;CAAE,CAAA;AAC7C,MAAM,MAAM,UAAU,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAA;AAE/E;;;GAGG;AACH,eAAO,MAAM,GAAG;IACZ;;;;;;;;;;;OAWG;;IAGH;;;;;;OAMG;;IAGH;;;;;;OAMG;;IAGH;;;;;;;;OAQG;;IAGH;;;;;;;;;;;KAWC;;IAGD;;;;;;;;;;OAUG;;IAGH;;;;;;;OAOG;;IAGH;;;;;;;;OAQG;;IAGH;;;;;;;;;;;;;;;;;;;OAmBG;;CAEN,CAAA;AAGD;;;GAGG;AAEH,OAAO,EAAE,UAAU,IAAI,UAAU,EAAE,CAAA;AAEnC,OAAO,EAAE,WAAW,IAAI,WAAW,EAAE,CAAA;AAErC,OAAO,EAAE,QAAQ,IAAI,IAAI,EAAE,CAAA;AAG3B,cAAc,WAAW,CAAA;AACzB,cAAc,SAAS,CAAA"}
package/dist/index.js CHANGED
@@ -16,7 +16,7 @@ export const sql = {
16
16
  * // Result: (name, age) VALUES ($1, $2)
17
17
  *
18
18
  * @example
19
- * sql.insert([{ id: 1 }, { id: 2 }])
19
+ * sql.insert({ id: 1 }, { id: 2 })
20
20
  * // Result: (id) VALUES ($1), ($2)
21
21
  */
22
22
  insert: InsertClause.create,
package/dist/pool.d.ts CHANGED
@@ -73,6 +73,8 @@ export declare class Pool {
73
73
  get size(): number;
74
74
  /** Total connections managed (idle + in use). */
75
75
  get total(): number;
76
+ get isOpened(): boolean;
77
+ get isClosed(): boolean;
76
78
  /**
77
79
  * Closes idle connections and rejects pending `acquire()` calls. Not usable afterward.
78
80
  */
@@ -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;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"}
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,IAAI,QAAQ,YAEX;IAGD,IAAI,QAAQ,YAEX;IAGD;;OAEG;IACH,KAAK;CAiBR"}