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.

Files changed (101) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +431 -2
  3. data/README.rdoc +1 -1
  4. data/lib/active_record/aggregations.rb +4 -2
  5. data/lib/active_record/association_relation.rb +4 -1
  6. data/lib/active_record/associations/association.rb +11 -1
  7. data/lib/active_record/associations/association_scope.rb +1 -1
  8. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +4 -2
  9. data/lib/active_record/associations/builder/singular_association.rb +10 -1
  10. data/lib/active_record/associations/collection_association.rb +56 -48
  11. data/lib/active_record/associations/collection_proxy.rb +38 -20
  12. data/lib/active_record/associations/has_many_association.rb +1 -7
  13. data/lib/active_record/associations/has_many_through_association.rb +2 -4
  14. data/lib/active_record/associations/join_dependency/join_association.rb +6 -11
  15. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  16. data/lib/active_record/associations/join_dependency.rb +10 -4
  17. data/lib/active_record/associations/preloader/association.rb +24 -37
  18. data/lib/active_record/associations/preloader/collection_association.rb +0 -1
  19. data/lib/active_record/associations/preloader/singular_association.rb +0 -1
  20. data/lib/active_record/associations/preloader/through_association.rb +10 -4
  21. data/lib/active_record/associations/singular_association.rb +8 -2
  22. data/lib/active_record/associations/through_association.rb +1 -1
  23. data/lib/active_record/associations.rb +38 -17
  24. data/lib/active_record/attribute.rb +3 -3
  25. data/lib/active_record/attribute_methods/primary_key.rb +14 -1
  26. data/lib/active_record/attribute_methods/read.rb +1 -1
  27. data/lib/active_record/attribute_methods/time_zone_conversion.rb +2 -2
  28. data/lib/active_record/attribute_methods.rb +3 -7
  29. data/lib/active_record/attribute_set/builder.rb +37 -13
  30. data/lib/active_record/attribute_set.rb +2 -0
  31. data/lib/active_record/attributes.rb +3 -3
  32. data/lib/active_record/autosave_association.rb +15 -11
  33. data/lib/active_record/base.rb +1 -1
  34. data/lib/active_record/collection_cache_key.rb +16 -6
  35. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +41 -33
  36. data/lib/active_record/connection_adapters/abstract/database_statements.rb +1 -1
  37. data/lib/active_record/connection_adapters/abstract/query_cache.rb +37 -2
  38. data/lib/active_record/connection_adapters/abstract/quoting.rb +14 -5
  39. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +11 -14
  40. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +68 -56
  41. data/lib/active_record/connection_adapters/abstract_adapter.rb +36 -12
  42. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +63 -60
  43. data/lib/active_record/connection_adapters/column.rb +1 -1
  44. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  45. data/lib/active_record/connection_adapters/mysql/database_statements.rb +8 -25
  46. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  47. data/lib/active_record/connection_adapters/mysql2_adapter.rb +2 -6
  48. data/lib/active_record/connection_adapters/postgresql/column.rb +28 -1
  49. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +8 -0
  50. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +12 -2
  51. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  52. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
  53. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +10 -0
  54. data/lib/active_record/connection_adapters/postgresql/quoting.rb +32 -3
  55. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +18 -8
  56. data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -2
  57. data/lib/active_record/connection_adapters/postgresql_adapter.rb +25 -19
  58. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
  59. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +4 -4
  60. data/lib/active_record/core.rb +6 -4
  61. data/lib/active_record/enum.rb +8 -5
  62. data/lib/active_record/explain.rb +20 -9
  63. data/lib/active_record/fixtures.rb +5 -5
  64. data/lib/active_record/gem_version.rb +2 -2
  65. data/lib/active_record/integration.rb +13 -10
  66. data/lib/active_record/log_subscriber.rb +19 -17
  67. data/lib/active_record/migration.rb +35 -14
  68. data/lib/active_record/model_schema.rb +161 -52
  69. data/lib/active_record/no_touching.rb +4 -0
  70. data/lib/active_record/persistence.rb +41 -20
  71. data/lib/active_record/query_cache.rb +15 -17
  72. data/lib/active_record/querying.rb +3 -3
  73. data/lib/active_record/railties/controller_runtime.rb +1 -1
  74. data/lib/active_record/railties/databases.rake +9 -21
  75. data/lib/active_record/reflection.rb +20 -0
  76. data/lib/active_record/relation/batches.rb +10 -10
  77. data/lib/active_record/relation/calculations.rb +16 -12
  78. data/lib/active_record/relation/delegation.rb +2 -1
  79. data/lib/active_record/relation/finder_methods.rb +13 -11
  80. data/lib/active_record/relation/query_methods.rb +3 -3
  81. data/lib/active_record/relation.rb +12 -5
  82. data/lib/active_record/result.rb +7 -1
  83. data/lib/active_record/sanitization.rb +11 -1
  84. data/lib/active_record/schema_dumper.rb +10 -17
  85. data/lib/active_record/scoping/default.rb +5 -1
  86. data/lib/active_record/scoping/named.rb +18 -6
  87. data/lib/active_record/scoping.rb +4 -3
  88. data/lib/active_record/serialization.rb +1 -1
  89. data/lib/active_record/statement_cache.rb +2 -2
  90. data/lib/active_record/table_metadata.rb +4 -3
  91. data/lib/active_record/tasks/database_tasks.rb +14 -11
  92. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  93. data/lib/active_record/touch_later.rb +6 -1
  94. data/lib/active_record/transactions.rb +1 -1
  95. data/lib/active_record/type/internal/abstract_json.rb +5 -1
  96. data/lib/active_record/validations/uniqueness.rb +3 -4
  97. data/lib/active_record.rb +3 -2
  98. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
  99. data/lib/rails/generators/active_record/migration.rb +8 -0
  100. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  101. metadata +7 -8
@@ -4,6 +4,9 @@ module ActiveRecord
4
4
  class << self
5
5
  def included(base) #:nodoc:
6
6
  dirties_query_cache base, :insert, :update, :delete, :rollback_to_savepoint, :rollback_db_transaction
7
+
8
+ base.set_callback :checkout, :after, :configure_query_cache!
9
+ base.set_callback :checkin, :after, :disable_query_cache!
7
10
  end
8
11
 
9
12
  def dirties_query_cache(base, *method_names)
@@ -18,6 +21,27 @@ module ActiveRecord
18
21
  end
19
22
  end
20
23
 
24
+ module ConnectionPoolConfiguration
25
+ def initialize(*)
26
+ super
27
+ @query_cache_enabled = Concurrent::Map.new { false }
28
+ end
29
+
30
+ def enable_query_cache!
31
+ @query_cache_enabled[connection_cache_key(Thread.current)] = true
32
+ connection.enable_query_cache! if active_connection?
33
+ end
34
+
35
+ def disable_query_cache!
36
+ @query_cache_enabled.delete connection_cache_key(Thread.current)
37
+ connection.disable_query_cache! if active_connection?
38
+ end
39
+
40
+ def query_cache_enabled
41
+ @query_cache_enabled[connection_cache_key(Thread.current)]
42
+ end
43
+ end
44
+
21
45
  attr_reader :query_cache, :query_cache_enabled
22
46
 
23
47
  def initialize(*)
@@ -41,6 +65,7 @@ module ActiveRecord
41
65
 
42
66
  def disable_query_cache!
43
67
  @query_cache_enabled = false
68
+ clear_query_cache
44
69
  end
45
70
 
46
71
  # Disable the query cache within the block.
@@ -76,8 +101,14 @@ module ActiveRecord
76
101
  def cache_sql(sql, binds)
77
102
  result =
78
103
  if @query_cache[sql].key?(binds)
79
- ActiveSupport::Notifications.instrument("sql.active_record",
80
- :sql => sql, :binds => binds, :name => "CACHE", :connection_id => object_id)
104
+ ActiveSupport::Notifications.instrument(
105
+ "sql.active_record",
106
+ sql: sql,
107
+ binds: binds,
108
+ type_casted_binds: -> { type_casted_binds(binds) },
109
+ name: "CACHE",
110
+ connection_id: object_id,
111
+ )
81
112
  @query_cache[sql][binds]
82
113
  else
83
114
  @query_cache[sql][binds] = yield
@@ -90,6 +121,10 @@ module ActiveRecord
90
121
  def locked?(arel)
91
122
  arel.respond_to?(:locked) && arel.locked
92
123
  end
124
+
125
+ def configure_query_cache!
126
+ enable_query_cache! if pool.query_cache_enabled
127
+ end
93
128
  end
94
129
  end
95
130
  end
@@ -1,4 +1,5 @@
1
1
  require 'active_support/core_ext/big_decimal/conversions'
2
+ require "active_support/multibyte/chars"
2
3
 
3
4
  module ActiveRecord
4
5
  module ConnectionAdapters # :nodoc:
@@ -112,19 +113,19 @@ module ActiveRecord
112
113
  end
113
114
 
114
115
  def quoted_true
115
- "'t'"
116
+ "'t'".freeze
116
117
  end
117
118
 
118
119
  def unquoted_true
119
- 't'
120
+ 't'.freeze
120
121
  end
121
122
 
122
123
  def quoted_false
123
- "'f'"
124
+ "'f'".freeze
124
125
  end
125
126
 
126
127
  def unquoted_false
127
- 'f'
128
+ 'f'.freeze
128
129
  end
129
130
 
130
131
  # Quote date/time values for use in SQL input. Includes microseconds
@@ -147,13 +148,21 @@ module ActiveRecord
147
148
  end
148
149
 
149
150
  def quoted_time(value) # :nodoc:
150
- quoted_date(value).sub(/\A2000-01-01 /, '')
151
+ quoted_date(value).sub(/\A\d\d\d\d-\d\d-\d\d /, '')
151
152
  end
152
153
 
153
154
  def prepare_binds_for_database(binds) # :nodoc:
154
155
  binds.map(&:value_for_database)
155
156
  end
156
157
 
158
+ def type_casted_binds(binds) # :nodoc:
159
+ if binds.first.is_a?(Array)
160
+ binds.map { |column, value| type_cast(value, column) }
161
+ else
162
+ binds.map { |attr| type_cast(attr.value_for_database) }
163
+ end
164
+ end
165
+
157
166
  private
158
167
 
159
168
  def types_which_need_no_typecasting
@@ -51,11 +51,12 @@ module ActiveRecord
51
51
  options[:primary_key] != default_primary_key
52
52
  end
53
53
 
54
- def defined_for?(options_or_to_table = {})
55
- if options_or_to_table.is_a?(Hash)
56
- options_or_to_table.all? {|key, value| options[key].to_s == value.to_s }
54
+ def defined_for?(to_table_ord = nil, to_table: nil, **options)
55
+ if to_table_ord
56
+ self.to_table == to_table_ord.to_s
57
57
  else
58
- to_table == options_or_to_table.to_s
58
+ (to_table.nil? || to_table.to_s == self.to_table) &&
59
+ options.all? { |k, v| self.options[k].to_s == v.to_s }
59
60
  end
60
61
  end
61
62
 
@@ -106,16 +107,12 @@ module ActiveRecord
106
107
 
107
108
  private
108
109
 
109
- def as_options(value, default = {})
110
- if value.is_a?(Hash)
111
- value
112
- else
113
- default
114
- end
110
+ def as_options(value)
111
+ value.is_a?(Hash) ? value : {}
115
112
  end
116
113
 
117
114
  def polymorphic_options
118
- as_options(polymorphic, options)
115
+ as_options(polymorphic).merge(null: options[:null])
119
116
  end
120
117
 
121
118
  def index_options
@@ -211,7 +208,7 @@ module ActiveRecord
211
208
 
212
209
  def initialize(name, temporary = false, options = nil, as = nil, comment: nil)
213
210
  @columns_hash = {}
214
- @indexes = {}
211
+ @indexes = []
215
212
  @foreign_keys = []
216
213
  @primary_keys = nil
217
214
  @temporary = temporary
@@ -303,7 +300,7 @@ module ActiveRecord
303
300
  # end
304
301
  def column(name, type, options = {})
305
302
  name = name.to_s
306
- type = type.to_sym
303
+ type = type.to_sym if type
307
304
  options = options.dup
308
305
 
309
306
  if @columns_hash[name] && @columns_hash[name].primary_key?
@@ -327,7 +324,7 @@ module ActiveRecord
327
324
  #
328
325
  # index(:account_id, name: 'index_projects_on_account_id')
329
326
  def index(column_name, options = {})
330
- indexes[column_name] = options
327
+ indexes << [column_name, options]
331
328
  end
332
329
 
333
330
  def foreign_key(table_name, options = {}) # :nodoc:
@@ -129,14 +129,9 @@ module ActiveRecord
129
129
 
130
130
  # Returns just a table's primary key
131
131
  def primary_key(table_name)
132
- pks = primary_keys(table_name)
133
- warn <<-WARNING.strip_heredoc if pks.count > 1
134
- WARNING: Rails does not support composite primary key.
135
-
136
- #{table_name} has composite primary key. Composite primary key is ignored.
137
- WARNING
138
-
139
- pks.first if pks.one?
132
+ pk = primary_keys(table_name)
133
+ pk = pk.first unless pk.size > 1
134
+ pk
140
135
  end
141
136
 
142
137
  # Creates a new table with the name +table_name+. +table_name+ may either
@@ -283,16 +278,16 @@ module ActiveRecord
283
278
  result = execute schema_creation.accept td
284
279
 
285
280
  unless supports_indexes_in_create?
286
- td.indexes.each_pair do |column_name, index_options|
281
+ td.indexes.each do |column_name, index_options|
287
282
  add_index(table_name, column_name, index_options)
288
283
  end
289
284
  end
290
285
 
291
286
  if supports_comments? && !supports_comments_in_create?
292
- change_table_comment(table_name, comment) if comment
287
+ change_table_comment(table_name, comment) if comment.present?
293
288
 
294
289
  td.columns.each do |column|
295
- change_column_comment(table_name, column.name, column.comment) if column.comment
290
+ change_column_comment(table_name, column.name, column.comment) if column.comment.present?
296
291
  end
297
292
  end
298
293
 
@@ -767,7 +762,7 @@ module ActiveRecord
767
762
  raise ArgumentError, "You must specify the index name"
768
763
  end
769
764
  else
770
- index_name(table_name, :column => options)
765
+ index_name(table_name, index_name_options(options))
771
766
  end
772
767
  end
773
768
 
@@ -790,7 +785,7 @@ module ActiveRecord
790
785
  # [<tt>:type</tt>]
791
786
  # The reference column type. Defaults to +:integer+.
792
787
  # [<tt>:index</tt>]
793
- # Add an appropriate index. Defaults to false.
788
+ # Add an appropriate index. Defaults to true.
794
789
  # See #add_index for usage of this option.
795
790
  # [<tt>:foreign_key</tt>]
796
791
  # Add an appropriate foreign key constraint. Defaults to false.
@@ -847,14 +842,19 @@ module ActiveRecord
847
842
  #
848
843
  # remove_reference(:products, :user, index: true, foreign_key: true)
849
844
  #
850
- def remove_reference(table_name, ref_name, options = {})
851
- if options[:foreign_key]
845
+ def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)
846
+ if foreign_key
852
847
  reference_name = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
853
- remove_foreign_key(table_name, reference_name)
848
+ if foreign_key.is_a?(Hash)
849
+ foreign_key_options = foreign_key
850
+ else
851
+ foreign_key_options = { to_table: reference_name }
852
+ end
853
+ remove_foreign_key(table_name, **foreign_key_options)
854
854
  end
855
855
 
856
856
  remove_column(table_name, "#{ref_name}_id")
857
- remove_column(table_name, "#{ref_name}_type") if options[:polymorphic]
857
+ remove_column(table_name, "#{ref_name}_type") if polymorphic
858
858
  end
859
859
  alias :remove_belongs_to :remove_reference
860
860
 
@@ -986,21 +986,19 @@ module ActiveRecord
986
986
 
987
987
  def dump_schema_information #:nodoc:
988
988
  versions = ActiveRecord::SchemaMigration.order('version').pluck(:version)
989
- insert_versions_sql(versions)
989
+ insert_versions_sql(versions) if versions.any?
990
990
  end
991
991
 
992
992
  def insert_versions_sql(versions) # :nodoc:
993
- sm_table = ActiveRecord::Migrator.schema_migrations_table_name
993
+ sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
994
994
 
995
- if supports_multi_insert?
996
- sql = "INSERT INTO #{sm_table} (version) VALUES "
997
- sql << versions.map {|v| "('#{v}')" }.join(', ')
995
+ if versions.is_a?(Array)
996
+ sql = "INSERT INTO #{sm_table} (version) VALUES\n"
997
+ sql << versions.map { |v| "(#{quote(v)})" }.join(",\n")
998
998
  sql << ";\n\n"
999
999
  sql
1000
1000
  else
1001
- versions.map { |version|
1002
- "INSERT INTO #{sm_table} (version) VALUES ('#{version}');"
1003
- }.join "\n\n"
1001
+ "INSERT INTO #{sm_table} (version) VALUES (#{quote(versions)});"
1004
1002
  end
1005
1003
  end
1006
1004
 
@@ -1024,13 +1022,12 @@ module ActiveRecord
1024
1022
  sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
1025
1023
 
1026
1024
  migrated = select_values("SELECT version FROM #{sm_table}").map(&:to_i)
1027
- paths = migrations_paths.map {|p| "#{p}/[0-9]*_*.rb" }
1028
- versions = Dir[*paths].map do |filename|
1029
- filename.split('/').last.split('_').first.to_i
1025
+ versions = ActiveRecord::Migrator.migration_files(migrations_paths).map do |file|
1026
+ ActiveRecord::Migrator.parse_migration_filename(file).first.to_i
1030
1027
  end
1031
1028
 
1032
1029
  unless migrated.include?(version)
1033
- execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')"
1030
+ execute "INSERT INTO #{sm_table} (version) VALUES (#{quote(version)})"
1034
1031
  end
1035
1032
 
1036
1033
  inserting = (versions - migrated).select {|v| v < version}
@@ -1038,12 +1035,19 @@ module ActiveRecord
1038
1035
  if (duplicate = inserting.detect {|v| inserting.count(v) > 1})
1039
1036
  raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict."
1040
1037
  end
1041
- execute insert_versions_sql(inserting)
1038
+ if supports_multi_insert?
1039
+ execute insert_versions_sql(inserting)
1040
+ else
1041
+ inserting.each do |v|
1042
+ execute insert_versions_sql(v)
1043
+ end
1044
+ end
1042
1045
  end
1043
1046
  end
1044
1047
 
1045
1048
  def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
1046
- if native = native_database_types[type.to_sym]
1049
+ type = type.to_sym if type
1050
+ if native = native_database_types[type]
1047
1051
  column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
1048
1052
 
1049
1053
  if type == :decimal # ignore limit, use precision and scale
@@ -1111,18 +1115,14 @@ module ActiveRecord
1111
1115
  end
1112
1116
 
1113
1117
  def add_index_options(table_name, column_name, comment: nil, **options) # :nodoc:
1114
- if column_name.is_a?(String) && /\W/ === column_name
1115
- column_names = column_name
1116
- else
1117
- column_names = Array(column_name)
1118
- end
1118
+ column_names = index_column_names(column_name)
1119
1119
 
1120
1120
  options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type)
1121
1121
 
1122
1122
  index_type = options[:type].to_s if options.key?(:type)
1123
1123
  index_type ||= options[:unique] ? "UNIQUE" : ""
1124
1124
  index_name = options[:name].to_s if options.key?(:name)
1125
- index_name ||= index_name(table_name, index_name_options(column_names))
1125
+ index_name ||= index_name(table_name, column_names)
1126
1126
  max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
1127
1127
 
1128
1128
  if options.key?(:algorithm)
@@ -1163,31 +1163,35 @@ module ActiveRecord
1163
1163
  end
1164
1164
 
1165
1165
  protected
1166
- def add_index_sort_order(option_strings, column_names, options = {})
1167
- if options.is_a?(Hash) && order = options[:order]
1166
+
1167
+ def add_index_sort_order(quoted_columns, **options)
1168
+ if order = options[:order]
1168
1169
  case order
1169
1170
  when Hash
1170
- column_names.each {|name| option_strings[name] += " #{order[name].upcase}" if order.has_key?(name)}
1171
+ order = order.symbolize_keys
1172
+ quoted_columns.each { |name, column| column << " #{order[name].upcase}" if order[name].present? }
1171
1173
  when String
1172
- column_names.each {|name| option_strings[name] += " #{order.upcase}"}
1174
+ quoted_columns.each { |name, column| column << " #{order.upcase}" if order.present? }
1173
1175
  end
1174
1176
  end
1175
1177
 
1176
- return option_strings
1178
+ quoted_columns
1177
1179
  end
1178
1180
 
1179
1181
  # Overridden by the MySQL adapter for supporting index lengths
1180
- def quoted_columns_for_index(column_names, options = {})
1181
- return [column_names] if column_names.is_a?(String)
1182
-
1183
- option_strings = Hash[column_names.map {|name| [name, '']}]
1184
-
1185
- # add index sort order if supported
1182
+ def add_options_for_index_columns(quoted_columns, **options)
1186
1183
  if supports_index_sort_order?
1187
- option_strings = add_index_sort_order(option_strings, column_names, options)
1184
+ quoted_columns = add_index_sort_order(quoted_columns, options)
1188
1185
  end
1189
1186
 
1190
- column_names.map {|name| quote_column_name(name) + option_strings[name]}
1187
+ quoted_columns
1188
+ end
1189
+
1190
+ def quoted_columns_for_index(column_names, **options)
1191
+ return [column_names] if column_names.is_a?(String)
1192
+
1193
+ quoted_columns = Hash[column_names.map { |name| [name.to_sym, quote_column_name(name).dup] }]
1194
+ add_options_for_index_columns(quoted_columns, options).values
1191
1195
  end
1192
1196
 
1193
1197
  def index_name_for_remove(table_name, options = {})
@@ -1201,16 +1205,16 @@ module ActiveRecord
1201
1205
 
1202
1206
  if options.is_a?(Hash)
1203
1207
  checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
1204
- column_names = Array(options[:column]).map(&:to_s)
1208
+ column_names = index_column_names(options[:column])
1205
1209
  else
1206
- column_names = Array(options).map(&:to_s)
1210
+ column_names = index_column_names(options)
1207
1211
  end
1208
1212
 
1209
- if column_names.any?
1210
- checks << lambda { |i| i.columns.join('_and_') == column_names.join('_and_') }
1213
+ if column_names.present?
1214
+ checks << lambda { |i| index_name(table_name, i.columns) == index_name(table_name, column_names) }
1211
1215
  end
1212
1216
 
1213
- raise ArgumentError "No name or columns specified" if checks.none?
1217
+ raise ArgumentError, "No name or columns specified" if checks.none?
1214
1218
 
1215
1219
  matching_indexes = indexes(table_name).select { |i| checks.all? { |check| check[i] } }
1216
1220
 
@@ -1255,8 +1259,16 @@ module ActiveRecord
1255
1259
  AlterTable.new create_table_definition(name)
1256
1260
  end
1257
1261
 
1262
+ def index_column_names(column_names)
1263
+ if column_names.is_a?(String) && /\W/ === column_names
1264
+ column_names
1265
+ else
1266
+ Array(column_names)
1267
+ end
1268
+ end
1269
+
1258
1270
  def index_name_options(column_names) # :nodoc:
1259
- if column_names.is_a?(String)
1271
+ if column_names.is_a?(String) && /\W/ === column_names
1260
1272
  column_names = column_names.scan(/\w+/).join('_')
1261
1273
  end
1262
1274
 
@@ -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 = 'Abstract'.freeze
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
- @owner = nil
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
@@ -402,7 +425,7 @@ module ActiveRecord
402
425
 
403
426
  # Provides access to the underlying database driver for this adapter. For
404
427
  # example, this method returns a Mysql2::Client object in case of Mysql2Adapter,
405
- # and a PGconn object in case of PostgreSQLAdapter.
428
+ # and a PG::Connection object in case of PostgreSQLAdapter.
406
429
  #
407
430
  # This is useful for when you need to call a proprietary method such as
408
431
  # PostgreSQL's lo_* methods.
@@ -556,14 +579,15 @@ module ActiveRecord
556
579
  exception
557
580
  end
558
581
 
559
- def log(sql, name = "SQL", binds = [], statement_name = nil)
582
+ def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil)
560
583
  @instrumenter.instrument(
561
584
  "sql.active_record",
562
- :sql => sql,
563
- :name => name,
564
- :connection_id => object_id,
565
- :statement_name => statement_name,
566
- :binds => binds) { yield }
585
+ sql: sql,
586
+ name: name,
587
+ binds: binds,
588
+ type_casted_binds: type_casted_binds,
589
+ statement_name: statement_name,
590
+ connection_id: object_id) { yield }
567
591
  rescue => e
568
592
  raise translate_exception_class(e, sql)
569
593
  end