Add tests, schema sync safeguards, and query/handler improvements
- Add bun test suite: SQL generator and value unit tests, plus MariaDB integration tests gated on BUN_MARIADB_TEST_DB - schema: add --dry-run and require confirmation (or --yes) before dropping tables/columns; block unapproved destructive statements - Always AND targetId with the query, regardless of searchOperator - Initialize lazily on first call; init throws instead of exiting - Generate IN (?, ?, ...) lists for IN / NOT IN - Share value binding between insert and update (booleans, embeddings, function values, "" -> NULL for non-text columns) - Keep Buffers and convert BigInt/Date without a JSON round-trip - DbSQL: report success for empty SELECT results - Run update() in a transaction with row locking - Key the client cache on the full connection config - Consolidate identifier quoting, drop dead code, merge column lookup - Read CLI version from package.json, pin @types/bun, chmod CLI on compile - Rebuild dist Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
+63
-43
@@ -1,92 +1,112 @@
|
||||
import sqlGenOperatorGen from "./sql-gen-operator-gen";
|
||||
import { columnRef, sqlLogicalOperator, sqlRawComparison, } from "./sql-identifier";
|
||||
export default function sqlGenGenSearchStr({ queryObj, join, field, table_name, }) {
|
||||
let sqlSearhValues = [];
|
||||
const finalFieldName = (() => {
|
||||
if (queryObj?.tableName) {
|
||||
return `${queryObj.tableName}.${field}`;
|
||||
return columnRef(String(field), queryObj.tableName);
|
||||
}
|
||||
if (join) {
|
||||
return `${table_name}.${field}`;
|
||||
return columnRef(String(field), table_name);
|
||||
}
|
||||
return field;
|
||||
return columnRef(String(field));
|
||||
})();
|
||||
let str = `${finalFieldName}=?`;
|
||||
function grabValue(val) {
|
||||
const valueParsed = val;
|
||||
if (!valueParsed)
|
||||
if (val === undefined || val === null)
|
||||
return;
|
||||
const valueString = typeof valueParsed == "string" || typeof valueParsed == "number"
|
||||
? valueParsed
|
||||
: valueParsed
|
||||
? valueParsed.fieldName && valueParsed.tableName
|
||||
? `${valueParsed.tableName}.${valueParsed.fieldName}`
|
||||
: valueParsed.value
|
||||
: undefined;
|
||||
const valueEquality = typeof valueParsed == "object"
|
||||
? valueParsed.equality || queryObj.equality
|
||||
const isFieldRef = Boolean(typeof val == "object" && val.fieldName && val.tableName);
|
||||
const valueString = typeof val == "string" || typeof val == "number"
|
||||
? val
|
||||
: isFieldRef
|
||||
? columnRef(val.fieldName, val.tableName)
|
||||
: val.value;
|
||||
const valueEquality = typeof val == "object"
|
||||
? val.equality || queryObj.equality
|
||||
: queryObj.equality;
|
||||
const operatorStrParam = sqlGenOperatorGen({
|
||||
return sqlGenOperatorGen({
|
||||
queryObj,
|
||||
equality: valueEquality,
|
||||
fieldName: finalFieldName || "",
|
||||
value: valueString || "",
|
||||
isValueFieldValue: Boolean(typeof valueParsed == "object" &&
|
||||
valueParsed.fieldName &&
|
||||
valueParsed.tableName),
|
||||
fieldName: finalFieldName,
|
||||
value: valueString ?? "",
|
||||
isValueFieldValue: isFieldRef,
|
||||
});
|
||||
return operatorStrParam;
|
||||
}
|
||||
if (Array.isArray(queryObj.value)) {
|
||||
const listEquality = queryObj.equality == "IN" || queryObj.equality == "NOT IN"
|
||||
? queryObj.equality
|
||||
: undefined;
|
||||
if (listEquality &&
|
||||
!queryObj.nullValue &&
|
||||
!queryObj.notNullValue &&
|
||||
queryObj.value !== undefined &&
|
||||
queryObj.value !== null) {
|
||||
const rawValues = Array.isArray(queryObj.value)
|
||||
? queryObj.value
|
||||
: [queryObj.value];
|
||||
const listValues = rawValues
|
||||
.map((val) => val !== null && typeof val == "object" ? val.value : val)
|
||||
.filter((val) => val !== undefined && val !== null);
|
||||
if (listValues.length === 0) {
|
||||
// Empty IN () is invalid SQL: IN matches nothing, NOT IN matches everything
|
||||
str = listEquality == "IN" ? "1 = 0" : "1 = 1";
|
||||
}
|
||||
else {
|
||||
str = `${finalFieldName} ${listEquality} (${listValues.map(() => "?").join(",")})`;
|
||||
sqlSearhValues.push(...listValues);
|
||||
}
|
||||
}
|
||||
else if (Array.isArray(queryObj.value)) {
|
||||
const strArray = [];
|
||||
queryObj.value.forEach((val) => {
|
||||
const operatorStrParam = grabValue(val);
|
||||
if (!operatorStrParam)
|
||||
if (!operatorStrParam?.str)
|
||||
return;
|
||||
if (operatorStrParam.str && operatorStrParam.param) {
|
||||
strArray.push(operatorStrParam.str);
|
||||
strArray.push(operatorStrParam.str);
|
||||
if (operatorStrParam.param !== undefined) {
|
||||
sqlSearhValues.push(operatorStrParam.param);
|
||||
}
|
||||
else if (operatorStrParam.str) {
|
||||
strArray.push(operatorStrParam.str);
|
||||
}
|
||||
});
|
||||
str = "(" + strArray.join(` ${queryObj.operator || "AND"} `) + ")";
|
||||
str =
|
||||
"(" +
|
||||
strArray.join(` ${sqlLogicalOperator(queryObj.operator)} `) +
|
||||
")";
|
||||
}
|
||||
else if (typeof queryObj.value == "object") {
|
||||
else if (queryObj.value !== null && typeof queryObj.value == "object") {
|
||||
const operatorStrParam = grabValue(queryObj.value);
|
||||
if (operatorStrParam?.str) {
|
||||
str = operatorStrParam.str;
|
||||
if (operatorStrParam.param) {
|
||||
if (operatorStrParam.param !== undefined) {
|
||||
sqlSearhValues.push(operatorStrParam.param);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (queryObj.raw_equality && queryObj.value) {
|
||||
str = `${finalFieldName} ${queryObj.raw_equality} ?`;
|
||||
else if (queryObj.raw_equality &&
|
||||
queryObj.value !== undefined &&
|
||||
queryObj.value !== null) {
|
||||
str = `${finalFieldName} ${sqlRawComparison(queryObj.raw_equality)} ?`;
|
||||
sqlSearhValues.push(queryObj.value);
|
||||
}
|
||||
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 valueParsed = queryObj.value ? queryObj.value : undefined;
|
||||
const operatorStrParam = sqlGenOperatorGen({
|
||||
equality: queryObj.equality,
|
||||
fieldName: finalFieldName || "",
|
||||
value: valueParsed,
|
||||
fieldName: finalFieldName,
|
||||
value: queryObj.value ?? undefined,
|
||||
queryObj,
|
||||
});
|
||||
if (operatorStrParam.str && operatorStrParam.param) {
|
||||
if (operatorStrParam.str) {
|
||||
str = operatorStrParam.str;
|
||||
}
|
||||
if (operatorStrParam.param !== undefined) {
|
||||
sqlSearhValues.push(operatorStrParam.param);
|
||||
}
|
||||
else if (operatorStrParam.str && !operatorStrParam.str.match(/\?/)) {
|
||||
str = operatorStrParam.str;
|
||||
}
|
||||
else {
|
||||
sqlSearhValues.push(valueParsed || "");
|
||||
}
|
||||
}
|
||||
return { str, values: sqlSearhValues };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user