UNPKG

alinea

Version:

[![npm](https://img.shields.io/npm/v/alinea.svg)](https://npmjs.org/package/alinea) [![install size](https://packagephobia.com/badge?p=alinea)](https://packagephobia.com/result?p=alinea)

1,482 lines (1,474 loc) 45.7 kB
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 };