activerecord 5.2.4.5 → 6.0.0.beta1

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 (241) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +299 -739
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +1 -1
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +2 -1
  7. data/lib/active_record/aggregations.rb +4 -2
  8. data/lib/active_record/associations.rb +16 -12
  9. data/lib/active_record/associations/association.rb +35 -19
  10. data/lib/active_record/associations/association_scope.rb +4 -6
  11. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  13. data/lib/active_record/associations/builder/belongs_to.rb +14 -50
  14. data/lib/active_record/associations/builder/collection_association.rb +3 -3
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
  16. data/lib/active_record/associations/collection_association.rb +11 -25
  17. data/lib/active_record/associations/collection_proxy.rb +32 -6
  18. data/lib/active_record/associations/foreign_association.rb +7 -0
  19. data/lib/active_record/associations/has_many_association.rb +1 -1
  20. data/lib/active_record/associations/has_many_through_association.rb +25 -18
  21. data/lib/active_record/associations/has_one_association.rb +28 -30
  22. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  23. data/lib/active_record/associations/join_dependency.rb +15 -20
  24. data/lib/active_record/associations/join_dependency/join_association.rb +11 -26
  25. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  26. data/lib/active_record/associations/preloader.rb +32 -29
  27. data/lib/active_record/associations/preloader/association.rb +1 -2
  28. data/lib/active_record/associations/singular_association.rb +2 -16
  29. data/lib/active_record/attribute_assignment.rb +7 -10
  30. data/lib/active_record/attribute_methods.rb +34 -56
  31. data/lib/active_record/attribute_methods/dirty.rb +64 -26
  32. data/lib/active_record/attribute_methods/primary_key.rb +8 -7
  33. data/lib/active_record/attribute_methods/read.rb +16 -48
  34. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  35. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  36. data/lib/active_record/attribute_methods/write.rb +15 -16
  37. data/lib/active_record/autosave_association.rb +7 -21
  38. data/lib/active_record/base.rb +2 -2
  39. data/lib/active_record/callbacks.rb +3 -17
  40. data/lib/active_record/collection_cache_key.rb +1 -1
  41. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +13 -36
  42. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  43. data/lib/active_record/connection_adapters/abstract/database_statements.rb +25 -84
  44. data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -14
  45. data/lib/active_record/connection_adapters/abstract/quoting.rb +5 -11
  46. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -11
  47. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -13
  48. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +0 -2
  49. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +41 -27
  50. data/lib/active_record/connection_adapters/abstract/transaction.rb +81 -52
  51. data/lib/active_record/connection_adapters/abstract_adapter.rb +95 -31
  52. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +65 -90
  53. data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
  54. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +5 -9
  55. data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -7
  56. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  57. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
  58. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +65 -10
  59. data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -4
  60. data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
  61. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +16 -1
  62. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  63. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  64. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
  65. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
  66. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  67. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
  68. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  69. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  70. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +11 -36
  71. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +9 -2
  72. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +38 -20
  73. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -1
  74. data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -56
  75. data/lib/active_record/connection_adapters/schema_cache.rb +5 -0
  76. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +5 -5
  77. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +14 -9
  78. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +95 -62
  79. data/lib/active_record/connection_handling.rb +132 -26
  80. data/lib/active_record/core.rb +76 -43
  81. data/lib/active_record/counter_cache.rb +4 -29
  82. data/lib/active_record/database_configurations.rb +184 -0
  83. data/lib/active_record/database_configurations/database_config.rb +37 -0
  84. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  85. data/lib/active_record/database_configurations/url_config.rb +74 -0
  86. data/lib/active_record/enum.rb +22 -7
  87. data/lib/active_record/errors.rb +24 -21
  88. data/lib/active_record/explain.rb +1 -1
  89. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  90. data/lib/active_record/fixture_set/render_context.rb +17 -0
  91. data/lib/active_record/fixture_set/table_row.rb +153 -0
  92. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  93. data/lib/active_record/fixtures.rb +140 -472
  94. data/lib/active_record/gem_version.rb +4 -4
  95. data/lib/active_record/inheritance.rb +12 -2
  96. data/lib/active_record/integration.rb +56 -16
  97. data/lib/active_record/internal_metadata.rb +5 -1
  98. data/lib/active_record/locking/optimistic.rb +2 -2
  99. data/lib/active_record/locking/pessimistic.rb +3 -3
  100. data/lib/active_record/log_subscriber.rb +7 -26
  101. data/lib/active_record/migration.rb +38 -37
  102. data/lib/active_record/migration/command_recorder.rb +35 -5
  103. data/lib/active_record/migration/compatibility.rb +34 -16
  104. data/lib/active_record/model_schema.rb +30 -9
  105. data/lib/active_record/nested_attributes.rb +2 -2
  106. data/lib/active_record/no_touching.rb +7 -0
  107. data/lib/active_record/persistence.rb +18 -7
  108. data/lib/active_record/query_cache.rb +11 -4
  109. data/lib/active_record/querying.rb +19 -11
  110. data/lib/active_record/railtie.rb +71 -42
  111. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  112. data/lib/active_record/railties/controller_runtime.rb +30 -35
  113. data/lib/active_record/railties/databases.rake +94 -43
  114. data/lib/active_record/reflection.rb +60 -44
  115. data/lib/active_record/relation.rb +150 -69
  116. data/lib/active_record/relation/batches.rb +13 -10
  117. data/lib/active_record/relation/calculations.rb +38 -28
  118. data/lib/active_record/relation/delegation.rb +4 -13
  119. data/lib/active_record/relation/finder_methods.rb +12 -25
  120. data/lib/active_record/relation/merger.rb +2 -6
  121. data/lib/active_record/relation/predicate_builder.rb +4 -6
  122. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  123. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  124. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  125. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  126. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  127. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  128. data/lib/active_record/relation/query_attribute.rb +15 -12
  129. data/lib/active_record/relation/query_methods.rb +29 -52
  130. data/lib/active_record/relation/where_clause.rb +4 -0
  131. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  132. data/lib/active_record/result.rb +30 -11
  133. data/lib/active_record/sanitization.rb +2 -39
  134. data/lib/active_record/schema.rb +1 -10
  135. data/lib/active_record/schema_dumper.rb +12 -6
  136. data/lib/active_record/schema_migration.rb +4 -0
  137. data/lib/active_record/scoping.rb +9 -8
  138. data/lib/active_record/scoping/default.rb +10 -3
  139. data/lib/active_record/scoping/named.rb +10 -14
  140. data/lib/active_record/statement_cache.rb +32 -5
  141. data/lib/active_record/store.rb +39 -8
  142. data/lib/active_record/table_metadata.rb +1 -4
  143. data/lib/active_record/tasks/database_tasks.rb +89 -23
  144. data/lib/active_record/tasks/mysql_database_tasks.rb +2 -4
  145. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
  146. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
  147. data/lib/active_record/test_databases.rb +38 -0
  148. data/lib/active_record/test_fixtures.rb +224 -0
  149. data/lib/active_record/timestamp.rb +4 -6
  150. data/lib/active_record/transactions.rb +3 -22
  151. data/lib/active_record/translation.rb +1 -1
  152. data/lib/active_record/type.rb +3 -4
  153. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  154. data/lib/active_record/type_caster/connection.rb +1 -6
  155. data/lib/active_record/type_caster/map.rb +1 -4
  156. data/lib/active_record/validations/uniqueness.rb +13 -25
  157. data/lib/arel.rb +44 -0
  158. data/lib/arel/alias_predication.rb +9 -0
  159. data/lib/arel/attributes.rb +22 -0
  160. data/lib/arel/attributes/attribute.rb +37 -0
  161. data/lib/arel/collectors/bind.rb +24 -0
  162. data/lib/arel/collectors/composite.rb +31 -0
  163. data/lib/arel/collectors/plain_string.rb +20 -0
  164. data/lib/arel/collectors/sql_string.rb +20 -0
  165. data/lib/arel/collectors/substitute_binds.rb +28 -0
  166. data/lib/arel/crud.rb +42 -0
  167. data/lib/arel/delete_manager.rb +18 -0
  168. data/lib/arel/errors.rb +9 -0
  169. data/lib/arel/expressions.rb +29 -0
  170. data/lib/arel/factory_methods.rb +49 -0
  171. data/lib/arel/insert_manager.rb +49 -0
  172. data/lib/arel/math.rb +45 -0
  173. data/lib/arel/nodes.rb +67 -0
  174. data/lib/arel/nodes/and.rb +32 -0
  175. data/lib/arel/nodes/ascending.rb +23 -0
  176. data/lib/arel/nodes/binary.rb +52 -0
  177. data/lib/arel/nodes/bind_param.rb +36 -0
  178. data/lib/arel/nodes/case.rb +55 -0
  179. data/lib/arel/nodes/casted.rb +50 -0
  180. data/lib/arel/nodes/count.rb +12 -0
  181. data/lib/arel/nodes/delete_statement.rb +45 -0
  182. data/lib/arel/nodes/descending.rb +23 -0
  183. data/lib/arel/nodes/equality.rb +18 -0
  184. data/lib/arel/nodes/extract.rb +24 -0
  185. data/lib/arel/nodes/false.rb +16 -0
  186. data/lib/arel/nodes/full_outer_join.rb +8 -0
  187. data/lib/arel/nodes/function.rb +44 -0
  188. data/lib/arel/nodes/grouping.rb +8 -0
  189. data/lib/arel/nodes/in.rb +8 -0
  190. data/lib/arel/nodes/infix_operation.rb +80 -0
  191. data/lib/arel/nodes/inner_join.rb +8 -0
  192. data/lib/arel/nodes/insert_statement.rb +37 -0
  193. data/lib/arel/nodes/join_source.rb +20 -0
  194. data/lib/arel/nodes/matches.rb +18 -0
  195. data/lib/arel/nodes/named_function.rb +23 -0
  196. data/lib/arel/nodes/node.rb +50 -0
  197. data/lib/arel/nodes/node_expression.rb +13 -0
  198. data/lib/arel/nodes/outer_join.rb +8 -0
  199. data/lib/arel/nodes/over.rb +15 -0
  200. data/lib/arel/nodes/regexp.rb +16 -0
  201. data/lib/arel/nodes/right_outer_join.rb +8 -0
  202. data/lib/arel/nodes/select_core.rb +63 -0
  203. data/lib/arel/nodes/select_statement.rb +41 -0
  204. data/lib/arel/nodes/sql_literal.rb +16 -0
  205. data/lib/arel/nodes/string_join.rb +11 -0
  206. data/lib/arel/nodes/table_alias.rb +27 -0
  207. data/lib/arel/nodes/terminal.rb +16 -0
  208. data/lib/arel/nodes/true.rb +16 -0
  209. data/lib/arel/nodes/unary.rb +44 -0
  210. data/lib/arel/nodes/unary_operation.rb +20 -0
  211. data/lib/arel/nodes/unqualified_column.rb +22 -0
  212. data/lib/arel/nodes/update_statement.rb +41 -0
  213. data/lib/arel/nodes/values.rb +16 -0
  214. data/lib/arel/nodes/values_list.rb +24 -0
  215. data/lib/arel/nodes/window.rb +126 -0
  216. data/lib/arel/nodes/with.rb +11 -0
  217. data/lib/arel/order_predications.rb +13 -0
  218. data/lib/arel/predications.rb +257 -0
  219. data/lib/arel/select_manager.rb +271 -0
  220. data/lib/arel/table.rb +110 -0
  221. data/lib/arel/tree_manager.rb +72 -0
  222. data/lib/arel/update_manager.rb +34 -0
  223. data/lib/arel/visitors.rb +20 -0
  224. data/lib/arel/visitors/depth_first.rb +199 -0
  225. data/lib/arel/visitors/dot.rb +292 -0
  226. data/lib/arel/visitors/ibm_db.rb +21 -0
  227. data/lib/arel/visitors/informix.rb +56 -0
  228. data/lib/arel/visitors/mssql.rb +143 -0
  229. data/lib/arel/visitors/mysql.rb +83 -0
  230. data/lib/arel/visitors/oracle.rb +159 -0
  231. data/lib/arel/visitors/oracle12.rb +67 -0
  232. data/lib/arel/visitors/postgresql.rb +116 -0
  233. data/lib/arel/visitors/sqlite.rb +39 -0
  234. data/lib/arel/visitors/to_sql.rb +913 -0
  235. data/lib/arel/visitors/visitor.rb +42 -0
  236. data/lib/arel/visitors/where_sql.rb +23 -0
  237. data/lib/arel/window_predications.rb +9 -0
  238. data/lib/rails/generators/active_record/migration.rb +14 -1
  239. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  240. data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
  241. metadata +104 -26
@@ -3,7 +3,7 @@
3
3
  module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module DetermineIfPreparableVisitor
6
- attr_accessor :preparable
6
+ attr_reader :preparable
7
7
 
8
8
  def accept(*)
9
9
  @preparable = true
@@ -12,15 +12,11 @@ module ActiveRecord
12
12
 
13
13
  def visit_Arel_Nodes_In(o, collector)
14
14
  @preparable = false
15
+ super
16
+ end
15
17
 
16
- if Array === o.right && !o.right.empty?
17
- o.right.delete_if do |bind|
18
- if Arel::Nodes::BindParam === bind && Relation::QueryAttribute === bind.value
19
- !bind.value.boundable?
20
- end
21
- end
22
- end
23
-
18
+ def visit_Arel_Nodes_NotIn(o, collector)
19
+ @preparable = false
24
20
  super
25
21
  end
26
22
 
@@ -19,8 +19,19 @@ module ActiveRecord
19
19
  execute(sql, name).to_a
20
20
  end
21
21
 
22
+ READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(:begin, :commit, :explain, :select, :set, :show, :release, :savepoint, :rollback) # :nodoc:
23
+ private_constant :READ_QUERY
24
+
25
+ def write_query?(sql) # :nodoc:
26
+ !READ_QUERY.match?(sql)
27
+ end
28
+
22
29
  # Executes the SQL statement in the context of this connection.
23
30
  def execute(sql, name = nil)
31
+ if preventing_writes? && write_query?(sql)
32
+ raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
33
+ end
34
+
24
35
  # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
25
36
  # made since we established the connection
26
37
  @connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
@@ -31,11 +42,19 @@ module ActiveRecord
31
42
  def exec_query(sql, name = "SQL", binds = [], prepare: false)
32
43
  if without_prepared_statement?(binds)
33
44
  execute_and_free(sql, name) do |result|
34
- ActiveRecord::Result.new(result.fields, result.to_a) if result
45
+ if result
46
+ ActiveRecord::Result.new(result.fields, result.to_a)
47
+ else
48
+ ActiveRecord::Result.new([], [])
49
+ end
35
50
  end
36
51
  else
37
52
  exec_stmt_and_free(sql, name, binds, cache_stmt: prepare) do |_, result|
38
- ActiveRecord::Result.new(result.fields, result.to_a) if result
53
+ if result
54
+ ActiveRecord::Result.new(result.fields, result.to_a)
55
+ else
56
+ ActiveRecord::Result.new([], [])
57
+ end
39
58
  end
40
59
  end
41
60
  end
@@ -59,7 +78,7 @@ module ActiveRecord
59
78
  end
60
79
 
61
80
  def discard_remaining_results
62
- @connection.next_result while @connection.more_results?
81
+ @connection.abandon_results!
63
82
  end
64
83
 
65
84
  def supports_set_server_option?
@@ -99,6 +118,12 @@ module ActiveRecord
99
118
  end
100
119
 
101
120
  def exec_stmt_and_free(sql, name, binds, cache_stmt: false)
121
+ if preventing_writes? && write_query?(sql)
122
+ raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
123
+ end
124
+
125
+ materialize_transactions
126
+
102
127
  # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
103
128
  # made since we established the connection
104
129
  @connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
@@ -107,10 +132,7 @@ module ActiveRecord
107
132
 
108
133
  log(sql, name, binds, type_casted_binds) do
109
134
  if cache_stmt
110
- cache = @statements[sql] ||= {
111
- stmt: @connection.prepare(sql)
112
- }
113
- stmt = cache[:stmt]
135
+ stmt = @statements[sql] ||= @connection.prepare(sql)
114
136
  else
115
137
  stmt = @connection.prepare(sql)
116
138
  end
@@ -5,7 +5,7 @@ module ActiveRecord
5
5
  module MySQL
6
6
  module Quoting # :nodoc:
7
7
  def quote_column_name(name)
8
- @quoted_column_names[name] ||= "`#{super.gsub('`', '``')}`".freeze
8
+ @quoted_column_names[name] ||= "`#{super.gsub('`', '``')}`"
9
9
  end
10
10
 
11
11
  def quote_table_name(name)
@@ -4,8 +4,7 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module MySQL
6
6
  class SchemaCreation < AbstractAdapter::SchemaCreation # :nodoc:
7
- delegate :add_sql_comment!, :mariadb?, to: :@conn
8
- private :add_sql_comment!, :mariadb?
7
+ delegate :add_sql_comment!, :mariadb?, to: :@conn, private: true
9
8
 
10
9
  private
11
10
 
@@ -18,7 +17,7 @@ module ActiveRecord
18
17
  end
19
18
 
20
19
  def visit_ChangeColumnDefinition(o)
21
- change_column_sql = "CHANGE #{quote_column_name(o.name)} #{accept(o.column)}".dup
20
+ change_column_sql = +"CHANGE #{quote_column_name(o.name)} #{accept(o.column)}"
22
21
  add_column_position!(change_column_sql, column_options(o.column))
23
22
  end
24
23
 
@@ -65,7 +64,7 @@ module ActiveRecord
65
64
 
66
65
  def index_in_create(table_name, column_name, options)
67
66
  index_name, index_type, index_columns, _, _, index_using, comment = @conn.add_index_options(table_name, column_name, options)
68
- add_sql_comment!("#{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})".dup, comment)
67
+ add_sql_comment!((+"#{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})"), comment)
69
68
  end
70
69
  end
71
70
  end
@@ -35,13 +35,39 @@ module ActiveRecord
35
35
  ]
36
36
  end
37
37
 
38
- indexes.last[-2] << row[:Column_name]
39
- indexes.last[-1][:lengths].merge!(row[:Column_name] => row[:Sub_part].to_i) if row[:Sub_part]
40
- indexes.last[-1][:orders].merge!(row[:Column_name] => :desc) if row[:Collation] == "D"
38
+ if row[:Expression]
39
+ expression = row[:Expression]
40
+ expression = +"(#{expression})" unless expression.start_with?("(")
41
+ indexes.last[-2] << expression
42
+ indexes.last[-1][:expressions] ||= {}
43
+ indexes.last[-1][:expressions][expression] = expression
44
+ indexes.last[-1][:orders][expression] = :desc if row[:Collation] == "D"
45
+ else
46
+ indexes.last[-2] << row[:Column_name]
47
+ indexes.last[-1][:lengths][row[:Column_name]] = row[:Sub_part].to_i if row[:Sub_part]
48
+ indexes.last[-1][:orders][row[:Column_name]] = :desc if row[:Collation] == "D"
49
+ end
41
50
  end
42
51
  end
43
52
 
44
- indexes.map { |index| IndexDefinition.new(*index) }
53
+ indexes.map do |index|
54
+ options = index.last
55
+
56
+ if expressions = options.delete(:expressions)
57
+ orders = options.delete(:orders)
58
+ lengths = options.delete(:lengths)
59
+
60
+ columns = index[-2].map { |name|
61
+ [ name.to_sym, expressions[name] || +quote_column_name(name) ]
62
+ }.to_h
63
+
64
+ index[-2] = add_options_for_index_columns(
65
+ columns, order: orders, length: lengths
66
+ ).values.join(", ")
67
+ end
68
+
69
+ IndexDefinition.new(*index)
70
+ end
45
71
  end
46
72
 
47
73
  def remove_column(table_name, column_name, type = nil, options = {})
@@ -51,9 +77,13 @@ module ActiveRecord
51
77
  super
52
78
  end
53
79
 
80
+ def create_table(table_name, options: default_row_format, **)
81
+ super
82
+ end
83
+
54
84
  def internal_string_options_for_primary_key
55
85
  super.tap do |options|
56
- if CHARSETS_OF_4BYTES_MAXLEN.include?(charset) && (mariadb? || version < "8.0.0")
86
+ if !row_format_dynamic_by_default? && CHARSETS_OF_4BYTES_MAXLEN.include?(charset)
57
87
  options[:collation] = collation.sub(/\A[^_]+/, "utf8")
58
88
  end
59
89
  end
@@ -70,6 +100,28 @@ module ActiveRecord
70
100
  private
71
101
  CHARSETS_OF_4BYTES_MAXLEN = ["utf8mb4", "utf16", "utf16le", "utf32"]
72
102
 
103
+ def row_format_dynamic_by_default?
104
+ if mariadb?
105
+ version >= "10.2.2"
106
+ else
107
+ version >= "5.7.9"
108
+ end
109
+ end
110
+
111
+ def default_row_format
112
+ return if row_format_dynamic_by_default?
113
+
114
+ unless defined?(@default_row_format)
115
+ if query_value("SELECT @@innodb_file_per_table = 1 AND @@innodb_file_format = 'Barracuda'") == 1
116
+ @default_row_format = "ROW_FORMAT=DYNAMIC"
117
+ else
118
+ @default_row_format = nil
119
+ end
120
+ end
121
+
122
+ @default_row_format
123
+ end
124
+
73
125
  def schema_creation
74
126
  MySQL::SchemaCreation.new(self)
75
127
  end
@@ -80,10 +132,13 @@ module ActiveRecord
80
132
 
81
133
  def new_column_from_field(table_name, field)
82
134
  type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
83
- if type_metadata.type == :datetime && /\ACURRENT_TIMESTAMP(?:\([0-6]?\))?\z/i.match?(field[:Default])
84
- default, default_function = nil, field[:Default]
85
- else
86
- default, default_function = field[:Default], nil
135
+ default, default_function = field[:Default], nil
136
+
137
+ if type_metadata.type == :datetime && /\ACURRENT_TIMESTAMP(?:\([0-6]?\))?\z/i.match?(default)
138
+ default, default_function = nil, default
139
+ elsif type_metadata.extra == "DEFAULT_GENERATED"
140
+ default = +"(#{default})" unless default.start_with?("(")
141
+ default, default_function = nil, default
87
142
  end
88
143
 
89
144
  MySQL::Column.new(
@@ -121,7 +176,7 @@ module ActiveRecord
121
176
  def data_source_sql(name = nil, type: nil)
122
177
  scope = quoted_scope(name, type: type)
123
178
 
124
- sql = "SELECT table_name FROM information_schema.tables".dup
179
+ sql = +"SELECT table_name FROM information_schema.tables"
125
180
  sql << " WHERE table_schema = #{scope[:schema]}"
126
181
  sql << " AND table_name = #{scope[:name]}" if scope[:name]
127
182
  sql << " AND table_type = #{scope[:type]}" if scope[:type]
@@ -3,7 +3,7 @@
3
3
  require "active_record/connection_adapters/abstract_mysql_adapter"
4
4
  require "active_record/connection_adapters/mysql/database_statements"
5
5
 
6
- gem "mysql2", ">= 0.4.4", "< 0.6.0"
6
+ gem "mysql2", ">= 0.4.4"
7
7
  require "mysql2"
8
8
 
9
9
  module ActiveRecord
@@ -14,7 +14,7 @@ module ActiveRecord
14
14
  config[:flags] ||= 0
15
15
 
16
16
  if config[:flags].kind_of? Array
17
- config[:flags].push "FOUND_ROWS".freeze
17
+ config[:flags].push "FOUND_ROWS"
18
18
  else
19
19
  config[:flags] |= Mysql2::Client::FOUND_ROWS
20
20
  end
@@ -32,7 +32,7 @@ module ActiveRecord
32
32
 
33
33
  module ConnectionAdapters
34
34
  class Mysql2Adapter < AbstractMysqlAdapter
35
- ADAPTER_NAME = "Mysql2".freeze
35
+ ADAPTER_NAME = "Mysql2"
36
36
 
37
37
  include MySQL::DatabaseStatements
38
38
 
@@ -58,6 +58,10 @@ module ActiveRecord
58
58
  true
59
59
  end
60
60
 
61
+ def supports_lazy_transactions?
62
+ true
63
+ end
64
+
61
65
  # HELPER METHODS ===========================================
62
66
 
63
67
  def each_hash(result) # :nodoc:
@@ -117,7 +121,7 @@ module ActiveRecord
117
121
  end
118
122
 
119
123
  def configure_connection
120
- @connection.query_options.merge!(as: :array)
124
+ @connection.query_options[:as] = :array
121
125
  super
122
126
  end
123
127
 
@@ -20,10 +20,9 @@ module ActiveRecord
20
20
  end
21
21
  end
22
22
 
23
- protected
23
+ private
24
24
  attr_reader :max_identifier_length
25
25
 
26
- private
27
26
  def sequence_name_from_parts(table_name, column_name, suffix)
28
27
  over_length = [table_name, column_name, suffix].map(&:length).sum + 2 - max_identifier_length
29
28
 
@@ -58,6 +58,8 @@ module ActiveRecord
58
58
 
59
59
  # Queries the database and returns the results in an Array-like object
60
60
  def query(sql, name = nil) #:nodoc:
61
+ materialize_transactions
62
+
61
63
  log(sql, name) do
62
64
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
63
65
  result_as_array @connection.async_exec(sql)
@@ -65,11 +67,24 @@ module ActiveRecord
65
67
  end
66
68
  end
67
69
 
70
+ READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(:begin, :commit, :explain, :select, :set, :show, :release, :savepoint, :rollback) # :nodoc:
71
+ private_constant :READ_QUERY
72
+
73
+ def write_query?(sql) # :nodoc:
74
+ !READ_QUERY.match?(sql)
75
+ end
76
+
68
77
  # Executes an SQL statement, returning a PG::Result object on success
69
78
  # or raising a PG::Error exception otherwise.
70
79
  # Note: the PG::Result object is manually memory managed; if you don't
71
80
  # need it specifically, you may want consider the <tt>exec_query</tt> wrapper.
72
81
  def execute(sql, name = nil)
82
+ if preventing_writes? && write_query?(sql)
83
+ raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
84
+ end
85
+
86
+ materialize_transactions
87
+
73
88
  log(sql, name) do
74
89
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
75
90
  @connection.async_exec(sql)
@@ -95,7 +110,7 @@ module ActiveRecord
95
110
  end
96
111
  alias :exec_update :exec_delete
97
112
 
98
- def sql_for_insert(sql, pk, id_value, sequence_name, binds) # :nodoc:
113
+ def sql_for_insert(sql, pk, sequence_name, binds) # :nodoc:
99
114
  if pk.nil?
100
115
  # Extract the table from the insert sql. Yuck.
101
116
  table_ref = extract_table_ref_from_insert_sql(sql)
@@ -5,7 +5,7 @@ module ActiveRecord
5
5
  module PostgreSQL
6
6
  module OID # :nodoc:
7
7
  class Array < Type::Value # :nodoc:
8
- include Type::Helpers::Mutable
8
+ include ActiveModel::Type::Helpers::Mutable
9
9
 
10
10
  Data = Struct.new(:encoder, :values) # :nodoc:
11
11
 
@@ -43,10 +43,7 @@ module ActiveRecord
43
43
  /\A[0-9A-F]*\Z/i.match?(value)
44
44
  end
45
45
 
46
- # TODO Change this to private once we've dropped Ruby 2.2 support.
47
- # Workaround for Ruby 2.2 "private attribute?" warning.
48
- protected
49
-
46
+ private
50
47
  attr_reader :value
51
48
  end
52
49
  end
@@ -5,7 +5,7 @@ module ActiveRecord
5
5
  module PostgreSQL
6
6
  module OID # :nodoc:
7
7
  class Hstore < Type::Value # :nodoc:
8
- include Type::Helpers::Mutable
8
+ include ActiveModel::Type::Helpers::Mutable
9
9
 
10
10
  def type
11
11
  :hstore
@@ -5,7 +5,7 @@ module ActiveRecord
5
5
  module PostgreSQL
6
6
  module OID # :nodoc:
7
7
  class LegacyPoint < Type::Value # :nodoc:
8
- include Type::Helpers::Mutable
8
+ include ActiveModel::Type::Helpers::Mutable
9
9
 
10
10
  def type
11
11
  :point
@@ -26,9 +26,9 @@ module ActiveRecord
26
26
 
27
27
  value = value.sub(/^\((.+)\)$/, '-\1') # (4)
28
28
  case value
29
- when /^-?\D*+[\d,]+\.\d{2}$/ # (1)
29
+ when /^-?\D+[\d,]+\.\d{2}$/ # (1)
30
30
  value.gsub!(/[^-\d.]/, "")
31
- when /^-?\D*+[\d.]+,\d{2}$/ # (2)
31
+ when /^-?\D+[\d.]+,\d{2}$/ # (2)
32
32
  value.gsub!(/[^-\d,]/, "").sub!(/,/, ".")
33
33
  end
34
34
 
@@ -7,7 +7,7 @@ module ActiveRecord
7
7
  module PostgreSQL
8
8
  module OID # :nodoc:
9
9
  class Point < Type::Value # :nodoc:
10
- include Type::Helpers::Mutable
10
+ include ActiveModel::Type::Helpers::Mutable
11
11
 
12
12
  def type
13
13
  :point
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/array/extract"
4
+
3
5
  module ActiveRecord
4
6
  module ConnectionAdapters
5
7
  module PostgreSQL
@@ -16,12 +18,12 @@ module ActiveRecord
16
18
 
17
19
  def run(records)
18
20
  nodes = records.reject { |row| @store.key? row["oid"].to_i }
19
- mapped, nodes = nodes.partition { |row| @store.key? row["typname"] }
20
- ranges, nodes = nodes.partition { |row| row["typtype"] == "r".freeze }
21
- enums, nodes = nodes.partition { |row| row["typtype"] == "e".freeze }
22
- domains, nodes = nodes.partition { |row| row["typtype"] == "d".freeze }
23
- arrays, nodes = nodes.partition { |row| row["typinput"] == "array_in".freeze }
24
- composites, nodes = nodes.partition { |row| row["typelem"].to_i != 0 }
21
+ mapped = nodes.extract! { |row| @store.key? row["typname"] }
22
+ ranges = nodes.extract! { |row| row["typtype"] == "r" }
23
+ enums = nodes.extract! { |row| row["typtype"] == "e" }
24
+ domains = nodes.extract! { |row| row["typtype"] == "d" }
25
+ arrays = nodes.extract! { |row| row["typinput"] == "array_in" }
26
+ composites = nodes.extract! { |row| row["typelem"].to_i != 0 }
25
27
 
26
28
  mapped.each { |row| register_mapped_type(row) }
27
29
  enums.each { |row| register_enum_type(row) }
@@ -34,7 +36,7 @@ module ActiveRecord
34
36
  def query_conditions_for_initial_load
35
37
  known_type_names = @store.keys.map { |n| "'#{n}'" }
36
38
  known_type_types = %w('r' 'e' 'd')
37
- <<-SQL % [known_type_names.join(", "), known_type_types.join(", ")]
39
+ <<~SQL % [known_type_names.join(", "), known_type_types.join(", ")]
38
40
  WHERE
39
41
  t.typname IN (%s)
40
42
  OR t.typtype IN (%s)