activerecord 3.2.14.rc2 → 3.2.14

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

@@ -1,4 +1,4 @@
1
- ## Rails 3.2.14.rc2 (Jun 16, 2013) ##
1
+ ## Rails 3.2.14 (Jul 22, 2013) ##
2
2
 
3
3
  * Do not re-create destroyed association when saving the parent object.
4
4
 
@@ -6,9 +6,6 @@
6
6
 
7
7
  *Paul Nikitochkin*
8
8
 
9
-
10
- ## Rails 3.2.14.rc1 (Jul 12, 2013) ##
11
-
12
9
  * Do not shallow the original exception in `exec_cache` on PostgreSQL adapter.
13
10
 
14
11
  Fixes #11260.
@@ -3,7 +3,7 @@ module ActiveRecord
3
3
  MAJOR = 3
4
4
  MINOR = 2
5
5
  TINY = 14
6
- PRE = "rc2"
6
+ PRE = nil
7
7
 
8
8
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
9
9
  end
metadata CHANGED
@@ -1,15 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord
3
3
  version: !ruby/object:Gem::Version
4
- hash: -3949349072
5
- prerelease: 7
4
+ hash: 19
5
+ prerelease:
6
6
  segments:
7
7
  - 3
8
8
  - 2
9
9
  - 14
10
- - rc
11
- - 2
12
- version: 3.2.14.rc2
10
+ version: 3.2.14
13
11
  platform: ruby
14
12
  authors:
15
13
  - David Heinemeier Hansson
@@ -17,7 +15,7 @@ autorequire:
17
15
  bindir: bin
18
16
  cert_chain: []
19
17
 
20
- date: 2013-07-16 00:00:00 -03:00
18
+ date: 2013-07-22 00:00:00 -03:00
21
19
  default_executable:
22
20
  dependencies:
23
21
  - !ruby/object:Gem::Dependency
@@ -26,14 +24,12 @@ dependencies:
26
24
  requirements:
27
25
  - - "="
28
26
  - !ruby/object:Gem::Version
29
- hash: -3949349072
27
+ hash: 19
30
28
  segments:
31
29
  - 3
32
30
  - 2
33
31
  - 14
34
- - rc
35
- - 2
36
- version: 3.2.14.rc2
32
+ version: 3.2.14
37
33
  type: :runtime
38
34
  name: activesupport
39
35
  version_requirements: *id001
@@ -44,14 +40,12 @@ dependencies:
44
40
  requirements:
45
41
  - - "="
46
42
  - !ruby/object:Gem::Version
47
- hash: -3949349072
43
+ hash: 19
48
44
  segments:
49
45
  - 3
50
46
  - 2
51
47
  - 14
52
- - rc
53
- - 2
54
- version: 3.2.14.rc2
48
+ version: 3.2.14
55
49
  type: :runtime
56
50
  name: activemodel
57
51
  version_requirements: *id002
@@ -111,7 +105,6 @@ files:
111
105
  - lib/active_record/associations/belongs_to_polymorphic_association.rb
112
106
  - lib/active_record/associations/builder/association.rb
113
107
  - lib/active_record/associations/builder/belongs_to.rb
114
- - lib/active_record/associations/builder/belongs_to.rb.orig
115
108
  - lib/active_record/associations/builder/collection_association.rb
116
109
  - lib/active_record/associations/builder/has_and_belongs_to_many.rb
117
110
  - lib/active_record/associations/builder/has_many.rb
@@ -121,7 +114,6 @@ files:
121
114
  - lib/active_record/associations/collection_proxy.rb
122
115
  - lib/active_record/associations/has_and_belongs_to_many_association.rb
123
116
  - lib/active_record/associations/has_many_association.rb
124
- - lib/active_record/associations/has_many_association.rb.orig
125
117
  - lib/active_record/associations/has_many_through_association.rb
126
118
  - lib/active_record/associations/has_one_association.rb
127
119
  - lib/active_record/associations/has_one_through_association.rb
@@ -167,21 +159,16 @@ files:
167
159
  - lib/active_record/connection_adapters/abstract/quoting.rb
168
160
  - lib/active_record/connection_adapters/abstract/schema_definitions.rb
169
161
  - lib/active_record/connection_adapters/abstract/schema_statements.rb
170
- - lib/active_record/connection_adapters/abstract/schema_statements.rb.orig
171
162
  - lib/active_record/connection_adapters/abstract_adapter.rb
172
163
  - lib/active_record/connection_adapters/abstract_mysql_adapter.rb
173
164
  - lib/active_record/connection_adapters/column.rb
174
- - lib/active_record/connection_adapters/connection_specification.rb.orig
175
165
  - lib/active_record/connection_adapters/mysql2_adapter.rb
176
166
  - lib/active_record/connection_adapters/mysql_adapter.rb
177
- - lib/active_record/connection_adapters/postgresql/cast.rb.orig
178
- - lib/active_record/connection_adapters/postgresql/schema_statements.rb.orig
179
167
  - lib/active_record/connection_adapters/postgresql_adapter.rb
180
168
  - lib/active_record/connection_adapters/schema_cache.rb
181
169
  - lib/active_record/connection_adapters/sqlite3_adapter.rb
182
170
  - lib/active_record/connection_adapters/sqlite_adapter.rb
183
171
  - lib/active_record/connection_adapters/statement_pool.rb
184
- - lib/active_record/core.rb.orig
185
172
  - lib/active_record/counter_cache.rb
186
173
  - lib/active_record/dynamic_finder_match.rb
187
174
  - lib/active_record/dynamic_matchers.rb
@@ -215,10 +202,8 @@ files:
215
202
  - lib/active_record/reflection.rb
216
203
  - lib/active_record/relation/batches.rb
217
204
  - lib/active_record/relation/calculations.rb
218
- - lib/active_record/relation/calculations.rb.orig
219
205
  - lib/active_record/relation/delegation.rb
220
206
  - lib/active_record/relation/finder_methods.rb
221
- - lib/active_record/relation/finder_methods.rb.orig
222
207
  - lib/active_record/relation/predicate_builder.rb
223
208
  - lib/active_record/relation/query_methods.rb
224
209
  - lib/active_record/relation/spawn_methods.rb
@@ -251,7 +236,6 @@ files:
251
236
  - lib/rails/generators/active_record/model/templates/model.rb
252
237
  - lib/rails/generators/active_record/model/templates/module.rb
253
238
  - lib/rails/generators/active_record/observer/observer_generator.rb
254
- - lib/rails/generators/active_record/observer/observer_generator.rb.orig
255
239
  - lib/rails/generators/active_record/observer/templates/observer.rb
256
240
  - lib/rails/generators/active_record/session_migration/session_migration_generator.rb
257
241
  - lib/rails/generators/active_record/session_migration/templates/migration.rb
@@ -280,14 +264,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
280
264
  required_rubygems_version: !ruby/object:Gem::Requirement
281
265
  none: false
282
266
  requirements:
283
- - - ">"
267
+ - - ">="
284
268
  - !ruby/object:Gem::Version
285
- hash: 25
269
+ hash: 3
286
270
  segments:
287
- - 1
288
- - 3
289
- - 1
290
- version: 1.3.1
271
+ - 0
272
+ version: "0"
291
273
  requirements: []
292
274
 
293
275
  rubyforge_project:
@@ -1,95 +0,0 @@
1
- require 'active_support/core_ext/object/inclusion'
2
-
3
- module ActiveRecord::Associations::Builder
4
- class BelongsTo < SingularAssociation #:nodoc:
5
- self.macro = :belongs_to
6
-
7
- self.valid_options += [:foreign_type, :polymorphic, :touch]
8
-
9
- def constructable?
10
- !options[:polymorphic]
11
- end
12
-
13
- def build
14
- reflection = super
15
- add_counter_cache_callbacks(reflection) if options[:counter_cache]
16
- add_touch_callbacks(reflection) if options[:touch]
17
- configure_dependency
18
- reflection
19
- end
20
-
21
- private
22
-
23
- def add_counter_cache_callbacks(reflection)
24
- cache_column = reflection.counter_cache_column
25
- name = self.name
26
-
27
- <<<<<<< HEAD
28
- method_name = "belongs_to_counter_cache_after_create_for_#{name}"
29
- mixin.redefine_method(method_name) do
30
- record = send(name)
31
- record.class.increment_counter(cache_column, record.id) unless record.nil?
32
- =======
33
- def belongs_to_counter_cache_before_destroy_for_#{name}
34
- unless destroyed_by_association && destroyed_by_association.foreign_key.to_sym == #{foreign_key.to_sym.inspect}
35
- record = #{name}
36
- if record && !self.destroyed?
37
- record.class.decrement_counter(:#{cache_column}, record.id)
38
- end
39
- end
40
- >>>>>>> 19d32e8... Merge pull request #10489 from greenriver/ar_counter_cache_multiple_destroy
41
- end
42
- model.after_create(method_name)
43
-
44
- method_name = "belongs_to_counter_cache_before_destroy_for_#{name}"
45
- mixin.redefine_method(method_name) do
46
- record = send(name)
47
- record.class.decrement_counter(cache_column, record.id) unless record.nil?
48
- end
49
- model.before_destroy(method_name)
50
-
51
- model.send(:module_eval,
52
- "#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)", __FILE__, __LINE__
53
- )
54
- end
55
-
56
- def add_touch_callbacks(reflection)
57
- name = self.name
58
- method_name = "belongs_to_touch_after_save_or_destroy_for_#{name}"
59
- touch = options[:touch]
60
-
61
- mixin.redefine_method(method_name) do
62
- record = send(name)
63
-
64
- unless record.nil?
65
- if touch == true
66
- record.touch
67
- else
68
- record.touch(touch)
69
- end
70
- end
71
- end
72
-
73
- model.after_save(method_name)
74
- model.after_touch(method_name)
75
- model.after_destroy(method_name)
76
- end
77
-
78
- def configure_dependency
79
- if options[:dependent]
80
- unless options[:dependent].in?([:destroy, :delete])
81
- raise ArgumentError, "The :dependent option expects either :destroy or :delete (#{options[:dependent].inspect})"
82
- end
83
-
84
- method_name = "belongs_to_dependent_#{options[:dependent]}_for_#{name}"
85
- model.send(:class_eval, <<-eoruby, __FILE__, __LINE__ + 1)
86
- def #{method_name}
87
- association = #{name}
88
- association.#{options[:dependent]} if association
89
- end
90
- eoruby
91
- model.after_destroy method_name
92
- end
93
- end
94
- end
95
- end
@@ -1,116 +0,0 @@
1
- module ActiveRecord
2
- # = Active Record Has Many Association
3
- module Associations
4
- # This is the proxy that handles a has many association.
5
- #
6
- # If the association has a <tt>:through</tt> option further specialization
7
- # is provided by its child HasManyThroughAssociation.
8
- class HasManyAssociation < CollectionAssociation #:nodoc:
9
-
10
- def insert_record(record, validate = true, raise = false)
11
- set_owner_attributes(record)
12
-
13
- if raise
14
- record.save!(:validate => validate)
15
- else
16
- record.save(:validate => validate)
17
- end
18
- end
19
-
20
- private
21
-
22
- # Returns the number of records in this collection.
23
- #
24
- # If the association has a counter cache it gets that value. Otherwise
25
- # it will attempt to do a count via SQL, bounded to <tt>:limit</tt> if
26
- # there's one. Some configuration options like :group make it impossible
27
- # to do an SQL count, in those cases the array count will be used.
28
- #
29
- # That does not depend on whether the collection has already been loaded
30
- # or not. The +size+ method is the one that takes the loaded flag into
31
- # account and delegates to +count_records+ if needed.
32
- #
33
- # If the collection is empty the target is set to an empty array and
34
- # the loaded flag is set to true as well.
35
- def count_records
36
- count = if has_cached_counter?
37
- owner.send(:read_attribute, cached_counter_attribute_name)
38
- elsif options[:counter_sql] || options[:finder_sql]
39
- reflection.klass.count_by_sql(custom_counter_sql)
40
- else
41
- scoped.count
42
- end
43
-
44
- # If there's nothing in the database and @target has no new records
45
- # we are certain the current target is an empty array. This is a
46
- # documented side-effect of the method that may avoid an extra SELECT.
47
- @target ||= [] and loaded! if count == 0
48
-
49
- [options[:limit], count].compact.min
50
- end
51
-
52
- def has_cached_counter?(reflection = reflection)
53
- owner.attribute_present?(cached_counter_attribute_name(reflection))
54
- end
55
-
56
- def cached_counter_attribute_name(reflection = reflection)
57
- "#{reflection.name}_count"
58
- end
59
-
60
- def update_counter(difference, reflection = reflection)
61
- if has_cached_counter?(reflection)
62
- counter = cached_counter_attribute_name(reflection)
63
- owner.class.update_counters(owner.id, counter => difference)
64
- owner[counter] += difference
65
- owner.changed_attributes.delete(counter) # eww
66
- end
67
- end
68
-
69
- # This shit is nasty. We need to avoid the following situation:
70
- #
71
- # * An associated record is deleted via record.destroy
72
- # * Hence the callbacks run, and they find a belongs_to on the record with a
73
- # :counter_cache options which points back at our owner. So they update the
74
- # counter cache.
75
- # * In which case, we must make sure to *not* update the counter cache, or else
76
- # it will be decremented twice.
77
- #
78
- # Hence this method.
79
- def inverse_updates_counter_cache?(reflection = reflection)
80
- counter_name = cached_counter_attribute_name(reflection)
81
- reflection.klass.reflect_on_all_associations(:belongs_to).any? { |inverse_reflection|
82
- inverse_reflection.counter_cache_column == counter_name
83
- }
84
- end
85
-
86
- # Deletes the records according to the <tt>:dependent</tt> option.
87
- def delete_records(records, method)
88
- if method == :destroy
89
- records.each { |r| r.destroy }
90
- update_counter(-records.length) unless inverse_updates_counter_cache?
91
- else
92
- <<<<<<< HEAD
93
- keys = records.map { |r| r[reflection.association_primary_key] }
94
- scope = scoped.where(reflection.association_primary_key => keys)
95
- =======
96
- if records == :all
97
- scope = self.scope
98
- else
99
- scope = self.scope.where(reflection.klass.primary_key => records)
100
- end
101
- >>>>>>> 030d30d... Merge pull request #10713 from senny/10693_fix_primary_key_option_on_has_many
102
-
103
- if method == :delete_all
104
- update_counter(-scope.delete_all)
105
- else
106
- update_counter(-scope.update_all(reflection.foreign_key => nil))
107
- end
108
- end
109
- end
110
-
111
- def foreign_key_present?
112
- owner.attribute_present?(reflection.association_primary_key)
113
- end
114
- end
115
- end
116
- end
@@ -1,619 +0,0 @@
1
- require 'active_support/core_ext/array/wrap'
2
- require 'active_support/deprecation/reporting'
3
-
4
- module ActiveRecord
5
- module ConnectionAdapters # :nodoc:
6
- module SchemaStatements
7
- # Returns a Hash of mappings from the abstract data types to the native
8
- # database types. See TableDefinition#column for details on the recognized
9
- # abstract data types.
10
- def native_database_types
11
- {}
12
- end
13
-
14
- # Truncates a table alias according to the limits of the current adapter.
15
- def table_alias_for(table_name)
16
- table_name[0...table_alias_length].gsub(/\./, '_')
17
- end
18
-
19
- # Checks to see if the table +table_name+ exists on the database.
20
- #
21
- # === Example
22
- # table_exists?(:developers)
23
- def table_exists?(table_name)
24
- tables.include?(table_name.to_s)
25
- end
26
-
27
- # Returns an array of indexes for the given table.
28
- # def indexes(table_name, name = nil) end
29
-
30
- # Checks to see if an index exists on a table for a given index definition.
31
- #
32
- # === Examples
33
- # # Check an index exists
34
- # index_exists?(:suppliers, :company_id)
35
- #
36
- # # Check an index on multiple columns exists
37
- # index_exists?(:suppliers, [:company_id, :company_type])
38
- #
39
- # # Check a unique index exists
40
- # index_exists?(:suppliers, :company_id, :unique => true)
41
- #
42
- # # Check an index with a custom name exists
43
- # index_exists?(:suppliers, :company_id, :name => "idx_company_id"
44
- def index_exists?(table_name, column_name, options = {})
45
- column_names = Array.wrap(column_name)
46
- index_name = options.key?(:name) ? options[:name].to_s : index_name(table_name, :column => column_names)
47
- if options[:unique]
48
- indexes(table_name).any?{ |i| i.unique && i.name == index_name }
49
- else
50
- indexes(table_name).any?{ |i| i.name == index_name }
51
- end
52
- end
53
-
54
- # Returns an array of Column objects for the table specified by +table_name+.
55
- # See the concrete implementation for details on the expected parameter values.
56
- def columns(table_name, name = nil) end
57
-
58
- # Checks to see if a column exists in a given table.
59
- #
60
- # === Examples
61
- # # Check a column exists
62
- # column_exists?(:suppliers, :name)
63
- #
64
- # # Check a column exists of a particular type
65
- # column_exists?(:suppliers, :name, :string)
66
- #
67
- # # Check a column exists with a specific definition
68
- # column_exists?(:suppliers, :name, :string, :limit => 100)
69
- def column_exists?(table_name, column_name, type = nil, options = {})
70
- columns(table_name).any?{ |c| c.name == column_name.to_s &&
71
- (!type || c.type == type) &&
72
- (!options[:limit] || c.limit == options[:limit]) &&
73
- (!options[:precision] || c.precision == options[:precision]) &&
74
- (!options[:scale] || c.scale == options[:scale]) }
75
- end
76
-
77
- # Creates a new table with the name +table_name+. +table_name+ may either
78
- # be a String or a Symbol.
79
- #
80
- # There are two ways to work with +create_table+. You can use the block
81
- # form or the regular form, like this:
82
- #
83
- # === Block form
84
- # # create_table() passes a TableDefinition object to the block.
85
- # # This form will not only create the table, but also columns for the
86
- # # table.
87
- #
88
- # create_table(:suppliers) do |t|
89
- # t.column :name, :string, :limit => 60
90
- # # Other fields here
91
- # end
92
- #
93
- # === Block form, with shorthand
94
- # # You can also use the column types as method calls, rather than calling the column method.
95
- # create_table(:suppliers) do |t|
96
- # t.string :name, :limit => 60
97
- # # Other fields here
98
- # end
99
- #
100
- # === Regular form
101
- # # Creates a table called 'suppliers' with no columns.
102
- # create_table(:suppliers)
103
- # # Add a column to 'suppliers'.
104
- # add_column(:suppliers, :name, :string, {:limit => 60})
105
- #
106
- # The +options+ hash can include the following keys:
107
- # [<tt>:id</tt>]
108
- # Whether to automatically add a primary key column. Defaults to true.
109
- # Join tables for +has_and_belongs_to_many+ should set it to false.
110
- # [<tt>:primary_key</tt>]
111
- # The name of the primary key, if one is to be added automatically.
112
- # Defaults to +id+. If <tt>:id</tt> is false this option is ignored.
113
- #
114
- # Also note that this just sets the primary key in the table. You additionally
115
- # need to configure the primary key in the model via +self.primary_key=+.
116
- # Models do NOT auto-detect the primary key from their table definition.
117
- #
118
- # [<tt>:options</tt>]
119
- # Any extra options you want appended to the table definition.
120
- # [<tt>:temporary</tt>]
121
- # Make a temporary table.
122
- # [<tt>:force</tt>]
123
- # Set to true to drop the table before creating it.
124
- # Defaults to false.
125
- #
126
- # ===== Examples
127
- # ====== Add a backend specific option to the generated SQL (MySQL)
128
- # create_table(:suppliers, :options => 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
129
- # generates:
130
- # CREATE TABLE suppliers (
131
- # id int(11) DEFAULT NULL auto_increment PRIMARY KEY
132
- # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
133
- #
134
- # ====== Rename the primary key column
135
- # create_table(:objects, :primary_key => 'guid') do |t|
136
- # t.column :name, :string, :limit => 80
137
- # end
138
- # generates:
139
- # CREATE TABLE objects (
140
- # guid int(11) DEFAULT NULL auto_increment PRIMARY KEY,
141
- # name varchar(80)
142
- # )
143
- #
144
- # ====== Do not add a primary key column
145
- # create_table(:categories_suppliers, :id => false) do |t|
146
- # t.column :category_id, :integer
147
- # t.column :supplier_id, :integer
148
- # end
149
- # generates:
150
- # CREATE TABLE categories_suppliers (
151
- # category_id int,
152
- # supplier_id int
153
- # )
154
- #
155
- # See also TableDefinition#column for details on how to create columns.
156
- def create_table(table_name, options = {})
157
- td = table_definition
158
- td.primary_key(options[:primary_key] || Base.get_primary_key(table_name.to_s.singularize)) unless options[:id] == false
159
-
160
- yield td if block_given?
161
-
162
- if options[:force] && table_exists?(table_name)
163
- drop_table(table_name, options)
164
- end
165
-
166
- create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
167
- create_sql << "#{quote_table_name(table_name)} ("
168
- create_sql << td.to_sql
169
- create_sql << ") #{options[:options]}"
170
- execute create_sql
171
- end
172
-
173
- # A block for changing columns in +table+.
174
- #
175
- # === Example
176
- # # change_table() yields a Table instance
177
- # change_table(:suppliers) do |t|
178
- # t.column :name, :string, :limit => 60
179
- # # Other column alterations here
180
- # end
181
- #
182
- # The +options+ hash can include the following keys:
183
- # [<tt>:bulk</tt>]
184
- # Set this to true to make this a bulk alter query, such as
185
- # ALTER TABLE `users` ADD COLUMN age INT(11), ADD COLUMN birthdate DATETIME ...
186
- #
187
- # Defaults to false.
188
- #
189
- # ===== Examples
190
- # ====== Add a column
191
- # change_table(:suppliers) do |t|
192
- # t.column :name, :string, :limit => 60
193
- # end
194
- #
195
- # ====== Add 2 integer columns
196
- # change_table(:suppliers) do |t|
197
- # t.integer :width, :height, :null => false, :default => 0
198
- # end
199
- #
200
- # ====== Add created_at/updated_at columns
201
- # change_table(:suppliers) do |t|
202
- # t.timestamps
203
- # end
204
- #
205
- # ====== Add a foreign key column
206
- # change_table(:suppliers) do |t|
207
- # t.references :company
208
- # end
209
- #
210
- # Creates a <tt>company_id(integer)</tt> column
211
- #
212
- # ====== Add a polymorphic foreign key column
213
- # change_table(:suppliers) do |t|
214
- # t.belongs_to :company, :polymorphic => true
215
- # end
216
- #
217
- # Creates <tt>company_type(varchar)</tt> and <tt>company_id(integer)</tt> columns
218
- #
219
- # ====== Remove a column
220
- # change_table(:suppliers) do |t|
221
- # t.remove :company
222
- # end
223
- #
224
- # ====== Remove several columns
225
- # change_table(:suppliers) do |t|
226
- # t.remove :company_id
227
- # t.remove :width, :height
228
- # end
229
- #
230
- # ====== Remove an index
231
- # change_table(:suppliers) do |t|
232
- # t.remove_index :company_id
233
- # end
234
- #
235
- # See also Table for details on
236
- # all of the various column transformation
237
- def change_table(table_name, options = {})
238
- if supports_bulk_alter? && options[:bulk]
239
- recorder = ActiveRecord::Migration::CommandRecorder.new(self)
240
- yield Table.new(table_name, recorder)
241
- bulk_change_table(table_name, recorder.commands)
242
- else
243
- yield Table.new(table_name, self)
244
- end
245
- end
246
-
247
- # Renames a table.
248
- # ===== Example
249
- # rename_table('octopuses', 'octopi')
250
- def rename_table(table_name, new_name)
251
- raise NotImplementedError, "rename_table is not implemented"
252
- end
253
-
254
- # Drops a table from the database.
255
- def drop_table(table_name, options = {})
256
- execute "DROP TABLE #{quote_table_name(table_name)}"
257
- end
258
-
259
- # Adds a new column to the named table.
260
- # See TableDefinition#column for details of the options you can use.
261
- def add_column(table_name, column_name, type, options = {})
262
- add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
263
- add_column_options!(add_column_sql, options)
264
- execute(add_column_sql)
265
- end
266
-
267
- # Removes the column(s) from the table definition.
268
- # ===== Examples
269
- # remove_column(:suppliers, :qualification)
270
- # remove_columns(:suppliers, :qualification, :experience)
271
- def remove_column(table_name, *column_names)
272
- if column_names.flatten!
273
- message = 'Passing array to remove_columns is deprecated, please use ' +
274
- 'multiple arguments, like: `remove_columns(:posts, :foo, :bar)`'
275
- ActiveSupport::Deprecation.warn message, caller
276
- end
277
-
278
- columns_for_remove(table_name, *column_names).each do |column_name|
279
- execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{column_name}"
280
- end
281
- end
282
- alias :remove_columns :remove_column
283
-
284
- # Changes the column's definition according to the new options.
285
- # See TableDefinition#column for details of the options you can use.
286
- # ===== Examples
287
- # change_column(:suppliers, :name, :string, :limit => 80)
288
- # change_column(:accounts, :description, :text)
289
- def change_column(table_name, column_name, type, options = {})
290
- raise NotImplementedError, "change_column is not implemented"
291
- end
292
-
293
- # Sets a new default value for a column.
294
- # ===== Examples
295
- # change_column_default(:suppliers, :qualification, 'new')
296
- # change_column_default(:accounts, :authorized, 1)
297
- # change_column_default(:users, :email, nil)
298
- def change_column_default(table_name, column_name, default)
299
- raise NotImplementedError, "change_column_default is not implemented"
300
- end
301
-
302
- # Renames a column.
303
- # ===== Example
304
- # rename_column(:suppliers, :description, :name)
305
- def rename_column(table_name, column_name, new_column_name)
306
- raise NotImplementedError, "rename_column is not implemented"
307
- end
308
-
309
- # Adds a new index to the table. +column_name+ can be a single Symbol, or
310
- # an Array of Symbols.
311
- #
312
- # The index will be named after the table and the column name(s), unless
313
- # you pass <tt>:name</tt> as an option.
314
- #
315
- # ===== Examples
316
- #
317
- # ====== Creating a simple index
318
- # add_index(:suppliers, :name)
319
- # generates
320
- # CREATE INDEX suppliers_name_index ON suppliers(name)
321
- #
322
- # ====== Creating a unique index
323
- # add_index(:accounts, [:branch_id, :party_id], :unique => true)
324
- # generates
325
- # CREATE UNIQUE INDEX accounts_branch_id_party_id_index ON accounts(branch_id, party_id)
326
- #
327
- # ====== Creating a named index
328
- # add_index(:accounts, [:branch_id, :party_id], :unique => true, :name => 'by_branch_party')
329
- # generates
330
- # CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id)
331
- #
332
- # ====== Creating an index with specific key length
333
- # add_index(:accounts, :name, :name => 'by_name', :length => 10)
334
- # generates
335
- # CREATE INDEX by_name ON accounts(name(10))
336
- #
337
- # add_index(:accounts, [:name, :surname], :name => 'by_name_surname', :length => {:name => 10, :surname => 15})
338
- # generates
339
- # CREATE INDEX by_name_surname ON accounts(name(10), surname(15))
340
- #
341
- # Note: SQLite doesn't support index length
342
- #
343
- # ====== Creating an index with a sort order (desc or asc, asc is the default)
344
- # add_index(:accounts, [:branch_id, :party_id, :surname], :order => {:branch_id => :desc, :part_id => :asc})
345
- # generates
346
- # CREATE INDEX by_branch_desc_party ON accounts(branch_id DESC, party_id ASC, surname)
347
- #
348
- # Note: mysql doesn't yet support index order (it accepts the syntax but ignores it)
349
- #
350
- def add_index(table_name, column_name, options = {})
351
- index_name, index_type, index_columns = add_index_options(table_name, column_name, options)
352
- execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})"
353
- end
354
-
355
- # Remove the given index from the table.
356
- #
357
- # Remove the index_accounts_on_column in the accounts table.
358
- # remove_index :accounts, :column
359
- # Remove the index named index_accounts_on_branch_id in the accounts table.
360
- # remove_index :accounts, :column => :branch_id
361
- # Remove the index named index_accounts_on_branch_id_and_party_id in the accounts table.
362
- # remove_index :accounts, :column => [:branch_id, :party_id]
363
- # Remove the index named by_branch_party in the accounts table.
364
- # remove_index :accounts, :name => :by_branch_party
365
- def remove_index(table_name, options = {})
366
- remove_index!(table_name, index_name_for_remove(table_name, options))
367
- end
368
-
369
- def remove_index!(table_name, index_name) #:nodoc:
370
- execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
371
- end
372
-
373
- # Rename an index.
374
- #
375
- # Rename the index_people_on_last_name index to index_users_on_last_name
376
- # rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
377
- def rename_index(table_name, old_name, new_name)
378
- # this is a naive implementation; some DBs may support this more efficiently (Postgres, for instance)
379
- old_index_def = indexes(table_name).detect { |i| i.name == old_name }
380
- return unless old_index_def
381
- remove_index(table_name, :name => old_name)
382
- add_index(table_name, old_index_def.columns, :name => new_name, :unique => old_index_def.unique)
383
- end
384
-
385
- def index_name(table_name, options) #:nodoc:
386
- if Hash === options # legacy support
387
- if options[:column]
388
- "index_#{table_name}_on_#{Array.wrap(options[:column]) * '_and_'}"
389
- elsif options[:name]
390
- options[:name]
391
- else
392
- raise ArgumentError, "You must specify the index name"
393
- end
394
- else
395
- index_name(table_name, :column => options)
396
- end
397
- end
398
-
399
- # Verify the existence of an index with a given name.
400
- #
401
- # The default argument is returned if the underlying implementation does not define the indexes method,
402
- # as there's no way to determine the correct answer in that case.
403
- def index_name_exists?(table_name, index_name, default)
404
- return default unless respond_to?(:indexes)
405
- index_name = index_name.to_s
406
- indexes(table_name).detect { |i| i.name == index_name }
407
- end
408
-
409
- # Returns a string of <tt>CREATE TABLE</tt> SQL statement(s) for recreating the
410
- # entire structure of the database.
411
- def structure_dump
412
- end
413
-
414
- def dump_schema_information #:nodoc:
415
- sm_table = ActiveRecord::Migrator.schema_migrations_table_name
416
- migrated = select_values("SELECT version FROM #{sm_table} ORDER BY version")
417
- migrated.map { |v| "INSERT INTO #{sm_table} (version) VALUES ('#{v}');" }.join("\n\n")
418
- end
419
-
420
- # Should not be called normally, but this operation is non-destructive.
421
- # The migrations module handles this automatically.
422
- def initialize_schema_migrations_table
423
- sm_table = ActiveRecord::Migrator.schema_migrations_table_name
424
-
425
- unless table_exists?(sm_table)
426
- create_table(sm_table, :id => false) do |schema_migrations_table|
427
- schema_migrations_table.column :version, :string, :null => false
428
- end
429
- add_index sm_table, :version, :unique => true,
430
- :name => "#{Base.table_name_prefix}unique_schema_migrations#{Base.table_name_suffix}"
431
-
432
- # Backwards-compatibility: if we find schema_info, assume we've
433
- # migrated up to that point:
434
- si_table = Base.table_name_prefix + 'schema_info' + Base.table_name_suffix
435
-
436
- if table_exists?(si_table)
437
- ActiveSupport::Deprecation.warn "Usage of the schema table `#{si_table}` is deprecated. Please switch to using `schema_migrations` table"
438
-
439
- old_version = select_value("SELECT version FROM #{quote_table_name(si_table)}").to_i
440
- assume_migrated_upto_version(old_version)
441
- drop_table(si_table)
442
- end
443
- end
444
- end
445
-
446
- def assume_migrated_upto_version(version, migrations_paths = ActiveRecord::Migrator.migrations_paths)
447
- migrations_paths = Array.wrap(migrations_paths)
448
- version = version.to_i
449
- sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
450
-
451
- migrated = select_values("SELECT version FROM #{sm_table}").map { |v| v.to_i }
452
- paths = migrations_paths.map {|p| "#{p}/[0-9]*_*.rb" }
453
- versions = Dir[*paths].map do |filename|
454
- filename.split('/').last.split('_').first.to_i
455
- end
456
-
457
- unless migrated.include?(version)
458
- execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')"
459
- end
460
-
461
- inserted = Set.new
462
- (versions - migrated).each do |v|
463
- if inserted.include?(v)
464
- raise "Duplicate migration #{v}. Please renumber your migrations to resolve the conflict."
465
- elsif v < version
466
- execute "INSERT INTO #{sm_table} (version) VALUES ('#{v}')"
467
- inserted << v
468
- end
469
- end
470
- end
471
-
472
- def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
473
- if native = native_database_types[type.to_sym]
474
- column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
475
-
476
- if type == :decimal # ignore limit, use precision and scale
477
- scale ||= native[:scale]
478
-
479
- if precision ||= native[:precision]
480
- if scale
481
- column_type_sql << "(#{precision},#{scale})"
482
- else
483
- column_type_sql << "(#{precision})"
484
- end
485
- elsif scale
486
- raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale if specified"
487
- end
488
-
489
- elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
490
- column_type_sql << "(#{limit})"
491
- end
492
-
493
- column_type_sql
494
- else
495
- type
496
- end
497
- end
498
-
499
- def add_column_options!(sql, options) #:nodoc:
500
- sql << " DEFAULT #{quote(options[:default], options[:column])}" if options_include_default?(options)
501
- # must explicitly check for :null to allow change_column to work on migrations
502
- if options[:null] == false
503
- sql << " NOT NULL"
504
- end
505
- end
506
-
507
- # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
508
- #
509
- <<<<<<< HEAD
510
- # distinct("posts.id", "posts.created_at desc")
511
- =======
512
- # distinct("posts.id", ["posts.created_at desc"])
513
- #
514
- >>>>>>> 1b02299... Merge pull request #6792 from Empact/postgres-distinct
515
- def distinct(columns, order_by)
516
- "DISTINCT #{columns_for_distinct(columns, order_by)}"
517
- end
518
-
519
- # Given a set of columns and an ORDER BY clause, returns the columns for a SELECT DISTINCT.
520
- # Both PostgreSQL and Oracle overrides this for custom DISTINCT syntax - they
521
- # require the order columns appear in the SELECT.
522
- #
523
- # columns_for_distinct("posts.id", ["posts.created_at desc"])
524
- def columns_for_distinct(columns, orders)
525
- columns
526
- end
527
-
528
- # Adds timestamps (created_at and updated_at) columns to the named table.
529
- # ===== Examples
530
- # add_timestamps(:suppliers)
531
- def add_timestamps(table_name)
532
- add_column table_name, :created_at, :datetime
533
- add_column table_name, :updated_at, :datetime
534
- end
535
-
536
- # Removes the timestamp columns (created_at and updated_at) from the table definition.
537
- # ===== Examples
538
- # remove_timestamps(:suppliers)
539
- def remove_timestamps(table_name)
540
- remove_column table_name, :updated_at
541
- remove_column table_name, :created_at
542
- end
543
-
544
- protected
545
- def add_index_sort_order(option_strings, column_names, options = {})
546
- if options.is_a?(Hash) && order = options[:order]
547
- case order
548
- when Hash
549
- column_names.each {|name| option_strings[name] += " #{order[name].to_s.upcase}" if order.has_key?(name)}
550
- when String
551
- column_names.each {|name| option_strings[name] += " #{order.upcase}"}
552
- end
553
- end
554
-
555
- return option_strings
556
- end
557
-
558
- # Overridden by the mysql adapter for supporting index lengths
559
- def quoted_columns_for_index(column_names, options = {})
560
- option_strings = Hash[column_names.map {|name| [name, '']}]
561
-
562
- # add index sort order if supported
563
- if supports_index_sort_order?
564
- option_strings = add_index_sort_order(option_strings, column_names, options)
565
- end
566
-
567
- column_names.map {|name| quote_column_name(name) + option_strings[name]}
568
- end
569
-
570
- def options_include_default?(options)
571
- options.include?(:default) && !(options[:null] == false && options[:default].nil?)
572
- end
573
-
574
- def add_index_options(table_name, column_name, options = {})
575
- column_names = Array.wrap(column_name)
576
- index_name = index_name(table_name, :column => column_names)
577
-
578
- if Hash === options # legacy support, since this param was a string
579
- index_type = options[:unique] ? "UNIQUE" : ""
580
- index_name = options[:name].to_s if options.key?(:name)
581
- else
582
- index_type = options
583
- end
584
-
585
- if index_name.length > index_name_length
586
- raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters"
587
- end
588
- if index_name_exists?(table_name, index_name, false)
589
- raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
590
- end
591
- index_columns = quoted_columns_for_index(column_names, options).join(", ")
592
-
593
- [index_name, index_type, index_columns]
594
- end
595
-
596
- def index_name_for_remove(table_name, options = {})
597
- index_name = index_name(table_name, options)
598
-
599
- unless index_name_exists?(table_name, index_name, true)
600
- raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
601
- end
602
-
603
- index_name
604
- end
605
-
606
- def columns_for_remove(table_name, *column_names)
607
- column_names = column_names.flatten
608
-
609
- raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.blank?
610
- column_names.map {|column_name| quote_column_name(column_name) }
611
- end
612
-
613
- private
614
- def table_definition
615
- TableDefinition.new(self)
616
- end
617
- end
618
- end
619
- end