@m2k-5f/pgtx 2.3.3 → 2.4.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
@@ -7,8 +7,7 @@
7
7
 
8
8
  Pipeline execution, automatic prepared statements, typed SQL, transactions, and zero dependencies.
9
9
 
10
- **Up to 3.4× faster than Postgres.js and 15.6× faster than `pg` in concurrent pipeline workloads.**
11
-
10
+ **Up to 25% faster than `Postgres.js` and 15.6× faster than `pg` in concurrent pipeline workloads.**
12
11
 
13
12
  ---
14
13
 
@@ -21,6 +20,35 @@
21
20
  # bun add @m2k-5f/pgtx
22
21
  ```
23
22
 
23
+ ---
24
+
25
+ ## 🚀 Quick Start
26
+
27
+ ```typescript
28
+ import { sql, Pool } from "@m2k-5f/pgtx";
29
+
30
+ const pool = new Pool({
31
+ host: 'localhost',
32
+ user: 'postgres',
33
+ password: 'postgres',
34
+ database: 'myapp'
35
+ })
36
+
37
+ // Type-safe query
38
+ const [user] = await pool.query<User>`SELECT * FROM users WHERE id = ${1}`
39
+
40
+ // Bulk insert
41
+ await pool.query`
42
+ INSERT INTO users ${sql.insert([{ name: 'Alice', age: 25 }, { name: 'Bob', age: 30 }])}
43
+ `
44
+
45
+ // Transaction
46
+ await pool.begin(async (tx) => {
47
+ await tx.query`UPDATE accounts SET balance = balance - 100 WHERE id = ${1}`
48
+ await tx.query`UPDATE accounts SET balance = balance + 100 WHERE id = ${2}`
49
+ })
50
+ ```
51
+
24
52
  ---
25
53
 
26
54
  ## ✨ Features
@@ -37,26 +65,26 @@
37
65
 
38
66
  ---
39
67
 
40
- ## ⚡ Performance
68
+ ## ⚡ Performance & Benchmarks
41
69
 
42
- ### Stress test 3000 concurrent `SELECT` queries
70
+ ### 1. In-Engine Pipeline Blast (3000 Parallel Queries)
71
+ *Measured using `mitata` inside GitHub Actions cloud runners (Ubuntu, 2 vCPUs, 10 DB Connections).*
43
72
 
44
- **Environment**
73
+ | Driver | Avg Time per Iteration | Relative Speed | Memory (p75) |
74
+ | :--- | :---: | :---: | :---: |
75
+ | **Pgtx (Pipeline)** | **19.21 ms** | **Baseline (3.6×)** | **874.71 KB** |
76
+ | Postgres.js (Pipeline) | 70.50 ms | 3.6× Slower | 1.29 MB |
77
+ | node-postgres (no pipeline) | 327.07 ms | 17.0× Slower | 4.05 MB |
45
78
 
46
- * PostgreSQL connection pool: **10 connections**
47
- * **3000 concurrent parameterized `SELECT` queries**
48
- * Same SQL and identical workload for all drivers
79
+ > **Stability Note:** Pgtx provides an rock-solid flat latency graph (p99 is strictly bounded to `21.76 ms`), while maintaining a 2.5× smaller memory footprint compared to Postgres.js due to zero-allocation binary parsing.
49
80
 
50
- | Driver | Time |
51
- | ---------------------- | -----------: |
52
- | 🥇 **Pgtx (Pipeline)** | **24.18 ms** |
53
- | Postgres.js (Pipeline) | 83.36 ms |
54
- | node-postgres (`pg`) | 377.95 ms |
81
+ ### 2. Real-World HTTP Throughput (`wrk` Stress Test)
82
+ *HTTP server baseline using a `node:http` instance on GitHub Actions runner, handling 1,000 concurrent network connections (`wrk -t2 -c1000 -d10s`).*
55
83
 
56
- ### Relative performance
84
+ - **Pgtx:** **~14,500 RPS**
85
+ - **Postgres.js:** **~11,500 RPS**
57
86
 
58
- * 🚀 **3.4× faster than Postgres.js**
59
- * 🚀 **15.6× faster than node-postgres (`pg`)**
87
+ On high-concurrency bare metal servers, Pgtx effortlessly maintains a **+25% performance lead** over Postgres.js.
60
88
 
61
89
  Pgtx achieves high throughput by:
62
90
 
@@ -66,9 +94,9 @@
66
94
  * Automatic prepared statement caching
67
95
  * Row description caching
68
96
  * Zero-dependency implementation
97
+ * Binary protocol support
69
98
 
70
- > Benchmark source is available in the repository and can be reproduced locally.
71
-
99
+ > Benchmarks source is available in the repository and can be reproduced locally.
72
100
 
73
101
  ---
74
102
 
@@ -89,37 +117,10 @@
89
117
 
90
118
  ---
91
119
 
92
- ## 🚀 Quick Start
93
-
94
- ```typescript
95
- import { sql, Pool } from "@m2k-5f/pgtx";
96
-
97
- const pool = new Pool({
98
- host: 'localhost',
99
- user: 'postgres',
100
- password: 'postgres',
101
- database: 'myapp'
102
- })
103
-
104
- // Type-safe query
105
- const [user] = await pool.query<User>`SELECT * FROM users WHERE id = ${1}`
106
-
107
- // Bulk insert
108
- await pool.query`
109
- INSERT INTO users ${sql.insert([{ name: 'Alice', age: 25 }, { name: 'Bob', age: 30 }])}
110
- `
111
-
112
- // Transaction
113
- await pool.begin(async (tx) => {
114
- await tx.query`UPDATE accounts SET balance = balance - 100 WHERE id = ${1}`
115
- await tx.query`UPDATE accounts SET balance = balance + 100 WHERE id = ${2}`
116
- })
117
- ```
118
-
119
- ---
120
120
 
121
+ ## 📖 Features
121
122
 
122
- ## 🎯 Typed Error Handling
123
+ ### 🎯 Typed Error Handling
123
124
 
124
125
  Pgtx queries return `Future<T, PostgresError>` from [fluent-future](https://www.npmjs.com/package/fluent-future) instead of raw `Promise<T>`. This gives you:
125
126
 
@@ -136,10 +137,6 @@
136
137
 
137
138
  ---
138
139
 
139
-
140
- ## 📖 Features
141
-
142
-
143
140
  ### Pipeline by Default
144
141
 
145
142
  `Bind` and `.bind` from [fluent-future](https://www.npmjs.com/package/fluent-future) automatically multiplex independent queries over PostgreSQL pipeline protocol — no manual batching required:
@@ -69,10 +69,8 @@ export declare class Connection {
69
69
  private _isReconnecting;
70
70
  private _socket;
71
71
  private _writer;
72
- private _activeQuery;
73
72
  private _pipelinesQueue;
74
73
  private _described;
75
- private _rowMappers;
76
74
  private _describingPending;
77
75
  private _parsed;
78
76
  private _parsingPending;
@@ -80,7 +78,6 @@ export declare class Connection {
80
78
  private _stmtCounter;
81
79
  private _logLevel;
82
80
  private _nextStatement;
83
- private _getRowMapper;
84
81
  private _checkOpened;
85
82
  private constructor();
86
83
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,3 @@
1
- import { nextTick } from "process";
2
1
  import { ConnectionRequestWriter } from "./protocol/connection-request-writer";
3
2
  import { createAuthorizedSocket } from "./protocol/socket-authorization";
4
3
  import { ResponseTypes } from "./protocol/constants";
@@ -46,15 +45,6 @@ export class Connection {
46
45
  _nextStatement() {
47
46
  return `s-${this._stmtCounter++}`;
48
47
  }
49
- _getRowMapper(statementName, descriptions) {
50
- let mapper = this._rowMappers.get(statementName);
51
- if (mapper)
52
- return mapper;
53
- const fields = descriptions.map((desc, i) => `"${desc.name}": values[${i}]`).join(',\n ');
54
- mapper = new Function('values', `return ${fields}`)();
55
- this._rowMappers.set(statementName, mapper);
56
- return mapper;
57
- }
58
48
  _checkOpened() {
59
49
  if (!this.isOpened)
60
50
  throw ErrConnectionClosed;
@@ -63,10 +53,8 @@ export class Connection {
63
53
  this._isFlushing = false;
64
54
  this._isOpened = true;
65
55
  this._isReconnecting = false;
66
- this._activeQuery = null;
67
56
  this._pipelinesQueue = new Queue();
68
57
  this._described = new Map();
69
- this._rowMappers = new Map();
70
58
  this._describingPending = new Set();
71
59
  this._parsed = new Map();
72
60
  this._parsingPending = new Map();
@@ -288,7 +276,7 @@ export class Connection {
288
276
  if (!this._isFlushing) {
289
277
  this._isFlushing = true;
290
278
  this._pipelinesQueue.push(new Queue());
291
- nextTick(() => {
279
+ setImmediate(() => {
292
280
  this._flush();
293
281
  });
294
282
  }
@@ -384,6 +372,7 @@ export class Connection {
384
372
  this._describingPending.delete(query.statementName);
385
373
  this._described.set(query.statementName, []);
386
374
  query.state = QueryState.Executing;
375
+ query.columns = [];
387
376
  }
388
377
  break;
389
378
  case ResponseTypes.RowDescription:
@@ -398,11 +387,7 @@ export class Connection {
398
387
  break;
399
388
  case ResponseTypes.DataRow:
400
389
  {
401
- let query = this._activeQuery;
402
- if (!query) {
403
- query = this._getCurrentQuery();
404
- this._activeQuery = query;
405
- }
390
+ let query = this._getCurrentQuery();
406
391
  if (!query.columns) {
407
392
  query.columns = this._described.get(query.statementName);
408
393
  }
@@ -416,17 +401,13 @@ export class Connection {
416
401
  this.close();
417
402
  break;
418
403
  }
419
- const query = this._activeQuery || this._getCurrentQuery();
404
+ const query = this._getCurrentQuery();
420
405
  if (!query) {
421
406
  this.close();
422
407
  break;
423
408
  }
424
- if (!query.columns) {
425
- query.columns = this._described.get(query.statementName);
426
- }
427
409
  query.state = QueryState.Completed;
428
410
  this._pipelinesQueue.get().next();
429
- this._activeQuery = null;
430
411
  query.resolve();
431
412
  }
432
413
  break;
@@ -1 +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,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,KAAK,CAAU;gBAGZ,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;IAKrB,IAAI,CAAC,KAAK,EAAE,CAAC;IAIb,MAAM,CAAC,KAAK,EAAE,KAAK;IAKnB,OAAO;CAGV"}
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,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,KAAK,CAAU;gBAGZ,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;IASxC,QAAQ,CAAC,KAAK,EAAE,KAAK;IAKrB,IAAI,CAAC,KAAK,EAAE,CAAC;IAKb,MAAM,CAAC,KAAK,EAAE,KAAK;IAKnB,OAAO;CAGV"}
@@ -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;IASJ,GAAG;IAKH,KAAK;IAYL,IAAI,CAAC,IAAI,EAAE,CAAC;IAIZ,IAAI,IAAI,WAEP;IAGD,IAAI,OAAO,YAEV;IAGD,IAAI,MAAM,YAA+C;CAC5D"}
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;IAcJ,GAAG;IAKH,KAAK;IAQL,IAAI,CAAC,IAAI,EAAE,CAAC;IAIZ,IAAI,IAAI,WAEP;IAGD,IAAI,OAAO,YAEV;IAGD,IAAI,MAAM,YAA+C;CAC5D"}
package/dist/queue.js CHANGED
@@ -5,8 +5,12 @@ export class Queue {
5
5
  }
6
6
  next() {
7
7
  this._pointer++;
8
+ if (this._pointer > 10000) {
9
+ this._queue.splice(0, this._pointer);
10
+ this._pointer = 0;
11
+ }
8
12
  if (this._pointer >= this._queue.length) {
9
- this._queue = [];
13
+ this._queue.length = 0;
10
14
  this._pointer = 0;
11
15
  }
12
16
  }
@@ -15,11 +19,7 @@ export class Queue {
15
19
  }
16
20
  shift() {
17
21
  const item = this._queue[this._pointer];
18
- this._pointer++;
19
- if (this._pointer >= this._queue.length) {
20
- this._queue.length = 0;
21
- this._pointer = 0;
22
- }
22
+ this.next();
23
23
  return item;
24
24
  }
25
25
  push(item) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@m2k-5f/pgtx",
3
- "version": "2.3.3",
4
- "type": "module",
3
+ "version": "2.4.0",
4
+ "type": "module",
5
5
  "description": "Blazing-fast PostgreSQL driver with pipeline support.",
6
6
  "files": [
7
7
  "dist",
@@ -1,5 +0,0 @@
1
- import { CompiledSqlQuery } from "./../utils";
2
- export declare abstract class Clause {
3
- abstract map(argCounter: number): CompiledSqlQuery;
4
- }
5
- //# sourceMappingURL=base.clause.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"base.clause.d.ts","sourceRoot":"","sources":["../../src/clauses/base.clause.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,8BAAsB,MAAM;IACxB,QAAQ,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB;CACrD"}
@@ -1,6 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Clause = void 0;
4
- class Clause {
5
- }
6
- exports.Clause = Clause;
@@ -1,9 +0,0 @@
1
- import { Clause } from "./base.clause";
2
- import { CompiledSqlQuery } from "../utils";
3
- export declare class StaticClause<T extends string> extends Clause {
4
- readonly value: T;
5
- constructor(value: T);
6
- map(argCounter: number): CompiledSqlQuery;
7
- }
8
- export declare function staticClause<T extends string>(value: T): StaticClause<T>;
9
- //# sourceMappingURL=static.clause.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"static.clause.d.ts","sourceRoot":"","sources":["../../src/clauses/static.clause.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAC,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C,qBAAa,YAAY,CAAC,CAAC,SAAS,MAAM,CAAE,SAAQ,MAAM;IAElD,QAAQ,CAAC,KAAK,EAAE,CAAC;gBAAR,KAAK,EAAE,CAAC;IAKZ,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB;CAMrD;AAGD,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAExE"}
@@ -1,21 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.StaticClause = void 0;
4
- exports.staticClause = staticClause;
5
- const base_clause_1 = require("./base.clause");
6
- class StaticClause extends base_clause_1.Clause {
7
- constructor(value) {
8
- super();
9
- this.value = value;
10
- }
11
- map(argCounter) {
12
- if (this.value === undefined) {
13
- throw new TypeError(`Query parameter undefined at position ${argCounter}`);
14
- }
15
- return { text: this.value, args: [], argCounter };
16
- }
17
- }
18
- exports.StaticClause = StaticClause;
19
- function staticClause(value) {
20
- return new StaticClause(value);
21
- }
@@ -1,9 +0,0 @@
1
- import { Clause } from "./base.clause";
2
- import { CompiledSqlQuery } from "../utils";
3
- export declare class InsertClause extends Clause {
4
- readonly inserts: Record<string, any>[];
5
- constructor(inserts: Record<string, any>[]);
6
- map(argCounter: number): CompiledSqlQuery;
7
- }
8
- export declare function valueClause<T extends Record<string, any>>(...objects: [T, ...NoInfer<T>[]]): InsertClause;
9
- //# sourceMappingURL=values.clause.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"values.clause.d.ts","sourceRoot":"","sources":["../../src/clauses/values.clause.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C,qBAAa,YAAa,SAAQ,MAAM;IAEhC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE;gBAA9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE;IAGlC,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB;CAgBrD;AAED,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,gBAE1F"}
@@ -1,27 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.InsertClause = void 0;
4
- exports.valueClause = valueClause;
5
- const base_clause_1 = require("./base.clause");
6
- class InsertClause extends base_clause_1.Clause {
7
- constructor(inserts) {
8
- super();
9
- this.inserts = inserts;
10
- }
11
- map(argCounter) {
12
- const columns = Object.keys(this.inserts[0]);
13
- let text = `(${columns.join(', ')}) VALUES `;
14
- let args = [];
15
- text += `${this.inserts.map(values => {
16
- return `(${Object.values(values).map(value => {
17
- args.push(value);
18
- return `$${argCounter++}`;
19
- }).join(", ")})`;
20
- }).join(", ")}`;
21
- return { text, args, argCounter };
22
- }
23
- }
24
- exports.InsertClause = InsertClause;
25
- function valueClause(...objects) {
26
- return new InsertClause(objects);
27
- }
@@ -1,17 +0,0 @@
1
- import { PoolClient, Pool, QueryResultRow } from "pg";
2
- import { PreparedStatement } from "./types";
3
- export declare class ConnPraparedStatement<Result extends QueryResultRow, Args extends any[]> {
4
- private readonly conn;
5
- readonly text: string;
6
- readonly name: string;
7
- constructor(conn: PoolClient, text: string, name: string);
8
- execute(...params: Args): Promise<Result[]>;
9
- }
10
- export declare class PoolPreparedStatement<Result extends QueryResultRow, Args extends any[]> implements PreparedStatement<Result, Args> {
11
- private readonly pool;
12
- readonly text: string;
13
- readonly name: string;
14
- constructor(pool: Pool, text: string, name: string);
15
- execute(...params: Args): Promise<Result[]>;
16
- }
17
- //# sourceMappingURL=prepared.statement.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"prepared.statement.d.ts","sourceRoot":"","sources":["../src/prepared.statement.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,IAAI,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAE5C,qBAAa,qBAAqB,CAAC,MAAM,SAAS,cAAc,EAAE,IAAI,SAAS,GAAG,EAAE;IAE5E,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM;gBAFJ,IAAI,EAAE,UAAU,EACxB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM;IAGnB,OAAO,CAAC,GAAG,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;CAQpD;AAED,qBAAa,qBAAqB,CAAC,MAAM,SAAS,cAAc,EAAE,IAAI,SAAS,GAAG,EAAE,CAAE,YAAW,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAC;IAExH,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM;gBAFJ,IAAI,EAAE,IAAI,EAClB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM;IAGnB,OAAO,CAAC,GAAG,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;CAepD"}
@@ -1,41 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PoolPreparedStatement = exports.ConnPraparedStatement = void 0;
4
- class ConnPraparedStatement {
5
- constructor(conn, text, name) {
6
- this.conn = conn;
7
- this.text = text;
8
- this.name = name;
9
- }
10
- async execute(...params) {
11
- return this.conn.query({
12
- text: this.text,
13
- name: this.name,
14
- values: params
15
- })
16
- .then(result => result.rows);
17
- }
18
- }
19
- exports.ConnPraparedStatement = ConnPraparedStatement;
20
- class PoolPreparedStatement {
21
- constructor(pool, text, name) {
22
- this.pool = pool;
23
- this.text = text;
24
- this.name = name;
25
- }
26
- async execute(...params) {
27
- const conn = await this.pool.connect();
28
- try {
29
- return await conn.query({
30
- text: this.text,
31
- name: this.name,
32
- values: params
33
- })
34
- .then(result => result.rows);
35
- }
36
- finally {
37
- conn.release();
38
- }
39
- }
40
- }
41
- exports.PoolPreparedStatement = PoolPreparedStatement;
@@ -1,6 +0,0 @@
1
- import { CompiledSqlQuery, CompileSQLParams } from "./types";
2
- export declare class QueryCacher {
3
- private readonly cache;
4
- cachedBuild(params: CompileSQLParams): CompiledSqlQuery;
5
- }
6
- //# sourceMappingURL=query.cacher.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"query.cacher.d.ts","sourceRoot":"","sources":["../src/query.cacher.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAI7D,qBAAa,WAAW;IACpB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAyE;IAExF,WAAW,CAAC,MAAM,EAAE,gBAAgB,GAAG,gBAAgB;CAgBjE"}
@@ -1,23 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.QueryCacher = void 0;
4
- const abstract_clause_1 = require("./clauses/abstract.clause");
5
- const utils_1 = require("./utils");
6
- class QueryCacher {
7
- constructor() {
8
- this.cache = new WeakMap();
9
- }
10
- cachedBuild(params) {
11
- const cached = this.cache.get(params.templates);
12
- if (cached?.isStatic) {
13
- return { text: cached.text, args: params.args, counter: params.counter };
14
- }
15
- const result = (0, utils_1.compileSqlTemplate)(params);
16
- if (!cached) {
17
- const isStatic = !params.args.some(value => value instanceof abstract_clause_1.Clause);
18
- this.cache.set(params.templates, { text: result.text, isStatic });
19
- }
20
- return result;
21
- }
22
- }
23
- exports.QueryCacher = QueryCacher;
@@ -1,6 +0,0 @@
1
- import { ColumnDescription } from "../types";
2
- declare const Parsers: Record<number, (value: string) => unknown>;
3
- export declare function parseRowValues(columns: ColumnDescription[], rows: (string | null)[][]): Record<string, any>[];
4
- export declare const setTypeParser: (typeOID: keyof typeof Parsers, parser: (value: string) => unknown) => void;
5
- export {};
6
- //# sourceMappingURL=value-parser.d.ts.map
@@ -1 +0,0 @@
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"}
@@ -1,96 +0,0 @@
1
- const parsePostgresArray = (v) => {
2
- if (v === '{}')
3
- return [];
4
- const str = v.substring(1, v.length - 1);
5
- const matches = str.match(/"(?:\\.|[^"\\])*"|[^,]+/g);
6
- if (!matches)
7
- return [];
8
- return matches.map(el => {
9
- const trimmed = el.trim();
10
- if (trimmed === 'NULL')
11
- return null;
12
- if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
13
- return trimmed
14
- .substring(1, trimmed.length - 1)
15
- .replace(/\\"/g, '"')
16
- .replace(/\\\\/g, '\\');
17
- }
18
- return trimmed;
19
- });
20
- };
21
- const parsePoint = (v) => {
22
- const [x, y] = v.replace(/[()]/g, '').split(',');
23
- return { x: parseFloat(x), y: parseFloat(y) };
24
- };
25
- const Parsers = {
26
- 16: (v) => v === 't', // BOOL
27
- 21: (v) => parseInt(v, 10), // INT2 (SMALLINT)
28
- 23: (v) => parseInt(v, 10), // INT4 (INTEGER)
29
- 26: (v) => parseInt(v, 10), // OID
30
- 20: (v) => parseInt(v), // INT8 (BIGINT)
31
- 700: (v) => parseFloat(v), // FLOAT4 (REAL)
32
- 701: (v) => parseFloat(v), // FLOAT8 (DOUBLE PRECISION)
33
- 1700: (v) => parseFloat(v), // NUMERIC (DECIMAL)
34
- 18: (v) => v, // CHAR
35
- 19: (v) => v, // NAME
36
- 25: (v) => v, // TEXT
37
- 1042: (v) => v, // BPCHAR (CHAR(N))
38
- 1043: (v) => v, // VARCHAR
39
- 2950: (v) => v, // UUID
40
- 17: (v) => Buffer.from(v.substring(2), 'hex'), // BYTEA
41
- 1082: (v) => new Date(v), // DATE
42
- 1114: (v) => new Date(v + 'Z'), // TIMESTAMP
43
- 1184: (v) => new Date(v), // TIMESTAMPTZ
44
- 1083: (v) => v, // TIME
45
- 1266: (v) => v, // TIMETZ
46
- 1186: (v) => v, // INTERVAL
47
- 114: (v) => JSON.parse(v), // JSON
48
- 3802: (v) => JSON.parse(v), // JSONB
49
- 869: (v) => v, // INET (IP-адреса)
50
- 650: (v) => v, // CIDR
51
- 829: (v) => v, // MACADDR
52
- 600: parsePoint, // POINT
53
- 603: (v) => v.replace(/[()]/g, '') // POLYGON
54
- .split(',')
55
- .map(parsePoint),
56
- 1000: (v) => parsePostgresArray(v).map(el => el === 't'),
57
- 1005: (v) => parsePostgresArray(v).map(el => parseInt(el, 10)),
58
- 1007: (v) => parsePostgresArray(v).map(el => parseInt(el, 10)), // int4[]
59
- 1016: (v) => parsePostgresArray(v).map(el => BigInt(el)), // int8[]
60
- 1021: (v) => parsePostgresArray(v).map(el => parseFloat(el)),
61
- 1022: (v) => parsePostgresArray(v).map(el => parseFloat(el)), // float8[]
62
- 1231: (v) => parsePostgresArray(v).map(el => parseFloat(el)), // numeric[]
63
- 1009: (v) => parsePostgresArray(v), // text[]
64
- 1015: (v) => parsePostgresArray(v), // varchar[]
65
- 199: (v) => parsePostgresArray(v).map(el => JSON.parse(el)), // json[]
66
- 3807: (v) => parsePostgresArray(v).map(el => JSON.parse(el)), // jsonb[]
67
- };
68
- const defaultParser = (v) => v;
69
- export function parseRowValues(columns, rows) {
70
- const result = [];
71
- const columnsLength = columns.length;
72
- const columnParsers = columns.map(col => Parsers[col.typeOID] || defaultParser);
73
- for (let r = 0; r < rows.length; r++) {
74
- const rawRow = rows[r];
75
- const rowObject = {};
76
- for (let c = 0; c < columnsLength; c++) {
77
- const val = rawRow[c];
78
- const colName = columns[c].name;
79
- if (val === null) {
80
- rowObject[colName] = null;
81
- continue;
82
- }
83
- try {
84
- rowObject[colName] = columnParsers[c](val);
85
- }
86
- catch {
87
- rowObject[colName] = val;
88
- }
89
- }
90
- result.push(rowObject);
91
- }
92
- return result;
93
- }
94
- export const setTypeParser = (typeOID, parser) => {
95
- Parsers[typeOID] = parser;
96
- };
package/dist/utils.d.ts DELETED
@@ -1,3 +0,0 @@
1
- import { CompiledSqlQuery, CompileSQLParams } from "./types";
2
- export declare function compileSqlTemplate(params: Readonly<CompileSQLParams>): CompiledSqlQuery;
3
- //# sourceMappingURL=utils.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AACA,OAAO,EAAwB,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAIlF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,QAAQ,CAAC,gBAAgB,CAAC,GAAG,gBAAgB,CAgCvF"}
package/dist/utils.js DELETED
@@ -1,30 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.compileSqlTemplate = compileSqlTemplate;
4
- const abstract_clause_1 = require("./clauses/abstract.clause");
5
- function compileSqlTemplate(params) {
6
- const templateLength = params.templates.length;
7
- const query = {
8
- text: [],
9
- args: [],
10
- counter: params.counter
11
- };
12
- params.templates.forEach((template, index) => {
13
- query.text.push(template);
14
- if (index === templateLength - 1)
15
- return;
16
- const value = params.args[index];
17
- if (value instanceof abstract_clause_1.Clause) {
18
- value.mapIntoQuery(query);
19
- }
20
- else {
21
- if (value === undefined) {
22
- throw new TypeError(`Query parameter at position ${query.counter} is undefined.
23
- Use null if you want NULL in SQL, or ensure the value is defined.`);
24
- }
25
- query.args.push(value);
26
- query.text.push(`$${query.counter++}`);
27
- }
28
- });
29
- return { ...query, text: query.text.join('') };
30
- }