mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
r3616@asus: jeremy | 2005-09-26 23:09:28 -0700
Ticket 2292 - Sequences, schemas, and fixtures r3917@asus: jeremy | 2005-10-15 10:43:24 -0700 fix pk assert r3918@asus: jeremy | 2005-10-15 10:46:52 -0700 rework query cache connection= override r3919@asus: jeremy | 2005-10-15 10:47:45 -0700 correct fixtures usage r3920@asus: jeremy | 2005-10-15 10:53:23 -0700 correct attr assignment r3921@asus: jeremy | 2005-10-15 12:59:10 -0700 sequences r3922@asus: jeremy | 2005-10-15 16:36:09 -0700 reset fixtures work with sequences r3951@asus: jeremy | 2005-10-15 23:23:12 -0700 cut down excess features r3952@asus: jeremy | 2005-10-15 23:40:30 -0700 don't test for PostgreSQL specifically git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@2639 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
parent
22b77daeee
commit
7117fdb8ce
13 changed files with 112 additions and 41 deletions
|
@ -625,9 +625,9 @@ module ActiveRecord #:nodoc:
|
|||
"type"
|
||||
end
|
||||
|
||||
# Defines the sequence_name (for Oracle) -- can be overridden in subclasses.
|
||||
# Default sequence_name. Use set_sequence_name to override.
|
||||
def sequence_name
|
||||
"#{table_name}_seq"
|
||||
connection.default_sequence_name(table_name, primary_key)
|
||||
end
|
||||
|
||||
# Sets the table name to use to the given value, or (if the value
|
||||
|
@ -675,8 +675,8 @@ module ActiveRecord #:nodoc:
|
|||
|
||||
# Sets the name of the sequence to use when generating ids to the given
|
||||
# value, or (if the value is nil or false) to the value returned by the
|
||||
# given block. Currently useful only when using Oracle, which requires
|
||||
# explicit sequences.
|
||||
# given block. This is required for Oracle and is useful for any
|
||||
# database which relies on sequences for primary key generation.
|
||||
#
|
||||
# Setting the sequence name when using other dbs will have no effect.
|
||||
# If a sequence name is not explicitly set when using Oracle, it will
|
||||
|
|
|
@ -114,9 +114,15 @@ module ActiveRecord
|
|||
|
||||
# Set the connection for the class.
|
||||
def self.connection=(spec)
|
||||
raise ConnectionNotEstablished unless spec
|
||||
conn = self.send(spec.adapter_method, spec.config)
|
||||
active_connections[self] = conn
|
||||
if spec.kind_of?(ActiveRecord::ConnectionAdapters::AbstractAdapter)
|
||||
active_connections[self] = spec
|
||||
elsif spec.kind_of?(ConnectionSpecification)
|
||||
self.connection = self.send(spec.adapter_method, spec.config)
|
||||
elsif spec.nil?
|
||||
raise ConnectionNotEstablished
|
||||
else
|
||||
establish_connection spec
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -90,6 +90,15 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
def default_sequence_name(table, column)
|
||||
nil
|
||||
end
|
||||
|
||||
# Set the sequence to the max value of the table's column.
|
||||
def reset_sequence!(table, column, sequence = nil)
|
||||
# Do nothing by default. Implement for PostgreSQL, Oracle, ...
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -99,6 +99,10 @@ begin
|
|||
# * <tt>:password</tt> -- Defaults to nothing
|
||||
# * <tt>:host</tt> -- Defaults to localhost
|
||||
class OCIAdapter < AbstractAdapter
|
||||
def default_sequence_name(table, column)
|
||||
"#{table}_seq"
|
||||
end
|
||||
|
||||
def quote_string(string)
|
||||
string.gsub(/'/, "''")
|
||||
end
|
||||
|
|
|
@ -102,7 +102,7 @@ module ActiveRecord
|
|||
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, pk)
|
||||
id_value || last_insert_id(table, sequence_name)
|
||||
end
|
||||
|
||||
def query(sql, name = nil) #:nodoc:
|
||||
|
@ -198,6 +198,37 @@ module ActiveRecord
|
|||
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"
|
||||
end
|
||||
|
||||
# Set the sequence to the max value of the table's pk.
|
||||
def reset_pk_sequence!(table)
|
||||
sequence, pk = sequence_and_pk_for(table)
|
||||
if sequence and pk
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
# Find a table's primary key and sequence.
|
||||
def sequence_and_pk_for(table, column = nil)
|
||||
execute(<<-end_sql, 'Find pk sequence')[0]
|
||||
SELECT (name.nspname || '.' || seq.relname) AS sequence, attr.attname AS pk
|
||||
FROM pg_class seq, pg_attribute attr, pg_depend dep, pg_namespace name, pg_constraint cons
|
||||
WHERE seq.oid = dep.objid
|
||||
AND seq.relnamespace = name.oid
|
||||
AND attr.attrelid = dep.refobjid
|
||||
AND attr.attnum = dep.refobjsubid
|
||||
AND attr.attrelid = cons.conrelid
|
||||
AND attr.attnum = cons.conkey[1]
|
||||
AND cons.contype = 'p'
|
||||
AND dep.refobjid = '#{table}'::regclass
|
||||
end_sql
|
||||
end
|
||||
|
||||
|
||||
def rename_table(name, new_name)
|
||||
execute "ALTER TABLE #{name} RENAME TO #{new_name}"
|
||||
|
@ -242,9 +273,10 @@ module ActiveRecord
|
|||
private
|
||||
BYTEA_COLUMN_TYPE_OID = 17
|
||||
|
||||
def last_insert_id(table, column = nil)
|
||||
sequence_name = "#{table}_#{column || 'id'}_seq"
|
||||
@connection.exec("SELECT currval('#{sequence_name}')")[0][0].to_i
|
||||
def last_insert_id(table, sequence_name)
|
||||
if sequence_name
|
||||
@connection.exec("SELECT currval('#{sequence_name}')")[0][0].to_i
|
||||
end
|
||||
end
|
||||
|
||||
def select(sql, name = nil)
|
||||
|
|
|
@ -257,8 +257,13 @@ class Fixtures < YAML::Omap
|
|||
fixtures.reverse.each { |fixture| fixture.delete_existing_fixtures }
|
||||
fixtures.each { |fixture| fixture.insert_fixtures }
|
||||
end
|
||||
|
||||
reset_sequences(connection, table_names) if connection.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
|
||||
|
||||
# Cap primary key sequences to max(pk).
|
||||
if connection.respond_to?(:reset_pk_sequence!)
|
||||
table_names.flatten.each do |table_name|
|
||||
connection.reset_pk_sequence!(table_name)
|
||||
end
|
||||
end
|
||||
|
||||
return fixtures.size > 1 ? fixtures : fixtures.first
|
||||
ensure
|
||||
|
@ -266,21 +271,6 @@ class Fixtures < YAML::Omap
|
|||
end
|
||||
end
|
||||
|
||||
# Start PostgreSQL fixtures at id 1. Skip tables without models
|
||||
# and models with nonstandard primary keys.
|
||||
def self.reset_sequences(connection, table_names)
|
||||
table_names.flatten.each do |table|
|
||||
if table_class = table.to_s.classify.constantize rescue nil
|
||||
pk = table_class.columns_hash[table_class.primary_key]
|
||||
if pk and pk.type == :integer
|
||||
connection.execute(
|
||||
"SELECT setval('#{table}_#{pk.name}_seq', (SELECT COALESCE(MAX(#{pk.name}), 0)+1 FROM #{table}), false)",
|
||||
'Setting Sequence'
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :table_name
|
||||
|
||||
|
|
|
@ -44,14 +44,15 @@ module ActiveRecord
|
|||
|
||||
class Base
|
||||
# Set the connection for the class with caching on
|
||||
def self.connection=(spec)
|
||||
raise ConnectionNotEstablished unless spec
|
||||
class << self
|
||||
alias_method :connection_without_query_cache=, :connection=
|
||||
|
||||
conn = spec.config[:query_cache] ?
|
||||
QueryCache.new(self.send(spec.adapter_method, spec.config)) :
|
||||
self.send(spec.adapter_method, spec.config)
|
||||
|
||||
active_connections[self] = conn
|
||||
def connection=(spec)
|
||||
if spec.is_a?(ConnectionSpecification) and spec.config[:query_cache]
|
||||
spec = QueryCache.new(self.send(spec.adapter_method, spec.config))
|
||||
end
|
||||
self.connection_without_query_cache = spec
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -710,7 +710,7 @@ end
|
|||
|
||||
class BelongsToAssociationsTest < Test::Unit::TestCase
|
||||
fixtures :accounts, :companies, :developers, :projects, :topics,
|
||||
:developers_projects
|
||||
:developers_projects, :computers, :authors, :posts
|
||||
|
||||
def test_belongs_to
|
||||
Client.find(3).firm.name
|
||||
|
@ -832,7 +832,7 @@ class BelongsToAssociationsTest < Test::Unit::TestCase
|
|||
end
|
||||
|
||||
def test_field_name_same_as_foreign_key
|
||||
computer = Computer.find 1
|
||||
computer = Computer.find(1)
|
||||
assert_not_nil computer.developer, ":foreign key == attribute didn't lock up" # '
|
||||
end
|
||||
|
||||
|
@ -939,7 +939,10 @@ class BelongsToAssociationsTest < Test::Unit::TestCase
|
|||
|
||||
def test_association_assignment_sticks
|
||||
post = Post.find(:first)
|
||||
|
||||
author1, author2 = Author.find(:all, :limit => 2)
|
||||
assert_not_nil author1
|
||||
assert_not_nil author2
|
||||
|
||||
# make sure the association is loaded
|
||||
post.author
|
||||
|
|
|
@ -553,7 +553,7 @@ class BasicsTest < Test::Unit::TestCase
|
|||
end
|
||||
|
||||
def test_mass_assignment_accessible
|
||||
reply = Reply.new("title" => "hello", "content" => "world", "approved" => false)
|
||||
reply = Reply.new("title" => "hello", "content" => "world", "approved" => true)
|
||||
reply.save
|
||||
|
||||
assert reply.approved?
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
DROP TABLE accounts;
|
||||
DROP TABLE companies;
|
||||
DROP SEQUENCE companies_nonstd_seq;
|
||||
DROP TABLE topics;
|
||||
DROP TABLE developers;
|
||||
DROP TABLE projects;
|
||||
|
|
|
@ -6,8 +6,10 @@ CREATE TABLE accounts (
|
|||
);
|
||||
SELECT setval('accounts_id_seq', 100);
|
||||
|
||||
CREATE SEQUENCE companies_nonstd_seq START 101;
|
||||
|
||||
CREATE TABLE companies (
|
||||
id serial,
|
||||
id integer DEFAULT nextval('companies_nonstd_seq'),
|
||||
"type" character varying(50),
|
||||
"ruby_type" character varying(50),
|
||||
firm_id integer,
|
||||
|
@ -16,7 +18,6 @@ CREATE TABLE companies (
|
|||
rating integer default 1,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
SELECT setval('companies_id_seq', 100);
|
||||
|
||||
CREATE TABLE developers_projects (
|
||||
developer_id integer NOT NULL,
|
||||
|
|
|
@ -168,6 +168,30 @@ class FixturesTest < Test::Unit::TestCase
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
if Account.connection.respond_to?(:reset_pk_sequence!)
|
||||
def test_resets_to_min_pk
|
||||
Account.delete_all
|
||||
Account.connection.reset_pk_sequence!(Account.table_name)
|
||||
|
||||
one = Account.new(:credit_limit => 50)
|
||||
one.save!
|
||||
assert_equal 1, one.id
|
||||
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
|
||||
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
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ class PrimaryKeysTest < Test::Unit::TestCase
|
|||
subscriber.id = "jdoe"
|
||||
assert_equal("jdoe", subscriber.id)
|
||||
subscriber.name = "John Doe"
|
||||
assert_nothing_raised{ subscriber.save }
|
||||
assert_nothing_raised { subscriber.save! }
|
||||
|
||||
subscriberReloaded = Subscriber.find("jdoe")
|
||||
assert_equal("John Doe", subscriberReloaded.name)
|
||||
|
|
Loading…
Reference in a new issue