activerecord 4.2.5 → 4.2.11.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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +156 -0
- data/lib/active_record.rb +2 -1
- data/lib/active_record/aggregations.rb +6 -3
- data/lib/active_record/associations/collection_association.rb +17 -6
- data/lib/active_record/associations/collection_proxy.rb +2 -2
- data/lib/active_record/associations/has_many_through_association.rb +5 -0
- data/lib/active_record/associations/join_dependency/join_association.rb +7 -1
- data/lib/active_record/attribute_assignment.rb +1 -1
- data/lib/active_record/attribute_methods.rb +4 -8
- data/lib/active_record/attribute_methods/dirty.rb +1 -0
- data/lib/active_record/attribute_methods/write.rb +1 -1
- data/lib/active_record/attributes.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +21 -7
- data/lib/active_record/connection_adapters/abstract_adapter.rb +12 -0
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +44 -9
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +5 -19
- data/lib/active_record/connection_adapters/mysql_adapter.rb +9 -2
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +12 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -12
- data/lib/active_record/enum.rb +1 -1
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/migration.rb +30 -5
- data/lib/active_record/model_schema.rb +3 -1
- data/lib/active_record/nested_attributes.rb +12 -2
- data/lib/active_record/railtie.rb +4 -2
- data/lib/active_record/railties/databases.rake +7 -17
- data/lib/active_record/reflection.rb +3 -3
- data/lib/active_record/relation/calculations.rb +10 -3
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -1
- data/lib/active_record/relation/spawn_methods.rb +7 -3
- data/lib/active_record/schema_migration.rb +1 -4
- data/lib/active_record/tasks/database_tasks.rb +3 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +13 -9
- data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -2
- data/lib/active_record/type/date.rb +4 -0
- data/lib/active_record/type/decimal.rb +17 -3
- data/lib/active_record/type/value.rb +5 -0
- metadata +7 -8
@@ -111,6 +111,18 @@ module ActiveRecord
|
|
111
111
|
@prepared_statements = false
|
112
112
|
end
|
113
113
|
|
114
|
+
class Version
|
115
|
+
include Comparable
|
116
|
+
|
117
|
+
def initialize(version_string)
|
118
|
+
@version = version_string.split('.').map(&:to_i)
|
119
|
+
end
|
120
|
+
|
121
|
+
def <=>(version_string)
|
122
|
+
@version <=> version_string.split('.').map(&:to_i)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
114
126
|
class BindCollector < Arel::Collectors::Bind
|
115
127
|
def compile(bvs, conn)
|
116
128
|
super(bvs.map { |bv| conn.quote(*bv.reverse) })
|
@@ -203,7 +203,7 @@ module ActiveRecord
|
|
203
203
|
#
|
204
204
|
# http://bugs.mysql.com/bug.php?id=39170
|
205
205
|
def supports_transaction_isolation?
|
206
|
-
version
|
206
|
+
version >= '5.0.0'
|
207
207
|
end
|
208
208
|
|
209
209
|
def supports_indexes_in_create?
|
@@ -215,7 +215,11 @@ module ActiveRecord
|
|
215
215
|
end
|
216
216
|
|
217
217
|
def supports_views?
|
218
|
-
version
|
218
|
+
version >= '5.0.0'
|
219
|
+
end
|
220
|
+
|
221
|
+
def supports_datetime_with_precision?
|
222
|
+
version >= '5.6.4'
|
219
223
|
end
|
220
224
|
|
221
225
|
def native_database_types
|
@@ -278,6 +282,14 @@ module ActiveRecord
|
|
278
282
|
0
|
279
283
|
end
|
280
284
|
|
285
|
+
def quoted_date(value)
|
286
|
+
if supports_datetime_with_precision? && value.acts_like?(:time) && value.respond_to?(:usec)
|
287
|
+
"#{super}.#{sprintf("%06d", value.usec)}"
|
288
|
+
else
|
289
|
+
super
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
281
293
|
# REFERENTIAL INTEGRITY ====================================
|
282
294
|
|
283
295
|
def disable_referential_integrity #:nodoc:
|
@@ -407,6 +419,7 @@ module ActiveRecord
|
|
407
419
|
result.collect { |field| field.first }
|
408
420
|
end
|
409
421
|
end
|
422
|
+
alias data_sources tables
|
410
423
|
|
411
424
|
def truncate(table_name, name = nil)
|
412
425
|
execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
|
@@ -426,6 +439,7 @@ module ActiveRecord
|
|
426
439
|
|
427
440
|
tables(nil, schema, table).any?
|
428
441
|
end
|
442
|
+
alias data_source_exists? table_exists?
|
429
443
|
|
430
444
|
# Returns an array of indexes for the given table.
|
431
445
|
def indexes(table_name, name = nil) #:nodoc:
|
@@ -604,8 +618,10 @@ module ActiveRecord
|
|
604
618
|
|
605
619
|
# SHOW VARIABLES LIKE 'name'
|
606
620
|
def show_variable(name)
|
607
|
-
variables = select_all("
|
621
|
+
variables = select_all("select @@#{name} as 'Value'", 'SCHEMA')
|
608
622
|
variables.first['Value'] unless variables.empty?
|
623
|
+
rescue ActiveRecord::StatementInvalid
|
624
|
+
nil
|
609
625
|
end
|
610
626
|
|
611
627
|
# Returns a table's primary key and belonging sequence.
|
@@ -648,6 +664,21 @@ module ActiveRecord
|
|
648
664
|
end
|
649
665
|
end
|
650
666
|
|
667
|
+
# In MySQL 5.7.5 and up, ONLY_FULL_GROUP_BY affects handling of queries that use
|
668
|
+
# DISTINCT and ORDER BY. It requires the ORDER BY columns in the select list for
|
669
|
+
# distinct queries, and requires that the ORDER BY include the distinct column.
|
670
|
+
# See https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html
|
671
|
+
def columns_for_distinct(columns, orders) # :nodoc:
|
672
|
+
order_columns = orders.reject(&:blank?).map { |s|
|
673
|
+
# Convert Arel node to string
|
674
|
+
s = s.to_sql unless s.is_a?(String)
|
675
|
+
# Remove any ASC/DESC modifiers
|
676
|
+
s.gsub(/\s+(?:ASC|DESC)\b/i, '')
|
677
|
+
}.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
|
678
|
+
|
679
|
+
[super, *order_columns].join(', ')
|
680
|
+
end
|
681
|
+
|
651
682
|
def strict_mode?
|
652
683
|
self.class.type_cast_config_to_boolean(@config.fetch(:strict, true))
|
653
684
|
end
|
@@ -713,6 +744,10 @@ module ActiveRecord
|
|
713
744
|
subsubselect = select.clone
|
714
745
|
subsubselect.projections = [key]
|
715
746
|
|
747
|
+
# Materialize subquery by adding distinct
|
748
|
+
# to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on'
|
749
|
+
subsubselect.distinct unless select.limit || select.offset || select.orders.any?
|
750
|
+
|
716
751
|
subselect = Arel::SelectManager.new(select.engine)
|
717
752
|
subselect.project Arel.sql(key.name)
|
718
753
|
subselect.from subsubselect.as('__active_record_temp')
|
@@ -723,7 +758,7 @@ module ActiveRecord
|
|
723
758
|
case length
|
724
759
|
when Hash
|
725
760
|
column_names.each {|name| option_strings[name] += "(#{length[name]})" if length.has_key?(name) && length[name].present?}
|
726
|
-
when
|
761
|
+
when Integer
|
727
762
|
column_names.each {|name| option_strings[name] += "(#{length})"}
|
728
763
|
end
|
729
764
|
end
|
@@ -817,7 +852,7 @@ module ActiveRecord
|
|
817
852
|
private
|
818
853
|
|
819
854
|
def version
|
820
|
-
@version ||= full_version.
|
855
|
+
@version ||= Version.new(full_version.match(/^\d+\.\d+\.\d+/)[0])
|
821
856
|
end
|
822
857
|
|
823
858
|
def mariadb?
|
@@ -825,7 +860,7 @@ module ActiveRecord
|
|
825
860
|
end
|
826
861
|
|
827
862
|
def supports_rename_index?
|
828
|
-
mariadb? ? false :
|
863
|
+
mariadb? ? false : version >= '5.7.6'
|
829
864
|
end
|
830
865
|
|
831
866
|
def configure_connection
|
@@ -836,9 +871,9 @@ module ActiveRecord
|
|
836
871
|
variables['sql_auto_is_null'] = 0
|
837
872
|
|
838
873
|
# Increase timeout so the server doesn't disconnect us.
|
839
|
-
wait_timeout = @config[:wait_timeout]
|
840
|
-
wait_timeout = 2147483 unless wait_timeout.is_a?(
|
841
|
-
variables[
|
874
|
+
wait_timeout = self.class.type_cast_config_to_integer(@config[:wait_timeout])
|
875
|
+
wait_timeout = 2147483 unless wait_timeout.is_a?(Integer)
|
876
|
+
variables["wait_timeout"] = wait_timeout
|
842
877
|
|
843
878
|
# Make MySQL reject illegal values rather than truncating or blanking them, see
|
844
879
|
# http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html#sqlmode_strict_all_tables
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'active_record/connection_adapters/abstract_mysql_adapter'
|
2
2
|
|
3
|
-
gem 'mysql2', '>= 0.3.13', '< 0.
|
3
|
+
gem 'mysql2', '>= 0.3.13', '< 0.6.0'
|
4
4
|
require 'mysql2'
|
5
5
|
|
6
6
|
module ActiveRecord
|
@@ -74,20 +74,11 @@ module ActiveRecord
|
|
74
74
|
@connection.escape(string)
|
75
75
|
end
|
76
76
|
|
77
|
-
def quoted_date(value)
|
78
|
-
if value.acts_like?(:time) && value.respond_to?(:usec)
|
79
|
-
"#{super}.#{sprintf("%06d", value.usec)}"
|
80
|
-
else
|
81
|
-
super
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
77
|
#--
|
86
78
|
# CONNECTION MANAGEMENT ====================================
|
87
79
|
#++
|
88
80
|
|
89
81
|
def active?
|
90
|
-
return false unless @connection
|
91
82
|
@connection.ping
|
92
83
|
end
|
93
84
|
|
@@ -102,10 +93,7 @@ module ActiveRecord
|
|
102
93
|
# Otherwise, this method does nothing.
|
103
94
|
def disconnect!
|
104
95
|
super
|
105
|
-
|
106
|
-
@connection.close
|
107
|
-
@connection = nil
|
108
|
-
end
|
96
|
+
@connection.close
|
109
97
|
end
|
110
98
|
|
111
99
|
#--
|
@@ -222,11 +210,9 @@ module ActiveRecord
|
|
222
210
|
|
223
211
|
# Executes the SQL statement in the context of this connection.
|
224
212
|
def execute(sql, name = nil)
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
@connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
|
229
|
-
end
|
213
|
+
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
|
214
|
+
# made since we established the connection
|
215
|
+
@connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
|
230
216
|
|
231
217
|
super
|
232
218
|
end
|
@@ -245,7 +245,7 @@ module ActiveRecord
|
|
245
245
|
return @client_encoding if @client_encoding
|
246
246
|
|
247
247
|
result = exec_query(
|
248
|
-
"
|
248
|
+
"select @@character_set_client",
|
249
249
|
'SCHEMA')
|
250
250
|
@client_encoding = ENCODINGS[result.rows.last.last]
|
251
251
|
end
|
@@ -282,6 +282,10 @@ module ActiveRecord
|
|
282
282
|
super
|
283
283
|
end
|
284
284
|
end
|
285
|
+
|
286
|
+
def has_precision?
|
287
|
+
precision || 0
|
288
|
+
end
|
285
289
|
end
|
286
290
|
|
287
291
|
class Time < Type::Time # :nodoc:
|
@@ -328,8 +332,11 @@ module ActiveRecord
|
|
328
332
|
|
329
333
|
def initialize_type_map(m) # :nodoc:
|
330
334
|
super
|
331
|
-
m.register_type %r(datetime)i, Fields::DateTime.new
|
332
335
|
m.register_type %r(time)i, Fields::Time.new
|
336
|
+
m.register_type(%r(datetime)i) do |sql_type|
|
337
|
+
precision = extract_precision(sql_type)
|
338
|
+
Fields::DateTime.new(precision: precision)
|
339
|
+
end
|
333
340
|
end
|
334
341
|
|
335
342
|
def exec_without_stmt(sql, name = 'SQL') # :nodoc:
|
@@ -95,6 +95,16 @@ module ActiveRecord
|
|
95
95
|
SQL
|
96
96
|
end
|
97
97
|
|
98
|
+
def data_sources # :nodoc
|
99
|
+
select_values(<<-SQL, 'SCHEMA')
|
100
|
+
SELECT c.relname
|
101
|
+
FROM pg_class c
|
102
|
+
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
103
|
+
WHERE c.relkind IN ('r', 'v','m') -- (r)elation/table, (v)iew, (m)aterialized view
|
104
|
+
AND n.nspname = ANY (current_schemas(false))
|
105
|
+
SQL
|
106
|
+
end
|
107
|
+
|
98
108
|
# Returns true if table exists.
|
99
109
|
# If the schema is not specified as part of +name+ then it will only find tables within
|
100
110
|
# the current schema search path (regardless of permissions to access tables in other schemas)
|
@@ -111,6 +121,7 @@ module ActiveRecord
|
|
111
121
|
AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
|
112
122
|
SQL
|
113
123
|
end
|
124
|
+
alias data_source_exists? table_exists?
|
114
125
|
|
115
126
|
def drop_table(table_name, options = {})
|
116
127
|
execute "DROP TABLE #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
|
@@ -552,7 +563,7 @@ module ActiveRecord
|
|
552
563
|
when 1, 2; 'smallint'
|
553
564
|
when 3, 4; 'integer'
|
554
565
|
when 5..8; 'bigint'
|
555
|
-
else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with
|
566
|
+
else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with scale 0 instead.")
|
556
567
|
end
|
557
568
|
when 'datetime'
|
558
569
|
return super unless precision
|
@@ -74,18 +74,6 @@ module ActiveRecord
|
|
74
74
|
boolean: { name: "boolean" }
|
75
75
|
}
|
76
76
|
|
77
|
-
class Version
|
78
|
-
include Comparable
|
79
|
-
|
80
|
-
def initialize(version_string)
|
81
|
-
@version = version_string.split('.').map { |v| v.to_i }
|
82
|
-
end
|
83
|
-
|
84
|
-
def <=>(version_string)
|
85
|
-
@version <=> version_string.split('.').map { |v| v.to_i }
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
77
|
class StatementPool < ConnectionAdapters::StatementPool
|
90
78
|
def initialize(connection, max)
|
91
79
|
super
|
@@ -375,10 +363,12 @@ module ActiveRecord
|
|
375
363
|
row['name']
|
376
364
|
end
|
377
365
|
end
|
366
|
+
alias data_sources tables
|
378
367
|
|
379
368
|
def table_exists?(table_name)
|
380
369
|
table_name && tables(nil, table_name).any?
|
381
370
|
end
|
371
|
+
alias data_source_exists? table_exists?
|
382
372
|
|
383
373
|
# Returns an array of +Column+ objects for the table specified by +table_name+.
|
384
374
|
def columns(table_name) #:nodoc:
|
data/lib/active_record/enum.rb
CHANGED
@@ -69,7 +69,7 @@ module ActiveRecord
|
|
69
69
|
# Where conditions on an enum attribute must use the ordinal value of an enum.
|
70
70
|
module Enum
|
71
71
|
def self.extended(base) # :nodoc:
|
72
|
-
base.class_attribute(:defined_enums)
|
72
|
+
base.class_attribute(:defined_enums, instance_writer: false)
|
73
73
|
base.defined_enums = {}
|
74
74
|
end
|
75
75
|
|
@@ -880,14 +880,15 @@ module ActiveRecord
|
|
880
880
|
migrations_paths.first
|
881
881
|
end
|
882
882
|
|
883
|
+
def parse_migration_filename(filename) # :nodoc:
|
884
|
+
File.basename(filename).scan(/\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/).first
|
885
|
+
end
|
886
|
+
|
883
887
|
def migrations(paths)
|
884
888
|
paths = Array(paths)
|
885
889
|
|
886
|
-
|
887
|
-
|
888
|
-
migrations = files.map do |file|
|
889
|
-
version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/).first
|
890
|
-
|
890
|
+
migrations = migration_files(paths).map do |file|
|
891
|
+
version, name, scope = parse_migration_filename(file)
|
891
892
|
raise IllegalMigrationNameError.new(file) unless version
|
892
893
|
version = version.to_i
|
893
894
|
name = name.camelize
|
@@ -898,6 +899,30 @@ module ActiveRecord
|
|
898
899
|
migrations.sort_by(&:version)
|
899
900
|
end
|
900
901
|
|
902
|
+
def migrations_status(paths)
|
903
|
+
paths = Array(paths)
|
904
|
+
|
905
|
+
db_list = ActiveRecord::SchemaMigration.normalized_versions
|
906
|
+
|
907
|
+
file_list = migration_files(paths).map do |file|
|
908
|
+
version, name, scope = parse_migration_filename(file)
|
909
|
+
raise IllegalMigrationNameError.new(file) unless version
|
910
|
+
version = ActiveRecord::SchemaMigration.normalize_migration_number(version)
|
911
|
+
status = db_list.delete(version) ? "up" : "down"
|
912
|
+
[status, version, (name + scope).humanize]
|
913
|
+
end.compact
|
914
|
+
|
915
|
+
db_list.map! do |version|
|
916
|
+
["up", version, "********** NO FILE **********"]
|
917
|
+
end
|
918
|
+
|
919
|
+
(db_list + file_list).sort_by { |_, version, _| version }
|
920
|
+
end
|
921
|
+
|
922
|
+
def migration_files(paths)
|
923
|
+
Dir[*paths.flat_map { |path| "#{path}/**/[0-9]*_*.rb" }]
|
924
|
+
end
|
925
|
+
|
901
926
|
private
|
902
927
|
|
903
928
|
def move(direction, migrations_paths, steps)
|
@@ -247,7 +247,7 @@ module ActiveRecord
|
|
247
247
|
# Returns a hash where the keys are column names and the values are
|
248
248
|
# default values when instantiating the AR object for this table.
|
249
249
|
def column_defaults
|
250
|
-
_default_attributes.to_hash
|
250
|
+
_default_attributes.dup.to_hash
|
251
251
|
end
|
252
252
|
|
253
253
|
def _default_attributes # :nodoc:
|
@@ -304,6 +304,8 @@ module ActiveRecord
|
|
304
304
|
@default_attributes = nil
|
305
305
|
@inheritance_column = nil unless defined?(@explicit_inheritance_column) && @explicit_inheritance_column
|
306
306
|
@relation = nil
|
307
|
+
|
308
|
+
initialize_find_by_cache
|
307
309
|
end
|
308
310
|
|
309
311
|
private
|
@@ -523,7 +523,7 @@ module ActiveRecord
|
|
523
523
|
# has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this
|
524
524
|
# association and evaluates to +true+.
|
525
525
|
def reject_new_record?(association_name, attributes)
|
526
|
-
|
526
|
+
will_be_destroyed?(association_name, attributes) || call_reject_if(association_name, attributes)
|
527
527
|
end
|
528
528
|
|
529
529
|
# Determines if a record with the particular +attributes+ should be
|
@@ -532,7 +532,8 @@ module ActiveRecord
|
|
532
532
|
#
|
533
533
|
# Returns false if there is a +destroy_flag+ on the attributes.
|
534
534
|
def call_reject_if(association_name, attributes)
|
535
|
-
return false if
|
535
|
+
return false if will_be_destroyed?(association_name, attributes)
|
536
|
+
|
536
537
|
case callback = self.nested_attributes_options[association_name][:reject_if]
|
537
538
|
when Symbol
|
538
539
|
method(callback).arity == 0 ? send(callback) : send(callback, attributes)
|
@@ -541,6 +542,15 @@ module ActiveRecord
|
|
541
542
|
end
|
542
543
|
end
|
543
544
|
|
545
|
+
# Only take into account the destroy flag if <tt>:allow_destroy</tt> is true
|
546
|
+
def will_be_destroyed?(association_name, attributes)
|
547
|
+
allow_destroy?(association_name) && has_destroy_flag?(attributes)
|
548
|
+
end
|
549
|
+
|
550
|
+
def allow_destroy?(association_name)
|
551
|
+
self.nested_attributes_options[association_name][:allow_destroy]
|
552
|
+
end
|
553
|
+
|
544
554
|
def raise_nested_attributes_record_not_found!(association_name, record_id)
|
545
555
|
raise RecordNotFound, "Couldn't find #{self.class._reflect_on_association(association_name).klass.name} with ID=#{record_id} for #{self.class.name} with ID=#{id}"
|
546
556
|
end
|
@@ -57,8 +57,10 @@ module ActiveRecord
|
|
57
57
|
console do |app|
|
58
58
|
require "active_record/railties/console_sandbox" if app.sandbox?
|
59
59
|
require "active_record/base"
|
60
|
-
|
61
|
-
|
60
|
+
unless ActiveSupport::Logger.logger_outputs_to?(Rails.logger, STDERR, STDOUT)
|
61
|
+
console = ActiveSupport::Logger.new(STDERR)
|
62
|
+
Rails.logger.extend ActiveSupport::Logger.broadcast console
|
63
|
+
end
|
62
64
|
end
|
63
65
|
|
64
66
|
runner do
|