Skip to content

Commit deac3f5

Browse files
ryu-ichirohkyleconroy
authored andcommitted
fixing the code to make the subselect nullable
1 parent bc17be2 commit deac3f5

File tree

30 files changed

+417
-139
lines changed

30 files changed

+417
-139
lines changed

internal/compiler/output_columns.go

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package compiler
33
import (
44
"errors"
55
"fmt"
6+
"strings"
67

78
"github.com/sqlc-dev/sqlc/internal/sql/ast"
89
"github.com/sqlc-dev/sqlc/internal/sql/astutils"
@@ -315,12 +316,14 @@ func (c *Compiler) outputColumns(qc *QueryCatalog, node ast.Node) ([]*Column, er
315316
DataType: dataType(fun.ReturnType),
316317
NotNull: !fun.ReturnTypeNullable,
317318
IsFuncCall: true,
319+
FuncName: rel.Name,
318320
})
319321
} else {
320322
cols = append(cols, &Column{
321323
Name: name,
322324
DataType: "any",
323325
IsFuncCall: true,
326+
FuncName: rel.Name,
324327
})
325328
}
326329

@@ -341,6 +344,10 @@ func (c *Compiler) outputColumns(qc *QueryCatalog, node ast.Node) ([]*Column, er
341344
if res.Name != nil {
342345
first.Name = *res.Name
343346
}
347+
if !(first.IsFuncCall && strings.EqualFold(first.FuncName, "count")) {
348+
first.NotNull = false
349+
}
350+
344351
cols = append(cols, first)
345352
default:
346353
cols = append(cols, &Column{Name: name, DataType: "any", NotNull: false})
@@ -377,8 +384,9 @@ func (c *Compiler) outputColumns(qc *QueryCatalog, node ast.Node) ([]*Column, er
377384
if res.Name != nil {
378385
first.Name = *res.Name
379386
}
380-
381-
if hasWhereOrHavingClause(n) {
387+
if !(first.IsFuncCall &&
388+
(strings.EqualFold(first.FuncName, "count") ||
389+
strings.EqualFold(first.FuncName, "total"))) {
382390
first.NotNull = false
383391
}
384392

@@ -769,17 +777,3 @@ func findColumnForRef(ref *ast.ColumnRef, tables []*Table, targetList *ast.List)
769777

770778
return nil
771779
}
772-
773-
// hasWhereOrHavingClause returns true if the statement contains WHERE or HAVING clause
774-
func hasWhereOrHavingClause(node ast.Node) bool {
775-
stmt := node.(*ast.SelectStmt)
776-
777-
if _, isTODO := stmt.WhereClause.(*ast.TODO); stmt.WhereClause != nil && !isTODO {
778-
return true
779-
}
780-
if _, isTODO := stmt.HavingClause.(*ast.TODO); stmt.HavingClause != nil && !isTODO {
781-
return true
782-
}
783-
784-
return false
785-
}

internal/compiler/query.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ type Column struct {
2929
Length *int
3030
IsNamedParam bool
3131
IsFuncCall bool
32+
FuncName string
3233

3334
// XXX: Figure out what PostgreSQL calls `foo.id`
3435
Scope string

internal/endtoend/testdata/nullable_subselect/mysql/go/models.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/endtoend/testdata/nullable_subselect/mysql/go/query.sql.go

Lines changed: 53 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
CREATE TABLE foo (a int not null, b int);
1+
-- name: FirstRowFromFooTable :many
2+
SELECT a, (SELECT a FROM foo limit 1) as "first" FROM foo;
23

3-
-- name: SubqueryWithWhereClause :many
4-
SELECT a, (SELECT COUNT(a) FROM foo WHERE a > 10) as "total" FROM foo;
4+
-- name: FirstRowFromEmptyTable :many
5+
SELECT a, (SELECT a FROM empty limit 1) as "first" FROM foo;
56

6-
-- name: SubqueryWithHavingClause :many
7-
SELECT a, (SELECT COUNT(a) FROM foo GROUP BY b HAVING COUNT(a) > 10) as "total" FROM foo;
7+
-- In MySQL, only count() returns 0 for empty table.
8+
-- https://dev.mysql.com/doc/refman/8.0/en/aggregate-functions.html
9+
-- name: CountRowsEmptyTable :many
10+
SELECT a, (SELECT count(a) FROM empty) as "count" FROM foo;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CREATE TABLE foo (a int not NULL, b int);
2+
3+
INSERT INTO foo VALUES (1, 2);
4+
INSERT INTO foo VALUES (3, NULL);
5+
INSERT INTO foo VALUES (4, 5);
6+
7+
CREATE TABLE empty (a int not NULL, b int);

internal/endtoend/testdata/nullable_subselect/mysql/sqlc.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"path": "go",
66
"engine": "mysql",
77
"name": "querytest",
8-
"schema": "query.sql",
8+
"schema": "schema.sql",
99
"queries": "query.sql"
1010
}
1111
]

internal/endtoend/testdata/nullable_subselect/postgresql/pgx/v4/go/models.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/endtoend/testdata/nullable_subselect/postgresql/pgx/v4/go/query.sql.go

Lines changed: 50 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
CREATE TABLE foo (a int not null, b int);
1+
-- name: FirstRowFromFooTable :many
2+
SELECT a, (SELECT a FROM foo limit 1) as "first" FROM foo;
23

3-
-- name: SubqueryWithWhereClause :many
4-
SELECT a, (SELECT COUNT(a) FROM foo WHERE a > 10) as "total" FROM foo;
4+
-- name: FirstRowFromEmptyTable :many
5+
SELECT a, (SELECT a FROM empty limit 1) as "first" FROM foo;
56

6-
-- name: SubqueryWithHavingClause :many
7-
SELECT a, (SELECT COUNT(a) FROM foo GROUP BY b HAVING COUNT(a) > 10) as "total" FROM foo;
7+
-- In PostgreSQL, only count() returns 0 for empty table.
8+
-- https://www.postgresql.org/docs/15/functions-aggregate.html
9+
-- name: CountRowsEmptyTable :many
10+
SELECT a, (SELECT count(a) FROM empty) as "count" FROM foo;

0 commit comments

Comments
 (0)