diff --git a/lib/bmg/sql/processor/transform.rb b/lib/bmg/sql/processor/transform.rb index 5765ac8..6c5dc76 100644 --- a/lib/bmg/sql/processor/transform.rb +++ b/lib/bmg/sql/processor/transform.rb @@ -56,12 +56,14 @@ def on_select_list(sexpr) } end + AstAble = ->(t){ t.respond_to?(:to_sql_ast) } + def on_select_item(sexpr) as = sexpr.as_name.to_sym case t = transformation_for(as) when NilClass sexpr - when Class, Array + when AstAble, Class, Array sexpr([:select_item, func_call_node(sexpr, Array(t).reverse), sexpr[2] @@ -83,10 +85,15 @@ def _func_call_node(sexpr, head, tail) else _func_call_node(sexpr, tail.first, tail[1..-1]) end - [:func_call, - :cast, - inside, - [ :literal, head ] ] + case head + when AstAble + head.to_sql_ast(self, inside) + when Class + [:func_call, + :cast, + inside, + [ :literal, head ] ] + end end def transformation_for(as) diff --git a/lib/bmg/sql/relation.rb b/lib/bmg/sql/relation.rb index 0616841..1aa4403 100644 --- a/lib/bmg/sql/relation.rb +++ b/lib/bmg/sql/relation.rb @@ -164,7 +164,8 @@ def _summarize(type, by, defs) def _transform(type, transformation, options) expr = before_use(self.expr) sup, unsup = Processor::Transform.split_supported(transformation){|x| - [String, Integer, Float, Date, DateTime].include?(x) + [String, Integer, Float, Date, DateTime].include?(x) || \ + x.respond_to?(:to_sql_ast) } return super if sup.nil? expr = Processor::Transform.new(sup, options, builder).call(expr) diff --git a/spec/integration/sequel/base/transform.yml b/spec/integration/sequel/base/transform.yml index 986bc98..6ded7a2 100644 --- a/spec/integration/sequel/base/transform.yml +++ b/spec/integration/sequel/base/transform.yml @@ -1,4 +1,3 @@ - --- - bmg: |- supplies.transform(:qty => String) diff --git a/spec/unit/sql/processor/transform/test_on_select_list.rb b/spec/unit/sql/processor/transform/test_on_select_list.rb index 592d96a..95a04a7 100644 --- a/spec/unit/sql/processor/transform/test_on_select_list.rb +++ b/spec/unit/sql/processor/transform/test_on_select_list.rb @@ -4,41 +4,86 @@ module Sql class Processor describe Transform, "on_select_list" do - subject{ - Transform.new({ - :a => Date - }, {}, Builder.new).on_select_list(expr) - } - - let(:expr){ - sexpr [:select_list, - [:select_item, - [:qualified_name, - [:range_var_name, "t1"], + context 'with a Date' do + subject{ + Transform.new({ + :a => Date + }, {}, Builder.new).on_select_list(expr) + } + + let(:expr){ + sexpr [:select_list, + [:select_item, + [:qualified_name, + [:range_var_name, "t1"], + [:column_name, "a"] + ], + [:column_name, "a"] + ] + ] + } + + let(:expected){ + sexpr [:select_list, + [:select_item, + [:func_call, + :cast, + [:qualified_name, + [:range_var_name, "t1"], + [:column_name, "a"] + ], + [ :literal, Date ] + ], [:column_name, "a"] - ], - [:column_name, "a"] + ] ] - ] - } - - let(:expected){ - sexpr [:select_list, - [:select_item, - [:func_call, - :cast, + } + + it{ should eq(expected) } + end + + context 'with a Translatable transformation' do + class Translatable + def to_sql_ast(engine, operand) + [ :func_call, :upcase, operand ] + end + end + + subject{ + Transform.new({ + :a => Translatable.new + }, {}, Builder.new).on_select_list(expr) + } + + let(:expr){ + sexpr [:select_list, + [:select_item, [:qualified_name, [:range_var_name, "t1"], [:column_name, "a"] ], - [ :literal, Date ] - ], - [:column_name, "a"] + [:column_name, "a"] + ] + ] + } + + let(:expected){ + sexpr [:select_list, + [:select_item, + [:func_call, + :upcase, + [:qualified_name, + [:range_var_name, "t1"], + [:column_name, "a"] + ] + ], + [:column_name, "a"] + ] ] - ] - } + } - it{ should eq(expected) } + it{ should eq(expected) } + end end end