Skip to content
Browse files

r3008@asus (orig r2978): david | 2005-11-11 01:50:42 -0800

 Make sure that legacy db tasks also reference :database for SQLite (closes #2830) [kazuhiko@fdiary.net]
 r3009@asus (orig r2979):  david | 2005-11-11 02:02:44 -0800
 Changelogging
 r3011@asus (orig r2981):  bitsweat | 2005-11-11 10:49:01 -0800
 Include the Enumerable module in ActiveRecord::Errors.
 r3012@asus (orig r2982):  bitsweat | 2005-11-11 15:45:02 -0800
 SQLServer: don't report limits for unsupported field types.  Closes #2835.
 r3014@asus (orig r2984):  minam | 2005-11-11 21:09:05 -0800
 Make Validations#create! use the current scope
 
 r3015@asus (orig r2985):  bitsweat | 2005-11-12 03:59:54 -0800
  r4325@asus:  jeremy | 2005-11-12 03:57:46 -0800
  PostgreSQL: correctly discover custom primary key sequences.  PostgreSQL: smarter sequence name defaults, stricter last_insert_id, warn on pk without sequence.  Base.reset_sequence_name analogous to reset_table_name (mostly useful for testing).  Base.define_attr_method allows nil values.  References #2594.
 
 r3017@asus (orig r2987):  david | 2005-11-12 08:26:23 -0800
 Pulled auto-starting browser: More pain than gain
 r3019@asus (orig r2989):  bitsweat | 2005-11-12 14:28:38 -0800
 PostgreSQL: min_messages = warning for AR tests.
 r3020@asus (orig r2990):  bitsweat | 2005-11-12 17:12:48 -0800
 SQLite: the clone_structure_to_test Rake task should always use the test environment.  References #2846.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@2991 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information...
1 parent e823ef8 commit 17836f4819aba47e6b0518c41c74436fff04eb26 @jeremy jeremy committed Nov 13, 2005
View
6 activerecord/CHANGELOG
@@ -1,5 +1,11 @@
*SVN*
+* Base.reset_sequence_name analogous to reset_table_name (mostly useful for testing). Base.define_attr_method allows nil values. [Jeremy Kemper]
+
+* PostgreSQL: smarter sequence name defaults, stricter last_insert_id, warn on pk without sequence. [Jeremy Kemper]
+
+* PostgreSQL: correctly discover custom primary key sequences. #2594 [Blair Zajac <blair@orcaware.com>, meadow.nnick@gmail.com, Jeremy Kemper]
+
* SQLServer: don't report limits for unsupported field types. #2835 [Ryan Tomayko]
* Include the Enumerable module in ActiveRecord::Errors. [Rick Bradley <rick@rickbradley.com>]
View
17 activerecord/lib/active_record/base.rb
@@ -646,9 +646,16 @@ def inheritance_column
"type"
end
- # Default sequence_name. Use set_sequence_name to override.
+ # Lazy-set the sequence name to the connection's default. This method
+ # is only ever called once since set_sequence_name overrides it.
def sequence_name
- connection.default_sequence_name(table_name, primary_key)
+ reset_sequence_name
+ end
+
+ def reset_sequence_name
+ default = connection.default_sequence_name(table_name, primary_key)
+ set_sequence_name(default)
+ default
end
# Sets the table name to use to the given value, or (if the value
@@ -1053,12 +1060,12 @@ def attribute_condition(argument)
def define_attr_method(name, value=nil, &block)
sing = class << self; self; end
sing.send :alias_method, "original_#{name}", name
- if value
+ if block_given?
+ sing.send :define_method, name, &block
+ else
# use eval instead of a block to work around a memory leak in dev
# mode in fcgi
sing.class_eval "def #{name}; #{value.to_s.inspect}; end"
- else
- sing.send :define_method, name, &block
end
end
View
57 activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -102,7 +102,7 @@ def select_one(sql, name = nil) #:nodoc:
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
execute(sql, name)
table = sql.split(" ", 4)[2]
- id_value || last_insert_id(table, sequence_name)
+ id_value || last_insert_id(table, sequence_name || default_sequence_name(table, pk))
end
def query(sql, name = nil) #:nodoc:
@@ -199,23 +199,34 @@ def schema_search_path #:nodoc:
@schema_search_path ||= query('SHOW search_path')[0][0]
end
- def default_sequence_name(table_name, pk = 'id')
- "#{table_name}_#{pk}_seq"
+ def default_sequence_name(table_name, pk = nil)
+ default_pk, default_seq = pk_and_sequence_for(table_name)
+ default_seq || "#{table_name}_#{pk || default_pk}_seq"
end
- # Set the sequence to the max value of the table's pk.
- def reset_pk_sequence!(table)
- pk, sequence = pk_and_sequence_for(table)
- if pk and sequence
- select_value <<-end_sql, 'Reset sequence'
- SELECT setval('#{sequence}', (SELECT COALESCE(MAX(#{pk})+(SELECT increment_by FROM #{sequence}), (SELECT min_value FROM #{sequence})) FROM #{table}), false)
- end_sql
+ # Resets sequence to the max value of the table's pk if present.
+ def reset_pk_sequence!(table, pk = nil, sequence = nil)
+ unless pk and sequence
+ default_pk, default_sequence = pk_and_sequence_for(table)
+ pk ||= default_pk
+ sequence ||= default_sequence
+ end
+ if pk
+ if sequence
+ select_value <<-end_sql, 'Reset sequence'
+ SELECT setval('#{sequence}', (SELECT COALESCE(MAX(#{pk})+(SELECT increment_by FROM #{sequence}), (SELECT min_value FROM #{sequence})) FROM #{table}), false)
+ end_sql
+ else
+ @logger.warn "#{table} has primary key #{pk} with no default sequence" if @logger
+ end
end
end
# Find a table's primary key and sequence.
def pk_and_sequence_for(table)
- execute(<<-end_sql, 'Find pk sequence')[0]
+ # First try looking for a sequence with a dependency on the
+ # given table's primary key.
+ result = execute(<<-end_sql, 'PK and serial sequence')[0]
SELECT attr.attname, (name.nspname || '.' || seq.relname)
FROM pg_class seq,
pg_attribute attr,
@@ -232,11 +243,29 @@ def pk_and_sequence_for(table)
AND cons.contype = 'p'
AND dep.refobjid = '#{table}'::regclass
end_sql
+
+ if result.nil? or result.empty?
+ # If that fails, try parsing the primary key's default value.
+ # Support the 7.x and 8.0 nextval('foo'::text) as well as
+ # the 8.1+ nextval('foo'::regclass).
+ # TODO: assumes sequence is in same schema as table.
+ result = execute(<<-end_sql, 'PK and custom sequence')[0]
+ SELECT attr.attname, (name.nspname || '.' || split_part(def.adsrc, '\\\'', 2))
+ FROM pg_class t
+ JOIN pg_namespace name ON (t.relnamespace = name.oid)
+ JOIN pg_attribute attr ON (t.oid = attrelid)
+ JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
+ JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
+ WHERE t.oid = '#{table}'::regclass
+ AND cons.contype = 'p'
+ AND def.adsrc ~ 'nextval\\\\(\\\'[^\\\']*\\\'::[^\\\\)]*\\\\)'
+ end_sql
+ end
+ result
rescue
nil
end
-
def rename_table(name, new_name)
execute "ALTER TABLE #{name} RENAME TO #{new_name}"
end
@@ -281,9 +310,7 @@ def remove_index(table_name, options) #:nodoc:
BYTEA_COLUMN_TYPE_OID = 17
def last_insert_id(table, sequence_name)
- if sequence_name
- @connection.exec("SELECT currval('#{sequence_name}')")[0][0].to_i
- end
+ Integer(@connection.exec("SELECT currval('#{sequence_name}')")[0][0])
end
def select(sql, name = nil)
View
6 activerecord/test/base_test.rb
@@ -839,10 +839,10 @@ def test_quote_keys
def test_column_name_properly_quoted
col_record = ColumnName.new
col_record.references = 40
- col_record.save
+ assert col_record.save
col_record.references = 41
- col_record.save
- c2 = ColumnName.find(col_record.id)
+ assert col_record.save
+ assert_not_nil c2 = ColumnName.find(col_record.id)
assert_equal(41, c2.references)
end
View
10 activerecord/test/connections/native_postgresql/connection.rb
@@ -9,16 +9,16 @@
ActiveRecord::Base.establish_connection(
:adapter => "postgresql",
- :host => nil,
:username => "postgres",
:password => "postgres",
- :database => db1
+ :database => db1,
+ :min_messages => "warning"
)
Course.establish_connection(
:adapter => "postgresql",
- :host => nil,
:username => "postgres",
:password => "postgres",
- :database => db2
-)
+ :database => db2,
+ :min_messages => "warning"
+)
View
48 activerecord/test/fixtures_test.rb
@@ -173,27 +173,45 @@ def test_omap_fixtures
if Account.connection.respond_to?(:reset_pk_sequence!)
class FixturesResetPkSequenceTest < Test::Unit::TestCase
fixtures :accounts
+ fixtures :companies
- def test_resets_to_min_pk
- Account.delete_all
- Account.connection.reset_pk_sequence!(Account.table_name)
+ def setup
+ @instances = [Account.new(:credit_limit => 50), Company.new(:name => 'RoR Consulting')]
+ end
+
+ def test_resets_to_min_pk_with_specified_pk_and_sequence
+ @instances.each do |instance|
+ model = instance.class
+ model.delete_all
+ model.connection.reset_pk_sequence!(model.table_name, model.primary_key, model.sequence_name)
- one = Account.new(:credit_limit => 50)
- one.save!
- assert_equal 1, one.id
+ instance.save!
+ assert_equal 1, instance.id, "Sequence reset for #{model.table_name} failed."
+ end
end
- def test_create_fixtures_resets_sequences
- # create_fixtures performs reset_pk_sequence!
- max_id = create_fixtures('accounts').inject(0) do |max_id, (name, fixture)|
- fixture_id = fixture['id'].to_i
- fixture_id > max_id ? fixture_id : max_id
+ def test_resets_to_min_pk_with_default_pk_and_sequence
+ @instances.each do |instance|
+ model = instance.class
+ model.delete_all
+ model.connection.reset_pk_sequence!(model.table_name)
+
+ instance.save!
+ assert_equal 1, instance.id, "Sequence reset for #{model.table_name} failed."
end
+ end
- # Clone the last fixture to check that it gets the next greatest id.
- another = Account.new(:credit_limit => 1200)
- another.save!
- assert_equal max_id + 1, another.id
+ def test_create_fixtures_resets_sequences
+ @instances.each do |instance|
+ max_id = create_fixtures(instance.class.table_name).inject(0) do |max_id, (name, fixture)|
+ fixture_id = fixture['id'].to_i
+ fixture_id > max_id ? fixture_id : max_id
+ end
+
+ # Clone the last fixture to check that it gets the next greatest id.
+ instance.save!
+ assert_equal max_id + 1, instance.id, "Sequence reset for #{instance.class.table_name} failed."
+ end
end
end
end
View
8 activerecord/test/migration_test.rb
@@ -362,6 +362,9 @@ def test_schema_info_table_name
ActiveRecord::Base.table_name_suffix = ""
Reminder.reset_table_name
assert_equal "schema_info", ActiveRecord::Migrator.schema_info_table_name
+ ensure
+ ActiveRecord::Base.table_name_prefix = ""
+ ActiveRecord::Base.table_name_suffix = ""
end
def test_proper_table_name
@@ -398,17 +401,20 @@ def test_add_drop_table_with_prefix_and_suffix
ActiveRecord::Base.table_name_prefix = 'prefix_'
ActiveRecord::Base.table_name_suffix = '_suffix'
Reminder.reset_table_name
+ Reminder.reset_sequence_name
WeNeedReminders.up
assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
assert_equal "hello world", Reminder.find(:first).content
WeNeedReminders.down
assert_raises(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
+ ensure
ActiveRecord::Base.table_name_prefix = ''
ActiveRecord::Base.table_name_suffix = ''
Reminder.reset_table_name
+ Reminder.reset_sequence_name
end
-
+
def test_migrator_with_duplicates
assert_raises(ActiveRecord::DuplicateMigrationVersionError) do
ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations_with_duplicate/', nil)
View
2 railties/CHANGELOG
@@ -1,5 +1,7 @@
*SVN*
+* SQLite: the clone_structure_to_test Rake task should always use the test environment. #2846 [Rick Olson]
+
* Make sure that legacy db tasks also reference :database for SQLite #2830 [kazuhiko@fdiary.net]
* Pass __FILE__ when evaluating plugins' init.rb. #2817 [james.adam@gmail.com]
View
2 railties/lib/tasks/databases.rake
@@ -78,7 +78,7 @@ task :clone_structure_to_test => [ :db_structure_dump, :purge_test_database ] do
ENV['PGPASSWORD'] = abcs["test"]["password"].to_s if abcs["test"]["password"]
`psql -U "#{abcs["test"]["username"]}" -f db/#{RAILS_ENV}_structure.sql #{abcs["test"]["database"]}`
when "sqlite", "sqlite3"
- dbfile = abcs[RAILS_ENV]["database"] || abcs[RAILS_ENV]["dbfile"]
+ dbfile = abcs["test"]["database"] || abcs["test"]["dbfile"]
`#{abcs["test"]["adapter"]} #{dbfile} < db/#{RAILS_ENV}_structure.sql`
when "sqlserver"
`osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{RAILS_ENV}_structure.sql`

0 comments on commit 17836f4

Please sign in to comment.
Something went wrong with that request. Please try again.