264 lines
7.6 KiB
Factor
Executable File
264 lines
7.6 KiB
Factor
Executable File
! Copyright (C) 2007, 2008 Doug Coleman.
|
|
! See http://factorcode.org/license.txt for BSD license.
|
|
USING: arrays assocs alien alien.syntax continuations io
|
|
kernel math math.parser namespaces prettyprint quotations
|
|
sequences debugger db db.postgresql.lib db.postgresql.ffi
|
|
db.tuples db.types tools.annotations math.ranges
|
|
combinators sequences.lib classes locals words tools.walker
|
|
namespaces.lib accessors random db.queries ;
|
|
USE: tools.walker
|
|
IN: db.postgresql
|
|
|
|
TUPLE: postgresql-db < db
|
|
host port pgopts pgtty db user pass ;
|
|
|
|
TUPLE: postgresql-statement < statement ;
|
|
|
|
TUPLE: postgresql-result-set < result-set ;
|
|
|
|
M: postgresql-db make-db* ( seq tuple -- db )
|
|
>r first4 r>
|
|
swap >>db
|
|
swap >>pass
|
|
swap >>user
|
|
swap >>host ;
|
|
|
|
M: postgresql-db db-open ( db -- db )
|
|
dup {
|
|
[ host>> ]
|
|
[ port>> ]
|
|
[ pgopts>> ]
|
|
[ pgtty>> ]
|
|
[ db>> ]
|
|
[ user>> ]
|
|
[ pass>> ]
|
|
} cleave connect-postgres >>handle ;
|
|
|
|
M: postgresql-db dispose ( db -- )
|
|
handle>> PQfinish ;
|
|
|
|
M: postgresql-statement bind-statement* ( statement -- )
|
|
drop ;
|
|
|
|
GENERIC: postgresql-bind-conversion ( tuple obj -- low-level-binding )
|
|
|
|
M: sql-spec postgresql-bind-conversion ( tuple spec -- obj )
|
|
slot-name>> swap get-slot-named <low-level-binding> ;
|
|
|
|
M: literal-bind postgresql-bind-conversion ( tuple literal-bind -- obj )
|
|
nip value>> <low-level-binding> ;
|
|
|
|
M: generator-bind postgresql-bind-conversion ( tuple generate-bind -- obj )
|
|
dup generator-singleton>> eval-generator
|
|
[ swap slot-name>> rot set-slot-named ] [ <low-level-binding> ] bi ;
|
|
|
|
M: postgresql-statement bind-tuple ( tuple statement -- )
|
|
tuck in-params>>
|
|
[ postgresql-bind-conversion ] with map
|
|
>>bind-params drop ;
|
|
|
|
M: postgresql-result-set #rows ( result-set -- n )
|
|
handle>> PQntuples ;
|
|
|
|
M: postgresql-result-set #columns ( result-set -- n )
|
|
handle>> PQnfields ;
|
|
|
|
: result-handle-n ( result-set -- handle n )
|
|
[ handle>> ] [ n>> ] bi ;
|
|
|
|
M: postgresql-result-set row-column ( result-set column -- obj )
|
|
>r result-handle-n r> pq-get-string ;
|
|
|
|
M: postgresql-result-set row-column-typed ( result-set column -- obj )
|
|
dup pick out-params>> nth type>>
|
|
>r >r result-handle-n r> r> postgresql-column-typed ;
|
|
|
|
M: postgresql-statement query-results ( query -- result-set )
|
|
dup bind-params>> [
|
|
over [ bind-statement ] keep
|
|
do-postgresql-bound-statement
|
|
] [
|
|
dup do-postgresql-statement
|
|
] if*
|
|
postgresql-result-set construct-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 -- ? )
|
|
[ n>> ] [ max>> ] bi < ;
|
|
|
|
M: postgresql-statement dispose ( query -- )
|
|
dup handle>> PQclear
|
|
f >>handle drop ;
|
|
|
|
M: postgresql-result-set dispose ( result-set -- )
|
|
[ handle>> PQclear ]
|
|
[
|
|
0 >>n
|
|
0 >>max
|
|
f >>handle drop
|
|
] bi ;
|
|
|
|
M: postgresql-statement prepare-statement ( statement -- )
|
|
dup
|
|
>r db get handle>> f r>
|
|
[ sql>> ] [ in-params>> ] bi
|
|
length f PQprepare postgresql-error
|
|
>>handle drop ;
|
|
|
|
M: postgresql-db <simple-statement> ( sql in out -- statement )
|
|
postgresql-statement construct-statement ;
|
|
|
|
M: postgresql-db <prepared-statement> ( sql in out -- statement )
|
|
<simple-statement> dup prepare-statement ;
|
|
|
|
: bind-name% ( -- )
|
|
CHAR: $ 0,
|
|
sql-counter [ inc ] [ get 0# ] bi ;
|
|
|
|
M: postgresql-db bind% ( spec -- )
|
|
bind-name% 1, ;
|
|
|
|
M: postgresql-db bind# ( spec obj -- )
|
|
>r bind-name% f swap type>> r> <literal-bind> 1, ;
|
|
|
|
: create-table-sql ( class -- statement )
|
|
[
|
|
"create table " 0% 0%
|
|
"(" 0% [ ", " 0% ] [
|
|
dup column-name>> 0%
|
|
" " 0%
|
|
dup type>> lookup-create-type 0%
|
|
modifiers 0%
|
|
] interleave ");" 0%
|
|
] query-make ;
|
|
|
|
: create-function-sql ( class -- statement )
|
|
[
|
|
>r remove-id r>
|
|
"create function add_" 0% dup 0%
|
|
"(" 0%
|
|
over [ "," 0% ]
|
|
[
|
|
type>> lookup-type 0%
|
|
] interleave
|
|
")" 0%
|
|
" returns bigint as '" 0%
|
|
|
|
"insert into " 0%
|
|
dup 0%
|
|
"(" 0%
|
|
over [ ", " 0% ] [ column-name>> 0% ] interleave
|
|
") values(" 0%
|
|
swap [ ", " 0% ] [ drop bind-name% ] interleave
|
|
"); " 0%
|
|
"select currval(''" 0% 0% "_id_seq'');' language sql;" 0%
|
|
] query-make ;
|
|
|
|
M: postgresql-db create-sql-statement ( class -- seq )
|
|
[
|
|
[ create-table-sql , ] keep
|
|
dup db-columns find-primary-key db-assigned-id-spec?
|
|
[ create-function-sql , ] [ drop ] if
|
|
] { } make ;
|
|
|
|
: drop-function-sql ( class -- statement )
|
|
[
|
|
"drop function add_" 0% 0%
|
|
"(" 0%
|
|
remove-id
|
|
[ ", " 0% ] [ type>> lookup-type 0% ] interleave
|
|
");" 0%
|
|
] query-make ;
|
|
|
|
: drop-table-sql ( table -- statement )
|
|
[
|
|
"drop table " 0% 0% ";" 0% drop
|
|
] query-make ;
|
|
|
|
M: postgresql-db drop-sql-statement ( class -- seq )
|
|
[
|
|
[ drop-table-sql , ] keep
|
|
dup db-columns find-primary-key db-assigned-id-spec?
|
|
[ drop-function-sql , ] [ drop ] if
|
|
] { } make ;
|
|
|
|
M: postgresql-db <insert-db-assigned-statement> ( class -- statement )
|
|
[
|
|
"select add_" 0% 0%
|
|
"(" 0%
|
|
dup find-primary-key 2,
|
|
remove-id
|
|
[ ", " 0% ] [ bind% ] interleave
|
|
");" 0%
|
|
] query-make ;
|
|
|
|
M: postgresql-db <insert-user-assigned-statement> ( class -- statement )
|
|
[
|
|
"insert into " 0% 0%
|
|
"(" 0%
|
|
dup [ ", " 0% ] [ column-name>> 0% ] interleave
|
|
")" 0%
|
|
|
|
" values(" 0%
|
|
[ ", " 0% ] [
|
|
dup type>> +random-id+ = [
|
|
[
|
|
bind-name%
|
|
slot-name>>
|
|
f
|
|
random-id-generator
|
|
] [ type>> ] bi <generator-bind> 1,
|
|
] [
|
|
bind%
|
|
] if
|
|
] interleave
|
|
");" 0%
|
|
] query-make ;
|
|
|
|
M: postgresql-db insert-tuple* ( tuple statement -- )
|
|
query-modify-tuple ;
|
|
|
|
M: postgresql-db persistent-table ( -- hashtable )
|
|
H{
|
|
{ +db-assigned-id+ { "integer" "serial primary key" f } }
|
|
{ +user-assigned-id+ { f f "primary key" } }
|
|
{ +random-id+ { "bigint" "bigint primary key" f } }
|
|
{ TEXT { "text" "text" f } }
|
|
{ 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 } }
|
|
{ +foreign-id+ { f f "references" } }
|
|
{ +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 } }
|
|
} ;
|
|
|
|
M: postgresql-db compound ( str obj -- str' )
|
|
over {
|
|
{ "default" [ first number>string join-space ] }
|
|
{ "varchar" [ first number>string paren append ] }
|
|
{ "references" [
|
|
first2 >r [ unparse join-space ] keep db-columns r>
|
|
swap [ slot-name>> = ] with find nip
|
|
column-name>> paren append
|
|
] }
|
|
[ "no compound found" 3array throw ]
|
|
} case ;
|