activerecord 2.1.0 → 2.1.1
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.
- data/CHANGELOG +34 -0
- data/README +0 -0
- data/Rakefile +6 -5
- data/lib/active_record.rb +8 -10
- data/lib/active_record/association_preload.rb +17 -12
- data/lib/active_record/associations.rb +45 -27
- data/lib/active_record/associations/association_collection.rb +8 -5
- data/lib/active_record/associations/association_proxy.rb +2 -6
- data/lib/active_record/associations/belongs_to_association.rb +0 -0
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +2 -3
- data/lib/active_record/associations/has_many_association.rb +16 -4
- data/lib/active_record/associations/has_many_through_association.rb +1 -1
- data/lib/active_record/associations/has_one_association.rb +2 -2
- data/lib/active_record/associations/has_one_through_association.rb +4 -0
- data/lib/active_record/base.rb +33 -15
- data/lib/active_record/calculations.rb +20 -7
- data/lib/active_record/callbacks.rb +0 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +9 -6
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +17 -10
- data/lib/active_record/connection_adapters/abstract_adapter.rb +0 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +53 -24
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +66 -20
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +12 -0
- data/lib/active_record/dirty.rb +10 -3
- data/lib/active_record/fixtures.rb +0 -0
- data/lib/active_record/locking/optimistic.rb +1 -0
- data/lib/active_record/migration.rb +35 -8
- data/lib/active_record/named_scope.rb +6 -1
- data/lib/active_record/observer.rb +7 -5
- data/lib/active_record/test_case.rb +13 -2
- data/lib/active_record/validations.rb +19 -9
- data/lib/active_record/version.rb +1 -1
- data/test/cases/active_schema_test_postgresql.rb +2 -2
- data/test/cases/adapter_test.rb +1 -1
- data/test/cases/associations/belongs_to_associations_test.rb +19 -0
- data/test/cases/associations/cascaded_eager_loading_test.rb +13 -1
- data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +36 -0
- data/test/cases/associations/eager_test.rb +25 -1
- data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +27 -5
- data/test/cases/associations/has_many_associations_test.rb +106 -4
- data/test/cases/associations/has_many_through_associations_test.rb +10 -0
- data/test/cases/associations/has_one_associations_test.rb +22 -0
- data/test/cases/associations/has_one_through_associations_test.rb +44 -5
- data/test/cases/associations/join_model_test.rb +7 -0
- data/test/cases/associations_test.rb +2 -2
- data/test/cases/attribute_methods_test.rb +10 -10
- data/test/cases/base_test.rb +39 -16
- data/test/cases/calculations_test.rb +53 -1
- data/test/cases/column_definition_test.rb +36 -0
- data/test/cases/database_statements_test.rb +12 -0
- data/test/cases/defaults_test.rb +1 -1
- data/test/cases/deprecated_finder_test.rb +0 -0
- data/test/cases/dirty_test.rb +94 -0
- data/test/cases/finder_test.rb +7 -0
- data/test/cases/fixtures_test.rb +0 -0
- data/test/cases/helper.rb +5 -5
- data/test/cases/inheritance_test.rb +9 -2
- data/test/cases/lifecycle_test.rb +54 -1
- data/test/cases/locking_test.rb +20 -0
- data/test/cases/method_scoping_test.rb +11 -1
- data/test/cases/migration_test.rb +147 -22
- data/test/cases/multiple_db_test.rb +1 -1
- data/test/cases/named_scope_test.rb +50 -1
- data/test/cases/query_cache_test.rb +4 -3
- data/test/cases/readonly_test.rb +0 -0
- data/test/cases/reflection_test.rb +3 -3
- data/test/cases/schema_dumper_test.rb +46 -0
- data/test/cases/unconnected_test.rb +0 -0
- data/test/cases/validations_test.rb +30 -5
- data/test/debug.log +358 -0
- data/test/fixtures/fixture_database.sqlite3 +0 -0
- data/test/fixtures/fixture_database_2.sqlite3 +0 -0
- data/test/models/author.rb +4 -0
- data/test/models/category.rb +1 -0
- data/test/models/company.rb +10 -1
- data/test/models/developer.rb +4 -1
- data/test/models/person.rb +1 -1
- data/test/models/post.rb +6 -1
- data/test/models/project.rb +1 -1
- data/test/models/reply.rb +0 -0
- data/test/models/topic.rb +1 -0
- data/test/schema/mysql_specific_schema.rb +2 -2
- data/test/schema/schema.rb +8 -0
- metadata +11 -5
- data/lib/active_record/vendor/db2.rb +0 -362
@@ -69,8 +69,8 @@ module ActiveRecord
|
|
69
69
|
@target
|
70
70
|
end
|
71
71
|
|
72
|
-
def respond_to?(
|
73
|
-
proxy_respond_to?(
|
72
|
+
def respond_to?(*args)
|
73
|
+
proxy_respond_to?(*args) || (load_target && @target.respond_to?(*args))
|
74
74
|
end
|
75
75
|
|
76
76
|
# Explicitly proxy === because the instance method removal above
|
@@ -131,10 +131,6 @@ module ActiveRecord
|
|
131
131
|
records.map { |record| record.quoted_id }.join(',')
|
132
132
|
end
|
133
133
|
|
134
|
-
def interpolate_sql_options!(options, *keys)
|
135
|
-
keys.each { |key| options[key] &&= interpolate_sql(options[key]) }
|
136
|
-
end
|
137
|
-
|
138
134
|
def interpolate_sql(sql, record = nil)
|
139
135
|
@owner.send(:interpolate_sql, sql, record)
|
140
136
|
end
|
File without changes
|
File without changes
|
@@ -70,10 +70,8 @@ module ActiveRecord
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def construct_sql
|
73
|
-
interpolate_sql_options!(@reflection.options, :finder_sql)
|
74
|
-
|
75
73
|
if @reflection.options[:finder_sql]
|
76
|
-
@finder_sql = @reflection.options[:finder_sql]
|
74
|
+
@finder_sql = interpolate_sql(@reflection.options[:finder_sql])
|
77
75
|
else
|
78
76
|
@finder_sql = "#{@owner.connection.quote_table_name @reflection.options[:join_table]}.#{@reflection.primary_key_name} = #{@owner.quoted_id} "
|
79
77
|
@finder_sql << " AND (#{conditions})" if conditions
|
@@ -87,6 +85,7 @@ module ActiveRecord
|
|
87
85
|
:joins => @join_sql,
|
88
86
|
:readonly => false,
|
89
87
|
:order => @reflection.options[:order],
|
88
|
+
:include => @reflection.options[:include],
|
90
89
|
:limit => @reflection.options[:limit] } }
|
91
90
|
end
|
92
91
|
|
@@ -14,7 +14,16 @@ module ActiveRecord
|
|
14
14
|
@finder_sql + " AND (#{sanitize_sql(options[:conditions])})"
|
15
15
|
options[:include] ||= @reflection.options[:include]
|
16
16
|
|
17
|
-
@reflection.klass.count(column_name, options)
|
17
|
+
value = @reflection.klass.count(column_name, options)
|
18
|
+
|
19
|
+
limit = @reflection.options[:limit]
|
20
|
+
offset = @reflection.options[:offset]
|
21
|
+
|
22
|
+
if limit || offset
|
23
|
+
[ [value - offset.to_i, 0].max, limit.to_i ].min
|
24
|
+
else
|
25
|
+
value
|
26
|
+
end
|
18
27
|
end
|
19
28
|
end
|
20
29
|
|
@@ -27,8 +36,11 @@ module ActiveRecord
|
|
27
36
|
else
|
28
37
|
@reflection.klass.count(:conditions => @counter_sql, :include => @reflection.options[:include])
|
29
38
|
end
|
30
|
-
|
31
|
-
|
39
|
+
|
40
|
+
# If there's nothing in the database and @target has no new records
|
41
|
+
# we are certain the current target is an empty array. This is a
|
42
|
+
# documented side-effect of the method that may avoid an extra SELECT.
|
43
|
+
@target ||= [] and loaded if count == 0
|
32
44
|
|
33
45
|
if @reflection.options[:limit]
|
34
46
|
count = [ @reflection.options[:limit], count ].min
|
@@ -100,7 +112,7 @@ module ActiveRecord
|
|
100
112
|
create_scoping = {}
|
101
113
|
set_belongs_to_association_for(create_scoping)
|
102
114
|
{
|
103
|
-
:find => { :conditions => @finder_sql, :readonly => false, :order => @reflection.options[:order], :limit => @reflection.options[:limit] },
|
115
|
+
:find => { :conditions => @finder_sql, :readonly => false, :order => @reflection.options[:order], :limit => @reflection.options[:limit], :include => @reflection.options[:include]},
|
104
116
|
:create => create_scoping
|
105
117
|
}
|
106
118
|
end
|
@@ -237,7 +237,7 @@ module ActiveRecord
|
|
237
237
|
end
|
238
238
|
|
239
239
|
def build_sti_condition
|
240
|
-
|
240
|
+
@reflection.through_reflection.klass.send(:type_condition)
|
241
241
|
end
|
242
242
|
|
243
243
|
alias_method :sql_conditions, :conditions
|
@@ -21,8 +21,8 @@ module ActiveRecord
|
|
21
21
|
def replace(obj, dont_save = false)
|
22
22
|
load_target
|
23
23
|
|
24
|
-
unless @target.nil?
|
25
|
-
if dependent? && !dont_save
|
24
|
+
unless @target.nil? || @target == obj
|
25
|
+
if dependent? && !dont_save
|
26
26
|
@target.destroy unless @target.new_record?
|
27
27
|
@owner.clear_association_cache
|
28
28
|
else
|
data/lib/active_record/base.rb
CHANGED
@@ -372,7 +372,7 @@ module ActiveRecord #:nodoc:
|
|
372
372
|
def self.reset_subclasses #:nodoc:
|
373
373
|
nonreloadables = []
|
374
374
|
subclasses.each do |klass|
|
375
|
-
unless Dependencies.autoloaded? klass
|
375
|
+
unless ActiveSupport::Dependencies.autoloaded? klass
|
376
376
|
nonreloadables << klass
|
377
377
|
next
|
378
378
|
end
|
@@ -439,6 +439,10 @@ module ActiveRecord #:nodoc:
|
|
439
439
|
cattr_accessor :schema_format , :instance_writer => false
|
440
440
|
@@schema_format = :ruby
|
441
441
|
|
442
|
+
# Specify whether or not to use timestamps for migration numbers
|
443
|
+
cattr_accessor :timestamped_migrations , :instance_writer => false
|
444
|
+
@@timestamped_migrations = true
|
445
|
+
|
442
446
|
# Determine whether to store the full constant name including namespace when using STI
|
443
447
|
superclass_delegating_accessor :store_full_sti_class
|
444
448
|
self.store_full_sti_class = false
|
@@ -828,7 +832,7 @@ module ActiveRecord #:nodoc:
|
|
828
832
|
def update_counters(id, counters)
|
829
833
|
updates = counters.inject([]) { |list, (counter_name, increment)|
|
830
834
|
sign = increment < 0 ? "-" : "+"
|
831
|
-
list << "#{connection.quote_column_name(counter_name)} = #{connection.quote_column_name(counter_name)} #{sign} #{increment.abs}"
|
835
|
+
list << "#{connection.quote_column_name(counter_name)} = COALESCE(#{connection.quote_column_name(counter_name)}, 0) #{sign} #{increment.abs}"
|
832
836
|
}.join(", ")
|
833
837
|
update_all(updates, "#{connection.quote_column_name(primary_key)} = #{quote_value(id)}")
|
834
838
|
end
|
@@ -1465,7 +1469,7 @@ module ActiveRecord #:nodoc:
|
|
1465
1469
|
|
1466
1470
|
def construct_finder_sql(options)
|
1467
1471
|
scope = scope(:find)
|
1468
|
-
sql = "SELECT #{options[:select] || (scope && scope[:select]) || (options[:joins] && quoted_table_name + '.*') || '*'} "
|
1472
|
+
sql = "SELECT #{options[:select] || (scope && scope[:select]) || ((options[:joins] || (scope && scope[:joins])) && quoted_table_name + '.*') || '*'} "
|
1469
1473
|
sql << "FROM #{(scope && scope[:from]) || options[:from] || quoted_table_name} "
|
1470
1474
|
|
1471
1475
|
add_joins!(sql, options, scope)
|
@@ -1577,10 +1581,11 @@ module ActiveRecord #:nodoc:
|
|
1577
1581
|
sql << "WHERE #{merged_conditions} " unless merged_conditions.blank?
|
1578
1582
|
end
|
1579
1583
|
|
1580
|
-
def type_condition
|
1584
|
+
def type_condition(table_alias=nil)
|
1585
|
+
quoted_table_alias = self.connection.quote_table_name(table_alias || table_name)
|
1581
1586
|
quoted_inheritance_column = connection.quote_column_name(inheritance_column)
|
1582
|
-
type_condition = subclasses.inject("#{
|
1583
|
-
condition << "OR #{
|
1587
|
+
type_condition = subclasses.inject("#{quoted_table_alias}.#{quoted_inheritance_column} = '#{sti_name}' ") do |condition, subclass|
|
1588
|
+
condition << "OR #{quoted_table_alias}.#{quoted_inheritance_column} = '#{subclass.sti_name}' "
|
1584
1589
|
end
|
1585
1590
|
|
1586
1591
|
" (#{type_condition}) "
|
@@ -1717,7 +1722,7 @@ module ActiveRecord #:nodoc:
|
|
1717
1722
|
def attribute_condition(argument)
|
1718
1723
|
case argument
|
1719
1724
|
when nil then "IS ?"
|
1720
|
-
when Array, ActiveRecord::Associations::AssociationCollection then "IN (?)"
|
1725
|
+
when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope then "IN (?)"
|
1721
1726
|
when Range then "BETWEEN ? AND ?"
|
1722
1727
|
else "= ?"
|
1723
1728
|
end
|
@@ -2053,9 +2058,10 @@ module ActiveRecord #:nodoc:
|
|
2053
2058
|
end
|
2054
2059
|
|
2055
2060
|
def replace_named_bind_variables(statement, bind_vars) #:nodoc:
|
2056
|
-
statement.gsub(
|
2057
|
-
|
2058
|
-
|
2061
|
+
statement.gsub(/(:?):([a-zA-Z]\w*)/) do
|
2062
|
+
if $1 == ':' # skip postgresql casts
|
2063
|
+
$& # return the whole match
|
2064
|
+
elsif bind_vars.include?(match = $2.to_sym)
|
2059
2065
|
quote_bound_value(bind_vars[match])
|
2060
2066
|
else
|
2061
2067
|
raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
|
@@ -2064,13 +2070,18 @@ module ActiveRecord #:nodoc:
|
|
2064
2070
|
end
|
2065
2071
|
|
2066
2072
|
def expand_range_bind_variables(bind_vars) #:nodoc:
|
2067
|
-
|
2073
|
+
expanded = []
|
2074
|
+
|
2075
|
+
bind_vars.each do |var|
|
2068
2076
|
if var.is_a?(Range)
|
2069
|
-
|
2077
|
+
expanded << var.first
|
2078
|
+
expanded << var.last
|
2070
2079
|
else
|
2071
|
-
|
2080
|
+
expanded << var
|
2072
2081
|
end
|
2073
2082
|
end
|
2083
|
+
|
2084
|
+
expanded
|
2074
2085
|
end
|
2075
2086
|
|
2076
2087
|
def quote_bound_value(value) #:nodoc:
|
@@ -2572,8 +2583,15 @@ module ActiveRecord #:nodoc:
|
|
2572
2583
|
quoted = {}
|
2573
2584
|
connection = self.class.connection
|
2574
2585
|
attribute_names.each do |name|
|
2575
|
-
if column = column_for_attribute(name)
|
2576
|
-
|
2586
|
+
if (column = column_for_attribute(name)) && (include_primary_key || !column.primary)
|
2587
|
+
value = read_attribute(name)
|
2588
|
+
|
2589
|
+
# We need explicit to_yaml because quote() does not properly convert Time/Date fields to YAML.
|
2590
|
+
if value && self.class.serialized_attributes.has_key?(name) && (value.acts_like?(:date) || value.acts_like?(:time))
|
2591
|
+
value = value.to_yaml
|
2592
|
+
end
|
2593
|
+
|
2594
|
+
quoted[name] = connection.quote(value, column)
|
2577
2595
|
end
|
2578
2596
|
end
|
2579
2597
|
include_readonly_attributes ? quoted : remove_readonly_attributes(quoted)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module Calculations #:nodoc:
|
3
|
-
CALCULATIONS_OPTIONS = [:conditions, :joins, :order, :select, :group, :having, :distinct, :limit, :offset, :include]
|
3
|
+
CALCULATIONS_OPTIONS = [:conditions, :joins, :order, :select, :group, :having, :distinct, :limit, :offset, :include, :from]
|
4
4
|
def self.included(base)
|
5
5
|
base.extend(ClassMethods)
|
6
6
|
end
|
@@ -27,6 +27,8 @@ module ActiveRecord
|
|
27
27
|
# * <tt>:select</tt>: By default, this is * as in SELECT * FROM, but can be changed if you, for example, want to do a join but not
|
28
28
|
# include the joined columns.
|
29
29
|
# * <tt>:distinct</tt>: Set this to true to make this a distinct calculation, such as SELECT COUNT(DISTINCT posts.id) ...
|
30
|
+
# * <tt>:from</tt> - By default, this is the table name of the class, but can be changed to an alternate table name (or even the name
|
31
|
+
# of a database view).
|
30
32
|
#
|
31
33
|
# Examples for counting all:
|
32
34
|
# Person.count # returns the total count of all people
|
@@ -71,7 +73,7 @@ module ActiveRecord
|
|
71
73
|
#
|
72
74
|
# Person.sum('age')
|
73
75
|
def sum(column_name, options = {})
|
74
|
-
calculate(:sum, column_name, options)
|
76
|
+
calculate(:sum, column_name, options)
|
75
77
|
end
|
76
78
|
|
77
79
|
# This calculates aggregate values in the given column. Methods for count, sum, average, minimum, and maximum have been added as shortcuts.
|
@@ -178,13 +180,23 @@ module ActiveRecord
|
|
178
180
|
sql = "SELECT COUNT(*) AS #{aggregate_alias}" if use_workaround
|
179
181
|
|
180
182
|
sql << ", #{options[:group_field]} AS #{options[:group_alias]}" if options[:group]
|
181
|
-
|
182
|
-
|
183
|
+
if options[:from]
|
184
|
+
sql << " FROM #{options[:from]} "
|
185
|
+
else
|
186
|
+
sql << " FROM (SELECT #{distinct}#{column_name}" if use_workaround
|
187
|
+
sql << " FROM #{connection.quote_table_name(table_name)} "
|
188
|
+
end
|
189
|
+
|
190
|
+
joins = ""
|
191
|
+
add_joins!(joins, options, scope)
|
192
|
+
|
183
193
|
if merged_includes.any?
|
184
|
-
join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, merged_includes,
|
194
|
+
join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, merged_includes, joins)
|
185
195
|
sql << join_dependency.join_associations.collect{|join| join.association_join }.join
|
186
196
|
end
|
187
|
-
|
197
|
+
|
198
|
+
sql << joins unless joins.blank?
|
199
|
+
|
188
200
|
add_conditions!(sql, options[:conditions], scope)
|
189
201
|
add_limited_ids_condition!(sql, options, join_dependency) if join_dependency && !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit])
|
190
202
|
|
@@ -205,7 +217,7 @@ module ActiveRecord
|
|
205
217
|
|
206
218
|
sql << " ORDER BY #{options[:order]} " if options[:order]
|
207
219
|
add_limit!(sql, options, scope)
|
208
|
-
sql << ')' if use_workaround
|
220
|
+
sql << ') AS #{aggregate_alias}_subquery' if use_workaround
|
209
221
|
sql
|
210
222
|
end
|
211
223
|
|
@@ -266,6 +278,7 @@ module ActiveRecord
|
|
266
278
|
operation = operation.to_s.downcase
|
267
279
|
case operation
|
268
280
|
when 'count' then value.to_i
|
281
|
+
when 'sum' then value =~ /\./ ? value.to_f : value.to_i
|
269
282
|
when 'avg' then value && value.to_f
|
270
283
|
else column ? column.type_cast(value) : value
|
271
284
|
end
|
File without changes
|
@@ -257,7 +257,10 @@ module ActiveRecord
|
|
257
257
|
|
258
258
|
def to_sql
|
259
259
|
column_sql = "#{base.quote_column_name(name)} #{sql_type}"
|
260
|
-
|
260
|
+
column_options = {}
|
261
|
+
column_options[:null] = null unless null.nil?
|
262
|
+
column_options[:default] = default unless default.nil?
|
263
|
+
add_column_options!(column_sql, column_options) unless type.to_sym == :primary_key
|
261
264
|
column_sql
|
262
265
|
end
|
263
266
|
alias to_s :to_sql
|
@@ -304,8 +307,7 @@ module ActiveRecord
|
|
304
307
|
#
|
305
308
|
# Available options are (none of these exists by default):
|
306
309
|
# * <tt>:limit</tt> -
|
307
|
-
# Requests a maximum column length
|
308
|
-
# <tt>:binary</tt> or <tt>:integer</tt> columns only)
|
310
|
+
# Requests a maximum column length. This is number of characters for <tt>:string</tt> and <tt>:text</tt> columns and number of bytes for :binary and :integer columns.
|
309
311
|
# * <tt>:default</tt> -
|
310
312
|
# The column's default value. Use nil for NULL.
|
311
313
|
# * <tt>:null</tt> -
|
@@ -442,9 +444,10 @@ module ActiveRecord
|
|
442
444
|
|
443
445
|
# Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
|
444
446
|
# <tt>:updated_at</tt> to the table.
|
445
|
-
def timestamps
|
446
|
-
|
447
|
-
column(:
|
447
|
+
def timestamps(*args)
|
448
|
+
options = args.extract_options!
|
449
|
+
column(:created_at, :datetime, options)
|
450
|
+
column(:updated_at, :datetime, options)
|
448
451
|
end
|
449
452
|
|
450
453
|
def references(*args)
|
@@ -331,15 +331,26 @@ module ActiveRecord
|
|
331
331
|
end
|
332
332
|
|
333
333
|
def assume_migrated_upto_version(version)
|
334
|
+
version = version.to_i
|
334
335
|
sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
|
336
|
+
|
335
337
|
migrated = select_values("SELECT version FROM #{sm_table}").map(&:to_i)
|
336
338
|
versions = Dir['db/migrate/[0-9]*_*.rb'].map do |filename|
|
337
339
|
filename.split('/').last.split('_').first.to_i
|
338
340
|
end
|
339
341
|
|
340
|
-
|
341
|
-
|
342
|
-
|
342
|
+
unless migrated.include?(version)
|
343
|
+
execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')"
|
344
|
+
end
|
345
|
+
|
346
|
+
inserted = Set.new
|
347
|
+
(versions - migrated).each do |v|
|
348
|
+
if inserted.include?(v)
|
349
|
+
raise "Duplicate migration #{v}. Please renumber your migrations to resolve the conflict."
|
350
|
+
elsif v < version
|
351
|
+
execute "INSERT INTO #{sm_table} (version) VALUES ('#{v}')"
|
352
|
+
inserted << v
|
353
|
+
end
|
343
354
|
end
|
344
355
|
end
|
345
356
|
|
@@ -372,13 +383,9 @@ module ActiveRecord
|
|
372
383
|
|
373
384
|
def add_column_options!(sql, options) #:nodoc:
|
374
385
|
sql << " DEFAULT #{quote(options[:default], options[:column])}" if options_include_default?(options)
|
375
|
-
# must
|
376
|
-
if options
|
377
|
-
|
378
|
-
sql << " NOT NULL"
|
379
|
-
else
|
380
|
-
sql << " NULL"
|
381
|
-
end
|
386
|
+
# must explicitly check for :null to allow change_column to work on migrations
|
387
|
+
if options[:null] == false
|
388
|
+
sql << " NOT NULL"
|
382
389
|
end
|
383
390
|
end
|
384
391
|
|
File without changes
|
@@ -50,10 +50,7 @@ module ActiveRecord
|
|
50
50
|
rescue LoadError => cannot_require_mysql
|
51
51
|
# Use the bundled Ruby/MySQL driver if no driver is already in place
|
52
52
|
begin
|
53
|
-
|
54
|
-
"WARNING: You're using the Ruby-based MySQL library that ships with Rails. This library is not suited for production. " +
|
55
|
-
"Please install the C-based MySQL library instead (gem install mysql)."
|
56
|
-
) if ActiveRecord::Base.logger
|
53
|
+
ActiveSupport::Deprecation.warn "You're using the Ruby-based MySQL library that ships with Rails. This library will be REMOVED FROM RAILS 2.2. Please switch to the offical mysql gem: `gem install mysql`", caller
|
57
54
|
|
58
55
|
require 'active_record/vendor/mysql'
|
59
56
|
rescue LoadError
|
@@ -113,7 +110,8 @@ module ActiveRecord
|
|
113
110
|
end
|
114
111
|
|
115
112
|
def extract_limit(sql_type)
|
116
|
-
|
113
|
+
case sql_type
|
114
|
+
when /blob|text/i
|
117
115
|
case sql_type
|
118
116
|
when /tiny/i
|
119
117
|
255
|
@@ -124,6 +122,11 @@ module ActiveRecord
|
|
124
122
|
else
|
125
123
|
super # we could return 65535 here, but we leave it undecorated by default
|
126
124
|
end
|
125
|
+
when /^bigint/i; 8
|
126
|
+
when /^int/i; 4
|
127
|
+
when /^mediumint/i; 3
|
128
|
+
when /^smallint/i; 2
|
129
|
+
when /^tinyint/i; 1
|
127
130
|
else
|
128
131
|
super
|
129
132
|
end
|
@@ -193,10 +196,10 @@ module ActiveRecord
|
|
193
196
|
|
194
197
|
def native_database_types #:nodoc:
|
195
198
|
{
|
196
|
-
:primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY",
|
199
|
+
:primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY".freeze,
|
197
200
|
:string => { :name => "varchar", :limit => 255 },
|
198
201
|
:text => { :name => "text" },
|
199
|
-
:integer => { :name => "int"},
|
202
|
+
:integer => { :name => "int", :limit => 4 },
|
200
203
|
:float => { :name => "float" },
|
201
204
|
:decimal => { :name => "decimal" },
|
202
205
|
:datetime => { :name => "datetime" },
|
@@ -336,10 +339,11 @@ module ActiveRecord
|
|
336
339
|
|
337
340
|
def add_limit_offset!(sql, options) #:nodoc:
|
338
341
|
if limit = options[:limit]
|
342
|
+
limit = sanitize_limit(limit)
|
339
343
|
unless offset = options[:offset]
|
340
344
|
sql << " LIMIT #{limit}"
|
341
345
|
else
|
342
|
-
sql << " LIMIT #{offset}, #{limit}"
|
346
|
+
sql << " LIMIT #{offset.to_i}, #{limit}"
|
343
347
|
end
|
344
348
|
end
|
345
349
|
end
|
@@ -439,18 +443,29 @@ module ActiveRecord
|
|
439
443
|
end
|
440
444
|
|
441
445
|
def change_column_default(table_name, column_name, default) #:nodoc:
|
442
|
-
|
446
|
+
column = column_for(table_name, column_name)
|
447
|
+
change_column table_name, column_name, column.sql_type, :default => default
|
448
|
+
end
|
449
|
+
|
450
|
+
def change_column_null(table_name, column_name, null, default = nil)
|
451
|
+
column = column_for(table_name, column_name)
|
452
|
+
|
453
|
+
unless null || default.nil?
|
454
|
+
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
455
|
+
end
|
443
456
|
|
444
|
-
|
457
|
+
change_column table_name, column_name, column.sql_type, :null => null
|
445
458
|
end
|
446
459
|
|
447
460
|
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
461
|
+
column = column_for(table_name, column_name)
|
462
|
+
|
448
463
|
unless options_include_default?(options)
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
464
|
+
options[:default] = column.default
|
465
|
+
end
|
466
|
+
|
467
|
+
unless options.has_key?(:null)
|
468
|
+
options[:null] = column.null
|
454
469
|
end
|
455
470
|
|
456
471
|
change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
@@ -459,8 +474,17 @@ module ActiveRecord
|
|
459
474
|
end
|
460
475
|
|
461
476
|
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
477
|
+
options = {}
|
478
|
+
if column = columns(table_name).find { |c| c.name == column_name.to_s }
|
479
|
+
options[:default] = column.default
|
480
|
+
options[:null] = column.null
|
481
|
+
else
|
482
|
+
raise ActiveRecordError, "No such column: #{table_name}.#{column_name}"
|
483
|
+
end
|
462
484
|
current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
|
463
|
-
|
485
|
+
rename_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
|
486
|
+
add_column_options!(rename_column_sql, options)
|
487
|
+
execute(rename_column_sql)
|
464
488
|
end
|
465
489
|
|
466
490
|
# Maps logical Rails types to MySQL-specific data types.
|
@@ -468,14 +492,12 @@ module ActiveRecord
|
|
468
492
|
return super unless type.to_s == 'integer'
|
469
493
|
|
470
494
|
case limit
|
471
|
-
when
|
472
|
-
|
473
|
-
when
|
474
|
-
|
475
|
-
when
|
476
|
-
|
477
|
-
else
|
478
|
-
'int(11)'
|
495
|
+
when 1; 'tinyint'
|
496
|
+
when 2; 'smallint'
|
497
|
+
when 3; 'mediumint'
|
498
|
+
when nil, 4, 11; 'int(11)' # compatibility with MySQL default
|
499
|
+
when 5..8; 'bigint'
|
500
|
+
else raise(ActiveRecordError, "No integer type has byte size #{limit}")
|
479
501
|
end
|
480
502
|
end
|
481
503
|
|
@@ -525,6 +547,13 @@ module ActiveRecord
|
|
525
547
|
def version
|
526
548
|
@version ||= @connection.server_info.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
|
527
549
|
end
|
550
|
+
|
551
|
+
def column_for(table_name, column_name)
|
552
|
+
unless column = columns(table_name).find { |c| c.name == column_name.to_s }
|
553
|
+
raise "No such column: #{table_name}.#{column_name}"
|
554
|
+
end
|
555
|
+
column
|
556
|
+
end
|
528
557
|
end
|
529
558
|
end
|
530
559
|
end
|