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