(* mosml/src/dynlibs/mpq/Postgres.sml. sestoft@dina.kvl.dk -- 1998 -- version 0.05 of 2000-04-28 *) open Dynlib; exception Closed and Null; (* Obtain a handle pointing to the library defining the C functions: *) val dlh = dlopen { lib = "libmpq.so", flag = RTLD_LAZY, global = false } prim_type pgconn_ (* an abstract object containing a PGconn pointer *) type dbconn = { conn : pgconn_, closed : bool ref } prim_type dbresult (* a finalized object containing a PGResult pointer *) (* The alphabetical order of constructors below must agree with function pq_resultstatus in file mpq.c *) datatype dbresultstatus = Bad_response (* An unexpected response was received *) | Command_ok (* The query was a command *) | Copy_in (* The query was "copy <table> from ..." *) | Copy_out (* The query was "copy <table> to ..." *) | Empty_query | Fatal_error | Nonfatal_error | Tuples_ok (* The query successfully returned tuples *) val pq_setdb : { dbhost : string option, (* 0 database server host *) dbname : string option, (* 1 database name *) dboptions : string option, (* 2 options *) dbport : string option, (* 3 database server port *) dbpwd : string option, (* 4 user passwd *) dbtty : string option, (* 5 tty for error log *) dbuser : string option (* 6 database user *) } -> pgconn_ = app1 (dlsym dlh "pq_setdb") val pq_db : pgconn_ -> string = app1 (dlsym dlh "pq_db") val pq_host : pgconn_ -> string option = app1 (dlsym dlh "pq_host") val pq_options : pgconn_ -> string = app1 (dlsym dlh "pq_options") val pq_port : pgconn_ -> string = app1 (dlsym dlh "pq_port") val pq_tty : pgconn_ -> string = app1 (dlsym dlh "pq_tty") val pq_status : pgconn_ -> bool = app1 (dlsym dlh "pq_status") val pq_errormessage : pgconn_ -> string option = app1 (dlsym dlh "pq_errormessage") val pq_finish : pgconn_ -> unit = app1 (dlsym dlh "pq_finish") val pq_reset : pgconn_ -> unit = app1 (dlsym dlh "pq_reset") val pq_exec : pgconn_ -> string -> dbresult = app2 (dlsym dlh "pq_exec") val pq_resultstatus : dbresult -> dbresultstatus = app1 (dlsym dlh "pq_resultstatus") val pq_ntuples : dbresult -> int = app1 (dlsym dlh "pq_ntuples") val pq_cmdtuples : dbresult -> int = app1 (dlsym dlh "pq_cmdtuples") val pq_nfields : dbresult -> int = app1 (dlsym dlh "pq_nfields") val pq_fname : dbresult -> int -> string = app2 (dlsym dlh "pq_fname") val pq_ftype : dbresult -> int -> int = app2 (dlsym dlh "pq_ftype") val pq_fnumber : dbresult -> string -> int = app2 (dlsym dlh "pq_fnumber") val pq_fsize : dbresult -> int -> int = app2 (dlsym dlh "pq_fsize") val pq_getint : dbresult -> int -> int -> int = app3 (dlsym dlh "pq_getint") val pq_getreal : dbresult -> int -> int -> real = app3 (dlsym dlh "pq_getreal") val pq_getstring : dbresult -> int -> int -> string = app3 (dlsym dlh "pq_getstring") val pq_getbool : dbresult -> int -> int -> bool = app3 (dlsym dlh "pq_getbool") val pq_isnull : dbresult -> int -> int -> bool = app3 (dlsym dlh "pq_getisnull") (* Auxiliaries *) fun errormsg fcn conn = case pq_errormessage conn of NONE => raise Fail ("Postgres." ^ fcn ^ ": unknown error") | SOME msg => raise Fail ("Postgres." ^ fcn ^ ": " ^ msg) fun checknot (ref false) = () | checknot (ref true) = raise Closed (* Monitoring the database connection *) fun db {conn, closed} = (checknot closed; pq_db conn) fun host {conn, closed} = (checknot closed; pq_host conn) fun options {conn, closed} = (checknot closed; pq_options conn) fun port {conn, closed} = (checknot closed; pq_port conn) fun tty {conn, closed} = (checknot closed; pq_tty conn) fun status {conn, closed} = (checknot closed; pq_status conn) fun errormessage {conn, closed} : string option = (checknot closed; pq_errormessage conn) fun reset {conn, closed} = (checknot closed; pq_reset conn) (* Query execution and result set access *) fun execute {conn, closed} query = (checknot closed; (pq_exec conn query) handle Fail _ => errormsg "execute" conn) fun resultstatus dbres = pq_resultstatus dbres fun ntuples dbres = pq_ntuples dbres fun cmdtuples dbres = pq_cmdtuples dbres fun nfields dbres = pq_nfields dbres fun fname dbres fno = pq_fname dbres fno fun fnames dbres = Vector.tabulate(nfields dbres, pq_fname dbres) fun fnumber dbres fname = let val i = pq_fnumber dbres fname in if i < 0 then NONE else SOME i end fun fsize dbres fno = pq_fsize dbres fno fun getint dbres fno tupno = if pq_isnull dbres tupno fno then raise Null else pq_getint dbres tupno fno fun getreal dbres fno tupno = if pq_isnull dbres tupno fno then raise Null else pq_getreal dbres tupno fno fun getstring dbres fno tupno = if pq_isnull dbres tupno fno then raise Null else pq_getstring dbres tupno fno fun pq_getdatetime dbres fno tupno : Date.date = case Date.fromString (pq_getstring dbres fno tupno) of NONE => raise Fail "Postgres.pq_getdatetime" | SOME dt => dt fun getdatetime dbres fno tupno = if pq_isnull dbres tupno fno then raise Null else pq_getdatetime dbres tupno fno fun pq_gettime dbres fno tupno : int * int * int = let val s = pq_getstring dbres fno tupno open Substring (* for getc, all *) fun getint src = Option.valOf (Int.scan StringCvt.DEC getc src) fun drop p = StringCvt.dropl p getc fun isSep c = (c = #":") val (hour, src1) = getint (all s) val (min, src2) = getint (drop isSep src1) val (sec, src3) = getint (drop isSep src2) in (hour, min, sec) end handle Option.Option => raise Fail "Postgres.pq_gettime" fun gettime dbres fno tupno = if pq_isnull dbres tupno fno then raise Null else pq_gettime dbres tupno fno fun pq_getdate dbres fno tupno : int * int * int = let val s = pq_getstring dbres fno tupno open Substring (* for getc, all *) fun getint src = Option.valOf (Int.scan StringCvt.DEC getc src) fun drop p = StringCvt.dropl p getc fun isSep c = (c = #"-") val (month, src1) = getint (all s) val (day, src2) = getint (drop isSep src1) val (year, src3) = getint (drop isSep src2) in (year, month, day) end handle Option.Option => raise Fail "Postgres.pq_getdate" fun getdate dbres fno tupno = if pq_isnull dbres tupno fno then raise Null else pq_getdate dbres tupno fno fun getbool dbres fno tupno = if pq_isnull dbres tupno fno then raise Null else pq_getbool dbres tupno fno fun isnull dbres fno tupno = pq_isnull dbres tupno fno type oid = int (* In reality, a 32 bit quantity *) datatype dynval = Bool of bool (* psql bool *) | Int of int (* psql int4 *) | Real of real (* psql float8, float4 *) | String of string (* psql text, varchar *) | Date of int * int * int (* psql date yyyy-mm-dd *) | Time of int * int * int (* psql time hh:mm:ss *) | DateTime of Date.date (* psql datetime *) | Oid of oid (* psql oid *) | Bytea of Word8Array.array (* psql bytea *) | NullVal (* psql NULL *) datatype dyntype = BoolTy | IntTy | RealTy | StringTy | DateTy | TimeTy | DateTimeTy | OidTy | ByteArrTy | UnknownTy of oid (* A translation from Postgres types to Moscow ML types: *) fun totag "bool" = SOME BoolTy | totag "int4" = SOME IntTy | totag "float8" = SOME RealTy | totag "float4" = SOME RealTy | totag "text" = SOME StringTy | totag "varchar" = SOME StringTy | totag "date" = SOME DateTy | totag "datetime" = SOME DateTimeTy | totag "abstime" = SOME DateTimeTy | totag "time" = SOME TimeTy | totag "oid" = SOME OidTy | totag "bytea" = SOME ByteArrTy | totag _ = NONE (* Translation from Moscow ML types to Postgres types: *) fun fromtag BoolTy = "bool" | fromtag IntTy = "int4" | fromtag RealTy = "float8" | fromtag StringTy = "text" | fromtag TimeTy = "time" | fromtag DateTy = "date" | fromtag DateTimeTy = "datetime" | fromtag OidTy = "oid" | fromtag ByteArrTy = "bytea" | fromtag (UnknownTy _) = raise Fail "Postgres.fromtag" local val typetable = ref [] : (oid * dyntype) list refin fun settypetable pconn : unit = let (* Get the oid-to-typname translation from PostgreSQL: *) val res = execute pconn "select typname as type, oid from pg_type" val tups = List.tabulate(ntuples res, fn tupno => (pq_getstring res tupno 0, pq_getint res tupno 1)) fun addty ((tyname, oid), res) = case totag tyname of NONE => res | SOME tag => (oid, tag) :: res in typetable := List.foldr addty [] tups end fun typeof oid = let fun h [] = UnknownTy oid | h ((k, tyno) :: rest) = if k = oid then tyno else h rest in h (!typetable) endend fun ftype dbres fno = typeof (pq_ftype dbres fno) fun ftypes dbres = Vector.tabulate(nfields dbres, ftype dbres) (* Copying to and from the database *) val pq_endcopy : pgconn_ -> unit = app1 (dlsym dlh "pq_endcopy") val pq_getline : pgconn_ -> string option = app1 (dlsym dlh "pq_getline") (* Implements "copy <tablename> to stdout" : *) fun copytableto (pconn as { conn, closed } : dbconn, tablename : string, put : string -> unit) : unit = let val res = execute pconn ("copy " ^ tablename ^ " to stdout"); fun loop NONE = () | loop (SOME s) = (put s; loop (pq_getline conn)) in case resultstatus res of Copy_out => (loop (pq_getline conn); pq_endcopy conn) | _ => raise Fail "Postgres.copytableto" end val pq_putline : pgconn_ -> string -> unit = app2 (dlsym dlh "pq_putline") (* Implements "copy <tablename> from stdin" : *) fun copytablefrom (pconn as { conn, closed } : dbconn, tablename : string, useput : (string -> unit) -> unit) : unit = let val res = execute pconn ("copy " ^ tablename ^ " from stdin"); val putline = pq_putline conn in case resultstatus res of Copy_in => (useput putline; pq_putline conn "\\.\n"; pq_endcopy conn) | _ => raise Fail "Postgres.copytablefrom" end fun openbase (args as { dbhost : string option, (* 0 database server host *) dbname : string option, (* 1 database name *) dboptions : string option, (* 2 options *) dbport : string option, (* 3 database server port *) dbpwd : string option, (* 4 user passwd *) dbtty : string option, (* 5 tty for error log *) dbuser : string option (* 6 database user *) }) : dbconn = let val conn = pq_setdb args in if pq_status conn then let val pconn = { conn = conn, closed = ref false } in (* The typetable is created anew for each database opened: *) settypetable pconn; pconn end else errormsg "openbase" conn end fun closebase { conn : pgconn_, closed : bool ref } = (pq_finish conn; closed := true) fun getdynfield dbres fno : int -> dynval = case ftype dbres fno of BoolTy => (fn tupno => if pq_isnull dbres tupno fno then NullVal else Bool (pq_getbool dbres tupno fno)) | IntTy => (fn tupno => if pq_isnull dbres tupno fno then NullVal else Int (pq_getint dbres tupno fno)) | RealTy => (fn tupno => if pq_isnull dbres tupno fno then NullVal else Real (pq_getreal dbres tupno fno)) | StringTy => (fn tupno => if pq_isnull dbres tupno fno then NullVal else String (pq_getstring dbres tupno fno)) | TimeTy => (fn tupno => if pq_isnull dbres tupno fno then NullVal else Time (pq_gettime dbres tupno fno)) | DateTy => (fn tupno => if pq_isnull dbres tupno fno then NullVal else Date (pq_getdate dbres tupno fno)) | DateTimeTy => (fn tupno => if pq_isnull dbres tupno fno then NullVal else DateTime (pq_getdatetime dbres tupno fno)) | OidTy => raise Fail "Postgres.getdynfield: unimplemented: OidTy" | ByteArrTy => raise Fail "Postgres.getdynfield: unimplemented: ByteArrTy" | UnknownTy oid => raise Fail ("Postgres.getdynfield: unknown type, oid = " ^ Int.toString oid) fun applyto x f = f x fun getdyntup dbres : int -> dynval vector = let val getters = Vector.tabulate(nfields dbres, getdynfield dbres) in fn fieldno => Vector.map (applyto fieldno) getters end fun getdyntups dbres : dynval vector vector = Vector.tabulate(ntuples dbres, getdyntup dbres) local fun i2s i = StringCvt.padLeft #"0" 2 (Int.toString i) fun fmttrip sep (a,b,c) = String.concat[i2s a, sep, i2s b, sep, i2s c]in fun dynval2s (Bool b) = Bool.toString b | dynval2s (Int i) = Int.toString i | dynval2s (Real r) = Real.toString r | dynval2s (String s) = s | dynval2s (Date ymd) = fmttrip "-" ymd | dynval2s (Time hms) = fmttrip ":" hms | dynval2s (DateTime dt) = Date.toString dt | dynval2s (Oid oid) = "<oid>" | dynval2s (Bytea w8a) = "<bytearray>" | dynval2s NullVal = "NULL"end (* Formatting the result of database queries *) local open Msp infix &&in fun formattable (dbres : dbresult) = let fun (f o g) x = f (g x) val fieldnms = prmap (th o $) (vec2list (fnames dbres)) fun fmtval dynval = case dynval of Int _ => tda "ALIGN=RIGHT" ($ (dynval2s dynval)) | Real _ => tda "ALIGN=RIGHT" ($ (dynval2s dynval)) | _ => td ($ (dynval2s dynval)) fun fmtrow tuple = tr(prmap fmtval (vec2list tuple)) val tuples = vec2list (getdyntups dbres) in tablea "BORDER" (tr fieldnms && prsep Nl fmtrow tuples) end fun showquery (dbconn : dbconn) (sql : string) : wseq = let val dbres = execute dbconn sql in case resultstatus dbres of Tuples_ok => formattable dbres | _ => $ "Error: Database query failed or was not a SELECT" end handle Fail msg => $ msgend
(* mosml/src/dynlibs/mpq/Postgres.sml.
sestoft@dina.kvl.dk -- 1998 -- version 0.05 of 2000-04-28 *)
open Dynlib;
exception Closed and Null;
(* Obtain a handle pointing to the library defining the C functions: *)
val dlh = dlopen { lib = "libmpq.so",
flag = RTLD_LAZY,
global = false }
prim_type pgconn_ (* an abstract object containing a PGconn pointer *)
type dbconn = { conn : pgconn_, closed : bool ref }
prim_type dbresult (* a finalized object containing a PGResult pointer *)
(* The alphabetical order of constructors below must agree
with function pq_resultstatus in file mpq.c *)
datatype dbresultstatus =
Bad_response (* An unexpected response was received *)
| Command_ok (* The query was a command *)
| Copy_in (* The query was "copy <table> from ..." *)
| Copy_out (* The query was "copy <table> to ..." *)
| Empty_query
| Fatal_error
| Nonfatal_error
| Tuples_ok (* The query successfully returned tuples *)
val pq_setdb : { dbhost : string option, (* 0 database server host *)
dbname : string option, (* 1 database name *)
dboptions : string option, (* 2 options *)
dbport : string option, (* 3 database server port *)
dbpwd : string option, (* 4 user passwd *)
dbtty : string option, (* 5 tty for error log *)
dbuser : string option (* 6 database user *)
} -> pgconn_ =
app1 (dlsym dlh "pq_setdb")
val pq_db : pgconn_ -> string =
app1 (dlsym dlh "pq_db")
val pq_host : pgconn_ -> string option =
app1 (dlsym dlh "pq_host")
val pq_options : pgconn_ -> string =
app1 (dlsym dlh "pq_options")
val pq_port : pgconn_ -> string =
app1 (dlsym dlh "pq_port")
val pq_tty : pgconn_ -> string =
app1 (dlsym dlh "pq_tty")
val pq_status : pgconn_ -> bool =
app1 (dlsym dlh "pq_status")
val pq_errormessage : pgconn_ -> string option =
app1 (dlsym dlh "pq_errormessage")
val pq_finish : pgconn_ -> unit =
app1 (dlsym dlh "pq_finish")
val pq_reset : pgconn_ -> unit =
app1 (dlsym dlh "pq_reset")
val pq_exec : pgconn_ -> string -> dbresult =
app2 (dlsym dlh "pq_exec")
val pq_resultstatus : dbresult -> dbresultstatus =
app1 (dlsym dlh "pq_resultstatus")
val pq_ntuples : dbresult -> int =
app1 (dlsym dlh "pq_ntuples")
val pq_cmdtuples : dbresult -> int =
app1 (dlsym dlh "pq_cmdtuples")
val pq_nfields : dbresult -> int =
app1 (dlsym dlh "pq_nfields")
val pq_fname : dbresult -> int -> string =
app2 (dlsym dlh "pq_fname")
val pq_ftype : dbresult -> int -> int =
app2 (dlsym dlh "pq_ftype")
val pq_fnumber : dbresult -> string -> int =
app2 (dlsym dlh "pq_fnumber")
val pq_fsize : dbresult -> int -> int =
app2 (dlsym dlh "pq_fsize")
val pq_getint : dbresult -> int -> int -> int =
app3 (dlsym dlh "pq_getint")
val pq_getreal : dbresult -> int -> int -> real =
app3 (dlsym dlh "pq_getreal")
val pq_getstring : dbresult -> int -> int -> string =
app3 (dlsym dlh "pq_getstring")
val pq_getbool : dbresult -> int -> int -> bool =
app3 (dlsym dlh "pq_getbool")
val pq_isnull : dbresult -> int -> int -> bool =
app3 (dlsym dlh "pq_getisnull")
(* Auxiliaries *)
fun errormsg fcn conn =
case pq_errormessage conn of
NONE => raise Fail ("Postgres." ^ fcn ^ ": unknown error")
| SOME msg => raise Fail ("Postgres." ^ fcn ^ ": " ^ msg)
fun checknot (ref false) = ()
| checknot (ref true) = raise Closed
(* Monitoring the database connection *)
fun db {conn, closed} =
(checknot closed; pq_db conn)
fun host {conn, closed} =
(checknot closed; pq_host conn)
fun options {conn, closed} =
(checknot closed; pq_options conn)
fun port {conn, closed} =
(checknot closed; pq_port conn)
fun tty {conn, closed} =
(checknot closed; pq_tty conn)
fun status {conn, closed} =
(checknot closed; pq_status conn)
fun errormessage {conn, closed} : string option =
(checknot closed; pq_errormessage conn)
fun reset {conn, closed} =
(checknot closed; pq_reset conn)
(* Query execution and result set access *)
fun execute {conn, closed} query =
(checknot closed;
(pq_exec conn query) handle Fail _ => errormsg "execute" conn)
fun resultstatus dbres =
pq_resultstatus dbres
fun ntuples dbres =
pq_ntuples dbres
fun cmdtuples dbres =
pq_cmdtuples dbres
fun nfields dbres =
pq_nfields dbres
fun fname dbres fno =
pq_fname dbres fno
fun fnames dbres =
Vector.tabulate(nfields dbres, pq_fname dbres)
fun fnumber dbres fname =
let val i = pq_fnumber dbres fname
in if i < 0 then NONE else SOME i end
fun fsize dbres fno =
pq_fsize dbres fno
fun getint dbres fno tupno =
if pq_isnull dbres tupno fno then
raise Null
else
pq_getint dbres tupno fno
fun getreal dbres fno tupno =
if pq_isnull dbres tupno fno then
raise Null
else
pq_getreal dbres tupno fno
fun getstring dbres fno tupno =
if pq_isnull dbres tupno fno then
raise Null
else
pq_getstring dbres tupno fno
fun pq_getdatetime dbres fno tupno : Date.date =
case Date.fromString (pq_getstring dbres fno tupno) of
NONE => raise Fail "Postgres.pq_getdatetime"
| SOME dt => dt
fun getdatetime dbres fno tupno =
if pq_isnull dbres tupno fno then
raise Null
else
pq_getdatetime dbres tupno fno
fun pq_gettime dbres fno tupno : int * int * int =
let val s = pq_getstring dbres fno tupno
open Substring (* for getc, all *)
fun getint src = Option.valOf (Int.scan StringCvt.DEC getc src)
fun drop p = StringCvt.dropl p getc
fun isSep c = (c = #":")
val (hour, src1) = getint (all s)
val (min, src2) = getint (drop isSep src1)
val (sec, src3) = getint (drop isSep src2)
in
(hour, min, sec)
end
handle Option.Option => raise Fail "Postgres.pq_gettime"
fun gettime dbres fno tupno =
if pq_isnull dbres tupno fno then
raise Null
else
pq_gettime dbres tupno fno
fun pq_getdate dbres fno tupno : int * int * int =
let val s = pq_getstring dbres fno tupno
open Substring (* for getc, all *)
fun getint src = Option.valOf (Int.scan StringCvt.DEC getc src)
fun drop p = StringCvt.dropl p getc
fun isSep c = (c = #"-")
val (month, src1) = getint (all s)
val (day, src2) = getint (drop isSep src1)
val (year, src3) = getint (drop isSep src2)
in
(year, month, day)
end
handle Option.Option => raise Fail "Postgres.pq_getdate"
fun getdate dbres fno tupno =
if pq_isnull dbres tupno fno then
raise Null
else
pq_getdate dbres tupno fno
fun getbool dbres fno tupno =
if pq_isnull dbres tupno fno then
raise Null
else
pq_getbool dbres tupno fno
fun isnull dbres fno tupno =
pq_isnull dbres tupno fno
type oid = int (* In reality, a 32 bit quantity *)
datatype dynval =
Bool of bool (* psql bool *)
| Int of int (* psql int4 *)
| Real of real (* psql float8, float4 *)
| String of string (* psql text, varchar *)
| Date of int * int * int (* psql date yyyy-mm-dd *)
| Time of int * int * int (* psql time hh:mm:ss *)
| DateTime of Date.date (* psql datetime *)
| Oid of oid (* psql oid *)
| Bytea of Word8Array.array (* psql bytea *)
| NullVal (* psql NULL *)
datatype dyntype =
BoolTy | IntTy | RealTy | StringTy | DateTy | TimeTy | DateTimeTy
| OidTy | ByteArrTy | UnknownTy of oid
(* A translation from Postgres types to Moscow ML types: *)
fun totag "bool" = SOME BoolTy
| totag "int4" = SOME IntTy
| totag "float8" = SOME RealTy
| totag "float4" = SOME RealTy
| totag "text" = SOME StringTy
| totag "varchar" = SOME StringTy
| totag "date" = SOME DateTy
| totag "datetime" = SOME DateTimeTy
| totag "abstime" = SOME DateTimeTy
| totag "time" = SOME TimeTy
| totag "oid" = SOME OidTy
| totag "bytea" = SOME ByteArrTy
| totag _ = NONE
(* Translation from Moscow ML types to Postgres types: *)
fun fromtag BoolTy = "bool"
| fromtag IntTy = "int4"
| fromtag RealTy = "float8"
| fromtag StringTy = "text"
| fromtag TimeTy = "time"
| fromtag DateTy = "date"
| fromtag DateTimeTy = "datetime"
| fromtag OidTy = "oid"
| fromtag ByteArrTy = "bytea"
| fromtag (UnknownTy _) = raise Fail "Postgres.fromtag"
local
val typetable = ref [] : (oid * dyntype) list ref
in
fun settypetable pconn : unit =
let (* Get the oid-to-typname translation from PostgreSQL: *)
val res = execute pconn "select typname as type, oid from pg_type"
val tups =
List.tabulate(ntuples res,
fn tupno => (pq_getstring res tupno 0,
pq_getint res tupno 1))
fun addty ((tyname, oid), res) =
case totag tyname of
NONE => res
| SOME tag => (oid, tag) :: res
in
typetable := List.foldr addty [] tups
end
fun typeof oid =
let fun h [] = UnknownTy oid
| h ((k, tyno) :: rest) = if k = oid then tyno else h rest
in h (!typetable) end
end
fun ftype dbres fno =
typeof (pq_ftype dbres fno)
fun ftypes dbres =
Vector.tabulate(nfields dbres, ftype dbres)
(* Copying to and from the database *)
val pq_endcopy : pgconn_ -> unit =
app1 (dlsym dlh "pq_endcopy")
val pq_getline : pgconn_ -> string option =
app1 (dlsym dlh "pq_getline")
(* Implements "copy <tablename> to stdout" : *)
fun copytableto (pconn as { conn, closed } : dbconn,
tablename : string,
put : string -> unit) : unit =
let val res = execute pconn ("copy " ^ tablename ^ " to stdout");
fun loop NONE = ()
| loop (SOME s) = (put s; loop (pq_getline conn))
in
case resultstatus res of
Copy_out => (loop (pq_getline conn); pq_endcopy conn)
| _ => raise Fail "Postgres.copytableto"
end
val pq_putline : pgconn_ -> string -> unit =
app2 (dlsym dlh "pq_putline")
(* Implements "copy <tablename> from stdin" : *)
fun copytablefrom (pconn as { conn, closed } : dbconn,
tablename : string,
useput : (string -> unit) -> unit) : unit =
let val res = execute pconn ("copy " ^ tablename ^ " from stdin");
val putline = pq_putline conn
in
case resultstatus res of
Copy_in => (useput putline;
pq_putline conn "\\.\n";
pq_endcopy conn)
| _ => raise Fail "Postgres.copytablefrom"
end
fun openbase (args as
{ dbhost : string option, (* 0 database server host *)
dbname : string option, (* 1 database name *)
dboptions : string option, (* 2 options *)
dbport : string option, (* 3 database server port *)
dbpwd : string option, (* 4 user passwd *)
dbtty : string option, (* 5 tty for error log *)
dbuser : string option (* 6 database user *)
}) : dbconn =
let val conn = pq_setdb args
in
if pq_status conn then
let val pconn = { conn = conn, closed = ref false }
in (* The typetable is created anew for each database opened: *)
settypetable pconn;
pconn
end
else
errormsg "openbase" conn
end
fun closebase { conn : pgconn_, closed : bool ref } =
(pq_finish conn; closed := true)
fun getdynfield dbres fno : int -> dynval =
case ftype dbres fno of
BoolTy => (fn tupno =>
if pq_isnull dbres tupno fno then NullVal
else Bool (pq_getbool dbres tupno fno))
| IntTy => (fn tupno =>
if pq_isnull dbres tupno fno then NullVal
else Int (pq_getint dbres tupno fno))
| RealTy => (fn tupno =>
if pq_isnull dbres tupno fno then NullVal
else Real (pq_getreal dbres tupno fno))
| StringTy => (fn tupno =>
if pq_isnull dbres tupno fno then NullVal
else String (pq_getstring dbres tupno fno))
| TimeTy => (fn tupno =>
if pq_isnull dbres tupno fno then NullVal
else Time (pq_gettime dbres tupno fno))
| DateTy => (fn tupno =>
if pq_isnull dbres tupno fno then NullVal
else Date (pq_getdate dbres tupno fno))
| DateTimeTy => (fn tupno =>
if pq_isnull dbres tupno fno then NullVal
else DateTime (pq_getdatetime dbres tupno fno))
| OidTy => raise Fail "Postgres.getdynfield: unimplemented: OidTy"
| ByteArrTy => raise Fail "Postgres.getdynfield: unimplemented: ByteArrTy"
| UnknownTy oid =>
raise Fail ("Postgres.getdynfield: unknown type, oid = "
^ Int.toString oid)
fun applyto x f = f x
fun getdyntup dbres : int -> dynval vector =
let val getters = Vector.tabulate(nfields dbres, getdynfield dbres)
in fn fieldno => Vector.map (applyto fieldno) getters end
fun getdyntups dbres : dynval vector vector =
Vector.tabulate(ntuples dbres, getdyntup dbres)
local
fun i2s i = StringCvt.padLeft #"0" 2 (Int.toString i)
fun fmttrip sep (a,b,c) = String.concat[i2s a, sep, i2s b, sep, i2s c]
in
fun dynval2s (Bool b) = Bool.toString b
| dynval2s (Int i) = Int.toString i
| dynval2s (Real r) = Real.toString r
| dynval2s (String s) = s
| dynval2s (Date ymd) = fmttrip "-" ymd
| dynval2s (Time hms) = fmttrip ":" hms
| dynval2s (DateTime dt) = Date.toString dt
| dynval2s (Oid oid) = "<oid>"
| dynval2s (Bytea w8a) = "<bytearray>"
| dynval2s NullVal = "NULL"
end
(* Formatting the result of database queries *)
local
open Msp
infix &&
in
fun formattable (dbres : dbresult) =
let fun (f o g) x = f (g x)
val fieldnms = prmap (th o $) (vec2list (fnames dbres))
fun fmtval dynval =
case dynval of
Int _ => tda "ALIGN=RIGHT" ($ (dynval2s dynval))
| Real _ => tda "ALIGN=RIGHT" ($ (dynval2s dynval))
| _ => td ($ (dynval2s dynval))
fun fmtrow tuple = tr(prmap fmtval (vec2list tuple))
val tuples = vec2list (getdyntups dbres)
in
tablea "BORDER" (tr fieldnms && prsep Nl fmtrow tuples)
end
fun showquery (dbconn : dbconn) (sql : string) : wseq =
let val dbres = execute dbconn sql
in
case resultstatus dbres of
Tuples_ok => formattable dbres
| _ => $ "Error: Database query failed or was not a SELECT"
end
handle Fail msg => $ msg
end