@rudderjs/database 1.1.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -0
- package/dist/db.d.ts +21 -3
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +27 -5
- package/dist/db.js.map +1 -1
- package/dist/index.d.ts +14 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -4
- package/dist/index.js.map +1 -1
- package/dist/native/adapter.d.ts +202 -0
- package/dist/native/adapter.d.ts.map +1 -0
- package/dist/native/adapter.js +440 -0
- package/dist/native/adapter.js.map +1 -0
- package/dist/native/compiler.d.ts +371 -0
- package/dist/native/compiler.d.ts.map +1 -0
- package/dist/native/compiler.js +978 -0
- package/dist/native/compiler.js.map +1 -0
- package/dist/native/dialect-mysql.d.ts +26 -0
- package/dist/native/dialect-mysql.d.ts.map +1 -0
- package/dist/native/dialect-mysql.js +188 -0
- package/dist/native/dialect-mysql.js.map +1 -0
- package/dist/native/dialect-pg.d.ts +26 -0
- package/dist/native/dialect-pg.d.ts.map +1 -0
- package/dist/native/dialect-pg.js +192 -0
- package/dist/native/dialect-pg.js.map +1 -0
- package/dist/native/dialect.d.ts +255 -0
- package/dist/native/dialect.d.ts.map +1 -0
- package/dist/native/dialect.js +237 -0
- package/dist/native/dialect.js.map +1 -0
- package/dist/native/driver.d.ts +37 -0
- package/dist/native/driver.d.ts.map +1 -0
- package/dist/native/driver.js +19 -0
- package/dist/native/driver.js.map +1 -0
- package/dist/native/drivers/better-sqlite3.d.ts +56 -0
- package/dist/native/drivers/better-sqlite3.d.ts.map +1 -0
- package/dist/native/drivers/better-sqlite3.js +171 -0
- package/dist/native/drivers/better-sqlite3.js.map +1 -0
- package/dist/native/drivers/mysql.d.ts +30 -0
- package/dist/native/drivers/mysql.d.ts.map +1 -0
- package/dist/native/drivers/mysql.js +176 -0
- package/dist/native/drivers/mysql.js.map +1 -0
- package/dist/native/drivers/postgres.d.ts +57 -0
- package/dist/native/drivers/postgres.d.ts.map +1 -0
- package/dist/native/drivers/postgres.js +155 -0
- package/dist/native/drivers/postgres.js.map +1 -0
- package/dist/native/errors.d.ts +43 -0
- package/dist/native/errors.d.ts.map +1 -0
- package/dist/native/errors.js +64 -0
- package/dist/native/errors.js.map +1 -0
- package/dist/native/index.d.ts +27 -0
- package/dist/native/index.d.ts.map +1 -0
- package/dist/native/index.js +55 -0
- package/dist/native/index.js.map +1 -0
- package/dist/native/isolation.d.ts +14 -0
- package/dist/native/isolation.d.ts.map +1 -0
- package/dist/native/isolation.js +37 -0
- package/dist/native/isolation.js.map +1 -0
- package/dist/native/query-builder.d.ts +303 -0
- package/dist/native/query-builder.d.ts.map +1 -0
- package/dist/native/query-builder.js +984 -0
- package/dist/native/query-builder.js.map +1 -0
- package/dist/native/replica-picker.d.ts +22 -0
- package/dist/native/replica-picker.d.ts.map +1 -0
- package/dist/native/replica-picker.js +65 -0
- package/dist/native/replica-picker.js.map +1 -0
- package/dist/native/schema/alter-blueprint.d.ts +37 -0
- package/dist/native/schema/alter-blueprint.d.ts.map +1 -0
- package/dist/native/schema/alter-blueprint.js +56 -0
- package/dist/native/schema/alter-blueprint.js.map +1 -0
- package/dist/native/schema/blueprint.d.ts +151 -0
- package/dist/native/schema/blueprint.d.ts.map +1 -0
- package/dist/native/schema/blueprint.js +286 -0
- package/dist/native/schema/blueprint.js.map +1 -0
- package/dist/native/schema/column.d.ts +168 -0
- package/dist/native/schema/column.d.ts.map +1 -0
- package/dist/native/schema/column.js +190 -0
- package/dist/native/schema/column.js.map +1 -0
- package/dist/native/schema/ddl-compiler.d.ts +34 -0
- package/dist/native/schema/ddl-compiler.d.ts.map +1 -0
- package/dist/native/schema/ddl-compiler.js +352 -0
- package/dist/native/schema/ddl-compiler.js.map +1 -0
- package/dist/native/schema/inspect.d.ts +67 -0
- package/dist/native/schema/inspect.d.ts.map +1 -0
- package/dist/native/schema/inspect.js +312 -0
- package/dist/native/schema/inspect.js.map +1 -0
- package/dist/native/schema/introspect.d.ts +34 -0
- package/dist/native/schema/introspect.d.ts.map +1 -0
- package/dist/native/schema/introspect.js +101 -0
- package/dist/native/schema/introspect.js.map +1 -0
- package/dist/native/schema/migration.d.ts +8 -0
- package/dist/native/schema/migration.d.ts.map +1 -0
- package/dist/native/schema/migration.js +19 -0
- package/dist/native/schema/migration.js.map +1 -0
- package/dist/native/schema/migrator.d.ts +144 -0
- package/dist/native/schema/migrator.d.ts.map +1 -0
- package/dist/native/schema/migrator.js +240 -0
- package/dist/native/schema/migrator.js.map +1 -0
- package/dist/native/schema/rebuild.d.ts +11 -0
- package/dist/native/schema/rebuild.d.ts.map +1 -0
- package/dist/native/schema/rebuild.js +92 -0
- package/dist/native/schema/rebuild.js.map +1 -0
- package/dist/native/schema/schema-builder.d.ts +46 -0
- package/dist/native/schema/schema-builder.d.ts.map +1 -0
- package/dist/native/schema/schema-builder.js +153 -0
- package/dist/native/schema/schema-builder.js.map +1 -0
- package/dist/native/schema/schema-facade.d.ts +63 -0
- package/dist/native/schema/schema-facade.d.ts.map +1 -0
- package/dist/native/schema/schema-facade.js +124 -0
- package/dist/native/schema/schema-facade.js.map +1 -0
- package/dist/native/schema/schema-types.d.ts +27 -0
- package/dist/native/schema/schema-types.d.ts.map +1 -0
- package/dist/native/schema/schema-types.js +52 -0
- package/dist/native/schema/schema-types.js.map +1 -0
- package/dist/native/schema/types-generator.d.ts +73 -0
- package/dist/native/schema/types-generator.d.ts.map +1 -0
- package/dist/native/schema/types-generator.js +181 -0
- package/dist/native/schema/types-generator.js.map +1 -0
- package/dist/registry-bridge.d.ts +24 -4
- package/dist/registry-bridge.d.ts.map +1 -1
- package/dist/registry-bridge.js +20 -0
- package/dist/registry-bridge.js.map +1 -1
- package/dist/sticky.d.ts +22 -0
- package/dist/sticky.d.ts.map +1 -0
- package/dist/sticky.js +61 -0
- package/dist/sticky.js.map +1 -0
- package/package.json +32 -2
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// ─── better-sqlite3 Driver (Node) ──────────────────────────
|
|
2
|
+
//
|
|
3
|
+
// Concrete {@link Driver} over the `better-sqlite3` package. NODE-ONLY:
|
|
4
|
+
// `better-sqlite3` is an optional peer of `@rudderjs/orm` and is loaded with a
|
|
5
|
+
// lazy `await import()` inside `open()` so this module never drags the native
|
|
6
|
+
// addon into a client bundle at eval time (cross-phase rule 5).
|
|
7
|
+
//
|
|
8
|
+
// better-sqlite3 is synchronous; we wrap its calls in resolved promises to
|
|
9
|
+
// satisfy the async {@link Driver} contract shared with RN/WASM drivers.
|
|
10
|
+
import { NativeDriverError } from '../errors.js';
|
|
11
|
+
/**
|
|
12
|
+
* {@link Driver} backed by better-sqlite3. Construct via the static
|
|
13
|
+
* {@link BetterSqlite3Driver.open} factory, which performs the lazy import.
|
|
14
|
+
*/
|
|
15
|
+
export class BetterSqlite3Driver {
|
|
16
|
+
db;
|
|
17
|
+
/**
|
|
18
|
+
* Current transaction nesting depth. 0 = no open transaction; 1 = inside a
|
|
19
|
+
* top-level `BEGIN`; ≥2 = inside N−1 nested SAVEPOINTs. Drives the BEGIN-vs-
|
|
20
|
+
* SAVEPOINT choice in {@link transaction}. Single field is safe because
|
|
21
|
+
* better-sqlite3 is single-connection and synchronous — there is exactly one
|
|
22
|
+
* open transaction stack per driver instance.
|
|
23
|
+
*/
|
|
24
|
+
depth = 0;
|
|
25
|
+
constructor(db) {
|
|
26
|
+
this.db = db;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Resolve the `better-sqlite3` package, open the database, and return a
|
|
30
|
+
* ready driver. Throws {@link NativeDriverError} with install guidance when
|
|
31
|
+
* the package isn't present (it's an optional peer).
|
|
32
|
+
*/
|
|
33
|
+
static async open(config = {}) {
|
|
34
|
+
const filename = normalizeFilename(config.filename);
|
|
35
|
+
let Database;
|
|
36
|
+
try {
|
|
37
|
+
// `better-sqlite3` uses `export =`, so a dynamic import wraps it in
|
|
38
|
+
// `.default`. Fall back to the namespace object for older interop.
|
|
39
|
+
const mod = await import('better-sqlite3');
|
|
40
|
+
Database = mod.default ?? mod;
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
throw new NativeDriverError(`[RudderJS ORM native] Failed to load "better-sqlite3". It is an optional ` +
|
|
44
|
+
`peer of @rudderjs/orm — install it with \`pnpm add better-sqlite3\` to use ` +
|
|
45
|
+
`the native SQLite engine.`, { cause: err });
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
const db = new Database(filename, config.options);
|
|
49
|
+
return new BetterSqlite3Driver(db);
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
53
|
+
throw new NativeDriverError(`[RudderJS ORM native] Could not open SQLite database at ${JSON.stringify(filename)}: ${msg}`, { cause: err });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async execute(sql, bindings) {
|
|
57
|
+
const params = normalizeBindings(bindings);
|
|
58
|
+
const stmt = this.db.prepare(sql);
|
|
59
|
+
// `.all()` is only valid on statements that return rows. A SELECT — or any
|
|
60
|
+
// write with a `RETURNING` clause — is a reader; a plain INSERT/UPDATE/DELETE
|
|
61
|
+
// is not and routes through `.run()`, yielding no rows.
|
|
62
|
+
if (stmt.reader) {
|
|
63
|
+
return stmt.all(...params);
|
|
64
|
+
}
|
|
65
|
+
stmt.run(...params);
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Run `fn` inside a transaction. The top-level call wraps it in
|
|
70
|
+
* BEGIN/COMMIT/ROLLBACK; a nested call (depth ≥ 1) wraps it in a uniquely
|
|
71
|
+
* named SAVEPOINT so an inner failure rolls back only its own work, leaving
|
|
72
|
+
* the outer transaction intact. The transaction-scoped {@link Transaction} is
|
|
73
|
+
* the driver itself — better-sqlite3 is synchronous and single-connection, so
|
|
74
|
+
* every `execute` between the markers runs on the one open transaction.
|
|
75
|
+
*
|
|
76
|
+
* (We don't use better-sqlite3's own `db.transaction()` wrapper because it
|
|
77
|
+
* only accepts a *synchronous* function, and our `fn` is async — savepoints
|
|
78
|
+
* give us the same nesting semantics over an async callback.)
|
|
79
|
+
*
|
|
80
|
+
* **Single-connection caveat:** because there is one connection, two
|
|
81
|
+
* *concurrently* in-flight top-level `transaction()` calls would collide on
|
|
82
|
+
* the same connection. SQLite serializes writers anyway; the native engine
|
|
83
|
+
* assumes transactions are not run concurrently against one SQLite handle.
|
|
84
|
+
* Pooled drivers (pg/mysql, Phase 5/6) pin a dedicated client per transaction.
|
|
85
|
+
*/
|
|
86
|
+
async transaction(fn, opts) {
|
|
87
|
+
// SQLite has no SQL-standard isolation levels (one writer; readers see a
|
|
88
|
+
// serializable snapshot) — a requested level would silently mean nothing,
|
|
89
|
+
// so throw rather than no-op. Checked before the nesting branch: the error
|
|
90
|
+
// is the same at any depth.
|
|
91
|
+
if (opts?.isolationLevel) {
|
|
92
|
+
throw new Error('[RudderJS ORM native] SQLite does not support transaction isolation levels — ' +
|
|
93
|
+
'its single-writer model is already serializable. Drop the isolationLevel ' +
|
|
94
|
+
'option, or use the Postgres/MySQL engine.');
|
|
95
|
+
}
|
|
96
|
+
const top = this.depth === 0;
|
|
97
|
+
const savepoint = top ? null : `rudder_sp_${this.depth}`;
|
|
98
|
+
if (top)
|
|
99
|
+
this.db.exec('BEGIN');
|
|
100
|
+
else
|
|
101
|
+
this.db.exec(`SAVEPOINT ${savepoint}`);
|
|
102
|
+
this.depth++;
|
|
103
|
+
try {
|
|
104
|
+
const result = await fn(this);
|
|
105
|
+
if (top)
|
|
106
|
+
this.db.exec('COMMIT');
|
|
107
|
+
else
|
|
108
|
+
this.db.exec(`RELEASE ${savepoint}`);
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
try {
|
|
113
|
+
if (top) {
|
|
114
|
+
this.db.exec('ROLLBACK');
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
// Roll back to the savepoint, then release it so the saved name is
|
|
118
|
+
// discarded — otherwise it lingers on the outer transaction's stack.
|
|
119
|
+
this.db.exec(`ROLLBACK TO ${savepoint}`);
|
|
120
|
+
this.db.exec(`RELEASE ${savepoint}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
// A failed ROLLBACK (e.g. the transaction already aborted) must not mask
|
|
125
|
+
// the original error — swallow it and re-throw the cause below.
|
|
126
|
+
}
|
|
127
|
+
throw err;
|
|
128
|
+
}
|
|
129
|
+
finally {
|
|
130
|
+
this.depth--;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
async close() {
|
|
134
|
+
this.db.close();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* better-sqlite3 binds only numbers, strings, bigints, buffers, and `null` —
|
|
139
|
+
* a JS `boolean` throws `TypeError: SQLite3 can only bind …`. SQLite has no
|
|
140
|
+
* boolean type, so map `true`/`false` to the integers `1`/`0`; this round-trips
|
|
141
|
+
* with the ORM's `boolean` cast (which reads `0`/`1` back). The mapping covers
|
|
142
|
+
* raw boolean values that bypass a column cast — an untyped `where('flag', true)`
|
|
143
|
+
* predicate, or a `query().create({ flag: true })` on a column without a
|
|
144
|
+
* boolean cast. Other unbindable values (`Date`, plain objects) are passed
|
|
145
|
+
* through so better-sqlite3 still rejects them with its own clear error.
|
|
146
|
+
*
|
|
147
|
+
* Returns the original array reference when nothing needed coercion, so the
|
|
148
|
+
* common boolean-free path allocates nothing.
|
|
149
|
+
*
|
|
150
|
+
* Any future SQLite driver (libsql, op-sqlite for React Native) needs the same
|
|
151
|
+
* mapping — share this helper rather than re-deriving it per driver.
|
|
152
|
+
*/
|
|
153
|
+
function normalizeBindings(bindings) {
|
|
154
|
+
let hasBoolean = false;
|
|
155
|
+
for (const v of bindings) {
|
|
156
|
+
if (typeof v === 'boolean') {
|
|
157
|
+
hasBoolean = true;
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (!hasBoolean)
|
|
162
|
+
return bindings;
|
|
163
|
+
return bindings.map(v => (typeof v === 'boolean' ? (v ? 1 : 0) : v));
|
|
164
|
+
}
|
|
165
|
+
/** Strip a `file:` scheme and default to an in-memory database. */
|
|
166
|
+
function normalizeFilename(filename) {
|
|
167
|
+
if (!filename)
|
|
168
|
+
return ':memory:';
|
|
169
|
+
return filename.replace(/^file:/, '');
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=better-sqlite3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"better-sqlite3.js","sourceRoot":"","sources":["../../../src/native/drivers/better-sqlite3.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Driver, Transaction, Row, AffectingExecutor, AffectingResult, TransactionOptions } from '../driver.js';
|
|
2
|
+
/** Connection config for {@link MysqlDriver.open}. */
|
|
3
|
+
export interface MysqlDriverConfig {
|
|
4
|
+
/** MySQL connection string, e.g. `mysql://user:pass@host:3306/db`. */
|
|
5
|
+
url: string;
|
|
6
|
+
/** Forwarded to `mysql2.createPool()` (e.g. `connectionLimit`, `ssl`). */
|
|
7
|
+
options?: Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* {@link Driver} backed by `mysql2`. Construct via the static
|
|
11
|
+
* {@link MysqlDriver.open} factory, which lazy-imports the package, builds a
|
|
12
|
+
* pool, and validates connectivity up front. Autocommit statements run on the
|
|
13
|
+
* pool; `transaction()` reserves a dedicated connection.
|
|
14
|
+
*/
|
|
15
|
+
export declare class MysqlDriver implements Driver, AffectingExecutor {
|
|
16
|
+
private readonly pool;
|
|
17
|
+
private constructor();
|
|
18
|
+
/**
|
|
19
|
+
* Resolve `mysql2`, build a pool for `config.url`, validate the connection, and
|
|
20
|
+
* return a ready driver. Throws {@link NativeDriverError} with install guidance
|
|
21
|
+
* when the package is missing, or a connection error when the URL is
|
|
22
|
+
* unreachable — so failures surface at setup, not deep in a request.
|
|
23
|
+
*/
|
|
24
|
+
static open(config: MysqlDriverConfig): Promise<MysqlDriver>;
|
|
25
|
+
execute(sql: string, bindings: readonly unknown[]): Promise<Row[]>;
|
|
26
|
+
affectingExecute(sql: string, bindings: readonly unknown[]): Promise<AffectingResult>;
|
|
27
|
+
transaction<T>(fn: (tx: Transaction) => Promise<T>, opts?: TransactionOptions): Promise<T>;
|
|
28
|
+
close(): Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=mysql.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mysql.d.ts","sourceRoot":"","sources":["../../../src/native/drivers/mysql.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,iBAAiB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AA8BpH,sDAAsD;AACtD,MAAM,WAAW,iBAAiB;IAChC,sEAAsE;IACtE,GAAG,EAAE,MAAM,CAAA;IACX,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAClC;AA4DD;;;;;GAKG;AACH,qBAAa,WAAY,YAAW,MAAM,EAAE,iBAAiB;IACvC,OAAO,CAAC,QAAQ,CAAC,IAAI;IAAzC,OAAO;IAEP;;;;;OAKG;WACU,IAAI,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,WAAW,CAAC;IA2ClE,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAIlE,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,eAAe,CAAC;IAU/E,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,CAAC,CAAC;IAmB1F,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// ─── MySQL Driver (Node, mysql2/promise) ───────────────────
|
|
2
|
+
//
|
|
3
|
+
// Concrete {@link Driver} over the `mysql2` package (its `mysql2/promise` API).
|
|
4
|
+
// NODE-ONLY: `mysql2` is an optional peer of `@rudderjs/orm`, lazy-`import()`ed
|
|
5
|
+
// inside `open()` so this module never drags the driver into a client bundle at
|
|
6
|
+
// eval time (cross-phase rule 5). Mirrors the postgres driver's shape.
|
|
7
|
+
//
|
|
8
|
+
// Like postgres (and unlike single-connection better-sqlite3), mysql2 pools
|
|
9
|
+
// connections — so a transaction MUST pin one connection. The top-level driver
|
|
10
|
+
// runs autocommit statements on the pool; `transaction()` reserves a connection
|
|
11
|
+
// (`getConnection()` + `beginTransaction`), and nested transactions map to
|
|
12
|
+
// SAVEPOINTs on that pinned connection (same savepoint semantics the prisma/
|
|
13
|
+
// drizzle adapters use).
|
|
14
|
+
//
|
|
15
|
+
// MySQL has no `RETURNING`, so this driver also implements {@link
|
|
16
|
+
// AffectingExecutor}: writes report their auto-increment `insertId` and
|
|
17
|
+
// `affectedRows` from mysql2's `ResultSetHeader`, which the query builder's
|
|
18
|
+
// no-RETURNING path consumes.
|
|
19
|
+
import { NativeDriverError } from '../errors.js';
|
|
20
|
+
import { isolationLevelSql, nestedIsolationError } from '../isolation.js';
|
|
21
|
+
/** Monotonic counter for unique nested-transaction SAVEPOINT names. */
|
|
22
|
+
let savepointSeq = 0;
|
|
23
|
+
/** Run a statement on a queryable and normalize the row result to `Row[]`
|
|
24
|
+
* (a write's ResultSetHeader is not an array → no rows). */
|
|
25
|
+
async function runQuery(q, sql, bindings) {
|
|
26
|
+
const [result] = await q.query(sql, bindings);
|
|
27
|
+
return Array.isArray(result) ? result : [];
|
|
28
|
+
}
|
|
29
|
+
/** Run a write and read its metadata. A SELECT (array result) reports its row
|
|
30
|
+
* count as `affectedRows` and no `insertId`; a write reads the header. */
|
|
31
|
+
async function runAffecting(q, sql, bindings) {
|
|
32
|
+
const [result] = await q.query(sql, bindings);
|
|
33
|
+
if (Array.isArray(result))
|
|
34
|
+
return { insertId: null, affectedRows: result.length };
|
|
35
|
+
const header = result;
|
|
36
|
+
const insertId = typeof header.insertId === 'number' && header.insertId > 0 ? header.insertId : null;
|
|
37
|
+
return { insertId, affectedRows: header.affectedRows ?? 0 };
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* A {@link Transaction} scope pinned to one mysql2 connection. Every query on
|
|
41
|
+
* the scope runs on that connection, so a transaction-scoped query stays on the
|
|
42
|
+
* transaction's connection; nesting opens a SAVEPOINT on it.
|
|
43
|
+
*/
|
|
44
|
+
class MysqlScope {
|
|
45
|
+
conn;
|
|
46
|
+
constructor(conn) {
|
|
47
|
+
this.conn = conn;
|
|
48
|
+
}
|
|
49
|
+
execute(sql, bindings) {
|
|
50
|
+
return runQuery(this.conn, sql, bindings);
|
|
51
|
+
}
|
|
52
|
+
affectingExecute(sql, bindings) {
|
|
53
|
+
return runAffecting(this.conn, sql, bindings);
|
|
54
|
+
}
|
|
55
|
+
// Nested transaction → SAVEPOINT on the current (already-transactional)
|
|
56
|
+
// connection, so an inner failure rolls back only its own work. An isolation
|
|
57
|
+
// level is rejected — the open transaction's isolation is already fixed (MySQL
|
|
58
|
+
// itself errors on SET TRANSACTION while a transaction is in progress).
|
|
59
|
+
async transaction(fn, opts) {
|
|
60
|
+
if (opts?.isolationLevel)
|
|
61
|
+
throw nestedIsolationError();
|
|
62
|
+
const name = `rudder_sp_${(savepointSeq = (savepointSeq + 1) % Number.MAX_SAFE_INTEGER)}`;
|
|
63
|
+
await this.conn.query(`SAVEPOINT ${name}`);
|
|
64
|
+
try {
|
|
65
|
+
const result = await fn(this);
|
|
66
|
+
await this.conn.query(`RELEASE SAVEPOINT ${name}`);
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
try {
|
|
71
|
+
// Roll back to the savepoint, then release it so the name is discarded.
|
|
72
|
+
await this.conn.query(`ROLLBACK TO SAVEPOINT ${name}`);
|
|
73
|
+
await this.conn.query(`RELEASE SAVEPOINT ${name}`);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// A failed rollback must not mask the original error.
|
|
77
|
+
}
|
|
78
|
+
throw err;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* {@link Driver} backed by `mysql2`. Construct via the static
|
|
84
|
+
* {@link MysqlDriver.open} factory, which lazy-imports the package, builds a
|
|
85
|
+
* pool, and validates connectivity up front. Autocommit statements run on the
|
|
86
|
+
* pool; `transaction()` reserves a dedicated connection.
|
|
87
|
+
*/
|
|
88
|
+
export class MysqlDriver {
|
|
89
|
+
pool;
|
|
90
|
+
constructor(pool) {
|
|
91
|
+
this.pool = pool;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Resolve `mysql2`, build a pool for `config.url`, validate the connection, and
|
|
95
|
+
* return a ready driver. Throws {@link NativeDriverError} with install guidance
|
|
96
|
+
* when the package is missing, or a connection error when the URL is
|
|
97
|
+
* unreachable — so failures surface at setup, not deep in a request.
|
|
98
|
+
*/
|
|
99
|
+
static async open(config) {
|
|
100
|
+
let mod;
|
|
101
|
+
try {
|
|
102
|
+
// mysql2 ships its promise API at `mysql2/promise`.
|
|
103
|
+
mod = await import('mysql2/promise');
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
throw new NativeDriverError(`[RudderJS ORM native] Failed to load "mysql2". It is an optional peer of ` +
|
|
107
|
+
`@rudderjs/orm — install it with \`pnpm add mysql2\` to use the native MySQL engine.`, { cause: err });
|
|
108
|
+
}
|
|
109
|
+
const pool = mod.createPool({
|
|
110
|
+
uri: config.url,
|
|
111
|
+
// MySQL has no boolean type — `t.boolean()` columns are `tinyint(1)`
|
|
112
|
+
// (the BOOLEAN alias). Map them back to JS booleans on read so boolean
|
|
113
|
+
// columns round-trip like they do on Postgres (whose driver parses the
|
|
114
|
+
// native bool type). Only display-width-1 TINY columns qualify — a plain
|
|
115
|
+
// `t.tinyInt()` (width 4) stays numeric. `config.options` can override.
|
|
116
|
+
typeCast: (field, next) => {
|
|
117
|
+
if (field.type === 'TINY' && field.length === 1) {
|
|
118
|
+
const value = field.string();
|
|
119
|
+
return value === null ? null : value === '1';
|
|
120
|
+
}
|
|
121
|
+
return next();
|
|
122
|
+
},
|
|
123
|
+
...config.options,
|
|
124
|
+
});
|
|
125
|
+
const driver = new MysqlDriver(pool);
|
|
126
|
+
try {
|
|
127
|
+
await pool.query('select 1');
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
await pool.end().catch(() => { });
|
|
131
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
132
|
+
throw new NativeDriverError(`[RudderJS ORM native] Could not connect to MySQL at the configured URL: ${msg}`, { cause: err });
|
|
133
|
+
}
|
|
134
|
+
return driver;
|
|
135
|
+
}
|
|
136
|
+
execute(sql, bindings) {
|
|
137
|
+
return runQuery(this.pool, sql, bindings);
|
|
138
|
+
}
|
|
139
|
+
affectingExecute(sql, bindings) {
|
|
140
|
+
return runAffecting(this.pool, sql, bindings);
|
|
141
|
+
}
|
|
142
|
+
// Top-level transaction → reserve a connection, BEGIN, COMMIT (ROLLBACK on
|
|
143
|
+
// throw), always release. Every query inside runs on the pinned connection.
|
|
144
|
+
// An isolation level is applied via `SET TRANSACTION ISOLATION LEVEL` BEFORE
|
|
145
|
+
// `beginTransaction` — on MySQL the un-scoped form applies only to the NEXT
|
|
146
|
+
// transaction started on the connection (issuing it inside one errors), and
|
|
147
|
+
// it does not persist, so the pooled connection is released clean.
|
|
148
|
+
async transaction(fn, opts) {
|
|
149
|
+
const level = opts?.isolationLevel ? isolationLevelSql(opts.isolationLevel) : null;
|
|
150
|
+
const conn = await this.pool.getConnection();
|
|
151
|
+
try {
|
|
152
|
+
if (level)
|
|
153
|
+
await conn.query(`SET TRANSACTION ISOLATION LEVEL ${level}`);
|
|
154
|
+
await conn.beginTransaction();
|
|
155
|
+
try {
|
|
156
|
+
const result = await fn(new MysqlScope(conn));
|
|
157
|
+
await conn.commit();
|
|
158
|
+
return result;
|
|
159
|
+
}
|
|
160
|
+
catch (err) {
|
|
161
|
+
try {
|
|
162
|
+
await conn.rollback();
|
|
163
|
+
}
|
|
164
|
+
catch { /* best effort */ }
|
|
165
|
+
throw err;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
finally {
|
|
169
|
+
conn.release();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
async close() {
|
|
173
|
+
await this.pool.end();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=mysql.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mysql.js","sourceRoot":"","sources":["../../../src/native/drivers/mysql.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { Driver, Transaction, Row, TransactionOptions } from '../driver.js';
|
|
2
|
+
/**
|
|
3
|
+
* The structural slice of porsager's `postgres` API we depend on. Typed here
|
|
4
|
+
* (not via `@types`) so the optional peer carries no compile-time dependency —
|
|
5
|
+
* same approach as the better-sqlite3 driver.
|
|
6
|
+
*/
|
|
7
|
+
interface PgSql {
|
|
8
|
+
/** Run raw SQL with positional ($1, $2, …) params; resolves to the result rows
|
|
9
|
+
* (empty for statements with no result set). */
|
|
10
|
+
unsafe(query: string, params?: readonly unknown[]): Promise<unknown[]>;
|
|
11
|
+
/** Open a transaction; `fn` receives a connection-pinned `sql`. BEGIN/COMMIT,
|
|
12
|
+
* ROLLBACK on throw. */
|
|
13
|
+
begin<T>(fn: (sql: PgSql) => Promise<T>): Promise<T>;
|
|
14
|
+
/** Open a SAVEPOINT inside a transaction; only valid on a transaction `sql`. */
|
|
15
|
+
savepoint<T>(fn: (sql: PgSql) => Promise<T>): Promise<T>;
|
|
16
|
+
/** Drain the pool and close all connections. */
|
|
17
|
+
end(options?: {
|
|
18
|
+
timeout?: number;
|
|
19
|
+
}): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
/** Connection config for {@link PostgresDriver.open}. */
|
|
22
|
+
export interface PostgresDriverConfig {
|
|
23
|
+
/** Postgres connection string, e.g. `postgres://user:pass@host:5432/db`. */
|
|
24
|
+
url: string;
|
|
25
|
+
/** Forwarded to the porsager `postgres()` factory (e.g. `max`, `ssl`). */
|
|
26
|
+
options?: Record<string, unknown>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A {@link Transaction} scope bound to one porsager `sql` handle — the pool, a
|
|
30
|
+
* transaction, or a savepoint. Every query on the scope runs on that handle, so
|
|
31
|
+
* a transaction-scoped query stays on the transaction's pinned connection.
|
|
32
|
+
*/
|
|
33
|
+
declare class PgScope implements Transaction {
|
|
34
|
+
protected readonly sql: PgSql;
|
|
35
|
+
constructor(sql: PgSql);
|
|
36
|
+
execute(query: string, bindings: readonly unknown[]): Promise<Row[]>;
|
|
37
|
+
transaction<T>(fn: (tx: Transaction) => Promise<T>, opts?: TransactionOptions): Promise<T>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* {@link Driver} backed by porsager `postgres`. Construct via the static
|
|
41
|
+
* {@link PostgresDriver.open} factory, which lazy-imports the package and
|
|
42
|
+
* validates connectivity up front.
|
|
43
|
+
*/
|
|
44
|
+
export declare class PostgresDriver extends PgScope implements Driver {
|
|
45
|
+
private constructor();
|
|
46
|
+
/**
|
|
47
|
+
* Resolve the `postgres` package, build a pool for `config.url`, validate the
|
|
48
|
+
* connection, and return a ready driver. Throws {@link NativeDriverError} with
|
|
49
|
+
* install guidance when the package is missing, or a connection error when the
|
|
50
|
+
* URL is unreachable — so failures surface at setup, not deep in a request.
|
|
51
|
+
*/
|
|
52
|
+
static open(config: PostgresDriverConfig): Promise<PostgresDriver>;
|
|
53
|
+
transaction<T>(fn: (tx: Transaction) => Promise<T>, opts?: TransactionOptions): Promise<T>;
|
|
54
|
+
close(): Promise<void>;
|
|
55
|
+
}
|
|
56
|
+
export {};
|
|
57
|
+
//# sourceMappingURL=postgres.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../../src/native/drivers/postgres.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAIhF;;;;GAIG;AACH,UAAU,KAAK;IACb;qDACiD;IACjD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAA;IACtE;6BACyB;IACzB,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;IACpD,gFAAgF;IAChF,SAAS,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;IACxD,gDAAgD;IAChD,GAAG,CAAC,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACnD;AAGD,yDAAyD;AACzD,MAAM,WAAW,oBAAoB;IACnC,4EAA4E;IAC5E,GAAG,EAAE,MAAM,CAAA;IACX,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAClC;AAED;;;;GAIG;AACH,cAAM,OAAQ,YAAW,WAAW;IACtB,SAAS,CAAC,QAAQ,CAAC,GAAG,EAAE,KAAK;gBAAV,GAAG,EAAE,KAAK;IAEnC,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAgBpE,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,CAAC,CAAC;CAIjG;AAED;;;;GAIG;AACH,qBAAa,cAAe,SAAQ,OAAQ,YAAW,MAAM;IAC3D,OAAO;IAIP;;;;;OAKG;WACU,IAAI,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,cAAc,CAAC;IAyFzD,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,CAAC,CAAC;IAQnG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// ─── postgres Driver (Node, porsager `postgres`) ───────────
|
|
2
|
+
//
|
|
3
|
+
// Concrete {@link Driver} over the `postgres` package (porsager). NODE-ONLY:
|
|
4
|
+
// `postgres` is an optional peer of `@rudderjs/orm`, lazy-`import()`ed inside
|
|
5
|
+
// `open()` so this module never drags the driver into a client bundle at eval
|
|
6
|
+
// time (cross-phase rule 5). Mirrors the same shape used by `@rudderjs/orm-drizzle`.
|
|
7
|
+
//
|
|
8
|
+
// Unlike better-sqlite3 (single synchronous connection), porsager pools
|
|
9
|
+
// connections. So a transaction MUST run on one pinned connection — we use
|
|
10
|
+
// porsager's own `sql.begin()` / `sql.savepoint()`, which reserve a connection
|
|
11
|
+
// for the whole scope, rather than issuing a bare `BEGIN` on the pool (which
|
|
12
|
+
// could land BEGIN and the following statements on different connections).
|
|
13
|
+
import { NativeDriverError } from '../errors.js';
|
|
14
|
+
import { isolationLevelSql, nestedIsolationError } from '../isolation.js';
|
|
15
|
+
/**
|
|
16
|
+
* A {@link Transaction} scope bound to one porsager `sql` handle — the pool, a
|
|
17
|
+
* transaction, or a savepoint. Every query on the scope runs on that handle, so
|
|
18
|
+
* a transaction-scoped query stays on the transaction's pinned connection.
|
|
19
|
+
*/
|
|
20
|
+
class PgScope {
|
|
21
|
+
sql;
|
|
22
|
+
constructor(sql) {
|
|
23
|
+
this.sql = sql;
|
|
24
|
+
}
|
|
25
|
+
async execute(query, bindings) {
|
|
26
|
+
// porsager binds JS booleans / Dates / numbers to their native Postgres
|
|
27
|
+
// types, so no value coercion is needed (unlike the SQLite driver's
|
|
28
|
+
// boolean→0/1 mapping). A no-binding call (DDL) goes through the simple
|
|
29
|
+
// query protocol; statements with no result set resolve to an empty list.
|
|
30
|
+
const rows = bindings.length
|
|
31
|
+
? await this.sql.unsafe(query, bindings)
|
|
32
|
+
: await this.sql.unsafe(query);
|
|
33
|
+
return rows;
|
|
34
|
+
}
|
|
35
|
+
// Nested transaction → SAVEPOINT on the current (already-transactional)
|
|
36
|
+
// connection. Reachable only from a scope created inside `begin` (the
|
|
37
|
+
// top-level driver overrides `transaction` to open the BEGIN), so `savepoint`
|
|
38
|
+
// is always valid here. An isolation level is rejected — a savepoint runs
|
|
39
|
+
// inside the open transaction, whose isolation is already fixed.
|
|
40
|
+
async transaction(fn, opts) {
|
|
41
|
+
if (opts?.isolationLevel)
|
|
42
|
+
throw nestedIsolationError();
|
|
43
|
+
return this.sql.savepoint((sp) => fn(new PgScope(sp)));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* {@link Driver} backed by porsager `postgres`. Construct via the static
|
|
48
|
+
* {@link PostgresDriver.open} factory, which lazy-imports the package and
|
|
49
|
+
* validates connectivity up front.
|
|
50
|
+
*/
|
|
51
|
+
export class PostgresDriver extends PgScope {
|
|
52
|
+
constructor(sql) {
|
|
53
|
+
super(sql);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Resolve the `postgres` package, build a pool for `config.url`, validate the
|
|
57
|
+
* connection, and return a ready driver. Throws {@link NativeDriverError} with
|
|
58
|
+
* install guidance when the package is missing, or a connection error when the
|
|
59
|
+
* URL is unreachable — so failures surface at setup, not deep in a request.
|
|
60
|
+
*/
|
|
61
|
+
static async open(config) {
|
|
62
|
+
let factory;
|
|
63
|
+
try {
|
|
64
|
+
// porsager uses `export =`; a dynamic import wraps it in `.default`.
|
|
65
|
+
const mod = await import('postgres');
|
|
66
|
+
factory = mod.default ?? mod;
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
throw new NativeDriverError(`[RudderJS ORM native] Failed to load "postgres". It is an optional peer of ` +
|
|
70
|
+
`@rudderjs/orm — install it with \`pnpm add postgres\` to use the native Postgres engine.`, { cause: err });
|
|
71
|
+
}
|
|
72
|
+
// Silence Postgres NOTICEs (e.g. "table does not exist, skipping" on DROP IF
|
|
73
|
+
// EXISTS) so they don't pollute CLI / migration output, and parse int8 /
|
|
74
|
+
// bigserial (OID 20) as a JS number rather than porsager's default string —
|
|
75
|
+
// so a model's auto-increment `id` is a number on Postgres just as it is on
|
|
76
|
+
// SQLite (INTEGER PK → number). Precision caveat: int8 values above 2^53 lose
|
|
77
|
+
// precision; that's fine for auto-increment ids, and a column needing the
|
|
78
|
+
// full int8 range should declare a cast.
|
|
79
|
+
const sql = factory(config.url, {
|
|
80
|
+
onnotice: () => { },
|
|
81
|
+
types: {
|
|
82
|
+
int8AsNumber: {
|
|
83
|
+
to: 20,
|
|
84
|
+
from: [20],
|
|
85
|
+
serialize: (x) => String(x),
|
|
86
|
+
parse: (x) => parseInt(x, 10),
|
|
87
|
+
},
|
|
88
|
+
// Replace porsager's default `date` type. Its serializer round-trips
|
|
89
|
+
// EVERY bound value through `new Date(x).toISOString()` — and because
|
|
90
|
+
// serializers register for all `from` OIDs, a param the server
|
|
91
|
+
// describes as date/timestamp/timestamptz (1082/1114/1184) hits it
|
|
92
|
+
// even when the JS value is a plain string. `new Date('2026-01-20
|
|
93
|
+
// 11:20:45')` parses as MACHINE-LOCAL time, so bound string timestamps
|
|
94
|
+
// were stored TZ-shifted on any non-UTC machine (silent data
|
|
95
|
+
// corruption; CI is UTC, which hid it). Strings now pass through
|
|
96
|
+
// verbatim — Postgres casts text natively, machine-TZ independent.
|
|
97
|
+
// `Date` values keep the exact previous behavior (`toISOString()`,
|
|
98
|
+
// same instant) and reads keep porsager's default parse (JS `Date`).
|
|
99
|
+
date: {
|
|
100
|
+
to: 1184,
|
|
101
|
+
from: [1082, 1114, 1184],
|
|
102
|
+
serialize: (x) => x instanceof Date ? x.toISOString()
|
|
103
|
+
: typeof x === 'number' ? new Date(x).toISOString()
|
|
104
|
+
: String(x),
|
|
105
|
+
parse: (x) => new Date(x),
|
|
106
|
+
},
|
|
107
|
+
// Replace porsager's default `json` type for the same reason as `date`
|
|
108
|
+
// above: its serializer `JSON.stringify`s EVERY bound value a server
|
|
109
|
+
// describes as json/jsonb (114/3802), so an already-stringified JSON
|
|
110
|
+
// param — the pg dialect's `jsonContains` binds `JSON.stringify(value)`,
|
|
111
|
+
// and apps porting sqlite-style code bind JSON text into jsonb columns —
|
|
112
|
+
// was DOUBLE-encoded: `'"php"'` arrived server-side as the JSON string
|
|
113
|
+
// `"\"php\""` and `@>` containment silently matched nothing. Strings now
|
|
114
|
+
// pass through verbatim (Postgres parses the JSON text natively; an
|
|
115
|
+
// invalid document surfaces as a clear server parse error); non-strings
|
|
116
|
+
// keep porsager's `JSON.stringify`, and reads keep its `JSON.parse`.
|
|
117
|
+
json: {
|
|
118
|
+
to: 114,
|
|
119
|
+
from: [114, 3802],
|
|
120
|
+
serialize: (x) => (typeof x === 'string' ? x : JSON.stringify(x)),
|
|
121
|
+
parse: (x) => JSON.parse(x),
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
...config.options,
|
|
125
|
+
});
|
|
126
|
+
const driver = new PostgresDriver(sql);
|
|
127
|
+
try {
|
|
128
|
+
await sql.unsafe('select 1');
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
await sql.end({ timeout: 1 }).catch(() => { });
|
|
132
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
133
|
+
throw new NativeDriverError(`[RudderJS ORM native] Could not connect to Postgres at the configured URL: ${msg}`, { cause: err });
|
|
134
|
+
}
|
|
135
|
+
return driver;
|
|
136
|
+
}
|
|
137
|
+
// Top-level transaction → BEGIN/COMMIT (ROLLBACK on throw). porsager's `begin`
|
|
138
|
+
// reserves a connection for the whole scope, so every query inside runs on it.
|
|
139
|
+
// An isolation level is applied via `SET TRANSACTION ISOLATION LEVEL` as the
|
|
140
|
+
// FIRST statement inside the transaction — Postgres allows it any time before
|
|
141
|
+
// the first query, and `isolationLevelSql` validates the level (the keyword is
|
|
142
|
+
// spliced, never bound).
|
|
143
|
+
async transaction(fn, opts) {
|
|
144
|
+
const level = opts?.isolationLevel ? isolationLevelSql(opts.isolationLevel) : null;
|
|
145
|
+
return this.sql.begin(async (tx) => {
|
|
146
|
+
if (level)
|
|
147
|
+
await tx.unsafe(`SET TRANSACTION ISOLATION LEVEL ${level}`);
|
|
148
|
+
return fn(new PgScope(tx));
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
async close() {
|
|
152
|
+
await this.sql.end({ timeout: 5 });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=postgres.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres.js","sourceRoot":"","sources":["../../../src/native/drivers/postgres.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,EAAE;AACF,6EAA6E;AAC7E,8EAA8E;AAC9E,8EAA8E;AAC9E,qFAAqF;AACrF,EAAE;AACF,wEAAwE;AACxE,2EAA2E;AAC3E,+EAA+E;AAC/E,6EAA6E;AAC7E,2EAA2E;AAG3E,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAChD,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AA6BzE;;;;GAIG;AACH,MAAM,OAAO;IACoB;IAA/B,YAA+B,GAAU;QAAV,QAAG,GAAH,GAAG,CAAO;IAAG,CAAC;IAE7C,KAAK,CAAC,OAAO,CAAC,KAAa,EAAE,QAA4B;QACvD,wEAAwE;QACxE,oEAAoE;QACpE,wEAAwE;QACxE,0EAA0E;QAC1E,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM;YAC1B,CAAC,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC;YACxC,CAAC,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QAChC,OAAO,IAAa,CAAA;IACtB,CAAC;IAED,wEAAwE;IACxE,sEAAsE;IACtE,8EAA8E;IAC9E,0EAA0E;IAC1E,iEAAiE;IACjE,KAAK,CAAC,WAAW,CAAI,EAAmC,EAAE,IAAyB;QACjF,IAAI,IAAI,EAAE,cAAc;YAAE,MAAM,oBAAoB,EAAE,CAAA;QACtD,OAAO,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;IACxD,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,OAAO,cAAe,SAAQ,OAAO;IACzC,YAAoB,GAAU;QAC5B,KAAK,CAAC,GAAG,CAAC,CAAA;IACZ,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAA4B;QAC5C,IAAI,OAAkB,CAAA;QACtB,IAAI,CAAC;YACH,qEAAqE;YACrE,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,UAAU,CAAuC,CAAA;YAC1E,OAAO,GAAG,GAAG,CAAC,OAAO,IAAK,GAA4B,CAAA;QACxD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,iBAAiB,CACzB,6EAA6E;gBAC7E,0FAA0F,EAC1F,EAAE,KAAK,EAAE,GAAG,EAAE,CACf,CAAA;QACH,CAAC;QAED,6EAA6E;QAC7E,yEAAyE;QACzE,4EAA4E;QAC5E,4EAA4E;QAC5E,8EAA8E;QAC9E,0EAA0E;QAC1E,yCAAyC;QACzC,MAAM,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE;YAC9B,QAAQ,EAAE,GAAG,EAAE,GAAyB,CAAC;YACzC,KAAK,EAAE;gBACL,YAAY,EAAE;oBACZ,EAAE,EAAS,EAAE;oBACb,IAAI,EAAO,CAAC,EAAE,CAAC;oBACf,SAAS,EAAE,CAAC,CAA2B,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;oBACrD,KAAK,EAAM,CAAC,CAAS,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC;iBAC1C;gBACD,qEAAqE;gBACrE,sEAAsE;gBACtE,+DAA+D;gBAC/D,mEAAmE;gBACnE,kEAAkE;gBAClE,uEAAuE;gBACvE,6DAA6D;gBAC7D,iEAAiE;gBACjE,mEAAmE;gBACnE,mEAAmE;gBACnE,qEAAqE;gBACrE,IAAI,EAAE;oBACJ,EAAE,EAAI,IAAI;oBACV,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;oBACxB,SAAS,EAAE,CAAC,CAAU,EAAE,EAAE,CACxB,CAAC,YAAY,IAAI,CAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE;wBAC1C,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAE,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE;4BACpD,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;oBACb,KAAK,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC;iBAClC;gBACD,uEAAuE;gBACvE,qEAAqE;gBACrE,qEAAqE;gBACrE,yEAAyE;gBACzE,yEAAyE;gBACzE,uEAAuE;gBACvE,yEAAyE;gBACzE,oEAAoE;gBACpE,wEAAwE;gBACxE,qEAAqE;gBACrE,IAAI,EAAE;oBACJ,EAAE,EAAI,GAAG;oBACT,IAAI,EAAE,CAAC,GAAG,EAAE,IAAI,CAAC;oBACjB,SAAS,EAAE,CAAC,CAAU,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;oBAC1E,KAAK,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAY;iBAC/C;aACF;YACD,GAAG,MAAM,CAAC,OAAO;SAClB,CAAC,CAAA;QACF,MAAM,MAAM,GAAG,IAAI,cAAc,CAAC,GAAG,CAAC,CAAA;QACtC,IAAI,CAAC;YACH,MAAM,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;QAC9B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,GAAG,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAqB,CAAC,CAAC,CAAA;YAChE,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YAC5D,MAAM,IAAI,iBAAiB,CACzB,8EAA8E,GAAG,EAAE,EACnF,EAAE,KAAK,EAAE,GAAG,EAAE,CACf,CAAA;QACH,CAAC;QACD,OAAO,MAAM,CAAA;IACf,CAAC;IAED,+EAA+E;IAC/E,+EAA+E;IAC/E,6EAA6E;IAC7E,8EAA8E;IAC9E,+EAA+E;IAC/E,yBAAyB;IAChB,KAAK,CAAC,WAAW,CAAI,EAAmC,EAAE,IAAyB;QAC1F,MAAM,KAAK,GAAG,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;QAClF,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;YACjC,IAAI,KAAK;gBAAE,MAAM,EAAE,CAAC,MAAM,CAAC,mCAAmC,KAAK,EAAE,CAAC,CAAA;YACtE,OAAO,EAAE,CAAC,IAAI,OAAO,CAAC,EAAE,CAAC,CAAC,CAAA;QAC5B,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,KAAK;QACT,MAAM,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAA;IACpC,CAAC;CACF"}
|