]> gitweb.factorcode.org Git - factor.git/blob - basis/db/postgresql/lib/lib.factor
0d50d1ab2c915f5cddb8fa31bca87c3dc23a3676
[factor.git] / basis / db / postgresql / lib / lib.factor
1 ! Copyright (C) 2008 Doug Coleman.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: arrays continuations db io kernel math namespaces
4 quotations sequences db.postgresql.ffi alien alien.c-types
5 db.types tools.walker ascii splitting math.parser combinators
6 libc calendar.format byte-arrays destructors prettyprint
7 accessors strings serialize io.encodings.binary io.encodings.utf8
8 alien.strings io.streams.byte-array summary present urls
9 specialized-arrays.uint specialized-arrays.alien db.private ;
10 IN: db.postgresql.lib
11
12 : postgresql-result-error-message ( res -- str/f )
13     dup zero? [
14         drop f
15     ] [
16         PQresultErrorMessage [ blank? ] trim
17     ] if ;
18
19 : postgres-result-error ( res -- )
20     postgresql-result-error-message [ throw ] when* ;
21
22 : (postgresql-error-message) ( handle -- str )
23     PQerrorMessage
24     "\n" split [ [ blank? ] trim ] map "\n" join ;
25
26 : postgresql-error-message ( -- str )
27     db-connection get handle>> (postgresql-error-message) ;
28
29 : postgresql-error ( res -- res )
30     dup [ postgresql-error-message throw ] unless ;
31
32 ERROR: postgresql-result-null ;
33
34 M: postgresql-result-null summary ( obj -- str )
35     drop "PQexec returned f." ;
36
37 : postgresql-result-ok? ( res -- ? )
38     [ postgresql-result-null ] unless*
39     PQresultStatus
40     PGRES_COMMAND_OK PGRES_TUPLES_OK 2array member? ;
41
42 : connect-postgres ( host port pgopts pgtty db user pass -- conn )
43     PQsetdbLogin
44     dup PQstatus zero? [ (postgresql-error-message) throw ] unless ;
45
46 : do-postgresql-statement ( statement -- res )
47     db-connection get handle>> swap sql>> PQexec dup postgresql-result-ok? [
48         [ postgresql-result-error-message ] [ PQclear ] bi throw
49     ] unless ;
50
51 : type>oid ( symbol -- n )
52     dup array? [ first ] when
53     {
54         { BLOB [ BYTEA-OID ] }
55         { FACTOR-BLOB [ BYTEA-OID ] }
56         [ drop 0 ]
57     } case ;
58
59 : type>param-format ( symbol -- n )
60     dup array? [ first ] when
61     {
62         { BLOB [ 1 ] }
63         { FACTOR-BLOB [ 1 ] }
64         [ drop 0 ]
65     } case ;
66
67 : param-types ( statement -- seq )
68     in-params>> [ type>> type>oid ] uint-array{ } map-as ;
69
70 : malloc-byte-array/length ( byte-array -- alien length )
71     [ malloc-byte-array &free ] [ length ] bi ;
72
73 : default-param-value ( obj -- alien n )
74     number>string* dup [ utf8 malloc-string &free ] when 0 ;
75
76 : param-values ( statement -- seq seq2 )
77     [ bind-params>> ] [ in-params>> ] bi
78     [
79         [ value>> ] [ type>> ] bi* {
80             { FACTOR-BLOB [
81                 dup [ object>bytes malloc-byte-array/length ] [ 0 ] if
82             ] }
83             { BLOB [ dup [ malloc-byte-array/length ] [ 0 ] if ] }
84             { DATE [ dup [ timestamp>ymd ] when default-param-value ] }
85             { TIME [ dup [ timestamp>hms ] when default-param-value ] }
86             { DATETIME [ dup [ timestamp>ymdhms ] when default-param-value ] }
87             { TIMESTAMP [ dup [ timestamp>ymdhms ] when default-param-value ] }
88             { URL [ dup [ present ] when default-param-value ] }
89             [ drop default-param-value ]
90         } case 2array
91     ] 2map flip [
92         f f
93     ] [
94         first2 [ >void*-array ] [ >uint-array ] bi*
95     ] if-empty ;
96
97 : param-formats ( statement -- seq )
98     in-params>> [ type>> type>param-format ] uint-array{ } map-as ;
99
100 : do-postgresql-bound-statement ( statement -- res )
101     [
102         [ db-connection get handle>> ] dip
103         {
104             [ sql>> ]
105             [ bind-params>> length ]
106             [ param-types ]
107             [ param-values ]
108             [ param-formats ]
109         } cleave
110         0 PQexecParams dup postgresql-result-ok? [
111             [ postgresql-result-error-message ] [ PQclear ] bi throw
112         ] unless
113     ] with-destructors ;
114
115 : pq-get-is-null ( handle row column -- ? )
116     PQgetisnull 1 = ;
117
118 : pq-get-string ( handle row column -- obj )
119     3dup PQgetvalue utf8 alien>string
120     dup empty? [ [ pq-get-is-null f ] dip ? ] [ [ 3drop ] dip ] if ;
121
122 : pq-get-number ( handle row column -- obj )
123     pq-get-string dup [ string>number ] when ;
124
125 TUPLE: postgresql-malloc-destructor alien ;
126 C: <postgresql-malloc-destructor> postgresql-malloc-destructor
127
128 M: postgresql-malloc-destructor dispose ( obj -- )
129     alien>> PQfreemem ;
130
131 : &postgresql-free ( alien -- alien )
132     dup <postgresql-malloc-destructor> &dispose drop ; inline
133
134 : pq-get-blob ( handle row column -- obj/f )
135     [ PQgetvalue ] 3keep 3dup PQgetlength
136     dup 0 > [
137         [ 3drop ] dip
138         [
139             memory>byte-array >string
140             0 <uint>
141             [
142                 PQunescapeBytea dup zero? [
143                     postgresql-result-error-message throw
144                 ] [
145                     &postgresql-free
146                 ] if
147             ] keep
148             *uint memory>byte-array
149         ] with-destructors 
150     ] [
151         drop pq-get-is-null nip [ f ] [ B{ } clone ] if
152     ] if ;
153
154 : postgresql-column-typed ( handle row column type -- obj )
155     dup array? [ first ] when
156     {
157         { +db-assigned-id+ [ pq-get-number ] }
158         { +random-id+ [ pq-get-number ] }
159         { INTEGER [ pq-get-number ] }
160         { BIG-INTEGER [ pq-get-number ] }
161         { DOUBLE [ pq-get-number ] }
162         { TEXT [ pq-get-string ] }
163         { VARCHAR [ pq-get-string ] }
164         { DATE [ pq-get-string dup [ ymd>timestamp ] when ] }
165         { TIME [ pq-get-string dup [ hms>timestamp ] when ] }
166         { TIMESTAMP [ pq-get-string dup [ ymdhms>timestamp ] when ] }
167         { DATETIME [ pq-get-string dup [ ymdhms>timestamp ] when ] }
168         { BLOB [ pq-get-blob ] }
169         { URL [ pq-get-string dup [ >url ] when ] }
170         { FACTOR-BLOB [
171             pq-get-blob
172             dup [ bytes>object ] when ] }
173         [ no-sql-type ]
174     } case ;