diff options
author | Jon Leighton <j@jonathanleighton.com> | 2011-10-05 01:09:43 +0100 |
---|---|---|
committer | Jon Leighton <j@jonathanleighton.com> | 2011-10-05 01:11:40 +0100 |
commit | ee2be435b1e5c0e94a4ee93a1a310e0471a77d07 (patch) | |
tree | d30036d8c7f8520df4c6cd4c47e0f7733abe3525 /activerecord | |
parent | 5711a35ad8faa3fb6d138b234cbe9acfad27a9a8 (diff) | |
download | rails-ee2be435b1e5c0e94a4ee93a1a310e0471a77d07.tar.gz rails-ee2be435b1e5c0e94a4ee93a1a310e0471a77d07.tar.bz2 rails-ee2be435b1e5c0e94a4ee93a1a310e0471a77d07.zip |
Raise error on unknown primary key.
If we don't have a primary key when we ask for it, it's better to fail
fast. Fixes GH #2307.
Diffstat (limited to 'activerecord')
-rw-r--r-- | activerecord/lib/active_record/attribute_methods/primary_key.rb | 7 | ||||
-rw-r--r-- | activerecord/lib/active_record/attribute_methods/read.rb | 6 | ||||
-rw-r--r-- | activerecord/lib/active_record/attribute_methods/write.rb | 2 | ||||
-rw-r--r-- | activerecord/lib/active_record/base.rb | 9 | ||||
-rw-r--r-- | activerecord/lib/active_record/errors.rb | 14 | ||||
-rw-r--r-- | activerecord/lib/active_record/fixtures.rb | 2 | ||||
-rw-r--r-- | activerecord/lib/active_record/persistence.rb | 2 | ||||
-rw-r--r-- | activerecord/lib/active_record/relation.rb | 6 | ||||
-rw-r--r-- | activerecord/lib/active_record/transactions.rb | 2 | ||||
-rw-r--r-- | activerecord/test/cases/attribute_methods/read_test.rb | 4 | ||||
-rw-r--r-- | activerecord/test/cases/base_test.rb | 4 | ||||
-rw-r--r-- | activerecord/test/cases/primary_keys_test.rb | 14 |
12 files changed, 54 insertions, 18 deletions
diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb index a404a5edd7..36d7f4ad11 100644 --- a/activerecord/lib/active_record/attribute_methods/primary_key.rb +++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb @@ -14,6 +14,8 @@ module ActiveRecord # primary_key_prefix_type setting, though. def primary_key @primary_key ||= reset_primary_key + raise ActiveRecord::UnknownPrimaryKey.new(self) unless @primary_key + @primary_key end # Returns a quoted version of the primary key name, used to construct SQL statements. @@ -29,6 +31,11 @@ module ActiveRecord key end + def primary_key? #:nodoc: + @primary_key ||= reset_primary_key + !@primary_key.nil? + end + def get_primary_key(base_name) #:nodoc: return 'id' unless base_name && !base_name.blank? diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb index 4174e4da09..8566ecad14 100644 --- a/activerecord/lib/active_record/attribute_methods/read.rb +++ b/activerecord/lib/active_record/attribute_methods/read.rb @@ -40,7 +40,7 @@ module ActiveRecord define_read_method(attr_name, attr_name, columns_hash[attr_name]) end - if attr_name == primary_key && attr_name != "id" + if primary_key? && attr_name == primary_key && attr_name != "id" define_read_method('id', attr_name, columns_hash[attr_name]) end end @@ -63,7 +63,7 @@ module ActiveRecord cast_code = column.type_cast_code('v') access_code = "(v=@attributes['#{attr_name}']) && #{cast_code}" - unless attr_name.to_s == self.primary_key.to_s + unless primary_key? && attr_name.to_s == primary_key.to_s access_code.insert(0, "missing_attribute('#{attr_name}', caller) unless @attributes.has_key?('#{attr_name}'); ") end @@ -107,7 +107,7 @@ module ActiveRecord def _read_attribute(attr_name) attr_name = attr_name.to_s - attr_name = self.class.primary_key if attr_name == 'id' + attr_name = self.class.primary_key? && self.class.primary_key if attr_name == 'id' value = @attributes[attr_name] unless value.nil? if column = column_for_attribute(attr_name) diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb index e9cdb130db..4db6d71ba6 100644 --- a/activerecord/lib/active_record/attribute_methods/write.rb +++ b/activerecord/lib/active_record/attribute_methods/write.rb @@ -18,7 +18,7 @@ module ActiveRecord end end - if attr_name == primary_key && attr_name != "id" + if primary_key? && attr_name == primary_key && attr_name != "id" generated_attribute_methods.module_eval("alias :id= :'#{primary_key}='") end end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 78159d13d4..137b4c6534 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -708,7 +708,7 @@ module ActiveRecord #:nodoc: # Returns an array of column objects for the table associated with this class. def columns if defined?(@primary_key) - connection_pool.primary_keys[table_name] ||= primary_key + connection_pool.primary_keys[table_name] ||= @primary_key end connection_pool.columns[table_name] @@ -953,7 +953,7 @@ module ActiveRecord #:nodoc: # objects of different types from the same table. def instantiate(record) sti_class = find_sti_class(record[inheritance_column]) - record_id = sti_class.primary_key && record[sti_class.primary_key] + record_id = sti_class.primary_key? && record[sti_class.primary_key] if ActiveRecord::IdentityMap.enabled? && record_id if (column = sti_class.columns_hash[sti_class.primary_key]) && column.number? @@ -1941,8 +1941,9 @@ MSG # The primary key and inheritance column can never be set by mass-assignment for security reasons. def self.attributes_protected_by_default - default = [ primary_key, inheritance_column ] - default << 'id' unless primary_key.eql? 'id' + default = [ inheritance_column ] + default << primary_key if primary_key? + default << 'id' unless primary_key? && primary_key == 'id' default end diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb index ad7d8cd63c..8262b60f6e 100644 --- a/activerecord/lib/active_record/errors.rb +++ b/activerecord/lib/active_record/errors.rb @@ -169,4 +169,18 @@ module ActiveRecord @errors = errors end end + + # Raised when a model attempts to fetch its primary key from the database, but the table + # has no primary key declared. + class UnknownPrimaryKey < ActiveRecordError + attr_reader :model + + def initialize(model) + @model = model + end + + def message + "Unknown primary key for table #{model.table_name} in model #{model}." + end + end end diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 6f1ec7f9b3..97af15c9e8 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -622,7 +622,7 @@ module ActiveRecord private def primary_key_name - @primary_key_name ||= model_class && model_class.primary_key + @primary_key_name ||= model_class && model_class.primary_key? && model_class.primary_key end def has_primary_key_column? diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 5e65e46a7d..b5dadb929d 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -314,7 +314,7 @@ module ActiveRecord new_id = self.class.unscoped.insert attributes_values - self.id ||= new_id if self.class.primary_key + self.id ||= new_id if self.class.primary_key? IdentityMap.add(self) if IdentityMap.enabled? @new_record = false diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index ecefaa633c..bf61d79a2c 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -13,7 +13,7 @@ module ActiveRecord # These are explicitly delegated to improve performance (avoids method_missing) delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to => :to_a - delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key, :connection, :column_hash,:to => :klass + delegate :table_name, :quoted_table_name, :primary_key, :primary_key?, :quoted_primary_key, :connection, :column_hash,:to => :klass attr_reader :table, :klass, :loaded attr_accessor :extensions, :default_scoped @@ -36,7 +36,7 @@ module ActiveRecord def insert(values) primary_key_value = nil - if primary_key && Hash === values + if primary_key? && Hash === values primary_key_value = values[values.keys.find { |k| k.name == primary_key }] @@ -70,7 +70,7 @@ module ActiveRecord conn.insert( im, 'SQL', - primary_key, + primary_key? && primary_key, primary_key_value, nil, binds) diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index ae97a3f3ca..d4870dd3f2 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -303,7 +303,7 @@ module ActiveRecord # Save the new record state and id of a record so it can be restored later if a transaction fails. def remember_transaction_record_state #:nodoc @_start_transaction_state ||= {} - @_start_transaction_state[:id] = id if has_attribute?(self.class.primary_key) + @_start_transaction_state[:id] = id if self.class.primary_key? unless @_start_transaction_state.include?(:new_record) @_start_transaction_state[:new_record] = @new_record end diff --git a/activerecord/test/cases/attribute_methods/read_test.rb b/activerecord/test/cases/attribute_methods/read_test.rb index e03ed33591..814476ce73 100644 --- a/activerecord/test/cases/attribute_methods/read_test.rb +++ b/activerecord/test/cases/attribute_methods/read_test.rb @@ -24,6 +24,10 @@ module ActiveRecord def self.primary_key end + def self.primary_key? + false + end + def self.columns column_names.map { FakeColumn.new(name) } end diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 12c1cfb30e..77fd1d2fad 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -97,10 +97,6 @@ class BasicsTest < ActiveRecord::TestCase assert pk.primary, 'nick should be primary key' end - def test_primary_key_with_no_id - assert_nil Edge.primary_key - end - unless current_adapter?(:PostgreSQLAdapter,:OracleAdapter,:SQLServerAdapter) def test_limit_with_comma assert_nothing_raised do diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb index 489c7d8310..58badd6266 100644 --- a/activerecord/test/cases/primary_keys_test.rb +++ b/activerecord/test/cases/primary_keys_test.rb @@ -5,6 +5,7 @@ require 'models/subscriber' require 'models/movie' require 'models/keyboard' require 'models/mixed_case_monkey' +require 'models/edge' class PrimaryKeysTest < ActiveRecord::TestCase fixtures :topics, :subscribers, :movies, :mixed_case_monkeys @@ -161,4 +162,17 @@ class PrimaryKeysTest < ActiveRecord::TestCase assert_equal 'foo', model.primary_key end + + def test_no_primary_key_raises + assert_raises(ActiveRecord::UnknownPrimaryKey) do + Edge.primary_key + end + + begin + Edge.primary_key + rescue ActiveRecord::UnknownPrimaryKey => e + assert e.message.include?('edges') + assert e.message.include?('Edge') + end + end end |