@minatojs/sql-utils
Version:
SQL Utilities for Minato
574 lines (572 loc) • 26.2 kB
JavaScript
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
// src/index.ts
import { isNullable } from "cosmokit";
import {
Eval,
Field,
flatten,
isAggrExpr,
isComparable,
isEvalExpr,
isFlat,
randomId,
Selection,
Type,
unravel
} from "minato";
function escapeId(value) {
return "`" + value + "`";
}
__name(escapeId, "escapeId");
function isBracketed(value) {
return value.startsWith("(") && value.endsWith(")");
}
__name(isBracketed, "isBracketed");
function isSqlJson(type) {
return type ? type.type === "json" || !!type.inner : false;
}
__name(isSqlJson, "isSqlJson");
var Builder = class {
constructor(driver, tables) {
this.driver = driver;
this.state.tables = tables;
this.queryOperators = {
// logical
$or: /* @__PURE__ */ __name((key, value) => this.logicalOr(value.map((value2) => this.parseFieldQuery(key, value2))), "$or"),
$and: /* @__PURE__ */ __name((key, value) => this.logicalAnd(value.map((value2) => this.parseFieldQuery(key, value2))), "$and"),
$not: /* @__PURE__ */ __name((key, value) => this.logicalNot(this.parseFieldQuery(key, value)), "$not"),
// existence
$exists: /* @__PURE__ */ __name((key, value) => this.createNullQuery(key, value), "$exists"),
// comparison
$eq: this.createEqualQuery,
$ne: this.comparator("!="),
$gt: this.comparator(">"),
$gte: this.comparator(">="),
$lt: this.comparator("<"),
$lte: this.comparator("<="),
// membership
$in: /* @__PURE__ */ __name((key, value) => this.createMemberQuery(key, value, ""), "$in"),
$nin: /* @__PURE__ */ __name((key, value) => this.createMemberQuery(key, value, " NOT"), "$nin"),
// regexp
$regex: /* @__PURE__ */ __name((key, value) => this.createRegExpQuery(key, value), "$regex"),
$regexFor: /* @__PURE__ */ __name((key, value) => typeof value === "string" ? `${this.escape(value)} collate utf8mb4_bin regexp ${key}` : `${this.escape(value.input)} ${value.flags?.includes("i") ? "regexp" : "collate utf8mb4_bin regexp"} ${key}`, "$regexFor"),
// bitwise
$bitsAllSet: /* @__PURE__ */ __name((key, value) => `${key} & ${this.escape(value)} = ${this.escape(value)}`, "$bitsAllSet"),
$bitsAllClear: /* @__PURE__ */ __name((key, value) => `${key} & ${this.escape(value)} = 0`, "$bitsAllClear"),
$bitsAnySet: /* @__PURE__ */ __name((key, value) => `${key} & ${this.escape(value)} != 0`, "$bitsAnySet"),
$bitsAnyClear: /* @__PURE__ */ __name((key, value) => `${key} & ${this.escape(value)} != ${this.escape(value)}`, "$bitsAnyClear"),
// list
$el: /* @__PURE__ */ __name((key, value) => {
if (Array.isArray(value)) {
return this.logicalOr(value.map((value2) => this.createElementQuery(key, value2)));
} else if (typeof value !== "number" && typeof value !== "string") {
throw new TypeError("query expr under $el is not supported");
} else {
return this.createElementQuery(key, value);
}
}, "$el"),
$size: /* @__PURE__ */ __name((key, value) => {
if (this.isJsonQuery(key)) {
return `${this.jsonLength(key)} = ${this.escape(value)}`;
} else {
if (!value) return this.logicalNot(key);
return `${key} AND LENGTH(${key}) - LENGTH(REPLACE(${key}, ${this.escape(",")}, ${this.escape("")})) = ${this.escape(value)} - 1`;
}
}, "$size")
};
this.evalOperators = {
// universal
$: /* @__PURE__ */ __name((key) => this.getRecursive(key), "$"),
$select: /* @__PURE__ */ __name((args) => `${args.map((arg) => this.parseEval(arg)).join(", ")}`, "$select"),
$if: /* @__PURE__ */ __name((args) => `if(${args.map((arg) => this.parseEval(arg)).join(", ")})`, "$if"),
$ifNull: /* @__PURE__ */ __name((args) => `ifnull(${args.map((arg) => this.parseEval(arg)).join(", ")})`, "$ifNull"),
// number
$add: /* @__PURE__ */ __name((args) => `(${args.map((arg) => this.parseEval(arg)).join(" + ")})`, "$add"),
$multiply: /* @__PURE__ */ __name((args) => `(${args.map((arg) => this.parseEval(arg)).join(" * ")})`, "$multiply"),
$subtract: this.binary("-"),
$divide: this.binary("/"),
$modulo: this.binary("%"),
// mathemetic
$abs: /* @__PURE__ */ __name((arg) => `abs(${this.parseEval(arg)})`, "$abs"),
$floor: /* @__PURE__ */ __name((arg) => `floor(${this.parseEval(arg)})`, "$floor"),
$ceil: /* @__PURE__ */ __name((arg) => `ceil(${this.parseEval(arg)})`, "$ceil"),
$round: /* @__PURE__ */ __name((arg) => `round(${this.parseEval(arg)})`, "$round"),
$exp: /* @__PURE__ */ __name((arg) => `exp(${this.parseEval(arg)})`, "$exp"),
$log: /* @__PURE__ */ __name((args) => `log(${args.filter((x) => !isNullable(x)).map((arg) => this.parseEval(arg)).reverse().join(", ")})`, "$log"),
$power: /* @__PURE__ */ __name((args) => `power(${args.map((arg) => this.parseEval(arg)).join(", ")})`, "$power"),
$random: /* @__PURE__ */ __name(() => `rand()`, "$random"),
// string
$concat: /* @__PURE__ */ __name((args) => `concat(${args.map((arg) => this.parseEval(arg)).join(", ")})`, "$concat"),
$regex: /* @__PURE__ */ __name(([key, value, flags]) => `(${this.parseEval(key)} ${flags?.includes("i") || value instanceof RegExp && value.flags.includes("i") ? "regexp" : "collate utf8mb4_bin regexp"} ${this.parseEval(value)})`, "$regex"),
// logical / bitwise
$or: /* @__PURE__ */ __name((args) => {
const type = Type.fromTerm(this.state.expr, Type.Boolean);
if (Field.boolean.includes(type.type)) return this.logicalOr(args.map((arg) => this.parseEval(arg)));
else return `(${args.map((arg) => this.parseEval(arg)).join(" | ")})`;
}, "$or"),
$and: /* @__PURE__ */ __name((args) => {
const type = Type.fromTerm(this.state.expr, Type.Boolean);
if (Field.boolean.includes(type.type)) return this.logicalAnd(args.map((arg) => this.parseEval(arg)));
else return `(${args.map((arg) => this.parseEval(arg)).join(" & ")})`;
}, "$and"),
$not: /* @__PURE__ */ __name((arg) => {
const type = Type.fromTerm(this.state.expr, Type.Boolean);
if (Field.boolean.includes(type.type)) return this.logicalNot(this.parseEval(arg));
else return `(~(${this.parseEval(arg)}))`;
}, "$not"),
// boolean
$eq: this.binary("="),
$ne: this.binary("!="),
$gt: this.binary(">"),
$gte: this.binary(">="),
$lt: this.binary("<"),
$lte: this.binary("<="),
// membership
$in: /* @__PURE__ */ __name(([key, value]) => this.asEncoded(this.createMemberQuery(this.parseEval(key, false), value, ""), false), "$in"),
$nin: /* @__PURE__ */ __name(([key, value]) => this.asEncoded(this.createMemberQuery(this.parseEval(key, false), value, " NOT"), false), "$nin"),
// typecast
$literal: /* @__PURE__ */ __name(([value, type]) => this.escape(value, type), "$literal"),
// aggregation
$sum: /* @__PURE__ */ __name((expr) => this.createAggr(expr, (value) => `ifnull(sum(${value}), 0)`), "$sum"),
$avg: /* @__PURE__ */ __name((expr) => this.createAggr(expr, (value) => `avg(${value})`), "$avg"),
$min: /* @__PURE__ */ __name((expr) => this.createAggr(expr, (value) => `min(${value})`), "$min"),
$max: /* @__PURE__ */ __name((expr) => this.createAggr(expr, (value) => `max(${value})`), "$max"),
$count: /* @__PURE__ */ __name((expr) => this.createAggr(expr, (value) => `count(distinct ${value})`), "$count"),
$length: /* @__PURE__ */ __name((expr) => this.createAggr(expr, (value) => `count(${value})`, (value) => this.isEncoded() ? this.jsonLength(value) : this.asEncoded(`if(${value}, LENGTH(${value}) - LENGTH(REPLACE(${value}, ${this.escape(",")}, ${this.escape("")})) + 1, 0)`, false)), "$length"),
$object: /* @__PURE__ */ __name((fields) => this.groupObject(fields), "$object"),
$array: /* @__PURE__ */ __name((expr) => this.groupArray(this.transform(this.parseEval(expr, false), expr, "encode")), "$array"),
$get: /* @__PURE__ */ __name(([x, key]) => typeof key === "string" ? this.asEncoded(`json_extract(${this.parseEval(x, false)}, '$.${key}')`, true) : this.asEncoded(`json_extract(${this.parseEval(x, false)}, concat('$[', ${this.parseEval(key)}, ']'))`, true), "$get"),
$exec: /* @__PURE__ */ __name((sel) => this.parseSelection(sel), "$exec")
};
}
static {
__name(this, "Builder");
}
escapeMap = {};
escapeRegExp;
createEqualQuery = this.comparator("=");
queryOperators;
evalOperators;
state = {};
$true = "1";
$false = "0";
modifiedTable;
transformers = /* @__PURE__ */ Object.create(null);
createNullQuery(key, value) {
return `${key} is ${value ? "not " : ""}null`;
}
createMemberQuery(key, value, notStr = "") {
if (Array.isArray(value)) {
if (!value.length) return notStr ? this.$true : this.$false;
if (Array.isArray(value[0])) {
return `(${key})${notStr} in (${value.map((val) => `(${val.map((x) => this.escape(x)).join(", ")})`).join(", ")})`;
}
return `${key}${notStr} in (${value.map((val) => this.escape(val)).join(", ")})`;
} else if (value.$exec) {
return `(${key})${notStr} in ${this.parseSelection(value.$exec, true)}`;
} else if (Type.fromTerm(value)?.type === "list") {
const res = this.listContains(this.parseEval(value), key);
return notStr ? this.logicalNot(res) : res;
} else {
const res = this.jsonContains(this.parseEval(value, false), this.encode(key, true, true));
return notStr ? this.logicalNot(res) : res;
}
}
createRegExpQuery(key, value) {
if (typeof value !== "string" && value.flags?.includes("i")) {
return `${key} regexp ${this.escape(value.source)}`;
} else {
return `${key} collate utf8mb4_bin regexp ${this.escape(typeof value === "string" ? value : value.source)}`;
}
}
listContains(list, value) {
return `find_in_set(${value}, ${list})`;
}
createElementQuery(key, value) {
if (this.isJsonQuery(key)) {
return this.jsonContains(key, this.encode(this.escape(value), true, true));
} else {
return `find_in_set(${this.escape(value)}, ${key})`;
}
}
isJsonQuery(key) {
return Type.fromTerm(this.state.expr)?.type === "json" || this.isEncoded(key);
}
comparator(operator) {
return (key, value) => {
return `${key} ${operator} ${this.escape(value)}`;
};
}
binary(operator) {
return ([left, right]) => {
return `(${this.parseEval(left)} ${operator} ${this.parseEval(right)})`;
};
}
logicalAnd(conditions) {
if (!conditions.length) return this.$true;
if (conditions.includes(this.$false)) return this.$false;
return conditions.join(" AND ");
}
logicalOr(conditions) {
if (!conditions.length) return this.$false;
if (conditions.includes(this.$true)) return this.$true;
return `(${conditions.join(" OR ")})`;
}
logicalNot(condition) {
return `NOT(${condition})`;
}
parseSelection(sel, inline = false) {
const { args: [expr], ref, table, tables } = sel;
const restore = this.saveState({ tables });
const inner = this.get(table, true, true);
const output = this.parseEval(expr, false);
const fields = expr["$select"]?.map((x) => this.getRecursive(x["$"]));
const where = fields && this.logicalAnd(fields.map((x) => `(${x} is not null)`));
restore();
if (inline || !isAggrExpr(expr)) {
return `(SELECT ${output} FROM ${inner} ${isBracketed(inner) ? ref : ""}${where ? ` WHERE ${where}` : ""})`;
} else {
return [
`(ifnull((SELECT ${this.groupArray(this.transform(output, Type.getInner(Type.fromTerm(expr)), "encode"))}`,
`FROM ${inner} ${isBracketed(inner) ? ref : ""}), json_array()))`
].join(" ");
}
}
jsonLength(value) {
return this.asEncoded(`json_length(${value})`, false);
}
jsonContains(obj, value) {
return this.asEncoded(`json_contains(${obj}, ${value})`, false);
}
asEncoded(value, encoded) {
if (encoded !== void 0) this.state.encoded = encoded;
return value;
}
encode(value, encoded, pure = false, type) {
return this.asEncoded(encoded === this.isEncoded() && !pure ? value : encoded ? `cast(${this.transform(value, type, "encode")} as json)` : this.transform(`json_unquote(${value})`, type, "decode"), pure ? void 0 : encoded);
}
isEncoded(key) {
return key ? this.state.encodedMap?.[key] : this.state.encoded;
}
createAggr(expr, aggr, nonaggr) {
if (this.state.group) {
this.state.group = false;
const value = aggr(this.parseEval(expr, false));
this.state.group = true;
return value;
} else {
const value = this.parseEval(expr, false);
const res = nonaggr ? nonaggr(value) : `(select ${aggr(`json_unquote(${this.escapeId("value")})`)} from json_table(${value}, '$[*]' columns (value json path '$')) ${randomId()})`;
return res;
}
}
/**
* Convert value from SQL field to JSON field
*/
transform(value, type, method, miss) {
type = Type.isType(type) ? type : Type.fromTerm(type);
const transformer = this.transformers[type.type] ?? this.transformers[this.driver.database.types[type.type]?.type];
return transformer?.[method] ? transformer[method](value) : miss ?? value;
}
groupObject(_fields) {
const _groupObject = /* @__PURE__ */ __name((fields, type, prefix = "") => {
const parse = /* @__PURE__ */ __name((expr, key) => {
const value = !_fields[`${prefix}${key}`] && type && Type.getInner(type, key)?.inner ? _groupObject(expr, Type.getInner(type, key), `${prefix}${key}.`) : this.parseEval(expr, false);
return this.isEncoded() ? `json_extract(${value}, '$')` : this.transform(value, expr, "encode");
}, "parse");
return `json_object(` + Object.entries(fields).map(([key, expr]) => `'${key}', ${parse(expr, key)}`).join(",") + `)`;
}, "_groupObject");
return this.asEncoded(_groupObject(unravel(_fields), Type.fromTerm(this.state.expr), ""), true);
}
groupArray(value) {
return this.asEncoded(`ifnull(json_arrayagg(${value}), json_array())`, true);
}
parseFieldQuery(key, query) {
const conditions = [];
if (Array.isArray(query)) {
conditions.push(this.createMemberQuery(key, query));
} else if (query instanceof RegExp) {
conditions.push(this.createRegExpQuery(key, query));
} else if (isComparable(query)) {
conditions.push(this.createEqualQuery(key, query));
} else if (isNullable(query)) {
conditions.push(this.createNullQuery(key, false));
} else {
for (const prop in query) {
if (prop in this.queryOperators) {
conditions.push(this.queryOperators[prop](key, query[prop]));
}
}
}
return this.logicalAnd(conditions);
}
parseQuery(query) {
const conditions = [];
for (const key in query) {
if (key === "$not") {
conditions.push(this.logicalNot(this.parseQuery(query.$not)));
} else if (key === "$and") {
conditions.push(this.logicalAnd(query.$and.map(this.parseQuery.bind(this))));
} else if (key === "$or") {
conditions.push(this.logicalOr(query.$or.map(this.parseQuery.bind(this))));
} else if (key === "$expr") {
conditions.push(this.parseEval(query.$expr));
} else {
const flattenQuery = isFlat(query[key]) ? { [key]: query[key] } : flatten(query[key], `${key}.`);
for (const key2 in flattenQuery) {
const model = this.state.tables[this.state.table] ?? Object.values(this.state.tables)[0];
const expr = Eval("", [this.state.table ?? Object.keys(this.state.tables)[0], key2], model.getType(key2));
conditions.push(this.parseFieldQuery(this.parseEval(expr), flattenQuery[key2]));
}
}
}
return this.logicalAnd(conditions);
}
parseEvalExpr(expr) {
this.state.encoded = false;
for (const key in expr) {
if (key in this.evalOperators) {
this.state.expr = expr;
return this.evalOperators[key](expr[key]);
}
}
return this.escape(expr);
}
transformJsonField(obj, path) {
return this.asEncoded(`json_extract(${obj}, '$${path}')`, true);
}
transformKey(key, fields, prefix) {
if (key in fields || !key.includes(".")) {
return this.asEncoded(prefix + this.escapeId(key), this.isEncoded(key) ?? isSqlJson(fields[key]?.type));
}
const field = Object.keys(fields).find((k) => key.startsWith(k + ".")) || key.split(".")[0];
const rest = key.slice(field.length + 1).split(".");
return this.transformJsonField(`${prefix}${this.escapeId(field)}`, rest.map((key2) => `.${this.escapeKey(key2)}`).join(""));
}
getRecursive(args) {
if (typeof args === "string") {
return this.getRecursive(["_", args]);
}
const [table, key] = args;
const fields = this.state.tables?.[table]?.fields || {};
const fkey = Object.keys(fields).find((field) => key === field || key.startsWith(field + "."));
if (fkey && fields[fkey]?.expr) {
if (key === fkey) {
return this.parseEvalExpr(fields[fkey]?.expr);
} else {
const field = this.parseEvalExpr(fields[fkey]?.expr);
const rest = key.slice(fkey.length + 1).split(".");
return this.transformJsonField(`${field}`, rest.map((key2) => `.${this.escapeKey(key2)}`).join(""));
}
}
const prefix = this.modifiedTable ? `${this.escapeId(this.state.tables?.[table]?.name ?? this.modifiedTable)}.` : !this.state.tables || table === "_" || key in fields || table in this.state.tables ? "" : `${this.escapeId(table)}.`;
if (!(table in (this.state.tables || {})) && table in (this.state.innerTables || {})) {
const fields2 = this.state.innerTables?.[table]?.fields || {};
const res = fields2[key]?.expr ? this.parseEvalExpr(fields2[key]?.expr) : this.transformKey(key, fields2, `${this.escapeId(table)}.`);
return res;
}
if (!(table in (this.state.tables || {})) && table in (this.state.refTables || {})) {
const fields2 = this.state.refTables?.[table]?.fields || {};
const res = fields2[key]?.expr ? this.parseEvalExpr(fields2[key]?.expr) : this.transformKey(key, fields2, `${this.escapeId(table)}.`);
if (this.state.wrappedSubquery) {
if (res in (this.state.refFields ?? {})) return this.state.refFields[res];
const key2 = `minato_tvar_${randomId()}`;
(this.state.refFields ??= {})[res] = key2;
return this.asEncoded(this.escapeId(key2), true);
} else return res;
}
return this.transformKey(key, fields, prefix);
}
parseEval(expr, unquote = true) {
this.state.encoded = false;
if (typeof expr === "string" || typeof expr === "number" || typeof expr === "boolean" || expr instanceof Date || expr instanceof RegExp) {
return this.escape(expr);
}
return unquote ? this.encode(this.parseEvalExpr(expr), false, false, Type.fromTerm(expr)) : this.parseEvalExpr(expr);
}
saveState(extra = {}) {
const thisState = this.state;
this.state = { refTables: { ...this.state.refTables || {}, ...this.state.tables || {} }, ...extra };
return () => {
thisState.encoded = this.state.encoded;
this.state = thisState;
};
}
suffix(modifier) {
const { limit, offset, sort, group, having } = modifier;
let sql = "";
if (group?.length) {
sql += ` GROUP BY ${group.map(this.escapeId).join(", ")}`;
const filter = this.parseEval(having);
if (filter !== this.$true) sql += ` HAVING ${filter}`;
}
if (sort.length) {
sql += " ORDER BY " + sort.map(([expr, dir]) => {
return `${this.parseEval(expr)} ${dir.toUpperCase()}`;
}).join(", ");
}
if (limit < Infinity) sql += " LIMIT " + limit;
if (offset > 0) sql += " OFFSET " + offset;
return sql;
}
get(sel, inline = false, group = false, addref = true) {
const { args, table, query, ref, model } = sel;
this.state.table = ref;
let prefix;
if (typeof table === "string") {
prefix = this.escapeId(table);
} else if (Selection.is(table)) {
prefix = this.get(table, true);
if (!prefix) return;
} else {
this.state.innerTables = Object.fromEntries(Object.values(table).map((t) => [t.ref, t.model]));
const joins = Object.entries(table).map(([key, table2]) => {
const restore = this.saveState({ tables: { ...table2.tables } });
const t = `${this.get(table2, true, false, false)} AS ${this.escapeId(table2.ref)}`;
restore();
return [key, t];
});
prefix = [
// the leading space is to prevent from being parsed as bracketed and added ref
" ",
joins[0][1],
...joins.slice(1, -1).map(([key, join]) => `${args[0].optional?.[key] ? "LEFT" : ""} JOIN ${join} ON ${this.$true}`),
`${args[0].optional?.[joins.at(-1)[0]] ? "LEFT " : ""}JOIN`,
joins.at(-1)[1]
].join(" ");
const filter2 = this.parseEval(args[0].having);
prefix += ` ON ${filter2}`;
}
const filter = this.parseQuery(query);
if (filter === this.$false) return;
this.state.group = group || !!args[0].group;
const encodedMap = {};
const fields = args[0].fields ?? Object.fromEntries(Object.entries(model.fields).filter(([, field]) => Field.available(field)).map(([key, field]) => [key, field.expr ? field.expr : Eval("", [ref, key], Type.fromField(field))]));
const keys = Object.entries(fields).map(([key, value]) => {
value = this.parseEval(value, false);
encodedMap[key] = this.state.encoded;
return this.escapeId(key) === value ? this.escapeId(key) : `${value} AS ${this.escapeId(key)}`;
}).join(", ");
let suffix = this.suffix(args[0]);
this.state.encodedMap = encodedMap;
if (filter !== this.$true) {
suffix = ` WHERE ${filter}` + suffix;
}
if (inline && !args[0].fields && !suffix && (typeof table === "string" || Selection.is(table))) {
return addref && isBracketed(prefix) ? `${prefix} ${ref}` : prefix;
}
if (!prefix.includes(" ") || isBracketed(prefix)) {
suffix = ` ${ref}` + suffix;
}
const result = `SELECT ${keys} FROM ${prefix}${suffix}`;
return inline ? `(${result})` : result;
}
/**
* Convert value from Type to Field.Type.
* @param root indicate whether the context is inside json
*/
dump(value, type, root = true) {
if (!type) return value;
if (Type.isType(type) || isEvalExpr(type)) {
type = Type.isType(type) ? type : Type.fromTerm(type);
const converter = type.inner || type.type === "json" ? root ? this.driver.types["json"] : void 0 : this.driver.types[type.type];
if (type.inner || type.type === "json") root = false;
let res = value;
res = Type.transform(res, type, (value2, type2) => this.dump(value2, type2, root));
res = converter?.dump ? converter.dump(res) : res;
const ancestor = this.driver.database.types[type.type]?.type;
if (!root && !ancestor) res = this.transform(res, type, "dump");
res = this.dump(res, ancestor ? Type.fromField(ancestor) : void 0, root);
return res;
}
value = type.format(value);
const result = {};
for (const key in value) {
const { type: ftype } = type.fields[key];
result[key] = this.dump(value[key], ftype);
}
return result;
}
/**
* Convert value from Field.Type to Type.
*/
load(value, type, root = true) {
if (!type) return value;
if (Type.isType(type) || isEvalExpr(type)) {
type = Type.isType(type) ? type : Type.fromTerm(type);
const converter = this.driver.types[root && value && type.type === "json" ? "json" : type.type];
const ancestor = this.driver.database.types[type.type]?.type;
let res = this.load(value, ancestor ? Type.fromField(ancestor) : void 0, root);
res = this.transform(res, type, "load");
res = converter?.load ? converter.load(res) : res;
res = Type.transform(res, type, (value2, type2) => this.load(value2, type2, false));
return !isNullable(res) && type.inner && !Type.isArray(type) ? unravel(res) : res;
}
const result = {};
for (const key in value) {
if (!(key in type.fields)) continue;
result[key] = value[key];
let subroot = root;
if (subroot && result[key] && this.isEncoded(key)) {
subroot = false;
result[key] = this.driver.types["json"].load(result[key]);
}
result[key] = this.load(result[key], type.fields[key].type, subroot);
}
return type.parse(result);
}
/**
* Convert value from Type to SQL.
*/
escape(value, type) {
type &&= Type.fromField(type);
return this.escapePrimitive(type ? this.dump(value, type) : value, type);
}
/**
* Convert value from Field.Type to SQL.
*/
escapePrimitive(value, type) {
if (isNullable(value)) return "NULL";
switch (typeof value) {
case "boolean":
case "number":
case "bigint":
return value + "";
case "object":
return this.quote(JSON.stringify(value));
default:
return this.quote(value);
}
}
escapeId(value) {
return escapeId(value);
}
escapeKey(value) {
return `"${value}"`;
}
quote(value) {
this.escapeRegExp ??= new RegExp(`[${Object.values(this.escapeMap).join("")}]`, "g");
let chunkIndex = this.escapeRegExp.lastIndex = 0;
let escapedVal = "";
let match;
while (match = this.escapeRegExp.exec(value)) {
escapedVal += value.slice(chunkIndex, match.index) + this.escapeMap[match[0]];
chunkIndex = this.escapeRegExp.lastIndex;
}
if (chunkIndex === 0) {
return "'" + value + "'";
}
if (chunkIndex < value.length) {
return "'" + escapedVal + value.slice(chunkIndex) + "'";
}
return "'" + escapedVal + "'";
}
};
export {
Builder,
escapeId,
isBracketed,
isSqlJson
};
//# sourceMappingURL=index.mjs.map