bun-query-builder 0.2.28 → 0.2.30

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/src/index.js CHANGED
@@ -11861,6 +11861,68 @@ import { Database } from "bun:sqlite";
11861
11861
  import { mkdirSync as mkdirSync5 } from "fs";
11862
11862
  import { dirname as dirname5 } from "path";
11863
11863
  import process21 from "process";
11864
+ function bindNumberedPlaceholders(sql, params) {
11865
+ if (params.length === 0 || !/\$\d/.test(sql))
11866
+ return { sql, params };
11867
+ const out = [];
11868
+ let rewritten = "";
11869
+ let highest = 0;
11870
+ for (let i = 0;i < sql.length; i++) {
11871
+ const char = sql[i];
11872
+ if (char === "'" || char === '"' || char === "`") {
11873
+ const end = skipQuoted(sql, i, char);
11874
+ rewritten += sql.slice(i, end);
11875
+ i = end - 1;
11876
+ continue;
11877
+ }
11878
+ if (char === "-" && sql[i + 1] === "-") {
11879
+ const end = sql.indexOf(`
11880
+ `, i);
11881
+ const stop = end === -1 ? sql.length : end;
11882
+ rewritten += sql.slice(i, stop);
11883
+ i = stop - 1;
11884
+ continue;
11885
+ }
11886
+ if (char === "/" && sql[i + 1] === "*") {
11887
+ const end = sql.indexOf("*/", i + 2);
11888
+ const stop = end === -1 ? sql.length : end + 2;
11889
+ rewritten += sql.slice(i, stop);
11890
+ i = stop - 1;
11891
+ continue;
11892
+ }
11893
+ if (char === "$") {
11894
+ const digits = /^\d+/.exec(sql.slice(i + 1))?.[0];
11895
+ if (digits) {
11896
+ const index = Number(digits);
11897
+ highest = Math.max(highest, index);
11898
+ if (index >= 1 && index <= params.length) {
11899
+ out.push(params[index - 1]);
11900
+ rewritten += "?";
11901
+ i += digits.length;
11902
+ continue;
11903
+ }
11904
+ }
11905
+ }
11906
+ rewritten += char;
11907
+ }
11908
+ if (highest > params.length)
11909
+ throw new Error(`SQL references $${highest} but only ${params.length} value${params.length === 1 ? " was" : "s were"} provided`);
11910
+ if (out.length === 0)
11911
+ return { sql, params };
11912
+ return { sql: rewritten, params: out };
11913
+ }
11914
+ function skipQuoted(sql, start, quote) {
11915
+ for (let i = start + 1;i < sql.length; i++) {
11916
+ if (sql[i] !== quote)
11917
+ continue;
11918
+ if (sql[i + 1] === quote) {
11919
+ i++;
11920
+ continue;
11921
+ }
11922
+ return i + 1;
11923
+ }
11924
+ return sql.length;
11925
+ }
11864
11926
 
11865
11927
  class SQLiteWrapper {
11866
11928
  db;
@@ -11875,12 +11937,14 @@ class SQLiteWrapper {
11875
11937
  applySqliteBootstrapPragmas(this.db);
11876
11938
  }
11877
11939
  query(sql, params = []) {
11878
- const stmt = this.db.prepare(sql);
11879
- return stmt.all(...params);
11940
+ const bound = bindNumberedPlaceholders(sql, params);
11941
+ const stmt = this.db.prepare(bound.sql);
11942
+ return stmt.all(...bound.params);
11880
11943
  }
11881
11944
  run(sql, params = []) {
11882
- const stmt = this.db.prepare(sql);
11883
- return stmt.run(...params);
11945
+ const bound = bindNumberedPlaceholders(sql, params);
11946
+ const stmt = this.db.prepare(bound.sql);
11947
+ return stmt.run(...bound.params);
11884
11948
  }
11885
11949
  close() {
11886
11950
  this.db.close();
@@ -12554,6 +12618,30 @@ var init_pivot = __esm(() => {
12554
12618
  init_config();
12555
12619
  });
12556
12620
 
12621
+ // src/sql-fragments.ts
12622
+ function renderWhereTerms(terms) {
12623
+ if (terms.length === 0)
12624
+ return "";
12625
+ const runs = [];
12626
+ for (const term of terms) {
12627
+ if (term.conn === "OR" && runs.length > 0)
12628
+ runs[runs.length - 1].push(term);
12629
+ else
12630
+ runs.push([term]);
12631
+ }
12632
+ return runs.map((run, i) => {
12633
+ const body = run.map((t) => t.sql).join(" OR ");
12634
+ const piece = run.length > 1 && runs.length > 1 ? `(${body})` : body;
12635
+ return i === 0 ? piece : `${run[0].conn} ${piece}`;
12636
+ }).join(" ");
12637
+ }
12638
+ function renderInPredicate(column, values, negated, placeholders) {
12639
+ if (values.length === 0)
12640
+ return negated ? TRUE_PREDICATE : FALSE_PREDICATE;
12641
+ return `${column} ${negated ? "NOT IN" : "IN"} (${placeholders})`;
12642
+ }
12643
+ var FALSE_PREDICATE = "1 = 0", TRUE_PREDICATE = "1 = 1";
12644
+
12557
12645
  // src/client.ts
12558
12646
  function isRawExpression(expr) {
12559
12647
  return typeof expr === "object" && expr !== null && "raw" in expr && typeof expr.raw === "string";
@@ -12625,10 +12713,20 @@ function renderSelectColumn(col) {
12625
12713
  }
12626
12714
  throw new TypeError(`[query-builder] select(): unsupported column ${String(col)} \u2014 pass a column name, a string[], or a SQL fragment (e.g. sql\`count(*) as c\`)`);
12627
12715
  }
12628
- function renderInPredicate(column, values, negated, placeholders) {
12629
- if (values.length === 0)
12630
- return negated ? TRUE_PREDICATE : FALSE_PREDICATE;
12631
- return `${column} ${negated ? "NOT IN" : "IN"} (${placeholders})`;
12716
+ function hasTopLevelOrderBy(s) {
12717
+ const re = /\(|\)|\bORDER\s+BY\b/gi;
12718
+ let depth = 0;
12719
+ let m;
12720
+ while (m = re.exec(s)) {
12721
+ if (m[0] === "(") {
12722
+ depth++;
12723
+ } else if (m[0] === ")") {
12724
+ depth = Math.max(0, depth - 1);
12725
+ } else if (depth === 0) {
12726
+ return true;
12727
+ }
12728
+ }
12729
+ return false;
12632
12730
  }
12633
12731
  function validateIdentifier(name, context) {
12634
12732
  if (!SQL_PATTERNS.IDENTIFIER.test(name)) {
@@ -13106,20 +13204,125 @@ function createQueryBuilder(state) {
13106
13204
  function makeSelect(table, columns) {
13107
13205
  const sql = _sql;
13108
13206
  let text = columns && columns.length > 0 ? `SELECT ${columns.join(", ")} FROM ${String(table)}` : `SELECT * FROM ${String(table)}`;
13207
+ const whereTerms = [];
13208
+ const whereParams = [];
13209
+ let whereTail = false;
13109
13210
  let built = null;
13110
13211
  const ensureBuilt = () => {
13111
13212
  if (built === null) {
13112
- const finalText = reorderSelectClauses(text);
13213
+ const finalText = reorderSelectClauses(currentSql());
13113
13214
  built = whereParams.length > 0 ? _sql.unsafe(finalText, whereParams) : _sql.unsafe(finalText);
13114
13215
  }
13115
13216
  return built;
13116
13217
  };
13218
+ const pushWhere = (conn, clause) => {
13219
+ whereTerms.push({ conn, sql: clause });
13220
+ built = null;
13221
+ };
13117
13222
  const addWhereText = (prefix, clause) => {
13118
- const hasWhere = SQL_PATTERNS.WHERE.test(text);
13119
- const p = !hasWhere ? "WHERE" : prefix === "WHERE" ? "AND" : prefix;
13120
- text = `${text} ${p} ${clause}`;
13223
+ pushWhere(prefix === "OR" ? "OR" : "AND", clause);
13224
+ };
13225
+ const TAIL_CLAUSE = /\(|\)|\b(?:GROUP\s+BY|HAVING|ORDER\s+BY|LIMIT|OFFSET|WINDOW|UNION|INTERSECT|EXCEPT|FOR\s+UPDATE|FOR\s+SHARE|LOCK\s+IN\s+SHARE\s+MODE)\b/gi;
13226
+ const firstTailIndex = (s) => {
13227
+ TAIL_CLAUSE.lastIndex = 0;
13228
+ let depth = 0;
13229
+ let m;
13230
+ while (m = TAIL_CLAUSE.exec(s)) {
13231
+ if (m[0] === "(") {
13232
+ depth++;
13233
+ } else if (m[0] === ")") {
13234
+ depth = Math.max(0, depth - 1);
13235
+ } else if (depth === 0) {
13236
+ return m.index;
13237
+ }
13238
+ }
13239
+ return -1;
13240
+ };
13241
+ const currentSql = (extra) => {
13242
+ const body = renderWhereTerms(extra ? [...whereTerms, extra] : whereTerms);
13243
+ if (!body)
13244
+ return text;
13245
+ if (whereTail) {
13246
+ const kw = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
13247
+ return `${text} ${kw} ${body}`;
13248
+ }
13249
+ const cut = firstTailIndex(text);
13250
+ return cut >= 0 ? `${text.slice(0, cut)}WHERE ${body} ${text.slice(cut)}` : `${text} WHERE ${body}`;
13251
+ };
13252
+ const addWhereGroup = (conn, cb) => {
13253
+ const sub = makeSelect(table);
13254
+ cb(sub);
13255
+ const st = sub.__whereState?.();
13256
+ if (!st || !st.body) {
13257
+ throw new TypeError("[query-builder] whereGroup(callback): the callback added no conditions. " + "Add at least one where()/orWhere() to the builder it receives.");
13258
+ }
13259
+ const offset = whereParams.length;
13260
+ const body = offset > 0 && config5.dialect === "postgres" ? st.body.replace(/\$(\d+)/g, (_m, n) => `$${Number(n) + offset}`) : st.body;
13261
+ whereParams.push(...st.params);
13262
+ pushWhere(conn, `(${body})`);
13263
+ };
13264
+ const addBooleanWhere = (self, conn, label, expr, op, value) => {
13265
+ if (typeof expr === "string" && op !== undefined) {
13266
+ validateIdentifier(String(expr), `${label}(column)`);
13267
+ assertSafeWhereOperator(op, `${label}(operator)`);
13268
+ const paramIndex = whereParams.length + 1;
13269
+ whereParams.push(value);
13270
+ pushWhere(conn, `${String(expr)} ${String(op)} ${getPlaceholder(paramIndex)}`);
13271
+ return self;
13272
+ }
13273
+ if (Array.isArray(expr)) {
13274
+ const [col, rawOp, val] = expr;
13275
+ const colName = String(col);
13276
+ validateIdentifier(colName, `${label}(column)`);
13277
+ const operator = assertSafeWhereOperator(rawOp, `${label}(operator)`);
13278
+ if (operator === "in" || operator === "not in") {
13279
+ const values = Array.isArray(val) ? val : [val];
13280
+ const placeholders = getPlaceholders(values.length, whereParams.length + 1);
13281
+ const clause = renderInPredicate(colName, values, operator === "not in", placeholders);
13282
+ whereParams.push(...values);
13283
+ pushWhere(conn, clause);
13284
+ } else {
13285
+ const paramIndex = whereParams.length + 1;
13286
+ whereParams.push(val);
13287
+ pushWhere(conn, `${colName} ${operator} ${getPlaceholder(paramIndex)}`);
13288
+ }
13289
+ return self;
13290
+ }
13291
+ if (typeof expr === "function") {
13292
+ addWhereGroup(conn, expr);
13293
+ return self;
13294
+ }
13295
+ if (expr && typeof expr === "object" && !("raw" in expr)) {
13296
+ const conditions = [];
13297
+ for (const key of Object.keys(expr)) {
13298
+ validateIdentifier(key, `${label}(column)`);
13299
+ const v = expr[key];
13300
+ if (Array.isArray(v)) {
13301
+ const placeholders = getPlaceholders(v.length, whereParams.length + 1);
13302
+ conditions.push(renderInPredicate(key, v, false, placeholders));
13303
+ whereParams.push(...v);
13304
+ } else {
13305
+ const paramIndex = whereParams.length + 1;
13306
+ conditions.push(`${key} = ${getPlaceholder(paramIndex)}`);
13307
+ whereParams.push(v);
13308
+ }
13309
+ }
13310
+ if (conditions.length > 0)
13311
+ pushWhere(conn, conditions.length > 1 ? `(${conditions.join(" AND ")})` : conditions[0]);
13312
+ return self;
13313
+ }
13314
+ if (expr && typeof expr.raw !== "undefined") {
13315
+ pushWhere(conn, expr.raw);
13316
+ return self;
13317
+ }
13318
+ return self;
13121
13319
  };
13122
13320
  const appendSetOp = (op, other) => {
13321
+ if (whereTerms.length > 0) {
13322
+ text = currentSql();
13323
+ whereTerms.length = 0;
13324
+ }
13325
+ whereTail = true;
13123
13326
  const st = other.__rawState?.();
13124
13327
  if (st) {
13125
13328
  const offset = whereParams.length;
@@ -13209,8 +13412,6 @@ function createQueryBuilder(state) {
13209
13412
  text = `${text} LEFT JOIN ${resolved.pivotTable} ON ${resolved.pivotTable}.${resolved.fkParent} = ${parentTable}.${parentPk}`;
13210
13413
  joinedTables.add(resolved.pivotTable);
13211
13414
  };
13212
- const whereConditions = [];
13213
- const whereParams = [];
13214
13415
  const addToSelectClause = (columnsToAdd) => {
13215
13416
  if (SQL_PATTERNS.SELECT_STAR.test(text)) {
13216
13417
  text = text.replace(SQL_PATTERNS.SELECT_STAR, `SELECT *, ${columnsToAdd}`);
@@ -13834,10 +14035,7 @@ function createQueryBuilder(state) {
13834
14035
  } else {
13835
14036
  throw new Error(`[query-builder] Unsupported relationship type '${type}' for whereHas`);
13836
14037
  }
13837
- built = sql`${ensureBuilt()} WHERE EXISTS (${sql([subquerySQL])})`;
13838
- try {
13839
- addWhereText("WHERE", `EXISTS (${subquerySQL})`);
13840
- } catch {}
14038
+ addWhereText("WHERE", `EXISTS (${subquerySQL})`);
13841
14039
  return this;
13842
14040
  },
13843
14041
  whereDoesntHave(relation, callback) {
@@ -13870,10 +14068,7 @@ function createQueryBuilder(state) {
13870
14068
  } else {
13871
14069
  throw new Error(`[query-builder] Unsupported relationship type '${type}' for whereDoesntHave`);
13872
14070
  }
13873
- built = sql`${ensureBuilt()} WHERE NOT EXISTS (${sql([subquerySQL])})`;
13874
- try {
13875
- addWhereText("WHERE", `NOT EXISTS (${subquerySQL})`);
13876
- } catch {}
14071
+ addWhereText("WHERE", `NOT EXISTS (${subquerySQL})`);
13877
14072
  return this;
13878
14073
  },
13879
14074
  has(relation) {
@@ -13904,10 +14099,7 @@ function createQueryBuilder(state) {
13904
14099
  } else {
13905
14100
  throw new Error(`[query-builder] Unsupported relationship type '${type}' for has`);
13906
14101
  }
13907
- built = sql`${ensureBuilt()} WHERE EXISTS (${sql([subquerySQL])})`;
13908
- try {
13909
- addWhereText("WHERE", `EXISTS (${subquerySQL})`);
13910
- } catch {}
14102
+ addWhereText("WHERE", `EXISTS (${subquerySQL})`);
13911
14103
  return this;
13912
14104
  },
13913
14105
  doesntHave(relation) {
@@ -13938,10 +14130,7 @@ function createQueryBuilder(state) {
13938
14130
  } else {
13939
14131
  throw new Error(`[query-builder] Unsupported relationship type '${type}' for doesntHave`);
13940
14132
  }
13941
- built = sql`${ensureBuilt()} WHERE NOT EXISTS (${sql([subquerySQL])})`;
13942
- try {
13943
- addWhereText("WHERE", `NOT EXISTS (${subquerySQL})`);
13944
- } catch {}
14133
+ addWhereText("WHERE", `NOT EXISTS (${subquerySQL})`);
13945
14134
  return this;
13946
14135
  },
13947
14136
  withCount(...relations) {
@@ -14024,17 +14213,14 @@ function createQueryBuilder(state) {
14024
14213
  const val = value === undefined ? opOrValue : value;
14025
14214
  const paramIndex = whereParams.length + 1;
14026
14215
  const clause = `${resolved.pivotTable}.${column} ${op} ${getPlaceholder(paramIndex)}`;
14027
- whereConditions.push(clause);
14028
14216
  whereParams.push(val);
14029
- const kw = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
14030
- text = `${text} ${kw} ${clause}`;
14031
- built = null;
14217
+ pushWhere("AND", clause);
14032
14218
  if (resolved.hasConfig)
14033
14219
  pivotConfigRelations.add(relation);
14034
14220
  return this;
14035
14221
  },
14036
14222
  wherePivotIn(relation, column, values) {
14037
- if (!meta || !Array.isArray(values) || values.length === 0)
14223
+ if (!meta || !Array.isArray(values))
14038
14224
  return this;
14039
14225
  const resolved = resolvePivotLocal(relation);
14040
14226
  if (!resolved) {
@@ -14044,12 +14230,9 @@ function createQueryBuilder(state) {
14044
14230
  validateIdentifier(column, "wherePivotIn (column)");
14045
14231
  ensurePivotJoined(resolved);
14046
14232
  const placeholders = getPlaceholders(values.length, whereParams.length + 1);
14047
- const clause = `${resolved.pivotTable}.${column} IN (${placeholders})`;
14048
- whereConditions.push(clause);
14233
+ const clause = renderInPredicate(`${resolved.pivotTable}.${column}`, values, false, placeholders);
14049
14234
  whereParams.push(...values);
14050
- const kw = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
14051
- text = `${text} ${kw} ${clause}`;
14052
- built = null;
14235
+ pushWhere("AND", clause);
14053
14236
  if (resolved.hasConfig)
14054
14237
  pivotConfigRelations.add(relation);
14055
14238
  return this;
@@ -14066,11 +14249,8 @@ function createQueryBuilder(state) {
14066
14249
  ensurePivotJoined(resolved);
14067
14250
  const placeholders = getPlaceholders(values.length, whereParams.length + 1);
14068
14251
  const clause = `${resolved.pivotTable}.${column} NOT IN (${placeholders})`;
14069
- whereConditions.push(clause);
14070
14252
  whereParams.push(...values);
14071
- const kw = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
14072
- text = `${text} ${kw} ${clause}`;
14073
- built = null;
14253
+ pushWhere("AND", clause);
14074
14254
  if (resolved.hasConfig)
14075
14255
  pivotConfigRelations.add(relation);
14076
14256
  return this;
@@ -14085,11 +14265,7 @@ function createQueryBuilder(state) {
14085
14265
  validateIdentifier(resolved.pivotTable, "wherePivotNull (pivot table)");
14086
14266
  validateIdentifier(column, "wherePivotNull (column)");
14087
14267
  ensurePivotJoined(resolved);
14088
- const clause = `${resolved.pivotTable}.${column} IS NULL`;
14089
- whereConditions.push(clause);
14090
- const kw = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
14091
- text = `${text} ${kw} ${clause}`;
14092
- built = null;
14268
+ pushWhere("AND", `${resolved.pivotTable}.${column} IS NULL`);
14093
14269
  if (resolved.hasConfig)
14094
14270
  pivotConfigRelations.add(relation);
14095
14271
  return this;
@@ -14104,17 +14280,12 @@ function createQueryBuilder(state) {
14104
14280
  validateIdentifier(resolved.pivotTable, "wherePivotNotNull (pivot table)");
14105
14281
  validateIdentifier(column, "wherePivotNotNull (column)");
14106
14282
  ensurePivotJoined(resolved);
14107
- const clause = `${resolved.pivotTable}.${column} IS NOT NULL`;
14108
- whereConditions.push(clause);
14109
- const kw = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
14110
- text = `${text} ${kw} ${clause}`;
14111
- built = null;
14283
+ pushWhere("AND", `${resolved.pivotTable}.${column} IS NOT NULL`);
14112
14284
  if (resolved.hasConfig)
14113
14285
  pivotConfigRelations.add(relation);
14114
14286
  return this;
14115
14287
  },
14116
14288
  where(expr, op, value) {
14117
- const getWhereKeyword = () => SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
14118
14289
  if (typeof expr === "string" && op !== undefined) {
14119
14290
  validateIdentifier(String(expr), "where(column)");
14120
14291
  assertSafeWhereOperator(op, "where(operator)");
@@ -14122,18 +14293,14 @@ function createQueryBuilder(state) {
14122
14293
  if (operator === "in" || operator === "not in") {
14123
14294
  const values = Array.isArray(value) ? value : [value];
14124
14295
  const placeholders = getPlaceholders(values.length, whereParams.length + 1);
14125
- const clause = `${String(expr)} ${operator.toUpperCase()} (${placeholders})`;
14126
- whereConditions.push(clause);
14296
+ const clause = renderInPredicate(String(expr), values, operator === "not in", placeholders);
14127
14297
  whereParams.push(...values);
14128
- text = `${text} ${getWhereKeyword()} ${clause}`;
14129
- built = null;
14298
+ pushWhere("AND", clause);
14130
14299
  return this;
14131
14300
  }
14132
14301
  const paramIndex = whereParams.length + 1;
14133
- whereConditions.push(`${String(expr)} ${String(op)} ${getPlaceholder(paramIndex)}`);
14134
14302
  whereParams.push(value);
14135
- text = `${text} ${getWhereKeyword()} ${String(expr)} ${String(op)} ${getPlaceholder(paramIndex)}`;
14136
- built = null;
14303
+ pushWhere("AND", `${String(expr)} ${String(op)} ${getPlaceholder(paramIndex)}`);
14137
14304
  return this;
14138
14305
  }
14139
14306
  if (Array.isArray(expr)) {
@@ -14144,16 +14311,13 @@ function createQueryBuilder(state) {
14144
14311
  if (operator === "in" || operator === "not in") {
14145
14312
  const values = Array.isArray(val) ? val : [val];
14146
14313
  const placeholders = getPlaceholders(values.length, whereParams.length + 1);
14147
- whereConditions.push(`${colName} ${operator.toUpperCase()} (${placeholders})`);
14314
+ const clause = renderInPredicate(colName, values, operator === "not in", placeholders);
14148
14315
  whereParams.push(...values);
14149
- text = `${text} ${getWhereKeyword()} ${colName} ${operator.toUpperCase()} (${placeholders})`;
14150
- built = null;
14316
+ pushWhere("AND", clause);
14151
14317
  } else {
14152
14318
  const paramIndex = whereParams.length + 1;
14153
- whereConditions.push(`${colName} ${operator} ${getPlaceholder(paramIndex)}`);
14154
14319
  whereParams.push(val);
14155
- text = `${text} ${getWhereKeyword()} ${colName} ${operator} ${getPlaceholder(paramIndex)}`;
14156
- built = null;
14320
+ pushWhere("AND", `${colName} ${operator} ${getPlaceholder(paramIndex)}`);
14157
14321
  }
14158
14322
  return this;
14159
14323
  }
@@ -14166,30 +14330,25 @@ function createQueryBuilder(state) {
14166
14330
  const value2 = whereObject[key];
14167
14331
  if (Array.isArray(value2)) {
14168
14332
  const placeholders = getPlaceholders(value2.length, whereParams.length + 1);
14169
- conditions.push(`${key} IN (${placeholders})`);
14170
- whereConditions.push(`${key} IN (${placeholders})`);
14333
+ conditions.push(renderInPredicate(key, value2, false, placeholders));
14171
14334
  whereParams.push(...value2);
14172
14335
  } else {
14173
14336
  const paramIndex = whereParams.length + 1;
14174
14337
  conditions.push(`${key} = ${getPlaceholder(paramIndex)}`);
14175
- whereConditions.push(`${key} = ${getPlaceholder(paramIndex)}`);
14176
14338
  whereParams.push(value2);
14177
14339
  }
14178
14340
  }
14179
- if (conditions.length > 0) {
14180
- text = `${text} ${getWhereKeyword()} ${conditions.join(" AND ")}`;
14181
- built = null;
14182
- }
14341
+ if (conditions.length > 0)
14342
+ pushWhere("AND", conditions.length > 1 ? `(${conditions.join(" AND ")})` : conditions[0]);
14183
14343
  return this;
14184
14344
  }
14185
14345
  if (isRawExpression(expr)) {
14186
- whereConditions.push(expr.raw);
14187
- text = `${text} ${getWhereKeyword()} ${expr.raw}`;
14188
- built = null;
14346
+ pushWhere("AND", expr.raw);
14189
14347
  return this;
14190
14348
  }
14191
14349
  if (typeof expr === "function") {
14192
- throw new TypeError("where(callback) is not supported. This builder has no expression callback, and " + "ignoring one silently would leave the query matching every row the filter was " + "meant to exclude. Use whereNested(subquery), whereAny(...), or the orWhere* " + "helpers to build a grouped condition.");
14350
+ addWhereGroup("AND", expr);
14351
+ return this;
14193
14352
  }
14194
14353
  if (expr === undefined || expr === null) {
14195
14354
  throw new TypeError("where() was called with no condition.");
@@ -14201,61 +14360,83 @@ function createQueryBuilder(state) {
14201
14360
  },
14202
14361
  whereNull(column) {
14203
14362
  validateIdentifier(String(column), "whereNull(column)");
14204
- const keyword = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
14205
- text = `${text} ${keyword} ${String(column)} IS NULL`;
14206
- built = null;
14363
+ pushWhere("AND", `${String(column)} IS NULL`);
14207
14364
  return this;
14208
14365
  },
14209
14366
  whereNotNull(column) {
14210
14367
  validateIdentifier(String(column), "whereNotNull(column)");
14211
- const keyword = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
14212
- text = `${text} ${keyword} ${String(column)} IS NOT NULL`;
14213
- built = null;
14368
+ pushWhere("AND", `${String(column)} IS NOT NULL`);
14369
+ return this;
14370
+ },
14371
+ orWhereNull(column) {
14372
+ validateIdentifier(String(column), "orWhereNull(column)");
14373
+ pushWhere("OR", `${String(column)} IS NULL`);
14374
+ return this;
14375
+ },
14376
+ orWhereNotNull(column) {
14377
+ validateIdentifier(String(column), "orWhereNotNull(column)");
14378
+ pushWhere("OR", `${String(column)} IS NOT NULL`);
14214
14379
  return this;
14215
14380
  },
14216
14381
  whereBetween(column, start, end) {
14217
14382
  validateIdentifier(String(column), "whereBetween(column)");
14218
- const keyword = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
14219
14383
  const i = whereParams.length + 1;
14220
- text = `${text} ${keyword} ${String(column)} BETWEEN ${getPlaceholder(i)} AND ${getPlaceholder(i + 1)}`;
14221
14384
  whereParams.push(start, end);
14222
- built = null;
14385
+ pushWhere("AND", `${String(column)} BETWEEN ${getPlaceholder(i)} AND ${getPlaceholder(i + 1)}`);
14386
+ return this;
14387
+ },
14388
+ orWhereBetween(column, start, end) {
14389
+ validateIdentifier(String(column), "orWhereBetween(column)");
14390
+ const i = whereParams.length + 1;
14391
+ whereParams.push(start, end);
14392
+ pushWhere("OR", `${String(column)} BETWEEN ${getPlaceholder(i)} AND ${getPlaceholder(i + 1)}`);
14223
14393
  return this;
14224
14394
  },
14225
14395
  whereExists(subquery) {
14226
- const keyword = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
14227
- text = `${text} ${keyword} EXISTS (${subquery.toSQL()})`;
14228
- built = null;
14396
+ pushWhere("AND", `EXISTS (${subquery.toSQL()})`);
14229
14397
  return this;
14230
14398
  },
14399
+ orWhereExists(subquery) {
14400
+ pushWhere("OR", `EXISTS (${subquery.toSQL()})`);
14401
+ return this;
14402
+ },
14403
+ whereGroup(cb) {
14404
+ addWhereGroup("AND", cb);
14405
+ return this;
14406
+ },
14407
+ orWhereGroup(cb) {
14408
+ addWhereGroup("OR", cb);
14409
+ return this;
14410
+ },
14411
+ __whereState() {
14412
+ return { body: renderWhereTerms(whereTerms), params: [...whereParams] };
14413
+ },
14231
14414
  whereJsonContains(column, json) {
14232
14415
  validateIdentifier(String(column), "whereJsonContains(column)");
14233
- const keyword = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
14234
14416
  const dialect = config5.dialect;
14235
14417
  const idx = whereParams.length + 1;
14236
14418
  if (dialect === "postgres") {
14237
14419
  if (config5.sql?.jsonContainsMode === "function")
14238
- text += ` ${keyword} jsonb_contains(${column}, ${getPlaceholder(idx)})`;
14420
+ pushWhere("AND", `jsonb_contains(${column}, ${getPlaceholder(idx)})`);
14239
14421
  else
14240
- text += ` ${keyword} ${column} @> ${getPlaceholder(idx)}`;
14422
+ pushWhere("AND", `${column} @> ${getPlaceholder(idx)}`);
14241
14423
  whereParams.push(JSON.stringify(json));
14242
14424
  } else if (isMysqlLike(dialect)) {
14243
- text += ` ${keyword} JSON_CONTAINS(${column}, ${getPlaceholder(idx)})`;
14425
+ pushWhere("AND", `JSON_CONTAINS(${column}, ${getPlaceholder(idx)})`);
14244
14426
  whereParams.push(JSON.stringify(json));
14245
14427
  } else {
14246
14428
  if (Array.isArray(json)) {
14247
14429
  const conds = json.map((_, i) => `EXISTS (SELECT 1 FROM json_each(${column}) WHERE json_each.value = ${getPlaceholder(idx + i)})`);
14248
- text += ` ${keyword} (${conds.join(" AND ")})`;
14430
+ pushWhere("AND", `(${conds.join(" AND ")})`);
14249
14431
  for (const v of json)
14250
14432
  whereParams.push(v);
14251
14433
  } else if (json !== null && typeof json === "object") {
14252
14434
  throw new Error("[query-builder] whereJsonContains: object containment is not supported on SQLite \u2014 pass a scalar or array, or use whereJsonPath.");
14253
14435
  } else {
14254
- text += ` ${keyword} EXISTS (SELECT 1 FROM json_each(${column}) WHERE json_each.value = ${getPlaceholder(idx)})`;
14436
+ pushWhere("AND", `EXISTS (SELECT 1 FROM json_each(${column}) WHERE json_each.value = ${getPlaceholder(idx)})`);
14255
14437
  whereParams.push(json);
14256
14438
  }
14257
14439
  }
14258
- built = null;
14259
14440
  return this;
14260
14441
  },
14261
14442
  whereJsonPath(path, op, value) {
@@ -14265,17 +14446,14 @@ function createQueryBuilder(state) {
14265
14446
  if (!/^[A-Za-z0-9_.[\]$'\->* ,]+$/.test(path))
14266
14447
  throw new TypeError(`[query-builder] whereJsonPath(path): refusing to use '${path}' \u2014 contains characters outside the allowed JSON-path set`);
14267
14448
  const dialect = config5.dialect;
14268
- const keyword = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
14269
14449
  const idx = whereParams.length + 1;
14270
- if (dialect === "postgres") {
14271
- text += ` ${keyword} ${path} ${op} ${getPlaceholder(idx)}`;
14272
- } else if (isMysqlLike(dialect)) {
14273
- text += ` ${keyword} JSON_EXTRACT(${path}) ${op} ${getPlaceholder(idx)}`;
14274
- } else {
14275
- text += ` ${keyword} json_extract(${path}) ${op} ${getPlaceholder(idx)}`;
14276
- }
14450
+ if (dialect === "postgres")
14451
+ pushWhere("AND", `${path} ${op} ${getPlaceholder(idx)}`);
14452
+ else if (isMysqlLike(dialect))
14453
+ pushWhere("AND", `JSON_EXTRACT(${path}) ${op} ${getPlaceholder(idx)}`);
14454
+ else
14455
+ pushWhere("AND", `json_extract(${path}) ${op} ${getPlaceholder(idx)}`);
14277
14456
  whereParams.push(value);
14278
- built = null;
14279
14457
  return this;
14280
14458
  },
14281
14459
  whereLike(column, pattern, caseSensitive = false) {
@@ -14348,51 +14526,41 @@ function createQueryBuilder(state) {
14348
14526
  },
14349
14527
  whereAny(cols, op, value) {
14350
14528
  if (cols.length === 0) {
14351
- const kw = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
14352
- text += ` ${kw} (${FALSE_PREDICATE})`;
14353
- built = null;
14529
+ pushWhere("AND", `(${FALSE_PREDICATE})`);
14354
14530
  return this;
14355
14531
  }
14356
- const keyword = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
14357
14532
  const idx = whereParams.length + 1;
14358
14533
  const conds = cols.map((c, i) => `${c} ${op} ${getPlaceholder(idx + i)}`);
14359
- text += ` ${keyword} (${conds.join(" OR ")})`;
14360
14534
  for (let i = 0;i < cols.length; i++)
14361
14535
  whereParams.push(value);
14362
- built = null;
14536
+ pushWhere("AND", `(${conds.join(" OR ")})`);
14363
14537
  return this;
14364
14538
  },
14365
14539
  whereAll(cols, op, value) {
14366
14540
  if (cols.length === 0)
14367
14541
  return this;
14368
- const keyword = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
14369
14542
  const idx = whereParams.length + 1;
14370
14543
  const conds = cols.map((c, i) => `${c} ${op} ${getPlaceholder(idx + i)}`);
14371
- text += ` ${keyword} (${conds.join(" AND ")})`;
14372
14544
  for (let i = 0;i < cols.length; i++)
14373
14545
  whereParams.push(value);
14374
- built = null;
14546
+ pushWhere("AND", `(${conds.join(" AND ")})`);
14375
14547
  return this;
14376
14548
  },
14377
14549
  whereNone(cols, op, value) {
14378
14550
  if (cols.length === 0)
14379
14551
  return this;
14380
- const keyword = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
14381
14552
  const idx = whereParams.length + 1;
14382
14553
  const conds = cols.map((c, i) => `${c} ${op} ${getPlaceholder(idx + i)}`);
14383
- text += ` ${keyword} NOT (${conds.join(" OR ")})`;
14384
14554
  for (let i = 0;i < cols.length; i++)
14385
14555
  whereParams.push(value);
14386
- built = null;
14556
+ pushWhere("AND", `NOT (${conds.join(" OR ")})`);
14387
14557
  return this;
14388
14558
  },
14389
14559
  whereNotBetween(column, start, end) {
14390
14560
  validateIdentifier(String(column), "whereNotBetween(column)");
14391
- const keyword = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
14392
14561
  const i = whereParams.length + 1;
14393
- text += ` ${keyword} ${column} NOT BETWEEN ${getPlaceholder(i)} AND ${getPlaceholder(i + 1)}`;
14394
14562
  whereParams.push(start, end);
14395
- built = null;
14563
+ pushWhere("AND", `${column} NOT BETWEEN ${getPlaceholder(i)} AND ${getPlaceholder(i + 1)}`);
14396
14564
  return this;
14397
14565
  },
14398
14566
  whereDate(column, op, date) {
@@ -14400,223 +14568,94 @@ function createQueryBuilder(state) {
14400
14568
  const dateString = date instanceof Date ? date.toISOString() : typeof date === "string" ? date : (() => {
14401
14569
  throw new TypeError(`[query-builder] whereDate(date): expected string or Date, got ${typeof date}`);
14402
14570
  })();
14403
- const keyword = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
14404
14571
  const idx = whereParams.length + 1;
14405
- text += ` ${keyword} ${column} ${op} ${getPlaceholder(idx)}`;
14406
14572
  whereParams.push(dateString);
14407
- built = null;
14573
+ pushWhere("AND", `${column} ${op} ${getPlaceholder(idx)}`);
14408
14574
  return this;
14409
14575
  },
14410
14576
  whereRaw(fragment) {
14411
- const frag = renderRawFragment(fragment, "whereRaw(fragment)");
14412
- const keyword = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
14413
- text += ` ${keyword} ${frag}`;
14414
- built = null;
14577
+ pushWhere("AND", renderRawFragment(fragment, "whereRaw(fragment)"));
14578
+ return this;
14579
+ },
14580
+ orWhereRaw(fragment) {
14581
+ pushWhere("OR", renderRawFragment(fragment, "orWhereRaw(fragment)"));
14415
14582
  return this;
14416
14583
  },
14417
14584
  whereColumn(left, op, right) {
14418
14585
  validateIdentifier(left, "whereColumn(left)");
14419
14586
  validateIdentifier(right, "whereColumn(right)");
14420
- const keyword = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
14421
- text += ` ${keyword} ${left} ${op} ${right}`;
14422
- built = null;
14587
+ pushWhere("AND", `${left} ${op} ${right}`);
14423
14588
  return this;
14424
14589
  },
14425
14590
  orWhereColumn(left, op, right) {
14426
14591
  validateIdentifier(left, "orWhereColumn(left)");
14427
14592
  validateIdentifier(right, "orWhereColumn(right)");
14428
- text += ` OR ${left} ${op} ${right}`;
14429
- built = null;
14593
+ pushWhere("OR", `${left} ${op} ${right}`);
14430
14594
  return this;
14431
14595
  },
14432
14596
  whereIn(column, values) {
14433
14597
  validateIdentifier(String(column), "whereIn(column)");
14434
- const keyword = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
14435
14598
  if (Array.isArray(values)) {
14436
14599
  const placeholders = getPlaceholders(values.length, whereParams.length + 1);
14437
- text += ` ${keyword} ${renderInPredicate(column, values, false, placeholders)}`;
14600
+ const clause = renderInPredicate(column, values, false, placeholders);
14438
14601
  whereParams.push(...values);
14602
+ pushWhere("AND", clause);
14439
14603
  } else {
14440
- text += ` ${keyword} ${column} IN (${String(values.toSQL())})`;
14604
+ pushWhere("AND", `${column} IN (${String(values.toSQL())})`);
14441
14605
  }
14442
- built = null;
14443
14606
  return this;
14444
14607
  },
14445
14608
  orWhereIn(column, values) {
14446
14609
  validateIdentifier(String(column), "orWhereIn(column)");
14447
14610
  if (Array.isArray(values)) {
14448
14611
  const placeholders = getPlaceholders(values.length, whereParams.length + 1);
14449
- text += ` OR ${renderInPredicate(column, values, false, placeholders)}`;
14612
+ const clause = renderInPredicate(column, values, false, placeholders);
14450
14613
  whereParams.push(...values);
14614
+ pushWhere("OR", clause);
14451
14615
  } else {
14452
- text += ` OR ${column} IN (${String(values.toSQL())})`;
14616
+ pushWhere("OR", `${column} IN (${String(values.toSQL())})`);
14453
14617
  }
14454
- built = null;
14455
14618
  return this;
14456
14619
  },
14457
14620
  whereNotIn(column, values) {
14458
14621
  validateIdentifier(String(column), "whereNotIn(column)");
14459
- const keyword = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
14460
14622
  if (Array.isArray(values)) {
14461
14623
  const placeholders = getPlaceholders(values.length, whereParams.length + 1);
14462
- text += ` ${keyword} ${renderInPredicate(column, values, true, placeholders)}`;
14624
+ const clause = renderInPredicate(column, values, true, placeholders);
14463
14625
  whereParams.push(...values);
14626
+ pushWhere("AND", clause);
14464
14627
  } else {
14465
- text += ` ${keyword} ${column} NOT IN (${String(values.toSQL())})`;
14628
+ pushWhere("AND", `${column} NOT IN (${String(values.toSQL())})`);
14466
14629
  }
14467
- built = null;
14468
14630
  return this;
14469
14631
  },
14470
14632
  orWhereNotIn(column, values) {
14471
14633
  validateIdentifier(String(column), "orWhereNotIn(column)");
14472
14634
  if (Array.isArray(values)) {
14473
14635
  const placeholders = getPlaceholders(values.length, whereParams.length + 1);
14474
- text += ` OR ${renderInPredicate(column, values, true, placeholders)}`;
14636
+ const clause = renderInPredicate(column, values, true, placeholders);
14475
14637
  whereParams.push(...values);
14638
+ pushWhere("OR", clause);
14476
14639
  } else {
14477
- text += ` OR ${column} NOT IN (${String(values.toSQL())})`;
14640
+ pushWhere("OR", `${column} NOT IN (${String(values.toSQL())})`);
14478
14641
  }
14479
- built = null;
14480
14642
  return this;
14481
14643
  },
14482
14644
  whereNested(fragment) {
14483
- const keyword = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
14484
14645
  const inner = fragment.toSQL ? String(fragment.toSQL()) : String(fragment);
14485
- text += ` ${keyword} (${inner})`;
14486
- built = null;
14646
+ pushWhere("AND", `(${inner})`);
14487
14647
  return this;
14488
14648
  },
14489
14649
  orWhereNested(fragment) {
14490
14650
  const inner = fragment.toSQL ? String(fragment.toSQL()) : String(fragment);
14491
- text += ` OR (${inner})`;
14492
- built = null;
14651
+ pushWhere("OR", `(${inner})`);
14493
14652
  return this;
14494
14653
  },
14495
14654
  andWhere(expr, op, value) {
14496
- if (typeof expr === "string" && op !== undefined) {
14497
- validateIdentifier(String(expr), "andWhere(column)");
14498
- assertSafeWhereOperator(op, "andWhere(operator)");
14499
- const paramIndex = whereParams.length + 1;
14500
- whereConditions.push(`${String(expr)} ${String(op)} ${getPlaceholder(paramIndex)}`);
14501
- whereParams.push(value);
14502
- text = `${text} AND ${String(expr)} ${String(op)} ${getPlaceholder(paramIndex)}`;
14503
- built = null;
14504
- return this;
14505
- }
14506
- if (Array.isArray(expr)) {
14507
- const [col, op2, val] = expr;
14508
- const colName = String(col);
14509
- validateIdentifier(colName, "andWhere(column)");
14510
- const operator = assertSafeWhereOperator(op2, "andWhere(operator)");
14511
- if (operator === "in" || operator === "not in") {
14512
- const values = Array.isArray(val) ? val : [val];
14513
- const placeholders = getPlaceholders(values.length, whereParams.length + 1);
14514
- whereConditions.push(`${colName} ${operator.toUpperCase()} (${placeholders})`);
14515
- whereParams.push(...values);
14516
- text = `${text} AND ${colName} ${operator.toUpperCase()} (${placeholders})`;
14517
- built = null;
14518
- } else {
14519
- const paramIndex = whereParams.length + 1;
14520
- whereConditions.push(`${colName} ${operator} ${getPlaceholder(paramIndex)}`);
14521
- whereParams.push(val);
14522
- text = `${text} AND ${colName} ${operator} ${getPlaceholder(paramIndex)}`;
14523
- built = null;
14524
- }
14525
- return this;
14526
- }
14527
- if (expr && typeof expr === "object" && !("raw" in expr)) {
14528
- const keys = Object.keys(expr);
14529
- const conditions = [];
14530
- for (const key of keys) {
14531
- const value2 = expr[key];
14532
- if (Array.isArray(value2)) {
14533
- const placeholders = getPlaceholders(value2.length, whereParams.length + 1);
14534
- conditions.push(`${key} IN (${placeholders})`);
14535
- whereConditions.push(`${key} IN (${placeholders})`);
14536
- whereParams.push(...value2);
14537
- } else {
14538
- const paramIndex = whereParams.length + 1;
14539
- conditions.push(`${key} = ${getPlaceholder(paramIndex)}`);
14540
- whereConditions.push(`${key} = ${getPlaceholder(paramIndex)}`);
14541
- whereParams.push(value2);
14542
- }
14543
- }
14544
- if (conditions.length > 0) {
14545
- text = `${text} AND ${conditions.join(" AND ")}`;
14546
- built = null;
14547
- }
14548
- return this;
14549
- }
14550
- if (expr && typeof expr.raw !== "undefined") {
14551
- whereConditions.push(expr.raw);
14552
- text = `${text} AND ${expr.raw}`;
14553
- built = null;
14554
- return this;
14555
- }
14556
- return this;
14655
+ return addBooleanWhere(this, "AND", "andWhere", expr, op, value);
14557
14656
  },
14558
14657
  orWhere(expr, op, value) {
14559
- if (typeof expr === "string" && op !== undefined) {
14560
- validateIdentifier(String(expr), "orWhere(column)");
14561
- assertSafeWhereOperator(op, "orWhere(operator)");
14562
- const paramIndex = whereParams.length + 1;
14563
- whereConditions.push(`OR ${String(expr)} ${String(op)} ${getPlaceholder(paramIndex)}`);
14564
- whereParams.push(value);
14565
- text = `${text} OR ${String(expr)} ${String(op)} ${getPlaceholder(paramIndex)}`;
14566
- built = null;
14567
- return this;
14568
- }
14569
- if (Array.isArray(expr)) {
14570
- const [col, op2, val] = expr;
14571
- const colName = String(col);
14572
- validateIdentifier(colName, "orWhere(column)");
14573
- const operator = assertSafeWhereOperator(op2, "orWhere(operator)");
14574
- if (operator === "in" || operator === "not in") {
14575
- const values = Array.isArray(val) ? val : [val];
14576
- const placeholders = getPlaceholders(values.length, whereParams.length + 1);
14577
- whereConditions.push(`OR ${colName} ${operator.toUpperCase()} (${placeholders})`);
14578
- whereParams.push(...values);
14579
- text = `${text} OR ${colName} ${operator.toUpperCase()} (${placeholders})`;
14580
- built = null;
14581
- } else {
14582
- const paramIndex = whereParams.length + 1;
14583
- whereConditions.push(`OR ${colName} ${operator} ${getPlaceholder(paramIndex)}`);
14584
- whereParams.push(val);
14585
- text = `${text} OR ${colName} ${operator} ${getPlaceholder(paramIndex)}`;
14586
- built = null;
14587
- }
14588
- return this;
14589
- }
14590
- if (expr && typeof expr === "object" && !("raw" in expr)) {
14591
- const keys = Object.keys(expr);
14592
- const conditions = [];
14593
- for (const key of keys) {
14594
- const value2 = expr[key];
14595
- if (Array.isArray(value2)) {
14596
- const placeholders = getPlaceholders(value2.length, whereParams.length + 1);
14597
- conditions.push(`${key} IN (${placeholders})`);
14598
- whereConditions.push(`OR ${key} IN (${placeholders})`);
14599
- whereParams.push(...value2);
14600
- } else {
14601
- const paramIndex = whereParams.length + 1;
14602
- conditions.push(`${key} = ${getPlaceholder(paramIndex)}`);
14603
- whereConditions.push(`OR ${key} = ${getPlaceholder(paramIndex)}`);
14604
- whereParams.push(value2);
14605
- }
14606
- }
14607
- if (conditions.length > 0) {
14608
- text = `${text} OR ${conditions.join(" AND ")}`;
14609
- built = null;
14610
- }
14611
- return this;
14612
- }
14613
- if (expr && typeof expr.raw !== "undefined") {
14614
- whereConditions.push(`OR ${expr.raw}`);
14615
- text = `${text} OR ${expr.raw}`;
14616
- built = null;
14617
- return this;
14618
- }
14619
- return this;
14658
+ return addBooleanWhere(this, "OR", "orWhere", expr, op, value);
14620
14659
  },
14621
14660
  orderBy(column, direction = "asc") {
14622
14661
  const dir = direction === "asc" ? "ASC" : "DESC";
@@ -14853,7 +14892,7 @@ function createQueryBuilder(state) {
14853
14892
  return this;
14854
14893
  },
14855
14894
  toSQL() {
14856
- const sqlText = reorderSelectClauses(text);
14895
+ const sqlText = reorderSelectClauses(currentSql());
14857
14896
  return {
14858
14897
  sql: sqlText,
14859
14898
  toString: () => sqlText,
@@ -14899,7 +14938,7 @@ function createQueryBuilder(state) {
14899
14938
  if (!Number.isFinite(page) || page < 1 || !Number.isInteger(page))
14900
14939
  throw new TypeError(`[query-builder] paginate(page): expected integer >= 1, got ${page}`);
14901
14940
  if (opts.tx) {
14902
- const baseSql = reorderSelectClauses(text);
14941
+ const baseSql = reorderSelectClauses(currentSql());
14903
14942
  const baseParams = [...whereParams];
14904
14943
  const cRows2 = await opts.tx.unsafe(`SELECT COUNT(*) as c FROM (${baseSql}) as sub`, baseParams);
14905
14944
  const total2 = Number(cRows2?.[0]?.c ?? 0);
@@ -14927,25 +14966,33 @@ function createQueryBuilder(state) {
14927
14966
  return { data: hasMore ? data.slice(0, perPage) : data, meta: { perPage, page: p, hasMore } };
14928
14967
  },
14929
14968
  async cursorPaginate(perPage, cursor, column = "id", direction = "asc") {
14930
- let q = ensureBuilt();
14969
+ if (!Number.isInteger(perPage) || perPage <= 0)
14970
+ throw new TypeError(`[query-builder] cursorPaginate(perPage): expected positive integer, got ${perPage}`);
14971
+ const cols = (Array.isArray(column) ? column : [column]).map(String);
14972
+ for (const c of cols)
14973
+ validateIdentifier(c, "cursorPaginate(column)");
14974
+ const cmp = direction === "asc" ? ">" : "<";
14975
+ const dir = direction === "asc" ? "ASC" : "DESC";
14976
+ const params = [...whereParams];
14977
+ let predicate = "";
14931
14978
  if (cursor !== undefined && cursor !== null) {
14932
14979
  if (Array.isArray(column)) {
14933
- const cols = column.map((c) => sql(String(c)));
14934
- const comp = direction === "asc" ? sql`>` : sql`<`;
14935
- const tupleCols = sql`(${sql(cols)})`;
14936
- const tupleVals = sql`(${sql(cursor)})`;
14937
- q = sql`${q} WHERE ${tupleCols} ${comp} ${tupleVals}`;
14980
+ const phs = cursor.map((v) => {
14981
+ params.push(v);
14982
+ return getPlaceholder(params.length);
14983
+ });
14984
+ predicate = `(${cols.join(", ")}) ${cmp} (${phs.join(", ")})`;
14938
14985
  } else {
14939
- q = direction === "asc" ? sql`${q} WHERE ${sql(String(column))} > ${cursor}` : sql`${q} WHERE ${sql(String(column))} < ${cursor}`;
14986
+ params.push(cursor);
14987
+ predicate = `${cols[0]} ${cmp} ${getPlaceholder(params.length)}`;
14940
14988
  }
14941
14989
  }
14942
- if (Array.isArray(column)) {
14943
- const orderParts = column.map((c) => sql`${sql(String(c))} ${direction === "asc" ? sql`ASC` : sql`DESC`}`);
14944
- const orderExpr = orderParts.reduce((acc, p, i) => i === 0 ? p : sql`${acc}, ${p}`);
14945
- q = sql`${q} ORDER BY ${orderExpr} LIMIT ${perPage + 1}`;
14946
- } else {
14947
- q = sql`${q} ORDER BY ${sql(String(column))} ${direction === "asc" ? sql`ASC` : sql`DESC`} LIMIT ${perPage + 1}`;
14990
+ const base2 = reorderSelectClauses(predicate ? currentSql({ conn: "AND", sql: predicate }) : currentSql());
14991
+ const order = cols.map((c) => `${c} ${dir}`).join(", ");
14992
+ if (hasTopLevelOrderBy(base2)) {
14993
+ throw new TypeError(`[query-builder] cursorPaginate() cannot be combined with orderBy() \u2014 it orders by ` + `${order} itself, and a cursor is only meaningful in that order. ` + `Remove the orderBy(), or pass the column and direction to cursorPaginate().`);
14948
14994
  }
14995
+ const q = params.length > 0 ? _sql.unsafe(`${base2} ORDER BY ${order} LIMIT ${perPage + 1}`, params) : _sql.unsafe(`${base2} ORDER BY ${order} LIMIT ${perPage + 1}`);
14949
14996
  const rows = await runWithHooks(q, "select", { signal: abortSignal, timeoutMs });
14950
14997
  const hasMore = rows.length > perPage;
14951
14998
  const data = rows.slice(0, perPage);
@@ -15019,8 +15066,8 @@ function createQueryBuilder(state) {
15019
15066
  };
15020
15067
  const predicate = `${table}.${softDeleteColumn} IS NOT NULL`;
15021
15068
  text = splice(text, predicate);
15022
- const currentSql = String(ensureBuilt());
15023
- built = sql([splice(currentSql, predicate)]);
15069
+ const currentSql2 = String(ensureBuilt());
15070
+ built = sql([splice(currentSql2, predicate)]);
15024
15071
  return this;
15025
15072
  },
15026
15073
  scope(name, value) {
@@ -15063,7 +15110,7 @@ function createQueryBuilder(state) {
15063
15110
  return ensureBuilt().simple();
15064
15111
  },
15065
15112
  toText() {
15066
- return text;
15113
+ return currentSql();
15067
15114
  },
15068
15115
  async get() {
15069
15116
  const hooks = activeHooks();
@@ -15071,11 +15118,12 @@ function createQueryBuilder(state) {
15071
15118
  if (!config5.softDeletes?.enabled && !useCache && !timeoutMs && !abortSignal && !hasQueryHooks) {
15072
15119
  const prepareFn = _sql._prepareStatement;
15073
15120
  if (prepareFn) {
15074
- const stmt = prepareFn(text);
15121
+ const stmt = prepareFn(currentSql());
15075
15122
  return hydratePivotRows(whereParams.length > 0 ? stmt.all(...whereParams) : stmt.all());
15076
15123
  }
15077
15124
  }
15078
- built = whereParams.length > 0 ? _sql.unsafe(text, whereParams) : _sql.unsafe(text);
15125
+ const getText = currentSql();
15126
+ built = whereParams.length > 0 ? _sql.unsafe(getText, whereParams) : _sql.unsafe(getText);
15079
15127
  if (!config5.softDeletes?.enabled && !useCache && !timeoutMs && !abortSignal && !hasQueryHooks) {
15080
15128
  const stmt = ensureBuilt()._stmt;
15081
15129
  const params = ensureBuilt()._params;
@@ -15092,9 +15140,9 @@ function createQueryBuilder(state) {
15092
15140
  const col = config5.softDeletes.column;
15093
15141
  const tbl = String(table);
15094
15142
  const hasCol = schema ? Boolean(schema[tbl]?.columns?.[col]) : true;
15095
- if (hasCol && !SQL_PATTERNS.DELETED_AT.test(text)) {
15096
- finalQuery = sql`${ensureBuilt()} WHERE ${sql(String(col))} IS ${onlyTrashed ? sql`NOT NULL` : sql`NULL`}`;
15143
+ if (hasCol && !SQL_PATTERNS.DELETED_AT.test(currentSql())) {
15097
15144
  addWhereText("WHERE", `${String(col)} IS ${onlyTrashed ? "NOT " : ""}NULL`);
15145
+ finalQuery = ensureBuilt();
15098
15146
  }
15099
15147
  }
15100
15148
  const cacheKey = useCache ? `${String(finalQuery)}\x00${JSON.stringify(whereParams)}` : "";
@@ -15124,7 +15172,8 @@ function createQueryBuilder(state) {
15124
15172
  if (!config5.softDeletes?.enabled && !useCache && !timeoutMs && !abortSignal && !fHasQueryHooks) {
15125
15173
  const prepareFn = _sql._prepareStatement;
15126
15174
  if (prepareFn) {
15127
- const firstText = text.includes(" LIMIT ") ? text : `${text} LIMIT 1`;
15175
+ const base2 = currentSql();
15176
+ const firstText = base2.includes(" LIMIT ") ? base2 : `${base2} LIMIT 1`;
15128
15177
  const stmt = prepareFn(firstText);
15129
15178
  const rows2 = whereParams.length > 0 ? stmt.all(...whereParams) : stmt.all();
15130
15179
  return hydratePivotRow(rows2[0]);
@@ -15191,13 +15240,14 @@ function createQueryBuilder(state) {
15191
15240
  return fn(this);
15192
15241
  },
15193
15242
  async count() {
15194
- const fromIdx = text.indexOf(" FROM ");
15195
- const hasGroupBy = / GROUP BY /i.test(text);
15243
+ const src = currentSql();
15244
+ const fromIdx = src.indexOf(" FROM ");
15245
+ const hasGroupBy = / GROUP BY /i.test(src);
15196
15246
  let countText;
15197
15247
  if (hasGroupBy) {
15198
- countText = `SELECT COUNT(*) as c FROM (${text}) AS _bqb_count_sub`;
15248
+ countText = `SELECT COUNT(*) as c FROM (${src}) AS _bqb_count_sub`;
15199
15249
  } else if (fromIdx !== -1) {
15200
- countText = `SELECT COUNT(*) as c${text.substring(fromIdx)}`;
15250
+ countText = `SELECT COUNT(*) as c${src.substring(fromIdx)}`;
15201
15251
  } else {
15202
15252
  countText = `SELECT COUNT(*) as c FROM ${table}`;
15203
15253
  }
@@ -15217,8 +15267,9 @@ function createQueryBuilder(state) {
15217
15267
  return Number(row?.c ?? 0);
15218
15268
  },
15219
15269
  async avg(column) {
15220
- const fromIdx = text.indexOf(" FROM ");
15221
- const avgText = fromIdx !== -1 ? `SELECT AVG(${column}) as a${text.substring(fromIdx)}` : `SELECT AVG(${column}) as a FROM ${table}`;
15270
+ const src = currentSql();
15271
+ const fromIdx = src.indexOf(" FROM ");
15272
+ const avgText = fromIdx !== -1 ? `SELECT AVG(${column}) as a${src.substring(fromIdx)}` : `SELECT AVG(${column}) as a FROM ${table}`;
15222
15273
  const aHooks = activeHooks();
15223
15274
  const aHasHooks = aHooks && (aHooks.onQueryStart || aHooks.onQueryEnd || aHooks.onQueryError || aHooks.startSpan || hasSlowQueryHook(aHooks));
15224
15275
  if (!config5.softDeletes?.enabled && !useCache && !timeoutMs && !abortSignal && !aHasHooks) {
@@ -15235,24 +15286,27 @@ function createQueryBuilder(state) {
15235
15286
  return Number(row?.a ?? 0);
15236
15287
  },
15237
15288
  async sum(column) {
15238
- const fromIdx = text.indexOf(" FROM ");
15239
- const sumText = fromIdx !== -1 ? `SELECT SUM(${column}) as s${text.substring(fromIdx)}` : `SELECT SUM(${column}) as s FROM ${table}`;
15289
+ const src = currentSql();
15290
+ const fromIdx = src.indexOf(" FROM ");
15291
+ const sumText = fromIdx !== -1 ? `SELECT SUM(${column}) as s${src.substring(fromIdx)}` : `SELECT SUM(${column}) as s FROM ${table}`;
15240
15292
  const q = whereParams.length > 0 ? _sql.unsafe(sumText, whereParams) : _sql.unsafe(sumText);
15241
15293
  const rows = await runWithHooks(q, "select", { signal: abortSignal, timeoutMs });
15242
15294
  const [row] = rows;
15243
15295
  return Number(row?.s ?? 0);
15244
15296
  },
15245
15297
  async max(column) {
15246
- const fromIdx = text.indexOf(" FROM ");
15247
- const maxText = fromIdx !== -1 ? `SELECT MAX(${column}) as m${text.substring(fromIdx)}` : `SELECT MAX(${column}) as m FROM ${table}`;
15298
+ const src = currentSql();
15299
+ const fromIdx = src.indexOf(" FROM ");
15300
+ const maxText = fromIdx !== -1 ? `SELECT MAX(${column}) as m${src.substring(fromIdx)}` : `SELECT MAX(${column}) as m FROM ${table}`;
15248
15301
  const q = whereParams.length > 0 ? _sql.unsafe(maxText, whereParams) : _sql.unsafe(maxText);
15249
15302
  const rows = await runWithHooks(q, "select", { signal: abortSignal, timeoutMs });
15250
15303
  const [row] = rows;
15251
15304
  return row?.m;
15252
15305
  },
15253
15306
  async min(column) {
15254
- const fromIdx = text.indexOf(" FROM ");
15255
- const minText = fromIdx !== -1 ? `SELECT MIN(${column}) as m${text.substring(fromIdx)}` : `SELECT MIN(${column}) as m FROM ${table}`;
15307
+ const src = currentSql();
15308
+ const fromIdx = src.indexOf(" FROM ");
15309
+ const minText = fromIdx !== -1 ? `SELECT MIN(${column}) as m${src.substring(fromIdx)}` : `SELECT MIN(${column}) as m FROM ${table}`;
15256
15310
  const q = whereParams.length > 0 ? _sql.unsafe(minText, whereParams) : _sql.unsafe(minText);
15257
15311
  const rows = await runWithHooks(q, "select", { signal: abortSignal, timeoutMs });
15258
15312
  const [row] = rows;
@@ -15291,7 +15345,7 @@ function createQueryBuilder(state) {
15291
15345
  return [...whereParams];
15292
15346
  },
15293
15347
  __rawState() {
15294
- return { sql: reorderSelectClauses(text), params: [...whereParams] };
15348
+ return { sql: reorderSelectClauses(currentSql()), params: [...whereParams] };
15295
15349
  },
15296
15350
  raw() {
15297
15351
  return ensureBuilt().raw();
@@ -15335,8 +15389,6 @@ function createQueryBuilder(state) {
15335
15389
  return () => receiver;
15336
15390
  const column = chosen;
15337
15391
  return (value) => {
15338
- const expr = Array.isArray(value) ? sql`${sql(column)} IN ${sql(value)}` : sql`${sql(column)} = ${value}`;
15339
- built = isOr ? sql`${ensureBuilt()} OR ${expr}` : isAnd ? sql`${ensureBuilt()} AND ${expr}` : sql`${ensureBuilt()} WHERE ${expr}`;
15340
15392
  if (Array.isArray(value)) {
15341
15393
  const phs = getPlaceholders(value.length, whereParams.length + 1);
15342
15394
  addWhereText(isOr ? "OR" : isAnd ? "AND" : "WHERE", `${column} IN (${phs})`);
@@ -15391,8 +15443,7 @@ function createQueryBuilder(state) {
15391
15443
  const subText = rawState?.sql ?? (typeof subExec === "string" ? subExec : subExec?.sql ?? String(subExec));
15392
15444
  const subParams = rawState?.params ?? (Array.isArray(subExec?.values) ? subExec.values : []);
15393
15445
  const baseText = `SELECT * FROM (${subText}) AS ${String(alias)}`;
15394
- const appendWhere = (text, params, hasWhere, expr, op, value, connector) => {
15395
- const kw = !hasWhere ? "WHERE" : connector === "WHERE" ? "AND" : connector;
15446
+ const appendWhere = (params, expr, op, value) => {
15396
15447
  const out = [...params];
15397
15448
  const cmp = (col, operator, val) => {
15398
15449
  validateIdentifier(col, "selectFromSub where(column)");
@@ -15408,61 +15459,91 @@ function createQueryBuilder(state) {
15408
15459
  return `${col} ${o} ${ph}`;
15409
15460
  };
15410
15461
  let clause;
15411
- if (typeof expr === "string" && op !== undefined)
15462
+ if (typeof expr === "string" && op !== undefined) {
15412
15463
  clause = cmp(expr, op, value);
15413
- else if (Array.isArray(expr) && expr.length === 3)
15464
+ } else if (Array.isArray(expr) && expr.length === 3) {
15414
15465
  clause = cmp(expr[0], expr[1], expr[2]);
15415
- else if (expr && typeof expr === "object")
15416
- clause = Object.entries(expr).map(([k, v]) => cmp(k, "=", v)).join(" AND ");
15417
- else
15418
- return { text, params };
15419
- return { text: `${text} ${kw} ${clause}`, params: out };
15466
+ } else if (expr && typeof expr === "object") {
15467
+ const parts = Object.entries(expr).map(([k, v]) => cmp(k, "=", v));
15468
+ clause = parts.length > 1 ? `(${parts.join(" AND ")})` : parts[0];
15469
+ } else {
15470
+ return { clause: null, params };
15471
+ }
15472
+ return { clause, params: out };
15420
15473
  };
15421
- function makeSub(text, params, hasWhere = false) {
15474
+ function makeSub(baseText2, terms, params, tail = "") {
15475
+ const body = renderWhereTerms(terms);
15476
+ const text = body ? `${baseText2} WHERE ${body}${tail}` : `${baseText2}${tail}`;
15422
15477
  const build = () => params.length > 0 ? _sql.unsafe(text, params) : _sql.unsafe(text);
15478
+ const withTerm = (conn, expr, op, value) => {
15479
+ const r = appendWhere(params, expr, op, value);
15480
+ return r.clause === null ? makeSub(baseText2, terms, params, tail) : makeSub(baseText2, [...terms, { conn, sql: r.clause }], r.params, tail);
15481
+ };
15482
+ const withGroup = (conn, cb) => {
15483
+ const gTerms = [];
15484
+ let gParams = [...params];
15485
+ const add = (c) => (expr, op, value) => {
15486
+ const r = appendWhere(gParams, expr, op, value);
15487
+ if (r.clause !== null) {
15488
+ gTerms.push({ conn: c, sql: r.clause });
15489
+ gParams = r.params;
15490
+ }
15491
+ return collector;
15492
+ };
15493
+ const collector = { where: add("AND"), andWhere: add("AND"), orWhere: add("OR") };
15494
+ cb(collector);
15495
+ const body2 = renderWhereTerms(gTerms);
15496
+ if (!body2) {
15497
+ throw new TypeError("[query-builder] selectFromSub.whereGroup(callback): the callback added no conditions. " + "Add at least one where()/orWhere() to the builder it receives.");
15498
+ }
15499
+ return makeSub(baseText2, [...terms, { conn, sql: `(${body2})` }], gParams, tail);
15500
+ };
15423
15501
  const base = {
15424
15502
  distinct() {
15425
- return makeSub(text.replace(/^SELECT\s+/i, "SELECT DISTINCT "), params, hasWhere);
15503
+ return makeSub(baseText2.replace(/^SELECT\s+/i, "SELECT DISTINCT "), terms, params, tail);
15426
15504
  },
15427
15505
  distinctOn(...columns) {
15428
15506
  const cols = columns.map(String).join(", ");
15429
- return makeSub(text.replace(/^SELECT\s+/i, `SELECT DISTINCT ON (${cols}) `), params, hasWhere);
15507
+ return makeSub(baseText2.replace(/^SELECT\s+/i, `SELECT DISTINCT ON (${cols}) `), terms, params, tail);
15430
15508
  },
15431
15509
  selectRaw(fragment) {
15432
15510
  const frag = renderRawFragment(fragment, "selectFromSub.selectRaw(fragment)");
15433
- const fromIdx = text.indexOf(" FROM ");
15434
- const newText = fromIdx !== -1 ? `${text.slice(0, fromIdx)}, ${frag}${text.slice(fromIdx)}` : `${text}, ${frag}`;
15435
- return makeSub(newText, params, hasWhere);
15511
+ const fromIdx = baseText2.indexOf(" FROM ");
15512
+ const newBase = fromIdx !== -1 ? `${baseText2.slice(0, fromIdx)}, ${frag}${baseText2.slice(fromIdx)}` : `${baseText2}, ${frag}`;
15513
+ return makeSub(newBase, terms, params, tail);
15436
15514
  },
15437
15515
  where(expr, op, value) {
15438
- const r = appendWhere(text, params, hasWhere, expr, op, value, "WHERE");
15439
- return makeSub(r.text, r.params, true);
15516
+ return withTerm("AND", expr, op, value);
15440
15517
  },
15441
15518
  andWhere(expr, op, value) {
15442
- const r = appendWhere(text, params, hasWhere, expr, op, value, "AND");
15443
- return makeSub(r.text, r.params, true);
15519
+ return withTerm("AND", expr, op, value);
15444
15520
  },
15445
15521
  orWhere(expr, op, value) {
15446
- const r = appendWhere(text, params, hasWhere, expr, op, value, "OR");
15447
- return makeSub(r.text, r.params, true);
15522
+ return withTerm("OR", expr, op, value);
15523
+ },
15524
+ whereGroup(cb) {
15525
+ return withGroup("AND", cb);
15526
+ },
15527
+ orWhereGroup(cb) {
15528
+ return withGroup("OR", cb);
15448
15529
  },
15449
15530
  orderBy(column, direction = "asc") {
15450
15531
  validateIdentifier(String(column), "selectFromSub.orderBy(column)");
15451
15532
  const dir = direction === "asc" ? "ASC" : "DESC";
15452
- const newText = SQL_PATTERNS.ORDER_BY.test(text) ? `${text}, ${column} ${dir}` : `${text} ORDER BY ${column} ${dir}`;
15453
- return makeSub(newText, params, hasWhere);
15533
+ const newTail = SQL_PATTERNS.ORDER_BY.test(tail) ? `${tail}, ${column} ${dir}` : `${tail} ORDER BY ${column} ${dir}`;
15534
+ return makeSub(baseText2, terms, params, newTail);
15454
15535
  },
15455
15536
  limit(n) {
15456
15537
  if (!Number.isInteger(n) || n < 0)
15457
15538
  throw new TypeError(`[query-builder] selectFromSub.limit(n): expected non-negative integer, got ${n}`);
15458
- const newText = SQL_PATTERNS.LIMIT.test(text) ? text.replace(SQL_PATTERNS.LIMIT, ` LIMIT ${n}`) : `${text} LIMIT ${n}`;
15459
- return makeSub(newText, params, hasWhere);
15539
+ const newTail = SQL_PATTERNS.LIMIT.test(tail) ? tail.replace(SQL_PATTERNS.LIMIT, ` LIMIT ${n}`) : `${tail} LIMIT ${n}`;
15540
+ return makeSub(baseText2, terms, params, newTail);
15460
15541
  },
15461
15542
  offset(n) {
15462
15543
  if (!Number.isInteger(n) || n < 0)
15463
15544
  throw new TypeError(`[query-builder] selectFromSub.offset(n): expected non-negative integer, got ${n}`);
15464
- const newText = SQL_PATTERNS.OFFSET.test(text) ? text.replace(SQL_PATTERNS.OFFSET, ` OFFSET ${n}`) : `${text} OFFSET ${n}`;
15465
- return makeSub(newText, params, hasWhere);
15545
+ const newTail = SQL_PATTERNS.OFFSET.test(tail) ? tail.replace(SQL_PATTERNS.OFFSET, ` OFFSET ${n}`) : `${tail} OFFSET ${n}`;
15546
+ return makeSub(baseText2, terms, params, newTail);
15466
15547
  },
15467
15548
  toSQL() {
15468
15549
  return makeExecutableQuery(build(), text);
@@ -15545,8 +15626,13 @@ function createQueryBuilder(state) {
15545
15626
  whereJsonContains: subqueryNotSupported("whereJsonContains"),
15546
15627
  whereJsonPath: subqueryNotSupported("whereJsonPath"),
15547
15628
  whereNull: subqueryNotSupported("whereNull"),
15629
+ orWhereNull: subqueryNotSupported("orWhereNull"),
15548
15630
  whereNotNull: subqueryNotSupported("whereNotNull"),
15631
+ orWhereNotNull: subqueryNotSupported("orWhereNotNull"),
15549
15632
  whereExists: subqueryNotSupported("whereExists"),
15633
+ orWhereExists: subqueryNotSupported("orWhereExists"),
15634
+ orWhereBetween: subqueryNotSupported("orWhereBetween"),
15635
+ orWhereRaw: subqueryNotSupported("orWhereRaw"),
15550
15636
  whereJsonDoesntContain: subqueryNotSupported("whereJsonDoesntContain"),
15551
15637
  whereJsonContainsKey: subqueryNotSupported("whereJsonContainsKey"),
15552
15638
  whereJsonDoesntContainKey: subqueryNotSupported("whereJsonDoesntContainKey"),
@@ -15625,7 +15711,7 @@ function createQueryBuilder(state) {
15625
15711
  };
15626
15712
  return base;
15627
15713
  }
15628
- return makeSub(baseText, subParams);
15714
+ return makeSub(baseText, [], subParams);
15629
15715
  },
15630
15716
  insertInto(table) {
15631
15717
  let built;
@@ -16730,7 +16816,7 @@ function clearQueryCache() {
16730
16816
  function setQueryCacheMaxSize(size) {
16731
16817
  queryCache.setMaxSize(size);
16732
16818
  }
16733
- var FALSE_PREDICATE = "1 = 0", TRUE_PREDICATE = "1 = 1", SQL_PATTERNS, SAFE_WHERE_OPERATORS, warnedSqlFragmentContexts, queryCache, reorderCache, REORDER_CACHE_MAX = 500;
16819
+ var SQL_PATTERNS, SAFE_WHERE_OPERATORS, warnedSqlFragmentContexts, queryCache, reorderCache, REORDER_CACHE_MAX = 500;
16734
16820
  var init_client = __esm(() => {
16735
16821
  init_config();
16736
16822
  init_db();
@@ -19465,7 +19551,7 @@ var init_migrate = __esm(() => {
19465
19551
  init_src2();
19466
19552
  init_introspect_db();
19467
19553
  init_workspace();
19468
- NON_TRANSACTIONAL_SQL = /\bCONCURRENTLY\b|^\s*VACUUM\b/im;
19554
+ NON_TRANSACTIONAL_SQL = /\bCONCURRENTLY\b|^\s*VACUUM\b|^\s*BEGIN\s*(?:;|TRANSACTION\b)|^\s*PRAGMA\s+foreign_keys\b/im;
19469
19555
  });
19470
19556
 
19471
19557
  // src/actions/migrate-generate.ts
@@ -20524,6 +20610,135 @@ var init_actions = __esm(() => {
20524
20610
  init_wait_ready();
20525
20611
  });
20526
20612
 
20613
+ // src/faker-compat.ts
20614
+ function asStringOptions(value) {
20615
+ if (typeof value === "number")
20616
+ return { length: value };
20617
+ return value;
20618
+ }
20619
+ function asNumberOptions(value) {
20620
+ if (typeof value === "number")
20621
+ return { max: value };
20622
+ return value;
20623
+ }
20624
+ function callable(value) {
20625
+ return typeof value === "function";
20626
+ }
20627
+ function flattened(name) {
20628
+ return name.replace(/[A-Z]/g, (letter) => letter.toLowerCase());
20629
+ }
20630
+ function withFractionDigits(value, options) {
20631
+ if (!options || typeof options.fractionDigits !== "number")
20632
+ return value;
20633
+ return Number(value.toFixed(options.fractionDigits));
20634
+ }
20635
+ function compatModule(underlying, overrides = {}) {
20636
+ if (!underlying)
20637
+ return overrides;
20638
+ return new Proxy(underlying, {
20639
+ get(target, property) {
20640
+ if (property in overrides)
20641
+ return overrides[property];
20642
+ const direct = target[property];
20643
+ if (direct !== undefined)
20644
+ return direct;
20645
+ const fallback = target[flattened(property)];
20646
+ if (callable(fallback))
20647
+ return fallback.bind(target);
20648
+ return;
20649
+ }
20650
+ });
20651
+ }
20652
+ function createFakerCompatLayer(underlying) {
20653
+ const pick = (items) => {
20654
+ const list = Array.isArray(items) ? items : [];
20655
+ const index = Math.floor(Math.random() * list.length);
20656
+ return list[index];
20657
+ };
20658
+ const strings = () => underlying.string;
20659
+ const numbers = () => underlying.number;
20660
+ const random = () => underlying.random;
20661
+ const compatString = compatModule(underlying.string, {
20662
+ alpha: (options) => strings()?.alpha?.(asStringOptions(options)),
20663
+ alphanumeric: (options) => strings()?.alphanumeric?.(asStringOptions(options)),
20664
+ numeric: (options) => strings()?.numeric?.(asStringOptions(options)),
20665
+ sample: (options) => strings()?.sample?.(asStringOptions(options)),
20666
+ hexadecimal: (options) => strings()?.hexadecimal?.(asStringOptions(options))
20667
+ });
20668
+ const compatNumber = compatModule(underlying.number, {
20669
+ int: (options) => numbers()?.int?.(asNumberOptions(options)),
20670
+ float: (options) => {
20671
+ const parsed = asNumberOptions(options);
20672
+ return withFractionDigits(Number(numbers()?.float?.(parsed) ?? 0), parsed);
20673
+ }
20674
+ });
20675
+ const compatHelpers = compatModule(underlying.helpers, {
20676
+ arrayElement: pick,
20677
+ arrayElements: (items, count) => {
20678
+ const list = [...Array.isArray(items) ? items : []];
20679
+ const wanted = typeof count === "number" ? count : Math.max(1, Math.floor(Math.random() * list.length));
20680
+ for (let at = list.length - 1;at > 0; at -= 1) {
20681
+ const swap = Math.floor(Math.random() * (at + 1));
20682
+ [list[at], list[swap]] = [list[swap], list[at]];
20683
+ }
20684
+ return list.slice(0, Math.min(wanted, list.length));
20685
+ },
20686
+ shuffle: (items) => {
20687
+ const list = [...Array.isArray(items) ? items : []];
20688
+ for (let at = list.length - 1;at > 0; at -= 1) {
20689
+ const swap = Math.floor(Math.random() * (at + 1));
20690
+ [list[at], list[swap]] = [list[swap], list[at]];
20691
+ }
20692
+ return list;
20693
+ },
20694
+ maybe: (produce, options) => {
20695
+ const probability = typeof options?.probability === "number" ? options.probability : 0.5;
20696
+ return Math.random() < probability ? produce() : undefined;
20697
+ },
20698
+ slugify: (text) => String(text ?? "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "")
20699
+ });
20700
+ const compatDatatype = {
20701
+ boolean: (options) => {
20702
+ const probability = typeof options === "number" ? options : options?.probability;
20703
+ if (typeof probability === "number")
20704
+ return Math.random() < probability;
20705
+ return Boolean(random()?.boolean?.());
20706
+ },
20707
+ number: (options) => Number(numbers()?.int?.(asNumberOptions(options)) ?? 0),
20708
+ float: (options) => withFractionDigits(Number(numbers()?.float?.(options) ?? 0), options),
20709
+ uuid: () => crypto.randomUUID(),
20710
+ string: (length) => String(strings()?.alphanumeric?.({ length: length ?? 10 }) ?? ""),
20711
+ hexadecimal: (options) => String(strings()?.hexadecimal?.(asStringOptions(options)) ?? ""),
20712
+ array: (length) => Array.from({ length: length ?? 3 }, () => strings()?.alphanumeric?.({ length: 8 })),
20713
+ json: () => JSON.stringify({ value: strings()?.alphanumeric?.({ length: 8 }) })
20714
+ };
20715
+ const modules = {
20716
+ string: compatString,
20717
+ number: compatNumber,
20718
+ helpers: compatHelpers,
20719
+ datatype: compatDatatype,
20720
+ location: compatModule(underlying.address),
20721
+ address: compatModule(underlying.address),
20722
+ vehicle: compatModule(underlying.vehicle, {
20723
+ vrm: () => underlying.vehicle?.registration?.()
20724
+ }),
20725
+ company: compatModule(underlying.company, {
20726
+ catchPhrase: () => underlying.company?.catchphrase?.(),
20727
+ buzzPhrase: () => underlying.company?.bs?.() ?? underlying.company?.buzzword?.()
20728
+ })
20729
+ };
20730
+ return new Proxy(underlying, {
20731
+ get(target, property) {
20732
+ if (property in modules)
20733
+ return modules[property];
20734
+ const direct = target[property];
20735
+ if (direct && typeof direct === "object")
20736
+ return compatModule(direct);
20737
+ return direct;
20738
+ }
20739
+ });
20740
+ }
20741
+
20527
20742
  // src/relation-utils.ts
20528
20743
  function normalizeRelationEntry(entry) {
20529
20744
  if (typeof entry === "string")
@@ -26231,9 +26446,8 @@ class ModelQueryBuilder {
26231
26446
  return this._withRelations;
26232
26447
  }
26233
26448
  buildWhereClauses(params) {
26234
- const clauses = [];
26235
- for (let i2 = 0;i2 < this._wheres.length; i2++) {
26236
- const w = this._wheres[i2];
26449
+ const terms = [];
26450
+ for (const w of this._wheres) {
26237
26451
  let clause;
26238
26452
  if (w.raw) {
26239
26453
  clause = w.raw;
@@ -26244,15 +26458,15 @@ class ModelQueryBuilder {
26244
26458
  clause = w.operator === "=" ? `${col} IS NULL` : `${col} IS NOT NULL`;
26245
26459
  } else if (w.operator === "in" || w.operator === "not in") {
26246
26460
  const arr = w.value;
26247
- clause = `${sqlColumn(w.column)} ${w.operator.toUpperCase()} (${arr.map(() => "?").join(", ")})`;
26461
+ clause = renderInPredicate(sqlColumn(w.column), arr, w.operator === "not in", arr.map(() => "?").join(", "));
26248
26462
  params.push(...arr);
26249
26463
  } else {
26250
26464
  clause = `${sqlColumn(w.column)} ${w.operator} ?`;
26251
26465
  params.push(w.value);
26252
26466
  }
26253
- clauses.push(i2 === 0 ? clause : `${w.boolean.toUpperCase()} ${clause}`);
26467
+ terms.push({ conn: w.boolean === "or" ? "OR" : "AND", sql: clause });
26254
26468
  }
26255
- return clauses.join(" ");
26469
+ return renderWhereTerms(terms);
26256
26470
  }
26257
26471
  softDeleteClause() {
26258
26472
  if (this._trashed === "include" || !softDeletesEnabled(this._definition))
@@ -26844,27 +27058,6 @@ async function createTableFromModel(definition) {
26844
27058
  }
26845
27059
  await exec.run(`CREATE TABLE IF NOT EXISTS ${definition.table} (${columns.join(", ")})`, []);
26846
27060
  }
26847
- function createFakerCompatLayer(underlying) {
26848
- return new Proxy(underlying, {
26849
- get(target, prop) {
26850
- if (prop === "location")
26851
- return target.address;
26852
- if (prop === "datatype") {
26853
- const rng = target.random;
26854
- const num = target.number;
26855
- const str = target.string;
26856
- return {
26857
- boolean: () => rng?.boolean?.(),
26858
- number: (opts) => num?.int?.(opts),
26859
- float: (opts) => num?.float?.(opts),
26860
- uuid: () => crypto.randomUUID(),
26861
- string: (length) => str?.alphanumeric?.(length ?? 10)
26862
- };
26863
- }
26864
- return target[prop];
26865
- }
26866
- });
26867
- }
26868
27061
  async function seedModel(definition, count, faker) {
26869
27062
  const exec = getExecutor();
26870
27063
  const seeder = definition.traits?.useSeeder;
@@ -29912,6 +30105,7 @@ export {
29912
30105
  createQueryBuilder,
29913
30106
  createModel,
29914
30107
  createMigrationDriver,
30108
+ createFakerCompatLayer,
29915
30109
  createDynamoDBToolingAdapter,
29916
30110
  createDynamoDBQueryBuilder,
29917
30111
  createDynamoDBItemBuilder,