Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Changeset 8571

Show
Ignore:
Timestamp:
01/05/08 14:58:28 (1 year ago)
Author:
bitsweat
Message:

More thoroughly quote table names. Exposes some issues with sqlite2 adapter. Closes #10698.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/activerecord/CHANGELOG

    r8554 r8571  
    11*SVN* 
     2 
     3* More thoroughly quote table names.  #10698 [dimdenis, lotswholetime, Jeremy Kemper] 
    24 
    35* update_all ignores scoped :order and :limit, so post.comments.update_all doesn't try to include the comment order in the update statement.  #10686 [Brendan Ribera] 
  • trunk/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb

    r8481 r8571  
    101101 
    102102            sql = 
    103               "INSERT INTO #{@reflection.options[:join_table]} (#{@owner.send(:quoted_column_names, attributes).join(', ')}) " + 
     103              "INSERT INTO #{@owner.connection.quote_table_name @reflection.options[:join_table]} (#{@owner.send(:quoted_column_names, attributes).join(', ')}) " + 
    104104              "VALUES (#{attributes.values.join(', ')})" 
    105105 
     
    115115          else 
    116116            ids = quoted_record_ids(records) 
    117             sql = "DELETE FROM #{@reflection.options[:join_table]} WHERE #{@reflection.primary_key_name} = #{@owner.quoted_id} AND #{@reflection.association_foreign_key} IN (#{ids})" 
     117            sql = "DELETE FROM #{@owner.connection.quote_table_name @reflection.options[:join_table]} WHERE #{@reflection.primary_key_name} = #{@owner.quoted_id} AND #{@reflection.association_foreign_key} IN (#{ids})" 
    118118            @owner.connection.execute(sql) 
    119119          end 
     
    126126            @finder_sql = @reflection.options[:finder_sql] 
    127127          else 
    128             @finder_sql = "#{@reflection.options[:join_table]}.#{@reflection.primary_key_name} = #{@owner.quoted_id} " 
     128            @finder_sql = "#{@owner.connection.quote_table_name @reflection.options[:join_table]}.#{@reflection.primary_key_name} = #{@owner.quoted_id} " 
    129129            @finder_sql << " AND (#{conditions})" if conditions 
    130130          end 
    131131 
    132           @join_sql = "INNER JOIN #{@reflection.options[:join_table]} ON #{@reflection.klass.table_name}.#{@reflection.klass.primary_key} = #{@reflection.options[:join_table]}.#{@reflection.association_foreign_key}" 
     132          @join_sql = "INNER JOIN #{@owner.connection.quote_table_name @reflection.options[:join_table]} ON #{@reflection.quoted_table_name}.#{@reflection.klass.primary_key} = #{@owner.connection.quote_table_name @reflection.options[:join_table]}.#{@reflection.association_foreign_key}" 
    133133        end 
    134134 
  • trunk/activerecord/lib/active_record/associations/has_many_association.rb

    r8481 r8571  
    145145            when @reflection.options[:as] 
    146146              @finder_sql =  
    147                 "#{@reflection.klass.table_name}.#{@reflection.options[:as]}_id = #{@owner.quoted_id} AND " +  
    148                 "#{@reflection.klass.table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}" 
     147                "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_id = #{@owner.quoted_id} AND " + 
     148                "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}" 
    149149              @finder_sql << " AND (#{conditions})" if conditions 
    150150             
    151151            else 
    152               @finder_sql = "#{@reflection.klass.table_name}.#{@reflection.primary_key_name} = #{@owner.quoted_id}" 
     152              @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{@owner.quoted_id}" 
    153153              @finder_sql << " AND (#{conditions})" if conditions 
    154154          end 
  • trunk/activerecord/lib/active_record/associations/has_many_through_association.rb

    r8481 r8571  
    122122        if @reflection.options[:uniq] 
    123123          # This is needed because 'SELECT count(DISTINCT *)..' is not valid sql statement. 
    124           column_name = "#{@reflection.klass.table_name}.#{@reflection.klass.primary_key}" if column_name == :all 
     124          column_name = "#{@reflection.quoted_table_name}.#{@reflection.klass.primary_key}" if column_name == :all 
    125125          options.merge!(:distinct => true)  
    126126        end 
     
    186186        # Build SQL conditions from attributes, qualified by table name. 
    187187        def construct_conditions 
    188           table_name = @reflection.through_reflection.table_name 
     188          table_name = @reflection.through_reflection.quoted_table_name 
    189189          conditions = construct_quoted_owner_attributes(@reflection.through_reflection).map do |attr, value| 
    190190            "#{table_name}.#{attr} = #{value}" 
     
    195195 
    196196        def construct_from 
    197           @reflection.table_name 
     197          @reflection.quoted_table_name 
    198198        end 
    199199 
    200200        def construct_select(custom_select = nil) 
    201           selected = custom_select || @reflection.options[:select] || "#{@reflection.table_name}.*" 
     201          selected = custom_select || @reflection.options[:select] || "#{@reflection.quoted_table_name}.*" 
    202202        end 
    203203 
     
    209209            if @reflection.options[:source_type] 
    210210              polymorphic_join = "AND %s.%s = %s" % [ 
    211                 @reflection.through_reflection.table_name, "#{@reflection.source_reflection.options[:foreign_type]}", 
     211                @reflection.through_reflection.quoted_table_name, "#{@reflection.source_reflection.options[:foreign_type]}", 
    212212                @owner.class.quote_value(@reflection.options[:source_type]) 
    213213              ] 
     
    218218            if @reflection.source_reflection.options[:as] 
    219219              polymorphic_join = "AND %s.%s = %s" % [ 
    220                 @reflection.table_name, "#{@reflection.source_reflection.options[:as]}_type", 
     220                @reflection.quoted_table_name, "#{@reflection.source_reflection.options[:as]}_type", 
    221221                @owner.class.quote_value(@reflection.through_reflection.klass.name) 
    222222              ] 
     
    247247              @finder_sql = interpolate_sql(@reflection.options[:finder_sql]) 
    248248 
    249               @finder_sql = "#{@reflection.klass.table_name}.#{@reflection.primary_key_name} = #{@owner.quoted_id}" 
     249              @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{@owner.quoted_id}" 
    250250              @finder_sql << " AND (#{conditions})" if conditions 
    251251          end 
     
    287287 
    288288        def build_sti_condition 
    289           "#{@reflection.through_reflection.table_name}.#{@reflection.through_reflection.klass.inheritance_column} = #{@reflection.klass.quote_value(@reflection.through_reflection.klass.name.demodulize)}" 
     289          "#{@reflection.through_reflection.quoted_table_name}.#{@reflection.through_reflection.klass.inheritance_column} = #{@reflection.klass.quote_value(@reflection.through_reflection.klass.name.demodulize)}" 
    290290        end 
    291291 
  • trunk/activerecord/lib/active_record/associations/has_one_association.rb

    r7368 r8571  
    6262            when @reflection.options[:as] 
    6363              @finder_sql =  
    64                 "#{@reflection.klass.table_name}.#{@reflection.options[:as]}_id = #{@owner.quoted_id} AND " +  
    65                 "#{@reflection.klass.table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}"           
     64                "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_id = #{@owner.quoted_id} AND " + 
     65                "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}" 
    6666            else 
    67               @finder_sql = "#{@reflection.table_name}.#{@reflection.primary_key_name} = #{@owner.quoted_id}" 
     67              @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{@owner.quoted_id}" 
    6868          end 
    6969          @finder_sql << " AND (#{conditions})" if conditions 
  • trunk/activerecord/lib/active_record/base.rb

    r8554 r8571  
    678678      #                         :order => 'created_at', :limit => 5 ) 
    679679      def update_all(updates, conditions = nil, options = {}) 
    680         sql  = "UPDATE #{table_name} SET #{sanitize_sql_for_assignment(updates)} " 
     680        sql  = "UPDATE #{quoted_table_name} SET #{sanitize_sql_for_assignment(updates)} " 
    681681        scope = scope(:find) 
    682682        add_conditions!(sql, conditions, scope) 
  • trunk/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb

    r8481 r8571  
    6767      # QUOTING ================================================== 
    6868 
    69       # Override to return the quoted table name if the database needs it 
     69      # Override to return the quoted column name. Defaults to no quoting. 
    7070      def quote_table_name(name) 
    7171        name 
     72      end 
     73 
     74      # Override to return the quoted table name. Defaults to column quoting. 
     75      def quote_table_name(name) 
     76        quote_column_name(name) 
    7277      end 
    7378 
  • trunk/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb

    r8254 r8571  
    530530        if pk 
    531531          if sequence 
     532            quoted_sequence = quote_column_name(sequence) 
     533 
    532534            select_value <<-end_sql, 'Reset sequence' 
    533               SELECT setval('#{sequence}', (SELECT COALESCE(MAX(#{pk})+(SELECT increment_by FROM #{sequence}), (SELECT min_value FROM #{sequence})) FROM #{table}), false) 
     535              SELECT setval('#{sequence}', (SELECT COALESCE(MAX(#{pk})+(SELECT increment_by FROM #{quoted_sequence}), (SELECT min_value FROM #{quoted_sequence})) FROM #{quote_table_name(table)}), false) 
    534536            end_sql 
    535537          else 
     
    592594 
    593595        # Add the column. 
    594         execute("ALTER TABLE #{table_name} ADD COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit])}") 
     596        execute("ALTER TABLE #{quote_table_name(table_name)} ADD COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit])}") 
    595597 
    596598        change_column_default(table_name, column_name, default) if options_include_default?(options) 
     
    600602      # Changes the column of a table. 
    601603      def change_column(table_name, column_name, type, options = {}) 
     604        quoted_table_name = quote_table_name(table_name) 
     605 
    602606        begin 
    603           execute "ALTER TABLE #{table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" 
     607          execute "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" 
    604608        rescue ActiveRecord::StatementInvalid 
    605609          # This is PostgreSQL 7.x, so we have to use a more arcane way of doing it. 
     
    607611          tmp_column_name = "#{column_name}_ar_tmp" 
    608612          add_column(table_name, tmp_column_name, type, options) 
    609           execute "UPDATE #{table_name} SET #{quote_column_name(tmp_column_name)} = CAST(#{quote_column_name(column_name)} AS #{type_to_sql(type, options[:limit], options[:precision], options[:scale])})" 
     613          execute "UPDATE #{quoted_table_name} SET #{quote_column_name(tmp_column_name)} = CAST(#{quote_column_name(column_name)} AS #{type_to_sql(type, options[:limit], options[:precision], options[:scale])})" 
    610614          remove_column(table_name, column_name) 
    611615          rename_column(table_name, tmp_column_name, column_name) 
     
    619623      # Changes the default value of a table column. 
    620624      def change_column_default(table_name, column_name, default) 
    621         execute "ALTER TABLE #{table_name} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote(default)}" 
     625        execute "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote(default)}" 
    622626      end 
    623627 
    624628      def change_column_null(table_name, column_name, null, default = nil) 
    625629        unless null || default.nil? 
    626           execute("UPDATE #{table_name} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL") 
    627         end 
    628         execute("ALTER TABLE #{table_name} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL") 
     630          execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL") 
     631        end 
     632        execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL") 
    629633      end 
    630634 
    631635      # Renames a column in a table. 
    632636      def rename_column(table_name, column_name, new_column_name) 
    633         execute "ALTER TABLE #{table_name} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}" 
     637        execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}" 
    634638      end 
    635639 
  • trunk/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb

    r8453 r8571  
    193193 
    194194      def indexes(table_name, name = nil) #:nodoc: 
    195         execute("PRAGMA index_list(#{table_name})", name).map do |row| 
     195        execute("PRAGMA index_list(#{quote_table_name(table_name)})", name).map do |row| 
    196196          index = IndexDefinition.new(table_name, row['name']) 
    197197          index.unique = row['unique'] != '0' 
     
    266266 
    267267        def table_structure(table_name) 
    268           returning structure = execute("PRAGMA table_info(#{table_name})") do 
     268          returning structure = execute("PRAGMA table_info(#{quote_table_name(table_name)})") do 
    269269            raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty? 
    270270          end 
     
    341341          quoted_columns = columns.map { |col| quote_column_name(col) } * ',' 
    342342 
    343           @connection.execute "SELECT * FROM #{from}" do |row| 
    344             sql = "INSERT INTO #{to} (#{quoted_columns}) VALUES (" 
     343          quoted_to = quote_table_name(to) 
     344          @connection.execute "SELECT * FROM #{quote_table_name(from)}" do |row| 
     345            sql = "INSERT INTO #{quoted_to} (#{quoted_columns}) VALUES (" 
    345346            sql << columns.map {|col| quote row[column_mappings[col]]} * ', ' 
    346347            sql << ')' 
  • trunk/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb

    r6833 r8571  
    2626    class SQLite3Adapter < SQLiteAdapter # :nodoc: 
    2727      def table_structure(table_name) 
    28         returning structure = @connection.table_info(table_name) do 
     28        returning structure = @connection.table_info(quote_table_name(table_name)) do 
    2929          raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty? 
    3030        end 
  • trunk/activerecord/lib/active_record/reflection.rb

    r8207 r8571  
    130130      end 
    131131 
     132      def quoted_table_name 
     133        @quoted_table_name ||= klass.quoted_table_name 
     134      end 
     135 
    132136      def primary_key_name 
    133137        @primary_key_name ||= options[:foreign_key] || derive_primary_key_name 
  • trunk/activerecord/lib/active_record/validations.rb

    r8379 r8571  
    654654        validates_each(attr_names,configuration) do |record, attr_name, value| 
    655655          if value.nil? || (configuration[:case_sensitive] || !columns_hash[attr_name.to_s].text?) 
    656             condition_sql = "#{record.class.table_name}.#{attr_name} #{attribute_condition(value)}" 
     656            condition_sql = "#{record.class.quoted_table_name}.#{attr_name} #{attribute_condition(value)}" 
    657657            condition_params = [value] 
    658658          else 
    659             condition_sql = "LOWER(#{record.class.table_name}.#{attr_name}) #{attribute_condition(value)}" 
     659            condition_sql = "LOWER(#{record.class.quoted_table_name}.#{attr_name}) #{attribute_condition(value)}" 
    660660            condition_params = [value.downcase] 
    661661          end 
     
    664664            Array(scope).map do |scope_item| 
    665665              scope_value = record.send(scope_item) 
    666               condition_sql << " AND #{record.class.table_name}.#{scope_item} #{attribute_condition(scope_value)}" 
     666              condition_sql << " AND #{record.class.quoted_table_name}.#{scope_item} #{attribute_condition(scope_value)}" 
    667667              condition_params << scope_value 
    668668            end 
     
    670670 
    671671          unless record.new_record? 
    672             condition_sql << " AND #{record.class.table_name}.#{record.class.primary_key} <> ?" 
     672            condition_sql << " AND #{record.class.quoted_table_name}.#{record.class.primary_key} <> ?" 
    673673            condition_params << record.send(:id) 
    674674          end 
  • trunk/activerecord/test/associations/inner_join_association_test.rb

    r8570 r8571  
    1111  def test_construct_finder_sql_creates_inner_joins 
    1212    sql = Author.send(:construct_finder_sql, :joins => :posts) 
    13     assert_match /INNER JOIN `?posts`? ON `?posts`?.author_id = authors.id/, sql 
     13    assert_match /INNER JOIN .?posts.? ON .?posts.?.author_id = authors.id/, sql 
    1414  end 
    1515   
    1616  def test_construct_finder_sql_cascades_inner_joins 
    1717    sql = Author.send(:construct_finder_sql, :joins => {:posts => :comments}) 
    18     assert_match /INNER JOIN `?posts`? ON `?posts`?.author_id = authors.id/, sql 
    19     assert_match /INNER JOIN `?comments`? ON `?comments`?.post_id = posts.id/, sql 
     18    assert_match /INNER JOIN .?posts.? ON .?posts.?.author_id = authors.id/, sql 
     19    assert_match /INNER JOIN .?comments.? ON .?comments.?.post_id = posts.id/, sql 
    2020  end 
    2121   
    2222  def test_construct_finder_sql_inner_joins_through_associations 
    2323    sql = Author.send(:construct_finder_sql, :joins => :categorized_posts) 
    24     assert_match /INNER JOIN `?categorizations`?.*INNER JOIN `?posts`?/, sql 
     24    assert_match /INNER JOIN .?categorizations.?.*INNER JOIN .?posts.?/, sql 
    2525  end 
    2626   
    2727  def test_construct_finder_sql_applies_association_conditions 
    2828    sql = Author.send(:construct_finder_sql, :joins => :categories_like_general, :conditions => "TERMINATING_MARKER") 
    29     assert_match /INNER JOIN `?categories`? ON.*AND.*`?General`?.*TERMINATING_MARKER/, sql 
     29    assert_match /INNER JOIN .?categories.? ON.*AND.*.?General.?.*TERMINATING_MARKER/, sql 
    3030  end 
    3131 
     
    3333    sql = Author.send(:construct_finder_sql, :joins => {:posts => [[:comments]]}) 
    3434    assert_no_match /inner join.*inner join.*inner join/i, sql, "only two join clauses should be present" 
    35     assert_match /INNER JOIN `?posts`? ON `?posts`?.author_id = authors.id/, sql 
    36     assert_match /INNER JOIN `?comments`? ON `?comments`?.post_id = `?posts`?.id/, sql 
     35    assert_match /INNER JOIN .?posts.? ON .?posts.?.author_id = authors.id/, sql 
     36    assert_match /INNER JOIN .?comments.? ON .?comments.?.post_id = .?posts.?.id/, sql 
    3737  end 
    3838 
  • trunk/activerecord/test/base_test.rb

    r8570 r8571  
    1313require 'fixtures/post' 
    1414require 'fixtures/minimalistic' 
     15require 'fixtures/warehouse_thing' 
    1516require 'rexml/document' 
    1617 
     
    7273 
    7374class BasicsTest < ActiveSupport::TestCase 
    74   fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics 
     75  fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things' 
    7576 
    7677  def test_table_exists 
     
    591592  end 
    592593 
     594  def test_update_all_with_non_standard_table_name 
     595    assert_equal 1, WarehouseThing.update_all(['value = ?', 0], ['id = ?', 1]) 
     596    assert_equal 0, WarehouseThing.find(1).value 
     597  end 
     598 
    593599  if current_adapter?(:MysqlAdapter) 
    594600    def test_update_all_with_order_and_limit 
  • trunk/activerecord/test/fixtures/db_definitions/schema.rb

    r8219 r8571  
    352352    t.datetime :updated_on 
    353353  end 
     354 
     355  create_table 'warehouse-things', :force => true do |t| 
     356    t.integer :value 
     357  end 
    354358end 
  • trunk/activerecord/test/validations_test.rb

    r8570 r8571  
    44require 'fixtures/person' 
    55require 'fixtures/developer' 
     6require 'fixtures/warehouse_thing' 
    67 
    78# The following methods in Topic are used in test_conditional_validation_* 
     
    5556 
    5657class ValidationsTest < ActiveSupport::TestCase 
    57   fixtures :topics, :developers 
     58  fixtures :topics, :developers, 'warehouse-things' 
    5859 
    5960  def setup 
     
    435436    assert t2.save, "should save with nil" 
    436437  end 
     438 
     439  def test_validate_uniqueness_with_non_standard_table_names 
     440    i1 = WarehouseThing.create(:value => 1000) 
     441    assert !i1.valid?, "i1 should not be valid" 
     442    assert i1.errors.on(:value), "Should not be empty" 
     443  end 
     444 
    437445 
    438446  def test_validate_straight_inheritance_uniqueness