@@ -132,33 +132,14 @@ func (c *Compiler) outputColumns(qc *QueryCatalog, node ast.Node) ([]*Column, er
132
132
if res .Name != nil {
133
133
name = * res .Name
134
134
}
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 ))
147
136
148
137
case * ast.A_Expr :
149
138
name := ""
150
139
if res .Name != nil {
151
140
name = * res .Name
152
141
}
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 ))
162
143
163
144
case * ast.BoolExpr :
164
145
name := ""
@@ -187,40 +168,49 @@ func (c *Compiler) outputColumns(qc *QueryCatalog, node ast.Node) ([]*Column, er
187
168
if res .Name != nil {
188
169
name = * res .Name
189
170
}
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
195
189
}
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
199
192
}
200
- if res . Name != nil {
201
- name = * res . Name
193
+ if ! col . NotNull {
194
+ chosenNullable = true
202
195
}
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
219
208
}
220
- } else {
221
- cols = append (cols , & Column {Name : name , DataType : "any" , NotNull : false })
222
209
}
223
210
211
+ chosenColumn := & Column {Name : name , DataType : chosenType , NotNull : ! chosenNullable }
212
+ cols = append (cols , chosenColumn )
213
+
224
214
case * ast.CoalesceExpr :
225
215
name := "coalesce"
226
216
if res .Name != nil {
@@ -371,6 +361,7 @@ func (c *Compiler) outputColumns(qc *QueryCatalog, node ast.Node) ([]*Column, er
371
361
}
372
362
}
373
363
cols = append (cols , col )
364
+
374
365
case * ast.SelectStmt :
375
366
subcols , err := c .outputColumns (qc , n )
376
367
if err != nil {
@@ -767,3 +758,72 @@ func findColumnForRef(ref *ast.ColumnRef, tables []*Table, targetList *ast.List)
767
758
768
759
return nil
769
760
}
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