@framers/sql-storage-adapter 0.7.2 → 0.7.3
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 +20 -0
- package/dist/adapters/electron/preload.cjs +289 -0
- package/dist/adapters/electron/renderer.d.ts +9 -0
- package/dist/adapters/electron/renderer.d.ts.map +1 -0
- package/dist/adapters/electron/renderer.js +8 -0
- package/dist/adapters/electron/renderer.js.map +1 -0
- package/dist/cjs/adapters/__tests__/indexedDbAdapter.spec.d.ts +2 -0
- package/dist/cjs/adapters/__tests__/indexedDbAdapter.spec.js +133 -0
- package/dist/cjs/adapters/baseStorageAdapter.d.ts +368 -0
- package/dist/cjs/adapters/baseStorageAdapter.js +744 -0
- package/dist/cjs/adapters/betterSqliteAdapter.d.ts +124 -0
- package/dist/cjs/adapters/betterSqliteAdapter.js +334 -0
- package/dist/cjs/adapters/capacitorSqliteAdapter.d.ts +83 -0
- package/dist/cjs/adapters/capacitorSqliteAdapter.js +250 -0
- package/dist/cjs/adapters/electron/electronMainAdapter.d.ts +251 -0
- package/dist/cjs/adapters/electron/electronMainAdapter.js +392 -0
- package/dist/cjs/adapters/electron/electronRendererAdapter.d.ts +177 -0
- package/dist/cjs/adapters/electron/electronRendererAdapter.js +283 -0
- package/dist/cjs/adapters/electron/index.d.ts +74 -0
- package/dist/cjs/adapters/electron/index.js +86 -0
- package/dist/cjs/adapters/electron/ipc/channels.d.ts +196 -0
- package/dist/cjs/adapters/electron/ipc/channels.js +127 -0
- package/dist/cjs/adapters/electron/ipc/index.d.ts +11 -0
- package/dist/cjs/adapters/electron/ipc/index.js +38 -0
- package/dist/cjs/adapters/electron/ipc/protocol.d.ts +78 -0
- package/dist/cjs/adapters/electron/ipc/protocol.js +341 -0
- package/dist/cjs/adapters/electron/ipc/types.d.ts +248 -0
- package/dist/cjs/adapters/electron/ipc/types.js +15 -0
- package/dist/cjs/adapters/electron/migration/autoMigrator.d.ts +184 -0
- package/dist/cjs/adapters/electron/migration/autoMigrator.js +441 -0
- package/dist/cjs/adapters/electron/migration/index.d.ts +9 -0
- package/dist/cjs/adapters/electron/migration/index.js +29 -0
- package/dist/cjs/adapters/electron/preload.d.ts +126 -0
- package/dist/cjs/adapters/electron/preload.js +233 -0
- package/dist/cjs/adapters/electron/recovery/corruptionDetector.d.ts +214 -0
- package/dist/cjs/adapters/electron/recovery/corruptionDetector.js +359 -0
- package/dist/cjs/adapters/electron/recovery/index.d.ts +11 -0
- package/dist/cjs/adapters/electron/recovery/index.js +34 -0
- package/dist/cjs/adapters/electron/recovery/walCheckpoint.d.ts +186 -0
- package/dist/cjs/adapters/electron/recovery/walCheckpoint.js +263 -0
- package/dist/cjs/adapters/electron/renderer.d.ts +9 -0
- package/dist/cjs/adapters/electron/renderer.js +33 -0
- package/dist/cjs/adapters/electron/window/index.d.ts +9 -0
- package/dist/cjs/adapters/electron/window/index.js +29 -0
- package/dist/cjs/adapters/electron/window/windowManager.d.ts +190 -0
- package/dist/cjs/adapters/electron/window/windowManager.js +321 -0
- package/dist/cjs/adapters/indexedDbAdapter.d.ts +490 -0
- package/dist/cjs/adapters/indexedDbAdapter.js +993 -0
- package/dist/cjs/adapters/postgresAdapter.d.ts +424 -0
- package/dist/cjs/adapters/postgresAdapter.js +889 -0
- package/dist/cjs/adapters/sqlJsAdapter.d.ts +64 -0
- package/dist/cjs/adapters/sqlJsAdapter.js +268 -0
- package/dist/cjs/adapters/supabase.d.ts +58 -0
- package/dist/cjs/adapters/supabase.js +401 -0
- package/dist/cjs/codecs/BrowserBlobCodec.d.ts +14 -0
- package/dist/cjs/codecs/BrowserBlobCodec.js +57 -0
- package/dist/cjs/codecs/NodeBlobCodec.d.ts +14 -0
- package/dist/cjs/codecs/NodeBlobCodec.js +59 -0
- package/dist/cjs/core/contracts/blobCodec.d.ts +15 -0
- package/dist/cjs/core/contracts/blobCodec.js +15 -0
- package/dist/cjs/core/contracts/context.d.ts +221 -0
- package/dist/cjs/core/contracts/context.js +15 -0
- package/dist/cjs/core/contracts/dialect.d.ts +154 -0
- package/dist/cjs/core/contracts/dialect.js +15 -0
- package/dist/cjs/core/contracts/events.d.ts +225 -0
- package/dist/cjs/core/contracts/events.js +15 -0
- package/dist/cjs/core/contracts/exporter.d.ts +13 -0
- package/dist/cjs/core/contracts/exporter.js +15 -0
- package/dist/cjs/core/contracts/extensions.d.ts +73 -0
- package/dist/cjs/core/contracts/extensions.js +15 -0
- package/dist/cjs/core/contracts/features.d.ts +25 -0
- package/dist/cjs/core/contracts/features.js +61 -0
- package/dist/cjs/core/contracts/fts.d.ts +66 -0
- package/dist/cjs/core/contracts/fts.js +15 -0
- package/dist/cjs/core/contracts/hooks.d.ts +481 -0
- package/dist/cjs/core/contracts/hooks.js +140 -0
- package/dist/cjs/core/contracts/index.d.ts +260 -0
- package/dist/cjs/core/contracts/index.js +41 -0
- package/dist/cjs/core/contracts/limitations.d.ts +46 -0
- package/dist/cjs/core/contracts/limitations.js +218 -0
- package/dist/cjs/core/contracts/performance.d.ts +308 -0
- package/dist/cjs/core/contracts/performance.js +144 -0
- package/dist/cjs/core/database.d.ts +220 -0
- package/dist/cjs/core/database.js +136 -0
- package/dist/cjs/core/portable.d.ts +23 -0
- package/dist/cjs/core/portable.js +66 -0
- package/dist/cjs/core/resolver.d.ts +77 -0
- package/dist/cjs/core/resolver.js +235 -0
- package/dist/cjs/dialects/PostgresDialect.d.ts +35 -0
- package/dist/cjs/dialects/PostgresDialect.js +143 -0
- package/dist/cjs/dialects/SqliteDialect.d.ts +35 -0
- package/dist/cjs/dialects/SqliteDialect.js +142 -0
- package/dist/cjs/dialects/shared.d.ts +17 -0
- package/dist/cjs/dialects/shared.js +90 -0
- package/dist/cjs/exporters/PostgresExporter.d.ts +14 -0
- package/dist/cjs/exporters/PostgresExporter.js +61 -0
- package/dist/cjs/exporters/SqliteFileExporter.d.ts +16 -0
- package/dist/cjs/exporters/SqliteFileExporter.js +43 -0
- package/dist/cjs/features/backup/cloudBackup.d.ts +219 -0
- package/dist/cjs/features/backup/cloudBackup.js +209 -0
- package/dist/cjs/features/kv/idb.d.ts +79 -0
- package/dist/cjs/features/kv/idb.js +32 -0
- package/dist/cjs/features/kv/index.d.ts +14 -0
- package/dist/cjs/features/kv/index.js +40 -0
- package/dist/cjs/features/kv/indexedDbKeyValueStore.d.ts +77 -0
- package/dist/cjs/features/kv/indexedDbKeyValueStore.js +116 -0
- package/dist/cjs/features/kv/keyValueStore.d.ts +48 -0
- package/dist/cjs/features/kv/keyValueStore.js +67 -0
- package/dist/cjs/features/kv/sqlKeyValueStore.d.ts +34 -0
- package/dist/cjs/features/kv/sqlKeyValueStore.js +81 -0
- package/dist/cjs/features/kv/valueCodec.d.ts +35 -0
- package/dist/cjs/features/kv/valueCodec.js +426 -0
- package/dist/cjs/features/migrations/dataExport.d.ts +77 -0
- package/dist/cjs/features/migrations/dataExport.js +194 -0
- package/dist/cjs/features/migrations/dataImport.d.ts +54 -0
- package/dist/cjs/features/migrations/dataImport.js +300 -0
- package/dist/cjs/features/migrations/migration.d.ts +89 -0
- package/dist/cjs/features/migrations/migration.js +187 -0
- package/dist/cjs/features/migrations/schemaMigrations.d.ts +93 -0
- package/dist/cjs/features/migrations/schemaMigrations.js +220 -0
- package/dist/cjs/features/sync/conflicts/conflictResolver.d.ts +222 -0
- package/dist/cjs/features/sync/conflicts/conflictResolver.js +366 -0
- package/dist/cjs/features/sync/conflicts/index.d.ts +9 -0
- package/dist/cjs/features/sync/conflicts/index.js +21 -0
- package/dist/cjs/features/sync/crossPlatformSync.d.ts +281 -0
- package/dist/cjs/features/sync/crossPlatformSync.js +551 -0
- package/dist/cjs/features/sync/devices/deviceManager.d.ts +243 -0
- package/dist/cjs/features/sync/devices/deviceManager.js +450 -0
- package/dist/cjs/features/sync/devices/index.d.ts +10 -0
- package/dist/cjs/features/sync/devices/index.js +21 -0
- package/dist/cjs/features/sync/index.d.ts +37 -0
- package/dist/cjs/features/sync/index.js +111 -0
- package/dist/cjs/features/sync/protocol/index.d.ts +11 -0
- package/dist/cjs/features/sync/protocol/index.js +23 -0
- package/dist/cjs/features/sync/protocol/messages.d.ts +348 -0
- package/dist/cjs/features/sync/protocol/messages.js +215 -0
- package/dist/cjs/features/sync/protocol/vectorClock.d.ts +164 -0
- package/dist/cjs/features/sync/protocol/vectorClock.js +256 -0
- package/dist/cjs/features/sync/syncManager.d.ts +348 -0
- package/dist/cjs/features/sync/syncManager.js +455 -0
- package/dist/cjs/features/sync/tables/index.d.ts +10 -0
- package/dist/cjs/features/sync/tables/index.js +21 -0
- package/dist/cjs/features/sync/tables/syncLogManager.d.ts +216 -0
- package/dist/cjs/features/sync/tables/syncLogManager.js +423 -0
- package/dist/cjs/features/sync/transport/httpTransport.d.ts +123 -0
- package/dist/cjs/features/sync/transport/httpTransport.js +346 -0
- package/dist/cjs/features/sync/transport/index.d.ts +12 -0
- package/dist/cjs/features/sync/transport/index.js +25 -0
- package/dist/cjs/features/sync/transport/transport.d.ts +259 -0
- package/dist/cjs/features/sync/transport/transport.js +163 -0
- package/dist/cjs/features/sync/transport/websocketTransport.d.ts +126 -0
- package/dist/cjs/features/sync/transport/websocketTransport.js +342 -0
- package/dist/cjs/fts/PostgresFts.d.ts +29 -0
- package/dist/cjs/fts/PostgresFts.js +77 -0
- package/dist/cjs/fts/SqliteFts5.d.ts +22 -0
- package/dist/cjs/fts/SqliteFts5.js +58 -0
- package/dist/cjs/index.d.ts +30 -0
- package/dist/cjs/index.js +101 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/shared/connectionGate.d.ts +94 -0
- package/dist/cjs/shared/connectionGate.js +77 -0
- package/dist/cjs/shared/errors.d.ts +23 -0
- package/dist/cjs/shared/errors.js +45 -0
- package/dist/cjs/shared/logger.d.ts +26 -0
- package/dist/cjs/shared/logger.js +40 -0
- package/dist/cjs/shared/parameterUtils.d.ts +16 -0
- package/dist/cjs/shared/parameterUtils.js +59 -0
- package/dist/cjs/shared/postgresTarget.d.ts +11 -0
- package/dist/cjs/shared/postgresTarget.js +30 -0
- package/dist/cjs/shared/segment.d.ts +43 -0
- package/dist/cjs/shared/segment.js +63 -0
- package/dist/cjs/shared/serializedTransaction.d.ts +14 -0
- package/dist/cjs/shared/serializedTransaction.js +74 -0
- package/dist/cjs/shared/splitSqlStatements.d.ts +27 -0
- package/dist/cjs/shared/splitSqlStatements.js +163 -0
- package/dist/cjs/shared/sqlPlaceholders.d.ts +48 -0
- package/dist/cjs/shared/sqlPlaceholders.js +263 -0
- package/dist/cjs/types/index.d.ts +29 -0
- package/dist/cjs/types/index.js +33 -0
- package/dist/core/portable.d.ts +23 -0
- package/dist/core/portable.d.ts.map +1 -0
- package/dist/core/portable.js +23 -0
- package/dist/core/portable.js.map +1 -0
- package/package.json +139 -24
|
@@ -0,0 +1,889 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
var postgresAdapter_exports = {};
|
|
29
|
+
__export(postgresAdapter_exports, {
|
|
30
|
+
PostgresAdapter: () => PostgresAdapter,
|
|
31
|
+
createPostgresAdapter: () => createPostgresAdapter
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(postgresAdapter_exports);
|
|
34
|
+
var import_sqlPlaceholders = require("../shared/sqlPlaceholders.js");
|
|
35
|
+
var import_splitSqlStatements = require("../shared/splitSqlStatements.js");
|
|
36
|
+
var import_segment = require("../shared/segment.js");
|
|
37
|
+
var import_logger = require("../shared/logger.js");
|
|
38
|
+
const __isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined";
|
|
39
|
+
const typeParserOverrides = (parsers) => {
|
|
40
|
+
const overrides = /* @__PURE__ */ new Map();
|
|
41
|
+
if (!parsers)
|
|
42
|
+
return overrides;
|
|
43
|
+
const choice = (field, allowed) => {
|
|
44
|
+
const value = parsers[field];
|
|
45
|
+
if (value === void 0)
|
|
46
|
+
return void 0;
|
|
47
|
+
if (!allowed.includes(value)) {
|
|
48
|
+
throw new Error(`typeParsers.${field} must be ${allowed.map((option) => `'${option}'`).join(" or ")}; got ${JSON.stringify(value)}.`);
|
|
49
|
+
}
|
|
50
|
+
return value;
|
|
51
|
+
};
|
|
52
|
+
const int8 = choice("int8", ["string", "number", "bigint"]);
|
|
53
|
+
if (int8 === "string")
|
|
54
|
+
overrides.set(20, (text) => text);
|
|
55
|
+
if (int8 === "bigint")
|
|
56
|
+
overrides.set(20, (text) => BigInt(text));
|
|
57
|
+
if (int8 === "number") {
|
|
58
|
+
overrides.set(20, (text) => {
|
|
59
|
+
const value = Number(text);
|
|
60
|
+
if (!Number.isSafeInteger(value)) {
|
|
61
|
+
throw new RangeError(`int8 value ${text} is outside Number's safe integer range; read it with typeParsers.int8 'bigint' or 'string'.`);
|
|
62
|
+
}
|
|
63
|
+
return value;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
const numeric = choice("numeric", ["string", "number"]);
|
|
67
|
+
if (numeric === "string")
|
|
68
|
+
overrides.set(1700, (text) => text);
|
|
69
|
+
if (numeric === "number")
|
|
70
|
+
overrides.set(1700, (text) => Number(text));
|
|
71
|
+
if (choice("date", ["date", "string"]) === "string")
|
|
72
|
+
overrides.set(1082, (text) => text);
|
|
73
|
+
if (choice("json", ["parse", "string"]) === "string") {
|
|
74
|
+
overrides.set(114, (text) => text);
|
|
75
|
+
overrides.set(3802, (text) => text);
|
|
76
|
+
}
|
|
77
|
+
return overrides;
|
|
78
|
+
};
|
|
79
|
+
const POOL_SETTINGS = [
|
|
80
|
+
"connectionString",
|
|
81
|
+
"host",
|
|
82
|
+
"port",
|
|
83
|
+
"database",
|
|
84
|
+
"user",
|
|
85
|
+
"password",
|
|
86
|
+
"ssl",
|
|
87
|
+
"max",
|
|
88
|
+
"min",
|
|
89
|
+
"idleTimeoutMillis",
|
|
90
|
+
"connectionTimeoutMillis",
|
|
91
|
+
"application_name",
|
|
92
|
+
"statement_timeout",
|
|
93
|
+
"query_timeout",
|
|
94
|
+
"allowExitOnIdle",
|
|
95
|
+
"typeParsers"
|
|
96
|
+
];
|
|
97
|
+
const ISOLATION_LEVELS = /* @__PURE__ */ new Map([
|
|
98
|
+
["read uncommitted", "READ UNCOMMITTED"],
|
|
99
|
+
["read committed", "READ COMMITTED"],
|
|
100
|
+
["repeatable read", "REPEATABLE READ"],
|
|
101
|
+
["serializable", "SERIALIZABLE"]
|
|
102
|
+
]);
|
|
103
|
+
const beginStatement = (options) => {
|
|
104
|
+
const modes = [];
|
|
105
|
+
if (options?.isolationLevel !== void 0) {
|
|
106
|
+
const level = ISOLATION_LEVELS.get(options.isolationLevel);
|
|
107
|
+
if (!level) {
|
|
108
|
+
throw new Error(`Unknown isolation level "${String(options.isolationLevel)}". Use 'read uncommitted', 'read committed', 'repeatable read' or 'serializable'.`);
|
|
109
|
+
}
|
|
110
|
+
modes.push(`ISOLATION LEVEL ${level}`);
|
|
111
|
+
}
|
|
112
|
+
if (options?.readOnly !== void 0)
|
|
113
|
+
modes.push(options.readOnly ? "READ ONLY" : "READ WRITE");
|
|
114
|
+
if (options?.deferrable !== void 0)
|
|
115
|
+
modes.push(options.deferrable ? "DEFERRABLE" : "NOT DEFERRABLE");
|
|
116
|
+
return modes.length > 0 ? `BEGIN ${modes.join(", ")}` : "BEGIN";
|
|
117
|
+
};
|
|
118
|
+
const transactionStatus = (client) => {
|
|
119
|
+
const read = client.getTransactionStatus;
|
|
120
|
+
return typeof read === "function" ? read.call(client) : null;
|
|
121
|
+
};
|
|
122
|
+
const prepareStatement = import_sqlPlaceholders.toPostgresStatement;
|
|
123
|
+
const runWith = async (query, statement, parameters) => {
|
|
124
|
+
const { text, values } = prepareStatement(statement, parameters);
|
|
125
|
+
const result = await query(text, values);
|
|
126
|
+
const firstRow = result.rows?.[0];
|
|
127
|
+
const lastInsertRowid = firstRow && typeof firstRow === "object" && "id" in firstRow ? firstRow.id : null;
|
|
128
|
+
return { changes: result.rowCount ?? 0, lastInsertRowid };
|
|
129
|
+
};
|
|
130
|
+
const getWith = async (query, statement, parameters) => {
|
|
131
|
+
const { text, values } = prepareStatement(statement, parameters);
|
|
132
|
+
const result = await query(text, values);
|
|
133
|
+
return result.rows?.[0] ?? null;
|
|
134
|
+
};
|
|
135
|
+
const allWith = async (query, statement, parameters) => {
|
|
136
|
+
const { text, values } = prepareStatement(statement, parameters);
|
|
137
|
+
const result = await query(text, values);
|
|
138
|
+
return result.rows ?? [];
|
|
139
|
+
};
|
|
140
|
+
const execWith = async (query, script) => {
|
|
141
|
+
const statements = (0, import_splitSqlStatements.splitSqlStatements)(script);
|
|
142
|
+
for (const text of statements) {
|
|
143
|
+
await query(text, []);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
const leadingWords = (statement, limit) => {
|
|
147
|
+
const words = [];
|
|
148
|
+
let i = 0;
|
|
149
|
+
const length = statement.length;
|
|
150
|
+
while (i < length && words.length < limit) {
|
|
151
|
+
const char = statement[i];
|
|
152
|
+
if (/\s/.test(char)) {
|
|
153
|
+
i += 1;
|
|
154
|
+
} else if (char === "-" && statement[i + 1] === "-") {
|
|
155
|
+
const end = statement.indexOf("\n", i);
|
|
156
|
+
i = end === -1 ? length : end + 1;
|
|
157
|
+
} else if (char === "/" && statement[i + 1] === "*") {
|
|
158
|
+
let depth = 1;
|
|
159
|
+
i += 2;
|
|
160
|
+
while (i < length && depth > 0) {
|
|
161
|
+
if (statement[i] === "/" && statement[i + 1] === "*") {
|
|
162
|
+
depth += 1;
|
|
163
|
+
i += 2;
|
|
164
|
+
} else if (statement[i] === "*" && statement[i + 1] === "/") {
|
|
165
|
+
depth -= 1;
|
|
166
|
+
i += 2;
|
|
167
|
+
} else {
|
|
168
|
+
i += 1;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
} else if (char === "'") {
|
|
172
|
+
i += 1;
|
|
173
|
+
while (i < length) {
|
|
174
|
+
if (statement[i] === "'" && statement[i + 1] === "'")
|
|
175
|
+
i += 2;
|
|
176
|
+
else if (statement[i] === "'")
|
|
177
|
+
break;
|
|
178
|
+
else
|
|
179
|
+
i += 1;
|
|
180
|
+
}
|
|
181
|
+
i += 1;
|
|
182
|
+
words.push({ keyword: "'", identifier: "" });
|
|
183
|
+
} else if (char === '"') {
|
|
184
|
+
let identifier = "";
|
|
185
|
+
i += 1;
|
|
186
|
+
while (i < length) {
|
|
187
|
+
if (statement[i] === '"' && statement[i + 1] === '"') {
|
|
188
|
+
identifier += '"';
|
|
189
|
+
i += 2;
|
|
190
|
+
} else if (statement[i] === '"') {
|
|
191
|
+
break;
|
|
192
|
+
} else {
|
|
193
|
+
identifier += statement[i];
|
|
194
|
+
i += 1;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
i += 1;
|
|
198
|
+
words.push({ keyword: '"', identifier });
|
|
199
|
+
} else if (char === "$") {
|
|
200
|
+
const tag = /^\$[A-Za-z_]*\$/.exec(statement.slice(i));
|
|
201
|
+
if (tag) {
|
|
202
|
+
const end = statement.indexOf(tag[0], i + tag[0].length);
|
|
203
|
+
i = end === -1 ? length : end + tag[0].length;
|
|
204
|
+
words.push({ keyword: "$", identifier: "" });
|
|
205
|
+
} else {
|
|
206
|
+
i += 1;
|
|
207
|
+
words.push({ keyword: "$", identifier: "" });
|
|
208
|
+
}
|
|
209
|
+
} else if (/[A-Za-z_]/.test(char)) {
|
|
210
|
+
const word = /^[A-Za-z_][A-Za-z0-9_$]*/.exec(statement.slice(i))[0];
|
|
211
|
+
i += word.length;
|
|
212
|
+
words.push({ keyword: word.toUpperCase(), identifier: word.toLowerCase() });
|
|
213
|
+
} else {
|
|
214
|
+
i += 1;
|
|
215
|
+
words.push({ keyword: char, identifier: "" });
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return words;
|
|
219
|
+
};
|
|
220
|
+
const unqualifiedCreateExtension = (statement) => {
|
|
221
|
+
const words = leadingWords(statement, 16);
|
|
222
|
+
if (words[0]?.keyword !== "CREATE" || words[1]?.keyword !== "EXTENSION")
|
|
223
|
+
return null;
|
|
224
|
+
let at = 2;
|
|
225
|
+
if (words[2]?.keyword === "IF" && words[3]?.keyword === "NOT" && words[4]?.keyword === "EXISTS")
|
|
226
|
+
at = 5;
|
|
227
|
+
const name = words[at];
|
|
228
|
+
if (!name || name.keyword !== '"' && !/^[A-Z_]/.test(name.keyword))
|
|
229
|
+
return null;
|
|
230
|
+
if (words.slice(at + 1).some((word) => word.keyword === "SCHEMA"))
|
|
231
|
+
return null;
|
|
232
|
+
return name.identifier;
|
|
233
|
+
};
|
|
234
|
+
const asError = (value) => value instanceof Error ? value : new Error(String(value));
|
|
235
|
+
const keepRollbackError = (error, rollbackError) => {
|
|
236
|
+
if (typeof error !== "object" || error === null || !Object.isExtensible(error))
|
|
237
|
+
return;
|
|
238
|
+
try {
|
|
239
|
+
Object.defineProperty(error, "rollbackError", {
|
|
240
|
+
value: rollbackError,
|
|
241
|
+
enumerable: false,
|
|
242
|
+
configurable: true,
|
|
243
|
+
writable: true
|
|
244
|
+
});
|
|
245
|
+
} catch {
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
const watchClient = (client) => {
|
|
249
|
+
let failure;
|
|
250
|
+
if (typeof client.on !== "function" || typeof client.removeListener !== "function") {
|
|
251
|
+
return { failure: () => void 0, stop: () => void 0 };
|
|
252
|
+
}
|
|
253
|
+
const onError = (error) => {
|
|
254
|
+
if (!failure)
|
|
255
|
+
failure = error;
|
|
256
|
+
};
|
|
257
|
+
client.on("error", onError);
|
|
258
|
+
return {
|
|
259
|
+
failure: () => failure,
|
|
260
|
+
stop: () => {
|
|
261
|
+
client.removeListener?.("error", onError);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
};
|
|
265
|
+
const EXTENSION_PLACE = "SELECT n.nspname AS schema, e.extrelocatable AS relocatable FROM pg_extension e JOIN pg_namespace n ON n.oid = e.extnamespace WHERE e.extname = $1";
|
|
266
|
+
class SegmentStatements {
|
|
267
|
+
constructor(segment) {
|
|
268
|
+
this.segment = segment;
|
|
269
|
+
this.reported = /* @__PURE__ */ new Set();
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Runs `text` on the pool. The connection that runs a `CREATE EXTENSION`
|
|
273
|
+
* goes back to the pool unless its BEGIN or ROLLBACK failed or it broke
|
|
274
|
+
* while held, in which case it is closed.
|
|
275
|
+
*/
|
|
276
|
+
async onPool(pool, text, values) {
|
|
277
|
+
const extension = unqualifiedCreateExtension(text);
|
|
278
|
+
if (extension === null) {
|
|
279
|
+
return pool.query(text, values);
|
|
280
|
+
}
|
|
281
|
+
const client = await pool.connect();
|
|
282
|
+
const watch = watchClient(client);
|
|
283
|
+
let brokenBy;
|
|
284
|
+
try {
|
|
285
|
+
return await this.inOwnTransaction(client, text, values, extension, (error) => {
|
|
286
|
+
if (!brokenBy)
|
|
287
|
+
brokenBy = error;
|
|
288
|
+
});
|
|
289
|
+
} finally {
|
|
290
|
+
watch.stop();
|
|
291
|
+
client.release(brokenBy ?? watch.failure());
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
/** Runs `text` on a transaction's client, inside the caller's transaction. */
|
|
295
|
+
async inTransaction(client, text, values) {
|
|
296
|
+
const extension = unqualifiedCreateExtension(text);
|
|
297
|
+
if (extension === null) {
|
|
298
|
+
return client.query(text, values);
|
|
299
|
+
}
|
|
300
|
+
return this.withinTransaction(client, text, values, extension);
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Runs `text` on the connection a `withConnection()` callback holds: inside
|
|
304
|
+
* the transaction the callback began, as `inTransaction` does, or outside
|
|
305
|
+
* one in a transaction of its own, as on the pool. On a pg that does not
|
|
306
|
+
* report a connection's transaction status (before 8.21), the session's
|
|
307
|
+
* search_path is set to `public` around the statement and set back after it.
|
|
308
|
+
* `onBroken` hears when the connection cannot be trusted any more, so
|
|
309
|
+
* `withConnection()` closes it instead of returning it to the pool.
|
|
310
|
+
*/
|
|
311
|
+
async onConnection(client, text, values, onBroken) {
|
|
312
|
+
const extension = unqualifiedCreateExtension(text);
|
|
313
|
+
if (extension === null) {
|
|
314
|
+
return client.query(text, values);
|
|
315
|
+
}
|
|
316
|
+
switch (transactionStatus(client)) {
|
|
317
|
+
case "T":
|
|
318
|
+
return this.withinTransaction(client, text, values, extension);
|
|
319
|
+
case "I":
|
|
320
|
+
return this.inOwnTransaction(client, text, values, extension, onBroken);
|
|
321
|
+
case "E":
|
|
322
|
+
return client.query(text, values);
|
|
323
|
+
default:
|
|
324
|
+
return this.aroundSession(client, text, values, extension, onBroken);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* In a transaction of its own on `client`, whose COMMIT or ROLLBACK ends the
|
|
329
|
+
* setting. A BEGIN or ROLLBACK that fails leaves the connection in a state
|
|
330
|
+
* nobody knows, so `onBroken` hears of it.
|
|
331
|
+
*/
|
|
332
|
+
async inOwnTransaction(client, text, values, extension, onBroken) {
|
|
333
|
+
try {
|
|
334
|
+
await client.query("BEGIN");
|
|
335
|
+
} catch (error) {
|
|
336
|
+
onBroken(asError(error));
|
|
337
|
+
throw error;
|
|
338
|
+
}
|
|
339
|
+
let result;
|
|
340
|
+
try {
|
|
341
|
+
await client.query("SELECT set_config('search_path', 'public', true)");
|
|
342
|
+
result = await client.query(text, values);
|
|
343
|
+
await client.query("COMMIT");
|
|
344
|
+
} catch (error) {
|
|
345
|
+
try {
|
|
346
|
+
await client.query("ROLLBACK");
|
|
347
|
+
} catch (rollbackError) {
|
|
348
|
+
onBroken(asError(rollbackError));
|
|
349
|
+
keepRollbackError(error, asError(rollbackError));
|
|
350
|
+
}
|
|
351
|
+
throw error;
|
|
352
|
+
}
|
|
353
|
+
await this.checkReach((t, v) => client.query(t, v), extension);
|
|
354
|
+
return result;
|
|
355
|
+
}
|
|
356
|
+
/** Inside the transaction open on `client`, setting the search_path for the statement and back. */
|
|
357
|
+
async withinTransaction(client, text, values, extension) {
|
|
358
|
+
const saved = await client.query("SELECT current_setting('search_path') AS path");
|
|
359
|
+
const path = saved.rows[0]?.path;
|
|
360
|
+
await client.query("SELECT set_config('search_path', 'public', true)");
|
|
361
|
+
const result = await client.query(text, values);
|
|
362
|
+
await client.query("SELECT set_config($1, $2, true)", ["search_path", typeof path === "string" ? path : `${this.segment}, public`]);
|
|
363
|
+
await this.checkReach((t, v) => client.query(t, v), extension);
|
|
364
|
+
return result;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* With the session's search_path set to `public` for the statement, and set
|
|
368
|
+
* back after it. When the statement fails inside a transaction, setting it
|
|
369
|
+
* back fails too, and that transaction's rollback restores it. Without the
|
|
370
|
+
* transaction status this cannot tell that case from a connection whose
|
|
371
|
+
* search_path stays on `public`, so a failed set-back tells `onBroken`, and
|
|
372
|
+
* the connection is closed rather than returned to the pool outside the
|
|
373
|
+
* segment.
|
|
374
|
+
*/
|
|
375
|
+
async aroundSession(client, text, values, extension, onBroken) {
|
|
376
|
+
const saved = await client.query("SELECT current_setting('search_path') AS path");
|
|
377
|
+
const savedPath = saved.rows[0]?.path;
|
|
378
|
+
const path = typeof savedPath === "string" ? savedPath : `${this.segment}, public`;
|
|
379
|
+
await client.query("SELECT set_config('search_path', 'public', false)");
|
|
380
|
+
const setBack = async () => {
|
|
381
|
+
try {
|
|
382
|
+
await client.query("SELECT set_config($1, $2, false)", ["search_path", path]);
|
|
383
|
+
} catch (error) {
|
|
384
|
+
onBroken(asError(error));
|
|
385
|
+
throw error;
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
let result;
|
|
389
|
+
try {
|
|
390
|
+
result = await client.query(text, values);
|
|
391
|
+
} catch (error) {
|
|
392
|
+
await setBack().catch(() => void 0);
|
|
393
|
+
throw error;
|
|
394
|
+
}
|
|
395
|
+
await setBack();
|
|
396
|
+
await this.checkReach((t, v) => client.query(t, v), extension);
|
|
397
|
+
return result;
|
|
398
|
+
}
|
|
399
|
+
async checkReach(query, extension) {
|
|
400
|
+
if (this.reported.has(extension))
|
|
401
|
+
return;
|
|
402
|
+
const found = await query(EXTENSION_PLACE, [extension]);
|
|
403
|
+
const place = found.rows[0];
|
|
404
|
+
if (!place || place.relocatable !== true)
|
|
405
|
+
return;
|
|
406
|
+
if (place.schema === "public" || place.schema === this.segment)
|
|
407
|
+
return;
|
|
408
|
+
this.reported.add(extension);
|
|
409
|
+
(0, import_logger.getStorageLogger)().warn({ adapter: "postgres", segment: this.segment, extension, schema: String(place.schema) }, `PostgreSQL extension "${extension}" is installed in schema "${String(place.schema)}", which segment "${this.segment}" does not reach: its search_path is ${this.segment}, public, so the extension's types and functions do not resolve here. ALTER EXTENSION "${extension}" SET SCHEMA public moves it where every segment finds it.`);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
const hasEnded = (pool) => pool.ending === true || pool.ended === true;
|
|
413
|
+
const warnOfIdleError = (error) => {
|
|
414
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
415
|
+
(0, import_logger.getStorageLogger)().warn({ adapter: "postgres", error: message }, `PostgreSQL: an idle connection of the pool failed; the pool opens another when one is needed. ${message}`);
|
|
416
|
+
};
|
|
417
|
+
const readEnv = (key) => typeof process !== "undefined" && process.env ? process.env[key] : void 0;
|
|
418
|
+
const refuseOwnStartupOptions = (connectionString) => {
|
|
419
|
+
if (connectionString && /[?&]options=/i.test(connectionString)) {
|
|
420
|
+
throw new Error("PostgresAdapter: a segment cannot be used with a connection string that sets its own startup options (options=...): pg sends those in place of the segment's search_path. Move them out of the connection string, into PGOPTIONS.");
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
const createSchema = async (client, segment) => {
|
|
424
|
+
const found = await client.query("SELECT 1 FROM pg_namespace WHERE nspname = $1", [segment]);
|
|
425
|
+
if (found.rows.length > 0) {
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
try {
|
|
429
|
+
await client.query(`CREATE SCHEMA IF NOT EXISTS "${segment}"`);
|
|
430
|
+
} catch (error) {
|
|
431
|
+
const code = error?.code;
|
|
432
|
+
if (code !== "23505" && code !== "42P06") {
|
|
433
|
+
throw error;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
class PostgresTransactionAdapter {
|
|
438
|
+
constructor(client, capabilities, segment, afterEnd) {
|
|
439
|
+
this.capabilities = capabilities;
|
|
440
|
+
this.afterEnd = afterEnd;
|
|
441
|
+
this.kind = "postgres";
|
|
442
|
+
this.ended = false;
|
|
443
|
+
this.query = segment ? (text, values) => segment.inTransaction(client, text, values) : (text, values) => client.query(text, values);
|
|
444
|
+
}
|
|
445
|
+
async open() {
|
|
446
|
+
}
|
|
447
|
+
async run(statement, parameters) {
|
|
448
|
+
if (this.ended)
|
|
449
|
+
return this.afterEnd.run(statement, parameters);
|
|
450
|
+
return runWith(this.query, statement, parameters);
|
|
451
|
+
}
|
|
452
|
+
async get(statement, parameters) {
|
|
453
|
+
if (this.ended)
|
|
454
|
+
return this.afterEnd.get(statement, parameters);
|
|
455
|
+
return getWith(this.query, statement, parameters);
|
|
456
|
+
}
|
|
457
|
+
async all(statement, parameters) {
|
|
458
|
+
if (this.ended)
|
|
459
|
+
return this.afterEnd.all(statement, parameters);
|
|
460
|
+
return allWith(this.query, statement, parameters);
|
|
461
|
+
}
|
|
462
|
+
async exec(script) {
|
|
463
|
+
if (this.ended)
|
|
464
|
+
return this.afterEnd.exec(script);
|
|
465
|
+
await execWith(this.query, script);
|
|
466
|
+
}
|
|
467
|
+
async transaction(fn) {
|
|
468
|
+
if (this.ended)
|
|
469
|
+
return this.afterEnd.transaction(fn);
|
|
470
|
+
return fn(this);
|
|
471
|
+
}
|
|
472
|
+
async close() {
|
|
473
|
+
}
|
|
474
|
+
/** @internal runTransaction() calls this when the transaction has ended. */
|
|
475
|
+
end() {
|
|
476
|
+
this.ended = true;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
const runTransaction = async (client, begin, fn, capabilities, segment, afterEnd, onBroken) => {
|
|
480
|
+
try {
|
|
481
|
+
await client.query(begin);
|
|
482
|
+
} catch (error) {
|
|
483
|
+
onBroken(asError(error));
|
|
484
|
+
throw error;
|
|
485
|
+
}
|
|
486
|
+
const handle = new PostgresTransactionAdapter(client, capabilities, segment, afterEnd);
|
|
487
|
+
try {
|
|
488
|
+
const result = await fn(handle);
|
|
489
|
+
await client.query("COMMIT");
|
|
490
|
+
return result;
|
|
491
|
+
} catch (error) {
|
|
492
|
+
try {
|
|
493
|
+
await client.query("ROLLBACK");
|
|
494
|
+
} catch (rollbackError) {
|
|
495
|
+
const failure = asError(rollbackError);
|
|
496
|
+
onBroken(failure);
|
|
497
|
+
keepRollbackError(error, failure);
|
|
498
|
+
(0, import_logger.getStorageLogger)().warn({ adapter: "postgres", error: failure.message }, `PostgreSQL ROLLBACK failed after a transaction error, so the connection was closed; the transaction's own error is rethrown. ${failure.message}`);
|
|
499
|
+
}
|
|
500
|
+
throw error;
|
|
501
|
+
} finally {
|
|
502
|
+
handle.end();
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
class PostgresConnectionAdapter {
|
|
506
|
+
constructor(client, capabilities, segment) {
|
|
507
|
+
this.client = client;
|
|
508
|
+
this.capabilities = capabilities;
|
|
509
|
+
this.segment = segment;
|
|
510
|
+
this.kind = "postgres";
|
|
511
|
+
this.ended = false;
|
|
512
|
+
this.transactionOpen = false;
|
|
513
|
+
this.onBroken = (error) => {
|
|
514
|
+
if (!this.brokenBy)
|
|
515
|
+
this.brokenBy = error;
|
|
516
|
+
};
|
|
517
|
+
this.query = (text, values) => {
|
|
518
|
+
const live = this.live();
|
|
519
|
+
return segment ? segment.onConnection(live, text, values, this.onBroken) : live.query(text, values);
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
async open() {
|
|
523
|
+
}
|
|
524
|
+
async run(statement, parameters) {
|
|
525
|
+
return runWith(this.query, statement, parameters);
|
|
526
|
+
}
|
|
527
|
+
async get(statement, parameters) {
|
|
528
|
+
return getWith(this.query, statement, parameters);
|
|
529
|
+
}
|
|
530
|
+
async all(statement, parameters) {
|
|
531
|
+
return allWith(this.query, statement, parameters);
|
|
532
|
+
}
|
|
533
|
+
async exec(script) {
|
|
534
|
+
await execWith(this.query, script);
|
|
535
|
+
}
|
|
536
|
+
async transaction(fn, options) {
|
|
537
|
+
const begin = beginStatement(options);
|
|
538
|
+
const client = this.live();
|
|
539
|
+
if (this.transactionOpen) {
|
|
540
|
+
throw new Error("A transaction is already open on this connection. Run its statements through the handle its callback receives.");
|
|
541
|
+
}
|
|
542
|
+
this.transactionOpen = true;
|
|
543
|
+
try {
|
|
544
|
+
return await runTransaction(client, begin, fn, this.capabilities, this.segment, this, this.onBroken);
|
|
545
|
+
} finally {
|
|
546
|
+
this.transactionOpen = false;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
async close() {
|
|
550
|
+
}
|
|
551
|
+
/** @internal withConnection() calls this when the callback ends. */
|
|
552
|
+
end() {
|
|
553
|
+
this.ended = true;
|
|
554
|
+
}
|
|
555
|
+
live() {
|
|
556
|
+
if (this.ended) {
|
|
557
|
+
throw new Error("This connection was handed back when its withConnection() callback ended.");
|
|
558
|
+
}
|
|
559
|
+
return this.client;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
class PostgresAdapter {
|
|
563
|
+
constructor(options) {
|
|
564
|
+
this.kind = "postgres";
|
|
565
|
+
this.capabilities = /* @__PURE__ */ new Set([
|
|
566
|
+
"transactions",
|
|
567
|
+
// Full ACID transaction support
|
|
568
|
+
"locks",
|
|
569
|
+
// Row-level and advisory locks
|
|
570
|
+
"persistence",
|
|
571
|
+
// Data persisted to disk
|
|
572
|
+
"concurrent",
|
|
573
|
+
// Excellent concurrent access
|
|
574
|
+
"json",
|
|
575
|
+
// Native JSON/JSONB support
|
|
576
|
+
"arrays",
|
|
577
|
+
// Native array data types
|
|
578
|
+
"prepared"
|
|
579
|
+
// Prepared statements for security/performance
|
|
580
|
+
]);
|
|
581
|
+
this.pool = null;
|
|
582
|
+
this.pgModule = null;
|
|
583
|
+
this.listening = null;
|
|
584
|
+
this.opening = null;
|
|
585
|
+
this.closing = null;
|
|
586
|
+
this.query = (text, values) => {
|
|
587
|
+
const pool = this.requirePool();
|
|
588
|
+
return this.segmentStatements ? this.segmentStatements.onPool(pool, text, values) : pool.query(text, values);
|
|
589
|
+
};
|
|
590
|
+
this.options = typeof options === "string" ? { connectionString: options } : { ...options };
|
|
591
|
+
const segment = this.options.segment === void 0 ? void 0 : (0, import_segment.validateSegment)(this.options.segment);
|
|
592
|
+
this.segmentStatements = segment === void 0 ? null : new SegmentStatements(segment);
|
|
593
|
+
const given = this.options.pool;
|
|
594
|
+
if (given) {
|
|
595
|
+
if (segment !== void 0) {
|
|
596
|
+
throw new Error("PostgresAdapter: a segment cannot run on a pool its caller made: the segment's search_path is set when each connection of the pool starts, and the adapter does not start the connections of a pool it did not make. Give the connection settings and the segment instead of the pool.");
|
|
597
|
+
}
|
|
598
|
+
const settings = POOL_SETTINGS.filter((key) => this.options[key] !== void 0);
|
|
599
|
+
if (settings.length > 0) {
|
|
600
|
+
throw new Error(`PostgresAdapter: ${settings.join(", ")} cannot be given with pool: they configure a pool when one is made, and the pool given was made by its caller. Set them on that pool` + (settings.includes("typeParsers") ? " (typeParsers through the pool's own types option)." : "."));
|
|
601
|
+
}
|
|
602
|
+
this.givenPool = given;
|
|
603
|
+
this.ownsPool = this.options.ownsPool === true;
|
|
604
|
+
this.pool = given;
|
|
605
|
+
this.listenOn(given);
|
|
606
|
+
} else {
|
|
607
|
+
this.givenPool = null;
|
|
608
|
+
this.ownsPool = true;
|
|
609
|
+
if (segment !== void 0) {
|
|
610
|
+
refuseOwnStartupOptions(this.options.connectionString);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* The pool the adapter runs on: the pool it was given, or the pool it made
|
|
616
|
+
* while it is open. Null while the adapter is closed.
|
|
617
|
+
*/
|
|
618
|
+
getPool() {
|
|
619
|
+
return this.pool;
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Lazy loader for pg module to keep the dependency optional and prevent bundling in browser builds.
|
|
623
|
+
*/
|
|
624
|
+
async loadPgModule() {
|
|
625
|
+
if (this.pgModule) {
|
|
626
|
+
return this.pgModule;
|
|
627
|
+
}
|
|
628
|
+
if (__isBrowser) {
|
|
629
|
+
throw new Error("[StorageAdapter] PostgreSQL adapter cannot be used in a browser environment.");
|
|
630
|
+
}
|
|
631
|
+
try {
|
|
632
|
+
this.pgModule = await Promise.resolve().then(() => __toESM(require("pg")));
|
|
633
|
+
return this.pgModule;
|
|
634
|
+
} catch (error) {
|
|
635
|
+
throw new Error(`[StorageAdapter] Failed to load pg module. Install it with: npm install pg. Error: ${error}`);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Opens the adapter: makes its pool and checks that a connection can be
|
|
640
|
+
* made (and with a segment, creates the segment's schema when it is
|
|
641
|
+
* missing), or takes up the pool it was given. A failure ends the pool it
|
|
642
|
+
* made, so a later `open()` starts again. Two calls at once open once.
|
|
643
|
+
*/
|
|
644
|
+
open(openOptions) {
|
|
645
|
+
if (this.pool) {
|
|
646
|
+
return Promise.resolve();
|
|
647
|
+
}
|
|
648
|
+
if (!this.opening) {
|
|
649
|
+
this.opening = this.openNow(openOptions).finally(() => {
|
|
650
|
+
this.opening = null;
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
return this.opening;
|
|
654
|
+
}
|
|
655
|
+
async openNow(openOptions) {
|
|
656
|
+
if (__isBrowser) {
|
|
657
|
+
throw new Error("[StorageAdapter] PostgreSQL adapter cannot be opened in a browser environment.");
|
|
658
|
+
}
|
|
659
|
+
if (this.givenPool) {
|
|
660
|
+
if (hasEnded(this.givenPool)) {
|
|
661
|
+
throw new Error("PostgresAdapter: the pool it was given has ended, so the adapter cannot open on it again.");
|
|
662
|
+
}
|
|
663
|
+
this.pool = this.givenPool;
|
|
664
|
+
this.listenOn(this.givenPool);
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
const connectionString = openOptions?.connectionString ?? this.options.connectionString;
|
|
668
|
+
if (this.segmentStatements) {
|
|
669
|
+
refuseOwnStartupOptions(connectionString);
|
|
670
|
+
}
|
|
671
|
+
const pg = await this.loadPgModule();
|
|
672
|
+
const pool = new pg.Pool(this.poolConfig(pg, connectionString));
|
|
673
|
+
this.listenOn(pool);
|
|
674
|
+
try {
|
|
675
|
+
const client = await pool.connect();
|
|
676
|
+
try {
|
|
677
|
+
if (this.segmentStatements) {
|
|
678
|
+
await createSchema(client, this.segmentStatements.segment);
|
|
679
|
+
}
|
|
680
|
+
} finally {
|
|
681
|
+
client.release();
|
|
682
|
+
}
|
|
683
|
+
} catch (error) {
|
|
684
|
+
this.listening = null;
|
|
685
|
+
await pool.end().catch(() => void 0);
|
|
686
|
+
throw error;
|
|
687
|
+
}
|
|
688
|
+
this.pool = pool;
|
|
689
|
+
}
|
|
690
|
+
/** The configuration of the pool the adapter makes. */
|
|
691
|
+
poolConfig(pg, connectionString) {
|
|
692
|
+
const poolConfig = {};
|
|
693
|
+
if (connectionString) {
|
|
694
|
+
poolConfig.connectionString = connectionString;
|
|
695
|
+
} else {
|
|
696
|
+
poolConfig.host = this.options.host ?? "localhost";
|
|
697
|
+
poolConfig.port = this.options.port ?? 5432;
|
|
698
|
+
poolConfig.database = this.options.database;
|
|
699
|
+
poolConfig.user = this.options.user;
|
|
700
|
+
poolConfig.password = this.options.password;
|
|
701
|
+
}
|
|
702
|
+
if (this.options.max !== void 0)
|
|
703
|
+
poolConfig.max = this.options.max;
|
|
704
|
+
if (this.options.min !== void 0)
|
|
705
|
+
poolConfig.min = this.options.min;
|
|
706
|
+
if (this.options.idleTimeoutMillis !== void 0) {
|
|
707
|
+
poolConfig.idleTimeoutMillis = this.options.idleTimeoutMillis;
|
|
708
|
+
}
|
|
709
|
+
if (this.options.connectionTimeoutMillis !== void 0) {
|
|
710
|
+
poolConfig.connectionTimeoutMillis = this.options.connectionTimeoutMillis;
|
|
711
|
+
}
|
|
712
|
+
if (this.options.application_name !== void 0) {
|
|
713
|
+
poolConfig.application_name = this.options.application_name;
|
|
714
|
+
}
|
|
715
|
+
if (this.options.statement_timeout !== void 0) {
|
|
716
|
+
poolConfig.statement_timeout = this.options.statement_timeout;
|
|
717
|
+
}
|
|
718
|
+
if (this.options.query_timeout !== void 0) {
|
|
719
|
+
poolConfig.query_timeout = this.options.query_timeout;
|
|
720
|
+
}
|
|
721
|
+
if (this.options.allowExitOnIdle !== void 0) {
|
|
722
|
+
poolConfig.allowExitOnIdle = this.options.allowExitOnIdle;
|
|
723
|
+
}
|
|
724
|
+
const overrides = typeParserOverrides(this.options.typeParsers);
|
|
725
|
+
if (overrides.size > 0) {
|
|
726
|
+
const defaults = pg.types;
|
|
727
|
+
poolConfig.types = {
|
|
728
|
+
getTypeParser: (oid, format) => (format === void 0 || format === "text") && overrides.has(oid) ? overrides.get(oid) : defaults.getTypeParser(oid, format)
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
if (this.options.ssl !== void 0) {
|
|
732
|
+
poolConfig.ssl = this.options.ssl;
|
|
733
|
+
}
|
|
734
|
+
if (this.segmentStatements) {
|
|
735
|
+
const searchPath = `-c search_path=${this.segmentStatements.segment},public`;
|
|
736
|
+
const inherited = readEnv("PGOPTIONS")?.trim();
|
|
737
|
+
poolConfig.options = inherited ? `${inherited} ${searchPath}` : searchPath;
|
|
738
|
+
}
|
|
739
|
+
return poolConfig;
|
|
740
|
+
}
|
|
741
|
+
/** Puts the adapter's error listener on `pool`: `onError`, or the warning on a pool it owns. */
|
|
742
|
+
listenOn(pool) {
|
|
743
|
+
const listener = this.options.onError ?? (this.ownsPool ? warnOfIdleError : null);
|
|
744
|
+
if (!listener || typeof pool.on !== "function" || this.listening?.pool === pool)
|
|
745
|
+
return;
|
|
746
|
+
pool.on("error", listener);
|
|
747
|
+
this.listening = { pool, listener };
|
|
748
|
+
}
|
|
749
|
+
async run(statement, parameters) {
|
|
750
|
+
return runWith(this.query, statement, parameters);
|
|
751
|
+
}
|
|
752
|
+
async get(statement, parameters) {
|
|
753
|
+
return getWith(this.query, statement, parameters);
|
|
754
|
+
}
|
|
755
|
+
async all(statement, parameters) {
|
|
756
|
+
return allWith(this.query, statement, parameters);
|
|
757
|
+
}
|
|
758
|
+
async exec(script) {
|
|
759
|
+
await execWith(this.query, script);
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Runs `fn` in a transaction on one pooled connection: COMMIT when it
|
|
763
|
+
* resolves, ROLLBACK when it or COMMIT throws, and that error is rethrown.
|
|
764
|
+
* `options` sets the isolation level and the read-only and deferrable modes;
|
|
765
|
+
* an unknown isolation level is refused before a connection is checked out.
|
|
766
|
+
*
|
|
767
|
+
* When ROLLBACK fails too (the connection broke), the error thrown is still
|
|
768
|
+
* the transaction's own, with the ROLLBACK error on it as `rollbackError`,
|
|
769
|
+
* and the connection is closed instead of returned to the pool. The handle
|
|
770
|
+
* `fn` receives runs on the pool once the transaction has ended.
|
|
771
|
+
*/
|
|
772
|
+
async transaction(fn, options) {
|
|
773
|
+
const begin = beginStatement(options);
|
|
774
|
+
const client = await this.requirePool().connect();
|
|
775
|
+
const watch = watchClient(client);
|
|
776
|
+
let brokenBy;
|
|
777
|
+
try {
|
|
778
|
+
return await runTransaction(client, begin, fn, this.capabilities, this.segmentStatements, this, (error) => {
|
|
779
|
+
if (!brokenBy)
|
|
780
|
+
brokenBy = error;
|
|
781
|
+
});
|
|
782
|
+
} finally {
|
|
783
|
+
watch.stop();
|
|
784
|
+
client.release(brokenBy ?? watch.failure());
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
/**
|
|
788
|
+
* Holds one pooled connection for `fn`, for work that needs one session:
|
|
789
|
+
* `SET` and other session settings, session advisory locks
|
|
790
|
+
* (`pg_advisory_lock`), temporary tables, or statements that must not run
|
|
791
|
+
* inside a transaction. Its statements run outside a transaction unless
|
|
792
|
+
* `fn` opens one with `connection.transaction()` or `BEGIN`.
|
|
793
|
+
*
|
|
794
|
+
* When `fn` resolves, the connection goes back to the pool with its session
|
|
795
|
+
* state, so undo what `fn` set (or use `SET LOCAL` inside a transaction).
|
|
796
|
+
* When `fn` throws, the connection is closed instead, which ends its session
|
|
797
|
+
* state and any transaction it left open, and `fn`'s error is rethrown.
|
|
798
|
+
* When `fn` resolves inside a transaction it began and did not end, the
|
|
799
|
+
* connection is closed, which rolls the transaction back, and the call
|
|
800
|
+
* rejects (pg 8.21 and later report the transaction status). The connection
|
|
801
|
+
* refuses statements once `fn` has ended. A connection that broke while `fn`
|
|
802
|
+
* held it (the server ended the session) is closed, not returned. On a
|
|
803
|
+
* segmented adapter the connection starts in the segment, as every
|
|
804
|
+
* connection of its pool does.
|
|
805
|
+
*/
|
|
806
|
+
async withConnection(fn) {
|
|
807
|
+
const client = await this.requirePool().connect();
|
|
808
|
+
const watch = watchClient(client);
|
|
809
|
+
const connection = new PostgresConnectionAdapter(client, this.capabilities, this.segmentStatements);
|
|
810
|
+
let releaseError;
|
|
811
|
+
try {
|
|
812
|
+
const result = await fn(connection);
|
|
813
|
+
const status = transactionStatus(client);
|
|
814
|
+
if (status === "T" || status === "E") {
|
|
815
|
+
releaseError = new Error("withConnection(): the callback returned inside an open transaction. The connection was closed, which rolled the transaction back. End each transaction the callback begins, or run it through connection.transaction().");
|
|
816
|
+
throw releaseError;
|
|
817
|
+
}
|
|
818
|
+
return result;
|
|
819
|
+
} catch (error) {
|
|
820
|
+
if (!releaseError) {
|
|
821
|
+
releaseError = error instanceof Error ? error : new Error(String(error));
|
|
822
|
+
}
|
|
823
|
+
throw error;
|
|
824
|
+
} finally {
|
|
825
|
+
connection.end();
|
|
826
|
+
watch.stop();
|
|
827
|
+
client.release(releaseError ?? connection.brokenBy ?? watch.failure());
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
/**
|
|
831
|
+
* The pool's counts, or null when the adapter is not open. A pool of the
|
|
832
|
+
* caller's that does not count its connections, as pg's Pool does, reads 0.
|
|
833
|
+
*/
|
|
834
|
+
getPoolStats() {
|
|
835
|
+
const pool = this.pool;
|
|
836
|
+
if (!pool) {
|
|
837
|
+
return null;
|
|
838
|
+
}
|
|
839
|
+
return { total: pool.totalCount ?? 0, idle: pool.idleCount ?? 0, waiting: pool.waitingCount ?? 0 };
|
|
840
|
+
}
|
|
841
|
+
/**
|
|
842
|
+
* Closes the adapter. A pool the adapter made or owns is ended, once; a pool
|
|
843
|
+
* of its caller's that it does not own is left open, with the adapter's
|
|
844
|
+
* listener taken off it. A call made while a close is running waits for it.
|
|
845
|
+
*/
|
|
846
|
+
close() {
|
|
847
|
+
if (!this.closing) {
|
|
848
|
+
this.closing = this.closeNow().finally(() => {
|
|
849
|
+
this.closing = null;
|
|
850
|
+
});
|
|
851
|
+
}
|
|
852
|
+
return this.closing;
|
|
853
|
+
}
|
|
854
|
+
async closeNow() {
|
|
855
|
+
if (this.opening) {
|
|
856
|
+
await this.opening.catch(() => void 0);
|
|
857
|
+
}
|
|
858
|
+
const pool = this.pool;
|
|
859
|
+
this.pool = null;
|
|
860
|
+
if (!pool) {
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
const listening = this.listening?.pool === pool ? this.listening : null;
|
|
864
|
+
this.listening = null;
|
|
865
|
+
if (this.ownsPool) {
|
|
866
|
+
if (!hasEnded(pool)) {
|
|
867
|
+
await pool.end();
|
|
868
|
+
}
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
if (listening && typeof pool.removeListener === "function") {
|
|
872
|
+
pool.removeListener("error", listening.listener);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
requirePool() {
|
|
876
|
+
if (!this.pool) {
|
|
877
|
+
throw new Error("Postgres adapter not opened.");
|
|
878
|
+
}
|
|
879
|
+
return this.pool;
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
function createPostgresAdapter(optionsOrString) {
|
|
883
|
+
return new PostgresAdapter(optionsOrString);
|
|
884
|
+
}
|
|
885
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
886
|
+
0 && (module.exports = {
|
|
887
|
+
PostgresAdapter,
|
|
888
|
+
createPostgresAdapter
|
|
889
|
+
});
|