arel 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +8 -0
- data/README.markdown +0 -2
- data/Rakefile +2 -2
- data/arel.gemspec +13 -3
- data/lib/arel.rb +2 -2
- data/lib/arel/algebra/relations/relation.rb +3 -2
- data/lib/arel/engines/sql/compilers/ibm_db_compiler.rb +62 -0
- data/lib/arel/engines/sql/compilers/mysql_compiler.rb +11 -0
- data/lib/arel/engines/sql/compilers/oracle_compiler.rb +95 -0
- data/lib/arel/engines/sql/compilers/postgresql_compiler.rb +42 -0
- data/lib/arel/engines/sql/compilers/sqlite_compiler.rb +9 -0
- data/lib/arel/engines/sql/engine.rb +17 -3
- data/lib/arel/engines/sql/formatters.rb +3 -3
- data/lib/arel/engines/sql/relations.rb +1 -0
- data/lib/arel/engines/sql/relations/compiler.rb +118 -0
- data/lib/arel/engines/sql/relations/relation.rb +38 -63
- data/lib/arel/engines/sql/relations/table.rb +23 -3
- data/lib/arel/engines/sql/relations/utilities/externalization.rb +1 -1
- data/lib/arel/engines/sql/relations/writes.rb +4 -71
- data/spec/arel/algebra/unit/relations/relation_spec.rb +1 -2
- data/spec/arel/algebra/unit/relations/table_spec.rb +0 -1
- data/spec/arel/engines/memory/integration/joins/cross_engine_spec.rb +9 -4
- data/spec/arel/engines/sql/integration/joins/with_adjacency_spec.rb +68 -19
- data/spec/arel/engines/sql/integration/joins/with_aggregations_spec.rb +74 -20
- data/spec/arel/engines/sql/integration/joins/with_compounds_spec.rb +33 -3
- data/spec/arel/engines/sql/unit/predicates/binary_spec.rb +22 -2
- data/spec/arel/engines/sql/unit/predicates/equality_spec.rb +15 -3
- data/spec/arel/engines/sql/unit/predicates/in_spec.rb +59 -5
- data/spec/arel/engines/sql/unit/predicates/predicates_spec.rb +12 -0
- data/spec/arel/engines/sql/unit/primitives/attribute_spec.rb +24 -1
- data/spec/arel/engines/sql/unit/primitives/expression_spec.rb +5 -1
- data/spec/arel/engines/sql/unit/primitives/literal_spec.rb +10 -2
- data/spec/arel/engines/sql/unit/relations/alias_spec.rb +11 -1
- data/spec/arel/engines/sql/unit/relations/delete_spec.rb +23 -3
- data/spec/arel/engines/sql/unit/relations/from_spec.rb +16 -2
- data/spec/arel/engines/sql/unit/relations/group_spec.rb +18 -2
- data/spec/arel/engines/sql/unit/relations/having_spec.rb +12 -3
- data/spec/arel/engines/sql/unit/relations/insert_spec.rb +37 -1
- data/spec/arel/engines/sql/unit/relations/join_spec.rb +53 -11
- data/spec/arel/engines/sql/unit/relations/lock_spec.rb +25 -0
- data/spec/arel/engines/sql/unit/relations/order_spec.rb +52 -4
- data/spec/arel/engines/sql/unit/relations/project_spec.rb +38 -5
- data/spec/arel/engines/sql/unit/relations/skip_spec.rb +10 -1
- data/spec/arel/engines/sql/unit/relations/table_spec.rb +26 -5
- data/spec/arel/engines/sql/unit/relations/take_spec.rb +18 -1
- data/spec/arel/engines/sql/unit/relations/update_spec.rb +47 -1
- data/spec/arel/engines/sql/unit/relations/where_spec.rb +18 -2
- data/spec/connections/oracle_connection.rb +19 -0
- data/spec/schemas/mysql_schema.rb +2 -1
- data/spec/schemas/oracle_schema.rb +20 -0
- data/spec/schemas/postgresql_schema.rb +2 -1
- data/spec/schemas/sqlite3_schema.rb +2 -1
- data/spec/spec_helper.rb +16 -7
- metadata +31 -9
| @@ -0,0 +1,118 @@ | |
| 1 | 
            +
            module Arel
         | 
| 2 | 
            +
              module SqlCompiler
         | 
| 3 | 
            +
                class GenericCompiler
         | 
| 4 | 
            +
                  attr_reader :relation
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  def initialize(relation)
         | 
| 7 | 
            +
                    @relation = relation
         | 
| 8 | 
            +
                  end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  def select_sql
         | 
| 11 | 
            +
                    build_query \
         | 
| 12 | 
            +
                      "SELECT     #{select_clauses.join(', ')}",
         | 
| 13 | 
            +
                      "FROM       #{from_clauses}",
         | 
| 14 | 
            +
                      (joins(self)                                   unless joins(self).blank? ),
         | 
| 15 | 
            +
                      ("WHERE     #{where_clauses.join(" AND ")}"    unless wheres.blank?      ),
         | 
| 16 | 
            +
                      ("GROUP BY  #{group_clauses.join(', ')}"       unless groupings.blank?   ),
         | 
| 17 | 
            +
                      ("HAVING    #{having_clauses.join(', ')}"      unless havings.blank?     ),
         | 
| 18 | 
            +
                      ("ORDER BY  #{order_clauses.join(', ')}"       unless orders.blank?      ),
         | 
| 19 | 
            +
                      ("LIMIT     #{taken}"                          unless taken.blank?       ),
         | 
| 20 | 
            +
                      ("OFFSET    #{skipped}"                        unless skipped.blank?     ),
         | 
| 21 | 
            +
                      ("#{locked}"                                   unless locked.blank?)
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  def delete_sql
         | 
| 25 | 
            +
                    build_query \
         | 
| 26 | 
            +
                      "DELETE",
         | 
| 27 | 
            +
                      "FROM #{table_sql}",
         | 
| 28 | 
            +
                      ("WHERE #{wheres.collect(&:to_sql).join(' AND ')}" unless wheres.blank? ),
         | 
| 29 | 
            +
                      (add_limit_on_delete(taken)                        unless taken.blank?  )
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  def add_limit_on_delete(taken)
         | 
| 33 | 
            +
                    "LIMIT #{taken}"
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  def insert_sql(include_returning = true)
         | 
| 37 | 
            +
                    insertion_attributes_values_sql = if record.is_a?(Value)
         | 
| 38 | 
            +
                      record.value
         | 
| 39 | 
            +
                    else
         | 
| 40 | 
            +
                      attributes = record.keys.sort_by do |attribute|
         | 
| 41 | 
            +
                        attribute.name.to_s
         | 
| 42 | 
            +
                      end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                      first = attributes.collect do |key|
         | 
| 45 | 
            +
                        engine.quote_column_name(key.name)
         | 
| 46 | 
            +
                      end.join(', ')
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                      second = attributes.collect do |key|
         | 
| 49 | 
            +
                        key.format(record[key])
         | 
| 50 | 
            +
                      end.join(', ')
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                      build_query "(#{first})", "VALUES (#{second})"
         | 
| 53 | 
            +
                    end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                    build_query \
         | 
| 56 | 
            +
                      "INSERT",
         | 
| 57 | 
            +
                      "INTO #{table_sql}",
         | 
| 58 | 
            +
                      insertion_attributes_values_sql,
         | 
| 59 | 
            +
                      ("RETURNING #{engine.quote_column_name(primary_key)}" if include_returning && compiler.supports_insert_with_returning?)
         | 
| 60 | 
            +
                  end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                  def supports_insert_with_returning?
         | 
| 63 | 
            +
                    false
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                  def update_sql
         | 
| 67 | 
            +
                    build_query \
         | 
| 68 | 
            +
                      "UPDATE #{table_sql} SET",
         | 
| 69 | 
            +
                      assignment_sql,
         | 
| 70 | 
            +
                      build_update_conditions_sql
         | 
| 71 | 
            +
                  end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                protected
         | 
| 74 | 
            +
                  def method_missing(method, *args, &block)
         | 
| 75 | 
            +
                    relation.send(method, *args, &block)
         | 
| 76 | 
            +
                  end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                  def build_query(*parts)
         | 
| 79 | 
            +
                    parts.compact.join(" ")
         | 
| 80 | 
            +
                  end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                  def assignment_sql
         | 
| 83 | 
            +
                    if assignments.respond_to?(:collect)
         | 
| 84 | 
            +
                      attributes = assignments.keys.sort_by do |attribute|
         | 
| 85 | 
            +
                        attribute.name.to_s
         | 
| 86 | 
            +
                      end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                      attributes.map do |attribute|
         | 
| 89 | 
            +
                        value = assignments[attribute]
         | 
| 90 | 
            +
                        "#{engine.quote_column_name(attribute.name)} = #{attribute.format(value)}"
         | 
| 91 | 
            +
                      end.join(", ")
         | 
| 92 | 
            +
                    else
         | 
| 93 | 
            +
                      assignments.value
         | 
| 94 | 
            +
                    end
         | 
| 95 | 
            +
                  end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                  def build_update_conditions_sql
         | 
| 98 | 
            +
                    conditions = ""
         | 
| 99 | 
            +
                    conditions << " WHERE #{wheres.collect(&:to_sql).join(' AND ')}" unless wheres.blank?
         | 
| 100 | 
            +
                    conditions << " ORDER BY #{order_clauses.join(', ')}" unless orders.blank?
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                    unless taken.blank?
         | 
| 103 | 
            +
                      conditions = limited_update_conditions(conditions, taken)
         | 
| 104 | 
            +
                    end
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                    conditions
         | 
| 107 | 
            +
                  end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                  def limited_update_conditions(conditions, taken)
         | 
| 110 | 
            +
                    conditions << " LIMIT #{taken}"
         | 
| 111 | 
            +
                    quoted_primary_key = engine.quote_column_name(primary_key)
         | 
| 112 | 
            +
                    "WHERE #{quoted_primary_key} IN (SELECT #{quoted_primary_key} FROM #{engine.connection.quote_table_name table.name} #{conditions})"
         | 
| 113 | 
            +
                  end
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                end
         | 
| 116 | 
            +
             | 
| 117 | 
            +
              end
         | 
| 118 | 
            +
            end
         | 
| @@ -1,86 +1,61 @@ | |
| 1 1 | 
             
            module Arel
         | 
| 2 2 | 
             
              class Relation
         | 
| 3 | 
            -
                 | 
| 4 | 
            -
                  formatter.select select_sql, self
         | 
| 5 | 
            -
                end
         | 
| 6 | 
            -
             | 
| 7 | 
            -
                def select_sql
         | 
| 8 | 
            -
                  if engine.adapter_name == "PostgreSQL" && !orders.blank? && using_distinct_on?
         | 
| 9 | 
            -
                    # PostgreSQL does not allow arbitrary ordering when using DISTINCT ON, so we work around this
         | 
| 10 | 
            -
                    # by wrapping the +sql+ string as a sub-select and ordering in that query.
         | 
| 11 | 
            -
                    order = order_clauses.join(', ').split(',').map { |s| s.strip }.reject(&:blank?)
         | 
| 12 | 
            -
                    order = order.zip((0...order.size).to_a).map { |s,i| "id_list.alias_#{i} #{'DESC' if s =~ /\bdesc$/i}" }.join(', ')
         | 
| 13 | 
            -
             | 
| 14 | 
            -
                    query = build_query \
         | 
| 15 | 
            -
                      "SELECT     #{select_clauses.kind_of?(::Array) ? select_clauses.join("") : select_clauses.to_s}",
         | 
| 16 | 
            -
                      "FROM       #{from_clauses}",
         | 
| 17 | 
            -
                      (joins(self)                                   unless joins(self).blank? ),
         | 
| 18 | 
            -
                      ("WHERE     #{where_clauses.join(" AND ")}"    unless wheres.blank?      ),
         | 
| 19 | 
            -
                      ("GROUP BY  #{group_clauses.join(', ')}"       unless groupings.blank?   ),
         | 
| 20 | 
            -
                      ("HAVING    #{having_clauses.join(', ')}"      unless havings.blank?   ),
         | 
| 21 | 
            -
                      ("#{locked}"                                   unless locked.blank?     )
         | 
| 3 | 
            +
                @@connection_tables_primary_keys = {}
         | 
| 22 4 |  | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 29 | 
            -
                  else
         | 
| 30 | 
            -
                    build_query \
         | 
| 31 | 
            -
                      "SELECT     #{select_clauses.join(', ')}",
         | 
| 32 | 
            -
                      "FROM       #{from_clauses}",
         | 
| 33 | 
            -
                      (joins(self)                                   unless joins(self).blank? ),
         | 
| 34 | 
            -
                      ("WHERE     #{where_clauses.join(" AND ")}"    unless wheres.blank?      ),
         | 
| 35 | 
            -
                      ("GROUP BY  #{group_clauses.join(', ')}"       unless groupings.blank?   ),
         | 
| 36 | 
            -
                      ("HAVING    #{having_clauses.join(', ')}"      unless havings.blank?     ),
         | 
| 37 | 
            -
                      ("ORDER BY  #{order_clauses.join(', ')}"       unless orders.blank?      ),
         | 
| 38 | 
            -
                      ("LIMIT     #{taken}"                          unless taken.blank?       ),
         | 
| 39 | 
            -
                      ("OFFSET    #{skipped}"                        unless skipped.blank?     ),
         | 
| 40 | 
            -
                      ("#{locked}"                                   unless engine.adapter_name =~ /SQLite/ || locked.blank?)
         | 
| 5 | 
            +
                def compiler
         | 
| 6 | 
            +
                  @compiler ||=  begin
         | 
| 7 | 
            +
                    "Arel::SqlCompiler::#{engine.adapter_name}Compiler".constantize.new(self)
         | 
| 8 | 
            +
                  rescue
         | 
| 9 | 
            +
                    Arel::SqlCompiler::GenericCompiler.new(self)
         | 
| 41 10 | 
             
                  end
         | 
| 42 11 | 
             
                end
         | 
| 43 12 |  | 
| 44 | 
            -
                def  | 
| 45 | 
            -
                   | 
| 13 | 
            +
                def to_sql(formatter = Sql::SelectStatement.new(self))
         | 
| 14 | 
            +
                  formatter.select compiler.select_sql, self
         | 
| 46 15 | 
             
                end
         | 
| 47 16 |  | 
| 48 17 | 
             
                def christener
         | 
| 49 18 | 
             
                  @christener ||= Sql::Christener.new
         | 
| 50 19 | 
             
                end
         | 
| 51 20 |  | 
| 52 | 
            -
             | 
| 53 | 
            -
             | 
| 54 | 
            -
                def build_query(*parts)
         | 
| 55 | 
            -
                  parts.compact.join(" ")
         | 
| 21 | 
            +
                def inclusion_predicate_sql
         | 
| 22 | 
            +
                  "IN"
         | 
| 56 23 | 
             
                end
         | 
| 57 24 |  | 
| 58 | 
            -
                def  | 
| 59 | 
            -
                   | 
| 25 | 
            +
                def primary_key
         | 
| 26 | 
            +
                  connection_id = engine.connection.object_id
         | 
| 27 | 
            +
                  if @@connection_tables_primary_keys[connection_id] && @@connection_tables_primary_keys[connection_id].has_key?(table.name)
         | 
| 28 | 
            +
                    @@connection_tables_primary_keys[connection_id][table.name]
         | 
| 29 | 
            +
                  else
         | 
| 30 | 
            +
                    @@connection_tables_primary_keys[connection_id] ||= {}
         | 
| 31 | 
            +
                    @@connection_tables_primary_keys[connection_id][table.name] = engine.connection.primary_key(table.name)
         | 
| 32 | 
            +
                  end
         | 
| 60 33 | 
             
                end
         | 
| 61 34 |  | 
| 62 | 
            -
                 | 
| 63 | 
            -
                  attributes.collect { |a| a.to_sql(Sql::SelectClause.new(self)) }
         | 
| 64 | 
            -
                end
         | 
| 35 | 
            +
                protected
         | 
| 65 36 |  | 
| 66 | 
            -
             | 
| 67 | 
            -
             | 
| 68 | 
            -
             | 
| 37 | 
            +
                  def from_clauses
         | 
| 38 | 
            +
                    sources.blank? ? table_sql(Sql::TableReference.new(self)) : sources
         | 
| 39 | 
            +
                  end
         | 
| 69 40 |  | 
| 70 | 
            -
             | 
| 71 | 
            -
             | 
| 72 | 
            -
             | 
| 41 | 
            +
                  def select_clauses
         | 
| 42 | 
            +
                    attributes.collect { |a| a.to_sql(Sql::SelectClause.new(self)) }
         | 
| 43 | 
            +
                  end
         | 
| 73 44 |  | 
| 74 | 
            -
             | 
| 75 | 
            -
             | 
| 76 | 
            -
             | 
| 45 | 
            +
                  def where_clauses
         | 
| 46 | 
            +
                    wheres.collect { |w| w.to_sql(Sql::WhereClause.new(self)) }
         | 
| 47 | 
            +
                  end
         | 
| 77 48 |  | 
| 78 | 
            -
             | 
| 79 | 
            -
             | 
| 80 | 
            -
             | 
| 49 | 
            +
                  def group_clauses
         | 
| 50 | 
            +
                    groupings.collect { |g| g.to_sql(Sql::GroupClause.new(self)) }
         | 
| 51 | 
            +
                  end
         | 
| 81 52 |  | 
| 82 | 
            -
             | 
| 83 | 
            -
             | 
| 84 | 
            -
             | 
| 53 | 
            +
                  def having_clauses
         | 
| 54 | 
            +
                    havings.collect { |g| g.to_sql(Sql::HavingClause.new(self)) }
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  def order_clauses
         | 
| 58 | 
            +
                    orders.collect { |o| o.to_sql(Sql::OrderClause.new(self)) }
         | 
| 59 | 
            +
                  end
         | 
| 85 60 | 
             
              end
         | 
| 86 61 | 
             
            end
         | 
| @@ -2,7 +2,7 @@ module Arel | |
| 2 2 | 
             
              class Table < Relation
         | 
| 3 3 | 
             
                include Recursion::BaseCase
         | 
| 4 4 |  | 
| 5 | 
            -
                cattr_accessor :engine
         | 
| 5 | 
            +
                cattr_accessor :engine, :tables
         | 
| 6 6 | 
             
                attr_reader :name, :engine, :table_alias, :options
         | 
| 7 7 |  | 
| 8 8 | 
             
                def initialize(name, options = {})
         | 
| @@ -15,15 +15,35 @@ module Arel | |
| 15 15 | 
             
                  else
         | 
| 16 16 | 
             
                    @engine = options # Table.new('foo', engine)
         | 
| 17 17 | 
             
                  end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  if @engine.connection
         | 
| 20 | 
            +
                    begin
         | 
| 21 | 
            +
                      require "arel/engines/sql/compilers/#{@engine.adapter_name.downcase}_compiler"
         | 
| 22 | 
            +
                      @@tables ||= engine.tables
         | 
| 23 | 
            +
                    rescue LoadError
         | 
| 24 | 
            +
                      raise "#{@engine.adapter_name} is not supported by Arel."
         | 
| 25 | 
            +
                    end
         | 
| 26 | 
            +
                  end
         | 
| 18 27 | 
             
                end
         | 
| 19 28 |  | 
| 20 29 | 
             
                def as(table_alias)
         | 
| 21 30 | 
             
                  Table.new(name, options.merge(:as => table_alias))
         | 
| 22 31 | 
             
                end
         | 
| 23 32 |  | 
| 33 | 
            +
                def table_exists?
         | 
| 34 | 
            +
                  if @table_exists
         | 
| 35 | 
            +
                    true
         | 
| 36 | 
            +
                  else
         | 
| 37 | 
            +
                    @table_exists = @@tables.include?(name) || engine.table_exists?(name)
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
             | 
| 24 41 | 
             
                def attributes
         | 
| 25 | 
            -
                  @attributes  | 
| 26 | 
            -
             | 
| 42 | 
            +
                  return @attributes if defined?(@attributes)
         | 
| 43 | 
            +
                  if table_exists?
         | 
| 44 | 
            +
                    @attributes = columns.collect { |column| Attribute.new(self, column.name.to_sym) }
         | 
| 45 | 
            +
                  else
         | 
| 46 | 
            +
                    []
         | 
| 27 47 | 
             
                  end
         | 
| 28 48 | 
             
                end
         | 
| 29 49 |  | 
| @@ -1,86 +1,19 @@ | |
| 1 1 | 
             
            module Arel
         | 
| 2 2 | 
             
              class Deletion < Compound
         | 
| 3 3 | 
             
                def to_sql
         | 
| 4 | 
            -
                   | 
| 5 | 
            -
                    "DELETE",
         | 
| 6 | 
            -
                    "FROM #{table_sql}",
         | 
| 7 | 
            -
                    ("WHERE #{wheres.collect(&:to_sql).join(' AND ')}" unless wheres.blank? ),
         | 
| 8 | 
            -
                    ("LIMIT #{taken}"                                     unless taken.blank?  )
         | 
| 4 | 
            +
                  compiler.delete_sql
         | 
| 9 5 | 
             
                end
         | 
| 10 6 | 
             
              end
         | 
| 11 7 |  | 
| 12 8 | 
             
              class Insert < Compound
         | 
| 13 | 
            -
                def to_sql
         | 
| 14 | 
            -
                   | 
| 15 | 
            -
                    record.value
         | 
| 16 | 
            -
                  else
         | 
| 17 | 
            -
                    attributes = record.keys.sort_by do |attribute|
         | 
| 18 | 
            -
                      attribute.name.to_s
         | 
| 19 | 
            -
                    end
         | 
| 20 | 
            -
             | 
| 21 | 
            -
                    first = attributes.collect do |key|
         | 
| 22 | 
            -
                      engine.quote_column_name(key.name)
         | 
| 23 | 
            -
                    end.join(', ')
         | 
| 24 | 
            -
             | 
| 25 | 
            -
                    second = attributes.collect do |key|
         | 
| 26 | 
            -
                      key.format(record[key])
         | 
| 27 | 
            -
                    end.join(', ')
         | 
| 28 | 
            -
             | 
| 29 | 
            -
                    build_query "(#{first})", "VALUES (#{second})"
         | 
| 30 | 
            -
                  end
         | 
| 31 | 
            -
             | 
| 32 | 
            -
                  build_query \
         | 
| 33 | 
            -
                    "INSERT",
         | 
| 34 | 
            -
                    "INTO #{table_sql}",
         | 
| 35 | 
            -
                    insertion_attributes_values_sql
         | 
| 9 | 
            +
                def to_sql(include_returning = true)
         | 
| 10 | 
            +
                  compiler.insert_sql(include_returning)
         | 
| 36 11 | 
             
                end
         | 
| 37 12 | 
             
              end
         | 
| 38 13 |  | 
| 39 14 | 
             
              class Update < Compound
         | 
| 40 15 | 
             
                def to_sql
         | 
| 41 | 
            -
                   | 
| 42 | 
            -
                    "UPDATE #{table_sql} SET",
         | 
| 43 | 
            -
                    assignment_sql,
         | 
| 44 | 
            -
                    build_update_conditions_sql
         | 
| 45 | 
            -
                end
         | 
| 46 | 
            -
             | 
| 47 | 
            -
              protected
         | 
| 48 | 
            -
             | 
| 49 | 
            -
                def assignment_sql
         | 
| 50 | 
            -
                  if assignments.respond_to?(:collect)
         | 
| 51 | 
            -
                    attributes = assignments.keys.sort_by do |attribute|
         | 
| 52 | 
            -
                      attribute.name.to_s
         | 
| 53 | 
            -
                    end
         | 
| 54 | 
            -
             | 
| 55 | 
            -
                    attributes.map do |attribute|
         | 
| 56 | 
            -
                      value = assignments[attribute]
         | 
| 57 | 
            -
                      "#{engine.quote_column_name(attribute.name)} = #{attribute.format(value)}"
         | 
| 58 | 
            -
                    end.join(", ")
         | 
| 59 | 
            -
                  else
         | 
| 60 | 
            -
                    assignments.value
         | 
| 61 | 
            -
                  end
         | 
| 62 | 
            -
                end
         | 
| 63 | 
            -
             | 
| 64 | 
            -
                def build_update_conditions_sql
         | 
| 65 | 
            -
                  conditions = ""
         | 
| 66 | 
            -
                  conditions << " WHERE #{wheres.collect(&:to_sql).join(' AND ')}" unless wheres.blank?
         | 
| 67 | 
            -
                  conditions << " ORDER BY #{order_clauses.join(', ')}" unless orders.blank?
         | 
| 68 | 
            -
             | 
| 69 | 
            -
                  unless taken.blank?
         | 
| 70 | 
            -
                    conditions << " LIMIT #{taken}"
         | 
| 71 | 
            -
             | 
| 72 | 
            -
                    if engine.adapter_name != "MySQL"
         | 
| 73 | 
            -
                      begin
         | 
| 74 | 
            -
                        quote_primary_key = engine.quote_column_name(table.name.classify.constantize.primary_key)
         | 
| 75 | 
            -
                      rescue NameError
         | 
| 76 | 
            -
                        quote_primary_key = engine.quote_column_name("id")
         | 
| 77 | 
            -
                      end
         | 
| 78 | 
            -
             | 
| 79 | 
            -
                      conditions =  "WHERE #{quote_primary_key} IN (SELECT #{quote_primary_key} FROM #{engine.connection.quote_table_name table.name} #{conditions})"
         | 
| 80 | 
            -
                    end
         | 
| 81 | 
            -
                  end
         | 
| 82 | 
            -
             | 
| 83 | 
            -
                  conditions
         | 
| 16 | 
            +
                  compiler.update_sql
         | 
| 84 17 | 
             
                end
         | 
| 85 18 | 
             
              end
         | 
| 86 19 | 
             
            end
         | 
| @@ -16,10 +16,9 @@ module Arel | |
| 16 16 | 
             
                  end
         | 
| 17 17 |  | 
| 18 18 | 
             
                  describe 'when given a', Symbol, String do
         | 
| 19 | 
            -
                    it "returns the attribute with the same name | 
| 19 | 
            +
                    it "returns the attribute with the same name" do
         | 
| 20 20 | 
             
                      check @relation[:id].should == @attribute1
         | 
| 21 21 | 
             
                      check @relation['id'].should == @attribute1
         | 
| 22 | 
            -
                      @relation[:does_not_exist].should be_nil
         | 
| 23 22 | 
             
                    end
         | 
| 24 23 | 
             
                  end
         | 
| 25 24 | 
             
                end
         | 
| @@ -10,7 +10,6 @@ module Arel | |
| 10 10 | 
             
                  describe 'when given a', Symbol do
         | 
| 11 11 | 
             
                    it "manufactures an attribute if the symbol names an attribute within the relation" do
         | 
| 12 12 | 
             
                      check @relation[:id].should == Attribute.new(@relation, :id)
         | 
| 13 | 
            -
                      @relation[:does_not_exist].should be_nil
         | 
| 14 13 | 
             
                    end
         | 
| 15 14 | 
             
                  end
         | 
| 16 15 |  | 
| @@ -12,6 +12,11 @@ module Arel | |
| 12 12 | 
             
                  @photos.delete
         | 
| 13 13 | 
             
                  @photos.insert(@photos[:id] => 1, @photos[:user_id] => 1, @photos[:camera_id] => 6)
         | 
| 14 14 | 
             
                  @photos.insert(@photos[:id] => 2, @photos[:user_id] => 2, @photos[:camera_id] => 42)
         | 
| 15 | 
            +
                  # Oracle adapter returns database integers as Ruby integers and not strings
         | 
| 16 | 
            +
                  @adapter_returns_integer = false
         | 
| 17 | 
            +
                  adapter_is :oracle do
         | 
| 18 | 
            +
                    @adapter_returns_integer = true
         | 
| 19 | 
            +
                  end
         | 
| 15 20 | 
             
                end
         | 
| 16 21 |  | 
| 17 22 | 
             
                describe 'when the in memory relation is on the left' do
         | 
| @@ -22,8 +27,8 @@ module Arel | |
| 22 27 | 
             
                      .project(@users[:name], @photos[:camera_id]) \
         | 
| 23 28 | 
             
                    .let do |relation|
         | 
| 24 29 | 
             
                      relation.call.should == [
         | 
| 25 | 
            -
                        Row.new(relation, ['bryan', '6']),
         | 
| 26 | 
            -
                        Row.new(relation, ['emilio', '42'])
         | 
| 30 | 
            +
                        Row.new(relation, ['bryan', @adapter_returns_integer ? 6 : '6']),
         | 
| 31 | 
            +
                        Row.new(relation, ['emilio', @adapter_returns_integer ? 42 : '42'])
         | 
| 27 32 | 
             
                      ]
         | 
| 28 33 | 
             
                    end
         | 
| 29 34 | 
             
                  end
         | 
| @@ -37,8 +42,8 @@ module Arel | |
| 37 42 | 
             
                      .project(@users[:name], @photos[:camera_id]) \
         | 
| 38 43 | 
             
                    .let do |relation|
         | 
| 39 44 | 
             
                      relation.call.should == [
         | 
| 40 | 
            -
                        Row.new(relation, ['bryan', '6']),
         | 
| 41 | 
            -
                        Row.new(relation, ['emilio', '42'])
         | 
| 45 | 
            +
                        Row.new(relation, ['bryan', @adapter_returns_integer ? 6 : '6']),
         | 
| 46 | 
            +
                        Row.new(relation, ['emilio', @adapter_returns_integer ? 42 : '42'])
         | 
| 42 47 | 
             
                      ]
         | 
| 43 48 | 
             
                    end
         | 
| 44 49 | 
             
                  end
         |