activerecord 5.0.0 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +218 -2
- data/README.rdoc +1 -1
- data/lib/active_record/aggregations.rb +4 -2
- data/lib/active_record/association_relation.rb +4 -1
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +4 -2
- data/lib/active_record/associations/builder/singular_association.rb +10 -1
- data/lib/active_record/associations/collection_association.rb +22 -17
- data/lib/active_record/associations/collection_proxy.rb +20 -7
- data/lib/active_record/associations/has_many_through_association.rb +4 -0
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/join_dependency.rb +10 -4
- data/lib/active_record/associations/preloader/association.rb +18 -4
- data/lib/active_record/associations/preloader/collection_association.rb +0 -1
- data/lib/active_record/associations/preloader/singular_association.rb +0 -1
- data/lib/active_record/associations/singular_association.rb +8 -2
- data/lib/active_record/associations.rb +5 -0
- data/lib/active_record/attribute.rb +3 -3
- data/lib/active_record/attribute_methods/primary_key.rb +14 -1
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
- data/lib/active_record/attribute_methods.rb +3 -7
- data/lib/active_record/attribute_set/builder.rb +29 -7
- data/lib/active_record/attribute_set.rb +2 -0
- data/lib/active_record/attributes.rb +3 -3
- data/lib/active_record/autosave_association.rb +15 -11
- data/lib/active_record/base.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +40 -32
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +29 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +4 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +8 -7
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +38 -33
- data/lib/active_record/connection_adapters/abstract_adapter.rb +28 -5
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +43 -45
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +6 -23
- data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -5
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +12 -1
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +7 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +6 -2
- data/lib/active_record/core.rb +3 -1
- data/lib/active_record/enum.rb +6 -5
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/integration.rb +13 -10
- data/lib/active_record/migration.rb +6 -5
- data/lib/active_record/model_schema.rb +134 -47
- data/lib/active_record/no_touching.rb +4 -0
- data/lib/active_record/persistence.rb +10 -4
- data/lib/active_record/query_cache.rb +13 -15
- data/lib/active_record/querying.rb +3 -3
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/reflection.rb +12 -0
- data/lib/active_record/relation/calculations.rb +11 -11
- data/lib/active_record/relation/delegation.rb +1 -1
- data/lib/active_record/relation/finder_methods.rb +11 -9
- data/lib/active_record/relation/query_methods.rb +3 -3
- data/lib/active_record/relation.rb +7 -4
- data/lib/active_record/result.rb +7 -1
- data/lib/active_record/sanitization.rb +11 -1
- data/lib/active_record/schema_dumper.rb +10 -17
- data/lib/active_record/scoping/named.rb +1 -1
- data/lib/active_record/statement_cache.rb +2 -2
- data/lib/active_record/table_metadata.rb +4 -3
- data/lib/active_record/touch_later.rb +6 -1
- data/lib/active_record/type/internal/abstract_json.rb +5 -1
- data/lib/active_record/validations/uniqueness.rb +3 -4
- data/lib/active_record.rb +1 -1
- metadata +8 -7
|
@@ -61,18 +61,18 @@ module ActiveRecord
|
|
|
61
61
|
# Most of the methods in the adapter are useful during migrations. Most
|
|
62
62
|
# notably, the instance methods provided by SchemaStatements are very useful.
|
|
63
63
|
class AbstractAdapter
|
|
64
|
-
ADAPTER_NAME =
|
|
64
|
+
ADAPTER_NAME = "Abstract".freeze
|
|
65
|
+
include ActiveSupport::Callbacks
|
|
66
|
+
define_callbacks :checkout, :checkin
|
|
67
|
+
|
|
65
68
|
include Quoting, DatabaseStatements, SchemaStatements
|
|
66
69
|
include DatabaseLimits
|
|
67
70
|
include QueryCache
|
|
68
|
-
include ActiveSupport::Callbacks
|
|
69
71
|
include ColumnDumper
|
|
70
72
|
include Savepoints
|
|
71
73
|
|
|
72
74
|
SIMPLE_INT = /\A\d+\z/
|
|
73
75
|
|
|
74
|
-
define_callbacks :checkout, :checkin
|
|
75
|
-
|
|
76
76
|
attr_accessor :visitor, :pool
|
|
77
77
|
attr_reader :schema_cache, :owner, :logger
|
|
78
78
|
alias :in_use? :owner
|
|
@@ -184,7 +184,30 @@ module ActiveRecord
|
|
|
184
184
|
|
|
185
185
|
# this method must only be called while holding connection pool's mutex
|
|
186
186
|
def expire
|
|
187
|
-
|
|
187
|
+
if in_use?
|
|
188
|
+
if @owner != Thread.current
|
|
189
|
+
raise ActiveRecordError, "Cannot expire connection, " <<
|
|
190
|
+
"it is owned by a different thread: #{@owner}. " <<
|
|
191
|
+
"Current thread: #{Thread.current}."
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
@owner = nil
|
|
195
|
+
else
|
|
196
|
+
raise ActiveRecordError, 'Cannot expire connection, it is not currently leased.'
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# this method must only be called while holding connection pool's mutex (and a desire for segfaults)
|
|
201
|
+
def steal! # :nodoc:
|
|
202
|
+
if in_use?
|
|
203
|
+
if @owner != Thread.current
|
|
204
|
+
pool.send :remove_connection_from_thread_cache, self, @owner
|
|
205
|
+
|
|
206
|
+
@owner = Thread.current
|
|
207
|
+
end
|
|
208
|
+
else
|
|
209
|
+
raise ActiveRecordError, 'Cannot steal connection, it is not currently leased.'
|
|
210
|
+
end
|
|
188
211
|
end
|
|
189
212
|
|
|
190
213
|
def unprepared_statement
|
|
@@ -383,11 +383,11 @@ module ActiveRecord
|
|
|
383
383
|
mysql_index_type = row[:Index_type].downcase.to_sym
|
|
384
384
|
index_type = INDEX_TYPES.include?(mysql_index_type) ? mysql_index_type : nil
|
|
385
385
|
index_using = INDEX_USINGS.include?(mysql_index_type) ? mysql_index_type : nil
|
|
386
|
-
indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique].to_i == 0, [],
|
|
386
|
+
indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique].to_i == 0, [], {}, nil, nil, index_type, index_using, row[:Index_comment].presence)
|
|
387
387
|
end
|
|
388
388
|
|
|
389
389
|
indexes.last.columns << row[:Column_name]
|
|
390
|
-
indexes.last.lengths
|
|
390
|
+
indexes.last.lengths.merge!(row[:Column_name] => row[:Sub_part].to_i) if row[:Sub_part]
|
|
391
391
|
end
|
|
392
392
|
end
|
|
393
393
|
|
|
@@ -409,10 +409,13 @@ module ActiveRecord
|
|
|
409
409
|
end
|
|
410
410
|
|
|
411
411
|
def table_comment(table_name) # :nodoc:
|
|
412
|
+
schema, name = extract_schema_qualified_name(table_name)
|
|
413
|
+
|
|
412
414
|
select_value(<<-SQL.strip_heredoc, 'SCHEMA')
|
|
413
415
|
SELECT table_comment
|
|
414
416
|
FROM information_schema.tables
|
|
415
|
-
WHERE
|
|
417
|
+
WHERE table_schema = #{quote(schema)}
|
|
418
|
+
AND table_name = #{quote(name)}
|
|
416
419
|
SQL
|
|
417
420
|
end
|
|
418
421
|
|
|
@@ -460,7 +463,6 @@ module ActiveRecord
|
|
|
460
463
|
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
|
|
461
464
|
# In that case, +options+ and the block will be used by create_table.
|
|
462
465
|
def drop_table(table_name, options = {})
|
|
463
|
-
create_table_info_cache.delete(table_name) if create_table_info_cache.key?(table_name)
|
|
464
466
|
execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
|
|
465
467
|
end
|
|
466
468
|
|
|
@@ -506,7 +508,7 @@ module ActiveRecord
|
|
|
506
508
|
end
|
|
507
509
|
|
|
508
510
|
def add_sql_comment!(sql, comment) # :nodoc:
|
|
509
|
-
sql << " COMMENT #{quote(comment)}" if comment
|
|
511
|
+
sql << " COMMENT #{quote(comment)}" if comment.present?
|
|
510
512
|
sql
|
|
511
513
|
end
|
|
512
514
|
|
|
@@ -515,19 +517,21 @@ module ActiveRecord
|
|
|
515
517
|
|
|
516
518
|
schema, name = extract_schema_qualified_name(table_name)
|
|
517
519
|
|
|
518
|
-
fk_info = select_all
|
|
519
|
-
SELECT fk.referenced_table_name
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
520
|
+
fk_info = select_all(<<-SQL.strip_heredoc, 'SCHEMA')
|
|
521
|
+
SELECT fk.referenced_table_name AS 'to_table',
|
|
522
|
+
fk.referenced_column_name AS 'primary_key',
|
|
523
|
+
fk.column_name AS 'column',
|
|
524
|
+
fk.constraint_name AS 'name',
|
|
525
|
+
rc.update_rule AS 'on_update',
|
|
526
|
+
rc.delete_rule AS 'on_delete'
|
|
523
527
|
FROM information_schema.key_column_usage fk
|
|
524
|
-
|
|
528
|
+
JOIN information_schema.referential_constraints rc
|
|
529
|
+
USING (constraint_schema, constraint_name)
|
|
530
|
+
WHERE fk.referenced_column_name IS NOT NULL
|
|
525
531
|
AND fk.table_schema = #{quote(schema)}
|
|
526
532
|
AND fk.table_name = #{quote(name)}
|
|
527
533
|
SQL
|
|
528
534
|
|
|
529
|
-
create_table_info = create_table_info(table_name)
|
|
530
|
-
|
|
531
535
|
fk_info.map do |row|
|
|
532
536
|
options = {
|
|
533
537
|
column: row['column'],
|
|
@@ -535,14 +539,16 @@ module ActiveRecord
|
|
|
535
539
|
primary_key: row['primary_key']
|
|
536
540
|
}
|
|
537
541
|
|
|
538
|
-
options[:on_update] = extract_foreign_key_action(
|
|
539
|
-
options[:on_delete] = extract_foreign_key_action(
|
|
542
|
+
options[:on_update] = extract_foreign_key_action(row['on_update'])
|
|
543
|
+
options[:on_delete] = extract_foreign_key_action(row['on_delete'])
|
|
540
544
|
|
|
541
545
|
ForeignKeyDefinition.new(table_name, row['to_table'], options)
|
|
542
546
|
end
|
|
543
547
|
end
|
|
544
548
|
|
|
545
|
-
def table_options(table_name)
|
|
549
|
+
def table_options(table_name) # :nodoc:
|
|
550
|
+
table_options = {}
|
|
551
|
+
|
|
546
552
|
create_table_info = create_table_info(table_name)
|
|
547
553
|
|
|
548
554
|
# strip create_definitions and partition_options
|
|
@@ -551,10 +557,14 @@ module ActiveRecord
|
|
|
551
557
|
# strip AUTO_INCREMENT
|
|
552
558
|
raw_table_options.sub!(/(ENGINE=\w+)(?: AUTO_INCREMENT=\d+)/, '\1')
|
|
553
559
|
|
|
560
|
+
table_options[:options] = raw_table_options
|
|
561
|
+
|
|
554
562
|
# strip COMMENT
|
|
555
|
-
raw_table_options.sub!(/ COMMENT='.+'/, '')
|
|
563
|
+
if raw_table_options.sub!(/ COMMENT='.+'/, '')
|
|
564
|
+
table_options[:comment] = table_comment(table_name)
|
|
565
|
+
end
|
|
556
566
|
|
|
557
|
-
|
|
567
|
+
table_options
|
|
558
568
|
end
|
|
559
569
|
|
|
560
570
|
# Maps logical Rails types to MySQL-specific data types.
|
|
@@ -682,7 +692,7 @@ module ActiveRecord
|
|
|
682
692
|
|
|
683
693
|
def register_integer_type(mapping, key, options) # :nodoc:
|
|
684
694
|
mapping.register_type(key) do |sql_type|
|
|
685
|
-
if /\bunsigned\
|
|
695
|
+
if /\bunsigned\b/ === sql_type
|
|
686
696
|
Type::UnsignedInteger.new(options)
|
|
687
697
|
else
|
|
688
698
|
Type::Integer.new(options)
|
|
@@ -702,29 +712,23 @@ module ActiveRecord
|
|
|
702
712
|
MySQL::TypeMetadata.new(super(sql_type), extra: extra, strict: strict_mode?)
|
|
703
713
|
end
|
|
704
714
|
|
|
705
|
-
def add_index_length(
|
|
706
|
-
if
|
|
715
|
+
def add_index_length(quoted_columns, **options)
|
|
716
|
+
if length = options[:length]
|
|
707
717
|
case length
|
|
708
718
|
when Hash
|
|
709
|
-
|
|
719
|
+
length = length.symbolize_keys
|
|
720
|
+
quoted_columns.each { |name, column| column << "(#{length[name]})" if length[name].present? }
|
|
710
721
|
when Integer
|
|
711
|
-
|
|
722
|
+
quoted_columns.each { |name, column| column << "(#{length})" }
|
|
712
723
|
end
|
|
713
724
|
end
|
|
714
725
|
|
|
715
|
-
|
|
726
|
+
quoted_columns
|
|
716
727
|
end
|
|
717
728
|
|
|
718
|
-
def
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
# add index length
|
|
722
|
-
option_strings = add_index_length(option_strings, column_names, options)
|
|
723
|
-
|
|
724
|
-
# add index sort order
|
|
725
|
-
option_strings = add_index_sort_order(option_strings, column_names, options)
|
|
726
|
-
|
|
727
|
-
column_names.map {|name| quote_column_name(name) + option_strings[name]}
|
|
729
|
+
def add_options_for_index_columns(quoted_columns, **options)
|
|
730
|
+
quoted_columns = add_index_length(quoted_columns, options)
|
|
731
|
+
super
|
|
728
732
|
end
|
|
729
733
|
|
|
730
734
|
def translate_exception(exception, message)
|
|
@@ -883,21 +887,15 @@ module ActiveRecord
|
|
|
883
887
|
end
|
|
884
888
|
end
|
|
885
889
|
|
|
886
|
-
def extract_foreign_key_action(
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
when 'SET NULL'; :nullify
|
|
891
|
-
end
|
|
890
|
+
def extract_foreign_key_action(specifier) # :nodoc:
|
|
891
|
+
case specifier
|
|
892
|
+
when 'CASCADE'; :cascade
|
|
893
|
+
when 'SET NULL'; :nullify
|
|
892
894
|
end
|
|
893
895
|
end
|
|
894
896
|
|
|
895
|
-
def create_table_info_cache # :nodoc:
|
|
896
|
-
@create_table_info_cache ||= {}
|
|
897
|
-
end
|
|
898
|
-
|
|
899
897
|
def create_table_info(table_name) # :nodoc:
|
|
900
|
-
|
|
898
|
+
select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
|
|
901
899
|
end
|
|
902
900
|
|
|
903
901
|
def create_table_definition(*args) # :nodoc:
|
|
@@ -13,19 +13,6 @@ module ActiveRecord
|
|
|
13
13
|
result
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
# Returns a record hash with the column names as keys and column values
|
|
17
|
-
# as values.
|
|
18
|
-
def select_one(arel, name = nil, binds = [])
|
|
19
|
-
arel, binds = binds_from_relation(arel, binds)
|
|
20
|
-
@connection.query_options.merge!(as: :hash)
|
|
21
|
-
select_result(to_sql(arel, binds), name, binds) do |result|
|
|
22
|
-
@connection.next_result while @connection.more_results?
|
|
23
|
-
result.first
|
|
24
|
-
end
|
|
25
|
-
ensure
|
|
26
|
-
@connection.query_options.merge!(as: :array)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
16
|
# Returns an array of arrays containing the field values.
|
|
30
17
|
# Order is the same as that returned by +columns+.
|
|
31
18
|
def select_rows(sql, name = nil, binds = [])
|
|
@@ -37,11 +24,9 @@ module ActiveRecord
|
|
|
37
24
|
|
|
38
25
|
# Executes the SQL statement in the context of this connection.
|
|
39
26
|
def execute(sql, name = nil)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
@connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
|
|
44
|
-
end
|
|
27
|
+
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
|
|
28
|
+
# made since we established the connection
|
|
29
|
+
@connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
|
|
45
30
|
|
|
46
31
|
super
|
|
47
32
|
end
|
|
@@ -84,11 +69,9 @@ module ActiveRecord
|
|
|
84
69
|
end
|
|
85
70
|
|
|
86
71
|
def exec_stmt_and_free(sql, name, binds, cache_stmt: false)
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
@connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
|
|
91
|
-
end
|
|
72
|
+
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
|
|
73
|
+
# made since we established the connection
|
|
74
|
+
@connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
|
|
92
75
|
|
|
93
76
|
type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) }
|
|
94
77
|
|
|
@@ -2,7 +2,7 @@ module ActiveRecord
|
|
|
2
2
|
module ConnectionAdapters
|
|
3
3
|
module MySQL
|
|
4
4
|
module Quoting # :nodoc:
|
|
5
|
-
QUOTED_TRUE, QUOTED_FALSE = '1', '0'
|
|
5
|
+
QUOTED_TRUE, QUOTED_FALSE = '1'.freeze, '0'.freeze
|
|
6
6
|
|
|
7
7
|
def quote_column_name(name)
|
|
8
8
|
@quoted_column_names[name] ||= "`#{super.gsub('`', '``')}`"
|
|
@@ -90,7 +90,6 @@ module ActiveRecord
|
|
|
90
90
|
#++
|
|
91
91
|
|
|
92
92
|
def active?
|
|
93
|
-
return false unless @connection
|
|
94
93
|
@connection.ping
|
|
95
94
|
end
|
|
96
95
|
|
|
@@ -105,10 +104,7 @@ module ActiveRecord
|
|
|
105
104
|
# Otherwise, this method does nothing.
|
|
106
105
|
def disconnect!
|
|
107
106
|
super
|
|
108
|
-
|
|
109
|
-
@connection.close
|
|
110
|
-
@connection = nil
|
|
111
|
-
end
|
|
107
|
+
@connection.close
|
|
112
108
|
end
|
|
113
109
|
|
|
114
110
|
private
|
|
@@ -124,6 +124,8 @@ module ActiveRecord
|
|
|
124
124
|
pk = primary_key(table_ref) if table_ref
|
|
125
125
|
end
|
|
126
126
|
|
|
127
|
+
pk = suppress_composite_primary_key(pk)
|
|
128
|
+
|
|
127
129
|
if pk && use_insert_returning?
|
|
128
130
|
sql = "#{sql} RETURNING #{quote_column_name(pk)}"
|
|
129
131
|
end
|
|
@@ -164,6 +166,12 @@ module ActiveRecord
|
|
|
164
166
|
def exec_rollback_db_transaction
|
|
165
167
|
execute "ROLLBACK"
|
|
166
168
|
end
|
|
169
|
+
|
|
170
|
+
private
|
|
171
|
+
|
|
172
|
+
def suppress_composite_primary_key(pk)
|
|
173
|
+
pk unless pk.is_a?(Array)
|
|
174
|
+
end
|
|
167
175
|
end
|
|
168
176
|
end
|
|
169
177
|
end
|
|
@@ -33,7 +33,11 @@ module ActiveRecord
|
|
|
33
33
|
|
|
34
34
|
def serialize(value)
|
|
35
35
|
if value.is_a?(::Array)
|
|
36
|
-
@pg_encoder.encode(type_cast_array(value, :serialize))
|
|
36
|
+
result = @pg_encoder.encode(type_cast_array(value, :serialize))
|
|
37
|
+
if encoding = determine_encoding_of_strings(value)
|
|
38
|
+
result.force_encoding(encoding)
|
|
39
|
+
end
|
|
40
|
+
result
|
|
37
41
|
else
|
|
38
42
|
super
|
|
39
43
|
end
|
|
@@ -63,6 +67,13 @@ module ActiveRecord
|
|
|
63
67
|
@subtype.public_send(method, value)
|
|
64
68
|
end
|
|
65
69
|
end
|
|
70
|
+
|
|
71
|
+
def determine_encoding_of_strings(value)
|
|
72
|
+
case value
|
|
73
|
+
when ::Array then determine_encoding_of_strings(value.first)
|
|
74
|
+
when ::String then value.encoding
|
|
75
|
+
end
|
|
76
|
+
end
|
|
66
77
|
end
|
|
67
78
|
end
|
|
68
79
|
end
|
|
@@ -7,7 +7,7 @@ module ActiveRecord
|
|
|
7
7
|
:bit
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
def
|
|
10
|
+
def cast_value(value)
|
|
11
11
|
if ::String === value
|
|
12
12
|
case value
|
|
13
13
|
when /^0x/i
|
|
@@ -16,7 +16,7 @@ module ActiveRecord
|
|
|
16
16
|
value # Bit-string notation
|
|
17
17
|
end
|
|
18
18
|
else
|
|
19
|
-
value
|
|
19
|
+
value.to_s
|
|
20
20
|
end
|
|
21
21
|
end
|
|
22
22
|
|
|
@@ -238,6 +238,12 @@ module ActiveRecord
|
|
|
238
238
|
PostgreSQLColumn.new(*args)
|
|
239
239
|
end
|
|
240
240
|
|
|
241
|
+
def table_options(table_name) # :nodoc:
|
|
242
|
+
if comment = table_comment(table_name)
|
|
243
|
+
{ comment: comment }
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
241
247
|
# Returns a comment stored in database for given table
|
|
242
248
|
def table_comment(table_name) # :nodoc:
|
|
243
249
|
name = Utils.extract_schema_qualified_name(table_name.to_s)
|
|
@@ -579,7 +585,7 @@ module ActiveRecord
|
|
|
579
585
|
end
|
|
580
586
|
|
|
581
587
|
def foreign_keys(table_name)
|
|
582
|
-
fk_info = select_all
|
|
588
|
+
fk_info = select_all(<<-SQL.strip_heredoc, 'SCHEMA')
|
|
583
589
|
SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete
|
|
584
590
|
FROM pg_constraint c
|
|
585
591
|
JOIN pg_class t1 ON c.conrelid = t1.oid
|
|
@@ -764,11 +764,15 @@ module ActiveRecord
|
|
|
764
764
|
@case_insensitive_cache[column.sql_type] ||= begin
|
|
765
765
|
sql = <<-end_sql
|
|
766
766
|
SELECT exists(
|
|
767
|
+
SELECT * FROM pg_proc
|
|
768
|
+
WHERE proname = 'lower'
|
|
769
|
+
AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
|
|
770
|
+
) OR exists(
|
|
767
771
|
SELECT * FROM pg_proc
|
|
768
772
|
INNER JOIN pg_cast
|
|
769
|
-
ON casttarget::
|
|
773
|
+
ON ARRAY[casttarget]::oidvector = proargtypes
|
|
770
774
|
WHERE proname = 'lower'
|
|
771
|
-
AND castsource =
|
|
775
|
+
AND castsource = #{quote column.sql_type}::regtype
|
|
772
776
|
)
|
|
773
777
|
end_sql
|
|
774
778
|
execute_and_clear(sql, "SCHEMA", []) do |result|
|
data/lib/active_record/core.rb
CHANGED
|
@@ -310,8 +310,8 @@ module ActiveRecord
|
|
|
310
310
|
# # Instantiates a single new object
|
|
311
311
|
# User.new(first_name: 'Jamie')
|
|
312
312
|
def initialize(attributes = nil)
|
|
313
|
-
@attributes = self.class._default_attributes.deep_dup
|
|
314
313
|
self.class.define_attribute_methods
|
|
314
|
+
@attributes = self.class._default_attributes.deep_dup
|
|
315
315
|
|
|
316
316
|
init_internals
|
|
317
317
|
initialize_internals_callback
|
|
@@ -346,6 +346,8 @@ module ActiveRecord
|
|
|
346
346
|
|
|
347
347
|
self.class.define_attribute_methods
|
|
348
348
|
|
|
349
|
+
yield self if block_given?
|
|
350
|
+
|
|
349
351
|
_run_find_callbacks
|
|
350
352
|
_run_initialize_callbacks
|
|
351
353
|
|
data/lib/active_record/enum.rb
CHANGED
|
@@ -159,8 +159,9 @@ module ActiveRecord
|
|
|
159
159
|
detect_enum_conflict!(name, name)
|
|
160
160
|
detect_enum_conflict!(name, "#{name}=")
|
|
161
161
|
|
|
162
|
-
|
|
163
|
-
|
|
162
|
+
attr = attribute_alias?(name) ? attribute_alias(name) : name
|
|
163
|
+
decorate_attribute_type(attr, :enum) do |subtype|
|
|
164
|
+
EnumType.new(attr, enum_values, subtype)
|
|
164
165
|
end
|
|
165
166
|
|
|
166
167
|
_enum_methods_module.module_eval do
|
|
@@ -182,15 +183,15 @@ module ActiveRecord
|
|
|
182
183
|
|
|
183
184
|
# def active?() status == 0 end
|
|
184
185
|
klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
|
|
185
|
-
define_method("#{value_method_name}?") { self[
|
|
186
|
+
define_method("#{value_method_name}?") { self[attr] == value.to_s }
|
|
186
187
|
|
|
187
188
|
# def active!() update! status: :active end
|
|
188
189
|
klass.send(:detect_enum_conflict!, name, "#{value_method_name}!")
|
|
189
|
-
define_method("#{value_method_name}!") { update!
|
|
190
|
+
define_method("#{value_method_name}!") { update!(attr => value) }
|
|
190
191
|
|
|
191
192
|
# scope :active, -> { where status: 0 }
|
|
192
193
|
klass.send(:detect_enum_conflict!, name, value_method_name, true)
|
|
193
|
-
klass.scope value_method_name, -> { where(
|
|
194
|
+
klass.scope value_method_name, -> { where(attr => value) }
|
|
194
195
|
end
|
|
195
196
|
end
|
|
196
197
|
defined_enums[name.to_s] = enum_values
|
|
@@ -53,18 +53,21 @@ module ActiveRecord
|
|
|
53
53
|
#
|
|
54
54
|
# Person.find(5).cache_key(:updated_at, :last_reviewed_at)
|
|
55
55
|
def cache_key(*timestamp_names)
|
|
56
|
-
|
|
57
|
-
when new_record?
|
|
56
|
+
if new_record?
|
|
58
57
|
"#{model_name.cache_key}/new"
|
|
59
|
-
when timestamp_names.any?
|
|
60
|
-
timestamp = max_updated_column_timestamp(timestamp_names)
|
|
61
|
-
timestamp = timestamp.utc.to_s(cache_timestamp_format)
|
|
62
|
-
"#{model_name.cache_key}/#{id}-#{timestamp}"
|
|
63
|
-
when timestamp = max_updated_column_timestamp
|
|
64
|
-
timestamp = timestamp.utc.to_s(cache_timestamp_format)
|
|
65
|
-
"#{model_name.cache_key}/#{id}-#{timestamp}"
|
|
66
58
|
else
|
|
67
|
-
|
|
59
|
+
timestamp = if timestamp_names.any?
|
|
60
|
+
max_updated_column_timestamp(timestamp_names)
|
|
61
|
+
else
|
|
62
|
+
max_updated_column_timestamp
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
if timestamp
|
|
66
|
+
timestamp = timestamp.utc.to_s(cache_timestamp_format)
|
|
67
|
+
"#{model_name.cache_key}/#{id}-#{timestamp}"
|
|
68
|
+
else
|
|
69
|
+
"#{model_name.cache_key}/#{id}"
|
|
70
|
+
end
|
|
68
71
|
end
|
|
69
72
|
end
|
|
70
73
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
require "set"
|
|
2
|
+
require "zlib"
|
|
1
3
|
require "active_support/core_ext/module/attribute_accessors"
|
|
2
|
-
require 'set'
|
|
3
4
|
|
|
4
5
|
module ActiveRecord
|
|
5
6
|
class MigrationError < ActiveRecordError#:nodoc:
|
|
@@ -126,9 +127,9 @@ module ActiveRecord
|
|
|
126
127
|
class PendingMigrationError < MigrationError#:nodoc:
|
|
127
128
|
def initialize(message = nil)
|
|
128
129
|
if !message && defined?(Rails.env)
|
|
129
|
-
super("Migrations are pending. To resolve this issue, run:\n\n
|
|
130
|
+
super("Migrations are pending. To resolve this issue, run:\n\n bin/rails db:migrate RAILS_ENV=#{::Rails.env}")
|
|
130
131
|
elsif !message
|
|
131
|
-
super("Migrations are pending. To resolve this issue, run:\n\n
|
|
132
|
+
super("Migrations are pending. To resolve this issue, run:\n\n bin/rails db:migrate")
|
|
132
133
|
else
|
|
133
134
|
super
|
|
134
135
|
end
|
|
@@ -145,7 +146,7 @@ module ActiveRecord
|
|
|
145
146
|
|
|
146
147
|
class NoEnvironmentInSchemaError < MigrationError #:nodoc:
|
|
147
148
|
def initialize
|
|
148
|
-
msg = "Environment data not found in the schema. To resolve this issue, run: \n\n
|
|
149
|
+
msg = "Environment data not found in the schema. To resolve this issue, run: \n\n bin/rails db:environment:set"
|
|
149
150
|
if defined?(Rails.env)
|
|
150
151
|
super("#{msg} RAILS_ENV=#{::Rails.env}")
|
|
151
152
|
else
|
|
@@ -168,7 +169,7 @@ module ActiveRecord
|
|
|
168
169
|
msg = "You are attempting to modify a database that was last run in `#{ stored }` environment.\n"
|
|
169
170
|
msg << "You are running in `#{ current }` environment. "
|
|
170
171
|
msg << "If you are sure you want to continue, first set the environment using:\n\n"
|
|
171
|
-
msg << "
|
|
172
|
+
msg << " bin/rails db:environment:set"
|
|
172
173
|
if defined?(Rails.env)
|
|
173
174
|
super("#{msg} RAILS_ENV=#{::Rails.env}\n\n")
|
|
174
175
|
else
|