Skip to content
Open
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
35 changes: 34 additions & 1 deletion internal/compiler/output_columns.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/sqlc-dev/sqlc/internal/sql/astutils"
"github.com/sqlc-dev/sqlc/internal/sql/catalog"
"github.com/sqlc-dev/sqlc/internal/sql/lang"
"github.com/sqlc-dev/sqlc/internal/sql/preprocess"
"github.com/sqlc-dev/sqlc/internal/sql/sqlerr"
)

Expand Down Expand Up @@ -53,6 +54,8 @@ func hasStarRef(cf *ast.ColumnRef) bool {
// Return an error if column references are ambiguous
// Return an error if column references don't exist
func (c *Compiler) outputColumns(qc *QueryCatalog, node ast.Node) ([]*Column, error) {
var embedErr error

tables, err := c.sourceTables(qc, node)
if err != nil {
return nil, err
Expand Down Expand Up @@ -267,6 +270,11 @@ func (c *Compiler) outputColumns(qc *QueryCatalog, node ast.Node) ([]*Column, er

// add a column with a reference to an embedded table
if embed, ok := qc.embeds.Find(n.Location, res.Location); ok {
// Keep the column and report the error with the rest, so an
// analyzer that discards inference errors still sees the embed.
if err := checkEmbedRequired(node, embed, res.Location); err != nil && embedErr == nil {
embedErr = err
}
cols = append(cols, &Column{
Name: embed.Table.Name,
EmbedTable: embed.Table,
Expand Down Expand Up @@ -416,7 +424,7 @@ func (c *Compiler) outputColumns(qc *QueryCatalog, node ast.Node) ([]*Column, er
}
}

return cols, nil
return cols, embedErr
}

const (
Expand Down Expand Up @@ -467,6 +475,31 @@ func isTableRequired(n ast.Node, col *Column, prior int) int {
return tableNotFound
}

// An embedded struct is the model of its table, whose columns may not be
// nullable, so it cannot hold the NULLs the optional side of an outer join
// produces.
func checkEmbedRequired(node ast.Node, embed *preprocess.Embed, location int) error {
n, ok := node.(*ast.SelectStmt)
if !ok {
return nil
}
col := &Column{Table: embed.Table, TableAlias: embed.Param()}
for _, f := range n.FromClause.Items {
res := isTableRequired(f, col, tableRequired)
if res != tableNotFound {
if res == tableOptional {
return &sqlerr.Error{
Code: "0A000",
Message: fmt.Sprintf("sqlc.embed(%s) on the nullable side of an outer join is not supported", embed.Param()),
Location: location,
}
}
break
}
}
return nil
}

type tableVisitor struct {
list ast.List
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"contexts": ["base"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-- name: LeftJoin :many
SELECT sqlc.embed(posts), sqlc.embed(users) FROM posts
LEFT JOIN users ON posts.user_id = users.id;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
CREATE TABLE users (
id integer PRIMARY KEY,
name text NOT NULL
);

CREATE TABLE posts (
id integer PRIMARY KEY,
user_id integer
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": "1",
"packages": [
{
"path": "go",
"engine": "postgresql",
"name": "querytest",
"schema": "schema.sql",
"queries": "query.sql"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# package querytest
query.sql:2:27: sqlc.embed(users) on the nullable side of an outer join is not supported
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"contexts": ["base"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-- name: LeftJoin :many
SELECT sqlc.embed(p), sqlc.embed(u) FROM posts AS p
LEFT JOIN users AS u ON p.user_id = u.id;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
CREATE TABLE users (
id integer PRIMARY KEY,
name text NOT NULL
);

CREATE TABLE posts (
id integer PRIMARY KEY,
user_id integer
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": "1",
"packages": [
{
"path": "go",
"engine": "sqlite",
"name": "querytest",
"schema": "schema.sql",
"queries": "query.sql"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# package querytest
query.sql:2:23: sqlc.embed(u) on the nullable side of an outer join is not supported

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

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

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

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-- name: LeftSideOfLeftJoin :many
SELECT sqlc.embed(p), u.name FROM posts AS p
LEFT JOIN users AS u ON p.user_id = u.id;

-- name: RightSideOfRightJoin :many
SELECT sqlc.embed(u), p.id FROM posts AS p
RIGHT JOIN users AS u ON p.user_id = u.id;

-- name: InnerJoin :many
SELECT sqlc.embed(p), sqlc.embed(u) FROM posts AS p
INNER JOIN users AS u ON p.user_id = u.id;

-- name: SelfJoinRequiredAlias :many
SELECT sqlc.embed(a), b.name FROM users AS a
LEFT JOIN users AS b ON a.id = b.id;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
CREATE TABLE users (
id integer PRIMARY KEY,
name text NOT NULL
);

CREATE TABLE posts (
id integer PRIMARY KEY,
user_id integer
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": "1",
"packages": [
{
"path": "go",
"engine": "sqlite",
"name": "querytest",
"schema": "schema.sql",
"queries": "query.sql"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"contexts": ["base"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-- name: RightJoin :many
SELECT sqlc.embed(p), sqlc.embed(u) FROM posts AS p
RIGHT JOIN users AS u ON p.user_id = u.id;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
CREATE TABLE users (
id integer PRIMARY KEY,
name text NOT NULL
);

CREATE TABLE posts (
id integer PRIMARY KEY,
user_id integer
);
Loading
Loading