@m2k-5f/pgtx 1.0.5 → 1.2.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,7 +7,7 @@ Experience ORM-like convenience (auto-inserts, updates, recursive fragments) wit
7
7
 
8
8
  ## 🔥 Why Pgtx?
9
9
 
10
- * **Zero-Cost Abstraction**: Only ~2% overhead compared to raw `pg.query`.
10
+ * **Zero-Cost Abstraction**: Only ~2% - 0% overhead compared to raw `pg.query`.
11
11
  * **Structural Caching**: Uses `WeakMap` to cache SQL templates. Static parts are parsed only once.
12
12
  * **Explicit Prepared Statements**: Create and reuse prepared statements with type safety
13
13
  * **True Recursion**: Nest `sql.fragment` anywhere. Argument numbering ($1, $2) is managed automatically across all nesting levels.
@@ -46,8 +46,8 @@ The following results were measured during sequential execution of 10,000 comple
46
46
 
47
47
  | Tool | RPS | Avg. Query Time | Performance Overhead |
48
48
  | :--- | :---: | :---: | :---: |
49
- | **Native `pg.query`** | **~194** | **5.15 ms** | **0% (Baseline)** |
50
- | **Pgtx** | **~190** | **5.27 ms** | **~2.1%** |
49
+ | **Native `pg.query`** | **~176** | **5.696 ms** | **0% (Baseline)** |
50
+ | **Pgtx** | **~177** | **5.664 ms** | **-0.56%(zero-cost)** |
51
51
  | Typical Node.js ORM | **~58** | 12.5+ ms | > 150% |
52
52
 
53
53
 
@@ -76,10 +76,14 @@ await pool.begin(async (tx) => {
76
76
  await tx.query`UPDATE accounts SET balance = balance - 100 WHERE id = 1`;
77
77
 
78
78
  // Nested transaction (Savepoint)
79
- await tx.savepoint('inventory', async (stx) => {
79
+ let err = await tx.savepoint('inventory', async (stx) => {
80
80
  await stx.query`UPDATE stock SET count = count - 1 WHERE item_id = ${42}`;
81
81
  if (outOfStock) throw new Error(); // Only 'inventory' rolls back
82
82
  });
83
+ if (err) {
84
+ // Handle error
85
+ // tx still active & can be commited
86
+ }
83
87
  });
84
88
  // Main transaction commits or rolls back based on callback success
85
89
  ```
@@ -139,6 +143,44 @@ await pool.query`UPDATE users SET ${sql.update(data)} WHERE id = ${1}`
139
143
  // SQL: UPDATE users SET status = $1, last_login = $2 WHERE id = $3
140
144
  ```
141
145
 
146
+ ## 🔄 Null & Undefined Handling
147
+
148
+ Pgtx treats `null` and `undefined` differently to match SQL semantics and JavaScript expectations:
149
+
150
+ | Value | In INSERT | In UPDATE | In VALUES/Parameters | In Arrays |
151
+ |-------|-----------|-----------|----------------------|-----------|
152
+ | `null` | `NULL` | `NULL` | `NULL` | `NULL` |
153
+ | `undefined` | `DEFAULT` | Field skipped | Error | Error |
154
+
155
+ ### Examples
156
+
157
+ ```typescript
158
+ // INSERT: undefined becomes DEFAULT
159
+ await pool.query`
160
+ INSERT INTO users ${sql.insert({
161
+ name: 'Alice',
162
+ age: undefined, // -> DEFAULT
163
+ email: null // -> NULL
164
+ })}
165
+ `
166
+ // SQL: INSERT INTO users (name, age, email) VALUES ($1, DEFAULT, $2)
167
+
168
+ // UPDATE: undefined fields are skipped
169
+ await pool.query`
170
+ UPDATE users SET ${sql.update({
171
+ name: 'Bob',
172
+ age: undefined, // skipped - age remains unchanged
173
+ deleted_at: null // explicitly set to NULL
174
+ })} WHERE id = 1
175
+ `
176
+ // SQL: UPDATE users SET name = $1, deleted_at = $2 WHERE id = 1
177
+
178
+ // Arrays: undefined throw
179
+ sql.array([1, undefined, 3]) // ❌ TypeError(`Array item at index 1 is undefined`)
180
+
181
+ // Empty arrays throw (use [null] for NULL result)
182
+ sql.array([]) // ❌ Error: Array clause is empty. Use sql.array([null])...
183
+ ```
142
184
 
143
185
  ## 🛡️ Security
144
186
  * **SQL Injection**: Automatically uses native placeholders ($1, $2) for all values.
@@ -1 +1 @@
1
- {"version":3,"file":"array.clause.d.ts","sourceRoot":"","sources":["../../src/clauses/array.clause.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C,qBAAa,WAAY,SAAQ,MAAM;IAE/B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS;gBADT,KAAK,EAAE,GAAG,EAAE,EACZ,SAAS,GAAE,MAAa;IAGpC,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB;CAmBrD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,SAAS,GAAE,MAAa,GAAG,WAAW,CAE/E"}
1
+ {"version":3,"file":"array.clause.d.ts","sourceRoot":"","sources":["../../src/clauses/array.clause.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C,qBAAa,WAAY,SAAQ,MAAM;IAE/B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS;gBADT,KAAK,EAAE,GAAG,EAAE,EACZ,SAAS,GAAE,MAAa;IAGpC,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB;CAyBrD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,SAAS,GAAE,MAAa,GAAG,WAAW,CAE/E"}
@@ -11,9 +11,12 @@ class ArrayClause extends base_clause_1.Clause {
11
11
  }
12
12
  map(argCounter) {
13
13
  if (this.array.length === 0)
14
- return { text: 'NULL', args: [], argCounter };
14
+ throw new Error('Array clause is empty.\n Use sql.array([null]) if you want no results, or check your data.');
15
15
  const args = [];
16
- const text = `${this.array.map(value => {
16
+ const text = `${this.array.map((value, index) => {
17
+ if (value === undefined) {
18
+ throw new TypeError(`Array item at index ${index} is undefined`);
19
+ }
17
20
  if (value instanceof base_clause_1.Clause) {
18
21
  const result = value.map(argCounter);
19
22
  args.push(...result.args);
@@ -1 +1 @@
1
- {"version":3,"file":"iden.caluse.d.ts","sourceRoot":"","sources":["../../src/clauses/iden.caluse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C,qBAAa,gBAAgB,CAAC,CAAC,SAAS,MAAM,CAAE,SAAQ,MAAM;IAEtD,QAAQ,CAAC,KAAK,EAAE,CAAC;gBAAR,KAAK,EAAE,CAAC;IAGZ,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB;CAMrD;AAED,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,EAAE,aAAa,EAAE,CAAC,uBAE7D"}
1
+ {"version":3,"file":"iden.caluse.d.ts","sourceRoot":"","sources":["../../src/clauses/iden.caluse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C,qBAAa,gBAAgB,CAAC,CAAC,SAAS,MAAM,CAAE,SAAQ,MAAM;IAEtD,QAAQ,CAAC,KAAK,EAAE,CAAC;gBAAR,KAAK,EAAE,CAAC;IAGZ,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB;CASrD;AAED,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,EAAE,aAAa,EAAE,CAAC,uBAE7D"}
@@ -9,6 +9,9 @@ class IdentifierClause extends base_clause_1.Clause {
9
9
  this.value = value;
10
10
  }
11
11
  map(argCounter) {
12
+ if (this.value === undefined) {
13
+ throw new TypeError(`Query parameter undefined at position ${argCounter}`);
14
+ }
12
15
  const text = `"${this.value}"`;
13
16
  const args = [];
14
17
  return { text, args, argCounter };
@@ -0,0 +1,9 @@
1
+ import { Clause } from "./base.clause";
2
+ import { CompiledSqlQuery } from "../utils";
3
+ export declare class InsertClause<T extends Record<string, any>> extends Clause {
4
+ readonly inserts: T[];
5
+ constructor(inserts: T[]);
6
+ map(argCounter: number): CompiledSqlQuery;
7
+ }
8
+ export declare function insertClause<T extends Record<string, any>>(...objects: NoInfer<T>[]): InsertClause<T>;
9
+ //# sourceMappingURL=insert.clause.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"insert.clause.d.ts","sourceRoot":"","sources":["../../src/clauses/insert.clause.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C,qBAAa,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAE,SAAQ,MAAM;IAE/D,QAAQ,CAAC,OAAO,EAAE,CAAC,EAAE;gBAAZ,OAAO,EAAE,CAAC,EAAE;IAGhB,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB;CAgCrD;AAED,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,mBAEnF"}
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InsertClause = void 0;
4
+ exports.insertClause = insertClause;
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
+ if (this.inserts.length === 0) {
13
+ throw new Error('Insert clause has no rows to insert.\n' +
14
+ 'Provide at least one object with data.');
15
+ }
16
+ const columns = Object.keys(this.inserts[0]);
17
+ const columnsCount = columns.length;
18
+ let text = `(${columns.join(', ')}) VALUES `;
19
+ let args = [];
20
+ const valuesText = this.inserts.map(values => {
21
+ if (Object.keys(values).length !== columnsCount)
22
+ throw new Error(`
23
+ all rows must have the same columns
24
+ `);
25
+ return `(${Object.values(values).map(value => {
26
+ if (value === undefined)
27
+ return "DEFAULT";
28
+ args.push(value);
29
+ return `$${argCounter++}`;
30
+ }).join(", ")})`;
31
+ }).join(", ");
32
+ text += valuesText;
33
+ return { text, args, argCounter };
34
+ }
35
+ }
36
+ exports.InsertClause = InsertClause;
37
+ function insertClause(...objects) {
38
+ return new InsertClause(objects);
39
+ }
@@ -0,0 +1,9 @@
1
+ import { Clause } from "./base.clause";
2
+ import { CompiledSqlQuery } from "../utils";
3
+ export declare class LiteralClause<T extends string> extends Clause {
4
+ readonly value: T;
5
+ constructor(value: T);
6
+ map(argCounter: number): CompiledSqlQuery;
7
+ }
8
+ export declare function literalClause<T extends string>(value: T): LiteralClause<T>;
9
+ //# sourceMappingURL=literal.clause.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"literal.clause.d.ts","sourceRoot":"","sources":["../../src/clauses/literal.clause.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAC,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C,qBAAa,aAAa,CAAC,CAAC,SAAS,MAAM,CAAE,SAAQ,MAAM;IAEnD,QAAQ,CAAC,KAAK,EAAE,CAAC;gBAAR,KAAK,EAAE,CAAC;IAKZ,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB;CAMrD;AAGD,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAE1E"}
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LiteralClause = void 0;
4
+ exports.literalClause = literalClause;
5
+ const base_clause_1 = require("./base.clause");
6
+ class LiteralClause 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.LiteralClause = LiteralClause;
19
+ function literalClause(value) {
20
+ return new LiteralClause(value);
21
+ }
@@ -1 +1 @@
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;CAGrD;AAGD,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAExE"}
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"}
@@ -9,6 +9,9 @@ class StaticClause extends base_clause_1.Clause {
9
9
  this.value = value;
10
10
  }
11
11
  map(argCounter) {
12
+ if (this.value === undefined) {
13
+ throw new TypeError(`Query parameter undefined at position ${argCounter}`);
14
+ }
12
15
  return { text: this.value, args: [], argCounter };
13
16
  }
14
17
  }
@@ -2,9 +2,8 @@ import { Clause } from "./base.clause";
2
2
  import { CompiledSqlQuery } from "../utils";
3
3
  export declare class UpdateClause<T extends Record<string, any>> extends Clause {
4
4
  readonly updateMap: T;
5
- readonly columns?: (keyof T)[] | undefined;
6
- constructor(updateMap: T, columns?: (keyof T)[] | undefined);
5
+ constructor(updateMap: T);
7
6
  map(argCounter: number): CompiledSqlQuery;
8
7
  }
9
- export declare function updateClause<T extends Record<string, any>>(object: T, ...updateColumns: (keyof T)[]): UpdateClause<T>;
8
+ export declare function updateClause<T extends Record<string, any>>(object: T): UpdateClause<T>;
10
9
  //# sourceMappingURL=update.clause.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"update.clause.d.ts","sourceRoot":"","sources":["../../src/clauses/update.clause.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C,qBAAa,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAE,SAAQ,MAAM;IAE/D,QAAQ,CAAC,SAAS,EAAE,CAAC;IACrB,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE;gBADrB,SAAS,EAAE,CAAC,EACZ,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,YAAA;IAGzB,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB;CAerD;AAED,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,aAAa,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,YAAY,CAAC,CAAC,CAAC,CAErH"}
1
+ {"version":3,"file":"update.clause.d.ts","sourceRoot":"","sources":["../../src/clauses/update.clause.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C,qBAAa,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAE,SAAQ,MAAM;IAE/D,QAAQ,CAAC,SAAS,EAAE,CAAC;gBAAZ,SAAS,EAAE,CAAC;IAGhB,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB;CAcrD;AAED,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC,mBAEpE"}
@@ -4,23 +4,23 @@ exports.UpdateClause = void 0;
4
4
  exports.updateClause = updateClause;
5
5
  const base_clause_1 = require("./base.clause");
6
6
  class UpdateClause extends base_clause_1.Clause {
7
- constructor(updateMap, columns) {
7
+ constructor(updateMap) {
8
8
  super();
9
9
  this.updateMap = updateMap;
10
- this.columns = columns;
11
10
  }
12
11
  map(argCounter) {
13
- const columns = this.columns || Object.keys(this.updateMap);
14
12
  const args = [];
15
- const text = columns
16
- .map((key) => {
17
- args.push(this.updateMap[key]);
18
- return `${key.toString()} = $${argCounter++}`;
13
+ const entries = Object.entries(this.updateMap).filter(([_, value]) => value !== undefined);
14
+ if (entries.length === 0)
15
+ throw new Error('Update clause has no data to update. All values are `undefined`');
16
+ const text = entries.map(([key, value]) => {
17
+ args.push(value);
18
+ return `${key} = $${argCounter++}`;
19
19
  }).join(', ');
20
20
  return { text, args, argCounter };
21
21
  }
22
22
  }
23
23
  exports.UpdateClause = UpdateClause;
24
- function updateClause(object, ...updateColumns) {
25
- return new UpdateClause(object, updateColumns.length > 0 ? updateColumns : undefined);
24
+ function updateClause(object) {
25
+ return new UpdateClause(object);
26
26
  }
@@ -26,7 +26,7 @@ export declare class Connection {
26
26
  * Releases the connection back to the pool.
27
27
  * The connection cannot be used after this call.
28
28
  */
29
- release(): void;
29
+ release(): Promise<void>;
30
30
  private checkActive;
31
31
  /**
32
32
  * Executes a tagged SQL query using structural caching.
@@ -1 +1 @@
1
- {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,IAAI,CAAA;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAK3C;;;GAGG;AACH,qBAAa,UAAU;IAIf,OAAO,CAAC,QAAQ,CAAC,MAAM;IAH3B,OAAO,CAAC,UAAU,CAAiB;gBAGd,MAAM,EAAE,UAAU;IAGvC;;OAEG;IACH,IAAW,QAAQ,IAAI,OAAO,CAE7B;IAED;;;;;;;OAOG;IACU,OAAO,CAAC,OAAO,SAAS,cAAc,GAAG,GAAG,EAAE,OAAO,SAAS,GAAG,EAAE,GAAG,GAAG,EAAE,EACpF,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,iBAAiB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAoB/C;;;OAGG;IACI,OAAO,IAAI,IAAI;IAMtB,OAAO,CAAC,WAAW;IAMnB;;;;;;OAMG;IACU,KAAK,CAAC,CAAC,SAAS,cAAc,GAAG,GAAG,EAAE,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAUjH;;;;;;;;OAQG;IACU,KAAK,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;CAmB/E"}
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,IAAI,CAAA;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAK3C;;;GAGG;AACH,qBAAa,UAAU;IAIf,OAAO,CAAC,QAAQ,CAAC,MAAM;IAH3B,OAAO,CAAC,UAAU,CAAiB;gBAGd,MAAM,EAAE,UAAU;IAGvC;;OAEG;IACH,IAAW,QAAQ,IAAI,OAAO,CAE7B;IAED;;;;;;;OAOG;IACU,OAAO,CAAC,OAAO,SAAS,cAAc,GAAG,GAAG,EAAE,OAAO,SAAS,GAAG,EAAE,GAAG,GAAG,EAAE,EACpF,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,iBAAiB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAoB/C;;;OAGG;IACU,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAMrC,OAAO,CAAC,WAAW;IAMnB;;;;;;OAMG;IACU,KAAK,CAAC,CAAC,SAAS,cAAc,GAAG,GAAG,EAAE,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAUjH;;;;;;;;OAQG;IACU,KAAK,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;CAmB/E"}
@@ -48,7 +48,7 @@ class Connection {
48
48
  * Releases the connection back to the pool.
49
49
  * The connection cannot be used after this call.
50
50
  */
51
- release() {
51
+ async release() {
52
52
  if (this.isReleased)
53
53
  return;
54
54
  this.isReleased = true;
package/dist/index.d.ts CHANGED
@@ -2,9 +2,9 @@ import { arrayClause } from "./clauses/array.clause";
2
2
  import { fragmentClause } from "./clauses/fragment.clause";
3
3
  import { identClause } from "./clauses/iden.caluse";
4
4
  import { Pool as PgtxPool } from "./pool";
5
- import { staticClause } from "./clauses/static.clause";
5
+ import { literalClause } from "./clauses/literal.clause";
6
6
  import { updateClause } from "./clauses/update.clause";
7
- import { valueClause } from "./clauses/values.clause";
7
+ import { insertClause } from "./clauses/insert.clause";
8
8
  /**
9
9
  * Core SQL tagging utility for Pgtx.
10
10
  * Provides type-safe helpers for building dynamic queries with recursive support.
@@ -22,7 +22,7 @@ export declare const sql: {
22
22
  * sql.insert([{ id: 1 }, { id: 2 }])
23
23
  * // Result: (id) VALUES ($1), ($2)
24
24
  */
25
- insert: typeof valueClause;
25
+ insert: typeof insertClause;
26
26
  /**
27
27
  * Generates a SET clause for UPDATE queries from a JavaScript object.
28
28
  *
@@ -51,7 +51,7 @@ export declare const sql: {
51
51
  * sql.literal('DESC')
52
52
  * // Result: DESC
53
53
  */
54
- literal: typeof staticClause;
54
+ literal: typeof literalClause;
55
55
  /**
56
56
  * Creates a reusable, recursive SQL fragment.
57
57
  * Fragments can be nested within each other; argument numbering is handled automatically.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAEtD;;;GAGG;AACH,eAAO,MAAM,GAAG;IACZ;;;;;;;;;;;OAWG;;IAGH;;;;;;OAMG;;IAGH;;;;;;;;;;OAUG;;IAGH;;;;;;;OAOG;;IAGH;;;;;;;;OAQG;;IAGH;;;;;;;;;;;;;;;;;;;OAmBG;;CAEN,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,IAAI,iBAAW,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvD;;;GAGG;AACH,eAAO,MAAM,GAAG;IACZ;;;;;;;;;;;OAWG;;IAGH;;;;;;OAMG;;IAGH;;;;;;;;;;OAUG;;IAGH;;;;;;;OAOG;;IAGH;;;;;;;;OAQG;;IAGH;;;;;;;;;;;;;;;;;;;OAmBG;;CAEN,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,IAAI,iBAAW,CAAA"}
package/dist/index.js CHANGED
@@ -5,9 +5,9 @@ const array_clause_1 = require("./clauses/array.clause");
5
5
  const fragment_clause_1 = require("./clauses/fragment.clause");
6
6
  const iden_caluse_1 = require("./clauses/iden.caluse");
7
7
  const pool_1 = require("./pool");
8
- const static_clause_1 = require("./clauses/static.clause");
8
+ const literal_clause_1 = require("./clauses/literal.clause");
9
9
  const update_clause_1 = require("./clauses/update.clause");
10
- const values_clause_1 = require("./clauses/values.clause");
10
+ const insert_clause_1 = require("./clauses/insert.clause");
11
11
  /**
12
12
  * Core SQL tagging utility for Pgtx.
13
13
  * Provides type-safe helpers for building dynamic queries with recursive support.
@@ -25,7 +25,7 @@ exports.sql = {
25
25
  * sql.insert([{ id: 1 }, { id: 2 }])
26
26
  * // Result: (id) VALUES ($1), ($2)
27
27
  */
28
- insert: values_clause_1.valueClause,
28
+ insert: insert_clause_1.insertClause,
29
29
  /**
30
30
  * Generates a SET clause for UPDATE queries from a JavaScript object.
31
31
  *
@@ -54,7 +54,7 @@ exports.sql = {
54
54
  * sql.literal('DESC')
55
55
  * // Result: DESC
56
56
  */
57
- literal: static_clause_1.staticClause,
57
+ literal: literal_clause_1.literalClause,
58
58
  /**
59
59
  * Creates a reusable, recursive SQL fragment.
60
60
  * Fragments can be nested within each other; argument numbering is handled automatically.
@@ -35,6 +35,6 @@ export declare class Transaction {
35
35
  * if (error) throw new Error(); // Only this insert rolls back
36
36
  * });
37
37
  */
38
- savepoint(name: string, callback: (tx: Transaction) => Promise<void>): Promise<void>;
38
+ savepoint(name: string, callback: (tx: Transaction) => Promise<void>): Promise<Error | null>;
39
39
  }
40
40
  //# sourceMappingURL=transaction.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../src/transaction.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,IAAI,CAAA;AACnC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAIzC;;;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,cAAc,GAAG,GAAG,EAAE,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAKjH;;;;;;;;;OASG;IACU,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;CAcpG"}
1
+ {"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../src/transaction.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,IAAI,CAAA;AACnC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAIzC;;;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,cAAc,GAAG,GAAG,EAAE,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAKjH;;;;;;;;;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"}
@@ -61,10 +61,11 @@ class Transaction {
61
61
  try {
62
62
  await callback(this);
63
63
  await this.conn.query `RELEASE SAVEPOINT ${(0, iden_caluse_1.identClause)(name)}`;
64
+ return null;
64
65
  }
65
66
  catch (err) {
66
67
  await this.conn.query `ROLLBACK TO SAVEPOINT ${(0, iden_caluse_1.identClause)(name)}`;
67
- throw err;
68
+ return err instanceof Error ? err : new Error(String(err));
68
69
  }
69
70
  }
70
71
  }
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,gBAAgB,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,UAAU,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,oBAAoB,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,gBAAgB,CAyBrH"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,gBAAgB,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,UAAU,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,oBAAoB,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,gBAAgB,CAgCrH"}
package/dist/utils.js CHANGED
@@ -18,6 +18,10 @@ function compileSqlTemplate(strings, values, argCounter) {
18
18
  args.push(...result.args);
19
19
  }
20
20
  else {
21
+ if (value === undefined) {
22
+ throw new TypeError(`Query parameter at position ${argCounter} is undefined.
23
+ Use null if you want NULL in SQL, or ensure the value is defined.`);
24
+ }
21
25
  args.push(value);
22
26
  text += `$${argCounter}`;
23
27
  argCounter++;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@m2k-5f/pgtx",
3
- "version": "1.0.5",
3
+ "version": "1.2.0",
4
4
  "description": "Lightweight, high-performance SQL toolkit for node-postgres",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",