@coffre/db 0.14.4 → 0.15.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 +2 -1
- package/dist/change-records-_fKvahvs.js +55 -0
- package/dist/connect.d.ts +22 -10
- package/dist/connect.js +74 -12
- package/dist/database-CVteLhp1.d.ts +410 -0
- package/dist/{database-BadNeeN-.js → database-D55ENPX-.js} +46 -25
- package/dist/dialect.d.ts +4 -3
- package/dist/dialect.js +14 -5
- package/dist/grants.d.ts +4 -4
- package/dist/grants.js +4 -4
- package/dist/hyperdrive.d.ts +48 -23
- package/dist/hyperdrive.js +168 -91
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/log-DaD7fyUq.d.ts +722 -0
- package/dist/log.d.ts +2 -596
- package/dist/log.js +224 -10
- package/dist/migrate.d.ts +43 -3
- package/dist/migrate.js +149 -29
- package/dist/migrations/postgres/0021_prune_vault_releases.sql +19 -0
- package/dist/migrations/postgres/0022_log_015.sql +672 -0
- package/dist/migrations/postgres/0023_change_path.sql +153 -0
- package/dist/migrations/postgres/0024_use_015.sql +556 -0
- package/dist/migrations/postgres/0025_app_change.sql +221 -0
- package/dist/migrations/postgres/0026_issue_lock.sql +106 -0
- package/dist/migrations/postgres/0027_retention.sql +118 -0
- package/dist/migrations/postgres/0028_use_stalls.sql +594 -0
- package/dist/migrations/postgres/0029_use_objects.sql +887 -0
- package/dist/migrations/postgres/0030_unarchive_restores.sql +259 -0
- package/dist/migrations/postgres/0031_app_read.sql +65 -0
- package/dist/migrations/postgres/0032_secrets_live_index.sql +4 -0
- package/dist/migrations/postgres/0033_use_generations.sql +582 -0
- package/dist/migrations/postgres/0034_vault_credentials.sql +153 -0
- package/dist/migrations/postgres/0035_first_days.sql +3 -0
- package/dist/migrations/postgres/0036_vault_issues.sql +91 -0
- package/dist/migrations/postgres/0037_vault_bindings.sql +271 -0
- package/dist/migrations/postgres/0040_binding_origin.sql +159 -0
- package/dist/migrations/postgres/0041_cutover.sql +132 -0
- package/dist/migrations/postgres/0042_sign_in_index.sql +7 -0
- package/dist/migrations/postgres/0043_issue_revoke_index.sql +4 -0
- package/dist/migrations/postgres/0044_use_by_name.sql +705 -0
- package/dist/migrations/postgres/0045_decide_first.sql +878 -0
- package/dist/migrations/postgres/0046_way_in_uses.sql +1149 -0
- package/dist/migrations/postgres/0047_cutover_finish.sql +268 -0
- package/dist/migrations/postgres/0048_use_batch.sql +59 -0
- package/dist/migrations/postgres/0049_reads.sql +171 -0
- package/dist/migrations/postgres/meta/0021_snapshot.json +3334 -0
- package/dist/migrations/postgres/meta/0022_snapshot.json +3831 -0
- package/dist/migrations/postgres/meta/0023_snapshot.json +3831 -0
- package/dist/migrations/postgres/meta/0024_snapshot.json +3831 -0
- package/dist/migrations/postgres/meta/0025_snapshot.json +3831 -0
- package/dist/migrations/postgres/meta/0026_snapshot.json +3831 -0
- package/dist/migrations/postgres/meta/0027_snapshot.json +3853 -0
- package/dist/migrations/postgres/meta/0028_snapshot.json +3853 -0
- package/dist/migrations/postgres/meta/0029_snapshot.json +3859 -0
- package/dist/migrations/postgres/meta/0030_snapshot.json +3859 -0
- package/dist/migrations/postgres/meta/0031_snapshot.json +3859 -0
- package/dist/migrations/postgres/meta/0032_snapshot.json +3875 -0
- package/dist/migrations/postgres/meta/0033_snapshot.json +3875 -0
- package/dist/migrations/postgres/meta/0034_snapshot.json +3875 -0
- package/dist/migrations/postgres/meta/0035_snapshot.json +3875 -0
- package/dist/migrations/postgres/meta/0036_snapshot.json +3875 -0
- package/dist/migrations/postgres/meta/0037_snapshot.json +3875 -0
- package/dist/migrations/postgres/meta/0038_snapshot.json +3881 -0
- package/dist/migrations/postgres/meta/0039_snapshot.json +3881 -0
- package/dist/migrations/postgres/meta/0040_snapshot.json +3931 -0
- package/dist/migrations/postgres/meta/0041_snapshot.json +3981 -0
- package/dist/migrations/postgres/meta/0042_snapshot.json +3981 -0
- package/dist/migrations/postgres/meta/0043_snapshot.json +3981 -0
- package/dist/migrations/postgres/meta/0044_snapshot.json +3991 -0
- package/dist/migrations/postgres/meta/0045_snapshot.json +3997 -0
- package/dist/migrations/postgres/meta/0046_snapshot.json +3997 -0
- package/dist/migrations/postgres/meta/0047_snapshot.json +4234 -0
- package/dist/migrations/postgres/meta/_journal.json +189 -0
- package/dist/migrations/sqlite/0018_log_015.sql +84 -0
- package/dist/migrations/sqlite/0019_retention.sql +2 -0
- package/dist/migrations/sqlite/0020_ended_references.sql +1 -0
- package/dist/migrations/sqlite/0021_secrets_live_index.sql +1 -0
- package/dist/migrations/sqlite/0022_binding_origin.sql +1 -0
- package/dist/migrations/sqlite/0023_sign_in_index.sql +2 -0
- package/dist/migrations/sqlite/0024_issue_revoke_index.sql +2 -0
- package/dist/migrations/sqlite/0025_way_in_uses.sql +42 -0
- package/dist/migrations/sqlite/0026_cutover_finish.sql +11 -0
- package/dist/migrations/sqlite/0027_reads.sql +38 -0
- package/dist/migrations/sqlite/meta/0018_snapshot.json +3529 -0
- package/dist/migrations/sqlite/meta/0019_snapshot.json +3538 -0
- package/dist/migrations/sqlite/meta/0020_snapshot.json +3545 -0
- package/dist/migrations/sqlite/meta/0021_snapshot.json +3553 -0
- package/dist/migrations/sqlite/meta/0022_snapshot.json +3560 -0
- package/dist/migrations/sqlite/meta/0023_snapshot.json +3579 -0
- package/dist/migrations/sqlite/meta/0024_snapshot.json +3598 -0
- package/dist/migrations/sqlite/meta/0025_snapshot.json +3609 -0
- package/dist/migrations/sqlite/meta/0026_snapshot.json +3616 -0
- package/dist/migrations/sqlite/meta/0027_snapshot.json +3834 -0
- package/dist/migrations/sqlite/meta/_journal.json +70 -0
- package/dist/platform-Y3gICK6n.d.ts +58 -0
- package/dist/portable.d.ts +2 -2
- package/dist/postgres-BI3xTnyw.js +1718 -0
- package/dist/postgres-platform-node.d.ts +14 -0
- package/dist/postgres-platform-node.js +21 -0
- package/dist/postgres-platform-workerd.d.ts +61 -0
- package/dist/postgres-platform-workerd.js +163 -0
- package/dist/{schema-z8GcN_Xu.d.ts → schema-DgoPqqsI.d.ts} +1329 -9
- package/dist/schema-sqlite.d.ts +1308 -8
- package/dist/schema-sqlite.js +156 -20
- package/dist/schema-version.d.ts +1 -1
- package/dist/schema-version.js +261 -2
- package/dist/schema.d.ts +2 -2
- package/dist/schema.js +229 -22
- package/package.json +13 -5
- package/src/active-subject.ts +9 -0
- package/src/change-records.ts +38 -0
- package/src/connect.ts +67 -13
- package/src/database.ts +67 -51
- package/src/dialect.ts +17 -8
- package/src/grants.ts +3 -3
- package/src/hyperdrive.ts +181 -104
- package/src/log.ts +244 -11
- package/src/migrate.ts +157 -30
- package/src/migrations/postgres/0021_prune_vault_releases.sql +19 -0
- package/src/migrations/postgres/0022_log_015.sql +672 -0
- package/src/migrations/postgres/0023_change_path.sql +153 -0
- package/src/migrations/postgres/0024_use_015.sql +556 -0
- package/src/migrations/postgres/0025_app_change.sql +221 -0
- package/src/migrations/postgres/0026_issue_lock.sql +106 -0
- package/src/migrations/postgres/0027_retention.sql +118 -0
- package/src/migrations/postgres/0028_use_stalls.sql +594 -0
- package/src/migrations/postgres/0029_use_objects.sql +887 -0
- package/src/migrations/postgres/0030_unarchive_restores.sql +259 -0
- package/src/migrations/postgres/0031_app_read.sql +65 -0
- package/src/migrations/postgres/0032_secrets_live_index.sql +4 -0
- package/src/migrations/postgres/0033_use_generations.sql +582 -0
- package/src/migrations/postgres/0034_vault_credentials.sql +153 -0
- package/src/migrations/postgres/0035_first_days.sql +3 -0
- package/src/migrations/postgres/0036_vault_issues.sql +91 -0
- package/src/migrations/postgres/0037_vault_bindings.sql +271 -0
- package/src/migrations/postgres/0040_binding_origin.sql +159 -0
- package/src/migrations/postgres/0041_cutover.sql +132 -0
- package/src/migrations/postgres/0042_sign_in_index.sql +7 -0
- package/src/migrations/postgres/0043_issue_revoke_index.sql +4 -0
- package/src/migrations/postgres/0044_use_by_name.sql +705 -0
- package/src/migrations/postgres/0045_decide_first.sql +878 -0
- package/src/migrations/postgres/0046_way_in_uses.sql +1149 -0
- package/src/migrations/postgres/0047_cutover_finish.sql +268 -0
- package/src/migrations/postgres/0048_use_batch.sql +59 -0
- package/src/migrations/postgres/0049_reads.sql +171 -0
- package/src/migrations/postgres/meta/0021_snapshot.json +3334 -0
- package/src/migrations/postgres/meta/0022_snapshot.json +3831 -0
- package/src/migrations/postgres/meta/0023_snapshot.json +3831 -0
- package/src/migrations/postgres/meta/0024_snapshot.json +3831 -0
- package/src/migrations/postgres/meta/0025_snapshot.json +3831 -0
- package/src/migrations/postgres/meta/0026_snapshot.json +3831 -0
- package/src/migrations/postgres/meta/0027_snapshot.json +3853 -0
- package/src/migrations/postgres/meta/0028_snapshot.json +3853 -0
- package/src/migrations/postgres/meta/0029_snapshot.json +3859 -0
- package/src/migrations/postgres/meta/0030_snapshot.json +3859 -0
- package/src/migrations/postgres/meta/0031_snapshot.json +3859 -0
- package/src/migrations/postgres/meta/0032_snapshot.json +3875 -0
- package/src/migrations/postgres/meta/0033_snapshot.json +3875 -0
- package/src/migrations/postgres/meta/0034_snapshot.json +3875 -0
- package/src/migrations/postgres/meta/0035_snapshot.json +3875 -0
- package/src/migrations/postgres/meta/0036_snapshot.json +3875 -0
- package/src/migrations/postgres/meta/0037_snapshot.json +3875 -0
- package/src/migrations/postgres/meta/0038_snapshot.json +3881 -0
- package/src/migrations/postgres/meta/0039_snapshot.json +3881 -0
- package/src/migrations/postgres/meta/0040_snapshot.json +3931 -0
- package/src/migrations/postgres/meta/0041_snapshot.json +3981 -0
- package/src/migrations/postgres/meta/0042_snapshot.json +3981 -0
- package/src/migrations/postgres/meta/0043_snapshot.json +3981 -0
- package/src/migrations/postgres/meta/0044_snapshot.json +3991 -0
- package/src/migrations/postgres/meta/0045_snapshot.json +3997 -0
- package/src/migrations/postgres/meta/0046_snapshot.json +3997 -0
- package/src/migrations/postgres/meta/0047_snapshot.json +4234 -0
- package/src/migrations/postgres/meta/_journal.json +189 -0
- package/src/migrations/sqlite/0018_log_015.sql +84 -0
- package/src/migrations/sqlite/0019_retention.sql +2 -0
- package/src/migrations/sqlite/0020_ended_references.sql +1 -0
- package/src/migrations/sqlite/0021_secrets_live_index.sql +1 -0
- package/src/migrations/sqlite/0022_binding_origin.sql +1 -0
- package/src/migrations/sqlite/0023_sign_in_index.sql +2 -0
- package/src/migrations/sqlite/0024_issue_revoke_index.sql +2 -0
- package/src/migrations/sqlite/0025_way_in_uses.sql +42 -0
- package/src/migrations/sqlite/0026_cutover_finish.sql +11 -0
- package/src/migrations/sqlite/0027_reads.sql +38 -0
- package/src/migrations/sqlite/meta/0018_snapshot.json +3529 -0
- package/src/migrations/sqlite/meta/0019_snapshot.json +3538 -0
- package/src/migrations/sqlite/meta/0020_snapshot.json +3545 -0
- package/src/migrations/sqlite/meta/0021_snapshot.json +3553 -0
- package/src/migrations/sqlite/meta/0022_snapshot.json +3560 -0
- package/src/migrations/sqlite/meta/0023_snapshot.json +3579 -0
- package/src/migrations/sqlite/meta/0024_snapshot.json +3598 -0
- package/src/migrations/sqlite/meta/0025_snapshot.json +3609 -0
- package/src/migrations/sqlite/meta/0026_snapshot.json +3616 -0
- package/src/migrations/sqlite/meta/0027_snapshot.json +3834 -0
- package/src/migrations/sqlite/meta/_journal.json +70 -0
- package/src/postgres-js/README.md +183 -0
- package/src/postgres-js/UNLICENSE +24 -0
- package/src/postgres-js/bytes.ts +89 -0
- package/src/postgres-js/connection.ts +882 -0
- package/src/postgres-js/errors.ts +84 -0
- package/src/postgres-js/index.ts +516 -0
- package/src/postgres-js/platform.node.ts +29 -0
- package/src/postgres-js/platform.ts +60 -0
- package/src/postgres-js/platform.workerd.ts +225 -0
- package/src/postgres-js/query.ts +123 -0
- package/src/postgres-js/queue.ts +42 -0
- package/src/postgres-js/result.ts +46 -0
- package/src/postgres-js/types.ts +52 -0
- package/src/postgres.ts +375 -26
- package/src/schema-version.ts +2 -2
- package/src/schema.sqlite.ts +200 -9
- package/src/schema.ts +294 -8
- package/src/verify-runtime.ts +18 -29
- package/LICENSE +0 -21
- package/dist/database-CFhTnnzM.d.ts +0 -73
- package/dist/postgres-Bmar8y0q.js +0 -29
- package/dist/relations-BfR3JjT1.js +0 -38
- package/src/relations.ts +0 -40
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/** What Postgres said in an ErrorResponse or a NoticeResponse, each field by its name. */
|
|
2
|
+
export type Notice = Partial<Record<(typeof FIELDS)[keyof typeof FIELDS], string>>;
|
|
3
|
+
|
|
4
|
+
/** An ErrorResponse's fields, by their code. `data type_name` is upstream's spelling, kept. */
|
|
5
|
+
export const FIELDS = {
|
|
6
|
+
83: 'severity_local', // S
|
|
7
|
+
86: 'severity', // V
|
|
8
|
+
67: 'code', // C
|
|
9
|
+
77: 'message', // M
|
|
10
|
+
68: 'detail', // D
|
|
11
|
+
72: 'hint', // H
|
|
12
|
+
80: 'position', // P
|
|
13
|
+
112: 'internal_position', // p
|
|
14
|
+
113: 'internal_query', // q
|
|
15
|
+
87: 'where', // W
|
|
16
|
+
115: 'schema_name', // s
|
|
17
|
+
116: 'table_name', // t
|
|
18
|
+
99: 'column_name', // c
|
|
19
|
+
100: 'data type_name', // d
|
|
20
|
+
110: 'constraint_name', // n
|
|
21
|
+
70: 'file', // F
|
|
22
|
+
76: 'line', // L
|
|
23
|
+
82: 'routine', // R
|
|
24
|
+
} as const;
|
|
25
|
+
|
|
26
|
+
/** An error Postgres answered, with its fields: `code` (the SQLSTATE), `detail`, `constraint_name`… */
|
|
27
|
+
export class PostgresError extends Error {
|
|
28
|
+
declare readonly severity_local: string;
|
|
29
|
+
declare readonly severity: string;
|
|
30
|
+
declare readonly code: string;
|
|
31
|
+
declare readonly detail?: string;
|
|
32
|
+
declare readonly hint?: string;
|
|
33
|
+
declare readonly position?: string;
|
|
34
|
+
declare readonly internal_position?: string;
|
|
35
|
+
declare readonly internal_query?: string;
|
|
36
|
+
declare readonly where?: string;
|
|
37
|
+
declare readonly schema_name?: string;
|
|
38
|
+
declare readonly table_name?: string;
|
|
39
|
+
declare readonly column_name?: string;
|
|
40
|
+
declare readonly constraint_name?: string;
|
|
41
|
+
declare readonly file?: string;
|
|
42
|
+
declare readonly line?: string;
|
|
43
|
+
declare readonly routine?: string;
|
|
44
|
+
|
|
45
|
+
constructor(fields: Notice) {
|
|
46
|
+
super(fields.message);
|
|
47
|
+
this.name = this.constructor.name;
|
|
48
|
+
Object.assign(this, fields);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** An error of the driver's own, with its `code`. */
|
|
53
|
+
export type DriverError = Error & { code: string };
|
|
54
|
+
|
|
55
|
+
/** The connection lost, never made or ended (`CONNECTION_CLOSED`, `CONNECT_TIMEOUT`…), naming where it was to. */
|
|
56
|
+
export function connection(code: string, options: { host: string; port: number; path?: string | undefined }): DriverError {
|
|
57
|
+
const where = options.path ?? options.host + ':' + options.port;
|
|
58
|
+
const error = Object.assign(new Error('write ' + code + ' ' + where), {
|
|
59
|
+
code,
|
|
60
|
+
errno: code,
|
|
61
|
+
address: options.path ?? options.host,
|
|
62
|
+
...(options.path ? {} : { port: options.port }),
|
|
63
|
+
});
|
|
64
|
+
Error.captureStackTrace(error, connection);
|
|
65
|
+
return error;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function postgres(fields: Notice): PostgresError {
|
|
69
|
+
const error = new PostgresError(fields);
|
|
70
|
+
Error.captureStackTrace(error, postgres);
|
|
71
|
+
return error;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function generic(code: string, message: string): DriverError {
|
|
75
|
+
const error = Object.assign(new Error(code + ': ' + message), { code });
|
|
76
|
+
Error.captureStackTrace(error, generic);
|
|
77
|
+
return error;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function notSupported(name: string): DriverError {
|
|
81
|
+
const error = Object.assign(new Error(name + ' (B) is not supported'), { code: 'MESSAGE_NOT_SUPPORTED', name });
|
|
82
|
+
Error.captureStackTrace(error, notSupported);
|
|
83
|
+
return error;
|
|
84
|
+
}
|
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
// postgres.js, coffre's own: the pool, its statements and transactions. See
|
|
2
|
+
// README.md beside this file for where it comes from and how it differs.
|
|
3
|
+
import { createConnection, type Connection, type Reservation, type Reserved } from './connection.ts';
|
|
4
|
+
import * as Errors from './errors.ts';
|
|
5
|
+
import { PostgresError, type Notice } from './errors.ts';
|
|
6
|
+
import type { Socket, TlsOptions } from './platform.ts';
|
|
7
|
+
import { Query } from './query.ts';
|
|
8
|
+
import { Queue } from './queue.ts';
|
|
9
|
+
import type { Column } from './result.ts';
|
|
10
|
+
import { defaultTypes, type Parsers, type Serializers } from './types.ts';
|
|
11
|
+
|
|
12
|
+
export { PostgresError, Query };
|
|
13
|
+
export type { Column, Notice, Socket };
|
|
14
|
+
|
|
15
|
+
/** What a client is opened with. Nothing is read from the environment: what is not given is the default named. */
|
|
16
|
+
export interface Options {
|
|
17
|
+
/** Default `localhost`. */
|
|
18
|
+
host?: string;
|
|
19
|
+
/** Default 5432. */
|
|
20
|
+
port?: number;
|
|
21
|
+
/** A Unix socket's path, in place of `host` and `port`. */
|
|
22
|
+
path?: string | undefined;
|
|
23
|
+
/** Default the server's: the user's name. */
|
|
24
|
+
database?: string | undefined;
|
|
25
|
+
user?: string;
|
|
26
|
+
pass?: string;
|
|
27
|
+
/** TLS, with Node's options for it; default none. */
|
|
28
|
+
ssl?: false | TlsOptions;
|
|
29
|
+
/** `direct`: TLS from the first byte, with no SSLRequest (Postgres 17). */
|
|
30
|
+
sslnegotiation?: 'direct' | null | undefined;
|
|
31
|
+
/** Connections at most. Default 10. */
|
|
32
|
+
max?: number;
|
|
33
|
+
/** Statements sent on a connection before the first has answered. Default 100. */
|
|
34
|
+
max_pipeline?: number;
|
|
35
|
+
/** Seconds a connection may be idle before it closes. Default none. */
|
|
36
|
+
idle_timeout?: number | null | undefined;
|
|
37
|
+
/** Seconds a connection may take to open. Default 30. */
|
|
38
|
+
connect_timeout?: number | null | undefined;
|
|
39
|
+
/** Seconds a connection may live, or a function that says how many. Default 30 to 60 minutes. */
|
|
40
|
+
max_lifetime?: number | null | (() => number);
|
|
41
|
+
/** Seconds before a closed connection is opened again, by how many in a row closed with an error. */
|
|
42
|
+
backoff?: number | ((retries: number) => number);
|
|
43
|
+
/** Seconds of TCP keep-alive. Default 60. */
|
|
44
|
+
keep_alive?: number | null;
|
|
45
|
+
/** True to keep where each statement was made, for its error's stack, and its text on its error; or a function told each statement. */
|
|
46
|
+
debug?: boolean | ((connection: number, query: string, parameters: unknown[], types: number[]) => void);
|
|
47
|
+
/** Run-time parameters sent as each connection starts. Default `application_name` postgres.js. */
|
|
48
|
+
connection?: Record<string, string | number | boolean | undefined>;
|
|
49
|
+
/** Each notice Postgres sends. Default: logged. */
|
|
50
|
+
onnotice?: (notice: Notice) => void;
|
|
51
|
+
/** Each connection's close, by its id. */
|
|
52
|
+
onclose?: (connectionId: number) => void;
|
|
53
|
+
/** The stream to speak Postgres on, made for each connection, rather than a TCP socket to `host`. */
|
|
54
|
+
socket?: () => Socket | Promise<Socket>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** A client's options, every one settled, and what it keeps beside them: `parsers` and `serializers`, which it may change. */
|
|
58
|
+
export interface Settings {
|
|
59
|
+
host: string;
|
|
60
|
+
port: number;
|
|
61
|
+
path: string | undefined;
|
|
62
|
+
database: string | undefined;
|
|
63
|
+
user: string;
|
|
64
|
+
pass: string;
|
|
65
|
+
ssl: false | TlsOptions;
|
|
66
|
+
sslnegotiation: 'direct' | null | undefined;
|
|
67
|
+
max: number;
|
|
68
|
+
max_pipeline: number;
|
|
69
|
+
idle_timeout: number | null | undefined;
|
|
70
|
+
connect_timeout: number | null | undefined;
|
|
71
|
+
max_lifetime: number | null | undefined | (() => number);
|
|
72
|
+
backoff: number | ((retries: number) => number);
|
|
73
|
+
keep_alive: number | null | undefined;
|
|
74
|
+
debug: boolean | ((connection: number, query: string, parameters: unknown[], types: number[]) => void);
|
|
75
|
+
connection: Record<string, string | number | boolean | undefined>;
|
|
76
|
+
onnotice: ((notice: Notice) => void) | undefined;
|
|
77
|
+
onclose: ((connectionId: number) => void) | undefined;
|
|
78
|
+
socket: (() => Socket | Promise<Socket>) | undefined;
|
|
79
|
+
parsers: Parsers;
|
|
80
|
+
serializers: Serializers;
|
|
81
|
+
/** The server's run-time parameters, as its ParameterStatus messages said them. */
|
|
82
|
+
parameters: Record<string, string>;
|
|
83
|
+
/** What the pool's connections share: how many in a row closed with an error, which backs off reopening. */
|
|
84
|
+
shared: { retries: number };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** A row, by its columns' names: whatever a hand-written statement selects, as upstream types it. */
|
|
88
|
+
export type Row = Record<string, any>;
|
|
89
|
+
|
|
90
|
+
/** What a statement's CommandComplete said, beside its rows. */
|
|
91
|
+
export interface ResultMeta {
|
|
92
|
+
/** Rows inserted, updated, deleted or returned; null for a command that counts none. */
|
|
93
|
+
readonly count: number | null;
|
|
94
|
+
readonly command: string;
|
|
95
|
+
/** The backend that ran it: its process id and secret key, what a CancelRequest names. */
|
|
96
|
+
readonly state: { readonly pid: number; readonly secret: number };
|
|
97
|
+
readonly columns: Column[] | null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** A statement's rows, with what Postgres said of it. */
|
|
101
|
+
export type RowList<T extends readonly unknown[]> = T & ResultMeta;
|
|
102
|
+
|
|
103
|
+
/** A statement, sent once awaited or `execute`d. */
|
|
104
|
+
export type PendingQuery<T extends readonly unknown[]> = Query<RowList<T>>;
|
|
105
|
+
|
|
106
|
+
/** A transaction's statements, and its savepoints. */
|
|
107
|
+
export interface TransactionSql {
|
|
108
|
+
unsafe<T extends readonly unknown[] = Row[]>(query: string, parameters?: unknown[], options?: { simple?: boolean }): PendingQuery<T>;
|
|
109
|
+
/** A savepoint, rolled back to if `fn` fails. */
|
|
110
|
+
savepoint<T>(fn: (sql: TransactionSql) => T | Promise<T>): Promise<Settled<T>>;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** A connection held until `release()`. */
|
|
114
|
+
export interface ReservedSql {
|
|
115
|
+
unsafe<T extends readonly unknown[] = Row[]>(query: string, parameters?: unknown[], options?: { simple?: boolean }): PendingQuery<T>;
|
|
116
|
+
release(): void;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** A pool of connections to Postgres. */
|
|
120
|
+
export interface Sql {
|
|
121
|
+
/**
|
|
122
|
+
* A statement: with `parameters`, the extended protocol, one round trip;
|
|
123
|
+
* without, the simple one, unless `simple: false`.
|
|
124
|
+
*/
|
|
125
|
+
unsafe<T extends readonly unknown[] = Row[]>(query: string, parameters?: unknown[], options?: { simple?: boolean }): PendingQuery<T>;
|
|
126
|
+
/**
|
|
127
|
+
* A transaction, on a connection held for it: BEGIN (`options` after it,
|
|
128
|
+
* `isolation level serializable` and the like), `fn`, then COMMIT, or
|
|
129
|
+
* ROLLBACK if `fn` fails, or a statement of it did that `fn` did not wait for.
|
|
130
|
+
*/
|
|
131
|
+
begin<T>(fn: (sql: TransactionSql) => T | Promise<T>): Promise<Settled<T>>;
|
|
132
|
+
begin<T>(options: string, fn: (sql: TransactionSql) => T | Promise<T>): Promise<Settled<T>>;
|
|
133
|
+
/**
|
|
134
|
+
* Coffre: statements sent together under one Sync, a round trip in all
|
|
135
|
+
* through a pooler that sends a client's Syncs on one at a time, as
|
|
136
|
+
* Hyperdrive does; answered once the last has, each with its own rows,
|
|
137
|
+
* as an array (`arrays` for a statement's rows as arrays of values). They
|
|
138
|
+
* run as one implicit transaction: if one fails, Postgres skips those
|
|
139
|
+
* after it, and the group fails, with that error. Plain reads alone
|
|
140
|
+
* (`select …`), on the pool alone, never in a transaction, whose
|
|
141
|
+
* statements each go under its own; each with the extended protocol, as
|
|
142
|
+
* a statement with parameters.
|
|
143
|
+
*/
|
|
144
|
+
group(statements: readonly { query: string; parameters?: unknown[]; arrays?: boolean }[]): PendingQuery<RowList<Row[]>[]>;
|
|
145
|
+
/** A connection of the pool, held for the caller until it releases it. */
|
|
146
|
+
reserve(): Promise<ReservedSql>;
|
|
147
|
+
/** Close every connection once what it was sent has answered; after `timeout` seconds, at once. */
|
|
148
|
+
end(options?: { timeout?: number | null }): Promise<void>;
|
|
149
|
+
readonly options: Settings;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** What a transaction's work answers: an array of promises, each awaited. */
|
|
153
|
+
export type Settled<T> = T extends readonly unknown[] ? { -readonly [K in keyof T]: Awaited<T[K]> } : Awaited<T>;
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Coffre: whether a statement is a plain read, what a group may hold (`group`): one that starts `select`. A `select`
|
|
157
|
+
* that writes through a function says so first, in a comment, which this does not take (`pruneSpentTokens` in
|
|
158
|
+
* @coffre/server). The extended protocol takes one statement a message, so none rides behind it.
|
|
159
|
+
*/
|
|
160
|
+
export function plainRead(text: string): boolean {
|
|
161
|
+
return /^\s*select\b/i.test(text);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** A pool of connections, opened as statements ask for them. */
|
|
165
|
+
export default function postgres(given: Options): Sql {
|
|
166
|
+
const options = settle(given);
|
|
167
|
+
|
|
168
|
+
let ending: Promise<void> | null = null;
|
|
169
|
+
|
|
170
|
+
const queries = new Queue<Query | Reservation>();
|
|
171
|
+
const connecting = new Queue<Connection>();
|
|
172
|
+
const reserved = new Queue<Connection>();
|
|
173
|
+
const closed = new Queue<Connection>();
|
|
174
|
+
const ended = new Queue<Connection>();
|
|
175
|
+
const open = new Queue<Connection>();
|
|
176
|
+
const busy = new Queue<Connection>();
|
|
177
|
+
const full = new Queue<Connection>();
|
|
178
|
+
|
|
179
|
+
const connections = Array.from({ length: options.max }, () => createConnection(options, closed, { onopen, onend, onclose }));
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
unsafe: unsafeOn(pooled),
|
|
183
|
+
group,
|
|
184
|
+
begin,
|
|
185
|
+
reserve,
|
|
186
|
+
end,
|
|
187
|
+
options,
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
function group(statements: readonly { query: string; parameters?: unknown[]; arrays?: boolean }[]): PendingQuery<RowList<Row[]>[]> {
|
|
191
|
+
if (statements.length === 0) throw Errors.generic('EMPTY_GROUP', 'A group needs a statement');
|
|
192
|
+
// Reads alone: a write grouped with a statement that fails would be rolled back with it, by an error not its own.
|
|
193
|
+
const write = statements.find((statement) => !plainRead(statement.query));
|
|
194
|
+
if (write !== undefined) throw Errors.generic('GROUP_NOT_READ', `A group holds plain reads alone, select …, not: ${write.query.slice(0, 80)}`);
|
|
195
|
+
const query = new Query(statements.map((statement) => statement.query).join(';\n'), [], pooled, { simple: false }, Boolean(options.debug));
|
|
196
|
+
query.group = statements.map(({ query: text, parameters = [], arrays = false }) => ({ text, args: parameters, arrays, parameters: [], statement: null }));
|
|
197
|
+
return query as never;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** `unsafe`, sending each statement through `handler`. */
|
|
201
|
+
function unsafeOn(handler: (query: Query) => void): Sql['unsafe'] {
|
|
202
|
+
return (text, args = [], { simple = args.length === 0 } = {}) => new Query(text, args, handler, { simple }, Boolean(options.debug)) as never;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** A connection of the pool's own, held until given back: `reserve`'s, and a transaction's. */
|
|
206
|
+
async function hold(): Promise<Connection> {
|
|
207
|
+
// Coffre: an ending client opens no connection again (onclose, below), not one to reserve either.
|
|
208
|
+
if (ending) throw Errors.connection('CONNECTION_ENDED', options);
|
|
209
|
+
// Coffre: the connection idle the shortest (queue.ts, pop), as for a query below.
|
|
210
|
+
const c = open.length
|
|
211
|
+
? open.pop()!
|
|
212
|
+
: await new Promise<Connection>((resolve, reject) => {
|
|
213
|
+
// Coffre: what a connection opens for, given it once open (connection.ts, ReadyForQuery), or what waits for
|
|
214
|
+
// one, never both: upstream did both, and a reservation taken from the queue to open a connection lost its.
|
|
215
|
+
const query: Reservation = { reserve: resolve, reject };
|
|
216
|
+
if (closed.length) connect(closed.shift()!, query);
|
|
217
|
+
else queries.push(query);
|
|
218
|
+
});
|
|
219
|
+
return move(c, reserved);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async function reserve(): Promise<ReservedSql> {
|
|
223
|
+
const queue = new Queue<Query>();
|
|
224
|
+
const c = await hold();
|
|
225
|
+
c.reserved = { next: () => (queue.length ? c.execute(queue.shift()!) : move(c, reserved)) };
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
unsafe: unsafeOn(onReserved),
|
|
229
|
+
// Coffre: given back once the client is ending, the connection closes, which end() waits for (Connection.release).
|
|
230
|
+
release: () => c.release(),
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
/** A statement on the connection held: sent, or queued until the connection can take more. */
|
|
234
|
+
function onReserved(q: Query): void {
|
|
235
|
+
if (c.queue === full) queue.push(q);
|
|
236
|
+
else if (!c.execute(q)) move(c, full);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
async function begin<T>(...args: [fn: (sql: TransactionSql) => T | Promise<T>] | [options: string, fn: (sql: TransactionSql) => T | Promise<T>]): Promise<Settled<T>> {
|
|
241
|
+
const [mode, fn] = args.length === 1 ? ['', args[0]] : args;
|
|
242
|
+
const queries = new Queue<Query>();
|
|
243
|
+
let savepoints = 0;
|
|
244
|
+
/** The transaction's first statement, which carries its BEGIN (`toBuffer`, connection.ts), once asked. */
|
|
245
|
+
let first: Query | null = null;
|
|
246
|
+
/** Every statement of the transaction's asked: it ends once each has answered. */
|
|
247
|
+
const asked = new Set<Query>();
|
|
248
|
+
/** Its end is decided, COMMIT or ROLLBACK: what its work asks now is refused, unsent. */
|
|
249
|
+
let decided = false;
|
|
250
|
+
/** Asked of it once its end was decided, and refused, before its COMMIT went: its work is not all done, it rolls back. */
|
|
251
|
+
let unfinished: Error | null = null;
|
|
252
|
+
/** Its COMMIT or ROLLBACK has gone: what is refused now changes nothing. */
|
|
253
|
+
let closing = false;
|
|
254
|
+
/** Why it failed, a savepoint's statements included: what a COMMIT Postgres answered with ROLLBACK says. */
|
|
255
|
+
let failure: unknown;
|
|
256
|
+
/**
|
|
257
|
+
* Why nothing more of the transaction is sent, which would run outside it: its BEGIN failed, its connection closed,
|
|
258
|
+
* or it has ended, and its connection is the pool's again (what its work left running may still ask).
|
|
259
|
+
*/
|
|
260
|
+
let stopped: Error | null = null;
|
|
261
|
+
const stop = (error: Error) => {
|
|
262
|
+
stopped ??= error;
|
|
263
|
+
while (queries.length) queries.shift()!.reject(stopped);
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
// Coffre: the transaction holds its connection from its first statement, BEGIN with it, until it has ended, and
|
|
267
|
+
// gives it back then. Upstream sent BEGIN alone, waited for it, and gave the connection back once Postgres said it
|
|
268
|
+
// was idle, which a BEGIN that failed would also have said, its transaction still running.
|
|
269
|
+
const c = await hold();
|
|
270
|
+
const mine: Reserved = {
|
|
271
|
+
next: () => {
|
|
272
|
+
if (first !== null && !first.began) {
|
|
273
|
+
stop(Errors.generic('TRANSACTION_NOT_BEGUN', 'The transaction did not begin, so its statements were not sent'));
|
|
274
|
+
return void move(c, reserved);
|
|
275
|
+
}
|
|
276
|
+
// What waited for the BEGIN goes at once, as many as the connection pipelines; upstream sent one an answer.
|
|
277
|
+
move(c, reserved);
|
|
278
|
+
while (queries.length) if (!c.execute(queries.shift()!)) return void move(c, full);
|
|
279
|
+
},
|
|
280
|
+
};
|
|
281
|
+
c.reserved = mine;
|
|
282
|
+
try {
|
|
283
|
+
return await Promise.race([
|
|
284
|
+
scope(fn),
|
|
285
|
+
new Promise<never>((_, reject) => (c.onclose = (error) => {
|
|
286
|
+
stop(error);
|
|
287
|
+
reject(error);
|
|
288
|
+
})),
|
|
289
|
+
]);
|
|
290
|
+
} finally {
|
|
291
|
+
stop(Errors.generic('TRANSACTION_ENDED', 'The transaction has ended, so the statement was not sent'));
|
|
292
|
+
c.onclose = null;
|
|
293
|
+
// Closed meanwhile, the connection is the pool's again already.
|
|
294
|
+
if (c.reserved === mine) c.release();
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
async function scope<R>(fn: (sql: TransactionSql) => R | Promise<R>, name?: string): Promise<Settled<R>> {
|
|
298
|
+
let uncaughtError: unknown;
|
|
299
|
+
let result: Settled<R>;
|
|
300
|
+
const sql: TransactionSql = {
|
|
301
|
+
unsafe: unsafeOn(inTransaction),
|
|
302
|
+
savepoint: (fn) => scope(fn, 's' + savepoints++),
|
|
303
|
+
};
|
|
304
|
+
// The transaction's own statements, sent as the simple protocol's, as a statement with no parameters is.
|
|
305
|
+
const own = (text: string) => sql.unsafe(text);
|
|
306
|
+
|
|
307
|
+
if (name) await own('savepoint ' + quote(name));
|
|
308
|
+
try {
|
|
309
|
+
result = await new Promise<Settled<R>>((resolve, reject) => {
|
|
310
|
+
const x = fn(sql);
|
|
311
|
+
Promise.resolve(Array.isArray(x) ? Promise.all(x) : x).then(resolve as (value: unknown) => void, reject);
|
|
312
|
+
});
|
|
313
|
+
if (!name) await settle();
|
|
314
|
+
if (uncaughtError) throw uncaughtError;
|
|
315
|
+
if (!name && unfinished) throw unfinished;
|
|
316
|
+
} catch (e) {
|
|
317
|
+
if (!name) await settle();
|
|
318
|
+
// A transaction that never began has nothing to roll back: none asked of it, or its BEGIN failed; nor one whose
|
|
319
|
+
// connection closed, which Postgres rolled back.
|
|
320
|
+
if (!stopped && (name || first?.began)) await (name ? own('rollback to ' + quote(name)) : last('rollback'));
|
|
321
|
+
// "Current transaction is aborted" says nothing of why: what failed first, unwaited, or in a savepoint, does.
|
|
322
|
+
if (aborted(e)) throw (uncaughtError !== undefined && !aborted(uncaughtError) ? uncaughtError : failure) ?? e;
|
|
323
|
+
throw e;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (!name && first !== null) {
|
|
327
|
+
// Coffre: Postgres answers the COMMIT of a transaction a statement of which failed, unwaited, in a savepoint
|
|
328
|
+
// its work had left, with ROLLBACK, not an error: the transaction fails. Upstream resolved.
|
|
329
|
+
const committed = (await last('commit')) as unknown as ResultMeta;
|
|
330
|
+
if (committed.command === 'ROLLBACK') {
|
|
331
|
+
throw failure ?? Errors.generic('TRANSACTION_ROLLED_BACK', 'Postgres rolled the transaction back at its COMMIT');
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return result;
|
|
336
|
+
|
|
337
|
+
/** A statement of the transaction's: one that fails and is not waited for fails the transaction all the same. */
|
|
338
|
+
function inTransaction(q: Query): void {
|
|
339
|
+
// Coffre: refused once the end is decided; before the COMMIT goes, the transaction's work is not all done, and it
|
|
340
|
+
// rolls back rather than commit part of it.
|
|
341
|
+
if (decided) {
|
|
342
|
+
const refused = stopped ?? Errors.generic('TRANSACTION_ENDED', 'The transaction has ended, so the statement was not sent');
|
|
343
|
+
if (!closing) unfinished ??= Errors.generic('TRANSACTION_UNFINISHED', 'The transaction\'s work asked a statement once it had settled, so the transaction rolled back');
|
|
344
|
+
q.catch(() => {});
|
|
345
|
+
return q.reject(refused);
|
|
346
|
+
}
|
|
347
|
+
q.catch((e: unknown) => {
|
|
348
|
+
uncaughtError ||= e;
|
|
349
|
+
// The latest error of Postgres's own: after a savepoint rolled back to, the one that aborted it again.
|
|
350
|
+
if (e instanceof PostgresError && !aborted(e)) failure = e;
|
|
351
|
+
else failure ??= e;
|
|
352
|
+
});
|
|
353
|
+
asked.add(q);
|
|
354
|
+
send(q);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Coffre: the end decided, nothing more of the work's is sent, and the transaction ends once what it asked has
|
|
360
|
+
* answered, with a ROLLBACK if any of it failed. Upstream sent the COMMIT at once: what the work asked after it ran
|
|
361
|
+
* outside the transaction, in autocommit, and a statement that failed unwaited left a COMMIT that Postgres
|
|
362
|
+
* answered with ROLLBACK, which upstream took for success.
|
|
363
|
+
*/
|
|
364
|
+
async function settle(): Promise<void> {
|
|
365
|
+
decided = true;
|
|
366
|
+
await Promise.allSettled(asked);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/** The transaction's COMMIT or ROLLBACK, past the door that settle closed. */
|
|
370
|
+
function last(text: string): Query {
|
|
371
|
+
closing = true;
|
|
372
|
+
return new Query(text, [], send, { simple: true }, Boolean(options.debug));
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/** `q` on the transaction's connection: sent, or queued until the connection can take more. */
|
|
376
|
+
function send(q: Query): void {
|
|
377
|
+
if (stopped) return q.reject(stopped);
|
|
378
|
+
if (first === null) {
|
|
379
|
+
first = q;
|
|
380
|
+
q.begin = 'begin ' + mode.replace(/[^a-z ]/gi, '');
|
|
381
|
+
}
|
|
382
|
+
// Nothing follows the BEGIN until Postgres has answered it, whatever execute says: a statement that failed to
|
|
383
|
+
// be written, its BEGIN with it, has the next wait all the same, to be refused.
|
|
384
|
+
if (c.queue === full) queries.push(q);
|
|
385
|
+
else if (!c.execute(q) || q === first) move(c, full);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function move(c: Connection, queue: Queue<Connection>): Connection {
|
|
390
|
+
c.queue.remove(c);
|
|
391
|
+
queue.push(c);
|
|
392
|
+
c.queue = queue;
|
|
393
|
+
if (queue === open) c.idleTimer.start();
|
|
394
|
+
else c.idleTimer.cancel();
|
|
395
|
+
return c;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/** A statement of the pool's: sent on an open connection, one opened for it, a busy one, or queued for the first free. */
|
|
399
|
+
function pooled(query: Query): void {
|
|
400
|
+
if (ending) return query.reject(Errors.connection('CONNECTION_ENDED', options));
|
|
401
|
+
|
|
402
|
+
// Coffre: the connection idle the shortest, as pg-pool takes it; upstream took the one idle the longest.
|
|
403
|
+
if (open.length) return void go(open.pop()!, query);
|
|
404
|
+
|
|
405
|
+
if (closed.length) return void connect(closed.shift()!, query);
|
|
406
|
+
|
|
407
|
+
if (busy.length) go(busy.shift()!, query);
|
|
408
|
+
else queries.push(query);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function go(c: Connection, query: Query): Connection {
|
|
412
|
+
return c.execute(query) ? move(c, busy) : move(c, full);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
async function end({ timeout = null }: { timeout?: number | null } = {}): Promise<void> {
|
|
416
|
+
if (ending) return ending;
|
|
417
|
+
|
|
418
|
+
// What is asked in this tick is still served, and waited for.
|
|
419
|
+
await null;
|
|
420
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
421
|
+
return (ending = Promise.race([
|
|
422
|
+
new Promise<void>((resolve) => {
|
|
423
|
+
if (timeout !== null) timer = setTimeout(() => void destroy(resolve), timeout * 1000);
|
|
424
|
+
}),
|
|
425
|
+
Promise.all(connections.map((c) => c.end())).then(() => undefined),
|
|
426
|
+
]).then(() => clearTimeout(timer)));
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
async function destroy(resolve: () => void): Promise<void> {
|
|
430
|
+
await Promise.all(connections.map((c) => c.terminate(true)));
|
|
431
|
+
while (queries.length) queries.shift()!.reject(Errors.connection('CONNECTION_DESTROYED', options));
|
|
432
|
+
resolve();
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function connect(c: Connection, query: Query | Reservation): Connection {
|
|
436
|
+
move(c, connecting);
|
|
437
|
+
c.connect(query);
|
|
438
|
+
return c;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
function onend(c: Connection): void {
|
|
442
|
+
move(c, ended);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function onopen(c: Connection): void {
|
|
446
|
+
if (queries.length === 0) return void move(c, open);
|
|
447
|
+
|
|
448
|
+
let max = Math.ceil(queries.length / (connecting.length + 1));
|
|
449
|
+
let ready = true;
|
|
450
|
+
|
|
451
|
+
while (ready && queries.length && max-- > 0) {
|
|
452
|
+
const query = queries.shift()!;
|
|
453
|
+
// Out of the pool's reach from now: none of its statements takes it before its holder resumes.
|
|
454
|
+
if ('reserve' in query) return void query.reserve(move(c, reserved));
|
|
455
|
+
|
|
456
|
+
ready = c.execute(query);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if (ready) move(c, busy);
|
|
460
|
+
else move(c, full);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function onclose(c: Connection, e: Error): void {
|
|
464
|
+
move(c, closed);
|
|
465
|
+
c.reserved = null;
|
|
466
|
+
if (c.onclose) {
|
|
467
|
+
c.onclose(e);
|
|
468
|
+
c.onclose = null;
|
|
469
|
+
}
|
|
470
|
+
options.onclose?.(c.id);
|
|
471
|
+
// Coffre: once the client is ending, what waits for a connection fails, as what it asks next does; upstream opened
|
|
472
|
+
// the connection again for it, after end() had answered, and no end() could close that one.
|
|
473
|
+
if (ending) {
|
|
474
|
+
while (queries.length) queries.shift()!.reject(Errors.connection('CONNECTION_ENDED', options));
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
if (queries.length) connect(c, queries.shift()!);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/** Postgres's "current transaction is aborted": a statement after one that failed. */
|
|
482
|
+
const aborted = (error: unknown) => error instanceof PostgresError && error.code === '25P02';
|
|
483
|
+
|
|
484
|
+
/** `options`, each one not given at its default. */
|
|
485
|
+
function settle(options: Options): Settings {
|
|
486
|
+
return {
|
|
487
|
+
host: options.host ?? 'localhost',
|
|
488
|
+
port: options.port ?? 5432,
|
|
489
|
+
path: options.path,
|
|
490
|
+
database: options.database,
|
|
491
|
+
user: options.user ?? '',
|
|
492
|
+
pass: options.pass ?? '',
|
|
493
|
+
ssl: options.ssl ?? false,
|
|
494
|
+
sslnegotiation: options.sslnegotiation,
|
|
495
|
+
max: options.max ?? 10,
|
|
496
|
+
max_pipeline: options.max_pipeline ?? 100,
|
|
497
|
+
idle_timeout: options.idle_timeout,
|
|
498
|
+
connect_timeout: 'connect_timeout' in options ? options.connect_timeout : 30,
|
|
499
|
+
max_lifetime: 'max_lifetime' in options ? options.max_lifetime : () => 60 * (30 + Math.random() * 30),
|
|
500
|
+
backoff: options.backoff ?? ((retries) => (0.5 + Math.random() / 2) * Math.min(3 ** retries / 100, 20)),
|
|
501
|
+
keep_alive: 'keep_alive' in options ? options.keep_alive : 60,
|
|
502
|
+
debug: options.debug ?? false,
|
|
503
|
+
connection: { application_name: 'postgres.js', ...options.connection },
|
|
504
|
+
onnotice: options.onnotice,
|
|
505
|
+
onclose: options.onclose,
|
|
506
|
+
socket: options.socket,
|
|
507
|
+
...defaultTypes(),
|
|
508
|
+
parameters: {},
|
|
509
|
+
shared: { retries: 0 },
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/** `name` as an identifier, quoted. */
|
|
514
|
+
function quote(name: string): string {
|
|
515
|
+
return '"' + name.replace(/"/g, '""').replace(/\./g, '"."') + '"';
|
|
516
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// The driver on Node: Node's sockets, TLS and crypto (platform.ts).
|
|
2
|
+
import crypto from 'node:crypto';
|
|
3
|
+
import net from 'node:net';
|
|
4
|
+
import { performance } from 'node:perf_hooks';
|
|
5
|
+
import tls from 'node:tls';
|
|
6
|
+
|
|
7
|
+
import type { Platform, Socket } from './platform.ts';
|
|
8
|
+
|
|
9
|
+
export const tcp: Platform['tcp'] = (to, connected) => ('path' in to ? net.connect(to.path, connected) : net.connect(to.port, to.host, connected)) as Socket;
|
|
10
|
+
|
|
11
|
+
export const startTls: Platform['startTls'] = (socket, options) => tls.connect({ ...options, socket: socket as net.Socket }) as Socket;
|
|
12
|
+
|
|
13
|
+
export const isIP: Platform['isIP'] = (host) => net.isIP(host);
|
|
14
|
+
|
|
15
|
+
export const setImmediate: Platform['setImmediate'] = (fn) => globalThis.setImmediate(fn);
|
|
16
|
+
export const clearImmediate: Platform['clearImmediate'] = (handle) => globalThis.clearImmediate(handle as NodeJS.Immediate | undefined);
|
|
17
|
+
|
|
18
|
+
export const now: Platform['now'] = () => performance.now();
|
|
19
|
+
|
|
20
|
+
export const randomBytes: Platform['randomBytes'] = (length) => crypto.randomBytes(length);
|
|
21
|
+
|
|
22
|
+
// Synchronous, as upstream salts on Node: a few milliseconds once per connection.
|
|
23
|
+
export const pbkdf2: Platform['pbkdf2'] = async (password, salt, iterations, length) => crypto.pbkdf2Sync(password, salt, iterations, length, 'sha256');
|
|
24
|
+
|
|
25
|
+
export const hmac: Platform['hmac'] = async (key, text) => crypto.createHmac('sha256', key).update(text).digest();
|
|
26
|
+
|
|
27
|
+
export const sha256: Platform['sha256'] = async (data) => crypto.createHash('sha256').update(data).digest();
|
|
28
|
+
|
|
29
|
+
export const md5: Platform['md5'] = async (data) => crypto.createHash('md5').update(data).digest('hex');
|