From 9f3aab35ee9e27f040afad2b1669da2b013bb781 Mon Sep 17 00:00:00 2001 From: Takashi Nozawa Date: Fri, 28 Apr 2023 15:25:45 +0900 Subject: [PATCH 1/2] fix: CASE-ELSE clause is not properly parsed when a value is constant --- internal/compiler/output_columns.go | 17 +++++--- .../testdata/case_text/pgx/v4/go/db.go | 32 +++++++++++++++ .../testdata/case_text/pgx/v4/go/models.go | 11 +++++ .../testdata/case_text/pgx/v4/go/query.sql.go | 38 +++++++++++++++++ .../testdata/case_text/pgx/v4/query.sql | 8 ++++ .../testdata/case_text/pgx/v4/sqlc.json | 13 ++++++ .../testdata/case_text/pgx/v5/go/db.go | 32 +++++++++++++++ .../testdata/case_text/pgx/v5/go/models.go | 11 +++++ .../testdata/case_text/pgx/v5/go/query.sql.go | 38 +++++++++++++++++ .../testdata/case_text/pgx/v5/query.sql | 8 ++++ .../testdata/case_text/pgx/v5/sqlc.json | 13 ++++++ .../testdata/case_text/stdlib/go/db.go | 31 ++++++++++++++ .../testdata/case_text/stdlib/go/models.go | 11 +++++ .../testdata/case_text/stdlib/go/query.sql.go | 41 +++++++++++++++++++ .../testdata/case_text/stdlib/query.sql | 8 ++++ .../testdata/case_text/stdlib/sqlc.json | 11 +++++ 16 files changed, 318 insertions(+), 5 deletions(-) create mode 100644 internal/endtoend/testdata/case_text/pgx/v4/go/db.go create mode 100644 internal/endtoend/testdata/case_text/pgx/v4/go/models.go create mode 100644 internal/endtoend/testdata/case_text/pgx/v4/go/query.sql.go create mode 100644 internal/endtoend/testdata/case_text/pgx/v4/query.sql create mode 100644 internal/endtoend/testdata/case_text/pgx/v4/sqlc.json create mode 100644 internal/endtoend/testdata/case_text/pgx/v5/go/db.go create mode 100644 internal/endtoend/testdata/case_text/pgx/v5/go/models.go create mode 100644 internal/endtoend/testdata/case_text/pgx/v5/go/query.sql.go create mode 100644 internal/endtoend/testdata/case_text/pgx/v5/query.sql create mode 100644 internal/endtoend/testdata/case_text/pgx/v5/sqlc.json create mode 100644 internal/endtoend/testdata/case_text/stdlib/go/db.go create mode 100644 internal/endtoend/testdata/case_text/stdlib/go/models.go create mode 100644 internal/endtoend/testdata/case_text/stdlib/go/query.sql.go create mode 100644 internal/endtoend/testdata/case_text/stdlib/query.sql create mode 100644 internal/endtoend/testdata/case_text/stdlib/sqlc.json diff --git a/internal/compiler/output_columns.go b/internal/compiler/output_columns.go index 112d684764..8a450470bc 100644 --- a/internal/compiler/output_columns.go +++ b/internal/compiler/output_columns.go @@ -157,7 +157,7 @@ func outputColumns(qc *QueryCatalog, node ast.Node) ([]*Column, error) { if res.Name != nil { name = *res.Name } - // TODO: The TypeCase code has been copied from below. Instead, we + // TODO: The TypeCase and A_Const code has been copied from below. Instead, we // need a recurse function to get the type of a node. if tc, ok := n.Defresult.(*ast.TypeCast); ok { if tc.TypeName == nil { @@ -175,11 +175,18 @@ func outputColumns(qc *QueryCatalog, node ast.Node) ([]*Column, error) { col.Name = name cols = append(cols, col) } else if aconst, ok := n.Defresult.(*ast.A_Const); ok { - tn, err := ParseTypeName(aconst.Val) - if err != nil { - return nil, err + switch aconst.Val.(type) { + case *ast.String: + cols = append(cols, &Column{Name: name, DataType: "text", NotNull: true}) + case *ast.Integer: + cols = append(cols, &Column{Name: name, DataType: "int", NotNull: true}) + case *ast.Float: + cols = append(cols, &Column{Name: name, DataType: "float", NotNull: true}) + case *ast.Boolean: + cols = append(cols, &Column{Name: name, DataType: "bool", NotNull: true}) + default: + cols = append(cols, &Column{Name: name, DataType: "any", NotNull: false}) } - cols = append(cols, &Column{Name: name, DataType: dataType(tn), NotNull: true}) } else { cols = append(cols, &Column{Name: name, DataType: "any", NotNull: false}) } diff --git a/internal/endtoend/testdata/case_text/pgx/v4/go/db.go b/internal/endtoend/testdata/case_text/pgx/v4/go/db.go new file mode 100644 index 0000000000..5a2cde8cda --- /dev/null +++ b/internal/endtoend/testdata/case_text/pgx/v4/go/db.go @@ -0,0 +1,32 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.18.0 + +package querytest + +import ( + "context" + + "github.com/jackc/pgconn" + "github.com/jackc/pgx/v4" +) + +type DBTX interface { + Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) + Query(context.Context, string, ...interface{}) (pgx.Rows, error) + QueryRow(context.Context, string, ...interface{}) pgx.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx pgx.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/case_text/pgx/v4/go/models.go b/internal/endtoend/testdata/case_text/pgx/v4/go/models.go new file mode 100644 index 0000000000..378d4a1b78 --- /dev/null +++ b/internal/endtoend/testdata/case_text/pgx/v4/go/models.go @@ -0,0 +1,11 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.18.0 + +package querytest + +import () + +type Foo struct { + ID string +} diff --git a/internal/endtoend/testdata/case_text/pgx/v4/go/query.sql.go b/internal/endtoend/testdata/case_text/pgx/v4/go/query.sql.go new file mode 100644 index 0000000000..aed1226409 --- /dev/null +++ b/internal/endtoend/testdata/case_text/pgx/v4/go/query.sql.go @@ -0,0 +1,38 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.18.0 +// source: query.sql + +package querytest + +import ( + "context" +) + +const caseStatementText = `-- name: CaseStatementText :many +SELECT CASE + WHEN id = $1 THEN 'foo' + ELSE 'bar' +END is_one +FROM foo +` + +func (q *Queries) CaseStatementText(ctx context.Context, id string) ([]string, error) { + rows, err := q.db.Query(ctx, caseStatementText, id) + if err != nil { + return nil, err + } + defer rows.Close() + var items []string + for rows.Next() { + var is_one string + if err := rows.Scan(&is_one); err != nil { + return nil, err + } + items = append(items, is_one) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/endtoend/testdata/case_text/pgx/v4/query.sql b/internal/endtoend/testdata/case_text/pgx/v4/query.sql new file mode 100644 index 0000000000..857a74ff91 --- /dev/null +++ b/internal/endtoend/testdata/case_text/pgx/v4/query.sql @@ -0,0 +1,8 @@ +CREATE TABLE foo (id text not null); + +-- name: CaseStatementText :many +SELECT CASE + WHEN id = $1 THEN 'foo' + ELSE 'bar' +END is_one +FROM foo; diff --git a/internal/endtoend/testdata/case_text/pgx/v4/sqlc.json b/internal/endtoend/testdata/case_text/pgx/v4/sqlc.json new file mode 100644 index 0000000000..9403bd0279 --- /dev/null +++ b/internal/endtoend/testdata/case_text/pgx/v4/sqlc.json @@ -0,0 +1,13 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "postgresql", + "sql_package": "pgx/v4", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} diff --git a/internal/endtoend/testdata/case_text/pgx/v5/go/db.go b/internal/endtoend/testdata/case_text/pgx/v5/go/db.go new file mode 100644 index 0000000000..7bc02a1d7d --- /dev/null +++ b/internal/endtoend/testdata/case_text/pgx/v5/go/db.go @@ -0,0 +1,32 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.18.0 + +package querytest + +import ( + "context" + + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" +) + +type DBTX interface { + Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) + Query(context.Context, string, ...interface{}) (pgx.Rows, error) + QueryRow(context.Context, string, ...interface{}) pgx.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx pgx.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/case_text/pgx/v5/go/models.go b/internal/endtoend/testdata/case_text/pgx/v5/go/models.go new file mode 100644 index 0000000000..378d4a1b78 --- /dev/null +++ b/internal/endtoend/testdata/case_text/pgx/v5/go/models.go @@ -0,0 +1,11 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.18.0 + +package querytest + +import () + +type Foo struct { + ID string +} diff --git a/internal/endtoend/testdata/case_text/pgx/v5/go/query.sql.go b/internal/endtoend/testdata/case_text/pgx/v5/go/query.sql.go new file mode 100644 index 0000000000..aed1226409 --- /dev/null +++ b/internal/endtoend/testdata/case_text/pgx/v5/go/query.sql.go @@ -0,0 +1,38 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.18.0 +// source: query.sql + +package querytest + +import ( + "context" +) + +const caseStatementText = `-- name: CaseStatementText :many +SELECT CASE + WHEN id = $1 THEN 'foo' + ELSE 'bar' +END is_one +FROM foo +` + +func (q *Queries) CaseStatementText(ctx context.Context, id string) ([]string, error) { + rows, err := q.db.Query(ctx, caseStatementText, id) + if err != nil { + return nil, err + } + defer rows.Close() + var items []string + for rows.Next() { + var is_one string + if err := rows.Scan(&is_one); err != nil { + return nil, err + } + items = append(items, is_one) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/endtoend/testdata/case_text/pgx/v5/query.sql b/internal/endtoend/testdata/case_text/pgx/v5/query.sql new file mode 100644 index 0000000000..857a74ff91 --- /dev/null +++ b/internal/endtoend/testdata/case_text/pgx/v5/query.sql @@ -0,0 +1,8 @@ +CREATE TABLE foo (id text not null); + +-- name: CaseStatementText :many +SELECT CASE + WHEN id = $1 THEN 'foo' + ELSE 'bar' +END is_one +FROM foo; diff --git a/internal/endtoend/testdata/case_text/pgx/v5/sqlc.json b/internal/endtoend/testdata/case_text/pgx/v5/sqlc.json new file mode 100644 index 0000000000..6645ccbd1b --- /dev/null +++ b/internal/endtoend/testdata/case_text/pgx/v5/sqlc.json @@ -0,0 +1,13 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "postgresql", + "sql_package": "pgx/v5", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} diff --git a/internal/endtoend/testdata/case_text/stdlib/go/db.go b/internal/endtoend/testdata/case_text/stdlib/go/db.go new file mode 100644 index 0000000000..8c5b31f933 --- /dev/null +++ b/internal/endtoend/testdata/case_text/stdlib/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.18.0 + +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/case_text/stdlib/go/models.go b/internal/endtoend/testdata/case_text/stdlib/go/models.go new file mode 100644 index 0000000000..378d4a1b78 --- /dev/null +++ b/internal/endtoend/testdata/case_text/stdlib/go/models.go @@ -0,0 +1,11 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.18.0 + +package querytest + +import () + +type Foo struct { + ID string +} diff --git a/internal/endtoend/testdata/case_text/stdlib/go/query.sql.go b/internal/endtoend/testdata/case_text/stdlib/go/query.sql.go new file mode 100644 index 0000000000..c028d246d5 --- /dev/null +++ b/internal/endtoend/testdata/case_text/stdlib/go/query.sql.go @@ -0,0 +1,41 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.18.0 +// source: query.sql + +package querytest + +import ( + "context" +) + +const caseStatementText = `-- name: CaseStatementText :many +SELECT CASE + WHEN id = $1 THEN 'foo' + ELSE 'bar' +END is_one +FROM foo +` + +func (q *Queries) CaseStatementText(ctx context.Context, id string) ([]string, error) { + rows, err := q.db.QueryContext(ctx, caseStatementText, id) + if err != nil { + return nil, err + } + defer rows.Close() + var items []string + for rows.Next() { + var is_one string + if err := rows.Scan(&is_one); err != nil { + return nil, err + } + items = append(items, is_one) + } + 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/case_text/stdlib/query.sql b/internal/endtoend/testdata/case_text/stdlib/query.sql new file mode 100644 index 0000000000..857a74ff91 --- /dev/null +++ b/internal/endtoend/testdata/case_text/stdlib/query.sql @@ -0,0 +1,8 @@ +CREATE TABLE foo (id text not null); + +-- name: CaseStatementText :many +SELECT CASE + WHEN id = $1 THEN 'foo' + ELSE 'bar' +END is_one +FROM foo; diff --git a/internal/endtoend/testdata/case_text/stdlib/sqlc.json b/internal/endtoend/testdata/case_text/stdlib/sqlc.json new file mode 100644 index 0000000000..ac7c2ed829 --- /dev/null +++ b/internal/endtoend/testdata/case_text/stdlib/sqlc.json @@ -0,0 +1,11 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} From bb7b1b1fc4caf14d510e091a882583b102f080db Mon Sep 17 00:00:00 2001 From: Takashi Nozawa Date: Fri, 28 Apr 2023 06:28:02 +0000 Subject: [PATCH 2/2] fix: error message when failed to parse a type name --- internal/compiler/compat.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/compiler/compat.go b/internal/compiler/compat.go index 77b735df87..d546f992bb 100644 --- a/internal/compiler/compat.go +++ b/internal/compiler/compat.go @@ -96,7 +96,7 @@ func ParseTableName(node ast.Node) (*ast.TableName, error) { func ParseTypeName(node ast.Node) (*ast.TypeName, error) { rel, err := parseRelation(node) if err != nil { - return nil, fmt.Errorf("parse table name: %w", err) + return nil, fmt.Errorf("parse type name: %w", err) } return &ast.TypeName{ Catalog: rel.Catalog,