Merge git://factorcode.org/git/factor

db4
Eduardo Cavazos 2008-02-25 15:52:41 -06:00
commit f414f304ab
31 changed files with 1184 additions and 510 deletions

View File

@ -42,7 +42,7 @@ definition-observers global [ V{ } like ] change-at
GENERIC: definitions-changed ( assoc obj -- ) GENERIC: definitions-changed ( assoc obj -- )
: add-definition-observer ( obj -- ) : add-definition-observer ( obj -- )
definition-observers get push ; definition-observers get push-new ;
: remove-definition-observer ( obj -- ) : remove-definition-observer ( obj -- )
definition-observers get delete ; definition-observers get delete ;

View File

@ -19,8 +19,7 @@ ARTICLE: "threads-yield" "Yielding and suspending threads"
{ $subsection yield } { $subsection yield }
"Sleeping for a period of time:" "Sleeping for a period of time:"
{ $subsection sleep } { $subsection sleep }
"Interruptible sleep:" "Interrupting sleep:"
{ $subsection nap }
{ $subsection interrupt } { $subsection interrupt }
"Threads can be suspended and woken up at some point in the future when a condition is satisfied:" "Threads can be suspended and woken up at some point in the future when a condition is satisfied:"
{ $subsection suspend } { $subsection suspend }
@ -106,14 +105,17 @@ HELP: stop
HELP: yield HELP: yield
{ $description "Adds the current thread to the end of the run queue, and switches to the next runnable thread." } ; { $description "Adds the current thread to the end of the run queue, and switches to the next runnable thread." } ;
HELP: sleep-until
{ $values { "time/f" "a non-negative integer or " { $link f } } }
{ $description "Suspends the current thread until the given time, or indefinitely if a value of " { $link f } " is passed in."
$nl
"Other threads may interrupt the sleep by calling " { $link interrupt } "." } ;
HELP: sleep HELP: sleep
{ $values { "ms" "a non-negative integer" } } { $values { "ms" "a non-negative integer" } }
{ $description "Suspends the current thread for " { $snippet "ms" } " milliseconds." } { $description "Suspends the current thread for " { $snippet "ms" } " milliseconds."
{ $errors "Throws an error if another thread interrupted the sleep with " { $link interrupt } "." } ; $nl
"Other threads may interrupt the sleep by calling " { $link interrupt } "." } ;
HELP: nap
{ $values { "ms/f" "a non-negative integer or " { $link f } } { "?" "a boolean indicating whether the thread was interrupted" } }
{ $description "Suspends the current thread until another thread interrupts it with " { $link interrupt } ". If the input parameter is not " { $link f } ", then the thread will also wake up if the timeout expires before an interrupt is received." } ;
HELP: interrupt HELP: interrupt
{ $values { "thread" thread } } { $values { "thread" thread } }

View File

@ -75,12 +75,15 @@ PRIVATE>
: sleep-queue 43 getenv ; : sleep-queue 43 getenv ;
: resume ( thread -- ) : resume ( thread -- )
f over set-thread-state
check-registered run-queue push-front ; check-registered run-queue push-front ;
: resume-now ( thread -- ) : resume-now ( thread -- )
f over set-thread-state
check-registered run-queue push-back ; check-registered run-queue push-back ;
: resume-with ( obj thread -- ) : resume-with ( obj thread -- )
f over set-thread-state
check-registered 2array run-queue push-front ; check-registered 2array run-queue push-front ;
<PRIVATE <PRIVATE
@ -131,34 +134,27 @@ PRIVATE>
self swap call next self swap call next
] callcc1 2nip ; inline ] callcc1 2nip ; inline
: yield ( -- ) [ resume ] "yield" suspend drop ; : yield ( -- ) [ resume ] f suspend drop ;
GENERIC: nap-until ( time -- ? ) GENERIC: sleep-until ( time/f -- )
M: integer nap-until [ schedule-sleep ] curry "sleep" suspend ; M: integer sleep-until
[ schedule-sleep ] curry "sleep" suspend drop ;
M: f nap-until drop [ drop ] "interrupt" suspend ; M: f sleep-until
drop [ drop ] "interrupt" suspend drop ;
GENERIC: nap ( time -- ? ) GENERIC: sleep ( ms -- )
M: real nap millis + >integer nap-until ; M: real sleep
millis + >integer sleep-until ;
M: f nap nap-until ;
: sleep-until ( time -- )
nap-until [ "Sleep interrupted" throw ] when ;
: sleep ( time -- )
nap [ "Sleep interrupted" throw ] when ;
: interrupt ( thread -- ) : interrupt ( thread -- )
dup self eq? [ dup thread-state [
drop
] [
dup thread-sleep-entry [ sleep-queue heap-delete ] when* dup thread-sleep-entry [ sleep-queue heap-delete ] when*
f over set-thread-sleep-entry f over set-thread-sleep-entry
t swap resume-with dup resume
] if ; ] when drop ;
: (spawn) ( thread -- ) : (spawn) ( thread -- )
[ [
@ -204,6 +200,7 @@ M: f nap nap-until ;
initial-thread global initial-thread global
[ drop f "Initial" [ die ] <thread> ] cache [ drop f "Initial" [ die ] <thread> ] cache
<box> over set-thread-continuation <box> over set-thread-continuation
f over set-thread-state
dup register-thread dup register-thread
set-self ; set-self ;

View File

@ -62,7 +62,7 @@ SYMBOL: alarm-thread
: alarm-thread-loop ( -- ) : alarm-thread-loop ( -- )
alarms get-global alarms get-global
dup next-alarm nap-until drop dup next-alarm sleep-until
dup trigger-alarms dup trigger-alarms
alarm-thread-loop ; alarm-thread-loop ;

View File

@ -473,9 +473,9 @@ M: timestamp year. ( timestamp -- )
: seconds-since-midnight ( timestamp -- x ) : seconds-since-midnight ( timestamp -- x )
dup beginning-of-day timestamp- ; dup beginning-of-day timestamp- ;
M: timestamp nap-until timestamp>millis nap-until ; M: timestamp sleep-until timestamp>millis sleep-until ;
M: dt nap from-now nap-until ; M: dt sleep from-now sleep-until ;
{ {
{ [ unix? ] [ "calendar.unix" ] } { [ unix? ] [ "calendar.unix" ] }

View File

@ -1,14 +1,18 @@
! Copyright (C) 2008 Doug Coleman. ! Copyright (C) 2008 Doug Coleman.
! See http://factorcode.org/license.txt for BSD license. ! See http://factorcode.org/license.txt for BSD license.
USING: arrays assocs classes continuations kernel math USING: arrays assocs classes continuations kernel math
namespaces sequences sequences.lib tuples words strings ; namespaces sequences sequences.lib tuples words strings
tools.walker ;
IN: db IN: db
TUPLE: db handle insert-statements update-statements delete-statements ; TUPLE: db handle ;
! TUPLE: db handle insert-statements update-statements delete-statements ;
: <db> ( handle -- obj ) : <db> ( handle -- obj )
H{ } clone H{ } clone H{ } clone ! H{ } clone H{ } clone H{ } clone
db construct-boa ; db construct-boa ;
GENERIC: make-db* ( seq class -- db )
: make-db ( seq class -- db ) construct-empty make-db* ;
GENERIC: db-open ( db -- ) GENERIC: db-open ( db -- )
HOOK: db-close db ( handle -- ) HOOK: db-close db ( handle -- )
@ -17,22 +21,29 @@ HOOK: db-close db ( handle -- )
: dispose-db ( db -- ) : dispose-db ( db -- )
dup db [ dup db [
dup db-insert-statements dispose-statements ! dup db-insert-statements dispose-statements
dup db-update-statements dispose-statements ! dup db-update-statements dispose-statements
dup db-delete-statements dispose-statements ! dup db-delete-statements dispose-statements
db-handle db-close db-handle db-close
] with-variable ; ] with-variable ;
TUPLE: statement sql params handle bound? slot-names ; TUPLE: statement handle sql in-params out-params bind-params bound? ;
: <statement> ( sql in out -- statement )
{
set-statement-sql
set-statement-in-params
set-statement-out-params
} statement construct ;
TUPLE: simple-statement ; TUPLE: simple-statement ;
TUPLE: prepared-statement ; TUPLE: prepared-statement ;
HOOK: <simple-statement> db ( str -- statement ) HOOK: <simple-statement> db ( str in out -- statement )
HOOK: <prepared-statement> db ( str -- statement ) HOOK: <prepared-statement> db ( str in out -- statement )
GENERIC: prepare-statement ( statement -- ) GENERIC: prepare-statement ( statement -- )
GENERIC: bind-statement* ( obj statement -- ) GENERIC: bind-statement* ( obj statement -- )
GENERIC: reset-statement ( statement -- ) GENERIC: reset-statement ( statement -- )
GENERIC: insert-statement ( statement -- id ) GENERIC: bind-tuple ( tuple statement -- )
TUPLE: result-set sql params handle n max ; TUPLE: result-set sql params handle n max ;
GENERIC: query-results ( query -- result-set ) GENERIC: query-results ( query -- result-set )
@ -42,12 +53,17 @@ GENERIC# row-column 1 ( result-set n -- obj )
GENERIC: advance-row ( result-set -- ) GENERIC: advance-row ( result-set -- )
GENERIC: more-rows? ( result-set -- ? ) GENERIC: more-rows? ( result-set -- ? )
: execute-statement ( statement -- ) query-results dispose ; : execute-statement ( statement -- )
dup sequence? [
[ execute-statement ] each
] [
query-results dispose
] if ;
: bind-statement ( obj statement -- ) : bind-statement ( obj statement -- )
dup statement-bound? [ dup reset-statement ] when dup statement-bound? [ dup reset-statement ] when
[ bind-statement* ] 2keep [ bind-statement* ] 2keep
[ set-statement-params ] keep [ set-statement-bind-params ] keep
t swap set-statement-bound? ; t swap set-statement-bound? ;
: init-result-set ( result-set -- ) : init-result-set ( result-set -- )
@ -55,7 +71,7 @@ GENERIC: more-rows? ( result-set -- ? )
0 swap set-result-set-n ; 0 swap set-result-set-n ;
: <result-set> ( query handle tuple -- result-set ) : <result-set> ( query handle tuple -- result-set )
>r >r { statement-sql statement-params } get-slots r> >r >r { statement-sql statement-in-params } get-slots r>
{ {
set-result-set-sql set-result-set-sql
set-result-set-params set-result-set-params
@ -75,17 +91,15 @@ GENERIC: more-rows? ( result-set -- ? )
: query-map ( statement quot -- seq ) : query-map ( statement quot -- seq )
accumulator >r query-each r> { } like ; inline accumulator >r query-each r> { } like ; inline
: with-db ( db quot -- ) : with-db ( db seq quot -- )
[ >r make-db dup db-open db r>
over db-open [ db get swap [ drop ] swap compose with-disposal ] curry with-variable ;
[ db swap with-variable ] curry with-disposal
] with-scope ;
: do-query ( query -- result-set ) : default-query ( query -- result-set )
query-results [ [ sql-row ] query-map ] with-disposal ; query-results [ [ sql-row ] query-map ] with-disposal ;
: do-bound-query ( obj query -- rows ) : do-bound-query ( obj query -- rows )
[ bind-statement ] keep do-query ; [ bind-statement ] keep default-query ;
: do-bound-command ( obj query -- ) : do-bound-command ( obj query -- )
[ bind-statement ] keep execute-statement ; [ bind-statement ] keep execute-statement ;
@ -105,11 +119,11 @@ HOOK: rollback-transaction db ( -- )
] with-variable ; ] with-variable ;
: sql-query ( sql -- rows ) : sql-query ( sql -- rows )
<simple-statement> [ do-query ] with-disposal ; f f <simple-statement> [ default-query ] with-disposal ;
: sql-command ( sql -- ) : sql-command ( sql -- )
dup string? [ dup string? [
<simple-statement> [ execute-statement ] with-disposal f f <simple-statement> [ execute-statement ] with-disposal
] [ ] [
! [ ! [
[ sql-command ] each [ sql-command ] each

18
extra/db/postgresql/lib/lib.factor Normal file → Executable file
View File

@ -2,21 +2,25 @@
! See http://factorcode.org/license.txt for BSD license. ! See http://factorcode.org/license.txt for BSD license.
USING: arrays continuations db io kernel math namespaces USING: arrays continuations db io kernel math namespaces
quotations sequences db.postgresql.ffi alien alien.c-types quotations sequences db.postgresql.ffi alien alien.c-types
db.types ; db.types tools.walker ascii splitting ;
IN: db.postgresql.lib IN: db.postgresql.lib
: postgresql-result-error-message ( res -- str/f ) : postgresql-result-error-message ( res -- str/f )
dup zero? [ dup zero? [
drop f drop f
] [ ] [
PQresultErrorMessage [ CHAR: \n = ] right-trim PQresultErrorMessage [ blank? ] trim
] if ; ] if ;
: postgres-result-error ( res -- ) : postgres-result-error ( res -- )
postgresql-result-error-message [ throw ] when* ; postgresql-result-error-message [ throw ] when* ;
: (postgresql-error-message) ( handle -- str )
PQerrorMessage
"\n" split [ [ blank? ] trim ] map "\n" join ;
: postgresql-error-message ( -- str ) : postgresql-error-message ( -- str )
db get db-handle PQerrorMessage [ CHAR: \n = ] right-trim ; db get db-handle (postgresql-error-message) ;
: postgresql-error ( res -- res ) : postgresql-error ( res -- res )
dup [ postgresql-error-message throw ] unless ; dup [ postgresql-error-message throw ] unless ;
@ -27,7 +31,7 @@ IN: db.postgresql.lib
: connect-postgres ( host port pgopts pgtty db user pass -- conn ) : connect-postgres ( host port pgopts pgtty db user pass -- conn )
PQsetdbLogin PQsetdbLogin
dup PQstatus zero? [ postgresql-error-message throw ] unless ; dup PQstatus zero? [ (postgresql-error-message) throw ] unless ;
: do-postgresql-statement ( statement -- res ) : do-postgresql-statement ( statement -- res )
db get db-handle swap statement-sql PQexec dup postgresql-result-ok? [ db get db-handle swap statement-sql PQexec dup postgresql-result-ok? [
@ -37,9 +41,9 @@ IN: db.postgresql.lib
: do-postgresql-bound-statement ( statement -- res ) : do-postgresql-bound-statement ( statement -- res )
>r db get db-handle r> >r db get db-handle r>
[ statement-sql ] keep [ statement-sql ] keep
[ statement-params length f ] keep [ statement-bind-params length f ] keep
statement-params statement-bind-params
[ first number>string* malloc-char-string ] map >c-void*-array [ number>string* malloc-char-string ] map >c-void*-array
f f 0 PQexecParams f f 0 PQexecParams
dup postgresql-result-ok? [ dup postgresql-result-ok? [
dup postgresql-result-error-message swap PQclear throw dup postgresql-result-error-message swap PQclear throw

254
extra/db/postgresql/postgresql-tests.factor Normal file → Executable file
View File

@ -1,13 +1,14 @@
! You will need to run 'createdb factor-test' to create the database. ! You will need to run 'createdb factor-test' to create the database.
! Set username and password in the 'connect' word. ! Set username and password in the 'connect' word.
USING: kernel db.postgresql alien continuations io prettyprint USING: kernel db.postgresql alien continuations io classes
sequences namespaces tools.test db db.types ; prettyprint sequences namespaces tools.test db
db.tuples db.types unicode.case ;
IN: temporary IN: temporary
IN: scratchpad IN: scratchpad
: test-db ( -- postgresql-db ) : test-db ( -- postgresql-db )
"localhost" "postgres" "" "factor-test" <postgresql-db> ; { "localhost" "postgres" "" "factor-test" } postgresql-db ;
IN: temporary IN: temporary
[ ] [ test-db [ ] with-db ] unit-test [ ] [ test-db [ ] with-db ] unit-test
@ -39,7 +40,7 @@ IN: temporary
] [ ] [
test-db [ test-db [
"select * from person where name = $1 and country = $2" "select * from person where name = $1 and country = $2"
<simple-statement> [ f f <simple-statement> [
{ { "Jane" TEXT } { "New Zealand" TEXT } } { { "Jane" TEXT } { "New Zealand" TEXT } }
over do-bound-query over do-bound-query
@ -108,3 +109,248 @@ IN: temporary
"select * from person" sql-query length "select * from person" sql-query length
] with-db ] with-db
] unit-test ] unit-test
: with-dummy-db ( quot -- )
>r T{ postgresql-db } db r> with-variable ;
! TEST TUPLE DB
TUPLE: puppy id name age ;
: <puppy> ( name age -- puppy )
{ set-puppy-name set-puppy-age } puppy construct ;
puppy "PUPPY" {
{ "id" "ID" +native-id+ +not-null+ }
{ "name" "NAME" { VARCHAR 256 } }
{ "age" "AGE" INTEGER }
} define-persistent
TUPLE: kitty id name age ;
: <kitty> ( name age -- kitty )
{ set-kitty-name set-kitty-age } kitty construct ;
kitty "KITTY" {
{ "id" "ID" INTEGER +assigned-id+ }
{ "name" "NAME" TEXT }
{ "age" "AGE" INTEGER }
} define-persistent
TUPLE: basket id puppies kitties ;
basket "BASKET"
{
{ "id" "ID" +native-id+ +not-null+ }
{ "location" "LOCATION" TEXT }
{ "puppies" { +has-many+ puppy } }
{ "kitties" { +has-many+ kitty } }
} define-persistent
! Create table
[
"create table puppy(id serial primary key not null, name varchar 256, age integer);"
] [
T{ postgresql-db } db [
puppy dup db-columns swap db-table create-table-sql >lower
] with-variable
] unit-test
[
"create table kitty(id integer primary key, name text, age integer);"
] [
T{ postgresql-db } db [
kitty dup db-columns swap db-table create-table-sql >lower
] with-variable
] unit-test
[
"create table basket(id serial primary key not null, location text);"
] [
T{ postgresql-db } db [
basket dup db-columns swap db-table create-table-sql >lower
] with-variable
] unit-test
! Create function
[
"create function add_puppy(varchar,integer) returns bigint as 'insert into puppy(name, age) values($1, $2); select currval(''puppy_id_seq'');' language sql;"
] [
T{ postgresql-db } db [
puppy dup db-columns swap db-table create-function-sql >lower
] with-variable
] unit-test
! Drop table
[
"drop table puppy;"
] [
T{ postgresql-db } db [
puppy db-table drop-table-sql >lower
] with-variable
] unit-test
[
"drop table kitty;"
] [
T{ postgresql-db } db [
kitty db-table drop-table-sql >lower
] with-variable
] unit-test
[
"drop table basket;"
] [
T{ postgresql-db } db [
basket db-table drop-table-sql >lower
] with-variable
] unit-test
! Drop function
[
"drop function add_puppy(varchar, integer);"
] [
T{ postgresql-db } db [
puppy dup db-columns swap db-table drop-function-sql >lower
] with-variable
] unit-test
! Insert
[
] [
T{ postgresql-db } db [
puppy <insert-native-statement>
] with-variable
] unit-test
[
"insert into kitty(id, name, age) values($1, $2, $3);"
{
T{
sql-spec
f
"id"
"ID"
INTEGER
{ +assigned-id+ }
+assigned-id+
}
T{ sql-spec f "name" "NAME" TEXT { } f }
T{ sql-spec f "age" "AGE" INTEGER { } f }
}
{ }
] [
T{ postgresql-db } db [
kitty <insert-assigned-statement>
] with-variable
] unit-test
! Update
[
"update puppy set name = $1, age = $2 where id = $3"
{
T{ sql-spec f "name" "NAME" { VARCHAR 256 } { } f }
T{ sql-spec f "age" "AGE" INTEGER { } f }
T{
sql-spec
f
"id"
"ID"
+native-id+
{ +not-null+ }
+native-id+
}
}
{ }
] [
T{ postgresql-db } db [
puppy dup db-columns swap db-table <update-tuple-statement> >r >r >lower r> r>
] with-variable
] unit-test
[
"update kitty set name = $1, age = $2 where id = $3"
{
T{ sql-spec f "name" "NAME" TEXT { } f }
T{ sql-spec f "age" "AGE" INTEGER { } f }
T{
sql-spec
f
"id"
"ID"
INTEGER
{ +assigned-id+ }
+assigned-id+
}
}
{ }
] [
T{ postgresql-db } db [
kitty dup db-columns swap db-table <update-tuple-statement> >r >r >lower r> r>
] with-variable
] unit-test
! Delete
[
"delete from puppy where id = $1"
{
T{
sql-spec
f
"id"
"ID"
+native-id+
{ +not-null+ }
+native-id+
}
}
{ }
] [
T{ postgresql-db } db [
puppy dup db-columns swap db-table <delete-tuple-statement> >r >r >lower r> r>
] with-variable
] unit-test
[
"delete from KITTY where ID = $1"
{
T{
sql-spec
f
"id"
"ID"
INTEGER
{ +assigned-id+ }
+assigned-id+
}
}
{ }
] [
T{ postgresql-db } db [
kitty dup db-columns swap db-table <delete-tuple-statement>
] with-variable
] unit-test
! Select
[
"select from PUPPY ID, NAME, AGE where NAME = $1;"
{ T{ sql-spec f "name" "NAME" { VARCHAR 256 } { } f } }
{
T{
sql-spec
f
"id"
"ID"
+native-id+
{ +not-null+ }
+native-id+
}
T{ sql-spec f "name" "NAME" { VARCHAR 256 } { } f }
T{ sql-spec f "age" "AGE" INTEGER { } f }
}
] [
T{ postgresql-db } db [
T{ puppy f f "Mr. Clunkers" }
<select-by-slots-statement>
] with-variable
] unit-test

View File

@ -4,25 +4,28 @@ USING: arrays assocs alien alien.syntax continuations io
kernel math math.parser namespaces prettyprint quotations kernel math math.parser namespaces prettyprint quotations
sequences debugger db db.postgresql.lib db.postgresql.ffi sequences debugger db db.postgresql.lib db.postgresql.ffi
db.tuples db.types tools.annotations math.ranges db.tuples db.types tools.annotations math.ranges
combinators ; combinators sequences.lib classes locals words tools.walker ;
IN: db.postgresql IN: db.postgresql
TUPLE: postgresql-db host port pgopts pgtty db user pass ; TUPLE: postgresql-db host port pgopts pgtty db user pass ;
TUPLE: postgresql-statement ; TUPLE: postgresql-statement ;
TUPLE: postgresql-result-set ; TUPLE: postgresql-result-set ;
: <postgresql-statement> ( statement -- postgresql-statement ) : <postgresql-statement> ( statement in out -- postgresql-statement )
<statement>
postgresql-statement construct-delegate ; postgresql-statement construct-delegate ;
: <postgresql-db> ( host user pass db -- obj ) M: postgresql-db make-db* ( seq tuple -- db )
{ >r first4 r> [
set-postgresql-db-host {
set-postgresql-db-user set-postgresql-db-host
set-postgresql-db-pass set-postgresql-db-user
set-postgresql-db-db set-postgresql-db-pass
} postgresql-db construct ; set-postgresql-db-db
} set-slots
] keep ;
M: postgresql-db db-open ( db -- ) M: postgresql-db db-open ( db -- )
dup { dup {
postgresql-db-host postgresql-db-host
postgresql-db-port postgresql-db-port
postgresql-db-pgopts postgresql-db-pgopts
@ -35,15 +38,18 @@ M: postgresql-db db-open ( db -- )
M: postgresql-db dispose ( db -- ) M: postgresql-db dispose ( db -- )
db-handle PQfinish ; db-handle PQfinish ;
: with-postgresql ( host ust pass db quot -- )
>r <postgresql-db> r> with-disposal ;
M: postgresql-statement bind-statement* ( seq statement -- ) M: postgresql-statement bind-statement* ( seq statement -- )
set-statement-params ; set-statement-bind-params ;
M: postgresql-statement reset-statement ( statement -- ) M: postgresql-statement reset-statement ( statement -- )
drop ; drop ;
M: postgresql-statement bind-tuple ( tuple statement -- )
[
statement-in-params
[ sql-spec-slot-name swap get-slot-named ] with map
] keep set-statement-bind-params ;
M: postgresql-result-set #rows ( result-set -- n ) M: postgresql-result-set #rows ( result-set -- n )
result-set-handle PQntuples ; result-set-handle PQntuples ;
@ -56,19 +62,8 @@ M: postgresql-result-set row-column ( result-set n -- obj )
M: postgresql-result-set row-column-typed ( result-set n type -- obj ) M: postgresql-result-set row-column-typed ( result-set n type -- obj )
>r row-column r> sql-type>factor-type ; >r row-column r> sql-type>factor-type ;
M: postgresql-result-set sql-type>factor-type ( obj type -- newobj )
{
{ INTEGER [ string>number ] }
{ BIG_INTEGER [ string>number ] }
{ DOUBLE [ string>number ] }
[ drop ]
} case ;
M: postgresql-statement insert-statement ( statement -- id )
query-results [ 0 row-column ] with-disposal string>number ;
M: postgresql-statement query-results ( query -- result-set ) M: postgresql-statement query-results ( query -- result-set )
dup statement-params [ dup statement-bind-params [
over [ bind-statement ] keep over [ bind-statement ] keep
do-postgresql-bound-statement do-postgresql-bound-statement
] [ ] [
@ -96,17 +91,15 @@ M: postgresql-result-set dispose ( result-set -- )
M: postgresql-statement prepare-statement ( statement -- ) M: postgresql-statement prepare-statement ( statement -- )
[ [
>r db get db-handle "" r> >r db get db-handle "" r>
dup statement-sql swap statement-params dup statement-sql swap statement-in-params
length f PQprepare postgresql-error length f PQprepare postgresql-error
] keep set-statement-handle ; ] keep set-statement-handle ;
M: postgresql-db <simple-statement> ( sql -- statement ) M: postgresql-db <simple-statement> ( sql in out -- statement )
{ set-statement-sql } statement construct
<postgresql-statement> ; <postgresql-statement> ;
M: postgresql-db <prepared-statement> ( sql -- statement ) M: postgresql-db <prepared-statement> ( sql in out -- statement )
{ set-statement-sql } statement construct <postgresql-statement> dup prepare-statement ;
<postgresql-statement> ;
M: postgresql-db begin-transaction ( -- ) M: postgresql-db begin-transaction ( -- )
"BEGIN" sql-command ; "BEGIN" sql-command ;
@ -117,139 +110,176 @@ M: postgresql-db commit-transaction ( -- )
M: postgresql-db rollback-transaction ( -- ) M: postgresql-db rollback-transaction ( -- )
"ROLLBACK" sql-command ; "ROLLBACK" sql-command ;
: postgresql-type-hash* ( -- assoc ) SYMBOL: postgresql-counter
H{ : bind-name% ( -- )
{ SERIAL "serial" } CHAR: $ 0,
} ; postgresql-counter [ inc ] keep get 0# ;
: postgresql-type-hash ( -- assoc ) M: postgresql-db bind% ( spec -- )
1, bind-name% ;
: postgresql-make ( class quot -- )
>r sql-props r>
[ postgresql-counter off ] swap compose
{ "" { } { } } nmake <postgresql-statement> ;
: create-table-sql ( class -- statement )
[
"create table " 0% 0%
"(" 0%
[ ", " 0% ] [
dup sql-spec-column-name 0%
" " 0%
dup sql-spec-type t lookup-type 0%
modifiers 0%
] interleave ");" 0%
] postgresql-make ;
: create-function-sql ( class -- statement )
[
>r remove-id r>
"create function add_" 0% dup 0%
"(" 0%
over [ "," 0% ]
[
sql-spec-type f lookup-type 0%
] interleave
")" 0%
" returns bigint as '" 0%
"insert into " 0%
dup 0%
"(" 0%
over [ ", " 0% ] [ sql-spec-column-name 0% ] interleave
") values(" 0%
swap [ ", " 0% ] [ drop bind-name% ] interleave
"); " 0%
"select currval(''" 0% 0% "_id_seq'');' language sql;" 0%
] postgresql-make ;
M: postgresql-db create-sql-statement ( class -- seq )
[
[ create-table-sql , ] keep
dup db-columns find-primary-key native-id?
[ create-function-sql , ] [ drop ] if
] { } make ;
: drop-function-sql ( class -- statement )
[
"drop function add_" 0% 0%
"(" 0%
remove-id
[ ", " 0% ] [ sql-spec-type f lookup-type 0% ] interleave
");" 0%
] postgresql-make ;
: drop-table-sql ( table -- statement )
[
"drop table " 0% 0% ";" 0% drop
] postgresql-make ;
M: postgresql-db drop-sql-statement ( class -- seq )
[
[ drop-table-sql , ] keep
dup db-columns find-primary-key native-id?
[ drop-function-sql , ] [ drop ] if
] { } make ;
M: postgresql-db <insert-native-statement> ( class -- statement )
[
"select add_" 0% 0%
"(" 0%
dup find-primary-key 2,
remove-id
[ ", " 0% ] [ bind% ] interleave
");" 0%
] postgresql-make ;
M: postgresql-db <insert-assigned-statement> ( class -- statement )
[
"insert into " 0% 0%
"(" 0%
dup [ ", " 0% ] [ sql-spec-column-name 0% ] interleave
")" 0%
" values(" 0%
[ ", " 0% ] [ bind% ] interleave
");" 0%
] postgresql-make ;
M: postgresql-db insert-tuple* ( tuple statement -- )
query-modify-tuple ;
M: postgresql-db <update-tuple-statement> ( class -- statement )
[
"update " 0% 0%
" set " 0%
dup remove-id
[ ", " 0% ]
[ dup sql-spec-column-name 0% " = " 0% bind% ] interleave
" where " 0%
find-primary-key
dup sql-spec-column-name 0% " = " 0% bind%
] postgresql-make ;
M: postgresql-db <delete-tuple-statement> ( class -- statement )
[
"delete from " 0% 0%
" where " 0%
find-primary-key
dup sql-spec-column-name 0% " = " 0% bind%
] postgresql-make ;
M: postgresql-db <select-by-slots-statement> ( tuple class -- statement )
[
! tuple columns table
"select " 0%
over [ ", " 0% ]
[ dup sql-spec-column-name 0% 2, ] interleave
" from " 0% 0%
[ sql-spec-slot-name swap get-slot-named ] with subset
" where " 0%
[ ", " 0% ]
[ dup sql-spec-column-name 0% " = " 0% bind% ] interleave
";" 0%
] postgresql-make ;
M: postgresql-db type-table ( -- hash )
H{ H{
{ INTEGER "integer" } { +native-id+ "integer" }
{ SERIAL "integer" }
{ TEXT "text" } { TEXT "text" }
{ VARCHAR "varchar" } { VARCHAR "varchar" }
{ INTEGER "integer" }
{ DOUBLE "real" } { DOUBLE "real" }
{ TIMESTAMP "timestamp" }
} ; } ;
: enquote ( str -- newstr ) "(" swap ")" 3append ; M: postgresql-db create-type-table ( -- hash )
: postgresql-type ( str n/str -- newstr )
" " swap number>string* enquote 3append ;
: >sql-type* ( obj -- str )
dup pair? [
first2 >r >sql-type* r> postgresql-type
] [
dup postgresql-type-hash* at* [
nip
] [
drop >sql-type
] if
] if ;
M: postgresql-db >sql-type ( hash obj -- str )
dup pair? [
first2 >r >sql-type r> postgresql-type
] [
postgresql-type-hash at* [
no-sql-type
] unless
] if ;
: insert-function ( columns table -- sql )
[
>r remove-id r>
"create function add_" % dup %
"(" %
over [ "," % ]
[ third dup array? [ first ] when >sql-type % ] interleave
")" %
" returns bigint as '" %
2dup "insert into " %
%
"(" %
dup [ ", " % ] [ second % ] interleave
") " %
" values (" %
length [1,b] [ ", " % ] [ "$" % # ] interleave
"); " %
"select currval(''" % % "_id_seq'');' language sql;" %
drop
] "" make ;
: drop-function ( columns table -- sql )
[
>r remove-id r>
"drop function add_" % %
"(" %
[ "," % ] [ third >sql-type % ] interleave
")" %
] "" make ;
M: postgresql-db create-sql ( columns table -- seq )
[
[
2dup
"create table " % %
" (" % [ ", " % ] [
dup second % " " %
dup third >sql-type* % " " %
sql-modifiers " " join %
] interleave "); " %
] "" make ,
over native-id? [ insert-function , ] [ 2drop ] if
] { } make ;
M: postgresql-db drop-sql ( columns table -- seq )
[
[
dup "drop table " % % ";" %
] "" make ,
over native-id? [ drop-function , ] [ 2drop ] if
] { } make ;
M: postgresql-db insert-sql* ( columns table -- slot-names sql )
[
"select add_" % %
"(" %
length [1,b] [ ", " % ] [ "$" % # ] interleave
")" %
] "" make ;
M: postgresql-db update-sql* ( columns table -- slot-names sql )
[
"update " %
%
" set " %
dup remove-id
dup length [1,b] swap 2array flip
[ ", " % ] [ first2 second % " = $" % # ] interleave
" where " %
[ primary-key? ] find nip second dup % " = $" % length 2 + #
] "" make ;
M: postgresql-db delete-sql* ( columns table -- slot-names sql )
[
"delete from " %
%
" where " %
first second % " = $1" %
] "" make ;
M: postgresql-db select-sql ( columns table -- slot-names sql )
drop ;
M: postgresql-db tuple>params ( columns tuple -- obj )
[ >r dup third swap first r> get-slot-named swap ]
curry { } map>assoc ;
: postgresql-db-modifiers ( -- hashtable )
H{ H{
{ +native-id+ "not null primary key" } { +native-id+ "serial primary key" }
} ;
: postgresql-compound ( str n -- newstr )
over {
{ "default" [ first number>string join-space ] }
{ "varchar" [ first number>string paren append ] }
{ "references" [
first2 >r [ unparse join-space ] keep db-columns r>
swap [ sql-spec-slot-name = ] with find nip
sql-spec-column-name paren append
] }
[ "no compound found" 3array throw ]
} case ;
M: postgresql-db compound-modifier ( str seq -- newstr )
postgresql-compound ;
M: postgresql-db modifier-table ( -- hashtable )
H{
{ +native-id+ "primary key" }
{ +assigned-id+ "primary key" } { +assigned-id+ "primary key" }
{ +foreign-id+ "references" }
{ +autoincrement+ "autoincrement" } { +autoincrement+ "autoincrement" }
{ +unique+ "unique" } { +unique+ "unique" }
{ +default+ "default" } { +default+ "default" }
@ -257,13 +287,5 @@ M: postgresql-db tuple>params ( columns tuple -- obj )
{ +not-null+ "not null" } { +not-null+ "not null" }
} ; } ;
M: postgresql-db sql-modifiers* ( modifiers -- str ) M: postgresql-db compound-type ( str n -- newstr )
postgresql-db-modifiers swap [ postgresql-compound ;
dup array? [
first2
>r swap at r> number>string*
" " swap 3append
] [
swap at
] if
] with map [ ] subset ;

View File

@ -78,7 +78,8 @@ IN: db.sqlite.lib
{ TEXT [ sqlite-bind-text-by-name ] } { TEXT [ sqlite-bind-text-by-name ] }
{ VARCHAR [ sqlite-bind-text-by-name ] } { VARCHAR [ sqlite-bind-text-by-name ] }
{ DOUBLE [ sqlite-bind-double-by-name ] } { DOUBLE [ sqlite-bind-double-by-name ] }
{ SERIAL [ sqlite-bind-int-by-name ] } { TIMESTAMP [ sqlite-bind-double-by-name ] }
{ +native-id+ [ sqlite-bind-int-by-name ] }
! { NULL [ sqlite-bind-null-by-name ] } ! { NULL [ sqlite-bind-null-by-name ] }
[ no-sql-type ] [ no-sql-type ]
} case ; } case ;
@ -102,6 +103,8 @@ IN: db.sqlite.lib
{ BIG_INTEGER [ sqlite3_column_int64 ] } { BIG_INTEGER [ sqlite3_column_int64 ] }
{ TEXT [ sqlite3_column_text ] } { TEXT [ sqlite3_column_text ] }
{ DOUBLE [ sqlite3_column_double ] } { DOUBLE [ sqlite3_column_double ] }
{ TIMESTAMP [ sqlite3_column_double ] }
[ no-sql-type ]
} case ; } case ;
! TODO ! TODO

157
extra/db/sqlite/sqlite-tests.factor Normal file → Executable file
View File

@ -1,6 +1,6 @@
USING: io io.files io.launcher kernel namespaces USING: io io.files io.launcher kernel namespaces
prettyprint tools.test db.sqlite db sequences prettyprint tools.test db.sqlite db sequences
continuations db.types ; continuations db.types db.tuples unicode.case ;
IN: temporary IN: temporary
: test.db "extra/db/sqlite/test.db" resource-path ; : test.db "extra/db/sqlite/test.db" resource-path ;
@ -89,3 +89,158 @@ IN: temporary
"select * from person" sql-query length "select * from person" sql-query length
] with-sqlite ] with-sqlite
] unit-test ] unit-test
! TEST TUPLE DB
TUPLE: puppy id name age ;
: <puppy> ( name age -- puppy )
{ set-puppy-name set-puppy-age } puppy construct ;
puppy "PUPPY" {
{ "id" "ID" +native-id+ +not-null+ }
{ "name" "NAME" { VARCHAR 256 } }
{ "age" "AGE" INTEGER }
} define-persistent
TUPLE: kitty id name age ;
: <kitty> ( name age -- kitty )
{ set-kitty-name set-kitty-age } kitty construct ;
kitty "KITTY" {
{ "id" "ID" INTEGER +assigned-id+ }
{ "name" "NAME" TEXT }
{ "age" "AGE" INTEGER }
} define-persistent
TUPLE: basket id puppies kitties ;
basket "BASKET"
{
{ "id" "ID" +native-id+ +not-null+ }
{ "location" "LOCATION" TEXT }
{ "puppies" { +has-many+ puppy } }
{ "kitties" { +has-many+ kitty } }
} define-persistent
! Create table
[
"create table puppy(id integer primary key not null, name varchar, age integer);"
] [
T{ sqlite-db } db [
puppy dup db-columns swap db-table create-sql >lower
] with-variable
] unit-test
[
"create table kitty(id integer primary key, name text, age integer);"
] [
T{ sqlite-db } db [
kitty dup db-columns swap db-table create-sql >lower
] with-variable
] unit-test
[
"create table basket(id integer primary key not null, location text);"
] [
T{ sqlite-db } db [
basket dup db-columns swap db-table create-sql >lower
] with-variable
] unit-test
! Drop table
[
"drop table puppy;"
] [
T{ sqlite-db } db [
puppy db-table drop-sql >lower
] with-variable
] unit-test
[
"drop table kitty;"
] [
T{ sqlite-db } db [
kitty db-table drop-sql >lower
] with-variable
] unit-test
[
"drop table basket;"
] [
T{ sqlite-db } db [
basket db-table drop-sql >lower
] with-variable
] unit-test
! Insert
[
"insert into puppy(name, age) values(:name, :age);"
] [
T{ sqlite-db } db [
puppy dup db-columns swap db-table insert-sql* >lower
] with-variable
] unit-test
[
"insert into kitty(id, name, age) values(:id, :name, :age);"
] [
T{ sqlite-db } db [
kitty dup db-columns swap db-table insert-sql* >lower
] with-variable
] unit-test
! Update
[
"update puppy set name = :name, age = :age where id = :id"
] [
T{ sqlite-db } db [
puppy dup db-columns swap db-table update-sql* >lower
] with-variable
] unit-test
[
"update kitty set name = :name, age = :age where id = :id"
] [
T{ sqlite-db } db [
kitty dup db-columns swap db-table update-sql* >lower
] with-variable
] unit-test
! Delete
[
"delete from puppy where id = :id"
] [
T{ sqlite-db } db [
puppy dup db-columns swap db-table delete-sql* >lower
] with-variable
] unit-test
[
"delete from kitty where id = :id"
] [
T{ sqlite-db } db [
kitty dup db-columns swap db-table delete-sql* >lower
] with-variable
] unit-test
! Select
[
"select from puppy id, name, age where name = :name;"
{
T{
sql-spec
f
"id"
"ID"
+native-id+
{ +not-null+ }
+native-id+
}
T{ sql-spec f "name" "NAME" { VARCHAR 256 } { } f }
T{ sql-spec f "age" "AGE" INTEGER { } f }
}
] [
T{ sqlite-db } db [
T{ puppy f f "Mr. Clunkers" }
select-sql >r >lower r>
] with-variable
] unit-test

View File

@ -4,7 +4,7 @@ USING: alien arrays assocs classes compiler db
hashtables io.files kernel math math.parser namespaces hashtables io.files kernel math math.parser namespaces
prettyprint sequences strings tuples alien.c-types prettyprint sequences strings tuples alien.c-types
continuations db.sqlite.lib db.sqlite.ffi db.tuples continuations db.sqlite.lib db.sqlite.ffi db.tuples
words combinators.lib db.types ; words combinators.lib db.types combinators tools.walker ;
IN: db.sqlite IN: db.sqlite
TUPLE: sqlite-db path ; TUPLE: sqlite-db path ;
@ -23,7 +23,6 @@ M: sqlite-db dispose ( db -- ) dispose-db ;
>r <sqlite-db> r> with-db ; inline >r <sqlite-db> r> with-db ; inline
TUPLE: sqlite-statement ; TUPLE: sqlite-statement ;
C: <sqlite-statement> sqlite-statement
TUPLE: sqlite-result-set has-more? ; TUPLE: sqlite-result-set has-more? ;
@ -31,9 +30,15 @@ M: sqlite-db <simple-statement> ( str -- obj )
<prepared-statement> ; <prepared-statement> ;
M: sqlite-db <prepared-statement> ( str -- obj ) M: sqlite-db <prepared-statement> ( str -- obj )
db get db-handle over sqlite-prepare db get db-handle
{ set-statement-sql set-statement-handle } statement construct {
<sqlite-statement> [ set-delegate ] keep ; set-statement-sql
set-statement-in-params
set-statement-out-params
set-statement-handle
} statement construct
dup statement-handle over statement-sql sqlite-prepare
sqlite-statement construct-delegate ;
M: sqlite-statement dispose ( statement -- ) M: sqlite-statement dispose ( statement -- )
statement-handle sqlite-finalize ; statement-handle sqlite-finalize ;
@ -41,10 +46,11 @@ M: sqlite-statement dispose ( statement -- )
M: sqlite-result-set dispose ( result-set -- ) M: sqlite-result-set dispose ( result-set -- )
f swap set-result-set-handle ; f swap set-result-set-handle ;
: sqlite-bind ( triples handle -- ) : sqlite-bind ( specs handle -- )
swap [ first3 sqlite-bind-type ] with each ; break
swap [ sqlite-bind-type ] with each ;
M: sqlite-statement bind-statement* ( triples statement -- ) M: sqlite-statement bind-statement* ( obj statement -- )
statement-handle sqlite-bind ; statement-handle sqlite-bind ;
M: sqlite-statement reset-statement ( statement -- ) M: sqlite-statement reset-statement ( statement -- )
@ -54,8 +60,8 @@ M: sqlite-statement reset-statement ( statement -- )
db get db-handle sqlite3_last_insert_rowid db get db-handle sqlite3_last_insert_rowid
dup zero? [ "last-id failed" throw ] when ; dup zero? [ "last-id failed" throw ] when ;
M: sqlite-statement insert-statement ( statement -- id ) M: sqlite-statement insert-tuple* ( tuple statement -- )
execute-statement last-insert-id ; execute-statement last-insert-id swap set-primary-key ;
M: sqlite-result-set #columns ( result-set -- n ) M: sqlite-result-set #columns ( result-set -- n )
result-set-handle sqlite-#columns ; result-set-handle sqlite-#columns ;
@ -74,6 +80,7 @@ M: sqlite-result-set more-rows? ( result-set -- ? )
sqlite-result-set-has-more? ; sqlite-result-set-has-more? ;
M: sqlite-statement query-results ( query -- result-set ) M: sqlite-statement query-results ( query -- result-set )
break
dup statement-handle sqlite-result-set <result-set> dup statement-handle sqlite-result-set <result-set>
dup advance-row ; dup advance-row ;
@ -86,78 +93,85 @@ M: sqlite-db commit-transaction ( -- )
M: sqlite-db rollback-transaction ( -- ) M: sqlite-db rollback-transaction ( -- )
"ROLLBACK" sql-command ; "ROLLBACK" sql-command ;
M: sqlite-db create-sql ( columns table -- sql ) : sqlite-make ( class quot -- )
[ >r sql-props r>
"create table " % % { "" { } { } } nmake <simple-statement> ;
" (" % [ ", " % ] [
dup second % " " %
dup third >sql-type % " " %
sql-modifiers " " join %
] interleave ")" %
] "" make ;
M: sqlite-db drop-sql ( columns table -- sql ) M: sqlite-db create-sql-statement ( class -- statement )
[ [
"drop table " % % "create table " 0% 0%
drop "(" 0% [ ", " 0% ] [
] "" make ; dup sql-spec-column-name 0%
" " 0%
dup sql-spec-type t lookup-type 0%
modifiers 0%
] interleave ");" 0%
] sqlite-make ;
M: sqlite-db insert-sql* ( columns table -- sql ) M: sqlite-db drop-sql-statement ( class -- statement )
[ [
"insert into " % "drop table " 0% 0% ";" 0% drop
% ] sqlite-make ;
"(" %
dup [ ", " % ] [ second % ] interleave
") " %
" values (" %
[ ", " % ] [ ":" % second % ] interleave
")" %
] "" make ;
: where-primary-key% ( columns -- ) M: sqlite-db <insert-native-statement> ( tuple -- statement )
" where " %
[ primary-key? ] find nip second dup % " = :" % % ;
M: sqlite-db update-sql* ( columns table -- sql )
[ [
"update " % "insert into " 0% 0%
% "(" 0%
" set " % maybe-remove-id
dup [ ", " 0% ] [ sql-spec-column-name 0% ] interleave
") values(" 0%
[ ", " 0% ] [ bind% ] interleave
");" 0%
] sqlite-make ;
M: sqlite-db <insert-assigned-statement> ( tuple -- statement )
<insert-native-statement> ;
: where-primary-key% ( specs -- )
" where " 0%
find-primary-key sql-spec-column-name dup 0% " = " 0% bind% ;
M: sqlite-db <update-tuple-statement> ( class -- statement )
[
"update " 0%
0%
" set " 0%
dup remove-id dup remove-id
[ ", " % ] [ second dup % " = :" % % ] interleave [ ", " 0% ] [ sql-spec-column-name dup 0% " = " 0% bind% ] interleave
where-primary-key% where-primary-key%
] "" make ; ] sqlite-make ;
M: sqlite-db delete-sql* ( columns table -- sql ) M: sqlite-db <delete-tuple-statement> ( specs table -- sql )
[ [
"delete from " % "delete from " 0% 0%
% " where " 0%
" where " % find-primary-key
first second dup % " = :" % % sql-spec-column-name dup 0% " = " 0% bind%
] "" make ; ] sqlite-make ;
: select-interval ( interval name -- ) ! : select-interval ( interval name -- ) ;
; ! : select-sequence ( seq name -- ) ;
: select-sequence ( seq name -- ) M: sqlite-db bind% ( spec -- )
; dup 1, sql-spec-column-name ":" swap append 0% ;
! dup 1, sql-spec-column-name
! dup 0% " = " 0% ":" swap append 0% ;
M: sqlite-db select-sql ( columns table -- sql ) M: sqlite-db <select-by-slots-statement> ( tuple class -- statement )
[ [
"select ROWID, " % "select " 0%
over [ ", " % ] [ second % ] interleave over [ ", " 0% ]
" from " % % [ dup sql-spec-column-name 0% 2, ] interleave
" where " %
] "" make ;
M: sqlite-db tuple>params ( columns tuple -- obj ) " from " 0% 0%
[ [ sql-spec-slot-name swap get-slot-named ] with subset
>r [ second ":" swap append ] keep r> " where " 0%
dupd >r first r> get-slot-named swap [ ", " 0% ]
third 3array [ dup sql-spec-column-name 0% " = " 0% bind% ] interleave
] curry map ; ";" 0%
] sqlite-make ;
: sqlite-db-modifiers ( -- hashtable ) M: sqlite-db modifier-table ( -- hashtable )
H{ H{
{ +native-id+ "primary key" } { +native-id+ "primary key" }
{ +assigned-id+ "primary key" } { +assigned-id+ "primary key" }
@ -168,32 +182,27 @@ M: sqlite-db tuple>params ( columns tuple -- obj )
{ +not-null+ "not null" } { +not-null+ "not null" }
} ; } ;
M: sqlite-db sql-modifiers* ( modifiers -- str ) M: sqlite-db compound-modifier ( str obj -- newstr )
sqlite-db-modifiers swap [ compound-type ;
dup array? [
first2
>r swap at r> number>string*
" " swap 3append
] [
swap at
] if
] with map [ ] subset ;
: sqlite-type-hash ( -- assoc ) M: sqlite-db compound-type ( str seq -- newstr )
over {
{ "default" [ first number>string join-space ] }
[ 2drop ] ! "no sqlite compound data type" 3array throw ]
} case ;
M: sqlite-db type-table ( -- assoc )
H{ H{
{ +native-id+ "integer primary key" }
{ INTEGER "integer" } { INTEGER "integer" }
{ SERIAL "integer" }
{ TEXT "text" } { TEXT "text" }
{ VARCHAR "text" } { VARCHAR "text" }
{ TIMESTAMP "timestamp" }
{ DOUBLE "real" } { DOUBLE "real" }
} ; } ;
M: sqlite-db >sql-type ( obj -- str ) M: sqlite-db create-type-table
dup pair? [ type-table ;
first >sql-type
] [
sqlite-type-hash at* [ T{ no-sql-type } throw ] unless
] if ;
! HOOK: get-column-value ( n result-set type -- ) ! HOOK: get-column-value ( n result-set type -- )
! M: sqlite get-column-value { { "TEXT" get-text-column } { ! M: sqlite get-column-value { { "TEXT" get-text-column } {

View File

@ -1,70 +1,118 @@
! Copyright (C) 2008 Doug Coleman. ! Copyright (C) 2008 Doug Coleman.
! See http://factorcode.org/license.txt for BSD license. ! See http://factorcode.org/license.txt for BSD license.
USING: io.files kernel tools.test db db.sqlite db.tuples USING: io.files kernel tools.test db db.tuples
db.types continuations namespaces db.postgresql math ; db.types continuations namespaces db.postgresql math
! tools.time ; prettyprint tools.walker db.sqlite ;
IN: temporary IN: temporary
TUPLE: person the-id the-name the-number real ; TUPLE: person the-id the-name the-number the-real ;
: <person> ( name age real -- person ) : <person> ( name age real -- person )
{ {
set-person-the-name set-person-the-name
set-person-the-number set-person-the-number
set-person-real set-person-the-real
} person construct ; } person construct ;
: <assigned-person> ( id name number real -- obj ) : <assigned-person> ( id name number the-real -- obj )
<person> [ set-person-the-id ] keep ; <person> [ set-person-the-id ] keep ;
SYMBOL: the-person SYMBOL: the-person1
SYMBOL: the-person2
: test-tuples ( -- ) : test-tuples ( -- )
[ person drop-table ] [ drop ] recover [ person drop-table ] [ drop ] recover
[ ] [ person create-table ] unit-test [ ] [ person create-table ] unit-test
[ ] [ the-person get insert-tuple ] unit-test [ ] [ the-person1 get insert-tuple ] unit-test
[ 1 ] [ the-person get person-the-id ] unit-test [ 1 ] [ the-person1 get person-the-id ] unit-test
200 the-person get set-person-the-number 200 the-person1 get set-person-the-number
[ ] [ the-person get update-tuple ] unit-test [ ] [ the-person1 get update-tuple ] unit-test
[ ] [ the-person get delete-tuple ] unit-test [ T{ person f 1 "billy" 200 3.14 } ]
; ! 1 [ ] [ person drop-table ] unit-test ; [ T{ person f 1 } select-tuple ] unit-test
[ ] [ the-person2 get insert-tuple ] unit-test
[
{
T{ person f 1 "billy" 200 3.14 }
T{ person f 2 "johnny" 10 3.14 }
}
] [ T{ person f f f f 3.14 } select-tuples ] unit-test
[ ] [ the-person1 get delete-tuple ] unit-test
[ f ] [ T{ person f 1 } select-tuple ] unit-test
[ ] [ person drop-table ] unit-test ;
: test-sqlite ( -- ) : test-sqlite ( -- )
"tuples-test.db" resource-path <sqlite-db> [ "tuples-test.db" resource-path sqlite-db [
test-tuples test-tuples
] with-db ; ] with-db ;
: test-postgresql ( -- ) : test-postgresql ( -- )
"localhost" "postgres" "" "factor-test" <postgresql-db> [ { "localhost" "postgres" "" "factor-test" } postgresql-db [
test-tuples test-tuples
] with-db ; ] with-db ;
person "PERSON" person "PERSON"
{ {
{ "the-id" "ID" SERIAL +native-id+ } { "the-id" "ID" +native-id+ }
{ "the-name" "NAME" { VARCHAR 256 } +not-null+ } { "the-name" "NAME" { VARCHAR 256 } +not-null+ }
{ "the-number" "AGE" INTEGER { +default+ 0 } } { "the-number" "AGE" INTEGER { +default+ 0 } }
{ "real" "REAL" DOUBLE { +default+ 0.3 } } { "the-real" "REAL" DOUBLE { +default+ 0.3 } }
} define-persistent } define-persistent
"billy" 10 3.14 <person> the-person set "billy" 10 3.14 <person> the-person1 set
"johnny" 10 3.14 <person> the-person2 set
! test-sqlite ! test-sqlite
test-postgresql test-postgresql
! person "PERSON" person "PERSON"
! { {
! { "the-id" "ID" INTEGER +assigned-id+ } { "the-id" "ID" INTEGER +assigned-id+ }
! { "the-name" "NAME" { VARCHAR 256 } +not-null+ } { "the-name" "NAME" { VARCHAR 256 } +not-null+ }
! { "the-number" "AGE" INTEGER { +default+ 0 } } { "the-number" "AGE" INTEGER { +default+ 0 } }
! { "real" "REAL" DOUBLE { +default+ 0.3 } } { "the-real" "REAL" DOUBLE { +default+ 0.3 } }
! } define-persistent } define-persistent
! 1 "billy" 20 6.28 <assigned-person> the-person set 1 "billy" 10 3.14 <assigned-person> the-person1 set
2 "johnny" 10 3.14 <assigned-person> the-person2 set
! test-sqlite ! test-sqlite
! test-postgresql test-postgresql
TUPLE: paste n summary author channel mode contents timestamp annotations ;
TUPLE: annotation n paste-id summary author mode contents ;
paste "PASTE"
{
{ "n" "ID" +native-id+ }
{ "summary" "SUMMARY" TEXT }
{ "author" "AUTHOR" TEXT }
{ "channel" "CHANNEL" TEXT }
{ "mode" "MODE" TEXT }
{ "contents" "CONTENTS" TEXT }
{ "date" "DATE" TIMESTAMP }
{ "annotations" { +has-many+ annotation } }
} define-persistent
annotation "ANNOTATION"
{
{ "n" "ID" +native-id+ }
{ "paste-id" "PASTE_ID" INTEGER { +foreign-id+ paste "n" } }
{ "summary" "SUMMARY" TEXT }
{ "author" "AUTHOR" TEXT }
{ "mode" "MODE" TEXT }
{ "contents" "CONTENTS" TEXT }
} define-persistent
{ "localhost" "postgres" "" "factor-test" } postgresql-db [
[ paste drop-table ] [ drop ] recover
[ annotation drop-table ] [ drop ] recover
[ paste drop-table ] [ drop ] recover
[ annotation drop-table ] [ drop ] recover
[ ] [ paste create-table ] unit-test
[ ] [ annotation create-table ] unit-test
] with-db

View File

@ -1,115 +1,100 @@
! Copyright (C) 2008 Doug Coleman. ! Copyright (C) 2008 Doug Coleman.
! See http://factorcode.org/license.txt for BSD license. ! See http://factorcode.org/license.txt for BSD license.
USING: arrays assocs classes db kernel namespaces USING: arrays assocs classes db kernel namespaces
tuples words sequences slots slots.private math tuples words sequences slots math
math.parser io prettyprint db.types continuations ; math.parser io prettyprint db.types continuations
mirrors sequences.lib tools.walker combinators.lib ;
IN: db.tuples IN: db.tuples
: db-columns ( class -- obj ) "db-columns" word-prop ; : define-persistent ( class table columns -- )
>r dupd "db-table" set-word-prop dup r>
[ relation? ] partition swapd
dupd [ spec>tuple ] with map
"db-columns" set-word-prop
"db-relations" set-word-prop ;
: db-table ( class -- obj ) "db-table" word-prop ; : db-table ( class -- obj ) "db-table" word-prop ;
: db-columns ( class -- obj ) "db-columns" word-prop ;
: db-relations ( class -- obj ) "db-relations" word-prop ;
TUPLE: no-slot-named ; : set-primary-key ( key tuple -- )
: no-slot-named ( -- * ) T{ no-slot-named } throw ; [
class db-columns find-primary-key sql-spec-slot-name
] keep set-slot-named ;
: slot-spec-named ( str class -- slot-spec ) ! returns a sequence of prepared-statements
"slots" word-prop [ slot-spec-name = ] with find nip HOOK: create-sql-statement db ( class -- obj )
[ no-slot-named ] unless* ; HOOK: drop-sql-statement db ( class -- obj )
: offset-of-slot ( str obj -- n ) HOOK: <insert-native-statement> db ( tuple -- obj )
class slot-spec-named slot-spec-offset ; HOOK: <insert-assigned-statement> db ( tuple -- obj )
: get-slot-named ( str obj -- value ) HOOK: <update-tuple-statement> db ( tuple -- obj )
tuck offset-of-slot [ no-slot-named ] unless* slot ; HOOK: <update-tuples-statement> db ( tuple -- obj )
: set-slot-named ( value str obj -- ) HOOK: <delete-tuple-statement> db ( tuple -- obj )
tuck offset-of-slot [ no-slot-named ] unless* set-slot ; HOOK: <delete-tuples-statement> db ( tuple -- obj )
: primary-key-spec ( class -- spec ) HOOK: <select-by-slots-statement> db ( tuple -- tuple )
db-columns [ primary-key? ] find nip ;
: primary-key ( tuple -- obj )
dup class primary-key-spec get-slot-named ;
: set-primary-key ( obj tuple -- )
[ class primary-key-spec first ] keep
set-slot-named ;
: cache-statement ( columns class assoc quot -- statement )
[ db-table dupd ] swap
[ <prepared-statement> ] 3compose cache nip ; inline
HOOK: create-sql db ( columns table -- seq )
HOOK: drop-sql db ( columns table -- seq )
HOOK: insert-sql* db ( columns table -- slot-names sql )
HOOK: update-sql* db ( columns table -- slot-names sql )
HOOK: delete-sql* db ( columns table -- slot-names sql )
HOOK: select-sql db ( tuple -- statement )
HOOK: row-column-typed db ( result-set n type -- sql ) HOOK: row-column-typed db ( result-set n type -- sql )
HOOK: sql-type>factor-type db ( obj type -- obj ) HOOK: insert-tuple* db ( tuple statement -- )
HOOK: tuple>params db ( columns tuple -- obj )
: resulting-tuple ( row out-params -- tuple )
dup first sql-spec-class construct-empty [
[
>r [ sql-spec-type sql-type>factor-type ] keep
sql-spec-slot-name r> set-slot-named
] curry 2each
] keep ;
HOOK: make-slot-names* db ( quot -- seq ) : query-tuples ( statement -- seq )
HOOK: column-slot-name% db ( spec -- ) [ statement-out-params ] keep query-results [
HOOK: column-bind-name% db ( spec -- ) [ sql-row swap resulting-tuple ] with query-map
] with-disposal ;
: make-slots-names ( quot -- seq str ) : query-modify-tuple ( tuple statement -- )
[ make-slot-names* ] "" make ; inline [ query-results [ sql-row ] with-disposal ] keep
: slot-name% ( seq -- ) first % ; statement-out-params rot [
: column-name% ( seq -- ) second % ; >r [ sql-spec-type sql-type>factor-type ] keep
: column-type% ( seq -- ) third % ; sql-spec-slot-name r> set-slot-named
] curry 2each ;
: insert-sql ( columns class -- statement ) : sql-props ( class -- columns table )
db get db-insert-statements [ insert-sql* ] cache-statement ; dup db-columns swap db-table ;
: update-sql ( columns class -- statement ) : create-table ( class -- ) create-sql-statement execute-statement ;
db get db-update-statements [ update-sql* ] cache-statement ; : drop-table ( class -- ) drop-sql-statement execute-statement ;
: delete-sql ( columns class -- statement ) : insert-native ( tuple -- )
db get db-delete-statements [ delete-sql* ] cache-statement ; dup class <insert-native-statement>
[ bind-tuple ] 2keep insert-tuple* ;
: insert-assigned ( tuple -- )
: tuple-statement ( columns tuple quot -- statement ) dup class <insert-assigned-statement>
>r [ tuple>params ] 2keep class r> call [ bind-tuple ] keep execute-statement ;
2dup . .
[ bind-statement ] keep ;
: make-tuple-statement ( tuple columns-quot statement-quot -- statement )
>r [ class db-columns ] swap compose keep
r> tuple-statement ;
: do-tuple-statement ( tuple columns-quot statement-quot -- )
make-tuple-statement execute-statement ;
: create-table ( class -- )
dup db-columns swap db-table create-sql sql-command ;
: drop-table ( class -- )
dup db-columns swap db-table drop-sql sql-command ;
: insert-tuple ( tuple -- ) : insert-tuple ( tuple -- )
[ dup class db-columns find-primary-key assigned-id? [
[ maybe-remove-id ] [ insert-sql ] insert-assigned
make-tuple-statement insert-statement ] [
] keep set-primary-key ; insert-native
] if ;
: update-tuple ( tuple -- ) : update-tuple ( tuple -- )
[ ] [ update-sql ] do-tuple-statement ; dup class <update-tuple-statement>
[ bind-tuple ] keep execute-statement ;
: update-tuples ( seq -- )
<update-tuples-statement> execute-statement ;
: delete-tuple ( tuple -- ) : delete-tuple ( tuple -- )
[ [ primary-key? ] subset ] [ delete-sql ] do-tuple-statement ; dup class <delete-tuple-statement>
[ bind-tuple ] keep execute-statement ;
: select-tuple ( tuple -- ) : setup-select ( tuple -- statement )
[ select-sql ] keep do-query ; dup dup class <select-by-slots-statement>
[ bind-tuple ] keep ;
: persist ( tuple -- ) : select-tuples ( tuple -- tuple ) setup-select query-tuples ;
dup primary-key [ update-tuple ] [ insert-tuple ] if ; : select-tuple ( tuple -- tuple/f ) select-tuples ?first ;
: define-persistent ( class table columns -- )
>r dupd "db-table" set-word-prop r>
"db-columns" set-word-prop ;
: define-relation ( spec -- )
drop ;

View File

@ -1,21 +1,50 @@
! Copyright (C) 2008 Doug Coleman. ! Copyright (C) 2008 Doug Coleman.
! See http://factorcode.org/license.txt for BSD license. ! See http://factorcode.org/license.txt for BSD license.
USING: arrays assocs db kernel math math.parser USING: arrays assocs db kernel math math.parser
sequences continuations ; sequences continuations sequences.deep sequences.lib
words namespaces tools.walker slots slots.private classes
mirrors tuples combinators ;
IN: db.types IN: db.types
HOOK: modifier-table db ( -- hash )
HOOK: compound-modifier db ( str seq -- hash )
HOOK: type-table db ( -- hash )
HOOK: create-type-table db ( -- hash )
HOOK: compound-type db ( str n -- hash )
TUPLE: sql-spec class slot-name column-name type modifiers primary-key ;
! ID is the Primary key ! ID is the Primary key
! +native-id+ can be a columns type or a modifier
SYMBOL: +native-id+ SYMBOL: +native-id+
! +assigned-id+ can only be a modifier
SYMBOL: +assigned-id+ SYMBOL: +assigned-id+
: (primary-key?) ( obj -- ? )
{ +native-id+ +assigned-id+ } member? ;
: primary-key? ( spec -- ? ) : primary-key? ( spec -- ? )
[ { +native-id+ +assigned-id+ } member? ] contains? ; sql-spec-primary-key (primary-key?) ;
: contains-id? ( columns id -- ? ) : normalize-spec ( spec -- )
swap [ member? ] with contains? ; dup sql-spec-type dup (primary-key?) [
swap set-sql-spec-primary-key
] [
drop dup sql-spec-modifiers [
(primary-key?)
] deep-find
[ swap set-sql-spec-primary-key ] [ drop ] if*
] if ;
: assigned-id? ( columns -- ? ) +assigned-id+ contains-id? ; : find-primary-key ( specs -- obj )
: native-id? ( columns -- ? ) +native-id+ contains-id? ; [ sql-spec-primary-key ] find nip ;
: native-id? ( spec -- ? )
sql-spec-primary-key +native-id+ = ;
: assigned-id? ( spec -- ? )
sql-spec-primary-key +assigned-id+ = ;
SYMBOL: +foreign-id+
! Same concept, SQLite has autoincrement, PostgreSQL has serial ! Same concept, SQLite has autoincrement, PostgreSQL has serial
SYMBOL: +autoincrement+ SYMBOL: +autoincrement+
@ -28,40 +57,168 @@ SYMBOL: +not-null+
SYMBOL: +has-many+ SYMBOL: +has-many+
SYMBOL: SERIAL : relation? ( spec -- ? ) [ +has-many+ = ] deep-find ;
SYMBOL: INTEGER
SYMBOL: DOUBLE
SYMBOL: BOOLEAN
SYMBOL: INTEGER
SYMBOL: BIG_INTEGER
SYMBOL: DOUBLE
SYMBOL: REAL
SYMBOL: BOOLEAN
SYMBOL: TEXT SYMBOL: TEXT
SYMBOL: VARCHAR SYMBOL: VARCHAR
SYMBOL: TIMESTAMP SYMBOL: TIMESTAMP
SYMBOL: DATE SYMBOL: DATE
SYMBOL: BIG_INTEGER : spec>tuple ( class spec -- tuple )
[ ?first3 ] keep 3 ?tail*
{
set-sql-spec-class
set-sql-spec-slot-name
set-sql-spec-column-name
set-sql-spec-type
set-sql-spec-modifiers
} sql-spec construct
dup normalize-spec ;
: sql-type-hash ( -- assoc )
H{
{ INTEGER "integer" }
{ TEXT "text" }
{ VARCHAR "varchar" }
{ DOUBLE "real" }
{ TIMESTAMP "timestamp" }
} ;
TUPLE: no-sql-type ; TUPLE: no-sql-type ;
: no-sql-type ( -- * ) T{ no-sql-type } throw ; : no-sql-type ( -- * ) T{ no-sql-type } throw ;
HOOK: sql-modifiers* db ( modifiers -- str ) TUPLE: no-sql-modifier ;
HOOK: >sql-type db ( obj -- str ) : no-sql-modifier ( -- * ) T{ no-sql-modifier } throw ;
! HOOK: >factor-type db ( obj -- obj )
: number>string* ( n/str -- str ) : number>string* ( n/str -- str )
dup number? [ number>string ] when ; dup number? [ number>string ] when ;
: maybe-remove-id ( columns -- obj ) : maybe-remove-id ( specs -- obj )
[ +native-id+ swap member? not ] subset ; [ native-id? not ] subset ;
: remove-id ( columns -- obj ) : remove-relations ( specs -- newcolumns )
[ primary-key? not ] subset ; [ relation? not ] subset ;
: sql-modifiers ( spec -- seq ) : remove-id ( specs -- obj )
3 tail sql-modifiers* ; [ sql-spec-primary-key not ] subset ;
! SQLite Types: http://www.sqlite.org/datatype3.html ! SQLite Types: http://www.sqlite.org/datatype3.html
! NULL INTEGER REAL TEXT BLOB ! NULL INTEGER REAL TEXT BLOB
! PostgreSQL Types: ! PostgreSQL Types:
! http://developer.postgresql.org/pgdocs/postgres/datatype.html ! http://developer.postgresql.org/pgdocs/postgres/datatype.html
: lookup-modifier ( obj -- str )
dup array? [
unclip lookup-modifier swap compound-modifier
] [
modifier-table at*
[ "unknown modifier" throw ] unless
] if ;
: lookup-type* ( obj -- str )
dup array? [
first lookup-type*
] [
type-table at*
[ no-sql-type ] unless
] if ;
: lookup-create-type ( obj -- str )
dup array? [
unclip lookup-create-type swap compound-type
] [
dup create-type-table at*
[ nip ] [ drop lookup-type* ] if
] if ;
: lookup-type ( obj create? -- str )
[ lookup-create-type ] [ lookup-type* ] if ;
: single-quote ( str -- newstr )
"'" swap "'" 3append ;
: double-quote ( str -- newstr )
"\"" swap "\"" 3append ;
: paren ( str -- newstr )
"(" swap ")" 3append ;
: join-space ( str1 str2 -- newstr )
" " swap 3append ;
: modifiers ( spec -- str )
sql-spec-modifiers
[ lookup-modifier ] map " " join
dup empty? [ " " swap append ] unless ;
SYMBOL: building-seq
: get-building-seq ( n -- seq )
building-seq get nth ;
: n, get-building-seq push ;
: n% get-building-seq push-all ;
: n# >r number>string r> n% ;
: 0, 0 n, ;
: 0% 0 n% ;
: 0# 0 n# ;
: 1, 1 n, ;
: 1% 1 n% ;
: 1# 1 n# ;
: 2, 2 n, ;
: 2% 2 n% ;
: 2# 2 n# ;
: nmake ( quot exemplars -- seqs )
dup length dup zero? [ 1+ ] when
[
[
[ drop 1024 swap new-resizable ] 2map
[ building-seq set call ] keep
] 2keep >r [ like ] 2map r> firstn
] with-scope ;
HOOK: bind% db ( spec -- )
TUPLE: no-slot-named ;
: no-slot-named ( -- * ) T{ no-slot-named } throw ;
: slot-spec-named ( str class -- slot-spec )
"slots" word-prop [ slot-spec-name = ] with find nip
[ no-slot-named ] unless* ;
: offset-of-slot ( str obj -- n )
class slot-spec-named slot-spec-offset ;
: get-slot-named ( str obj -- value )
tuck offset-of-slot [ no-slot-named ] unless* slot ;
: set-slot-named ( value str obj -- )
tuck offset-of-slot [ no-slot-named ] unless* set-slot ;
: tuple>filled-slots ( tuple -- alist )
dup <mirror> mirror-slots [ slot-spec-name ] map
swap tuple-slots 2array flip [ nip ] assoc-subset ;
: tuple>params ( specs tuple -- obj )
[
>r dup sql-spec-type swap sql-spec-slot-name r>
get-slot-named swap
] curry { } map>assoc ;
: sql-type>factor-type ( obj type -- obj )
dup array? [ first ] when
{
{ +native-id+ [ string>number ] }
{ INTEGER [ string>number ] }
{ DOUBLE [ string>number ] }
{ REAL [ string>number ] }
{ TEXT [ ] }
{ VARCHAR [ ] }
[ "no conversion from sql type to factor type" throw ]
} case ;

View File

@ -61,5 +61,5 @@ SYMBOL: realms
#! Check if the user is authenticated in the given realm #! Check if the user is authenticated in the given realm
#! to run the specified quotation. If not, use Basic #! to run the specified quotation. If not, use Basic
#! Authentication to ask for authorization details. #! Authentication to ask for authorization details.
over "Authorization" header-param authorization-ok? over "authorization" header-param authorization-ok?
[ nip call ] [ drop authentication-error ] if ; [ nip call ] [ drop authentication-error ] if ;

View File

@ -77,7 +77,7 @@ SYMBOL: max-post-request
1024 256 * max-post-request set-global 1024 256 * max-post-request set-global
: content-length ( header -- n ) : content-length ( header -- n )
"Content-Length" swap at string>number dup [ "content-length" peek-at string>number dup [
dup max-post-request get > [ dup max-post-request get > [
"Content-Length > max-post-request" throw "Content-Length > max-post-request" throw
] when ] when
@ -136,7 +136,7 @@ LOG: log-headers DEBUG
: host ( -- string ) : host ( -- string )
#! The host the current responder was called from. #! The host the current responder was called from.
"Host" header-param ":" split1 drop ; "host" header-param ":" split1 drop ;
: add-responder ( responder -- ) : add-responder ( responder -- )
#! Add a responder object to the list. #! Add a responder object to the list.

View File

@ -146,8 +146,8 @@ M: windows-io kill-process* ( handle -- )
: wait-loop ( -- ) : wait-loop ( -- )
processes get dup assoc-empty? processes get dup assoc-empty?
[ drop f nap drop ] [ drop f sleep-until ]
[ wait-for-processes [ 100 nap drop ] when ] if ; [ wait-for-processes [ 100 sleep ] when ] if ;
SYMBOL: wait-thread SYMBOL: wait-thread

View File

@ -14,7 +14,7 @@ USING: kernel alien ogg ogg.vorbis ogg.theora io byte-arrays
sequences libc shuffle alien.c-types system openal math sequences libc shuffle alien.c-types system openal math
namespaces threads shuffle opengl arrays ui.gadgets.worlds namespaces threads shuffle opengl arrays ui.gadgets.worlds
combinators math.parser ui.gadgets ui.render opengl.gl ui combinators math.parser ui.gadgets ui.render opengl.gl ui
continuations io.files hints combinators.lib sequences.lib ; continuations io.files hints combinators.lib sequences.lib debugger ;
IN: ogg.player IN: ogg.player
@ -149,7 +149,7 @@ HINTS: yuv>rgb byte-array byte-array ;
dup player-gadget [ dup player-gadget [
dup { player-td player-yuv } get-slots theora_decode_YUVout drop dup { player-td player-yuv } get-slots theora_decode_YUVout drop
dup player-rgb over player-yuv yuv>rgb dup player-rgb over player-yuv yuv>rgb
dup player-gadget find-world draw-world dup player-gadget relayout yield
] when ; ] when ;
: num-audio-buffers-processed ( player -- player n ) : num-audio-buffers-processed ( player -- player n )
@ -177,7 +177,7 @@ HINTS: yuv>rgb byte-array byte-array ;
: append-audio ( player -- player bool ) : append-audio ( player -- player bool )
num-audio-buffers-processed { num-audio-buffers-processed {
{ [ over player-buffers length 1 = over zero? and ] [ drop append-new-audio-buffer t ] } { [ over player-buffers length 1 = over zero? and ] [ drop append-new-audio-buffer t ] }
{ [ over player-buffers length 2 = over zero? and ] [ 0 sleep drop f ] } { [ over player-buffers length 2 = over zero? and ] [ yield drop f ] }
{ [ t ] [ fill-processed-audio-buffer t ] } { [ t ] [ fill-processed-audio-buffer t ] }
} cond ; } cond ;
@ -602,8 +602,7 @@ M: theora-gadget draw-gadget* ( gadget -- )
parse-remaining-headers parse-remaining-headers
initialize-decoder initialize-decoder
dup player-gadget [ initialize-gui ] when* dup player-gadget [ initialize-gui ] when*
[ decode ] [ drop ] recover [ decode ] try
! decode
wait-for-sound wait-for-sound
cleanup cleanup
drop ; drop ;

View File

@ -1,5 +1,6 @@
USING: compiler continuations io kernel math namespaces USING: compiler continuations io kernel math namespaces
prettyprint quotations random sequences vectors ; prettyprint quotations random sequences vectors
compiler.units ;
USING: random-tester.databank random-tester.safe-words ; USING: random-tester.databank random-tester.safe-words ;
IN: random-tester IN: random-tester

View File

@ -20,8 +20,6 @@ IN: temporary
[ 10 ] [ { 1 2 3 4 } [ + ] reduce* ] unit-test [ 10 ] [ { 1 2 3 4 } [ + ] reduce* ] unit-test
[ 24 ] [ { 1 2 3 4 } [ * ] reduce* ] unit-test [ 24 ] [ { 1 2 3 4 } [ * ] reduce* ] unit-test
[ 1 2 3 4 ] [ { 1 2 3 4 } 4 nfirst ] unit-test
[ -4 ] [ 1 -4 [ abs ] higher ] unit-test [ -4 ] [ 1 -4 [ abs ] higher ] unit-test
[ 1 ] [ 1 -4 [ abs ] lower ] unit-test [ 1 ] [ 1 -4 [ abs ] lower ] unit-test
@ -80,4 +78,4 @@ IN: temporary
{ 13 } [ 1 2 { 3 4 } [ + + ] 2 each-withn + ] unit-test { 13 } [ 1 2 { 3 4 } [ + + ] 2 each-withn + ] unit-test
[ { 910 911 912 } ] [ 10 900 3 [ + + ] map-with2 ] unit-test [ { 910 911 912 } ] [ 10 900 3 [ + + ] map-with2 ] unit-test
[ 1 2 3 4 ] [ { 1 2 3 4 } 4 nfirst ] unit-test [ 1 2 3 4 ] [ { 1 2 3 4 } 4 firstn ] unit-test

View File

@ -18,8 +18,9 @@ IN: sequences.lib
: map-with2 ( obj obj list quot -- newseq ) 2 map-withn ; inline : map-with2 ( obj obj list quot -- newseq ) 2 map-withn ; inline
MACRO: nfirst ( n -- ) MACRO: firstn ( n -- )
[ [ swap nth ] curry [ keep ] curry ] map concat [ drop ] compose ; [ [ swap nth ] curry
[ keep ] curry ] map concat [ drop ] compose ;
: prepare-index ( seq quot -- seq n quot ) : prepare-index ( seq quot -- seq n quot )
>r dup length r> ; inline >r dup length r> ; inline
@ -182,6 +183,14 @@ PRIVATE>
: ?first3 ( seq -- 1st/f 2nd/f 3rd/f ) dup ?first2 rot ?third ; inline : ?first3 ( seq -- 1st/f 2nd/f 3rd/f ) dup ?first2 rot ?third ; inline
: ?first4 ( seq -- 1st/f 2nd/f 3rd/f 4th/f ) dup ?first3 roll ?fourth ; inline : ?first4 ( seq -- 1st/f 2nd/f 3rd/f 4th/f ) dup ?first3 roll ?fourth ; inline
USE: continuations
: ?subseq ( from to seq -- subseq )
>r >r 0 max r> r>
[ length tuck min >r min r> ] keep subseq ;
: ?head* ( seq n -- seq/f ) (head) ?subseq ;
: ?tail* ( seq n -- seq/f ) (tail) ?subseq ;
: accumulator ( quot -- quot vec ) : accumulator ( quot -- quot vec )
V{ } clone [ [ push ] curry compose ] keep ; V{ } clone [ [ push ] curry compose ] keep ;

View File

@ -8,7 +8,10 @@ heaps.private system math math.parser ;
: thread. ( thread -- ) : thread. ( thread -- )
dup thread-id pprint-cell dup thread-id pprint-cell
dup thread-name over [ write-object ] with-cell dup thread-name over [ write-object ] with-cell
dup thread-state "running" or [ write ] with-cell dup thread-state [
[ dup self eq? "running" "yield" ? ] unless*
write
] with-cell
[ [
thread-sleep-entry [ thread-sleep-entry [
entry-key millis [-] number>string write entry-key millis [-] number>string write

View File

@ -19,7 +19,7 @@ SYMBOL: stop-after-last-window?
: event-loop ( -- ) : event-loop ( -- )
event-loop? [ event-loop? [
[ [
[ NSApp do-events ui-step 10 sleep ] ui-try [ NSApp do-events ui-step ui-wait ] ui-try
] with-autorelease-pool event-loop ] with-autorelease-pool event-loop
] when ; ] when ;

View File

@ -2,7 +2,7 @@
! See http://factorcode.org/license.txt for BSD license. ! See http://factorcode.org/license.txt for BSD license.
USING: arrays hashtables kernel models math namespaces sequences USING: arrays hashtables kernel models math namespaces sequences
quotations math.vectors combinators sorting vectors dlists quotations math.vectors combinators sorting vectors dlists
models ; models threads ;
IN: ui.gadgets IN: ui.gadgets
TUPLE: rect loc dim ; TUPLE: rect loc dim ;
@ -178,13 +178,17 @@ M: array gadget-text*
: forget-pref-dim ( gadget -- ) f swap set-gadget-pref-dim ; : forget-pref-dim ( gadget -- ) f swap set-gadget-pref-dim ;
SYMBOL: ui-thread
: notify-ui-thread ( -- ) ui-thread get interrupt ;
: layout-queue ( -- queue ) \ layout-queue get ; : layout-queue ( -- queue ) \ layout-queue get ;
: layout-later ( gadget -- ) : layout-later ( gadget -- )
#! When unit testing gadgets without the UI running, the #! When unit testing gadgets without the UI running, the
#! invalid queue is not initialized and we simply ignore #! invalid queue is not initialized and we simply ignore
#! invalidation requests. #! invalidation requests.
layout-queue [ push-front ] [ drop ] if* ; layout-queue [ push-front notify-ui-thread ] [ drop ] if* ;
DEFER: relayout DEFER: relayout
@ -256,11 +260,11 @@ M: gadget layout* drop ;
: queue-graft ( gadget -- ) : queue-graft ( gadget -- )
{ f t } over set-gadget-graft-state { f t } over set-gadget-graft-state
graft-queue push-front ; graft-queue push-front notify-ui-thread ;
: queue-ungraft ( gadget -- ) : queue-ungraft ( gadget -- )
{ t f } over set-gadget-graft-state { t f } over set-gadget-graft-state
graft-queue push-front ; graft-queue push-front notify-ui-thread ;
: graft-later ( gadget -- ) : graft-later ( gadget -- )
dup gadget-graft-state { dup gadget-graft-state {

View File

@ -133,6 +133,9 @@ SYMBOL: ui-hook
: ui-step ( -- ) : ui-step ( -- )
[ notify-queued layout-queued redraw-worlds ] assert-depth ; [ notify-queued layout-queued redraw-worlds ] assert-depth ;
: ui-wait ( -- )
10 sleep ;
: open-world-window ( world -- ) : open-world-window ( world -- )
dup pref-dim over set-gadget-dim dup relayout graft ui-step ; dup pref-dim over set-gadget-dim dup relayout graft ui-step ;
@ -155,6 +158,7 @@ M: object close-window
find-world [ ungraft ] when* ; find-world [ ungraft ] when* ;
: start-ui ( -- ) : start-ui ( -- )
self ui-thread set-global
restore-windows? [ restore-windows? [
restore-windows restore-windows
] [ ] [

View File

@ -15,8 +15,11 @@ TUPLE: windows-ui-backend ;
: lf>crlf [ [ dup CHAR: \n = [ CHAR: \r , ] when , ] each ] "" make ; : lf>crlf [ [ dup CHAR: \n = [ CHAR: \r , ] when , ] each ] "" make ;
: enum-clipboard ( -- seq ) : enum-clipboard ( -- seq )
0 [ EnumClipboardFormats win32-error dup dup 0 > ] [ ] 0
{ } unfold nip ; [ EnumClipboardFormats win32-error dup dup 0 > ]
[ ]
[ drop ]
unfold nip ;
: with-clipboard ( quot -- ) : with-clipboard ( quot -- )
f OpenClipboard win32-error=0/f f OpenClipboard win32-error=0/f
@ -40,13 +43,12 @@ TUPLE: windows-ui-backend ;
: copy ( str -- ) : copy ( str -- )
lf>crlf [ lf>crlf [
string>u16-alien string>u16-alien
f OpenClipboard win32-error=0/f
EmptyClipboard win32-error=0/f EmptyClipboard win32-error=0/f
GMEM_MOVEABLE over length 1+ GlobalAlloc GMEM_MOVEABLE over length 1+ GlobalAlloc
dup win32-error=0/f dup win32-error=0/f
dup GlobalLock dup win32-error=0/f dup GlobalLock dup win32-error=0/f
rot dup length memcpy swapd byte-array>memory
dup GlobalUnlock win32-error=0/f dup GlobalUnlock win32-error=0/f
CF_UNICODETEXT swap SetClipboardData win32-error=0/f CF_UNICODETEXT swap SetClipboardData win32-error=0/f
] with-clipboard ; ] with-clipboard ;
@ -72,31 +74,29 @@ SYMBOL: mouse-captured
: style ( -- n ) WS_OVERLAPPEDWINDOW ; inline : style ( -- n ) WS_OVERLAPPEDWINDOW ; inline
: ex-style ( -- n ) WS_EX_APPWINDOW WS_EX_WINDOWEDGE bitor ; inline : ex-style ( -- n ) WS_EX_APPWINDOW WS_EX_WINDOWEDGE bitor ; inline
: adjust-RECT ( RECT -- )
style 0 ex-style AdjustWindowRectEx win32-error=0/f ;
: make-RECT ( width height -- RECT )
"RECT" <c-object> [ set-RECT-bottom ] keep [ set-RECT-right ] keep ;
: make-adjusted-RECT ( width height -- RECT )
make-RECT dup adjust-RECT ;
: get-RECT-dimensions ( RECT -- width height )
[ RECT-right ] keep [ RECT-left - ] keep
[ RECT-bottom ] keep RECT-top - ;
: get-RECT-top-left ( RECT -- x y ) : get-RECT-top-left ( RECT -- x y )
[ RECT-left ] keep RECT-top ; [ RECT-left ] keep RECT-top ;
: get-RECT-dimensions ( RECT -- x y width height )
[ get-RECT-top-left ] keep
[ RECT-right ] keep [ RECT-left - ] keep
[ RECT-bottom ] keep RECT-top - ;
: handle-wm-paint ( hWnd uMsg wParam lParam -- ) : handle-wm-paint ( hWnd uMsg wParam lParam -- )
#! wParam and lParam are unused #! wParam and lParam are unused
#! only paint if width/height both > 0 #! only paint if width/height both > 0
3drop window draw-world ; 3drop window draw-world ;
: handle-wm-size ( hWnd uMsg wParam lParam -- ) : handle-wm-size ( hWnd uMsg wParam lParam -- )
[ lo-word ] keep hi-word make-RECT get-RECT-dimensions 2array 2nip 2nip
[ lo-word ] keep hi-word 2array
dup { 0 0 } = [ 2drop ] [ swap window set-gadget-dim ui-step ] if ; dup { 0 0 } = [ 2drop ] [ swap window set-gadget-dim ui-step ] if ;
: handle-wm-move ( hWnd uMsg wParam lParam -- )
2nip
[ lo-word ] keep hi-word 2array
swap window set-world-loc ;
: wm-keydown-codes ( -- key ) : wm-keydown-codes ( -- key )
H{ H{
{ 8 "BACKSPACE" } { 8 "BACKSPACE" }
@ -240,7 +240,7 @@ M: windows-ui-backend (close-window)
: mouse-absolute>relative ( lparam handle -- array ) : mouse-absolute>relative ( lparam handle -- array )
>r >lo-hi r> >r >lo-hi r>
0 0 make-RECT [ GetWindowRect win32-error=0/f ] keep "RECT" <c-object> [ GetWindowRect win32-error=0/f ] keep
get-RECT-top-left 2array v- ; get-RECT-top-left 2array v- ;
: mouse-event>gesture ( uMsg -- button ) : mouse-event>gesture ( uMsg -- button )
@ -317,6 +317,7 @@ M: windows-ui-backend (close-window)
{ [ dup WM_PAINT = ] { [ dup WM_PAINT = ]
[ drop 4dup handle-wm-paint DefWindowProc ] } [ drop 4dup handle-wm-paint DefWindowProc ] }
{ [ dup WM_SIZE = ] [ drop handle-wm-size 0 ] } { [ dup WM_SIZE = ] [ drop handle-wm-size 0 ] }
{ [ dup WM_MOVE = ] [ drop handle-wm-move 0 ] }
! Keyboard events ! Keyboard events
{ [ dup WM_KEYDOWN = over WM_SYSKEYDOWN = or ] { [ dup WM_KEYDOWN = over WM_SYSKEYDOWN = or ]
@ -353,7 +354,7 @@ M: windows-ui-backend (close-window)
{ {
{ [ windows get empty? ] [ drop ] } { [ windows get empty? ] [ drop ] }
{ [ dup peek-message? ] [ { [ dup peek-message? ] [
>r [ ui-step 10 sleep ] ui-try >r [ ui-step ui-wait ] ui-try
r> event-loop r> event-loop
] } ] }
{ [ dup MSG-message WM_QUIT = ] [ drop ] } { [ dup MSG-message WM_QUIT = ] [ drop ] }
@ -383,13 +384,26 @@ M: windows-ui-backend (close-window)
RegisterClassEx dup win32-error=0/f RegisterClassEx dup win32-error=0/f
] when ; ] when ;
: create-window ( width height -- hwnd ) : adjust-RECT ( RECT -- )
style 0 ex-style AdjustWindowRectEx win32-error=0/f ;
: make-RECT ( world -- RECT )
dup world-loc { 40 40 } vmax dup rot rect-dim v+
"RECT" <c-object>
over first over set-RECT-right
swap second over set-RECT-bottom
over first over set-RECT-left
swap second over set-RECT-top ;
: make-adjusted-RECT ( rect -- RECT )
make-RECT dup adjust-RECT ;
: create-window ( rect -- hwnd )
make-adjusted-RECT make-adjusted-RECT
>r class-name-ptr get-global f r> >r class-name-ptr get-global f r>
>r >r >r ex-style r> r> >r >r >r ex-style r> r>
{ WS_CLIPSIBLINGS WS_CLIPCHILDREN style } flags { WS_CLIPSIBLINGS WS_CLIPCHILDREN style } flags
CW_USEDEFAULT dup r> r> get-RECT-dimensions
get-RECT-dimensions
f f f GetModuleHandle f CreateWindowEx dup win32-error=0/f ; f f f GetModuleHandle f CreateWindowEx dup win32-error=0/f ;
: show-window ( hWnd -- ) : show-window ( hWnd -- )
@ -424,7 +438,7 @@ M: windows-ui-backend (close-window)
get-dc dup setup-pixel-format dup get-rc ; get-dc dup setup-pixel-format dup get-rc ;
M: windows-ui-backend (open-window) ( world -- ) M: windows-ui-backend (open-window) ( world -- )
[ rect-dim first2 create-window dup setup-gl ] keep [ create-window dup setup-gl ] keep
[ f <win> ] keep [ f <win> ] keep
[ swap win-hWnd register-window ] 2keep [ swap win-hWnd register-window ] 2keep
dupd set-world-handle dupd set-world-handle
@ -445,8 +459,8 @@ M: windows-ui-backend raise-window* ( world -- )
M: windows-ui-backend set-title ( string world -- ) M: windows-ui-backend set-title ( string world -- )
world-handle [ nip win-hWnd WM_SETTEXT 0 ] 2keep world-handle [ nip win-hWnd WM_SETTEXT 0 ] 2keep
dup win-title [ free ] when* dup win-title [ free ] when*
>r malloc-u16-string r> >r malloc-u16-string dup r>
dupd set-win-title alien-address set-win-title alien-address
SendMessage drop ; SendMessage drop ;
M: windows-ui-backend ui M: windows-ui-backend ui

View File

@ -178,7 +178,7 @@ M: world client-event
next-event dup next-event dup
None XFilterEvent zero? [ drop wait-event ] unless None XFilterEvent zero? [ drop wait-event ] unless
] [ ] [
ui-step 10 sleep wait-event ui-step ui-wait wait-event
] if ; ] if ;
: do-events ( -- ) : do-events ( -- )

View File

@ -31,13 +31,13 @@ SYMBOL: cgi-root
"method" get >upper "REQUEST_METHOD" set "method" get >upper "REQUEST_METHOD" set
"raw-query" get "QUERY_STRING" set "raw-query" get "QUERY_STRING" set
"Cookie" header-param "HTTP_COOKIE" set "cookie" header-param "HTTP_COOKIE" set
"User-Agent" header-param "HTTP_USER_AGENT" set "user-agent" header-param "HTTP_USER_AGENT" set
"Accept" header-param "HTTP_ACCEPT" set "accept" header-param "HTTP_ACCEPT" set
post? [ post? [
"Content-Type" header-param "CONTENT_TYPE" set "content-type" header-param "CONTENT_TYPE" set
"raw-response" get length number>string "CONTENT_LENGTH" set "raw-response" get length number>string "CONTENT_LENGTH" set
] when ] when
] H{ } make-assoc ; ] H{ } make-assoc ;

View File

@ -25,7 +25,7 @@ SYMBOL: doc-root
: last-modified-matches? ( filename -- bool ) : last-modified-matches? ( filename -- bool )
file-http-date dup [ file-http-date dup [
"If-Modified-Since" header-param = "if-modified-since" header-param =
] when ; ] when ;
: not-modified-response ( -- ) : not-modified-response ( -- )

View File

@ -25,7 +25,7 @@ IN: webapps.fjsc
: compile-url ( url -- ) : compile-url ( url -- )
#! Compile the factor code at the given url, return the javascript. #! Compile the factor code at the given url, return the javascript.
dup "http:" head? [ "Unable to access remote sites." throw ] when dup "http:" head? [ "Unable to access remote sites." throw ] when
"http://" "Host" header-param rot 3append http-get compile "();" write flush ; "http://" "host" header-param rot 3append http-get compile "();" write flush ;
\ compile-url { \ compile-url {
{ "url" v-required } { "url" v-required }