@m2k-5f/pgtx 2.0.1 → 2.1.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.
package/README.md CHANGED
@@ -25,10 +25,12 @@ npm install @m2k-5f/pgtx
25
25
 
26
26
  | Tool | RPS | Avg Time | Connections |
27
27
  |------|-----|----------|-------------|
28
- | **Pgtx** | **1584** | **0.631ms** | **20 (used only one)** |
29
- | Native `pg` | 179 | 5.585ms | 20 |
28
+ | **Pgtx** | **1584** | **0.631ms** | **20 pool connections (pipeline multiplexing)** |
29
+ | Native `pg` | 179 | 5.585ms | 20 pool connections |
30
30
 
31
- **9x faster**
31
+ **Up to 9x faster in concurrent pipeline workloads**
32
+
33
+ **Pgtx achieves higher throughput by multiplexing concurrent queries over PostgreSQL connections using pipeline execution.**
32
34
 
33
35
 
34
36
  > Benchmark source available in the [repository](https://github.com/M2K-5F/pgtx).
@@ -43,7 +45,7 @@ npm install @m2k-5f/pgtx
43
45
  - **Bulk inserts** — Auto-extract columns from objects
44
46
  - **Dynamic updates** — Generate SET clauses from objects
45
47
  - **Recursive fragments** — Compose SQL like Lego
46
- - **Prepared statements** — Pre-parse any SQL with parameters
48
+ - **Prepared statements** — Automatic prepared statement caching
47
49
  - **Connection pool** — Auto-management connections with support for pipeline queries via the pool itself.
48
50
  - **Zero dependencies** — Lightweight and blazing
49
51
 
@@ -1,6 +1,5 @@
1
- import { ColumnDescription } from "./types";
1
+ import { Branded, ColumnDescription } from "./types";
2
2
  import { Transaction } from "./transaction";
3
- import { Queue } from "./queue";
4
3
  type LogLevel = "none" | "error" | "notice" | "query";
5
4
  export type ConnectionParams = {
6
5
  user: string;
@@ -10,17 +9,25 @@ export type ConnectionParams = {
10
9
  database: string;
11
10
  logLevel?: LogLevel;
12
11
  };
13
- export type ConnectionQueryContext = {
14
- columns: ColumnDescription[];
12
+ export type ExecuteQueueUnit = {
15
13
  rows: (string | null)[][];
16
14
  resolve: (value: any) => void;
17
15
  reject: (err: Error) => void;
18
- text: string;
19
- statementName: string | null;
16
+ statementName: StatementName;
20
17
  };
21
- export declare class ConnectionQueryQueue extends Queue<ConnectionQueryContext> {
22
- rejectAllNext(error: Error): void;
23
- }
18
+ export type ParsingQueueUnit = {
19
+ resolve: (statementName: StatementName) => void;
20
+ reject: (error: Error) => void;
21
+ text: QueryText;
22
+ statementName: StatementName;
23
+ };
24
+ export type DescribeQueueUnit = {
25
+ resolve: (value: ColumnDescription[]) => void;
26
+ reject: (error: Error) => void;
27
+ statementName: StatementName;
28
+ };
29
+ export type StatementName = Branded<string, 'statementName'>;
30
+ export type QueryText = Branded<string, 'queryText'>;
24
31
  /**
25
32
  * Represents a single dedicated connection to the PostgreSQL database.
26
33
  *
@@ -56,10 +63,11 @@ export declare class Connection {
56
63
  private _isAlive;
57
64
  private _socket;
58
65
  private _writer;
59
- private _queue;
60
- private _statementDescriptions;
61
- private _statements;
62
- private _parsePending;
66
+ private _pipelinesQueue;
67
+ private _described;
68
+ private _describingPending;
69
+ private _parsed;
70
+ private _parsingPending;
63
71
  private _stmtCounter;
64
72
  private _logLevel;
65
73
  private _nextStatement;
@@ -69,7 +77,7 @@ export declare class Connection {
69
77
  *
70
78
  * @param params - Connection parameters
71
79
  * @returns A new Connection instance
72
- * @throws {Error} If authentication fails or connection cannot be established
80
+ * @throws {PostgresError} If authentication fails or connection cannot be established
73
81
  *
74
82
  * @example
75
83
  * ```ts
@@ -107,7 +115,9 @@ export declare class Connection {
107
115
  * const users = await conn.query<User>`SELECT * FROM users`
108
116
  * ```
109
117
  */
110
- query<T extends Record<string, unknown>>(templates: TemplateStringsArray, ...args: any[]): Promise<T[]>;
118
+ query<T extends Record<string, any>>(templates: TemplateStringsArray, ...params: any[]): Promise<T[]>;
119
+ private _createQuery;
120
+ private _writeQuery;
111
121
  /**
112
122
  * Starts a managed transaction on this connection.
113
123
  *
@@ -132,13 +142,15 @@ export declare class Connection {
132
142
  begin<T>(txCallback: (transaction: Transaction) => Promise<T>): Promise<T>;
133
143
  private _registerFlush;
134
144
  private _flush;
145
+ private _getCurrentQuery;
146
+ private _rejectPipeline;
135
147
  private _handlePacket;
136
- private _handleError;
137
148
  /**
138
149
  * Checks if the connection is still alive and usable.
139
150
  * Returns `false` if the socket is destroyed or connection is dead.
140
151
  */
141
152
  get isAlive(): boolean;
153
+ private _destroyConnection;
142
154
  /**
143
155
  * Closes the connection immediately.
144
156
  * All pending queries will be rejected with an error.
@@ -1 +1 @@
1
- {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAE3C,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAE/B,KAAK,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAA;AAErD,MAAM,MAAM,gBAAgB,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,QAAQ,CAAA;CACtB,CAAA;AAKD,MAAM,MAAM,sBAAsB,GAAG;IACjC,OAAO,EAAE,iBAAiB,EAAE,CAAA;IAC5B,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,EAAE,CAAA;IACzB,OAAO,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAA;IAC7B,MAAM,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAA;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;CAC/B,CAAA;AAGD,qBAAa,oBAAqB,SAAQ,KAAK,CAAC,sBAAsB,CAAC;IAEnE,aAAa,CAAC,KAAK,EAAE,KAAK;CAM7B;AAGD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBAAa,UAAU;IACnB,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,QAAQ,CAAO;IACvB,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,MAAM,CAA6B;IAC3C,OAAO,CAAC,sBAAsB,CAAyC;IACvE,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,aAAa,CAA4B;IACjD,OAAO,CAAC,YAAY,CAAI;IACxB,OAAO,CAAC,SAAS,CAAU;IAG3B,OAAO,CAAC,cAAc;IAItB,OAAO;IAcP;;;;;;;;;;;;;;;;;OAiBG;WACU,GAAG,CAAC,MAAM,EAAE,gBAAgB;IAQzC;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,oBAAoB,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IA0DvG;;;;;;;;;;;;;;;;;;;;OAoBG;IACG,KAAK,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,WAAW,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAqBhF,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,MAAM;IAkBd,OAAO,CAAC,aAAa;IAoFrB,OAAO,CAAC,YAAY;IAQpB;;;OAGG;IACH,IAAI,OAAO,YAEV;IAGD;;;;;;;;;OASG;IACH,KAAK;CAIR"}
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Connection = exports.ConnectionQueryQueue = void 0;
3
+ exports.Connection = void 0;
4
4
  const process_1 = require("process");
5
5
  const connection_request_writer_1 = require("./protocol/connection-request-writer");
6
6
  const socket_authorization_1 = require("./protocol/socket-authorization");
@@ -10,16 +10,8 @@ const template_compiler_1 = require("./utils/template-compiler");
10
10
  const transaction_1 = require("./transaction");
11
11
  const socket_connector_1 = require("./protocol/socket-connector");
12
12
  const queue_1 = require("./queue");
13
+ const query_1 = require("./query");
13
14
  const ErrConnectionDead = new Error("Connection is Dead");
14
- class ConnectionQueryQueue extends queue_1.Queue {
15
- rejectAllNext(error) {
16
- while (!this.isFree) {
17
- this.get().reject(error);
18
- this.next();
19
- }
20
- }
21
- }
22
- exports.ConnectionQueryQueue = ConnectionQueryQueue;
23
15
  /**
24
16
  * Represents a single dedicated connection to the PostgreSQL database.
25
17
  *
@@ -57,13 +49,16 @@ class Connection {
57
49
  constructor(socket, writer, logLevel) {
58
50
  this._isFlushing = false;
59
51
  this._isAlive = true;
60
- this._queue = new ConnectionQueryQueue();
61
- this._statementDescriptions = new Map();
62
- this._statements = new Map();
63
- this._parsePending = new Map();
52
+ this._pipelinesQueue = new queue_1.Queue();
53
+ this._described = new Map();
54
+ this._describingPending = new Set();
55
+ this._parsed = new Map();
56
+ this._parsingPending = new Map();
64
57
  this._stmtCounter = 0;
65
58
  this._logLevel = logLevel;
66
- this._socket = new socket_connector_1.SocketConnector(socket, (type, reader) => this._handlePacket(type, reader), error => this._handleError(error));
59
+ this._socket = new socket_connector_1.SocketConnector(socket, (type, reader) => this._handlePacket(type, reader), (err) => {
60
+ this._destroyConnection();
61
+ });
67
62
  this._writer = writer;
68
63
  }
69
64
  /**
@@ -71,7 +66,7 @@ class Connection {
71
66
  *
72
67
  * @param params - Connection parameters
73
68
  * @returns A new Connection instance
74
- * @throws {Error} If authentication fails or connection cannot be established
69
+ * @throws {PostgresError} If authentication fails or connection cannot be established
75
70
  *
76
71
  * @example
77
72
  * ```ts
@@ -113,47 +108,67 @@ class Connection {
113
108
  * const users = await conn.query<User>`SELECT * FROM users`
114
109
  * ```
115
110
  */
116
- query(templates, ...args) {
111
+ query(templates, ...params) {
112
+ this._registerFlush();
117
113
  if (!this._isAlive)
118
114
  throw ErrConnectionDead;
119
- const query = (0, template_compiler_1.compileSqlTemplate)({ templates, args });
115
+ const { text, args } = (0, template_compiler_1.compileSqlTemplate)({ templates, args: params });
120
116
  if (this._logLevel === 'query') {
121
- console.log(`\nQUERY: ${query.text}\n${query.args.length !== 0 ? `ARGUMENTS: [${query.args}]\n` : ""}`);
117
+ console.log(`\nQUERY: ${text}\n${args.length !== 0 ? `ARGUMENTS: [${args}]\n` : ""}`);
122
118
  }
123
- return new Promise((resolve, reject) => {
124
- this._registerFlush();
125
- if (query.args.length === 0) {
126
- this._writer.writeQuery(query.text);
127
- this._queue.push({
128
- resolve, reject, columns: [], rows: [], statementName: null, text: query.text
129
- });
119
+ const query = this._createQuery(text, args);
120
+ this._writeQuery(query);
121
+ return query.promise;
122
+ }
123
+ _createQuery(text, args) {
124
+ if (this._parsed.has(text)) {
125
+ const statementName = this._parsed.get(text);
126
+ if (this._described.has(statementName)) {
127
+ const columns = this._described.get(statementName);
128
+ return new query_1.Query(text, args, query_1.QueryState.Executing, statementName, columns);
130
129
  }
131
130
  else {
132
- let rejectParsed = reject;
133
- let statementName = '';
134
- if (!this._statements.has(query.text) && !this._parsePending.has(query.text)) {
135
- statementName = this._nextStatement();
136
- this._parsePending.set(query.text, statementName);
137
- rejectParsed = (error) => {
138
- this._parsePending.delete(query.text);
139
- reject(error);
140
- };
141
- this._writer
142
- .writeParse(statementName, query.text);
143
- console.log('parsed once', statementName, query.text);
131
+ if (this._describingPending.has(statementName)) {
132
+ return new query_1.Query(text, args, query_1.QueryState.Executing, statementName);
144
133
  }
145
- statementName = this._statements.get(query.text) || this._parsePending.get(query.text);
146
- if (!this._statementDescriptions.has(statementName)) {
147
- this._writer.writeDescribe(statementName);
134
+ else {
135
+ this._describingPending.add(statementName);
136
+ return new query_1.Query(text, args, query_1.QueryState.Describing, statementName);
148
137
  }
149
- this._queue.push({
150
- resolve, reject: rejectParsed, columns: [], rows: [], statementName: statementName, text: query.text
151
- });
152
- this._writer
153
- .writeBind("", statementName, query.args)
154
- .writeExecute("");
155
138
  }
156
- });
139
+ }
140
+ else {
141
+ if (this._parsingPending.has(text)) {
142
+ const statementName = this._parsingPending.get(text);
143
+ return new query_1.Query(text, args, query_1.QueryState.Describing, statementName);
144
+ }
145
+ else {
146
+ const statementName = this._nextStatement();
147
+ this._parsingPending.set(text, statementName);
148
+ return new query_1.Query(text, args, query_1.QueryState.Parsing, statementName);
149
+ }
150
+ }
151
+ }
152
+ _writeQuery(query) {
153
+ if (query.state === query_1.QueryState.Parsing) {
154
+ this._writer
155
+ .writeParse(query.statementName, query.text)
156
+ .writeDescribe(query.statementName)
157
+ .writeBind("", query.statementName, query.args)
158
+ .writeExecute("");
159
+ }
160
+ if (query.state === query_1.QueryState.Describing) {
161
+ this._writer
162
+ .writeDescribe(query.statementName)
163
+ .writeBind("", query.statementName, query.args)
164
+ .writeExecute("");
165
+ }
166
+ if (query.state === query_1.QueryState.Executing) {
167
+ this._writer
168
+ .writeBind("", query.statementName, query.args)
169
+ .writeExecute("");
170
+ }
171
+ this._pipelinesQueue.get().push(query);
157
172
  }
158
173
  /**
159
174
  * Starts a managed transaction on this connection.
@@ -196,12 +211,16 @@ class Connection {
196
211
  _registerFlush() {
197
212
  if (!this._isFlushing) {
198
213
  this._isFlushing = true;
199
- (0, process_1.nextTick)(() => this._flush());
214
+ this._pipelinesQueue.push(new queue_1.Queue());
215
+ (0, process_1.nextTick)(() => {
216
+ this._flush();
217
+ });
200
218
  }
201
219
  }
202
220
  _flush() {
221
+ this._isFlushing = false;
203
222
  if (!this._isAlive) {
204
- this._queue.rejectAllNext(ErrConnectionDead);
223
+ this._rejectPipeline(ErrConnectionDead);
205
224
  return;
206
225
  }
207
226
  try {
@@ -209,19 +228,30 @@ class Connection {
209
228
  this._writer.clear();
210
229
  }
211
230
  catch (err) {
212
- this._isAlive = false;
213
- this._socket.destroy();
214
- this._queue.rejectAllNext(ErrConnectionDead);
231
+ this._destroyConnection();
232
+ }
233
+ }
234
+ _getCurrentQuery() {
235
+ return this._pipelinesQueue.get().get();
236
+ }
237
+ _rejectPipeline(error) {
238
+ if (this._pipelinesQueue.isFree)
239
+ return;
240
+ const queue = this._pipelinesQueue.get();
241
+ while (queue.hasMore) {
242
+ queue.get().reject(error);
243
+ queue.next();
215
244
  }
216
245
  }
217
246
  _handlePacket(type, reader) {
218
- const context = this._queue.get();
219
247
  switch (type) {
220
248
  case constants_1.ResponseTypes.ParseComplete:
221
249
  {
222
250
  reader.readParseComplete();
223
- this._statements.set(context.text, context.statementName);
224
- this._parsePending.delete(context.text);
251
+ const query = this._getCurrentQuery();
252
+ this._parsingPending.delete(query.text);
253
+ this._parsed.set(query.text, query.statementName);
254
+ query.state = query_1.QueryState.Describing;
225
255
  }
226
256
  break;
227
257
  case constants_1.ResponseTypes.BindComplete:
@@ -242,50 +272,74 @@ class Connection {
242
272
  case constants_1.ResponseTypes.NoData:
243
273
  {
244
274
  reader.readNoData();
245
- if (context.statementName) {
246
- this._statementDescriptions.set(context.statementName, []);
247
- }
275
+ const query = this._getCurrentQuery();
276
+ this._describingPending.delete(query.statementName);
277
+ this._described.set(query.statementName, []);
278
+ query.state = query_1.QueryState.Executing;
248
279
  }
249
280
  break;
250
281
  case constants_1.ResponseTypes.RowDescription:
251
282
  {
252
- if (context.statementName) {
253
- this._statementDescriptions.set(context.statementName, reader.readRowDescription());
254
- }
255
- else {
256
- context.columns = reader.readRowDescription();
257
- }
283
+ const columns = reader.readRowDescription();
284
+ const query = this._getCurrentQuery();
285
+ this._describingPending.delete(query.statementName);
286
+ this._described.set(query.statementName, columns);
287
+ query.state = query_1.QueryState.Executing;
288
+ query.columns = columns;
258
289
  }
259
290
  break;
260
291
  case constants_1.ResponseTypes.DataRow:
261
292
  {
262
- context.rows.push(reader.readDataRow());
293
+ const query = this._getCurrentQuery();
294
+ query.rows.push(reader.readDataRow());
263
295
  }
264
296
  break;
265
297
  case constants_1.ResponseTypes.ComandComplete:
266
298
  {
267
299
  reader.readCommandComplete();
268
- if (context.statementName) {
269
- context.columns = this._statementDescriptions.get(context.statementName);
300
+ if (this._pipelinesQueue.isFree) {
301
+ this._destroyConnection();
302
+ break;
303
+ }
304
+ const query = this._getCurrentQuery();
305
+ if (!query) {
306
+ this._destroyConnection();
307
+ break;
270
308
  }
271
- context.resolve((0, value_parser_1.parseRowValues)(context.columns, context.rows));
272
- this._queue.next();
309
+ if (!query.columns) {
310
+ query.columns = this._described.get(query.statementName);
311
+ }
312
+ query.state = query_1.QueryState.Completed;
313
+ this._pipelinesQueue.get().next();
314
+ query.resolve((0, value_parser_1.parseRowValues)(query.columns, query.rows));
273
315
  }
274
316
  break;
275
317
  case constants_1.ResponseTypes.ErrorResponse:
276
318
  {
277
- const message = reader.readErrorResponse();
319
+ const error = reader.readErrorResponse();
278
320
  if (this._logLevel === 'error' || this._logLevel === 'notice' || this._logLevel === 'query') {
279
- console.log(`\nError: ${message}\n`);
321
+ console.log(`\nError: ${error}\n`);
280
322
  }
281
- const error = new Error(message);
282
- this._queue.rejectAllNext(error);
323
+ const query = this._getCurrentQuery();
324
+ switch (query.state) {
325
+ case query_1.QueryState.Parsing:
326
+ this._parsingPending.delete(query.text);
327
+ break;
328
+ case query_1.QueryState.Describing:
329
+ this._describingPending.delete(query.statementName);
330
+ break;
331
+ }
332
+ query.state = query_1.QueryState.Failed;
333
+ this._rejectPipeline(error);
283
334
  }
284
335
  break;
285
336
  case constants_1.ResponseTypes.ReadyForQuery:
286
337
  {
287
- this._isFlushing = false;
288
338
  reader.readReadyForQuery();
339
+ const pipeline = this._pipelinesQueue.get();
340
+ if (!pipeline.hasMore) {
341
+ this._pipelinesQueue.next();
342
+ }
289
343
  }
290
344
  break;
291
345
  case constants_1.ResponseTypes.Notice:
@@ -299,11 +353,6 @@ class Connection {
299
353
  default: console.warn('Undeclared response type: ', type);
300
354
  }
301
355
  }
302
- _handleError(error) {
303
- this._isAlive = false;
304
- this._queue.rejectAllNext(error);
305
- this._socket.destroy();
306
- }
307
356
  /**
308
357
  * Checks if the connection is still alive and usable.
309
358
  * Returns `false` if the socket is destroyed or connection is dead.
@@ -311,6 +360,14 @@ class Connection {
311
360
  get isAlive() {
312
361
  return this._isAlive && !this._socket.isDestroyed;
313
362
  }
363
+ _destroyConnection() {
364
+ this._isAlive = false;
365
+ this._socket.destroy();
366
+ while (this._pipelinesQueue.hasMore) {
367
+ this._rejectPipeline(ErrConnectionDead);
368
+ this._pipelinesQueue.next();
369
+ }
370
+ }
314
371
  /**
315
372
  * Closes the connection immediately.
316
373
  * All pending queries will be rejected with an error.
@@ -322,8 +379,7 @@ class Connection {
322
379
  * ```
323
380
  */
324
381
  close() {
325
- this._isAlive = false;
326
- this._socket.destroy();
382
+ this._destroyConnection();
327
383
  }
328
384
  }
329
385
  exports.Connection = Connection;
@@ -0,0 +1,18 @@
1
+ export declare class PostgresError extends Error {
2
+ message: string;
3
+ code: string;
4
+ detail: string;
5
+ severity: string;
6
+ where: string;
7
+ hint: string;
8
+ position: string;
9
+ dataType: string;
10
+ constraint: string;
11
+ constructor(message: string, code: string, detail: string, severity: string, where: string, hint: string, position: string, dataType: string, constraint: string);
12
+ get isParseError(): boolean;
13
+ get isDeadlock(): boolean;
14
+ get isConstraintViolation(): boolean;
15
+ get isTimeout(): boolean;
16
+ get isConnectionFailure(): boolean;
17
+ }
18
+ //# sourceMappingURL=error.d.ts.map
@@ -0,0 +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,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM;IAQ7B,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"}
package/dist/error.js ADDED
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PostgresError = void 0;
4
+ class PostgresError extends Error {
5
+ constructor(message, code, detail, severity, where, hint, position, dataType, constraint) {
6
+ super(message);
7
+ this.message = message;
8
+ this.code = code;
9
+ this.detail = detail;
10
+ this.severity = severity;
11
+ this.where = where;
12
+ this.hint = hint;
13
+ this.position = position;
14
+ this.dataType = dataType;
15
+ this.constraint = constraint;
16
+ this.name = "PostgresError";
17
+ }
18
+ // Parse error
19
+ get isParseError() {
20
+ return this.code.startsWith('42');
21
+ }
22
+ // Execute error
23
+ get isDeadlock() {
24
+ return this.code === '40P01';
25
+ }
26
+ // Execute error
27
+ get isConstraintViolation() {
28
+ return this.code.startsWith('23');
29
+ }
30
+ get isTimeout() {
31
+ return this.code === '57014';
32
+ }
33
+ get isConnectionFailure() {
34
+ return this.code.startsWith('08') || this.code.startsWith('57') && this.code !== '57014';
35
+ }
36
+ }
37
+ exports.PostgresError = PostgresError;
package/dist/index.d.ts CHANGED
@@ -121,4 +121,5 @@ export { Connection as Connection };
121
121
  export { Transaction as Transaction };
122
122
  export { PgtxPool as Pool };
123
123
  export { setTypeParser as setTypeParser };
124
+ export * from "./clauses";
124
125
  //# sourceMappingURL=index.d.ts.map
@@ -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,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD;;;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;AAE3B,OAAO,EAAE,aAAa,IAAI,aAAa,EAAE,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;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD;;;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;AAE3B,OAAO,EAAE,aAAa,IAAI,aAAa,EAAE,CAAA;AAEzC,cAAc,WAAW,CAAA"}
package/dist/index.js CHANGED
@@ -1,4 +1,18 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
2
16
  Object.defineProperty(exports, "__esModule", { value: true });
3
17
  exports.setTypeParser = exports.Pool = exports.Transaction = exports.Connection = exports.sql = void 0;
4
18
  const pool_1 = require("./pool");
@@ -120,3 +134,4 @@ exports.sql = {
120
134
  */
121
135
  array: clauses_1.ArrayClause.create,
122
136
  };
137
+ __exportStar(require("./clauses"), exports);
package/dist/pool.d.ts CHANGED
@@ -127,7 +127,7 @@ export declare class Pool {
127
127
  * const users = await pool.query<User>`SELECT * FROM users`
128
128
  * ```
129
129
  */
130
- query<T extends Record<string, unknown>>(templates: TemplateStringsArray, ...args: any[]): Promise<T[]>;
130
+ query<T extends Record<string, any>>(templates: TemplateStringsArray, ...args: any[]): Promise<T[]>;
131
131
  /**
132
132
  * Number of available (idle) connections in the pool.
133
133
  */
@@ -1 +1 @@
1
- {"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAI3C,KAAK,UAAU,GAAG,gBAAgB,GAAG;IACjC,GAAG,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AASD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,qBAAa,IAAI;IACb,OAAO,CAAC,UAAU,CAA0B;IAC5C,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,IAAI,CAAQ;IACpB,OAAO,CAAC,MAAM,CAAI;IAClB,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,SAAS,CAAQ;IAEzB,OAAO,CAAC,YAAY;gBAIR,MAAM,EAAE,UAAU;IAM9B;;;;;;;;;;;;;;;;;OAiBG;IACG,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC;IA6BpC;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,IAAI,EAAE,UAAU;IAoBxB;;;;;;;;;;;;;;;;;OAiBG;IACG,KAAK,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,WAAW,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAYhF;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,oBAAoB,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAmCvG;;OAEG;IACH,IAAI,IAAI,WAEP;IAGD;;;OAGG;IACH,IAAI,KAAK,WAER;IAGD;;;;;;;;;;OAUG;IACG,KAAK;CAed"}
1
+ {"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAK3C,KAAK,UAAU,GAAG,gBAAgB,GAAG;IACjC,GAAG,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAQD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,qBAAa,IAAI;IACb,OAAO,CAAC,UAAU,CAA0B;IAC5C,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,IAAI,CAAQ;IACpB,OAAO,CAAC,MAAM,CAAI;IAClB,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,SAAS,CAAQ;IAEzB,OAAO,CAAC,YAAY;gBAIR,MAAM,EAAE,UAAU;IAM9B;;;;;;;;;;;;;;;;;OAiBG;IACG,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC;IA6BpC;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,IAAI,EAAE,UAAU;IAoBxB;;;;;;;;;;;;;;;;;OAiBG;IACG,KAAK,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,WAAW,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAYhF;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,SAAS,EAAE,oBAAoB,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAmCnG;;OAEG;IACH,IAAI,IAAI,WAEP;IAGD;;;OAGG;IACH,IAAI,KAAK,WAER;IAGD;;;;;;;;;;OAUG;IACG,KAAK;CAed"}
package/dist/pool.js CHANGED
@@ -71,7 +71,7 @@ class Pool {
71
71
  */
72
72
  async acquire() {
73
73
  this._checkClosed();
74
- while (!this._available.isFree) {
74
+ while (this._available.hasMore) {
75
75
  const conn = this._available.get();
76
76
  this._available.next();
77
77
  if (conn.isAlive) {
@@ -117,7 +117,7 @@ class Pool {
117
117
  this._total--;
118
118
  return;
119
119
  }
120
- if (!this._waiting.isFree) {
120
+ if (this._waiting.hasMore) {
121
121
  const waiter = this._waiting.get();
122
122
  this._waiting.next();
123
123
  waiter.resolve(conn);
@@ -178,7 +178,7 @@ class Pool {
178
178
  */
179
179
  query(templates, ...args) {
180
180
  this._checkClosed();
181
- while (!this._available.isFree) {
181
+ while (this._available.hasMore) {
182
182
  const conn = this._available.get();
183
183
  if (conn.isAlive) {
184
184
  return conn.query(templates, ...args);
@@ -229,12 +229,12 @@ class Pool {
229
229
  * ```
230
230
  */
231
231
  async close() {
232
- while (!this._available.isFree) {
232
+ while (this._available.hasMore) {
233
233
  const conn = this._available.get();
234
234
  this._available.next();
235
235
  conn.close();
236
236
  }
237
- while (!this._waiting.isFree) {
237
+ while (this._waiting.hasMore) {
238
238
  const waiter = this._waiting.get();
239
239
  this._waiting.next();
240
240
  waiter.reject(new Error('Pool closed'));
@@ -1,5 +1,6 @@
1
1
  import { AuthenticationCode, ResponseType, TransactionStatus } from "./constants";
2
2
  import { ColumnDescription } from "../types";
3
+ import { PostgresError } from "../error";
3
4
  export declare class ConnectionResponseBuffer {
4
5
  private buffer;
5
6
  private caret;
@@ -31,7 +32,7 @@ export declare class ConnectionResponseReader {
31
32
  PID: number;
32
33
  secret: number;
33
34
  };
34
- readErrorResponse(): string;
35
+ readErrorResponse(): PostgresError;
35
36
  readReadyForQuery(): TransactionStatus;
36
37
  readSaslMechanisms(): string[];
37
38
  readSaslMessage(): string;
@@ -1 +1 @@
1
- {"version":3,"file":"connection-response-reader.d.ts","sourceRoot":"","sources":["../../src/protocol/connection-response-reader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAE5C,qBAAa,wBAAwB;IAI7B,OAAO,CAAC,MAAM;IAHlB,OAAO,CAAC,KAAK,CAAI;IAEjB,OAAO;IAKP,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM;IAK1B,QAAQ,IAAI,MAAM;IAOlB,SAAS,IAAI,MAAM;IAOnB,SAAS,IAAI,MAAM;IAOnB,WAAW,IAAI,MAAM;IAQrB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAOrC,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAOjC,OAAO,IAAI,OAAO;IAKlB,aAAa,IAAI,OAAO;IAaxB,iBAAiB;CAGpB;AAGD,qBAAa,wBAAwB;IAI7B,OAAO,CAAC,MAAM;IAHlB,OAAO,CAAC,mBAAmB,CAAK;IAEhC,OAAO;IAKP,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM;IAK1B,QAAQ,IAC6B,YAAY;IAIjD,kBAAkB,IAGoB,kBAAkB;IAIxD,WAAW;IAKX,mBAAmB;;;;IAUnB,kBAAkB;;;;IAUlB,iBAAiB;IAgBjB,iBAAiB,IAEoB,iBAAiB;IAItD,kBAAkB,IAAI,MAAM,EAAE;IAa9B,eAAe,IAAI,MAAM;IAOzB,kBAAkB;IAuBlB,WAAW;IAmBX,mBAAmB,IAAI,MAAM;IAM7B,OAAO;IAKP,aAAa;IAKb,iBAAiB;IAKjB,iBAAiB;IAKjB,gBAAgB;IAKhB,wBAAwB;IAQxB,UAAU;CAGb"}
1
+ {"version":3,"file":"connection-response-reader.d.ts","sourceRoot":"","sources":["../../src/protocol/connection-response-reader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAGxC,qBAAa,wBAAwB;IAI7B,OAAO,CAAC,MAAM;IAHlB,OAAO,CAAC,KAAK,CAAI;IAEjB,OAAO;IAKP,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM;IAK1B,QAAQ,IAAI,MAAM;IAOlB,SAAS,IAAI,MAAM;IAOnB,SAAS,IAAI,MAAM;IAOnB,WAAW,IAAI,MAAM;IAQrB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAOrC,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAOjC,OAAO,IAAI,OAAO;IAKlB,aAAa,IAAI,OAAO;IAaxB,iBAAiB;CAGpB;AAGD,qBAAa,wBAAwB;IAI7B,OAAO,CAAC,MAAM;IAHlB,OAAO,CAAC,mBAAmB,CAAK;IAEhC,OAAO;IAKP,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM;IAK1B,QAAQ,IAC6B,YAAY;IAIjD,kBAAkB,IAGoB,kBAAkB;IAIxD,WAAW;IAKX,mBAAmB;;;;IAUnB,kBAAkB;;;;IAUlB,iBAAiB,IAAI,aAAa;IA8ClC,iBAAiB,IAEoB,iBAAiB;IAItD,kBAAkB,IAAI,MAAM,EAAE;IAa9B,eAAe,IAAI,MAAM;IAOzB,kBAAkB;IAuBlB,WAAW;IAmBX,mBAAmB,IAAI,MAAM;IAM7B,OAAO;IAKP,aAAa;IAKb,iBAAiB;IAKjB,iBAAiB;IAKjB,gBAAgB;IAKhB,wBAAwB;IAQxB,UAAU;CAGb"}
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ConnectionResponseReader = exports.ConnectionResponseBuffer = void 0;
4
+ const error_1 = require("../error");
4
5
  class ConnectionResponseBuffer {
5
6
  constructor(buffer) {
6
7
  this.buffer = buffer;
@@ -91,16 +92,51 @@ class ConnectionResponseReader {
91
92
  }
92
93
  readErrorResponse() {
93
94
  this.buffer.readInt32();
95
+ let severity = '';
96
+ let code = '';
94
97
  let message = '';
98
+ let detail = '';
99
+ let where = '';
100
+ let hint = '';
101
+ let position = '';
102
+ let dataType = '';
103
+ let constraint = '';
95
104
  while (true) {
96
105
  const marker = this.buffer.readChar();
97
106
  if (marker === '\0')
98
107
  break;
99
108
  const text = this.buffer.readCString();
100
- if (marker === 'M')
101
- message += text;
109
+ switch (marker) {
110
+ case 'S':
111
+ severity = text;
112
+ break;
113
+ case 'C':
114
+ code = text;
115
+ break;
116
+ case 'M':
117
+ message = text;
118
+ break;
119
+ case 'D':
120
+ detail = text;
121
+ break;
122
+ case 'W':
123
+ where = text;
124
+ break;
125
+ case 'H':
126
+ hint = text;
127
+ break;
128
+ case 'P':
129
+ position = text;
130
+ break;
131
+ case 'd':
132
+ dataType = text;
133
+ break;
134
+ case 'n':
135
+ constraint = text;
136
+ break;
137
+ }
102
138
  }
103
- return message;
139
+ return new error_1.PostgresError(message, code, detail, severity, where, hint, position, dataType, constraint);
104
140
  }
105
141
  readReadyForQuery() {
106
142
  this.buffer.readInt32();
@@ -0,0 +1,24 @@
1
+ import { QueryText, StatementName } from "./connection";
2
+ import { ColumnDescription, ValueOF } from "./types";
3
+ export declare const QueryState: {
4
+ readonly Parsing: 0;
5
+ readonly Describing: 1;
6
+ readonly Executing: 2;
7
+ readonly Completed: 3;
8
+ readonly Failed: 4;
9
+ };
10
+ export type State = ValueOF<typeof QueryState>;
11
+ export declare class Query<T> {
12
+ text: QueryText;
13
+ args: (string | null)[];
14
+ state: State;
15
+ statementName: StatementName;
16
+ columns?: ColumnDescription[] | undefined;
17
+ promise: Promise<T[]>;
18
+ resolve: (value: T[]) => void;
19
+ reject: (error: Error) => void;
20
+ rows: (string | null)[][];
21
+ constructor(text: QueryText, args: (string | null)[], state: State, statementName: StatementName, columns?: ColumnDescription[] | undefined);
22
+ setState(state: State): void;
23
+ }
24
+ //# sourceMappingURL=query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAGrD,eAAO,MAAM,UAAU;;;;;;CAMb,CAAA;AAGV,MAAM,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,UAAU,CAAC,CAAA;AAG9C,qBAAa,KAAK,CAAC,CAAC;IAOL,IAAI,EAAE,SAAS;IACf,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE;IACvB,KAAK,EAAE,KAAK;IACZ,aAAa,EAAE,aAAa;IAC5B,OAAO,CAAC,EAAE,iBAAiB,EAAE;IAVxC,OAAO,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,CAAA;IACrB,OAAO,EAAG,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,IAAI,CAAA;IAC9B,MAAM,EAAG,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IAC/B,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,EAAE,CAAK;gBAGnB,IAAI,EAAE,SAAS,EACf,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,EACvB,KAAK,EAAE,KAAK,EACZ,aAAa,EAAE,aAAa,EAC5B,OAAO,CAAC,EAAE,iBAAiB,EAAE,YAAA;IAQxC,QAAQ,CAAC,KAAK,EAAE,KAAK;CAGxB"}
package/dist/query.js ADDED
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Query = exports.QueryState = void 0;
4
+ exports.QueryState = {
5
+ Parsing: 0,
6
+ Describing: 1,
7
+ Executing: 2,
8
+ Completed: 3,
9
+ Failed: 4
10
+ };
11
+ class Query {
12
+ constructor(text, args, state, statementName, columns) {
13
+ this.text = text;
14
+ this.args = args;
15
+ this.state = state;
16
+ this.statementName = statementName;
17
+ this.columns = columns;
18
+ this.rows = [];
19
+ this.promise = new Promise((a, b) => {
20
+ this.resolve = a;
21
+ this.reject = b;
22
+ });
23
+ }
24
+ setState(state) {
25
+ this.state = state;
26
+ }
27
+ }
28
+ exports.Query = Query;
package/dist/queue.d.ts CHANGED
@@ -3,8 +3,10 @@ export declare class Queue<T> {
3
3
  private _pointer;
4
4
  next(): void;
5
5
  get(): T;
6
+ shift(): T;
6
7
  push(item: T): void;
7
8
  get size(): number;
9
+ get hasMore(): boolean;
8
10
  get isFree(): boolean;
9
11
  }
10
12
  //# sourceMappingURL=queue.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AAAA,qBAAa,KAAK,CAAC,CAAC;IAChB,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,QAAQ,CAAI;IAEpB,IAAI;IAQJ,GAAG;IAIH,IAAI,CAAC,IAAI,EAAE,CAAC;IAIZ,IAAI,IAAI,WAEP;IAED,IAAI,MAAM,YAAgD;CAC7D"}
1
+ {"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AAAA,qBAAa,KAAK,CAAC,CAAC;IAChB,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,QAAQ,CAAI;IAEpB,IAAI;IASJ,GAAG;IAKH,KAAK;IAYL,IAAI,CAAC,IAAI,EAAE,CAAC;IAIZ,IAAI,IAAI,WAEP;IAGD,IAAI,OAAO,YAEV;IAGD,IAAI,MAAM,YAA+C;CAC5D"}
package/dist/queue.js CHANGED
@@ -8,7 +8,7 @@ class Queue {
8
8
  }
9
9
  next() {
10
10
  this._pointer++;
11
- if (this._pointer === this._queue.length) {
11
+ if (this._pointer >= this._queue.length) {
12
12
  this._queue = [];
13
13
  this._pointer = 0;
14
14
  }
@@ -16,12 +16,24 @@ class Queue {
16
16
  get() {
17
17
  return this._queue[this._pointer];
18
18
  }
19
+ shift() {
20
+ const item = this._queue[this._pointer];
21
+ this._pointer++;
22
+ if (this._pointer >= this._queue.length) {
23
+ this._queue = [];
24
+ this._pointer = 0;
25
+ }
26
+ return item;
27
+ }
19
28
  push(item) {
20
29
  this._queue.push(item);
21
30
  }
22
31
  get size() {
23
32
  return this._queue.length - this._pointer;
24
33
  }
25
- get isFree() { return this._pointer === this._queue.length; }
34
+ get hasMore() {
35
+ return this._pointer < this._queue.length;
36
+ }
37
+ get isFree() { return this._pointer >= this._queue.length; }
26
38
  }
27
39
  exports.Queue = Queue;
@@ -23,7 +23,7 @@ export declare class Transaction {
23
23
  /**
24
24
  * Executes a query within the current transaction.
25
25
  */
26
- query<T extends Record<string, unknown>>(strings: TemplateStringsArray, ...values: any[]): Promise<T[]>;
26
+ query<T extends Record<string, any>>(strings: TemplateStringsArray, ...values: any[]): Promise<T[]>;
27
27
  /**
28
28
  * Creates a sub-transaction using PostgreSQL SAVEPOINT.
29
29
  * If the callback throws, only the actions within this savepoint are rolled back.
@@ -1 +1 @@
1
- {"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../src/transaction.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAEzC;;;GAGG;AACH,qBAAa,WAAW;IAIhB,QAAQ,CAAC,IAAI,EAAE,UAAU;IAH7B,OAAO,CAAC,UAAU,CAAiB;gBAGtB,IAAI,EAAE,UAAU;IAG7B;;OAEG;IACH,IAAW,QAAQ,IAAI,OAAO,CAE7B;IAED,OAAO,CAAC,WAAW;IAMnB;;OAEG;IACU,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAOpC;;OAEG;IACU,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAOtC;;OAEG;IACU,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAKpH;;;;;;;;;OASG;IACU,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;CAe5G"}
1
+ {"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../src/transaction.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAEzC;;;GAGG;AACH,qBAAa,WAAW;IAIhB,QAAQ,CAAC,IAAI,EAAE,UAAU;IAH7B,OAAO,CAAC,UAAU,CAAiB;gBAGtB,IAAI,EAAE,UAAU;IAG7B;;OAEG;IACH,IAAW,QAAQ,IAAI,OAAO,CAE7B;IAED,OAAO,CAAC,WAAW;IAMnB;;OAEG;IACU,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAOpC;;OAEG;IACU,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAOtC;;OAEG;IACU,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAKhH;;;;;;;;;OASG;IACU,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;CAe5G"}
package/dist/types.d.ts CHANGED
@@ -19,5 +19,8 @@ export type ColumnDescription = {
19
19
  name: string;
20
20
  typeOID: number;
21
21
  };
22
+ export type Branded<T, Brand> = T & {
23
+ __brand: Brand;
24
+ };
22
25
  export type ValueOF<T extends Record<string, unknown>> = T[keyof T];
23
26
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,CAAC,OAAO,SAAS,GAAG,EAAE,OAAO,SAAS,GAAG,EAAE,IAAI;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,CAAA;CACpD,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;CAC3B,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IAC/B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,EAAE,GAAG,EAAE,CAAC;CACf,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC3B,SAAS,EAAE,oBAAoB,CAAC;IAChC,IAAI,EAAE,GAAG,EAAE,CAAC;CACf,CAAA;AAGD,MAAM,MAAM,iBAAiB,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CAClB,CAAA;AAGD,MAAM,MAAM,OAAO,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,CAAC,OAAO,SAAS,GAAG,EAAE,OAAO,SAAS,GAAG,EAAE,IAAI;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,CAAA;CACpD,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;CAC3B,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IAC/B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,EAAE,GAAG,EAAE,CAAC;CACf,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC3B,SAAS,EAAE,oBAAoB,CAAC;IAChC,IAAI,EAAE,GAAG,EAAE,CAAC;CACf,CAAA;AAGD,MAAM,MAAM,iBAAiB,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,OAAO,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,GAAG;IAAC,OAAO,EAAE,KAAK,CAAA;CAAC,CAAA;AAEpD,MAAM,MAAM,OAAO,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA"}
@@ -1,6 +1,6 @@
1
1
  import { ColumnDescription } from "../types";
2
2
  declare const Parsers: Record<number, (value: string) => unknown>;
3
- export declare function parseRowValues(columns: ColumnDescription[], rows: (string | null)[][]): Record<string, any>;
3
+ export declare function parseRowValues(columns: ColumnDescription[], rows: (string | null)[][]): Record<string, any>[];
4
4
  export declare const setTypeParser: (typeOID: keyof typeof Parsers, parser: (value: string) => unknown) => void;
5
5
  export {};
6
6
  //# sourceMappingURL=value-parser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"value-parser.d.ts","sourceRoot":"","sources":["../../src/utils/value-parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AA+B5C,QAAA,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAkD9C,CAAA;AAMV,wBAAgB,cAAc,CAAC,OAAO,EAAE,iBAAiB,EAAE,EAAE,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CA8B3G;AAGD,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,OAAO,OAAO,EAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,SAE9F,CAAA"}
1
+ {"version":3,"file":"value-parser.d.ts","sourceRoot":"","sources":["../../src/utils/value-parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AA+B5C,QAAA,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAkD9C,CAAA;AAMV,wBAAgB,cAAc,CAAC,OAAO,EAAE,iBAAiB,EAAE,EAAE,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CA8B7G;AAGD,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,OAAO,OAAO,EAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,SAE9F,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@m2k-5f/pgtx",
3
- "version": "2.0.1",
3
+ "version": "2.1.0",
4
4
  "description": "Blazing-fast PostgreSQL driver with pipeline support.",
5
5
  "files": [
6
6
  "dist",
@@ -40,8 +40,9 @@
40
40
  }
41
41
  },
42
42
  "devDependencies": {
43
+ "@types/node": "^26.1.1",
44
+ "pg": "^8.22.0",
43
45
  "tsx": "^4.21.0",
44
- "typescript": "^5.0.0",
45
- "@types/node": "^26.1.1"
46
+ "typescript": "^5.0.0"
46
47
  }
47
48
  }