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:
Vendored
+80
@@ -0,0 +1,80 @@
|
||||
import { ServerQueryOperators, SQlComparisons } from "../types";
|
||||
/**
|
||||
* Quote a single identifier (table, column, alias) with backticks.
|
||||
* Already-quoted identifiers (e.g. "`users`") are returned unchanged.
|
||||
*/
|
||||
export function quoteIdentifier(identifier) {
|
||||
const str = String(identifier);
|
||||
if (/^`(?:[^`]|``)+`$/.test(str))
|
||||
return str;
|
||||
return `\`${str.replace(/`/g, "``")}\``;
|
||||
}
|
||||
/**
|
||||
* Quote a possibly qualified identifier: "table.field" -> `table`.`field`.
|
||||
* A bare "*" (or "table.*") is kept as a wildcard.
|
||||
*/
|
||||
export function quoteQualified(identifier) {
|
||||
const str = String(identifier);
|
||||
if (/^`(?:[^`]|``)+`$/.test(str))
|
||||
return str;
|
||||
return str
|
||||
.split(".")
|
||||
.map((part) => (part === "*" ? "*" : quoteIdentifier(part)))
|
||||
.join(".");
|
||||
}
|
||||
/**
|
||||
* Build a column reference, prefixing the table when one is given and the
|
||||
* field is not already qualified.
|
||||
*/
|
||||
export function columnRef(field, table) {
|
||||
const fieldStr = String(field);
|
||||
if (!table || fieldStr.includes("."))
|
||||
return quoteQualified(fieldStr);
|
||||
return `${quoteQualified(table)}.${fieldStr === "*" ? "*" : quoteIdentifier(fieldStr)}`;
|
||||
}
|
||||
/**
|
||||
* Escape a value for use inside a single-quoted SQL string literal.
|
||||
*/
|
||||
export function escapeStringLiteral(value) {
|
||||
return String(value).replace(/\\/g, "\\\\").replace(/'/g, "''");
|
||||
}
|
||||
export function sqlLogicalOperator(operator) {
|
||||
if (!operator)
|
||||
return "AND";
|
||||
const upper = String(operator).toUpperCase();
|
||||
if (ServerQueryOperators.includes(upper)) {
|
||||
return upper;
|
||||
}
|
||||
throw new Error(`Invalid logical operator: ${operator}`);
|
||||
}
|
||||
export function sqlOrderStrategy(strategy) {
|
||||
if (!strategy)
|
||||
return "ASC";
|
||||
const upper = String(strategy).toUpperCase();
|
||||
if (upper === "ASC" || upper === "DESC")
|
||||
return upper;
|
||||
throw new Error(`Invalid order strategy: ${strategy}`);
|
||||
}
|
||||
export function sqlRawComparison(comparison) {
|
||||
const upper = String(comparison).toUpperCase();
|
||||
if (SQlComparisons.includes(upper))
|
||||
return upper;
|
||||
throw new Error(`Invalid comparison operator: ${comparison}`);
|
||||
}
|
||||
const JoinTypes = ["INNER JOIN", "JOIN", "LEFT JOIN", "RIGHT JOIN"];
|
||||
export function sqlJoinType(joinType) {
|
||||
const upper = String(joinType).toUpperCase();
|
||||
if (JoinTypes.includes(upper))
|
||||
return upper;
|
||||
throw new Error(`Invalid join type: ${joinType}`);
|
||||
}
|
||||
/**
|
||||
* Validate a LIMIT / OFFSET / page value as a non-negative integer.
|
||||
*/
|
||||
export function sqlNonNegativeInt(value, name) {
|
||||
const num = typeof value == "string" && value.trim() !== "" ? Number(value) : value;
|
||||
if (typeof num != "number" || !Number.isInteger(num) || num < 0) {
|
||||
throw new Error(`Invalid ${name}: ${String(value)}`);
|
||||
}
|
||||
return num;
|
||||
}
|
||||
Reference in New Issue
Block a user