2 * SQL database interfaces for Standard ML
3 * Copyright (C
) 2003 Adam Chlipala
5 * This library is free software
; you can redistribute it
and/or
6 * modify it under the terms
of the GNU Lesser General Public
7 * License
as published by the Free Software Foundation
; either
8 * version
2.1 of the License
, or (at your option
) any later version
.
10 * This library is distributed
in the hope that it will be useful
,
11 * but WITHOUT ANY WARRANTY
; without even the implied warranty
of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
. See the GNU
13 * Lesser General Public License for more details
.
15 * You should have received a copy
of the GNU Lesser General Public
16 * License along
with this library
; if not
, write to the Free Software
17 * Foundation
, Inc
., 59 Temple Place
, Suite
330, Boston
, MA
02111-1307 USA
20 structure PgDriver
:> SQL_DRIVER
=
22 val print
= TextIO.print
24 type conn
= (ST_pg_conn
.tag
, C
.rw
) C
.su_obj C
.ptr
'
26 exception Sql
of string
28 fun cerrmsg con
= Int32
.toString (F_PQstatus
.f
' (C
.Ptr
.ro
' con
)) ^
": "
29 ^ ZString
.toML
' (F_PQerrorMessage
.f
' (C
.Ptr
.ro
' con
))
31 fun errmsg (con
, res
, query
) = Int32
.toString (F_PQresultStatus
.f
' (C
.Ptr
.ro
' res
)) ^
": " ^ ZString
.toML
' (F_PQresultErrorMessage
.f
' (C
.Ptr
.ro
' res
)) ^
": " ^ ZString
.toML
' query
35 val params
= ZString
.dupML
' params
36 val c
= F_PQconnectdb
.f
' params
37 val _
= C
.free
' params
39 if C
.Ptr
.isNull
' c
then
40 raise Sql
"Null connection returned"
42 (case F_PQstatus
.f
' (C
.Ptr
.ro
' c
) of
53 fun close c
= ignore (F_PQfinish
.f
' c
)
57 val q
= ZString
.dupML
' q
58 val res
= F_PQexec
.f
' (c
, q
)
59 val roRes
= C
.Ptr
.ro
' res
60 val code
= F_PQresultStatus
.f
' roRes
61 fun done () = (C
.free
' q
;
69 val msg
= errmsg (c
, res
, q
)
78 val q
= ZString
.dupML
' q
79 val res
= F_PQexec
.f
' (c
, q
)
80 val roRes
= C
.Ptr
.ro
' res
81 fun done () = (C
.free
' q
;
84 val code
= F_PQresultStatus
.f
' roRes
89 val nt
= F_PQntuples
.f
' roRes
90 val nf
= F_PQnfields
.f
' roRes
92 fun builder (i
, acc
) =
97 fun build (~
1, acc
) = acc
99 build (j
-1, ZString
.toML
' (F_PQgetvalue
.f
' (roRes
, i
, j
)) :: acc
)
101 builder (i
+1, f (build (nf
-1, []), acc
))
109 val msg
= errmsg (c
, res
, q
)
117 type timestamp
= Time
.time
118 exception Format
of string
122 "-" ^
Int.toString(~n
)
125 fun intFromSql
"" = 0
127 (case Int.fromString s
of
128 NONE
=> raise Format ("Bad integer: " ^ s
)
138 foldl (fn (c
, s
) => s ^ xch c
) "'" (String.explode s
) ^
"'"
140 fun stringFromSql s
= s
144 "-" ^
Real.toString(~s
)
147 fun realFromSql
"" = 0.0
149 (case Real.fromString s
of
150 NONE
=> raise Format ("Bad real: " ^ s
)
152 fun realToString s
= realToSql s
171 | _
=> raise Format
"Invalid month number"
194 | pad
' (s
, n
) = pad
' ("0" ^ s
, n
-1)
197 val base
= Int.toString n
199 pad
' (base
, Int.max (i
- size base
, 0))
202 fun offsetStr NONE
= "+00"
203 |
offsetStr (SOME n
) =
205 val n
= Int32
.toInt (Time
.toSeconds n
) div 3600
213 fun timestampToSqlUnquoted t
=
215 val d
= Date
.fromTimeLocal t
217 pad (Date
.year d
, 4) ^
"-" ^
pad (fromMonth (Date
.month d
), 2) ^
"-" ^
pad (Date
.day d
, 2) ^
218 " " ^
pad (Date
.hour d
, 2) ^
":" ^
pad (Date
.minute d
, 2) ^
":" ^
pad (Date
.second d
, 2) ^
219 ".000000" ^
offsetStr (Date
.offset d
)
221 fun timestampToSql t
= "'" ^ timestampToSqlUnquoted t ^
"'"
222 fun timestampFromSql s
=
224 val tokens
= String.tokens (fn ch
=> ch
= #
"-" orelse ch
= #
" " orelse ch
= #
":"
225 orelse ch
= #
"." orelse ch
= #
"+") s
228 [year
, mon
, day
, hour
, minute
, second
, _
, offset
] =>
229 Date
.toTime (Date
.date
{day
= intFromSql day
, hour
= intFromSql mon
, minute
= intFromSql minute
,
230 month
= toMonth (intFromSql mon
),
231 offset
= SOME (Time
.fromSeconds (Int32
.fromInt (intFromSql offset
* 3600))),
232 second
= intFromSql second
div 1000, year
= intFromSql year
})
233 |
[year
, mon
, day
, hour
, minute
, second
, _
] =>
234 Date
.toTime (Date
.date
{day
= intFromSql day
, hour
= intFromSql mon
, minute
= intFromSql minute
,
235 month
= toMonth (intFromSql mon
),
237 second
= intFromSql second
div 1000, year
= intFromSql year
})
238 |
[year
, mon
, day
, hour
, minute
, second
] =>
239 Date
.toTime (Date
.date
{day
= intFromSql day
, hour
= intFromSql mon
, minute
= intFromSql minute
,
240 month
= toMonth (intFromSql mon
),
242 second
= intFromSql second
div 1000, year
= intFromSql year
})
243 | _
=> raise Format ("Invalid timestamp " ^ s
)
247 fun boolToSql
true = "TRUE"
248 | boolToSql
false = "FALSE"
250 fun boolFromSql
"FALSE" = false
251 | boolFromSql
"f" = false
252 | boolFromSql
"false" = false
253 | boolFromSql
"n" = false
254 | boolFromSql
"no" = false
255 | boolFromSql
"0" = false
256 | boolFromSql
"" = false
257 | boolFromSql _
= true
260 structure PgClient
= SqlClient(PgDriver
)