Skip to content

Commit ae2e92a

Browse files
author
Pulung Ragil
committed
feat: case expr handling and json operator handling
1 parent 28ea78e commit ae2e92a

File tree

4 files changed

+533
-65
lines changed

4 files changed

+533
-65
lines changed

internal/compiler/output_columns.go

Lines changed: 109 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -132,33 +132,14 @@ func (c *Compiler) outputColumns(qc *QueryCatalog, node ast.Node) ([]*Column, er
132132
if res.Name != nil {
133133
name = *res.Name
134134
}
135-
switch n.Val.(type) {
136-
case *ast.String:
137-
cols = append(cols, &Column{Name: name, DataType: "text", NotNull: true})
138-
case *ast.Integer:
139-
cols = append(cols, &Column{Name: name, DataType: "int", NotNull: true})
140-
case *ast.Float:
141-
cols = append(cols, &Column{Name: name, DataType: "float", NotNull: true})
142-
case *ast.Boolean:
143-
cols = append(cols, &Column{Name: name, DataType: "bool", NotNull: true})
144-
default:
145-
cols = append(cols, &Column{Name: name, DataType: "any", NotNull: false})
146-
}
135+
cols = append(cols, convertAConstToColumn(n, name))
147136

148137
case *ast.A_Expr:
149138
name := ""
150139
if res.Name != nil {
151140
name = *res.Name
152141
}
153-
switch op := astutils.Join(n.Name, ""); {
154-
case lang.IsComparisonOperator(op):
155-
// TODO: Generate a name for these operations
156-
cols = append(cols, &Column{Name: name, DataType: "bool", NotNull: true})
157-
case lang.IsMathematicalOperator(op):
158-
cols = append(cols, &Column{Name: name, DataType: "int", NotNull: true})
159-
default:
160-
cols = append(cols, &Column{Name: name, DataType: "any", NotNull: false})
161-
}
142+
cols = append(cols, convertAExprToColumn(n, name))
162143

163144
case *ast.BoolExpr:
164145
name := ""
@@ -187,40 +168,49 @@ func (c *Compiler) outputColumns(qc *QueryCatalog, node ast.Node) ([]*Column, er
187168
if res.Name != nil {
188169
name = *res.Name
189170
}
190-
// TODO: The TypeCase and A_Const code has been copied from below. Instead, we
191-
// need a recurse function to get the type of a node.
192-
if tc, ok := n.Defresult.(*ast.TypeCast); ok {
193-
if tc.TypeName == nil {
194-
return nil, errors.New("no type name type cast")
171+
172+
typePrecedence := map[string]int{
173+
"any": 0,
174+
"bool": 1,
175+
"int": 2,
176+
"pg_catalog.int4": 2,
177+
"float": 3,
178+
"pg_catalog.float8": 3,
179+
"text": 4,
180+
}
181+
182+
chosenType := "any"
183+
chosenNullable := false
184+
for _, i := range n.Args.Items {
185+
cw := i.(*ast.CaseWhen)
186+
col, err := convertCaseExprCondToColumn(cw.Result, res.Name)
187+
if err != nil {
188+
return nil, err
195189
}
196-
name := ""
197-
if ref, ok := tc.Arg.(*ast.ColumnRef); ok {
198-
name = astutils.Join(ref.Fields, "_")
190+
if typePrecedence[col.DataType] > typePrecedence[chosenType] {
191+
chosenType = col.DataType
199192
}
200-
if res.Name != nil {
201-
name = *res.Name
193+
if !col.NotNull {
194+
chosenNullable = true
202195
}
203-
// TODO Validate column names
204-
col := toColumn(tc.TypeName)
205-
col.Name = name
206-
cols = append(cols, col)
207-
} else if aconst, ok := n.Defresult.(*ast.A_Const); ok {
208-
switch aconst.Val.(type) {
209-
case *ast.String:
210-
cols = append(cols, &Column{Name: name, DataType: "text", NotNull: true})
211-
case *ast.Integer:
212-
cols = append(cols, &Column{Name: name, DataType: "int", NotNull: true})
213-
case *ast.Float:
214-
cols = append(cols, &Column{Name: name, DataType: "float", NotNull: true})
215-
case *ast.Boolean:
216-
cols = append(cols, &Column{Name: name, DataType: "bool", NotNull: true})
217-
default:
218-
cols = append(cols, &Column{Name: name, DataType: "any", NotNull: false})
196+
}
197+
198+
if n.Defresult != nil {
199+
defaultCol, err := convertCaseExprCondToColumn(n.Defresult, res.Name)
200+
if err != nil {
201+
return nil, err
202+
}
203+
if typePrecedence[defaultCol.DataType] > typePrecedence[chosenType] {
204+
chosenType = defaultCol.DataType
205+
}
206+
if !defaultCol.NotNull {
207+
chosenNullable = true
219208
}
220-
} else {
221-
cols = append(cols, &Column{Name: name, DataType: "any", NotNull: false})
222209
}
223210

211+
chosenColumn := &Column{Name: name, DataType: chosenType, NotNull: !chosenNullable}
212+
cols = append(cols, chosenColumn)
213+
224214
case *ast.CoalesceExpr:
225215
name := "coalesce"
226216
if res.Name != nil {
@@ -371,6 +361,7 @@ func (c *Compiler) outputColumns(qc *QueryCatalog, node ast.Node) ([]*Column, er
371361
}
372362
}
373363
cols = append(cols, col)
364+
374365
case *ast.SelectStmt:
375366
subcols, err := c.outputColumns(qc, n)
376367
if err != nil {
@@ -767,3 +758,72 @@ func findColumnForRef(ref *ast.ColumnRef, tables []*Table, targetList *ast.List)
767758

768759
return nil
769760
}
761+
762+
func convertCaseExprCondToColumn(n ast.Node, resTargetName *string) (*Column, error) {
763+
var col *Column
764+
name := ""
765+
if resTargetName != nil {
766+
name = *resTargetName
767+
}
768+
769+
if tc, ok := n.(*ast.TypeCast); ok {
770+
if tc.TypeName == nil {
771+
return nil, errors.New("no type name type cast")
772+
}
773+
if ref, ok := tc.Arg.(*ast.ColumnRef); ok {
774+
name = astutils.Join(ref.Fields, "_")
775+
}
776+
// TODO Validate column names
777+
col = toColumn(tc.TypeName)
778+
779+
if x, ok := tc.Arg.(*ast.A_Const); ok {
780+
if _, ok := x.Val.(*ast.Null); ok {
781+
col.NotNull = false
782+
}
783+
}
784+
col.Name = name
785+
786+
} else if aconst, ok := n.(*ast.A_Const); ok {
787+
col = convertAConstToColumn(aconst, name)
788+
} else if aexpr, ok := n.(*ast.A_Expr); ok {
789+
col = convertAExprToColumn(aexpr, name)
790+
} else {
791+
col = &Column{Name: name, DataType: "any", NotNull: false}
792+
}
793+
794+
return col, nil
795+
}
796+
797+
func convertAExprToColumn(aexpr *ast.A_Expr, name string) *Column {
798+
var col *Column
799+
switch op := astutils.Join(aexpr.Name, ""); {
800+
case lang.IsComparisonOperator(op):
801+
// TODO: Generate a name for these operations
802+
col = &Column{Name: name, DataType: "bool", NotNull: true}
803+
case lang.IsMathematicalOperator(op):
804+
col = &Column{Name: name, DataType: "int", NotNull: true}
805+
case lang.IsJSONOperator(op) && lang.IsJSONResultAsText(op):
806+
col = &Column{Name: name, DataType: "text", NotNull: false}
807+
default:
808+
col = &Column{Name: name, DataType: "any", NotNull: false}
809+
}
810+
811+
return col
812+
}
813+
814+
func convertAConstToColumn(aconst *ast.A_Const, name string) *Column {
815+
var col *Column
816+
switch aconst.Val.(type) {
817+
case *ast.String:
818+
col = &Column{Name: name, DataType: "text", NotNull: true}
819+
case *ast.Integer:
820+
col = &Column{Name: name, DataType: "int", NotNull: true}
821+
case *ast.Float:
822+
col = &Column{Name: name, DataType: "float", NotNull: true}
823+
case *ast.Boolean:
824+
col = &Column{Name: name, DataType: "bool", NotNull: true}
825+
default:
826+
col = &Column{Name: name, DataType: "any", NotNull: false}
827+
}
828+
return col
829+
}

0 commit comments

Comments
 (0)