Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions internal/compiler/output_columns.go
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ func isTableRequired(n ast.Node, col *Column, prior int) int {
// Return an error if an unknown column is referenced
func sourceTables(qc *QueryCatalog, node ast.Node) ([]*Table, error) {
var list *ast.List
var nullableNodes []*ast.Node
switch n := node.(type) {
case *ast.DeleteStmt:
list = &ast.List{
Expand All @@ -369,14 +370,29 @@ func sourceTables(qc *QueryCatalog, node ast.Node) ([]*Table, error) {
Items: []ast.Node{n.Relation},
}
case *ast.SelectStmt:
list = astutils.Search(n.FromClause, func(node ast.Node) bool {
switch node.(type) {
dirtyList := astutils.Search(n.FromClause, func(node ast.Node) bool {
switch t := node.(type) {
case *ast.RangeVar, *ast.RangeSubselect, *ast.FuncName:
return true
case *ast.JoinExpr:
if t.Jointype == ast.JoinTypeLeft || t.Jointype == ast.JoinTypeFull {
return true
}
return false
default:
return false
}
})
// split result into nullableJoins and list of nodes to handle
list = &ast.List{}
for i, v := range dirtyList.Items {
switch t := dirtyList.Items[i].(type) {
case *ast.JoinExpr:
nullableNodes = append(nullableNodes, &t.Rarg)
default:
list.Items = append(list.Items, v)
}
}
case *ast.TruncateStmt:
list = astutils.Search(n.Relations, func(node ast.Node) bool {
_, ok := node.(*ast.RangeVar)
Expand Down Expand Up @@ -413,9 +429,15 @@ func sourceTables(qc *QueryCatalog, node ast.Node) ([]*Table, error) {

case *ast.RangeSubselect:
cols, err := outputColumns(qc, n.Subquery)

if err != nil {
return nil, err
}
if astutils.IsChildOfNodes(nullableNodes, &item) {
for _, c := range cols {
c.NotNull = false
}
}
tables = append(tables, &Table{
Rel: &ast.TableName{
Name: *n.Alias.Aliasname,
Expand Down
45 changes: 45 additions & 0 deletions internal/endtoend/testdata/join_left/postgresql/go/query.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions internal/endtoend/testdata/join_left/postgresql/query.sql
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,14 @@ FROM users_2 u
ON u.user_avatar_id = m.media_id
WHERE u.user_id != @user_id
LIMIT @user_imit;

-- name: GetMayorsOptionalInnerSelect :many
SELECT t1.user_id, t2.full_name
FROM (
SELECT user_id, city_id FROM users WHERE users.city_id = $1 LIMIT 1 OFFSET 0
) AS t1
LEFT JOIN cities on t1.city_id = cities.city_id
LEFT JOIN (
SELECT mayors.mayor_id, mayors.full_name
FROM mayors where mayors.mayor_id = $2
) AS t2 on t2.mayor_id = cities.mayor_id;
22 changes: 22 additions & 0 deletions internal/sql/astutils/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,25 @@ func Search(root ast.Node, f func(ast.Node) bool) *ast.List {
Walk(ns, root)
return ns.list
}

func IsChildOfNodes(parents []*ast.Node, node *ast.Node) bool {
for _, v := range parents {
if IsChildOfNode(v, node) {
return true
}
}
return false
}

func IsChildOfNode(parent *ast.Node, node *ast.Node) bool {
res := Search(*parent, func(n ast.Node) bool {
if n == *node {
return true
}
return false
})
if len(res.Items) > 0 {
return true
}
return false
}