first 11 predicate tests pass

This commit is contained in:
jackfoxy 2023-01-10 15:26:55 -08:00
parent 969bf84d89
commit f04d177ab4
5 changed files with 217 additions and 150 deletions

View File

@ -2,19 +2,45 @@
## Manifesto
The relational data model is a fundamental component of the computing stack that until now has been cospicuously missing from Urbit. Why is this fundamental technology, with a sound foundation in relational algebra, set theory, and first order predicate calculus so frequently overlooked?
1. RDBMS technology is not typically covered in today's CS curiculums.
2. Developers don't want to hassle with setting up a server.
3. Proprietary closed-source RDBMS implementations.
4. Trendy _no sql_ alternatives.
5. Re-inventing the wheel for reasons.
Some of these rearsons are irrational, others are just wrong.
1. Speculatively, this may be because there is nothing new to discover. The relational model rests on well-understood math theory.
2. Urbit fixes this.
3. Urbit fixes this.
4. Most programmers will never face a situation where an RDBMS is inadequate or inferior for the task. _Key-value Store_ is a very simple relational database. The SQL standard was hastily developed and has some unnecesary baggage which makes it hard to master. Cyclic graphs such as social graphs are difficult to model and work with in SQL. This can be addressed in a blank-slate Urbit implementation.
5. New and junior programmers with little or no SQL exposure mistakenly think they can write better/faster IO by hand, whereas experienced engineers know to use SQL first for all the functionality wherein it can be used (except sorting, which is not strictly part of the relational model).
An Urbit native RDBMS implementation opens new opportunities for composability. All of a ship's data is transparently available for _mash up_ apps and _ad hoc_ queries.
An Urbit RDBMS deserves a _first principles_ approach to design and implementation. The _urQL_ language is heavily influenced by _The Third Manefesto_ (Date and Darwen), emphasizing composability and type safety. Areas where SQL was too hastily designed and/or developed without regard to theory (like nullable columns) have been eliminated, making urQl much more like the _ur Query Language_ Codd and Date would have been proud of. Excellent integration with the entire Urbit stack including through traditional SQL extensions like _Stored Procedures_ and _Triggers_ is to be expected.
## Functionality
The Urbit RDBMS (still to be named) consists of
1. A scripting language and parser (this document)
2. A plan builder
3. Eventually, a front-end app...anyone can write one from the parser and plan APIs.
The scripting language, _urQL_, derives from SQL and varies in only a few cases.
Queries are constructed in FROM..WHERE..SELECT.. order, the order of events in plan exection.
(The user should be cognizant of the ordering of events.)
Table definitions do not allow for nullable columns.
Columns are atoms with auras.
Table definitions do not allow for nullable columns.
All user-defined names follow hoon term naming standard.
All user-defined names follow the hoon term naming standard.
All except the simplest functions and statements separated into their own clause and aliased inline into select clause and predicates.
All except the simplest functions are collected in their own section and aliased inline into select clause and predicates.
Emphasizes composability and improves readability.
There are no subqueries.
@ -24,12 +50,13 @@ Emphasizes composability and improves readability.
Reading and/or updating data on foreign ships is allowed provided the ship's pilot has granted permission. Cross database joins are allowed, but not cross ship joins.
Views cannot be defined on foreign databases.
## urQL language diagrams
[ ] indicate optional entries.
{ } nest options | delimited.
In some cases it groups a portion of the diagram to indicate optional repeating [ ,...n ].
< > hint for user input, e.g. \<alias>, \<table>, or is a placeholder for expanded diagram defined elsewhere.
In some cases { } groups a portion of the diagram to indicate optional repeating [ ,...n ].
< > hint for user input, e.g. \<alias>, \<table>, or is a placeholder for an expanded diagram defined elsewhere.
The following hints are used througout the reference.

View File

@ -142,6 +142,10 @@
::
(stag %p fed:ag)
==
::++ select-rule :: does not build
:: |* text=tape
:: ^- rule
:: ;~(pfix (jest 'select') (funk "select" (easy text)))
++ jester :: match a cord, case agnostic, thanks ~tinnus-napbus
|= daf=@t
|= tub=nail
@ -515,7 +519,6 @@
==
++ build-query-object ~+
|= parsed=*
::~& "build-query-object: {<parsed>}"
?: ?=([@ @ @ @ @] parsed)
(query-object:ast %query-object parsed ~)
?: ?=([[@ @ @ @ @] @] parsed)
@ -545,7 +548,7 @@
:: ?: ?&(?=([@ @ [@ @ @ @ @] @] parsed) ?=(query-object:ast +.parsed))
:: (joined-object:ast %joined-object -.parsed +.parsed ~)
:: (joined-object:ast %joined-object -.parsed +<.parsed (produce-predicate (predicate-list +>.parsed)))
++ parse-object-and-joins ;~ plug
++ parse-object-and-joins ~+ ;~ plug
parse-query-object
;~(pose parse-cross-joined-object (star build-joined-object))
==
@ -805,7 +808,6 @@
?. =(working-depth target-depth.a)
$(components.a +.components.a, resolved [-.components.a resolved])
|-
~& "Hello2 predicates.a: {<predicates.a>}"
::
:: if there are superfluous levels of nesting we will end up here
:: to do: test if this is still working/required
@ -813,7 +815,6 @@
::
:: if () enclosed tree is first thing, then it is always the left subtree
:: ~& "(lent components.a): {<(lent components.a)>}"
~& "-.components.a: {<-.components.a>}"
?: =(-.components.a %pal)
?: =(+>-.components.a %par)
:: stand-alone tree
@ -949,7 +950,7 @@
;~(plug whitespace (jester 'then'))
==
++ predicate-part ~+ ;~ pose
parse-aggregate
:: parse-aggregate
value-literal-list
;~(pose ;~(pfix whitespace parse-operator) parse-operator)
parse-datum
@ -985,7 +986,7 @@
^- predicate:ast
=/ working-tree=predicate:ast ~
=/ tree-stack=(list predicate:ast) ~
:: ~& "predicate-state-machine parsed: {<parsed>}"
~| "predicate-state-machine parsed: {<parsed>}"
|-
?: =((lent parsed) 0) working-tree
?- -.parsed
@ -1026,20 +1027,29 @@
qualified-column:ast
?~ working-tree
?: ?=(binary-operator:ast +<.parsed)
:: ~& "working-tree1: {<+<.parsed>} {<-.parsed>}"
%= $
working-tree [+<.parsed [-.parsed ~ ~] ~]
parsed +>.parsed
==
?: ?&(=(%not +<.parsed) =(%between +>-.parsed))
?: =(%and +>+>-.parsed)
%= $
working-tree
[%not [%between (predicate-state-machine ~[-.parsed %gte +>+<.parsed]) (predicate-state-machine ~[-.parsed %lte +>+>+<.parsed])] ~]
parsed +>+>+>.parsed
==
%= $
working-tree
[%not [%between (predicate-state-machine ~[-.parsed %gte +>+<.parsed]) (predicate-state-machine ~[-.parsed %lte +>+>-.parsed])] ~]
parsed +>+>+.parsed
==
!!
?~ l.working-tree
:: ~& "working-tree2: {<-.working-tree>} {<-.parsed>}"
%= $
working-tree [-.working-tree [-.parsed ~ ~] ~]
parsed +.parsed
==
?~ r.working-tree
:: ~& "working-tree3: {<-.working-tree>} {<+<.working-tree>} {<-.parsed>}"
%= $
working-tree [-.working-tree +<.working-tree [-.parsed ~ ~]]
parsed +.parsed
@ -1291,7 +1301,6 @@
::@@@@@@@@@@@@@@@@@@@@@@
++ produce-from
|= a=*
~& "produce-from: {<a>}"
^- from:ast
?> ?=(query-object:ast -.a)
=/ query-object=query-object:ast -.a
@ -1299,12 +1308,6 @@
=/ joined-objects=(list joined-object:ast) ~
=/ is-cross-join=? %.n
|-
::~| "raw-joined-objects: {<raw-joined-objects>}"
~| "-<.raw-joined-objects: {<-<.raw-joined-objects>}"
~| "->-.raw-joined-objects: {<->-.raw-joined-objects>}"
~| "->+.raw-joined-objects: {<->+.raw-joined-objects>}"
?: =(raw-joined-objects ~)
?: is-cross-join
?: =((lent joined-objects) 1)
@ -1322,12 +1325,10 @@
is-cross-join %.y
raw-joined-objects +.raw-joined-objects
==
=/ pred=predicate:ast (predicate-state-machine (predicate-list ->+.raw-joined-objects))
~| "predicate: {<pred>}"
=/ joined=joined-object:ast (joined-object:ast %joined-object -<.raw-joined-objects ->-.raw-joined-objects `pred)
=/ joined=joined-object:ast
(joined-object:ast %joined-object -<.raw-joined-objects ->-.raw-joined-objects `(predicate-state-machine (predicate-list ->+.raw-joined-objects)))
%= $
joined-objects [joined joined-objects]
:: [(joined-object:ast %joined-object -<.raw-joined-objects ->-.raw-joined-objects `pred) joined-objects]
raw-joined-objects +.raw-joined-objects
==
++ produce-select
@ -1338,28 +1339,28 @@
=/ distinct=? %.n
=/ columns=(list selected-column:ast) ~
~| "produce-select a: {<a>}"
?: ?=([%top @ %bottom @ %distinct %all] a)
?: ?=([%top @ %bottom @ %distinct %all ~] a)
?> ?=(selected-column:ast %all)
(select:ast %select `+<.a `+>+<.a ~ %.y ~[(selected-column:ast %all)])
?: ?=([%top @ %bottom @ %all] a)
?: ?=([%top @ %bottom @ %all ~] a)
?> ?=(selected-column:ast %all)
(select:ast %select `+<.a `+>+<.a ~ %.n ~[(selected-column:ast %all)])
?: ?=([%top @ %distinct %all] a)
?: ?=([%top @ %distinct %all ~] a)
?> ?=(selected-column:ast %all)
(select:ast %select `+<.a ~ %.y ~[(selected-column:ast %all)])
?: ?=([%top @ %all] a)
?: ?=([%top @ %all ~] a)
?> ?=(selected-column:ast %all)
(select:ast %select `+<.a ~ %.n ~[(selected-column:ast %all)])
?: ?=([%bottom @ %distinct %all] a)
?: ?=([%bottom @ %distinct %all ~] a)
?> ?=(selected-column:ast %all)
(select:ast %select ~ `+<.a %.y ~[(selected-column:ast %all)])
?: ?=([%bottom @ %all] a)
?: ?=([%bottom @ %all ~] a)
?> ?=(selected-column:ast %all)
(select:ast %select ~ `+<.a %.n ~[(selected-column:ast %all)])
?: ?=([%distinct %all] a)
?: ?=([%distinct %all ~] a)
?> ?=(selected-column:ast %all)
(select:ast %select ~ ~ %.y ~[(selected-column:ast %all)])
?: ?=([%all] a)
?: ?=([%all ~] a)
?> ?=(selected-column:ast %all)
(select:ast %select ~ ~ %.n ~[(selected-column:ast %all)])
:: |-
@ -1389,16 +1390,16 @@
=/ order-by=(unit order-by:ast) ~
|-
?~ a ~|("cannot parse simple-query {<a>}" !!)
?: =(i.a %query) ~& "%query" $(a t.a)
?: =(i.a %end-command) (build-simple-query [from scalars predicate (need select) group-by having order-by])
?: =(i.a %query) $(a t.a)
?: =(i.a %end-command) (build-simple-query [from scalars predicate (need select) group-by having order-by])
::?: =(i.a %scalars) $(a t.a, scalars +.i.a)
?: =(-<.a %scalars) ~& "%scalars" $(a t.a, scalars ~)
?: =(-<.a %where) ~& "%where" $(a t.a, predicate `(predicate-state-machine (predicate-list +.i.a)))
?: =(-<.a %select) ~& "%select" $(a t.a, select `(produce-select +.i.a))
?: =(-<.a %group-by) ~& "%group-by" $(a t.a, group-by ~)
?: =(-<.a %having) ~& "%having" $(a t.a, having ~)
?: =(-<.a %order-by) ~& "%order-by" $(a t.a, order-by ~)
?: =(-<-.a %query-object) ~& "%query-object" $(a t.a, from `(produce-from i.a))
?: =(-<.a %scalars) $(a t.a, scalars ~)
?: =(-<.a %where) $(a t.a, predicate `(predicate-state-machine (predicate-list +.i.a)))
?: =(-<.a %select) $(a t.a, select `(produce-select +.i.a))
?: =(-<.a %group-by) $(a t.a, group-by ~)
?: =(-<.a %having) $(a t.a, having ~)
?: =(-<.a %order-by) $(a t.a, order-by ~)
?: =(-<-.a %query-object) $(a t.a, from `(produce-from i.a))
~|("cannot parse simple-query {<a>}" !!)
::
:: parse urQL command
@ -1472,15 +1473,27 @@
;~(pfix whitespace (more whitespace (ifix [pal par] (more com parse-insert-value))))
end-or-next-command
==
++ parse-query ;~ plug
++ parse-query1 ;~ plug
parse-object-and-joins
:: (stag %scalars (star parse-scalar))
:: ;~(pfix whitespace ;~(plug (cold %where (jester 'where')) parse-predicate))
;~(pfix whitespace ;~(plug (cold %where (jester 'where')) parse-predicate))
parse-select
:: parse-group-by
:: parse-order-by
end-or-next-command
==
++ parse-query2 ;~ plug
parse-object-and-joins
:: (stag %scalars (star parse-scalar))
parse-select
:: parse-group-by
:: parse-order-by
end-or-next-command
==
++ parse-query ;~ pose
parse-query1
parse-query2
==
++ parse-revoke ;~ plug
:: permission
;~(pfix whitespace ;~(pose (jester 'adminread') (jester 'readonly') (jester 'readwrite') (jester 'all')))
@ -1517,6 +1530,8 @@
(cold %grant ;~(plug whitespace (jester 'grant')))
(cold %insert ;~(plug whitespace (jester 'insert') whitespace (jester 'into')))
(cold %query ;~(plug whitespace (jester 'from')))
::(cold %query ;~(plug whitespace (jester 'select')))
::(cold %query ;~(plug whitespace select-rule))
(cold %revoke ;~(plug whitespace (jester 'revoke')))
(cold %truncate-table ;~(plug whitespace (jester 'truncate') whitespace (jester 'table')))
==
@ -1853,7 +1868,6 @@
=/ parsed (wonk insert-nail)
=/ next-cursor
(get-next-cursor [script-position +<.command-nail p.q.u.+3:q.+3:insert-nail])
~| "parsed: {<parsed>}"
?: ?=([[@ @ @ @ @] @ *] [parsed]) ::"insert rows"
%= $
script q.q.u.+3.q:insert-nail
@ -1882,7 +1896,7 @@
script q.q.u.+3.q:query-nail
script-position next-cursor
commands
[`command-ast`(simple-query:ast %simple-query (produce-simple-query parsed)) commands]
[`command-ast`(produce-simple-query parsed) commands]
==
%revoke

View File

@ -126,7 +126,7 @@
:: query
::
+$ selected-scalar
$%
$:
%selected-scalar
scalar=scalar-function
alias=(unit @t)
@ -143,7 +143,7 @@
alias=(unit @t)
==
+$ joined-object
$%
$:
%joined-object
join=join-type
object=query-object

View File

@ -6,14 +6,14 @@
::
:: each arm tests one urql command
::
:: common things to test
:: common things to test
:: 1) basic command works producing AST object
:: 2) multiple ASTs
:: 3) all keywords are case ambivalent
:: 4) all names follow rules for faces
:: 5) all qualifier combinations work
::
:: -test /=urql=/tests/lib/parse/hoon ~
:: -test /=urql=/tests/lib/parse/hoon ~
|%
:: current database must be proper face
++ test-fail-current-database
@ -91,7 +91,7 @@
::
:: alter table
::
:: tests 1, 2, 3, 5, and extra whitespace characters
:: tests 1, 2, 3, 5, and extra whitespace characters
:: alter column db.ns.table 3 columns ; alter column db..table 1 column
++ test-alter-table-1
=/ expected1 [%alter-table table=[%qualified-object ship=~ database='db' namespace='ns' name='table'] alter-columns=~ add-columns=~[[%column name='col1' column-type='@t'] [%column name='col2' column-type='@p'] [%column name='col3' column-type='@ud']] drop-columns=~ add-foreign-keys=~ drop-foreign-keys=~]
@ -157,7 +157,7 @@
!> ~[expected]
!> (parse:parse(current-database 'db1') "ALTER TABLE ns.mytable DROP FOREIGN KEY (fk1)")
::
:: fail when table name not a term
:: fail when table name not a term
++ test-fail-alter-table-10
%- expect-fail
|. (parse:parse(current-database 'db1') "ALTER TABLE ns.myTable DROP FOREIGN KEY (fk1)")
@ -331,7 +331,7 @@
=/ urql "create table my-table (col1 @t,col2 @p,col3 @ud) primary key (col1) foreign key fk (col2 desc) reFerences fk-table (col20), fk2 (col1, col2 desc) reFerences fk-table2 (col19, col20)"
%+ expect-eq
!> ~[expected]
!> (parse:parse(current-database 'db1') urql)
!> (parse:parse(current-database 'db1') urql)
::
:: fail when database qualifier on foreign key table db.ns.fk-table
++ test-fail-create-table-9
@ -929,14 +929,14 @@
++ bar [[%qualified-column [%qualified-object ~zod 'UNKNOWN' 'COLUMN-OR-CTE' 'bar'] 'bar' ~] ~ ~]
++ t2-bar [[%qualified-column [%qualified-object ~zod 'UNKNOWN' 'COLUMN' 'T2'] 'bar' ~] ~ ~]
++ foobar [[%qualified-column [%qualified-object ~zod 'UNKNOWN' 'COLUMN-OR-CTE' 'foobar'] 'foobar' ~] ~ ~]
++ a1-adoption-email [[%qualified-column [%qualified-object 0 'UNKNOWN' 'COLUMN' 'A1'] 'adoption-email' 0] 0 0]
++ a2-adoption-email [[%qualified-column [%qualified-object 0 'UNKNOWN' 'COLUMN' 'A2'] 'adoption-email' 0] 0 0]
++ a1-adoption-date [[%qualified-column [%qualified-object 0 'UNKNOWN' 'COLUMN' 'A1'] 'adoption-date' 0] 0 0]
++ a2-adoption-date [[%qualified-column [%qualified-object 0 'UNKNOWN' 'COLUMN' 'A2'] 'adoption-date' 0] 0 0]
++ a1-name [[%qualified-column [%qualified-object 0 'UNKNOWN' 'COLUMN' 'A1'] 'name' 0] 0 0]
++ a2-name [[%qualified-column [%qualified-object 0 'UNKNOWN' 'COLUMN' 'A2'] 'name' 0] 0 0]
++ a1-species [[%qualified-column [%qualified-object 0 'UNKNOWN' 'COLUMN' 'A1'] 'species' 0] 0 0]
++ a2-species [[%qualified-column [%qualified-object 0 'UNKNOWN' 'COLUMN' 'A2'] 'species' 0] 0 0]
++ a1-adoption-email [[%qualified-column [%qualified-object 0 'UNKNOWN' 'COLUMN' 'A1'] 'adoption-email' 0] 0 0]
++ a2-adoption-email [[%qualified-column [%qualified-object 0 'UNKNOWN' 'COLUMN' 'A2'] 'adoption-email' 0] 0 0]
++ a1-adoption-date [[%qualified-column [%qualified-object 0 'UNKNOWN' 'COLUMN' 'A1'] 'adoption-date' 0] 0 0]
++ a2-adoption-date [[%qualified-column [%qualified-object 0 'UNKNOWN' 'COLUMN' 'A2'] 'adoption-date' 0] 0 0]
++ a1-name [[%qualified-column [%qualified-object 0 'UNKNOWN' 'COLUMN' 'A1'] 'name' 0] 0 0]
++ a2-name [[%qualified-column [%qualified-object 0 'UNKNOWN' 'COLUMN' 'A2'] 'name' 0] 0 0]
++ a1-species [[%qualified-column [%qualified-object 0 'UNKNOWN' 'COLUMN' 'A1'] 'species' 0] 0 0]
++ a2-species [[%qualified-column [%qualified-object 0 'UNKNOWN' 'COLUMN' 'A2'] 'species' 0] 0 0]
++ value-literal-list [[%value-literal-list %ud '3;2;1'] ~ ~]
++ aggregate-count-foo [%aggregate %count %qualified-column [%qualified-object 0 'UNKNOWN' 'COLUMN-OR-CTE' %foo] %foo 0]
++ literal-10 [[%ud 10] 0 0]
@ -966,61 +966,14 @@
++ or3 [%and [%eq foo3 foo4] [%eq foo5 foo6]]
++ big-or [%or [%or [%or and-t1f-gt-f2--t2b-in-l or2] or3] [%eq foo4 foo5]]
++ big-and [%and and-fb-gt-f--fb-lt-b big-or]
++ a-a-l-a-o-l-a-a-r-o-r-a-l-o-r-a
++ a-a-l-a-o-l-a-a-r-o-r-a-l-o-r-a
[%and big-and [%eq foo6 foo7]]
++ first-or [%or [%gt foobar foo] [%lt foobar bar]]
++ last-or [%or t1-foo3-lt-any-list [%and t1-foo2-eq-zod foo-eq-1]]
++ first-and [%and first-or t1-foo-gt-foo2]
++ second-and [%and first-and t2-bar-in-list]
++ king-and [%and [second-and] last-or]
::
:: test binary operators, varying spacing
++ test-predicate-01
%+ expect-eq
!> [%eq t1-foo t2-bar]
!> (wonk (parse-predicate:parse [[1 1] "T1.foo = T2.bar"]))
++ test-predicate-02
%+ expect-eq
!> [%neq foo bar]
!> (wonk (parse-predicate:parse [[1 1] "foo<>bar"]))
++ test-predicate-03
%+ expect-eq
!> [%neq foo bar]
!> (wonk (parse-predicate:parse [[1 1] "foo!= bar"]))
++ test-predicate-04
%+ expect-eq
!> [%gt foo bar]
!> (wonk (parse-predicate:parse [[1 1] " foo >bar"]))
++ test-predicate-05
%+ expect-eq
!> [%lt foo bar]
!> (wonk (parse-predicate:parse [[1 1] " foo <bar"]))
++ test-predicate-06
%+ expect-eq
!> [%gte foo bar]
!> (wonk (parse-predicate:parse [[1 1] " foo>= bar"]))
++ test-predicate-07
%+ expect-eq
!> [%gte foo bar]
!> (wonk (parse-predicate:parse [[1 1] " foo!< bar"]))
++ test-predicate-08
%+ expect-eq
!> [%lte foo bar]
!> (wonk (parse-predicate:parse [[1 1] " foo <= bar"]))
++ test-predicate-09
%+ expect-eq
!> [%lte foo bar]
!> (wonk (parse-predicate:parse [[1 1] " foo !> bar"]))
::
:: remaining simple predicates, varying spacing and keywork casing
++ test-predicate-10
%+ expect-eq
!> [%not [%between foobar-gte-foo foobar-lte-bar] ~]
!> (wonk (parse-predicate:parse [[1 1] " foobar Not Between foo And bar"]))
++ test-predicate-11
%+ expect-eq
!> [%not [%between foobar-gte-foo foobar-lte-bar] ~]
!> (wonk (parse-predicate:parse [[1 1] " foobar Not Between foo bar"]))
++ king-and [%and [second-and] last-or]
++ test-predicate-12
%+ expect-eq
@ -1085,7 +1038,7 @@
%+ expect-eq
!> and-and-or
!> (wonk (parse-predicate:parse [[1 1] predicate]))
++ test-predicate-26
++ test-predicate-26
=/ predicate "foobar >=foo And foobar<=bar ".
" and T1.foo2 = ~zod ".
" or ".
@ -1138,7 +1091,7 @@
" AND (T1.foo3< any (1,2,3) OR T1.foo2=~zod AND foo=1 )) "
%+ expect-eq
!> king-and
!> (wonk (parse-predicate:parse [[1 1] predicate]))
!> (wonk (parse-predicate:parse [[1 1] predicate]))
::
:: aggregate inequality
++ test-predicate-31
@ -1343,7 +1296,7 @@
!> [%select %top 10 [%all ~]]
!> (wonk (parse-select:parse [[1 1] select]))
::
:: star select, trailing whitespace
:: star select, trailing whitespace
++ test-select-08
=/ select "select * "
%+ expect-eq
@ -1357,7 +1310,7 @@
!> [%select [%all ~]]
!> (wonk (parse-select:parse [[1 1] select]))
::
:: star select bottom, distinct, trailing whitespace
:: star select bottom, distinct, trailing whitespace
++ test-select-10
=/ select "select bottom 10 distinct * "
%+ expect-eq
@ -1371,7 +1324,7 @@
!> [%select %bottom 10 %distinct [%all ~]]
!> (wonk (parse-select:parse [[1 1] select]))
::
:: star select bottom, trailing whitespace
:: star select bottom, trailing whitespace
++ test-select-12
=/ select "select bottom 10 * "
%+ expect-eq
@ -1385,7 +1338,7 @@
!> [%select %bottom 10 [%all ~]]
!> (wonk (parse-select:parse [[1 1] select]))
::
:: star select distinct, trailing whitespace
:: star select distinct, trailing whitespace
++ test-select-14
=/ select "select distinct * "
%+ expect-eq
@ -1526,7 +1479,7 @@
!> [%select [mixed-all]]
!> (wonk (parse-select:parse [[1 1] select]))
::
:: mixed aggregates
:: mixed aggregates
++ test-select-36
=/ select "select foo , COUNT(foo) as CountFoo, cOUNT( bar) ,sum(bar ) , sum( foobar ) as foobar "
%+ expect-eq

View File

@ -1,22 +1,28 @@
/- ast
/+ parse, *test
|%
:: predicate
::
:: re-used components
++ foo [[%qualified-column [%qualified-object ~zod 'UNKNOWN' 'COLUMN-OR-CTE' 'foo'] 'foo' ~] ~ ~]
++ t1-foo [[%qualified-column [%qualified-object ~zod 'UNKNOWN' 'COLUMN' 'T1'] 'foo' ~] ~ ~]
++ foo2 [[%qualified-column [%qualified-object ~zod 'UNKNOWN' 'COLUMN-OR-CTE' 'foo2'] 'foo2' ~] ~ ~]
++ t1-foo2 [[%qualified-column [%qualified-object ~zod 'UNKNOWN' 'COLUMN' 'T1'] 'foo2' ~] ~ ~]
++ foo3 [[%qualified-column [%qualified-object ~zod 'UNKNOWN' 'COLUMN-OR-CTE' 'foo3'] 'foo3' ~] ~ ~]
++ t1-foo3 [[%qualified-column [%qualified-object ~zod 'UNKNOWN' 'COLUMN' 'T1'] 'foo3' ~] ~ ~]
++ foo4 [[%qualified-column [%qualified-object ~zod 'UNKNOWN' 'COLUMN-OR-CTE' 'foo4'] 'foo4' ~] ~ ~]
++ foo5 [[%qualified-column [%qualified-object ~zod 'UNKNOWN' 'COLUMN-OR-CTE' 'foo5'] 'foo5' ~] ~ ~]
++ foo6 [[%qualified-column [%qualified-object ~zod 'UNKNOWN' 'COLUMN-OR-CTE' 'foo6'] 'foo6' ~] ~ ~]
++ foo7 [[%qualified-column [%qualified-object ~zod 'UNKNOWN' 'COLUMN-OR-CTE' 'foo7'] 'foo7' ~] ~ ~]
++ bar [[%qualified-column [%qualified-object ~zod 'UNKNOWN' 'COLUMN-OR-CTE' 'bar'] 'bar' ~] ~ ~]
++ t2-bar [[%qualified-column [%qualified-object ~zod 'UNKNOWN' 'COLUMN' 'T2'] 'bar' ~] ~ ~]
++ foobar [[%qualified-column [%qualified-object ~zod 'UNKNOWN' 'COLUMN-OR-CTE' 'foobar'] 'foobar' ~] ~ ~]
++ foo
`(tree predicate-component:ast)`[[%qualified-column [%qualified-object ~ 'UNKNOWN' 'COLUMN-OR-CTE' 'foo'] 'foo' ~] ~ ~]
++ t1-foo
`(tree predicate-component:ast)`[[%qualified-column [%qualified-object ~ 'UNKNOWN' 'COLUMN' 'T1'] 'foo' ~] ~ ~]
++ foo2 [[%qualified-column [%qualified-object ~ 'UNKNOWN' 'COLUMN-OR-CTE' 'foo2'] 'foo2' ~] ~ ~]
++ t1-foo2 [[%qualified-column [%qualified-object ~ 'UNKNOWN' 'COLUMN' 'T1'] 'foo2' ~] ~ ~]
++ foo3 [[%qualified-column [%qualified-object ~ 'UNKNOWN' 'COLUMN-OR-CTE' 'foo3'] 'foo3' ~] ~ ~]
++ t1-foo3 [[%qualified-column [%qualified-object ~ 'UNKNOWN' 'COLUMN' 'T1'] 'foo3' ~] ~ ~]
++ foo4 [[%qualified-column [%qualified-object ~ 'UNKNOWN' 'COLUMN-OR-CTE' 'foo4'] 'foo4' ~] ~ ~]
++ foo5 [[%qualified-column [%qualified-object ~ 'UNKNOWN' 'COLUMN-OR-CTE' 'foo5'] 'foo5' ~] ~ ~]
++ foo6 [[%qualified-column [%qualified-object ~ 'UNKNOWN' 'COLUMN-OR-CTE' 'foo6'] 'foo6' ~] ~ ~]
++ foo7 [[%qualified-column [%qualified-object ~ 'UNKNOWN' 'COLUMN-OR-CTE' 'foo7'] 'foo7' ~] ~ ~]
++ bar
`(tree predicate-component:ast)`[[%qualified-column [%qualified-object ~ 'UNKNOWN' 'COLUMN-OR-CTE' 'bar'] 'bar' ~] ~ ~]
++ t2-bar
`(tree predicate-component:ast)`[[%qualified-column [%qualified-object ~ 'UNKNOWN' 'COLUMN' 'T2'] 'bar' ~] ~ ~]
++ foobar [[%qualified-column [%qualified-object ~ 'UNKNOWN' 'COLUMN-OR-CTE' 'foobar'] 'foobar' ~] ~ ~]
++ a1-adoption-email [[%qualified-column [%qualified-object 0 'UNKNOWN' 'COLUMN' 'A1'] 'adoption-email' 0] 0 0]
++ a2-adoption-email [[%qualified-column [%qualified-object 0 'UNKNOWN' 'COLUMN' 'A2'] 'adoption-email' 0] 0 0]
@ -29,15 +35,13 @@
++ a1-species [[%qualified-column [%qualified-object 0 'UNKNOWN' 'COLUMN' 'A1'] 'species' 0] 0 0]
++ a2-species [[%qualified-column [%qualified-object 0 'UNKNOWN' 'COLUMN' 'A2'] 'species' 0] 0 0]
++ value-literal-list [[%value-literal-list %ud '3;2;1'] ~ ~]
++ aggregate-count-foo [%aggregate %count %qualified-column [%qualified-object 0 'UNKNOWN' 'COLUMN-OR-CTE' %foo] %foo 0]
++ literal-10 [[%ud 10] 0 0]
::
:: re-used simple predicates
++ foobar-gte-foo [%gte foobar foo]
++ foobar-lte-bar [%lte foobar bar]
++ foobar-gte-foo `(tree predicate-component:ast)`[%gte foobar foo]
++ foobar-lte-bar `(tree predicate-component:ast)`[%lte foobar bar]
++ foo-eq-1 [%eq foo [[%ud 1] ~ ~]]
++ t1-foo-gt-foo2 [%gt t1-foo foo2]
++ t2-bar-in-list [%in t2-bar value-literal-list]
@ -70,22 +74,91 @@
::
:: test binary operators, varying spacing
++ test-predicate-01
=/ query "FROM adoptions AS T1 JOIN adoptions AS T2 ON T1.foo = T2.bar SELECT *"
=/ pred=(tree predicate-component:ast) [%eq t1-foo t2-bar]
=/ expected=simple-query:ast [%simple-query [~ [%priori [~ [%from object=[%query-object object=[%qualified-object ship=~ database='db1' namespace='dbo' name='adoptions'] alias=[~ 'T1']] joins=~[[%joined-object join=%join object=[%query-object object=[%qualified-object ship=~ database='db1' namespace='dbo' name='adoptions'] alias=[~ 'T2']] predicate=`pred]]]] ~ ~]] [%select top=~ bottom=~ distinct=%.n columns=~[%all]] ~]
%+ expect-eq
!> [%eq t1-foo t2-bar]
!> (wonk (parse-predicate:parse [[1 1] "T1.foo = T2.bar"]))
!> ~[expected]
!> (parse:parse(current-database 'db1') query)
++ test-predicate-02
=/ query "FROM adoptions AS T1 JOIN adoptions AS T2 ON foo<>bar SELECT *"
=/ pred=(tree predicate-component:ast) [%neq foo bar]
=/ expected=simple-query:ast [%simple-query [~ [%priori [~ [%from object=[%query-object object=[%qualified-object ship=~ database='db1' namespace='dbo' name='adoptions'] alias=[~ 'T1']] joins=~[[%joined-object join=%join object=[%query-object object=[%qualified-object ship=~ database='db1' namespace='dbo' name='adoptions'] alias=[~ 'T2']] predicate=`pred]]]] ~ ~]] [%select top=~ bottom=~ distinct=%.n columns=~[%all]] ~]
%+ expect-eq
!> ~[expected]
!> (parse:parse(current-database 'db1') query)
++ test-predicate-03
=/ query "FROM adoptions AS T1 JOIN adoptions AS T2 ON foo!= bar SELECT *"
=/ pred=(tree predicate-component:ast) [%neq foo bar]
=/ expected=simple-query:ast [%simple-query [~ [%priori [~ [%from object=[%query-object object=[%qualified-object ship=~ database='db1' namespace='dbo' name='adoptions'] alias=[~ 'T1']] joins=~[[%joined-object join=%join object=[%query-object object=[%qualified-object ship=~ database='db1' namespace='dbo' name='adoptions'] alias=[~ 'T2']] predicate=`pred]]]] ~ ~]] [%select top=~ bottom=~ distinct=%.n columns=~[%all]] ~]
%+ expect-eq
!> ~[expected]
!> (parse:parse(current-database 'db1') query)
++ test-predicate-04
=/ query "FROM adoptions AS T1 JOIN adoptions AS T2 ON foo >bar SELECT *"
=/ pred=(tree predicate-component:ast) [%gt foo bar]
=/ expected=simple-query:ast [%simple-query [~ [%priori [~ [%from object=[%query-object object=[%qualified-object ship=~ database='db1' namespace='dbo' name='adoptions'] alias=[~ 'T1']] joins=~[[%joined-object join=%join object=[%query-object object=[%qualified-object ship=~ database='db1' namespace='dbo' name='adoptions'] alias=[~ 'T2']] predicate=`pred]]]] ~ ~]] [%select top=~ bottom=~ distinct=%.n columns=~[%all]] ~]
%+ expect-eq
!> ~[expected]
!> (parse:parse(current-database 'db1') query)
++ test-predicate-05
=/ query "FROM adoptions AS T1 JOIN adoptions AS T2 ON foo <bar SELECT *"
=/ pred=(tree predicate-component:ast) [%lt foo bar]
=/ expected=simple-query:ast [%simple-query [~ [%priori [~ [%from object=[%query-object object=[%qualified-object ship=~ database='db1' namespace='dbo' name='adoptions'] alias=[~ 'T1']] joins=~[[%joined-object join=%join object=[%query-object object=[%qualified-object ship=~ database='db1' namespace='dbo' name='adoptions'] alias=[~ 'T2']] predicate=`pred]]]] ~ ~]] [%select top=~ bottom=~ distinct=%.n columns=~[%all]] ~]
%+ expect-eq
!> ~[expected]
!> (parse:parse(current-database 'db1') query)
++ test-predicate-06
=/ query "FROM adoptions AS T1 JOIN adoptions AS T2 ON foo>= bar SELECT *"
=/ pred=(tree predicate-component:ast) [%gte foo bar]
=/ expected=simple-query:ast [%simple-query [~ [%priori [~ [%from object=[%query-object object=[%qualified-object ship=~ database='db1' namespace='dbo' name='adoptions'] alias=[~ 'T1']] joins=~[[%joined-object join=%join object=[%query-object object=[%qualified-object ship=~ database='db1' namespace='dbo' name='adoptions'] alias=[~ 'T2']] predicate=`pred]]]] ~ ~]] [%select top=~ bottom=~ distinct=%.n columns=~[%all]] ~]
%+ expect-eq
!> ~[expected]
!> (parse:parse(current-database 'db1') query)
++ test-predicate-07
=/ query "FROM adoptions AS T1 JOIN adoptions AS T2 ON foo!< bar SELECT *"
=/ pred=(tree predicate-component:ast) [%gte foo bar]
=/ expected=simple-query:ast [%simple-query [~ [%priori [~ [%from object=[%query-object object=[%qualified-object ship=~ database='db1' namespace='dbo' name='adoptions'] alias=[~ 'T1']] joins=~[[%joined-object join=%join object=[%query-object object=[%qualified-object ship=~ database='db1' namespace='dbo' name='adoptions'] alias=[~ 'T2']] predicate=`pred]]]] ~ ~]] [%select top=~ bottom=~ distinct=%.n columns=~[%all]] ~]
%+ expect-eq
!> ~[expected]
!> (parse:parse(current-database 'db1') query)
++ test-predicate-08
=/ query "FROM adoptions AS T1 JOIN adoptions AS T2 ON foo <= bar SELECT *"
=/ pred=(tree predicate-component:ast) [%lte foo bar]
=/ expected=simple-query:ast [%simple-query [~ [%priori [~ [%from object=[%query-object object=[%qualified-object ship=~ database='db1' namespace='dbo' name='adoptions'] alias=[~ 'T1']] joins=~[[%joined-object join=%join object=[%query-object object=[%qualified-object ship=~ database='db1' namespace='dbo' name='adoptions'] alias=[~ 'T2']] predicate=`pred]]]] ~ ~]] [%select top=~ bottom=~ distinct=%.n columns=~[%all]] ~]
%+ expect-eq
!> ~[expected]
!> (parse:parse(current-database 'db1') query)
++ test-predicate-09
=/ query "FROM adoptions AS T1 JOIN adoptions AS T2 ON foo !> bar SELECT *"
=/ pred=(tree predicate-component:ast) [%lte foo bar]
=/ expected=simple-query:ast [%simple-query [~ [%priori [~ [%from object=[%query-object object=[%qualified-object ship=~ database='db1' namespace='dbo' name='adoptions'] alias=[~ 'T1']] joins=~[[%joined-object join=%join object=[%query-object object=[%qualified-object ship=~ database='db1' namespace='dbo' name='adoptions'] alias=[~ 'T2']] predicate=`pred]]]] ~ ~]] [%select top=~ bottom=~ distinct=%.n columns=~[%all]] ~]
%+ expect-eq
!> ~[expected]
!> (parse:parse(current-database 'db1') query)
::
:: complext predicate
++ test-predicate-34
=/ predicate " A1.adoption-email = A2.adoption-email ".
" AND A1.adoption-date = A2.adoption-date ".
" AND foo = bar ".
" AND ((A1.name = A2.name AND A1.species > A2.species) ".
" OR ".
" (A1.name > A2.name AND A1.species = A2.species) ".
" OR ".
" (A1.name > A2.name AND A1.species > A2.species) ".
" ) "
:: remaining simple predicates, varying spacing and keywork casing
++ test-predicate-10
=/ query "FROM adoptions AS T1 JOIN adoptions AS T2 ON T1.foo = T2.bar ".
" WHERE foobar Not Between foo And bar ".
" SELECT *"
=/ joinpred=(tree predicate-component:ast) [%eq t1-foo t2-bar]
=/ pred=(tree predicate-component:ast) [%not [%between foobar-gte-foo foobar-lte-bar] ~]
=/ expected=simple-query:ast
[%simple-query [~ [%priori [~ [%from object=[%query-object object=[%qualified-object ship=~ database='db1' namespace='dbo' name='adoptions'] alias=[~ 'T1']] joins=~[[%joined-object join=%join object=[%query-object object=[%qualified-object ship=~ database='db1' namespace='dbo' name='adoptions'] alias=[~ 'T2']] predicate=`joinpred]]]] ~ `pred]] [%select top=~ bottom=~ distinct=%.n columns=~[%all]] ~]
%+ expect-eq
!> [%and [%and [%and [%eq a1-adoption-email a2-adoption-email] [%eq a1-adoption-date a2-adoption-date]] [%eq foo bar]] [%or [%or [%and [%eq a1-name a2-name] [%gt a1-species a2-species]] [%and [%gt a1-name a2-name] [%eq a1-species a2-species]]] [%and [%gt a1-name a2-name] [%gt a1-species a2-species]]]]
!> (wonk (parse-predicate:parse [[1 1] predicate]))
!> ~[expected]
!> (parse:parse(current-database 'db1') query)
++ test-predicate-11
=/ query "FROM adoptions AS T1 JOIN adoptions AS T2 ON T1.foo = T2.bar ".
" WHERE foobar Not Between foo bar ".
" SELECT *"
=/ joinpred=(tree predicate-component:ast) [%eq t1-foo t2-bar]
=/ pred=(tree predicate-component:ast) [%not [%between foobar-gte-foo foobar-lte-bar] ~]
=/ expected=simple-query:ast
[%simple-query [~ [%priori [~ [%from object=[%query-object object=[%qualified-object ship=~ database='db1' namespace='dbo' name='adoptions'] alias=[~ 'T1']] joins=~[[%joined-object join=%join object=[%query-object object=[%qualified-object ship=~ database='db1' namespace='dbo' name='adoptions'] alias=[~ 'T2']] predicate=`joinpred]]]] ~ `pred]] [%select top=~ bottom=~ distinct=%.n columns=~[%all]] ~]
%+ expect-eq
!> ~[expected]
!> (parse:parse(current-database 'db1') query)
--