activerecord 5.2.5 → 6.0.4.6

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 (294) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +913 -549
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +5 -3
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/advisory_lock_base.rb +18 -0
  7. data/lib/active_record/aggregations.rb +4 -3
  8. data/lib/active_record/association_relation.rb +10 -8
  9. data/lib/active_record/associations/alias_tracker.rb +0 -1
  10. data/lib/active_record/associations/association.rb +55 -19
  11. data/lib/active_record/associations/association_scope.rb +11 -7
  12. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  14. data/lib/active_record/associations/builder/association.rb +14 -18
  15. data/lib/active_record/associations/builder/belongs_to.rb +19 -52
  16. data/lib/active_record/associations/builder/collection_association.rb +3 -13
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -40
  18. data/lib/active_record/associations/builder/has_many.rb +2 -0
  19. data/lib/active_record/associations/builder/has_one.rb +35 -1
  20. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  21. data/lib/active_record/associations/collection_association.rb +19 -23
  22. data/lib/active_record/associations/collection_proxy.rb +14 -17
  23. data/lib/active_record/associations/foreign_association.rb +7 -0
  24. data/lib/active_record/associations/has_many_association.rb +2 -11
  25. data/lib/active_record/associations/has_many_through_association.rb +14 -14
  26. data/lib/active_record/associations/has_one_association.rb +28 -30
  27. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  28. data/lib/active_record/associations/join_dependency/join_association.rb +16 -10
  29. data/lib/active_record/associations/join_dependency/join_part.rb +4 -4
  30. data/lib/active_record/associations/join_dependency.rb +47 -30
  31. data/lib/active_record/associations/preloader/association.rb +61 -41
  32. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  33. data/lib/active_record/associations/preloader.rb +44 -33
  34. data/lib/active_record/associations/singular_association.rb +2 -16
  35. data/lib/active_record/associations/through_association.rb +1 -1
  36. data/lib/active_record/associations.rb +21 -16
  37. data/lib/active_record/attribute_assignment.rb +7 -11
  38. data/lib/active_record/attribute_decorators.rb +0 -2
  39. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -2
  40. data/lib/active_record/attribute_methods/dirty.rb +111 -40
  41. data/lib/active_record/attribute_methods/primary_key.rb +15 -24
  42. data/lib/active_record/attribute_methods/query.rb +2 -3
  43. data/lib/active_record/attribute_methods/read.rb +15 -54
  44. data/lib/active_record/attribute_methods/serialization.rb +1 -2
  45. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -3
  46. data/lib/active_record/attribute_methods/write.rb +17 -25
  47. data/lib/active_record/attribute_methods.rb +28 -100
  48. data/lib/active_record/attributes.rb +13 -1
  49. data/lib/active_record/autosave_association.rb +12 -14
  50. data/lib/active_record/base.rb +2 -3
  51. data/lib/active_record/callbacks.rb +6 -21
  52. data/lib/active_record/coders/yaml_column.rb +0 -1
  53. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +109 -18
  54. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
  55. data/lib/active_record/connection_adapters/abstract/database_statements.rb +102 -124
  56. data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -9
  57. data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
  58. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +20 -14
  59. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +105 -72
  60. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
  61. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +175 -79
  62. data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -57
  63. data/lib/active_record/connection_adapters/abstract_adapter.rb +197 -43
  64. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +149 -217
  65. data/lib/active_record/connection_adapters/column.rb +17 -13
  66. data/lib/active_record/connection_adapters/connection_specification.rb +54 -45
  67. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
  68. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  69. data/lib/active_record/connection_adapters/mysql/database_statements.rb +70 -14
  70. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +0 -1
  71. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  72. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +4 -6
  73. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  74. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  75. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +139 -19
  76. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  77. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -10
  78. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
  79. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +26 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  81. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  82. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +8 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
  89. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  91. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
  92. data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
  93. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  94. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +14 -3
  95. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  96. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  97. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +63 -75
  98. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
  99. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  100. data/lib/active_record/connection_adapters/postgresql_adapter.rb +168 -75
  101. data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
  102. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  103. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +119 -0
  104. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
  105. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -12
  106. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +137 -147
  107. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  108. data/lib/active_record/connection_handling.rb +139 -26
  109. data/lib/active_record/core.rb +107 -66
  110. data/lib/active_record/counter_cache.rb +8 -30
  111. data/lib/active_record/database_configurations/database_config.rb +37 -0
  112. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  113. data/lib/active_record/database_configurations/url_config.rb +78 -0
  114. data/lib/active_record/database_configurations.rb +233 -0
  115. data/lib/active_record/dynamic_matchers.rb +3 -4
  116. data/lib/active_record/enum.rb +44 -7
  117. data/lib/active_record/errors.rb +15 -7
  118. data/lib/active_record/explain.rb +1 -2
  119. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  120. data/lib/active_record/fixture_set/render_context.rb +17 -0
  121. data/lib/active_record/fixture_set/table_row.rb +152 -0
  122. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  123. data/lib/active_record/fixtures.rb +144 -474
  124. data/lib/active_record/gem_version.rb +4 -4
  125. data/lib/active_record/inheritance.rb +13 -6
  126. data/lib/active_record/insert_all.rb +179 -0
  127. data/lib/active_record/integration.rb +68 -16
  128. data/lib/active_record/internal_metadata.rb +11 -3
  129. data/lib/active_record/locking/optimistic.rb +14 -7
  130. data/lib/active_record/locking/pessimistic.rb +3 -3
  131. data/lib/active_record/log_subscriber.rb +8 -27
  132. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  133. data/lib/active_record/middleware/database_selector/resolver.rb +87 -0
  134. data/lib/active_record/middleware/database_selector.rb +74 -0
  135. data/lib/active_record/migration/command_recorder.rb +54 -22
  136. data/lib/active_record/migration/compatibility.rb +79 -52
  137. data/lib/active_record/migration/join_table.rb +0 -1
  138. data/lib/active_record/migration.rb +104 -85
  139. data/lib/active_record/model_schema.rb +62 -11
  140. data/lib/active_record/nested_attributes.rb +2 -4
  141. data/lib/active_record/no_touching.rb +9 -2
  142. data/lib/active_record/null_relation.rb +0 -1
  143. data/lib/active_record/persistence.rb +232 -29
  144. data/lib/active_record/query_cache.rb +11 -4
  145. data/lib/active_record/querying.rb +33 -21
  146. data/lib/active_record/railtie.rb +80 -43
  147. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  148. data/lib/active_record/railties/controller_runtime.rb +30 -35
  149. data/lib/active_record/railties/databases.rake +199 -46
  150. data/lib/active_record/reflection.rb +51 -51
  151. data/lib/active_record/relation/batches.rb +13 -11
  152. data/lib/active_record/relation/calculations.rb +55 -49
  153. data/lib/active_record/relation/delegation.rb +35 -50
  154. data/lib/active_record/relation/finder_methods.rb +23 -28
  155. data/lib/active_record/relation/from_clause.rb +4 -0
  156. data/lib/active_record/relation/merger.rb +12 -17
  157. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  158. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  159. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  160. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  161. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  162. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  163. data/lib/active_record/relation/predicate_builder.rb +5 -11
  164. data/lib/active_record/relation/query_attribute.rb +13 -8
  165. data/lib/active_record/relation/query_methods.rb +232 -69
  166. data/lib/active_record/relation/spawn_methods.rb +1 -2
  167. data/lib/active_record/relation/where_clause.rb +14 -11
  168. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  169. data/lib/active_record/relation.rb +326 -81
  170. data/lib/active_record/result.rb +30 -12
  171. data/lib/active_record/sanitization.rb +32 -40
  172. data/lib/active_record/schema.rb +2 -11
  173. data/lib/active_record/schema_dumper.rb +22 -7
  174. data/lib/active_record/schema_migration.rb +6 -2
  175. data/lib/active_record/scoping/default.rb +4 -6
  176. data/lib/active_record/scoping/named.rb +25 -16
  177. data/lib/active_record/scoping.rb +8 -9
  178. data/lib/active_record/statement_cache.rb +30 -3
  179. data/lib/active_record/store.rb +87 -8
  180. data/lib/active_record/suppressor.rb +2 -2
  181. data/lib/active_record/table_metadata.rb +23 -15
  182. data/lib/active_record/tasks/database_tasks.rb +194 -25
  183. data/lib/active_record/tasks/mysql_database_tasks.rb +5 -6
  184. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -8
  185. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -9
  186. data/lib/active_record/test_databases.rb +23 -0
  187. data/lib/active_record/test_fixtures.rb +243 -0
  188. data/lib/active_record/timestamp.rb +39 -26
  189. data/lib/active_record/touch_later.rb +5 -4
  190. data/lib/active_record/transactions.rb +64 -73
  191. data/lib/active_record/translation.rb +1 -1
  192. data/lib/active_record/type/adapter_specific_registry.rb +3 -13
  193. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  194. data/lib/active_record/type/serialized.rb +0 -1
  195. data/lib/active_record/type/time.rb +10 -0
  196. data/lib/active_record/type/type_map.rb +0 -1
  197. data/lib/active_record/type/unsigned_integer.rb +0 -1
  198. data/lib/active_record/type.rb +3 -5
  199. data/lib/active_record/type_caster/connection.rb +15 -14
  200. data/lib/active_record/type_caster/map.rb +1 -4
  201. data/lib/active_record/validations/associated.rb +0 -1
  202. data/lib/active_record/validations/uniqueness.rb +15 -27
  203. data/lib/active_record/validations.rb +3 -3
  204. data/lib/active_record.rb +10 -2
  205. data/lib/arel/alias_predication.rb +9 -0
  206. data/lib/arel/attributes/attribute.rb +37 -0
  207. data/lib/arel/attributes.rb +22 -0
  208. data/lib/arel/collectors/bind.rb +24 -0
  209. data/lib/arel/collectors/composite.rb +31 -0
  210. data/lib/arel/collectors/plain_string.rb +20 -0
  211. data/lib/arel/collectors/sql_string.rb +20 -0
  212. data/lib/arel/collectors/substitute_binds.rb +28 -0
  213. data/lib/arel/crud.rb +42 -0
  214. data/lib/arel/delete_manager.rb +18 -0
  215. data/lib/arel/errors.rb +9 -0
  216. data/lib/arel/expressions.rb +29 -0
  217. data/lib/arel/factory_methods.rb +49 -0
  218. data/lib/arel/insert_manager.rb +49 -0
  219. data/lib/arel/math.rb +45 -0
  220. data/lib/arel/nodes/and.rb +32 -0
  221. data/lib/arel/nodes/ascending.rb +23 -0
  222. data/lib/arel/nodes/binary.rb +52 -0
  223. data/lib/arel/nodes/bind_param.rb +36 -0
  224. data/lib/arel/nodes/case.rb +55 -0
  225. data/lib/arel/nodes/casted.rb +50 -0
  226. data/lib/arel/nodes/comment.rb +29 -0
  227. data/lib/arel/nodes/count.rb +12 -0
  228. data/lib/arel/nodes/delete_statement.rb +45 -0
  229. data/lib/arel/nodes/descending.rb +23 -0
  230. data/lib/arel/nodes/equality.rb +18 -0
  231. data/lib/arel/nodes/extract.rb +24 -0
  232. data/lib/arel/nodes/false.rb +16 -0
  233. data/lib/arel/nodes/full_outer_join.rb +8 -0
  234. data/lib/arel/nodes/function.rb +44 -0
  235. data/lib/arel/nodes/grouping.rb +8 -0
  236. data/lib/arel/nodes/in.rb +8 -0
  237. data/lib/arel/nodes/infix_operation.rb +80 -0
  238. data/lib/arel/nodes/inner_join.rb +8 -0
  239. data/lib/arel/nodes/insert_statement.rb +37 -0
  240. data/lib/arel/nodes/join_source.rb +20 -0
  241. data/lib/arel/nodes/matches.rb +18 -0
  242. data/lib/arel/nodes/named_function.rb +23 -0
  243. data/lib/arel/nodes/node.rb +50 -0
  244. data/lib/arel/nodes/node_expression.rb +13 -0
  245. data/lib/arel/nodes/outer_join.rb +8 -0
  246. data/lib/arel/nodes/over.rb +15 -0
  247. data/lib/arel/nodes/regexp.rb +16 -0
  248. data/lib/arel/nodes/right_outer_join.rb +8 -0
  249. data/lib/arel/nodes/select_core.rb +67 -0
  250. data/lib/arel/nodes/select_statement.rb +41 -0
  251. data/lib/arel/nodes/sql_literal.rb +16 -0
  252. data/lib/arel/nodes/string_join.rb +11 -0
  253. data/lib/arel/nodes/table_alias.rb +27 -0
  254. data/lib/arel/nodes/terminal.rb +16 -0
  255. data/lib/arel/nodes/true.rb +16 -0
  256. data/lib/arel/nodes/unary.rb +45 -0
  257. data/lib/arel/nodes/unary_operation.rb +20 -0
  258. data/lib/arel/nodes/unqualified_column.rb +22 -0
  259. data/lib/arel/nodes/update_statement.rb +41 -0
  260. data/lib/arel/nodes/values_list.rb +9 -0
  261. data/lib/arel/nodes/window.rb +126 -0
  262. data/lib/arel/nodes/with.rb +11 -0
  263. data/lib/arel/nodes.rb +68 -0
  264. data/lib/arel/order_predications.rb +13 -0
  265. data/lib/arel/predications.rb +256 -0
  266. data/lib/arel/select_manager.rb +271 -0
  267. data/lib/arel/table.rb +110 -0
  268. data/lib/arel/tree_manager.rb +72 -0
  269. data/lib/arel/update_manager.rb +34 -0
  270. data/lib/arel/visitors/depth_first.rb +203 -0
  271. data/lib/arel/visitors/dot.rb +296 -0
  272. data/lib/arel/visitors/ibm_db.rb +34 -0
  273. data/lib/arel/visitors/informix.rb +62 -0
  274. data/lib/arel/visitors/mssql.rb +156 -0
  275. data/lib/arel/visitors/mysql.rb +83 -0
  276. data/lib/arel/visitors/oracle.rb +158 -0
  277. data/lib/arel/visitors/oracle12.rb +65 -0
  278. data/lib/arel/visitors/postgresql.rb +109 -0
  279. data/lib/arel/visitors/sqlite.rb +38 -0
  280. data/lib/arel/visitors/to_sql.rb +888 -0
  281. data/lib/arel/visitors/visitor.rb +45 -0
  282. data/lib/arel/visitors/where_sql.rb +22 -0
  283. data/lib/arel/visitors.rb +20 -0
  284. data/lib/arel/window_predications.rb +9 -0
  285. data/lib/arel.rb +62 -0
  286. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  287. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  288. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  289. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  290. data/lib/rails/generators/active_record/migration.rb +14 -2
  291. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  292. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  293. metadata +115 -29
  294. data/lib/active_record/collection_cache_key.rb +0 -53
@@ -10,6 +10,10 @@ module ActiveRecord
10
10
  spec[:unsigned] = "true" if column.unsigned?
11
11
  spec[:auto_increment] = "true" if column.auto_increment?
12
12
 
13
+ if /\A(?<size>tiny|medium|long)(?:text|blob)/ =~ column.sql_type
14
+ spec = { size: size.to_sym.inspect }.merge!(spec)
15
+ end
16
+
13
17
  if @connection.supports_virtual_columns? && column.virtual?
14
18
  spec[:as] = extract_expression_for_virtual_column(column)
15
19
  spec[:stored] = "true" if /\b(?:STORED|PERSISTENT)\b/.match?(column.extra)
@@ -37,19 +41,23 @@ module ActiveRecord
37
41
  case column.sql_type
38
42
  when /\Atimestamp\b/
39
43
  :timestamp
40
- when "tinyblob"
41
- :blob
44
+ when /\A(?:enum|set)\b/
45
+ column.sql_type
42
46
  else
43
47
  super
44
48
  end
45
49
  end
46
50
 
51
+ def schema_limit(column)
52
+ super unless /\A(?:enum|set|(?:tiny|medium|long)?(?:text|blob))\b/.match?(column.sql_type)
53
+ end
54
+
47
55
  def schema_precision(column)
48
56
  super unless /\A(?:date)?time(?:stamp)?\b/.match?(column.sql_type) && column.precision == 0
49
57
  end
50
58
 
51
59
  def schema_collation(column)
52
- if column.collation && table_name = column.table_name
60
+ if column.collation
53
61
  @table_collation_cache ||= {}
54
62
  @table_collation_cache[table_name] ||=
55
63
  @connection.exec_query("SHOW TABLE STATUS LIKE #{@connection.quote(table_name)}", "SCHEMA").first["Collation"]
@@ -58,14 +66,14 @@ module ActiveRecord
58
66
  end
59
67
 
60
68
  def extract_expression_for_virtual_column(column)
61
- if @connection.mariadb? && @connection.version < "10.2.5"
62
- create_table_info = @connection.send(:create_table_info, column.table_name)
69
+ if @connection.mariadb? && @connection.database_version < "10.2.5"
70
+ create_table_info = @connection.send(:create_table_info, table_name)
63
71
  column_name = @connection.quote_column_name(column.name)
64
72
  if %r/#{column_name} #{Regexp.quote(column.sql_type)}(?: COLLATE \w+)? AS \((?<expression>.+?)\) #{column.extra}/ =~ create_table_info
65
73
  $~[:expression].inspect
66
74
  end
67
75
  else
68
- scope = @connection.send(:quoted_scope, column.table_name)
76
+ scope = @connection.send(:quoted_scope, table_name)
69
77
  column_name = @connection.quote(column.name)
70
78
  sql = "SELECT generation_expression FROM information_schema.columns" \
71
79
  " WHERE table_schema = #{scope[:schema]}" \
@@ -35,25 +35,55 @@ 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.pop
55
+
56
+ if expressions = options.delete(:expressions)
57
+ orders = options.delete(:orders)
58
+ lengths = options.delete(:lengths)
59
+
60
+ columns = index[-1].map { |name|
61
+ [ name.to_sym, expressions[name] || +quote_column_name(name) ]
62
+ }.to_h
63
+
64
+ index[-1] = add_options_for_index_columns(
65
+ columns, order: orders, length: lengths
66
+ ).values.join(", ")
67
+ end
68
+
69
+ IndexDefinition.new(*index, **options)
70
+ end
45
71
  end
46
72
 
47
- def remove_column(table_name, column_name, type = nil, options = {})
73
+ def remove_column(table_name, column_name, type = nil, **options)
48
74
  if foreign_key_exists?(table_name, column: column_name)
49
75
  remove_foreign_key(table_name, column: column_name)
50
76
  end
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
@@ -67,23 +97,76 @@ module ActiveRecord
67
97
  MySQL::SchemaDumper.create(self, options)
68
98
  end
69
99
 
100
+ # Maps logical Rails types to MySQL-specific data types.
101
+ def type_to_sql(type, limit: nil, precision: nil, scale: nil, size: limit_to_size(limit, type), unsigned: nil, **)
102
+ sql =
103
+ case type.to_s
104
+ when "integer"
105
+ integer_to_sql(limit)
106
+ when "text"
107
+ type_with_size_to_sql("text", size)
108
+ when "blob"
109
+ type_with_size_to_sql("blob", size)
110
+ when "binary"
111
+ if (0..0xfff) === limit
112
+ "varbinary(#{limit})"
113
+ else
114
+ type_with_size_to_sql("blob", size)
115
+ end
116
+ else
117
+ super
118
+ end
119
+
120
+ sql = "#{sql} unsigned" if unsigned && type != :primary_key
121
+ sql
122
+ end
123
+
124
+ def table_alias_length
125
+ 256 # https://dev.mysql.com/doc/refman/8.0/en/identifiers.html
126
+ end
127
+
70
128
  private
71
129
  CHARSETS_OF_4BYTES_MAXLEN = ["utf8mb4", "utf16", "utf16le", "utf32"]
72
130
 
131
+ def row_format_dynamic_by_default?
132
+ if mariadb?
133
+ database_version >= "10.2.2"
134
+ else
135
+ database_version >= "5.7.9"
136
+ end
137
+ end
138
+
139
+ def default_row_format
140
+ return if row_format_dynamic_by_default?
141
+
142
+ unless defined?(@default_row_format)
143
+ if query_value("SELECT @@innodb_file_per_table = 1 AND @@innodb_file_format = 'Barracuda'") == 1
144
+ @default_row_format = "ROW_FORMAT=DYNAMIC"
145
+ else
146
+ @default_row_format = nil
147
+ end
148
+ end
149
+
150
+ @default_row_format
151
+ end
152
+
73
153
  def schema_creation
74
154
  MySQL::SchemaCreation.new(self)
75
155
  end
76
156
 
77
- def create_table_definition(*args)
78
- MySQL::TableDefinition.new(*args)
157
+ def create_table_definition(*args, **options)
158
+ MySQL::TableDefinition.new(self, *args, **options)
79
159
  end
80
160
 
81
161
  def new_column_from_field(table_name, field)
82
162
  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
163
+ default, default_function = field[:Default], nil
164
+
165
+ if type_metadata.type == :datetime && /\ACURRENT_TIMESTAMP(?:\([0-6]?\))?\z/i.match?(default)
166
+ default, default_function = nil, default
167
+ elsif type_metadata.extra == "DEFAULT_GENERATED"
168
+ default = +"(#{default})" unless default.start_with?("(")
169
+ default, default_function = nil, default
87
170
  end
88
171
 
89
172
  MySQL::Column.new(
@@ -91,9 +174,8 @@ module ActiveRecord
91
174
  default,
92
175
  type_metadata,
93
176
  field[:Null] == "YES",
94
- table_name,
95
177
  default_function,
96
- field[:Collation],
178
+ collation: field[:Collation],
97
179
  comment: field[:Comment].presence
98
180
  )
99
181
  end
@@ -114,17 +196,21 @@ module ActiveRecord
114
196
  end
115
197
 
116
198
  def add_options_for_index_columns(quoted_columns, **options)
117
- quoted_columns = add_index_length(quoted_columns, options)
199
+ quoted_columns = add_index_length(quoted_columns, **options)
118
200
  super
119
201
  end
120
202
 
121
203
  def data_source_sql(name = nil, type: nil)
122
204
  scope = quoted_scope(name, type: type)
123
205
 
124
- sql = "SELECT table_name FROM information_schema.tables".dup
125
- sql << " WHERE table_schema = #{scope[:schema]}"
126
- sql << " AND table_name = #{scope[:name]}" if scope[:name]
127
- sql << " AND table_type = #{scope[:type]}" if scope[:type]
206
+ sql = +"SELECT table_name FROM (SELECT * FROM information_schema.tables "
207
+ sql << " WHERE table_schema = #{scope[:schema]}) _subquery"
208
+ if scope[:type] || scope[:name]
209
+ conditions = []
210
+ conditions << "_subquery.table_type = #{scope[:type]}" if scope[:type]
211
+ conditions << "_subquery.table_name = #{scope[:name]}" if scope[:name]
212
+ sql << " WHERE #{conditions.join(" AND ")}"
213
+ end
128
214
  sql
129
215
  end
130
216
 
@@ -142,6 +228,40 @@ module ActiveRecord
142
228
  schema, name = nil, schema unless name
143
229
  [schema, name]
144
230
  end
231
+
232
+ def type_with_size_to_sql(type, size)
233
+ case size&.to_s
234
+ when nil, "tiny", "medium", "long"
235
+ "#{size}#{type}"
236
+ else
237
+ raise ArgumentError,
238
+ "#{size.inspect} is invalid :size value. Only :tiny, :medium, and :long are allowed."
239
+ end
240
+ end
241
+
242
+ def limit_to_size(limit, type)
243
+ case type.to_s
244
+ when "text", "blob", "binary"
245
+ case limit
246
+ when 0..0xff; "tiny"
247
+ when nil, 0x100..0xffff; nil
248
+ when 0x10000..0xffffff; "medium"
249
+ when 0x1000000..0xffffffff; "long"
250
+ else raise ArgumentError, "No #{type} type has byte size #{limit}"
251
+ end
252
+ end
253
+ end
254
+
255
+ def integer_to_sql(limit)
256
+ case limit
257
+ when 1; "tinyint"
258
+ when 2; "smallint"
259
+ when 3; "mediumint"
260
+ when nil, 4; "int"
261
+ when 5..8; "bigint"
262
+ else raise ArgumentError, "No integer type has byte size #{limit}. Use a decimal with scale 0 instead."
263
+ end
264
+ end
145
265
  end
146
266
  end
147
267
  end
@@ -10,25 +10,21 @@ module ActiveRecord
10
10
 
11
11
  def initialize(type_metadata, extra: "")
12
12
  super(type_metadata)
13
- @type_metadata = type_metadata
14
13
  @extra = extra
15
14
  end
16
15
 
17
16
  def ==(other)
18
- other.is_a?(MySQL::TypeMetadata) &&
19
- attributes_for_hash == other.attributes_for_hash
17
+ other.is_a?(TypeMetadata) &&
18
+ __getobj__ == other.__getobj__ &&
19
+ extra == other.extra
20
20
  end
21
21
  alias eql? ==
22
22
 
23
23
  def hash
24
- attributes_for_hash.hash
24
+ TypeMetadata.hash ^
25
+ __getobj__.hash ^
26
+ extra.hash
25
27
  end
26
-
27
- protected
28
-
29
- def attributes_for_hash
30
- [self.class, @type_metadata, extra]
31
- end
32
28
  end
33
29
  end
34
30
  end
@@ -3,18 +3,20 @@
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
10
10
  module ConnectionHandling # :nodoc:
11
+ ER_BAD_DB_ERROR = 1049
12
+
11
13
  # Establishes a connection to the database that's used by all Active Record objects.
12
14
  def mysql2_connection(config)
13
15
  config = config.symbolize_keys
14
16
  config[:flags] ||= 0
15
17
 
16
18
  if config[:flags].kind_of? Array
17
- config[:flags].push "FOUND_ROWS".freeze
19
+ config[:flags].push "FOUND_ROWS"
18
20
  else
19
21
  config[:flags] |= Mysql2::Client::FOUND_ROWS
20
22
  end
@@ -22,7 +24,7 @@ module ActiveRecord
22
24
  client = Mysql2::Client.new(config)
23
25
  ConnectionAdapters::Mysql2Adapter.new(client, logger, nil, config)
24
26
  rescue Mysql2::Error => error
25
- if error.message.include?("Unknown database")
27
+ if error.error_number == ER_BAD_DB_ERROR
26
28
  raise ActiveRecord::NoDatabaseError
27
29
  else
28
30
  raise
@@ -32,18 +34,24 @@ module ActiveRecord
32
34
 
33
35
  module ConnectionAdapters
34
36
  class Mysql2Adapter < AbstractMysqlAdapter
35
- ADAPTER_NAME = "Mysql2".freeze
37
+ ADAPTER_NAME = "Mysql2"
36
38
 
37
39
  include MySQL::DatabaseStatements
38
40
 
39
41
  def initialize(connection, logger, connection_options, config)
40
- super
41
- @prepared_statements = false unless config.key?(:prepared_statements)
42
+ superclass_config = config.reverse_merge(prepared_statements: false)
43
+ super(connection, logger, connection_options, superclass_config)
42
44
  configure_connection
43
45
  end
44
46
 
47
+ def self.database_exists?(config)
48
+ !!ActiveRecord::Base.mysql2_connection(config)
49
+ rescue ActiveRecord::NoDatabaseError
50
+ false
51
+ end
52
+
45
53
  def supports_json?
46
- !mariadb? && version >= "5.7.8"
54
+ !mariadb? && database_version >= "5.7.8"
47
55
  end
48
56
 
49
57
  def supports_comments?
@@ -58,6 +66,10 @@ module ActiveRecord
58
66
  true
59
67
  end
60
68
 
69
+ def supports_lazy_transactions?
70
+ true
71
+ end
72
+
61
73
  # HELPER METHODS ===========================================
62
74
 
63
75
  def each_hash(result) # :nodoc:
@@ -105,24 +117,28 @@ module ActiveRecord
105
117
  end
106
118
 
107
119
  def discard! # :nodoc:
120
+ super
108
121
  @connection.automatic_close = false
109
122
  @connection = nil
110
123
  end
111
124
 
112
125
  private
113
-
114
126
  def connect
115
127
  @connection = Mysql2::Client.new(@config)
116
128
  configure_connection
117
129
  end
118
130
 
119
131
  def configure_connection
120
- @connection.query_options.merge!(as: :array)
132
+ @connection.query_options[:as] = :array
121
133
  super
122
134
  end
123
135
 
124
136
  def full_version
125
- @full_version ||= @connection.server_info[:version]
137
+ schema_cache.database_version.full_version_string
138
+ end
139
+
140
+ def get_full_version
141
+ @connection.server_info[:version]
126
142
  end
127
143
  end
128
144
  end
@@ -2,43 +2,29 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module ConnectionAdapters
5
- # PostgreSQL-specific extensions to column definitions in a table.
6
- class PostgreSQLColumn < Column #:nodoc:
7
- delegate :array, :oid, :fmod, to: :sql_type_metadata
8
- alias :array? :array
5
+ module PostgreSQL
6
+ class Column < ConnectionAdapters::Column # :nodoc:
7
+ delegate :oid, :fmod, to: :sql_type_metadata
9
8
 
10
- def initialize(*, max_identifier_length: 63, **)
11
- super
12
- @max_identifier_length = max_identifier_length
13
- end
14
-
15
- def serial?
16
- return unless default_function
17
-
18
- if %r{\Anextval\('"?(?<sequence_name>.+_(?<suffix>seq\d*))"?'::regclass\)\z} =~ default_function
19
- sequence_name_from_parts(table_name, name, suffix) == sequence_name
9
+ def initialize(*, serial: nil, **)
10
+ super
11
+ @serial = serial
20
12
  end
21
- end
22
-
23
- protected
24
- attr_reader :max_identifier_length
25
13
 
26
- private
27
- def sequence_name_from_parts(table_name, column_name, suffix)
28
- over_length = [table_name, column_name, suffix].map(&:length).sum + 2 - max_identifier_length
29
-
30
- if over_length > 0
31
- column_name_length = [(max_identifier_length - suffix.length - 2) / 2, column_name.length].min
32
- over_length -= column_name.length - column_name_length
33
- column_name = column_name[0, column_name_length - [over_length, 0].min]
34
- end
14
+ def serial?
15
+ @serial
16
+ end
35
17
 
36
- if over_length > 0
37
- table_name = table_name[0, table_name.length - over_length]
38
- end
18
+ def array
19
+ sql_type_metadata.sql_type.end_with?("[]")
20
+ end
21
+ alias :array? :array
39
22
 
40
- "#{table_name}_#{column_name}_#{suffix}"
23
+ def sql_type
24
+ super.sub(/\[\]\z/, "")
41
25
  end
26
+ end
42
27
  end
28
+ PostgreSQLColumn = PostgreSQL::Column # :nodoc:
43
29
  end
44
30
  end
@@ -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,26 @@ module ActiveRecord
65
67
  end
66
68
  end
67
69
 
70
+ READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
71
+ :close, :declare, :fetch, :move, :set, :show
72
+ ) # :nodoc:
73
+ private_constant :READ_QUERY
74
+
75
+ def write_query?(sql) # :nodoc:
76
+ !READ_QUERY.match?(sql)
77
+ end
78
+
68
79
  # Executes an SQL statement, returning a PG::Result object on success
69
80
  # or raising a PG::Error exception otherwise.
70
81
  # Note: the PG::Result object is manually memory managed; if you don't
71
82
  # need it specifically, you may want consider the <tt>exec_query</tt> wrapper.
72
83
  def execute(sql, name = nil)
84
+ if preventing_writes? && write_query?(sql)
85
+ raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
86
+ end
87
+
88
+ materialize_transactions
89
+
73
90
  log(sql, name) do
74
91
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
75
92
  @connection.async_exec(sql)
@@ -95,7 +112,7 @@ module ActiveRecord
95
112
  end
96
113
  alias :exec_update :exec_delete
97
114
 
98
- def sql_for_insert(sql, pk, id_value, sequence_name, binds) # :nodoc:
115
+ def sql_for_insert(sql, pk, binds) # :nodoc:
99
116
  if pk.nil?
100
117
  # Extract the table from the insert sql. Yuck.
101
118
  table_ref = extract_table_ref_from_insert_sql(sql)
@@ -149,6 +166,14 @@ module ActiveRecord
149
166
  end
150
167
 
151
168
  private
169
+ def execute_batch(statements, name = nil)
170
+ execute(combine_multi_statements(statements))
171
+ end
172
+
173
+ def build_truncate_statements(table_names)
174
+ ["TRUNCATE TABLE #{table_names.map(&method(:quote_table_name)).join(", ")}"]
175
+ end
176
+
152
177
  # Returns the current ID of a table's sequence.
153
178
  def last_insert_id_result(sequence_name)
154
179
  exec_query("SELECT currval(#{quote(sequence_name)})", "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
 
@@ -77,7 +77,6 @@ module ActiveRecord
77
77
  end
78
78
 
79
79
  private
80
-
81
80
  def type_cast_array(value, method)
82
81
  if value.is_a?(::Array)
83
82
  value.map { |item| type_cast_array(item, method) }
@@ -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
@@ -16,6 +16,14 @@ module ActiveRecord
16
16
  super
17
17
  end
18
18
  end
19
+
20
+ def type_cast_for_schema(value)
21
+ case value
22
+ when ::Float::INFINITY then "::Float::INFINITY"
23
+ when -::Float::INFINITY then "-::Float::INFINITY"
24
+ else super
25
+ end
26
+ end
19
27
  end
20
28
  end
21
29
  end
@@ -10,7 +10,6 @@ module ActiveRecord
10
10
  end
11
11
 
12
12
  private
13
-
14
13
  def cast_value(value)
15
14
  value.to_s
16
15
  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
@@ -46,7 +46,6 @@ module ActiveRecord
46
46
  end
47
47
 
48
48
  private
49
-
50
49
  HstorePair = begin
51
50
  quoted_string = /"[^"\\]*(?:\\.[^"\\]*)*"/
52
51
  unquoted_string = /(?:\\.|[^\s,])[^\s=,\\]*(?:\\.[^\s=,\\]*|=[^,>])*/
@@ -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
@@ -34,7 +34,6 @@ module ActiveRecord
34
34
  end
35
35
 
36
36
  private
37
-
38
37
  def number_for_point(number)
39
38
  number.to_s.gsub(/\.0$/, "")
40
39
  end
@@ -4,7 +4,7 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module PostgreSQL
6
6
  module OID # :nodoc:
7
- class Oid < Type::Integer # :nodoc:
7
+ class Oid < Type::UnsignedInteger # :nodoc:
8
8
  def type
9
9
  :oid
10
10
  end
@@ -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
@@ -50,7 +50,6 @@ module ActiveRecord
50
50
  end
51
51
 
52
52
  private
53
-
54
53
  def number_for_point(number)
55
54
  number.to_s.gsub(/\.0$/, "")
56
55
  end