bun-query-builder 0.2.25 → 0.2.26
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 +25 -1
- package/dist/actions/migrate.d.ts +0 -2
- package/dist/bin/cli.js +216 -101
- package/dist/client.d.ts +3 -0
- package/dist/config.d.ts +31 -2
- package/dist/src/index.js +216 -100
- package/dist/types.d.ts +87 -0
- package/package.json +1 -1
package/dist/bin/cli.js
CHANGED
|
@@ -11645,6 +11645,29 @@ import process19 from "process";
|
|
|
11645
11645
|
function isMysqlLike(dialect = config5.dialect) {
|
|
11646
11646
|
return dialect === "mysql" || dialect === "singlestore" || dialect === "vitess";
|
|
11647
11647
|
}
|
|
11648
|
+
function isPlainObject(value) {
|
|
11649
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
11650
|
+
return false;
|
|
11651
|
+
const proto = Object.getPrototypeOf(value);
|
|
11652
|
+
return proto === Object.prototype || proto === null;
|
|
11653
|
+
}
|
|
11654
|
+
function mergeObject(target, source, seen = new Set) {
|
|
11655
|
+
const out = { ...target };
|
|
11656
|
+
seen.add(source);
|
|
11657
|
+
for (const key of Object.keys(source)) {
|
|
11658
|
+
if (FORBIDDEN_MERGE_KEYS.has(key))
|
|
11659
|
+
continue;
|
|
11660
|
+
const next = source[key];
|
|
11661
|
+
if (isPlainObject(next) && !seen.has(next)) {
|
|
11662
|
+
const prev = out[key];
|
|
11663
|
+
out[key] = mergeObject(isPlainObject(prev) ? prev : {}, next, seen);
|
|
11664
|
+
} else {
|
|
11665
|
+
out[key] = next;
|
|
11666
|
+
}
|
|
11667
|
+
}
|
|
11668
|
+
seen.delete(source);
|
|
11669
|
+
return out;
|
|
11670
|
+
}
|
|
11648
11671
|
function resolveDialect(explicit) {
|
|
11649
11672
|
if (explicit)
|
|
11650
11673
|
return explicit;
|
|
@@ -11688,21 +11711,12 @@ async function getConfig() {
|
|
|
11688
11711
|
return config5;
|
|
11689
11712
|
}
|
|
11690
11713
|
function applyConfig(userConfig) {
|
|
11691
|
-
|
|
11692
|
-
|
|
11693
|
-
|
|
11694
|
-
|
|
11695
|
-
|
|
11696
|
-
|
|
11697
|
-
}
|
|
11698
|
-
if (userConfig.pagination) {
|
|
11699
|
-
config5.pagination = { ...config5.pagination, ...userConfig.pagination };
|
|
11700
|
-
}
|
|
11701
|
-
if (userConfig.softDeletes) {
|
|
11702
|
-
config5.softDeletes = { ...config5.softDeletes, ...userConfig.softDeletes };
|
|
11703
|
-
}
|
|
11704
|
-
if (userConfig.vitess) {
|
|
11705
|
-
config5.vitess = { ...config5.vitess, ...userConfig.vitess };
|
|
11714
|
+
const merged = mergeObject(config5, userConfig);
|
|
11715
|
+
const target = config5;
|
|
11716
|
+
for (const key of Object.keys(merged)) {
|
|
11717
|
+
if (FORBIDDEN_MERGE_KEYS.has(key))
|
|
11718
|
+
continue;
|
|
11719
|
+
target[key] = merged[key];
|
|
11706
11720
|
}
|
|
11707
11721
|
}
|
|
11708
11722
|
function setConfig(userConfig) {
|
|
@@ -11710,7 +11724,7 @@ function setConfig(userConfig) {
|
|
|
11710
11724
|
const key = `${_lastConfiguredDialect}->${userConfig.dialect}`;
|
|
11711
11725
|
if (!_warnedDialectConflicts.has(key)) {
|
|
11712
11726
|
_warnedDialectConflicts.add(key);
|
|
11713
|
-
console.warn(`[query-builder]
|
|
11727
|
+
console.warn(`[query-builder] Configuring dialect '${userConfig.dialect}' overrides the previously ` + `configured dialect '${_lastConfiguredDialect}'. ` + `Config is process-wide (setConfig, db.configure and query-builder.config.ts all write it); ` + `in-flight queries from the previous configuration may break. ` + `Run conflicting connections in separate processes \u2014 see stacksjs/stacks#1862 #18.`);
|
|
11714
11728
|
}
|
|
11715
11729
|
}
|
|
11716
11730
|
if (userConfig.dialect)
|
|
@@ -11719,7 +11733,10 @@ function setConfig(userConfig) {
|
|
|
11719
11733
|
configState.explicit.add(key);
|
|
11720
11734
|
applyConfig(userConfig);
|
|
11721
11735
|
}
|
|
11722
|
-
|
|
11736
|
+
function defineConfig(userConfig) {
|
|
11737
|
+
return userConfig;
|
|
11738
|
+
}
|
|
11739
|
+
var defaultConfig4, FORBIDDEN_MERGE_KEYS, CONFIG_SINGLETON_KEY, config5, CONFIG_STATE_KEY, configState, _lastConfiguredDialect = null, _warnedDialectConflicts;
|
|
11723
11740
|
var init_config = __esm(() => {
|
|
11724
11741
|
init_dist();
|
|
11725
11742
|
defaultConfig4 = {
|
|
@@ -11784,8 +11801,9 @@ var init_config = __esm(() => {
|
|
|
11784
11801
|
defaultFilter: true
|
|
11785
11802
|
}
|
|
11786
11803
|
};
|
|
11804
|
+
FORBIDDEN_MERGE_KEYS = new Set(["__proto__", "constructor", "prototype"]);
|
|
11787
11805
|
CONFIG_SINGLETON_KEY = Symbol.for("bun-query-builder.config");
|
|
11788
|
-
config5 = globalThis[CONFIG_SINGLETON_KEY] ??= {
|
|
11806
|
+
config5 = globalThis[CONFIG_SINGLETON_KEY] ??= mergeObject({}, defaultConfig4);
|
|
11789
11807
|
CONFIG_STATE_KEY = Symbol.for("bun-query-builder.config-state");
|
|
11790
11808
|
configState = globalThis[CONFIG_STATE_KEY] ??= { explicit: new Set, fileLoaded: false };
|
|
11791
11809
|
_warnedDialectConflicts = new Set;
|
|
@@ -12828,6 +12846,20 @@ function createQueryBuilder(state) {
|
|
|
12828
12846
|
const _sql = state?.sql ?? getOrCreateBunSql();
|
|
12829
12847
|
const meta = state?.meta;
|
|
12830
12848
|
const schema = state?.schema;
|
|
12849
|
+
function advisoryLockKey(key) {
|
|
12850
|
+
if (typeof key === "number")
|
|
12851
|
+
return key;
|
|
12852
|
+
const s = String(key);
|
|
12853
|
+
let hash = 7;
|
|
12854
|
+
for (let i = 0;i < s.length; i++)
|
|
12855
|
+
hash = hash * 31 + s.charCodeAt(i) | 0;
|
|
12856
|
+
return Math.abs(hash);
|
|
12857
|
+
}
|
|
12858
|
+
function activeHooks() {
|
|
12859
|
+
if (!state?.hooks)
|
|
12860
|
+
return config5.hooks;
|
|
12861
|
+
return { ...config5.hooks, ...state.hooks };
|
|
12862
|
+
}
|
|
12831
12863
|
const dynamicWhereColumnCache = new Map;
|
|
12832
12864
|
function applyCondition(expr) {
|
|
12833
12865
|
if (Array.isArray(expr)) {
|
|
@@ -12919,7 +12951,7 @@ function createQueryBuilder(state) {
|
|
|
12919
12951
|
return;
|
|
12920
12952
|
}
|
|
12921
12953
|
function runWithHooks(q, kind, opts) {
|
|
12922
|
-
const hooks =
|
|
12954
|
+
const hooks = activeHooks();
|
|
12923
12955
|
const slowMs = hooks?.slowQueryThresholdMs;
|
|
12924
12956
|
const slowEnabled = slowMs != null && slowMs >= 0;
|
|
12925
12957
|
const hasSlowQuery = Boolean(hooks?.onSlowQuery || slowEnabled);
|
|
@@ -15001,7 +15033,7 @@ function createQueryBuilder(state) {
|
|
|
15001
15033
|
return text;
|
|
15002
15034
|
},
|
|
15003
15035
|
async get() {
|
|
15004
|
-
const hooks =
|
|
15036
|
+
const hooks = activeHooks();
|
|
15005
15037
|
const hasQueryHooks = hooks && (hooks.onQueryStart || hooks.onQueryEnd || hooks.onQueryError || hooks.startSpan || hasSlowQueryHook(hooks));
|
|
15006
15038
|
if (!config5.softDeletes?.enabled && !useCache && !timeoutMs && !abortSignal && !hasQueryHooks) {
|
|
15007
15039
|
const prepareFn = _sql._prepareStatement;
|
|
@@ -15054,7 +15086,7 @@ function createQueryBuilder(state) {
|
|
|
15054
15086
|
return result;
|
|
15055
15087
|
},
|
|
15056
15088
|
async first() {
|
|
15057
|
-
const fHooks =
|
|
15089
|
+
const fHooks = activeHooks();
|
|
15058
15090
|
const fHasQueryHooks = fHooks && (fHooks.onQueryStart || fHooks.onQueryEnd || fHooks.onQueryError || fHooks.startSpan || hasSlowQueryHook(fHooks));
|
|
15059
15091
|
if (!config5.softDeletes?.enabled && !useCache && !timeoutMs && !abortSignal && !fHasQueryHooks) {
|
|
15060
15092
|
const prepareFn = _sql._prepareStatement;
|
|
@@ -15136,7 +15168,7 @@ function createQueryBuilder(state) {
|
|
|
15136
15168
|
} else {
|
|
15137
15169
|
countText = `SELECT COUNT(*) as c FROM ${table}`;
|
|
15138
15170
|
}
|
|
15139
|
-
const cHooks =
|
|
15171
|
+
const cHooks = activeHooks();
|
|
15140
15172
|
const cHasHooks = cHooks && (cHooks.onQueryStart || cHooks.onQueryEnd || cHooks.onQueryError || cHooks.startSpan || hasSlowQueryHook(cHooks));
|
|
15141
15173
|
if (!config5.softDeletes?.enabled && !useCache && !timeoutMs && !abortSignal && !cHasHooks) {
|
|
15142
15174
|
const prepareFn = _sql._prepareStatement;
|
|
@@ -15154,7 +15186,7 @@ function createQueryBuilder(state) {
|
|
|
15154
15186
|
async avg(column) {
|
|
15155
15187
|
const fromIdx = text.indexOf(" FROM ");
|
|
15156
15188
|
const avgText = fromIdx !== -1 ? `SELECT AVG(${column}) as a${text.substring(fromIdx)}` : `SELECT AVG(${column}) as a FROM ${table}`;
|
|
15157
|
-
const aHooks =
|
|
15189
|
+
const aHooks = activeHooks();
|
|
15158
15190
|
const aHasHooks = aHooks && (aHooks.onQueryStart || aHooks.onQueryEnd || aHooks.onQueryError || aHooks.startSpan || hasSlowQueryHook(aHooks));
|
|
15159
15191
|
if (!config5.softDeletes?.enabled && !useCache && !timeoutMs && !abortSignal && !aHasHooks) {
|
|
15160
15192
|
const prepareFn = _sql._prepareStatement;
|
|
@@ -15291,7 +15323,7 @@ function createQueryBuilder(state) {
|
|
|
15291
15323
|
}
|
|
15292
15324
|
return {
|
|
15293
15325
|
configure(opts) {
|
|
15294
|
-
|
|
15326
|
+
setConfig(opts);
|
|
15295
15327
|
return this;
|
|
15296
15328
|
},
|
|
15297
15329
|
id(name) {
|
|
@@ -15668,7 +15700,7 @@ function createQueryBuilder(state) {
|
|
|
15668
15700
|
return makeExecutableQuery(built, sqlText);
|
|
15669
15701
|
},
|
|
15670
15702
|
execute() {
|
|
15671
|
-
const hooks =
|
|
15703
|
+
const hooks = activeHooks();
|
|
15672
15704
|
const hasHooks = hooks && (hooks.onQueryStart || hooks.onQueryEnd || hooks.onQueryError || hooks.startSpan || hooks.beforeCreate || hooks.afterCreate || hasSlowQueryHook(hooks));
|
|
15673
15705
|
if (!hasHooks) {
|
|
15674
15706
|
const prepareFn = _sql._prepareStatement;
|
|
@@ -16022,13 +16054,13 @@ function createQueryBuilder(state) {
|
|
|
16022
16054
|
},
|
|
16023
16055
|
async execute() {
|
|
16024
16056
|
try {
|
|
16025
|
-
await
|
|
16057
|
+
await activeHooks()?.beforeDelete?.({ table: String(table), where: whereCondition });
|
|
16026
16058
|
} catch (err) {
|
|
16027
16059
|
throw err;
|
|
16028
16060
|
}
|
|
16029
16061
|
const result = mutationCount(await runWithHooks(ensureDelBuilt(), "delete"));
|
|
16030
16062
|
try {
|
|
16031
|
-
await
|
|
16063
|
+
await activeHooks()?.afterDelete?.({ table: String(table), where: whereCondition, result });
|
|
16032
16064
|
} catch {}
|
|
16033
16065
|
return result;
|
|
16034
16066
|
},
|
|
@@ -16092,18 +16124,14 @@ function createQueryBuilder(state) {
|
|
|
16092
16124
|
},
|
|
16093
16125
|
async advisoryLock(key) {
|
|
16094
16126
|
if (config5.dialect === "postgres") {
|
|
16095
|
-
const
|
|
16096
|
-
|
|
16097
|
-
for (let i = 0;i < s.length; i++)
|
|
16098
|
-
hash = hash * 31 + s.charCodeAt(i) | 0;
|
|
16099
|
-
const k = typeof key === "number" ? key : Math.abs(hash);
|
|
16100
|
-
const q = bunSql`SELECT pg_advisory_lock(${k})`;
|
|
16127
|
+
const k = advisoryLockKey(key);
|
|
16128
|
+
const q = _sql`SELECT pg_advisory_lock(${k})`;
|
|
16101
16129
|
await runWithHooks(q, "raw");
|
|
16102
16130
|
return;
|
|
16103
16131
|
}
|
|
16104
16132
|
if (isMysqlLike(config5.dialect)) {
|
|
16105
16133
|
const lockName = `bqb:${String(key)}`;
|
|
16106
|
-
const q =
|
|
16134
|
+
const q = _sql`SELECT GET_LOCK(${lockName}, -1) AS ok`;
|
|
16107
16135
|
await runWithHooks(q, "raw");
|
|
16108
16136
|
return;
|
|
16109
16137
|
}
|
|
@@ -16111,18 +16139,29 @@ function createQueryBuilder(state) {
|
|
|
16111
16139
|
},
|
|
16112
16140
|
async tryAdvisoryLock(key) {
|
|
16113
16141
|
if (config5.dialect === "postgres") {
|
|
16114
|
-
const
|
|
16115
|
-
|
|
16116
|
-
|
|
16117
|
-
|
|
16118
|
-
|
|
16119
|
-
|
|
16142
|
+
const k = advisoryLockKey(key);
|
|
16143
|
+
const q = _sql`SELECT pg_try_advisory_lock(${k}) as ok`;
|
|
16144
|
+
const rows = await runWithHooks(q, "raw");
|
|
16145
|
+
return Boolean(rows?.[0]?.ok);
|
|
16146
|
+
}
|
|
16147
|
+
if (isMysqlLike(config5.dialect)) {
|
|
16148
|
+
const lockName = `bqb:${String(key)}`;
|
|
16149
|
+
const q = _sql`SELECT GET_LOCK(${lockName}, 0) AS ok`;
|
|
16150
|
+
const rows = await runWithHooks(q, "raw");
|
|
16151
|
+
return Number(rows?.[0]?.ok) === 1;
|
|
16152
|
+
}
|
|
16153
|
+
return false;
|
|
16154
|
+
},
|
|
16155
|
+
async advisoryUnlock(key) {
|
|
16156
|
+
if (config5.dialect === "postgres") {
|
|
16157
|
+
const k = advisoryLockKey(key);
|
|
16158
|
+
const q = _sql`SELECT pg_advisory_unlock(${k}) as ok`;
|
|
16120
16159
|
const rows = await runWithHooks(q, "raw");
|
|
16121
16160
|
return Boolean(rows?.[0]?.ok);
|
|
16122
16161
|
}
|
|
16123
16162
|
if (isMysqlLike(config5.dialect)) {
|
|
16124
16163
|
const lockName = `bqb:${String(key)}`;
|
|
16125
|
-
const q =
|
|
16164
|
+
const q = _sql`SELECT RELEASE_LOCK(${lockName}) AS ok`;
|
|
16126
16165
|
const rows = await runWithHooks(q, "raw");
|
|
16127
16166
|
return Number(rows?.[0]?.ok) === 1;
|
|
16128
16167
|
}
|
|
@@ -16132,11 +16171,11 @@ function createQueryBuilder(state) {
|
|
|
16132
16171
|
return _sql.unsafe(query, params);
|
|
16133
16172
|
},
|
|
16134
16173
|
file(path, params) {
|
|
16135
|
-
return
|
|
16174
|
+
return _sql.file(path, params);
|
|
16136
16175
|
},
|
|
16137
16176
|
async reserve() {
|
|
16138
16177
|
const reserved = await bunSql.reserve();
|
|
16139
|
-
const qb = createQueryBuilder({ sql: reserved, meta, schema });
|
|
16178
|
+
const qb = createQueryBuilder({ sql: reserved, meta, schema, hooks: state?.hooks });
|
|
16140
16179
|
qb.release = () => reserved.release();
|
|
16141
16180
|
return qb;
|
|
16142
16181
|
},
|
|
@@ -16156,7 +16195,7 @@ function createQueryBuilder(state) {
|
|
|
16156
16195
|
},
|
|
16157
16196
|
async ping() {
|
|
16158
16197
|
try {
|
|
16159
|
-
const q =
|
|
16198
|
+
const q = _sql`SELECT 1`;
|
|
16160
16199
|
await runWithHooks(q, "select");
|
|
16161
16200
|
return true;
|
|
16162
16201
|
} catch {
|
|
@@ -16183,7 +16222,7 @@ function createQueryBuilder(state) {
|
|
|
16183
16222
|
opts.logger?.({ type: "start", attempt: attempt2 });
|
|
16184
16223
|
const start = Date.now();
|
|
16185
16224
|
return await txConn[txMethod](async (tx) => {
|
|
16186
|
-
const qb = createQueryBuilder({ sql: tx, meta, schema, inTransaction: true });
|
|
16225
|
+
const qb = createQueryBuilder({ sql: tx, meta, schema, hooks: state?.hooks, inTransaction: true });
|
|
16187
16226
|
if (opts?.isolation && !nested) {
|
|
16188
16227
|
const level = opts.isolation;
|
|
16189
16228
|
const upper = level === "read committed" ? "READ COMMITTED" : level === "repeatable read" ? "REPEATABLE READ" : "SERIALIZABLE";
|
|
@@ -16246,14 +16285,14 @@ function createQueryBuilder(state) {
|
|
|
16246
16285
|
if (!s || typeof s.savepoint !== "function")
|
|
16247
16286
|
throw new Error("savepoint() is not supported by the active connection");
|
|
16248
16287
|
return await s.savepoint(async (sp) => {
|
|
16249
|
-
const qb = createQueryBuilder({ sql: sp, meta, schema, inTransaction: true });
|
|
16288
|
+
const qb = createQueryBuilder({ sql: sp, meta, schema, hooks: state?.hooks, inTransaction: true });
|
|
16250
16289
|
return await fn(qb);
|
|
16251
16290
|
});
|
|
16252
16291
|
},
|
|
16253
16292
|
async beginDistributed(name, fn) {
|
|
16254
16293
|
const txConn = state?.sql ?? bunSql;
|
|
16255
16294
|
const res = await txConn.beginDistributed(name, async (tx) => {
|
|
16256
|
-
const qb = createQueryBuilder({ sql: tx, meta, schema });
|
|
16295
|
+
const qb = createQueryBuilder({ sql: tx, meta, schema, hooks: state?.hooks });
|
|
16257
16296
|
return await fn(qb);
|
|
16258
16297
|
});
|
|
16259
16298
|
return res;
|
|
@@ -16279,24 +16318,24 @@ function createQueryBuilder(state) {
|
|
|
16279
16318
|
const { colsSql, valuesSql, params } = buildInsertClause(rows);
|
|
16280
16319
|
const tbl = quoteInsertIdent(String(table));
|
|
16281
16320
|
const sqlText = isMysqlLike(config5.dialect) ? `INSERT IGNORE INTO ${tbl} (${colsSql}) VALUES ${valuesSql}` : `INSERT INTO ${tbl} (${colsSql}) VALUES ${valuesSql} ON CONFLICT DO NOTHING`;
|
|
16282
|
-
return
|
|
16321
|
+
return _sql.unsafe(sqlText, params).execute();
|
|
16283
16322
|
},
|
|
16284
16323
|
async insertGetId(table, values, idColumn = "id") {
|
|
16285
16324
|
const { colsSql, valuesSql, params } = buildInsertClause([values]);
|
|
16286
16325
|
const tbl = quoteInsertIdent(String(table));
|
|
16287
16326
|
if (isMysqlLike(config5.dialect)) {
|
|
16288
|
-
await
|
|
16289
|
-
const [row2] = await
|
|
16327
|
+
await _sql.unsafe(`INSERT INTO ${tbl} (${colsSql}) VALUES ${valuesSql}`, params).execute();
|
|
16328
|
+
const [row2] = await _sql.unsafe(`SELECT LAST_INSERT_ID() as id`).execute();
|
|
16290
16329
|
return row2?.id;
|
|
16291
16330
|
}
|
|
16292
16331
|
if (config5.dialect === "sqlite") {
|
|
16293
|
-
const res = await
|
|
16332
|
+
const res = await _sql.unsafe(`INSERT INTO ${tbl} (${colsSql}) VALUES ${valuesSql}`, params).execute();
|
|
16294
16333
|
if (res?.lastInsertRowid != null)
|
|
16295
16334
|
return res.lastInsertRowid;
|
|
16296
|
-
const [row2] = await
|
|
16335
|
+
const [row2] = await _sql.unsafe(`SELECT last_insert_rowid() as id`).execute();
|
|
16297
16336
|
return row2?.id;
|
|
16298
16337
|
}
|
|
16299
|
-
const [row] = await
|
|
16338
|
+
const [row] = await _sql.unsafe(`INSERT INTO ${tbl} (${colsSql}) VALUES ${valuesSql} RETURNING ${quoteInsertIdent(String(idColumn))} as id`, params).execute();
|
|
16300
16339
|
return row?.id;
|
|
16301
16340
|
},
|
|
16302
16341
|
async updateOrInsert(table, match, values) {
|
|
@@ -16305,18 +16344,18 @@ function createQueryBuilder(state) {
|
|
|
16305
16344
|
let idx = 1;
|
|
16306
16345
|
const whereSql = matchKeys.map((k) => `${quoteInsertIdent(k)} = ${getPlaceholder(idx++)}`).join(" AND ");
|
|
16307
16346
|
const whereParams = matchKeys.map((k) => match[k]);
|
|
16308
|
-
const existsRows = await
|
|
16347
|
+
const existsRows = await _sql.unsafe(`SELECT 1 FROM ${tbl} WHERE ${whereSql} LIMIT 1`, whereParams).execute();
|
|
16309
16348
|
if (existsRows.length) {
|
|
16310
16349
|
const setKeys = Object.keys(values);
|
|
16311
16350
|
let i = 1;
|
|
16312
16351
|
const setSql = setKeys.map((k) => `${quoteInsertIdent(k)} = ${getPlaceholder(i++)}`).join(", ");
|
|
16313
16352
|
const whereSql2 = matchKeys.map((k) => `${quoteInsertIdent(k)} = ${getPlaceholder(i++)}`).join(" AND ");
|
|
16314
16353
|
const params2 = [...setKeys.map((k) => values[k]), ...matchKeys.map((k) => match[k])];
|
|
16315
|
-
await
|
|
16354
|
+
await _sql.unsafe(`UPDATE ${tbl} SET ${setSql} WHERE ${whereSql2}`, params2).execute();
|
|
16316
16355
|
return true;
|
|
16317
16356
|
}
|
|
16318
16357
|
const { colsSql, valuesSql, params } = buildInsertClause([{ ...match, ...values }]);
|
|
16319
|
-
await
|
|
16358
|
+
await _sql.unsafe(`INSERT INTO ${tbl} (${colsSql}) VALUES ${valuesSql}`, params).execute();
|
|
16320
16359
|
return true;
|
|
16321
16360
|
},
|
|
16322
16361
|
async upsert(table, rows, conflictColumns, mergeColumns) {
|
|
@@ -16330,15 +16369,15 @@ function createQueryBuilder(state) {
|
|
|
16330
16369
|
const insert = `INSERT INTO ${tbl} (${colsSql}) VALUES ${valuesSql}`;
|
|
16331
16370
|
if (isMysqlLike(config5.dialect)) {
|
|
16332
16371
|
if (setCols.length === 0)
|
|
16333
|
-
return
|
|
16372
|
+
return _sql.unsafe(`INSERT IGNORE INTO ${tbl} (${colsSql}) VALUES ${valuesSql}`, params).execute();
|
|
16334
16373
|
const updateList2 = setCols.map((c) => `${quoteInsertIdent(c)} = VALUES(${quoteInsertIdent(c)})`).join(", ");
|
|
16335
|
-
return
|
|
16374
|
+
return _sql.unsafe(`${insert} ON DUPLICATE KEY UPDATE ${updateList2}`, params).execute();
|
|
16336
16375
|
}
|
|
16337
16376
|
const targets = targetCols.map(quoteInsertIdent).join(", ");
|
|
16338
16377
|
if (setCols.length === 0)
|
|
16339
|
-
return
|
|
16378
|
+
return _sql.unsafe(`${insert} ON CONFLICT (${targets}) DO NOTHING`, params).execute();
|
|
16340
16379
|
const updateList = setCols.map((c) => `${quoteInsertIdent(c)} = EXCLUDED.${quoteInsertIdent(c)}`).join(", ");
|
|
16341
|
-
return
|
|
16380
|
+
return _sql.unsafe(`${insert} ON CONFLICT (${targets}) DO UPDATE SET ${updateList}`, params).execute();
|
|
16342
16381
|
},
|
|
16343
16382
|
async save(table, values) {
|
|
16344
16383
|
const pk = meta?.primaryKeys[String(table)] ?? "id";
|
|
@@ -16381,26 +16420,25 @@ function createQueryBuilder(state) {
|
|
|
16381
16420
|
async rawQuery(query) {
|
|
16382
16421
|
const start = Date.now();
|
|
16383
16422
|
try {
|
|
16384
|
-
|
|
16385
|
-
const res = await
|
|
16386
|
-
|
|
16423
|
+
activeHooks()?.onQueryStart?.({ sql: query, kind: "raw" });
|
|
16424
|
+
const res = await _sql.unsafe(query);
|
|
16425
|
+
activeHooks()?.onQueryEnd?.({ sql: query, durationMs: Date.now() - start, kind: "raw" });
|
|
16387
16426
|
return res;
|
|
16388
16427
|
} catch (err) {
|
|
16389
|
-
|
|
16428
|
+
activeHooks()?.onQueryError?.({ sql: query, error: err, durationMs: Date.now() - start, kind: "raw" });
|
|
16390
16429
|
throw err;
|
|
16391
16430
|
}
|
|
16392
16431
|
},
|
|
16393
16432
|
async create(table, values) {
|
|
16394
16433
|
const pk = meta?.primaryKeys[String(table)] ?? "id";
|
|
16395
16434
|
try {
|
|
16396
|
-
await
|
|
16435
|
+
await activeHooks()?.beforeCreate?.({ table: String(table), data: values });
|
|
16397
16436
|
} catch (err) {
|
|
16398
16437
|
throw err;
|
|
16399
16438
|
}
|
|
16400
16439
|
if (config5.dialect === "postgres") {
|
|
16401
|
-
const q =
|
|
16440
|
+
const q = _sql`INSERT INTO ${_sql(String(table))} ${_sql(values)} RETURNING ${_sql(String(pk))} as id`;
|
|
16402
16441
|
const [result] = await q.execute();
|
|
16403
|
-
console.log("resultId", result);
|
|
16404
16442
|
if (!result?.id) {
|
|
16405
16443
|
console.error(`create() failed to get insert ID for table ${String(table)}`);
|
|
16406
16444
|
console.error("Inserted values:", values);
|
|
@@ -16413,7 +16451,7 @@ function createQueryBuilder(state) {
|
|
|
16413
16451
|
throw new Error(`create() failed to retrieve inserted row for table ${String(table)} with id ${result.id}`);
|
|
16414
16452
|
}
|
|
16415
16453
|
try {
|
|
16416
|
-
await
|
|
16454
|
+
await activeHooks()?.afterCreate?.({ table: String(table), data: values, result: row });
|
|
16417
16455
|
} catch {}
|
|
16418
16456
|
return row;
|
|
16419
16457
|
} else {
|
|
@@ -16428,7 +16466,7 @@ function createQueryBuilder(state) {
|
|
|
16428
16466
|
throw new Error(`create() failed to retrieve inserted row for table ${String(table)} with id ${id}`);
|
|
16429
16467
|
}
|
|
16430
16468
|
try {
|
|
16431
|
-
await
|
|
16469
|
+
await activeHooks()?.afterCreate?.({ table: String(table), data: values, result: row });
|
|
16432
16470
|
} catch {}
|
|
16433
16471
|
return row;
|
|
16434
16472
|
}
|
|
@@ -16568,28 +16606,28 @@ function createQueryBuilder(state) {
|
|
|
16568
16606
|
return await this.create(table, { ...match, ...values });
|
|
16569
16607
|
},
|
|
16570
16608
|
async count(table, column) {
|
|
16571
|
-
const col = column ?
|
|
16572
|
-
const q =
|
|
16609
|
+
const col = column ? _sql(String(column)) : _sql`*`;
|
|
16610
|
+
const q = _sql`SELECT COUNT(${col}) as c FROM ${_sql(String(table))}`;
|
|
16573
16611
|
const [row] = await q.execute();
|
|
16574
16612
|
return Number(row?.c ?? 0);
|
|
16575
16613
|
},
|
|
16576
16614
|
async sum(table, column) {
|
|
16577
|
-
const q =
|
|
16615
|
+
const q = _sql`SELECT SUM(${_sql(String(column))}) as s FROM ${_sql(String(table))}`;
|
|
16578
16616
|
const [row] = await q.execute();
|
|
16579
16617
|
return Number(row?.s ?? 0);
|
|
16580
16618
|
},
|
|
16581
16619
|
async avg(table, column) {
|
|
16582
|
-
const q =
|
|
16620
|
+
const q = _sql`SELECT AVG(${_sql(String(column))}) as a FROM ${_sql(String(table))}`;
|
|
16583
16621
|
const [row] = await q.execute();
|
|
16584
16622
|
return Number(row?.a ?? 0);
|
|
16585
16623
|
},
|
|
16586
16624
|
async min(table, column) {
|
|
16587
|
-
const q =
|
|
16625
|
+
const q = _sql`SELECT MIN(${_sql(String(column))}) as m FROM ${_sql(String(table))}`;
|
|
16588
16626
|
const [row] = await q.execute();
|
|
16589
16627
|
return row?.m;
|
|
16590
16628
|
},
|
|
16591
16629
|
async max(table, column) {
|
|
16592
|
-
const q =
|
|
16630
|
+
const q = _sql`SELECT MAX(${_sql(String(column))}) as m FROM ${_sql(String(table))}`;
|
|
16593
16631
|
const [row] = await q.execute();
|
|
16594
16632
|
return row?.m;
|
|
16595
16633
|
},
|
|
@@ -19114,6 +19152,33 @@ async function generateMigration(dir, opts = {}) {
|
|
|
19114
19152
|
savePlanSnapshot(workspaceRoot, dialect, plan);
|
|
19115
19153
|
return { sql, sqlStatements, hasChanges, plan, operations };
|
|
19116
19154
|
}
|
|
19155
|
+
function supportsTransactionalDdl(dialect) {
|
|
19156
|
+
return dialect === "postgres" || dialect === "sqlite";
|
|
19157
|
+
}
|
|
19158
|
+
async function withMigrationLock(dialect, fn) {
|
|
19159
|
+
if (dialect === "sqlite")
|
|
19160
|
+
return await fn();
|
|
19161
|
+
const qb = createQueryBuilder();
|
|
19162
|
+
let reserved;
|
|
19163
|
+
try {
|
|
19164
|
+
reserved = await qb.reserve();
|
|
19165
|
+
} catch {
|
|
19166
|
+
return await fn();
|
|
19167
|
+
}
|
|
19168
|
+
let held = false;
|
|
19169
|
+
try {
|
|
19170
|
+
await reserved.advisoryLock(MIGRATION_LOCK_KEY);
|
|
19171
|
+
held = true;
|
|
19172
|
+
return await fn();
|
|
19173
|
+
} finally {
|
|
19174
|
+
try {
|
|
19175
|
+
if (held)
|
|
19176
|
+
await reserved.advisoryUnlock(MIGRATION_LOCK_KEY);
|
|
19177
|
+
} finally {
|
|
19178
|
+
reserved.release?.();
|
|
19179
|
+
}
|
|
19180
|
+
}
|
|
19181
|
+
}
|
|
19117
19182
|
async function executeMigration(dir) {
|
|
19118
19183
|
await getConfig();
|
|
19119
19184
|
const workspaceRoot = getWorkspaceRoot();
|
|
@@ -19129,31 +19194,46 @@ async function executeMigration(dir) {
|
|
|
19129
19194
|
try {
|
|
19130
19195
|
const qb = createQueryBuilder();
|
|
19131
19196
|
await createMigrationsTable(qb, dialect);
|
|
19132
|
-
|
|
19133
|
-
|
|
19134
|
-
|
|
19135
|
-
|
|
19136
|
-
|
|
19137
|
-
|
|
19138
|
-
|
|
19139
|
-
|
|
19140
|
-
const
|
|
19141
|
-
|
|
19142
|
-
|
|
19143
|
-
|
|
19144
|
-
|
|
19145
|
-
|
|
19146
|
-
|
|
19147
|
-
|
|
19148
|
-
|
|
19149
|
-
|
|
19150
|
-
|
|
19197
|
+
return await withMigrationLock(dialect, async () => {
|
|
19198
|
+
const executedMigrations = await getExecutedMigrations(qb, dialect);
|
|
19199
|
+
const pending = scriptFiles.filter((file2) => !executedMigrations.includes(file2));
|
|
19200
|
+
if (pending.length === 0) {
|
|
19201
|
+
info("-- No pending migrations to execute");
|
|
19202
|
+
return true;
|
|
19203
|
+
}
|
|
19204
|
+
info(`-- Executing ${pending.length} migration${pending.length === 1 ? "" : "s"}`);
|
|
19205
|
+
for (const file2 of pending) {
|
|
19206
|
+
const filePath = join10(sqlDir, file2);
|
|
19207
|
+
info(`-- Executing: ${file2}`);
|
|
19208
|
+
try {
|
|
19209
|
+
const wrappable = supportsTransactionalDdl(dialect) && !NON_TRANSACTIONAL_SQL.test(readFileSync3(filePath, "utf-8"));
|
|
19210
|
+
if (wrappable) {
|
|
19211
|
+
await qb.transaction(async (tx) => {
|
|
19212
|
+
await tx.file(filePath);
|
|
19213
|
+
await recordMigration(tx, file2, dialect);
|
|
19214
|
+
});
|
|
19215
|
+
} else {
|
|
19216
|
+
await qb.file(filePath);
|
|
19217
|
+
await recordMigration(qb, file2, dialect);
|
|
19218
|
+
if (!supportsTransactionalDdl(dialect))
|
|
19219
|
+
info(`-- (${dialect} commits DDL implicitly, so ${file2} was not applied atomically)`);
|
|
19220
|
+
else
|
|
19221
|
+
info(`-- (${file2} contains a statement that cannot run in a transaction, so it was not applied atomically)`);
|
|
19222
|
+
}
|
|
19223
|
+
info(`-- \u2713 Migration ${file2} executed and recorded`);
|
|
19224
|
+
} catch (err) {
|
|
19225
|
+
if (isGeneratedMigration(sqlDir, file2) && isAlreadyAppliedError(err)) {
|
|
19226
|
+
await recordMigration(qb, file2, dialect);
|
|
19227
|
+
info(`-- \u2713 Migration ${file2} was already applied; recorded it (${err instanceof Error ? err.message : String(err)})`);
|
|
19228
|
+
continue;
|
|
19229
|
+
}
|
|
19230
|
+
console.error(`-- \u2717 Migration ${file2} failed:`, err);
|
|
19231
|
+
throw err;
|
|
19151
19232
|
}
|
|
19152
|
-
console.error(`-- \u2717 Migration ${file2} failed:`, err);
|
|
19153
|
-
throw err;
|
|
19154
19233
|
}
|
|
19155
|
-
|
|
19156
|
-
|
|
19234
|
+
info("-- All migrations executed successfully");
|
|
19235
|
+
return true;
|
|
19236
|
+
});
|
|
19157
19237
|
} catch (err) {
|
|
19158
19238
|
console.error("-- Migration execution failed:", err);
|
|
19159
19239
|
throw err;
|
|
@@ -19344,6 +19424,7 @@ async function recordMigration(qb, migrationFile, dialect) {
|
|
|
19344
19424
|
throw err;
|
|
19345
19425
|
}
|
|
19346
19426
|
}
|
|
19427
|
+
var MIGRATION_LOCK_KEY = "bun-query-builder:migrate", NON_TRANSACTIONAL_SQL;
|
|
19347
19428
|
var init_migrate = __esm(() => {
|
|
19348
19429
|
init_config();
|
|
19349
19430
|
init_db();
|
|
@@ -19351,6 +19432,7 @@ var init_migrate = __esm(() => {
|
|
|
19351
19432
|
init_src2();
|
|
19352
19433
|
init_introspect_db();
|
|
19353
19434
|
init_workspace();
|
|
19435
|
+
NON_TRANSACTIONAL_SQL = /\bCONCURRENTLY\b|^\s*VACUUM\b/im;
|
|
19354
19436
|
});
|
|
19355
19437
|
|
|
19356
19438
|
// src/actions/migrate-generate.ts
|
|
@@ -25432,6 +25514,28 @@ function toTableName2(modelName) {
|
|
|
25432
25514
|
tableNameCache.set(modelName, cached);
|
|
25433
25515
|
return cached;
|
|
25434
25516
|
}
|
|
25517
|
+
function relationNamesFor(definition, kind) {
|
|
25518
|
+
const rel = definition[kind];
|
|
25519
|
+
if (!rel)
|
|
25520
|
+
return [];
|
|
25521
|
+
if (Array.isArray(rel))
|
|
25522
|
+
return rel.map((item) => typeof item === "string" ? item : String(item?.model ?? "")).filter(Boolean);
|
|
25523
|
+
if (typeof rel === "object")
|
|
25524
|
+
return Object.keys(rel);
|
|
25525
|
+
if (typeof rel === "string")
|
|
25526
|
+
return [rel];
|
|
25527
|
+
return [];
|
|
25528
|
+
}
|
|
25529
|
+
function unresolvedRelationMessage(definition, relationName) {
|
|
25530
|
+
const model = definition.name ?? definition.table ?? "model";
|
|
25531
|
+
const declaredUnder = DECLARABLE_RELATION_KINDS.filter((kind) => relationNamesFor(definition, kind).some((n2) => n2.toLowerCase() === relationName.toLowerCase()));
|
|
25532
|
+
const unsupported = declaredUnder.filter((k2) => !RESOLVABLE_RELATION_KINDS.includes(k2));
|
|
25533
|
+
if (unsupported.length > 0) {
|
|
25534
|
+
return `[orm] '${model}' declares '${relationName}' as ${unsupported.join("/")}, which eager loading does not support yet. ` + `Supported kinds: ${RESOLVABLE_RELATION_KINDS.join(", ")}. ` + `Load it with a manual query for now \u2014 see stacksjs/bun-query-builder#1068.`;
|
|
25535
|
+
}
|
|
25536
|
+
const available = RESOLVABLE_RELATION_KINDS.flatMap((kind) => relationNamesFor(definition, kind));
|
|
25537
|
+
return available.length > 0 ? `[orm] '${model}' has no relation '${relationName}'. Available: ${available.join(", ")}.` : `[orm] '${model}' has no relation '${relationName}' \u2014 it declares no relations at all.`;
|
|
25538
|
+
}
|
|
25435
25539
|
function resolveRelation(definition, relationName) {
|
|
25436
25540
|
const parentName = definition.name;
|
|
25437
25541
|
const parentTable = definition.table;
|
|
@@ -26166,7 +26270,7 @@ class ModelQueryBuilder {
|
|
|
26166
26270
|
relationCache.set(cacheKey, rel);
|
|
26167
26271
|
}
|
|
26168
26272
|
if (!rel)
|
|
26169
|
-
|
|
26273
|
+
throw new Error(unresolvedRelationMessage(this._definition, relationName));
|
|
26170
26274
|
if (rel.type === "hasMany" || rel.type === "hasOne") {
|
|
26171
26275
|
const parentIds = distinctParentIds(instances, pk);
|
|
26172
26276
|
if (parentIds.length === 0)
|
|
@@ -26758,7 +26862,7 @@ async function seedModel(definition, count, faker) {
|
|
|
26758
26862
|
await exec.run(`INSERT INTO ${definition.table} (${columns.join(", ")}) VALUES (${columns.map(() => "?").join(", ")})`, Object.values(data));
|
|
26759
26863
|
}
|
|
26760
26864
|
}
|
|
26761
|
-
var SAFE_SQL_IDENTIFIER, _getModel = null, pgPlaceholderCache, PG_PLACEHOLDER_CACHE_MAX = 500, globalDb = null, _executor = null, _executorForDb = null, _executorDialect = null, _executorDatabase = null, SOFT_DELETE_COLUMN = "deleted_at", BTM_RELATED_ALIAS = "__btm_rel__", btmKeysCache, snakeCaseCache, tableNameCache, relationCache, RELATION_CACHE_MAX = 1000;
|
|
26865
|
+
var SAFE_SQL_IDENTIFIER, _getModel = null, pgPlaceholderCache, PG_PLACEHOLDER_CACHE_MAX = 500, globalDb = null, _executor = null, _executorForDb = null, _executorDialect = null, _executorDatabase = null, SOFT_DELETE_COLUMN = "deleted_at", BTM_RELATED_ALIAS = "__btm_rel__", btmKeysCache, snakeCaseCache, tableNameCache, relationCache, RELATION_CACHE_MAX = 1000, RESOLVABLE_RELATION_KINDS, DECLARABLE_RELATION_KINDS;
|
|
26762
26866
|
var init_orm = __esm(() => {
|
|
26763
26867
|
init_config();
|
|
26764
26868
|
init_db();
|
|
@@ -26769,6 +26873,17 @@ var init_orm = __esm(() => {
|
|
|
26769
26873
|
snakeCaseCache = new Map;
|
|
26770
26874
|
tableNameCache = new Map;
|
|
26771
26875
|
relationCache = new Map;
|
|
26876
|
+
RESOLVABLE_RELATION_KINDS = ["hasMany", "hasOne", "belongsTo", "belongsToMany"];
|
|
26877
|
+
DECLARABLE_RELATION_KINDS = [
|
|
26878
|
+
...RESOLVABLE_RELATION_KINDS,
|
|
26879
|
+
"hasOneThrough",
|
|
26880
|
+
"hasManyThrough",
|
|
26881
|
+
"morphOne",
|
|
26882
|
+
"morphMany",
|
|
26883
|
+
"morphTo",
|
|
26884
|
+
"morphToMany",
|
|
26885
|
+
"morphedByMany"
|
|
26886
|
+
];
|
|
26772
26887
|
});
|
|
26773
26888
|
|
|
26774
26889
|
// src/model.ts
|
|
@@ -31690,7 +31805,7 @@ function getPrefix() {
|
|
|
31690
31805
|
}
|
|
31691
31806
|
var prefix = getPrefix();
|
|
31692
31807
|
// package.json
|
|
31693
|
-
var version2 = "0.2.
|
|
31808
|
+
var version2 = "0.2.26";
|
|
31694
31809
|
|
|
31695
31810
|
// bin/cli.ts
|
|
31696
31811
|
init_actions();
|
package/dist/client.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { config } from './config';
|
|
2
2
|
import { resetConnection } from './db';
|
|
3
3
|
import type { DatabaseSchema } from './schema';
|
|
4
|
+
import type { QueryHooks } from './types';
|
|
4
5
|
import type { SchemaMeta } from './meta';
|
|
5
6
|
/**
|
|
6
7
|
* # `raw`
|
|
@@ -380,6 +381,7 @@ export declare interface QueryBuilder<DB extends DatabaseSchema<any>> {
|
|
|
380
381
|
ids?: (...names: string[]) => any
|
|
381
382
|
advisoryLock?: (key: number | string) => Promise<void>
|
|
382
383
|
tryAdvisoryLock?: (key: number | string) => Promise<boolean>
|
|
384
|
+
advisoryUnlock?: (key: number | string) => Promise<boolean>
|
|
383
385
|
getRelationships?: (table: string) => Record<string, any>
|
|
384
386
|
hasRelationship?: (table: string, relationName: string) => boolean
|
|
385
387
|
getRelationshipType?: (table: string, relationName: string) => string | null
|
|
@@ -390,6 +392,7 @@ declare interface InternalState {
|
|
|
390
392
|
meta?: SchemaMeta
|
|
391
393
|
schema?: any
|
|
392
394
|
txDefaults?: TransactionOptions
|
|
395
|
+
hooks?: QueryHooks
|
|
393
396
|
inTransaction?: boolean
|
|
394
397
|
}
|
|
395
398
|
declare interface TxBackoff { baseMs?: number, maxMs?: number, factor?: number, jitter?: boolean }
|