Skip to content

Commit 3479048

Browse files
left-join-correction (#641)
Summary: - Correction of `left join` from defective `inner join` implementation to proper `left outer join` implementation. - Updated robot test `Subquery Left Joined With Aliasing and Name Collision`. - Updated robot test `Inner Join Users Cross Cloud Fuzzy Match`. - Added robot test `Inner Join Negative LHS Inline`. - Added robot test `Inner Join Positive LHS Inline`. - Added robot test `Left Join Negative LHS Inline`. - Added robot test `Left Join Positive LHS Inline`. - Added robot test `View Left Join View Returns Results`. - Added robot test `View Left Join Subquery Returns Results`. - Added robot test `View Left Join Materialized View Returns Results`.
1 parent fde7228 commit 3479048

2 files changed

Lines changed: 182 additions & 5 deletions

File tree

internal/stackql/router/table_routing.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -537,11 +537,11 @@ func (v *standardTableRouteAstVisitor) Visit(node sqlparser.SQLNode) error {
537537
// and also so that
538538
// these are omitted from the WHERE clause.
539539
switch node.Join {
540-
case sqlparser.LeftJoinStr:
541540
case sqlparser.RightJoinStr:
542541
case sqlparser.StraightJoinStr:
543542
case sqlparser.JoinStr:
544-
case sqlparser.LeftOuterJoinStr:
543+
case sqlparser.LeftJoinStr, sqlparser.LeftOuterJoinStr:
544+
// LEFT JOIN and LEFT OUTER JOIN are semantically identical in SQL.
545545
rhs := v.tables[node.RightExpr]
546546
if rhs == nil {
547547
return fmt.Errorf("table routing: nil RHS table in left outer join")

test/robot/functional/stackql_mocked_from_cmd_line.robot

Lines changed: 180 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1389,7 +1389,7 @@ Create and Interrogate Materialized View With Userspace Table Join and Aliasing
13891389

13901390
Subquery Left Joined With Aliasing and Name Collision
13911391
${inputStr} = Catenate
1392-
... select u1.UserName, u.UserId, u.Arn, u1.region from ( select Arn, UserName, UserId from aws.iam.users where region = 'us-east-1' ) u inner join aws.iam.users u1 on u1.Arn = u.Arn where region = 'us-east-1' order by u1.UserName desc;
1392+
... select u1.UserName, u.UserId, u.Arn, u1.region from ( select Arn, UserName, UserId from aws.iam.users where region = 'us-east-1' ) u left join aws.iam.users u1 on u1.Arn = u.Arn where region = 'us-east-1' order by u1.UserName desc;
13931393
${outputStr} = Catenate SEPARATOR=\n
13941394
... |----------|-----------------------|--------------------------------------------------------------------------------|-----------|
13951395
... |${SPACE}UserName${SPACE}|${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}UserId${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}|${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}Arn${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}|${SPACE}${SPACE}region${SPACE}${SPACE}${SPACE}|
@@ -2535,7 +2535,7 @@ Left Outer Join Network Infra Scalar in ON Condition
25352535
... ${outputStr}
25362536
... stdout=${CURDIR}/tmp/Left-Outer-Join-Network-Infra-Scalar-in-ON-Condition.tmp
25372537

2538-
Left Inner Join Users
2538+
Inner Join Users Cross Cloud Fuzzy Match
25392539
${sqliteInputStr} = Catenate
25402540
... select
25412541
... aid.UserName as aws_user_name
@@ -2591,7 +2591,7 @@ Left Inner Join Users
25912591
... ${SQL_BACKEND_CFG_STR_CANONICAL}
25922592
... ${inputStr}
25932593
... ${outputStr}
2594-
... stdout=${CURDIR}/tmp/Left-Inner-Join-Users.tmp
2594+
... stdout=${CURDIR}/tmp/Inner-Join-Users-Cross-Cloud-Fuzzy-Match.tmp
25952595

25962596
Google Admin Directory Small Response Also De Facto Credentials Path Env Var
25972597
Set Environment Variable GOOGLE_APPLICATION_CREDENTIALS ${GOOGLE_APPLICATION_CREDENTIALS}
@@ -9479,6 +9479,122 @@ Left Outer Join Positive LHS Inline
94799479
... stdout=${CURDIR}/tmp/Left-Outer-Join-Positive-LHS-Inline.tmp
94809480
... stderr=${CURDIR}/tmp/Left-Outer-Join-Positive-LHS-Inline-stderr.tmp
94819481

9482+
Inner Join Negative LHS Inline
9483+
${inputStr} = Catenate
9484+
... select lhs.id, lhs.secondary_field, rhs.volume_id
9485+
... from
9486+
... (select 'my-id' as id, 'some other field' as secondary_field) lhs
9487+
... join
9488+
... (select volume_id from aws.ec2.volumes_presented where region \= 'ap-southeast-2') rhs
9489+
... on lhs.id \= rhs.volume_id
9490+
... ;
9491+
${outputStr} = Catenate SEPARATOR=\n
9492+
... |----|-----------------|-----------|
9493+
... |${SPACE}id${SPACE}|${SPACE}secondary_field${SPACE}|${SPACE}volume_id${SPACE}|
9494+
... |----|-----------------|-----------|
9495+
Should Stackql Exec Inline Equal Both Streams
9496+
... ${STACKQL_EXE}
9497+
... ${OKTA_SECRET_STR}
9498+
... ${GITHUB_SECRET_STR}
9499+
... ${K8S_SECRET_STR}
9500+
... ${REGISTRY_NO_VERIFY_CFG_STR}
9501+
... ${AUTH_CFG_STR}
9502+
... ${SQL_BACKEND_CFG_STR_CANONICAL}
9503+
... ${inputStr}
9504+
... ${outputStr}
9505+
... ${EMPTY}
9506+
... stdout=${CURDIR}/tmp/Inner-Join-Negative-LHS-Inline.tmp
9507+
... stderr=${CURDIR}/tmp/Inner-Join-Negative-LHS-Inline-stderr.tmp
9508+
9509+
Inner Join Positive LHS Inline
9510+
${inputStr} = Catenate
9511+
... select lhs.id, lhs.secondary_field, rhs.volume_id
9512+
... from
9513+
... (select 'vol-00200000000000000' as id, 'some other field' as secondary_field) lhs
9514+
... join
9515+
... (select volume_id from aws.ec2.volumes_presented where region \= 'ap-southeast-2') rhs
9516+
... on lhs.id \= rhs.volume_id
9517+
... ;
9518+
${outputStr} = Catenate SEPARATOR=\n
9519+
... |-----------------------|------------------|-----------------------|
9520+
... |${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}id${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}|${SPACE}secondary_field${SPACE}${SPACE}|${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}volume_id${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}|
9521+
... |-----------------------|------------------|-----------------------|
9522+
... |${SPACE}vol-00200000000000000${SPACE}|${SPACE}some${SPACE}other${SPACE}field${SPACE}|${SPACE}vol-00200000000000000${SPACE}|
9523+
... |-----------------------|------------------|-----------------------|
9524+
Should Stackql Exec Inline Equal Both Streams
9525+
... ${STACKQL_EXE}
9526+
... ${OKTA_SECRET_STR}
9527+
... ${GITHUB_SECRET_STR}
9528+
... ${K8S_SECRET_STR}
9529+
... ${REGISTRY_NO_VERIFY_CFG_STR}
9530+
... ${AUTH_CFG_STR}
9531+
... ${SQL_BACKEND_CFG_STR_CANONICAL}
9532+
... ${inputStr}
9533+
... ${outputStr}
9534+
... ${EMPTY}
9535+
... stdout=${CURDIR}/tmp/Inner-Join-Positive-LHS-Inline.tmp
9536+
... stderr=${CURDIR}/tmp/Inner-Join-Positive-LHS-Inline-stderr.tmp
9537+
9538+
Left Join Negative LHS Inline
9539+
${inputStr} = Catenate
9540+
... select lhs.id, lhs.secondary_field, rhs.volume_id
9541+
... from
9542+
... (select 'my-id' as id, 'some other field' as secondary_field) lhs
9543+
... left join
9544+
... (select volume_id from aws.ec2.volumes_presented where region \= 'ap-southeast-2') rhs
9545+
... on lhs.id \= rhs.volume_id
9546+
... where volume_id is null
9547+
... ;
9548+
${outputStr} = Catenate SEPARATOR=\n
9549+
... |-------|------------------|-----------|
9550+
... |${SPACE}${SPACE}id${SPACE}${SPACE}${SPACE}|${SPACE}secondary_field${SPACE}${SPACE}|${SPACE}volume_id${SPACE}|
9551+
... |-------|------------------|-----------|
9552+
... |${SPACE}my-id${SPACE}|${SPACE}some${SPACE}other${SPACE}field${SPACE}|${SPACE}null${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}|
9553+
... |-------|------------------|-----------|
9554+
Should Stackql Exec Inline Equal Both Streams
9555+
... ${STACKQL_EXE}
9556+
... ${OKTA_SECRET_STR}
9557+
... ${GITHUB_SECRET_STR}
9558+
... ${K8S_SECRET_STR}
9559+
... ${REGISTRY_NO_VERIFY_CFG_STR}
9560+
... ${AUTH_CFG_STR}
9561+
... ${SQL_BACKEND_CFG_STR_CANONICAL}
9562+
... ${inputStr}
9563+
... ${outputStr}
9564+
... ${EMPTY}
9565+
... stdout=${CURDIR}/tmp/Left-Join-Negative-LHS-Inline.tmp
9566+
... stderr=${CURDIR}/tmp/Left-Join-Negative-LHS-Inline-stderr.tmp
9567+
9568+
Left Join Positive LHS Inline
9569+
${inputStr} = Catenate
9570+
... select lhs.id, lhs.secondary_field, rhs.volume_id
9571+
... from
9572+
... (select 'vol-00200000000000000' as id, 'some other field' as secondary_field) lhs
9573+
... left join
9574+
... (select volume_id from aws.ec2.volumes_presented where region \= 'ap-southeast-2') rhs
9575+
... on lhs.id \= rhs.volume_id
9576+
... where volume_id is not null
9577+
... ;
9578+
${outputStr} = Catenate SEPARATOR=\n
9579+
... |-----------------------|------------------|-----------------------|
9580+
... |${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}id${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}|${SPACE}secondary_field${SPACE}${SPACE}|${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}volume_id${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}|
9581+
... |-----------------------|------------------|-----------------------|
9582+
... |${SPACE}vol-00200000000000000${SPACE}|${SPACE}some${SPACE}other${SPACE}field${SPACE}|${SPACE}vol-00200000000000000${SPACE}|
9583+
... |-----------------------|------------------|-----------------------|
9584+
Should Stackql Exec Inline Equal Both Streams
9585+
... ${STACKQL_EXE}
9586+
... ${OKTA_SECRET_STR}
9587+
... ${GITHUB_SECRET_STR}
9588+
... ${K8S_SECRET_STR}
9589+
... ${REGISTRY_NO_VERIFY_CFG_STR}
9590+
... ${AUTH_CFG_STR}
9591+
... ${SQL_BACKEND_CFG_STR_CANONICAL}
9592+
... ${inputStr}
9593+
... ${outputStr}
9594+
... ${EMPTY}
9595+
... stdout=${CURDIR}/tmp/Left-Join-Positive-LHS-Inline.tmp
9596+
... stderr=${CURDIR}/tmp/Left-Join-Positive-LHS-Inline-stderr.tmp
9597+
94829598
View JOIN View Returns Results
94839599
${inputStr} = Catenate
94849600
... create or replace view vw_repos_name as select name from stackql_repositories;
@@ -9839,6 +9955,67 @@ View Left Outer Join Materialized View Returns Results
98399955
... stdout=${CURDIR}/tmp/View-Left-Outer-Join-Materialized-View-Returns-Results-stdout.tmp
98409956
... stderr=${CURDIR}/tmp/View-Left-Outer-Join-Materialized-View-Returns-Results-stderr.tmp
98419957

9958+
View Left Join View Returns Results
9959+
${inputStr} = Catenate
9960+
... create table partial_repos(name text unique);
9961+
... insert into partial_repos values('dummyapp.io');
9962+
... create or replace view vw_repos as select name, url from stackql_repositories;
9963+
... create or replace view vw_partial as select name from partial_repos;
9964+
... select v1.name, v1.url, v2.name as v2_name from vw_repos v1 left join vw_partial v2 on v1.name \= v2.name order by v1.name desc;
9965+
... drop view vw_partial;
9966+
... drop table partial_repos;
9967+
Should Stackql Exec Inline Contain
9968+
... ${STACKQL_EXE}
9969+
... ${OKTA_SECRET_STR}
9970+
... ${GITHUB_SECRET_STR}
9971+
... ${K8S_SECRET_STR}
9972+
... ${REGISTRY_NO_VERIFY_CFG_STR}
9973+
... ${AUTH_CFG_STR}
9974+
... ${SQL_BACKEND_CFG_STR_CANONICAL}
9975+
... ${inputStr}
9976+
... null
9977+
... stdout=${CURDIR}/tmp/View-Left-Join-View-Returns-Results-stdout.tmp
9978+
... stderr=${CURDIR}/tmp/View-Left-Join-View-Returns-Results-stderr.tmp
9979+
9980+
View Left Join Subquery Returns Results
9981+
${inputStr} = Catenate
9982+
... create or replace view vw_repos as select name, url from stackql_repositories;
9983+
... select v1.name, v1.url, sq.name as sq_name from vw_repos v1 left join (select 'dummyapp.io' as name) sq on v1.name \= sq.name order by v1.name desc;
9984+
Should Stackql Exec Inline Contain
9985+
... ${STACKQL_EXE}
9986+
... ${OKTA_SECRET_STR}
9987+
... ${GITHUB_SECRET_STR}
9988+
... ${K8S_SECRET_STR}
9989+
... ${REGISTRY_NO_VERIFY_CFG_STR}
9990+
... ${AUTH_CFG_STR}
9991+
... ${SQL_BACKEND_CFG_STR_CANONICAL}
9992+
... ${inputStr}
9993+
... null
9994+
... stdout=${CURDIR}/tmp/View-Left-Join-Subquery-Returns-Results-stdout.tmp
9995+
... stderr=${CURDIR}/tmp/View-Left-Join-Subquery-Returns-Results-stderr.tmp
9996+
9997+
View Left Join Materialized View Returns Results
9998+
${inputStr} = Catenate
9999+
... create table partial_repos(name text unique);
10000+
... insert into partial_repos values('dummyapp.io');
10001+
... create or replace view vw_repos as select name, url from stackql_repositories;
10002+
... create or replace materialized view mv_partial as select name from partial_repos;
10003+
... select v1.name, v1.url, mv.name as mv_name from vw_repos v1 left join mv_partial mv on v1.name \= mv.name order by v1.name desc;
10004+
... drop materialized view mv_partial;
10005+
... drop table partial_repos;
10006+
Should Stackql Exec Inline Contain
10007+
... ${STACKQL_EXE}
10008+
... ${OKTA_SECRET_STR}
10009+
... ${GITHUB_SECRET_STR}
10010+
... ${K8S_SECRET_STR}
10011+
... ${REGISTRY_NO_VERIFY_CFG_STR}
10012+
... ${AUTH_CFG_STR}
10013+
... ${SQL_BACKEND_CFG_STR_CANONICAL}
10014+
... ${inputStr}
10015+
... null
10016+
... stdout=${CURDIR}/tmp/View-Left-Join-Materialized-View-Returns-Results-stdout.tmp
10017+
... stderr=${CURDIR}/tmp/View-Left-Join-Materialized-View-Returns-Results-stderr.tmp
10018+
984210019
Three Way View Inner Join Subquery Left Outer Join Provider Table Returns Results
984310020
${inputStr} = Catenate
984410021
... create or replace view vw_repos as select name, url from stackql_repositories;

0 commit comments

Comments
 (0)