@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
package/src/database.ts
CHANGED
|
@@ -1,43 +1,31 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
getTableName,
|
|
7
|
-
is,
|
|
8
|
-
Table,
|
|
9
|
-
type ExtractTablesWithRelations,
|
|
10
|
-
type RelationalSchemaConfig,
|
|
11
|
-
} from 'drizzle-orm';
|
|
12
|
-
import { NodePgDatabase, NodePgDriver } from 'drizzle-orm/node-postgres';
|
|
13
|
-
import { PgDialect } from 'drizzle-orm/pg-core';
|
|
14
|
-
import type pg from 'pg';
|
|
3
|
+
import { getTableName, is, Table, type Assume, type RelationalSchemaConfig } from 'drizzle-orm';
|
|
4
|
+
import { PgDatabase, PgDialect, type PgQueryResultHKT, type PgTransactionConfig } from 'drizzle-orm/pg-core';
|
|
5
|
+
import { PostgresJsSession, PostgresJsTransaction } from 'drizzle-orm/postgres-js/session';
|
|
15
6
|
|
|
16
7
|
import { trackCommits } from './commits.ts';
|
|
8
|
+
import type { Row, RowList } from './postgres-js/index.ts';
|
|
9
|
+
import { textTypes, type PostgresClient } from './postgres.ts';
|
|
17
10
|
import * as schema from './schema.ts';
|
|
18
11
|
|
|
19
12
|
export { schema };
|
|
13
|
+
export type { PostgresClient } from './postgres.ts';
|
|
14
|
+
|
|
15
|
+
/** What a hand-written statement answers on Postgres: its rows, with `count` and `command`. */
|
|
16
|
+
export interface QueryResult extends PgQueryResultHKT {
|
|
17
|
+
type: RowList<Assume<this['row'], Row>[]>;
|
|
18
|
+
}
|
|
20
19
|
|
|
21
20
|
/**
|
|
22
21
|
* A database, typed as Postgres whichever it is; see portable.ts for why
|
|
23
22
|
* that holds. connect.ts opens one of each from a URL.
|
|
24
23
|
*/
|
|
25
|
-
export type Database =
|
|
24
|
+
export type Database = PgDatabase<QueryResult, typeof schema, NoRelations>;
|
|
26
25
|
export type Transaction = Parameters<Parameters<Database['transaction']>[0]>[0];
|
|
27
26
|
/** Anything a read can run on: the database, or an open transaction. */
|
|
28
27
|
export type Queryable = Database | Transaction;
|
|
29
28
|
|
|
30
|
-
/**
|
|
31
|
-
* What Drizzle needs from a connection pool. A `pg.Pool` fits, and so does the
|
|
32
|
-
* per-invocation Hyperdrive pool, whose class name ends in `Pool` for the
|
|
33
|
-
* same reason: that is how Drizzle knows to check out a client for a
|
|
34
|
-
* transaction rather than run BEGIN on a shared connection.
|
|
35
|
-
*/
|
|
36
|
-
export type PoolLike = pg.Pool | {
|
|
37
|
-
query: pg.Pool['query'];
|
|
38
|
-
connect: () => Promise<Pick<pg.PoolClient, 'query'> & { release: () => void }>;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
29
|
/**
|
|
42
30
|
* A query made on the database from inside one of its own transactions,
|
|
43
31
|
* rather than through the transaction. With one connection per call, as on
|
|
@@ -52,23 +40,28 @@ export class QueryOutsideTransaction extends Error {
|
|
|
52
40
|
}
|
|
53
41
|
}
|
|
54
42
|
|
|
55
|
-
/** The transaction a flow of work runs inside, with the
|
|
56
|
-
const inside = new AsyncLocalStorage<{
|
|
43
|
+
/** The transaction a flow of work runs inside, with the client it holds, while it is open. */
|
|
44
|
+
const inside = new AsyncLocalStorage<{ client: Client; open: boolean }>();
|
|
45
|
+
|
|
46
|
+
/** What Drizzle asks of the client: a statement, or a transaction, which postgres.js runs on a connection it holds. */
|
|
47
|
+
const ASKS = new Set<PropertyKey>(['unsafe', 'begin', 'reserve']);
|
|
57
48
|
|
|
58
49
|
/**
|
|
59
|
-
* `
|
|
50
|
+
* `client`, refusing a query or a transaction asked from inside a transaction
|
|
60
51
|
* it is already running. Work from any other flow, a concurrent request's,
|
|
61
52
|
* or what the transaction left running once its work was done, goes on as before.
|
|
62
53
|
*/
|
|
63
|
-
function guarded(
|
|
64
|
-
|
|
54
|
+
function guarded(client: Client): Client {
|
|
55
|
+
const refuse = (target: Client) => {
|
|
56
|
+
const open = inside.getStore();
|
|
57
|
+
if (open?.open === true && open.client === target) throw new QueryOutsideTransaction();
|
|
58
|
+
};
|
|
59
|
+
return new Proxy(client, {
|
|
65
60
|
get(target, key) {
|
|
66
61
|
const value: unknown = Reflect.get(target, key, target);
|
|
67
|
-
if (typeof value !== 'function') return value;
|
|
68
|
-
if (key !== 'query' && key !== 'connect') return value.bind(target);
|
|
62
|
+
if (typeof value !== 'function' || !ASKS.has(key)) return value;
|
|
69
63
|
return (...args: unknown[]) => {
|
|
70
|
-
|
|
71
|
-
if (open?.open === true && open.pool === target) throw new QueryOutsideTransaction();
|
|
64
|
+
refuse(target);
|
|
72
65
|
return (value as (...args: unknown[]) => unknown).apply(target, args);
|
|
73
66
|
};
|
|
74
67
|
},
|
|
@@ -84,34 +77,57 @@ function brokenRule(error: unknown): QueryOutsideTransaction | undefined {
|
|
|
84
77
|
}
|
|
85
78
|
|
|
86
79
|
/**
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
80
|
+
* Coffre runs no relational query (`db.query.*`), and Drizzle's work out of
|
|
81
|
+
* them, `extractTablesRelationalConfig` and a query builder per table for
|
|
82
|
+
* every database, was 4 to 11 ms of a cold isolate's CPU and 0.1 ms of each
|
|
83
|
+
* later call on Workers, where a database is made per call. So the database
|
|
84
|
+
* has none: `db.query` is empty, in type and at run time, and a `db.query.x`
|
|
85
|
+
* fails the typecheck. Only the tables stay, for `tablesOf`.
|
|
86
|
+
*/
|
|
87
|
+
type NoRelations = Record<never, never>;
|
|
88
|
+
type Relational = RelationalSchemaConfig<NoRelations>;
|
|
89
|
+
const relational: Relational = { fullSchema: schema, schema: {}, tableNamesMap: {} };
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Drizzle's session for postgres.js, but for a transaction's options, which
|
|
93
|
+
* go in its BEGIN, as node-postgres's session sends them, rather than in a
|
|
94
|
+
* SET TRANSACTION of their own: a round trip less for a snapshot read.
|
|
91
95
|
*/
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const { tables, tableNamesMap } = extractTablesRelationalConfig(schema, createTableRelationsHelpers);
|
|
98
|
-
// Drizzle's own construct passes the same untyped tables on as these.
|
|
99
|
-
relational = { fullSchema: schema, schema: tables as Relational['schema'], tableNamesMap };
|
|
96
|
+
class Session extends PostgresJsSession<PostgresClient, typeof schema, NoRelations> {
|
|
97
|
+
override transaction<T>(work: (tx: PostgresJsTransaction<typeof schema, NoRelations>) => Promise<T>, config?: PgTransactionConfig): Promise<T> {
|
|
98
|
+
const { dialect, schema: tables, options } = this as unknown as { dialect: PgDialect; schema: Relational; options: object };
|
|
99
|
+
return this.client.begin(config === undefined ? '' : transactionOptions(config), (client) =>
|
|
100
|
+
work(new PostgresJsTransaction(dialect, new PostgresJsSession(client, dialect, tables, options), tables))) as Promise<T>;
|
|
100
101
|
}
|
|
101
|
-
return relational;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
/** What follows BEGIN for `config`, as Drizzle writes it for SET TRANSACTION. */
|
|
105
|
+
function transactionOptions(config: PgTransactionConfig): string {
|
|
106
|
+
return [
|
|
107
|
+
config.isolationLevel === undefined ? null : `isolation level ${config.isolationLevel}`,
|
|
108
|
+
config.accessMode ?? null,
|
|
109
|
+
config.deferrable === undefined ? null : config.deferrable ? 'deferrable' : 'not deferrable',
|
|
110
|
+
].filter((option) => option !== null).join(' ');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* What a database runs on: a postgres.js client (`postgresClient`), or
|
|
115
|
+
* Workers' `HyperdrivePool`, which makes them. Drizzle asks it for
|
|
116
|
+
* statements (`unsafe`) and transactions (`begin`).
|
|
117
|
+
*/
|
|
118
|
+
export type Client = Pick<PostgresClient, 'unsafe' | 'begin'>;
|
|
119
|
+
|
|
120
|
+
export function createDatabase(client: Client): Database {
|
|
121
|
+
// What `drizzle(client, { schema })` sets up, but with no relational queries.
|
|
122
|
+
if ('options' in client) textTypes(client as PostgresClient);
|
|
106
123
|
const dialect = new PgDialect();
|
|
107
|
-
const
|
|
108
|
-
const db: Database = new NodePgDatabase(dialect, new NodePgDriver(guarded(pool) as pg.Pool, dialect).createSession(config), config);
|
|
124
|
+
const db = new PgDatabase(dialect, new Session(guarded(client) as PostgresClient, dialect, relational), relational) as Database;
|
|
109
125
|
// Each transaction's work runs knowing which one it is in, until that work is done. Not until
|
|
110
126
|
// the COMMIT comes back: the transaction waits on nothing then, so what the work left running
|
|
111
127
|
// may ask for a query, which waits for the transaction to end rather than for itself.
|
|
112
128
|
const run = db.transaction.bind(db);
|
|
113
129
|
db.transaction = (async (work, config) => {
|
|
114
|
-
const open = {
|
|
130
|
+
const open = { client, open: true };
|
|
115
131
|
try {
|
|
116
132
|
return await run(
|
|
117
133
|
(tx) =>
|
package/src/dialect.ts
CHANGED
|
@@ -11,7 +11,7 @@ import type { Queryable } from './database.ts';
|
|
|
11
11
|
* Postgres SQLite
|
|
12
12
|
* isolation READ COMMITTED one writer at a time
|
|
13
13
|
* lock a row SELECT … FOR UPDATE one writer at a time
|
|
14
|
-
* rows changed
|
|
14
|
+
* rows changed count rowsAffected
|
|
15
15
|
* duplicate key SQLSTATE 23505 SQLITE_CONSTRAINT_UNIQUE
|
|
16
16
|
* clock CURRENT_TIMESTAMP strftime(…, 'now')
|
|
17
17
|
* clock, in ms clock_timestamp() unixepoch('subsec')
|
|
@@ -56,7 +56,7 @@ export function forUpdate<Query extends { for: (strength: 'update' | 'no key upd
|
|
|
56
56
|
* are the query's own, unmapped.
|
|
57
57
|
*/
|
|
58
58
|
export async function rowsOf<Row>(db: Queryable, query: SQL): Promise<Row[]> {
|
|
59
|
-
if (engineOf(db) === 'postgres') return (await (db as unknown as { execute(query: SQL): Promise<
|
|
59
|
+
if (engineOf(db) === 'postgres') return [...(await (db as unknown as { execute(query: SQL): Promise<Row[]> }).execute(query))];
|
|
60
60
|
return (db as unknown as { all<R>(query: SQL): Promise<R[]> }).all<Row>(query);
|
|
61
61
|
}
|
|
62
62
|
|
|
@@ -98,8 +98,8 @@ export function truth(condition: SQL): SQL<boolean> {
|
|
|
98
98
|
|
|
99
99
|
/** How many rows an insert, update or delete touched. */
|
|
100
100
|
export function changedRows(result: unknown): number {
|
|
101
|
-
const header = result as {
|
|
102
|
-
return header.
|
|
101
|
+
const header = result as { count?: number | null; rowsAffected?: number };
|
|
102
|
+
return header.count ?? header.rowsAffected ?? 0;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
const DUPLICATE_KEY = new Set(['23505', 'SQLITE_CONSTRAINT_UNIQUE', 'SQLITE_CONSTRAINT_PRIMARYKEY']);
|
|
@@ -114,16 +114,25 @@ export function isUniqueViolation(error: unknown): boolean {
|
|
|
114
114
|
return false;
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
/**
|
|
118
|
-
|
|
117
|
+
/**
|
|
118
|
+
* The network's codes for a server that is not there, postgres.js's for a
|
|
119
|
+
* connection lost, cut short or never made, and Postgres's for one that will
|
|
120
|
+
* not take the connection, or has none left (53300).
|
|
121
|
+
*/
|
|
122
|
+
const UNREACHABLE = new Set([
|
|
123
|
+
'ECONNREFUSED', 'ECONNRESET', 'ETIMEDOUT', 'ENOTFOUND', 'EAI_AGAIN', 'EHOSTUNREACH', 'ENETUNREACH', 'EPIPE',
|
|
124
|
+
'CONNECTION_CLOSED', 'CONNECTION_DESTROYED', 'CONNECTION_ENDED', 'CONNECT_TIMEOUT',
|
|
125
|
+
'53300', '57P01', '57P02', '57P03',
|
|
126
|
+
]);
|
|
119
127
|
|
|
120
|
-
/** Hyperdrive's words for having no connection to give, beside
|
|
128
|
+
/** Hyperdrive's words for having no connection to give, beside Workers' for one lost. */
|
|
121
129
|
const UNREACHABLE_WORDS = /connection terminated|network connection lost|connect(ion)? timeout|open slot in the pool|acquire a connection from the pool|server connection attempt failed/i;
|
|
122
130
|
|
|
123
131
|
/**
|
|
124
132
|
* Whether a query failed because the database could not be reached or
|
|
125
133
|
* dropped the connection: an outage, not an answer. Postgres's connection
|
|
126
|
-
* exceptions are class 08;
|
|
134
|
+
* exceptions are class 08; postgres.js says so in codes of its own, Workers'
|
|
135
|
+
* sockets in words, and so
|
|
127
136
|
* does Hyperdrive when it has no connection to the database to give: every
|
|
128
137
|
* one of its pool busy for 15 s ("Timed out while waiting for an open slot
|
|
129
138
|
* in the pool", FATAL 58000; "Failed to acquire a connection from the
|
package/src/grants.ts
CHANGED
|
@@ -307,9 +307,9 @@ export async function reportStands(db: Queryable, principal: string, check: Auth
|
|
|
307
307
|
return report === 'unreached' || typeof report !== 'string';
|
|
308
308
|
}
|
|
309
309
|
|
|
310
|
-
/** The newest access entry about `principal` that `check` finds the vault's own. */
|
|
311
|
-
export function newestAccess(db: Queryable, principal: string, check: Authenticity): Promise<Newest> {
|
|
312
|
-
return newestAuthentic((limit, before) => accessEntriesAbout(db, principal, limit, before), check);
|
|
310
|
+
/** The newest access entry about `principal` that `check` finds the vault's own; at or below `through`, if given. */
|
|
311
|
+
export function newestAccess(db: Queryable, principal: string, check: Authenticity, through?: bigint): Promise<Newest> {
|
|
312
|
+
return newestAuthentic((limit, before) => accessEntriesAbout(db, principal, limit, before ?? (through === undefined ? undefined : through + 1n)), check);
|
|
313
313
|
}
|
|
314
314
|
|
|
315
315
|
/** Up to `limit` of the vault's allowed access entries about `principal` before `before` (the newest when undefined), newest first. */
|
package/src/hyperdrive.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import type
|
|
4
|
-
import { postgresConnection } from './postgres.ts';
|
|
1
|
+
import type { Client } from './database.ts';
|
|
2
|
+
import { plainRead } from './postgres-js/index.ts';
|
|
3
|
+
import { postgresClient, textTypes, type PostgresClient } from './postgres.ts';
|
|
5
4
|
|
|
6
5
|
/** Postgres through a Hyperdrive binding: `postgres(env.HYPERDRIVE)`. */
|
|
7
6
|
export type PostgresDatabase = { readonly engine: 'postgres'; readonly hyperdrive: { readonly connectionString: string } };
|
|
@@ -9,14 +8,24 @@ export type PostgresDatabase = { readonly engine: 'postgres'; readonly hyperdriv
|
|
|
9
8
|
/** Postgres through Hyperdrive, the one database a Worker runs on, the app's and the vault's. */
|
|
10
9
|
export function postgres(hyperdrive: { readonly connectionString: string }): PostgresDatabase {
|
|
11
10
|
if (typeof hyperdrive?.connectionString !== 'string') {
|
|
12
|
-
throw new Error('postgres()
|
|
11
|
+
throw new Error('postgres() was given no Hyperdrive binding: one the code names (HYPERDRIVE, VAULT_HYPERDRIVE, HOT_HYPERDRIVE) is missing from its wrangler.jsonc, or bound under another name');
|
|
13
12
|
}
|
|
14
13
|
return { engine: 'postgres', hyperdrive };
|
|
15
14
|
}
|
|
16
15
|
|
|
16
|
+
/** A statement asked of the pool, sent at its turn once awaited or `execute`d, as postgres.js sends one. */
|
|
17
|
+
type Statement = Promise<unknown> & { values(): Statement; execute(): Statement };
|
|
18
|
+
|
|
19
|
+
/** A read asked while a group gathers (`groupReads`): sent with the others at its turn, and answered with its own rows. */
|
|
20
|
+
type Gathered = { text: string; values: unknown[]; arrays: () => boolean; resolve: (rows: unknown) => void; reject: (error: unknown) => void };
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
/** What the client refuses to write of a group, before anything of it is sent (`build`, postgres-js/connection.ts). */
|
|
24
|
+
const UNSENT = new Set(['GROUP_UNTYPED', 'MAX_PARAMETERS_EXCEEDED', 'UNDEFINED_VALUE']);
|
|
25
|
+
|
|
17
26
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
27
|
+
* postgres.js for one call on Workers, through Hyperdrive: one connection,
|
|
28
|
+
* opened by the first query, which every query and transaction of the
|
|
20
29
|
* call takes its turn on.
|
|
21
30
|
*
|
|
22
31
|
* Hyperdrive keeps the pool to the database; a Worker's connections belong
|
|
@@ -25,134 +34,202 @@ export function postgres(hyperdrive: { readonly connectionString: string }): Pos
|
|
|
25
34
|
* once was now and then cancelled as hung, with nothing left pending, and
|
|
26
35
|
* every connection costs a round trip before the query can be sent.
|
|
27
36
|
*
|
|
28
|
-
* The turns keep a transaction whole:
|
|
29
|
-
* they are asked,
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* transaction
|
|
34
|
-
* does.
|
|
37
|
+
* The turns keep a transaction whole: statements go to the connection in
|
|
38
|
+
* the order they are asked, each once the one before it has answered; a
|
|
39
|
+
* transaction holds the connection from BEGIN until it ends, and a query
|
|
40
|
+
* asked meanwhile waits until then, so nothing runs inside a transaction it
|
|
41
|
+
* is not part of. So code inside a transaction must query through the
|
|
42
|
+
* transaction: a query on the database there would wait for the transaction
|
|
43
|
+
* to end, which waits for it. Neither the vault nor the server does.
|
|
44
|
+
*
|
|
45
|
+
* With `groupReads`, the plain reads (`select …`, outside a transaction)
|
|
46
|
+
* asked while the pool waits for their turn, or in the same task, take it
|
|
47
|
+
* together, sent under one Sync (`group` in postgres-js/index.ts): on
|
|
48
|
+
* Hyperdrive, which sends a client's Syncs on one at a time, a round trip
|
|
49
|
+
* for them all, where each alone took one. They run as one implicit
|
|
50
|
+
* transaction, so if one fails they all do, with its error; each is a
|
|
51
|
+
* read, so none had anything to keep. Anything else asked meanwhile, a
|
|
52
|
+
* write or a transaction, closes the group, and goes after it: statements
|
|
53
|
+
* still go in the order they are asked.
|
|
35
54
|
*
|
|
36
|
-
*
|
|
37
|
-
* a
|
|
55
|
+
* Closed by `end`, once what was asked has answered; asked again after, as
|
|
56
|
+
* a call's background work may, it opens a connection again. Unless `end`
|
|
57
|
+
* was given a time to close by and reached it: then the connection closes
|
|
58
|
+
* at once, failing what is still asked, and nothing opens it again. And
|
|
59
|
+
* `destroy` cuts it the same way, at any time.
|
|
38
60
|
*/
|
|
39
|
-
export class HyperdrivePool implements
|
|
61
|
+
export class HyperdrivePool implements Client {
|
|
40
62
|
readonly #connectionString: string;
|
|
41
|
-
/**
|
|
42
|
-
#
|
|
43
|
-
/**
|
|
44
|
-
#
|
|
45
|
-
/**
|
|
46
|
-
|
|
47
|
-
/**
|
|
63
|
+
/** How long the connection may sit idle before it closes, or undefined: closed by `end` alone. */
|
|
64
|
+
readonly #closeIdleMs: number | undefined;
|
|
65
|
+
/** The client, made by the first statement; null before it, and once `end` closed it. */
|
|
66
|
+
#client: PostgresClient | null = null;
|
|
67
|
+
/** The end of the last turn asked: the next one starts once it has. */
|
|
68
|
+
#tail: Promise<unknown> = Promise.resolve();
|
|
69
|
+
/** Turns asked and not done: waiting, or under way. */
|
|
48
70
|
#busy = 0;
|
|
49
|
-
/** What waits for
|
|
71
|
+
/** What waits for every turn to be done: `end`. */
|
|
50
72
|
readonly #idle: (() => void)[] = [];
|
|
51
|
-
/**
|
|
52
|
-
readonly #
|
|
53
|
-
|
|
73
|
+
/** Whether plain reads asked together go together (`groupReads`). */
|
|
74
|
+
readonly #groupReads: boolean;
|
|
75
|
+
/** The reads that will take the next group's turn, while it has not started: what a read asked now joins. */
|
|
76
|
+
#gathering: Gathered[] | null = null;
|
|
77
|
+
/** Closed at the time `end` was given, with turns still asked: none opens a connection again. */
|
|
78
|
+
#cut = false;
|
|
79
|
+
/** Set by `destroy`: every turn after it fails. */
|
|
80
|
+
#destroyed = false;
|
|
54
81
|
|
|
55
82
|
/**
|
|
56
83
|
* `closeIdleMs`: for what outlives a call, a Durable Object, which queries
|
|
57
|
-
* now and then: the
|
|
58
|
-
* the next query connects again.
|
|
84
|
+
* now and then: the connection closes once it has been idle that long,
|
|
85
|
+
* and the next query connects again. `groupReads`: plain reads asked
|
|
86
|
+
* together go together, under one Sync.
|
|
59
87
|
*/
|
|
60
|
-
constructor(connectionString: string, options: { closeIdleMs?: number } = {}) {
|
|
88
|
+
constructor(connectionString: string, options: { closeIdleMs?: number; groupReads?: boolean } = {}) {
|
|
61
89
|
if (connectionString.trim().length === 0) {
|
|
62
90
|
throw new Error('Hyperdrive returned an empty connection string');
|
|
63
91
|
}
|
|
64
92
|
this.#connectionString = connectionString;
|
|
65
|
-
this.#closeIdleMs = options.closeIdleMs
|
|
93
|
+
this.#closeIdleMs = options.closeIdleMs;
|
|
94
|
+
this.#groupReads = options.groupReads === true;
|
|
66
95
|
}
|
|
67
96
|
|
|
68
|
-
readonly
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
this.#
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
97
|
+
readonly unsafe = ((...args: Parameters<PostgresClient['unsafe']>) => {
|
|
98
|
+
let values = false;
|
|
99
|
+
let answer: Promise<unknown> | undefined;
|
|
100
|
+
const [text, parameters = [], options] = args;
|
|
101
|
+
/** Its turn, asked once: the first `then`, `catch`, `finally` or `execute` asks it. */
|
|
102
|
+
const asked = () =>
|
|
103
|
+
(answer ??= this.#groupReads && options === undefined && plainRead(text)
|
|
104
|
+
? this.#gather({ text, values: parameters, arrays: () => values })
|
|
105
|
+
: this.#turn(() => {
|
|
106
|
+
const query = this.#connected().unsafe(...args);
|
|
107
|
+
return values ? query.values() : query;
|
|
108
|
+
}));
|
|
109
|
+
const statement = {
|
|
110
|
+
values: () => {
|
|
111
|
+
values = true;
|
|
112
|
+
return statement;
|
|
113
|
+
},
|
|
114
|
+
execute: () => {
|
|
115
|
+
void asked().catch(() => {});
|
|
116
|
+
return statement;
|
|
117
|
+
},
|
|
118
|
+
then: (resolve?: (value: unknown) => unknown, reject?: (error: unknown) => unknown) => asked().then(resolve, reject),
|
|
119
|
+
catch: (reject: (error: unknown) => unknown) => asked().catch(reject),
|
|
120
|
+
finally: (done: () => void) => asked().finally(done),
|
|
121
|
+
[Symbol.toStringTag]: 'Statement',
|
|
122
|
+
} as Statement;
|
|
123
|
+
return statement;
|
|
124
|
+
}) as unknown as PostgresClient['unsafe'];
|
|
81
125
|
|
|
82
|
-
/**
|
|
83
|
-
|
|
84
|
-
this.#
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
126
|
+
/** A transaction: its turn lasts until it has committed or rolled back. */
|
|
127
|
+
readonly begin = ((...args: unknown[]) =>
|
|
128
|
+
this.#turn(() => (this.#connected().begin as (...beginArgs: unknown[]) => Promise<unknown>)(...args))) as PostgresClient['begin'];
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* A read, in the group gathering for the next turn, or in a new one: it
|
|
132
|
+
* starts a task later, so that the reads asked with it join, and once
|
|
133
|
+
* the turns before it are done, which those asked while they ran join.
|
|
134
|
+
*/
|
|
135
|
+
#gather(read: Omit<Gathered, 'resolve' | 'reject'>): Promise<unknown> {
|
|
136
|
+
let members = this.#gathering;
|
|
137
|
+
if (members === null) {
|
|
138
|
+
const gathered: Gathered[] = (members = []);
|
|
139
|
+
const asked = new Promise((resolve) => setTimeout(resolve, 0));
|
|
140
|
+
void this.#turn(async () => {
|
|
141
|
+
await asked;
|
|
142
|
+
if (this.#gathering === gathered) this.#gathering = null;
|
|
143
|
+
try {
|
|
144
|
+
const client = this.#connected();
|
|
145
|
+
const alone = async (member: Gathered) => {
|
|
146
|
+
const query = client.unsafe(member.text, member.values);
|
|
147
|
+
member.resolve(await (member.arrays() ? query.values() : query));
|
|
148
|
+
};
|
|
149
|
+
if (gathered.length === 1) return await alone(gathered[0]!);
|
|
150
|
+
try {
|
|
151
|
+
const answers = await client.group(gathered.map((member) => ({ query: member.text, parameters: member.values, arrays: member.arrays() })));
|
|
152
|
+
gathered.forEach((member, i) => member.resolve(answers[i]));
|
|
153
|
+
} catch (error) {
|
|
154
|
+
// A statement the client could not write, a value whose type only the server can say among them: nothing of
|
|
155
|
+
// the group went, and each goes alone, so that only that one fails, as it would have.
|
|
156
|
+
if (!UNSENT.has((error as { code?: unknown }).code as string)) throw error;
|
|
157
|
+
for (const member of gathered) await alone(member).catch(member.reject);
|
|
158
|
+
}
|
|
159
|
+
} catch (error) {
|
|
160
|
+
for (const member of gathered) member.reject(error);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
// After its turn is asked, which closes any group gathering before it.
|
|
164
|
+
this.#gathering = gathered;
|
|
92
165
|
}
|
|
93
|
-
|
|
94
|
-
this.#turn(() => {
|
|
95
|
-
this.#held = true;
|
|
96
|
-
resolve();
|
|
97
|
-
}),
|
|
98
|
-
);
|
|
99
|
-
let released = false;
|
|
100
|
-
return {
|
|
101
|
-
query: client.query.bind(client) as pg.PoolClient['query'],
|
|
102
|
-
release: () => {
|
|
103
|
-
if (released) return;
|
|
104
|
-
released = true;
|
|
105
|
-
this.#held = false;
|
|
106
|
-
this.#next();
|
|
107
|
-
this.#done();
|
|
108
|
-
},
|
|
109
|
-
};
|
|
166
|
+
return new Promise((resolve, reject) => members.push({ ...read, resolve, reject }));
|
|
110
167
|
}
|
|
111
168
|
|
|
112
|
-
/**
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
169
|
+
/**
|
|
170
|
+
* Close the connection once every turn asked is done: what a Worker hands `waitUntil` when the call is done. By
|
|
171
|
+
* `by` (ms since the epoch) at the latest, if given: then at once, what is still asked failing, the statement under
|
|
172
|
+
* way included, and none of it sent again. A use's call past its deadline, whose answer has gone, so lets go of
|
|
173
|
+
* Hyperdrive: what it asked and Hyperdrive had not sent goes with the connection (use.ts, `HELD_PAST_DEADLINE_MS`).
|
|
174
|
+
*/
|
|
175
|
+
async end(by?: number): Promise<void> {
|
|
176
|
+
// A statement awaited just before reaches the pool a few promise steps later, through Drizzle's session: a task
|
|
177
|
+
// later, every one has taken its turn, and none opens a connection that this would leave open.
|
|
178
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
179
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
180
|
+
const reached = by === undefined ? null : new Promise<'cut'>((resolve) => (timer = setTimeout(() => resolve('cut'), Math.max(0, by - Date.now()))));
|
|
181
|
+
while (this.#busy > 0 && !this.#cut) {
|
|
182
|
+
const idle = new Promise<void>((resolve) => this.#idle.push(resolve));
|
|
183
|
+
if ((await (reached === null ? idle : Promise.race([idle, reached]))) === 'cut') this.#cut = true;
|
|
117
184
|
}
|
|
185
|
+
clearTimeout(timer);
|
|
118
186
|
const client = this.#client;
|
|
119
187
|
this.#client = null;
|
|
120
|
-
|
|
121
|
-
await client.then((connected) => connected.end()).catch(() => {});
|
|
188
|
+
await client?.end(this.#cut ? { timeout: 0 } : undefined).catch(() => {});
|
|
122
189
|
}
|
|
123
190
|
|
|
124
|
-
/**
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
191
|
+
/**
|
|
192
|
+
* The connection cut now, what it was asked failing at once, and every
|
|
193
|
+
* turn after refused: for what a host stopped waiting on, a connection
|
|
194
|
+
* gone dead or a connect that never answers (T169), so that whatever
|
|
195
|
+
* still awaits it fails rather than waits for good. Its statement may
|
|
196
|
+
* still run in the database.
|
|
197
|
+
*/
|
|
198
|
+
destroy(): void {
|
|
199
|
+
this.#destroyed = true;
|
|
200
|
+
const client = this.#client;
|
|
201
|
+
this.#client = null;
|
|
202
|
+
void client?.end({ timeout: 0 }).catch(() => {});
|
|
128
203
|
}
|
|
129
204
|
|
|
130
|
-
/**
|
|
131
|
-
#
|
|
132
|
-
|
|
205
|
+
/** `go` once every turn asked before it is done. Anything but a group's closes the group gathering: it goes after. */
|
|
206
|
+
#turn<T>(go: () => PromiseLike<T>): Promise<T> {
|
|
207
|
+
this.#gathering = null;
|
|
208
|
+
this.#busy += 1;
|
|
209
|
+
const answer = this.#tail.then(go);
|
|
210
|
+
this.#tail = answer.then(
|
|
211
|
+
() => this.#done(),
|
|
212
|
+
() => this.#done(),
|
|
213
|
+
);
|
|
214
|
+
return answer;
|
|
133
215
|
}
|
|
134
216
|
|
|
135
|
-
/** One
|
|
217
|
+
/** One turn done: once none is left, what waits for the pool to be idle goes on. */
|
|
136
218
|
#done(): void {
|
|
137
219
|
this.#busy -= 1;
|
|
138
|
-
if (this.#busy
|
|
139
|
-
for (const resolve of this.#idle.splice(0)) resolve();
|
|
140
|
-
if (this.#closeIdleMs !== null && this.#client !== null) this.#idleTimer = setTimeout(() => void this.end(), this.#closeIdleMs);
|
|
220
|
+
if (this.#busy === 0) for (const resolve of this.#idle.splice(0)) resolve();
|
|
141
221
|
}
|
|
142
222
|
|
|
143
|
-
/** The call's client,
|
|
144
|
-
#connected():
|
|
145
|
-
if (this.#
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
client
|
|
154
|
-
connecting.catch(drop);
|
|
155
|
-
this.#client = connecting;
|
|
156
|
-
return connecting;
|
|
223
|
+
/** The call's client, made the first time. postgres.js opens its connection again when one is lost. */
|
|
224
|
+
#connected(): PostgresClient {
|
|
225
|
+
if (this.#destroyed) throw new Error('this connection was cut: its call stopped waiting on it');
|
|
226
|
+
if (this.#cut) throw new Error("the call's connection was closed at the time it was given (`end`): nothing more is sent");
|
|
227
|
+
this.#client ??= textTypes(postgresClient(this.#connectionString, {
|
|
228
|
+
max: 1,
|
|
229
|
+
idle_timeout: this.#closeIdleMs === undefined ? undefined : this.#closeIdleMs / 1000,
|
|
230
|
+
// No timer left to outlive the call: `end` closes the connection, or idling does.
|
|
231
|
+
max_lifetime: null,
|
|
232
|
+
}));
|
|
233
|
+
return this.#client;
|
|
157
234
|
}
|
|
158
235
|
}
|