@m2k-5f/pgtx 2.6.2 → 2.7.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 +21 -12
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +10 -3
- package/dist/query.d.ts +1 -4
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +1 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,7 +25,6 @@ const pool = new Pool({
|
|
|
25
25
|
|
|
26
26
|
const [user] = await pool.query<User>`SELECT * FROM users WHERE id = ${1}`
|
|
27
27
|
|
|
28
|
-
// returns rows → query, doesn't → execute
|
|
29
28
|
await pool.execute`
|
|
30
29
|
INSERT INTO users ${sql.insert([{ name: 'Alice', age: 25 }, { name: 'Bob', age: 30 }])}
|
|
31
30
|
`
|
|
@@ -38,28 +37,30 @@ await pool.begin(async tx => {
|
|
|
38
37
|
|
|
39
38
|
That's most of what you need to know to use it. The rest of this document is for when you want to know *why* it's fast, or you need one of the sharper tools.
|
|
40
39
|
|
|
41
|
-
##
|
|
40
|
+
## Benchmarks
|
|
42
41
|
|
|
43
42
|
Benchmarks run on GitHub Actions (Ubuntu, 2 vCPUs), reproducible, sources in this repo. Take CI numbers with the usual grain of salt — noisy neighbors and all that — but the gap is wide enough that it holds.
|
|
44
43
|
|
|
45
|
-
**
|
|
44
|
+
**8192 concurrent parameterized SELECTs, measured with mitata:**
|
|
46
45
|
|
|
47
46
|
| Driver | Avg time | Relative | Memory (p75) |
|
|
48
47
|
|---|---:|---:|---:|
|
|
49
|
-
| Pgtx | **
|
|
50
|
-
| Postgres.js |
|
|
51
|
-
| node-postgres (`pg`) |
|
|
48
|
+
| Pgtx | **69.23 ms** | 1.00× | ≈10.00 MB |
|
|
49
|
+
| Postgres.js | 201.69 ms | 2.91× slower | ≈12.25 MB |
|
|
50
|
+
| node-postgres (`pg`) | 738.97 ms | 10.67× slower | ≈12.74 MB |
|
|
52
51
|
|
|
53
52
|
**Real HTTP throughput, `node:http` serving a PG-backed route, `wrk`:**
|
|
54
53
|
|
|
55
54
|
| Concurrency | Pgtx | Postgres.js | Speedup |
|
|
56
55
|
|---:|---:|---:|---:|
|
|
57
|
-
| 50 |
|
|
58
|
-
| 200 | **
|
|
59
|
-
| 1000 | **
|
|
60
|
-
| 10000 | **
|
|
56
|
+
| 50 | **13,575 req/s** | 5,507 req/s | 2.47× |
|
|
57
|
+
| 200 | **18,431 req/s** | 6,640 req/s | 2.78× |
|
|
58
|
+
| 1000 | **16,992 req/s** | 8,099 req/s | 2.10× |
|
|
59
|
+
| 10000 | **17,700 req/s** | 10,414 req/s | 1.70× |
|
|
61
60
|
|
|
62
|
-
|
|
61
|
+
(`/users` route — `SELECT` returning 5 rows, single connection pool, 2 wrk threads, 10s runs)
|
|
62
|
+
|
|
63
|
+
The gap is widest at low-to-mid concurrency, where per-request overhead dominates; it narrows past ~10k concurrent connections as both drivers start hitting OS/socket limits rather than protocol overhead.
|
|
63
64
|
|
|
64
65
|
**How:** everything you fire concurrently against the same connection gets folded into one pipelined write — Parse/Bind/Execute for every query in the batch goes out in a single `socket.write()`, and results get demuxed as they come back, in order, without buffering rows you haven't asked for yet. Prepared statements are cached and deduplicated automatically, row descriptions are cached alongside them, and the binary protocol skips text (de)serialization where it can. None of this requires you to change how you write queries.
|
|
65
66
|
|
|
@@ -151,7 +152,11 @@ await pool.execute`INSERT INTO users ${sql.insert(users)}`
|
|
|
151
152
|
|
|
152
153
|
// dynamic SET clause
|
|
153
154
|
await pool.execute`UPDATE users SET ${sql.update({ status: 'active', last_login: new Date() })} WHERE id = ${userId}`
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Rule of thumb: `execute` when you don't need rows back, `query` when you do — same rule as the raw driver, `sql.*` doesn't change it.
|
|
154
158
|
|
|
159
|
+
```typescript
|
|
155
160
|
// composable fragments
|
|
156
161
|
const filter = sql.fragment`status = ${'active'} AND age > ${21}`
|
|
157
162
|
await pool.query`SELECT * FROM users WHERE ${filter}`
|
|
@@ -275,4 +280,8 @@ Not an ORM. No migrations, no model layer, no query builder that hides SQL from
|
|
|
275
280
|
|
|
276
281
|
MIT © [M2K-5F](https://github.com/M2K-5F)
|
|
277
282
|
|
|
278
|
-
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
**Made with ❤️ and a bit of insanity**
|
|
286
|
+
|
|
287
|
+
*Manufactured under license by the **Blazing Corporation**. Side effects may include throughput.*
|
package/dist/connection.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAMA,OAAO,EAAiC,uBAAuB,EAAmC,GAAG,EAAiB,MAAM,SAAS,CAAA;AACrI,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAK3C,OAAO,EAAS,MAAM,EAAM,MAAM,eAAe,CAAA;AACjD,OAAO,EAAkD,aAAa,EAAE,MAAM,SAAS,CAAA;AAavF;;;;;;;;;GASG;AACH,qBAAa,UAAU;IACnB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IAEzC,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,QAAQ,CAAsD;IACtE,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,aAAa,CAA2C;IAChE,OAAO,CAAC,aAAa,CAAgC;IAErD,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,WAAW,CAA4B;IAE/C,OAAO,CAAC,OAAO,CAAkC;IACjD,OAAO,CAAC,QAAQ,CAAsC;IAEtD,OAAO,CAAC,mBAAmB,CAAyD;IACpF,OAAO,CAAC,YAAY,CAAI;IAGxB,OAAO,CAAC,cAAc;IAKtB,OAAO;IAYP;;;OAGG;IACH,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,uBAAuB;IAe1C,OAAO,CAAC,cAAc;IActB,OAAO,CAAC,KAAK;IASb;;;;;;OAMG;IACH,KAAK,CAAC,CAAC,SAAS,GAAG,EAAE,SAAS,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE;IAStE,OAAO,CAAC,aAAa;IA4CrB;;;;;OAKG;IACH,OAAO,CAAC,SAAS,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE;IASzD,OAAO,CAAC,eAAe;IAsCvB;;;;;;;OAOG;IACH,KAAK,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,WAAW,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC;IAoB7D,2EAA2E;IAC3E,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,MAAW;IAOhD,sFAAsF;IACtF,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI;IAe/D,4EAA4E;IAC5E,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,CAAC,IAAI,EAAE,aAAa,CAAC;IAoB/F;;;;;;OAMG;IACH,MAAM,CAAC,CAAC,SAAS,GAAG,EAAE,SAAS,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE;IAyBvE,OAAO,CAAC,cAAc;IA2CtB,OAAO,CAAC,UAAU;IAelB,OAAO,CAAC,iBAAiB;IA2BzB,OAAO,CAAC,qBAAqB;IAW7B,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,aAAa;
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAMA,OAAO,EAAiC,uBAAuB,EAAmC,GAAG,EAAiB,MAAM,SAAS,CAAA;AACrI,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAK3C,OAAO,EAAS,MAAM,EAAM,MAAM,eAAe,CAAA;AACjD,OAAO,EAAkD,aAAa,EAAE,MAAM,SAAS,CAAA;AAavF;;;;;;;;;GASG;AACH,qBAAa,UAAU;IACnB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IAEzC,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,QAAQ,CAAsD;IACtE,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,aAAa,CAA2C;IAChE,OAAO,CAAC,aAAa,CAAgC;IAErD,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,WAAW,CAA4B;IAE/C,OAAO,CAAC,OAAO,CAAkC;IACjD,OAAO,CAAC,QAAQ,CAAsC;IAEtD,OAAO,CAAC,mBAAmB,CAAyD;IACpF,OAAO,CAAC,YAAY,CAAI;IAGxB,OAAO,CAAC,cAAc;IAKtB,OAAO;IAYP;;;OAGG;IACH,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,uBAAuB;IAe1C,OAAO,CAAC,cAAc;IActB,OAAO,CAAC,KAAK;IASb;;;;;;OAMG;IACH,KAAK,CAAC,CAAC,SAAS,GAAG,EAAE,SAAS,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE;IAStE,OAAO,CAAC,aAAa;IA4CrB;;;;;OAKG;IACH,OAAO,CAAC,SAAS,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE;IASzD,OAAO,CAAC,eAAe;IAsCvB;;;;;;;OAOG;IACH,KAAK,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,WAAW,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC;IAoB7D,2EAA2E;IAC3E,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,MAAW;IAOhD,sFAAsF;IACtF,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI;IAe/D,4EAA4E;IAC5E,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,CAAC,IAAI,EAAE,aAAa,CAAC;IAoB/F;;;;;;OAMG;IACH,MAAM,CAAC,CAAC,SAAS,GAAG,EAAE,SAAS,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE;IAyBvE,OAAO,CAAC,cAAc;IA2CtB,OAAO,CAAC,UAAU;IAelB,OAAO,CAAC,iBAAiB;IA2BzB,OAAO,CAAC,qBAAqB;IAW7B,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,aAAa;IAoIrB,OAAO,KAAK,WAAW,GAEtB;IAGD,kDAAkD;IAClD,IAAI,QAAQ,YAEX;IAGD,mDAAmD;IACnD,IAAI,QAAQ,YAEX;IAGD;;OAEG;IACH,KAAK;CAwBR"}
|
package/dist/connection.js
CHANGED
|
@@ -133,17 +133,17 @@ export class Connection {
|
|
|
133
133
|
}
|
|
134
134
|
if (this._parsed.has(text)) {
|
|
135
135
|
const meta = this._parsed.get(text);
|
|
136
|
-
const query = new ExecuteQuery(meta.statement, text, args,
|
|
136
|
+
const query = new ExecuteQuery(meta.statement, text, args, this.config.queryTimeout);
|
|
137
137
|
this._registerBatch().registerQuery(query);
|
|
138
138
|
return query.future;
|
|
139
139
|
}
|
|
140
140
|
if (this._parsing.has(text)) {
|
|
141
141
|
const statement = this._parsing.get(text);
|
|
142
|
-
const query = new ExecuteQuery(statement, text, args,
|
|
142
|
+
const query = new ExecuteQuery(statement, text, args, this.config.queryTimeout);
|
|
143
143
|
this._registerBatch().registerQuery(query);
|
|
144
144
|
return query.future;
|
|
145
145
|
}
|
|
146
|
-
const query = new ExecuteQuery(this._nextStatement(), text, args,
|
|
146
|
+
const query = new ExecuteQuery(this._nextStatement(), text, args, this.config.queryTimeout);
|
|
147
147
|
this._parsing.set(text, query.statement);
|
|
148
148
|
this._registerBatch().registerParse(query);
|
|
149
149
|
this._registerBatch().registerQuery(query);
|
|
@@ -306,6 +306,9 @@ export class Connection {
|
|
|
306
306
|
{
|
|
307
307
|
reader.readBindComplete();
|
|
308
308
|
const query = this._getCurrentQuery();
|
|
309
|
+
if (query instanceof ExecuteQuery) {
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
309
312
|
if (!query.columns) {
|
|
310
313
|
query.columns = this._parsed.get(query.text).columns;
|
|
311
314
|
}
|
|
@@ -344,6 +347,10 @@ export class Connection {
|
|
|
344
347
|
case ResponseTypes.DataRow:
|
|
345
348
|
{
|
|
346
349
|
let query = this._getCurrentQuery();
|
|
350
|
+
if (query instanceof ExecuteQuery) {
|
|
351
|
+
reader.skip(length - 4);
|
|
352
|
+
break;
|
|
353
|
+
}
|
|
347
354
|
query.push(reader.readDataRow(query.columns, this.config.int8toBigint));
|
|
348
355
|
}
|
|
349
356
|
break;
|
package/dist/query.d.ts
CHANGED
|
@@ -9,7 +9,6 @@ export declare abstract class Query {
|
|
|
9
9
|
constructor(statement: StatementName, text: QueryText, args: (string | null)[], timeout: number);
|
|
10
10
|
abstract reject(cause: PostgresError): void;
|
|
11
11
|
abstract resolve(...args: any[]): void;
|
|
12
|
-
abstract push(...args: any[]): void;
|
|
13
12
|
}
|
|
14
13
|
export declare class CollectQuery<T extends Row> extends Query {
|
|
15
14
|
columns: ColumnDescription[] | null;
|
|
@@ -23,14 +22,12 @@ export declare class CollectQuery<T extends Row> extends Query {
|
|
|
23
22
|
resolve(): void;
|
|
24
23
|
}
|
|
25
24
|
export declare class ExecuteQuery extends Query {
|
|
26
|
-
columns: ColumnDescription[] | null;
|
|
27
25
|
future: Future<void, PostgresError>;
|
|
28
26
|
private _resolve;
|
|
29
27
|
private _reject;
|
|
30
|
-
constructor(statement: StatementName, text: QueryText, args: (string | null)[],
|
|
28
|
+
constructor(statement: StatementName, text: QueryText, args: (string | null)[], timeout: number);
|
|
31
29
|
reject(cause: PostgresError): void;
|
|
32
30
|
resolve(): void;
|
|
33
|
-
push(): void;
|
|
34
31
|
}
|
|
35
32
|
export declare class StreamQuery<T> extends Query {
|
|
36
33
|
controller: ReadableStreamDefaultController<T>;
|
package/dist/query.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAa,SAAS,EAAE,GAAG,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACtF,OAAO,EAAmB,aAAa,EAAE,MAAM,SAAS,CAAC;AAGzD,8BAAsB,KAAK;IAIZ,SAAS,EAAE,aAAa;IACxB,IAAI,EAAE,SAAS;IACf,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE;IALlC,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,OAAO,CAAA;gBAGtB,SAAS,EAAE,aAAa,EACxB,IAAI,EAAE,SAAS,EACf,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,EAC9B,OAAO,EAAE,MAAM;IAQnB,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAE3C,QAAQ,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAa,SAAS,EAAE,GAAG,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACtF,OAAO,EAAmB,aAAa,EAAE,MAAM,SAAS,CAAC;AAGzD,8BAAsB,KAAK;IAIZ,SAAS,EAAE,aAAa;IACxB,IAAI,EAAE,SAAS;IACf,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE;IALlC,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,OAAO,CAAA;gBAGtB,SAAS,EAAE,aAAa,EACxB,IAAI,EAAE,SAAS,EACf,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,EAC9B,OAAO,EAAE,MAAM;IAQnB,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAE3C,QAAQ,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI;CACzC;AAGD,qBAAa,YAAY,CAAC,CAAC,SAAS,GAAG,CAAE,SAAQ,KAAK;IAUvC,OAAO,EAAE,iBAAiB,EAAE,GAAG,IAAI;IATvC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,EAAE,aAAa,CAAC,CAAA;IACzC,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,OAAO,CAAiC;IAChD,OAAO,CAAC,KAAK,CAAU;gBAGnB,SAAS,EAAE,aAAa,EACxB,IAAI,EAAE,SAAS,EACf,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,EAChB,OAAO,EAAE,iBAAiB,EAAE,GAAG,IAAI,EAC1C,OAAO,EAAE,MAAM;IAWnB,IAAI,CAAC,KAAK,EAAE,CAAC;IAKb,MAAM,CAAC,KAAK,EAAE,aAAa;IAM3B,OAAO;CAIV;AAGD,qBAAa,YAAa,SAAQ,KAAK;IAC5B,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,aAAa,CAAC,CAAA;IAC1C,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,OAAO,CAAiC;gBAE5C,SAAS,EAAE,aAAa,EACxB,IAAI,EAAE,SAAS,EACf,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,EACvB,OAAO,EAAE,MAAM;IASnB,MAAM,CAAC,KAAK,EAAE,aAAa;IAM3B,OAAO;CAIV;AAGD,qBAAa,WAAW,CAAC,CAAC,CAAE,SAAQ,KAAK;IAK1B,UAAU,EAAE,+BAA+B,CAAC,CAAC,CAAC;IAC9C,OAAO,EAAE,iBAAiB,EAAE,GAAG,IAAI;gBAJ1C,SAAS,EAAE,aAAa,EACxB,IAAI,EAAE,SAAS,EACf,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,EAChB,UAAU,EAAE,+BAA+B,CAAC,CAAC,CAAC,EAC9C,OAAO,EAAE,iBAAiB,EAAE,GAAG,IAAI,EAC1C,OAAO,EAAE,MAAM;IAMnB,IAAI,CAAC,KAAK,EAAE,CAAC;IAOb,MAAM,CAAC,KAAK,EAAE,aAAa;IAM3B,OAAO;CAMV;AAED,MAAM,MAAM,aAAa,GAAG,YAAY,CAAC,GAAG,CAAC,GAAG,WAAW,CAAC,GAAG,CAAC,GAAG,YAAY,CAAA"}
|
package/dist/query.js
CHANGED
|
@@ -33,9 +33,8 @@ export class CollectQuery extends Query {
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
export class ExecuteQuery extends Query {
|
|
36
|
-
constructor(statement, text, args,
|
|
36
|
+
constructor(statement, text, args, timeout) {
|
|
37
37
|
super(statement, text, args, timeout);
|
|
38
|
-
this.columns = columns;
|
|
39
38
|
const { future, reject, resolve } = Future.withResolvers();
|
|
40
39
|
this.future = future;
|
|
41
40
|
this._reject = reject;
|
|
@@ -49,7 +48,6 @@ export class ExecuteQuery extends Query {
|
|
|
49
48
|
clearTimeout(this._timer);
|
|
50
49
|
this._resolve();
|
|
51
50
|
}
|
|
52
|
-
push() { }
|
|
53
51
|
}
|
|
54
52
|
export class StreamQuery extends Query {
|
|
55
53
|
constructor(statement, text, args, controller, columns, timeout) {
|