bun-query-builder 0.1.31 → 0.1.33
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 +169 -89
- package/dist/client.d.ts +31 -0
- package/dist/src/index.js +162 -81
- package/package.json +1 -1
package/dist/bin/cli.js
CHANGED
|
@@ -12344,6 +12344,14 @@ function* iterateAllPivots(meta, options = {}) {
|
|
|
12344
12344
|
function isRawExpression(expr) {
|
|
12345
12345
|
return typeof expr === "object" && expr !== null && "raw" in expr && typeof expr.raw === "string";
|
|
12346
12346
|
}
|
|
12347
|
+
function raw(strings, ...values) {
|
|
12348
|
+
if (typeof strings === "string")
|
|
12349
|
+
return { raw: strings };
|
|
12350
|
+
let out = strings[0];
|
|
12351
|
+
for (let i = 0;i < values.length; i++)
|
|
12352
|
+
out += formatSubqueryValue(values[i]) + strings[i + 1];
|
|
12353
|
+
return { raw: out };
|
|
12354
|
+
}
|
|
12347
12355
|
function quoteInsertIdent(id) {
|
|
12348
12356
|
return config5.dialect === "mysql" ? `\`${id.replace(/`/g, "``")}\`` : `"${id.replace(/"/g, '""')}"`;
|
|
12349
12357
|
}
|
|
@@ -12412,13 +12420,31 @@ function validateQualifiedIdentifier(value, context) {
|
|
|
12412
12420
|
throw new TypeError(`[query-builder] ${context}: identifier segment '${part}' contains characters outside [A-Za-z0-9_]`);
|
|
12413
12421
|
}
|
|
12414
12422
|
}
|
|
12415
|
-
function
|
|
12416
|
-
if (fragment === null || fragment === undefined) {
|
|
12417
|
-
throw new TypeError(`[query-builder] ${context}: fragment must be a SqlFragment, got ${fragment}`);
|
|
12418
|
-
}
|
|
12423
|
+
function renderRawFragment(fragment, context) {
|
|
12419
12424
|
if (typeof fragment === "string") {
|
|
12420
12425
|
warnOnceBareSqlFragment(context);
|
|
12426
|
+
return fragment;
|
|
12421
12427
|
}
|
|
12428
|
+
if (fragment === null || fragment === undefined)
|
|
12429
|
+
throw new TypeError(`[query-builder] ${context}: fragment must be a SqlFragment, got ${fragment}`);
|
|
12430
|
+
if (isRawExpression(fragment))
|
|
12431
|
+
return fragment.raw;
|
|
12432
|
+
if (typeof fragment === "object") {
|
|
12433
|
+
const f = fragment;
|
|
12434
|
+
if (typeof f.raw === "string")
|
|
12435
|
+
return f.raw;
|
|
12436
|
+
if (typeof f.raw === "function") {
|
|
12437
|
+
const r = f.raw();
|
|
12438
|
+
if (typeof r === "string")
|
|
12439
|
+
return r;
|
|
12440
|
+
}
|
|
12441
|
+
if (typeof f.sql === "string")
|
|
12442
|
+
return f.sql;
|
|
12443
|
+
const s = String(fragment);
|
|
12444
|
+
if (s !== "[object Object]" && s !== "[object Promise]")
|
|
12445
|
+
return s;
|
|
12446
|
+
}
|
|
12447
|
+
throw new TypeError(`[query-builder] ${context}: cannot render this value as a SQL fragment. ` + `A Bun \`sql\`...\`\` query object cannot be converted to SQL text \u2014 pass a ` + `string, or use the exported \`raw\` helper: raw\`count(*) as c\` / raw('age > 18').`);
|
|
12422
12448
|
}
|
|
12423
12449
|
function warnOnceBareSqlFragment(context) {
|
|
12424
12450
|
if (warnedSqlFragmentContexts.has(context))
|
|
@@ -12809,7 +12835,7 @@ function createQueryBuilder(state) {
|
|
|
12809
12835
|
};
|
|
12810
12836
|
const addWhereText = (prefix, clause) => {
|
|
12811
12837
|
const hasWhere = SQL_PATTERNS.WHERE.test(text);
|
|
12812
|
-
const p = hasWhere ?
|
|
12838
|
+
const p = !hasWhere ? "WHERE" : prefix === "WHERE" ? "AND" : prefix;
|
|
12813
12839
|
text = `${text} ${p} ${clause}`;
|
|
12814
12840
|
};
|
|
12815
12841
|
const appendSetOp = (op, other) => {
|
|
@@ -13093,12 +13119,12 @@ function createQueryBuilder(state) {
|
|
|
13093
13119
|
return this;
|
|
13094
13120
|
},
|
|
13095
13121
|
selectRaw(fragment) {
|
|
13096
|
-
|
|
13122
|
+
const frag = renderRawFragment(fragment, "selectRaw(fragment)");
|
|
13097
13123
|
const fromIdx = text.indexOf(" FROM ");
|
|
13098
13124
|
if (fromIdx !== -1) {
|
|
13099
|
-
text = `${text.substring(0, fromIdx)}, ${
|
|
13125
|
+
text = `${text.substring(0, fromIdx)}, ${frag}${text.substring(fromIdx)}`;
|
|
13100
13126
|
} else {
|
|
13101
|
-
text += `, ${
|
|
13127
|
+
text += `, ${frag}`;
|
|
13102
13128
|
}
|
|
13103
13129
|
built = null;
|
|
13104
13130
|
return this;
|
|
@@ -13211,12 +13237,15 @@ function createQueryBuilder(state) {
|
|
|
13211
13237
|
if (cols.length === 0)
|
|
13212
13238
|
return this;
|
|
13213
13239
|
const rendered = cols.map(renderSelectColumn);
|
|
13240
|
+
const distinctMatch = /^SELECT\s+(DISTINCT(?:\s+ON\s+\([^)]*\))?\s+)/i.exec(text);
|
|
13241
|
+
const distinctPrefix = distinctMatch ? distinctMatch[1] : "";
|
|
13214
13242
|
const fromIndex = text.indexOf(" FROM ");
|
|
13215
13243
|
if (fromIndex !== -1) {
|
|
13216
|
-
text = `SELECT ${rendered.join(", ")}${text.substring(fromIndex)}`;
|
|
13244
|
+
text = `SELECT ${distinctPrefix}${rendered.join(", ")}${text.substring(fromIndex)}`;
|
|
13217
13245
|
} else {
|
|
13218
|
-
text = `SELECT ${rendered.join(", ")} FROM ${table}`;
|
|
13246
|
+
text = `SELECT ${distinctPrefix}${rendered.join(", ")} FROM ${table}`;
|
|
13219
13247
|
}
|
|
13248
|
+
built = null;
|
|
13220
13249
|
return this;
|
|
13221
13250
|
},
|
|
13222
13251
|
addSelect(...columns2) {
|
|
@@ -13964,87 +13993,71 @@ function createQueryBuilder(state) {
|
|
|
13964
13993
|
return this;
|
|
13965
13994
|
},
|
|
13966
13995
|
whereLike(column, pattern, caseSensitive = false) {
|
|
13967
|
-
const expr = caseSensitive ? sql`${sql(String(column))} LIKE ${pattern}` : sql`LOWER(${sql(String(column))}) LIKE LOWER(${pattern})`;
|
|
13968
|
-
built = sql`${ensureBuilt()} WHERE ${expr}`;
|
|
13969
13996
|
const ph = getPlaceholder(whereParams.length + 1);
|
|
13970
13997
|
addWhereText("WHERE", `${caseSensitive ? String(column) : `LOWER(${String(column)})`} LIKE ${caseSensitive ? ph : `LOWER(${ph})`}`);
|
|
13971
13998
|
whereParams.push(pattern);
|
|
13999
|
+
built = null;
|
|
13972
14000
|
return this;
|
|
13973
14001
|
},
|
|
13974
14002
|
whereILike(column, pattern) {
|
|
13975
14003
|
const ph = getPlaceholder(whereParams.length + 1);
|
|
13976
|
-
if (config5.dialect === "postgres")
|
|
13977
|
-
built = sql`${ensureBuilt()} WHERE ${sql(String(column))} ILIKE ${pattern}`;
|
|
14004
|
+
if (config5.dialect === "postgres")
|
|
13978
14005
|
addWhereText("WHERE", `${String(column)} ILIKE ${ph}`);
|
|
13979
|
-
|
|
13980
|
-
const expr = sql`LOWER(${sql(String(column))}) LIKE LOWER(${pattern})`;
|
|
13981
|
-
built = sql`${ensureBuilt()} WHERE ${expr}`;
|
|
14006
|
+
else
|
|
13982
14007
|
addWhereText("WHERE", `LOWER(${String(column)}) LIKE LOWER(${ph})`);
|
|
13983
|
-
}
|
|
13984
14008
|
whereParams.push(pattern);
|
|
14009
|
+
built = null;
|
|
13985
14010
|
return this;
|
|
13986
14011
|
},
|
|
13987
14012
|
orWhereLike(column, pattern, caseSensitive = false) {
|
|
13988
|
-
const expr = caseSensitive ? sql`${sql(String(column))} LIKE ${pattern}` : sql`LOWER(${sql(String(column))}) LIKE LOWER(${pattern})`;
|
|
13989
|
-
built = sql`${ensureBuilt()} OR ${expr}`;
|
|
13990
14013
|
const ph = getPlaceholder(whereParams.length + 1);
|
|
13991
14014
|
addWhereText("OR", `${caseSensitive ? String(column) : `LOWER(${String(column)})`} LIKE ${caseSensitive ? ph : `LOWER(${ph})`}`);
|
|
13992
14015
|
whereParams.push(pattern);
|
|
14016
|
+
built = null;
|
|
13993
14017
|
return this;
|
|
13994
14018
|
},
|
|
13995
14019
|
orWhereILike(column, pattern) {
|
|
13996
14020
|
const ph = getPlaceholder(whereParams.length + 1);
|
|
13997
|
-
if (config5.dialect === "postgres")
|
|
13998
|
-
built = sql`${ensureBuilt()} OR ${sql(String(column))} ILIKE ${pattern}`;
|
|
14021
|
+
if (config5.dialect === "postgres")
|
|
13999
14022
|
addWhereText("OR", `${String(column)} ILIKE ${ph}`);
|
|
14000
|
-
|
|
14001
|
-
const expr = sql`LOWER(${sql(String(column))}) LIKE LOWER(${pattern})`;
|
|
14002
|
-
built = sql`${ensureBuilt()} OR ${expr}`;
|
|
14023
|
+
else
|
|
14003
14024
|
addWhereText("OR", `LOWER(${String(column)}) LIKE LOWER(${ph})`);
|
|
14004
|
-
}
|
|
14005
14025
|
whereParams.push(pattern);
|
|
14026
|
+
built = null;
|
|
14006
14027
|
return this;
|
|
14007
14028
|
},
|
|
14008
14029
|
whereNotLike(column, pattern, caseSensitive = false) {
|
|
14009
|
-
const expr = caseSensitive ? sql`${sql(String(column))} NOT LIKE ${pattern}` : sql`LOWER(${sql(String(column))}) NOT LIKE LOWER(${pattern})`;
|
|
14010
|
-
built = sql`${ensureBuilt()} WHERE ${expr}`;
|
|
14011
14030
|
const ph = getPlaceholder(whereParams.length + 1);
|
|
14012
14031
|
addWhereText("WHERE", `${caseSensitive ? String(column) : `LOWER(${String(column)})`} NOT LIKE ${caseSensitive ? ph : `LOWER(${ph})`}`);
|
|
14013
14032
|
whereParams.push(pattern);
|
|
14033
|
+
built = null;
|
|
14014
14034
|
return this;
|
|
14015
14035
|
},
|
|
14016
14036
|
whereNotILike(column, pattern) {
|
|
14017
14037
|
const ph = getPlaceholder(whereParams.length + 1);
|
|
14018
|
-
if (config5.dialect === "postgres")
|
|
14019
|
-
built = sql`${ensureBuilt()} WHERE ${sql(String(column))} NOT ILIKE ${pattern}`;
|
|
14038
|
+
if (config5.dialect === "postgres")
|
|
14020
14039
|
addWhereText("WHERE", `${String(column)} NOT ILIKE ${ph}`);
|
|
14021
|
-
|
|
14022
|
-
const expr = sql`LOWER(${sql(String(column))}) NOT LIKE LOWER(${pattern})`;
|
|
14023
|
-
built = sql`${ensureBuilt()} WHERE ${expr}`;
|
|
14040
|
+
else
|
|
14024
14041
|
addWhereText("WHERE", `LOWER(${String(column)}) NOT LIKE LOWER(${ph})`);
|
|
14025
|
-
}
|
|
14026
14042
|
whereParams.push(pattern);
|
|
14043
|
+
built = null;
|
|
14027
14044
|
return this;
|
|
14028
14045
|
},
|
|
14029
14046
|
orWhereNotLike(column, pattern, caseSensitive = false) {
|
|
14030
|
-
const expr = caseSensitive ? sql`${sql(String(column))} NOT LIKE ${pattern}` : sql`LOWER(${sql(String(column))}) NOT LIKE LOWER(${pattern})`;
|
|
14031
|
-
built = sql`${ensureBuilt()} OR ${expr}`;
|
|
14032
14047
|
const ph = getPlaceholder(whereParams.length + 1);
|
|
14033
14048
|
addWhereText("OR", `${caseSensitive ? String(column) : `LOWER(${String(column)})`} NOT LIKE ${caseSensitive ? ph : `LOWER(${ph})`}`);
|
|
14034
14049
|
whereParams.push(pattern);
|
|
14050
|
+
built = null;
|
|
14035
14051
|
return this;
|
|
14036
14052
|
},
|
|
14037
14053
|
orWhereNotILike(column, pattern) {
|
|
14038
14054
|
const ph = getPlaceholder(whereParams.length + 1);
|
|
14039
|
-
if (config5.dialect === "postgres")
|
|
14040
|
-
built = sql`${ensureBuilt()} OR ${sql(String(column))} NOT ILIKE ${pattern}`;
|
|
14055
|
+
if (config5.dialect === "postgres")
|
|
14041
14056
|
addWhereText("OR", `${String(column)} NOT ILIKE ${ph}`);
|
|
14042
|
-
|
|
14043
|
-
const expr = sql`LOWER(${sql(String(column))}) NOT LIKE LOWER(${pattern})`;
|
|
14044
|
-
built = sql`${ensureBuilt()} OR ${expr}`;
|
|
14057
|
+
else
|
|
14045
14058
|
addWhereText("OR", `LOWER(${String(column)}) NOT LIKE LOWER(${ph})`);
|
|
14046
|
-
}
|
|
14047
14059
|
whereParams.push(pattern);
|
|
14060
|
+
built = null;
|
|
14048
14061
|
return this;
|
|
14049
14062
|
},
|
|
14050
14063
|
whereAny(cols, op, value) {
|
|
@@ -14105,9 +14118,9 @@ function createQueryBuilder(state) {
|
|
|
14105
14118
|
return this;
|
|
14106
14119
|
},
|
|
14107
14120
|
whereRaw(fragment) {
|
|
14108
|
-
|
|
14121
|
+
const frag = renderRawFragment(fragment, "whereRaw(fragment)");
|
|
14109
14122
|
const keyword = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
|
|
14110
|
-
text += ` ${keyword} ${
|
|
14123
|
+
text += ` ${keyword} ${frag}`;
|
|
14111
14124
|
built = null;
|
|
14112
14125
|
return this;
|
|
14113
14126
|
},
|
|
@@ -14473,8 +14486,8 @@ function createQueryBuilder(state) {
|
|
|
14473
14486
|
return this;
|
|
14474
14487
|
},
|
|
14475
14488
|
groupByRaw(fragment) {
|
|
14476
|
-
|
|
14477
|
-
text = SQL_PATTERNS.GROUP_BY.test(text) ? `${text}, ${
|
|
14489
|
+
const frag = renderRawFragment(fragment, "groupByRaw(fragment)");
|
|
14490
|
+
text = SQL_PATTERNS.GROUP_BY.test(text) ? `${text}, ${frag}` : `${text} GROUP BY ${frag}`;
|
|
14478
14491
|
built = null;
|
|
14479
14492
|
return this;
|
|
14480
14493
|
},
|
|
@@ -14506,15 +14519,15 @@ function createQueryBuilder(state) {
|
|
|
14506
14519
|
return this;
|
|
14507
14520
|
},
|
|
14508
14521
|
havingRaw(fragment) {
|
|
14509
|
-
|
|
14522
|
+
const frag = renderRawFragment(fragment, "havingRaw(fragment)");
|
|
14510
14523
|
const kw = /\bHAVING\b/i.test(text) ? "AND" : "HAVING";
|
|
14511
|
-
text += ` ${kw} ${
|
|
14524
|
+
text += ` ${kw} ${frag}`;
|
|
14512
14525
|
built = null;
|
|
14513
14526
|
return this;
|
|
14514
14527
|
},
|
|
14515
14528
|
orderByRaw(fragment) {
|
|
14516
|
-
|
|
14517
|
-
text = SQL_PATTERNS.ORDER_BY.test(text) ? `${text}, ${
|
|
14529
|
+
const frag = renderRawFragment(fragment, "orderByRaw(fragment)");
|
|
14530
|
+
text = SQL_PATTERNS.ORDER_BY.test(text) ? `${text}, ${frag}` : `${text} ORDER BY ${frag}`;
|
|
14518
14531
|
built = null;
|
|
14519
14532
|
return this;
|
|
14520
14533
|
},
|
|
@@ -14644,19 +14657,21 @@ function createQueryBuilder(state) {
|
|
|
14644
14657
|
q = sql`${q} ORDER BY ${sql(String(column))} ${direction === "asc" ? sql`ASC` : sql`DESC`} LIMIT ${perPage + 1}`;
|
|
14645
14658
|
}
|
|
14646
14659
|
const rows = await runWithHooks(q, "select", { signal: abortSignal, timeoutMs });
|
|
14647
|
-
const
|
|
14660
|
+
const hasMore = rows.length > perPage;
|
|
14648
14661
|
const data = rows.slice(0, perPage);
|
|
14662
|
+
const lastRow = data[data.length - 1];
|
|
14663
|
+
const next = hasMore && lastRow ? Array.isArray(column) ? column.map((c) => lastRow[c]) : lastRow[column] : null;
|
|
14649
14664
|
const prevCursor = data.length ? Array.isArray(column) ? column.map((c) => data[0]?.[c]) : data[0]?.[column] : null;
|
|
14650
14665
|
return { data, meta: { perPage, nextCursor: next ?? null, prevCursor } };
|
|
14651
14666
|
},
|
|
14652
14667
|
async chunk(size, handler) {
|
|
14653
14668
|
let page = 1;
|
|
14654
14669
|
while (true) {
|
|
14655
|
-
const { data } = await this.paginate(size, page);
|
|
14670
|
+
const { data, meta: meta2 } = await this.paginate(size, page);
|
|
14656
14671
|
if (data.length === 0)
|
|
14657
14672
|
break;
|
|
14658
14673
|
await handler(data);
|
|
14659
|
-
if (
|
|
14674
|
+
if (page >= meta2.lastPage)
|
|
14660
14675
|
break;
|
|
14661
14676
|
page += 1;
|
|
14662
14677
|
}
|
|
@@ -14697,20 +14712,20 @@ function createQueryBuilder(state) {
|
|
|
14697
14712
|
includeTrashed = true;
|
|
14698
14713
|
onlyTrashed = true;
|
|
14699
14714
|
const softDeleteColumn = config5.softDeletes?.column || "deleted_at";
|
|
14700
|
-
const splice = (
|
|
14701
|
-
const upper =
|
|
14715
|
+
const splice = (raw2, predicate2) => {
|
|
14716
|
+
const upper = raw2.toUpperCase();
|
|
14702
14717
|
let depth = 0;
|
|
14703
|
-
for (let i = 0;i <
|
|
14704
|
-
const c =
|
|
14718
|
+
for (let i = 0;i < raw2.length; i++) {
|
|
14719
|
+
const c = raw2[i];
|
|
14705
14720
|
if (c === "(")
|
|
14706
14721
|
depth++;
|
|
14707
14722
|
else if (c === ")")
|
|
14708
14723
|
depth--;
|
|
14709
|
-
else if (depth === 0 && upper.substring(i, i + 5) === "WHERE" && (i === 0 || /\s/.test(
|
|
14710
|
-
return `${
|
|
14724
|
+
else if (depth === 0 && upper.substring(i, i + 5) === "WHERE" && (i === 0 || /\s/.test(raw2[i - 1] ?? "")) && /\s/.test(raw2[i + 5] ?? "")) {
|
|
14725
|
+
return `${raw2.substring(0, i)}WHERE ${predicate2} AND ${raw2.substring(i + 6)}`;
|
|
14711
14726
|
}
|
|
14712
14727
|
}
|
|
14713
|
-
return `${
|
|
14728
|
+
return `${raw2} WHERE ${predicate2}`;
|
|
14714
14729
|
};
|
|
14715
14730
|
const predicate = `${table}.${softDeleteColumn} IS NOT NULL`;
|
|
14716
14731
|
text = splice(text, predicate);
|
|
@@ -15014,13 +15029,13 @@ function createQueryBuilder(state) {
|
|
|
15014
15029
|
const cacheKey = `${String(table)}|${prop}`;
|
|
15015
15030
|
let chosen = dynamicWhereColumnCache.get(cacheKey);
|
|
15016
15031
|
if (chosen === undefined) {
|
|
15017
|
-
const
|
|
15018
|
-
if (!
|
|
15032
|
+
const raw2 = prop.replace(/^(?:or|and)?where/i, "");
|
|
15033
|
+
if (!raw2) {
|
|
15019
15034
|
dynamicWhereColumnCache.set(cacheKey, "");
|
|
15020
15035
|
chosen = "";
|
|
15021
15036
|
} else {
|
|
15022
|
-
const lowerFirst =
|
|
15023
|
-
const snake =
|
|
15037
|
+
const lowerFirst = raw2.charAt(0).toLowerCase() + raw2.slice(1);
|
|
15038
|
+
const snake = raw2.replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, "");
|
|
15024
15039
|
const available = schema ? Object.keys(schema[String(table)]?.columns ?? {}) : [];
|
|
15025
15040
|
chosen = [snake, lowerFirst, lowerFirst.toLowerCase()].find((n) => available.includes(n)) ?? snake;
|
|
15026
15041
|
dynamicWhereColumnCache.set(cacheKey, chosen);
|
|
@@ -15410,6 +15425,10 @@ function createQueryBuilder(state) {
|
|
|
15410
15425
|
returning(...cols) {
|
|
15411
15426
|
const returningSql = `${sqlText} RETURNING ${cols.join(", ")}`;
|
|
15412
15427
|
const q = _sql.unsafe(returningSql, params);
|
|
15428
|
+
const runFirst = async () => {
|
|
15429
|
+
const rows = await runWithHooks(q, "insert");
|
|
15430
|
+
return Array.isArray(rows) ? rows[0] : rows;
|
|
15431
|
+
};
|
|
15413
15432
|
return {
|
|
15414
15433
|
where: () => this,
|
|
15415
15434
|
andWhere: () => this,
|
|
@@ -15418,7 +15437,22 @@ function createQueryBuilder(state) {
|
|
|
15418
15437
|
limit: () => this,
|
|
15419
15438
|
offset: () => this,
|
|
15420
15439
|
toSQL: () => makeExecutableQuery(q, returningSql),
|
|
15421
|
-
execute: () => runWithHooks(q, "insert")
|
|
15440
|
+
execute: () => runWithHooks(q, "insert"),
|
|
15441
|
+
get: () => runWithHooks(q, "insert"),
|
|
15442
|
+
first: runFirst,
|
|
15443
|
+
executeTakeFirst: runFirst,
|
|
15444
|
+
async firstOrFail() {
|
|
15445
|
+
const row = await runFirst();
|
|
15446
|
+
if (!row)
|
|
15447
|
+
throw new Error("Insert with RETURNING returned no rows");
|
|
15448
|
+
return row;
|
|
15449
|
+
},
|
|
15450
|
+
async executeTakeFirstOrThrow() {
|
|
15451
|
+
const row = await runFirst();
|
|
15452
|
+
if (!row)
|
|
15453
|
+
throw new Error("Insert with RETURNING returned no rows");
|
|
15454
|
+
return row;
|
|
15455
|
+
}
|
|
15422
15456
|
};
|
|
15423
15457
|
},
|
|
15424
15458
|
toSQL() {
|
|
@@ -15457,19 +15491,27 @@ function createQueryBuilder(state) {
|
|
|
15457
15491
|
returningAll() {
|
|
15458
15492
|
const returningSql = `${sqlText} RETURNING *`;
|
|
15459
15493
|
const q = _sql.unsafe(returningSql, params);
|
|
15494
|
+
const runFirst = async () => {
|
|
15495
|
+
const result = await runWithHooks(q, "insert");
|
|
15496
|
+
return Array.isArray(result) ? result[0] : result;
|
|
15497
|
+
};
|
|
15460
15498
|
return {
|
|
15461
15499
|
toSQL: () => makeExecutableQuery(q, returningSql),
|
|
15462
15500
|
execute: () => runWithHooks(q, "insert"),
|
|
15463
|
-
|
|
15464
|
-
|
|
15465
|
-
|
|
15501
|
+
get: () => runWithHooks(q, "insert"),
|
|
15502
|
+
first: runFirst,
|
|
15503
|
+
executeTakeFirst: runFirst,
|
|
15504
|
+
async firstOrFail() {
|
|
15505
|
+
const row = await runFirst();
|
|
15506
|
+
if (!row)
|
|
15507
|
+
throw new Error("Insert with RETURNING returned no rows");
|
|
15508
|
+
return row;
|
|
15466
15509
|
},
|
|
15467
15510
|
async executeTakeFirstOrThrow() {
|
|
15468
|
-
const
|
|
15469
|
-
|
|
15470
|
-
|
|
15471
|
-
|
|
15472
|
-
return first;
|
|
15511
|
+
const row = await runFirst();
|
|
15512
|
+
if (!row)
|
|
15513
|
+
throw new Error("Insert with RETURNING returned no rows");
|
|
15514
|
+
return row;
|
|
15473
15515
|
}
|
|
15474
15516
|
};
|
|
15475
15517
|
}
|
|
@@ -15533,6 +15575,10 @@ function createQueryBuilder(state) {
|
|
|
15533
15575
|
returning(...cols) {
|
|
15534
15576
|
const retText = `${sqlText} RETURNING ${cols.join(", ")}`;
|
|
15535
15577
|
const q = params.length > 0 ? _sql.unsafe(retText, params) : _sql.unsafe(retText);
|
|
15578
|
+
const runFirst = async () => {
|
|
15579
|
+
const rows = await runWithHooks(q, "update");
|
|
15580
|
+
return Array.isArray(rows) ? rows[0] : rows;
|
|
15581
|
+
};
|
|
15536
15582
|
const obj = {
|
|
15537
15583
|
where: () => obj,
|
|
15538
15584
|
andWhere: () => obj,
|
|
@@ -15541,7 +15587,22 @@ function createQueryBuilder(state) {
|
|
|
15541
15587
|
limit: () => obj,
|
|
15542
15588
|
offset: () => obj,
|
|
15543
15589
|
toSQL: () => makeExecutableQuery(q, retText),
|
|
15544
|
-
execute: () => runWithHooks(q, "update")
|
|
15590
|
+
execute: () => runWithHooks(q, "update"),
|
|
15591
|
+
get: () => runWithHooks(q, "update"),
|
|
15592
|
+
first: runFirst,
|
|
15593
|
+
executeTakeFirst: runFirst,
|
|
15594
|
+
async firstOrFail() {
|
|
15595
|
+
const row = await runFirst();
|
|
15596
|
+
if (!row)
|
|
15597
|
+
throw new Error("Update with RETURNING returned no rows");
|
|
15598
|
+
return row;
|
|
15599
|
+
},
|
|
15600
|
+
async executeTakeFirstOrThrow() {
|
|
15601
|
+
const row = await runFirst();
|
|
15602
|
+
if (!row)
|
|
15603
|
+
throw new Error("Update with RETURNING returned no rows");
|
|
15604
|
+
return row;
|
|
15605
|
+
}
|
|
15545
15606
|
};
|
|
15546
15607
|
return obj;
|
|
15547
15608
|
},
|
|
@@ -15640,6 +15701,10 @@ function createQueryBuilder(state) {
|
|
|
15640
15701
|
returning(...cols) {
|
|
15641
15702
|
const retText = `${sqlText} RETURNING ${cols.join(", ")}`;
|
|
15642
15703
|
const q = delParams.length > 0 ? _sql.unsafe(retText, delParams) : _sql.unsafe(retText);
|
|
15704
|
+
const runFirst = async () => {
|
|
15705
|
+
const rows = await runWithHooks(q, "delete");
|
|
15706
|
+
return Array.isArray(rows) ? rows[0] : rows;
|
|
15707
|
+
};
|
|
15643
15708
|
const obj = {
|
|
15644
15709
|
where: () => obj,
|
|
15645
15710
|
andWhere: () => obj,
|
|
@@ -15648,7 +15713,22 @@ function createQueryBuilder(state) {
|
|
|
15648
15713
|
limit: () => obj,
|
|
15649
15714
|
offset: () => obj,
|
|
15650
15715
|
toSQL: () => makeExecutableQuery(q, retText),
|
|
15651
|
-
execute: () => runWithHooks(q, "delete")
|
|
15716
|
+
execute: () => runWithHooks(q, "delete"),
|
|
15717
|
+
get: () => runWithHooks(q, "delete"),
|
|
15718
|
+
first: runFirst,
|
|
15719
|
+
executeTakeFirst: runFirst,
|
|
15720
|
+
async firstOrFail() {
|
|
15721
|
+
const row = await runFirst();
|
|
15722
|
+
if (!row)
|
|
15723
|
+
throw new Error("Delete with RETURNING returned no rows");
|
|
15724
|
+
return row;
|
|
15725
|
+
},
|
|
15726
|
+
async executeTakeFirstOrThrow() {
|
|
15727
|
+
const row = await runFirst();
|
|
15728
|
+
if (!row)
|
|
15729
|
+
throw new Error("Delete with RETURNING returned no rows");
|
|
15730
|
+
return row;
|
|
15731
|
+
}
|
|
15652
15732
|
};
|
|
15653
15733
|
return obj;
|
|
15654
15734
|
},
|
|
@@ -18000,8 +18080,8 @@ function loadPlanSnapshot(workspaceRoot, dialect) {
|
|
|
18000
18080
|
return;
|
|
18001
18081
|
}
|
|
18002
18082
|
try {
|
|
18003
|
-
const
|
|
18004
|
-
const parsed = JSON.parse(
|
|
18083
|
+
const raw2 = readFileSync3(snapshotPath, "utf8");
|
|
18084
|
+
const parsed = JSON.parse(raw2);
|
|
18005
18085
|
if (parsed?.plan && Array.isArray(parsed.plan.tables) && parsed.plan.dialect) {
|
|
18006
18086
|
return parsed.plan;
|
|
18007
18087
|
}
|
|
@@ -18060,8 +18140,8 @@ async function generateMigration(dir, opts = {}) {
|
|
|
18060
18140
|
const statePath = String(opts.state || defaultStatePath);
|
|
18061
18141
|
if (existsSync16(statePath)) {
|
|
18062
18142
|
try {
|
|
18063
|
-
const
|
|
18064
|
-
const parsed = JSON.parse(
|
|
18143
|
+
const raw2 = readFileSync3(statePath, "utf8");
|
|
18144
|
+
const parsed = JSON.parse(raw2);
|
|
18065
18145
|
previous = parsed?.plan && parsed.plan.tables ? parsed.plan : parsed?.tables ? parsed : undefined;
|
|
18066
18146
|
if (previous) {
|
|
18067
18147
|
info("-- Comparing with legacy state file (will migrate to new snapshot format)");
|
|
@@ -24651,10 +24731,10 @@ class BelongsToManyRelationBuilder {
|
|
|
24651
24731
|
hydrateRows(rows) {
|
|
24652
24732
|
const fkParent = this.fkParent;
|
|
24653
24733
|
const fkRelated = this.fkRelated;
|
|
24654
|
-
return rows.map((
|
|
24734
|
+
return rows.map((raw2) => {
|
|
24655
24735
|
const relatedRow = {};
|
|
24656
24736
|
const pivotExtras = {};
|
|
24657
|
-
for (const [k2, v2] of Object.entries(
|
|
24737
|
+
for (const [k2, v2] of Object.entries(raw2)) {
|
|
24658
24738
|
if (k2.startsWith(BTM_RELATED_ALIAS))
|
|
24659
24739
|
relatedRow[k2.slice(BTM_RELATED_ALIAS.length)] = v2;
|
|
24660
24740
|
else if (k2 !== fkParent && k2 !== fkRelated)
|
|
@@ -29120,11 +29200,11 @@ Received ${signal}, cleaning up...`);
|
|
|
29120
29200
|
argsAfterDoubleDashes = argv.slice(doubleDashesIndex + 1);
|
|
29121
29201
|
argv = argv.slice(0, doubleDashesIndex);
|
|
29122
29202
|
}
|
|
29123
|
-
const
|
|
29124
|
-
const parsed = { _:
|
|
29125
|
-
for (const name of Object.keys(
|
|
29203
|
+
const raw2 = parseArgv(argv, mriOptions);
|
|
29204
|
+
const parsed = { _: raw2._ };
|
|
29205
|
+
for (const name of Object.keys(raw2)) {
|
|
29126
29206
|
if (name !== "_") {
|
|
29127
|
-
parsed[camelcaseOptionName(name)] =
|
|
29207
|
+
parsed[camelcaseOptionName(name)] = raw2[name];
|
|
29128
29208
|
}
|
|
29129
29209
|
}
|
|
29130
29210
|
const args = parsed._;
|
|
@@ -29169,11 +29249,11 @@ Received ${signal}, cleaning up...`);
|
|
|
29169
29249
|
if (!isUsage)
|
|
29170
29250
|
return;
|
|
29171
29251
|
const e2 = err;
|
|
29172
|
-
const
|
|
29252
|
+
const raw2 = e2.message ?? "command-line error";
|
|
29173
29253
|
const label = this.name ? `${this.name}: ` : "";
|
|
29174
|
-
const suffix = /--help/.test(
|
|
29254
|
+
const suffix = /--help/.test(raw2) ? "" : `
|
|
29175
29255
|
Run \`${this.name ?? "cli"} --help\` for usage.`;
|
|
29176
|
-
process52.stderr.write(`${label}${
|
|
29256
|
+
process52.stderr.write(`${label}${raw2}${suffix}
|
|
29177
29257
|
`);
|
|
29178
29258
|
process52.exit(e2.exitCode ?? 2);
|
|
29179
29259
|
}
|
|
@@ -30031,7 +30111,7 @@ function getPrefix() {
|
|
|
30031
30111
|
}
|
|
30032
30112
|
var prefix = getPrefix();
|
|
30033
30113
|
// package.json
|
|
30034
|
-
var version2 = "0.1.
|
|
30114
|
+
var version2 = "0.1.33";
|
|
30035
30115
|
|
|
30036
30116
|
// bin/cli.ts
|
|
30037
30117
|
init_actions();
|
package/dist/client.d.ts
CHANGED
|
@@ -2,6 +2,33 @@ import { config } from './config';
|
|
|
2
2
|
import { resetConnection } from './db';
|
|
3
3
|
import type { DatabaseSchema } from './schema';
|
|
4
4
|
import type { SchemaMeta } from './meta';
|
|
5
|
+
/**
|
|
6
|
+
* # `raw`
|
|
7
|
+
*
|
|
8
|
+
* Build a raw SQL fragment for the `*Raw` builder methods (`whereRaw`,
|
|
9
|
+
* `selectRaw`, `orderByRaw`, `groupByRaw`, `havingRaw`) and `select()`.
|
|
10
|
+
*
|
|
11
|
+
* Use this INSTEAD of a Bun `sql\`...\`` tag: a Bun query object cannot be
|
|
12
|
+
* converted back to SQL text (it stringifies to "[object Promise]"), so it
|
|
13
|
+
* silently corrupts the generated SQL. `raw` returns a `{ raw: string }`
|
|
14
|
+
* fragment that the builder renders correctly and that satisfies the
|
|
15
|
+
* `SqlFragment` type (so it passes the bare-string injection guard).
|
|
16
|
+
*
|
|
17
|
+
* Interpolated values in the tagged-template form are SQL-escaped (strings
|
|
18
|
+
* single-quote-doubled, dates → ISO, numbers/booleans/null inlined) — the
|
|
19
|
+
* same escaping the relation-subquery builders use. For user input that must
|
|
20
|
+
* be parameterised, prefer the typed `where(...)` methods over `raw`.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* import { raw } from 'bun-query-builder'
|
|
25
|
+
* db.selectFrom('users').selectRaw(raw`count(*) as c`)
|
|
26
|
+
* db.selectFrom('users').whereRaw(raw('age > 18'))
|
|
27
|
+
* db.selectFrom('users').orderByRaw(raw`created_at desc`)
|
|
28
|
+
* db.selectFrom('orders').whereRaw(raw`status = ${userStatus}`) // value escaped
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare function raw(strings: TemplateStringsArray | string, ...values: unknown[]): RawExpression;
|
|
5
32
|
// eslint-disable-next-line pickier/no-unused-vars
|
|
6
33
|
export declare function createQueryBuilder<DB extends DatabaseSchema<any>>(state?: Partial<InternalState>): QueryBuilder<DB>;
|
|
7
34
|
/**
|
|
@@ -26,6 +53,10 @@ export declare function clearQueryCache(): void;
|
|
|
26
53
|
* ```
|
|
27
54
|
*/
|
|
28
55
|
export declare function setQueryCacheMaxSize(size: number): void;
|
|
56
|
+
// Type guard for raw SQL expressions
|
|
57
|
+
declare interface RawExpression {
|
|
58
|
+
raw: string
|
|
59
|
+
}
|
|
29
60
|
export declare interface WhereRaw {
|
|
30
61
|
raw: any
|
|
31
62
|
}
|
package/dist/src/index.js
CHANGED
|
@@ -12344,6 +12344,14 @@ function* iterateAllPivots(meta, options = {}) {
|
|
|
12344
12344
|
function isRawExpression(expr) {
|
|
12345
12345
|
return typeof expr === "object" && expr !== null && "raw" in expr && typeof expr.raw === "string";
|
|
12346
12346
|
}
|
|
12347
|
+
function raw(strings, ...values) {
|
|
12348
|
+
if (typeof strings === "string")
|
|
12349
|
+
return { raw: strings };
|
|
12350
|
+
let out = strings[0];
|
|
12351
|
+
for (let i = 0;i < values.length; i++)
|
|
12352
|
+
out += formatSubqueryValue(values[i]) + strings[i + 1];
|
|
12353
|
+
return { raw: out };
|
|
12354
|
+
}
|
|
12347
12355
|
function quoteInsertIdent(id) {
|
|
12348
12356
|
return config5.dialect === "mysql" ? `\`${id.replace(/`/g, "``")}\`` : `"${id.replace(/"/g, '""')}"`;
|
|
12349
12357
|
}
|
|
@@ -12412,13 +12420,31 @@ function validateQualifiedIdentifier(value, context) {
|
|
|
12412
12420
|
throw new TypeError(`[query-builder] ${context}: identifier segment '${part}' contains characters outside [A-Za-z0-9_]`);
|
|
12413
12421
|
}
|
|
12414
12422
|
}
|
|
12415
|
-
function
|
|
12416
|
-
if (fragment === null || fragment === undefined) {
|
|
12417
|
-
throw new TypeError(`[query-builder] ${context}: fragment must be a SqlFragment, got ${fragment}`);
|
|
12418
|
-
}
|
|
12423
|
+
function renderRawFragment(fragment, context) {
|
|
12419
12424
|
if (typeof fragment === "string") {
|
|
12420
12425
|
warnOnceBareSqlFragment(context);
|
|
12426
|
+
return fragment;
|
|
12421
12427
|
}
|
|
12428
|
+
if (fragment === null || fragment === undefined)
|
|
12429
|
+
throw new TypeError(`[query-builder] ${context}: fragment must be a SqlFragment, got ${fragment}`);
|
|
12430
|
+
if (isRawExpression(fragment))
|
|
12431
|
+
return fragment.raw;
|
|
12432
|
+
if (typeof fragment === "object") {
|
|
12433
|
+
const f = fragment;
|
|
12434
|
+
if (typeof f.raw === "string")
|
|
12435
|
+
return f.raw;
|
|
12436
|
+
if (typeof f.raw === "function") {
|
|
12437
|
+
const r = f.raw();
|
|
12438
|
+
if (typeof r === "string")
|
|
12439
|
+
return r;
|
|
12440
|
+
}
|
|
12441
|
+
if (typeof f.sql === "string")
|
|
12442
|
+
return f.sql;
|
|
12443
|
+
const s = String(fragment);
|
|
12444
|
+
if (s !== "[object Object]" && s !== "[object Promise]")
|
|
12445
|
+
return s;
|
|
12446
|
+
}
|
|
12447
|
+
throw new TypeError(`[query-builder] ${context}: cannot render this value as a SQL fragment. ` + `A Bun \`sql\`...\`\` query object cannot be converted to SQL text \u2014 pass a ` + `string, or use the exported \`raw\` helper: raw\`count(*) as c\` / raw('age > 18').`);
|
|
12422
12448
|
}
|
|
12423
12449
|
function warnOnceBareSqlFragment(context) {
|
|
12424
12450
|
if (warnedSqlFragmentContexts.has(context))
|
|
@@ -12809,7 +12835,7 @@ function createQueryBuilder(state) {
|
|
|
12809
12835
|
};
|
|
12810
12836
|
const addWhereText = (prefix, clause) => {
|
|
12811
12837
|
const hasWhere = SQL_PATTERNS.WHERE.test(text);
|
|
12812
|
-
const p = hasWhere ?
|
|
12838
|
+
const p = !hasWhere ? "WHERE" : prefix === "WHERE" ? "AND" : prefix;
|
|
12813
12839
|
text = `${text} ${p} ${clause}`;
|
|
12814
12840
|
};
|
|
12815
12841
|
const appendSetOp = (op, other) => {
|
|
@@ -13093,12 +13119,12 @@ function createQueryBuilder(state) {
|
|
|
13093
13119
|
return this;
|
|
13094
13120
|
},
|
|
13095
13121
|
selectRaw(fragment) {
|
|
13096
|
-
|
|
13122
|
+
const frag = renderRawFragment(fragment, "selectRaw(fragment)");
|
|
13097
13123
|
const fromIdx = text.indexOf(" FROM ");
|
|
13098
13124
|
if (fromIdx !== -1) {
|
|
13099
|
-
text = `${text.substring(0, fromIdx)}, ${
|
|
13125
|
+
text = `${text.substring(0, fromIdx)}, ${frag}${text.substring(fromIdx)}`;
|
|
13100
13126
|
} else {
|
|
13101
|
-
text += `, ${
|
|
13127
|
+
text += `, ${frag}`;
|
|
13102
13128
|
}
|
|
13103
13129
|
built = null;
|
|
13104
13130
|
return this;
|
|
@@ -13211,12 +13237,15 @@ function createQueryBuilder(state) {
|
|
|
13211
13237
|
if (cols.length === 0)
|
|
13212
13238
|
return this;
|
|
13213
13239
|
const rendered = cols.map(renderSelectColumn);
|
|
13240
|
+
const distinctMatch = /^SELECT\s+(DISTINCT(?:\s+ON\s+\([^)]*\))?\s+)/i.exec(text);
|
|
13241
|
+
const distinctPrefix = distinctMatch ? distinctMatch[1] : "";
|
|
13214
13242
|
const fromIndex = text.indexOf(" FROM ");
|
|
13215
13243
|
if (fromIndex !== -1) {
|
|
13216
|
-
text = `SELECT ${rendered.join(", ")}${text.substring(fromIndex)}`;
|
|
13244
|
+
text = `SELECT ${distinctPrefix}${rendered.join(", ")}${text.substring(fromIndex)}`;
|
|
13217
13245
|
} else {
|
|
13218
|
-
text = `SELECT ${rendered.join(", ")} FROM ${table}`;
|
|
13246
|
+
text = `SELECT ${distinctPrefix}${rendered.join(", ")} FROM ${table}`;
|
|
13219
13247
|
}
|
|
13248
|
+
built = null;
|
|
13220
13249
|
return this;
|
|
13221
13250
|
},
|
|
13222
13251
|
addSelect(...columns2) {
|
|
@@ -13964,87 +13993,71 @@ function createQueryBuilder(state) {
|
|
|
13964
13993
|
return this;
|
|
13965
13994
|
},
|
|
13966
13995
|
whereLike(column, pattern, caseSensitive = false) {
|
|
13967
|
-
const expr = caseSensitive ? sql`${sql(String(column))} LIKE ${pattern}` : sql`LOWER(${sql(String(column))}) LIKE LOWER(${pattern})`;
|
|
13968
|
-
built = sql`${ensureBuilt()} WHERE ${expr}`;
|
|
13969
13996
|
const ph = getPlaceholder(whereParams.length + 1);
|
|
13970
13997
|
addWhereText("WHERE", `${caseSensitive ? String(column) : `LOWER(${String(column)})`} LIKE ${caseSensitive ? ph : `LOWER(${ph})`}`);
|
|
13971
13998
|
whereParams.push(pattern);
|
|
13999
|
+
built = null;
|
|
13972
14000
|
return this;
|
|
13973
14001
|
},
|
|
13974
14002
|
whereILike(column, pattern) {
|
|
13975
14003
|
const ph = getPlaceholder(whereParams.length + 1);
|
|
13976
|
-
if (config5.dialect === "postgres")
|
|
13977
|
-
built = sql`${ensureBuilt()} WHERE ${sql(String(column))} ILIKE ${pattern}`;
|
|
14004
|
+
if (config5.dialect === "postgres")
|
|
13978
14005
|
addWhereText("WHERE", `${String(column)} ILIKE ${ph}`);
|
|
13979
|
-
|
|
13980
|
-
const expr = sql`LOWER(${sql(String(column))}) LIKE LOWER(${pattern})`;
|
|
13981
|
-
built = sql`${ensureBuilt()} WHERE ${expr}`;
|
|
14006
|
+
else
|
|
13982
14007
|
addWhereText("WHERE", `LOWER(${String(column)}) LIKE LOWER(${ph})`);
|
|
13983
|
-
}
|
|
13984
14008
|
whereParams.push(pattern);
|
|
14009
|
+
built = null;
|
|
13985
14010
|
return this;
|
|
13986
14011
|
},
|
|
13987
14012
|
orWhereLike(column, pattern, caseSensitive = false) {
|
|
13988
|
-
const expr = caseSensitive ? sql`${sql(String(column))} LIKE ${pattern}` : sql`LOWER(${sql(String(column))}) LIKE LOWER(${pattern})`;
|
|
13989
|
-
built = sql`${ensureBuilt()} OR ${expr}`;
|
|
13990
14013
|
const ph = getPlaceholder(whereParams.length + 1);
|
|
13991
14014
|
addWhereText("OR", `${caseSensitive ? String(column) : `LOWER(${String(column)})`} LIKE ${caseSensitive ? ph : `LOWER(${ph})`}`);
|
|
13992
14015
|
whereParams.push(pattern);
|
|
14016
|
+
built = null;
|
|
13993
14017
|
return this;
|
|
13994
14018
|
},
|
|
13995
14019
|
orWhereILike(column, pattern) {
|
|
13996
14020
|
const ph = getPlaceholder(whereParams.length + 1);
|
|
13997
|
-
if (config5.dialect === "postgres")
|
|
13998
|
-
built = sql`${ensureBuilt()} OR ${sql(String(column))} ILIKE ${pattern}`;
|
|
14021
|
+
if (config5.dialect === "postgres")
|
|
13999
14022
|
addWhereText("OR", `${String(column)} ILIKE ${ph}`);
|
|
14000
|
-
|
|
14001
|
-
const expr = sql`LOWER(${sql(String(column))}) LIKE LOWER(${pattern})`;
|
|
14002
|
-
built = sql`${ensureBuilt()} OR ${expr}`;
|
|
14023
|
+
else
|
|
14003
14024
|
addWhereText("OR", `LOWER(${String(column)}) LIKE LOWER(${ph})`);
|
|
14004
|
-
}
|
|
14005
14025
|
whereParams.push(pattern);
|
|
14026
|
+
built = null;
|
|
14006
14027
|
return this;
|
|
14007
14028
|
},
|
|
14008
14029
|
whereNotLike(column, pattern, caseSensitive = false) {
|
|
14009
|
-
const expr = caseSensitive ? sql`${sql(String(column))} NOT LIKE ${pattern}` : sql`LOWER(${sql(String(column))}) NOT LIKE LOWER(${pattern})`;
|
|
14010
|
-
built = sql`${ensureBuilt()} WHERE ${expr}`;
|
|
14011
14030
|
const ph = getPlaceholder(whereParams.length + 1);
|
|
14012
14031
|
addWhereText("WHERE", `${caseSensitive ? String(column) : `LOWER(${String(column)})`} NOT LIKE ${caseSensitive ? ph : `LOWER(${ph})`}`);
|
|
14013
14032
|
whereParams.push(pattern);
|
|
14033
|
+
built = null;
|
|
14014
14034
|
return this;
|
|
14015
14035
|
},
|
|
14016
14036
|
whereNotILike(column, pattern) {
|
|
14017
14037
|
const ph = getPlaceholder(whereParams.length + 1);
|
|
14018
|
-
if (config5.dialect === "postgres")
|
|
14019
|
-
built = sql`${ensureBuilt()} WHERE ${sql(String(column))} NOT ILIKE ${pattern}`;
|
|
14038
|
+
if (config5.dialect === "postgres")
|
|
14020
14039
|
addWhereText("WHERE", `${String(column)} NOT ILIKE ${ph}`);
|
|
14021
|
-
|
|
14022
|
-
const expr = sql`LOWER(${sql(String(column))}) NOT LIKE LOWER(${pattern})`;
|
|
14023
|
-
built = sql`${ensureBuilt()} WHERE ${expr}`;
|
|
14040
|
+
else
|
|
14024
14041
|
addWhereText("WHERE", `LOWER(${String(column)}) NOT LIKE LOWER(${ph})`);
|
|
14025
|
-
}
|
|
14026
14042
|
whereParams.push(pattern);
|
|
14043
|
+
built = null;
|
|
14027
14044
|
return this;
|
|
14028
14045
|
},
|
|
14029
14046
|
orWhereNotLike(column, pattern, caseSensitive = false) {
|
|
14030
|
-
const expr = caseSensitive ? sql`${sql(String(column))} NOT LIKE ${pattern}` : sql`LOWER(${sql(String(column))}) NOT LIKE LOWER(${pattern})`;
|
|
14031
|
-
built = sql`${ensureBuilt()} OR ${expr}`;
|
|
14032
14047
|
const ph = getPlaceholder(whereParams.length + 1);
|
|
14033
14048
|
addWhereText("OR", `${caseSensitive ? String(column) : `LOWER(${String(column)})`} NOT LIKE ${caseSensitive ? ph : `LOWER(${ph})`}`);
|
|
14034
14049
|
whereParams.push(pattern);
|
|
14050
|
+
built = null;
|
|
14035
14051
|
return this;
|
|
14036
14052
|
},
|
|
14037
14053
|
orWhereNotILike(column, pattern) {
|
|
14038
14054
|
const ph = getPlaceholder(whereParams.length + 1);
|
|
14039
|
-
if (config5.dialect === "postgres")
|
|
14040
|
-
built = sql`${ensureBuilt()} OR ${sql(String(column))} NOT ILIKE ${pattern}`;
|
|
14055
|
+
if (config5.dialect === "postgres")
|
|
14041
14056
|
addWhereText("OR", `${String(column)} NOT ILIKE ${ph}`);
|
|
14042
|
-
|
|
14043
|
-
const expr = sql`LOWER(${sql(String(column))}) NOT LIKE LOWER(${pattern})`;
|
|
14044
|
-
built = sql`${ensureBuilt()} OR ${expr}`;
|
|
14057
|
+
else
|
|
14045
14058
|
addWhereText("OR", `LOWER(${String(column)}) NOT LIKE LOWER(${ph})`);
|
|
14046
|
-
}
|
|
14047
14059
|
whereParams.push(pattern);
|
|
14060
|
+
built = null;
|
|
14048
14061
|
return this;
|
|
14049
14062
|
},
|
|
14050
14063
|
whereAny(cols, op, value) {
|
|
@@ -14105,9 +14118,9 @@ function createQueryBuilder(state) {
|
|
|
14105
14118
|
return this;
|
|
14106
14119
|
},
|
|
14107
14120
|
whereRaw(fragment) {
|
|
14108
|
-
|
|
14121
|
+
const frag = renderRawFragment(fragment, "whereRaw(fragment)");
|
|
14109
14122
|
const keyword = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
|
|
14110
|
-
text += ` ${keyword} ${
|
|
14123
|
+
text += ` ${keyword} ${frag}`;
|
|
14111
14124
|
built = null;
|
|
14112
14125
|
return this;
|
|
14113
14126
|
},
|
|
@@ -14473,8 +14486,8 @@ function createQueryBuilder(state) {
|
|
|
14473
14486
|
return this;
|
|
14474
14487
|
},
|
|
14475
14488
|
groupByRaw(fragment) {
|
|
14476
|
-
|
|
14477
|
-
text = SQL_PATTERNS.GROUP_BY.test(text) ? `${text}, ${
|
|
14489
|
+
const frag = renderRawFragment(fragment, "groupByRaw(fragment)");
|
|
14490
|
+
text = SQL_PATTERNS.GROUP_BY.test(text) ? `${text}, ${frag}` : `${text} GROUP BY ${frag}`;
|
|
14478
14491
|
built = null;
|
|
14479
14492
|
return this;
|
|
14480
14493
|
},
|
|
@@ -14506,15 +14519,15 @@ function createQueryBuilder(state) {
|
|
|
14506
14519
|
return this;
|
|
14507
14520
|
},
|
|
14508
14521
|
havingRaw(fragment) {
|
|
14509
|
-
|
|
14522
|
+
const frag = renderRawFragment(fragment, "havingRaw(fragment)");
|
|
14510
14523
|
const kw = /\bHAVING\b/i.test(text) ? "AND" : "HAVING";
|
|
14511
|
-
text += ` ${kw} ${
|
|
14524
|
+
text += ` ${kw} ${frag}`;
|
|
14512
14525
|
built = null;
|
|
14513
14526
|
return this;
|
|
14514
14527
|
},
|
|
14515
14528
|
orderByRaw(fragment) {
|
|
14516
|
-
|
|
14517
|
-
text = SQL_PATTERNS.ORDER_BY.test(text) ? `${text}, ${
|
|
14529
|
+
const frag = renderRawFragment(fragment, "orderByRaw(fragment)");
|
|
14530
|
+
text = SQL_PATTERNS.ORDER_BY.test(text) ? `${text}, ${frag}` : `${text} ORDER BY ${frag}`;
|
|
14518
14531
|
built = null;
|
|
14519
14532
|
return this;
|
|
14520
14533
|
},
|
|
@@ -14644,19 +14657,21 @@ function createQueryBuilder(state) {
|
|
|
14644
14657
|
q = sql`${q} ORDER BY ${sql(String(column))} ${direction === "asc" ? sql`ASC` : sql`DESC`} LIMIT ${perPage + 1}`;
|
|
14645
14658
|
}
|
|
14646
14659
|
const rows = await runWithHooks(q, "select", { signal: abortSignal, timeoutMs });
|
|
14647
|
-
const
|
|
14660
|
+
const hasMore = rows.length > perPage;
|
|
14648
14661
|
const data = rows.slice(0, perPage);
|
|
14662
|
+
const lastRow = data[data.length - 1];
|
|
14663
|
+
const next = hasMore && lastRow ? Array.isArray(column) ? column.map((c) => lastRow[c]) : lastRow[column] : null;
|
|
14649
14664
|
const prevCursor = data.length ? Array.isArray(column) ? column.map((c) => data[0]?.[c]) : data[0]?.[column] : null;
|
|
14650
14665
|
return { data, meta: { perPage, nextCursor: next ?? null, prevCursor } };
|
|
14651
14666
|
},
|
|
14652
14667
|
async chunk(size, handler) {
|
|
14653
14668
|
let page = 1;
|
|
14654
14669
|
while (true) {
|
|
14655
|
-
const { data } = await this.paginate(size, page);
|
|
14670
|
+
const { data, meta: meta2 } = await this.paginate(size, page);
|
|
14656
14671
|
if (data.length === 0)
|
|
14657
14672
|
break;
|
|
14658
14673
|
await handler(data);
|
|
14659
|
-
if (
|
|
14674
|
+
if (page >= meta2.lastPage)
|
|
14660
14675
|
break;
|
|
14661
14676
|
page += 1;
|
|
14662
14677
|
}
|
|
@@ -14697,20 +14712,20 @@ function createQueryBuilder(state) {
|
|
|
14697
14712
|
includeTrashed = true;
|
|
14698
14713
|
onlyTrashed = true;
|
|
14699
14714
|
const softDeleteColumn = config5.softDeletes?.column || "deleted_at";
|
|
14700
|
-
const splice = (
|
|
14701
|
-
const upper =
|
|
14715
|
+
const splice = (raw2, predicate2) => {
|
|
14716
|
+
const upper = raw2.toUpperCase();
|
|
14702
14717
|
let depth = 0;
|
|
14703
|
-
for (let i = 0;i <
|
|
14704
|
-
const c =
|
|
14718
|
+
for (let i = 0;i < raw2.length; i++) {
|
|
14719
|
+
const c = raw2[i];
|
|
14705
14720
|
if (c === "(")
|
|
14706
14721
|
depth++;
|
|
14707
14722
|
else if (c === ")")
|
|
14708
14723
|
depth--;
|
|
14709
|
-
else if (depth === 0 && upper.substring(i, i + 5) === "WHERE" && (i === 0 || /\s/.test(
|
|
14710
|
-
return `${
|
|
14724
|
+
else if (depth === 0 && upper.substring(i, i + 5) === "WHERE" && (i === 0 || /\s/.test(raw2[i - 1] ?? "")) && /\s/.test(raw2[i + 5] ?? "")) {
|
|
14725
|
+
return `${raw2.substring(0, i)}WHERE ${predicate2} AND ${raw2.substring(i + 6)}`;
|
|
14711
14726
|
}
|
|
14712
14727
|
}
|
|
14713
|
-
return `${
|
|
14728
|
+
return `${raw2} WHERE ${predicate2}`;
|
|
14714
14729
|
};
|
|
14715
14730
|
const predicate = `${table}.${softDeleteColumn} IS NOT NULL`;
|
|
14716
14731
|
text = splice(text, predicate);
|
|
@@ -15014,13 +15029,13 @@ function createQueryBuilder(state) {
|
|
|
15014
15029
|
const cacheKey = `${String(table)}|${prop}`;
|
|
15015
15030
|
let chosen = dynamicWhereColumnCache.get(cacheKey);
|
|
15016
15031
|
if (chosen === undefined) {
|
|
15017
|
-
const
|
|
15018
|
-
if (!
|
|
15032
|
+
const raw2 = prop.replace(/^(?:or|and)?where/i, "");
|
|
15033
|
+
if (!raw2) {
|
|
15019
15034
|
dynamicWhereColumnCache.set(cacheKey, "");
|
|
15020
15035
|
chosen = "";
|
|
15021
15036
|
} else {
|
|
15022
|
-
const lowerFirst =
|
|
15023
|
-
const snake =
|
|
15037
|
+
const lowerFirst = raw2.charAt(0).toLowerCase() + raw2.slice(1);
|
|
15038
|
+
const snake = raw2.replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, "");
|
|
15024
15039
|
const available = schema ? Object.keys(schema[String(table)]?.columns ?? {}) : [];
|
|
15025
15040
|
chosen = [snake, lowerFirst, lowerFirst.toLowerCase()].find((n) => available.includes(n)) ?? snake;
|
|
15026
15041
|
dynamicWhereColumnCache.set(cacheKey, chosen);
|
|
@@ -15410,6 +15425,10 @@ function createQueryBuilder(state) {
|
|
|
15410
15425
|
returning(...cols) {
|
|
15411
15426
|
const returningSql = `${sqlText} RETURNING ${cols.join(", ")}`;
|
|
15412
15427
|
const q = _sql.unsafe(returningSql, params);
|
|
15428
|
+
const runFirst = async () => {
|
|
15429
|
+
const rows = await runWithHooks(q, "insert");
|
|
15430
|
+
return Array.isArray(rows) ? rows[0] : rows;
|
|
15431
|
+
};
|
|
15413
15432
|
return {
|
|
15414
15433
|
where: () => this,
|
|
15415
15434
|
andWhere: () => this,
|
|
@@ -15418,7 +15437,22 @@ function createQueryBuilder(state) {
|
|
|
15418
15437
|
limit: () => this,
|
|
15419
15438
|
offset: () => this,
|
|
15420
15439
|
toSQL: () => makeExecutableQuery(q, returningSql),
|
|
15421
|
-
execute: () => runWithHooks(q, "insert")
|
|
15440
|
+
execute: () => runWithHooks(q, "insert"),
|
|
15441
|
+
get: () => runWithHooks(q, "insert"),
|
|
15442
|
+
first: runFirst,
|
|
15443
|
+
executeTakeFirst: runFirst,
|
|
15444
|
+
async firstOrFail() {
|
|
15445
|
+
const row = await runFirst();
|
|
15446
|
+
if (!row)
|
|
15447
|
+
throw new Error("Insert with RETURNING returned no rows");
|
|
15448
|
+
return row;
|
|
15449
|
+
},
|
|
15450
|
+
async executeTakeFirstOrThrow() {
|
|
15451
|
+
const row = await runFirst();
|
|
15452
|
+
if (!row)
|
|
15453
|
+
throw new Error("Insert with RETURNING returned no rows");
|
|
15454
|
+
return row;
|
|
15455
|
+
}
|
|
15422
15456
|
};
|
|
15423
15457
|
},
|
|
15424
15458
|
toSQL() {
|
|
@@ -15457,19 +15491,27 @@ function createQueryBuilder(state) {
|
|
|
15457
15491
|
returningAll() {
|
|
15458
15492
|
const returningSql = `${sqlText} RETURNING *`;
|
|
15459
15493
|
const q = _sql.unsafe(returningSql, params);
|
|
15494
|
+
const runFirst = async () => {
|
|
15495
|
+
const result = await runWithHooks(q, "insert");
|
|
15496
|
+
return Array.isArray(result) ? result[0] : result;
|
|
15497
|
+
};
|
|
15460
15498
|
return {
|
|
15461
15499
|
toSQL: () => makeExecutableQuery(q, returningSql),
|
|
15462
15500
|
execute: () => runWithHooks(q, "insert"),
|
|
15463
|
-
|
|
15464
|
-
|
|
15465
|
-
|
|
15501
|
+
get: () => runWithHooks(q, "insert"),
|
|
15502
|
+
first: runFirst,
|
|
15503
|
+
executeTakeFirst: runFirst,
|
|
15504
|
+
async firstOrFail() {
|
|
15505
|
+
const row = await runFirst();
|
|
15506
|
+
if (!row)
|
|
15507
|
+
throw new Error("Insert with RETURNING returned no rows");
|
|
15508
|
+
return row;
|
|
15466
15509
|
},
|
|
15467
15510
|
async executeTakeFirstOrThrow() {
|
|
15468
|
-
const
|
|
15469
|
-
|
|
15470
|
-
|
|
15471
|
-
|
|
15472
|
-
return first;
|
|
15511
|
+
const row = await runFirst();
|
|
15512
|
+
if (!row)
|
|
15513
|
+
throw new Error("Insert with RETURNING returned no rows");
|
|
15514
|
+
return row;
|
|
15473
15515
|
}
|
|
15474
15516
|
};
|
|
15475
15517
|
}
|
|
@@ -15533,6 +15575,10 @@ function createQueryBuilder(state) {
|
|
|
15533
15575
|
returning(...cols) {
|
|
15534
15576
|
const retText = `${sqlText} RETURNING ${cols.join(", ")}`;
|
|
15535
15577
|
const q = params.length > 0 ? _sql.unsafe(retText, params) : _sql.unsafe(retText);
|
|
15578
|
+
const runFirst = async () => {
|
|
15579
|
+
const rows = await runWithHooks(q, "update");
|
|
15580
|
+
return Array.isArray(rows) ? rows[0] : rows;
|
|
15581
|
+
};
|
|
15536
15582
|
const obj = {
|
|
15537
15583
|
where: () => obj,
|
|
15538
15584
|
andWhere: () => obj,
|
|
@@ -15541,7 +15587,22 @@ function createQueryBuilder(state) {
|
|
|
15541
15587
|
limit: () => obj,
|
|
15542
15588
|
offset: () => obj,
|
|
15543
15589
|
toSQL: () => makeExecutableQuery(q, retText),
|
|
15544
|
-
execute: () => runWithHooks(q, "update")
|
|
15590
|
+
execute: () => runWithHooks(q, "update"),
|
|
15591
|
+
get: () => runWithHooks(q, "update"),
|
|
15592
|
+
first: runFirst,
|
|
15593
|
+
executeTakeFirst: runFirst,
|
|
15594
|
+
async firstOrFail() {
|
|
15595
|
+
const row = await runFirst();
|
|
15596
|
+
if (!row)
|
|
15597
|
+
throw new Error("Update with RETURNING returned no rows");
|
|
15598
|
+
return row;
|
|
15599
|
+
},
|
|
15600
|
+
async executeTakeFirstOrThrow() {
|
|
15601
|
+
const row = await runFirst();
|
|
15602
|
+
if (!row)
|
|
15603
|
+
throw new Error("Update with RETURNING returned no rows");
|
|
15604
|
+
return row;
|
|
15605
|
+
}
|
|
15545
15606
|
};
|
|
15546
15607
|
return obj;
|
|
15547
15608
|
},
|
|
@@ -15640,6 +15701,10 @@ function createQueryBuilder(state) {
|
|
|
15640
15701
|
returning(...cols) {
|
|
15641
15702
|
const retText = `${sqlText} RETURNING ${cols.join(", ")}`;
|
|
15642
15703
|
const q = delParams.length > 0 ? _sql.unsafe(retText, delParams) : _sql.unsafe(retText);
|
|
15704
|
+
const runFirst = async () => {
|
|
15705
|
+
const rows = await runWithHooks(q, "delete");
|
|
15706
|
+
return Array.isArray(rows) ? rows[0] : rows;
|
|
15707
|
+
};
|
|
15643
15708
|
const obj = {
|
|
15644
15709
|
where: () => obj,
|
|
15645
15710
|
andWhere: () => obj,
|
|
@@ -15648,7 +15713,22 @@ function createQueryBuilder(state) {
|
|
|
15648
15713
|
limit: () => obj,
|
|
15649
15714
|
offset: () => obj,
|
|
15650
15715
|
toSQL: () => makeExecutableQuery(q, retText),
|
|
15651
|
-
execute: () => runWithHooks(q, "delete")
|
|
15716
|
+
execute: () => runWithHooks(q, "delete"),
|
|
15717
|
+
get: () => runWithHooks(q, "delete"),
|
|
15718
|
+
first: runFirst,
|
|
15719
|
+
executeTakeFirst: runFirst,
|
|
15720
|
+
async firstOrFail() {
|
|
15721
|
+
const row = await runFirst();
|
|
15722
|
+
if (!row)
|
|
15723
|
+
throw new Error("Delete with RETURNING returned no rows");
|
|
15724
|
+
return row;
|
|
15725
|
+
},
|
|
15726
|
+
async executeTakeFirstOrThrow() {
|
|
15727
|
+
const row = await runFirst();
|
|
15728
|
+
if (!row)
|
|
15729
|
+
throw new Error("Delete with RETURNING returned no rows");
|
|
15730
|
+
return row;
|
|
15731
|
+
}
|
|
15652
15732
|
};
|
|
15653
15733
|
return obj;
|
|
15654
15734
|
},
|
|
@@ -18000,8 +18080,8 @@ function loadPlanSnapshot(workspaceRoot, dialect) {
|
|
|
18000
18080
|
return;
|
|
18001
18081
|
}
|
|
18002
18082
|
try {
|
|
18003
|
-
const
|
|
18004
|
-
const parsed = JSON.parse(
|
|
18083
|
+
const raw2 = readFileSync3(snapshotPath, "utf8");
|
|
18084
|
+
const parsed = JSON.parse(raw2);
|
|
18005
18085
|
if (parsed?.plan && Array.isArray(parsed.plan.tables) && parsed.plan.dialect) {
|
|
18006
18086
|
return parsed.plan;
|
|
18007
18087
|
}
|
|
@@ -18060,8 +18140,8 @@ async function generateMigration(dir, opts = {}) {
|
|
|
18060
18140
|
const statePath = String(opts.state || defaultStatePath);
|
|
18061
18141
|
if (existsSync16(statePath)) {
|
|
18062
18142
|
try {
|
|
18063
|
-
const
|
|
18064
|
-
const parsed = JSON.parse(
|
|
18143
|
+
const raw2 = readFileSync3(statePath, "utf8");
|
|
18144
|
+
const parsed = JSON.parse(raw2);
|
|
18065
18145
|
previous = parsed?.plan && parsed.plan.tables ? parsed.plan : parsed?.tables ? parsed : undefined;
|
|
18066
18146
|
if (previous) {
|
|
18067
18147
|
info("-- Comparing with legacy state file (will migrate to new snapshot format)");
|
|
@@ -24651,10 +24731,10 @@ class BelongsToManyRelationBuilder {
|
|
|
24651
24731
|
hydrateRows(rows) {
|
|
24652
24732
|
const fkParent = this.fkParent;
|
|
24653
24733
|
const fkRelated = this.fkRelated;
|
|
24654
|
-
return rows.map((
|
|
24734
|
+
return rows.map((raw2) => {
|
|
24655
24735
|
const relatedRow = {};
|
|
24656
24736
|
const pivotExtras = {};
|
|
24657
|
-
for (const [k2, v2] of Object.entries(
|
|
24737
|
+
for (const [k2, v2] of Object.entries(raw2)) {
|
|
24658
24738
|
if (k2.startsWith(BTM_RELATED_ALIAS))
|
|
24659
24739
|
relatedRow[k2.slice(BTM_RELATED_ALIAS.length)] = v2;
|
|
24660
24740
|
else if (k2 !== fkParent && k2 !== fkRelated)
|
|
@@ -28021,6 +28101,7 @@ export {
|
|
|
28021
28101
|
relationDiagram,
|
|
28022
28102
|
registerModel,
|
|
28023
28103
|
registerBrowserModels,
|
|
28104
|
+
raw,
|
|
28024
28105
|
queryExplainAll,
|
|
28025
28106
|
ping,
|
|
28026
28107
|
parseStacksModels,
|
package/package.json
CHANGED