activerecord 5.0.0 → 5.0.7.2
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 +431 -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/association.rb +11 -1
- data/lib/active_record/associations/association_scope.rb +1 -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 +56 -48
- data/lib/active_record/associations/collection_proxy.rb +38 -20
- data/lib/active_record/associations/has_many_association.rb +1 -7
- data/lib/active_record/associations/has_many_through_association.rb +2 -4
- data/lib/active_record/associations/join_dependency/join_association.rb +6 -11
- 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 +24 -37
- 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/preloader/through_association.rb +10 -4
- data/lib/active_record/associations/singular_association.rb +8 -2
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +38 -17
- 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/read.rb +1 -1
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +2 -2
- data/lib/active_record/attribute_methods.rb +3 -7
- data/lib/active_record/attribute_set/builder.rb +37 -13
- 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/collection_cache_key.rb +16 -6
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +41 -33
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +37 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +11 -14
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +68 -56
- data/lib/active_record/connection_adapters/abstract_adapter.rb +36 -12
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +63 -60
- data/lib/active_record/connection_adapters/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +8 -25
- data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +2 -6
- data/lib/active_record/connection_adapters/postgresql/column.rb +28 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +12 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +32 -3
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +18 -8
- data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +25 -19
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +4 -4
- data/lib/active_record/core.rb +6 -4
- data/lib/active_record/enum.rb +8 -5
- data/lib/active_record/explain.rb +20 -9
- data/lib/active_record/fixtures.rb +5 -5
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/integration.rb +13 -10
- data/lib/active_record/log_subscriber.rb +19 -17
- data/lib/active_record/migration.rb +35 -14
- data/lib/active_record/model_schema.rb +161 -52
- data/lib/active_record/no_touching.rb +4 -0
- data/lib/active_record/persistence.rb +41 -20
- data/lib/active_record/query_cache.rb +15 -17
- data/lib/active_record/querying.rb +3 -3
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +9 -21
- data/lib/active_record/reflection.rb +20 -0
- data/lib/active_record/relation/batches.rb +10 -10
- data/lib/active_record/relation/calculations.rb +16 -12
- data/lib/active_record/relation/delegation.rb +2 -1
- data/lib/active_record/relation/finder_methods.rb +13 -11
- data/lib/active_record/relation/query_methods.rb +3 -3
- data/lib/active_record/relation.rb +12 -5
- 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/default.rb +5 -1
- data/lib/active_record/scoping/named.rb +18 -6
- data/lib/active_record/scoping.rb +4 -3
- data/lib/active_record/serialization.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/tasks/database_tasks.rb +14 -11
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/touch_later.rb +6 -1
- data/lib/active_record/transactions.rb +1 -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 +3 -2
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
- data/lib/rails/generators/active_record/migration.rb +8 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
- metadata +7 -8
@@ -68,7 +68,7 @@ module ActiveRecord
|
|
68
68
|
@statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
|
69
69
|
|
70
70
|
if version < '5.0.0'
|
71
|
-
raise "Your version of MySQL (#{
|
71
|
+
raise "Your version of MySQL (#{version_string}) is too old. Active Record supports MySQL >= 5.0."
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
@@ -81,7 +81,7 @@ module ActiveRecord
|
|
81
81
|
end
|
82
82
|
|
83
83
|
def version #:nodoc:
|
84
|
-
@version ||= Version.new(
|
84
|
+
@version ||= Version.new(version_string)
|
85
85
|
end
|
86
86
|
|
87
87
|
def mariadb? # :nodoc:
|
@@ -324,7 +324,7 @@ module ActiveRecord
|
|
324
324
|
|
325
325
|
def data_sources
|
326
326
|
sql = "SELECT table_name FROM information_schema.tables "
|
327
|
-
sql << "WHERE table_schema =
|
327
|
+
sql << "WHERE table_schema = DATABASE()"
|
328
328
|
|
329
329
|
select_values(sql, 'SCHEMA')
|
330
330
|
end
|
@@ -350,7 +350,7 @@ module ActiveRecord
|
|
350
350
|
schema, name = extract_schema_qualified_name(table_name)
|
351
351
|
|
352
352
|
sql = "SELECT table_name FROM information_schema.tables "
|
353
|
-
sql << "WHERE table_schema = #{
|
353
|
+
sql << "WHERE table_schema = #{schema} AND table_name = #{name}"
|
354
354
|
|
355
355
|
select_values(sql, 'SCHEMA').any?
|
356
356
|
end
|
@@ -365,7 +365,7 @@ module ActiveRecord
|
|
365
365
|
schema, name = extract_schema_qualified_name(view_name)
|
366
366
|
|
367
367
|
sql = "SELECT table_name FROM information_schema.tables WHERE table_type = 'VIEW'"
|
368
|
-
sql << " AND table_schema = #{
|
368
|
+
sql << " AND table_schema = #{schema} AND table_name = #{name}"
|
369
369
|
|
370
370
|
select_values(sql, 'SCHEMA').any?
|
371
371
|
end
|
@@ -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
|
|
@@ -399,8 +399,8 @@ module ActiveRecord
|
|
399
399
|
table_name = table_name.to_s
|
400
400
|
column_definitions(table_name).map do |field|
|
401
401
|
type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
|
402
|
-
if type_metadata.type == :datetime && field[:Default]
|
403
|
-
default, default_function = nil,
|
402
|
+
if type_metadata.type == :datetime && field[:Default] =~ /\ACURRENT_TIMESTAMP(?:\(\))?\z/i
|
403
|
+
default, default_function = nil, "CURRENT_TIMESTAMP"
|
404
404
|
else
|
405
405
|
default, default_function = field[:Default], nil
|
406
406
|
end
|
@@ -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 = #{schema}
|
418
|
+
AND table_name = #{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,22 @@ 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
|
-
|
525
|
-
|
526
|
-
|
528
|
+
JOIN information_schema.referential_constraints rc
|
529
|
+
USING (constraint_schema, constraint_name)
|
530
|
+
WHERE fk.referenced_column_name IS NOT NULL
|
531
|
+
AND fk.table_schema = #{schema}
|
532
|
+
AND fk.table_name = #{name}
|
533
|
+
AND rc.table_name = #{name}
|
527
534
|
SQL
|
528
535
|
|
529
|
-
create_table_info = create_table_info(table_name)
|
530
|
-
|
531
536
|
fk_info.map do |row|
|
532
537
|
options = {
|
533
538
|
column: row['column'],
|
@@ -535,14 +540,16 @@ module ActiveRecord
|
|
535
540
|
primary_key: row['primary_key']
|
536
541
|
}
|
537
542
|
|
538
|
-
options[:on_update] = extract_foreign_key_action(
|
539
|
-
options[:on_delete] = extract_foreign_key_action(
|
543
|
+
options[:on_update] = extract_foreign_key_action(row['on_update'])
|
544
|
+
options[:on_delete] = extract_foreign_key_action(row['on_delete'])
|
540
545
|
|
541
546
|
ForeignKeyDefinition.new(table_name, row['to_table'], options)
|
542
547
|
end
|
543
548
|
end
|
544
549
|
|
545
|
-
def table_options(table_name)
|
550
|
+
def table_options(table_name) # :nodoc:
|
551
|
+
table_options = {}
|
552
|
+
|
546
553
|
create_table_info = create_table_info(table_name)
|
547
554
|
|
548
555
|
# strip create_definitions and partition_options
|
@@ -551,10 +558,14 @@ module ActiveRecord
|
|
551
558
|
# strip AUTO_INCREMENT
|
552
559
|
raw_table_options.sub!(/(ENGINE=\w+)(?: AUTO_INCREMENT=\d+)/, '\1')
|
553
560
|
|
561
|
+
table_options[:options] = raw_table_options
|
562
|
+
|
554
563
|
# strip COMMENT
|
555
|
-
raw_table_options.sub!(/ COMMENT='.+'/, '')
|
564
|
+
if raw_table_options.sub!(/ COMMENT='.+'/, '')
|
565
|
+
table_options[:comment] = table_comment(table_name)
|
566
|
+
end
|
556
567
|
|
557
|
-
|
568
|
+
table_options
|
558
569
|
end
|
559
570
|
|
560
571
|
# Maps logical Rails types to MySQL-specific data types.
|
@@ -596,8 +607,8 @@ module ActiveRecord
|
|
596
607
|
SELECT column_name
|
597
608
|
FROM information_schema.key_column_usage
|
598
609
|
WHERE constraint_name = 'PRIMARY'
|
599
|
-
AND table_schema = #{
|
600
|
-
AND table_name = #{
|
610
|
+
AND table_schema = #{schema}
|
611
|
+
AND table_name = #{name}
|
601
612
|
ORDER BY ordinal_position
|
602
613
|
SQL
|
603
614
|
end
|
@@ -682,7 +693,7 @@ module ActiveRecord
|
|
682
693
|
|
683
694
|
def register_integer_type(mapping, key, options) # :nodoc:
|
684
695
|
mapping.register_type(key) do |sql_type|
|
685
|
-
if /\bunsigned\
|
696
|
+
if /\bunsigned\b/ === sql_type
|
686
697
|
Type::UnsignedInteger.new(options)
|
687
698
|
else
|
688
699
|
Type::Integer.new(options)
|
@@ -702,29 +713,23 @@ module ActiveRecord
|
|
702
713
|
MySQL::TypeMetadata.new(super(sql_type), extra: extra, strict: strict_mode?)
|
703
714
|
end
|
704
715
|
|
705
|
-
def add_index_length(
|
706
|
-
if
|
716
|
+
def add_index_length(quoted_columns, **options)
|
717
|
+
if length = options[:length]
|
707
718
|
case length
|
708
719
|
when Hash
|
709
|
-
|
720
|
+
length = length.symbolize_keys
|
721
|
+
quoted_columns.each { |name, column| column << "(#{length[name]})" if length[name].present? }
|
710
722
|
when Integer
|
711
|
-
|
723
|
+
quoted_columns.each { |name, column| column << "(#{length})" }
|
712
724
|
end
|
713
725
|
end
|
714
726
|
|
715
|
-
|
727
|
+
quoted_columns
|
716
728
|
end
|
717
729
|
|
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]}
|
730
|
+
def add_options_for_index_columns(quoted_columns, **options)
|
731
|
+
quoted_columns = add_index_length(quoted_columns, options)
|
732
|
+
super
|
728
733
|
end
|
729
734
|
|
730
735
|
def translate_exception(exception, message)
|
@@ -831,9 +836,9 @@ module ActiveRecord
|
|
831
836
|
variables['sql_auto_is_null'] = 0
|
832
837
|
|
833
838
|
# Increase timeout so the server doesn't disconnect us.
|
834
|
-
wait_timeout = @config[:wait_timeout]
|
839
|
+
wait_timeout = self.class.type_cast_config_to_integer(@config[:wait_timeout])
|
835
840
|
wait_timeout = 2147483 unless wait_timeout.is_a?(Integer)
|
836
|
-
variables[
|
841
|
+
variables["wait_timeout"] = wait_timeout
|
837
842
|
|
838
843
|
defaults = [':default', :default].to_set
|
839
844
|
|
@@ -883,21 +888,15 @@ module ActiveRecord
|
|
883
888
|
end
|
884
889
|
end
|
885
890
|
|
886
|
-
def extract_foreign_key_action(
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
when 'SET NULL'; :nullify
|
891
|
-
end
|
891
|
+
def extract_foreign_key_action(specifier) # :nodoc:
|
892
|
+
case specifier
|
893
|
+
when 'CASCADE'; :cascade
|
894
|
+
when 'SET NULL'; :nullify
|
892
895
|
end
|
893
896
|
end
|
894
897
|
|
895
|
-
def create_table_info_cache # :nodoc:
|
896
|
-
@create_table_info_cache ||= {}
|
897
|
-
end
|
898
|
-
|
899
898
|
def create_table_info(table_name) # :nodoc:
|
900
|
-
|
899
|
+
select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
|
901
900
|
end
|
902
901
|
|
903
902
|
def create_table_definition(*args) # :nodoc:
|
@@ -905,8 +904,8 @@ module ActiveRecord
|
|
905
904
|
end
|
906
905
|
|
907
906
|
def extract_schema_qualified_name(string) # :nodoc:
|
908
|
-
schema, name = string.to_s.scan(/[^`.\s]+|`[^`]*`/)
|
909
|
-
schema, name =
|
907
|
+
schema, name = string.to_s.scan(/[^`.\s]+|`[^`]*`/).map { |s| quote(s) }
|
908
|
+
schema, name = "DATABASE()", schema unless name
|
910
909
|
[schema, name]
|
911
910
|
end
|
912
911
|
|
@@ -941,6 +940,10 @@ module ActiveRecord
|
|
941
940
|
end
|
942
941
|
end
|
943
942
|
|
943
|
+
def version_string
|
944
|
+
full_version.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
|
945
|
+
end
|
946
|
+
|
944
947
|
class MysqlJson < Type::Internal::AbstractJson # :nodoc:
|
945
948
|
def changed_in_place?(raw_old_value, new_value)
|
946
949
|
# Normalization is required because MySQL JSON data format includes
|
@@ -13,7 +13,7 @@ module ActiveRecord
|
|
13
13
|
# +default+ is the type-casted default value, such as +new+ in <tt>sales_stage varchar(20) default 'new'</tt>.
|
14
14
|
# +sql_type_metadata+ is various information about the type of the column
|
15
15
|
# +null+ determines if this column allows +NULL+ values.
|
16
|
-
def initialize(name, default, sql_type_metadata = nil, null = true, table_name = nil, default_function = nil, collation = nil, comment: nil)
|
16
|
+
def initialize(name, default, sql_type_metadata = nil, null = true, table_name = nil, default_function = nil, collation = nil, comment: nil, **)
|
17
17
|
@name = name.freeze
|
18
18
|
@table_name = table_name
|
19
19
|
@sql_type_metadata = sql_type_metadata
|
@@ -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,15 +69,13 @@ 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
|
-
type_casted_binds = binds
|
76
|
+
type_casted_binds = type_casted_binds(binds)
|
94
77
|
|
95
|
-
log(sql, name, binds) do
|
78
|
+
log(sql, name, binds, type_casted_binds) do
|
96
79
|
if cache_stmt
|
97
80
|
cache = @statements[sql] ||= {
|
98
81
|
stmt: @connection.prepare(sql)
|
@@ -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('`', '``')}`"
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'active_record/connection_adapters/abstract_mysql_adapter'
|
2
2
|
require 'active_record/connection_adapters/mysql/database_statements'
|
3
3
|
|
4
|
-
gem 'mysql2', '>= 0.3.18', '< 0.
|
4
|
+
gem 'mysql2', '>= 0.3.18', '< 0.6.0'
|
5
5
|
require 'mysql2'
|
6
6
|
raise 'mysql2 0.4.3 is not supported. Please upgrade to 0.4.4+' if Mysql2::VERSION == '0.4.3'
|
7
7
|
|
@@ -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
|
@@ -5,11 +5,38 @@ module ActiveRecord
|
|
5
5
|
delegate :array, :oid, :fmod, to: :sql_type_metadata
|
6
6
|
alias :array? :array
|
7
7
|
|
8
|
+
def initialize(*, max_identifier_length: 63, **)
|
9
|
+
super
|
10
|
+
@max_identifier_length = max_identifier_length
|
11
|
+
end
|
12
|
+
|
8
13
|
def serial?
|
9
14
|
return unless default_function
|
10
15
|
|
11
|
-
%r{\Anextval\('"
|
16
|
+
if %r{\Anextval\('"?(?<sequence_name>.+_(?<suffix>seq\d*))"?'::regclass\)\z} =~ default_function
|
17
|
+
sequence_name_from_parts(table_name, name, suffix) == sequence_name
|
18
|
+
end
|
12
19
|
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
attr_reader :max_identifier_length
|
23
|
+
|
24
|
+
private
|
25
|
+
def sequence_name_from_parts(table_name, column_name, suffix)
|
26
|
+
over_length = [table_name, column_name, suffix].map(&:length).sum + 2 - max_identifier_length
|
27
|
+
|
28
|
+
if over_length > 0
|
29
|
+
column_name_length = [(max_identifier_length - suffix.length - 2) / 2, column_name.length].min
|
30
|
+
over_length -= column_name.length - column_name_length
|
31
|
+
column_name = column_name[0, column_name_length - [over_length, 0].min]
|
32
|
+
end
|
33
|
+
|
34
|
+
if over_length > 0
|
35
|
+
table_name = table_name[0, table_name.length - over_length]
|
36
|
+
end
|
37
|
+
|
38
|
+
"#{table_name}_#{column_name}_#{suffix}"
|
39
|
+
end
|
13
40
|
end
|
14
41
|
end
|
15
42
|
end
|
@@ -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
|
@@ -5,6 +5,8 @@ module ActiveRecord
|
|
5
5
|
class Array < Type::Value # :nodoc:
|
6
6
|
include Type::Helpers::Mutable
|
7
7
|
|
8
|
+
Data = Struct.new(:encoder, :values) # :nodoc:
|
9
|
+
|
8
10
|
attr_reader :subtype, :delimiter
|
9
11
|
delegate :type, :user_input_in_time_zone, :limit, to: :subtype
|
10
12
|
|
@@ -17,8 +19,11 @@ module ActiveRecord
|
|
17
19
|
end
|
18
20
|
|
19
21
|
def deserialize(value)
|
20
|
-
|
22
|
+
case value
|
23
|
+
when ::String
|
21
24
|
type_cast_array(@pg_decoder.decode(value), :deserialize)
|
25
|
+
when Data
|
26
|
+
type_cast_array(value.values, :deserialize)
|
22
27
|
else
|
23
28
|
super
|
24
29
|
end
|
@@ -33,7 +38,8 @@ module ActiveRecord
|
|
33
38
|
|
34
39
|
def serialize(value)
|
35
40
|
if value.is_a?(::Array)
|
36
|
-
|
41
|
+
casted_values = type_cast_array(value, :serialize)
|
42
|
+
Data.new(@pg_encoder, casted_values)
|
37
43
|
else
|
38
44
|
super
|
39
45
|
end
|
@@ -54,6 +60,10 @@ module ActiveRecord
|
|
54
60
|
value.map(&block)
|
55
61
|
end
|
56
62
|
|
63
|
+
def changed_in_place?(raw_old_value, new_value)
|
64
|
+
deserialize(raw_old_value) != new_value
|
65
|
+
end
|
66
|
+
|
57
67
|
private
|
58
68
|
|
59
69
|
def type_cast_array(value, method)
|
@@ -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
|
|
@@ -24,6 +24,8 @@ module ActiveRecord
|
|
24
24
|
def serialize(value)
|
25
25
|
if value.is_a?(::Hash)
|
26
26
|
value.map { |k, v| "#{escape_hstore(k)}=>#{escape_hstore(v)}" }.join(', ')
|
27
|
+
elsif value.respond_to?(:to_unsafe_h)
|
28
|
+
serialize(value.to_unsafe_h)
|
27
29
|
else
|
28
30
|
value
|
29
31
|
end
|
@@ -33,6 +35,14 @@ module ActiveRecord
|
|
33
35
|
ActiveRecord::Store::StringKeyedHashAccessor
|
34
36
|
end
|
35
37
|
|
38
|
+
# Will compare the Hash equivalents of +raw_old_value+ and +new_value+.
|
39
|
+
# By comparing hashes, this avoids an edge case where the order of
|
40
|
+
# the keys change between the two hashes, and they would not be marked
|
41
|
+
# as equal.
|
42
|
+
def changed_in_place?(raw_old_value, new_value)
|
43
|
+
deserialize(raw_old_value) != new_value
|
44
|
+
end
|
45
|
+
|
36
46
|
private
|
37
47
|
|
38
48
|
HstorePair = begin
|
@@ -33,7 +33,7 @@ module ActiveRecord
|
|
33
33
|
|
34
34
|
# Quotes schema names for use in SQL queries.
|
35
35
|
def quote_schema_name(name)
|
36
|
-
|
36
|
+
PG::Connection.quote_ident(name)
|
37
37
|
end
|
38
38
|
|
39
39
|
def quote_table_name_for_assignment(table, attr)
|
@@ -42,7 +42,7 @@ module ActiveRecord
|
|
42
42
|
|
43
43
|
# Quotes column names for use in SQL queries.
|
44
44
|
def quote_column_name(name) # :nodoc:
|
45
|
-
@quoted_column_names[name] ||=
|
45
|
+
@quoted_column_names[name] ||= PG::Connection.quote_ident(super)
|
46
46
|
end
|
47
47
|
|
48
48
|
# Quote date/time values for use in SQL input.
|
@@ -92,6 +92,8 @@ module ActiveRecord
|
|
92
92
|
else
|
93
93
|
super
|
94
94
|
end
|
95
|
+
when OID::Array::Data
|
96
|
+
_quote(encode_array(value))
|
95
97
|
else
|
96
98
|
super
|
97
99
|
end
|
@@ -101,15 +103,42 @@ module ActiveRecord
|
|
101
103
|
case value
|
102
104
|
when Type::Binary::Data
|
103
105
|
# Return a bind param hash with format as binary.
|
104
|
-
# See
|
106
|
+
# See https://deveiate.org/code/pg/PG/Connection.html#method-i-exec_prepared-doc
|
105
107
|
# for more information
|
106
108
|
{ value: value.to_s, format: 1 }
|
107
109
|
when OID::Xml::Data, OID::Bit::Data
|
108
110
|
value.to_s
|
111
|
+
when OID::Array::Data
|
112
|
+
encode_array(value)
|
109
113
|
else
|
110
114
|
super
|
111
115
|
end
|
112
116
|
end
|
117
|
+
|
118
|
+
def encode_array(array_data)
|
119
|
+
encoder = array_data.encoder
|
120
|
+
values = type_cast_array(array_data.values)
|
121
|
+
|
122
|
+
result = encoder.encode(values)
|
123
|
+
if encoding = determine_encoding_of_strings_in_array(values)
|
124
|
+
result.force_encoding(encoding)
|
125
|
+
end
|
126
|
+
result
|
127
|
+
end
|
128
|
+
|
129
|
+
def determine_encoding_of_strings_in_array(value)
|
130
|
+
case value
|
131
|
+
when ::Array then determine_encoding_of_strings_in_array(value.first)
|
132
|
+
when ::String then value.encoding
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def type_cast_array(values)
|
137
|
+
case values
|
138
|
+
when ::Array then values.map { |item| type_cast_array(item) }
|
139
|
+
else _type_cast(values)
|
140
|
+
end
|
141
|
+
end
|
113
142
|
end
|
114
143
|
end
|
115
144
|
end
|