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