@m2k-5f/pgtx 1.4.12 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.md +225 -128
  2. package/dist/clauses/array.clause.js +1 -1
  3. package/dist/clauses/empty.clause.d.ts +1 -1
  4. package/dist/clauses/empty.clause.d.ts.map +1 -1
  5. package/dist/clauses/empty.clause.js +2 -2
  6. package/dist/clauses/fragment.clause.d.ts +1 -1
  7. package/dist/clauses/fragment.clause.d.ts.map +1 -1
  8. package/dist/clauses/fragment.clause.js +3 -5
  9. package/dist/clauses/iden.caluse.d.ts +1 -1
  10. package/dist/clauses/iden.caluse.d.ts.map +1 -1
  11. package/dist/clauses/index.d.ts +10 -0
  12. package/dist/clauses/index.d.ts.map +1 -0
  13. package/dist/clauses/index.js +25 -0
  14. package/dist/clauses/insert.clause.d.ts +1 -1
  15. package/dist/clauses/insert.clause.d.ts.map +1 -1
  16. package/dist/clauses/insert.clause.js +1 -1
  17. package/dist/clauses/literal.clause.d.ts +1 -1
  18. package/dist/clauses/literal.clause.d.ts.map +1 -1
  19. package/dist/clauses/update.clause.d.ts +1 -1
  20. package/dist/clauses/update.clause.d.ts.map +1 -1
  21. package/dist/clauses/update.clause.js +1 -1
  22. package/dist/clauses/where.clause.d.ts +1 -1
  23. package/dist/clauses/where.clause.d.ts.map +1 -1
  24. package/dist/clauses/where.clause.js +1 -1
  25. package/dist/connection.d.ts +135 -31
  26. package/dist/connection.d.ts.map +1 -1
  27. package/dist/connection.js +290 -59
  28. package/dist/index.d.ts +4 -10
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +13 -19
  31. package/dist/pool.d.ts +126 -32
  32. package/dist/pool.d.ts.map +1 -1
  33. package/dist/pool.js +203 -54
  34. package/dist/protocol/connection-request-writer.d.ts +38 -0
  35. package/dist/protocol/connection-request-writer.d.ts.map +1 -0
  36. package/dist/protocol/connection-request-writer.js +175 -0
  37. package/dist/protocol/connection-response-reader.d.ts +49 -0
  38. package/dist/protocol/connection-response-reader.d.ts.map +1 -0
  39. package/dist/protocol/connection-response-reader.js +185 -0
  40. package/dist/protocol/constants.d.ts +46 -0
  41. package/dist/protocol/constants.d.ts.map +1 -0
  42. package/dist/protocol/constants.js +43 -0
  43. package/dist/protocol/socket-authorization.d.ts +11 -0
  44. package/dist/protocol/socket-authorization.d.ts.map +1 -0
  45. package/dist/protocol/socket-authorization.js +95 -0
  46. package/dist/protocol/socket-connector.d.ts +17 -0
  47. package/dist/protocol/socket-connector.d.ts.map +1 -0
  48. package/dist/protocol/socket-connector.js +48 -0
  49. package/dist/queue.d.ts +10 -0
  50. package/dist/queue.d.ts.map +1 -0
  51. package/dist/queue.js +27 -0
  52. package/dist/security/md5.d.ts +2 -0
  53. package/dist/security/md5.d.ts.map +1 -0
  54. package/dist/security/md5.js +12 -0
  55. package/dist/security/sasl.d.ts +6 -0
  56. package/dist/security/sasl.d.ts.map +1 -0
  57. package/dist/security/sasl.js +29 -0
  58. package/dist/transaction.d.ts +1 -2
  59. package/dist/transaction.d.ts.map +1 -1
  60. package/dist/transaction.js +4 -4
  61. package/dist/types.d.ts +6 -8
  62. package/dist/types.d.ts.map +1 -1
  63. package/dist/utils/template-compiler.d.ts +3 -0
  64. package/dist/utils/template-compiler.d.ts.map +1 -0
  65. package/dist/utils/template-compiler.js +64 -0
  66. package/dist/utils/value-parser.d.ts +6 -0
  67. package/dist/utils/value-parser.d.ts.map +1 -0
  68. package/dist/utils/value-parser.js +101 -0
  69. package/package.json +8 -9
@@ -18,7 +18,7 @@ class UpdateClause extends abstract_clause_1.Clause {
18
18
  if (index)
19
19
  params.text.push(', ');
20
20
  params.args.push(value);
21
- params.text.push(`${key} = $${params.counter++}`);
21
+ params.text.push(`${key} = $${params.args.length}`);
22
22
  });
23
23
  }
24
24
  }
@@ -2,7 +2,7 @@ import { Clause } from "./abstract.clause";
2
2
  import { ClauseStrategyParams } from "../types";
3
3
  export declare class WhereClause<T extends Record<string, any>> extends Clause {
4
4
  private _value;
5
- constructor(_value: T);
5
+ private constructor();
6
6
  static create<T extends Record<string, any>>(whereMap: T): WhereClause<T>;
7
7
  mapIntoQuery(params: ClauseStrategyParams): void;
8
8
  }
@@ -1 +1 @@
1
- {"version":3,"file":"where.clause.d.ts","sourceRoot":"","sources":["../../src/clauses/where.clause.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAEhD,qBAAa,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAE,SAAQ,MAAM;IAE9D,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,CAAC;WAGP,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,QAAQ,EAAE,CAAC;IAItD,YAAY,CAAC,MAAM,EAAE,oBAAoB;CAYrD"}
1
+ {"version":3,"file":"where.clause.d.ts","sourceRoot":"","sources":["../../src/clauses/where.clause.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAEhD,qBAAa,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAE,SAAQ,MAAM;IAE9D,OAAO,CAAC,MAAM;IADlB,OAAO;WAIO,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,QAAQ,EAAE,CAAC;IAItD,YAAY,CAAC,MAAM,EAAE,oBAAoB;CAYrD"}
@@ -18,7 +18,7 @@ class WhereClause extends abstract_clause_1.Clause {
18
18
  if (index)
19
19
  params.text.push(' AND ');
20
20
  params.args.push(value);
21
- params.text.push(`${key} = $${params.counter++}`);
21
+ params.text.push(`"${key}" = $${params.args.length}`);
22
22
  });
23
23
  }
24
24
  }
@@ -1,51 +1,155 @@
1
- import { PoolClient, QueryResultRow } from "pg";
2
- import { PreparedStatement } from "./types";
1
+ import { ColumnDescription } from "./types";
3
2
  import { Transaction } from "./transaction";
3
+ import { Queue } from "./queue";
4
+ type LogLevel = "none" | "error" | "notice" | "query";
5
+ export type ConnectionParams = {
6
+ user: string;
7
+ password?: string;
8
+ host: string;
9
+ port: number;
10
+ database: string;
11
+ logLevel?: LogLevel;
12
+ };
13
+ export type ConnectionQueryContext = {
14
+ columns: ColumnDescription[];
15
+ rows: (string | null)[][];
16
+ resolve: (value: any) => void;
17
+ reject: (err: Error) => void;
18
+ text: string;
19
+ statementName: string | null;
20
+ };
21
+ export declare class ConnectionQueryQueue extends Queue<ConnectionQueryContext> {
22
+ rejectAllNext(error: Error): void;
23
+ }
4
24
  /**
5
- * Represents a single dedicated connection to the database.
6
- * Always remember to call `.release()` when finished.
25
+ * Represents a single dedicated connection to the PostgreSQL database.
26
+ *
27
+ * Supports:
28
+ * - Tagged template queries with automatic parameter binding
29
+ * - Prepared statements with caching
30
+ * - Transaction management with savepoints
31
+ * - Pipeline execution for concurrent queries
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * const conn = await Connection.new({
36
+ * host: 'localhost',
37
+ * user: 'postgres',
38
+ * password: 'postgres',
39
+ * database: 'test'
40
+ * })
41
+ *
42
+ * // Simple query
43
+ * const users = await conn.query`SELECT * FROM users WHERE id = ${1}`
44
+ *
45
+ * // Transaction
46
+ * await conn.begin(async tx => {
47
+ * await tx.query`INSERT INTO users ...`
48
+ * })
49
+ *
50
+ * // Close connection
51
+ * conn.close()
52
+ * ```
7
53
  */
8
54
  export declare class Connection {
9
- private readonly client;
10
- private readonly enableLogs;
11
- private isReleased;
12
- constructor(client: PoolClient, enableLogs: boolean);
13
- /**
14
- * Checks if the connection is still open and not returned to the pool.
15
- */
16
- get isActive(): boolean;
55
+ private _isFlushing;
56
+ private _isAlive;
57
+ private _socket;
58
+ private _writer;
59
+ private _queue;
60
+ private _statementDescriptions;
61
+ private _statements;
62
+ private _parsePending;
63
+ private _stmtCounter;
64
+ private _logLevel;
65
+ private _nextStatement;
66
+ private constructor();
17
67
  /**
18
- * Creates a prepared statement on this specific connection.
19
- * Maps '?' placeholders to native PostgreSQL '$1, $2' indexes.
68
+ * Creates a new database connection.
69
+ *
70
+ * @param params - Connection parameters
71
+ * @returns A new Connection instance
72
+ * @throws {Error} If authentication fails or connection cannot be established
20
73
  *
21
74
  * @example
22
- * const stmt = await conn.prepare('get_user', 'SELECT * FROM users WHERE id = ?');
23
- * const rows = await stmt.execute(1);
75
+ * ```ts
76
+ * const conn = await Connection.new({
77
+ * host: 'localhost',
78
+ * port: 5432,
79
+ * user: 'postgres',
80
+ * password: 'secret',
81
+ * database: 'myapp'
82
+ * })
83
+ * ```
24
84
  */
25
- prepare<TResult extends QueryResultRow, TParams extends any[] = []>(name: string, sqlTemplate: string): Promise<PreparedStatement<TResult, TParams>>;
85
+ static new(params: ConnectionParams): Promise<Connection>;
26
86
  /**
27
- * Releases the connection back to the pool.
28
- * The connection cannot be used after this call.
87
+ * Executes a query using tagged template literals.
88
+ *
89
+ * Parameters are automatically bound to `$1, $2, ...` placeholders.
90
+ * Queries with parameters use prepared statements for performance.
91
+ *
92
+ * @param templates - Tagged template string with SQL
93
+ * @param args - Query parameters
94
+ * @returns Array of rows with proper typing
95
+ * @throws {Error} If connection is dead or query fails
96
+ *
97
+ * @example
98
+ * ```ts
99
+ * // Simple query
100
+ * const users = await conn.query`SELECT * FROM users`
101
+ *
102
+ * // With parameters
103
+ * const user = await conn.query`SELECT * FROM users WHERE id = ${1}`
104
+ *
105
+ * // With typed result
106
+ * type User = { id: number, name: string }
107
+ * const users = await conn.query<User>`SELECT * FROM users`
108
+ * ```
29
109
  */
30
- release(): Promise<void>;
31
- private checkActive;
110
+ query<T extends Record<string, unknown>>(templates: TemplateStringsArray, ...args: any[]): Promise<T[]>;
32
111
  /**
33
- * Executes a tagged SQL query using structural caching.
34
- * Supports recursive fragments and clauses.
112
+ * Starts a managed transaction on this connection.
113
+ *
114
+ * Automatically handles:
115
+ * - `BEGIN` before the callback
116
+ * - `COMMIT` on successful completion
117
+ * - `ROLLBACK` if an error is thrown
118
+ *
119
+ * @param txCallback - Async function that receives a `Transaction` instance
120
+ * @returns The value returned from the callback
121
+ * @throws {Error} If connection is dead or transaction fails
35
122
  *
36
123
  * @example
37
- * const users = await conn.query`SELECT * FROM users WHERE id = ${1}`;
124
+ * ```ts
125
+ * const result = await conn.begin(async tx => {
126
+ * await tx.query`INSERT INTO accounts (id, balance) VALUES (1, 100)`
127
+ * await tx.query`UPDATE accounts SET balance = balance - 10 WHERE id = 1`
128
+ * return { success: true }
129
+ * })
130
+ * ```
38
131
  */
39
- query<T extends QueryResultRow = any>(strings: TemplateStringsArray, ...values: any[]): Promise<T[]>;
132
+ begin<T>(txCallback: (transaction: Transaction) => Promise<T>): Promise<T>;
133
+ private _registerFlush;
134
+ private _flush;
135
+ private _handlePacket;
136
+ private _handleError;
40
137
  /**
41
- * Starts a managed transaction on this connection.
42
- * Handles automatic COMMIT on success or ROLLBACK on error.
138
+ * Checks if the connection is still alive and usable.
139
+ * Returns `false` if the socket is destroyed or connection is dead.
140
+ */
141
+ get isAlive(): boolean;
142
+ /**
143
+ * Closes the connection immediately.
144
+ * All pending queries will be rejected with an error.
145
+ * The connection cannot be used after this call.
43
146
  *
44
147
  * @example
45
- * await conn.begin(async (tx) => {
46
- * await tx.query`INSERT INTO logs ...`;
47
- * });
148
+ * ```ts
149
+ * conn.close()
150
+ * ```
48
151
  */
49
- begin<T>(callback: (tx: Transaction) => Promise<T>): Promise<T>;
152
+ close(): void;
50
153
  }
154
+ export {};
51
155
  //# sourceMappingURL=connection.d.ts.map
@@ -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;AAM3C;;;GAGG;AACH,qBAAa,UAAU;IAIf,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAJ/B,OAAO,CAAC,UAAU,CAAiB;gBAGd,MAAM,EAAE,UAAU,EAClB,UAAU,EAAE,OAAO;IAGxC;;OAEG;IACH,IAAW,QAAQ,IAAI,OAAO,CAE7B;IAED;;;;;;;OAOG;IACU,OAAO,CAAC,OAAO,SAAS,cAAc,EAAE,OAAO,SAAS,GAAG,EAAE,GAAG,EAAE,EAC3E,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,iBAAiB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAY/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;IAkBjH;;;;;;;;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":"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,89 +1,188 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Connection = void 0;
3
+ exports.Connection = exports.ConnectionQueryQueue = void 0;
4
+ const process_1 = require("process");
5
+ const connection_request_writer_1 = require("./protocol/connection-request-writer");
6
+ const socket_authorization_1 = require("./protocol/socket-authorization");
7
+ const constants_1 = require("./protocol/constants");
8
+ const value_parser_1 = require("./utils/value-parser");
9
+ const template_compiler_1 = require("./utils/template-compiler");
4
10
  const transaction_1 = require("./transaction");
5
- const query_cacher_1 = require("./query.cacher");
6
- const prepared_statement_1 = require("./prepared.statement");
7
- const cacher = new query_cacher_1.QueryCacher();
11
+ const socket_connector_1 = require("./protocol/socket-connector");
12
+ const queue_1 = require("./queue");
13
+ 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;
8
23
  /**
9
- * Represents a single dedicated connection to the database.
10
- * Always remember to call `.release()` when finished.
24
+ * Represents a single dedicated connection to the PostgreSQL database.
25
+ *
26
+ * Supports:
27
+ * - Tagged template queries with automatic parameter binding
28
+ * - Prepared statements with caching
29
+ * - Transaction management with savepoints
30
+ * - Pipeline execution for concurrent queries
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * const conn = await Connection.new({
35
+ * host: 'localhost',
36
+ * user: 'postgres',
37
+ * password: 'postgres',
38
+ * database: 'test'
39
+ * })
40
+ *
41
+ * // Simple query
42
+ * const users = await conn.query`SELECT * FROM users WHERE id = ${1}`
43
+ *
44
+ * // Transaction
45
+ * await conn.begin(async tx => {
46
+ * await tx.query`INSERT INTO users ...`
47
+ * })
48
+ *
49
+ * // Close connection
50
+ * conn.close()
51
+ * ```
11
52
  */
12
53
  class Connection {
13
- constructor(client, enableLogs) {
14
- this.client = client;
15
- this.enableLogs = enableLogs;
16
- this.isReleased = false;
54
+ _nextStatement() {
55
+ return `s-${this._stmtCounter++}`;
17
56
  }
18
- /**
19
- * Checks if the connection is still open and not returned to the pool.
20
- */
21
- get isActive() {
22
- return !this.isReleased;
57
+ constructor(socket, writer, logLevel) {
58
+ this._isFlushing = false;
59
+ this._isAlive = true;
60
+ this._queue = new ConnectionQueryQueue();
61
+ this._statementDescriptions = new Map();
62
+ this._statements = new Map();
63
+ this._parsePending = new Map();
64
+ this._stmtCounter = 0;
65
+ this._logLevel = logLevel;
66
+ this._socket = new socket_connector_1.SocketConnector(socket, (type, reader) => this._handlePacket(type, reader), error => this._handleError(error));
67
+ this._writer = writer;
23
68
  }
24
69
  /**
25
- * Creates a prepared statement on this specific connection.
26
- * Maps '?' placeholders to native PostgreSQL '$1, $2' indexes.
70
+ * Creates a new database connection.
71
+ *
72
+ * @param params - Connection parameters
73
+ * @returns A new Connection instance
74
+ * @throws {Error} If authentication fails or connection cannot be established
27
75
  *
28
76
  * @example
29
- * const stmt = await conn.prepare('get_user', 'SELECT * FROM users WHERE id = ?');
30
- * const rows = await stmt.execute(1);
77
+ * ```ts
78
+ * const conn = await Connection.new({
79
+ * host: 'localhost',
80
+ * port: 5432,
81
+ * user: 'postgres',
82
+ * password: 'secret',
83
+ * database: 'myapp'
84
+ * })
85
+ * ```
31
86
  */
32
- async prepare(name, sqlTemplate) {
33
- let index = 1;
34
- const text = sqlTemplate.replace(/\?/g, () => `$${index++}`);
35
- return new prepared_statement_1.ConnPraparedStatement(this.client, text, name);
87
+ static async new(params) {
88
+ const writer = connection_request_writer_1.ConnectionRequestWriter.new();
89
+ const socket = await (0, socket_authorization_1.createAuthorizedSocket)(writer, params);
90
+ return new Connection(socket, writer, params.logLevel || 'error');
36
91
  }
37
92
  /**
38
- * Releases the connection back to the pool.
39
- * The connection cannot be used after this call.
40
- */
41
- async release() {
42
- if (this.isReleased)
43
- return;
44
- this.isReleased = true;
45
- this.client.release();
46
- }
47
- checkActive() {
48
- if (this.isReleased) {
49
- throw new Error("Connection has been released");
50
- }
51
- }
52
- /**
53
- * Executes a tagged SQL query using structural caching.
54
- * Supports recursive fragments and clauses.
93
+ * Executes a query using tagged template literals.
94
+ *
95
+ * Parameters are automatically bound to `$1, $2, ...` placeholders.
96
+ * Queries with parameters use prepared statements for performance.
97
+ *
98
+ * @param templates - Tagged template string with SQL
99
+ * @param args - Query parameters
100
+ * @returns Array of rows with proper typing
101
+ * @throws {Error} If connection is dead or query fails
55
102
  *
56
103
  * @example
57
- * const users = await conn.query`SELECT * FROM users WHERE id = ${1}`;
104
+ * ```ts
105
+ * // Simple query
106
+ * const users = await conn.query`SELECT * FROM users`
107
+ *
108
+ * // With parameters
109
+ * const user = await conn.query`SELECT * FROM users WHERE id = ${1}`
110
+ *
111
+ * // With typed result
112
+ * type User = { id: number, name: string }
113
+ * const users = await conn.query<User>`SELECT * FROM users`
114
+ * ```
58
115
  */
59
- async query(strings, ...values) {
60
- this.checkActive();
61
- const { text, args } = cacher.cachedBuild({
62
- templates: strings,
63
- args: values,
64
- counter: 1
65
- });
66
- if (this.enableLogs) {
67
- console.log(`\nQUERY: ${text}\n${args.length ? `ARGUMENTS: [${args}]\n` : ""}`);
116
+ query(templates, ...args) {
117
+ if (!this._isAlive)
118
+ throw ErrConnectionDead;
119
+ const query = (0, template_compiler_1.compileSqlTemplate)({ templates, args });
120
+ if (this._logLevel === 'query') {
121
+ console.log(`\nQUERY: ${query.text}\n${query.args.length !== 0 ? `ARGUMENTS: [${query.args}]\n` : ""}`);
68
122
  }
69
- const result = await this.client.query(text, args);
70
- return result.rows;
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
+ });
130
+ }
131
+ 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);
144
+ }
145
+ statementName = this._statements.get(query.text) || this._parsePending.get(query.text);
146
+ if (!this._statementDescriptions.has(statementName)) {
147
+ this._writer.writeDescribe(statementName);
148
+ }
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
+ }
156
+ });
71
157
  }
72
158
  /**
73
159
  * Starts a managed transaction on this connection.
74
- * Handles automatic COMMIT on success or ROLLBACK on error.
160
+ *
161
+ * Automatically handles:
162
+ * - `BEGIN` before the callback
163
+ * - `COMMIT` on successful completion
164
+ * - `ROLLBACK` if an error is thrown
165
+ *
166
+ * @param txCallback - Async function that receives a `Transaction` instance
167
+ * @returns The value returned from the callback
168
+ * @throws {Error} If connection is dead or transaction fails
75
169
  *
76
170
  * @example
77
- * await conn.begin(async (tx) => {
78
- * await tx.query`INSERT INTO logs ...`;
79
- * });
171
+ * ```ts
172
+ * const result = await conn.begin(async tx => {
173
+ * await tx.query`INSERT INTO accounts (id, balance) VALUES (1, 100)`
174
+ * await tx.query`UPDATE accounts SET balance = balance - 10 WHERE id = 1`
175
+ * return { success: true }
176
+ * })
177
+ * ```
80
178
  */
81
- async begin(callback) {
82
- this.checkActive();
179
+ async begin(txCallback) {
180
+ if (!this._isAlive)
181
+ throw ErrConnectionDead;
83
182
  const tx = new transaction_1.Transaction(this);
84
183
  try {
85
184
  await tx.query `BEGIN`;
86
- const result = await callback(tx);
185
+ const result = await txCallback(tx);
87
186
  if (tx.isActive)
88
187
  await tx.commit();
89
188
  return result;
@@ -94,5 +193,137 @@ class Connection {
94
193
  throw err;
95
194
  }
96
195
  }
196
+ _registerFlush() {
197
+ if (!this._isFlushing) {
198
+ this._isFlushing = true;
199
+ (0, process_1.nextTick)(() => this._flush());
200
+ }
201
+ }
202
+ _flush() {
203
+ if (!this._isAlive) {
204
+ this._queue.rejectAllNext(ErrConnectionDead);
205
+ return;
206
+ }
207
+ try {
208
+ this._socket.write(this._writer.writeSync());
209
+ this._writer.clear();
210
+ }
211
+ catch (err) {
212
+ this._isAlive = false;
213
+ this._socket.destroy();
214
+ this._queue.rejectAllNext(ErrConnectionDead);
215
+ }
216
+ }
217
+ _handlePacket(type, reader) {
218
+ const context = this._queue.get();
219
+ switch (type) {
220
+ case constants_1.ResponseTypes.ParseComplete:
221
+ {
222
+ reader.readParseComplete();
223
+ this._statements.set(context.text, context.statementName);
224
+ this._parsePending.delete(context.text);
225
+ }
226
+ break;
227
+ case constants_1.ResponseTypes.BindComplete:
228
+ {
229
+ reader.readBindComplete();
230
+ }
231
+ break;
232
+ case constants_1.ResponseTypes.CloseComplete:
233
+ {
234
+ reader.readBindComplete();
235
+ }
236
+ break;
237
+ case constants_1.ResponseTypes.ParameterDescription:
238
+ {
239
+ reader.readParameterDescription();
240
+ }
241
+ break;
242
+ case constants_1.ResponseTypes.NoData:
243
+ {
244
+ reader.readNoData();
245
+ if (context.statementName) {
246
+ this._statementDescriptions.set(context.statementName, []);
247
+ }
248
+ }
249
+ break;
250
+ case constants_1.ResponseTypes.RowDescription:
251
+ {
252
+ if (context.statementName) {
253
+ this._statementDescriptions.set(context.statementName, reader.readRowDescription());
254
+ }
255
+ else {
256
+ context.columns = reader.readRowDescription();
257
+ }
258
+ }
259
+ break;
260
+ case constants_1.ResponseTypes.DataRow:
261
+ {
262
+ context.rows.push(reader.readDataRow());
263
+ }
264
+ break;
265
+ case constants_1.ResponseTypes.ComandComplete:
266
+ {
267
+ reader.readCommandComplete();
268
+ if (context.statementName) {
269
+ context.columns = this._statementDescriptions.get(context.statementName);
270
+ }
271
+ context.resolve((0, value_parser_1.parseRowValues)(context.columns, context.rows));
272
+ this._queue.next();
273
+ }
274
+ break;
275
+ case constants_1.ResponseTypes.ErrorResponse:
276
+ {
277
+ const message = reader.readErrorResponse();
278
+ if (this._logLevel === 'error' || this._logLevel === 'notice' || this._logLevel === 'query') {
279
+ console.log(`\nError: ${message}\n`);
280
+ }
281
+ const error = new Error(message);
282
+ this._queue.rejectAllNext(error);
283
+ }
284
+ break;
285
+ case constants_1.ResponseTypes.ReadyForQuery:
286
+ {
287
+ this._isFlushing = false;
288
+ reader.readReadyForQuery();
289
+ }
290
+ break;
291
+ case constants_1.ResponseTypes.Notice:
292
+ {
293
+ const message = reader.readErrorResponse();
294
+ if (this._logLevel === 'notice' || this._logLevel === 'query') {
295
+ console.log(`\nError: ${message}\n`);
296
+ }
297
+ }
298
+ break;
299
+ default: console.warn('Undeclared response type: ', type);
300
+ }
301
+ }
302
+ _handleError(error) {
303
+ this._isAlive = false;
304
+ this._queue.rejectAllNext(error);
305
+ this._socket.destroy();
306
+ }
307
+ /**
308
+ * Checks if the connection is still alive and usable.
309
+ * Returns `false` if the socket is destroyed or connection is dead.
310
+ */
311
+ get isAlive() {
312
+ return this._isAlive && !this._socket.isDestroyed;
313
+ }
314
+ /**
315
+ * Closes the connection immediately.
316
+ * All pending queries will be rejected with an error.
317
+ * The connection cannot be used after this call.
318
+ *
319
+ * @example
320
+ * ```ts
321
+ * conn.close()
322
+ * ```
323
+ */
324
+ close() {
325
+ this._isAlive = false;
326
+ this._socket.destroy();
327
+ }
97
328
  }
98
329
  exports.Connection = Connection;
package/dist/index.d.ts CHANGED
@@ -1,15 +1,8 @@
1
1
  import { Pool as PgtxPool } from "./pool";
2
- import { EmptyClause } from "./clauses/empty.clause";
3
- import { WhereClause } from "./clauses/where.clause";
4
- import { InsertClause } from "./clauses/insert.clause";
5
- import { UpdateClause } from "./clauses/update.clause";
6
- import { ExcludeUpdateClause } from "./clauses/exclude.clause";
7
- import { IdentifierClause } from "./clauses/iden.caluse";
8
- import { LiteralClause } from "./clauses/literal.clause";
9
- import { FragmentClause } from "./clauses/fragment.clause";
10
- import { ArrayClause } from "./clauses/array.clause";
2
+ import { IdentifierClause, InsertClause, ArrayClause, UpdateClause, ExcludeUpdateClause, WhereClause, LiteralClause, FragmentClause } from './clauses';
11
3
  import { Connection } from "./connection";
12
4
  import { Transaction } from "./transaction";
5
+ import { setTypeParser } from "./utils/value-parser";
13
6
  /**
14
7
  * Core SQL tagging utility for Pgtx.
15
8
  * Provides type-safe helpers for building dynamic queries with recursive support.
@@ -53,7 +46,7 @@ export declare const sql: {
53
46
  * sql`SELECT * FROM users ${filters.length ? sql.fragment`WHERE ...` : sql.empty}`
54
47
  * // Result: SELECT * FROM users
55
48
  */
56
- empty: EmptyClause;
49
+ empty: import("./clauses").EmptyClause;
57
50
  /**
58
51
  * Generates a list of conditions for a WHERE clause from a JavaScript object.
59
52
  * Works similarly to sql.update, but uses ' AND ' as a separator instead of a comma.
@@ -127,4 +120,5 @@ export declare const sql: {
127
120
  export { Connection as Connection };
128
121
  export { Transaction as Transaction };
129
122
  export { PgtxPool as Pool };
123
+ export { setTypeParser as setTypeParser };
130
124
  //# 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,EAAe,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C;;;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,EAAC,UAAU,IAAI,UAAU,EAAC,CAAA;AAEjC,OAAO,EAAC,WAAW,IAAI,WAAW,EAAC,CAAA;AAEnC,OAAO,EAAE,QAAQ,IAAI,IAAI,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"}