(* Generic database interface for mod_caml programs.
* Copyright (C) 2003-2004 Merjis Ltd.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: dbi.mli,v 1.25 2005/01/25 17:59:10 ChriS Exp $
*)
(** Generic database interface.
*
* Making a connection to a specific type of database:
*
{[ module DB = Dbi_postgres
* let dbh = new DB.connection "database_name" ]}
*
* Simple usage, returning one row:
*
{[ let sth = dbh#prepare "SELECT name FROM employees WHERE empid = ?" in
* sth#execute [`Int 100];
* let [`String name] = sth#fetch1 in ... ]}
*
* Simple usage, returning multiple rows:
*
{[ let sth = dbh#prepare "SELECT firstname, name FROM employees
* WHERE salary > ?" in
* sth#execute [`Int 10000];
* sth#iter(function
* | [(`Null | `String _) as fname, `String name] ->
* do_something_with fname name
* | _ -> assert false); ]}
*
* Advanced usage, reusing prepared statements:
*
{[ let sth =
* dbh#prepare "INSERT INTO employees(name, salary) VALUES (?, ?)" in
* List.iter(fun (name, salary) ->
* sth#execute [`String name; `Int salary];
* let id = sth#serial "" in
* Printf.printf "Employee %s has been assigned ID %d\n" name id
* ) employees_list; ]}
*)
(** {1 Caml mappings of SQL types.} *)
type date = { year : int; month : int; day : int; }
type time = { hour : int; min : int; sec : int; microsec : int;
timezone : int option }
type datetime = date * time
module Decimal :
sig
(** Module to handle arbitrary precision decimal numbers.
A decimal number is a number of the form n 10^e with n, e
integers. It characterized by its precision (i.e., its total
number of digits) and its scale. For example, 103.12 has
precision 5 (or more) and scale 2. In this implementation, the
precision is taken as +infinity and the scale adapts dynamically.
*)
type t
(** Abstract type for decimal numbers *)
val to_string : t -> string
(** [to_string n] returns a string representation of the decimal
number [n]. *)
val to_float : t -> float
(** [to_float n] returns the closer float to [n]. *)
val of_string : ?scale:int -> string -> t
(** [of_string ?scale s] returns the decimal number represented by
the string [s]. If the option [scale] is not set, the scale
will be the one of the string representation. If [scale] is
given, it will be enforced, possibly truncating the number.
@raise Invalid_argument if [scale < 0].
*)
val of_int : ?scale:int -> int -> t
(** [of_int ?scale i] returns the decimal number [i * 10**(-scale)].
@param scale Scaling of [i] (default: 0).
@raise Invalid_argument if [scale < 0].
*)
val add : t -> t -> t
(** [add n m] returns the sum of [n] and [m]. *)
val sub : t -> t -> t
(** [sub n m] returns the difference of [n] by [m]. *)
val mul : t -> t -> t
(** [mul n m] returns the product of [n] and [m]. *)
val div : t -> t -> t
(** [div n m] returns the quotient of the division of [n] by [m]. *)
val compare : t -> t -> int
(** [compare n m] returns [0] if [x=y], a negative integer if
[x<y], and a positive integer if [x>y]. *)
end
(* Some types not yet implemented. XXX *)
type sql_t = [ `Null (* NULL value *)
| `Int of int (* integer, smallint *)
| `Float of float (* double precision, real *)
| `String of string (* char, varchar, text *)
| `Bool of bool (* boolean *)
| `Bigint of Big_int.big_int (* bigint *)
| `Decimal of Decimal.t (* numeric, decimal *)
| `Date of date (* date *)
| `Time of time (* time (with/without TZ) *)
| `Timestamp of datetime (* timestamp (with/without TZ) *)
| `Interval of datetime (* interval (no TZ) *)
(* | `Blob of blob (* blob *) *)
| `Binary of string (* Postgres's BYTEA and equivalent *)
| `Unknown of string (* cannot be represented by any above*)
]
val sql_t_to_string : sql_t -> string
(** [sql_t_to_string t] returns a string representation of [t]
* following the Ocaml conventions. The aim is to offer an easy way
* to print [sql_t] values.
*)
val sdebug : sql_t list -> string
(** [sdebug ss] can be used to debug the return value from a [#fetch1],
* [#map] or other query. It converts the [sql_t list] type into a
* printable form which may be printed on [stderr] to aid in debugging
* the actual types returned by the database.
*)
val intoption : int option -> sql_t
(** [intoption(Some i)] returns [`Int i] and
[intoption None] returns [`Null]. *)
val stringoption : string option -> sql_t
(** [stringoption(Some s)] returns [`String s] and
* [stringoption None] returns [`Null]. *)
type precommit_handle
(** See {!Dbi.connection.register_precommit}. *)
type postrollback_handle
(** See {!Dbi.connection.register_postrollback}. *)
(** {1 SQL utility functions.} *)
val string_escaped : string -> string
(** [escape_string s] escapes the string [s] according to SQL rules
* and surrounds it with single quotes. This should be hardly needed
* as the data provided through the sql_t types will automatically be
* escaped for you.
*)
val placeholders : int -> string
(** [placeholders n] returns a string of the form "(?, ?, ..., ?)" containing
* [n] question marks.
* @raise Invalid_argument if [n <= 0].
*)
exception SQL_error of string
(** Exceptions thrown by subclasses on SQL errors. *)
class virtual statement :
connection ->
object
method virtual execute : sql_t list -> unit
(** Execute the statement with the given list of arguments substituted
* for [?] placeholders in the query string. This method should
* be executed before trying to fetch data.
*
* This command can throw a variety of SQL-specific exceptions.
*)
method virtual fetch1 : unit -> sql_t list
(** Fetches one row from the result set and returns it.
* @raise Not_found if no tuple is returned by the database.
* @raise Failure if [#execute] has not been issued before. *)
method fetch1int : unit -> int
(** This fetches a single integer field.
* @raise Not_found if no tuples are remaining.
* @raise Invalid_argument if the tuple does not contain a single integer.
* @raise Failure if [#execute] has not been issued before. *)
method fetch1string : unit -> string
(** This fetches a single string field.
* @raise Not_found if no tuples are remaining.
* @raise Invalid_argument if the tuple does not contain a single string.
* @raise Failure if [#execute] has not been issued before. *)
method fetch1bool : unit -> bool
(** This fetches a single string field.
* @raise Not_found if no tuples are remaining.
* @raise Invalid_argument if the tuple does not contain a single boolean.
* @raise Failure if [#execute] has not been issued before. *)
method fetchall : unit -> sql_t list list
(** This returns a list of all tuples returned from the query. Note
* that this may be less efficient than reading them one at a time.
* @raise Failure if [#execute] has not been issued before. *)
method iter : (sql_t list -> unit) -> unit
(** Iterate over the result tuples.
* @raise Failure if [#execute] has not been issued before. *)
method map : 'a . (sql_t list -> 'a) -> 'a list
(** Map over the result tuples.
* @raise Failure if [#execute] has not been issued before. *)
method fold_left : 'a . ('a -> sql_t list -> 'a) -> 'a -> 'a
(** Fold left over the result tuples.
* @raise Failure if [#execute] has not been issued before. *)
method fold_right : 'a . (sql_t list -> 'a -> 'a) -> 'a -> 'a
(** Fold right over the result tuples. Not tail-recursive.
* @raise Failure if [#execute] has not been issued before. *)
method virtual names :