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; }