Something went wrong. Try again.
My own corner of monopam
Something went wrong. Try again.
7.9 kB · 198 lines
OCaml
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199(*--------------------------------------------------------------------------- Copyright (c) 2026 Thomas Gazagnaire. All rights reserved. SPDX-License-Identifier: ISC ---------------------------------------------------------------------------*)
(** SQL statement execution over a {!Catalog.Spi.t}.
[Engine] parses, lowers and runs SQL statements over any store that implements {!Catalog.Spi}, such as a B-tree, the in-memory reference backend or an Irmin tree.
{!run_select} runs the whole read dialect on the register VM: joins, aggregates with [GROUP BY] and [HAVING], window functions, compounds such as [UNION], [WITH] including recursive ones, correlated and [FROM] subqueries, and [VALUES] and table-function sources. {!exec} also runs [CREATE TABLE], [DROP TABLE], [INSERT] from [VALUES] or a [SELECT], [UPDATE] and [DELETE]. It stores the column types, nullability, primary key and collations of a new table in the schema of the store, but enforces no [CHECK] constraint, trigger, foreign key or [DEFAULT]. A backend such as sqlite enforces those with the functions of {{!write_path}the write path}. *)
(** The type for statement results. *)type result = | Rows of Catalog.Value.t list list (** The rows of a [SELECT]. *) | Done (** A statement with no rows. *)
val exec : Catalog.Spi.t -> string -> result(** [exec cat sql] runs the statements of [sql] in order over [cat] and is the result of the last one.
@raise Catalog.Error.Prepare on a parse error or a name that resolves to nothing. @raise Failure on a statement it does not support. *)
val run_select : ?scope:Query.binding list -> Query.catalog -> Ast.select -> Ast.value array -> Ast.value list list(** [run_select ~scope cat s params] is the rows of the [SELECT] [s] over [cat], run on the register VM. A backend builds [cat] with {!val-catalog} over its {!Catalog.Spi.t}. [scope] is an enclosing row, such as the outer row of a correlated subquery or the [OLD] and [NEW] rows of a trigger, substituted before the query runs.
@raise Failure on a correlated subquery it cannot substitute. *)
val select_runner : Query.catalog -> Query.binding list -> Ast.select -> Ast.value array -> Ast.value list list(** [select_runner cat scope s params] is [run_select ~scope cat s params], in the shape of {!Query.field-subquery}. A backend that builds a {!Query.catalog} field by field puts it, or a runner of its own, in that field. *)
val catalog : ?recursion_cap:int -> Catalog.t -> Query.catalog(** [catalog ?recursion_cap cat] is {!Query.of_catalog} of [cat] with {!select_runner} as its subquery runner and [recursion_cap] as its cap on a recursive CTE (default {!Query.default_recursion_cap}). @raise Invalid_argument if [recursion_cap] is not positive. *)
val combine_compound : Query.catalog -> Ast.value array -> Ast.select -> Ast.value list list -> (Ast.set_op * Ast.value list list) list -> Ast.value list list(** [combine_compound cat params s base_rows compound_rows] is the result of the compound select [s] from the rows of its arms: [base_rows] for the first arm and one [(op, rows)] for each following arm. It applies the set operators, then the [ORDER BY], [LIMIT] and [OFFSET] of [s] on the register VM, so a backend can compute the rows of each arm its own way.
@raise Failure if an [ORDER BY] term names no result column (lang_select.html section 4).*)
(** {1:write_path The write path}
These functions evaluate the expressions of a write: an [INSERT ... VALUES] tuple, a [SET] assignment, a [WHERE], the [DO UPDATE] of an upsert, a [RETURNING] item and an [UPDATE ... FROM] join. Each runs on the register VM over rows the caller holds, one cursor of one row for each named row, so a backend works on values and never evaluates SQL itself. Each raises [Failure] on an expression the VM does not compile. *)
val from_bindings : ?scope:Query.binding list -> Query.catalog -> Ast.value array -> Ast.from_clause -> Ast.join list -> Query.binding list list(** [from_bindings ~scope cat params from joins] is the rows of the join [FROM from joins], with no projection and no [WHERE], each named by source so that it can serve as an outer scope. It is the join of an [UPDATE ... FROM] (lang_update.html), run on the access paths {!Query.plan_sources} picks. [scope] is an enclosing row, such as the [OLD] and [NEW] rows of a trigger. *)
val eval_const : ?scope:Query.binding list -> Query.catalog -> Ast.value array -> Ast.expr -> Ast.value(** [eval_const ~scope cat params e] is the value of [e] with no table row, such as an [INSERT ... VALUES] cell, a column [DEFAULT] or the [WHEN] of a trigger. A column reference raises [Failure] unless [scope] binds it. *)
val eval_in_row : ?scope:Query.binding list -> ?rowid:int64 -> ?stored:Self_ref.t -> Query.catalog -> Ast.value array -> string -> string list -> Ast.value list -> Ast.expr -> Ast.value(** [eval_in_row ~scope ~rowid ~stored cat params table cols vals e] is the value of [e] over the row [vals] of [table], with [vals] aligned to [cols]. It serves an [UPDATE ... SET] value, a [CHECK], a generated column and a partial index predicate. [scope] adds outer bindings. [rowid] is the rowid of the row; without it, [e] cannot name the rowid.
[stored] says that [e] comes from the schema and which kind of expression it is. A date function that reads the clock ({!Func.reads_the_clock}) then fails with an error such as [non-deterministic use of julianday() in a CHECK constraint]. [stored] changes nothing else. *)
val eval_upsert : ?scope:Query.binding list -> Query.catalog -> Ast.value array -> table:string -> cols:string list -> current:Ast.value list -> excluded:Ast.value list -> Ast.expr -> Ast.value(** [eval_upsert ~scope cat params ~table ~cols ~current ~excluded e] is the value of [e] with the values [current] of the conflicting row bound under [table] and the values [excluded] of the row being inserted bound under [excluded], both aligned to [cols]. It serves the [DO UPDATE] assignments and [WHERE] of an upsert (lang_upsert.html). *)
val row_matches : ?scope:Query.binding list -> ?rowid:int64 -> Query.catalog -> Ast.value array -> string -> string list -> Ast.expr option -> Ast.value list -> bool(** [row_matches ~scope ~rowid cat params table cols where vals] is [true] iff the row [vals] of [table], aligned to [cols], satisfies [where]. [None] matches every row. It picks the rows of a [DELETE] or an [UPDATE]. [scope] and [rowid] are as in {!eval_in_row}. *)
val returning_rows : Query.catalog -> Ast.value array -> table:string -> cols:string list -> Ast.result_col list -> (int64 option * Ast.value list) list -> Ast.value list list(** [returning_rows cat params ~table ~cols returning rows] is the [RETURNING] list [returning] evaluated over each affected row of [table], whose values [cols] names. [*] is the whole row (lang_returning.html). Each row comes with its rowid, or [None] for a table without one, in which case a [RETURNING] item cannot name it. *)
val check_returning : Query.catalog -> Ast.value array -> table:string -> cols:string list -> rowid:bool -> Ast.result_col list -> unit(** [check_returning cat params ~table ~cols ~rowid returning] checks, before any row exists, that every name [returning] reads is a column of [cols], or the rowid if [rowid] is [true]. A statement with a [RETURNING] list calls it when it is prepared, as sqlite3 does, so a bad name fails before the table changes.
@raise Catalog.Error.Prepare for a name the row does not have. *)