factor/basis/db/postgresql/postgresql.factor

294 lines
8.6 KiB
Factor
Raw Normal View History

! Copyright (C) 2007, 2008 Doug Coleman.
2008-02-01 18:43:44 -05:00
! See http://factorcode.org/license.txt for BSD license.
USING: arrays assocs alien alien.syntax continuations io
2008-09-10 23:11:40 -04:00
kernel math math.parser namespaces make prettyprint quotations
sequences debugger db db.postgresql.lib db.postgresql.ffi
db.tuples db.types tools.annotations math.ranges
combinators classes classes.tuple locals words tools.walker
db.private nmake accessors random db.queries destructors
db.tuples.private db.postgresql db.errors.postgresql splitting ;
2008-02-01 18:43:44 -05:00
IN: db.postgresql
TUPLE: postgresql-db host port pgopts pgtty database username password ;
: <postgresql-db> ( -- postgresql-db )
postgresql-db new ;
<PRIVATE
TUPLE: postgresql-db-connection < db-connection ;
: <postgresql-db-connection> ( handle -- db-connection )
postgresql-db-connection new-db-connection
swap >>handle ;
PRIVATE>
2008-04-05 21:22:33 -04:00
TUPLE: postgresql-statement < statement ;
2008-04-05 21:22:33 -04:00
TUPLE: postgresql-result-set < result-set ;
M: postgresql-db db-open ( db -- db-connection )
{
2008-04-05 21:22:33 -04:00
[ host>> ]
[ port>> ]
[ pgopts>> ]
[ pgtty>> ]
[ database>> ]
[ username>> ]
[ password>> ]
} cleave connect-postgres <postgresql-db-connection> ;
2008-02-01 18:43:44 -05:00
2008-12-17 22:04:17 -05:00
M: postgresql-db-connection db-close ( handle -- ) PQfinish ;
2008-02-01 18:43:44 -05:00
2008-09-27 15:07:39 -04:00
M: postgresql-statement bind-statement* ( statement -- ) drop ;
2008-02-01 18:43:44 -05:00
2008-09-09 20:06:47 -04:00
GENERIC: postgresql-bind-conversion ( tuple object -- low-level-binding )
2008-09-09 20:06:47 -04:00
M: sql-spec postgresql-bind-conversion ( tuple spec -- object )
2008-04-21 14:11:19 -04:00
slot-name>> swap get-slot-named <low-level-binding> ;
2008-09-09 20:06:47 -04:00
M: literal-bind postgresql-bind-conversion ( tuple literal-bind -- object )
2008-04-21 14:11:19 -04:00
nip value>> <low-level-binding> ;
2008-09-09 20:06:47 -04:00
M: generator-bind postgresql-bind-conversion ( tuple generate-bind -- object )
2008-04-28 18:17:19 -04:00
dup generator-singleton>> eval-generator
[ swap slot-name>> rot set-slot-named ] [ <low-level-binding> ] bi ;
M: postgresql-statement bind-tuple ( tuple statement -- )
2009-01-23 19:20:47 -05:00
[ nip ] [
in-params>>
[ postgresql-bind-conversion ] with map
] 2bi
2008-04-20 00:18:12 -04:00
>>bind-params drop ;
M: postgresql-result-set #rows ( result-set -- n )
2008-04-05 21:22:33 -04:00
handle>> PQntuples ;
2008-02-01 18:43:44 -05:00
M: postgresql-result-set #columns ( result-set -- n )
2008-04-05 21:22:33 -04:00
handle>> PQnfields ;
2008-02-01 18:43:44 -05:00
2008-04-20 00:48:07 -04:00
: result-handle-n ( result-set -- handle n )
[ handle>> ] [ n>> ] bi ;
2008-09-09 20:06:47 -04:00
M: postgresql-result-set row-column ( result-set column -- object )
2008-09-27 15:07:39 -04:00
[ result-handle-n ] dip pq-get-string ;
2008-09-09 20:06:47 -04:00
M: postgresql-result-set row-column-typed ( result-set column -- object )
2008-04-20 00:41:48 -04:00
dup pick out-params>> nth type>>
2008-09-27 15:07:39 -04:00
[ result-handle-n ] 2dip postgresql-column-typed ;
M: postgresql-statement query-results ( query -- result-set )
2008-04-20 00:41:48 -04:00
dup bind-params>> [
over [ bind-statement ] keep
do-postgresql-bound-statement
] [
dup do-postgresql-statement
] if*
postgresql-result-set new-result-set
dup init-result-set ;
M: postgresql-result-set advance-row ( result-set -- )
[ 1 + ] change-n drop ;
M: postgresql-result-set more-rows? ( result-set -- ? )
2008-04-20 00:41:48 -04:00
[ n>> ] [ max>> ] bi < ;
2008-02-01 18:43:44 -05:00
M: postgresql-statement dispose ( query -- )
2008-04-20 00:41:48 -04:00
dup handle>> PQclear
f >>handle drop ;
M: postgresql-result-set dispose ( result-set -- )
2008-04-20 00:41:48 -04:00
[ handle>> PQclear ]
[
0 >>n
0 >>max
f >>handle drop
] bi ;
2008-02-01 18:43:44 -05:00
M: postgresql-statement prepare-statement ( statement -- )
2008-04-20 00:41:48 -04:00
dup
2008-12-17 22:04:17 -05:00
[ db-connection get handle>> f ] dip
2008-04-20 00:41:48 -04:00
[ sql>> ] [ in-params>> ] bi
length f PQprepare postgresql-error
>>handle drop ;
2008-02-01 18:43:44 -05:00
M: postgresql-db-connection <simple-statement> ( sql in out -- statement )
postgresql-statement new-statement ;
2008-02-01 18:43:44 -05:00
M: postgresql-db-connection <prepared-statement> ( sql in out -- statement )
2008-04-20 18:47:43 -04:00
<simple-statement> dup prepare-statement ;
: bind-name% ( -- )
CHAR: $ 0,
2008-04-20 18:50:39 -04:00
sql-counter [ inc ] [ get 0# ] bi ;
M: postgresql-db-connection bind% ( spec -- )
2008-04-20 00:18:12 -04:00
bind-name% 1, ;
M: postgresql-db-connection bind# ( spec object -- )
[ bind-name% f swap type>> ] dip
<literal-bind> 1, ;
: create-table-sql ( class -- statement )
[
2008-09-27 15:07:39 -04:00
dupd
"create table " 0% 0%
2008-04-20 00:18:12 -04:00
"(" 0% [ ", " 0% ] [
dup column-name>> 0%
" " 0%
2008-04-20 16:48:09 -04:00
dup type>> lookup-create-type 0%
modifiers 0%
2008-09-27 15:07:39 -04:00
] interleave
", " 0%
find-primary-key
"primary key(" 0%
[ "," 0% ] [ column-name>> 0% ] interleave
"));" 0%
2008-04-20 18:47:43 -04:00
] query-make ;
: create-function-sql ( class -- statement )
[
[ dup remove-id ] dip
"create function add_" 0% dup 0%
"(" 0%
over [ "," 0% ]
[
2008-04-20 16:48:09 -04:00
type>> lookup-type 0%
] interleave
")" 0%
" returns bigint as '" 0%
"insert into " 0%
dup 0%
"(" 0%
2008-04-20 00:41:48 -04:00
over [ ", " 0% ] [ column-name>> 0% ] interleave
") values(" 0%
swap [ ", " 0% ] [ drop bind-name% ] interleave
"); " 0%
"select currval(''" 0% 0% "_" 0%
find-primary-key first column-name>> 0%
"_seq'');' language sql;" 0%
2008-04-20 18:47:43 -04:00
] query-make ;
M: postgresql-db-connection create-sql-statement ( class -- seq )
[
[ create-table-sql , ] keep
2008-09-27 15:07:39 -04:00
dup db-assigned? [ create-function-sql , ] [ drop ] if
] { } make ;
: drop-function-sql ( class -- statement )
[
"drop function add_" 0% 0%
"(" 0%
remove-id
2008-04-20 16:48:09 -04:00
[ ", " 0% ] [ type>> lookup-type 0% ] interleave
");" 0%
2008-04-20 18:47:43 -04:00
] query-make ;
: drop-table-sql ( table -- statement )
[
"drop table " 0% 0% drop
2008-04-20 18:47:43 -04:00
] query-make ;
M: postgresql-db-connection drop-sql-statement ( class -- seq )
[
[ drop-table-sql , ] keep
2008-09-27 15:07:39 -04:00
dup db-assigned? [ drop-function-sql , ] [ drop ] if
] { } make ;
M: postgresql-db-connection <insert-db-assigned-statement> ( class -- statement )
[
"select add_" 0% 0%
"(" 0%
2008-09-27 15:07:39 -04:00
dup find-primary-key first 2,
remove-id
[ ", " 0% ] [ bind% ] interleave
");" 0%
2008-04-20 18:47:43 -04:00
] query-make ;
M: postgresql-db-connection <insert-user-assigned-statement> ( class -- statement )
[
"insert into " 0% 0%
"(" 0%
2008-04-20 00:41:48 -04:00
dup [ ", " 0% ] [ column-name>> 0% ] interleave
")" 0%
" values(" 0%
2008-04-21 14:11:19 -04:00
[ ", " 0% ] [
dup type>> +random-id+ = [
[
2008-04-28 18:17:19 -04:00
bind-name%
slot-name>>
f
random-id-generator
2008-04-21 14:11:19 -04:00
] [ type>> ] bi <generator-bind> 1,
] [
bind%
] if
] interleave
");" 0%
2008-04-20 18:47:43 -04:00
] query-make ;
M: postgresql-db-connection insert-tuple-set-key ( tuple statement -- )
query-modify-tuple ;
M: postgresql-db-connection persistent-table ( -- hashtable )
H{
2008-09-27 15:07:39 -04:00
{ +db-assigned-id+ { "integer" "serial" f } }
{ +user-assigned-id+ { f f f } }
{ +random-id+ { "bigint" "bigint" f } }
2008-09-27 16:56:43 -04:00
2008-09-27 17:26:21 -04:00
{ +foreign-id+ { f f "references" } }
2008-10-10 16:32:36 -04:00
{ +on-update+ { f f "on update" } }
2008-09-27 16:56:43 -04:00
{ +on-delete+ { f f "on delete" } }
{ +restrict+ { f f "restrict" } }
{ +cascade+ { f f "cascade" } }
{ +set-null+ { f f "set null" } }
{ +set-default+ { f f "set default" } }
{ TEXT { "text" "text" f } }
2008-04-20 16:48:09 -04:00
{ VARCHAR { "varchar" "varchar" f } }
{ INTEGER { "integer" "integer" f } }
{ BIG-INTEGER { "bigint" "bigint" f } }
{ UNSIGNED-BIG-INTEGER { "bigint" "bigint" f } }
{ SIGNED-BIG-INTEGER { "bigint" "bigint" f } }
{ DOUBLE { "real" "real" f } }
{ DATE { "date" "date" f } }
{ TIME { "time" "time" f } }
{ DATETIME { "timestamp" "timestamp" f } }
{ TIMESTAMP { "timestamp" "timestamp" f } }
{ BLOB { "bytea" "bytea" f } }
{ FACTOR-BLOB { "bytea" "bytea" f } }
{ URL { "varchar" "varchar" f } }
2008-04-20 16:48:09 -04:00
{ +autoincrement+ { f f "autoincrement" } }
{ +unique+ { f f "unique" } }
{ +default+ { f f "default" } }
{ +null+ { f f "null" } }
{ +not-null+ { f f "not null" } }
{ system-random-generator { f f f } }
{ secure-random-generator { f f f } }
{ random-generator { f f f } }
} ;
2008-09-09 20:06:47 -04:00
ERROR: no-compound-found string object ;
M: postgresql-db-connection compound ( string object -- string' )
over {
2008-12-04 01:06:02 -05:00
{ "default" [ first number>string " " glue ] }
2008-12-04 03:21:36 -05:00
{ "varchar" [ first number>string "(" ")" surround append ] }
2008-09-27 17:26:21 -04:00
{ "references" [ >reference-string ] }
[ drop no-compound-found ]
} case ;
2009-02-20 23:59:01 -05:00
M: postgresql-db-connection parse-db-error
"\n" split dup length {
{ 1 [ first parse-postgresql-sql-error ] }
{ 2 [ concat parse-postgresql-sql-error ] }
{ 3 [
first3
[ parse-postgresql-sql-error ] 2dip
postgresql-location >>location
] }
} case ;