Skip to content
Prev Previous commit
Next Next commit
fix: change type precedence auto-selection to data type any, to allow…
… user more freedom to choose query column type
  • Loading branch information
swallowstalker committed Dec 19, 2024
commit 409d2fbdf81ebb483ed9ba58cd935b3d4e4119f1
65 changes: 44 additions & 21 deletions internal/compiler/output_columns.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,11 @@ func (c *Compiler) outputColumns(qc *QueryCatalog, node ast.Node) ([]*Column, er
if res.Name != nil {
name = *res.Name
}
cols = append(cols, convertAConstToColumn(n, name))
col := convertAConstToColumn(n, name)
if col.DataType == "null" {
col.DataType = "any"
}
cols = append(cols, col)

case *ast.A_Expr:
name := ""
Expand Down Expand Up @@ -164,50 +168,67 @@ func (c *Compiler) outputColumns(qc *QueryCatalog, node ast.Node) ([]*Column, er
cols = append(cols, &Column{Name: name, DataType: "bool", NotNull: notNull})

case *ast.CaseExpr:
name := ""
var name string
if res.Name != nil {
name = *res.Name
}

typePrecedence := map[string]int{
"any": 0,
"bool": 1,
"int": 2,
"pg_catalog.int4": 2,
"float": 3,
"pg_catalog.float8": 3,
"text": 4,
}

chosenType := "any"
chosenType := ""
chosenNullable := false

for _, i := range n.Args.Items {
cw := i.(*ast.CaseWhen)
col, err := convertCaseExprCondToColumn(cw.Result, res.Name)
col, err := convertCaseExprCondToColumn(cw.Result, &name)
if err != nil {
return nil, err
}
if typePrecedence[col.DataType] > typePrecedence[chosenType] {
chosenType = col.DataType
if col.DataType == "null" {
// we don't choose type from this column if its value is null, only choose nullability
chosenNullable = true
continue
}
if col.DataType != chosenType {
if chosenType == "" {
chosenType = col.DataType
} else {
chosenType = "any"
}
}
if !col.NotNull {
chosenNullable = true
}
}

if n.Defresult != nil {
defaultCol, err := convertCaseExprCondToColumn(n.Defresult, res.Name)
var defaultCol *Column
if n.Defresult.Pos() != 0 {
defaultCol, err = convertCaseExprCondToColumn(n.Defresult, &name)
if err != nil {
return nil, err
}
if typePrecedence[defaultCol.DataType] > typePrecedence[chosenType] {
chosenType = defaultCol.DataType
} else {
defaultCol = &Column{Name: name, DataType: "null", NotNull: false}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing ELSE clause on CaseExpr is considered to return null value

}

if defaultCol.DataType == "null" {
// we don't choose type from this column if its value is null, only choose nullability
chosenNullable = true
} else {
if defaultCol.DataType != chosenType {
if chosenType == "" {
chosenType = defaultCol.DataType
} else {
chosenType = "any"
}
}
if !defaultCol.NotNull {
chosenNullable = true
}
}

if chosenType == "" {
chosenType = "any"
}

chosenColumn := &Column{Name: name, DataType: chosenType, NotNull: !chosenNullable}
cols = append(cols, chosenColumn)

Expand Down Expand Up @@ -811,7 +832,7 @@ func convertAExprToColumn(aexpr *ast.A_Expr, name string) *Column {
return col
}

func convertAConstToColumn(aconst *ast.A_Const, name string) *Column {
func convertAConstToColumn(aconst *ast.A_Const, name string) (*Column) {
var col *Column
switch aconst.Val.(type) {
case *ast.String:
Expand All @@ -822,6 +843,8 @@ func convertAConstToColumn(aconst *ast.A_Const, name string) *Column {
col = &Column{Name: name, DataType: "float", NotNull: true}
case *ast.Boolean:
col = &Column{Name: name, DataType: "bool", NotNull: true}
case *ast.Null:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is new addition to handle null const value. I need to distinguish between any and null, because null in CaseExpr would determine query column result nullability, but does not make it included in column type decision.

for ast.A_Const condition, i have converted DataType null back to any (line 136, outputColumns function) because we don't know its type and to preserve backward compatibility

col = &Column{Name: name, DataType: "null", NotNull: false}
default:
col = &Column{Name: name, DataType: "any", NotNull: false}
}
Expand Down

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
Expand Up @@ -80,4 +80,11 @@ SELECT CASE
WHEN id = 2 THEN null
ELSE 7 - id
END
FROM "mytable";

-- name: GetNullable2G :many
SELECT CASE
WHEN id = 2 THEN null
ELSE null
END
FROM "mytable";
Loading