Changeset 8571
- Timestamp:
- 01/05/08 14:58:28 (1 year ago)
- Files:
-
- trunk/activerecord/CHANGELOG (modified) (1 diff)
- trunk/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb (modified) (3 diffs)
- trunk/activerecord/lib/active_record/associations/has_many_association.rb (modified) (1 diff)
- trunk/activerecord/lib/active_record/associations/has_many_through_association.rb (modified) (7 diffs)
- trunk/activerecord/lib/active_record/associations/has_one_association.rb (modified) (1 diff)
- trunk/activerecord/lib/active_record/base.rb (modified) (1 diff)
- trunk/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb (modified) (1 diff)
- trunk/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb (modified) (5 diffs)
- trunk/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb (modified) (3 diffs)
- trunk/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb (modified) (1 diff)
- trunk/activerecord/lib/active_record/reflection.rb (modified) (1 diff)
- trunk/activerecord/lib/active_record/validations.rb (modified) (3 diffs)
- trunk/activerecord/test/associations/inner_join_association_test.rb (modified) (2 diffs)
- trunk/activerecord/test/base_test.rb (modified) (3 diffs)
- trunk/activerecord/test/fixtures/db_definitions/schema.rb (modified) (1 diff)
- trunk/activerecord/test/fixtures/warehouse_thing.rb (added)
- trunk/activerecord/test/fixtures/warehouse-things.yml (added)
- trunk/activerecord/test/validations_test.rb (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/activerecord/CHANGELOG
r8554 r8571 1 1 *SVN* 2 3 * More thoroughly quote table names. #10698 [dimdenis, lotswholetime, Jeremy Kemper] 2 4 3 5 * 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 101 101 102 102 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(', ')}) " + 104 104 "VALUES (#{attributes.values.join(', ')})" 105 105 … … 115 115 else 116 116 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})" 118 118 @owner.connection.execute(sql) 119 119 end … … 126 126 @finder_sql = @reflection.options[:finder_sql] 127 127 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} " 129 129 @finder_sql << " AND (#{conditions})" if conditions 130 130 end 131 131 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}" 133 133 end 134 134 trunk/activerecord/lib/active_record/associations/has_many_association.rb
r8481 r8571 145 145 when @reflection.options[:as] 146 146 @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)}" 149 149 @finder_sql << " AND (#{conditions})" if conditions 150 150 151 151 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}" 153 153 @finder_sql << " AND (#{conditions})" if conditions 154 154 end trunk/activerecord/lib/active_record/associations/has_many_through_association.rb
r8481 r8571 122 122 if @reflection.options[:uniq] 123 123 # 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 == :all124 column_name = "#{@reflection.quoted_table_name}.#{@reflection.klass.primary_key}" if column_name == :all 125 125 options.merge!(:distinct => true) 126 126 end … … 186 186 # Build SQL conditions from attributes, qualified by table name. 187 187 def construct_conditions 188 table_name = @reflection.through_reflection. table_name188 table_name = @reflection.through_reflection.quoted_table_name 189 189 conditions = construct_quoted_owner_attributes(@reflection.through_reflection).map do |attr, value| 190 190 "#{table_name}.#{attr} = #{value}" … … 195 195 196 196 def construct_from 197 @reflection. table_name197 @reflection.quoted_table_name 198 198 end 199 199 200 200 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}.*" 202 202 end 203 203 … … 209 209 if @reflection.options[:source_type] 210 210 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]}", 212 212 @owner.class.quote_value(@reflection.options[:source_type]) 213 213 ] … … 218 218 if @reflection.source_reflection.options[:as] 219 219 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", 221 221 @owner.class.quote_value(@reflection.through_reflection.klass.name) 222 222 ] … … 247 247 @finder_sql = interpolate_sql(@reflection.options[:finder_sql]) 248 248 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}" 250 250 @finder_sql << " AND (#{conditions})" if conditions 251 251 end … … 287 287 288 288 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)}" 290 290 end 291 291 trunk/activerecord/lib/active_record/associations/has_one_association.rb
r7368 r8571 62 62 when @reflection.options[:as] 63 63 @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)}" 66 66 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}" 68 68 end 69 69 @finder_sql << " AND (#{conditions})" if conditions trunk/activerecord/lib/active_record/base.rb
r8554 r8571 678 678 # :order => 'created_at', :limit => 5 ) 679 679 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)} " 681 681 scope = scope(:find) 682 682 add_conditions!(sql, conditions, scope) trunk/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
r8481 r8571 67 67 # QUOTING ================================================== 68 68 69 # Override to return the quoted table name if the database needs it69 # Override to return the quoted column name. Defaults to no quoting. 70 70 def quote_table_name(name) 71 71 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) 72 77 end 73 78 trunk/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
r8254 r8571 530 530 if pk 531 531 if sequence 532 quoted_sequence = quote_column_name(sequence) 533 532 534 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) 534 536 end_sql 535 537 else … … 592 594 593 595 # 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])}") 595 597 596 598 change_column_default(table_name, column_name, default) if options_include_default?(options) … … 600 602 # Changes the column of a table. 601 603 def change_column(table_name, column_name, type, options = {}) 604 quoted_table_name = quote_table_name(table_name) 605 602 606 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])}" 604 608 rescue ActiveRecord::StatementInvalid 605 609 # This is PostgreSQL 7.x, so we have to use a more arcane way of doing it. … … 607 611 tmp_column_name = "#{column_name}_ar_tmp" 608 612 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])})" 610 614 remove_column(table_name, column_name) 611 615 rename_column(table_name, tmp_column_name, column_name) … … 619 623 # Changes the default value of a table column. 620 624 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)}" 622 626 end 623 627 624 628 def change_column_null(table_name, column_name, null, default = nil) 625 629 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") 629 633 end 630 634 631 635 # Renames a column in a table. 632 636 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)}" 634 638 end 635 639 trunk/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
r8453 r8571 193 193 194 194 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| 196 196 index = IndexDefinition.new(table_name, row['name']) 197 197 index.unique = row['unique'] != '0' … … 266 266 267 267 def table_structure(table_name) 268 returning structure = execute("PRAGMA table_info(#{ table_name})") do268 returning structure = execute("PRAGMA table_info(#{quote_table_name(table_name)})") do 269 269 raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty? 270 270 end … … 341 341 quoted_columns = columns.map { |col| quote_column_name(col) } * ',' 342 342 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 (" 345 346 sql << columns.map {|col| quote row[column_mappings[col]]} * ', ' 346 347 sql << ')' trunk/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
r6833 r8571 26 26 class SQLite3Adapter < SQLiteAdapter # :nodoc: 27 27 def table_structure(table_name) 28 returning structure = @connection.table_info( table_name) do28 returning structure = @connection.table_info(quote_table_name(table_name)) do 29 29 raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty? 30 30 end trunk/activerecord/lib/active_record/reflection.rb
r8207 r8571 130 130 end 131 131 132 def quoted_table_name 133 @quoted_table_name ||= klass.quoted_table_name 134 end 135 132 136 def primary_key_name 133 137 @primary_key_name ||= options[:foreign_key] || derive_primary_key_name trunk/activerecord/lib/active_record/validations.rb
r8379 r8571 654 654 validates_each(attr_names,configuration) do |record, attr_name, value| 655 655 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)}" 657 657 condition_params = [value] 658 658 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)}" 660 660 condition_params = [value.downcase] 661 661 end … … 664 664 Array(scope).map do |scope_item| 665 665 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)}" 667 667 condition_params << scope_value 668 668 end … … 670 670 671 671 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} <> ?" 673 673 condition_params << record.send(:id) 674 674 end trunk/activerecord/test/associations/inner_join_association_test.rb
r8570 r8571 11 11 def test_construct_finder_sql_creates_inner_joins 12 12 sql = Author.send(:construct_finder_sql, :joins => :posts) 13 assert_match /INNER JOIN `?posts`? ON `?posts`?.author_id = authors.id/, sql13 assert_match /INNER JOIN .?posts.? ON .?posts.?.author_id = authors.id/, sql 14 14 end 15 15 16 16 def test_construct_finder_sql_cascades_inner_joins 17 17 sql = Author.send(:construct_finder_sql, :joins => {:posts => :comments}) 18 assert_match /INNER JOIN `?posts`? ON `?posts`?.author_id = authors.id/, sql19 assert_match /INNER JOIN `?comments`? ON `?comments`?.post_id = posts.id/, sql18 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 20 20 end 21 21 22 22 def test_construct_finder_sql_inner_joins_through_associations 23 23 sql = Author.send(:construct_finder_sql, :joins => :categorized_posts) 24 assert_match /INNER JOIN `?categorizations`?.*INNER JOIN `?posts`?/, sql24 assert_match /INNER JOIN .?categorizations.?.*INNER JOIN .?posts.?/, sql 25 25 end 26 26 27 27 def test_construct_finder_sql_applies_association_conditions 28 28 sql = Author.send(:construct_finder_sql, :joins => :categories_like_general, :conditions => "TERMINATING_MARKER") 29 assert_match /INNER JOIN `?categories`? ON.*AND.*`?General`?.*TERMINATING_MARKER/, sql29 assert_match /INNER JOIN .?categories.? ON.*AND.*.?General.?.*TERMINATING_MARKER/, sql 30 30 end 31 31 … … 33 33 sql = Author.send(:construct_finder_sql, :joins => {:posts => [[:comments]]}) 34 34 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/, sql36 assert_match /INNER JOIN `?comments`? ON `?comments`?.post_id = `?posts`?.id/, sql35 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 37 37 end 38 38 trunk/activerecord/test/base_test.rb
r8570 r8571 13 13 require 'fixtures/post' 14 14 require 'fixtures/minimalistic' 15 require 'fixtures/warehouse_thing' 15 16 require 'rexml/document' 16 17 … … 72 73 73 74 class BasicsTest < ActiveSupport::TestCase 74 fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics 75 fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things' 75 76 76 77 def test_table_exists … … 591 592 end 592 593 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 593 599 if current_adapter?(:MysqlAdapter) 594 600 def test_update_all_with_order_and_limit trunk/activerecord/test/fixtures/db_definitions/schema.rb
r8219 r8571 352 352 t.datetime :updated_on 353 353 end 354 355 create_table 'warehouse-things', :force => true do |t| 356 t.integer :value 357 end 354 358 end trunk/activerecord/test/validations_test.rb
r8570 r8571 4 4 require 'fixtures/person' 5 5 require 'fixtures/developer' 6 require 'fixtures/warehouse_thing' 6 7 7 8 # The following methods in Topic are used in test_conditional_validation_* … … 55 56 56 57 class ValidationsTest < ActiveSupport::TestCase 57 fixtures :topics, :developers 58 fixtures :topics, :developers, 'warehouse-things' 58 59 59 60 def setup … … 435 436 assert t2.save, "should save with nil" 436 437 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 437 445 438 446 def test_validate_straight_inheritance_uniqueness