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,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ # :stopdoc:
5
+ module ConnectionAdapters
6
+ # An abstract definition of a column in a table.
7
+ class Column
8
+ attr_reader :name, :default, :sql_type_metadata, :null, :table_name, :default_function, :collation, :comment
9
+
10
+ delegate :precision, :scale, :limit, :type, :sql_type, to: :sql_type_metadata, allow_nil: true
11
+
12
+ # Instantiates a new column in the table.
13
+ #
14
+ # +name+ is the column's name, such as <tt>supplier_id</tt> in <tt>supplier_id bigint</tt>.
15
+ # +default+ is the type-casted default value, such as +new+ in <tt>sales_stage varchar(20) default 'new'</tt>.
16
+ # +sql_type_metadata+ is various information about the type of the column
17
+ # +null+ determines if this column allows +NULL+ values.
18
+ def initialize(name, default, sql_type_metadata = nil, null = true, table_name = nil, default_function = nil, collation = nil, comment: nil, **)
19
+ @name = name.freeze
20
+ @table_name = table_name
21
+ @sql_type_metadata = sql_type_metadata
22
+ @null = null
23
+ @default = default
24
+ @default_function = default_function
25
+ @collation = collation
26
+ @comment = comment
27
+ end
28
+
29
+ def has_default?
30
+ !default.nil? || default_function
31
+ end
32
+
33
+ def bigint?
34
+ /\Abigint\b/.match?(sql_type)
35
+ end
36
+
37
+ # Returns the human name of the column name.
38
+ #
39
+ # ===== Examples
40
+ # Column.new('sales_stage', ...).human_name # => 'Sales stage'
41
+ def human_name
42
+ Base.human_attribute_name(@name)
43
+ end
44
+
45
+ def init_with(coder)
46
+ @name = coder["name"]
47
+ @table_name = coder["table_name"]
48
+ @sql_type_metadata = coder["sql_type_metadata"]
49
+ @null = coder["null"]
50
+ @default = coder["default"]
51
+ @default_function = coder["default_function"]
52
+ @collation = coder["collation"]
53
+ @comment = coder["comment"]
54
+ end
55
+
56
+ def encode_with(coder)
57
+ coder["name"] = @name
58
+ coder["table_name"] = @table_name
59
+ coder["sql_type_metadata"] = @sql_type_metadata
60
+ coder["null"] = @null
61
+ coder["default"] = @default
62
+ coder["default_function"] = @default_function
63
+ coder["collation"] = @collation
64
+ coder["comment"] = @comment
65
+ end
66
+
67
+ def ==(other)
68
+ other.is_a?(Column) &&
69
+ attributes_for_hash == other.attributes_for_hash
70
+ end
71
+ alias :eql? :==
72
+
73
+ def hash
74
+ attributes_for_hash.hash
75
+ end
76
+
77
+ protected
78
+
79
+ def attributes_for_hash
80
+ [self.class, name, default, sql_type_metadata, null, table_name, default_function, collation]
81
+ end
82
+ end
83
+
84
+ class NullColumn < Column
85
+ def initialize(name)
86
+ super(name, nil)
87
+ end
88
+ end
89
+ end
90
+ # :startdoc:
91
+ end
@@ -0,0 +1,287 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+
5
+ module ActiveRecord
6
+ module ConnectionAdapters
7
+ class ConnectionSpecification #:nodoc:
8
+ attr_reader :name, :config, :adapter_method
9
+
10
+ def initialize(name, config, adapter_method)
11
+ @name, @config, @adapter_method = name, config, adapter_method
12
+ end
13
+
14
+ def initialize_dup(original)
15
+ @config = original.config.dup
16
+ end
17
+
18
+ def to_hash
19
+ @config.merge(name: @name)
20
+ end
21
+
22
+ # Expands a connection string into a hash.
23
+ class ConnectionUrlResolver # :nodoc:
24
+ # == Example
25
+ #
26
+ # url = "postgresql://foo:bar@localhost:9000/foo_test?pool=5&timeout=3000"
27
+ # ConnectionUrlResolver.new(url).to_hash
28
+ # # => {
29
+ # "adapter" => "postgresql",
30
+ # "host" => "localhost",
31
+ # "port" => 9000,
32
+ # "database" => "foo_test",
33
+ # "username" => "foo",
34
+ # "password" => "bar",
35
+ # "pool" => "5",
36
+ # "timeout" => "3000"
37
+ # }
38
+ def initialize(url)
39
+ raise "Database URL cannot be empty" if url.blank?
40
+ @uri = uri_parser.parse(url)
41
+ @adapter = @uri.scheme && @uri.scheme.tr("-", "_")
42
+ @adapter = "postgresql" if @adapter == "postgres"
43
+
44
+ if @uri.opaque
45
+ @uri.opaque, @query = @uri.opaque.split("?", 2)
46
+ else
47
+ @query = @uri.query
48
+ end
49
+ end
50
+
51
+ # Converts the given URL to a full connection hash.
52
+ def to_hash
53
+ config = raw_config.reject { |_, value| value.blank? }
54
+ config.map { |key, value| config[key] = uri_parser.unescape(value) if value.is_a? String }
55
+ config
56
+ end
57
+
58
+ private
59
+
60
+ def uri
61
+ @uri
62
+ end
63
+
64
+ def uri_parser
65
+ @uri_parser ||= URI::Parser.new
66
+ end
67
+
68
+ # Converts the query parameters of the URI into a hash.
69
+ #
70
+ # "localhost?pool=5&reaping_frequency=2"
71
+ # # => { "pool" => "5", "reaping_frequency" => "2" }
72
+ #
73
+ # returns empty hash if no query present.
74
+ #
75
+ # "localhost"
76
+ # # => {}
77
+ def query_hash
78
+ Hash[(@query || "").split("&").map { |pair| pair.split("=") }]
79
+ end
80
+
81
+ def raw_config
82
+ if uri.opaque
83
+ query_hash.merge(
84
+ "adapter" => @adapter,
85
+ "database" => uri.opaque)
86
+ else
87
+ query_hash.merge(
88
+ "adapter" => @adapter,
89
+ "username" => uri.user,
90
+ "password" => uri.password,
91
+ "port" => uri.port,
92
+ "database" => database_from_path,
93
+ "host" => uri.hostname)
94
+ end
95
+ end
96
+
97
+ # Returns name of the database.
98
+ def database_from_path
99
+ if @adapter == "sqlite3"
100
+ # 'sqlite3:/foo' is absolute, because that makes sense. The
101
+ # corresponding relative version, 'sqlite3:foo', is handled
102
+ # elsewhere, as an "opaque".
103
+
104
+ uri.path
105
+ else
106
+ # Only SQLite uses a filename as the "database" name; for
107
+ # anything else, a leading slash would be silly.
108
+
109
+ uri.path.sub(%r{^/}, "")
110
+ end
111
+ end
112
+ end
113
+
114
+ ##
115
+ # Builds a ConnectionSpecification from user input.
116
+ class Resolver # :nodoc:
117
+ attr_reader :configurations
118
+
119
+ # Accepts a hash two layers deep, keys on the first layer represent
120
+ # environments such as "production". Keys must be strings.
121
+ def initialize(configurations)
122
+ @configurations = configurations
123
+ end
124
+
125
+ # Returns a hash with database connection information.
126
+ #
127
+ # == Examples
128
+ #
129
+ # Full hash Configuration.
130
+ #
131
+ # configurations = { "production" => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" } }
132
+ # Resolver.new(configurations).resolve(:production)
133
+ # # => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3"}
134
+ #
135
+ # Initialized with URL configuration strings.
136
+ #
137
+ # configurations = { "production" => "postgresql://localhost/foo" }
138
+ # Resolver.new(configurations).resolve(:production)
139
+ # # => { "host" => "localhost", "database" => "foo", "adapter" => "postgresql" }
140
+ #
141
+ def resolve(config)
142
+ if config
143
+ resolve_connection config
144
+ elsif env = ActiveRecord::ConnectionHandling::RAILS_ENV.call
145
+ resolve_symbol_connection env.to_sym
146
+ else
147
+ raise AdapterNotSpecified
148
+ end
149
+ end
150
+
151
+ # Expands each key in @configurations hash into fully resolved hash
152
+ def resolve_all
153
+ config = configurations.dup
154
+
155
+ if env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
156
+ env_config = config[env] if config[env].is_a?(Hash) && !(config[env].key?("adapter") || config[env].key?("url"))
157
+ end
158
+
159
+ config.reject! { |k, v| v.is_a?(Hash) && !(v.key?("adapter") || v.key?("url")) }
160
+ config.merge! env_config if env_config
161
+
162
+ config.each do |key, value|
163
+ config[key] = resolve(value) if value
164
+ end
165
+
166
+ config
167
+ end
168
+
169
+ # Returns an instance of ConnectionSpecification for a given adapter.
170
+ # Accepts a hash one layer deep that contains all connection information.
171
+ #
172
+ # == Example
173
+ #
174
+ # config = { "production" => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" } }
175
+ # spec = Resolver.new(config).spec(:production)
176
+ # spec.adapter_method
177
+ # # => "sqlite3_connection"
178
+ # spec.config
179
+ # # => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" }
180
+ #
181
+ def spec(config)
182
+ spec = resolve(config).symbolize_keys
183
+
184
+ raise(AdapterNotSpecified, "database configuration does not specify adapter") unless spec.key?(:adapter)
185
+
186
+ # Require the adapter itself and give useful feedback about
187
+ # 1. Missing adapter gems and
188
+ # 2. Adapter gems' missing dependencies.
189
+ path_to_adapter = "active_record/connection_adapters/#{spec[:adapter]}_adapter"
190
+ begin
191
+ require path_to_adapter
192
+ rescue LoadError => e
193
+ # We couldn't require the adapter itself. Raise an exception that
194
+ # points out config typos and missing gems.
195
+ if e.path == path_to_adapter
196
+ # We can assume that a non-builtin adapter was specified, so it's
197
+ # either misspelled or missing from Gemfile.
198
+ raise LoadError, "Could not load the '#{spec[:adapter]}' Active Record adapter. Ensure that the adapter is spelled correctly in config/database.yml and that you've added the necessary adapter gem to your Gemfile.", e.backtrace
199
+
200
+ # Bubbled up from the adapter require. Prefix the exception message
201
+ # with some guidance about how to address it and reraise.
202
+ else
203
+ raise LoadError, "Error loading the '#{spec[:adapter]}' Active Record adapter. Missing a gem it depends on? #{e.message}", e.backtrace
204
+ end
205
+ end
206
+
207
+ adapter_method = "#{spec[:adapter]}_connection"
208
+
209
+ unless ActiveRecord::Base.respond_to?(adapter_method)
210
+ raise AdapterNotFound, "database configuration specifies nonexistent #{spec.config[:adapter]} adapter"
211
+ end
212
+
213
+ ConnectionSpecification.new(spec.delete(:name) || "primary", spec, adapter_method)
214
+ end
215
+
216
+ private
217
+
218
+ # Returns fully resolved connection, accepts hash, string or symbol.
219
+ # Always returns a hash.
220
+ #
221
+ # == Examples
222
+ #
223
+ # Symbol representing current environment.
224
+ #
225
+ # Resolver.new("production" => {}).resolve_connection(:production)
226
+ # # => {}
227
+ #
228
+ # One layer deep hash of connection values.
229
+ #
230
+ # Resolver.new({}).resolve_connection("adapter" => "sqlite3")
231
+ # # => { "adapter" => "sqlite3" }
232
+ #
233
+ # Connection URL.
234
+ #
235
+ # Resolver.new({}).resolve_connection("postgresql://localhost/foo")
236
+ # # => { "host" => "localhost", "database" => "foo", "adapter" => "postgresql" }
237
+ #
238
+ def resolve_connection(spec)
239
+ case spec
240
+ when Symbol
241
+ resolve_symbol_connection spec
242
+ when String
243
+ resolve_url_connection spec
244
+ when Hash
245
+ resolve_hash_connection spec
246
+ end
247
+ end
248
+
249
+ # Takes the environment such as +:production+ or +:development+.
250
+ # This requires that the @configurations was initialized with a key that
251
+ # matches.
252
+ #
253
+ # Resolver.new("production" => {}).resolve_symbol_connection(:production)
254
+ # # => {}
255
+ #
256
+ def resolve_symbol_connection(spec)
257
+ if config = configurations[spec.to_s]
258
+ resolve_connection(config).merge("name" => spec.to_s)
259
+ else
260
+ raise(AdapterNotSpecified, "'#{spec}' database is not configured. Available: #{configurations.keys.inspect}")
261
+ end
262
+ end
263
+
264
+ # Accepts a hash. Expands the "url" key that contains a
265
+ # URL database connection to a full connection
266
+ # hash and merges with the rest of the hash.
267
+ # Connection details inside of the "url" key win any merge conflicts
268
+ def resolve_hash_connection(spec)
269
+ if spec["url"] && spec["url"] !~ /^jdbc:/
270
+ connection_hash = resolve_url_connection(spec.delete("url"))
271
+ spec.merge!(connection_hash)
272
+ end
273
+ spec
274
+ end
275
+
276
+ # Takes a connection URL.
277
+ #
278
+ # Resolver.new({}).resolve_url_connection("postgresql://localhost/foo")
279
+ # # => { "host" => "localhost", "database" => "foo", "adapter" => "postgresql" }
280
+ #
281
+ def resolve_url_connection(url)
282
+ ConnectionUrlResolver.new(url).to_hash
283
+ end
284
+ end
285
+ end
286
+ end
287
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module DetermineIfPreparableVisitor
6
+ attr_accessor :preparable
7
+
8
+ def accept(*)
9
+ @preparable = true
10
+ super
11
+ end
12
+
13
+ def visit_Arel_Nodes_In(o, collector)
14
+ @preparable = false
15
+
16
+ if Array === o.right && !o.right.empty?
17
+ o.right.delete_if do |bind|
18
+ if Arel::Nodes::BindParam === bind && Relation::QueryAttribute === bind.value
19
+ !bind.value.boundable?
20
+ end
21
+ end
22
+ end
23
+
24
+ super
25
+ end
26
+
27
+ def visit_Arel_Nodes_SqlLiteral(*)
28
+ @preparable = false
29
+ super
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module MySQL
6
+ class Column < ConnectionAdapters::Column # :nodoc:
7
+ delegate :extra, to: :sql_type_metadata, allow_nil: true
8
+
9
+ def unsigned?
10
+ /\bunsigned(?: zerofill)?\z/.match?(sql_type)
11
+ end
12
+
13
+ def case_sensitive?
14
+ collation && !/_ci\z/.match?(collation)
15
+ end
16
+
17
+ def auto_increment?
18
+ extra == "auto_increment"
19
+ end
20
+
21
+ def virtual?
22
+ /\b(?:VIRTUAL|STORED|PERSISTENT)\b/.match?(extra)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module MySQL
6
+ module DatabaseStatements
7
+ # Returns an ActiveRecord::Result instance.
8
+ def select_all(*) # :nodoc:
9
+ result = if ExplainRegistry.collect? && prepared_statements
10
+ unprepared_statement { super }
11
+ else
12
+ super
13
+ end
14
+ discard_remaining_results
15
+ result
16
+ end
17
+
18
+ def query(sql, name = nil) # :nodoc:
19
+ execute(sql, name).to_a
20
+ end
21
+
22
+ # Executes the SQL statement in the context of this connection.
23
+ def execute(sql, name = nil)
24
+ # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
25
+ # made since we established the connection
26
+ @connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
27
+
28
+ super
29
+ end
30
+
31
+ def exec_query(sql, name = "SQL", binds = [], prepare: false)
32
+ if without_prepared_statement?(binds)
33
+ execute_and_free(sql, name) do |result|
34
+ ActiveRecord::Result.new(result.fields, result.to_a) if result
35
+ end
36
+ else
37
+ exec_stmt_and_free(sql, name, binds, cache_stmt: prepare) do |_, result|
38
+ ActiveRecord::Result.new(result.fields, result.to_a) if result
39
+ end
40
+ end
41
+ end
42
+
43
+ def exec_delete(sql, name = nil, binds = [])
44
+ if without_prepared_statement?(binds)
45
+ execute_and_free(sql, name) { @connection.affected_rows }
46
+ else
47
+ exec_stmt_and_free(sql, name, binds) { |stmt| stmt.affected_rows }
48
+ end
49
+ end
50
+ alias :exec_update :exec_delete
51
+
52
+ private
53
+ def default_insert_value(column)
54
+ Arel.sql("DEFAULT") unless column.auto_increment?
55
+ end
56
+
57
+ def last_inserted_id(result)
58
+ @connection.last_id
59
+ end
60
+
61
+ def discard_remaining_results
62
+ @connection.next_result while @connection.more_results?
63
+ end
64
+
65
+ def supports_set_server_option?
66
+ @connection.respond_to?(:set_server_option)
67
+ end
68
+
69
+ def multi_statements_enabled?(flags)
70
+ if flags.is_a?(Array)
71
+ flags.include?("MULTI_STATEMENTS")
72
+ else
73
+ (flags & Mysql2::Client::MULTI_STATEMENTS) != 0
74
+ end
75
+ end
76
+
77
+ def with_multi_statements
78
+ previous_flags = @config[:flags]
79
+
80
+ unless multi_statements_enabled?(previous_flags)
81
+ if supports_set_server_option?
82
+ @connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_ON)
83
+ else
84
+ @config[:flags] = Mysql2::Client::MULTI_STATEMENTS
85
+ reconnect!
86
+ end
87
+ end
88
+
89
+ yield
90
+ ensure
91
+ unless multi_statements_enabled?(previous_flags)
92
+ if supports_set_server_option?
93
+ @connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)
94
+ else
95
+ @config[:flags] = previous_flags
96
+ reconnect!
97
+ end
98
+ end
99
+ end
100
+
101
+ def exec_stmt_and_free(sql, name, binds, cache_stmt: false)
102
+ # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
103
+ # made since we established the connection
104
+ @connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
105
+
106
+ type_casted_binds = type_casted_binds(binds)
107
+
108
+ log(sql, name, binds, type_casted_binds) do
109
+ if cache_stmt
110
+ cache = @statements[sql] ||= {
111
+ stmt: @connection.prepare(sql)
112
+ }
113
+ stmt = cache[:stmt]
114
+ else
115
+ stmt = @connection.prepare(sql)
116
+ end
117
+
118
+ begin
119
+ result = ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
120
+ stmt.execute(*type_casted_binds)
121
+ end
122
+ rescue Mysql2::Error => e
123
+ if cache_stmt
124
+ @statements.delete(sql)
125
+ else
126
+ stmt.close
127
+ end
128
+ raise e
129
+ end
130
+
131
+ ret = yield stmt, result
132
+ result.free if result
133
+ stmt.close unless cache_stmt
134
+ ret
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end