add foreign key integrity to sqlite
parent
b5664733ed
commit
34ce3e13e4
|
@ -3,7 +3,7 @@
|
||||||
USING: accessors kernel math namespaces make sequences random
|
USING: accessors kernel math namespaces make sequences random
|
||||||
strings math.parser math.intervals combinators math.bitwise
|
strings math.parser math.intervals combinators math.bitwise
|
||||||
nmake db db.tuples db.types db.sql classes words shuffle arrays
|
nmake db db.tuples db.types db.sql classes words shuffle arrays
|
||||||
destructors continuations db.tuples.private ;
|
destructors continuations db.tuples.private prettyprint ;
|
||||||
IN: db.queries
|
IN: db.queries
|
||||||
|
|
||||||
GENERIC: where ( specs obj -- )
|
GENERIC: where ( specs obj -- )
|
||||||
|
@ -45,11 +45,16 @@ M: retryable execute-statement* ( statement type -- )
|
||||||
: sql-props ( class -- columns table )
|
: sql-props ( class -- columns table )
|
||||||
[ db-columns ] [ db-table ] bi ;
|
[ db-columns ] [ db-table ] bi ;
|
||||||
|
|
||||||
: query-make ( class quot -- )
|
: query-make ( class quot -- statements )
|
||||||
|
#! query, input, outputs, secondary queries
|
||||||
|
over unparse "table" set
|
||||||
[ sql-props ] dip
|
[ sql-props ] dip
|
||||||
[ 0 sql-counter rot with-variable ] curry
|
[ 0 sql-counter rot with-variable ] curry
|
||||||
{ "" { } { } } nmake
|
{ "" { } { } { } } nmake
|
||||||
<simple-statement> maybe-make-retryable ; inline
|
[ <simple-statement> maybe-make-retryable ] dip
|
||||||
|
[
|
||||||
|
[ 1array ] dip append
|
||||||
|
] unless-empty ; inline
|
||||||
|
|
||||||
: where-primary-key% ( specs -- )
|
: where-primary-key% ( specs -- )
|
||||||
" where " 0%
|
" where " 0%
|
||||||
|
@ -152,25 +157,20 @@ M: db <select-by-slots-statement> ( tuple class -- statement )
|
||||||
where-clause
|
where-clause
|
||||||
] query-make ;
|
] query-make ;
|
||||||
|
|
||||||
|
: splice ( string1 string2 string3 -- string )
|
||||||
|
swap 3append ;
|
||||||
|
|
||||||
: do-group ( tuple groups -- )
|
: do-group ( tuple groups -- )
|
||||||
[
|
[ ", " join " group by " splice ] curry change-sql drop ;
|
||||||
", " join " group by " swap 3append
|
|
||||||
] curry change-sql drop ;
|
|
||||||
|
|
||||||
: do-order ( tuple order -- )
|
: do-order ( tuple order -- )
|
||||||
[
|
[ ", " join " order by " splice ] curry change-sql drop ;
|
||||||
", " join " order by " swap 3append
|
|
||||||
] curry change-sql drop ;
|
|
||||||
|
|
||||||
: do-offset ( tuple n -- )
|
: do-offset ( tuple n -- )
|
||||||
[
|
[ number>string " offset " splice ] curry change-sql drop ;
|
||||||
number>string " offset " swap 3append
|
|
||||||
] curry change-sql drop ;
|
|
||||||
|
|
||||||
: do-limit ( tuple n -- )
|
: do-limit ( tuple n -- )
|
||||||
[
|
[ number>string " limit " splice ] curry change-sql drop ;
|
||||||
number>string " limit " swap 3append
|
|
||||||
] curry change-sql drop ;
|
|
||||||
|
|
||||||
: make-query* ( tuple query -- tuple' )
|
: make-query* ( tuple query -- tuple' )
|
||||||
dupd
|
dupd
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
USING: kernel namespaces db.sql sequences math ;
|
|
||||||
IN: db.sql.tests
|
|
||||||
|
|
||||||
! TUPLE: person name age ;
|
|
||||||
: insert-1
|
|
||||||
{ insert
|
|
||||||
{
|
|
||||||
{ table "person" }
|
|
||||||
{ columns "name" "age" }
|
|
||||||
{ values "erg" 26 }
|
|
||||||
}
|
|
||||||
} ;
|
|
||||||
|
|
||||||
: update-1
|
|
||||||
{ update "person"
|
|
||||||
{ set { "name" "erg" }
|
|
||||||
{ "age" 6 } }
|
|
||||||
{ where { "age" 6 } }
|
|
||||||
} ;
|
|
||||||
|
|
||||||
: select-1
|
|
||||||
{ select
|
|
||||||
{ columns
|
|
||||||
"branchno"
|
|
||||||
{ count "staffno" as "mycount" }
|
|
||||||
{ sum "salary" as "mysum" } }
|
|
||||||
{ from "staff" "lol" }
|
|
||||||
{ where
|
|
||||||
{ "salary" > all
|
|
||||||
{ select
|
|
||||||
{ columns "salary" }
|
|
||||||
{ from "staff" }
|
|
||||||
{ where { "branchno" = "b003" } }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{ "branchno" > 3 } }
|
|
||||||
{ group-by "branchno" "lol2" }
|
|
||||||
{ having { count "staffno" > 1 } }
|
|
||||||
{ order-by "branchno" }
|
|
||||||
{ offset 40 }
|
|
||||||
{ limit 20 }
|
|
||||||
} ;
|
|
|
@ -1,172 +0,0 @@
|
||||||
USING: kernel parser quotations classes.tuple words math.order
|
|
||||||
nmake namespaces sequences arrays combinators
|
|
||||||
prettyprint strings math.parser math symbols db ;
|
|
||||||
IN: db.sql
|
|
||||||
|
|
||||||
SYMBOLS: insert update delete select distinct columns from as
|
|
||||||
where group-by having order-by limit offset is-null desc all
|
|
||||||
any count avg table values ;
|
|
||||||
|
|
||||||
: input-spec, ( obj -- ) 1, ;
|
|
||||||
: output-spec, ( obj -- ) 2, ;
|
|
||||||
: input, ( obj -- ) 3, ;
|
|
||||||
: output, ( obj -- ) 4, ;
|
|
||||||
|
|
||||||
DEFER: sql%
|
|
||||||
|
|
||||||
: (sql-interleave) ( seq sep -- )
|
|
||||||
[ sql% ] curry [ sql% ] interleave ;
|
|
||||||
|
|
||||||
: sql-interleave ( seq str sep -- )
|
|
||||||
swap sql% (sql-interleave) ;
|
|
||||||
|
|
||||||
: sql-function, ( seq function -- )
|
|
||||||
sql% "(" sql% unclip sql% ")" sql% [ sql% ] each ;
|
|
||||||
|
|
||||||
: sql-where, ( seq -- )
|
|
||||||
[
|
|
||||||
[ second 0, ]
|
|
||||||
[ first 0, ]
|
|
||||||
[ third 1, \ ? 0, ] tri
|
|
||||||
] each ;
|
|
||||||
|
|
||||||
HOOK: sql-create db ( object -- )
|
|
||||||
M: db sql-create ( object -- )
|
|
||||||
drop
|
|
||||||
"create table" sql% ;
|
|
||||||
|
|
||||||
HOOK: sql-drop db ( object -- )
|
|
||||||
M: db sql-drop ( object -- )
|
|
||||||
drop
|
|
||||||
"drop table" sql% ;
|
|
||||||
|
|
||||||
HOOK: sql-insert db ( object -- )
|
|
||||||
M: db sql-insert ( object -- )
|
|
||||||
drop
|
|
||||||
"insert into" sql% ;
|
|
||||||
|
|
||||||
HOOK: sql-update db ( object -- )
|
|
||||||
M: db sql-update ( object -- )
|
|
||||||
drop
|
|
||||||
"update" sql% ;
|
|
||||||
|
|
||||||
HOOK: sql-delete db ( object -- )
|
|
||||||
M: db sql-delete ( object -- )
|
|
||||||
drop
|
|
||||||
"delete" sql% ;
|
|
||||||
|
|
||||||
HOOK: sql-select db ( object -- )
|
|
||||||
M: db sql-select ( object -- )
|
|
||||||
"select" sql% "," (sql-interleave) ;
|
|
||||||
|
|
||||||
HOOK: sql-columns db ( object -- )
|
|
||||||
M: db sql-columns ( object -- )
|
|
||||||
"," (sql-interleave) ;
|
|
||||||
|
|
||||||
HOOK: sql-from db ( object -- )
|
|
||||||
M: db sql-from ( object -- )
|
|
||||||
"from" "," sql-interleave ;
|
|
||||||
|
|
||||||
HOOK: sql-where db ( object -- )
|
|
||||||
M: db sql-where ( object -- )
|
|
||||||
"where" 0, sql-where, ;
|
|
||||||
|
|
||||||
HOOK: sql-group-by db ( object -- )
|
|
||||||
M: db sql-group-by ( object -- )
|
|
||||||
"group by" "," sql-interleave ;
|
|
||||||
|
|
||||||
HOOK: sql-having db ( object -- )
|
|
||||||
M: db sql-having ( object -- )
|
|
||||||
"having" "," sql-interleave ;
|
|
||||||
|
|
||||||
HOOK: sql-order-by db ( object -- )
|
|
||||||
M: db sql-order-by ( object -- )
|
|
||||||
"order by" "," sql-interleave ;
|
|
||||||
|
|
||||||
HOOK: sql-offset db ( object -- )
|
|
||||||
M: db sql-offset ( object -- )
|
|
||||||
"offset" sql% sql% ;
|
|
||||||
|
|
||||||
HOOK: sql-limit db ( object -- )
|
|
||||||
M: db sql-limit ( object -- )
|
|
||||||
"limit" sql% sql% ;
|
|
||||||
|
|
||||||
! GENERIC: sql-subselect db ( object -- )
|
|
||||||
! M: db sql-subselectselect ( object -- )
|
|
||||||
! "(select" sql% sql% ")" sql% ;
|
|
||||||
|
|
||||||
HOOK: sql-table db ( object -- )
|
|
||||||
M: db sql-table ( object -- )
|
|
||||||
sql% ;
|
|
||||||
|
|
||||||
HOOK: sql-set db ( object -- )
|
|
||||||
M: db sql-set ( object -- )
|
|
||||||
"set" "," sql-interleave ;
|
|
||||||
|
|
||||||
HOOK: sql-values db ( object -- )
|
|
||||||
M: db sql-values ( object -- )
|
|
||||||
"values(" sql% "," (sql-interleave) ")" sql% ;
|
|
||||||
|
|
||||||
HOOK: sql-count db ( object -- )
|
|
||||||
M: db sql-count ( object -- )
|
|
||||||
"count" sql-function, ;
|
|
||||||
|
|
||||||
HOOK: sql-sum db ( object -- )
|
|
||||||
M: db sql-sum ( object -- )
|
|
||||||
"sum" sql-function, ;
|
|
||||||
|
|
||||||
HOOK: sql-avg db ( object -- )
|
|
||||||
M: db sql-avg ( object -- )
|
|
||||||
"avg" sql-function, ;
|
|
||||||
|
|
||||||
HOOK: sql-min db ( object -- )
|
|
||||||
M: db sql-min ( object -- )
|
|
||||||
"min" sql-function, ;
|
|
||||||
|
|
||||||
HOOK: sql-max db ( object -- )
|
|
||||||
M: db sql-max ( object -- )
|
|
||||||
"max" sql-function, ;
|
|
||||||
|
|
||||||
: sql-array% ( array -- )
|
|
||||||
unclip
|
|
||||||
{
|
|
||||||
{ \ create [ sql-create ] }
|
|
||||||
{ \ drop [ sql-drop ] }
|
|
||||||
{ \ insert [ sql-insert ] }
|
|
||||||
{ \ update [ sql-update ] }
|
|
||||||
{ \ delete [ sql-delete ] }
|
|
||||||
{ \ select [ sql-select ] }
|
|
||||||
{ \ columns [ sql-columns ] }
|
|
||||||
{ \ from [ sql-from ] }
|
|
||||||
{ \ where [ sql-where ] }
|
|
||||||
{ \ group-by [ sql-group-by ] }
|
|
||||||
{ \ having [ sql-having ] }
|
|
||||||
{ \ order-by [ sql-order-by ] }
|
|
||||||
{ \ offset [ sql-offset ] }
|
|
||||||
{ \ limit [ sql-limit ] }
|
|
||||||
{ \ table [ sql-table ] }
|
|
||||||
{ \ set [ sql-set ] }
|
|
||||||
{ \ values [ sql-values ] }
|
|
||||||
{ \ count [ sql-count ] }
|
|
||||||
{ \ sum [ sql-sum ] }
|
|
||||||
{ \ avg [ sql-avg ] }
|
|
||||||
{ \ min [ sql-min ] }
|
|
||||||
{ \ max [ sql-max ] }
|
|
||||||
[ sql% [ sql% ] each ]
|
|
||||||
} case ;
|
|
||||||
|
|
||||||
ERROR: no-sql-match ;
|
|
||||||
: sql% ( obj -- )
|
|
||||||
{
|
|
||||||
{ [ dup string? ] [ 0, ] }
|
|
||||||
{ [ dup array? ] [ sql-array% ] }
|
|
||||||
{ [ dup number? ] [ number>string sql% ] }
|
|
||||||
{ [ dup symbol? ] [ unparse sql% ] }
|
|
||||||
{ [ dup word? ] [ unparse sql% ] }
|
|
||||||
{ [ dup quotation? ] [ call ] }
|
|
||||||
[ no-sql-match ]
|
|
||||||
} cond ;
|
|
||||||
|
|
||||||
: parse-sql ( obj -- sql in-spec out-spec in out )
|
|
||||||
[ [ sql% ] each ] { { } { } { } } nmake
|
|
||||||
[ " " join ] 2dip ;
|
|
|
@ -5,7 +5,8 @@ io.files kernel math math.parser namespaces prettyprint
|
||||||
sequences strings classes.tuple alien.c-types continuations
|
sequences strings classes.tuple alien.c-types continuations
|
||||||
db.sqlite.lib db.sqlite.ffi db.tuples words db.types combinators
|
db.sqlite.lib db.sqlite.ffi db.tuples words db.types combinators
|
||||||
math.intervals io nmake accessors vectors math.ranges random
|
math.intervals io nmake accessors vectors math.ranges random
|
||||||
math.bitwise db.queries destructors db.tuples.private ;
|
math.bitwise db.queries destructors db.tuples.private interpolate
|
||||||
|
io.streams.string multiline make ;
|
||||||
IN: db.sqlite
|
IN: db.sqlite
|
||||||
|
|
||||||
TUPLE: sqlite-db < db path ;
|
TUPLE: sqlite-db < db path ;
|
||||||
|
@ -117,7 +118,8 @@ M: sqlite-db create-sql-statement ( class -- statement )
|
||||||
dupd
|
dupd
|
||||||
"create table " 0% 0%
|
"create table " 0% 0%
|
||||||
"(" 0% [ ", " 0% ] [
|
"(" 0% [ ", " 0% ] [
|
||||||
dup column-name>> 0%
|
dup "sql-spec" set
|
||||||
|
dup column-name>> [ "table-id" set ] [ 0% ] bi
|
||||||
" " 0%
|
" " 0%
|
||||||
dup type>> lookup-create-type 0%
|
dup type>> lookup-create-type 0%
|
||||||
modifiers 0%
|
modifiers 0%
|
||||||
|
@ -203,9 +205,110 @@ M: sqlite-db persistent-table ( -- assoc )
|
||||||
{ random-generator { f f f } }
|
{ random-generator { f f f } }
|
||||||
} ;
|
} ;
|
||||||
|
|
||||||
|
: insert-trigger ( -- string )
|
||||||
|
[
|
||||||
|
<"
|
||||||
|
CREATE TRIGGER fki_${table}_${foreign-table}_id
|
||||||
|
BEFORE INSERT ON ${table}
|
||||||
|
FOR EACH ROW BEGIN
|
||||||
|
SELECT RAISE(ROLLBACK, 'insert on table "${table}" violates foreign key constraint "fk_${foreign-table}_id"')
|
||||||
|
WHERE (SELECT ${foreign-table-id} FROM ${foreign-table} WHERE ${foreign-table-id} = NEW.${table-id}) IS NULL;
|
||||||
|
END;
|
||||||
|
"> interpolate
|
||||||
|
] with-string-writer ;
|
||||||
|
|
||||||
|
: insert-trigger-not-null ( -- string )
|
||||||
|
[
|
||||||
|
<"
|
||||||
|
CREATE TRIGGER fki_${table}_${foreign-table}_id
|
||||||
|
BEFORE INSERT ON ${table}
|
||||||
|
FOR EACH ROW BEGIN
|
||||||
|
SELECT RAISE(ROLLBACK, 'insert on table "${table}" violates foreign key constraint "fk_${foreign-table}_id"')
|
||||||
|
WHERE NEW.${foreign-table-id} IS NOT NULL
|
||||||
|
AND (SELECT ${foreign-table-id} FROM ${foreign-table} WHERE ${foreign-table-id} = NEW.${table-id}) IS NULL;
|
||||||
|
END;
|
||||||
|
"> interpolate
|
||||||
|
] with-string-writer ;
|
||||||
|
|
||||||
|
: update-trigger ( -- string )
|
||||||
|
[
|
||||||
|
<"
|
||||||
|
CREATE TRIGGER fku_${table}_${foreign-table}_id
|
||||||
|
BEFORE UPDATE ON ${table}
|
||||||
|
FOR EACH ROW BEGIN
|
||||||
|
SELECT RAISE(ROLLBACK, 'update on table "${table}" violates foreign key constraint "fk_${foreign-table}_id"')
|
||||||
|
WHERE (SELECT ${foreign-table-id} FROM ${foreign-table} WHERE ${foreign-table-id} = NEW.${table-id}) IS NULL;
|
||||||
|
END;
|
||||||
|
"> interpolate
|
||||||
|
] with-string-writer ;
|
||||||
|
|
||||||
|
: update-trigger-not-null ( -- string )
|
||||||
|
[
|
||||||
|
<"
|
||||||
|
CREATE TRIGGER fku_${table}_${foreign-table}_id
|
||||||
|
BEFORE UPDATE ON ${table}
|
||||||
|
FOR EACH ROW BEGIN
|
||||||
|
SELECT RAISE(ROLLBACK, 'update on table "${table}" violates foreign key constraint "fk_${foreign-table}_id"')
|
||||||
|
WHERE NEW.${foreign-table-id} IS NOT NULL
|
||||||
|
AND (SELECT ${foreign-table-id} FROM ${foreign-table} WHERE ${foreign-table-id} = NEW.${table-id}) IS NULL;
|
||||||
|
END;
|
||||||
|
"> interpolate
|
||||||
|
] with-string-writer ;
|
||||||
|
|
||||||
|
: delete-trigger-restrict ( -- string )
|
||||||
|
[
|
||||||
|
<"
|
||||||
|
CREATE TRIGGER fkd_${table}_${foreign-table}_id
|
||||||
|
BEFORE DELETE ON ${foreign-table}
|
||||||
|
FOR EACH ROW BEGIN
|
||||||
|
SELECT RAISE(ROLLBACK, 'delete on table "${foreign-table}" violates foreign key constraint "fk_${foreign-table}_id"')
|
||||||
|
WHERE (SELECT ${foreign-table-id} FROM ${foreign-table} WHERE ${foreign-table-id} = OLD.${foreign-table-id}) IS NOT NULL;
|
||||||
|
END;
|
||||||
|
"> interpolate
|
||||||
|
] with-string-writer ;
|
||||||
|
|
||||||
|
: delete-trigger-cascade ( -- string )
|
||||||
|
[
|
||||||
|
<"
|
||||||
|
CREATE TRIGGER fkd_${table}_${foreign-table}_id
|
||||||
|
BEFORE DELETE ON ${foreign-table}
|
||||||
|
FOR EACH ROW BEGIN
|
||||||
|
DELETE from ${table} WHERE ${table-id} = OLD.${foreign-table-id};
|
||||||
|
END;
|
||||||
|
"> interpolate
|
||||||
|
] with-string-writer ;
|
||||||
|
|
||||||
|
: can-be-null? ( -- ? )
|
||||||
|
"sql-spec" get modifiers>> [ +not-null+ = ] contains? not ;
|
||||||
|
|
||||||
|
: delete-cascade? ( -- ? )
|
||||||
|
"sql-spec" get modifiers>> [ +cascade+ = ] contains? ;
|
||||||
|
|
||||||
|
: sqlite-trigger, ( string -- )
|
||||||
|
{ } { } <simple-statement> 3, ;
|
||||||
|
|
||||||
|
: create-sqlite-triggers ( -- )
|
||||||
|
can-be-null? [
|
||||||
|
insert-trigger sqlite-trigger,
|
||||||
|
update-trigger sqlite-trigger,
|
||||||
|
] [
|
||||||
|
insert-trigger-not-null sqlite-trigger,
|
||||||
|
update-trigger-not-null sqlite-trigger,
|
||||||
|
] if
|
||||||
|
delete-cascade? [
|
||||||
|
delete-trigger-cascade sqlite-trigger,
|
||||||
|
] [
|
||||||
|
delete-trigger-restrict sqlite-trigger,
|
||||||
|
] if ;
|
||||||
|
|
||||||
M: sqlite-db compound ( string seq -- new-string )
|
M: sqlite-db compound ( string seq -- new-string )
|
||||||
over {
|
over {
|
||||||
{ "default" [ first number>string join-space ] }
|
{ "default" [ first number>string join-space ] }
|
||||||
{ "references" [ >reference-string ] }
|
{ "references" [
|
||||||
|
[ >reference-string ] keep
|
||||||
|
first2 [ "foreign-table" set ]
|
||||||
|
[ "foreign-table-id" set ] bi*
|
||||||
|
create-sqlite-triggers
|
||||||
|
] }
|
||||||
[ 2drop ]
|
[ 2drop ]
|
||||||
} case ;
|
} case ;
|
||||||
|
|
|
@ -176,26 +176,49 @@ SYMBOL: person4
|
||||||
T{ timestamp f 0 0 0 12 34 56 T{ duration f 0 0 0 0 0 0 } }
|
T{ timestamp f 0 0 0 12 34 56 T{ duration f 0 0 0 0 0 0 } }
|
||||||
f H{ { 1 2 } { 3 4 } { 5 "lol" } } URL" http://www.google.com/search?hl=en&q=trailer+park+boys&btnG=Google+Search" <user-assigned-person> person4 set ;
|
f H{ { 1 2 } { 3 4 } { 5 "lol" } } URL" http://www.google.com/search?hl=en&q=trailer+park+boys&btnG=Google+Search" <user-assigned-person> person4 set ;
|
||||||
|
|
||||||
|
|
||||||
TUPLE: paste n summary author channel mode contents timestamp annotations ;
|
TUPLE: paste n summary author channel mode contents timestamp annotations ;
|
||||||
TUPLE: annotation n paste-id summary author mode contents ;
|
TUPLE: annotation n paste-id summary author mode contents ;
|
||||||
|
|
||||||
: db-assigned-paste-schema ( -- )
|
paste "PASTE"
|
||||||
paste "PASTE"
|
{
|
||||||
{
|
{ "n" "ID" +db-assigned-id+ }
|
||||||
{ "n" "ID" +db-assigned-id+ }
|
{ "summary" "SUMMARY" TEXT }
|
||||||
{ "summary" "SUMMARY" TEXT }
|
{ "author" "AUTHOR" TEXT }
|
||||||
{ "author" "AUTHOR" TEXT }
|
{ "channel" "CHANNEL" TEXT }
|
||||||
{ "channel" "CHANNEL" TEXT }
|
{ "mode" "MODE" TEXT }
|
||||||
{ "mode" "MODE" TEXT }
|
{ "contents" "CONTENTS" TEXT }
|
||||||
{ "contents" "CONTENTS" TEXT }
|
{ "timestamp" "DATE" TIMESTAMP }
|
||||||
{ "timestamp" "DATE" TIMESTAMP }
|
{ "annotations" { +has-many+ annotation } }
|
||||||
{ "annotations" { +has-many+ annotation } }
|
} define-persistent
|
||||||
} define-persistent
|
|
||||||
|
|
||||||
|
: annotation-schema-foreign-key ( -- )
|
||||||
annotation "ANNOTATION"
|
annotation "ANNOTATION"
|
||||||
{
|
{
|
||||||
{ "n" "ID" +db-assigned-id+ }
|
{ "n" "ID" +db-assigned-id+ }
|
||||||
{ "paste-id" "PASTE_ID" INTEGER { +foreign-id+ paste "n" }
|
{ "paste-id" "PASTE_ID" INTEGER { +foreign-id+ paste "ID" } }
|
||||||
|
{ "summary" "SUMMARY" TEXT }
|
||||||
|
{ "author" "AUTHOR" TEXT }
|
||||||
|
{ "mode" "MODE" TEXT }
|
||||||
|
{ "contents" "CONTENTS" TEXT }
|
||||||
|
} define-persistent ;
|
||||||
|
|
||||||
|
: annotation-schema-foreign-key-not-null ( -- )
|
||||||
|
annotation "ANNOTATION"
|
||||||
|
{
|
||||||
|
{ "n" "ID" +db-assigned-id+ }
|
||||||
|
{ "paste-id" "PASTE_ID" INTEGER { +foreign-id+ paste "ID" } +not-null+ }
|
||||||
|
{ "summary" "SUMMARY" TEXT }
|
||||||
|
{ "author" "AUTHOR" TEXT }
|
||||||
|
{ "mode" "MODE" TEXT }
|
||||||
|
{ "contents" "CONTENTS" TEXT }
|
||||||
|
} define-persistent ;
|
||||||
|
|
||||||
|
: annotation-schema-cascade ( -- )
|
||||||
|
annotation "ANNOTATION"
|
||||||
|
{
|
||||||
|
{ "n" "ID" +db-assigned-id+ }
|
||||||
|
{ "paste-id" "PASTE_ID" INTEGER { +foreign-id+ paste "ID" }
|
||||||
+on-delete+ +cascade+ }
|
+on-delete+ +cascade+ }
|
||||||
{ "summary" "SUMMARY" TEXT }
|
{ "summary" "SUMMARY" TEXT }
|
||||||
{ "author" "AUTHOR" TEXT }
|
{ "author" "AUTHOR" TEXT }
|
||||||
|
@ -203,8 +226,18 @@ TUPLE: annotation n paste-id summary author mode contents ;
|
||||||
{ "contents" "CONTENTS" TEXT }
|
{ "contents" "CONTENTS" TEXT }
|
||||||
} define-persistent ;
|
} define-persistent ;
|
||||||
|
|
||||||
|
: annotation-schema-restrict ( -- )
|
||||||
|
annotation "ANNOTATION"
|
||||||
|
{
|
||||||
|
{ "n" "ID" +db-assigned-id+ }
|
||||||
|
{ "paste-id" "PASTE_ID" INTEGER { +foreign-id+ paste "ID" } }
|
||||||
|
{ "summary" "SUMMARY" TEXT }
|
||||||
|
{ "author" "AUTHOR" TEXT }
|
||||||
|
{ "mode" "MODE" TEXT }
|
||||||
|
{ "contents" "CONTENTS" TEXT }
|
||||||
|
} define-persistent ;
|
||||||
|
|
||||||
: test-paste-schema ( -- )
|
: test-paste-schema ( -- )
|
||||||
[ ] [ db-assigned-paste-schema ] unit-test
|
|
||||||
[ ] [ paste ensure-table ] unit-test
|
[ ] [ paste ensure-table ] unit-test
|
||||||
[ ] [ annotation ensure-table ] unit-test
|
[ ] [ annotation ensure-table ] unit-test
|
||||||
[ ] [ annotation drop-table ] unit-test
|
[ ] [ annotation drop-table ] unit-test
|
||||||
|
@ -229,14 +262,38 @@ TUPLE: annotation n paste-id summary author mode contents ;
|
||||||
"erg" >>author
|
"erg" >>author
|
||||||
"annotation contents" >>contents
|
"annotation contents" >>contents
|
||||||
insert-tuple
|
insert-tuple
|
||||||
] unit-test
|
] unit-test ;
|
||||||
|
|
||||||
[ ] [
|
: test-foreign-key ( -- )
|
||||||
] unit-test
|
[ ] [ annotation-schema-foreign-key ] unit-test
|
||||||
;
|
test-paste-schema
|
||||||
|
[ paste new 1 >>n delete-tuples ] must-fail ;
|
||||||
|
|
||||||
[ test-paste-schema ] test-sqlite
|
: test-foreign-key-not-null ( -- )
|
||||||
[ test-paste-schema ] test-postgresql
|
[ ] [ annotation-schema-foreign-key-not-null ] unit-test
|
||||||
|
test-paste-schema
|
||||||
|
[ paste new 1 >>n delete-tuples ] must-fail ;
|
||||||
|
|
||||||
|
: test-cascade ( -- )
|
||||||
|
[ ] [ annotation-schema-cascade ] unit-test
|
||||||
|
test-paste-schema
|
||||||
|
[ ] [ paste new 1 >>n delete-tuples ] unit-test
|
||||||
|
[ 0 ] [ paste new select-tuples length ] unit-test ;
|
||||||
|
|
||||||
|
: test-restrict ( -- )
|
||||||
|
[ ] [ annotation-schema-restrict ] unit-test
|
||||||
|
test-paste-schema
|
||||||
|
[ paste new 1 >>n delete-tuples ] must-fail ;
|
||||||
|
|
||||||
|
[ test-foreign-key ] test-sqlite
|
||||||
|
[ test-foreign-key-not-null ] test-sqlite
|
||||||
|
[ test-cascade ] test-sqlite
|
||||||
|
[ test-restrict ] test-sqlite
|
||||||
|
|
||||||
|
[ test-foreign-key ] test-postgresql
|
||||||
|
[ test-foreign-key-not-null ] test-postgresql
|
||||||
|
[ test-cascade ] test-postgresql
|
||||||
|
[ test-restrict ] test-postgresql
|
||||||
|
|
||||||
: test-repeated-insert
|
: test-repeated-insert
|
||||||
[ ] [ person ensure-table ] unit-test
|
[ ] [ person ensure-table ] unit-test
|
||||||
|
|
|
@ -87,16 +87,17 @@ SYMBOLS: INTEGER BIG-INTEGER SIGNED-BIG-INTEGER UNSIGNED-BIG-INTEGER
|
||||||
DOUBLE REAL BOOLEAN TEXT VARCHAR DATE TIME DATETIME TIMESTAMP BLOB
|
DOUBLE REAL BOOLEAN TEXT VARCHAR DATE TIME DATETIME TIMESTAMP BLOB
|
||||||
FACTOR-BLOB NULL URL ;
|
FACTOR-BLOB NULL URL ;
|
||||||
|
|
||||||
: spec>tuple ( class spec -- tuple )
|
: <sql-spec> ( class slot-name column-name type modifiers -- sql-spec )
|
||||||
3 f pad-right
|
|
||||||
[ first3 ] keep 3 tail
|
|
||||||
sql-spec new
|
sql-spec new
|
||||||
swap >>modifiers
|
swap >>modifiers
|
||||||
swap >>type
|
swap >>type
|
||||||
swap >>column-name
|
swap >>column-name
|
||||||
swap >>slot-name
|
swap >>slot-name
|
||||||
swap >>class
|
swap >>class
|
||||||
dup normalize-spec ;
|
dup normalize-spec ;
|
||||||
|
|
||||||
|
: spec>tuple ( class spec -- tuple )
|
||||||
|
3 f pad-right [ first3 ] keep 3 tail <sql-spec> ;
|
||||||
|
|
||||||
: number>string* ( n/string -- string )
|
: number>string* ( n/string -- string )
|
||||||
dup number? [ number>string ] when ;
|
dup number? [ number>string ] when ;
|
||||||
|
@ -115,7 +116,6 @@ FACTOR-BLOB NULL URL ;
|
||||||
! PostgreSQL Types:
|
! PostgreSQL Types:
|
||||||
! http://developer.postgresql.org/pgdocs/postgres/datatype.html
|
! http://developer.postgresql.org/pgdocs/postgres/datatype.html
|
||||||
|
|
||||||
|
|
||||||
: ?at ( obj assoc -- value/obj ? )
|
: ?at ( obj assoc -- value/obj ? )
|
||||||
dupd at* [ [ nip ] [ drop ] if ] keep ;
|
dupd at* [ [ nip ] [ drop ] if ] keep ;
|
||||||
|
|
||||||
|
@ -159,8 +159,11 @@ ERROR: no-sql-type type ;
|
||||||
HOOK: bind% db ( spec -- )
|
HOOK: bind% db ( spec -- )
|
||||||
HOOK: bind# db ( spec obj -- )
|
HOOK: bind# db ( spec obj -- )
|
||||||
|
|
||||||
|
ERROR: no-column column ;
|
||||||
|
|
||||||
: >reference-string ( string pair -- string )
|
: >reference-string ( string pair -- string )
|
||||||
first2
|
first2
|
||||||
[ [ unparse join-space ] [ db-columns ] bi ] dip
|
[ [ unparse join-space ] [ db-columns ] bi ] dip
|
||||||
swap [ slot-name>> = ] with find nip
|
swap [ column-name>> = ] with find nip
|
||||||
|
[ no-column ] unless*
|
||||||
column-name>> paren append ;
|
column-name>> paren append ;
|
||||||
|
|
Loading…
Reference in New Issue