alinea
Version:
[](https://npmjs.org/package/alinea) [](https://packagephobia.com/result?p=alinea)
1,482 lines (1,474 loc) • 45.7 kB
JavaScript
import {
sql
} from "./chunk-FLZ4KUMA.js";
import {
Action,
BinOpType,
Callable,
ColumnType,
Expr,
ExprData,
ExprType,
OrderDirection,
ParamData,
ParamType,
Query,
QueryData,
QueryType,
Schema,
Statement,
Table,
Target,
TargetType,
UnOpType
} from "./chunk-4JLFL6LD.js";
// node_modules/rado/dist/lib/SqlError.js
var SqlError = class extends Error {
constructor(cause, sql2) {
super(`${sql2}
\u2502
\u2514\u2500\u2500\u2500\u2500 ${cause}`, { cause });
}
};
// node_modules/rado/dist/lib/Driver.js
function isTemplateStringsArray(input) {
return Boolean(Array.isArray(input) && input.raw);
}
var DriverBase = class extends Callable {
constructor(formatter, options) {
super((...args) => {
const [input, ...rest] = args;
if (Query.isQuery(input) && rest.length === 0)
return this.executeQuery(input[Query.Data]);
if (isTemplateStringsArray(input))
return this.executeTemplate(void 0, input, ...rest);
return this.executeQuery(
new QueryData.Batch({
queries: args.filter(Query.isQuery).map((arg) => arg[Query.Data])
})
);
});
this.formatter = formatter;
this.options = options;
}
compile(create) {
const { length } = create;
const paramNames = Array.from({ length }, (_, i) => `p${i}`);
const params = paramNames.map(
(name) => new Expr(new ExprData.Param(new ParamData.Named(name)))
);
const cursor = create(...params);
const query = cursor[Query.Data];
return [query, this.formatter.compile(query)];
}
executeTemplate(expectedReturn, strings, ...params) {
return this.executeQuery(
new QueryData.Raw({ strings, params, expectedReturn })
);
}
};
var SyncDriver = class extends DriverBase {
transactionId;
constructor(formatter, options = {}) {
super(formatter, options);
this.transactionId = 0;
}
createStatement(stmt, discardAfter) {
try {
return this.handle(stmt, this.prepareStatement(stmt, discardAfter));
} catch (e) {
throw new SqlError(e, stmt.sql);
}
}
handle(stmt, prepared) {
const { logQuery } = this.options;
const wrap = (fn) => (...args) => {
const startTime = performance.now();
try {
const res = fn(...args);
if (logQuery)
logQuery(stmt, performance.now() - startTime);
return res;
} catch (e) {
throw new SqlError(e, stmt.sql);
}
};
return {
run: wrap(prepared.run.bind(prepared)),
iterate: wrap(prepared.iterate.bind(prepared)),
all: wrap(prepared.all.bind(prepared)),
get: wrap(prepared.get.bind(prepared)),
execute: wrap(prepared.execute.bind(prepared))
};
}
prepare(create) {
const [query, compiled] = this.compile(create);
const prepared = this.createStatement(compiled, false);
return (...params) => {
const namedParams = Object.fromEntries(
params.map((value, i) => [`p${i}`, value])
);
return this.executeQuery(
query,
prepared,
compiled.params(namedParams)
);
};
}
migrateSchema(...tables) {
const queries = [];
for (const current of Object.values(tables)) {
const schema = current[Table.Data];
const localSchema = this.schemaInstructions(schema.name);
if (!localSchema) {
queries.push(...Schema.create(schema).queries);
} else {
const changes = Schema.upgrade(this.formatter, localSchema, schema);
if (changes.length)
queries.push(...changes);
}
}
return this.executeQuery(new QueryData.Batch({ queries }));
}
executeQuery(query, stmt, params) {
switch (query.type) {
case QueryType.Batch:
let result;
const stmts = query.queries;
if (stmts.length === 0)
return void 0;
if (stmts.length === 1)
return this.executeQuery(stmts[0]);
return this.transaction((cnx) => {
for (const query2 of stmts)
result = cnx.executeQuery(query2);
return result;
});
default:
const compiled = stmt ? void 0 : this.formatter.compile(query);
stmt = stmt || this.createStatement(compiled, true);
params = params || compiled.params();
if ("selection" in query || query.type === QueryType.Union) {
const res = stmt.all(params).map((row) => JSON.parse(row.result).result);
if (query.singleResult) {
const row = res[0];
if (query.validate && row === void 0)
throw new Error("No row found");
return row ?? null;
}
return res;
} else if (query.type === QueryType.Raw) {
switch (query.expectedReturn) {
case "row":
return stmt.all(params)[0];
case "rows":
return stmt.all(params);
default:
stmt.execute(params);
return void 0;
}
} else {
return stmt.run(params);
}
}
}
*iterate(cursor) {
const stmt = this.createStatement(
this.formatter.compile(cursor[Query.Data]),
true
);
for (const row of stmt.iterate()) {
yield JSON.parse(row.result).result;
}
}
transaction(run) {
const id = `t${this.transactionId++}`;
this.executeQuery(
new QueryData.Transaction({ op: QueryData.TransactionOperation.Begin, id })
);
try {
const res = run(this);
this.executeQuery(
new QueryData.Transaction({
op: QueryData.TransactionOperation.Commit,
id
})
);
return res;
} catch (e) {
this.executeQuery(
new QueryData.Transaction({
op: QueryData.TransactionOperation.Rollback,
id
})
);
throw e;
}
}
toAsync() {
return new SyncWrapper(this);
}
};
var AsyncDriver = class extends DriverBase {
transactionId;
constructor(formatter, options = {}) {
super(formatter, options);
this.transactionId = 0;
}
createStatement(stmt, discardAfter) {
try {
return this.handle(stmt, this.prepareStatement(stmt, discardAfter));
} catch (e) {
throw new SqlError(e, stmt.sql);
}
}
handle(stmt, prepared) {
const { logQuery } = this.options;
const wrap = (fn) => async (...args) => {
const startTime = performance.now();
try {
const res = await fn(...args);
if (logQuery)
logQuery(stmt, performance.now() - startTime);
return res;
} catch (e) {
throw new SqlError(e, stmt.sql);
}
};
return {
run: wrap(prepared.run.bind(prepared)),
async *iterate(params) {
const iterator = prepared.iterate(params);
try {
yield* iterator;
} catch (e) {
throw new SqlError(e, stmt.sql);
}
},
all: wrap(prepared.all.bind(prepared)),
get: wrap(prepared.get.bind(prepared)),
execute: wrap(prepared.execute.bind(prepared))
};
}
prepare(create) {
const [query, compiled] = this.compile(create);
const prepared = this.createStatement(compiled, false);
return (...params) => {
const namedParams = Object.fromEntries(
params.map((value, i) => [`p${i}`, value])
);
return this.executeQuery(
query,
prepared,
compiled.params(namedParams)
);
};
}
async migrateSchema(...tables) {
const queries = [];
for (const current of Object.values(tables)) {
const schema = current[Table.Data];
const localSchema = await this.schemaInstructions(schema.name);
if (!localSchema) {
queries.push(...Schema.create(schema).queries);
} else {
const changes = Schema.upgrade(this.formatter, localSchema, schema);
if (changes.length)
queries.push(...changes);
}
}
return this.executeQuery(new QueryData.Batch({ queries }));
}
async executeQuery(query, stmt, params) {
switch (query.type) {
case QueryType.Batch:
let result;
const stmts = query.queries;
if (stmts.length === 0)
return void 0;
if (stmts.length === 1)
return this.executeQuery(stmts[0]);
return this.transaction(async (cnx) => {
for (const query2 of stmts)
result = await cnx.executeQuery(query2);
return result;
});
default:
const compiled = stmt ? void 0 : this.formatter.compile(query);
stmt = stmt || this.createStatement(compiled, true);
params = params || compiled.params();
if ("selection" in query || query.type === QueryType.Union) {
const res = (await stmt.all(params)).map(
(item) => JSON.parse(item.result).result
);
if (query.singleResult) {
const row = res[0];
if (query.validate && row === void 0)
throw new Error("No row found");
return row ?? null;
}
return res;
} else if (query.type === QueryType.Raw) {
switch (query.expectedReturn) {
case "row":
return (await stmt.all(params))[0];
case "rows":
return await stmt.all(params);
default:
await stmt.execute(params);
return void 0;
}
} else {
return await stmt.run(params);
}
}
}
async *iterate(cursor) {
const stmt = this.createStatement(
this.formatter.compile(cursor[Query.Data]),
true
);
for await (const row of stmt.iterate([])) {
yield JSON.parse(row.result).result;
}
}
async transaction(run) {
const id = `t${this.transactionId++}`;
const [connection, release] = this.isolate();
await connection.executeQuery(
new QueryData.Transaction({ op: QueryData.TransactionOperation.Begin, id })
);
try {
const res = await run(connection);
await connection.executeQuery(
new QueryData.Transaction({
op: QueryData.TransactionOperation.Commit,
id
})
);
return res;
} catch (e) {
await connection.executeQuery(
new QueryData.Transaction({
op: QueryData.TransactionOperation.Rollback,
id
})
).catch(() => {
});
throw e;
} finally {
await release();
}
}
};
var SyncPreparedStatementWrapper = class {
constructor(stmt) {
this.stmt = stmt;
}
async *iterate(params) {
for (const row of this.stmt.iterate(params))
yield row;
}
async run(params) {
return this.stmt.run(params);
}
async all(params) {
return this.stmt.all(params);
}
async get(params) {
return this.stmt.get(params);
}
async execute(params) {
return this.stmt.execute(params);
}
};
var SyncWrapper = class _SyncWrapper extends AsyncDriver {
constructor(sync) {
super(sync.formatter, sync.options);
this.sync = sync;
}
lock;
async close() {
this.sync.close();
}
async executeQuery(query, stmt, params) {
await this.lock;
return super.executeQuery(query, stmt, params);
}
prepareStatement(stmt, discardAfter) {
return new SyncPreparedStatementWrapper(
this.sync.prepareStatement(stmt, discardAfter)
);
}
async schemaInstructions(tableName) {
return this.sync.schemaInstructions(tableName);
}
isolate() {
const connection = new _SyncWrapper(this.sync);
let release, trigger = new Promise((resolve) => {
release = async () => resolve();
});
this.lock = Promise.resolve(this.lock).then(() => trigger);
return [connection, release];
}
};
var Driver;
((Driver2) => {
Driver2.Sync = SyncDriver;
Driver2.Async = AsyncDriver;
})(Driver || (Driver = {}));
// node_modules/rado/dist/lib/Formatter.js
var binOps = {
[BinOpType.Add]: "+",
[BinOpType.Subt]: "-",
[BinOpType.Mult]: "*",
[BinOpType.Mod]: "%",
[BinOpType.Div]: "/",
[BinOpType.Greater]: ">",
[BinOpType.GreaterOrEqual]: ">=",
[BinOpType.Less]: "<",
[BinOpType.LessOrEqual]: "<=",
[BinOpType.Equals]: "=",
[BinOpType.NotEquals]: "!=",
[BinOpType.And]: "AND",
[BinOpType.Or]: "OR",
[BinOpType.Like]: "LIKE",
[BinOpType.Glob]: "GLOB",
[BinOpType.Match]: "MATCH",
[BinOpType.In]: "IN",
[BinOpType.NotIn]: "NOT IN",
[BinOpType.Concat]: "||"
};
var joins = {
left: "LEFT",
inner: "INNER"
};
var unions = {
[QueryData.UnionOperation.Union]: "UNION",
[QueryData.UnionOperation.UnionAll]: "UNION ALL",
[QueryData.UnionOperation.Intersect]: "INTERSECT",
[QueryData.UnionOperation.Except]: "EXCEPT"
};
var actions = {
[Action.NoAction]: "NO ACTION",
[Action.Restrict]: "RESTRICT",
[Action.Cascade]: "CASCADE",
[Action.SetNull]: "SET NULL",
[Action.SetDefault]: "SET DEFAULT"
};
function formatAsResultObject(stmt, mkSubject) {
stmt.raw("json_object('result', ");
mkSubject();
stmt.raw(")");
}
var Formatter = class {
constructor() {
}
compile(query, options) {
const result = this.format(
this.createContext({ topLevel: true, ...options }),
query
);
return result;
}
createContext(options) {
return { stmt: new Statement(this, options), ...options };
}
format(ctx, query) {
switch (query.type) {
case QueryType.Select:
return this.formatSelect(ctx, query);
case QueryType.Union:
return this.formatUnion(ctx, query);
case QueryType.Insert:
return this.formatInsert(ctx, query);
case QueryType.Update:
return this.formatUpdate(ctx, query);
case QueryType.Delete:
return this.formatDelete(ctx, query);
case QueryType.CreateTable:
return this.formatCreateTable(ctx, query);
case QueryType.AlterTable:
return this.formatAlterTable(ctx, query);
case QueryType.DropTable:
return this.formatDropTable(ctx, query);
case QueryType.CreateIndex:
return this.formatCreateIndex(ctx, query);
case QueryType.DropIndex:
return this.formatDropIndex(ctx, query);
case QueryType.Transaction:
return this.formatTransaction(ctx, query);
case QueryType.Batch:
return this.formatBatch(ctx, query);
case QueryType.Raw:
return this.formatRaw(ctx, query);
}
}
formatSelect({ topLevel, ...ctx }, query) {
const { stmt } = ctx;
switch (query.from?.type) {
case TargetType.CTE:
if (ctx.formattingCte !== query.from.name) {
stmt.add("WITH RECURSIVE").addIdentifier(query.from.name).add("AS").space().openParenthesis();
this.formatUnion(
{
...ctx,
topLevel: false,
selectAsColumns: true,
formattingCte: query.from.name
},
query.from.union
);
stmt.closeParenthesis().space();
}
default:
stmt.raw("SELECT").space();
this.formatSelection(
ctx,
query.selection,
topLevel ? formatAsResultObject : void 0
);
if (query.from) {
stmt.addLine("FROM").space();
this.formatTarget(ctx, query.from);
}
this.formatWhere(ctx, query.where);
this.formatGroupBy(ctx, query.groupBy);
this.formatHaving(ctx, query.having);
this.formatOrderBy(ctx, query.orderBy);
this.formatLimit(ctx, query);
return stmt;
}
}
formatUnion(ctx, { a, operator, b }) {
const { stmt } = ctx;
this.format(ctx, a);
stmt.add(unions[operator]).space();
this.format(ctx, typeof b === "function" ? b() : b);
return stmt;
}
formatInsert(ctx, query) {
const { stmt } = ctx;
const columns = Object.values(query.into.columns).map((c) => c.name);
stmt.add("INSERT INTO").addIdentifier(query.into.name);
if (query.into.alias)
stmt.raw("AS").addIdentifier(query.into.alias);
for (const column of stmt.call(columns))
this.formatString(ctx, column);
if (query.data) {
stmt.add("VALUES").space();
for (const row of stmt.separate(query.data))
this.formatInsertRow(ctx, query.into.columns, row);
}
if (query.select) {
stmt.space();
this.formatSelect(
{
...ctx,
topLevel: false,
selectAsColumns: true
},
query.select
);
}
if (query.selection) {
stmt.add("RETURNING").space();
this.formatSelection(ctx, query.selection, formatAsResultObject);
}
return stmt;
}
formatUpdate({ topLevel, ...ctx }, query) {
const { stmt } = ctx;
const data = query.set || {};
stmt.add("UPDATE").addIdentifier(query.table.name).add("SET").space();
const keys = Object.keys(data).filter((key) => query.table.columns[key]);
for (const key of stmt.separate(keys)) {
const column = query.table.columns[key];
stmt.identifier(key).add("=").space();
let input = data[key];
if (Expr.hasExpr(input))
input = input[Expr.ToExpr]();
if (Expr.isExpr(input))
this.formatExpr(
{ ...ctx, formatAsJson: column.type === ColumnType.Json },
ExprData.create(data[key])
);
else
this.formatValue({ ...ctx, formatAsInsert: true }, input);
}
this.formatWhere(ctx, query.where);
this.formatOrderBy(ctx, query.orderBy);
this.formatLimit(ctx, query);
return stmt;
}
formatDelete(ctx, query) {
const { stmt } = ctx;
stmt.add("DELETE FROM").addIdentifier(query.table.name);
this.formatWhere(ctx, query.where);
this.formatOrderBy(ctx, query.orderBy);
this.formatLimit(ctx, query);
return stmt;
}
formatCreateTable(ctx, query) {
const { stmt } = ctx;
stmt.add("CREATE TABLE");
if (query.ifNotExists)
stmt.add("IF NOT EXISTS");
stmt.addIdentifier(query.table.name).space();
stmt.openParenthesis();
for (const column of stmt.separate(Object.values(query.table.columns))) {
this.formatColumn(ctx, column);
}
const meta = query.table.meta();
if (meta.primaryKey) {
stmt.raw(",").newline().raw("PRIMARY KEY").space();
for (const expr of stmt.call(meta.primaryKey))
switch (expr.type) {
case ExprType.Field:
stmt.identifier(expr.field);
continue;
default:
throw new Error(`Cannot format index of type ${expr.type}`);
}
}
stmt.closeParenthesis();
return stmt;
}
formatCreateIndex(ctx, query) {
const { stmt } = ctx;
stmt.add("CREATE");
if (query.index.unique)
stmt.add("UNIQUE");
stmt.add("INDEX");
if (query.ifNotExists)
stmt.add("IF NOT EXISTS");
stmt.addIdentifier(query.index.name).add("ON").addIdentifier(query.table.name);
for (const expr of stmt.call(query.index.on)) {
this.formatExprValue(
{
...ctx,
skipTableName: true,
forceInline: true
},
expr
);
}
this.formatWhere(ctx, query.where);
return stmt;
}
formatDropIndex(ctx, query) {
const { stmt } = ctx;
stmt.add("DROP INDEX");
if (query.ifExists)
stmt.add("IF EXISTS");
stmt.addIdentifier(query.name);
return stmt;
}
formatAlterTable(ctx, query) {
const { stmt } = ctx;
stmt.add("ALTER TABLE").addIdentifier(
query.renameTable ? query.renameTable.from : query.table.name
);
if (query.addColumn) {
stmt.add("ADD COLUMN").space();
this.formatColumn(ctx, query.addColumn);
} else if (query.dropColumn) {
stmt.add("DROP COLUMN").addIdentifier(query.dropColumn);
} else if (query.renameColumn) {
stmt.add("RENAME COLUMN");
stmt.addIdentifier(query.renameColumn.from).add("TO");
stmt.addIdentifier(query.renameColumn.to);
} else if (query.renameTable) {
stmt.add("RENAME TO").addIdentifier(query.table.name);
} else if (query.alterColumn) {
throw new Error(`Not available in this formatter: alter column`);
}
return stmt;
}
formatDropTable(ctx, query) {
const { stmt } = ctx;
stmt.add("DROP TABLE");
if (query.ifExists)
stmt.add("IF EXISTS");
stmt.addIdentifier(query.table.name);
return stmt;
}
formatTransaction(ctx, { op, id }) {
const { stmt } = ctx;
switch (op) {
case QueryData.TransactionOperation.Begin:
return stmt.raw("SAVEPOINT").addIdentifier(id);
case QueryData.TransactionOperation.Commit:
return stmt.raw("RELEASE").addIdentifier(id);
case QueryData.TransactionOperation.Rollback:
return stmt.raw("ROLLBACK TO").addIdentifier(id);
}
}
formatBatch(ctx, { queries }) {
const { stmt } = ctx;
for (const query of stmt.separate(queries, ";"))
this.format(ctx, query);
return stmt;
}
formatRaw(ctx, { strings, params }) {
const { stmt } = ctx;
for (let i = 0; i < strings.length; i++) {
ctx.stmt.raw(strings[i]);
if (i < params.length) {
const param = params[i];
if (Expr.isExpr(param))
this.formatExpr(ctx, param[Expr.Data]);
else
this.formatValue(ctx, param);
}
}
return stmt;
}
formatColumn(ctx, column) {
const { stmt } = ctx;
stmt.identifier(column.name).space();
this.formatType(ctx, column.type);
if (column.unique)
stmt.add("UNIQUE");
if (column.autoIncrement)
stmt.add("AUTOINCREMENT");
if (column.primaryKey)
stmt.add("PRIMARY KEY");
if (!column.nullable)
stmt.add("NOT NULL");
if (column.defaultValue !== void 0) {
if (typeof column.defaultValue !== "function") {
stmt.add("DEFAULT").space();
stmt.openParenthesis();
this.formatExpr(
{
...ctx,
formatAsInsert: true,
forceInline: true
},
column.defaultValue
);
stmt.closeParenthesis();
}
}
if (column.references)
this.formatConstraintReference(ctx, column);
return stmt;
}
formatConstraintReference(ctx, { references, onDelete, onUpdate }) {
const { stmt } = ctx;
if (!references)
return stmt;
const reference = references();
if (reference.type !== ExprType.Field || reference.expr.type !== ExprType.Row)
throw new Error("not supported");
const from = reference.expr.target;
stmt.add("REFERENCES").addIdentifier(Target.source(from).name).openParenthesis().identifier(reference.field).closeParenthesis();
if (onDelete && actions[onDelete])
stmt.add("ON DELETE").add(actions[onDelete]);
if (onUpdate && actions[onUpdate])
stmt.add("ON UPDATE").add(actions[onUpdate]);
return stmt;
}
formatType(ctx, type) {
const { stmt } = ctx;
switch (type) {
case ColumnType.String:
return stmt.raw("TEXT");
case ColumnType.Json:
return stmt.raw("JSON");
case ColumnType.Number:
return stmt.raw("NUMERIC");
case ColumnType.Boolean:
return stmt.raw("BOOLEAN");
case ColumnType.Integer:
return stmt.raw("INTEGER");
}
}
formatInsertRow(ctx, columns, row) {
const { stmt } = ctx;
for (const [property, column] of stmt.call(Object.entries(columns))) {
const columnValue = row[property];
this.formatColumnValue(ctx, column, columnValue);
}
return stmt;
}
formatColumnValue(ctx, column, columnValue) {
const { stmt } = ctx;
if (Expr.isExpr(columnValue))
return this.formatExprValue(ctx, columnValue[Expr.Data]);
const isNull = columnValue === void 0 || columnValue === null;
const isOptional = column.nullable || column.autoIncrement || column.primaryKey;
if (isNull) {
if (column.defaultValue !== void 0) {
if (typeof column.defaultValue === "function")
return this.formatExprJson(
{ ...ctx, formatAsInsert: true },
column.defaultValue()
);
return this.formatExprJson(
{ ...ctx, formatAsInsert: true },
column.defaultValue
);
}
if (!isOptional)
throw new TypeError(`Expected value for column ${column.name}`);
return stmt.raw("NULL");
}
switch (column.type) {
case ColumnType.String:
if (typeof columnValue !== "string")
throw new TypeError(`Expected string for column ${column.name}`);
return stmt.value(columnValue);
case ColumnType.Integer:
case ColumnType.Number:
if (typeof columnValue !== "number")
throw new TypeError(`Expected number for column ${column.name}`);
return stmt.value(columnValue);
case ColumnType.Boolean:
if (typeof columnValue !== "boolean")
throw new TypeError(`Expected boolean for column ${column.name}`);
return stmt.raw(this.escapeValue(columnValue));
case ColumnType.Json:
return stmt.value(JSON.stringify(columnValue));
}
}
formatTarget(ctx, target) {
const { stmt } = ctx;
switch (target.type) {
case TargetType.Table:
stmt.identifier(target.table.name);
if (target.table.alias)
stmt.add("AS").addIdentifier(target.table.alias);
if (target.indexedBy) {
stmt.add("INDEXED BY").addIdentifier(target.indexedBy.name);
}
return stmt;
case TargetType.Join:
const { left, right, joinType } = target;
this.formatTarget(ctx, left);
stmt.addLine(joins[joinType]).add("JOIN").space();
this.formatTarget(ctx, right);
stmt.add("ON").space();
this.formatExprValue(ctx, target.on);
return stmt;
case TargetType.Query:
stmt.openParenthesis();
this.format(ctx, target.query);
stmt.closeParenthesis();
if (target.alias)
stmt.add("AS").addIdentifier(target.alias);
return stmt;
case TargetType.CTE:
return stmt.addIdentifier(target.name);
case TargetType.Expr:
throw new Error("Cannot format expression as target");
}
}
formatWhere({ topLevel, ...ctx }, expr) {
const { stmt } = ctx;
if (!expr)
return stmt;
stmt.addLine("WHERE").space();
this.formatExprValue(ctx, expr);
return stmt;
}
formatHaving({ topLevel, ...ctx }, expr) {
const { stmt } = ctx;
if (!expr)
return stmt;
stmt.addLine("HAVING");
this.formatExprValue(ctx, expr);
return stmt;
}
formatGroupBy(ctx, groupBy) {
const { stmt } = ctx;
if (!groupBy || groupBy.length === 0)
return stmt;
stmt.addLine("GROUP BY").space();
for (const expr of stmt.separate(groupBy))
this.formatExprValue(ctx, expr);
return stmt;
}
formatOrderBy(ctx, orderBy) {
const { stmt } = ctx;
if (!orderBy || orderBy.length === 0)
return stmt;
stmt.addLine("ORDER BY").space();
for (const { expr, order } of stmt.separate(orderBy)) {
this.formatExprValue(ctx, expr);
stmt.add(order === OrderDirection.Asc ? "ASC" : "DESC");
}
return stmt;
}
formatLimit(ctx, { limit, offset, singleResult }) {
const { stmt } = ctx;
if (!limit && !offset && !singleResult)
return stmt;
stmt.addLine("LIMIT").space();
this.formatValue(ctx, (singleResult ? 1 : limit) || -1);
if (offset && offset > 0) {
stmt.add("OFFSET").space();
this.formatValue(ctx, offset);
}
return stmt;
}
formatSelection(ctx, selection, formatSubject) {
const { stmt } = ctx;
const mkSubject = () => this.formatExpr(
{
...ctx,
formatAsJson: true
},
selection
);
if (formatSubject)
formatSubject(stmt, mkSubject);
else
this.formatExpr(ctx, selection);
if (!ctx.selectAsColumns)
stmt.add("AS").addIdentifier("result");
return stmt;
}
formatString(ctx, input) {
const { stmt } = ctx;
return stmt.raw(this.escapeValue(String(input)));
}
formatInlineValue(ctx, rawValue) {
const { stmt } = ctx;
switch (true) {
case (rawValue === null || rawValue === void 0):
return stmt.raw("NULL");
case typeof rawValue === "boolean":
return rawValue ? stmt.raw("TRUE") : stmt.raw("FALSE");
case (typeof rawValue === "string" || typeof rawValue === "number"):
return this.formatString(ctx, rawValue);
default:
return this.formatString(ctx, JSON.stringify(rawValue));
}
}
formatValue(ctx, rawValue, inline = false) {
const { stmt, formatAsJson, formatAsInsert, forceInline } = ctx;
const inlineValue = inline || forceInline;
const asJson = formatAsJson || formatAsInsert;
switch (true) {
case (rawValue === null || rawValue === void 0):
return stmt.raw("NULL");
case ((formatAsInsert || !formatAsJson) && typeof rawValue === "boolean"):
return rawValue ? stmt.raw("1") : stmt.raw("0");
case (!formatAsInsert && Array.isArray(rawValue)):
if (asJson)
stmt.raw("json_array");
stmt.openParenthesis();
for (const v of stmt.separate(rawValue))
this.formatValue({ ...ctx, formatAsJson: asJson }, v);
stmt.closeParenthesis();
return stmt;
case (typeof rawValue === "string" || typeof rawValue === "number"):
if (inlineValue)
return stmt.raw(this.escapeValue(rawValue));
return stmt.value(rawValue);
default:
if (formatAsJson) {
stmt.raw("json");
stmt.openParenthesis();
}
if (inlineValue)
this.formatString(ctx, JSON.stringify(rawValue));
else
stmt.value(JSON.stringify(rawValue));
if (formatAsJson)
stmt.closeParenthesis();
return stmt;
}
}
formatExprJson(ctx, expr) {
return this.formatExpr({ ...ctx, formatAsJson: true }, expr);
}
formatExprValue(ctx, expr) {
return this.formatExpr({ ...ctx, formatAsJson: false }, expr);
}
formatExpr(ctx, expr) {
switch (expr.type) {
case ExprType.UnOp:
return this.formatUnOp(ctx, expr);
case ExprType.BinOp:
return this.formatBinOp(ctx, expr);
case ExprType.Param:
return this.formatParam(ctx, expr);
case ExprType.Field:
return this.formatField(ctx, expr);
case ExprType.Call:
return this.formatCall(ctx, expr);
case ExprType.Query:
return this.formatQuery(ctx, expr);
case ExprType.Row:
return this.formatRow(ctx, expr);
case ExprType.Merge:
return this.formatMerge(ctx, expr);
case ExprType.Record:
return this.formatRecord(ctx, expr);
case ExprType.Filter:
return this.formatFilter(ctx, expr);
case ExprType.Map:
return this.formatMap(ctx, expr);
}
}
formatUnOp(ctx, expr) {
const { stmt } = ctx;
switch (expr.op) {
case UnOpType.IsNull:
return this.formatExprValue(ctx, expr.expr).add("IS NULL");
case UnOpType.Not:
stmt.raw("NOT").space().openParenthesis();
this.formatExprValue(ctx, expr.expr);
return stmt.closeParenthesis();
}
}
formatBinOp(ctx, expr) {
const { stmt } = ctx;
stmt.openParenthesis();
this.formatExprValue(ctx, expr.a).add(binOps[expr.op]).space();
const asIn = expr.op === BinOpType.In || expr.op === BinOpType.NotIn;
if (asIn)
this.formatIn(ctx, expr.b);
else
this.formatExprValue(ctx, expr.b);
return stmt.closeParenthesis();
}
formatParam(ctx, expr) {
const { stmt } = ctx;
switch (expr.param.type) {
case ParamType.Value:
return this.formatValue(ctx, expr.param.value, expr.param.inline);
case ParamType.Named:
return stmt.param(expr.param);
}
}
retrieveField(expr, field) {
switch (expr.type) {
case ExprType.Record:
return expr.fields[field];
case ExprType.Merge:
return this.retrieveField(expr.a, field) || this.retrieveField(expr.b, field);
default:
return void 0;
}
}
/**
* Format a in "{a}->>{b}"
*/
formatExprAccess(ctx, expr) {
const { stmt } = ctx;
if (expr.type === ExprType.Field && expr.expr.type === ExprType.Row) {
const from = expr.expr;
switch (from.target.type) {
case TargetType.Table:
if (!ctx.skipTableName) {
stmt.identifier(from.target.table.alias || from.target.table.name).raw(".");
}
return stmt.identifier(expr.field);
}
}
return this.formatExpr(ctx, expr);
}
/**
* Format b in "{a} in {b}"
*/
formatIn(ctx, expr) {
const { stmt } = ctx;
switch (expr.type) {
case ExprType.Filter:
return this.formatFilterIn(ctx, expr);
case ExprType.Map:
return this.formatMapIn(ctx, expr);
case ExprType.Field:
stmt.openParenthesis();
stmt.raw("SELECT value FROM json_each");
stmt.openParenthesis();
this.formatExprValue(ctx, expr);
stmt.closeParenthesis();
return stmt.closeParenthesis();
case ExprType.Param:
if (expr.param.type === ParamType.Value && Array.isArray(expr.param.value)) {
stmt.openParenthesis();
for (const v of stmt.separate(expr.param.value))
this.formatValue(ctx, v);
return stmt.closeParenthesis();
}
default:
return this.formatExpr(ctx, expr);
}
}
formatFieldOf(ctx, from, field) {
const { stmt } = ctx;
const fieldExpr = this.retrieveField(from, field);
if (fieldExpr)
return this.formatExpr(ctx, fieldExpr);
switch (from.type) {
case ExprType.Row:
switch (from.target.type) {
case TargetType.CTE:
return stmt.identifier(from.target.name).raw(".").identifier(field);
case TargetType.Table:
const column = from.target.table.columns[field];
const asBoolean = column?.type === ColumnType.Boolean && ctx.formatAsJson;
const jsonColumn = column?.type === ColumnType.Json;
const asJson = jsonColumn && ctx.formatAsJson;
if (asJson) {
stmt.raw("json");
stmt.openParenthesis();
}
if (asBoolean)
stmt.add(`json(iif(`);
if (!ctx.skipTableName) {
stmt.identifier(from.target.table.alias || from.target.table.name).raw(".");
}
stmt.identifier(field);
if (asBoolean)
stmt.raw(`, 'true', 'false'))`);
if (asJson)
stmt.closeParenthesis();
if (jsonColumn && !asJson)
stmt.raw("->>'$'");
return stmt;
}
default:
return this.formatAccess(
ctx,
() => this.formatExprAccess(ctx, from),
field
);
}
}
formatField(ctx, expr) {
return this.formatFieldOf(ctx, expr.expr, expr.field);
}
formatCall(ctx, expr) {
const { stmt } = ctx;
if (expr.method === "cast") {
const [e, type] = expr.params;
const typeName = type.type === ExprType.Param && type.param.type === ParamType.Value && type.param.value;
stmt.raw("CAST").openParenthesis();
this.formatExprValue(ctx, e).add("AS").space();
this.formatString(ctx, typeName);
return stmt.closeParenthesis();
} else if (expr.method === "exists") {
stmt.raw("EXISTS").space();
return this.formatExprValue(ctx, expr.params[0]);
} else {
stmt.identifier(expr.method);
for (const param of stmt.call(expr.params))
this.formatExprValue(ctx, param);
return stmt;
}
}
formatQuery(ctx, expr) {
const { stmt } = ctx;
if (!ctx.formatAsJson) {
stmt.openParenthesis();
this.format(ctx, expr.query);
return stmt.closeParenthesis();
}
if (expr.query.singleResult) {
stmt.openParenthesis();
this.format({ ...ctx, topLevel: true }, expr.query);
return stmt.closeParenthesis().raw(`->'$.result'`);
}
stmt.openParenthesis().raw(`SELECT json_group_array(result->'$.result')`).newline().raw("FROM").space().openParenthesis();
this.format({ ...ctx, topLevel: true }, expr.query);
return stmt.closeParenthesis().closeParenthesis();
}
formatRow(ctx, expr) {
const { stmt } = ctx;
switch (expr.target.type) {
case TargetType.Table:
const table = Target.source(expr.target);
if (!table)
throw new Error(`Cannot select empty target`);
if (ctx.tableAsExpr)
return stmt.identifier(table.alias || table.name);
if (ctx.selectAsColumns) {
const inner = { ...ctx, selectAsColumns: false };
for (const [key, column] of stmt.separate(
Object.entries(table.columns)
)) {
this.formatFieldOf(inner, expr, column.name);
stmt.add("AS").addIdentifier(key);
}
return stmt;
}
stmt.identifier("json_object");
for (const [key, column] of stmt.call(Object.entries(table.columns))) {
this.formatString(ctx, key).raw(", ");
this.formatFieldOf(ctx, expr, column.name);
}
return stmt;
case TargetType.Query:
case TargetType.Expr:
return stmt.identifier(expr.target.alias).raw(".value");
default:
throw new Error(`Cannot select from ${expr.target.type}`);
}
}
formatMerge(ctx, expr) {
const { stmt } = ctx;
stmt.identifier("json_patch").openParenthesis();
this.formatExpr({ ...ctx, formatAsJson: true }, expr.a);
stmt.raw(", ");
this.formatExpr({ ...ctx, formatAsJson: true }, expr.b);
return stmt.closeParenthesis();
}
formatRecord(ctx, expr) {
const { stmt } = ctx;
if (ctx.selectAsColumns) {
const inner = { ...ctx, selectAsColumns: false };
for (const [key, value] of stmt.separate(Object.entries(expr.fields))) {
this.formatExprJson(inner, value);
stmt.add("AS").addIdentifier(key);
}
return stmt;
}
stmt.identifier("json_object").openParenthesis();
for (const [key, value] of stmt.separate(Object.entries(expr.fields))) {
this.formatString(ctx, key).raw(", ");
this.formatExprJson(ctx, value);
}
return stmt.closeParenthesis();
}
formatFilterIn(ctx, expr) {
const { stmt } = ctx;
const { target, condition } = expr;
switch (target.type) {
case TargetType.Expr:
stmt.openParenthesis();
stmt.raw("SELECT value AS result").add("FROM json_each").openParenthesis();
this.formatExpr(ctx, target.expr);
stmt.closeParenthesis();
if (target.alias)
stmt.add("AS").addIdentifier(target.alias);
stmt.add("WHERE").space();
this.formatExprValue(ctx, condition);
return stmt.closeParenthesis();
default:
throw new Error("todo");
}
}
formatFilter(ctx, expr) {
const { stmt } = ctx;
switch (expr.target.type) {
case TargetType.Expr:
stmt.openParenthesis().raw("SELECT json_group_array(json(result)) FROM ");
this.formatFilterIn(ctx, expr);
stmt.closeParenthesis();
return stmt;
default:
throw new Error("todo");
}
}
formatMapIn(ctx, expr) {
const { stmt } = ctx;
const { target, result } = expr;
switch (target.type) {
case TargetType.Expr:
stmt.openParenthesis();
stmt.raw("SELECT").space();
this.formatExpr(ctx, result).add("AS result").add("FROM json_each").openParenthesis();
this.formatExprJson(ctx, target.expr);
stmt.closeParenthesis();
if (target.alias)
stmt.add("AS").addIdentifier(target.alias);
return stmt.closeParenthesis();
default:
throw new Error("todo");
}
}
formatMap(ctx, expr) {
const { stmt } = ctx;
switch (expr.target.type) {
case TargetType.Expr:
stmt.openParenthesis().raw("SELECT json_group_array(json(result)) FROM ");
this.formatMapIn(ctx, expr);
stmt.closeParenthesis();
return stmt;
default:
throw new Error("todo");
}
}
};
// node_modules/rado/dist/sqlite/SqliteFormatter.js
var BACKTICK = "`";
var ESCAPE_BACKTICK = "``";
var SINGLE_QUOTE = "'";
var ESCAPE_SINGLE_QUOTE = "''";
var MATCH_BACKTICK = /`/g;
var MATCH_SINGLE_QUOTE = /'/g;
var SqliteFormatter = class extends Formatter {
formatParamValue(paramValue) {
if (paramValue === null || paramValue === void 0)
return null;
if (typeof paramValue === "boolean")
return paramValue ? 1 : 0;
if (typeof paramValue === "number")
return paramValue;
if (typeof paramValue === "string")
return paramValue;
return JSON.stringify(paramValue);
}
escapeValue(value) {
if (value === null || value === void 0)
return "NULL";
if (typeof value === "boolean")
return value ? "1" : "0";
if (typeof value === "number")
return String(value);
if (typeof value === "string")
return this.escapeString(value);
return "json(" + this.escapeString(JSON.stringify(value)) + ")";
}
escapeIdentifier(input) {
return BACKTICK + input.replace(MATCH_BACKTICK, ESCAPE_BACKTICK) + BACKTICK;
}
escapeString(input) {
return SINGLE_QUOTE + input.replace(MATCH_SINGLE_QUOTE, ESCAPE_SINGLE_QUOTE) + SINGLE_QUOTE;
}
formatAccess(ctx, mkSubject, field) {
const { stmt, formatAsJson } = ctx;
mkSubject();
stmt.raw(formatAsJson ? "->" : "->>");
return this.formatString(ctx, `$.${field}`);
}
formatCall(ctx, expr) {
const { stmt } = ctx;
switch (expr.method) {
case "match":
const [from, query] = expr.params;
this.formatExprValue({ ...ctx, tableAsExpr: true }, from);
stmt.raw(" MATCH ");
this.formatExprValue(ctx, query);
return stmt;
case "highlight":
case "snippet":
stmt.identifier(expr.method);
for (const param of stmt.call(expr.params))
this.formatExprValue({ ...ctx, tableAsExpr: true }, param);
return stmt;
default:
return super.formatCall(ctx, expr);
}
}
};
// node_modules/rado/dist/sqlite/SqliteSchema.js
var SqliteSchema;
((SqliteSchema2) => {
const formatter = new SqliteFormatter();
function tableData(tableName) {
return sql.all`select * from pragma_table_info(${tableName}) order by cid`;
}
SqliteSchema2.tableData = tableData;
function indexData(tableName) {
return sql.all`select * from sqlite_master where type='index' and tbl_name=${tableName}`;
}
SqliteSchema2.indexData = indexData;
function createInstructions(columnData, indexData2) {
if (columnData.length === 0 && indexData2.length === 0)
return void 0;
const columns = columnData.map(columnInstruction);
const indexes = indexData2.map(indexInstruction).filter((row) => row[1]);
return {
columns: Object.fromEntries(columns),
indexes: Object.fromEntries(indexes)
};
}
SqliteSchema2.createInstructions = createInstructions;
function columnInstruction(column) {
const stmt = new Statement(formatter);
stmt.identifier(column.name).add(column.type);
if (column.pk === 1)
stmt.add("PRIMARY KEY");
if (column.notnull === 1)
stmt.add("NOT NULL");
if (column.dflt_value !== null) {
stmt.add("DEFAULT").space().openParenthesis().raw(column.dflt_value).closeParenthesis();
}
return [column.name, stmt.sql];
}
SqliteSchema2.columnInstruction = columnInstruction;
function indexInstruction(index) {
return [index.name, index.sql];
}
SqliteSchema2.indexInstruction = indexInstruction;
})(SqliteSchema || (SqliteSchema = {}));
// node_modules/rado/dist/driver/sql.js.js
var PreparedStatement = class {
constructor(db, stmt, discardAfter) {
this.db = db;
this.stmt = stmt;
this.discardAfter = discardAfter;
}
*iterate(params) {
this.stmt.bind(params);
while (this.stmt.step())
yield this.stmt.getAsObject();
if (this.discardAfter)
this.stmt.free();
else
this.stmt.reset();
}
all(params) {
return Array.from(this.iterate(params));
}
run(params) {
this.stmt.run(params);
if (this.discardAfter)
this.stmt.free();
else
this.stmt.reset();
return { rowsAffected: this.db.getRowsModified() };
}
get(params) {
return this.all(params)[0];
}
execute(params) {
this.stmt.run(params);
if (this.discardAfter)
this.stmt.free();
else
this.stmt.reset();
}
};
var SqlJsDriver = class extends Driver.Sync {
constructor(db, options) {
super(new SqliteFormatter(), options);
this.db = db;
}
tableData;
indexData;
close() {
this.db.close();
}
prepareStatement(stmt, discardAfter) {
return new PreparedStatement(
this.db,
this.db.prepare(stmt.sql),
discardAfter
);
}
schemaInstructions(tableName) {
this.tableData = this.tableData || (this.tableData = this.prepare(SqliteSchema.tableData));
this.indexData = this.indexData || (this.indexData = this.prepare(SqliteSchema.indexData));
const columnData = this.tableData(tableName);
const indexData = this.indexData(tableName);
return SqliteSchema.createInstructions(columnData, indexData);
}
export() {
return this.db.export();
}
};
function connect(db, options) {
return new SqlJsDriver(db, options);
}
export {
SqlJsDriver,
connect
};