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
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module SQLite3
6
+ module DatabaseStatements
7
+ READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(:begin, :commit, :explain, :select, :pragma, :release, :savepoint, :rollback) # :nodoc:
8
+ private_constant :READ_QUERY
9
+
10
+ def write_query?(sql) # :nodoc:
11
+ !READ_QUERY.match?(sql)
12
+ end
13
+
14
+ def execute(sql, name = nil) #:nodoc:
15
+ if preventing_writes? && write_query?(sql)
16
+ raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
17
+ end
18
+
19
+ materialize_transactions
20
+
21
+ log(sql, name) do
22
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
23
+ @connection.execute(sql)
24
+ end
25
+ end
26
+ end
27
+
28
+ def exec_query(sql, name = nil, binds = [], prepare: false)
29
+ if preventing_writes? && write_query?(sql)
30
+ raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
31
+ end
32
+
33
+ materialize_transactions
34
+
35
+ type_casted_binds = type_casted_binds(binds)
36
+
37
+ log(sql, name, binds, type_casted_binds) do
38
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
39
+ # Don't cache statements if they are not prepared
40
+ unless prepare
41
+ stmt = @connection.prepare(sql)
42
+ begin
43
+ cols = stmt.columns
44
+ unless without_prepared_statement?(binds)
45
+ stmt.bind_params(type_casted_binds)
46
+ end
47
+ records = stmt.to_a
48
+ ensure
49
+ stmt.close
50
+ end
51
+ else
52
+ stmt = @statements[sql] ||= @connection.prepare(sql)
53
+ cols = stmt.columns
54
+ stmt.reset!
55
+ stmt.bind_params(type_casted_binds)
56
+ records = stmt.to_a
57
+ end
58
+
59
+ ActiveRecord::Result.new(cols, records)
60
+ end
61
+ end
62
+ end
63
+
64
+ def exec_delete(sql, name = "SQL", binds = [])
65
+ exec_query(sql, name, binds)
66
+ @connection.changes
67
+ end
68
+ alias :exec_update :exec_delete
69
+
70
+ def begin_db_transaction #:nodoc:
71
+ log("begin transaction", nil) { @connection.transaction }
72
+ end
73
+
74
+ def commit_db_transaction #:nodoc:
75
+ log("commit transaction", nil) { @connection.commit }
76
+ end
77
+
78
+ def exec_rollback_db_transaction #:nodoc:
79
+ log("rollback transaction", nil) { @connection.rollback }
80
+ end
81
+
82
+
83
+ private
84
+ def execute_batch(sql, name = nil)
85
+ if preventing_writes? && write_query?(sql)
86
+ raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
87
+ end
88
+
89
+ materialize_transactions
90
+
91
+ log(sql, name) do
92
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
93
+ @connection.execute_batch2(sql)
94
+ end
95
+ end
96
+ end
97
+
98
+ def last_inserted_id(result)
99
+ @connection.last_insert_row_id
100
+ end
101
+
102
+ def build_fixture_statements(fixture_set)
103
+ fixture_set.flat_map do |table_name, fixtures|
104
+ next if fixtures.empty?
105
+ fixtures.map { |fixture| build_fixture_sql([fixture], table_name) }
106
+ end.compact
107
+ end
108
+
109
+ def build_truncate_statements(*table_names)
110
+ truncate_tables = table_names.map do |table_name|
111
+ "DELETE FROM #{quote_table_name(table_name)}"
112
+ end
113
+ combine_multi_statements(truncate_tables)
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -13,11 +13,11 @@ module ActiveRecord
13
13
  end
14
14
 
15
15
  def quote_table_name(name)
16
- @quoted_table_names[name] ||= super.gsub(".", "\".\"").freeze
16
+ self.class.quoted_table_names[name] ||= super.gsub(".", "\".\"").freeze
17
17
  end
18
18
 
19
19
  def quote_column_name(name)
20
- @quoted_column_names[name] ||= %Q("#{super.gsub('"', '""')}").freeze
20
+ self.class.quoted_column_names[name] ||= %Q("#{super.gsub('"', '""')}")
21
21
  end
22
22
 
23
23
  def quoted_time(value)
@@ -30,21 +30,57 @@ module ActiveRecord
30
30
  end
31
31
 
32
32
  def quoted_true
33
- ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? "1".freeze : "'t'".freeze
33
+ "1"
34
34
  end
35
35
 
36
36
  def unquoted_true
37
- ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? 1 : "t".freeze
37
+ 1
38
38
  end
39
39
 
40
40
  def quoted_false
41
- ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? "0".freeze : "'f'".freeze
41
+ "0"
42
42
  end
43
43
 
44
44
  def unquoted_false
45
- ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? 0 : "f".freeze
45
+ 0
46
46
  end
47
47
 
48
+ def column_name_matcher
49
+ COLUMN_NAME
50
+ end
51
+
52
+ def column_name_with_order_matcher
53
+ COLUMN_NAME_WITH_ORDER
54
+ end
55
+
56
+ COLUMN_NAME = /
57
+ \A
58
+ (
59
+ (?:
60
+ # "table_name"."column_name" | function(one or no argument)
61
+ ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")) | \w+\((?:|\g<2>)\)
62
+ )
63
+ (?:\s+AS\s+(?:\w+|"\w+"))?
64
+ )
65
+ (?:\s*,\s*\g<1>)*
66
+ \z
67
+ /ix
68
+
69
+ COLUMN_NAME_WITH_ORDER = /
70
+ \A
71
+ (
72
+ (?:
73
+ # "table_name"."column_name" | function(one or no argument)
74
+ ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")) | \w+\((?:|\g<2>)\)
75
+ )
76
+ (?:\s+ASC|\s+DESC)?
77
+ )
78
+ (?:\s*,\s*\g<1>)*
79
+ \z
80
+ /ix
81
+
82
+ private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
83
+
48
84
  private
49
85
 
50
86
  def _type_cast(value)
@@ -11,7 +11,7 @@ module ActiveRecord
11
11
  # See https://www.sqlite.org/fileformat2.html#intschema
12
12
  next if row["name"].starts_with?("sqlite_")
13
13
 
14
- index_sql = query_value(<<-SQL, "SCHEMA")
14
+ index_sql = query_value(<<~SQL, "SCHEMA")
15
15
  SELECT sql
16
16
  FROM sqlite_master
17
17
  WHERE name = #{quote(row['name'])} AND type = 'index'
@@ -21,19 +21,24 @@ module ActiveRecord
21
21
  WHERE name = #{quote(row['name'])} AND type = 'index'
22
22
  SQL
23
23
 
24
- /\sWHERE\s+(?<where>.+)$/i =~ index_sql
24
+ /\bON\b\s*"?(\w+?)"?\s*\((?<expressions>.+?)\)(?:\s*WHERE\b\s*(?<where>.+))?\z/i =~ index_sql
25
25
 
26
26
  columns = exec_query("PRAGMA index_info(#{quote(row['name'])})", "SCHEMA").map do |col|
27
27
  col["name"]
28
28
  end
29
29
 
30
- # Add info on sort order for columns (only desc order is explicitly specified, asc is
31
- # the default)
32
30
  orders = {}
33
- if index_sql # index_sql can be null in case of primary key indexes
34
- index_sql.scan(/"(\w+)" DESC/).flatten.each { |order_column|
35
- orders[order_column] = :desc
36
- }
31
+
32
+ if columns.any?(&:nil?) # index created with an expression
33
+ columns = expressions
34
+ else
35
+ # Add info on sort order for columns (only desc order is explicitly specified,
36
+ # asc is the default)
37
+ if index_sql # index_sql can be null in case of primary key indexes
38
+ index_sql.scan(/"(\w+)" DESC/).flatten.each { |order_column|
39
+ orders[order_column] = :desc
40
+ }
41
+ end
37
42
  end
38
43
 
39
44
  IndexDefinition.new(
@@ -47,6 +52,32 @@ module ActiveRecord
47
52
  end.compact
48
53
  end
49
54
 
55
+ def add_foreign_key(from_table, to_table, **options)
56
+ alter_table(from_table) do |definition|
57
+ to_table = strip_table_name_prefix_and_suffix(to_table)
58
+ definition.foreign_key(to_table, options)
59
+ end
60
+ end
61
+
62
+ def remove_foreign_key(from_table, to_table = nil, **options)
63
+ to_table ||= options[:to_table]
64
+ options = options.except(:name, :to_table)
65
+ foreign_keys = foreign_keys(from_table)
66
+
67
+ fkey = foreign_keys.detect do |fk|
68
+ table = to_table || begin
69
+ table = options[:column].to_s.delete_suffix("_id")
70
+ Base.pluralize_table_names ? table.pluralize : table
71
+ end
72
+ table = strip_table_name_prefix_and_suffix(table)
73
+ fk_to_table = strip_table_name_prefix_and_suffix(fk.to_table)
74
+ fk_to_table == table && options.all? { |k, v| fk.options[k].to_s == v.to_s }
75
+ end || raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
76
+
77
+ foreign_keys.delete(fkey)
78
+ alter_table(from_table, foreign_keys)
79
+ end
80
+
50
81
  def create_schema_dumper(options)
51
82
  SQLite3::SchemaDumper.create(self, options)
52
83
  end
@@ -57,7 +88,7 @@ module ActiveRecord
57
88
  end
58
89
 
59
90
  def create_table_definition(*args)
60
- SQLite3::TableDefinition.new(*args)
91
+ SQLite3::TableDefinition.new(self, *args)
61
92
  end
62
93
 
63
94
  def new_column_from_field(table_name, field)
@@ -74,14 +105,14 @@ module ActiveRecord
74
105
  end
75
106
 
76
107
  type_metadata = fetch_type_metadata(field["type"])
77
- Column.new(field["name"], default, type_metadata, field["notnull"].to_i == 0, table_name, nil, field["collation"])
108
+ Column.new(field["name"], default, type_metadata, field["notnull"].to_i == 0, collation: field["collation"])
78
109
  end
79
110
 
80
111
  def data_source_sql(name = nil, type: nil)
81
112
  scope = quoted_scope(name, type: type)
82
113
  scope[:type] ||= "'table','view'"
83
114
 
84
- sql = "SELECT name FROM sqlite_master WHERE name <> 'sqlite_sequence'".dup
115
+ sql = +"SELECT name FROM sqlite_master WHERE name <> 'sqlite_sequence'"
85
116
  sql << " AND name = #{scope[:name]}" if scope[:name]
86
117
  sql << " AND type IN (#{scope[:type]})"
87
118
  sql