bun-query-builder 0.1.34 → 0.1.36

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/bin/cli.js CHANGED
@@ -11973,6 +11973,43 @@ function createSQLiteSQL(filename) {
11973
11973
  return Promise.reject(error);
11974
11974
  }
11975
11975
  };
11976
+ let txDepth = 0;
11977
+ sqlFunction.begin = async (fnOrName, maybeFn) => {
11978
+ const fn = typeof fnOrName === "function" ? fnOrName : maybeFn;
11979
+ if (typeof fn !== "function")
11980
+ throw new TypeError("[query-builder] sqlite begin(): a transaction callback is required");
11981
+ const isSavepoint = txDepth > 0;
11982
+ const spName = `qb_sp_${txDepth}`;
11983
+ if (isSavepoint)
11984
+ wrapper.run(`SAVEPOINT ${spName}`);
11985
+ else
11986
+ wrapper.run("BEGIN");
11987
+ txDepth++;
11988
+ try {
11989
+ const result = await fn(sqlFunction);
11990
+ txDepth--;
11991
+ if (isSavepoint)
11992
+ wrapper.run(`RELEASE SAVEPOINT ${spName}`);
11993
+ else
11994
+ wrapper.run("COMMIT");
11995
+ return result;
11996
+ } catch (err) {
11997
+ txDepth--;
11998
+ try {
11999
+ if (isSavepoint) {
12000
+ wrapper.run(`ROLLBACK TO SAVEPOINT ${spName}`);
12001
+ wrapper.run(`RELEASE SAVEPOINT ${spName}`);
12002
+ } else {
12003
+ wrapper.run("ROLLBACK");
12004
+ }
12005
+ } catch {}
12006
+ throw err;
12007
+ }
12008
+ };
12009
+ sqlFunction.savepoint = sqlFunction.begin;
12010
+ sqlFunction.beginDistributed = async () => {
12011
+ throw new Error("[query-builder] beginDistributed() (two-phase commit) is not supported on SQLite. Use begin()/transaction().");
12012
+ };
11976
12013
  sqlFunction._wrapper = wrapper;
11977
12014
  return sqlFunction;
11978
12015
  }
@@ -14999,7 +15036,7 @@ function createQueryBuilder(state) {
14999
15036
  return ensureBuilt().values();
15000
15037
  },
15001
15038
  toParams() {
15002
- return ensureBuilt().values?.() ?? [];
15039
+ return [...whereParams];
15003
15040
  },
15004
15041
  __rawState() {
15005
15042
  return { sql: reorderSelectClauses(text), params: [...whereParams] };
@@ -15096,265 +15133,247 @@ function createQueryBuilder(state) {
15096
15133
  throw new Error(`[query-builder] selectFromSub(...).${methodName}() is not supported. ` + `Apply ${methodName}() to the underlying subquery BEFORE passing it to selectFromSub, ` + `or use the regular selectFrom(...) builder. This previously silently returned without ` + `affecting the SQL, producing wrong results \u2014 see stacksjs/stacks#1862 #11.`);
15097
15134
  };
15098
15135
  }
15099
- const sql = _sql;
15100
- const q = _sql`SELECT * FROM (${sub.toSQL()}) AS ${_sql(alias)}`;
15101
- const base = {
15102
- distinct() {
15103
- const rest = String(q).replace(/^SELECT\s+/i, "");
15104
- const newQ = sql`SELECT DISTINCT ${sql``}${sql(rest)}`;
15105
- return createSubQueryBuilder(newQ);
15106
- },
15107
- distinctOn(...columns) {
15108
- const match = /^SELECT\s+(\S+)\s+FROM/i.exec(String(q));
15109
- const body = match ? `${match[1]} FROM` : String(q);
15110
- const newQ = sql`SELECT DISTINCT ON (${sql(columns)}) ${sql``}${sql(body)}`;
15111
- return createSubQueryBuilder(newQ);
15112
- },
15113
- selectRaw(fragment) {
15114
- const newQ = sql`${q} , ${fragment}`;
15115
- return createSubQueryBuilder(newQ);
15116
- },
15117
- where(expr, op, value) {
15118
- const newQ = applyWhereCondition(q, expr, op, value, "WHERE");
15119
- return createSubQueryBuilder(newQ);
15120
- },
15121
- andWhere(expr, op, value) {
15122
- const newQ = applyWhereCondition(q, expr, op, value, "AND");
15123
- return createSubQueryBuilder(newQ);
15124
- },
15125
- orWhere(expr, op, value) {
15126
- const newQ = applyWhereCondition(q, expr, op, value, "OR");
15127
- return createSubQueryBuilder(newQ);
15128
- },
15129
- orderBy(column, direction = "asc") {
15130
- const dir = direction === "asc" ? "ASC" : "DESC";
15131
- const current = String(q);
15132
- const newQ = SQL_PATTERNS.ORDER_BY.test(current) ? sql.unsafe(`${current}, ${column} ${dir}`) : sql`${q} ORDER BY ${sql(column)} ${direction === "asc" ? sql`ASC` : sql`DESC`}`;
15133
- return createSubQueryBuilder(newQ);
15134
- },
15135
- limit(n) {
15136
- const current = String(q);
15137
- const newQ = SQL_PATTERNS.LIMIT.test(current) ? sql.unsafe(current.replace(SQL_PATTERNS.LIMIT, ` LIMIT ${n}`)) : sql`${q} LIMIT ${n}`;
15138
- return createSubQueryBuilder(newQ);
15139
- },
15140
- offset(n) {
15141
- const current = String(q);
15142
- const newQ = SQL_PATTERNS.OFFSET.test(current) ? sql.unsafe(current.replace(SQL_PATTERNS.OFFSET, ` OFFSET ${n}`)) : sql`${q} OFFSET ${n}`;
15143
- return createSubQueryBuilder(newQ);
15144
- },
15145
- toSQL() {
15146
- return makeExecutableQuery(q);
15147
- },
15148
- async execute() {
15149
- return runWithHooks(q, "select");
15150
- },
15151
- async executeTakeFirst() {
15152
- const rows = await runWithHooks(q, "select");
15153
- return Array.isArray(rows) ? rows[0] : rows;
15154
- },
15155
- async executeTakeFirstOrThrow() {
15156
- const rows = await runWithHooks(q, "select");
15157
- const first = Array.isArray(rows) ? rows[0] : rows;
15158
- if (!first)
15159
- throw new Error("Record not found");
15160
- return first;
15161
- },
15162
- async get() {
15163
- return runWithHooks(q, "select");
15164
- },
15165
- async first() {
15166
- const rows = await runWithHooks(q, "select");
15167
- return Array.isArray(rows) ? rows[0] : rows;
15168
- },
15169
- async firstOrFail() {
15170
- const rows = await runWithHooks(q, "select");
15171
- const first = Array.isArray(rows) ? rows[0] : rows;
15172
- if (!first)
15173
- throw new Error("No rows found");
15174
- return first;
15175
- },
15176
- async exists() {
15177
- const countQ = sql`SELECT EXISTS(${q}) as exists`;
15178
- const result = await runWithHooks(countQ, "select");
15179
- return result?.[0]?.exists === true;
15180
- },
15181
- async doesntExist() {
15182
- const exists = await this.exists();
15183
- return !exists;
15184
- },
15185
- values() {
15186
- return q.values();
15187
- },
15188
- raw() {
15189
- return q.raw();
15190
- },
15191
- cancel() {
15192
- try {
15193
- q.cancel();
15194
- } catch {}
15195
- },
15196
- whereRaw: subqueryNotSupported("whereRaw"),
15197
- whereColumn: subqueryNotSupported("whereColumn"),
15198
- orWhereColumn: subqueryNotSupported("orWhereColumn"),
15199
- whereIn: subqueryNotSupported("whereIn"),
15200
- orWhereIn: subqueryNotSupported("orWhereIn"),
15201
- whereNotIn: subqueryNotSupported("whereNotIn"),
15202
- orWhereNotIn: subqueryNotSupported("orWhereNotIn"),
15203
- whereLike: subqueryNotSupported("whereLike"),
15204
- whereILike: subqueryNotSupported("whereILike"),
15205
- orWhereLike: subqueryNotSupported("orWhereLike"),
15206
- orWhereILike: subqueryNotSupported("orWhereILike"),
15207
- whereNotLike: subqueryNotSupported("whereNotLike"),
15208
- whereNotILike: subqueryNotSupported("whereNotILike"),
15209
- orWhereNotLike: subqueryNotSupported("orWhereNotLike"),
15210
- orWhereNotILike: subqueryNotSupported("orWhereNotILike"),
15211
- whereAny: subqueryNotSupported("whereAny"),
15212
- whereAll: subqueryNotSupported("whereAll"),
15213
- whereNone: subqueryNotSupported("whereNone"),
15214
- whereNested: subqueryNotSupported("whereNested"),
15215
- orWhereNested: subqueryNotSupported("orWhereNested"),
15216
- whereDate: subqueryNotSupported("whereDate"),
15217
- whereBetween: subqueryNotSupported("whereBetween"),
15218
- whereNotBetween: subqueryNotSupported("whereNotBetween"),
15219
- whereJsonContains: subqueryNotSupported("whereJsonContains"),
15220
- whereJsonPath: subqueryNotSupported("whereJsonPath"),
15221
- whereNull: subqueryNotSupported("whereNull"),
15222
- whereNotNull: subqueryNotSupported("whereNotNull"),
15223
- whereExists: subqueryNotSupported("whereExists"),
15224
- whereJsonDoesntContain: subqueryNotSupported("whereJsonDoesntContain"),
15225
- whereJsonContainsKey: subqueryNotSupported("whereJsonContainsKey"),
15226
- whereJsonDoesntContainKey: subqueryNotSupported("whereJsonDoesntContainKey"),
15227
- whereJsonLength: subqueryNotSupported("whereJsonLength"),
15228
- join: subqueryNotSupported("join"),
15229
- joinSub: subqueryNotSupported("joinSub"),
15230
- innerJoin: subqueryNotSupported("innerJoin"),
15231
- leftJoin: subqueryNotSupported("leftJoin"),
15232
- leftJoinSub: subqueryNotSupported("leftJoinSub"),
15233
- rightJoin: subqueryNotSupported("rightJoin"),
15234
- crossJoin: subqueryNotSupported("crossJoin"),
15235
- crossJoinSub: subqueryNotSupported("crossJoinSub"),
15236
- groupBy: subqueryNotSupported("groupBy"),
15237
- groupByRaw: subqueryNotSupported("groupByRaw"),
15238
- having: subqueryNotSupported("having"),
15239
- havingRaw: subqueryNotSupported("havingRaw"),
15240
- addSelect: subqueryNotSupported("addSelect"),
15241
- select: subqueryNotSupported("select"),
15242
- selectAll: subqueryNotSupported("selectAll"),
15243
- orderByDesc: subqueryNotSupported("orderByDesc"),
15244
- inRandomOrder: subqueryNotSupported("inRandomOrder"),
15245
- reorder: subqueryNotSupported("reorder"),
15246
- orderByRaw: subqueryNotSupported("orderByRaw"),
15247
- union: subqueryNotSupported("union"),
15248
- unionAll: subqueryNotSupported("unionAll"),
15249
- forPage: subqueryNotSupported("forPage"),
15250
- selectAllRelations: subqueryNotSupported("selectAllRelations"),
15251
- with: subqueryNotSupported("with"),
15252
- value: subqueryNotSupported("value"),
15253
- pluck: subqueryNotSupported("pluck"),
15254
- cursorPaginate: subqueryNotSupported("cursorPaginate"),
15255
- paginate: subqueryNotSupported("paginate"),
15256
- simplePaginate: subqueryNotSupported("simplePaginate"),
15257
- chunk: subqueryNotSupported("chunk"),
15258
- chunkById: subqueryNotSupported("chunkById"),
15259
- eachById: subqueryNotSupported("eachById"),
15260
- count: subqueryNotSupported("count"),
15261
- avg: subqueryNotSupported("avg"),
15262
- sum: subqueryNotSupported("sum"),
15263
- max: subqueryNotSupported("max"),
15264
- min: subqueryNotSupported("min"),
15265
- find: subqueryNotSupported("find"),
15266
- findOrFail: subqueryNotSupported("findOrFail"),
15267
- findMany: subqueryNotSupported("findMany"),
15268
- latest: subqueryNotSupported("latest"),
15269
- oldest: subqueryNotSupported("oldest"),
15270
- lazy: subqueryNotSupported("lazy"),
15271
- lazyById: subqueryNotSupported("lazyById"),
15272
- pipe: (fn) => fn(this),
15273
- when: subqueryNotSupported("when"),
15274
- tap: () => this,
15275
- dump: () => this,
15276
- dd: () => {
15277
- throw new Error("Dump and Die");
15278
- },
15279
- explain: () => Promise.resolve([]),
15280
- simple: () => q.simple(),
15281
- toText: () => String(q),
15282
- toParams: () => q.values?.() ?? [],
15283
- withTimeout: () => this,
15284
- abort: () => this,
15285
- lockForUpdate: () => this,
15286
- sharedLock: () => this,
15287
- withCTE: () => this,
15288
- withRecursive: () => this,
15289
- cache: () => this,
15290
- clone: () => this,
15291
- withTrashed: () => this,
15292
- onlyTrashed: () => this,
15293
- scope: () => this,
15294
- get rows() {
15295
- return [];
15296
- },
15297
- get row() {
15298
- return;
15299
- }
15136
+ validateIdentifier(String(alias), "selectFromSub(alias)");
15137
+ const rawState = typeof sub.__rawState === "function" ? sub.__rawState() : null;
15138
+ const subExec = sub.toSQL();
15139
+ const subText = rawState?.sql ?? (typeof subExec === "string" ? subExec : subExec?.sql ?? String(subExec));
15140
+ const subParams = rawState?.params ?? (Array.isArray(subExec?.values) ? subExec.values : []);
15141
+ const baseText = `SELECT * FROM (${subText}) AS ${String(alias)}`;
15142
+ const appendWhere = (text, params, hasWhere, expr, op, value, connector) => {
15143
+ const kw = !hasWhere ? "WHERE" : connector === "WHERE" ? "AND" : connector;
15144
+ const out = [...params];
15145
+ const cmp = (col, operator, val) => {
15146
+ validateIdentifier(col, "selectFromSub where(column)");
15147
+ const o = assertSafeWhereOperator(operator, "selectFromSub where(operator)");
15148
+ if (o === "in" || o === "not in") {
15149
+ const vals = Array.isArray(val) ? val : [val];
15150
+ const phs = getPlaceholders(vals.length, out.length + 1);
15151
+ out.push(...vals);
15152
+ return `${col} ${o.toUpperCase()} (${phs})`;
15153
+ }
15154
+ const ph = getPlaceholder(out.length + 1);
15155
+ out.push(val);
15156
+ return `${col} ${o} ${ph}`;
15157
+ };
15158
+ let clause;
15159
+ if (typeof expr === "string" && op !== undefined)
15160
+ clause = cmp(expr, op, value);
15161
+ else if (Array.isArray(expr) && expr.length === 3)
15162
+ clause = cmp(expr[0], expr[1], expr[2]);
15163
+ else if (expr && typeof expr === "object")
15164
+ clause = Object.entries(expr).map(([k, v]) => cmp(k, "=", v)).join(" AND ");
15165
+ else
15166
+ return { text, params };
15167
+ return { text: `${text} ${kw} ${clause}`, params: out };
15300
15168
  };
15301
- function createSubQueryBuilder(newQ) {
15302
- return {
15303
- ...base,
15304
- toSQL: () => makeExecutableQuery(newQ),
15305
- execute: () => runWithHooks(newQ, "select"),
15306
- get: () => runWithHooks(newQ, "select"),
15307
- first: async () => {
15308
- const rows = await runWithHooks(newQ, "select");
15169
+ function makeSub(text, params, hasWhere = false) {
15170
+ const build = () => params.length > 0 ? _sql.unsafe(text, params) : _sql.unsafe(text);
15171
+ const base = {
15172
+ distinct() {
15173
+ return makeSub(text.replace(/^SELECT\s+/i, "SELECT DISTINCT "), params, hasWhere);
15174
+ },
15175
+ distinctOn(...columns) {
15176
+ const cols = columns.map(String).join(", ");
15177
+ return makeSub(text.replace(/^SELECT\s+/i, `SELECT DISTINCT ON (${cols}) `), params, hasWhere);
15178
+ },
15179
+ selectRaw(fragment) {
15180
+ const frag = renderRawFragment(fragment, "selectFromSub.selectRaw(fragment)");
15181
+ const fromIdx = text.indexOf(" FROM ");
15182
+ const newText = fromIdx !== -1 ? `${text.slice(0, fromIdx)}, ${frag}${text.slice(fromIdx)}` : `${text}, ${frag}`;
15183
+ return makeSub(newText, params, hasWhere);
15184
+ },
15185
+ where(expr, op, value) {
15186
+ const r = appendWhere(text, params, hasWhere, expr, op, value, "WHERE");
15187
+ return makeSub(r.text, r.params, true);
15188
+ },
15189
+ andWhere(expr, op, value) {
15190
+ const r = appendWhere(text, params, hasWhere, expr, op, value, "AND");
15191
+ return makeSub(r.text, r.params, true);
15192
+ },
15193
+ orWhere(expr, op, value) {
15194
+ const r = appendWhere(text, params, hasWhere, expr, op, value, "OR");
15195
+ return makeSub(r.text, r.params, true);
15196
+ },
15197
+ orderBy(column, direction = "asc") {
15198
+ validateIdentifier(String(column), "selectFromSub.orderBy(column)");
15199
+ const dir = direction === "asc" ? "ASC" : "DESC";
15200
+ const newText = SQL_PATTERNS.ORDER_BY.test(text) ? `${text}, ${column} ${dir}` : `${text} ORDER BY ${column} ${dir}`;
15201
+ return makeSub(newText, params, hasWhere);
15202
+ },
15203
+ limit(n) {
15204
+ if (!Number.isInteger(n) || n < 0)
15205
+ throw new TypeError(`[query-builder] selectFromSub.limit(n): expected non-negative integer, got ${n}`);
15206
+ const newText = SQL_PATTERNS.LIMIT.test(text) ? text.replace(SQL_PATTERNS.LIMIT, ` LIMIT ${n}`) : `${text} LIMIT ${n}`;
15207
+ return makeSub(newText, params, hasWhere);
15208
+ },
15209
+ offset(n) {
15210
+ if (!Number.isInteger(n) || n < 0)
15211
+ throw new TypeError(`[query-builder] selectFromSub.offset(n): expected non-negative integer, got ${n}`);
15212
+ const newText = SQL_PATTERNS.OFFSET.test(text) ? text.replace(SQL_PATTERNS.OFFSET, ` OFFSET ${n}`) : `${text} OFFSET ${n}`;
15213
+ return makeSub(newText, params, hasWhere);
15214
+ },
15215
+ toSQL() {
15216
+ return makeExecutableQuery(build(), text);
15217
+ },
15218
+ async execute() {
15219
+ return runWithHooks(build(), "select");
15220
+ },
15221
+ async executeTakeFirst() {
15222
+ const rows = await runWithHooks(build(), "select");
15309
15223
  return Array.isArray(rows) ? rows[0] : rows;
15310
15224
  },
15311
- firstOrFail: async () => {
15312
- const rows = await runWithHooks(newQ, "select");
15225
+ async executeTakeFirstOrThrow() {
15226
+ const rows = await runWithHooks(build(), "select");
15227
+ const first = Array.isArray(rows) ? rows[0] : rows;
15228
+ if (!first)
15229
+ throw new Error("Record not found");
15230
+ return first;
15231
+ },
15232
+ async get() {
15233
+ return runWithHooks(build(), "select");
15234
+ },
15235
+ async first() {
15236
+ const rows = await runWithHooks(build(), "select");
15237
+ return Array.isArray(rows) ? rows[0] : rows;
15238
+ },
15239
+ async firstOrFail() {
15240
+ const rows = await runWithHooks(build(), "select");
15313
15241
  const first = Array.isArray(rows) ? rows[0] : rows;
15314
15242
  if (!first)
15315
15243
  throw new Error("No rows found");
15316
15244
  return first;
15317
15245
  },
15318
- exists: async () => {
15319
- const countQ = sql`SELECT EXISTS(${newQ}) as exists`;
15320
- const result = await runWithHooks(countQ, "select");
15321
- return result?.[0]?.exists === true;
15246
+ async count() {
15247
+ const q = params.length > 0 ? _sql.unsafe(`SELECT COUNT(*) as c FROM (${text}) as sub`, params) : _sql.unsafe(`SELECT COUNT(*) as c FROM (${text}) as sub`);
15248
+ const rows = await runWithHooks(q, "select");
15249
+ return Number(rows?.[0]?.c ?? 0);
15250
+ },
15251
+ async exists() {
15252
+ const q = params.length > 0 ? _sql.unsafe(`SELECT EXISTS(${text}) as e`, params) : _sql.unsafe(`SELECT EXISTS(${text}) as e`);
15253
+ const result = await runWithHooks(q, "select");
15254
+ return Boolean(result?.[0]?.e);
15255
+ },
15256
+ async doesntExist() {
15257
+ return !await base.exists();
15258
+ },
15259
+ values() {
15260
+ return build().values();
15322
15261
  },
15323
- doesntExist: async () => {
15324
- const exists = await base.exists();
15325
- return !exists;
15262
+ raw() {
15263
+ return build().raw();
15326
15264
  },
15327
- values: () => newQ.values(),
15328
- raw: () => newQ.raw(),
15329
- cancel: () => {
15265
+ cancel() {
15330
15266
  try {
15331
- newQ.cancel();
15267
+ build().cancel();
15332
15268
  } catch {}
15333
15269
  },
15334
- simple: () => newQ.simple(),
15335
- toText: () => String(newQ),
15336
- toParams: () => newQ.values?.() ?? []
15270
+ whereRaw: subqueryNotSupported("whereRaw"),
15271
+ whereColumn: subqueryNotSupported("whereColumn"),
15272
+ orWhereColumn: subqueryNotSupported("orWhereColumn"),
15273
+ whereIn: subqueryNotSupported("whereIn"),
15274
+ orWhereIn: subqueryNotSupported("orWhereIn"),
15275
+ whereNotIn: subqueryNotSupported("whereNotIn"),
15276
+ orWhereNotIn: subqueryNotSupported("orWhereNotIn"),
15277
+ whereLike: subqueryNotSupported("whereLike"),
15278
+ whereILike: subqueryNotSupported("whereILike"),
15279
+ orWhereLike: subqueryNotSupported("orWhereLike"),
15280
+ orWhereILike: subqueryNotSupported("orWhereILike"),
15281
+ whereNotLike: subqueryNotSupported("whereNotLike"),
15282
+ whereNotILike: subqueryNotSupported("whereNotILike"),
15283
+ orWhereNotLike: subqueryNotSupported("orWhereNotLike"),
15284
+ orWhereNotILike: subqueryNotSupported("orWhereNotILike"),
15285
+ whereAny: subqueryNotSupported("whereAny"),
15286
+ whereAll: subqueryNotSupported("whereAll"),
15287
+ whereNone: subqueryNotSupported("whereNone"),
15288
+ whereNested: subqueryNotSupported("whereNested"),
15289
+ orWhereNested: subqueryNotSupported("orWhereNested"),
15290
+ whereDate: subqueryNotSupported("whereDate"),
15291
+ whereBetween: subqueryNotSupported("whereBetween"),
15292
+ whereNotBetween: subqueryNotSupported("whereNotBetween"),
15293
+ whereJsonContains: subqueryNotSupported("whereJsonContains"),
15294
+ whereJsonPath: subqueryNotSupported("whereJsonPath"),
15295
+ whereNull: subqueryNotSupported("whereNull"),
15296
+ whereNotNull: subqueryNotSupported("whereNotNull"),
15297
+ whereExists: subqueryNotSupported("whereExists"),
15298
+ whereJsonDoesntContain: subqueryNotSupported("whereJsonDoesntContain"),
15299
+ whereJsonContainsKey: subqueryNotSupported("whereJsonContainsKey"),
15300
+ whereJsonDoesntContainKey: subqueryNotSupported("whereJsonDoesntContainKey"),
15301
+ whereJsonLength: subqueryNotSupported("whereJsonLength"),
15302
+ join: subqueryNotSupported("join"),
15303
+ joinSub: subqueryNotSupported("joinSub"),
15304
+ innerJoin: subqueryNotSupported("innerJoin"),
15305
+ leftJoin: subqueryNotSupported("leftJoin"),
15306
+ leftJoinSub: subqueryNotSupported("leftJoinSub"),
15307
+ rightJoin: subqueryNotSupported("rightJoin"),
15308
+ crossJoin: subqueryNotSupported("crossJoin"),
15309
+ crossJoinSub: subqueryNotSupported("crossJoinSub"),
15310
+ groupBy: subqueryNotSupported("groupBy"),
15311
+ groupByRaw: subqueryNotSupported("groupByRaw"),
15312
+ having: subqueryNotSupported("having"),
15313
+ havingRaw: subqueryNotSupported("havingRaw"),
15314
+ addSelect: subqueryNotSupported("addSelect"),
15315
+ select: subqueryNotSupported("select"),
15316
+ selectAll: subqueryNotSupported("selectAll"),
15317
+ orderByDesc: subqueryNotSupported("orderByDesc"),
15318
+ inRandomOrder: subqueryNotSupported("inRandomOrder"),
15319
+ reorder: subqueryNotSupported("reorder"),
15320
+ orderByRaw: subqueryNotSupported("orderByRaw"),
15321
+ union: subqueryNotSupported("union"),
15322
+ unionAll: subqueryNotSupported("unionAll"),
15323
+ forPage: subqueryNotSupported("forPage"),
15324
+ selectAllRelations: subqueryNotSupported("selectAllRelations"),
15325
+ with: subqueryNotSupported("with"),
15326
+ value: subqueryNotSupported("value"),
15327
+ pluck: subqueryNotSupported("pluck"),
15328
+ cursorPaginate: subqueryNotSupported("cursorPaginate"),
15329
+ paginate: subqueryNotSupported("paginate"),
15330
+ simplePaginate: subqueryNotSupported("simplePaginate"),
15331
+ chunk: subqueryNotSupported("chunk"),
15332
+ chunkById: subqueryNotSupported("chunkById"),
15333
+ eachById: subqueryNotSupported("eachById"),
15334
+ avg: subqueryNotSupported("avg"),
15335
+ sum: subqueryNotSupported("sum"),
15336
+ max: subqueryNotSupported("max"),
15337
+ min: subqueryNotSupported("min"),
15338
+ find: subqueryNotSupported("find"),
15339
+ findOrFail: subqueryNotSupported("findOrFail"),
15340
+ findMany: subqueryNotSupported("findMany"),
15341
+ latest: subqueryNotSupported("latest"),
15342
+ oldest: subqueryNotSupported("oldest"),
15343
+ lazy: subqueryNotSupported("lazy"),
15344
+ lazyById: subqueryNotSupported("lazyById"),
15345
+ pipe: (fn) => fn(base),
15346
+ when: subqueryNotSupported("when"),
15347
+ tap: () => base,
15348
+ dump: () => base,
15349
+ dd: () => {
15350
+ throw new Error("Dump and Die");
15351
+ },
15352
+ explain: () => Promise.resolve([]),
15353
+ simple: () => build().simple(),
15354
+ toText: () => text,
15355
+ toParams: () => [...params],
15356
+ withTimeout: () => base,
15357
+ abort: () => base,
15358
+ lockForUpdate: () => base,
15359
+ sharedLock: () => base,
15360
+ withCTE: () => base,
15361
+ withRecursive: () => base,
15362
+ cache: () => base,
15363
+ clone: () => base,
15364
+ withTrashed: () => base,
15365
+ onlyTrashed: () => base,
15366
+ scope: () => base,
15367
+ get rows() {
15368
+ return [];
15369
+ },
15370
+ get row() {
15371
+ return;
15372
+ }
15337
15373
  };
15374
+ return base;
15338
15375
  }
15339
- function applyWhereCondition(query, expr, op, value, prefix = "WHERE") {
15340
- if (typeof expr === "string" && op !== undefined) {
15341
- const clause = Array.isArray(value) ? `${expr} IN (?)` : `${expr} ${op} ?`;
15342
- return sql`${query} ${sql(prefix)} ${sql(clause)}`;
15343
- } else if (Array.isArray(expr) && expr.length === 3) {
15344
- const [column, operator, val] = expr;
15345
- const clause = Array.isArray(val) ? `${column} IN (?)` : `${column} ${operator} ?`;
15346
- return sql`${query} ${sql(prefix)} ${sql(clause)}`;
15347
- } else if (typeof expr === "object" && expr !== null) {
15348
- const conditions = Object.entries(expr).map(([key, val]) => {
15349
- const clause = Array.isArray(val) ? `${key} IN (?)` : `${key} = ?`;
15350
- return sql`${sql(clause)}`;
15351
- });
15352
- const combined = conditions.reduce((acc, cond, i) => i === 0 ? cond : sql`${acc} AND ${cond}`);
15353
- return sql`${query} ${sql(prefix)} ${combined}`;
15354
- }
15355
- return query;
15356
- }
15357
- return base;
15376
+ return makeSub(baseText, subParams);
15358
15377
  },
15359
15378
  insertInto(table) {
15360
15379
  let built;
@@ -15892,12 +15911,15 @@ function createQueryBuilder(state) {
15892
15911
  async transaction(fn, options) {
15893
15912
  const defaults = state?.txDefaults;
15894
15913
  const opts = { ...defaults, ...options };
15914
+ const txConn = state?.sql ?? bunSql;
15915
+ const nested = state?.inTransaction === true;
15916
+ const txMethod = nested && typeof txConn?.savepoint === "function" ? "savepoint" : "begin";
15895
15917
  const runWith = async (attempt2) => {
15896
15918
  opts.logger?.({ type: "start", attempt: attempt2 });
15897
15919
  const start = Date.now();
15898
- return await bunSql.begin(async (tx) => {
15899
- const qb = createQueryBuilder({ sql: tx, meta, schema });
15900
- if (opts?.isolation) {
15920
+ return await txConn[txMethod](async (tx) => {
15921
+ const qb = createQueryBuilder({ sql: tx, meta, schema, inTransaction: true });
15922
+ if (opts?.isolation && !nested) {
15901
15923
  const level = opts.isolation;
15902
15924
  const upper = level === "read committed" ? "READ COMMITTED" : level === "repeatable read" ? "REPEATABLE READ" : "SERIALIZABLE";
15903
15925
  if (config5.dialect === "postgres") {
@@ -15962,7 +15984,8 @@ function createQueryBuilder(state) {
15962
15984
  });
15963
15985
  },
15964
15986
  async beginDistributed(name, fn) {
15965
- const res = await bunSql.beginDistributed(name, async (tx) => {
15987
+ const txConn = state?.sql ?? bunSql;
15988
+ const res = await txConn.beginDistributed(name, async (tx) => {
15966
15989
  const qb = createQueryBuilder({ sql: tx, meta, schema });
15967
15990
  return await fn(qb);
15968
15991
  });
@@ -30112,7 +30135,7 @@ function getPrefix() {
30112
30135
  }
30113
30136
  var prefix = getPrefix();
30114
30137
  // package.json
30115
- var version2 = "0.1.34";
30138
+ var version2 = "0.1.36";
30116
30139
 
30117
30140
  // bin/cli.ts
30118
30141
  init_actions();
package/dist/client.d.ts CHANGED
@@ -386,6 +386,7 @@ declare interface InternalState {
386
386
  meta?: SchemaMeta
387
387
  schema?: any
388
388
  txDefaults?: TransactionOptions
389
+ inTransaction?: boolean
389
390
  }
390
391
  declare interface TxBackoff { baseMs?: number, maxMs?: number, factor?: number, jitter?: boolean }
391
392
  declare interface TxLoggerEvent { type: 'start' | 'retry' | 'commit' | 'rollback' | 'error', attempt: number, error?: any, durationMs?: number }
package/dist/src/index.js CHANGED
@@ -11973,6 +11973,43 @@ function createSQLiteSQL(filename) {
11973
11973
  return Promise.reject(error);
11974
11974
  }
11975
11975
  };
11976
+ let txDepth = 0;
11977
+ sqlFunction.begin = async (fnOrName, maybeFn) => {
11978
+ const fn = typeof fnOrName === "function" ? fnOrName : maybeFn;
11979
+ if (typeof fn !== "function")
11980
+ throw new TypeError("[query-builder] sqlite begin(): a transaction callback is required");
11981
+ const isSavepoint = txDepth > 0;
11982
+ const spName = `qb_sp_${txDepth}`;
11983
+ if (isSavepoint)
11984
+ wrapper.run(`SAVEPOINT ${spName}`);
11985
+ else
11986
+ wrapper.run("BEGIN");
11987
+ txDepth++;
11988
+ try {
11989
+ const result = await fn(sqlFunction);
11990
+ txDepth--;
11991
+ if (isSavepoint)
11992
+ wrapper.run(`RELEASE SAVEPOINT ${spName}`);
11993
+ else
11994
+ wrapper.run("COMMIT");
11995
+ return result;
11996
+ } catch (err) {
11997
+ txDepth--;
11998
+ try {
11999
+ if (isSavepoint) {
12000
+ wrapper.run(`ROLLBACK TO SAVEPOINT ${spName}`);
12001
+ wrapper.run(`RELEASE SAVEPOINT ${spName}`);
12002
+ } else {
12003
+ wrapper.run("ROLLBACK");
12004
+ }
12005
+ } catch {}
12006
+ throw err;
12007
+ }
12008
+ };
12009
+ sqlFunction.savepoint = sqlFunction.begin;
12010
+ sqlFunction.beginDistributed = async () => {
12011
+ throw new Error("[query-builder] beginDistributed() (two-phase commit) is not supported on SQLite. Use begin()/transaction().");
12012
+ };
11976
12013
  sqlFunction._wrapper = wrapper;
11977
12014
  return sqlFunction;
11978
12015
  }
@@ -14999,7 +15036,7 @@ function createQueryBuilder(state) {
14999
15036
  return ensureBuilt().values();
15000
15037
  },
15001
15038
  toParams() {
15002
- return ensureBuilt().values?.() ?? [];
15039
+ return [...whereParams];
15003
15040
  },
15004
15041
  __rawState() {
15005
15042
  return { sql: reorderSelectClauses(text), params: [...whereParams] };
@@ -15096,265 +15133,247 @@ function createQueryBuilder(state) {
15096
15133
  throw new Error(`[query-builder] selectFromSub(...).${methodName}() is not supported. ` + `Apply ${methodName}() to the underlying subquery BEFORE passing it to selectFromSub, ` + `or use the regular selectFrom(...) builder. This previously silently returned without ` + `affecting the SQL, producing wrong results \u2014 see stacksjs/stacks#1862 #11.`);
15097
15134
  };
15098
15135
  }
15099
- const sql = _sql;
15100
- const q = _sql`SELECT * FROM (${sub.toSQL()}) AS ${_sql(alias)}`;
15101
- const base = {
15102
- distinct() {
15103
- const rest = String(q).replace(/^SELECT\s+/i, "");
15104
- const newQ = sql`SELECT DISTINCT ${sql``}${sql(rest)}`;
15105
- return createSubQueryBuilder(newQ);
15106
- },
15107
- distinctOn(...columns) {
15108
- const match = /^SELECT\s+(\S+)\s+FROM/i.exec(String(q));
15109
- const body = match ? `${match[1]} FROM` : String(q);
15110
- const newQ = sql`SELECT DISTINCT ON (${sql(columns)}) ${sql``}${sql(body)}`;
15111
- return createSubQueryBuilder(newQ);
15112
- },
15113
- selectRaw(fragment) {
15114
- const newQ = sql`${q} , ${fragment}`;
15115
- return createSubQueryBuilder(newQ);
15116
- },
15117
- where(expr, op, value) {
15118
- const newQ = applyWhereCondition(q, expr, op, value, "WHERE");
15119
- return createSubQueryBuilder(newQ);
15120
- },
15121
- andWhere(expr, op, value) {
15122
- const newQ = applyWhereCondition(q, expr, op, value, "AND");
15123
- return createSubQueryBuilder(newQ);
15124
- },
15125
- orWhere(expr, op, value) {
15126
- const newQ = applyWhereCondition(q, expr, op, value, "OR");
15127
- return createSubQueryBuilder(newQ);
15128
- },
15129
- orderBy(column, direction = "asc") {
15130
- const dir = direction === "asc" ? "ASC" : "DESC";
15131
- const current = String(q);
15132
- const newQ = SQL_PATTERNS.ORDER_BY.test(current) ? sql.unsafe(`${current}, ${column} ${dir}`) : sql`${q} ORDER BY ${sql(column)} ${direction === "asc" ? sql`ASC` : sql`DESC`}`;
15133
- return createSubQueryBuilder(newQ);
15134
- },
15135
- limit(n) {
15136
- const current = String(q);
15137
- const newQ = SQL_PATTERNS.LIMIT.test(current) ? sql.unsafe(current.replace(SQL_PATTERNS.LIMIT, ` LIMIT ${n}`)) : sql`${q} LIMIT ${n}`;
15138
- return createSubQueryBuilder(newQ);
15139
- },
15140
- offset(n) {
15141
- const current = String(q);
15142
- const newQ = SQL_PATTERNS.OFFSET.test(current) ? sql.unsafe(current.replace(SQL_PATTERNS.OFFSET, ` OFFSET ${n}`)) : sql`${q} OFFSET ${n}`;
15143
- return createSubQueryBuilder(newQ);
15144
- },
15145
- toSQL() {
15146
- return makeExecutableQuery(q);
15147
- },
15148
- async execute() {
15149
- return runWithHooks(q, "select");
15150
- },
15151
- async executeTakeFirst() {
15152
- const rows = await runWithHooks(q, "select");
15153
- return Array.isArray(rows) ? rows[0] : rows;
15154
- },
15155
- async executeTakeFirstOrThrow() {
15156
- const rows = await runWithHooks(q, "select");
15157
- const first = Array.isArray(rows) ? rows[0] : rows;
15158
- if (!first)
15159
- throw new Error("Record not found");
15160
- return first;
15161
- },
15162
- async get() {
15163
- return runWithHooks(q, "select");
15164
- },
15165
- async first() {
15166
- const rows = await runWithHooks(q, "select");
15167
- return Array.isArray(rows) ? rows[0] : rows;
15168
- },
15169
- async firstOrFail() {
15170
- const rows = await runWithHooks(q, "select");
15171
- const first = Array.isArray(rows) ? rows[0] : rows;
15172
- if (!first)
15173
- throw new Error("No rows found");
15174
- return first;
15175
- },
15176
- async exists() {
15177
- const countQ = sql`SELECT EXISTS(${q}) as exists`;
15178
- const result = await runWithHooks(countQ, "select");
15179
- return result?.[0]?.exists === true;
15180
- },
15181
- async doesntExist() {
15182
- const exists = await this.exists();
15183
- return !exists;
15184
- },
15185
- values() {
15186
- return q.values();
15187
- },
15188
- raw() {
15189
- return q.raw();
15190
- },
15191
- cancel() {
15192
- try {
15193
- q.cancel();
15194
- } catch {}
15195
- },
15196
- whereRaw: subqueryNotSupported("whereRaw"),
15197
- whereColumn: subqueryNotSupported("whereColumn"),
15198
- orWhereColumn: subqueryNotSupported("orWhereColumn"),
15199
- whereIn: subqueryNotSupported("whereIn"),
15200
- orWhereIn: subqueryNotSupported("orWhereIn"),
15201
- whereNotIn: subqueryNotSupported("whereNotIn"),
15202
- orWhereNotIn: subqueryNotSupported("orWhereNotIn"),
15203
- whereLike: subqueryNotSupported("whereLike"),
15204
- whereILike: subqueryNotSupported("whereILike"),
15205
- orWhereLike: subqueryNotSupported("orWhereLike"),
15206
- orWhereILike: subqueryNotSupported("orWhereILike"),
15207
- whereNotLike: subqueryNotSupported("whereNotLike"),
15208
- whereNotILike: subqueryNotSupported("whereNotILike"),
15209
- orWhereNotLike: subqueryNotSupported("orWhereNotLike"),
15210
- orWhereNotILike: subqueryNotSupported("orWhereNotILike"),
15211
- whereAny: subqueryNotSupported("whereAny"),
15212
- whereAll: subqueryNotSupported("whereAll"),
15213
- whereNone: subqueryNotSupported("whereNone"),
15214
- whereNested: subqueryNotSupported("whereNested"),
15215
- orWhereNested: subqueryNotSupported("orWhereNested"),
15216
- whereDate: subqueryNotSupported("whereDate"),
15217
- whereBetween: subqueryNotSupported("whereBetween"),
15218
- whereNotBetween: subqueryNotSupported("whereNotBetween"),
15219
- whereJsonContains: subqueryNotSupported("whereJsonContains"),
15220
- whereJsonPath: subqueryNotSupported("whereJsonPath"),
15221
- whereNull: subqueryNotSupported("whereNull"),
15222
- whereNotNull: subqueryNotSupported("whereNotNull"),
15223
- whereExists: subqueryNotSupported("whereExists"),
15224
- whereJsonDoesntContain: subqueryNotSupported("whereJsonDoesntContain"),
15225
- whereJsonContainsKey: subqueryNotSupported("whereJsonContainsKey"),
15226
- whereJsonDoesntContainKey: subqueryNotSupported("whereJsonDoesntContainKey"),
15227
- whereJsonLength: subqueryNotSupported("whereJsonLength"),
15228
- join: subqueryNotSupported("join"),
15229
- joinSub: subqueryNotSupported("joinSub"),
15230
- innerJoin: subqueryNotSupported("innerJoin"),
15231
- leftJoin: subqueryNotSupported("leftJoin"),
15232
- leftJoinSub: subqueryNotSupported("leftJoinSub"),
15233
- rightJoin: subqueryNotSupported("rightJoin"),
15234
- crossJoin: subqueryNotSupported("crossJoin"),
15235
- crossJoinSub: subqueryNotSupported("crossJoinSub"),
15236
- groupBy: subqueryNotSupported("groupBy"),
15237
- groupByRaw: subqueryNotSupported("groupByRaw"),
15238
- having: subqueryNotSupported("having"),
15239
- havingRaw: subqueryNotSupported("havingRaw"),
15240
- addSelect: subqueryNotSupported("addSelect"),
15241
- select: subqueryNotSupported("select"),
15242
- selectAll: subqueryNotSupported("selectAll"),
15243
- orderByDesc: subqueryNotSupported("orderByDesc"),
15244
- inRandomOrder: subqueryNotSupported("inRandomOrder"),
15245
- reorder: subqueryNotSupported("reorder"),
15246
- orderByRaw: subqueryNotSupported("orderByRaw"),
15247
- union: subqueryNotSupported("union"),
15248
- unionAll: subqueryNotSupported("unionAll"),
15249
- forPage: subqueryNotSupported("forPage"),
15250
- selectAllRelations: subqueryNotSupported("selectAllRelations"),
15251
- with: subqueryNotSupported("with"),
15252
- value: subqueryNotSupported("value"),
15253
- pluck: subqueryNotSupported("pluck"),
15254
- cursorPaginate: subqueryNotSupported("cursorPaginate"),
15255
- paginate: subqueryNotSupported("paginate"),
15256
- simplePaginate: subqueryNotSupported("simplePaginate"),
15257
- chunk: subqueryNotSupported("chunk"),
15258
- chunkById: subqueryNotSupported("chunkById"),
15259
- eachById: subqueryNotSupported("eachById"),
15260
- count: subqueryNotSupported("count"),
15261
- avg: subqueryNotSupported("avg"),
15262
- sum: subqueryNotSupported("sum"),
15263
- max: subqueryNotSupported("max"),
15264
- min: subqueryNotSupported("min"),
15265
- find: subqueryNotSupported("find"),
15266
- findOrFail: subqueryNotSupported("findOrFail"),
15267
- findMany: subqueryNotSupported("findMany"),
15268
- latest: subqueryNotSupported("latest"),
15269
- oldest: subqueryNotSupported("oldest"),
15270
- lazy: subqueryNotSupported("lazy"),
15271
- lazyById: subqueryNotSupported("lazyById"),
15272
- pipe: (fn) => fn(this),
15273
- when: subqueryNotSupported("when"),
15274
- tap: () => this,
15275
- dump: () => this,
15276
- dd: () => {
15277
- throw new Error("Dump and Die");
15278
- },
15279
- explain: () => Promise.resolve([]),
15280
- simple: () => q.simple(),
15281
- toText: () => String(q),
15282
- toParams: () => q.values?.() ?? [],
15283
- withTimeout: () => this,
15284
- abort: () => this,
15285
- lockForUpdate: () => this,
15286
- sharedLock: () => this,
15287
- withCTE: () => this,
15288
- withRecursive: () => this,
15289
- cache: () => this,
15290
- clone: () => this,
15291
- withTrashed: () => this,
15292
- onlyTrashed: () => this,
15293
- scope: () => this,
15294
- get rows() {
15295
- return [];
15296
- },
15297
- get row() {
15298
- return;
15299
- }
15136
+ validateIdentifier(String(alias), "selectFromSub(alias)");
15137
+ const rawState = typeof sub.__rawState === "function" ? sub.__rawState() : null;
15138
+ const subExec = sub.toSQL();
15139
+ const subText = rawState?.sql ?? (typeof subExec === "string" ? subExec : subExec?.sql ?? String(subExec));
15140
+ const subParams = rawState?.params ?? (Array.isArray(subExec?.values) ? subExec.values : []);
15141
+ const baseText = `SELECT * FROM (${subText}) AS ${String(alias)}`;
15142
+ const appendWhere = (text, params, hasWhere, expr, op, value, connector) => {
15143
+ const kw = !hasWhere ? "WHERE" : connector === "WHERE" ? "AND" : connector;
15144
+ const out = [...params];
15145
+ const cmp = (col, operator, val) => {
15146
+ validateIdentifier(col, "selectFromSub where(column)");
15147
+ const o = assertSafeWhereOperator(operator, "selectFromSub where(operator)");
15148
+ if (o === "in" || o === "not in") {
15149
+ const vals = Array.isArray(val) ? val : [val];
15150
+ const phs = getPlaceholders(vals.length, out.length + 1);
15151
+ out.push(...vals);
15152
+ return `${col} ${o.toUpperCase()} (${phs})`;
15153
+ }
15154
+ const ph = getPlaceholder(out.length + 1);
15155
+ out.push(val);
15156
+ return `${col} ${o} ${ph}`;
15157
+ };
15158
+ let clause;
15159
+ if (typeof expr === "string" && op !== undefined)
15160
+ clause = cmp(expr, op, value);
15161
+ else if (Array.isArray(expr) && expr.length === 3)
15162
+ clause = cmp(expr[0], expr[1], expr[2]);
15163
+ else if (expr && typeof expr === "object")
15164
+ clause = Object.entries(expr).map(([k, v]) => cmp(k, "=", v)).join(" AND ");
15165
+ else
15166
+ return { text, params };
15167
+ return { text: `${text} ${kw} ${clause}`, params: out };
15300
15168
  };
15301
- function createSubQueryBuilder(newQ) {
15302
- return {
15303
- ...base,
15304
- toSQL: () => makeExecutableQuery(newQ),
15305
- execute: () => runWithHooks(newQ, "select"),
15306
- get: () => runWithHooks(newQ, "select"),
15307
- first: async () => {
15308
- const rows = await runWithHooks(newQ, "select");
15169
+ function makeSub(text, params, hasWhere = false) {
15170
+ const build = () => params.length > 0 ? _sql.unsafe(text, params) : _sql.unsafe(text);
15171
+ const base = {
15172
+ distinct() {
15173
+ return makeSub(text.replace(/^SELECT\s+/i, "SELECT DISTINCT "), params, hasWhere);
15174
+ },
15175
+ distinctOn(...columns) {
15176
+ const cols = columns.map(String).join(", ");
15177
+ return makeSub(text.replace(/^SELECT\s+/i, `SELECT DISTINCT ON (${cols}) `), params, hasWhere);
15178
+ },
15179
+ selectRaw(fragment) {
15180
+ const frag = renderRawFragment(fragment, "selectFromSub.selectRaw(fragment)");
15181
+ const fromIdx = text.indexOf(" FROM ");
15182
+ const newText = fromIdx !== -1 ? `${text.slice(0, fromIdx)}, ${frag}${text.slice(fromIdx)}` : `${text}, ${frag}`;
15183
+ return makeSub(newText, params, hasWhere);
15184
+ },
15185
+ where(expr, op, value) {
15186
+ const r = appendWhere(text, params, hasWhere, expr, op, value, "WHERE");
15187
+ return makeSub(r.text, r.params, true);
15188
+ },
15189
+ andWhere(expr, op, value) {
15190
+ const r = appendWhere(text, params, hasWhere, expr, op, value, "AND");
15191
+ return makeSub(r.text, r.params, true);
15192
+ },
15193
+ orWhere(expr, op, value) {
15194
+ const r = appendWhere(text, params, hasWhere, expr, op, value, "OR");
15195
+ return makeSub(r.text, r.params, true);
15196
+ },
15197
+ orderBy(column, direction = "asc") {
15198
+ validateIdentifier(String(column), "selectFromSub.orderBy(column)");
15199
+ const dir = direction === "asc" ? "ASC" : "DESC";
15200
+ const newText = SQL_PATTERNS.ORDER_BY.test(text) ? `${text}, ${column} ${dir}` : `${text} ORDER BY ${column} ${dir}`;
15201
+ return makeSub(newText, params, hasWhere);
15202
+ },
15203
+ limit(n) {
15204
+ if (!Number.isInteger(n) || n < 0)
15205
+ throw new TypeError(`[query-builder] selectFromSub.limit(n): expected non-negative integer, got ${n}`);
15206
+ const newText = SQL_PATTERNS.LIMIT.test(text) ? text.replace(SQL_PATTERNS.LIMIT, ` LIMIT ${n}`) : `${text} LIMIT ${n}`;
15207
+ return makeSub(newText, params, hasWhere);
15208
+ },
15209
+ offset(n) {
15210
+ if (!Number.isInteger(n) || n < 0)
15211
+ throw new TypeError(`[query-builder] selectFromSub.offset(n): expected non-negative integer, got ${n}`);
15212
+ const newText = SQL_PATTERNS.OFFSET.test(text) ? text.replace(SQL_PATTERNS.OFFSET, ` OFFSET ${n}`) : `${text} OFFSET ${n}`;
15213
+ return makeSub(newText, params, hasWhere);
15214
+ },
15215
+ toSQL() {
15216
+ return makeExecutableQuery(build(), text);
15217
+ },
15218
+ async execute() {
15219
+ return runWithHooks(build(), "select");
15220
+ },
15221
+ async executeTakeFirst() {
15222
+ const rows = await runWithHooks(build(), "select");
15309
15223
  return Array.isArray(rows) ? rows[0] : rows;
15310
15224
  },
15311
- firstOrFail: async () => {
15312
- const rows = await runWithHooks(newQ, "select");
15225
+ async executeTakeFirstOrThrow() {
15226
+ const rows = await runWithHooks(build(), "select");
15227
+ const first = Array.isArray(rows) ? rows[0] : rows;
15228
+ if (!first)
15229
+ throw new Error("Record not found");
15230
+ return first;
15231
+ },
15232
+ async get() {
15233
+ return runWithHooks(build(), "select");
15234
+ },
15235
+ async first() {
15236
+ const rows = await runWithHooks(build(), "select");
15237
+ return Array.isArray(rows) ? rows[0] : rows;
15238
+ },
15239
+ async firstOrFail() {
15240
+ const rows = await runWithHooks(build(), "select");
15313
15241
  const first = Array.isArray(rows) ? rows[0] : rows;
15314
15242
  if (!first)
15315
15243
  throw new Error("No rows found");
15316
15244
  return first;
15317
15245
  },
15318
- exists: async () => {
15319
- const countQ = sql`SELECT EXISTS(${newQ}) as exists`;
15320
- const result = await runWithHooks(countQ, "select");
15321
- return result?.[0]?.exists === true;
15246
+ async count() {
15247
+ const q = params.length > 0 ? _sql.unsafe(`SELECT COUNT(*) as c FROM (${text}) as sub`, params) : _sql.unsafe(`SELECT COUNT(*) as c FROM (${text}) as sub`);
15248
+ const rows = await runWithHooks(q, "select");
15249
+ return Number(rows?.[0]?.c ?? 0);
15250
+ },
15251
+ async exists() {
15252
+ const q = params.length > 0 ? _sql.unsafe(`SELECT EXISTS(${text}) as e`, params) : _sql.unsafe(`SELECT EXISTS(${text}) as e`);
15253
+ const result = await runWithHooks(q, "select");
15254
+ return Boolean(result?.[0]?.e);
15255
+ },
15256
+ async doesntExist() {
15257
+ return !await base.exists();
15258
+ },
15259
+ values() {
15260
+ return build().values();
15322
15261
  },
15323
- doesntExist: async () => {
15324
- const exists = await base.exists();
15325
- return !exists;
15262
+ raw() {
15263
+ return build().raw();
15326
15264
  },
15327
- values: () => newQ.values(),
15328
- raw: () => newQ.raw(),
15329
- cancel: () => {
15265
+ cancel() {
15330
15266
  try {
15331
- newQ.cancel();
15267
+ build().cancel();
15332
15268
  } catch {}
15333
15269
  },
15334
- simple: () => newQ.simple(),
15335
- toText: () => String(newQ),
15336
- toParams: () => newQ.values?.() ?? []
15270
+ whereRaw: subqueryNotSupported("whereRaw"),
15271
+ whereColumn: subqueryNotSupported("whereColumn"),
15272
+ orWhereColumn: subqueryNotSupported("orWhereColumn"),
15273
+ whereIn: subqueryNotSupported("whereIn"),
15274
+ orWhereIn: subqueryNotSupported("orWhereIn"),
15275
+ whereNotIn: subqueryNotSupported("whereNotIn"),
15276
+ orWhereNotIn: subqueryNotSupported("orWhereNotIn"),
15277
+ whereLike: subqueryNotSupported("whereLike"),
15278
+ whereILike: subqueryNotSupported("whereILike"),
15279
+ orWhereLike: subqueryNotSupported("orWhereLike"),
15280
+ orWhereILike: subqueryNotSupported("orWhereILike"),
15281
+ whereNotLike: subqueryNotSupported("whereNotLike"),
15282
+ whereNotILike: subqueryNotSupported("whereNotILike"),
15283
+ orWhereNotLike: subqueryNotSupported("orWhereNotLike"),
15284
+ orWhereNotILike: subqueryNotSupported("orWhereNotILike"),
15285
+ whereAny: subqueryNotSupported("whereAny"),
15286
+ whereAll: subqueryNotSupported("whereAll"),
15287
+ whereNone: subqueryNotSupported("whereNone"),
15288
+ whereNested: subqueryNotSupported("whereNested"),
15289
+ orWhereNested: subqueryNotSupported("orWhereNested"),
15290
+ whereDate: subqueryNotSupported("whereDate"),
15291
+ whereBetween: subqueryNotSupported("whereBetween"),
15292
+ whereNotBetween: subqueryNotSupported("whereNotBetween"),
15293
+ whereJsonContains: subqueryNotSupported("whereJsonContains"),
15294
+ whereJsonPath: subqueryNotSupported("whereJsonPath"),
15295
+ whereNull: subqueryNotSupported("whereNull"),
15296
+ whereNotNull: subqueryNotSupported("whereNotNull"),
15297
+ whereExists: subqueryNotSupported("whereExists"),
15298
+ whereJsonDoesntContain: subqueryNotSupported("whereJsonDoesntContain"),
15299
+ whereJsonContainsKey: subqueryNotSupported("whereJsonContainsKey"),
15300
+ whereJsonDoesntContainKey: subqueryNotSupported("whereJsonDoesntContainKey"),
15301
+ whereJsonLength: subqueryNotSupported("whereJsonLength"),
15302
+ join: subqueryNotSupported("join"),
15303
+ joinSub: subqueryNotSupported("joinSub"),
15304
+ innerJoin: subqueryNotSupported("innerJoin"),
15305
+ leftJoin: subqueryNotSupported("leftJoin"),
15306
+ leftJoinSub: subqueryNotSupported("leftJoinSub"),
15307
+ rightJoin: subqueryNotSupported("rightJoin"),
15308
+ crossJoin: subqueryNotSupported("crossJoin"),
15309
+ crossJoinSub: subqueryNotSupported("crossJoinSub"),
15310
+ groupBy: subqueryNotSupported("groupBy"),
15311
+ groupByRaw: subqueryNotSupported("groupByRaw"),
15312
+ having: subqueryNotSupported("having"),
15313
+ havingRaw: subqueryNotSupported("havingRaw"),
15314
+ addSelect: subqueryNotSupported("addSelect"),
15315
+ select: subqueryNotSupported("select"),
15316
+ selectAll: subqueryNotSupported("selectAll"),
15317
+ orderByDesc: subqueryNotSupported("orderByDesc"),
15318
+ inRandomOrder: subqueryNotSupported("inRandomOrder"),
15319
+ reorder: subqueryNotSupported("reorder"),
15320
+ orderByRaw: subqueryNotSupported("orderByRaw"),
15321
+ union: subqueryNotSupported("union"),
15322
+ unionAll: subqueryNotSupported("unionAll"),
15323
+ forPage: subqueryNotSupported("forPage"),
15324
+ selectAllRelations: subqueryNotSupported("selectAllRelations"),
15325
+ with: subqueryNotSupported("with"),
15326
+ value: subqueryNotSupported("value"),
15327
+ pluck: subqueryNotSupported("pluck"),
15328
+ cursorPaginate: subqueryNotSupported("cursorPaginate"),
15329
+ paginate: subqueryNotSupported("paginate"),
15330
+ simplePaginate: subqueryNotSupported("simplePaginate"),
15331
+ chunk: subqueryNotSupported("chunk"),
15332
+ chunkById: subqueryNotSupported("chunkById"),
15333
+ eachById: subqueryNotSupported("eachById"),
15334
+ avg: subqueryNotSupported("avg"),
15335
+ sum: subqueryNotSupported("sum"),
15336
+ max: subqueryNotSupported("max"),
15337
+ min: subqueryNotSupported("min"),
15338
+ find: subqueryNotSupported("find"),
15339
+ findOrFail: subqueryNotSupported("findOrFail"),
15340
+ findMany: subqueryNotSupported("findMany"),
15341
+ latest: subqueryNotSupported("latest"),
15342
+ oldest: subqueryNotSupported("oldest"),
15343
+ lazy: subqueryNotSupported("lazy"),
15344
+ lazyById: subqueryNotSupported("lazyById"),
15345
+ pipe: (fn) => fn(base),
15346
+ when: subqueryNotSupported("when"),
15347
+ tap: () => base,
15348
+ dump: () => base,
15349
+ dd: () => {
15350
+ throw new Error("Dump and Die");
15351
+ },
15352
+ explain: () => Promise.resolve([]),
15353
+ simple: () => build().simple(),
15354
+ toText: () => text,
15355
+ toParams: () => [...params],
15356
+ withTimeout: () => base,
15357
+ abort: () => base,
15358
+ lockForUpdate: () => base,
15359
+ sharedLock: () => base,
15360
+ withCTE: () => base,
15361
+ withRecursive: () => base,
15362
+ cache: () => base,
15363
+ clone: () => base,
15364
+ withTrashed: () => base,
15365
+ onlyTrashed: () => base,
15366
+ scope: () => base,
15367
+ get rows() {
15368
+ return [];
15369
+ },
15370
+ get row() {
15371
+ return;
15372
+ }
15337
15373
  };
15374
+ return base;
15338
15375
  }
15339
- function applyWhereCondition(query, expr, op, value, prefix = "WHERE") {
15340
- if (typeof expr === "string" && op !== undefined) {
15341
- const clause = Array.isArray(value) ? `${expr} IN (?)` : `${expr} ${op} ?`;
15342
- return sql`${query} ${sql(prefix)} ${sql(clause)}`;
15343
- } else if (Array.isArray(expr) && expr.length === 3) {
15344
- const [column, operator, val] = expr;
15345
- const clause = Array.isArray(val) ? `${column} IN (?)` : `${column} ${operator} ?`;
15346
- return sql`${query} ${sql(prefix)} ${sql(clause)}`;
15347
- } else if (typeof expr === "object" && expr !== null) {
15348
- const conditions = Object.entries(expr).map(([key, val]) => {
15349
- const clause = Array.isArray(val) ? `${key} IN (?)` : `${key} = ?`;
15350
- return sql`${sql(clause)}`;
15351
- });
15352
- const combined = conditions.reduce((acc, cond, i) => i === 0 ? cond : sql`${acc} AND ${cond}`);
15353
- return sql`${query} ${sql(prefix)} ${combined}`;
15354
- }
15355
- return query;
15356
- }
15357
- return base;
15376
+ return makeSub(baseText, subParams);
15358
15377
  },
15359
15378
  insertInto(table) {
15360
15379
  let built;
@@ -15892,12 +15911,15 @@ function createQueryBuilder(state) {
15892
15911
  async transaction(fn, options) {
15893
15912
  const defaults = state?.txDefaults;
15894
15913
  const opts = { ...defaults, ...options };
15914
+ const txConn = state?.sql ?? bunSql;
15915
+ const nested = state?.inTransaction === true;
15916
+ const txMethod = nested && typeof txConn?.savepoint === "function" ? "savepoint" : "begin";
15895
15917
  const runWith = async (attempt2) => {
15896
15918
  opts.logger?.({ type: "start", attempt: attempt2 });
15897
15919
  const start = Date.now();
15898
- return await bunSql.begin(async (tx) => {
15899
- const qb = createQueryBuilder({ sql: tx, meta, schema });
15900
- if (opts?.isolation) {
15920
+ return await txConn[txMethod](async (tx) => {
15921
+ const qb = createQueryBuilder({ sql: tx, meta, schema, inTransaction: true });
15922
+ if (opts?.isolation && !nested) {
15901
15923
  const level = opts.isolation;
15902
15924
  const upper = level === "read committed" ? "READ COMMITTED" : level === "repeatable read" ? "REPEATABLE READ" : "SERIALIZABLE";
15903
15925
  if (config5.dialect === "postgres") {
@@ -15962,7 +15984,8 @@ function createQueryBuilder(state) {
15962
15984
  });
15963
15985
  },
15964
15986
  async beginDistributed(name, fn) {
15965
- const res = await bunSql.beginDistributed(name, async (tx) => {
15987
+ const txConn = state?.sql ?? bunSql;
15988
+ const res = await txConn.beginDistributed(name, async (tx) => {
15966
15989
  const qb = createQueryBuilder({ sql: tx, meta, schema });
15967
15990
  return await fn(qb);
15968
15991
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bun-query-builder",
3
3
  "type": "module",
4
- "version": "0.1.34",
4
+ "version": "0.1.36",
5
5
  "description": "A simple yet performant query builder for TypeScript. Built with Bun.",
6
6
  "author": "Chris Breuer <chris@stacksjs.org>",
7
7
  "license": "MIT",