@@ -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