more predicate tests, more clean-up

This commit is contained in:
jackfoxy 2023-01-19 13:36:03 -08:00
parent ae1d4382b2
commit b49267d458
4 changed files with 124 additions and 98 deletions

View File

@ -1,2 +1,5 @@
# urQL # urQL
RDBMS for Urbit Scripting language grammar and parser for an Urbit RDBMS.
Pull Requests are appreciated, but you should start a discussion before you proceed. If green-lit then open an issue.

View File

@ -50,7 +50,12 @@ There are no subqueries.
JOINs and/or CTEs handle all such use cases and emphasize composability. JOINs and/or CTEs handle all such use cases and emphasize composability.
CTEs can be referenced for certain use cases in predicates. CTEs can be referenced for certain use cases in predicates.
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. The result of a UNION set operation is as according to _union_ in set theory (no duplicate rows returned).
Use COMBINE to include duplicates.
Relational division is supported with a DIVIDED BY operator.
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. Views cannot be defined on foreign databases.
This document has placeholders for Stored Procedures and Triggers, which have yet to be defined. We anticipate these will be points for integration with hoon. This document has placeholders for Stored Procedures and Triggers, which have yet to be defined. We anticipate these will be points for integration with hoon.

View File

@ -493,8 +493,6 @@
parse-join-type parse-join-type
parse-query-object parse-query-object
;~(pfix whitespace ;~(pfix (jester 'on') parse-predicate)) ;~(pfix whitespace ;~(pfix (jester 'on') parse-predicate))
::;~(pfix whitespace ;~(pfix (jester 'on') ;~(less predicate-stop prn)))
::(easy ~)
== ==
++ parse-object-and-joins ~+ ;~ plug ++ parse-object-and-joins ~+ ;~ plug
parse-query-object parse-query-object
@ -551,11 +549,12 @@
(cold %lt (just '<')) (cold %lt (just '<'))
(cold %and ;~(plug (jester 'and') whitespace)) (cold %and ;~(plug (jester 'and') whitespace))
(cold %or ;~(plug (jester 'or') whitespace)) (cold %or ;~(plug (jester 'or') whitespace))
:: to do %distinct %not-distinct:
:: (cold %distinct ;~(plug (jester 'is') whitespace (jester 'distinct') whitespace (jester 'from'))) :: (cold %distinct ;~(plug (jester 'is') whitespace (jester 'distinct') whitespace (jester 'from')))
:: (cold %not-distinct ;~(plug (jester 'is') whitespace (jester 'not') whitespace (jester 'distinct') whitespace (jester 'from'))) :: (cold %not-distinct ;~(plug (jester 'is') whitespace (jester 'not') whitespace (jester 'distinct') whitespace (jester 'from')))
:: ternary operator :: ternary operator
(cold %between ;~(plug (jester 'between') whitespace)) (cold %between ;~(plug (jester 'between') whitespace))
:: nesting directors :: nesting
(cold %pal pal) (cold %pal pal)
(cold %par par) (cold %par par)
== ==
@ -646,9 +645,7 @@
^- predicate:ast ^- predicate:ast
=/ working-tree=predicate:ast ~ =/ working-tree=predicate:ast ~
=/ tree-stack=(list predicate:ast) ~ =/ tree-stack=(list predicate:ast) ~
~| "produce-predicate parsed: {<parsed>}"
|- |-
~| "-.parsed: {<-.parsed>}"
?: =((lent parsed) 0) ?: =((lent parsed) 0)
|- |-
?~ tree-stack ?~ tree-stack
@ -661,6 +658,7 @@
== ==
?- -.parsed ?- -.parsed
%pal :: push working predicate onto the stack %pal :: push working predicate onto the stack
?~ working-tree $(parsed +.parsed)
%= $ %= $
tree-stack [working-tree tree-stack] tree-stack [working-tree tree-stack]
working-tree ~ working-tree ~
@ -695,10 +693,6 @@
?~ l.working-tree ~|("binary-operator, left tree empty" !!) ?~ l.working-tree ~|("binary-operator, left tree empty" !!)
?~ r.working-tree ~|("binary-operator, right tree empty" !!) ?~ r.working-tree ~|("binary-operator, right tree empty" !!)
~|("binary-operator can't get here {<working-tree>}" !!) ~|("binary-operator can't get here {<working-tree>}" !!)
:: %= $
:: working-tree [-.parsed working-tree ~]
:: parsed +.parsed
:: ==
ternary-operator:ast ternary-operator:ast
?~ working-tree !! ?~ working-tree !!
?~ l.working-tree ~|("ternary-operator, left tree empty" !!) ?~ l.working-tree ~|("ternary-operator, left tree empty" !!)
@ -720,13 +714,19 @@
?~ working-tree ~|("operator {<-.parsed>} can only follow equality or inequality operator" !!) ?~ working-tree ~|("operator {<-.parsed>} can only follow equality or inequality operator" !!)
?~ r.working-tree ?~ r.working-tree
?: ?&(?=(binary-operator:ast n.working-tree) ?!(=(%in n.working-tree))) ?: ?&(?=(binary-operator:ast n.working-tree) ?!(=(%in n.working-tree)))
?> ?=(qualified-column:ast +<.parsed) :: to do: this must resolve to a CTE ?: ?=(value-literal-list:ast +<.parsed)
%= $ %= $
working-tree [-.working-tree +<.working-tree [-.parsed [+<.parsed ~ ~] ~]] working-tree [-.working-tree +<.working-tree [-.parsed [+<.parsed ~ ~] ~]]
parsed +>.parsed parsed +>.parsed
== ==
~|("operator {<-.parsed>} can only follow equality or inequality operator" !!) ?: ?=(qualified-column:ast +<.parsed) :: to do: this must resolve to a CTE or list
~|("all-any-operator can't get here" !!) %= $
working-tree [-.working-tree +<.working-tree [-.parsed [+<.parsed ~ ~] ~]]
parsed +>.parsed
==
~|("all-any-operator {<-.parsed>} must target CTE or literal list {<+<.parsed>}" !!)
~|("all-any-operator {<-.parsed>} can only follow equality or inequality operator" !!)
~|("all-any-operator {<-.parsed>} can't get here, working-tree {<working-tree>}" !!)
qualified-column:ast qualified-column:ast
?~ working-tree ?~ working-tree
?: ?=(binary-operator:ast +<.parsed) ?: ?=(binary-operator:ast +<.parsed)
@ -752,7 +752,7 @@
working-tree [%not (produce-predicate ~[-.parsed %in +>+<.parsed]) ~] working-tree [%not (produce-predicate ~[-.parsed %in +>+<.parsed]) ~]
parsed +>+>.parsed parsed +>+>.parsed
== ==
!! ~|("unary-operator {<+<.parsed>} can't get here after qualified-column, working-tree {<working-tree>}" !!)
?: =(%between +<.parsed) ?: =(%between +<.parsed)
?: =(%and +>+<.parsed) ?: =(%and +>+<.parsed)
%= $ %= $
@ -807,7 +807,7 @@
working-tree [%not (produce-predicate ~[-.parsed %in +>+<.parsed]) ~] working-tree [%not (produce-predicate ~[-.parsed %in +>+<.parsed]) ~]
parsed +>+>.parsed parsed +>+>.parsed
== ==
!! ~|("unary-operator {<+<.parsed>} can't get here after value-literal, working-tree {<working-tree>}" !!)
?: =(%between +<.parsed) ?: =(%between +<.parsed)
?: =(%and +>+<.parsed) ?: =(%and +>+<.parsed)
%= $ %= $
@ -820,7 +820,7 @@
[%between (produce-predicate ~[-.parsed %gte +>-.parsed]) (produce-predicate ~[-.parsed %lte +>+<.parsed])] [%between (produce-predicate ~[-.parsed %gte +>-.parsed]) (produce-predicate ~[-.parsed %lte +>+<.parsed])]
parsed +>+>.parsed parsed +>+>.parsed
== ==
!! ~|("value-literal can't get here after {<+<.parsed>} , working-tree {<working-tree>}" !!)
?~ l.working-tree ?~ l.working-tree
%= $ %= $
working-tree [-.working-tree [-.parsed ~ ~] ~] working-tree [-.working-tree [-.parsed ~ ~] ~]
@ -1076,10 +1076,8 @@
(from:ast %from query-object (flop joined-objects)) (from:ast %from query-object (flop joined-objects))
~|("cross join must be only join in query" !!) :: to do, not sure this is required, investigate later ~|("cross join must be only join in query" !!) :: to do, not sure this is required, investigate later
(from:ast %from query-object (flop joined-objects)) (from:ast %from query-object (flop joined-objects))
?> ?=(join-type:ast -<.raw-joined-objects) ?> ?=(join-type:ast -<.raw-joined-objects)
?> ?=(query-object:ast ->-.raw-joined-objects) ?> ?=(query-object:ast ->-.raw-joined-objects)
?: ?=(%cross-join -<.raw-joined-objects) ?: ?=(%cross-join -<.raw-joined-objects)
%= $ %= $
joined-objects joined-objects
@ -1502,13 +1500,13 @@
script-position next-cursor script-position next-cursor
commands [`command-ast`(drop-database:ast %drop-database parsed %.n) commands] commands [`command-ast`(drop-database:ast %drop-database parsed %.n) commands]
== ==
~| "Cannot parse drop-database {<parsed>}"
?: ?=([@ @] parsed) :: force name ?: ?=([@ @] parsed) :: force name
%= $ %= $
script q.q.u.+3.q:drop-database-nail script q.q.u.+3.q:drop-database-nail
script-position next-cursor script-position next-cursor
commands [`command-ast`(drop-database:ast %drop-database +.parsed %.y) commands] commands [`command-ast`(drop-database:ast %drop-database +.parsed %.y) commands]
== ==
::~|("Cannot parse drop-database {<parsed>}" !!)
!! !!
%drop-index %drop-index
=/ drop-index-nail (parse-drop-index [[1 1] q.q.command-nail]) =/ drop-index-nail (parse-drop-index [[1 1] q.q.command-nail])
@ -1543,13 +1541,13 @@
script-position next-cursor script-position next-cursor
commands [`command-ast`(drop-namespace:ast %drop-namespace -.parsed +.parsed %.n) commands] commands [`command-ast`(drop-namespace:ast %drop-namespace -.parsed +.parsed %.n) commands]
== ==
~| "Cannot parse drop-namespace {<parsed>}"
?: ?=([* [@ @]] parsed) :: force db.name ?: ?=([* [@ @]] parsed) :: force db.name
%= $ %= $
script q.q.u.+3.q:drop-namespace-nail script q.q.u.+3.q:drop-namespace-nail
script-position next-cursor script-position next-cursor
commands [`command-ast`(drop-namespace:ast %drop-namespace +<.parsed +>.parsed %.y) commands] commands [`command-ast`(drop-namespace:ast %drop-namespace +<.parsed +>.parsed %.y) commands]
== ==
::~|("Cannot parse drop-namespace {<parsed>}" !!)
!! !!
%drop-table %drop-table
=/ drop-table-nail (drop-table-or-view [[1 1] q.q.command-nail]) =/ drop-table-nail (drop-table-or-view [[1 1] q.q.command-nail])
@ -1652,8 +1650,8 @@
=/ parsed (wonk query-nail) =/ parsed (wonk query-nail)
=/ next-cursor =/ next-cursor
(get-next-cursor [script-position +<.command-nail p.q.u.+3:q.+3:query-nail]) (get-next-cursor [script-position +<.command-nail p.q.u.+3:q.+3:query-nail])
~| "parsed: {<parsed>}" :: ~| "parsed: {<parsed>}"
~| "remainder: {<q.q.u.+3:q.+3.query-nail>}" :: ~| "remainder: {<q.q.u.+3:q.+3.query-nail>}"
%= $ %= $
script q.q.u.+3.q:query-nail script q.q.u.+3.q:query-nail
script-position next-cursor script-position next-cursor
@ -1661,7 +1659,6 @@
[`command-ast`(produce-simple-query parsed) commands] [`command-ast`(produce-simple-query parsed) commands]
== ==
%revoke %revoke
=/ revoke-nail (parse-revoke [[1 1] q.q.command-nail]) =/ revoke-nail (parse-revoke [[1 1] q.q.command-nail])
=/ parsed (wonk revoke-nail) =/ parsed (wonk revoke-nail)
=/ next-cursor =/ next-cursor

View File

@ -24,16 +24,16 @@
++ foobar [[%qualified-column [%qualified-object ~ 'UNKNOWN' 'COLUMN-OR-CTE' 'foobar'] 'foobar' ~] ~ ~] ++ 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] ++ a1-adoption-email [[%qualified-column [%qualified-object ~ 'UNKNOWN' 'COLUMN' 'A1'] 'adoption-email' ~] ~ ~]
++ a2-adoption-email [[%qualified-column [%qualified-object 0 'UNKNOWN' 'COLUMN' 'A2'] 'adoption-email' 0] 0 0] ++ a2-adoption-email [[%qualified-column [%qualified-object ~ 'UNKNOWN' 'COLUMN' 'A2'] 'adoption-email' ~] ~ ~]
++ a1-adoption-date [[%qualified-column [%qualified-object 0 'UNKNOWN' 'COLUMN' 'A1'] 'adoption-date' 0] 0 0] ++ a1-adoption-date [[%qualified-column [%qualified-object ~ 'UNKNOWN' 'COLUMN' 'A1'] 'adoption-date' ~] ~ ~]
++ a2-adoption-date [[%qualified-column [%qualified-object 0 'UNKNOWN' 'COLUMN' 'A2'] 'adoption-date' 0] 0 0] ++ a2-adoption-date [[%qualified-column [%qualified-object ~ 'UNKNOWN' 'COLUMN' 'A2'] 'adoption-date' ~] ~ ~]
++ a1-name [[%qualified-column [%qualified-object 0 'UNKNOWN' 'COLUMN' 'A1'] 'name' 0] 0 0] ++ a1-name [[%qualified-column [%qualified-object ~ 'UNKNOWN' 'COLUMN' 'A1'] 'name' ~] ~ ~]
++ a2-name [[%qualified-column [%qualified-object 0 'UNKNOWN' 'COLUMN' 'A2'] 'name' 0] 0 0] ++ a2-name [[%qualified-column [%qualified-object ~ 'UNKNOWN' 'COLUMN' 'A2'] 'name' ~] ~ ~]
++ a1-species [[%qualified-column [%qualified-object 0 'UNKNOWN' 'COLUMN' 'A1'] 'species' 0] 0 0] ++ a1-species [[%qualified-column [%qualified-object ~ 'UNKNOWN' 'COLUMN' 'A1'] 'species' ~] ~ ~]
++ a2-species [[%qualified-column [%qualified-object 0 'UNKNOWN' 'COLUMN' 'A2'] 'species' 0] 0 0] ++ a2-species [[%qualified-column [%qualified-object ~ 'UNKNOWN' 'COLUMN' 'A2'] 'species' ~] ~ ~]
++ value-literal-list [[%value-literal-list %ud '3;2;1'] ~ ~] ++ 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] ++ aggregate-count-foo [%aggregate %count %qualified-column [%qualified-object 0 'UNKNOWN' 'COLUMN-OR-CTE' %foo] %foo 0]
@ -341,70 +341,84 @@
:: !> ~[expected] :: !> ~[expected]
:: !> (parse:parse(current-database 'db1') query) :: !> (parse:parse(current-database 'db1') query)
++ test-predicate-27 :: expected/actual match
:: =/ predicate "foobar >=foo And foobar<=bar ". ::++ test-predicate-27
:: " and T1.foo2 = ~zod ". :: =/ query "FROM adoptions AS T1 JOIN adoptions AS T2 ON T1.foo = T2.bar ".
:: " or ". :: " WHERE foobar >=foo And foobar<=bar ".
:: " foobar>=foo ". :: " and T1.foo2 = ~zod ".
:: " AND T1.foo2=~zod ". :: " or ".
:: " OR ". :: " foobar>=foo ".
:: " foo = 1 ". :: " AND T1.foo2=~zod ".
:: " AND T1.foo3 < any (1,2,3)" :: " OR ".
:: " foo = 1 ".
:: " AND T1.foo3 < any (1,2,3) ".
:: " SELECT *"
:: =/ joinpred=(tree predicate-component:ast) [%eq t1-foo t2-bar]
:: =/ pred=(tree predicate-component:ast) and-and-or-and-or-and
:: =/ 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 :: %+ expect-eq
:: !> and-and-or-and-or-and :: !> ~[expected]
:: !> (wonk (parse-predicate:parse [[1 1] predicate])) :: !> (parse:parse(current-database 'db1') query)
=/ query "FROM adoptions AS T1 JOIN adoptions AS T2 ON T1.foo = T2.bar ".
" WHERE foobar >=foo And foobar<=bar ".
" and T1.foo2 = ~zod ".
" or ".
" foobar>=foo ".
" AND T1.foo2=~zod ".
" OR ".
" foo = 1 ".
" AND T1.foo3 < any (1,2,3) ".
" SELECT *"
=/ joinpred=(tree predicate-component:ast) [%eq t1-foo t2-bar]
=/ pred=(tree predicate-component:ast) and-and-or-and-or-and
=/ 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)
:: ::
:: simple nesting :: simple nesting
:: expected/actual match
::++ test-predicate-28 ::++ test-predicate-28
:: =/ predicate "(foobar > foo OR foobar < bar) ". :: =/ query "FROM adoptions AS T1 JOIN adoptions AS T2 ON T1.foo = T2.bar ".
:: " AND T1.foo>foo2 ". :: " WHERE (foobar > foo OR foobar < bar) ".
:: " AND T2.bar IN (1,2,3) ". :: " AND T1.foo>foo2 ".
:: " AND (T1.foo3< any (1,2,3) OR T1.foo2=~zod AND foo=1 ) " :: " AND T2.bar IN (1,2,3) ".
:: " AND (T1.foo3< any (1,2,3) OR T1.foo2=~zod AND foo=1 ) ".
:: " SELECT *"
:: =/ joinpred=(tree predicate-component:ast) [%eq t1-foo t2-bar]
:: =/ pred=(tree predicate-component:ast) king-and
:: =/ 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 :: %+ expect-eq
:: !> king-and :: !> ~[expected]
:: !> (wonk (parse-predicate:parse [[1 1] predicate])) :: !> (parse:parse(current-database 'db1') query)
:: ::
:: nesting :: nesting
:: expected/actual match
::++ test-predicate-29 ::++ test-predicate-29
:: =/ predicate "foobar > foo AND foobar < bar ". :: =/ query "FROM adoptions AS T1 JOIN adoptions AS T2 ON T1.foo = T2.bar ".
:: " AND ( T1.foo>foo2 AND T2.bar IN (1,2,3) ". :: " WHERE foobar > foo AND foobar < bar ".
:: " OR (T1.foo3< any (1,2,3) AND T1.foo2=~zod AND foo=1 ) ". :: " AND ( T1.foo>foo2 AND T2.bar IN (1,2,3) ".
:: " OR (foo3=foo4 AND foo5=foo6) ". :: " OR (T1.foo3< any (1,2,3) AND T1.foo2=~zod AND foo=1 ) ".
:: " OR foo4=foo5 ". :: " OR (foo3=foo4 AND foo5=foo6) ".
:: " ) ". :: " OR foo4=foo5 ".
:: " AND foo6=foo7" :: " ) ".
:: " AND foo6=foo7".
:: " SELECT *"
:: =/ joinpred=(tree predicate-component:ast) [%eq t1-foo t2-bar]
:: =/ pred=(tree predicate-component:ast) a-a-l-a-o-l-a-a-r-o-r-a-l-o-r-a
:: =/ 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 :: %+ expect-eq
:: !> a-a-l-a-o-l-a-a-r-o-r-a-l-o-r-a :: !> ~[expected]
:: !> (wonk (parse-predicate:parse [[1 1] predicate])) :: !> (parse:parse(current-database 'db1') query)
:: ::
:: simple nesting, superfluous () around entire predicate :: simple nesting, superfluous () around entire predicate
:: expected/actual match
::++ test-predicate-30 ::++ test-predicate-30
:: =/ predicate "((foobar > foo OR foobar < bar) ". :: =/ query "FROM adoptions AS T1 JOIN adoptions AS T2 ON T1.foo = T2.bar ".
:: " AND T1.foo>foo2 ". :: " WHERE ((foobar > foo OR foobar < bar) ".
:: " AND T2.bar IN (1,2,3) ". :: " AND T1.foo>foo2 ".
:: " AND (T1.foo3< any (1,2,3) OR T1.foo2=~zod AND foo=1 )) " :: " AND T2.bar IN (1,2,3) ".
:: " AND (T1.foo3< any (1,2,3) OR T1.foo2=~zod AND foo=1 )) ".
:: " SELECT *"
:: =/ joinpred=(tree predicate-component:ast) [%eq t1-foo t2-bar]
:: =/ pred=(tree predicate-component:ast) king-and
:: =/ 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 :: %+ expect-eq
:: !> king-and :: !> ~[expected]
:: !> (wonk (parse-predicate:parse [[1 1] predicate])) :: !> (parse:parse(current-database 'db1') query)
:: ::
:: aggregate inequality :: aggregate inequality
::++ test-predicate-31 ::++ test-predicate-31
@ -428,18 +442,25 @@
:: !> (wonk (parse-predicate:parse [[1 1] predicate])) :: !> (wonk (parse-predicate:parse [[1 1] predicate]))
:: ::
:: complext predicate, bug test :: complext predicate, bug test
:: expected/actual match
::++ test-predicate-34 ::++ test-predicate-34
:: =/ predicate " A1.adoption-email = A2.adoption-email ". :: =/ query "FROM adoptions AS T1 JOIN adoptions AS T2 ON T1.foo = T2.bar ".
:: " AND A1.adoption-date = A2.adoption-date ". :: " WHERE A1.adoption-email = A2.adoption-email ".
:: " AND foo = bar ". :: " AND A1.adoption-date = A2.adoption-date ".
:: " AND ((A1.name = A2.name AND A1.species > A2.species) ". :: " AND foo = bar ".
:: " OR ". :: " AND ((A1.name = A2.name AND A1.species > A2.species) ".
:: " (A1.name > A2.name AND A1.species = A2.species) ". :: " OR ".
:: " OR ". :: " (A1.name > A2.name AND A1.species = A2.species) ".
:: " (A1.name > A2.name AND A1.species > A2.species) ". :: " OR ".
:: " ) " :: " (A1.name > A2.name AND A1.species > A2.species) ".
:: " ) ".
:: " SELECT *"
:: =/ joinpred=(tree predicate-component:ast) [%eq t1-foo t2-bar]
:: =/ pred=(tree predicate-component:ast)
:: [%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]]]]
:: =/ 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 :: %+ 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]]]] :: !> ~[expected]
:: !> (wonk (parse-predicate:parse [[1 1] predicate])) :: !> (parse:parse(current-database 'db1') query)
-- --