activerecord 5.2.3

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 (244) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +937 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +217 -0
  5. data/examples/performance.rb +185 -0
  6. data/examples/simple.rb +15 -0
  7. data/lib/active_record.rb +188 -0
  8. data/lib/active_record/aggregations.rb +283 -0
  9. data/lib/active_record/association_relation.rb +40 -0
  10. data/lib/active_record/associations.rb +1860 -0
  11. data/lib/active_record/associations/alias_tracker.rb +81 -0
  12. data/lib/active_record/associations/association.rb +299 -0
  13. data/lib/active_record/associations/association_scope.rb +168 -0
  14. data/lib/active_record/associations/belongs_to_association.rb +130 -0
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +40 -0
  16. data/lib/active_record/associations/builder/association.rb +140 -0
  17. data/lib/active_record/associations/builder/belongs_to.rb +163 -0
  18. data/lib/active_record/associations/builder/collection_association.rb +82 -0
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +135 -0
  20. data/lib/active_record/associations/builder/has_many.rb +17 -0
  21. data/lib/active_record/associations/builder/has_one.rb +30 -0
  22. data/lib/active_record/associations/builder/singular_association.rb +42 -0
  23. data/lib/active_record/associations/collection_association.rb +513 -0
  24. data/lib/active_record/associations/collection_proxy.rb +1131 -0
  25. data/lib/active_record/associations/foreign_association.rb +13 -0
  26. data/lib/active_record/associations/has_many_association.rb +144 -0
  27. data/lib/active_record/associations/has_many_through_association.rb +227 -0
  28. data/lib/active_record/associations/has_one_association.rb +120 -0
  29. data/lib/active_record/associations/has_one_through_association.rb +45 -0
  30. data/lib/active_record/associations/join_dependency.rb +262 -0
  31. data/lib/active_record/associations/join_dependency/join_association.rb +60 -0
  32. data/lib/active_record/associations/join_dependency/join_base.rb +23 -0
  33. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  34. data/lib/active_record/associations/preloader.rb +193 -0
  35. data/lib/active_record/associations/preloader/association.rb +131 -0
  36. data/lib/active_record/associations/preloader/through_association.rb +107 -0
  37. data/lib/active_record/associations/singular_association.rb +73 -0
  38. data/lib/active_record/associations/through_association.rb +121 -0
  39. data/lib/active_record/attribute_assignment.rb +88 -0
  40. data/lib/active_record/attribute_decorators.rb +90 -0
  41. data/lib/active_record/attribute_methods.rb +492 -0
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +78 -0
  43. data/lib/active_record/attribute_methods/dirty.rb +150 -0
  44. data/lib/active_record/attribute_methods/primary_key.rb +143 -0
  45. data/lib/active_record/attribute_methods/query.rb +42 -0
  46. data/lib/active_record/attribute_methods/read.rb +85 -0
  47. data/lib/active_record/attribute_methods/serialization.rb +90 -0
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +91 -0
  49. data/lib/active_record/attribute_methods/write.rb +68 -0
  50. data/lib/active_record/attributes.rb +266 -0
  51. data/lib/active_record/autosave_association.rb +498 -0
  52. data/lib/active_record/base.rb +329 -0
  53. data/lib/active_record/callbacks.rb +353 -0
  54. data/lib/active_record/coders/json.rb +15 -0
  55. data/lib/active_record/coders/yaml_column.rb +50 -0
  56. data/lib/active_record/collection_cache_key.rb +53 -0
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1068 -0
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +72 -0
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +540 -0
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +145 -0
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +200 -0
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +146 -0
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +685 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +95 -0
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1396 -0
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +283 -0
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +628 -0
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +887 -0
  70. data/lib/active_record/connection_adapters/column.rb +91 -0
  71. data/lib/active_record/connection_adapters/connection_specification.rb +287 -0
  72. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
  73. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  74. data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
  75. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  76. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
  80. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
  81. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
  82. data/lib/active_record/connection_adapters/mysql2_adapter.rb +129 -0
  83. data/lib/active_record/connection_adapters/postgresql/column.rb +44 -0
  84. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +163 -0
  85. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +56 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +111 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  109. data/lib/active_record/connection_adapters/postgresql/quoting.rb +168 -0
  110. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +206 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  114. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +774 -0
  115. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
  116. data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
  117. data/lib/active_record/connection_adapters/postgresql_adapter.rb +863 -0
  118. data/lib/active_record/connection_adapters/schema_cache.rb +118 -0
  119. data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
  126. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +573 -0
  127. data/lib/active_record/connection_adapters/statement_pool.rb +61 -0
  128. data/lib/active_record/connection_handling.rb +145 -0
  129. data/lib/active_record/core.rb +559 -0
  130. data/lib/active_record/counter_cache.rb +218 -0
  131. data/lib/active_record/define_callbacks.rb +22 -0
  132. data/lib/active_record/dynamic_matchers.rb +122 -0
  133. data/lib/active_record/enum.rb +244 -0
  134. data/lib/active_record/errors.rb +380 -0
  135. data/lib/active_record/explain.rb +50 -0
  136. data/lib/active_record/explain_registry.rb +32 -0
  137. data/lib/active_record/explain_subscriber.rb +34 -0
  138. data/lib/active_record/fixture_set/file.rb +82 -0
  139. data/lib/active_record/fixtures.rb +1065 -0
  140. data/lib/active_record/gem_version.rb +17 -0
  141. data/lib/active_record/inheritance.rb +283 -0
  142. data/lib/active_record/integration.rb +155 -0
  143. data/lib/active_record/internal_metadata.rb +45 -0
  144. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  145. data/lib/active_record/locale/en.yml +48 -0
  146. data/lib/active_record/locking/optimistic.rb +198 -0
  147. data/lib/active_record/locking/pessimistic.rb +89 -0
  148. data/lib/active_record/log_subscriber.rb +137 -0
  149. data/lib/active_record/migration.rb +1378 -0
  150. data/lib/active_record/migration/command_recorder.rb +240 -0
  151. data/lib/active_record/migration/compatibility.rb +217 -0
  152. data/lib/active_record/migration/join_table.rb +17 -0
  153. data/lib/active_record/model_schema.rb +521 -0
  154. data/lib/active_record/nested_attributes.rb +600 -0
  155. data/lib/active_record/no_touching.rb +58 -0
  156. data/lib/active_record/null_relation.rb +68 -0
  157. data/lib/active_record/persistence.rb +763 -0
  158. data/lib/active_record/query_cache.rb +45 -0
  159. data/lib/active_record/querying.rb +70 -0
  160. data/lib/active_record/railtie.rb +226 -0
  161. data/lib/active_record/railties/console_sandbox.rb +7 -0
  162. data/lib/active_record/railties/controller_runtime.rb +56 -0
  163. data/lib/active_record/railties/databases.rake +377 -0
  164. data/lib/active_record/readonly_attributes.rb +24 -0
  165. data/lib/active_record/reflection.rb +1044 -0
  166. data/lib/active_record/relation.rb +629 -0
  167. data/lib/active_record/relation/batches.rb +287 -0
  168. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  169. data/lib/active_record/relation/calculations.rb +417 -0
  170. data/lib/active_record/relation/delegation.rb +147 -0
  171. data/lib/active_record/relation/finder_methods.rb +565 -0
  172. data/lib/active_record/relation/from_clause.rb +26 -0
  173. data/lib/active_record/relation/merger.rb +193 -0
  174. data/lib/active_record/relation/predicate_builder.rb +152 -0
  175. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  176. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  177. data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
  178. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
  179. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  180. data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
  181. data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
  182. data/lib/active_record/relation/query_attribute.rb +45 -0
  183. data/lib/active_record/relation/query_methods.rb +1231 -0
  184. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  185. data/lib/active_record/relation/spawn_methods.rb +77 -0
  186. data/lib/active_record/relation/where_clause.rb +186 -0
  187. data/lib/active_record/relation/where_clause_factory.rb +34 -0
  188. data/lib/active_record/result.rb +149 -0
  189. data/lib/active_record/runtime_registry.rb +24 -0
  190. data/lib/active_record/sanitization.rb +222 -0
  191. data/lib/active_record/schema.rb +70 -0
  192. data/lib/active_record/schema_dumper.rb +255 -0
  193. data/lib/active_record/schema_migration.rb +56 -0
  194. data/lib/active_record/scoping.rb +106 -0
  195. data/lib/active_record/scoping/default.rb +152 -0
  196. data/lib/active_record/scoping/named.rb +213 -0
  197. data/lib/active_record/secure_token.rb +40 -0
  198. data/lib/active_record/serialization.rb +22 -0
  199. data/lib/active_record/statement_cache.rb +121 -0
  200. data/lib/active_record/store.rb +211 -0
  201. data/lib/active_record/suppressor.rb +61 -0
  202. data/lib/active_record/table_metadata.rb +82 -0
  203. data/lib/active_record/tasks/database_tasks.rb +337 -0
  204. data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
  205. data/lib/active_record/tasks/postgresql_database_tasks.rb +143 -0
  206. data/lib/active_record/tasks/sqlite_database_tasks.rb +83 -0
  207. data/lib/active_record/timestamp.rb +153 -0
  208. data/lib/active_record/touch_later.rb +64 -0
  209. data/lib/active_record/transactions.rb +502 -0
  210. data/lib/active_record/translation.rb +24 -0
  211. data/lib/active_record/type.rb +79 -0
  212. data/lib/active_record/type/adapter_specific_registry.rb +136 -0
  213. data/lib/active_record/type/date.rb +9 -0
  214. data/lib/active_record/type/date_time.rb +9 -0
  215. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  216. data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
  217. data/lib/active_record/type/internal/timezone.rb +17 -0
  218. data/lib/active_record/type/json.rb +30 -0
  219. data/lib/active_record/type/serialized.rb +71 -0
  220. data/lib/active_record/type/text.rb +11 -0
  221. data/lib/active_record/type/time.rb +21 -0
  222. data/lib/active_record/type/type_map.rb +62 -0
  223. data/lib/active_record/type/unsigned_integer.rb +17 -0
  224. data/lib/active_record/type_caster.rb +9 -0
  225. data/lib/active_record/type_caster/connection.rb +33 -0
  226. data/lib/active_record/type_caster/map.rb +23 -0
  227. data/lib/active_record/validations.rb +93 -0
  228. data/lib/active_record/validations/absence.rb +25 -0
  229. data/lib/active_record/validations/associated.rb +60 -0
  230. data/lib/active_record/validations/length.rb +26 -0
  231. data/lib/active_record/validations/presence.rb +68 -0
  232. data/lib/active_record/validations/uniqueness.rb +238 -0
  233. data/lib/active_record/version.rb +10 -0
  234. data/lib/rails/generators/active_record.rb +19 -0
  235. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  236. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  237. data/lib/rails/generators/active_record/migration.rb +35 -0
  238. data/lib/rails/generators/active_record/migration/migration_generator.rb +78 -0
  239. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  240. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +46 -0
  241. data/lib/rails/generators/active_record/model/model_generator.rb +48 -0
  242. data/lib/rails/generators/active_record/model/templates/model.rb.tt +13 -0
  243. data/lib/rails/generators/active_record/model/templates/module.rb.tt +7 -0
  244. metadata +333 -0
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "concurrent/map"
4
+
5
+ module ActiveRecord
6
+ module ConnectionAdapters # :nodoc:
7
+ module QueryCache
8
+ class << self
9
+ def included(base) #:nodoc:
10
+ dirties_query_cache base, :insert, :update, :delete, :rollback_to_savepoint, :rollback_db_transaction
11
+
12
+ base.set_callback :checkout, :after, :configure_query_cache!
13
+ base.set_callback :checkin, :after, :disable_query_cache!
14
+ end
15
+
16
+ def dirties_query_cache(base, *method_names)
17
+ method_names.each do |method_name|
18
+ base.class_eval <<-end_code, __FILE__, __LINE__ + 1
19
+ def #{method_name}(*)
20
+ clear_query_cache if @query_cache_enabled
21
+ super
22
+ end
23
+ end_code
24
+ end
25
+ end
26
+ end
27
+
28
+ module ConnectionPoolConfiguration
29
+ def initialize(*)
30
+ super
31
+ @query_cache_enabled = Concurrent::Map.new { false }
32
+ end
33
+
34
+ def enable_query_cache!
35
+ @query_cache_enabled[connection_cache_key(Thread.current)] = true
36
+ connection.enable_query_cache! if active_connection?
37
+ end
38
+
39
+ def disable_query_cache!
40
+ @query_cache_enabled.delete connection_cache_key(Thread.current)
41
+ connection.disable_query_cache! if active_connection?
42
+ end
43
+
44
+ def query_cache_enabled
45
+ @query_cache_enabled[connection_cache_key(Thread.current)]
46
+ end
47
+ end
48
+
49
+ attr_reader :query_cache, :query_cache_enabled
50
+
51
+ def initialize(*)
52
+ super
53
+ @query_cache = Hash.new { |h, sql| h[sql] = {} }
54
+ @query_cache_enabled = false
55
+ end
56
+
57
+ # Enable the query cache within the block.
58
+ def cache
59
+ old, @query_cache_enabled = @query_cache_enabled, true
60
+ yield
61
+ ensure
62
+ @query_cache_enabled = old
63
+ clear_query_cache unless @query_cache_enabled
64
+ end
65
+
66
+ def enable_query_cache!
67
+ @query_cache_enabled = true
68
+ end
69
+
70
+ def disable_query_cache!
71
+ @query_cache_enabled = false
72
+ clear_query_cache
73
+ end
74
+
75
+ # Disable the query cache within the block.
76
+ def uncached
77
+ old, @query_cache_enabled = @query_cache_enabled, false
78
+ yield
79
+ ensure
80
+ @query_cache_enabled = old
81
+ end
82
+
83
+ # Clears the query cache.
84
+ #
85
+ # One reason you may wish to call this method explicitly is between queries
86
+ # that ask the database to randomize results. Otherwise the cache would see
87
+ # the same SQL query and repeatedly return the same result each time, silently
88
+ # undermining the randomness you were expecting.
89
+ def clear_query_cache
90
+ @lock.synchronize do
91
+ @query_cache.clear
92
+ end
93
+ end
94
+
95
+ def select_all(arel, name = nil, binds = [], preparable: nil)
96
+ if @query_cache_enabled && !locked?(arel)
97
+ arel = arel_from_relation(arel)
98
+ sql, binds = to_sql_and_binds(arel, binds)
99
+
100
+ if preparable.nil?
101
+ preparable = prepared_statements ? visitor.preparable : false
102
+ end
103
+
104
+ cache_sql(sql, name, binds) { super(sql, name, binds, preparable: preparable) }
105
+ else
106
+ super
107
+ end
108
+ end
109
+
110
+ private
111
+
112
+ def cache_sql(sql, name, binds)
113
+ @lock.synchronize do
114
+ result =
115
+ if @query_cache[sql].key?(binds)
116
+ ActiveSupport::Notifications.instrument(
117
+ "sql.active_record",
118
+ sql: sql,
119
+ binds: binds,
120
+ type_casted_binds: -> { type_casted_binds(binds) },
121
+ name: name,
122
+ connection_id: object_id,
123
+ cached: true,
124
+ )
125
+ @query_cache[sql][binds]
126
+ else
127
+ @query_cache[sql][binds] = yield
128
+ end
129
+ result.dup
130
+ end
131
+ end
132
+
133
+ # If arel is locked this is a SELECT ... FOR UPDATE or somesuch. Such
134
+ # queries should not be cached.
135
+ def locked?(arel)
136
+ arel = arel.arel if arel.is_a?(Relation)
137
+ arel.respond_to?(:locked) && arel.locked
138
+ end
139
+
140
+ def configure_query_cache!
141
+ enable_query_cache! if pool.query_cache_enabled
142
+ end
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,200 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/big_decimal/conversions"
4
+ require "active_support/multibyte/chars"
5
+
6
+ module ActiveRecord
7
+ module ConnectionAdapters # :nodoc:
8
+ module Quoting
9
+ # Quotes the column value to help prevent
10
+ # {SQL injection attacks}[https://en.wikipedia.org/wiki/SQL_injection].
11
+ def quote(value)
12
+ value = id_value_for_database(value) if value.is_a?(Base)
13
+
14
+ if value.respond_to?(:value_for_database)
15
+ value = value.value_for_database
16
+ end
17
+
18
+ _quote(value)
19
+ end
20
+
21
+ # Cast a +value+ to a type that the database understands. For example,
22
+ # SQLite does not understand dates, so this method will convert a Date
23
+ # to a String.
24
+ def type_cast(value, column = nil)
25
+ value = id_value_for_database(value) if value.is_a?(Base)
26
+
27
+ if column
28
+ value = type_cast_from_column(column, value)
29
+ end
30
+
31
+ _type_cast(value)
32
+ rescue TypeError
33
+ to_type = column ? " to #{column.type}" : ""
34
+ raise TypeError, "can't cast #{value.class}#{to_type}"
35
+ end
36
+
37
+ # If you are having to call this function, you are likely doing something
38
+ # wrong. The column does not have sufficient type information if the user
39
+ # provided a custom type on the class level either explicitly (via
40
+ # Attributes::ClassMethods#attribute) or implicitly (via
41
+ # AttributeMethods::Serialization::ClassMethods#serialize, +time_zone_aware_attributes+).
42
+ # In almost all cases, the sql type should only be used to change quoting behavior, when the primitive to
43
+ # represent the type doesn't sufficiently reflect the differences
44
+ # (varchar vs binary) for example. The type used to get this primitive
45
+ # should have been provided before reaching the connection adapter.
46
+ def type_cast_from_column(column, value) # :nodoc:
47
+ if column
48
+ type = lookup_cast_type_from_column(column)
49
+ type.serialize(value)
50
+ else
51
+ value
52
+ end
53
+ end
54
+
55
+ # See docs for #type_cast_from_column
56
+ def lookup_cast_type_from_column(column) # :nodoc:
57
+ lookup_cast_type(column.sql_type)
58
+ end
59
+
60
+ # Quotes a string, escaping any ' (single quote) and \ (backslash)
61
+ # characters.
62
+ def quote_string(s)
63
+ s.gsub('\\'.freeze, '\&\&'.freeze).gsub("'".freeze, "''".freeze) # ' (for ruby-mode)
64
+ end
65
+
66
+ # Quotes the column name. Defaults to no quoting.
67
+ def quote_column_name(column_name)
68
+ column_name.to_s
69
+ end
70
+
71
+ # Quotes the table name. Defaults to column name quoting.
72
+ def quote_table_name(table_name)
73
+ quote_column_name(table_name)
74
+ end
75
+
76
+ # Override to return the quoted table name for assignment. Defaults to
77
+ # table quoting.
78
+ #
79
+ # This works for mysql2 where table.column can be used to
80
+ # resolve ambiguity.
81
+ #
82
+ # We override this in the sqlite3 and postgresql adapters to use only
83
+ # the column name (as per syntax requirements).
84
+ def quote_table_name_for_assignment(table, attr)
85
+ quote_table_name("#{table}.#{attr}")
86
+ end
87
+
88
+ def quote_default_expression(value, column) # :nodoc:
89
+ if value.is_a?(Proc)
90
+ value.call
91
+ else
92
+ value = lookup_cast_type(column.sql_type).serialize(value)
93
+ quote(value)
94
+ end
95
+ end
96
+
97
+ def quoted_true
98
+ "TRUE".freeze
99
+ end
100
+
101
+ def unquoted_true
102
+ true
103
+ end
104
+
105
+ def quoted_false
106
+ "FALSE".freeze
107
+ end
108
+
109
+ def unquoted_false
110
+ false
111
+ end
112
+
113
+ # Quote date/time values for use in SQL input. Includes microseconds
114
+ # if the value is a Time responding to usec.
115
+ def quoted_date(value)
116
+ if value.acts_like?(:time)
117
+ zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
118
+
119
+ if value.respond_to?(zone_conversion_method)
120
+ value = value.send(zone_conversion_method)
121
+ end
122
+ end
123
+
124
+ result = value.to_s(:db)
125
+ if value.respond_to?(:usec) && value.usec > 0
126
+ "#{result}.#{sprintf("%06d", value.usec)}"
127
+ else
128
+ result
129
+ end
130
+ end
131
+
132
+ def quoted_time(value) # :nodoc:
133
+ value = value.change(year: 2000, month: 1, day: 1)
134
+ quoted_date(value).sub(/\A\d\d\d\d-\d\d-\d\d /, "")
135
+ end
136
+
137
+ def quoted_binary(value) # :nodoc:
138
+ "'#{quote_string(value.to_s)}'"
139
+ end
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
147
+ end
148
+
149
+ private
150
+ def lookup_cast_type(sql_type)
151
+ type_map.lookup(sql_type)
152
+ end
153
+
154
+ def id_value_for_database(value)
155
+ if primary_key = value.class.primary_key
156
+ value.instance_variable_get(:@attributes)[primary_key].value_for_database
157
+ end
158
+ end
159
+
160
+ def types_which_need_no_typecasting
161
+ [nil, Numeric, String]
162
+ end
163
+
164
+ def _quote(value)
165
+ case value
166
+ when String, ActiveSupport::Multibyte::Chars
167
+ "'#{quote_string(value.to_s)}'"
168
+ when true then quoted_true
169
+ when false then quoted_false
170
+ when nil then "NULL"
171
+ # BigDecimals need to be put in a non-normalized form and quoted.
172
+ when BigDecimal then value.to_s("F")
173
+ when Numeric, ActiveSupport::Duration then value.to_s
174
+ when Type::Binary::Data then quoted_binary(value)
175
+ when Type::Time::Value then "'#{quoted_time(value)}'"
176
+ when Date, Time then "'#{quoted_date(value)}'"
177
+ when Symbol then "'#{quote_string(value.to_s)}'"
178
+ when Class then "'#{value}'"
179
+ else raise TypeError, "can't quote #{value.class.name}"
180
+ end
181
+ end
182
+
183
+ def _type_cast(value)
184
+ case value
185
+ when Symbol, ActiveSupport::Multibyte::Chars, Type::Binary::Data
186
+ value.to_s
187
+ when true then unquoted_true
188
+ when false then unquoted_false
189
+ # BigDecimals need to be put in a non-normalized form and quoted.
190
+ when BigDecimal then value.to_s("F")
191
+ when Type::Time::Value then quoted_time(value)
192
+ when Date, Time then quoted_date(value)
193
+ when *types_which_need_no_typecasting
194
+ value
195
+ else raise TypeError
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module Savepoints
6
+ def current_savepoint_name
7
+ current_transaction.savepoint_name
8
+ end
9
+
10
+ def create_savepoint(name = current_savepoint_name)
11
+ execute("SAVEPOINT #{name}")
12
+ end
13
+
14
+ def exec_rollback_to_savepoint(name = current_savepoint_name)
15
+ execute("ROLLBACK TO SAVEPOINT #{name}")
16
+ end
17
+
18
+ def release_savepoint(name = current_savepoint_name)
19
+ execute("RELEASE SAVEPOINT #{name}")
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string/strip"
4
+
5
+ module ActiveRecord
6
+ module ConnectionAdapters
7
+ class AbstractAdapter
8
+ class SchemaCreation # :nodoc:
9
+ def initialize(conn)
10
+ @conn = conn
11
+ @cache = {}
12
+ end
13
+
14
+ def accept(o)
15
+ m = @cache[o.class] ||= "visit_#{o.class.name.split('::').last}"
16
+ send m, o
17
+ end
18
+
19
+ 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
23
+
24
+ private
25
+
26
+ def visit_AlterTable(o)
27
+ sql = "ALTER TABLE #{quote_table_name(o.name)} ".dup
28
+ sql << o.adds.map { |col| accept col }.join(" ")
29
+ sql << o.foreign_key_adds.map { |fk| visit_AddForeignKey fk }.join(" ")
30
+ sql << o.foreign_key_drops.map { |fk| visit_DropForeignKey fk }.join(" ")
31
+ end
32
+
33
+ def visit_ColumnDefinition(o)
34
+ o.sql_type = type_to_sql(o.type, o.options)
35
+ column_sql = "#{quote_column_name(o.name)} #{o.sql_type}".dup
36
+ add_column_options!(column_sql, column_options(o)) unless o.type == :primary_key
37
+ column_sql
38
+ end
39
+
40
+ def visit_AddColumnDefinition(o)
41
+ "ADD #{accept(o.column)}".dup
42
+ end
43
+
44
+ def visit_TableDefinition(o)
45
+ create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE #{quote_table_name(o.name)} ".dup
46
+
47
+ statements = o.columns.map { |c| accept c }
48
+ statements << accept(o.primary_keys) if o.primary_keys
49
+
50
+ if supports_indexes_in_create?
51
+ statements.concat(o.indexes.map { |column_name, options| index_in_create(o.name, column_name, options) })
52
+ end
53
+
54
+ if supports_foreign_keys_in_create?
55
+ statements.concat(o.foreign_keys.map { |to_table, options| foreign_key_in_create(o.name, to_table, options) })
56
+ end
57
+
58
+ create_sql << "(#{statements.join(', ')})" if statements.present?
59
+ add_table_options!(create_sql, table_options(o))
60
+ create_sql << " AS #{to_sql(o.as)}" if o.as
61
+ create_sql
62
+ end
63
+
64
+ def visit_PrimaryKeyDefinition(o)
65
+ "PRIMARY KEY (#{o.name.map { |name| quote_column_name(name) }.join(', ')})"
66
+ end
67
+
68
+ def visit_ForeignKeyDefinition(o)
69
+ sql = <<-SQL.strip_heredoc
70
+ CONSTRAINT #{quote_column_name(o.name)}
71
+ FOREIGN KEY (#{quote_column_name(o.column)})
72
+ REFERENCES #{quote_table_name(o.to_table)} (#{quote_column_name(o.primary_key)})
73
+ SQL
74
+ sql << " #{action_sql('DELETE', o.on_delete)}" if o.on_delete
75
+ sql << " #{action_sql('UPDATE', o.on_update)}" if o.on_update
76
+ sql
77
+ end
78
+
79
+ def visit_AddForeignKey(o)
80
+ "ADD #{accept(o)}"
81
+ end
82
+
83
+ def visit_DropForeignKey(name)
84
+ "DROP CONSTRAINT #{quote_column_name(name)}"
85
+ end
86
+
87
+ def table_options(o)
88
+ table_options = {}
89
+ table_options[:comment] = o.comment
90
+ table_options[:options] = o.options
91
+ table_options
92
+ end
93
+
94
+ def add_table_options!(create_sql, options)
95
+ if options_sql = options[:options]
96
+ create_sql << " #{options_sql}"
97
+ end
98
+ create_sql
99
+ end
100
+
101
+ def column_options(o)
102
+ o.options.merge(column: o)
103
+ end
104
+
105
+ def add_column_options!(sql, options)
106
+ sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options)
107
+ # must explicitly check for :null to allow change_column to work on migrations
108
+ if options[:null] == false
109
+ sql << " NOT NULL"
110
+ end
111
+ if options[:auto_increment] == true
112
+ sql << " AUTO_INCREMENT"
113
+ end
114
+ if options[:primary_key] == true
115
+ sql << " PRIMARY KEY"
116
+ end
117
+ sql
118
+ end
119
+
120
+ def to_sql(sql)
121
+ sql = sql.to_sql if sql.respond_to?(:to_sql)
122
+ sql
123
+ end
124
+
125
+ def foreign_key_in_create(from_table, to_table, options)
126
+ options = foreign_key_options(from_table, to_table, options)
127
+ accept ForeignKeyDefinition.new(from_table, to_table, options)
128
+ end
129
+
130
+ def action_sql(action, dependency)
131
+ case dependency
132
+ when :nullify then "ON #{action} SET NULL"
133
+ when :cascade then "ON #{action} CASCADE"
134
+ when :restrict then "ON #{action} RESTRICT"
135
+ else
136
+ raise ArgumentError, <<-MSG.strip_heredoc
137
+ '#{dependency}' is not supported for :on_update or :on_delete.
138
+ Supported values are: :nullify, :cascade, :restrict
139
+ MSG
140
+ end
141
+ end
142
+ end
143
+ end
144
+ SchemaCreation = AbstractAdapter::SchemaCreation # :nodoc:
145
+ end
146
+ end