create table

This commit is contained in:
jackfoxy 2022-09-01 17:14:57 -07:00
parent 3f7e81f5a9
commit c7f9f0a4ae
5 changed files with 167 additions and 50 deletions

View File

@ -27,7 +27,7 @@ INSERT INTO [ <ship-qualifer> ]<table-name>
```
Discussion:
If a column list is provided columns defines as `u(<aura>)` or defined with a default my be omitted. Otherwise the `VALUES` or `<query>` must provide data for all columns in the expected order.
The `VALUES` or `<query>` must provide data for all columns in the expected order.
Tables in the namespace *sys* cannot be inserted into.
`DEFAULT` is the bunt of the column type.

View File

@ -2,7 +2,7 @@
<query> ::=
[WITH <common-table-expression> ]
FROM { [<ship-qualifer>]<table-view> [ [AS] <alias> ]
[ { JOIN | LEFT JOIN | RIGHT JOIN | OUTER JOIN | CROSS JOIN }
[ { JOIN | LEFT JOIN | RIGHT JOIN | OUTER JOIN [ALL] }
<ship-qualifer><table-view> [ [AS] <alias> ]
ON <predicate>
]
@ -20,7 +20,11 @@ SELECT [ TOP <n> ] [ DISTINCT ]
[ HAVING <predicate> ]
[ INTO <new-table> ]
[ ORDER BY { <column> | <column-ordinal> } [ ,...n ] ]
[ { UNION | UNION ALL | EXCEPT | INTERSECT | DIVIDED BY [ WITH REMAINDER ] } <query> ] [ ...n ]
[ { UNION [ WITH DUPS ]
| EXCEPT
| INTERSECT
| DIVIDED BY [ WITH REMAINDER ]
| CROSS JOIN [ ( { { 1 | 2 }:{ * | 1..n } } ) ] } <query> ] [ ...n ]
```
```

View File

@ -38,20 +38,10 @@
::
:: helper types
::
+$ on-update
$:
%on-update
action=foreign-key-action:ast
==
+$ on-delete
$:
%on-delete
action=foreign-key-action:ast
==
+$ interim-key
$:
%interim-key
is-clustered=@t
is-clustered=?
columns=(list ordered-column:ast)
==
::
@ -67,6 +57,25 @@
[(sub (add -.next-hair -.end-hair) 1) +.end-hair] :: add lines and use last column
[-.next-hair (sub (add +.next-hair +.end-hair) 1)] :: else add column positions
::
:: foreign keys in create table
::
++ build-foreign-keys
|= a=[table=qualified-object:ast f-keys=(list *)]
=/ f-keys +.a
=/ foreign-keys `(list foreign-key:ast)`~
|-
?: =(~ f-keys)
foreign-keys
?@ -<.f-keys
%= $ :: foreign key table must be in same DB as table
foreign-keys [(foreign-key:ast %foreign-key -<.f-keys -.a ->-.f-keys (qualified-object:ast %qualified-object ~ ->+<.a ->+<+>+<.f-keys ->+<+>+>.f-keys) ->+>.f-keys ~) foreign-keys]
f-keys +.f-keys
==
%= $ :: foreign key table must be in same DB as table
foreign-keys [(foreign-key:ast %foreign-key -<-.f-keys -.a -<+<.f-keys (qualified-object:ast %qualified-object ~ ->+<.a -<+>->+>-.f-keys -<+>->+>+.f-keys) -<+>+.f-keys ->.f-keys) foreign-keys]
f-keys +.f-keys
==
::
:: parser rules and helpers
::
++ jester :: match a cord, case agnostic, thanks ~tinnus-napbus
@ -86,6 +95,12 @@
==
(fail tub)
$(p.tub (lust i.q.tub p.tub), q.tub t.q.tub, daf (rsh 3 daf))
++ cook-qualified-2object :: namespace.object-name
|= a=*
~+
?@ a
(qualified-object:ast %qualified-object ~ current-database 'dbo' a)
(qualified-object:ast %qualified-object ~ current-database -.a +.a)
++ cook-qualified-3object :: database.namespace.object-name
|= a=*
~+
@ -132,23 +147,44 @@
!!
++ cook-primary-key
|= a=*
?@ -.a
(interim-key %interim-key -.a +.a)
(interim-key %interim-key %nonclustered a)
++ cook-on-update
?@ -.a
?: =(-.a 'clustered') (interim-key %interim-key %.y +.a) (interim-key %interim-key %.n +.a)
(interim-key %interim-key %.n a)
++ cook-referential-integrity
|= a=*
?@ a
(on-update %on-update a)
(on-update %on-update %no-action)
++ cook-on-delete
?: ?=([[@ @] @ @] [a]) :: <type> cascade, <type> cascade
?: =(%delete -<.a)
?: =(%update +<.a)
~[%delete-cascade %update-cascade]
!!
?: =(%update -<.a)
?: =(%delete +<.a)
~[%delete-cascade %update-cascade]
!!
!!
?: ?=([@ @] [a]) :: <type> cascade
?: =(-.a %delete) [%delete-cascade ~] [%update-cascade ~]
?: ?=([[@ @] @ @ [@ %~] @] [a]) :: <type> cascade, <type> no action
?: =(-<.a %delete) [%delete-cascade ~] [%update-cascade ~]
?: ?=([[@ @ [@ %~] @] @ @] [a]) :: <type> no action, <type> cascade
?: =(+<.a %delete) [%delete-cascade ~] [%update-cascade ~]
?: ?=([@ [@ %~]] a) :: <type> no action
~
?: ?=([[@ @ [@ %~] @] @ @ [@ %~] @] a) :: <type> no action, <type> no action
~
!!
++ cook-foreign-key
|= a=*
?@ a
(on-delete %on-delete a)
(on-delete %on-delete %no-action)
~+
?: ?=([[@ * * [@ @] *] *] [a]) :: foreign key ns.table ... references fk-table ... on action on action
(foreign-key:ast %foreign-key -<.a ->-.a ->+<-.a ->+<+.a ->+>.a +.a)
?: ?=([[@ [[@ @ @] %~] @ [@ %~]] *] [a]) :: foreign key table ... references fk-table ... on action on action
(foreign-key:ast %foreign-key -<.a ->-.a ->+<-.a 'dbo' ->+.a +.a)
!!
++ whitespace ~+ (star ;~(pose gah (just '\09') (just '\0d')))
++ end-or-next-command ~+ ;~(pose ;~(plug whitespace mic) whitespace mic)
++ parse-face ~+ ;~(pfix whitespace sym)
++ face-list ~+ (more com parse-face)
++ face-list ~+ ;~(pfix whitespace (ifix [pal par] (more com ;~(pose ;~(sfix parse-face whitespace) parse-face))))
++ qualified-namespace :: database.namespace
|= [a=* current-database=@t]
~+
@ -324,6 +360,9 @@
|-
?: =(~ script) :: https://github.com/urbit/arvo/issues/1024
(flop commands)
=/ check-empty u.+3:q.+3:(whitespace [[1 1] script])
?: =(0 (lent q.q:check-empty)) :: trailing whitespace after last end-command (;)
(flop commands)
~| "Error parsing command keyword: {<script-position>}"
=/ command-nail u.+3:q.+3:(parse-command [script-position script])
?- `command`p.command-nail
@ -408,17 +447,18 @@
%create-table
=/ key-literal ;~(plug whitespace (jester 'primary') whitespace (jester 'key'))
=/ foreign-key-literal ;~(plug whitespace (jester 'foreign') whitespace (jester 'key'))
=/ foreign-key
;~(pfix foreign-key-literal parse-face ordered-column-list ;~(pfix ;~(plug whitespace (jester 'references')) parse-qualified-2-name face-list))
=/ parse-on-delete
(cook cook-on-delete ;~(pfix ;~(plug whitespace (jester 'on') whitespace (jester 'delete')) ;~(pfix whitespace ;~(pose (jester 'cascade') ;~(plug (jester 'no') whitespace (jester 'action'))))))
=/ parse-on-update
(cook cook-on-update ;~(pfix ;~(plug whitespace (jester 'on') whitespace (jester 'update')) ;~(pfix whitespace ;~(pose (jester 'cascade') ;~(plug (jester 'no') whitespace (jester 'action'))))))
=/ target-table (cook cook-qualified-2object parse-qualified-2-name)
=/ foreign-key
;~(pfix foreign-key-literal ;~(plug parse-face ordered-column-list ;~(pfix ;~(plug whitespace (jester 'references')) ;~(plug target-table face-list))))
=/ referential-integrity ;~ plug
;~(pfix ;~(plug whitespace (jester 'on') whitespace) ;~(pose (jester 'update') (jester 'delete')))
;~(pfix whitespace ;~(pose (jester 'cascade') ;~(plug (jester 'no') whitespace (jester 'action'))))
==
=/ full-foreign-key ;~ pose
;~(plug foreign-key parse-on-delete parse-on-update)
;~(plug foreign-key parse-on-update parse-on-delete)
;~(plug foreign-key parse-on-delete)
;~(plug foreign-key parse-on-update)
;~(plug foreign-key (cook cook-referential-integrity ;~(plug referential-integrity referential-integrity)))
;~(plug foreign-key (cook cook-referential-integrity ;~(plug referential-integrity referential-integrity)))
;~(plug foreign-key (cook cook-referential-integrity referential-integrity))
;~(plug foreign-key (cook cook-referential-integrity referential-integrity))
foreign-key
==
=/ parse-table ;~ plug
@ -428,21 +468,30 @@
;~(pfix whitespace (ifix [pal par] column-defintion-list))
:: primary key
(cook cook-primary-key ;~(pfix key-literal ;~(pose ;~(plug clustering ordered-column-list) ordered-column-list)))
:: foreign key
;~(pose ;~(plug full-foreign-key end-or-next-command) end-or-next-command)
:: foreign keys
;~(sfix (more com full-foreign-key) end-or-next-command)
==
~| "Cannot parse table {<p.q.command-nail>}"
=/ table-nail (parse-table [[1 1] q.q.command-nail])
~| "command-nail: {<command-nail>}"
~| "table-nail: {<table-nail>}"
=/ parsed (wonk table-nail)
=/ next-cursor
(get-next-cursor [script-position +<.command-nail p.q.u.+3:q.+3:table-nail])
~| "parsed: {<parsed>}"
=/ yikes 0
~| "remainder: {<q.q.u.+3.q:table-nail>}"
!!
=/ qualified-table -.parsed
=/ table-columns +<.parsed
=/ key +>-.parsed
=/ key-name (crip (weld (weld "ix-primary-" (trip +>+<.qualified-table)) (weld "-" (trip +>+>.qualified-table))))
=/ primary-key (create-index:ast %create-index key-name qualified-table %.y +<.key +>.key)
=/ foreign-keys (build-foreign-keys [qualified-table +>+.parsed])
%= $
script q.q.u.+3.q:table-nail
script-position next-cursor
commands
[`command-ast`(create-table:ast %create-table qualified-table table-columns primary-key foreign-keys) commands]
==
%create-view
!!
%drop-database

View File

@ -3,7 +3,7 @@
|%
:: helper types
::
+$ foreign-key-action ?(%no-action %cascade)
+$ referential-integrity-action ?(%delete-cascade %update-cascade)
+$ index-action ?(%rebuild %disable %resume)
+$ ordered-column
$:
@ -226,16 +226,16 @@
columns=(list ordered-column)
==
+$ create-namespace $:([%create-namespace database-name=@t name=@t])
+$ foreign-key
$:
%create-foreign-key
%foreign-key
name=@t
table=qualified-object
columns=(list @t) :: the source columns
reference-namespace=@t :: reference table and columns
reference-table-name=@t :: in other words, the target index
reference-columns=(list @t)
on-delete=foreign-key-action :: what to do when referenced item deletes
on-update=foreign-key-action :: and for updates?
columns=(list ordered-column) :: the source columns
reference-table=qualified-object :: reference (target) table
reference-columns=(list @t) :: and columns
referential-integrity=(list referential-integrity-action) :: what to do when referenced item deletes or updates
==
+$ create-table
$:

View File

@ -123,6 +123,70 @@
%- expect-fail
|. (parse:parse(current-database 'other-db') "cReate namesPace my-db.Bad-face")
::
:: create table
::
:: tests 1, 2, 3, 5, and extra whitespace characters, db.ns.table clustered on delete cascade on update cascade; db..table nonclustered on update cascade on delete cascade
++ test-create-table-1
=/ expected1 [%create-table table=[%qualified-object ship=~ database='db' namespace='ns' name='my-table'] columns=~[[%column name='col1' column-type='@t'] [%column name='col2' column-type='@p'] [%column name='col3' column-type='@ud']] primary-key=[%create-index name='ix-primary-ns-my-table' object-name=[%qualified-object ship=~ database='db' namespace='ns' name='my-table'] is-unique=%.y is-clustered=%.y columns=~[[%ordered-column column-name='col1' is-ascending=%.y] [%ordered-column column-name='col2' is-ascending=%.y]]] foreign-keys=~[[%foreign-key name='fk' table=[%qualified-object ship=~ database='db' namespace='ns' name='my-table'] columns=~[[%ordered-column column-name='col1' is-ascending=%.y] [%ordered-column column-name='col2' is-ascending=%.n]] reference-table=[%qualified-object ship=~ database='db' namespace='dbo' name='fk-table'] reference-columns=~['col19' 'col20'] referential-integrity=~[%delete-cascade %update-cascade]]]]
=/ expected2 [%create-table table=[%qualified-object ship=~ database='db' namespace='dbo' name='my-table'] columns=~[[%column name='col1' column-type='@t'] [%column name='col2' column-type='@p'] [%column name='col3' column-type='@ud']] primary-key=[%create-index name='ix-primary-dbo-my-table' object-name=[%qualified-object ship=~ database='db' namespace='dbo' name='my-table'] is-unique=%.y is-clustered=%.n columns=~[[%ordered-column column-name='col1' is-ascending=%.y] [%ordered-column column-name='col2' is-ascending=%.y]]] foreign-keys=~[[%foreign-key name='fk' table=[%qualified-object ship=~ database='db' namespace='dbo' name='my-table'] columns=~[[%ordered-column column-name='col1' is-ascending=%.y] [%ordered-column column-name='col2' is-ascending=%.n]] reference-table=[%qualified-object ship=~ database='db' namespace='dbo' name='fk-table'] reference-columns=~['col19' 'col20'] referential-integrity=~[%delete-cascade %update-cascade]]]]
=/ urql1 "crEate taBle db.ns.my-table ( col1 @t , col2 @p , col3 @ud ) pRimary kEy clusTered ( col1 , col2 ) foReign KeY fk ( col1 , col2 desc ) reFerences fk-table ( col19 , col20 ) On dELETE CAsCADE oN UPdATE CAScADE "
=/ urql2 "crEate taBle db..my-table ( col1 @t , col2 @p , col3 @ud ) pRimary kEy nonclusTered ( col1 , col2 ) foReign KeY fk ( col1 , col2 desc ) reFerences fk-table ( col19 , col20 ) On UPdATE CAsCADE oN dELETE CAScADE "
%+ expect-eq
!> ~[expected1 expected2]
!> (parse:parse(current-database 'db1') (weld urql1 (weld "\0a;\0a" urql2)))
::
:: leading whitespace characters, whitespace after end delimiter, create nonclustered table... table ... references ns.fk-table on update no action on delete no action
++ test-create-table-2
=/ expected [%create-table table=[%qualified-object ship=~ database='db1' namespace='dbo' name='my-table'] columns=~[[%column name='col1' column-type='@t'] [%column name='col2' column-type='@p'] [%column name='col3' column-type='@ud']] primary-key=[%create-index name='ix-primary-dbo-my-table' object-name=[%qualified-object ship=~ database='db1' namespace='dbo' name='my-table'] is-unique=%.y is-clustered=%.n columns=~[[%ordered-column column-name='col1' is-ascending=%.y] [%ordered-column column-name='col2' is-ascending=%.y]]] foreign-keys=~[[%foreign-key name='fk' table=[%qualified-object ship=~ database='db1' namespace='dbo' name='my-table'] columns=~[[%ordered-column column-name='col1' is-ascending=%.y] [%ordered-column column-name='col2' is-ascending=%.n]] reference-table=[%qualified-object ship=~ database='db1' namespace='ns' name='fk-table'] reference-columns=~['col19' 'col20'] referential-integrity=~]]]
=/ urql2 " \0acreate table my-table (col1 @t,col2 @p,col3 @ud) primary key nonclustered (col1, col2) foreign key fk (col1,col2 desc) reFerences ns.fk-table (col19, col20) on update no action on delete no action; "
%+ expect-eq
!> ~[expected]
!> (parse:parse(current-database 'db1') urql2)
::
:: create table... table ... references ns.fk-table on update no action on delete cascade
++ test-create-table-3
=/ expected [%create-table table=[%qualified-object ship=~ database='db1' namespace='dbo' name='my-table'] columns=~[[%column name='col1' column-type='@t'] [%column name='col2' column-type='@p'] [%column name='col3' column-type='@ud']] primary-key=[%create-index name='ix-primary-dbo-my-table' object-name=[%qualified-object ship=~ database='db1' namespace='dbo' name='my-table'] is-unique=%.y is-clustered=%.n columns=~[[%ordered-column column-name='col1' is-ascending=%.y] [%ordered-column column-name='col2' is-ascending=%.y]]] foreign-keys=~[[%foreign-key name='fk' table=[%qualified-object ship=~ database='db1' namespace='dbo' name='my-table'] columns=~[[%ordered-column column-name='col1' is-ascending=%.y] [%ordered-column column-name='col2' is-ascending=%.n]] reference-table=[%qualified-object ship=~ database='db1' namespace='ns' name='fk-table'] reference-columns=~['col19' 'col20'] referential-integrity=~[%delete-cascade]]]]
=/ urql "create table my-table (col1 @t,col2 @p,col3 @ud) primary key (col1, col2) foreign key fk (col1,col2 desc) reFerences ns.fk-table (col19, col20) on update no action on delete cascade"
%+ expect-eq
!> ~[expected]
!> (parse:parse(current-database 'db1') urql)
::
:: create table... table ... references fk-table on update cascade on delete no action
++ test-create-table-4
=/ expected [%create-table table=[%qualified-object ship=~ database='db1' namespace='dbo' name='my-table'] columns=~[[%column name='col1' column-type='@t'] [%column name='col2' column-type='@p'] [%column name='col3' column-type='@ud']] primary-key=[%create-index name='ix-primary-dbo-my-table' object-name=[%qualified-object ship=~ database='db1' namespace='dbo' name='my-table'] is-unique=%.y is-clustered=%.n columns=~[[%ordered-column column-name='col1' is-ascending=%.y] [%ordered-column column-name='col2' is-ascending=%.y]]] foreign-keys=~[[%foreign-key name='fk' table=[%qualified-object ship=~ database='db1' namespace='dbo' name='my-table'] columns=~[[%ordered-column column-name='col1' is-ascending=%.y] [%ordered-column column-name='col2' is-ascending=%.n]] reference-table=[%qualified-object ship=~ database='db1' namespace='dbo' name='fk-table'] reference-columns=~['col19' 'col20'] referential-integrity=~[%update-cascade]]]]
=/ urql "create table my-table (col1 @t,col2 @p,col3 @ud) primary key (col1, col2) foreign key fk (col1,col2 desc) reFerences fk-table (col19, col20) on update cascade on delete no action"
%+ expect-eq
!> ~[expected]
!> (parse:parse(current-database 'db1') urql)
::
:: create table... table ... single column indices... references fk-table on update cascade
++ test-create-table-5
=/ expected [%create-table table=[%qualified-object ship=~ database='db1' namespace='dbo' name='my-table'] columns=~[[%column name='col1' column-type='@t'] [%column name='col2' column-type='@p'] [%column name='col3' column-type='@ud']] primary-key=[%create-index name='ix-primary-dbo-my-table' object-name=[%qualified-object ship=~ database='db1' namespace='dbo' name='my-table'] is-unique=%.y is-clustered=%.n columns=~[[%ordered-column column-name='col1' is-ascending=%.y]]] foreign-keys=~[[%foreign-key name='fk' table=[%qualified-object ship=~ database='db1' namespace='dbo' name='my-table'] columns=~[[%ordered-column column-name='col2' is-ascending=%.n]] reference-table=[%qualified-object ship=~ database='db1' namespace='dbo' name='fk-table'] reference-columns=~['col20'] referential-integrity=~[%update-cascade]]]]
=/ urql "create table my-table (col1 @t,col2 @p,col3 @ud) primary key (col1) foreign key fk (col2 desc) reFerences fk-table (col20) on update cascade"
%+ expect-eq
!> ~[expected]
!> (parse:parse(current-database 'db1') urql)
::
:: create table... table ... single column indices... references fk-table
++ test-create-table-6
=/ expected [%create-table table=[%qualified-object ship=~ database='db1' namespace='dbo' name='my-table'] columns=~[[%column name='col1' column-type='@t'] [%column name='col2' column-type='@p'] [%column name='col3' column-type='@ud']] primary-key=[%create-index name='ix-primary-dbo-my-table' object-name=[%qualified-object ship=~ database='db1' namespace='dbo' name='my-table'] is-unique=%.y is-clustered=%.n columns=~[[%ordered-column column-name='col1' is-ascending=%.y]]] foreign-keys=~[[%foreign-key name='fk' table=[%qualified-object ship=~ database='db1' namespace='dbo' name='my-table'] columns=~[[%ordered-column column-name='col2' is-ascending=%.n]] reference-table=[%qualified-object ship=~ database='db1' namespace='dbo' name='fk-table'] reference-columns=~['col20'] referential-integrity=~]]]
=/ urql "create table my-table (col1 @t,col2 @p,col3 @ud) primary key (col1) foreign key fk (col2 desc) reFerences fk-table (col20) "
%+ expect-eq
!> ~[expected]
!> (parse:parse(current-database 'db1') urql)
::
:: fail when database qualifier on foreign key table db.ns.fk-table
++ test-create-table-7
=/ urql "create table my-table (col1 @t,col2 @p,col3 @ud) primary key (col1) foreign key fk (col2 desc) reFerences db.ns.fk-table (col20) "
%- expect-fail
|. (parse:parse(current-database 'other-db') urql)
::
:: fail when database qualifier on foreign key table db..fk-table
++ test-create-table-8
=/ urql "create table my-table (col1 @t,col2 @p,col3 @ud) primary key (col1) foreign key fk (col2 desc) reFerences db..fk-table (col20) "
%- expect-fail
|. (parse:parse(current-database 'other-db') urql)
::
:: drop database
::
:: tests 1, 2, 3, 5, and extra whitespace characters, force db.name, name