@happyvertical/sql 0.89.0 → 0.89.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 +17 -4
- package/dist/index.js +29 -11
- package/dist/index.js.map +1 -1
- package/dist/postgres.d.ts +12 -0
- package/dist/postgres.d.ts.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -32,6 +32,8 @@ const tursoDb = await getDatabase({
|
|
|
32
32
|
const pgDb = await getDatabase({
|
|
33
33
|
type: 'postgres',
|
|
34
34
|
url: 'postgresql://user:pass@localhost:5432/dbname',
|
|
35
|
+
connectionTimeoutMillis: 10_000,
|
|
36
|
+
idleTimeoutMillis: 30_000,
|
|
35
37
|
});
|
|
36
38
|
|
|
37
39
|
// DuckDB with JSON file auto-registration
|
|
@@ -50,6 +52,11 @@ const jsonDb = await getDatabase({
|
|
|
50
52
|
});
|
|
51
53
|
```
|
|
52
54
|
|
|
55
|
+
PostgreSQL also accepts `max` (20 by default), `connectionTimeoutMillis`, and
|
|
56
|
+
`idleTimeoutMillis`. Lifecycle timeouts must be integer milliseconds from `0`
|
|
57
|
+
through Node.js's maximum timer delay of `2,147,483,647`; `0` disables the
|
|
58
|
+
corresponding timeout. Omitted timeout values retain `pg`'s defaults.
|
|
59
|
+
|
|
53
60
|
### Connection caching and cleanup
|
|
54
61
|
|
|
55
62
|
PostgreSQL, JSON, and explicitly identified SQLite connections are shared by
|
|
@@ -82,10 +89,12 @@ always `await clearConnectionCache()` before opening a replacement connection.
|
|
|
82
89
|
An explicit `dbid` is an opaque, stable caller-owned cache identity and must be
|
|
83
90
|
non-empty. Without
|
|
84
91
|
one, PostgreSQL derives a credential- and pool-option-sensitive identity using
|
|
85
|
-
a process-keyed digest
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
92
|
+
a process-keyed digest. The pool identity includes `max`,
|
|
93
|
+
`connectionTimeoutMillis`, and `idleTimeoutMillis`; connection URLs, usernames,
|
|
94
|
+
passwords, and option names are not stored in readable cache keys. SQLite
|
|
95
|
+
caches only connections with a `dbid` (automatically assigned to the default
|
|
96
|
+
`:memory:` path). JSON derives an identity from its directory and behavior
|
|
97
|
+
options.
|
|
89
98
|
|
|
90
99
|
DuckDB already creates a fresh adapter for every call, so `cache` and
|
|
91
100
|
`clearCache` are accepted for uniform configuration but do not change its
|
|
@@ -364,6 +373,10 @@ await syncSchema({
|
|
|
364
373
|
const exists = await db.tableExists('users');
|
|
365
374
|
```
|
|
366
375
|
|
|
376
|
+
On PostgreSQL, `syncSchema()` recognizes `CREATE [UNIQUE] INDEX CONCURRENTLY
|
|
377
|
+
[IF NOT EXISTS]` statements, including optional `USING` index methods, and
|
|
378
|
+
skips indexes that already exist when a schema is applied again.
|
|
379
|
+
|
|
367
380
|
### Vector Search (PostgreSQL)
|
|
368
381
|
|
|
369
382
|
PostgreSQL adapters expose `db.vector` when pgvector is available:
|
package/dist/index.js
CHANGED
|
@@ -1717,6 +1717,7 @@ var postgres_exports = /* @__PURE__ */ __exportAll({
|
|
|
1717
1717
|
clearPostgresConnectionCache: () => clearPostgresConnectionCache,
|
|
1718
1718
|
getDatabase: () => getDatabase$1
|
|
1719
1719
|
});
|
|
1720
|
+
var MAX_TIMER_DELAY_MILLIS = 2147483647;
|
|
1720
1721
|
globalThis.__haveSqlPostgresCacheHmacKey ??= webcrypto.subtle.generateKey({
|
|
1721
1722
|
name: "HMAC",
|
|
1722
1723
|
hash: "SHA-256"
|
|
@@ -1753,8 +1754,11 @@ function resolvePostgresConfig(options) {
|
|
|
1753
1754
|
const legacyPort = process.env.SQLOO_PORT;
|
|
1754
1755
|
const port = legacy(primary.port, legacyPort === void 0 ? void 0 : Number(legacyPort)) ?? 5432;
|
|
1755
1756
|
const max = options.max ?? 20;
|
|
1757
|
+
const { connectionTimeoutMillis, idleTimeoutMillis } = options;
|
|
1756
1758
|
if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error("PostgreSQL port must be an integer between 1 and 65535");
|
|
1757
1759
|
if (!Number.isInteger(max) || max < 1) throw new Error("PostgreSQL pool max must be a positive integer");
|
|
1760
|
+
if (connectionTimeoutMillis !== void 0 && (!Number.isInteger(connectionTimeoutMillis) || connectionTimeoutMillis < 0 || connectionTimeoutMillis > MAX_TIMER_DELAY_MILLIS)) throw new Error(`PostgreSQL pool connectionTimeoutMillis must be an integer between 0 and ${MAX_TIMER_DELAY_MILLIS}`);
|
|
1761
|
+
if (idleTimeoutMillis !== void 0 && (!Number.isInteger(idleTimeoutMillis) || idleTimeoutMillis < 0 || idleTimeoutMillis > MAX_TIMER_DELAY_MILLIS)) throw new Error(`PostgreSQL pool idleTimeoutMillis must be an integer between 0 and ${MAX_TIMER_DELAY_MILLIS}`);
|
|
1758
1762
|
return {
|
|
1759
1763
|
url: legacy(primary.url, process.env.SQLOO_URL),
|
|
1760
1764
|
database: legacy(primary.database, process.env.SQLOO_DATABASE),
|
|
@@ -1762,7 +1766,9 @@ function resolvePostgresConfig(options) {
|
|
|
1762
1766
|
user: legacy(primary.user, process.env.SQLOO_USER),
|
|
1763
1767
|
password: legacy(primary.password, process.env.SQLOO_PASSWORD),
|
|
1764
1768
|
port,
|
|
1765
|
-
max
|
|
1769
|
+
max,
|
|
1770
|
+
connectionTimeoutMillis,
|
|
1771
|
+
idleTimeoutMillis
|
|
1766
1772
|
};
|
|
1767
1773
|
}
|
|
1768
1774
|
async function derivePostgresConnectionCacheKey(options, effective) {
|
|
@@ -1774,7 +1780,9 @@ async function derivePostgresConnectionCacheKey(options, effective) {
|
|
|
1774
1780
|
database: effective.database ?? null,
|
|
1775
1781
|
user: effective.user ?? null,
|
|
1776
1782
|
password: effective.password ?? null,
|
|
1777
|
-
max: effective.max
|
|
1783
|
+
max: effective.max,
|
|
1784
|
+
connectionTimeoutMillis: effective.connectionTimeoutMillis ?? null,
|
|
1785
|
+
idleTimeoutMillis: effective.idleTimeoutMillis ?? null
|
|
1778
1786
|
});
|
|
1779
1787
|
const hmacKey = await globalThis.__haveSqlPostgresCacheHmacKey;
|
|
1780
1788
|
if (!hmacKey) throw new Error("PostgreSQL cache identity key was not initialized");
|
|
@@ -2115,17 +2123,21 @@ async function getDatabase$1(options = {}) {
|
|
|
2115
2123
|
async function createDatabase(config) {
|
|
2116
2124
|
const { database, host = "localhost", user, password, port = 5432 } = config;
|
|
2117
2125
|
const url = config.url || `postgresql://${user}${password ? `:${password}` : ""}@${host}:${port}/${database || "postgres"}`;
|
|
2118
|
-
const poolMax = config
|
|
2126
|
+
const { max: poolMax, connectionTimeoutMillis, idleTimeoutMillis } = config;
|
|
2119
2127
|
const pool = new Pool(config.url ? {
|
|
2120
2128
|
connectionString: config.url,
|
|
2121
|
-
max: poolMax
|
|
2129
|
+
max: poolMax,
|
|
2130
|
+
connectionTimeoutMillis,
|
|
2131
|
+
idleTimeoutMillis
|
|
2122
2132
|
} : {
|
|
2123
2133
|
host,
|
|
2124
2134
|
user,
|
|
2125
2135
|
password,
|
|
2126
2136
|
port,
|
|
2127
2137
|
database,
|
|
2128
|
-
max: poolMax
|
|
2138
|
+
max: poolMax,
|
|
2139
|
+
connectionTimeoutMillis,
|
|
2140
|
+
idleTimeoutMillis
|
|
2129
2141
|
});
|
|
2130
2142
|
pool.on("error", (error) => {
|
|
2131
2143
|
console.warn("Warning: PostgreSQL pool client error (connection discarded):", error instanceof Error ? error.message : String(error));
|
|
@@ -2804,11 +2816,18 @@ async function createDatabase(config) {
|
|
|
2804
2816
|
};
|
|
2805
2817
|
const syncSchema = async (schema) => {
|
|
2806
2818
|
const commands = schema.trim().split(";").filter((command) => command.trim() !== "");
|
|
2807
|
-
const createIndexRegex = /CREATE
|
|
2819
|
+
const createIndexRegex = /CREATE\s+(?:UNIQUE\s+)?INDEX\s+(?:CONCURRENTLY\s+)?(?:IF\s+NOT\s+EXISTS\s+)?"?(?<indexName>\w+)"?\s+ON\s+"?(?<tableName>\w+)"?\s*(?:USING\s+"?\w+"?\s*)?\([^)]+\)/i;
|
|
2820
|
+
const parseCreateIndex = (command) => {
|
|
2821
|
+
const { indexName, tableName } = command.match(createIndexRegex)?.groups ?? {};
|
|
2822
|
+
return indexName && tableName ? {
|
|
2823
|
+
indexName,
|
|
2824
|
+
tableName
|
|
2825
|
+
} : null;
|
|
2826
|
+
};
|
|
2808
2827
|
const indexNames = [];
|
|
2809
2828
|
for (const command of commands) {
|
|
2810
|
-
const indexMatch = command.trim()
|
|
2811
|
-
if (indexMatch) indexNames.push(indexMatch
|
|
2829
|
+
const indexMatch = parseCreateIndex(command.trim());
|
|
2830
|
+
if (indexMatch) indexNames.push(indexMatch.indexName);
|
|
2812
2831
|
}
|
|
2813
2832
|
const existingIndexes = /* @__PURE__ */ new Set();
|
|
2814
2833
|
if (indexNames.length > 0) {
|
|
@@ -2850,10 +2869,9 @@ async function createDatabase(config) {
|
|
|
2850
2869
|
}
|
|
2851
2870
|
continue;
|
|
2852
2871
|
}
|
|
2853
|
-
const indexMatch = trimmedCommand
|
|
2872
|
+
const indexMatch = parseCreateIndex(trimmedCommand);
|
|
2854
2873
|
if (indexMatch) {
|
|
2855
|
-
const indexName = indexMatch
|
|
2856
|
-
const indexTableName = indexMatch[4];
|
|
2874
|
+
const { indexName, tableName: indexTableName } = indexMatch;
|
|
2857
2875
|
if (!existingIndexes.has(indexName)) try {
|
|
2858
2876
|
await runTolerated(() => executor.query(trimmedCommand));
|
|
2859
2877
|
existingIndexes.add(indexName);
|