activerecord 5.2.3 → 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 +624 -548
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +4 -2
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +9 -2
  7. data/lib/active_record/aggregations.rb +4 -2
  8. data/lib/active_record/associations.rb +19 -14
  9. data/lib/active_record/associations/association.rb +52 -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/association.rb +14 -18
  14. data/lib/active_record/associations/builder/belongs_to.rb +19 -52
  15. data/lib/active_record/associations/builder/collection_association.rb +5 -15
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
  17. data/lib/active_record/associations/builder/has_many.rb +2 -0
  18. data/lib/active_record/associations/builder/has_one.rb +35 -1
  19. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  20. data/lib/active_record/associations/collection_association.rb +6 -21
  21. data/lib/active_record/associations/collection_proxy.rb +12 -15
  22. data/lib/active_record/associations/foreign_association.rb +7 -0
  23. data/lib/active_record/associations/has_many_association.rb +2 -10
  24. data/lib/active_record/associations/has_many_through_association.rb +18 -25
  25. data/lib/active_record/associations/has_one_association.rb +28 -30
  26. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  27. data/lib/active_record/associations/join_dependency.rb +24 -28
  28. data/lib/active_record/associations/join_dependency/join_association.rb +27 -7
  29. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  30. data/lib/active_record/associations/preloader.rb +39 -31
  31. data/lib/active_record/associations/preloader/association.rb +38 -36
  32. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  33. data/lib/active_record/associations/singular_association.rb +2 -16
  34. data/lib/active_record/attribute_assignment.rb +7 -10
  35. data/lib/active_record/attribute_methods.rb +28 -100
  36. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
  37. data/lib/active_record/attribute_methods/dirty.rb +111 -40
  38. data/lib/active_record/attribute_methods/primary_key.rb +15 -22
  39. data/lib/active_record/attribute_methods/query.rb +2 -3
  40. data/lib/active_record/attribute_methods/read.rb +15 -53
  41. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  42. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  43. data/lib/active_record/attribute_methods/write.rb +17 -24
  44. data/lib/active_record/attributes.rb +13 -0
  45. data/lib/active_record/autosave_association.rb +16 -6
  46. data/lib/active_record/base.rb +2 -3
  47. data/lib/active_record/callbacks.rb +5 -19
  48. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +116 -19
  49. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
  50. data/lib/active_record/connection_adapters/abstract/database_statements.rb +95 -123
  51. data/lib/active_record/connection_adapters/abstract/query_cache.rb +20 -11
  52. data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
  53. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +19 -12
  54. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +76 -48
  55. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
  56. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +133 -54
  57. data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
  58. data/lib/active_record/connection_adapters/abstract_adapter.rb +180 -47
  59. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +128 -194
  60. data/lib/active_record/connection_adapters/column.rb +17 -13
  61. data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
  62. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
  63. data/lib/active_record/connection_adapters/mysql/database_statements.rb +73 -13
  64. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  65. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
  66. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  67. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  68. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +129 -13
  69. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  70. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -9
  71. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
  72. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +20 -1
  73. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  74. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  75. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
  76. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
  77. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  78. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
  79. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  81. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
  82. data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
  83. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +12 -1
  84. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  85. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +55 -53
  86. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
  87. data/lib/active_record/connection_adapters/postgresql_adapter.rb +160 -74
  88. data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
  89. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  90. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  91. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -6
  92. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
  93. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +127 -143
  94. data/lib/active_record/connection_handling.rb +149 -27
  95. data/lib/active_record/core.rb +100 -60
  96. data/lib/active_record/counter_cache.rb +4 -29
  97. data/lib/active_record/database_configurations.rb +233 -0
  98. data/lib/active_record/database_configurations/database_config.rb +37 -0
  99. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  100. data/lib/active_record/database_configurations/url_config.rb +79 -0
  101. data/lib/active_record/dynamic_matchers.rb +1 -1
  102. data/lib/active_record/enum.rb +37 -7
  103. data/lib/active_record/errors.rb +15 -7
  104. data/lib/active_record/explain.rb +1 -1
  105. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  106. data/lib/active_record/fixture_set/render_context.rb +17 -0
  107. data/lib/active_record/fixture_set/table_row.rb +153 -0
  108. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  109. data/lib/active_record/fixtures.rb +145 -472
  110. data/lib/active_record/gem_version.rb +3 -3
  111. data/lib/active_record/inheritance.rb +13 -3
  112. data/lib/active_record/insert_all.rb +179 -0
  113. data/lib/active_record/integration.rb +68 -16
  114. data/lib/active_record/internal_metadata.rb +10 -2
  115. data/lib/active_record/locking/optimistic.rb +5 -6
  116. data/lib/active_record/locking/pessimistic.rb +3 -3
  117. data/lib/active_record/log_subscriber.rb +7 -26
  118. data/lib/active_record/middleware/database_selector.rb +75 -0
  119. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  120. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  121. data/lib/active_record/migration.rb +100 -81
  122. data/lib/active_record/migration/command_recorder.rb +50 -6
  123. data/lib/active_record/migration/compatibility.rb +76 -49
  124. data/lib/active_record/model_schema.rb +30 -9
  125. data/lib/active_record/nested_attributes.rb +2 -2
  126. data/lib/active_record/no_touching.rb +7 -0
  127. data/lib/active_record/persistence.rb +228 -24
  128. data/lib/active_record/query_cache.rb +11 -4
  129. data/lib/active_record/querying.rb +32 -20
  130. data/lib/active_record/railtie.rb +80 -43
  131. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  132. data/lib/active_record/railties/controller_runtime.rb +30 -35
  133. data/lib/active_record/railties/databases.rake +196 -46
  134. data/lib/active_record/reflection.rb +42 -44
  135. data/lib/active_record/relation.rb +310 -80
  136. data/lib/active_record/relation/batches.rb +13 -10
  137. data/lib/active_record/relation/calculations.rb +58 -51
  138. data/lib/active_record/relation/delegation.rb +26 -43
  139. data/lib/active_record/relation/finder_methods.rb +14 -27
  140. data/lib/active_record/relation/merger.rb +11 -20
  141. data/lib/active_record/relation/predicate_builder.rb +4 -6
  142. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  143. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  144. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  145. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  146. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  147. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  148. data/lib/active_record/relation/query_attribute.rb +13 -8
  149. data/lib/active_record/relation/query_methods.rb +206 -78
  150. data/lib/active_record/relation/spawn_methods.rb +1 -1
  151. data/lib/active_record/relation/where_clause.rb +14 -10
  152. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  153. data/lib/active_record/result.rb +30 -11
  154. data/lib/active_record/sanitization.rb +32 -40
  155. data/lib/active_record/schema.rb +2 -11
  156. data/lib/active_record/schema_dumper.rb +22 -7
  157. data/lib/active_record/schema_migration.rb +5 -1
  158. data/lib/active_record/scoping.rb +8 -8
  159. data/lib/active_record/scoping/default.rb +6 -7
  160. data/lib/active_record/scoping/named.rb +19 -15
  161. data/lib/active_record/statement_cache.rb +32 -5
  162. data/lib/active_record/store.rb +87 -8
  163. data/lib/active_record/table_metadata.rb +10 -17
  164. data/lib/active_record/tasks/database_tasks.rb +194 -25
  165. data/lib/active_record/tasks/mysql_database_tasks.rb +5 -5
  166. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
  167. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
  168. data/lib/active_record/test_databases.rb +23 -0
  169. data/lib/active_record/test_fixtures.rb +224 -0
  170. data/lib/active_record/timestamp.rb +39 -25
  171. data/lib/active_record/touch_later.rb +4 -2
  172. data/lib/active_record/transactions.rb +56 -65
  173. data/lib/active_record/translation.rb +1 -1
  174. data/lib/active_record/type.rb +3 -4
  175. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  176. data/lib/active_record/type_caster/connection.rb +15 -14
  177. data/lib/active_record/type_caster/map.rb +1 -4
  178. data/lib/active_record/validations.rb +1 -0
  179. data/lib/active_record/validations/uniqueness.rb +15 -27
  180. data/lib/arel.rb +51 -0
  181. data/lib/arel/alias_predication.rb +9 -0
  182. data/lib/arel/attributes.rb +22 -0
  183. data/lib/arel/attributes/attribute.rb +37 -0
  184. data/lib/arel/collectors/bind.rb +24 -0
  185. data/lib/arel/collectors/composite.rb +31 -0
  186. data/lib/arel/collectors/plain_string.rb +20 -0
  187. data/lib/arel/collectors/sql_string.rb +20 -0
  188. data/lib/arel/collectors/substitute_binds.rb +28 -0
  189. data/lib/arel/crud.rb +42 -0
  190. data/lib/arel/delete_manager.rb +18 -0
  191. data/lib/arel/errors.rb +9 -0
  192. data/lib/arel/expressions.rb +29 -0
  193. data/lib/arel/factory_methods.rb +49 -0
  194. data/lib/arel/insert_manager.rb +49 -0
  195. data/lib/arel/math.rb +45 -0
  196. data/lib/arel/nodes.rb +68 -0
  197. data/lib/arel/nodes/and.rb +32 -0
  198. data/lib/arel/nodes/ascending.rb +23 -0
  199. data/lib/arel/nodes/binary.rb +52 -0
  200. data/lib/arel/nodes/bind_param.rb +36 -0
  201. data/lib/arel/nodes/case.rb +55 -0
  202. data/lib/arel/nodes/casted.rb +50 -0
  203. data/lib/arel/nodes/comment.rb +29 -0
  204. data/lib/arel/nodes/count.rb +12 -0
  205. data/lib/arel/nodes/delete_statement.rb +45 -0
  206. data/lib/arel/nodes/descending.rb +23 -0
  207. data/lib/arel/nodes/equality.rb +18 -0
  208. data/lib/arel/nodes/extract.rb +24 -0
  209. data/lib/arel/nodes/false.rb +16 -0
  210. data/lib/arel/nodes/full_outer_join.rb +8 -0
  211. data/lib/arel/nodes/function.rb +44 -0
  212. data/lib/arel/nodes/grouping.rb +8 -0
  213. data/lib/arel/nodes/in.rb +8 -0
  214. data/lib/arel/nodes/infix_operation.rb +80 -0
  215. data/lib/arel/nodes/inner_join.rb +8 -0
  216. data/lib/arel/nodes/insert_statement.rb +37 -0
  217. data/lib/arel/nodes/join_source.rb +20 -0
  218. data/lib/arel/nodes/matches.rb +18 -0
  219. data/lib/arel/nodes/named_function.rb +23 -0
  220. data/lib/arel/nodes/node.rb +50 -0
  221. data/lib/arel/nodes/node_expression.rb +13 -0
  222. data/lib/arel/nodes/outer_join.rb +8 -0
  223. data/lib/arel/nodes/over.rb +15 -0
  224. data/lib/arel/nodes/regexp.rb +16 -0
  225. data/lib/arel/nodes/right_outer_join.rb +8 -0
  226. data/lib/arel/nodes/select_core.rb +67 -0
  227. data/lib/arel/nodes/select_statement.rb +41 -0
  228. data/lib/arel/nodes/sql_literal.rb +16 -0
  229. data/lib/arel/nodes/string_join.rb +11 -0
  230. data/lib/arel/nodes/table_alias.rb +27 -0
  231. data/lib/arel/nodes/terminal.rb +16 -0
  232. data/lib/arel/nodes/true.rb +16 -0
  233. data/lib/arel/nodes/unary.rb +45 -0
  234. data/lib/arel/nodes/unary_operation.rb +20 -0
  235. data/lib/arel/nodes/unqualified_column.rb +22 -0
  236. data/lib/arel/nodes/update_statement.rb +41 -0
  237. data/lib/arel/nodes/values_list.rb +9 -0
  238. data/lib/arel/nodes/window.rb +126 -0
  239. data/lib/arel/nodes/with.rb +11 -0
  240. data/lib/arel/order_predications.rb +13 -0
  241. data/lib/arel/predications.rb +257 -0
  242. data/lib/arel/select_manager.rb +271 -0
  243. data/lib/arel/table.rb +110 -0
  244. data/lib/arel/tree_manager.rb +72 -0
  245. data/lib/arel/update_manager.rb +34 -0
  246. data/lib/arel/visitors.rb +20 -0
  247. data/lib/arel/visitors/depth_first.rb +204 -0
  248. data/lib/arel/visitors/dot.rb +297 -0
  249. data/lib/arel/visitors/ibm_db.rb +34 -0
  250. data/lib/arel/visitors/informix.rb +62 -0
  251. data/lib/arel/visitors/mssql.rb +157 -0
  252. data/lib/arel/visitors/mysql.rb +83 -0
  253. data/lib/arel/visitors/oracle.rb +159 -0
  254. data/lib/arel/visitors/oracle12.rb +66 -0
  255. data/lib/arel/visitors/postgresql.rb +110 -0
  256. data/lib/arel/visitors/sqlite.rb +39 -0
  257. data/lib/arel/visitors/to_sql.rb +889 -0
  258. data/lib/arel/visitors/visitor.rb +46 -0
  259. data/lib/arel/visitors/where_sql.rb +23 -0
  260. data/lib/arel/window_predications.rb +9 -0
  261. data/lib/rails/generators/active_record/migration.rb +14 -1
  262. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  263. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  264. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  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 +107 -25
  268. data/lib/active_record/collection_cache_key.rb +0 -53
@@ -60,7 +60,7 @@ module ActiveRecord
60
60
  # Quotes a string, escaping any ' (single quote) and \ (backslash)
61
61
  # characters.
62
62
  def quote_string(s)
63
- s.gsub('\\'.freeze, '\&\&'.freeze).gsub("'".freeze, "''".freeze) # ' (for ruby-mode)
63
+ s.gsub('\\', '\&\&').gsub("'", "''") # ' (for ruby-mode)
64
64
  end
65
65
 
66
66
  # Quotes the column name. Defaults to no quoting.
@@ -95,7 +95,7 @@ module ActiveRecord
95
95
  end
96
96
 
97
97
  def quoted_true
98
- "TRUE".freeze
98
+ "TRUE"
99
99
  end
100
100
 
101
101
  def unquoted_true
@@ -103,7 +103,7 @@ module ActiveRecord
103
103
  end
104
104
 
105
105
  def quoted_false
106
- "FALSE".freeze
106
+ "FALSE"
107
107
  end
108
108
 
109
109
  def unquoted_false
@@ -138,15 +138,72 @@ module ActiveRecord
138
138
  "'#{quote_string(value.to_s)}'"
139
139
  end
140
140
 
141
- def type_casted_binds(binds) # :nodoc:
142
- if binds.first.is_a?(Array)
143
- binds.map { |column, value| type_cast(value, column) }
144
- else
145
- binds.map { |attr| type_cast(attr.value_for_database) }
146
- end
141
+ def sanitize_as_sql_comment(value) # :nodoc:
142
+ value.to_s.gsub(%r{ (/ (?: | \g<1>) \*) \+? \s* | \s* (\* (?: | \g<2>) /) }x, "")
143
+ end
144
+
145
+ def column_name_matcher # :nodoc:
146
+ COLUMN_NAME
147
147
  end
148
148
 
149
+ def column_name_with_order_matcher # :nodoc:
150
+ COLUMN_NAME_WITH_ORDER
151
+ end
152
+
153
+ # Regexp for column names (with or without a table name prefix).
154
+ # Matches the following:
155
+ #
156
+ # "#{table_name}.#{column_name}"
157
+ # "#{column_name}"
158
+ COLUMN_NAME = /
159
+ \A
160
+ (
161
+ (?:
162
+ # table_name.column_name | function(one or no argument)
163
+ ((?:\w+\.)?\w+) | \w+\((?:|\g<2>)\)
164
+ )
165
+ (?:\s+AS\s+\w+)?
166
+ )
167
+ (?:\s*,\s*\g<1>)*
168
+ \z
169
+ /ix
170
+
171
+ # Regexp for column names with order (with or without a table name prefix,
172
+ # with or without various order modifiers). Matches the following:
173
+ #
174
+ # "#{table_name}.#{column_name}"
175
+ # "#{table_name}.#{column_name} #{direction}"
176
+ # "#{table_name}.#{column_name} #{direction} NULLS FIRST"
177
+ # "#{table_name}.#{column_name} NULLS LAST"
178
+ # "#{column_name}"
179
+ # "#{column_name} #{direction}"
180
+ # "#{column_name} #{direction} NULLS FIRST"
181
+ # "#{column_name} NULLS LAST"
182
+ COLUMN_NAME_WITH_ORDER = /
183
+ \A
184
+ (
185
+ (?:
186
+ # table_name.column_name | function(one or no argument)
187
+ ((?:\w+\.)?\w+) | \w+\((?:|\g<2>)\)
188
+ )
189
+ (?:\s+ASC|\s+DESC)?
190
+ (?:\s+NULLS\s+(?:FIRST|LAST))?
191
+ )
192
+ (?:\s*,\s*\g<1>)*
193
+ \z
194
+ /ix
195
+
196
+ private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
197
+
149
198
  private
199
+ def type_casted_binds(binds)
200
+ if binds.first.is_a?(Array)
201
+ binds.map { |column, value| type_cast(value, column) }
202
+ else
203
+ binds.map { |attr| type_cast(attr.value_for_database) }
204
+ end
205
+ end
206
+
150
207
  def lookup_cast_type(sql_type)
151
208
  type_map.lookup(sql_type)
152
209
  end
@@ -157,13 +214,9 @@ module ActiveRecord
157
214
  end
158
215
  end
159
216
 
160
- def types_which_need_no_typecasting
161
- [nil, Numeric, String]
162
- end
163
-
164
217
  def _quote(value)
165
218
  case value
166
- when String, ActiveSupport::Multibyte::Chars
219
+ when String, Symbol, ActiveSupport::Multibyte::Chars
167
220
  "'#{quote_string(value.to_s)}'"
168
221
  when true then quoted_true
169
222
  when false then quoted_false
@@ -174,7 +227,6 @@ module ActiveRecord
174
227
  when Type::Binary::Data then quoted_binary(value)
175
228
  when Type::Time::Value then "'#{quoted_time(value)}'"
176
229
  when Date, Time then "'#{quoted_date(value)}'"
177
- when Symbol then "'#{quote_string(value.to_s)}'"
178
230
  when Class then "'#{value}'"
179
231
  else raise TypeError, "can't quote #{value.class.name}"
180
232
  end
@@ -188,10 +240,9 @@ module ActiveRecord
188
240
  when false then unquoted_false
189
241
  # BigDecimals need to be put in a non-normalized form and quoted.
190
242
  when BigDecimal then value.to_s("F")
243
+ when nil, Numeric, String then value
191
244
  when Type::Time::Value then quoted_time(value)
192
245
  when Date, Time then quoted_date(value)
193
- when *types_which_need_no_typecasting
194
- value
195
246
  else raise TypeError
196
247
  end
197
248
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/string/strip"
4
-
5
3
  module ActiveRecord
6
4
  module ConnectionAdapters
7
5
  class AbstractAdapter
@@ -17,14 +15,13 @@ module ActiveRecord
17
15
  end
18
16
 
19
17
  delegate :quote_column_name, :quote_table_name, :quote_default_expression, :type_to_sql,
20
- :options_include_default?, :supports_indexes_in_create?, :supports_foreign_keys_in_create?, :foreign_key_options, to: :@conn
21
- private :quote_column_name, :quote_table_name, :quote_default_expression, :type_to_sql,
22
- :options_include_default?, :supports_indexes_in_create?, :supports_foreign_keys_in_create?, :foreign_key_options
18
+ :options_include_default?, :supports_indexes_in_create?, :supports_foreign_keys?, :foreign_key_options,
19
+ to: :@conn, private: true
23
20
 
24
21
  private
25
22
 
26
23
  def visit_AlterTable(o)
27
- sql = "ALTER TABLE #{quote_table_name(o.name)} ".dup
24
+ sql = +"ALTER TABLE #{quote_table_name(o.name)} "
28
25
  sql << o.adds.map { |col| accept col }.join(" ")
29
26
  sql << o.foreign_key_adds.map { |fk| visit_AddForeignKey fk }.join(" ")
30
27
  sql << o.foreign_key_drops.map { |fk| visit_DropForeignKey fk }.join(" ")
@@ -32,17 +29,19 @@ module ActiveRecord
32
29
 
33
30
  def visit_ColumnDefinition(o)
34
31
  o.sql_type = type_to_sql(o.type, o.options)
35
- column_sql = "#{quote_column_name(o.name)} #{o.sql_type}".dup
32
+ column_sql = +"#{quote_column_name(o.name)} #{o.sql_type}"
36
33
  add_column_options!(column_sql, column_options(o)) unless o.type == :primary_key
37
34
  column_sql
38
35
  end
39
36
 
40
37
  def visit_AddColumnDefinition(o)
41
- "ADD #{accept(o.column)}".dup
38
+ +"ADD #{accept(o.column)}"
42
39
  end
43
40
 
44
41
  def visit_TableDefinition(o)
45
- create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE #{quote_table_name(o.name)} ".dup
42
+ create_sql = +"CREATE#{table_modifier_in_create(o)} TABLE "
43
+ create_sql << "IF NOT EXISTS " if o.if_not_exists
44
+ create_sql << "#{quote_table_name(o.name)} "
46
45
 
47
46
  statements = o.columns.map { |c| accept c }
48
47
  statements << accept(o.primary_keys) if o.primary_keys
@@ -51,7 +50,7 @@ module ActiveRecord
51
50
  statements.concat(o.indexes.map { |column_name, options| index_in_create(o.name, column_name, options) })
52
51
  end
53
52
 
54
- if supports_foreign_keys_in_create?
53
+ if supports_foreign_keys?
55
54
  statements.concat(o.foreign_keys.map { |to_table, options| foreign_key_in_create(o.name, to_table, options) })
56
55
  end
57
56
 
@@ -66,7 +65,7 @@ module ActiveRecord
66
65
  end
67
66
 
68
67
  def visit_ForeignKeyDefinition(o)
69
- sql = <<-SQL.strip_heredoc
68
+ sql = +<<~SQL
70
69
  CONSTRAINT #{quote_column_name(o.name)}
71
70
  FOREIGN KEY (#{quote_column_name(o.column)})
72
71
  REFERENCES #{quote_table_name(o.to_table)} (#{quote_column_name(o.primary_key)})
@@ -122,7 +121,15 @@ module ActiveRecord
122
121
  sql
123
122
  end
124
123
 
124
+ # Returns any SQL string to go between CREATE and TABLE. May be nil.
125
+ def table_modifier_in_create(o)
126
+ " TEMPORARY" if o.temporary
127
+ end
128
+
125
129
  def foreign_key_in_create(from_table, to_table, options)
130
+ prefix = ActiveRecord::Base.table_name_prefix
131
+ suffix = ActiveRecord::Base.table_name_suffix
132
+ to_table = "#{prefix}#{to_table}#{suffix}"
126
133
  options = foreign_key_options(from_table, to_table, options)
127
134
  accept ForeignKeyDefinition.new(from_table, to_table, options)
128
135
  end
@@ -133,7 +140,7 @@ module ActiveRecord
133
140
  when :cascade then "ON #{action} CASCADE"
134
141
  when :restrict then "ON #{action} RESTRICT"
135
142
  else
136
- raise ArgumentError, <<-MSG.strip_heredoc
143
+ raise ArgumentError, <<~MSG
137
144
  '#{dependency}' is not supported for :on_update or :on_delete.
138
145
  Supported values are: :nullify, :cascade, :restrict
139
146
  MSG
@@ -101,13 +101,13 @@ module ActiveRecord
101
101
  end
102
102
  alias validated? validate?
103
103
 
104
- def defined_for?(to_table_ord = nil, to_table: nil, **options)
105
- if to_table_ord
106
- self.to_table == to_table_ord.to_s
107
- else
108
- (to_table.nil? || to_table.to_s == self.to_table) &&
109
- options.all? { |k, v| self.options[k].to_s == v.to_s }
110
- end
104
+ def export_name_on_schema_dump?
105
+ !ActiveRecord::SchemaDumper.fk_ignore_pattern.match?(name) if name
106
+ end
107
+
108
+ def defined_for?(to_table: nil, **options)
109
+ (to_table.nil? || to_table.to_s == self.to_table) &&
110
+ options.all? { |k, v| self.options[k].to_s == v.to_s }
111
111
  end
112
112
 
113
113
  private
@@ -151,13 +151,8 @@ module ActiveRecord
151
151
  end
152
152
  end
153
153
 
154
- # TODO Change this to private once we've dropped Ruby 2.2 support.
155
- # Workaround for Ruby 2.2 "private attribute?" warning.
156
- protected
157
-
158
- attr_reader :name, :polymorphic, :index, :foreign_key, :type, :options
159
-
160
154
  private
155
+ attr_reader :name, :polymorphic, :index, :foreign_key, :type, :options
161
156
 
162
157
  def as_options(value)
163
158
  value.is_a?(Hash) ? value : {}
@@ -199,41 +194,44 @@ module ActiveRecord
199
194
  end
200
195
 
201
196
  module ColumnMethods
197
+ extend ActiveSupport::Concern
198
+
202
199
  # Appends a primary key definition to the table definition.
203
200
  # Can be called multiple times, but this is probably not a good idea.
204
201
  def primary_key(name, type = :primary_key, **options)
205
202
  column(name, type, options.merge(primary_key: true))
206
203
  end
207
204
 
205
+ ##
206
+ # :method: column
207
+ # :call-seq: column(name, type, **options)
208
+ #
208
209
  # Appends a column or columns of a specified type.
209
210
  #
210
211
  # t.string(:goat)
211
212
  # t.string(:goat, :sheep)
212
213
  #
213
214
  # See TableDefinition#column
214
- [
215
- :bigint,
216
- :binary,
217
- :boolean,
218
- :date,
219
- :datetime,
220
- :decimal,
221
- :float,
222
- :integer,
223
- :json,
224
- :string,
225
- :text,
226
- :time,
227
- :timestamp,
228
- :virtual,
229
- ].each do |column_type|
230
- module_eval <<-CODE, __FILE__, __LINE__ + 1
231
- def #{column_type}(*args, **options)
232
- args.each { |name| column(name, :#{column_type}, options) }
215
+
216
+ included do
217
+ define_column_methods :bigint, :binary, :boolean, :date, :datetime, :decimal,
218
+ :float, :integer, :json, :string, :text, :time, :timestamp, :virtual
219
+
220
+ alias :numeric :decimal
221
+ end
222
+
223
+ class_methods do
224
+ private def define_column_methods(*column_types) # :nodoc:
225
+ column_types.each do |column_type|
226
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
227
+ def #{column_type}(*names, **options)
228
+ raise ArgumentError, "Missing column name(s) for #{column_type}" if names.empty?
229
+ names.each { |name| column(name, :#{column_type}, options) }
230
+ end
231
+ RUBY
233
232
  end
234
- CODE
233
+ end
235
234
  end
236
- alias_method :numeric, :decimal
237
235
  end
238
236
 
239
237
  # Represents the schema of an SQL table in an abstract way. This class
@@ -257,15 +255,25 @@ module ActiveRecord
257
255
  class TableDefinition
258
256
  include ColumnMethods
259
257
 
260
- attr_accessor :indexes
261
- attr_reader :name, :temporary, :options, :as, :foreign_keys, :comment
258
+ attr_reader :name, :temporary, :if_not_exists, :options, :as, :comment, :indexes, :foreign_keys
262
259
 
263
- def initialize(name, temporary = false, options = nil, as = nil, comment: nil)
260
+ def initialize(
261
+ conn,
262
+ name,
263
+ temporary: false,
264
+ if_not_exists: false,
265
+ options: nil,
266
+ as: nil,
267
+ comment: nil,
268
+ **
269
+ )
270
+ @conn = conn
264
271
  @columns_hash = {}
265
272
  @indexes = []
266
273
  @foreign_keys = []
267
274
  @primary_keys = nil
268
275
  @temporary = temporary
276
+ @if_not_exists = if_not_exists
269
277
  @options = options
270
278
  @as = as
271
279
  @name = name
@@ -349,16 +357,20 @@ module ActiveRecord
349
357
  #
350
358
  # create_table :taggings do |t|
351
359
  # t.references :tag, index: { name: 'index_taggings_on_tag_id' }
352
- # t.references :tagger, polymorphic: true, index: true
353
- # t.references :taggable, polymorphic: { default: 'Photo' }
360
+ # t.references :tagger, polymorphic: true
361
+ # t.references :taggable, polymorphic: { default: 'Photo' }, index: false
354
362
  # end
355
- def column(name, type, options = {})
363
+ def column(name, type, **options)
356
364
  name = name.to_s
357
365
  type = type.to_sym if type
358
366
  options = options.dup
359
367
 
360
- if @columns_hash[name] && @columns_hash[name].primary_key?
361
- raise ArgumentError, "you can't redefine the primary key column '#{name}'. To define a custom primary key, pass { id: false } to create_table."
368
+ if @columns_hash[name]
369
+ if @columns_hash[name].primary_key?
370
+ raise ArgumentError, "you can't redefine the primary key column '#{name}'. To define a custom primary key, pass { id: false } to create_table."
371
+ else
372
+ raise ArgumentError, "you can't define an already defined column '#{name}'."
373
+ end
362
374
  end
363
375
 
364
376
  index_options = options.delete(:index)
@@ -382,10 +394,7 @@ module ActiveRecord
382
394
  end
383
395
 
384
396
  def foreign_key(table_name, options = {}) # :nodoc:
385
- table_name_prefix = ActiveRecord::Base.table_name_prefix
386
- table_name_suffix = ActiveRecord::Base.table_name_suffix
387
- table_name = "#{table_name_prefix}#{table_name}#{table_name_suffix}"
388
- foreign_keys.push([table_name, options])
397
+ foreign_keys << [table_name, options]
389
398
  end
390
399
 
391
400
  # Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
@@ -395,6 +404,10 @@ module ActiveRecord
395
404
  def timestamps(**options)
396
405
  options[:null] = false if options[:null].nil?
397
406
 
407
+ if !options.key?(:precision) && @conn.supports_datetime_with_precision?
408
+ options[:precision] = 6
409
+ end
410
+
398
411
  column(:created_at, :datetime, options)
399
412
  column(:updated_at, :datetime, options)
400
413
  end
@@ -403,6 +416,7 @@ module ActiveRecord
403
416
  #
404
417
  # t.references(:user)
405
418
  # t.belongs_to(:supplier, foreign_key: true)
419
+ # t.belongs_to(:supplier, foreign_key: true, type: :integer)
406
420
  #
407
421
  # See {connection.add_reference}[rdoc-ref:SchemaStatements#add_reference] for details of the options you can use.
408
422
  def references(*args, **options)
@@ -502,6 +516,7 @@ module ActiveRecord
502
516
  # t.json
503
517
  # t.virtual
504
518
  # t.remove
519
+ # t.remove_foreign_key
505
520
  # t.remove_references
506
521
  # t.remove_belongs_to
507
522
  # t.remove_index
@@ -523,8 +538,10 @@ module ActiveRecord
523
538
  # t.column(:name, :string)
524
539
  #
525
540
  # See TableDefinition#column for details of the options you can use.
526
- def column(column_name, type, options = {})
541
+ def column(column_name, type, **options)
542
+ index_options = options.delete(:index)
527
543
  @base.add_column(name, column_name, type, options)
544
+ index(column_name, index_options.is_a?(Hash) ? index_options : {}) if index_options
528
545
  end
529
546
 
530
547
  # Checks to see if a column exists.
@@ -663,15 +680,26 @@ module ActiveRecord
663
680
  end
664
681
  alias :remove_belongs_to :remove_references
665
682
 
666
- # Adds a foreign key.
683
+ # Adds a foreign key to the table using a supplied table name.
667
684
  #
668
685
  # t.foreign_key(:authors)
686
+ # t.foreign_key(:authors, column: :author_id, primary_key: "id")
669
687
  #
670
688
  # See {connection.add_foreign_key}[rdoc-ref:SchemaStatements#add_foreign_key]
671
689
  def foreign_key(*args)
672
690
  @base.add_foreign_key(name, *args)
673
691
  end
674
692
 
693
+ # Removes the given foreign key from the table.
694
+ #
695
+ # t.remove_foreign_key(:authors)
696
+ # t.remove_foreign_key(column: :author_id)
697
+ #
698
+ # See {connection.remove_foreign_key}[rdoc-ref:SchemaStatements#remove_foreign_key]
699
+ def remove_foreign_key(*args)
700
+ @base.remove_foreign_key(name, *args)
701
+ end
702
+
675
703
  # Checks to see if a foreign key exists.
676
704
  #
677
705
  # t.foreign_key(:authors) unless t.foreign_key_exists?(:authors)