activerecord 5.2.6 → 6.0.0

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 (268) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +609 -622
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +4 -2
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +4 -2
  7. data/lib/active_record/associations/association.rb +52 -19
  8. data/lib/active_record/associations/association_scope.rb +4 -6
  9. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  11. data/lib/active_record/associations/builder/association.rb +14 -18
  12. data/lib/active_record/associations/builder/belongs_to.rb +19 -52
  13. data/lib/active_record/associations/builder/collection_association.rb +3 -13
  14. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
  15. data/lib/active_record/associations/builder/has_many.rb +2 -0
  16. data/lib/active_record/associations/builder/has_one.rb +35 -1
  17. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  18. data/lib/active_record/associations/collection_association.rb +6 -21
  19. data/lib/active_record/associations/collection_proxy.rb +12 -15
  20. data/lib/active_record/associations/foreign_association.rb +7 -0
  21. data/lib/active_record/associations/has_many_association.rb +2 -10
  22. data/lib/active_record/associations/has_many_through_association.rb +14 -14
  23. data/lib/active_record/associations/has_one_association.rb +28 -30
  24. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  25. data/lib/active_record/associations/join_dependency/join_association.rb +9 -10
  26. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  27. data/lib/active_record/associations/join_dependency.rb +24 -28
  28. data/lib/active_record/associations/preloader/association.rb +38 -36
  29. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  30. data/lib/active_record/associations/preloader.rb +40 -32
  31. data/lib/active_record/associations/singular_association.rb +2 -16
  32. data/lib/active_record/associations.rb +19 -14
  33. data/lib/active_record/attribute_assignment.rb +7 -10
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
  35. data/lib/active_record/attribute_methods/dirty.rb +111 -40
  36. data/lib/active_record/attribute_methods/primary_key.rb +15 -22
  37. data/lib/active_record/attribute_methods/query.rb +2 -3
  38. data/lib/active_record/attribute_methods/read.rb +15 -53
  39. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  41. data/lib/active_record/attribute_methods/write.rb +17 -24
  42. data/lib/active_record/attribute_methods.rb +28 -100
  43. data/lib/active_record/attributes.rb +13 -0
  44. data/lib/active_record/autosave_association.rb +5 -9
  45. data/lib/active_record/base.rb +2 -3
  46. data/lib/active_record/callbacks.rb +5 -19
  47. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +94 -16
  48. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
  49. data/lib/active_record/connection_adapters/abstract/database_statements.rb +95 -123
  50. data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -8
  51. data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
  52. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +19 -12
  53. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +76 -48
  54. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
  55. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +132 -53
  56. data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
  57. data/lib/active_record/connection_adapters/abstract_adapter.rb +180 -47
  58. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +128 -194
  59. data/lib/active_record/connection_adapters/column.rb +17 -13
  60. data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
  61. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
  62. data/lib/active_record/connection_adapters/mysql/database_statements.rb +73 -13
  63. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  64. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
  65. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  66. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  67. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +129 -13
  68. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  69. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -9
  70. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
  71. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +20 -1
  72. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  73. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  74. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
  75. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
  76. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  77. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
  78. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  79. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  80. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
  81. data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
  82. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +12 -1
  83. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  84. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +55 -53
  85. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
  86. data/lib/active_record/connection_adapters/postgresql_adapter.rb +160 -74
  87. data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
  88. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  89. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  90. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -6
  91. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
  92. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +125 -141
  93. data/lib/active_record/connection_handling.rb +149 -27
  94. data/lib/active_record/core.rb +100 -60
  95. data/lib/active_record/counter_cache.rb +4 -29
  96. data/lib/active_record/database_configurations/database_config.rb +37 -0
  97. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  98. data/lib/active_record/database_configurations/url_config.rb +79 -0
  99. data/lib/active_record/database_configurations.rb +233 -0
  100. data/lib/active_record/dynamic_matchers.rb +1 -1
  101. data/lib/active_record/enum.rb +37 -7
  102. data/lib/active_record/errors.rb +15 -7
  103. data/lib/active_record/explain.rb +1 -1
  104. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  105. data/lib/active_record/fixture_set/render_context.rb +17 -0
  106. data/lib/active_record/fixture_set/table_row.rb +153 -0
  107. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  108. data/lib/active_record/fixtures.rb +145 -472
  109. data/lib/active_record/gem_version.rb +3 -3
  110. data/lib/active_record/inheritance.rb +13 -3
  111. data/lib/active_record/insert_all.rb +179 -0
  112. data/lib/active_record/integration.rb +68 -16
  113. data/lib/active_record/internal_metadata.rb +10 -2
  114. data/lib/active_record/locking/optimistic.rb +5 -6
  115. data/lib/active_record/locking/pessimistic.rb +3 -3
  116. data/lib/active_record/log_subscriber.rb +7 -26
  117. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  118. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  119. data/lib/active_record/middleware/database_selector.rb +75 -0
  120. data/lib/active_record/migration/command_recorder.rb +50 -6
  121. data/lib/active_record/migration/compatibility.rb +76 -49
  122. data/lib/active_record/migration.rb +100 -81
  123. data/lib/active_record/model_schema.rb +30 -9
  124. data/lib/active_record/nested_attributes.rb +2 -2
  125. data/lib/active_record/no_touching.rb +7 -0
  126. data/lib/active_record/persistence.rb +228 -24
  127. data/lib/active_record/query_cache.rb +11 -4
  128. data/lib/active_record/querying.rb +32 -20
  129. data/lib/active_record/railtie.rb +80 -43
  130. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  131. data/lib/active_record/railties/controller_runtime.rb +30 -35
  132. data/lib/active_record/railties/databases.rake +196 -46
  133. data/lib/active_record/reflection.rb +32 -30
  134. data/lib/active_record/relation/batches.rb +13 -10
  135. data/lib/active_record/relation/calculations.rb +53 -47
  136. data/lib/active_record/relation/delegation.rb +26 -43
  137. data/lib/active_record/relation/finder_methods.rb +13 -26
  138. data/lib/active_record/relation/merger.rb +11 -20
  139. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  140. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  141. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  142. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  143. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  144. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  145. data/lib/active_record/relation/predicate_builder.rb +4 -6
  146. data/lib/active_record/relation/query_attribute.rb +13 -8
  147. data/lib/active_record/relation/query_methods.rb +189 -63
  148. data/lib/active_record/relation/spawn_methods.rb +1 -1
  149. data/lib/active_record/relation/where_clause.rb +14 -10
  150. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  151. data/lib/active_record/relation.rb +310 -80
  152. data/lib/active_record/result.rb +30 -11
  153. data/lib/active_record/sanitization.rb +32 -40
  154. data/lib/active_record/schema.rb +2 -11
  155. data/lib/active_record/schema_dumper.rb +22 -7
  156. data/lib/active_record/schema_migration.rb +5 -1
  157. data/lib/active_record/scoping/default.rb +4 -5
  158. data/lib/active_record/scoping/named.rb +19 -15
  159. data/lib/active_record/scoping.rb +8 -8
  160. data/lib/active_record/statement_cache.rb +30 -3
  161. data/lib/active_record/store.rb +87 -8
  162. data/lib/active_record/table_metadata.rb +10 -17
  163. data/lib/active_record/tasks/database_tasks.rb +194 -25
  164. data/lib/active_record/tasks/mysql_database_tasks.rb +5 -5
  165. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
  166. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
  167. data/lib/active_record/test_databases.rb +23 -0
  168. data/lib/active_record/test_fixtures.rb +224 -0
  169. data/lib/active_record/timestamp.rb +39 -25
  170. data/lib/active_record/touch_later.rb +4 -2
  171. data/lib/active_record/transactions.rb +57 -66
  172. data/lib/active_record/translation.rb +1 -1
  173. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  174. data/lib/active_record/type.rb +3 -4
  175. data/lib/active_record/type_caster/connection.rb +15 -14
  176. data/lib/active_record/type_caster/map.rb +1 -4
  177. data/lib/active_record/validations/uniqueness.rb +15 -27
  178. data/lib/active_record/validations.rb +1 -0
  179. data/lib/active_record.rb +9 -2
  180. data/lib/arel/alias_predication.rb +9 -0
  181. data/lib/arel/attributes/attribute.rb +37 -0
  182. data/lib/arel/attributes.rb +22 -0
  183. data/lib/arel/collectors/bind.rb +24 -0
  184. data/lib/arel/collectors/composite.rb +31 -0
  185. data/lib/arel/collectors/plain_string.rb +20 -0
  186. data/lib/arel/collectors/sql_string.rb +20 -0
  187. data/lib/arel/collectors/substitute_binds.rb +28 -0
  188. data/lib/arel/crud.rb +42 -0
  189. data/lib/arel/delete_manager.rb +18 -0
  190. data/lib/arel/errors.rb +9 -0
  191. data/lib/arel/expressions.rb +29 -0
  192. data/lib/arel/factory_methods.rb +49 -0
  193. data/lib/arel/insert_manager.rb +49 -0
  194. data/lib/arel/math.rb +45 -0
  195. data/lib/arel/nodes/and.rb +32 -0
  196. data/lib/arel/nodes/ascending.rb +23 -0
  197. data/lib/arel/nodes/binary.rb +52 -0
  198. data/lib/arel/nodes/bind_param.rb +36 -0
  199. data/lib/arel/nodes/case.rb +55 -0
  200. data/lib/arel/nodes/casted.rb +50 -0
  201. data/lib/arel/nodes/comment.rb +29 -0
  202. data/lib/arel/nodes/count.rb +12 -0
  203. data/lib/arel/nodes/delete_statement.rb +45 -0
  204. data/lib/arel/nodes/descending.rb +23 -0
  205. data/lib/arel/nodes/equality.rb +18 -0
  206. data/lib/arel/nodes/extract.rb +24 -0
  207. data/lib/arel/nodes/false.rb +16 -0
  208. data/lib/arel/nodes/full_outer_join.rb +8 -0
  209. data/lib/arel/nodes/function.rb +44 -0
  210. data/lib/arel/nodes/grouping.rb +8 -0
  211. data/lib/arel/nodes/in.rb +8 -0
  212. data/lib/arel/nodes/infix_operation.rb +80 -0
  213. data/lib/arel/nodes/inner_join.rb +8 -0
  214. data/lib/arel/nodes/insert_statement.rb +37 -0
  215. data/lib/arel/nodes/join_source.rb +20 -0
  216. data/lib/arel/nodes/matches.rb +18 -0
  217. data/lib/arel/nodes/named_function.rb +23 -0
  218. data/lib/arel/nodes/node.rb +50 -0
  219. data/lib/arel/nodes/node_expression.rb +13 -0
  220. data/lib/arel/nodes/outer_join.rb +8 -0
  221. data/lib/arel/nodes/over.rb +15 -0
  222. data/lib/arel/nodes/regexp.rb +16 -0
  223. data/lib/arel/nodes/right_outer_join.rb +8 -0
  224. data/lib/arel/nodes/select_core.rb +67 -0
  225. data/lib/arel/nodes/select_statement.rb +41 -0
  226. data/lib/arel/nodes/sql_literal.rb +16 -0
  227. data/lib/arel/nodes/string_join.rb +11 -0
  228. data/lib/arel/nodes/table_alias.rb +27 -0
  229. data/lib/arel/nodes/terminal.rb +16 -0
  230. data/lib/arel/nodes/true.rb +16 -0
  231. data/lib/arel/nodes/unary.rb +45 -0
  232. data/lib/arel/nodes/unary_operation.rb +20 -0
  233. data/lib/arel/nodes/unqualified_column.rb +22 -0
  234. data/lib/arel/nodes/update_statement.rb +41 -0
  235. data/lib/arel/nodes/values_list.rb +9 -0
  236. data/lib/arel/nodes/window.rb +126 -0
  237. data/lib/arel/nodes/with.rb +11 -0
  238. data/lib/arel/nodes.rb +68 -0
  239. data/lib/arel/order_predications.rb +13 -0
  240. data/lib/arel/predications.rb +257 -0
  241. data/lib/arel/select_manager.rb +271 -0
  242. data/lib/arel/table.rb +110 -0
  243. data/lib/arel/tree_manager.rb +72 -0
  244. data/lib/arel/update_manager.rb +34 -0
  245. data/lib/arel/visitors/depth_first.rb +204 -0
  246. data/lib/arel/visitors/dot.rb +297 -0
  247. data/lib/arel/visitors/ibm_db.rb +34 -0
  248. data/lib/arel/visitors/informix.rb +62 -0
  249. data/lib/arel/visitors/mssql.rb +157 -0
  250. data/lib/arel/visitors/mysql.rb +83 -0
  251. data/lib/arel/visitors/oracle.rb +159 -0
  252. data/lib/arel/visitors/oracle12.rb +66 -0
  253. data/lib/arel/visitors/postgresql.rb +110 -0
  254. data/lib/arel/visitors/sqlite.rb +39 -0
  255. data/lib/arel/visitors/to_sql.rb +889 -0
  256. data/lib/arel/visitors/visitor.rb +46 -0
  257. data/lib/arel/visitors/where_sql.rb +23 -0
  258. data/lib/arel/visitors.rb +20 -0
  259. data/lib/arel/window_predications.rb +9 -0
  260. data/lib/arel.rb +51 -0
  261. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  262. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  263. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  264. data/lib/rails/generators/active_record/migration.rb +14 -1
  265. data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
  266. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  267. metadata +108 -26
  268. data/lib/active_record/collection_cache_key.rb +0 -53
@@ -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
@@ -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
157
  def create_table_definition(*args)
78
- MySQL::TableDefinition.new(*args)
158
+ MySQL::TableDefinition.new(self, *args)
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
@@ -121,7 +203,7 @@ module ActiveRecord
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
206
+ sql = +"SELECT table_name FROM information_schema.tables"
125
207
  sql << " WHERE table_schema = #{scope[:schema]}"
126
208
  sql << " AND table_name = #{scope[:name]}" if scope[:name]
127
209
  sql << " AND table_type = #{scope[:type]}" if scope[:type]
@@ -142,6 +224,40 @@ module ActiveRecord
142
224
  schema, name = nil, schema unless name
143
225
  [schema, name]
144
226
  end
227
+
228
+ def type_with_size_to_sql(type, size)
229
+ case size&.to_s
230
+ when nil, "tiny", "medium", "long"
231
+ "#{size}#{type}"
232
+ else
233
+ raise ArgumentError,
234
+ "#{size.inspect} is invalid :size value. Only :tiny, :medium, and :long are allowed."
235
+ end
236
+ end
237
+
238
+ def limit_to_size(limit, type)
239
+ case type.to_s
240
+ when "text", "blob", "binary"
241
+ case limit
242
+ when 0..0xff; "tiny"
243
+ when nil, 0x100..0xffff; nil
244
+ when 0x10000..0xffffff; "medium"
245
+ when 0x1000000..0xffffffff; "long"
246
+ else raise ArgumentError, "No #{type} type has byte size #{limit}"
247
+ end
248
+ end
249
+ end
250
+
251
+ def integer_to_sql(limit)
252
+ case limit
253
+ when 1; "tinyint"
254
+ when 2; "smallint"
255
+ when 3; "mediumint"
256
+ when nil, 4; "int"
257
+ when 5..8; "bigint"
258
+ else raise ArgumentError, "No integer type has byte size #{limit}. Use a decimal with scale 0 instead."
259
+ end
260
+ end
145
261
  end
146
262
  end
147
263
  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,6 +117,7 @@ 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
@@ -117,12 +130,16 @@ module ActiveRecord
117
130
  end
118
131
 
119
132
  def configure_connection
120
- @connection.query_options.merge!(as: :array)
133
+ @connection.query_options[:as] = :array
121
134
  super
122
135
  end
123
136
 
124
137
  def full_version
125
- @full_version ||= @connection.server_info[:version]
138
+ schema_cache.database_version.full_version_string
139
+ end
140
+
141
+ def get_full_version
142
+ @connection.server_info[:version]
126
143
  end
127
144
  end
128
145
  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,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, 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)
@@ -149,6 +164,10 @@ module ActiveRecord
149
164
  end
150
165
 
151
166
  private
167
+ def build_truncate_statements(*table_names)
168
+ "TRUNCATE TABLE #{table_names.map(&method(:quote_table_name)).join(", ")}"
169
+ end
170
+
152
171
  # Returns the current ID of a table's sequence.
153
172
  def last_insert_id_result(sequence_name)
154
173
  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
 
@@ -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
@@ -64,7 +64,7 @@ module ActiveRecord
64
64
  end
65
65
 
66
66
  def type_cast_single_for_database(value)
67
- infinity?(value) ? value : @subtype.serialize(value)
67
+ infinity?(value) ? value : @subtype.serialize(@subtype.cast(value))
68
68
  end
69
69
 
70
70
  def extract_bounds(value)
@@ -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)
@@ -13,9 +13,12 @@ module ActiveRecord
13
13
  :uuid
14
14
  end
15
15
 
16
- def cast(value)
17
- value.to_s[ACCEPTABLE_UUID, 0]
18
- end
16
+ private
17
+
18
+ def cast_value(value)
19
+ casted = value.to_s
20
+ casted if casted.match?(ACCEPTABLE_UUID)
21
+ end
19
22
  end
20
23
  end
21
24
  end
@@ -30,7 +30,7 @@ module ActiveRecord
30
30
  # - "schema.name".table_name
31
31
  # - "schema.name"."table.name"
32
32
  def quote_table_name(name) # :nodoc:
33
- @quoted_table_names[name] ||= Utils.extract_schema_qualified_name(name.to_s).quoted.freeze
33
+ self.class.quoted_table_names[name] ||= Utils.extract_schema_qualified_name(name.to_s).quoted.freeze
34
34
  end
35
35
 
36
36
  # Quotes schema names for use in SQL queries.
@@ -44,7 +44,7 @@ module ActiveRecord
44
44
 
45
45
  # Quotes column names for use in SQL queries.
46
46
  def quote_column_name(name) # :nodoc:
47
- @quoted_column_names[name] ||= PG::Connection.quote_ident(super).freeze
47
+ self.class.quoted_column_names[name] ||= PG::Connection.quote_ident(super).freeze
48
48
  end
49
49
 
50
50
  # Quote date/time values for use in SQL input.
@@ -78,6 +78,43 @@ module ActiveRecord
78
78
  type_map.lookup(column.oid, column.fmod, column.sql_type)
79
79
  end
80
80
 
81
+ def column_name_matcher
82
+ COLUMN_NAME
83
+ end
84
+
85
+ def column_name_with_order_matcher
86
+ COLUMN_NAME_WITH_ORDER
87
+ end
88
+
89
+ COLUMN_NAME = /
90
+ \A
91
+ (
92
+ (?:
93
+ # "table_name"."column_name"::type_name | function(one or no argument)::type_name
94
+ ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")(?:::\w+)?) | \w+\((?:|\g<2>)\)(?:::\w+)?
95
+ )
96
+ (?:\s+AS\s+(?:\w+|"\w+"))?
97
+ )
98
+ (?:\s*,\s*\g<1>)*
99
+ \z
100
+ /ix
101
+
102
+ COLUMN_NAME_WITH_ORDER = /
103
+ \A
104
+ (
105
+ (?:
106
+ # "table_name"."column_name"::type_name | function(one or no argument)::type_name
107
+ ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")(?:::\w+)?) | \w+\((?:|\g<2>)\)(?:::\w+)?
108
+ )
109
+ (?:\s+ASC|\s+DESC)?
110
+ (?:\s+NULLS\s+(?:FIRST|LAST))?
111
+ )
112
+ (?:\s*,\s*\g<1>)*
113
+ \z
114
+ /ix
115
+
116
+ private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
117
+
81
118
  private
82
119
  def lookup_cast_type(sql_type)
83
120
  super(query_value("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").to_i)
@@ -93,11 +130,11 @@ module ActiveRecord
93
130
  elsif value.hex?
94
131
  "X'#{value}'"
95
132
  end
96
- when Float
97
- if value.infinite? || value.nan?
98
- "'#{value}'"
99
- else
133
+ when Numeric
134
+ if value.finite?
100
135
  super
136
+ else
137
+ "'#{value}'"
101
138
  end
102
139
  when OID::Array::Data
103
140
  _quote(encode_array(value))
@@ -138,7 +175,7 @@ module ActiveRecord
138
175
  end
139
176
 
140
177
  def encode_range(range)
141
- "[#{type_cast_range_value(range.first)},#{type_cast_range_value(range.last)}#{range.exclude_end? ? ')' : ']'}"
178
+ "[#{type_cast_range_value(range.begin)},#{type_cast_range_value(range.end)}#{range.exclude_end? ? ')' : ']'}"
142
179
  end
143
180
 
144
181
  def determine_encoding_of_strings_in_array(value)