M: postgresql-db dispose ( db -- )
handle>> PQfinish ;
-M: postgresql-statement bind-statement* ( statement -- )
- drop ;
+M: postgresql-statement bind-statement* ( statement -- ) drop ;
GENERIC: postgresql-bind-conversion ( tuple object -- low-level-binding )
[ handle>> ] [ n>> ] bi ;
M: postgresql-result-set row-column ( result-set column -- object )
- >r result-handle-n r> pq-get-string ;
+ [ result-handle-n ] dip pq-get-string ;
M: postgresql-result-set row-column-typed ( result-set column -- object )
dup pick out-params>> nth type>>
- >r >r result-handle-n r> r> postgresql-column-typed ;
+ [ result-handle-n ] 2dip postgresql-column-typed ;
M: postgresql-statement query-results ( query -- result-set )
dup bind-params>> [
: create-table-sql ( class -- statement )
[
+ dupd
"create table " 0% 0%
"(" 0% [ ", " 0% ] [
dup column-name>> 0%
" " 0%
dup type>> lookup-create-type 0%
modifiers 0%
- ] interleave ");" 0%
+ ] interleave
+
+ ", " 0%
+ find-primary-key
+ "primary key(" 0%
+ [ "," 0% ] [ column-name>> 0% ] interleave
+ "));" 0%
] query-make ;
: create-function-sql ( class -- statement )
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
+ dup db-assigned? [ create-function-sql , ] [ drop ] if
] { } make ;
: drop-function-sql ( class -- statement )
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
+ dup db-assigned? [ 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,
+ dup find-primary-key first 2,
remove-id
[ ", " 0% ] [ bind% ] interleave
");" 0%
");" 0%
] query-make ;
-M: postgresql-db insert-tuple* ( tuple statement -- )
+M: postgresql-db insert-tuple-set-key ( 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 } }
+ { +db-assigned-id+ { "integer" "serial" f } }
+ { +user-assigned-id+ { f f f } }
+ { +random-id+ { "bigint" "bigint" f } }
{ TEXT { "text" "text" f } }
{ VARCHAR { "varchar" "varchar" f } }
{ INTEGER { "integer" "integer" f } }
[ db-columns ] [ db-table ] bi ;
: query-make ( class quot -- )
- >r sql-props r>
- [ 0 sql-counter rot with-variable ] curry { "" { } { } } nmake
+ [ sql-props ] dip
+ [ 0 sql-counter rot with-variable ] curry
+ { "" { } { } } nmake
<simple-statement> maybe-make-retryable ; inline
: where-primary-key% ( specs -- )
" where " 0%
- find-primary-key dup column-name>> 0% " = " 0% bind% ;
+ find-primary-key [
+ " and " 0%
+ ] [
+ dup column-name>> 0% " = " 0% bind%
+ ] interleave ;
M: db <update-tuple-statement> ( class -- statement )
[
dup double-infinite-interval? [ drop f ] when
] with filter ;
-: where-clause ( tuple specs -- )
- dupd filter-slots [
- drop
+: many-where ( tuple seq -- )
+ " where " 0% [
+ " and " 0%
] [
- " where " 0% [
- " and " 0%
- ] [
- 2dup slot-name>> swap get-slot-named where
- ] interleave drop
- ] if-empty ;
+ 2dup slot-name>> swap get-slot-named where
+ ] interleave drop ;
+
+: where-clause ( tuple specs -- )
+ dupd filter-slots [ drop ] [ many-where ] if-empty ;
M: db <delete-tuples-statement> ( tuple table -- sql )
[
db get handle>> sqlite3_last_insert_rowid
dup zero? [ "last-id failed" throw ] when ;
-M: sqlite-db insert-tuple* ( tuple statement -- )
+M: sqlite-db insert-tuple-set-key ( tuple statement -- )
execute-statement last-insert-id swap set-primary-key ;
M: sqlite-result-set #columns ( result-set -- n )
M: sqlite-db create-sql-statement ( class -- statement )
[
+ dupd
"create table " 0% 0%
"(" 0% [ ", " 0% ] [
dup column-name>> 0%
" " 0%
dup type>> lookup-create-type 0%
modifiers 0%
- ] interleave ");" 0%
+ ] interleave
+
+ ", " 0%
+ find-primary-key
+ "primary key(" 0%
+ [ "," 0% ] [ column-name>> 0% ] interleave
+ "));" 0%
] query-make ;
M: sqlite-db drop-sql-statement ( class -- statement )
M: sqlite-db persistent-table ( -- assoc )
H{
- { +db-assigned-id+ { "integer primary key" "integer primary key" "primary key" } }
- { +user-assigned-id+ { f f "primary key" } }
- { +random-id+ { "integer primary key" "integer primary key" "primary key" } }
- { INTEGER { "integer" "integer" "primary key" } }
+ { +db-assigned-id+ { "integer" "integer" f } }
+ { +user-assigned-id+ { f f f } }
+ { +random-id+ { "integer" "integer" f } }
+ { INTEGER { "integer" "integer" f } }
{ BIG-INTEGER { "bigint" "bigint" } }
{ SIGNED-BIG-INTEGER { "bigint" "bigint" } }
{ UNSIGNED-BIG-INTEGER { "bigint" "bigint" } }
: test-queries ( -- )
[ ] [ exam ensure-table ] unit-test
- ! [ ] [ T{ exam f f "Kyle" 100 } insert-tuple ] unit-test
- ! [ ] [ T{ exam f f "Stan" 80 } insert-tuple ] unit-test
- ! [ ] [ T{ exam f f "Kenny" 60 } insert-tuple ] unit-test
- ! [ ] [ T{ exam f f "Cartman" 41 } insert-tuple ] unit-test
- [ ] [ 10 [ random-exam insert-tuple ] times ] unit-test
- [ 5 ] [ <query> T{ exam { score T{ interval { from { 0 t } } { to { 100 t } } } } } >>tuple 5 >>limit select-tuples length ] unit-test
- ! [ ] [ T{ exam { name "Kenny" } } >query ] unit-test
- ! [ ] [ query ] unit-test
- ;
+ [ ] [ 1000 [ random-exam insert-tuple ] times ] unit-test
+ [ 5 ] [
+ <query>
+ T{ exam { score T{ interval { from { 0 t } } { to { 100 t } } } } }
+ >>tuple
+ 5 >>limit select-tuples length
+ ] unit-test ;
+
+TUPLE: compound-foo a b c ;
+
+compound-foo "COMPOUND_FOO"
+{
+ { "a" "A" INTEGER +user-assigned-id+ }
+ { "b" "B" INTEGER +user-assigned-id+ }
+ { "c" "C" INTEGER }
+} define-persistent
+
+: test-compound-primary-key ( -- )
+ [ ] [ compound-foo ensure-table ] unit-test
+ [ ] [ compound-foo drop-table ] unit-test
+ [ ] [ compound-foo create-table ] unit-test
+ [ ] [ 1 2 3 compound-foo boa insert-tuple ] unit-test
+ [ 1 2 3 compound-foo boa insert-tuple ] must-fail
+ [ ] [ 2 3 4 compound-foo boa insert-tuple ] unit-test
+ [ T{ compound-foo { a 2 } { b 3 } { c 4 } } ]
+ [ compound-foo new 4 >>c select-tuple ] unit-test ;
+
+[ test-compound-primary-key ] test-sqlite
+[ test-compound-primary-key ] test-postgresql
: test-db ( -- )
"tuples-test.db" temp-file sqlite-db make-db db-open db set ;
HOOK: <count-statement> db ( query -- statement )
HOOK: query>statement db ( query -- statement )
-HOOK: insert-tuple* db ( tuple statement -- )
-
-ERROR: not-persistent class ;
-
-: db-table ( class -- object )
- dup "db-table" word-prop [ ] [ not-persistent ] ?if ;
-
-: db-columns ( class -- object )
- superclasses [ "db-columns" word-prop ] map concat ;
-
-: db-relations ( class -- object )
- "db-relations" word-prop ;
-
-: set-primary-key ( key tuple -- )
- [
- class db-columns find-primary-key slot-name>>
- ] keep set-slot-named ;
+HOOK: insert-tuple-set-key db ( tuple statement -- )
SYMBOL: sql-counter
: next-sql-counter ( -- str )
: insert-db-assigned-statement ( tuple -- )
dup class
db get insert-statements>> [ <insert-db-assigned-statement> ] cache
- [ bind-tuple ] 2keep insert-tuple* ;
+ [ bind-tuple ] 2keep insert-tuple-set-key ;
: insert-user-assigned-statement ( tuple -- )
dup class
SYMBOLS: +autoincrement+ +serial+ +unique+ +default+ +null+ +not-null+
+foreign-id+ +has-many+ ;
+: offset-of-slot ( string tuple -- n )
+ class superclasses [ "slots" word-prop ] map concat
+ slot-named offset>> ;
+
+: get-slot-named ( name tuple -- value )
+ tuck offset-of-slot slot ;
+
+: set-slot-named ( value name obj -- )
+ tuck offset-of-slot set-slot ;
+
+ERROR: not-persistent class ;
+
+: db-table ( class -- object )
+ dup "db-table" word-prop [ ] [ not-persistent ] ?if ;
+
+: db-columns ( class -- object )
+ superclasses [ "db-columns" word-prop ] map concat ;
+
+: db-relations ( class -- object )
+ "db-relations" word-prop ;
+
+: find-primary-key ( specs -- seq )
+ [ primary-key>> ] filter ;
+
+: set-primary-key ( value tuple -- )
+ [
+ class db-columns
+ find-primary-key first slot-name>>
+ ] keep set-slot-named ;
+
: primary-key? ( spec -- ? )
primary-key>> +primary-key+? ;
-: db-assigned-id-spec? ( spec -- ? )
- primary-key>> +db-assigned-id+? ;
+: db-assigned-id-spec? ( specs -- ? )
+ [ primary-key>> +db-assigned-id+? ] contains? ;
-: assigned-id-spec? ( spec -- ? )
- primary-key>> +user-assigned-id+? ;
+: assigned-id-spec? ( specs -- ? )
+ [ primary-key>> +user-assigned-id+? ] contains? ;
: normalize-spec ( spec -- )
dup type>> dup +primary-key+? [
[ >>primary-key drop ] [ drop ] if*
] if ;
-: find-primary-key ( specs -- obj )
- [ primary-key>> ] find nip ;
+: db-assigned? ( class -- ? )
+ db-columns find-primary-key db-assigned-id-spec? ;
: relation? ( spec -- ? ) [ +has-many+ = ] deep-find ;
HOOK: bind% db ( spec -- )
HOOK: bind# db ( spec obj -- )
-
-: offset-of-slot ( string tuple -- n )
- class superclasses [ "slots" word-prop ] map concat
- slot-named offset>> ;
-
-: get-slot-named ( name tuple -- value )
- tuck offset-of-slot slot ;
-
-: set-slot-named ( value name obj -- )
- tuck offset-of-slot set-slot ;