activerecord 3.2.22.5 → 5.2.8

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 (275) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +657 -621
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +41 -46
  5. data/examples/performance.rb +55 -42
  6. data/examples/simple.rb +6 -5
  7. data/lib/active_record/aggregations.rb +264 -236
  8. data/lib/active_record/association_relation.rb +40 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -42
  10. data/lib/active_record/associations/association.rb +127 -75
  11. data/lib/active_record/associations/association_scope.rb +126 -92
  12. data/lib/active_record/associations/belongs_to_association.rb +78 -27
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +9 -4
  14. data/lib/active_record/associations/builder/association.rb +117 -32
  15. data/lib/active_record/associations/builder/belongs_to.rb +135 -60
  16. data/lib/active_record/associations/builder/collection_association.rb +61 -54
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +120 -42
  18. data/lib/active_record/associations/builder/has_many.rb +10 -64
  19. data/lib/active_record/associations/builder/has_one.rb +19 -51
  20. data/lib/active_record/associations/builder/singular_association.rb +28 -18
  21. data/lib/active_record/associations/collection_association.rb +226 -293
  22. data/lib/active_record/associations/collection_proxy.rb +1067 -69
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +83 -47
  25. data/lib/active_record/associations/has_many_through_association.rb +98 -65
  26. data/lib/active_record/associations/has_one_association.rb +57 -20
  27. data/lib/active_record/associations/has_one_through_association.rb +18 -9
  28. data/lib/active_record/associations/join_dependency/join_association.rb +48 -126
  29. data/lib/active_record/associations/join_dependency/join_base.rb +11 -12
  30. data/lib/active_record/associations/join_dependency/join_part.rb +35 -42
  31. data/lib/active_record/associations/join_dependency.rb +212 -164
  32. data/lib/active_record/associations/preloader/association.rb +95 -89
  33. data/lib/active_record/associations/preloader/through_association.rb +84 -44
  34. data/lib/active_record/associations/preloader.rb +123 -111
  35. data/lib/active_record/associations/singular_association.rb +33 -24
  36. data/lib/active_record/associations/through_association.rb +60 -26
  37. data/lib/active_record/associations.rb +1759 -1506
  38. data/lib/active_record/attribute_assignment.rb +60 -193
  39. data/lib/active_record/attribute_decorators.rb +90 -0
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +55 -8
  41. data/lib/active_record/attribute_methods/dirty.rb +113 -74
  42. data/lib/active_record/attribute_methods/primary_key.rb +106 -77
  43. data/lib/active_record/attribute_methods/query.rb +8 -5
  44. data/lib/active_record/attribute_methods/read.rb +63 -114
  45. data/lib/active_record/attribute_methods/serialization.rb +60 -90
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +69 -43
  47. data/lib/active_record/attribute_methods/write.rb +43 -45
  48. data/lib/active_record/attribute_methods.rb +366 -149
  49. data/lib/active_record/attributes.rb +266 -0
  50. data/lib/active_record/autosave_association.rb +312 -225
  51. data/lib/active_record/base.rb +114 -505
  52. data/lib/active_record/callbacks.rb +145 -67
  53. data/lib/active_record/coders/json.rb +15 -0
  54. data/lib/active_record/coders/yaml_column.rb +32 -23
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +883 -284
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +16 -2
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +350 -200
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -27
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +150 -65
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +146 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +477 -284
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +95 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1100 -310
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +283 -0
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +450 -118
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +657 -446
  69. data/lib/active_record/connection_adapters/column.rb +50 -255
  70. data/lib/active_record/connection_adapters/connection_specification.rb +287 -0
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +59 -210
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +44 -0
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +163 -0
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +56 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +111 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +168 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +206 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +774 -0
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +620 -1080
  117. data/lib/active_record/connection_adapters/schema_cache.rb +85 -36
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +545 -27
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +145 -0
  128. data/lib/active_record/core.rb +559 -0
  129. data/lib/active_record/counter_cache.rb +200 -105
  130. data/lib/active_record/define_callbacks.rb +22 -0
  131. data/lib/active_record/dynamic_matchers.rb +107 -69
  132. data/lib/active_record/enum.rb +244 -0
  133. data/lib/active_record/errors.rb +245 -60
  134. data/lib/active_record/explain.rb +35 -71
  135. data/lib/active_record/explain_registry.rb +32 -0
  136. data/lib/active_record/explain_subscriber.rb +18 -9
  137. data/lib/active_record/fixture_set/file.rb +82 -0
  138. data/lib/active_record/fixtures.rb +418 -275
  139. data/lib/active_record/gem_version.rb +17 -0
  140. data/lib/active_record/inheritance.rb +209 -100
  141. data/lib/active_record/integration.rb +116 -21
  142. data/lib/active_record/internal_metadata.rb +45 -0
  143. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  144. data/lib/active_record/locale/en.yml +9 -1
  145. data/lib/active_record/locking/optimistic.rb +107 -94
  146. data/lib/active_record/locking/pessimistic.rb +20 -8
  147. data/lib/active_record/log_subscriber.rb +99 -34
  148. data/lib/active_record/migration/command_recorder.rb +199 -64
  149. data/lib/active_record/migration/compatibility.rb +217 -0
  150. data/lib/active_record/migration/join_table.rb +17 -0
  151. data/lib/active_record/migration.rb +893 -296
  152. data/lib/active_record/model_schema.rb +328 -175
  153. data/lib/active_record/nested_attributes.rb +338 -242
  154. data/lib/active_record/no_touching.rb +58 -0
  155. data/lib/active_record/null_relation.rb +68 -0
  156. data/lib/active_record/persistence.rb +557 -170
  157. data/lib/active_record/query_cache.rb +14 -43
  158. data/lib/active_record/querying.rb +36 -24
  159. data/lib/active_record/railtie.rb +147 -52
  160. data/lib/active_record/railties/console_sandbox.rb +5 -4
  161. data/lib/active_record/railties/controller_runtime.rb +13 -6
  162. data/lib/active_record/railties/databases.rake +206 -488
  163. data/lib/active_record/readonly_attributes.rb +4 -6
  164. data/lib/active_record/reflection.rb +734 -228
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  166. data/lib/active_record/relation/batches.rb +249 -52
  167. data/lib/active_record/relation/calculations.rb +330 -284
  168. data/lib/active_record/relation/delegation.rb +135 -37
  169. data/lib/active_record/relation/finder_methods.rb +450 -287
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +193 -0
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  173. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  174. data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
  179. data/lib/active_record/relation/predicate_builder.rb +132 -43
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +1037 -221
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +48 -151
  184. data/lib/active_record/relation/where_clause.rb +186 -0
  185. data/lib/active_record/relation/where_clause_factory.rb +34 -0
  186. data/lib/active_record/relation.rb +451 -359
  187. data/lib/active_record/result.rb +129 -20
  188. data/lib/active_record/runtime_registry.rb +24 -0
  189. data/lib/active_record/sanitization.rb +164 -136
  190. data/lib/active_record/schema.rb +31 -19
  191. data/lib/active_record/schema_dumper.rb +154 -107
  192. data/lib/active_record/schema_migration.rb +56 -0
  193. data/lib/active_record/scoping/default.rb +108 -98
  194. data/lib/active_record/scoping/named.rb +125 -112
  195. data/lib/active_record/scoping.rb +77 -123
  196. data/lib/active_record/secure_token.rb +40 -0
  197. data/lib/active_record/serialization.rb +10 -6
  198. data/lib/active_record/statement_cache.rb +121 -0
  199. data/lib/active_record/store.rb +175 -16
  200. data/lib/active_record/suppressor.rb +61 -0
  201. data/lib/active_record/table_metadata.rb +82 -0
  202. data/lib/active_record/tasks/database_tasks.rb +337 -0
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +143 -0
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +83 -0
  206. data/lib/active_record/timestamp.rb +80 -41
  207. data/lib/active_record/touch_later.rb +64 -0
  208. data/lib/active_record/transactions.rb +240 -119
  209. data/lib/active_record/translation.rb +2 -0
  210. data/lib/active_record/type/adapter_specific_registry.rb +136 -0
  211. data/lib/active_record/type/date.rb +9 -0
  212. data/lib/active_record/type/date_time.rb +9 -0
  213. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  214. data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
  215. data/lib/active_record/type/internal/timezone.rb +17 -0
  216. data/lib/active_record/type/json.rb +30 -0
  217. data/lib/active_record/type/serialized.rb +71 -0
  218. data/lib/active_record/type/text.rb +11 -0
  219. data/lib/active_record/type/time.rb +21 -0
  220. data/lib/active_record/type/type_map.rb +62 -0
  221. data/lib/active_record/type/unsigned_integer.rb +17 -0
  222. data/lib/active_record/type.rb +79 -0
  223. data/lib/active_record/type_caster/connection.rb +33 -0
  224. data/lib/active_record/type_caster/map.rb +23 -0
  225. data/lib/active_record/type_caster.rb +9 -0
  226. data/lib/active_record/validations/absence.rb +25 -0
  227. data/lib/active_record/validations/associated.rb +35 -18
  228. data/lib/active_record/validations/length.rb +26 -0
  229. data/lib/active_record/validations/presence.rb +68 -0
  230. data/lib/active_record/validations/uniqueness.rb +133 -75
  231. data/lib/active_record/validations.rb +53 -43
  232. data/lib/active_record/version.rb +7 -7
  233. data/lib/active_record.rb +89 -57
  234. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  235. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  236. data/lib/rails/generators/active_record/migration/migration_generator.rb +61 -8
  237. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  238. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +46 -0
  239. data/lib/rails/generators/active_record/migration.rb +28 -8
  240. data/lib/rails/generators/active_record/model/model_generator.rb +23 -22
  241. data/lib/rails/generators/active_record/model/templates/model.rb.tt +13 -0
  242. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +1 -1
  243. data/lib/rails/generators/active_record.rb +10 -16
  244. metadata +141 -62
  245. data/examples/associations.png +0 -0
  246. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  247. data/lib/active_record/associations/join_helper.rb +0 -55
  248. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  249. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  250. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  251. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  252. data/lib/active_record/associations/preloader/has_many_through.rb +0 -15
  253. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  254. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  255. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  256. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  257. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  258. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -441
  259. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  260. data/lib/active_record/dynamic_finder_match.rb +0 -68
  261. data/lib/active_record/dynamic_scope_match.rb +0 -23
  262. data/lib/active_record/fixtures/file.rb +0 -65
  263. data/lib/active_record/identity_map.rb +0 -162
  264. data/lib/active_record/observer.rb +0 -121
  265. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  266. data/lib/active_record/serializers/xml_serializer.rb +0 -203
  267. data/lib/active_record/session_store.rb +0 -360
  268. data/lib/active_record/test_case.rb +0 -73
  269. data/lib/rails/generators/active_record/migration/templates/migration.rb +0 -34
  270. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
  271. data/lib/rails/generators/active_record/model/templates/model.rb +0 -12
  272. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  273. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  274. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  275. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,42 +1,60 @@
1
- require 'active_record/connection_adapters/abstract_mysql_adapter'
1
+ # frozen_string_literal: true
2
2
 
3
- gem 'mysql2', '~> 0.3.10'
4
- require 'mysql2'
3
+ require "active_record/connection_adapters/abstract_mysql_adapter"
4
+ require "active_record/connection_adapters/mysql/database_statements"
5
+
6
+ gem "mysql2", ">= 0.4.4", "< 0.6.0"
7
+ require "mysql2"
5
8
 
6
9
  module ActiveRecord
7
- class Base
10
+ module ConnectionHandling # :nodoc:
8
11
  # Establishes a connection to the database that's used by all Active Record objects.
9
- def self.mysql2_connection(config)
10
- config[:username] = 'root' if config[:username].nil?
12
+ def mysql2_connection(config)
13
+ config = config.symbolize_keys
14
+ config[:flags] ||= 0
11
15
 
12
- if Mysql2::Client.const_defined? :FOUND_ROWS
13
- config[:flags] = Mysql2::Client::FOUND_ROWS
16
+ if config[:flags].kind_of? Array
17
+ config[:flags].push "FOUND_ROWS".freeze
18
+ else
19
+ config[:flags] |= Mysql2::Client::FOUND_ROWS
14
20
  end
15
21
 
16
- client = Mysql2::Client.new(config.symbolize_keys)
17
- options = [config[:host], config[:username], config[:password], config[:database], config[:port], config[:socket], 0]
18
- ConnectionAdapters::Mysql2Adapter.new(client, logger, options, config)
22
+ client = Mysql2::Client.new(config)
23
+ ConnectionAdapters::Mysql2Adapter.new(client, logger, nil, config)
24
+ rescue Mysql2::Error => error
25
+ if error.message.include?("Unknown database")
26
+ raise ActiveRecord::NoDatabaseError
27
+ else
28
+ raise
29
+ end
19
30
  end
20
31
  end
21
32
 
22
33
  module ConnectionAdapters
23
34
  class Mysql2Adapter < AbstractMysqlAdapter
35
+ ADAPTER_NAME = "Mysql2".freeze
24
36
 
25
- class Column < AbstractMysqlAdapter::Column # :nodoc:
26
- def adapter
27
- Mysql2Adapter
28
- end
29
- end
30
-
31
- ADAPTER_NAME = 'Mysql2'
37
+ include MySQL::DatabaseStatements
32
38
 
33
39
  def initialize(connection, logger, connection_options, config)
34
40
  super
35
- @visitor = BindSubstitution.new self
41
+ @prepared_statements = false unless config.key?(:prepared_statements)
36
42
  configure_connection
37
43
  end
38
44
 
39
- def supports_explain?
45
+ def supports_json?
46
+ !mariadb? && version >= "5.7.8"
47
+ end
48
+
49
+ def supports_comments?
50
+ true
51
+ end
52
+
53
+ def supports_comments_in_create?
54
+ true
55
+ end
56
+
57
+ def supports_savepoints?
40
58
  true
41
59
  end
42
60
 
@@ -44,7 +62,7 @@ module ActiveRecord
44
62
 
45
63
  def each_hash(result) # :nodoc:
46
64
  if block_given?
47
- result.each(:as => :hash, :symbolize_keys => true) do |row|
65
+ result.each(as: :hash, symbolize_keys: true) do |row|
48
66
  yield row
49
67
  end
50
68
  else
@@ -52,229 +70,60 @@ module ActiveRecord
52
70
  end
53
71
  end
54
72
 
55
- def new_column(field, default, type, null, collation) # :nodoc:
56
- Column.new(field, default, type, null, collation)
57
- end
58
-
59
73
  def error_number(exception)
60
74
  exception.error_number if exception.respond_to?(:error_number)
61
75
  end
62
76
 
77
+ #--
63
78
  # QUOTING ==================================================
79
+ #++
64
80
 
65
81
  def quote_string(string)
66
82
  @connection.escape(string)
67
83
  end
68
84
 
85
+ #--
69
86
  # CONNECTION MANAGEMENT ====================================
87
+ #++
70
88
 
71
89
  def active?
72
- return false unless @connection
73
90
  @connection.ping
74
91
  end
75
92
 
76
93
  def reconnect!
94
+ super
77
95
  disconnect!
78
96
  connect
79
97
  end
98
+ alias :reset! :reconnect!
80
99
 
81
100
  # Disconnects from the database if already connected.
82
101
  # Otherwise, this method does nothing.
83
102
  def disconnect!
84
- unless @connection.nil?
85
- @connection.close
86
- @connection = nil
87
- end
88
- end
89
-
90
- def reset!
91
- disconnect!
92
- connect
103
+ super
104
+ @connection.close
93
105
  end
94
106
 
95
- # DATABASE STATEMENTS ======================================
96
-
97
- def explain(arel, binds = [])
98
- sql = "EXPLAIN #{to_sql(arel, binds.dup)}"
99
- start = Time.now
100
- result = exec_query(sql, 'EXPLAIN', binds)
101
- elapsed = Time.now - start
102
-
103
- ExplainPrettyPrinter.new.pp(result, elapsed)
107
+ def discard! # :nodoc:
108
+ @connection.automatic_close = false
109
+ @connection = nil
104
110
  end
105
111
 
106
- class ExplainPrettyPrinter # :nodoc:
107
- # Pretty prints the result of a EXPLAIN in a way that resembles the output of the
108
- # MySQL shell:
109
- #
110
- # +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
111
- # | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
112
- # +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
113
- # | 1 | SIMPLE | users | const | PRIMARY | PRIMARY | 4 | const | 1 | |
114
- # | 1 | SIMPLE | posts | ALL | NULL | NULL | NULL | NULL | 1 | Using where |
115
- # +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
116
- # 2 rows in set (0.00 sec)
117
- #
118
- # This is an exercise in Ruby hyperrealism :).
119
- def pp(result, elapsed)
120
- widths = compute_column_widths(result)
121
- separator = build_separator(widths)
122
-
123
- pp = []
124
-
125
- pp << separator
126
- pp << build_cells(result.columns, widths)
127
- pp << separator
128
-
129
- result.rows.each do |row|
130
- pp << build_cells(row, widths)
131
- end
132
-
133
- pp << separator
134
- pp << build_footer(result.rows.length, elapsed)
135
-
136
- pp.join("\n") + "\n"
137
- end
138
-
139
- private
140
-
141
- def compute_column_widths(result)
142
- [].tap do |widths|
143
- result.columns.each_with_index do |column, i|
144
- cells_in_column = [column] + result.rows.map {|r| r[i].nil? ? 'NULL' : r[i].to_s}
145
- widths << cells_in_column.map(&:length).max
146
- end
147
- end
148
- end
149
-
150
- def build_separator(widths)
151
- padding = 1
152
- '+' + widths.map {|w| '-' * (w + (padding*2))}.join('+') + '+'
153
- end
112
+ private
154
113
 
155
- def build_cells(items, widths)
156
- cells = []
157
- items.each_with_index do |item, i|
158
- item = 'NULL' if item.nil?
159
- justifier = item.is_a?(Numeric) ? 'rjust' : 'ljust'
160
- cells << item.to_s.send(justifier, widths[i])
161
- end
162
- '| ' + cells.join(' | ') + ' |'
114
+ def connect
115
+ @connection = Mysql2::Client.new(@config)
116
+ configure_connection
163
117
  end
164
118
 
165
- def build_footer(nrows, elapsed)
166
- rows_label = nrows == 1 ? 'row' : 'rows'
167
- "#{nrows} #{rows_label} in set (%.2f sec)" % elapsed
119
+ def configure_connection
120
+ @connection.query_options.merge!(as: :array)
121
+ super
168
122
  end
169
- end
170
123
 
171
- # FIXME: re-enable the following once a "better" query_cache solution is in core
172
- #
173
- # The overrides below perform much better than the originals in AbstractAdapter
174
- # because we're able to take advantage of mysql2's lazy-loading capabilities
175
- #
176
- # # Returns a record hash with the column names as keys and column values
177
- # # as values.
178
- # def select_one(sql, name = nil)
179
- # result = execute(sql, name)
180
- # result.each(:as => :hash) do |r|
181
- # return r
182
- # end
183
- # end
184
- #
185
- # # Returns a single value from a record
186
- # def select_value(sql, name = nil)
187
- # result = execute(sql, name)
188
- # if first = result.first
189
- # first.first
190
- # end
191
- # end
192
- #
193
- # # Returns an array of the values of the first column in a select:
194
- # # select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
195
- # def select_values(sql, name = nil)
196
- # execute(sql, name).map { |row| row.first }
197
- # end
198
-
199
- # Returns an array of arrays containing the field values.
200
- # Order is the same as that returned by +columns+.
201
- def select_rows(sql, name = nil)
202
- execute(sql, name).to_a
203
- end
204
-
205
- # Executes the SQL statement in the context of this connection.
206
- def execute(sql, name = nil)
207
- if @connection
208
- # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
209
- # made since we established the connection
210
- @connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
124
+ def full_version
125
+ @full_version ||= @connection.server_info[:version]
211
126
  end
212
-
213
- super
214
- end
215
-
216
- def exec_query(sql, name = 'SQL', binds = [])
217
- result = execute(sql, name)
218
- ActiveRecord::Result.new(result.fields, result.to_a)
219
- end
220
-
221
- alias exec_without_stmt exec_query
222
-
223
- # Returns an array of record hashes with the column names as keys and
224
- # column values as values.
225
- def select(sql, name = nil, binds = [])
226
- exec_query(sql, name).to_a
227
- end
228
-
229
- def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
230
- super
231
- id_value || @connection.last_id
232
- end
233
- alias :create :insert_sql
234
-
235
- def exec_insert(sql, name, binds)
236
- execute to_sql(sql, binds), name
237
- end
238
-
239
- def exec_delete(sql, name, binds)
240
- execute to_sql(sql, binds), name
241
- @connection.affected_rows
242
- end
243
- alias :exec_update :exec_delete
244
-
245
- def last_inserted_id(result)
246
- @connection.last_id
247
- end
248
-
249
- private
250
-
251
- def connect
252
- @connection = Mysql2::Client.new(@config)
253
- configure_connection
254
- end
255
-
256
- def configure_connection
257
- @connection.query_options.merge!(:as => :array)
258
-
259
- # By default, MySQL 'where id is null' selects the last inserted id.
260
- # Turn this off. http://dev.rubyonrails.org/ticket/6778
261
- variable_assignments = ['SQL_AUTO_IS_NULL=0']
262
- encoding = @config[:encoding]
263
-
264
- # make sure we set the encoding
265
- variable_assignments << "NAMES '#{encoding}'" if encoding
266
-
267
- # increase timeout so mysql server doesn't disconnect us
268
- wait_timeout = @config[:wait_timeout]
269
- wait_timeout = 2147483 unless wait_timeout.is_a?(Fixnum)
270
- variable_assignments << "@@wait_timeout = #{wait_timeout}"
271
-
272
- execute("SET #{variable_assignments.join(', ')}", :skip_logging)
273
- end
274
-
275
- def version
276
- @version ||= @connection.info[:version].scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
277
- end
278
127
  end
279
128
  end
280
129
  end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ # PostgreSQL-specific extensions to column definitions in a table.
6
+ class PostgreSQLColumn < Column #:nodoc:
7
+ delegate :array, :oid, :fmod, to: :sql_type_metadata
8
+ alias :array? :array
9
+
10
+ def initialize(*, max_identifier_length: 63, **)
11
+ super
12
+ @max_identifier_length = max_identifier_length
13
+ end
14
+
15
+ def serial?
16
+ return unless default_function
17
+
18
+ if %r{\Anextval\('"?(?<sequence_name>.+_(?<suffix>seq\d*))"?'::regclass\)\z} =~ default_function
19
+ sequence_name_from_parts(table_name, name, suffix) == sequence_name
20
+ end
21
+ end
22
+
23
+ protected
24
+ attr_reader :max_identifier_length
25
+
26
+ private
27
+ def sequence_name_from_parts(table_name, column_name, suffix)
28
+ over_length = [table_name, column_name, suffix].map(&:length).sum + 2 - max_identifier_length
29
+
30
+ if over_length > 0
31
+ column_name_length = [(max_identifier_length - suffix.length - 2) / 2, column_name.length].min
32
+ over_length -= column_name.length - column_name_length
33
+ column_name = column_name[0, column_name_length - [over_length, 0].min]
34
+ end
35
+
36
+ if over_length > 0
37
+ table_name = table_name[0, table_name.length - over_length]
38
+ end
39
+
40
+ "#{table_name}_#{column_name}_#{suffix}"
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module PostgreSQL
6
+ module DatabaseStatements
7
+ def explain(arel, binds = [])
8
+ sql = "EXPLAIN #{to_sql(arel, binds)}"
9
+ PostgreSQL::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", binds))
10
+ end
11
+
12
+ # The internal PostgreSQL identifier of the money data type.
13
+ MONEY_COLUMN_TYPE_OID = 790 #:nodoc:
14
+ # The internal PostgreSQL identifier of the BYTEA data type.
15
+ BYTEA_COLUMN_TYPE_OID = 17 #:nodoc:
16
+
17
+ # create a 2D array representing the result set
18
+ def result_as_array(res) #:nodoc:
19
+ # check if we have any binary column and if they need escaping
20
+ ftypes = Array.new(res.nfields) do |i|
21
+ [i, res.ftype(i)]
22
+ end
23
+
24
+ rows = res.values
25
+ return rows unless ftypes.any? { |_, x|
26
+ x == BYTEA_COLUMN_TYPE_OID || x == MONEY_COLUMN_TYPE_OID
27
+ }
28
+
29
+ typehash = ftypes.group_by { |_, type| type }
30
+ binaries = typehash[BYTEA_COLUMN_TYPE_OID] || []
31
+ monies = typehash[MONEY_COLUMN_TYPE_OID] || []
32
+
33
+ rows.each do |row|
34
+ # unescape string passed BYTEA field (OID == 17)
35
+ binaries.each do |index, _|
36
+ row[index] = unescape_bytea(row[index])
37
+ end
38
+
39
+ # If this is a money type column and there are any currency symbols,
40
+ # then strip them off. Indeed it would be prettier to do this in
41
+ # PostgreSQLColumn.string_to_decimal but would break form input
42
+ # fields that call value_before_type_cast.
43
+ monies.each do |index, _|
44
+ data = row[index]
45
+ # Because money output is formatted according to the locale, there are two
46
+ # cases to consider (note the decimal separators):
47
+ # (1) $12,345,678.12
48
+ # (2) $12.345.678,12
49
+ case data
50
+ when /^-?\D+[\d,]+\.\d{2}$/ # (1)
51
+ data.gsub!(/[^-\d.]/, "")
52
+ when /^-?\D+[\d.]+,\d{2}$/ # (2)
53
+ data.gsub!(/[^-\d,]/, "").sub!(/,/, ".")
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ # Queries the database and returns the results in an Array-like object
60
+ def query(sql, name = nil) #:nodoc:
61
+ log(sql, name) do
62
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
63
+ result_as_array @connection.async_exec(sql)
64
+ end
65
+ end
66
+ end
67
+
68
+ # Executes an SQL statement, returning a PG::Result object on success
69
+ # or raising a PG::Error exception otherwise.
70
+ # Note: the PG::Result object is manually memory managed; if you don't
71
+ # need it specifically, you may want consider the <tt>exec_query</tt> wrapper.
72
+ def execute(sql, name = nil)
73
+ log(sql, name) do
74
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
75
+ @connection.async_exec(sql)
76
+ end
77
+ end
78
+ end
79
+
80
+ def exec_query(sql, name = "SQL", binds = [], prepare: false)
81
+ execute_and_clear(sql, name, binds, prepare: prepare) do |result|
82
+ types = {}
83
+ fields = result.fields
84
+ fields.each_with_index do |fname, i|
85
+ ftype = result.ftype i
86
+ fmod = result.fmod i
87
+ types[fname] = get_oid_type(ftype, fmod, fname)
88
+ end
89
+ ActiveRecord::Result.new(fields, result.values, types)
90
+ end
91
+ end
92
+
93
+ def exec_delete(sql, name = nil, binds = [])
94
+ execute_and_clear(sql, name, binds) { |result| result.cmd_tuples }
95
+ end
96
+ alias :exec_update :exec_delete
97
+
98
+ def sql_for_insert(sql, pk, id_value, sequence_name, binds) # :nodoc:
99
+ if pk.nil?
100
+ # Extract the table from the insert sql. Yuck.
101
+ table_ref = extract_table_ref_from_insert_sql(sql)
102
+ pk = primary_key(table_ref) if table_ref
103
+ end
104
+
105
+ if pk = suppress_composite_primary_key(pk)
106
+ sql = "#{sql} RETURNING #{quote_column_name(pk)}"
107
+ end
108
+
109
+ super
110
+ end
111
+ private :sql_for_insert
112
+
113
+ def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
114
+ if use_insert_returning? || pk == false
115
+ super
116
+ else
117
+ result = exec_query(sql, name, binds)
118
+ unless sequence_name
119
+ table_ref = extract_table_ref_from_insert_sql(sql)
120
+ if table_ref
121
+ pk = primary_key(table_ref) if pk.nil?
122
+ pk = suppress_composite_primary_key(pk)
123
+ sequence_name = default_sequence_name(table_ref, pk)
124
+ end
125
+ return result unless sequence_name
126
+ end
127
+ last_insert_id_result(sequence_name)
128
+ end
129
+ end
130
+
131
+ # Begins a transaction.
132
+ def begin_db_transaction
133
+ execute "BEGIN"
134
+ end
135
+
136
+ def begin_isolated_db_transaction(isolation)
137
+ begin_db_transaction
138
+ execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
139
+ end
140
+
141
+ # Commits a transaction.
142
+ def commit_db_transaction
143
+ execute "COMMIT"
144
+ end
145
+
146
+ # Aborts a transaction.
147
+ def exec_rollback_db_transaction
148
+ execute "ROLLBACK"
149
+ end
150
+
151
+ private
152
+ # Returns the current ID of a table's sequence.
153
+ def last_insert_id_result(sequence_name)
154
+ exec_query("SELECT currval(#{quote(sequence_name)})", "SQL")
155
+ end
156
+
157
+ def suppress_composite_primary_key(pk)
158
+ pk unless pk.is_a?(Array)
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module PostgreSQL
6
+ class ExplainPrettyPrinter # :nodoc:
7
+ # Pretty prints the result of an EXPLAIN in a way that resembles the output of the
8
+ # PostgreSQL shell:
9
+ #
10
+ # QUERY PLAN
11
+ # ------------------------------------------------------------------------------
12
+ # Nested Loop Left Join (cost=0.00..37.24 rows=8 width=0)
13
+ # Join Filter: (posts.user_id = users.id)
14
+ # -> Index Scan using users_pkey on users (cost=0.00..8.27 rows=1 width=4)
15
+ # Index Cond: (id = 1)
16
+ # -> Seq Scan on posts (cost=0.00..28.88 rows=8 width=4)
17
+ # Filter: (posts.user_id = 1)
18
+ # (6 rows)
19
+ #
20
+ def pp(result)
21
+ header = result.columns.first
22
+ lines = result.rows.map(&:first)
23
+
24
+ # We add 2 because there's one char of padding at both sides, note
25
+ # the extra hyphens in the example above.
26
+ width = [header, *lines].map(&:length).max + 2
27
+
28
+ pp = []
29
+
30
+ pp << header.center(width).rstrip
31
+ pp << "-" * width
32
+
33
+ pp += lines.map { |line| " #{line}" }
34
+
35
+ nrows = result.rows.length
36
+ rows_label = nrows == 1 ? "row" : "rows"
37
+ pp << "(#{nrows} #{rows_label})"
38
+
39
+ pp.join("\n") + "\n"
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module PostgreSQL
6
+ module OID # :nodoc:
7
+ class Array < Type::Value # :nodoc:
8
+ include Type::Helpers::Mutable
9
+
10
+ Data = Struct.new(:encoder, :values) # :nodoc:
11
+
12
+ attr_reader :subtype, :delimiter
13
+ delegate :type, :user_input_in_time_zone, :limit, :precision, :scale, to: :subtype
14
+
15
+ def initialize(subtype, delimiter = ",")
16
+ @subtype = subtype
17
+ @delimiter = delimiter
18
+
19
+ @pg_encoder = PG::TextEncoder::Array.new name: "#{type}[]", delimiter: delimiter
20
+ @pg_decoder = PG::TextDecoder::Array.new name: "#{type}[]", delimiter: delimiter
21
+ end
22
+
23
+ def deserialize(value)
24
+ case value
25
+ when ::String
26
+ type_cast_array(@pg_decoder.decode(value), :deserialize)
27
+ when Data
28
+ type_cast_array(value.values, :deserialize)
29
+ else
30
+ super
31
+ end
32
+ end
33
+
34
+ def cast(value)
35
+ if value.is_a?(::String)
36
+ value = begin
37
+ @pg_decoder.decode(value)
38
+ rescue TypeError
39
+ # malformed array string is treated as [], will raise in PG 2.0 gem
40
+ # this keeps a consistent implementation
41
+ []
42
+ end
43
+ end
44
+ type_cast_array(value, :cast)
45
+ end
46
+
47
+ def serialize(value)
48
+ if value.is_a?(::Array)
49
+ casted_values = type_cast_array(value, :serialize)
50
+ Data.new(@pg_encoder, casted_values)
51
+ else
52
+ super
53
+ end
54
+ end
55
+
56
+ def ==(other)
57
+ other.is_a?(Array) &&
58
+ subtype == other.subtype &&
59
+ delimiter == other.delimiter
60
+ end
61
+
62
+ def type_cast_for_schema(value)
63
+ return super unless value.is_a?(::Array)
64
+ "[" + value.map { |v| subtype.type_cast_for_schema(v) }.join(", ") + "]"
65
+ end
66
+
67
+ def map(value, &block)
68
+ value.map(&block)
69
+ end
70
+
71
+ def changed_in_place?(raw_old_value, new_value)
72
+ deserialize(raw_old_value) != new_value
73
+ end
74
+
75
+ def force_equality?(value)
76
+ value.is_a?(::Array)
77
+ end
78
+
79
+ private
80
+
81
+ def type_cast_array(value, method)
82
+ if value.is_a?(::Array)
83
+ value.map { |item| type_cast_array(item, method) }
84
+ else
85
+ @subtype.public_send(method, value)
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end