From 58f22cd4e4103c42c9e7774397cfed7d70ad0227 Mon Sep 17 00:00:00 2001 From: Ryad El-Dajani Date: Fri, 16 Oct 2020 21:20:27 +0200 Subject: [PATCH 1/2] Detect nullable fields when having LEFT_JOIN, OUTER_JOIN or RIGHT_JOIN --- examples/booktest/postgresql/query.sql.go | 3 +- .../booktest/postgresql/QueriesImpl.kt | 2 +- internal/compiler/output_columns.go | 58 +++++++++++++++++++ .../testdata/join_nullable/mysql/go/db.go | 29 ++++++++++ .../testdata/join_nullable/mysql/go/models.go | 16 +++++ .../join_nullable/mysql/go/query.sql.go | 45 ++++++++++++++ .../testdata/join_nullable/mysql/query.sql | 8 +++ .../testdata/join_nullable/mysql/sqlc.json | 12 ++++ .../join_nullable/postgresql/go/db.go | 29 ++++++++++ .../join_nullable/postgresql/go/models.go | 16 +++++ .../join_nullable/postgresql/go/query.sql.go | 45 ++++++++++++++ .../join_nullable/postgresql/query.sql | 8 +++ .../join_nullable/postgresql/sqlc.json | 12 ++++ .../testdata/join_nullable02/mysql/go/db.go | 29 ++++++++++ .../join_nullable02/mysql/go/models.go | 22 +++++++ .../join_nullable02/mysql/go/query.sql.go | 46 +++++++++++++++ .../testdata/join_nullable02/mysql/query.sql | 21 +++++++ .../testdata/join_nullable02/mysql/sqlc.json | 12 ++++ .../join_nullable02/postgresql/go/db.go | 29 ++++++++++ .../join_nullable02/postgresql/go/models.go | 22 +++++++ .../postgresql/go/query.sql.go | 46 +++++++++++++++ .../join_nullable02/postgresql/query.sql | 21 +++++++ .../join_nullable02/postgresql/sqlc.json | 12 ++++ .../postgresql/go/query.sql.go | 4 +- 24 files changed, 543 insertions(+), 4 deletions(-) create mode 100644 internal/endtoend/testdata/join_nullable/mysql/go/db.go create mode 100644 internal/endtoend/testdata/join_nullable/mysql/go/models.go create mode 100644 internal/endtoend/testdata/join_nullable/mysql/go/query.sql.go create mode 100644 internal/endtoend/testdata/join_nullable/mysql/query.sql create mode 100644 internal/endtoend/testdata/join_nullable/mysql/sqlc.json create mode 100644 internal/endtoend/testdata/join_nullable/postgresql/go/db.go create mode 100644 internal/endtoend/testdata/join_nullable/postgresql/go/models.go create mode 100644 internal/endtoend/testdata/join_nullable/postgresql/go/query.sql.go create mode 100644 internal/endtoend/testdata/join_nullable/postgresql/query.sql create mode 100644 internal/endtoend/testdata/join_nullable/postgresql/sqlc.json create mode 100644 internal/endtoend/testdata/join_nullable02/mysql/go/db.go create mode 100644 internal/endtoend/testdata/join_nullable02/mysql/go/models.go create mode 100644 internal/endtoend/testdata/join_nullable02/mysql/go/query.sql.go create mode 100644 internal/endtoend/testdata/join_nullable02/mysql/query.sql create mode 100644 internal/endtoend/testdata/join_nullable02/mysql/sqlc.json create mode 100644 internal/endtoend/testdata/join_nullable02/postgresql/go/db.go create mode 100644 internal/endtoend/testdata/join_nullable02/postgresql/go/models.go create mode 100644 internal/endtoend/testdata/join_nullable02/postgresql/go/query.sql.go create mode 100644 internal/endtoend/testdata/join_nullable02/postgresql/query.sql create mode 100644 internal/endtoend/testdata/join_nullable02/postgresql/sqlc.json diff --git a/examples/booktest/postgresql/query.sql.go b/examples/booktest/postgresql/query.sql.go index ff690ed6c8..a4cc01469b 100644 --- a/examples/booktest/postgresql/query.sql.go +++ b/examples/booktest/postgresql/query.sql.go @@ -5,6 +5,7 @@ package booktest import ( "context" + "database/sql" "time" "github.com/lib/pq" @@ -25,7 +26,7 @@ WHERE tags && $1::varchar[] type BooksByTagsRow struct { BookID int32 Title string - Name string + Name sql.NullString Isbn string Tags []string } diff --git a/examples/kotlin/src/main/kotlin/com/example/booktest/postgresql/QueriesImpl.kt b/examples/kotlin/src/main/kotlin/com/example/booktest/postgresql/QueriesImpl.kt index 5f9f7d70c0..6035b220f9 100644 --- a/examples/kotlin/src/main/kotlin/com/example/booktest/postgresql/QueriesImpl.kt +++ b/examples/kotlin/src/main/kotlin/com/example/booktest/postgresql/QueriesImpl.kt @@ -26,7 +26,7 @@ WHERE tags && ?::varchar[] data class BooksByTagsRow ( val bookId: Int, val title: String, - val name: String, + val name: String?, val isbn: String, val tags: List ) diff --git a/internal/compiler/output_columns.go b/internal/compiler/output_columns.go index cfbd5adfa7..2a1eac5781 100644 --- a/internal/compiler/output_columns.go +++ b/internal/compiler/output_columns.go @@ -3,6 +3,7 @@ package compiler import ( "errors" "fmt" + "github.com/lfittl/pg_query_go/nodes" "github.com/kyleconroy/sqlc/internal/sql/ast" "github.com/kyleconroy/sqlc/internal/sql/astutils" @@ -199,9 +200,66 @@ func outputColumns(qc *QueryCatalog, node ast.Node) ([]*Column, error) { } } + + // On certain join types (e.g. JOIN_LEFT, JOIN_FULL, JOIN_RIGHT) + // the referred joins may be null, thus we may have to adapt the appropriate + // join items to be nullable. + // See: https://github.com/kyleconroy/sqlc/issues/604 + if rangeVarJoin := primaryRangeVarJoin(node); rangeVarJoin != nil { + for _, col := range cols { + col.NotNull = col.NotNull && col.Table.Name == *rangeVarJoin.Relname + } + } + return cols, nil } +// Return the primary range var from a from clause, e.g. RangeVar of "foo" in: +// SELECT f.id, b.id FROM foo f LEFT JOIN bar b ON b.foo_id = f.id +func primaryRangeVarJoin(node ast.Node) *ast.RangeVar { + if n, ok := node.(*ast.SelectStmt); ok { + for _, fi := range n.FromClause.Items { + if j, ok := fi.(*ast.JoinExpr); ok { + if !isNullableJoinType(j) { + // there may be a nullable join expr on larg + if je, ok := j.Larg.(*ast.JoinExpr); ok { + return primaryRangeVar(je) + } + return nil + } + + return primaryRangeVar(j) + } + } + } + + return nil +} + +// Return the primary range var from a from clause, e.g. RangeVar of "foo" in: +// SELECT f.id, b.id FROM foo f LEFT JOIN bar b ON b.foo_id = f.id +func primaryRangeVar(j *ast.JoinExpr) *ast.RangeVar { + if rangeVar, ok := j.Larg.(*ast.RangeVar); ok { + return rangeVar + } + + l, _ := j.Larg.(*ast.JoinExpr) + return primaryRangeVar(l) +} + +// Return true, if JoinExpr is a nullable join expression +func isNullableJoinType(j *ast.JoinExpr) bool { + nullableJoinTypes := []pg_query.JoinType{pg_query.JOIN_LEFT, pg_query.JOIN_FULL, pg_query.JOIN_RIGHT} + + for _, nj := range nullableJoinTypes { + if uint(j.Jointype) == uint(nj) { + return true + } + } + + return false +} + // Compute the output columns for a statement. // // Return an error if column references are ambiguous diff --git a/internal/endtoend/testdata/join_nullable/mysql/go/db.go b/internal/endtoend/testdata/join_nullable/mysql/go/db.go new file mode 100644 index 0000000000..6a99519302 --- /dev/null +++ b/internal/endtoend/testdata/join_nullable/mysql/go/db.go @@ -0,0 +1,29 @@ +// Code generated by sqlc. DO NOT EDIT. + +package querytest + +import ( + "context" + "database/sql" +) + +type DBTX interface { + ExecContext(context.Context, string, ...interface{}) (sql.Result, error) + PrepareContext(context.Context, string) (*sql.Stmt, error) + QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) + QueryRowContext(context.Context, string, ...interface{}) *sql.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/join_nullable/mysql/go/models.go b/internal/endtoend/testdata/join_nullable/mysql/go/models.go new file mode 100644 index 0000000000..d7b3dd5a30 --- /dev/null +++ b/internal/endtoend/testdata/join_nullable/mysql/go/models.go @@ -0,0 +1,16 @@ +// Code generated by sqlc. DO NOT EDIT. + +package querytest + +import ( + "database/sql" +) + +type Bar struct { + ID int32 +} + +type Foo struct { + ID int32 + BarID sql.NullInt32 +} diff --git a/internal/endtoend/testdata/join_nullable/mysql/go/query.sql.go b/internal/endtoend/testdata/join_nullable/mysql/go/query.sql.go new file mode 100644 index 0000000000..626bee3317 --- /dev/null +++ b/internal/endtoend/testdata/join_nullable/mysql/go/query.sql.go @@ -0,0 +1,45 @@ +// Code generated by sqlc. DO NOT EDIT. +// source: query.sql + +package querytest + +import ( + "context" + "database/sql" +) + +const nullableJoin = `-- name: NullableJoin :many +SELECT f.id, f.bar_id, b.id +FROM foo f +FULL OUTER JOIN bar b ON b.id = f.bar_id +WHERE f.id = $1 +` + +type NullableJoinRow struct { + ID int32 + BarID sql.NullInt32 + ID_2 sql.NullInt32 +} + +func (q *Queries) NullableJoin(ctx context.Context, id int32) ([]NullableJoinRow, error) { + rows, err := q.db.QueryContext(ctx, nullableJoin, id) + if err != nil { + return nil, err + } + defer rows.Close() + var items []NullableJoinRow + for rows.Next() { + var i NullableJoinRow + if err := rows.Scan(&i.ID, &i.BarID, &i.ID_2); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/endtoend/testdata/join_nullable/mysql/query.sql b/internal/endtoend/testdata/join_nullable/mysql/query.sql new file mode 100644 index 0000000000..0b31189166 --- /dev/null +++ b/internal/endtoend/testdata/join_nullable/mysql/query.sql @@ -0,0 +1,8 @@ +CREATE TABLE foo (id serial not null, bar_id int references bar(id)); +CREATE TABLE bar (id serial not null); + +-- name: NullableJoin :many +SELECT f.id, f.bar_id, b.id +FROM foo f +FULL OUTER JOIN bar b ON b.id = f.bar_id +WHERE f.id = $1; \ No newline at end of file diff --git a/internal/endtoend/testdata/join_nullable/mysql/sqlc.json b/internal/endtoend/testdata/join_nullable/mysql/sqlc.json new file mode 100644 index 0000000000..c72b6132d5 --- /dev/null +++ b/internal/endtoend/testdata/join_nullable/mysql/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "postgresql", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} diff --git a/internal/endtoend/testdata/join_nullable/postgresql/go/db.go b/internal/endtoend/testdata/join_nullable/postgresql/go/db.go new file mode 100644 index 0000000000..6a99519302 --- /dev/null +++ b/internal/endtoend/testdata/join_nullable/postgresql/go/db.go @@ -0,0 +1,29 @@ +// Code generated by sqlc. DO NOT EDIT. + +package querytest + +import ( + "context" + "database/sql" +) + +type DBTX interface { + ExecContext(context.Context, string, ...interface{}) (sql.Result, error) + PrepareContext(context.Context, string) (*sql.Stmt, error) + QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) + QueryRowContext(context.Context, string, ...interface{}) *sql.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/join_nullable/postgresql/go/models.go b/internal/endtoend/testdata/join_nullable/postgresql/go/models.go new file mode 100644 index 0000000000..d7b3dd5a30 --- /dev/null +++ b/internal/endtoend/testdata/join_nullable/postgresql/go/models.go @@ -0,0 +1,16 @@ +// Code generated by sqlc. DO NOT EDIT. + +package querytest + +import ( + "database/sql" +) + +type Bar struct { + ID int32 +} + +type Foo struct { + ID int32 + BarID sql.NullInt32 +} diff --git a/internal/endtoend/testdata/join_nullable/postgresql/go/query.sql.go b/internal/endtoend/testdata/join_nullable/postgresql/go/query.sql.go new file mode 100644 index 0000000000..626bee3317 --- /dev/null +++ b/internal/endtoend/testdata/join_nullable/postgresql/go/query.sql.go @@ -0,0 +1,45 @@ +// Code generated by sqlc. DO NOT EDIT. +// source: query.sql + +package querytest + +import ( + "context" + "database/sql" +) + +const nullableJoin = `-- name: NullableJoin :many +SELECT f.id, f.bar_id, b.id +FROM foo f +FULL OUTER JOIN bar b ON b.id = f.bar_id +WHERE f.id = $1 +` + +type NullableJoinRow struct { + ID int32 + BarID sql.NullInt32 + ID_2 sql.NullInt32 +} + +func (q *Queries) NullableJoin(ctx context.Context, id int32) ([]NullableJoinRow, error) { + rows, err := q.db.QueryContext(ctx, nullableJoin, id) + if err != nil { + return nil, err + } + defer rows.Close() + var items []NullableJoinRow + for rows.Next() { + var i NullableJoinRow + if err := rows.Scan(&i.ID, &i.BarID, &i.ID_2); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/endtoend/testdata/join_nullable/postgresql/query.sql b/internal/endtoend/testdata/join_nullable/postgresql/query.sql new file mode 100644 index 0000000000..0b31189166 --- /dev/null +++ b/internal/endtoend/testdata/join_nullable/postgresql/query.sql @@ -0,0 +1,8 @@ +CREATE TABLE foo (id serial not null, bar_id int references bar(id)); +CREATE TABLE bar (id serial not null); + +-- name: NullableJoin :many +SELECT f.id, f.bar_id, b.id +FROM foo f +FULL OUTER JOIN bar b ON b.id = f.bar_id +WHERE f.id = $1; \ No newline at end of file diff --git a/internal/endtoend/testdata/join_nullable/postgresql/sqlc.json b/internal/endtoend/testdata/join_nullable/postgresql/sqlc.json new file mode 100644 index 0000000000..c72b6132d5 --- /dev/null +++ b/internal/endtoend/testdata/join_nullable/postgresql/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "postgresql", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} diff --git a/internal/endtoend/testdata/join_nullable02/mysql/go/db.go b/internal/endtoend/testdata/join_nullable02/mysql/go/db.go new file mode 100644 index 0000000000..6a99519302 --- /dev/null +++ b/internal/endtoend/testdata/join_nullable02/mysql/go/db.go @@ -0,0 +1,29 @@ +// Code generated by sqlc. DO NOT EDIT. + +package querytest + +import ( + "context" + "database/sql" +) + +type DBTX interface { + ExecContext(context.Context, string, ...interface{}) (sql.Result, error) + PrepareContext(context.Context, string) (*sql.Stmt, error) + QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) + QueryRowContext(context.Context, string, ...interface{}) *sql.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/join_nullable02/mysql/go/models.go b/internal/endtoend/testdata/join_nullable02/mysql/go/models.go new file mode 100644 index 0000000000..8a39dfa8ef --- /dev/null +++ b/internal/endtoend/testdata/join_nullable02/mysql/go/models.go @@ -0,0 +1,22 @@ +// Code generated by sqlc. DO NOT EDIT. + +package querytest + +import ( + "database/sql" +) + +type City struct { + CityID int32 + MayorID int32 +} + +type Mayor struct { + MayorID int32 + FullName string +} + +type User struct { + UserID int32 + CityID sql.NullInt32 +} diff --git a/internal/endtoend/testdata/join_nullable02/mysql/go/query.sql.go b/internal/endtoend/testdata/join_nullable02/mysql/go/query.sql.go new file mode 100644 index 0000000000..ede118a7b7 --- /dev/null +++ b/internal/endtoend/testdata/join_nullable02/mysql/go/query.sql.go @@ -0,0 +1,46 @@ +// Code generated by sqlc. DO NOT EDIT. +// source: query.sql + +package querytest + +import ( + "context" + "database/sql" +) + +const getMayors = `-- name: GetMayors :many +SELECT + user_id, + mayors.full_name +FROM users +LEFT JOIN cities USING (city_id) +INNER JOIN mayors USING (mayor_id) +` + +type GetMayorsRow struct { + UserID int32 + FullName sql.NullString +} + +func (q *Queries) GetMayors(ctx context.Context) ([]GetMayorsRow, error) { + rows, err := q.db.QueryContext(ctx, getMayors) + if err != nil { + return nil, err + } + defer rows.Close() + var items []GetMayorsRow + for rows.Next() { + var i GetMayorsRow + if err := rows.Scan(&i.UserID, &i.FullName); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/endtoend/testdata/join_nullable02/mysql/query.sql b/internal/endtoend/testdata/join_nullable02/mysql/query.sql new file mode 100644 index 0000000000..48b2a26d1e --- /dev/null +++ b/internal/endtoend/testdata/join_nullable02/mysql/query.sql @@ -0,0 +1,21 @@ +--- https://github.com/kyleconroy/sqlc/issues/604 +CREATE TABLE users ( + user_id INT PRIMARY KEY, + city_id INT -- nullable +); +CREATE TABLE cities ( + city_id INT PRIMARY KEY, + mayor_id INT NOT NULL +); +CREATE TABLE mayors ( + mayor_id INT PRIMARY KEY, + full_name TEXT NOT NULL +); + +-- name: GetMayors :many +SELECT + user_id, + mayors.full_name +FROM users +LEFT JOIN cities USING (city_id) +INNER JOIN mayors USING (mayor_id); \ No newline at end of file diff --git a/internal/endtoend/testdata/join_nullable02/mysql/sqlc.json b/internal/endtoend/testdata/join_nullable02/mysql/sqlc.json new file mode 100644 index 0000000000..c72b6132d5 --- /dev/null +++ b/internal/endtoend/testdata/join_nullable02/mysql/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "postgresql", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} diff --git a/internal/endtoend/testdata/join_nullable02/postgresql/go/db.go b/internal/endtoend/testdata/join_nullable02/postgresql/go/db.go new file mode 100644 index 0000000000..6a99519302 --- /dev/null +++ b/internal/endtoend/testdata/join_nullable02/postgresql/go/db.go @@ -0,0 +1,29 @@ +// Code generated by sqlc. DO NOT EDIT. + +package querytest + +import ( + "context" + "database/sql" +) + +type DBTX interface { + ExecContext(context.Context, string, ...interface{}) (sql.Result, error) + PrepareContext(context.Context, string) (*sql.Stmt, error) + QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) + QueryRowContext(context.Context, string, ...interface{}) *sql.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/join_nullable02/postgresql/go/models.go b/internal/endtoend/testdata/join_nullable02/postgresql/go/models.go new file mode 100644 index 0000000000..8a39dfa8ef --- /dev/null +++ b/internal/endtoend/testdata/join_nullable02/postgresql/go/models.go @@ -0,0 +1,22 @@ +// Code generated by sqlc. DO NOT EDIT. + +package querytest + +import ( + "database/sql" +) + +type City struct { + CityID int32 + MayorID int32 +} + +type Mayor struct { + MayorID int32 + FullName string +} + +type User struct { + UserID int32 + CityID sql.NullInt32 +} diff --git a/internal/endtoend/testdata/join_nullable02/postgresql/go/query.sql.go b/internal/endtoend/testdata/join_nullable02/postgresql/go/query.sql.go new file mode 100644 index 0000000000..ede118a7b7 --- /dev/null +++ b/internal/endtoend/testdata/join_nullable02/postgresql/go/query.sql.go @@ -0,0 +1,46 @@ +// Code generated by sqlc. DO NOT EDIT. +// source: query.sql + +package querytest + +import ( + "context" + "database/sql" +) + +const getMayors = `-- name: GetMayors :many +SELECT + user_id, + mayors.full_name +FROM users +LEFT JOIN cities USING (city_id) +INNER JOIN mayors USING (mayor_id) +` + +type GetMayorsRow struct { + UserID int32 + FullName sql.NullString +} + +func (q *Queries) GetMayors(ctx context.Context) ([]GetMayorsRow, error) { + rows, err := q.db.QueryContext(ctx, getMayors) + if err != nil { + return nil, err + } + defer rows.Close() + var items []GetMayorsRow + for rows.Next() { + var i GetMayorsRow + if err := rows.Scan(&i.UserID, &i.FullName); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/endtoend/testdata/join_nullable02/postgresql/query.sql b/internal/endtoend/testdata/join_nullable02/postgresql/query.sql new file mode 100644 index 0000000000..48b2a26d1e --- /dev/null +++ b/internal/endtoend/testdata/join_nullable02/postgresql/query.sql @@ -0,0 +1,21 @@ +--- https://github.com/kyleconroy/sqlc/issues/604 +CREATE TABLE users ( + user_id INT PRIMARY KEY, + city_id INT -- nullable +); +CREATE TABLE cities ( + city_id INT PRIMARY KEY, + mayor_id INT NOT NULL +); +CREATE TABLE mayors ( + mayor_id INT PRIMARY KEY, + full_name TEXT NOT NULL +); + +-- name: GetMayors :many +SELECT + user_id, + mayors.full_name +FROM users +LEFT JOIN cities USING (city_id) +INNER JOIN mayors USING (mayor_id); \ No newline at end of file diff --git a/internal/endtoend/testdata/join_nullable02/postgresql/sqlc.json b/internal/endtoend/testdata/join_nullable02/postgresql/sqlc.json new file mode 100644 index 0000000000..c72b6132d5 --- /dev/null +++ b/internal/endtoend/testdata/join_nullable02/postgresql/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "postgresql", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} diff --git a/internal/endtoend/testdata/params_location/postgresql/go/query.sql.go b/internal/endtoend/testdata/params_location/postgresql/go/query.sql.go index bef918ed5e..e137611b83 100644 --- a/internal/endtoend/testdata/params_location/postgresql/go/query.sql.go +++ b/internal/endtoend/testdata/params_location/postgresql/go/query.sql.go @@ -83,8 +83,8 @@ WHERE orders.price > $1 ` type ListUserOrdersRow struct { - ID int32 - FirstName string + ID sql.NullInt32 + FirstName sql.NullString Price string } From 10d8cb20b407c378488b560ed694d16ab6190112 Mon Sep 17 00:00:00 2001 From: Ryad El-Dajani Date: Fri, 16 Oct 2020 21:20:27 +0200 Subject: [PATCH 2/2] Detect nullable fields when having LEFT_JOIN, OUTER_JOIN or RIGHT_JOIN --- internal/compiler/output_columns.go | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/internal/compiler/output_columns.go b/internal/compiler/output_columns.go index 2a1eac5781..e36bc793d0 100644 --- a/internal/compiler/output_columns.go +++ b/internal/compiler/output_columns.go @@ -220,14 +220,6 @@ func primaryRangeVarJoin(node ast.Node) *ast.RangeVar { if n, ok := node.(*ast.SelectStmt); ok { for _, fi := range n.FromClause.Items { if j, ok := fi.(*ast.JoinExpr); ok { - if !isNullableJoinType(j) { - // there may be a nullable join expr on larg - if je, ok := j.Larg.(*ast.JoinExpr); ok { - return primaryRangeVar(je) - } - return nil - } - return primaryRangeVar(j) } } @@ -239,8 +231,14 @@ func primaryRangeVarJoin(node ast.Node) *ast.RangeVar { // Return the primary range var from a from clause, e.g. RangeVar of "foo" in: // SELECT f.id, b.id FROM foo f LEFT JOIN bar b ON b.foo_id = f.id func primaryRangeVar(j *ast.JoinExpr) *ast.RangeVar { - if rangeVar, ok := j.Larg.(*ast.RangeVar); ok { + if rangeVar, ok := j.Larg.(*ast.RangeVar); ok && isNullableJoinType(j) { return rangeVar + } else if !isNullableJoinType(j) { + // there may be a nullable join expr on larg + if je, ok := j.Larg.(*ast.JoinExpr); ok { + return primaryRangeVar(je) + } + return nil } l, _ := j.Larg.(*ast.JoinExpr)