diff options
Diffstat (limited to 'activerecord/lib/arel/visitors/mssql.rb')
-rw-r--r-- | activerecord/lib/arel/visitors/mssql.rb | 177 |
1 files changed, 89 insertions, 88 deletions
diff --git a/activerecord/lib/arel/visitors/mssql.rb b/activerecord/lib/arel/visitors/mssql.rb index 8347d05d06..4f2945e3a4 100644 --- a/activerecord/lib/arel/visitors/mssql.rb +++ b/activerecord/lib/arel/visitors/mssql.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + module Arel module Visitors class MSSQL < Arel::Visitors::ToSql @@ -11,114 +12,114 @@ module Arel private - # `top` wouldn't really work here. I.e. User.select("distinct first_name").limit(10) would generate - # "select top 10 distinct first_name from users", which is invalid query! it should be - # "select distinct top 10 first_name from users" - def visit_Arel_Nodes_Top o - "" - end - - def visit_Arel_Visitors_MSSQL_RowNumber o, collector - collector << "ROW_NUMBER() OVER (ORDER BY " - inject_join(o.children, collector, ', ') << ") as _row_num" - end + # `top` wouldn't really work here. I.e. User.select("distinct first_name").limit(10) would generate + # "select top 10 distinct first_name from users", which is invalid query! it should be + # "select distinct top 10 first_name from users" + def visit_Arel_Nodes_Top(o) + "" + end - def visit_Arel_Nodes_SelectStatement o, collector - if !o.limit && !o.offset - return super + def visit_Arel_Visitors_MSSQL_RowNumber(o, collector) + collector << "ROW_NUMBER() OVER (ORDER BY " + inject_join(o.children, collector, ", ") << ") as _row_num" end - is_select_count = false - o.cores.each { |x| - core_order_by = row_num_literal determine_order_by(o.orders, x) - if select_count? x - x.projections = [core_order_by] - is_select_count = true - else - x.projections << core_order_by + def visit_Arel_Nodes_SelectStatement(o, collector) + if !o.limit && !o.offset + return super end - } - if is_select_count - # fixme count distinct wouldn't work with limit or offset - collector << "SELECT COUNT(1) as count_id FROM (" - end + is_select_count = false + o.cores.each { |x| + core_order_by = row_num_literal determine_order_by(o.orders, x) + if select_count? x + x.projections = [core_order_by] + is_select_count = true + else + x.projections << core_order_by + end + } + + if is_select_count + # fixme count distinct wouldn't work with limit or offset + collector << "SELECT COUNT(1) as count_id FROM (" + end - collector << "SELECT _t.* FROM (" - collector = o.cores.inject(collector) { |c,x| - visit_Arel_Nodes_SelectCore x, c - } - collector << ") as _t WHERE #{get_offset_limit_clause(o)}" + collector << "SELECT _t.* FROM (" + collector = o.cores.inject(collector) { |c, x| + visit_Arel_Nodes_SelectCore x, c + } + collector << ") as _t WHERE #{get_offset_limit_clause(o)}" - if is_select_count - collector << ") AS subquery" - else - collector + if is_select_count + collector << ") AS subquery" + else + collector + end end - end - def get_offset_limit_clause o - first_row = o.offset ? o.offset.expr.to_i + 1 : 1 - last_row = o.limit ? o.limit.expr.to_i - 1 + first_row : nil - if last_row - " _row_num BETWEEN #{first_row} AND #{last_row}" - else - " _row_num >= #{first_row}" + def get_offset_limit_clause(o) + first_row = o.offset ? o.offset.expr.to_i + 1 : 1 + last_row = o.limit ? o.limit.expr.to_i - 1 + first_row : nil + if last_row + " _row_num BETWEEN #{first_row} AND #{last_row}" + else + " _row_num >= #{first_row}" + end end - end - def visit_Arel_Nodes_DeleteStatement o, collector - collector << 'DELETE ' - if o.limit - collector << 'TOP (' - visit o.limit.expr, collector - collector << ') ' - end - collector << 'FROM ' - collector = visit o.relation, collector - if o.wheres.any? - collector << ' WHERE ' - inject_join o.wheres, collector, AND - else - collector + def visit_Arel_Nodes_DeleteStatement(o, collector) + collector << "DELETE " + if o.limit + collector << "TOP (" + visit o.limit.expr, collector + collector << ") " + end + collector << "FROM " + collector = visit o.relation, collector + if o.wheres.any? + collector << " WHERE " + inject_join o.wheres, collector, AND + else + collector + end end - end - def determine_order_by orders, x - if orders.any? - orders - elsif x.groups.any? - x.groups - else - pk = find_left_table_pk(x.froms) - pk ? [pk] : [] + def determine_order_by(orders, x) + if orders.any? + orders + elsif x.groups.any? + x.groups + else + pk = find_left_table_pk(x.froms) + pk ? [pk] : [] + end end - end - def row_num_literal order_by - RowNumber.new order_by - end + def row_num_literal(order_by) + RowNumber.new order_by + end - def select_count? x - x.projections.length == 1 && Arel::Nodes::Count === x.projections.first - end + def select_count?(x) + x.projections.length == 1 && Arel::Nodes::Count === x.projections.first + end - # FIXME raise exception of there is no pk? - def find_left_table_pk o - if o.kind_of?(Arel::Nodes::Join) - find_left_table_pk(o.left) - elsif o.instance_of?(Arel::Table) - find_primary_key(o) + # FIXME raise exception of there is no pk? + def find_left_table_pk(o) + if o.kind_of?(Arel::Nodes::Join) + find_left_table_pk(o.left) + elsif o.instance_of?(Arel::Table) + find_primary_key(o) + end end - end - def find_primary_key(o) - @primary_keys[o.name] ||= begin - primary_key_name = @connection.primary_key(o.name) - # some tables might be without primary key - primary_key_name && o[primary_key_name] + def find_primary_key(o) + @primary_keys[o.name] ||= begin + primary_key_name = @connection.primary_key(o.name) + # some tables might be without primary key + primary_key_name && o[primary_key_name] + end end - end end end end |