pgocaml-1.7.1/0000775000175000017500000000000012073571262012127 5ustar dariodariopgocaml-1.7.1/tests/0000775000175000017500000000000012073571261013270 5ustar dariodariopgocaml-1.7.1/tests/test_pgocaml_highlevel.ml0000664000175000017500000000371712045002764020336 0ustar dariodario(* Example program using typesafe calls to PostgreSQL. * * PG'OCaml - type safe interface to PostgreSQL. * Copyright (C) 2005-2009 Richard Jones and other authors. * * 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 General Public License * along with this library; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. *) open Printf let () = let dbh = PGOCaml.connect () in let () = PGSQL(dbh) "execute" "create temporary table employees ( id serial not null primary key, name text not null, salary int4 not null, email text )" in let insert name salary email = PGSQL(dbh) "insert into employees (name, salary, email) values ($name, $salary, $?email)" in insert "Ann" 10_000_l None; insert "Bob" 45_000_l None; insert "Jim" 20_000_l None; insert "Mary" 30_000_l (Some "mary@example.com"); let rows = PGSQL(dbh) "select id, name, salary, email from employees" in List.iter begin fun (id, name, salary, email) -> let email = match email with Some email -> email | None -> "-" in printf "%ld %S %ld %S\n" id name salary email end rows; let ids = [ 1_l; 3_l ] in let rows = PGSQL(dbh) "select id, name, salary, email from employees where id in $@ids" in List.iter begin fun (id, name, salary, email) -> let email = match email with Some email -> email | None -> "-" in printf "%ld %S %ld %S\n" id name salary email end rows; PGOCaml.close dbh pgocaml-1.7.1/tests/test_pgocaml_lowlevel.ml0000664000175000017500000001004212044755477020224 0ustar dariodario(* Test the lowlevel interface to the database. * Assumes that $PGHOST, etc. are set to point to a database. * * PG'OCaml - type safe interface to PostgreSQL. * Copyright (C) 2005-2009 Richard Jones and other authors. * * 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 General Public License * along with this library; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. *) open Printf IFDEF USE_BATTERIES THEN module List = struct include List include BatList end ELSE open ExtList ENDIF let print_row i row = printf "row %d: [%s]\n" i (String.concat "; " (List.map (function | None -> "NULL" | Some str -> sprintf "%S" str) row)) let print_rows rows = List.iteri print_row rows let print_params_description params = printf "params:\n"; List.iteri ( fun i param -> printf " parameter %d:\n" i; printf " type: %ld\n" param.PGOCaml.param_type ) params let print_row_description results = match results with | None -> printf "this statement returns no data\n" | Some results -> printf "results:\n"; List.iteri ( fun i result -> printf " field %d:\n" i; printf " name: %s\n" result.PGOCaml.name; printf " type: %ld\n" result.PGOCaml.field_type ) results let () = let dbh = PGOCaml.connect () in (* Simple query with no parameters. *) let query = "select current_timestamp" in let name = "timestamp_query" in ignore (PGOCaml.prepare dbh ~query ~name ()); let i = ref 0 in PGOCaml.cursor dbh ~name ~params:[] (fun row -> incr i; print_row !i row); (* Describe the statement. *) let params, results = PGOCaml.describe_statement dbh ~name () in print_params_description params; print_row_description results; (* A query with parameters. *) let query = "select $1 + $2" in let types = [ 23l; 23l ] in (* 23 = int4 *) let name = "sum_query" in ignore (PGOCaml.prepare dbh ~query ~name ~types ()); let i = ref 0 in PGOCaml.cursor dbh ~name ~params:[Some "1"; Some "2"] (fun row -> incr i; print_row !i row); (* Describe the statement. *) let params, results = PGOCaml.describe_statement dbh ~name () in print_params_description params; print_row_description results; (* Create a temporary table and populate it. *) let query = "create temporary table employees ( id serial not null primary key, name text not null, salary numeric(8,2) not null, email text )" in ignore (PGOCaml.prepare dbh ~query ()); ignore (PGOCaml.execute dbh ~params:[] ()); let query = "insert into employees (name, salary, email) values ($1, $2, $3)" in ignore (PGOCaml.prepare dbh ~query ()); let params, results = PGOCaml.describe_statement dbh () in print_params_description params; print_row_description results; ignore (PGOCaml.execute dbh ~params:[Some "Ann"; Some "10000.00"; None] ()); ignore (PGOCaml.execute dbh ~params:[Some "Bob"; Some "45000.00"; None] ()); ignore (PGOCaml.execute dbh ~params:[Some "Jim"; Some "20000.00"; None] ()); ignore (PGOCaml.execute dbh ~params:[Some "Mary"; Some "30000.00"; None] ()); let query = "select * from employees where salary > $1 order by id" in ignore (PGOCaml.prepare dbh ~query ()); let params, results = PGOCaml.describe_statement dbh () in print_params_description params; print_row_description results; let rows = PGOCaml.execute dbh ~params:[Some "0"] () in print_rows rows; PGOCaml.close dbh pgocaml-1.7.1/doc/0000775000175000017500000000000012073571261012673 5ustar dariodariopgocaml-1.7.1/doc/CONTRIBUTORS.txt0000664000175000017500000000151512044754453015377 0ustar dariodario------------------------------------------------------------------------- | Contributors. | ------------------------------------------------------------------------- * Gabriel de Perthuis: Contributed patches to make OMakefile builds work using a '-syntax' flag. * Dario Teixeira: Tutorial, web site, miscellaneous patches, release maintenance. * Jérôme Vouillon: Code refactoring to work in a monadic fashion (for Lwt, for example). * Mykola Stryebkov: Support for large data sets. * Vincent Bernardoff: Support for "point" type. * Boris Yakobowski: Patch that replaces 'rows' and 'varname' with '_rows' and '_varname' (avoids unused variable warnings) * David Allsopp: Support for INET type, miscellaneous fixes * Michael Ekstrand: OCaml Batteries support. * Matías Giovannini: Cursor support. pgocaml-1.7.1/doc/PROFILING.txt0000664000175000017500000000155712044755255015002 0ustar dariodario------------------------------------------------------------------------- | Profiling. | ------------------------------------------------------------------------- If PG'OCaml programs are started with the environment variable $PGPROFILING pointing to the name of a writable file, then profiling information about events is written to this file. This information can be analysed later by using the pgocaml_prof tool. For example: PGPROFILING=$HOME/.test_prof export PGPROFILING ./test pgocaml_prof $HOME/.test_prof | less Information currently tracked includes the running time for SQL statements and connection time. You can usually use this information to find out which statements are taking too long, and optimise them accordingly. The profile file will contain some potentially sensitive information such as database and user names (but not passwords). pgocaml-1.7.1/doc/HOW_IT_WORKS.txt0000664000175000017500000000634412044754453015465 0ustar dariodario------------------------------------------------------------------------- | How PG'OCaml works. | ------------------------------------------------------------------------- Enough people have asked me how PG'OCaml works that I am compiling this document to explain what is going on. 1. Background ------------- 1.1 PREPARE A common operation on databases is 'PREPARE statement'. What this does is to take a fixed statement and optimise it. The idea is that if you execute the same statement lots of times, instead of having the database optimise it each time (a process which can be time-consuming), you PREPARE [optimise] it once and then EXECUTE the already optimised statement. For example: PREPARE SELECT [some very complicated set of joins which take a long time to optimise] EXECUTE EXECUTE EXECUTE Most databases extend this notion with placeholders in the original statement, so: PREPARE SELECT name FROM users where id = $1 EXECUTE ($1 = 10) EXECUTE ($1 = 20) EXECUTE ($1 = 23) 1.2 DESCRIBE Recent versions of PostgreSQL added a 'DESCRIBE statement' command to the database backend. It isn't normally exposed through clients like psql or libPQ (well - that may have changed by the time you read this), but it's there if you code directly to the database frontend/backend wire protocol. 'DESCRIBE' is just an extension of 'PREPARE'. Because the optimiser has decoded the statement into some internal format, it knows already the types of the placeholders and the types of the return columns, and it can supply this information back to the caller. For example: PREPARE SELECT id, name FROM users WHERE salary > $1 DESCRIBE ==> placeholder $1 has type DECIMAL ==> 2 columns returned with types SERIAL, VARCHAR(80) 2. In OCaml ----------- On the OCaml side we like to know the type of everything, and using DESCRIBE we can extend type inference right the way through to the database. Consider some code like: let salary = 15000.00 in let rows = PGSQL(dbh) "SELECT id, name FROM users WHERE salary > $salary" in List.iter ( fun (id, name) -> printf "id = %d, name = %s\n" id name ) rows How do we know that salary (type: float) when passed to the database has the same type that the database expects? How do we know that the id and name fields have the same type as what the database returns? During compilation we can use DESCRIBE to convert this code into: let salary = 15000.00 in let rows = (*vv-- generated by macro --vv*) do_PREPARE "SELECT id, name FROM users WHERE salary > $1"; let placeholder1 = string_of_decimal salary (* placeholder $1 *) in let rows = do_EXECUTE placeholder1 in List.map ( fun (col1, col2) -> (* returned columns id, name *) (serial_of_string col1, string_of_string col2) ) rows (*^^-- generated by macro --^^*) List.iter ( fun (id, name) -> printf "id = %d, name = %s\n" id name ) rows Notes: (1) The real code generated by the macro is a lot more complicated. (2) The database actually takes and returns strings. (3) In the real macro, prepared statements are cached so they don't need to be reoptimised each time. 3. Further reading ------------------ Now go and read: BUGS.txt, pGOCaml.mli, pa_pgsql.ml4 pgocaml-1.7.1/doc/BUGS.txt0000664000175000017500000001021312044754453014175 0ustar dariodario------------------------------------------------------------------------- | Current bugs and other problems. | ------------------------------------------------------------------------- 1. LEFT OUTER JOIN and nullability of named columns [FIXED] ----------------------------------------------------------- Consider tables like: create table users ( id serial not null primary key, u.name text not null, ... ); create table adverts ( author int references users (id), -- could be null ... ); and a query like: select a.author, u.name from adverts a LEFT OUTER JOIN users u on a.author = u.id The LEFT OUTER JOIN ensures that rows are returned even if the adverts.author field is null. For example, this query can return u.name as NULL. Unfortunately our nullability heuristic doesn't work well here. The u.name column is marked as NOT NULL and so we don't expect it to come out of the database as a NULL. The current workaround is to use PGSQL(dbh) "nullable-results" "query" instead of PGSQL(dbh) "query". This disables the nullability heuristic for all columns, and so all return columns will have type "'a option". 2. CREATE TEMPORARY TABLE [FIXED] --------------------------------- Normally at compile time, statements are only prepared in the database. You wouldn't ordinarily want to run statements at compile time, since that would be dangerous. However there is one case where it is desirable to run the statement, and that is if the statement creates a temporary table. By actually creating the temporary table, we allow further statements which use the table to be checked, and since temporary tables are temporary, no harm is done by creating them, even at compile time. For this case, use PGSQL(dbh) "execute" "query" instead of PGSQL(dbh) "query". For example: PGSQL(dbh,"execute") "create temporary table employees ( id serial not null primary key, name text not null, salary int4 not null, email text )"; let insert name salary email = PGSQL(dbh) "insert into employees (name, salary, email) values ($name, $salary, $?email)" in insert "Ann" 10_000_l None; insert "Bob" 45_000_l None; insert "Jim" 20_000_l None; insert "Mary" 30_000_l (Some "mary@example.com"); Note that the CREATE TEMPORARY TABLE statement needs to come before any statements which use the table. There doesn't seem to be a good way around this. 3. Specifying lists as parameters [FIXED] ----------------------------------------- Suppose I want to select a subset of employees from my database. We'd like to be able to write: let employee_ids = [ 3; 4; 5 ] in PGSQL(dbh) "select name from employees where id in $@employee_ids" which, at runtime, would expand to: select name from employees where id in (3, 4, 5) The implementation of this is complex. At compile time we prototype the following statement: select name from employees where id in ($1) and get the type of $1 as the type of each element in the list. At runtime we then have to be careful to cache each list arity separately - quite complex if there are several lists in the statement. This doesn't work at all if the list could be empty, because "... in ()" is a syntax error in SQL. This is a problem with the SQL standard. You need to treat that as a special case. 4. Generating SQL from fragments -------------------------------- It is fairly common to construct SQL statements from string fragments, as in this pseudocode example: let order_clause = match key, reverse with | `Author, false -> "author asc" | `Author, true -> "author desc" | `Title, false -> "title asc" | `Title, true -> "title desc" let sql = "select title, author from books " ^ order_clause Such statement-building is not currently permitted by PG'OCaml, unless you ditch the camlp4 extension and use the low-level, unsafe interface. It would be nice to have some sort of "fragment constructor" operator to allow the above to be expressed in a type-safe way. However because it is not possible to compile the fragments, it doesn't look like such a constructor could be written. If anyone has any ideas about this, please contact the author. pgocaml-1.7.1/CHANGELOG.txt0000664000175000017500000000351412073570730014161 0ustar dariodarioRelease 1.7.1 ============= * Fixed missing dependency in _oasis file. Release 1.7 =========== * Build system now uses OASIS. * Directory tree reorganisation. * Now using Batteries only. * Migration to Batteries 2.0. Release 1.6 =========== * Fixed Makefile: it should now work with any OCaml version. * Richard Jones patch converting all references of 'loc' into '_loc'. The former has been deprecated for a while now. Release 1.5 =========== * Dario Teixeira's patch adding support for more array types, namely bool[], int8[], text[], float4[], and float8[]. * Michael Ekstrand's patch to make PG'Ocaml work with batteries, if so requested (it still uses ExtLib by default). * Dario Teixeira's patch adding support for Hstore. * David Allsopp's patch fixing connection on Windows. * David Allsopp's patch for better reporting of nullable results. * Dario Teixeira's patch adding support for the 'hex' serialisation format introduced with PostgreSQL 9.0. * Matías Giovannini's patch adding support for cursors. * Dario Teixeira's patch adding support for the various transaction options in function 'begin_work'. Release 1.4 =========== * Boris Yakobowski's patch that replaces 'rows' and 'varname' with '_rows' and '_varname' (avoids unused variable warnings) * Support for INET type (patch by David Allsopp) * Dario Teixeira's patch for type unravelling Release 1.3 =========== * Applied (slightly modified) Vincent Bernardoff's patches for 'point' type * Large data set patch by Mykola Stryebkov Release 1.2 =========== * Jérôme Vouillon's monadic version * Updated to Calendar 2.x * Password authentication from $PGPASSWORD (by Dario Teixeira) * Syntax package now called 'syntax' instead of 'statements' Release 1.1 =========== * Added proper copyright notices to license. Release 1.0 =========== * First public release pgocaml-1.7.1/src/0000775000175000017500000000000012073571262012716 5ustar dariodariopgocaml-1.7.1/src/META0000664000175000017500000000114312073570730013365 0ustar dariodario# OASIS_START # DO NOT EDIT (digest: 1753dc20af857866adb3b2ea4ceaa82c) version = "1.7.1" description = "OCaml bindings for the PostgreSQL database" requires = "unix calendar csv pcre batteries" archive(byte) = "pgocaml.cma" archive(byte, plugin) = "pgocaml.cma" archive(native) = "pgocaml.cmxa" archive(native, plugin) = "pgocaml.cmxs" exists_if = "pgocaml.cma" package "syntax" ( version = "1.7.1" description = "Syntax extension for PG'OCaml" requires = "pgocaml camlp4" archive(syntax, preprocessor) = "pa_pgsql.cma" archive(syntax, toploop) = "pa_pgsql.cma" exists_if = "pa_pgsql.cma" ) # OASIS_STOP pgocaml-1.7.1/src/pgocaml.mllib0000664000175000017500000000017312073570415015361 0ustar dariodario# OASIS_START # DO NOT EDIT (digest: cc9f1798b5123ea57a5ca4587be94633) PGOCaml PGOCaml_generic PGOCaml_config # OASIS_STOP pgocaml-1.7.1/src/pa_pgsql.mllib0000664000175000017500000000013512064623641015543 0ustar dariodario# OASIS_START # DO NOT EDIT (digest: 7768dab9f11db9b2b947747fb6516f8b) Pa_pgsql # OASIS_STOP pgocaml-1.7.1/src/APIDOC.odocl0000664000175000017500000000017312073570415014677 0ustar dariodario# OASIS_START # DO NOT EDIT (digest: cc9f1798b5123ea57a5ca4587be94633) PGOCaml PGOCaml_generic PGOCaml_config # OASIS_STOP pgocaml-1.7.1/src/PGOCaml_generic.mli0000664000175000017500000002546612044774143016355 0ustar dariodario(* PG'OCaml is a set of OCaml bindings for the PostgreSQL database. * * PG'OCaml - type safe interface to PostgreSQL. * Copyright (C) 2005-2009 Richard Jones and other authors. * * 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 General Public License * along with this library; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. *) (** Type-safe access to PostgreSQL databases. *) open CalendarLib module type THREAD = sig type 'a t val return : 'a -> 'a t val (>>=) : 'a t -> ('a -> 'b t) -> 'b t val fail : exn -> 'a t type in_channel type out_channel val open_connection : Unix.sockaddr -> (in_channel * out_channel) t val output_char : out_channel -> char -> unit t val output_binary_int : out_channel -> int -> unit t val output_string : out_channel -> string -> unit t val flush : out_channel -> unit t val input_char : in_channel -> char t val input_binary_int : in_channel -> int t val really_input : in_channel -> string -> int -> int -> unit t val close_in : in_channel -> unit t end module type PGOCAML_GENERIC = sig type 'a t (** Database handle. *) type 'a monad type isolation = [ `Serializable | `Repeatable_read | `Read_committed | `Read_uncommitted ] type access = [ `Read_write | `Read_only ] exception Error of string (** For library errors. *) exception PostgreSQL_Error of string * (char * string) list (** For errors generated by the PostgreSQL database back-end. The * first argument is a printable error message. The second argument * is the complete set of error fields returned from the back-end. * See [http://www.postgresql.org/docs/8.1/static/protocol-error-fields.html] *) (** {6 Connection management} *) val connect : ?host:string -> ?port:int -> ?user:string -> ?password:string -> ?database:string -> ?unix_domain_socket_dir:string -> unit -> 'a t monad (** Connect to the database. The normal [$PGDATABASE], etc. environment * variables are available. *) val close : 'a t -> unit monad (** Close the database handle. You must call this after you have * finished with the handle, or else you will get leaked file * descriptors. *) val ping : 'a t -> unit monad (** Ping the database. If the database is not available, some sort of * exception will be thrown. *) (** {6 Transactions} *) val begin_work : ?isolation:isolation -> ?access:access -> ?deferrable:bool -> 'a t -> unit monad (** Start a transaction. *) val commit : 'a t -> unit monad (** Perform a COMMIT operation on the database. *) val rollback : 'a t -> unit monad (** Perform a ROLLBACK operation on the database. *) (** {6 Serial column} *) val serial : 'a t -> string -> int64 monad (** This is a shorthand for [SELECT CURRVAL(serial)]. For a table * called [table] with serial column [id] you would typically * call this as [serial dbh "table_id_seq"] after the previous INSERT * operation to get the serial number of the inserted row. *) val serial4 : 'a t -> string -> int32 monad (** As {!serial} but assumes that the column is a SERIAL or * SERIAL4 type. *) val serial8 : 'a t -> string -> int64 monad (** Same as {!serial}. *) (** {6 Miscellaneous} *) val max_message_length : int ref (** Maximum message length accepted from the back-end. The default * is [Sys.max_string_length], which means that we will try to read as * much data from the back-end as we can, and this may cause us to * run out of memory (particularly on 64 bit machines), causing a * possible denial of service. You may want to set this to a smaller * size to avoid this happening. *) val verbose : int ref (** Verbosity. 0 means don't print anything. 1 means print short * error messages as returned from the back-end. 2 means print all * messages as returned from the back-end. Messages are printed on [stderr]. * Default verbosity level is 1. *) val set_private_data : 'a t -> 'a -> unit (** Attach some private data to the database handle. * * NB. The pa_pgsql camlp4 extension uses this for its own purposes, which * means that in most programs you will not be able to attach private data * to the database handle. *) val private_data : 'a t -> 'a (** Retrieve some private data previously attached to the database handle. * If no data has been attached, raises [Not_found]. * * NB. The pa_pgsql camlp4 extension uses this for its own purposes, which * means that in most programs you will not be able to attach private data * to the database handle. *) type pa_pg_data = (string, bool) Hashtbl.t (** When using pa_pgsql, database handles have type * [PGOCaml.pa_pg_data PGOCaml.t] *) (** {6 Low level query interface - DO NOT USE DIRECTLY} *) type oid = int32 type param = string option (** None is NULL. *) type result = string option (** None is NULL. *) type row = result list (** One row is a list of fields. *) val prepare : 'a t -> query:string -> ?name:string -> ?types:oid list -> unit -> unit monad (** [prepare conn ~query ?name ?types ()] prepares the statement [query] * and optionally names it [name] and sets the parameter types to [types]. * If no name is given, then the "unnamed" statement is overwritten. If * no types are given, then the PostgreSQL engine infers types. * Synchronously checks for errors. *) val execute_rev : 'a t -> ?name:string -> ?portal:string -> params:param list -> unit -> row list monad val execute : 'a t -> ?name:string -> ?portal:string -> params:param list -> unit -> row list monad (** [execute conn ?name ~params ()] executes the named or unnamed * statement [name], with the given parameters [params], * returning the result rows (if any). * * There are several steps involved at the protocol layer: * (1) a "portal" is created from the statement, binding the * parameters in the statement (Bind). * (2) the portal is executed (Execute). * (3) we synchronise the connection (Sync). * * The optional [?portal] parameter may be used to name the portal * created in step (1) above (otherwise the unnamed portal is used). * This is only important if you want to call {!describe_portal} * to find out the result types. *) val cursor : 'a t -> ?name:string -> ?portal:string -> params:param list -> (row -> unit monad) -> unit monad val close_statement : 'a t -> ?name:string -> unit -> unit monad (** [close_statement conn ?name ()] closes a prepared statement and frees * up any resources. *) val close_portal : 'a t -> ?portal:string -> unit -> unit monad (** [close_portal conn ?portal ()] closes a portal and frees up any resources. *) type row_description = result_description list and result_description = { name : string; (** Field name. *) table : oid option; (** OID of table. *) column : int option; (** Column number of field in table. *) field_type : oid; (** The type of the field. *) length : int; (** Length of the field. *) modifier : int32; (** Type modifier. *) } type params_description = param_description list and param_description = { param_type : oid; (** The type of the parameter. *) } val describe_statement : 'a t -> ?name:string -> unit -> (params_description * row_description option) monad (** [describe_statement conn ?name ()] describes the named or unnamed * statement's parameter types and result types. *) val describe_portal : 'a t -> ?portal:string -> unit -> row_description option monad (** [describe_portal conn ?portal ()] describes the named or unnamed * portal's result types. *) (** {6 Low level type conversion functions - DO NOT USE DIRECTLY} *) val name_of_type : ?modifier:int32 -> oid -> string (** Returns the OCaml equivalent type name to the PostgreSQL type [oid]. * For instance, [name_of_type (Int32.of_int 23)] returns ["int32"] because * the OID for PostgreSQL's internal [int4] type is [23]. As another * example, [name_of_type (Int32.of_int 25)] returns ["string"]. *) type inet = Unix.inet_addr * int type timestamptz = Calendar.t * Time_Zone.t type int16 = int type bytea = string (* XXX *) type point = float * float type hstore = (string * string option) list type bool_array = bool array type int32_array = int32 array type int64_array = int64 array type string_array = string array type float_array = float array (** The following conversion functions are used by pa_pgsql to convert * values in and out of the database. *) val string_of_oid : oid -> string val string_of_bool : bool -> string val string_of_int : int -> string val string_of_int16 : int16 -> string val string_of_int32 : int32 -> string val string_of_int64 : int64 -> string val string_of_float : float -> string val string_of_point : point -> string val string_of_hstore : hstore -> string val string_of_inet : inet -> string val string_of_timestamp : Calendar.t -> string val string_of_timestamptz : timestamptz -> string val string_of_date : Date.t -> string val string_of_time : Time.t -> string val string_of_interval : Calendar.Period.t -> string val string_of_bytea : bytea -> string val string_of_string : string -> string val string_of_unit : unit -> string val string_of_bool_array : bool_array -> string val string_of_int32_array : int32_array -> string val string_of_int64_array : int64_array -> string val string_of_string_array : string_array -> string val string_of_float_array : float_array -> string val oid_of_string : string -> oid val bool_of_string : string -> bool val int_of_string : string -> int val int16_of_string : string -> int16 val int32_of_string : string -> int32 val int64_of_string : string -> int64 val float_of_string : string -> float val point_of_string : string -> point val hstore_of_string : string -> hstore val inet_of_string : string -> inet val timestamp_of_string : string -> Calendar.t val timestamptz_of_string : string -> timestamptz val date_of_string : string -> Date.t val time_of_string : string -> Time.t val interval_of_string : string -> Calendar.Period.t val bytea_of_string : string -> bytea val unit_of_string : string -> unit val bool_array_of_string : string -> bool_array val int32_array_of_string : string -> int32_array val int64_array_of_string : string -> int64_array val string_array_of_string : string -> string_array val float_array_of_string : string -> float_array val bind : 'a monad -> ('a -> 'b monad) -> 'b monad val return : 'a -> 'a monad end module Make : functor (Thread : THREAD) -> PGOCAML_GENERIC with type 'a monad = 'a Thread.t pgocaml-1.7.1/src/PGOCaml_config.ml0000664000175000017500000000007312044774143016020 0ustar dariodariolet default_unix_domain_socket_dir = "/var/run/postgresql" pgocaml-1.7.1/src/pa_pgsql.ml0000664000175000017500000003635012073273207015063 0ustar dariodario(* PG'OCaml - type safe interface to PostgreSQL. * Copyright (C) 2005-2009 Richard Jones and other authors. * * 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 General Public License * along with this library; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. *) open Camlp4.PreCast open Printf IFDEF USE_BATTERIES THEN open Batteries ELSE open ExtString open ExtList ENDIF let nullable_name = "nullable" let unravel_name = "unravel" let hstore_name = "hstore" (* We need a database connection while compiling. If people use the * override flags like "database=foo", then we may connect to several * databases at once. Keep track of that here. Note that in the normal * case we have just one database handle, opened to the default * database (as controlled by environment variables such as $PGHOST, * $PGDATABASE, etc.) *) type key = { (* None in any of these fields means use the default. *) host : string option; port : int option; user : string option; password : string option; database : string option; unix_domain_socket_dir : string option; } let connections : (key, unit PGOCaml.t) Hashtbl.t = Hashtbl.create 13 let get_connection key = try Hashtbl.find connections key with Not_found -> (* Create a new connection. *) let host = key.host in let port = key.port in let user = key.user in let password = key.password in let database = key.database in let unix_domain_socket_dir = key.unix_domain_socket_dir in let dbh = PGOCaml.connect ?host ?port ?user ?password ?database ?unix_domain_socket_dir () in (* Prepare the nullable test - see result conversions below. *) let nullable_query = "select attnotnull from pg_attribute where attrelid = $1 and attnum = $2" in PGOCaml.prepare dbh ~query:nullable_query ~name:nullable_name (); (* Prepare the unravel test. *) let unravel_query = "select typtype, typbasetype from pg_type where oid = $1" in PGOCaml.prepare dbh ~query:unravel_query ~name:unravel_name (); (* Prepare the hstore test. *) let hstore_query = "select typname from pg_type where oid = $1" in PGOCaml.prepare dbh ~query:hstore_query ~name:hstore_name (); Hashtbl.add connections key dbh; dbh (* Wrapper around [PGOCaml.name_of_type]. *) let name_of_type_wrapper ?modifier dbh oid = try PGOCaml.name_of_type ?modifier oid with PGOCaml.Error msg as exc -> let params = [ Some (PGOCaml.string_of_oid oid) ] in let rows = PGOCaml.execute dbh ~name:hstore_name ~params () in match rows with | [ [ Some "hstore" ] ] -> "hstore" | _ -> raise exc (* By using CREATE DOMAIN, the user may define types which are essentially aliases * for existing types. If the original type is not recognised by PG'OCaml, this * functions recurses through the pg_type table to see if it happens to be an alias * for a type which we do know how to handle. *) let unravel_type dbh orig_type = let rec unravel_type_aux ft = try name_of_type_wrapper dbh ft with PGOCaml.Error msg as exc -> let params = [ Some (PGOCaml.string_of_oid ft) ] in let rows = PGOCaml.execute dbh ~name:unravel_name ~params () in match rows with | [ [ Some typtype ; Some typbasetype ] ] when typtype = "d" -> unravel_type_aux (PGOCaml.oid_of_string typbasetype) | _ -> raise exc in unravel_type_aux orig_type (* Return the list of numbers a <= i < b. *) let rec range a b = if a < b then a :: range (a+1) b else [];; let rex = Pcre.regexp "\\$(@?)(\\??)([_a-z][_a-zA-Z0-9']*)" let pgsql_expand ?(flags = []) _loc dbh query = (* Get the option module *) let option_module = IFDEF USE_BATTERIES THEN <:expr> ELSE <:expr