Files
bun-sqlite/dist/utils/sql-generator-gen-search-str.js
T
tbenandClaude Opus 5.5 938a1da71d Add queue tables, quote SQL identifiers (v1.3.0)
- isQueue table option adds queue columns and a (status, available_at)
  index; BunSQLite.queue exposes add, claim, complete, fail and
  release_stale with atomic claims
- Set busy_timeout on connections so workers in separate processes wait
  instead of failing with SQLITE_BUSY
- Quote table and column names with backticks in generated SQL, so
  reserved words (to, order, group) work and unknown columns error
  instead of matching as string literals
- Tests for queue ops, multi-process claims and reserved-word names

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 16:29:14 +01:00

113 lines
4.2 KiB
JavaScript

import sqlGenOperatorGen from "./sql-gen-operator-gen";
import { sqlColumn, sqlName, sqlOperator, sqlRawEquality } from "./sql-safe";
function isPresent(val) {
return val !== undefined && val !== null;
}
function isValuesObject(val) {
return typeof val == "object" && val !== null && !ArrayBuffer.isView(val);
}
export default function sqlGenGenSearchStr({ queryObj, join, field, table_name, }) {
let sqlSearhValues = [];
const safeField = sqlColumn(field);
const finalFieldName = (() => {
if (queryObj?.tableName) {
return `${sqlName(queryObj.tableName, "table name")}.${safeField}`;
}
if (join) {
return `${table_name}.${safeField}`;
}
return safeField;
})();
function grabValue(val) {
if (!isPresent(val))
return;
if (isValuesObject(val)) {
const isFieldValue = Boolean(val.fieldName && val.tableName);
return sqlGenOperatorGen({
queryObj,
equality: val.equality || queryObj.equality,
fieldName: finalFieldName,
value: isFieldValue
? `${sqlName(val.tableName, "table name")}.${sqlName(val.fieldName, "field name")}`
: val.value,
isValueFieldValue: isFieldValue,
});
}
return sqlGenOperatorGen({
queryObj,
equality: queryObj.equality,
fieldName: finalFieldName,
value: val,
});
}
let str;
if (Array.isArray(queryObj.value)) {
const items = queryObj.value.filter(isPresent);
const rawValues = items
.map((v) => (isValuesObject(v) ? v.value : v))
.filter(isPresent);
const equality = queryObj.equality;
if ((equality == "IN" || equality == "NOT IN") && !rawValues.length) {
// Empty IN () is invalid SQL: IN matches nothing, NOT IN matches everything
str = equality == "IN" ? "1 = 0" : "1 = 1";
}
else if (equality == "IN" || equality == "NOT IN") {
str = `${finalFieldName} ${equality} (${rawValues
.map(() => "?")
.join(", ")})`;
sqlSearhValues.push(...rawValues);
}
else if (equality == "BETWEEN" || equality == "NOT BETWEEN") {
if (rawValues.length !== 2) {
throw new Error(`\`${equality}\` on \`${finalFieldName}\` requires exactly two values`);
}
str = `${finalFieldName} ${equality} ? AND ?`;
sqlSearhValues.push(...rawValues);
}
else {
if (!items.length) {
throw new Error(`Empty value array for \`${finalFieldName}\``);
}
const strArray = [];
for (const val of items) {
const operatorStrParam = grabValue(val);
if (!operatorStrParam)
continue;
strArray.push(operatorStrParam.str);
sqlSearhValues.push(...operatorStrParam.params);
}
str =
"(" +
strArray.join(` ${sqlOperator(queryObj.operator)} `) +
")";
}
}
else if (queryObj.raw_equality && isPresent(queryObj.value)) {
const value = isValuesObject(queryObj.value)
? queryObj.value.value
: queryObj.value;
str = `${finalFieldName} ${sqlRawEquality(queryObj.raw_equality)} ?`;
sqlSearhValues.push(value ?? null);
}
else if (queryObj.between) {
str = `${finalFieldName} BETWEEN ? AND ?`;
sqlSearhValues.push(queryObj.between.min, queryObj.between.max);
}
else if (queryObj.not_between) {
str = `${finalFieldName} NOT BETWEEN ? AND ?`;
sqlSearhValues.push(queryObj.not_between.min, queryObj.not_between.max);
}
else {
const operatorStrParam = grabValue(queryObj.value) ||
sqlGenOperatorGen({
equality: queryObj.equality,
fieldName: finalFieldName,
value: undefined,
queryObj,
});
str = operatorStrParam.str;
sqlSearhValues.push(...operatorStrParam.params);
}
return { str, values: sqlSearhValues };
}