@happyvertical/sql 0.80.3 → 0.80.5
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/dist/chunks/{duckdb-DykBBzd6.js → duckdb-D_Wu36JL.js} +43 -48
- package/dist/chunks/duckdb-D_Wu36JL.js.map +1 -0
- package/dist/chunks/{sqlite-YgU9LCVy.js → sqlite-B4ussRTS.js} +76 -54
- package/dist/chunks/sqlite-B4ussRTS.js.map +1 -0
- package/dist/chunks/{sqlite-native-BGtpx7Ro.js → sqlite-native-BgiF4301.js} +34 -6
- package/dist/chunks/{sqlite-native-BGtpx7Ro.js.map → sqlite-native-BgiF4301.js.map} +1 -1
- package/dist/duckdb.d.ts.map +1 -1
- package/dist/index.js +168 -69
- package/dist/index.js.map +1 -1
- package/dist/json.d.ts.map +1 -1
- package/dist/shared/types.d.ts +59 -0
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/sqlite.d.ts.map +1 -1
- package/package.json +3 -3
- package/dist/chunks/duckdb-DykBBzd6.js.map +0 -1
- package/dist/chunks/sqlite-YgU9LCVy.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -310,6 +310,34 @@ function countValueFromRow(value) {
|
|
|
310
310
|
//#endregion
|
|
311
311
|
//#region src/shared/types.ts
|
|
312
312
|
/**
|
|
313
|
+
* Error thrown when `transaction()` is called on a transaction-scoped interface
|
|
314
|
+
* belonging to an adapter whose engine has no savepoints.
|
|
315
|
+
*
|
|
316
|
+
* DuckDB — and therefore the JSON adapter built on it — does not implement
|
|
317
|
+
* `SAVEPOINT`, so there is no way to re-enter the current transaction. Adapters
|
|
318
|
+
* that can re-enter (PostgreSQL, SQLite) do so under a savepoint and never
|
|
319
|
+
* throw this.
|
|
320
|
+
*
|
|
321
|
+
* The alternative would be to open a second, independent transaction, which is
|
|
322
|
+
* what these adapters used to do; on a shared connection that silently
|
|
323
|
+
* destroyed the enclosing transaction's uncommitted work.
|
|
324
|
+
*
|
|
325
|
+
* @example Ensuring a helper works on every adapter
|
|
326
|
+
* ```typescript
|
|
327
|
+
* // Take the transaction as a parameter rather than opening a nested one.
|
|
328
|
+
* async function recordClick(db: DatabaseInterface, id: string) {
|
|
329
|
+
* await db.update('links', { id }, { clicks: 1 });
|
|
330
|
+
* }
|
|
331
|
+
* await db.transaction(async (tx) => recordClick(tx, id));
|
|
332
|
+
* ```
|
|
333
|
+
*/
|
|
334
|
+
var NestedTransactionError = class extends DatabaseError {
|
|
335
|
+
constructor(message, context) {
|
|
336
|
+
super(message, context);
|
|
337
|
+
this.name = "NestedTransactionError";
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
/**
|
|
313
341
|
* Resolves a SchemasOption to its concrete value.
|
|
314
342
|
*
|
|
315
343
|
* Adapters that need schemas (JSON, DuckDB) call this to unwrap the
|
|
@@ -1472,60 +1500,27 @@ async function getDatabase$2(options) {
|
|
|
1472
1500
|
const transaction = async (callback) => {
|
|
1473
1501
|
try {
|
|
1474
1502
|
await connection.run("BEGIN TRANSACTION");
|
|
1475
|
-
const result = await callback(
|
|
1476
|
-
url,
|
|
1477
|
-
client: connection,
|
|
1478
|
-
insert,
|
|
1479
|
-
get,
|
|
1480
|
-
list,
|
|
1481
|
-
update,
|
|
1482
|
-
upsert,
|
|
1483
|
-
getOrInsert,
|
|
1484
|
-
delete: deleteRecords,
|
|
1485
|
-
count,
|
|
1486
|
-
table,
|
|
1487
|
-
many,
|
|
1488
|
-
single,
|
|
1489
|
-
pluck,
|
|
1490
|
-
execute,
|
|
1491
|
-
query,
|
|
1492
|
-
oo,
|
|
1493
|
-
oO,
|
|
1494
|
-
ox,
|
|
1495
|
-
xx,
|
|
1496
|
-
tableExists,
|
|
1497
|
-
syncSchema,
|
|
1498
|
-
transaction
|
|
1499
|
-
});
|
|
1503
|
+
const result = await callback(createTransactionScope());
|
|
1500
1504
|
await connection.run("COMMIT");
|
|
1501
1505
|
return result;
|
|
1502
1506
|
} catch (error) {
|
|
1503
|
-
|
|
1507
|
+
try {
|
|
1508
|
+
await connection.run("ROLLBACK");
|
|
1509
|
+
} catch {}
|
|
1504
1510
|
throw error;
|
|
1505
1511
|
}
|
|
1506
1512
|
};
|
|
1507
1513
|
/**
|
|
1508
|
-
*
|
|
1509
|
-
*
|
|
1510
|
-
* Unlike transaction(), this gives you explicit control over commit/rollback.
|
|
1511
|
-
* Ideal for test isolation where you want to rollback after each test.
|
|
1514
|
+
* Builds the transaction-scoped interface handed to a transaction callback.
|
|
1512
1515
|
*
|
|
1513
|
-
*
|
|
1516
|
+
* The only difference from the top-level interface is `transaction`, which
|
|
1517
|
+
* refuses to nest. DuckDB has no SAVEPOINT, so there is no way to re-enter
|
|
1518
|
+
* the transaction already in progress; re-exposing the top-level
|
|
1519
|
+
* `transaction` (the previous behaviour) sent the nested call into a second
|
|
1520
|
+
* BEGIN on the same connection, which throws and then rolls back the
|
|
1521
|
+
* *enclosing* transaction, silently discarding its work.
|
|
1514
1522
|
*/
|
|
1515
|
-
const
|
|
1516
|
-
await connection.run("BEGIN TRANSACTION");
|
|
1517
|
-
let active = true;
|
|
1518
|
-
const commit = async () => {
|
|
1519
|
-
if (!active) throw new DatabaseError("Transaction already ended", {});
|
|
1520
|
-
await connection.run("COMMIT");
|
|
1521
|
-
active = false;
|
|
1522
|
-
};
|
|
1523
|
-
const rollback = async () => {
|
|
1524
|
-
if (!active) throw new DatabaseError("Transaction already ended", {});
|
|
1525
|
-
await connection.run("ROLLBACK");
|
|
1526
|
-
active = false;
|
|
1527
|
-
};
|
|
1528
|
-
const isActive = () => active;
|
|
1523
|
+
const createTransactionScope = () => {
|
|
1529
1524
|
return {
|
|
1530
1525
|
url,
|
|
1531
1526
|
client: connection,
|
|
@@ -1549,7 +1544,35 @@ async function getDatabase$2(options) {
|
|
|
1549
1544
|
xx,
|
|
1550
1545
|
tableExists,
|
|
1551
1546
|
syncSchema,
|
|
1552
|
-
transaction
|
|
1547
|
+
transaction: async () => {
|
|
1548
|
+
throw new NestedTransactionError("Nested transactions are not supported by this adapter because its engine has no SAVEPOINT. Pass the existing transaction to the callee instead of opening a new one.", { adapter: "json" });
|
|
1549
|
+
}
|
|
1550
|
+
};
|
|
1551
|
+
};
|
|
1552
|
+
/**
|
|
1553
|
+
* Begins a new transaction and returns a handle for manual control
|
|
1554
|
+
*
|
|
1555
|
+
* Unlike transaction(), this gives you explicit control over commit/rollback.
|
|
1556
|
+
* Ideal for test isolation where you want to rollback after each test.
|
|
1557
|
+
*
|
|
1558
|
+
* @returns Promise resolving to a TransactionHandle
|
|
1559
|
+
*/
|
|
1560
|
+
const beginTransaction = async () => {
|
|
1561
|
+
await connection.run("BEGIN TRANSACTION");
|
|
1562
|
+
let active = true;
|
|
1563
|
+
const commit = async () => {
|
|
1564
|
+
if (!active) throw new DatabaseError("Transaction already ended", {});
|
|
1565
|
+
await connection.run("COMMIT");
|
|
1566
|
+
active = false;
|
|
1567
|
+
};
|
|
1568
|
+
const rollback = async () => {
|
|
1569
|
+
if (!active) throw new DatabaseError("Transaction already ended", {});
|
|
1570
|
+
await connection.run("ROLLBACK");
|
|
1571
|
+
active = false;
|
|
1572
|
+
};
|
|
1573
|
+
const isActive = () => active;
|
|
1574
|
+
return {
|
|
1575
|
+
...createTransactionScope(),
|
|
1553
1576
|
commit,
|
|
1554
1577
|
rollback,
|
|
1555
1578
|
isActive
|
|
@@ -1963,6 +1986,9 @@ async function createDatabase(options, cacheKey) {
|
|
|
1963
1986
|
database,
|
|
1964
1987
|
max: poolMax
|
|
1965
1988
|
});
|
|
1989
|
+
pool.on("error", (error) => {
|
|
1990
|
+
console.warn("Warning: PostgreSQL pool client error (connection discarded):", error instanceof Error ? error.message : String(error));
|
|
1991
|
+
});
|
|
1966
1992
|
const originalEnd = pool.end.bind(pool);
|
|
1967
1993
|
const evictFromCache = () => {
|
|
1968
1994
|
for (const [key, cached] of connectionCache.entries()) if (cached.client === pool) connectionCache.delete(key);
|
|
@@ -2076,18 +2102,83 @@ async function createDatabase(options, cacheKey) {
|
|
|
2076
2102
|
affected: (await executor.query(insertSql, values)).rowCount ?? 0
|
|
2077
2103
|
};
|
|
2078
2104
|
};
|
|
2079
|
-
|
|
2105
|
+
/**
|
|
2106
|
+
* Absorbs 'error' events for the clients currently checked out for a
|
|
2107
|
+
* transaction, keyed by client so the listener can be removed on release.
|
|
2108
|
+
*/
|
|
2109
|
+
const txErrorAbsorbers = /* @__PURE__ */ new WeakMap();
|
|
2110
|
+
const releasedTxClients = /* @__PURE__ */ new WeakSet();
|
|
2111
|
+
/**
|
|
2112
|
+
* Checks out a pooled client for a transaction.
|
|
2113
|
+
*
|
|
2114
|
+
* pg-pool drops its own idle 'error' listener while a client is checked out,
|
|
2115
|
+
* so a backend that dies mid-transaction would emit an unhandled 'error' and
|
|
2116
|
+
* terminate the process. Absorb it here — the failure still reaches the
|
|
2117
|
+
* caller as a rejected query — and drop the listener again on release so
|
|
2118
|
+
* repeated checkouts of the same client cannot accumulate them.
|
|
2119
|
+
*/
|
|
2120
|
+
const acquireTxClient = async () => {
|
|
2080
2121
|
const txClient = await client.connect();
|
|
2122
|
+
releasedTxClients.delete(txClient);
|
|
2123
|
+
const absorb = () => {};
|
|
2124
|
+
txClient.on("error", absorb);
|
|
2125
|
+
txErrorAbsorbers.set(txClient, absorb);
|
|
2126
|
+
return txClient;
|
|
2127
|
+
};
|
|
2128
|
+
/**
|
|
2129
|
+
* Returns a transaction client to the pool exactly once.
|
|
2130
|
+
*
|
|
2131
|
+
* Every teardown path must run this, including the ones reached by a throwing
|
|
2132
|
+
* COMMIT or ROLLBACK — a client that is never released stays checked out for
|
|
2133
|
+
* the life of the process, and enough of them exhaust the pool. Releasing
|
|
2134
|
+
* twice throws, so the released clients are tracked rather than assumed.
|
|
2135
|
+
*
|
|
2136
|
+
* @param txClient - The pooled client backing a transaction
|
|
2137
|
+
* @param destroy - Destroy the connection instead of reusing it, for when the
|
|
2138
|
+
* transaction state could not be cleaned up
|
|
2139
|
+
*/
|
|
2140
|
+
const releaseTxClient = (txClient, destroy = false) => {
|
|
2141
|
+
if (releasedTxClients.has(txClient)) return;
|
|
2142
|
+
releasedTxClients.add(txClient);
|
|
2143
|
+
const absorb = txErrorAbsorbers.get(txClient);
|
|
2144
|
+
if (absorb) {
|
|
2145
|
+
txClient.off("error", absorb);
|
|
2146
|
+
txErrorAbsorbers.delete(txClient);
|
|
2147
|
+
}
|
|
2148
|
+
txClient.release(destroy || void 0);
|
|
2149
|
+
};
|
|
2150
|
+
/**
|
|
2151
|
+
* Rolls back a failed transaction and returns its client to the pool.
|
|
2152
|
+
*
|
|
2153
|
+
* The connection may still be inside a transaction, so ROLLBACK is attempted
|
|
2154
|
+
* to normalize it. If that works the connection is clean and worth reusing;
|
|
2155
|
+
* if it does not, the state is unknown and the connection is destroyed rather
|
|
2156
|
+
* than handed to unrelated code later.
|
|
2157
|
+
*
|
|
2158
|
+
* @returns The rollback failure, if the connection could not be normalized
|
|
2159
|
+
*/
|
|
2160
|
+
const discardTxClient = async (txClient) => {
|
|
2161
|
+
try {
|
|
2162
|
+
await txClient.query("ROLLBACK");
|
|
2163
|
+
releaseTxClient(txClient);
|
|
2164
|
+
return;
|
|
2165
|
+
} catch (rollbackError) {
|
|
2166
|
+
releaseTxClient(txClient, true);
|
|
2167
|
+
return rollbackError;
|
|
2168
|
+
}
|
|
2169
|
+
};
|
|
2170
|
+
const executeNullAwarePostgresUpsertInTransaction = async (table, conflictColumns, serializedData) => {
|
|
2171
|
+
const txClient = await acquireTxClient();
|
|
2081
2172
|
try {
|
|
2082
2173
|
await txClient.query("BEGIN");
|
|
2083
2174
|
const result = await executeNullAwarePostgresUpsert(txClient, table, conflictColumns, serializedData);
|
|
2084
2175
|
await txClient.query("COMMIT");
|
|
2176
|
+
releaseTxClient(txClient);
|
|
2085
2177
|
return result;
|
|
2086
2178
|
} catch (error) {
|
|
2087
|
-
await txClient
|
|
2179
|
+
const rollbackError = await discardTxClient(txClient);
|
|
2180
|
+
if (rollbackError !== void 0 && error instanceof Error) error.cause ??= rollbackError;
|
|
2088
2181
|
throw error;
|
|
2089
|
-
} finally {
|
|
2090
|
-
txClient.release();
|
|
2091
2182
|
}
|
|
2092
2183
|
};
|
|
2093
2184
|
const executePostgresUpsert = async (executor, table, conflictColumns, data, options, acquireTransaction) => {
|
|
@@ -2583,7 +2674,7 @@ async function createDatabase(options, cacheKey) {
|
|
|
2583
2674
|
* @returns Promise resolving to callback result
|
|
2584
2675
|
*/
|
|
2585
2676
|
const transaction = async (callback) => {
|
|
2586
|
-
const txClient = await
|
|
2677
|
+
const txClient = await acquireTxClient();
|
|
2587
2678
|
try {
|
|
2588
2679
|
await txClient.query("BEGIN");
|
|
2589
2680
|
const txDb = {
|
|
@@ -2719,12 +2810,12 @@ async function createDatabase(options, cacheKey) {
|
|
|
2719
2810
|
};
|
|
2720
2811
|
const result = await callback(txDb);
|
|
2721
2812
|
await txClient.query("COMMIT");
|
|
2813
|
+
releaseTxClient(txClient);
|
|
2722
2814
|
return result;
|
|
2723
2815
|
} catch (error) {
|
|
2724
|
-
await txClient
|
|
2816
|
+
const rollbackError = await discardTxClient(txClient);
|
|
2817
|
+
if (rollbackError !== void 0 && error instanceof Error) error.cause ??= rollbackError;
|
|
2725
2818
|
throw error;
|
|
2726
|
-
} finally {
|
|
2727
|
-
txClient.release();
|
|
2728
2819
|
}
|
|
2729
2820
|
};
|
|
2730
2821
|
/**
|
|
@@ -2736,21 +2827,29 @@ async function createDatabase(options, cacheKey) {
|
|
|
2736
2827
|
* @returns Promise resolving to a TransactionHandle
|
|
2737
2828
|
*/
|
|
2738
2829
|
const beginTransaction = async () => {
|
|
2739
|
-
const txClient = await
|
|
2740
|
-
|
|
2830
|
+
const txClient = await acquireTxClient();
|
|
2831
|
+
try {
|
|
2832
|
+
await txClient.query("BEGIN");
|
|
2833
|
+
} catch (error) {
|
|
2834
|
+
releaseTxClient(txClient, true);
|
|
2835
|
+
throw error;
|
|
2836
|
+
}
|
|
2741
2837
|
let active = true;
|
|
2742
|
-
const
|
|
2743
|
-
if (!active) throw new DatabaseError("Transaction already ended", {});
|
|
2744
|
-
await txClient.query("COMMIT");
|
|
2745
|
-
active = false;
|
|
2746
|
-
txClient.release();
|
|
2747
|
-
};
|
|
2748
|
-
const rollback = async () => {
|
|
2838
|
+
const end = async (command) => {
|
|
2749
2839
|
if (!active) throw new DatabaseError("Transaction already ended", {});
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2840
|
+
try {
|
|
2841
|
+
await txClient.query(command);
|
|
2842
|
+
active = false;
|
|
2843
|
+
releaseTxClient(txClient);
|
|
2844
|
+
} catch (error) {
|
|
2845
|
+
active = false;
|
|
2846
|
+
const rollbackError = await discardTxClient(txClient);
|
|
2847
|
+
if (rollbackError !== void 0 && error instanceof Error) error.cause ??= rollbackError;
|
|
2848
|
+
throw error;
|
|
2849
|
+
}
|
|
2753
2850
|
};
|
|
2851
|
+
const commit = () => end("COMMIT");
|
|
2852
|
+
const rollback = () => end("ROLLBACK");
|
|
2754
2853
|
const isActive = () => active;
|
|
2755
2854
|
const txHandle = {
|
|
2756
2855
|
url,
|
|
@@ -3177,8 +3276,8 @@ async function getDatabase(options = {}) {
|
|
|
3177
3276
|
}));
|
|
3178
3277
|
if (!options.type && (options.url?.startsWith("file:") || options.url === ":memory:")) options.type = "sqlite";
|
|
3179
3278
|
if (options.type === "postgres") return (await Promise.resolve().then(() => postgres_exports)).getDatabase(options);
|
|
3180
|
-
if (options.type === "sqlite") return (await import("./chunks/sqlite-
|
|
3181
|
-
if (options.type === "duckdb") return (await import("./chunks/duckdb-
|
|
3279
|
+
if (options.type === "sqlite") return (await import("./chunks/sqlite-B4ussRTS.js")).getDatabase(options);
|
|
3280
|
+
if (options.type === "duckdb") return (await import("./chunks/duckdb-D_Wu36JL.js")).getDatabase(options);
|
|
3182
3281
|
if (options.type === "json") return (await Promise.resolve().then(() => json_exports)).getDatabase(options);
|
|
3183
3282
|
throw new Error("Invalid database type");
|
|
3184
3283
|
}
|
|
@@ -3240,6 +3339,6 @@ var src_default = {
|
|
|
3240
3339
|
/** @internal */
|
|
3241
3340
|
var PACKAGE_VERSION_INITIALIZED = true;
|
|
3242
3341
|
//#endregion
|
|
3243
|
-
export { DatabaseSchemaManager, PACKAGE_VERSION_INITIALIZED, assertCanExportDatabase, assertCanImportDatabase, bucketExpr, buildAggregate, buildWhere, checkExpectedTables, checkRelationship, checkUniqueColumn, clearConnectionCache, clearPostgresConnectionCache, convertUniqueIndexesToInlineConstraints, createPostgresDatabase, databaseNameFromUrl, src_default as default, dropPostgresDatabase, dumpPostgresDatabase, escapeSqlValue, formatDbError, getDatabase, isLocalDatabaseUrl, postgresEnvFromUrl, redactDatabaseUrl, resolveSchemas, restorePostgresDatabase, runDoctor, syncSchema, tableExists, validateColumnName };
|
|
3342
|
+
export { DatabaseSchemaManager, NestedTransactionError, PACKAGE_VERSION_INITIALIZED, assertCanExportDatabase, assertCanImportDatabase, bucketExpr, buildAggregate, buildWhere, checkExpectedTables, checkRelationship, checkUniqueColumn, clearConnectionCache, clearPostgresConnectionCache, convertUniqueIndexesToInlineConstraints, createPostgresDatabase, databaseNameFromUrl, src_default as default, dropPostgresDatabase, dumpPostgresDatabase, escapeSqlValue, formatDbError, getDatabase, isLocalDatabaseUrl, postgresEnvFromUrl, redactDatabaseUrl, resolveSchemas, restorePostgresDatabase, runDoctor, syncSchema, tableExists, validateColumnName };
|
|
3244
3343
|
|
|
3245
3344
|
//# sourceMappingURL=index.js.map
|