activerecord 4.2.9 → 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 (274) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +614 -1572
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +10 -11
  5. data/examples/performance.rb +32 -31
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/aggregations.rb +263 -249
  8. data/lib/active_record/association_relation.rb +11 -6
  9. data/lib/active_record/associations/alias_tracker.rb +29 -35
  10. data/lib/active_record/associations/association.rb +77 -43
  11. data/lib/active_record/associations/association_scope.rb +106 -133
  12. data/lib/active_record/associations/belongs_to_association.rb +52 -41
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
  14. data/lib/active_record/associations/builder/association.rb +29 -38
  15. data/lib/active_record/associations/builder/belongs_to.rb +77 -30
  16. data/lib/active_record/associations/builder/collection_association.rb +9 -22
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +42 -35
  18. data/lib/active_record/associations/builder/has_many.rb +6 -4
  19. data/lib/active_record/associations/builder/has_one.rb +13 -6
  20. data/lib/active_record/associations/builder/singular_association.rb +15 -11
  21. data/lib/active_record/associations/collection_association.rb +139 -280
  22. data/lib/active_record/associations/collection_proxy.rb +231 -133
  23. data/lib/active_record/associations/foreign_association.rb +3 -1
  24. data/lib/active_record/associations/has_many_association.rb +34 -89
  25. data/lib/active_record/associations/has_many_through_association.rb +49 -76
  26. data/lib/active_record/associations/has_one_association.rb +38 -24
  27. data/lib/active_record/associations/has_one_through_association.rb +18 -9
  28. data/lib/active_record/associations/join_dependency/join_association.rb +40 -89
  29. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
  31. data/lib/active_record/associations/join_dependency.rb +133 -159
  32. data/lib/active_record/associations/preloader/association.rb +85 -120
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -74
  34. data/lib/active_record/associations/preloader.rb +81 -91
  35. data/lib/active_record/associations/singular_association.rb +27 -34
  36. data/lib/active_record/associations/through_association.rb +38 -18
  37. data/lib/active_record/associations.rb +1732 -1597
  38. data/lib/active_record/attribute_assignment.rb +58 -182
  39. data/lib/active_record/attribute_decorators.rb +39 -15
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +10 -8
  41. data/lib/active_record/attribute_methods/dirty.rb +94 -135
  42. data/lib/active_record/attribute_methods/primary_key.rb +86 -71
  43. data/lib/active_record/attribute_methods/query.rb +4 -2
  44. data/lib/active_record/attribute_methods/read.rb +45 -63
  45. data/lib/active_record/attribute_methods/serialization.rb +40 -20
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +58 -36
  47. data/lib/active_record/attribute_methods/write.rb +30 -45
  48. data/lib/active_record/attribute_methods.rb +166 -109
  49. data/lib/active_record/attributes.rb +201 -82
  50. data/lib/active_record/autosave_association.rb +94 -36
  51. data/lib/active_record/base.rb +57 -44
  52. data/lib/active_record/callbacks.rb +97 -57
  53. data/lib/active_record/coders/json.rb +3 -1
  54. data/lib/active_record/coders/yaml_column.rb +24 -12
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -290
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +237 -90
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +71 -21
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +118 -52
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +318 -217
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +81 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +570 -228
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +138 -70
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +325 -202
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +542 -593
  69. data/lib/active_record/connection_adapters/column.rb +50 -41
  70. data/lib/active_record/connection_adapters/connection_specification.rb +147 -135
  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 +41 -188
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +45 -114
  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 +50 -58
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +10 -6
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -22
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  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 +7 -9
  99. data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +55 -53
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +107 -47
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +144 -90
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -284
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +432 -323
  117. data/lib/active_record/connection_adapters/schema_cache.rb +48 -24
  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 +269 -308
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +40 -27
  128. data/lib/active_record/core.rb +178 -198
  129. data/lib/active_record/counter_cache.rb +79 -36
  130. data/lib/active_record/define_callbacks.rb +22 -0
  131. data/lib/active_record/dynamic_matchers.rb +87 -105
  132. data/lib/active_record/enum.rb +135 -88
  133. data/lib/active_record/errors.rb +179 -52
  134. data/lib/active_record/explain.rb +23 -11
  135. data/lib/active_record/explain_registry.rb +4 -2
  136. data/lib/active_record/explain_subscriber.rb +10 -5
  137. data/lib/active_record/fixture_set/file.rb +35 -9
  138. data/lib/active_record/fixtures.rb +188 -132
  139. data/lib/active_record/gem_version.rb +4 -2
  140. data/lib/active_record/inheritance.rb +148 -112
  141. data/lib/active_record/integration.rb +70 -28
  142. data/lib/active_record/internal_metadata.rb +45 -0
  143. data/lib/active_record/legacy_yaml_adapter.rb +21 -3
  144. data/lib/active_record/locale/en.yml +3 -2
  145. data/lib/active_record/locking/optimistic.rb +88 -96
  146. data/lib/active_record/locking/pessimistic.rb +15 -3
  147. data/lib/active_record/log_subscriber.rb +95 -33
  148. data/lib/active_record/migration/command_recorder.rb +133 -90
  149. data/lib/active_record/migration/compatibility.rb +217 -0
  150. data/lib/active_record/migration/join_table.rb +8 -6
  151. data/lib/active_record/migration.rb +581 -282
  152. data/lib/active_record/model_schema.rb +290 -111
  153. data/lib/active_record/nested_attributes.rb +264 -222
  154. data/lib/active_record/no_touching.rb +7 -1
  155. data/lib/active_record/null_relation.rb +24 -37
  156. data/lib/active_record/persistence.rb +347 -119
  157. data/lib/active_record/query_cache.rb +13 -24
  158. data/lib/active_record/querying.rb +19 -17
  159. data/lib/active_record/railtie.rb +94 -32
  160. data/lib/active_record/railties/console_sandbox.rb +2 -0
  161. data/lib/active_record/railties/controller_runtime.rb +9 -3
  162. data/lib/active_record/railties/databases.rake +149 -156
  163. data/lib/active_record/readonly_attributes.rb +5 -4
  164. data/lib/active_record/reflection.rb +414 -267
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  166. data/lib/active_record/relation/batches.rb +204 -55
  167. data/lib/active_record/relation/calculations.rb +256 -248
  168. data/lib/active_record/relation/delegation.rb +67 -60
  169. data/lib/active_record/relation/finder_methods.rb +288 -239
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +86 -86
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -24
  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 +7 -1
  179. data/lib/active_record/relation/predicate_builder.rb +116 -119
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +448 -393
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +11 -13
  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 +287 -340
  187. data/lib/active_record/result.rb +54 -36
  188. data/lib/active_record/runtime_registry.rb +6 -4
  189. data/lib/active_record/sanitization.rb +155 -124
  190. data/lib/active_record/schema.rb +30 -24
  191. data/lib/active_record/schema_dumper.rb +91 -87
  192. data/lib/active_record/schema_migration.rb +19 -16
  193. data/lib/active_record/scoping/default.rb +102 -85
  194. data/lib/active_record/scoping/named.rb +81 -32
  195. data/lib/active_record/scoping.rb +45 -26
  196. data/lib/active_record/secure_token.rb +40 -0
  197. data/lib/active_record/serialization.rb +5 -5
  198. data/lib/active_record/statement_cache.rb +45 -35
  199. data/lib/active_record/store.rb +42 -36
  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 +134 -96
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +56 -100
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +83 -41
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -16
  206. data/lib/active_record/timestamp.rb +70 -38
  207. data/lib/active_record/touch_later.rb +64 -0
  208. data/lib/active_record/transactions.rb +199 -124
  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 +4 -45
  212. data/lib/active_record/type/date_time.rb +4 -49
  213. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  214. data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
  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 +24 -15
  218. data/lib/active_record/type/text.rb +2 -2
  219. data/lib/active_record/type/time.rb +11 -16
  220. data/lib/active_record/type/type_map.rb +15 -17
  221. data/lib/active_record/type/unsigned_integer.rb +9 -7
  222. data/lib/active_record/type.rb +79 -23
  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 +13 -4
  228. data/lib/active_record/validations/length.rb +26 -0
  229. data/lib/active_record/validations/presence.rb +14 -13
  230. data/lib/active_record/validations/uniqueness.rb +40 -41
  231. data/lib/active_record/validations.rb +38 -35
  232. data/lib/active_record/version.rb +3 -1
  233. data/lib/active_record.rb +34 -22
  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 +43 -35
  237. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +8 -3
  238. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -1
  239. data/lib/rails/generators/active_record/migration.rb +18 -1
  240. data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
  241. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +3 -0
  242. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  243. data/lib/rails/generators/active_record.rb +7 -5
  244. metadata +72 -50
  245. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  246. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  247. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  248. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  249. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  250. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  251. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  252. data/lib/active_record/attribute.rb +0 -163
  253. data/lib/active_record/attribute_set/builder.rb +0 -106
  254. data/lib/active_record/attribute_set.rb +0 -81
  255. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  256. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  257. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  258. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  259. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  260. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  261. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  262. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  263. data/lib/active_record/type/big_integer.rb +0 -13
  264. data/lib/active_record/type/binary.rb +0 -50
  265. data/lib/active_record/type/boolean.rb +0 -31
  266. data/lib/active_record/type/decimal.rb +0 -64
  267. data/lib/active_record/type/decorator.rb +0 -14
  268. data/lib/active_record/type/float.rb +0 -19
  269. data/lib/active_record/type/integer.rb +0 -59
  270. data/lib/active_record/type/mutable.rb +0 -16
  271. data/lib/active_record/type/numeric.rb +0 -36
  272. data/lib/active_record/type/string.rb +0 -40
  273. data/lib/active_record/type/time_value.rb +0 -38
  274. data/lib/active_record/type/value.rb +0 -110
@@ -1,26 +1,29 @@
1
- require 'active_record/connection_adapters/abstract_mysql_adapter'
1
+ # frozen_string_literal: true
2
2
 
3
- gem 'mysql2', '>= 0.3.13', '< 0.5'
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
10
  module ConnectionHandling # :nodoc:
8
11
  # Establishes a connection to the database that's used by all Active Record objects.
9
12
  def mysql2_connection(config)
10
13
  config = config.symbolize_keys
14
+ config[:flags] ||= 0
11
15
 
12
- config[:username] = 'root' if config[:username].nil?
13
-
14
- if Mysql2::Client.const_defined? :FOUND_ROWS
15
- 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
16
20
  end
17
21
 
18
22
  client = Mysql2::Client.new(config)
19
- options = [config[:host], config[:username], config[:password], config[:database], config[:port], config[:socket], 0]
20
- ConnectionAdapters::Mysql2Adapter.new(client, logger, options, config)
23
+ ConnectionAdapters::Mysql2Adapter.new(client, logger, nil, config)
21
24
  rescue Mysql2::Error => error
22
25
  if error.message.include?("Unknown database")
23
- raise ActiveRecord::NoDatabaseError.new(error.message, error)
26
+ raise ActiveRecord::NoDatabaseError
24
27
  else
25
28
  raise
26
29
  end
@@ -29,24 +32,29 @@ module ActiveRecord
29
32
 
30
33
  module ConnectionAdapters
31
34
  class Mysql2Adapter < AbstractMysqlAdapter
32
- ADAPTER_NAME = 'Mysql2'.freeze
35
+ ADAPTER_NAME = "Mysql2".freeze
36
+
37
+ include MySQL::DatabaseStatements
33
38
 
34
39
  def initialize(connection, logger, connection_options, config)
35
40
  super
36
- @prepared_statements = false
41
+ @prepared_statements = false unless config.key?(:prepared_statements)
37
42
  configure_connection
38
43
  end
39
44
 
40
- MAX_INDEX_LENGTH_FOR_UTF8MB4 = 191
41
- def initialize_schema_migrations_table
42
- if charset == 'utf8mb4'
43
- ActiveRecord::SchemaMigration.create_table(MAX_INDEX_LENGTH_FOR_UTF8MB4)
44
- else
45
- ActiveRecord::SchemaMigration.create_table
46
- end
45
+ def supports_json?
46
+ !mariadb? && version >= "5.7.8"
47
+ end
48
+
49
+ def supports_comments?
50
+ true
47
51
  end
48
52
 
49
- def supports_explain?
53
+ def supports_comments_in_create?
54
+ true
55
+ end
56
+
57
+ def supports_savepoints?
50
58
  true
51
59
  end
52
60
 
@@ -54,7 +62,7 @@ module ActiveRecord
54
62
 
55
63
  def each_hash(result) # :nodoc:
56
64
  if block_given?
57
- result.each(:as => :hash, :symbolize_keys => true) do |row|
65
+ result.each(as: :hash, symbolize_keys: true) do |row|
58
66
  yield row
59
67
  end
60
68
  else
@@ -74,14 +82,6 @@ module ActiveRecord
74
82
  @connection.escape(string)
75
83
  end
76
84
 
77
- def quoted_date(value)
78
- if supports_datetime_with_precision? && value.acts_like?(:time) && value.respond_to?(:usec)
79
- "#{super}.#{sprintf("%06d", value.usec)}"
80
- else
81
- super
82
- end
83
- end
84
-
85
85
  #--
86
86
  # CONNECTION MANAGEMENT ====================================
87
87
  #++
@@ -104,173 +104,26 @@ module ActiveRecord
104
104
  @connection.close
105
105
  end
106
106
 
107
- #--
108
- # DATABASE STATEMENTS ======================================
109
- #++
110
-
111
- def explain(arel, binds = [])
112
- sql = "EXPLAIN #{to_sql(arel, binds.dup)}"
113
- start = Time.now
114
- result = exec_query(sql, 'EXPLAIN', binds)
115
- elapsed = Time.now - start
116
-
117
- ExplainPrettyPrinter.new.pp(result, elapsed)
107
+ def discard! # :nodoc:
108
+ @connection.automatic_close = false
109
+ @connection = nil
118
110
  end
119
111
 
120
- class ExplainPrettyPrinter # :nodoc:
121
- # Pretty prints the result of a EXPLAIN in a way that resembles the output of the
122
- # MySQL shell:
123
- #
124
- # +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
125
- # | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
126
- # +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
127
- # | 1 | SIMPLE | users | const | PRIMARY | PRIMARY | 4 | const | 1 | |
128
- # | 1 | SIMPLE | posts | ALL | NULL | NULL | NULL | NULL | 1 | Using where |
129
- # +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
130
- # 2 rows in set (0.00 sec)
131
- #
132
- # This is an exercise in Ruby hyperrealism :).
133
- def pp(result, elapsed)
134
- widths = compute_column_widths(result)
135
- separator = build_separator(widths)
136
-
137
- pp = []
138
-
139
- pp << separator
140
- pp << build_cells(result.columns, widths)
141
- pp << separator
142
-
143
- result.rows.each do |row|
144
- pp << build_cells(row, widths)
145
- end
146
-
147
- pp << separator
148
- pp << build_footer(result.rows.length, elapsed)
149
-
150
- pp.join("\n") + "\n"
151
- end
152
-
153
- private
154
-
155
- def compute_column_widths(result)
156
- [].tap do |widths|
157
- result.columns.each_with_index do |column, i|
158
- cells_in_column = [column] + result.rows.map {|r| r[i].nil? ? 'NULL' : r[i].to_s}
159
- widths << cells_in_column.map(&:length).max
160
- end
161
- end
162
- end
112
+ private
163
113
 
164
- def build_separator(widths)
165
- padding = 1
166
- '+' + widths.map {|w| '-' * (w + (padding*2))}.join('+') + '+'
114
+ def connect
115
+ @connection = Mysql2::Client.new(@config)
116
+ configure_connection
167
117
  end
168
118
 
169
- def build_cells(items, widths)
170
- cells = []
171
- items.each_with_index do |item, i|
172
- item = 'NULL' if item.nil?
173
- justifier = item.is_a?(Numeric) ? 'rjust' : 'ljust'
174
- cells << item.to_s.send(justifier, widths[i])
175
- end
176
- '| ' + cells.join(' | ') + ' |'
119
+ def configure_connection
120
+ @connection.query_options.merge!(as: :array)
121
+ super
177
122
  end
178
123
 
179
- def build_footer(nrows, elapsed)
180
- rows_label = nrows == 1 ? 'row' : 'rows'
181
- "#{nrows} #{rows_label} in set (%.2f sec)" % elapsed
124
+ def full_version
125
+ @full_version ||= @connection.server_info[:version]
182
126
  end
183
- end
184
-
185
- # FIXME: re-enable the following once a "better" query_cache solution is in core
186
- #
187
- # The overrides below perform much better than the originals in AbstractAdapter
188
- # because we're able to take advantage of mysql2's lazy-loading capabilities
189
- #
190
- # # Returns a record hash with the column names as keys and column values
191
- # # as values.
192
- # def select_one(sql, name = nil)
193
- # result = execute(sql, name)
194
- # result.each(as: :hash) do |r|
195
- # return r
196
- # end
197
- # end
198
- #
199
- # # Returns a single value from a record
200
- # def select_value(sql, name = nil)
201
- # result = execute(sql, name)
202
- # if first = result.first
203
- # first.first
204
- # end
205
- # end
206
- #
207
- # # Returns an array of the values of the first column in a select:
208
- # # select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
209
- # def select_values(sql, name = nil)
210
- # execute(sql, name).map { |row| row.first }
211
- # end
212
-
213
- # Returns an array of arrays containing the field values.
214
- # Order is the same as that returned by +columns+.
215
- def select_rows(sql, name = nil, binds = [])
216
- execute(sql, name).to_a
217
- end
218
-
219
- # Executes the SQL statement in the context of this connection.
220
- def execute(sql, name = nil)
221
- # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
222
- # made since we established the connection
223
- @connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
224
-
225
- super
226
- end
227
-
228
- def exec_query(sql, name = 'SQL', binds = [])
229
- result = execute(sql, name)
230
- ActiveRecord::Result.new(result.fields, result.to_a)
231
- end
232
-
233
- alias exec_without_stmt exec_query
234
-
235
- def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
236
- super
237
- id_value || @connection.last_id
238
- end
239
- alias :create :insert_sql
240
-
241
- def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
242
- execute to_sql(sql, binds), name
243
- end
244
-
245
- def exec_delete(sql, name, binds)
246
- execute to_sql(sql, binds), name
247
- @connection.affected_rows
248
- end
249
- alias :exec_update :exec_delete
250
-
251
- def last_inserted_id(result)
252
- @connection.last_id
253
- end
254
-
255
- private
256
-
257
- def connect
258
- @connection = Mysql2::Client.new(@config)
259
- configure_connection
260
- end
261
-
262
- def configure_connection
263
- @connection.query_options.merge!(:as => :array)
264
- super
265
- end
266
-
267
- def full_version
268
- @full_version ||= @connection.server_info[:version]
269
- end
270
-
271
- def set_field_encoding field_name
272
- field_name
273
- end
274
127
  end
275
128
  end
276
129
  end
@@ -1,20 +1,44 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  # PostgreSQL-specific extensions to column definitions in a table.
4
6
  class PostgreSQLColumn < Column #:nodoc:
5
- attr_accessor :array
6
-
7
- def initialize(name, default, cast_type, sql_type = nil, null = true, default_function = nil)
8
- if sql_type =~ /\[\]$/
9
- @array = true
10
- super(name, default, cast_type, sql_type[0..sql_type.length - 3], null)
11
- else
12
- @array = false
13
- super(name, default, cast_type, sql_type, null)
14
- end
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
15
17
 
16
- @default_function = default_function
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
17
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
18
42
  end
19
43
  end
20
44
  end
@@ -1,97 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module PostgreSQL
4
6
  module DatabaseStatements
5
7
  def explain(arel, binds = [])
6
8
  sql = "EXPLAIN #{to_sql(arel, binds)}"
7
- ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', binds))
8
- end
9
-
10
- class ExplainPrettyPrinter # :nodoc:
11
- # Pretty prints the result of a EXPLAIN in a way that resembles the output of the
12
- # PostgreSQL shell:
13
- #
14
- # QUERY PLAN
15
- # ------------------------------------------------------------------------------
16
- # Nested Loop Left Join (cost=0.00..37.24 rows=8 width=0)
17
- # Join Filter: (posts.user_id = users.id)
18
- # -> Index Scan using users_pkey on users (cost=0.00..8.27 rows=1 width=4)
19
- # Index Cond: (id = 1)
20
- # -> Seq Scan on posts (cost=0.00..28.88 rows=8 width=4)
21
- # Filter: (posts.user_id = 1)
22
- # (6 rows)
23
- #
24
- def pp(result)
25
- header = result.columns.first
26
- lines = result.rows.map(&:first)
27
-
28
- # We add 2 because there's one char of padding at both sides, note
29
- # the extra hyphens in the example above.
30
- width = [header, *lines].map(&:length).max + 2
31
-
32
- pp = []
33
-
34
- pp << header.center(width).rstrip
35
- pp << '-' * width
36
-
37
- pp += lines.map {|line| " #{line}"}
38
-
39
- nrows = result.rows.length
40
- rows_label = nrows == 1 ? 'row' : 'rows'
41
- pp << "(#{nrows} #{rows_label})"
42
-
43
- pp.join("\n") + "\n"
44
- end
45
- end
46
-
47
- def select_value(arel, name = nil, binds = [])
48
- arel, binds = binds_from_relation arel, binds
49
- sql = to_sql(arel, binds)
50
- execute_and_clear(sql, name, binds) do |result|
51
- result.getvalue(0, 0) if result.ntuples > 0 && result.nfields > 0
52
- end
53
- end
54
-
55
- def select_values(arel, name = nil)
56
- arel, binds = binds_from_relation arel, []
57
- sql = to_sql(arel, binds)
58
- execute_and_clear(sql, name, binds) do |result|
59
- if result.nfields > 0
60
- result.column_values(0)
61
- else
62
- []
63
- end
64
- end
65
- end
66
-
67
- # Executes a SELECT query and returns an array of rows. Each row is an
68
- # array of field values.
69
- def select_rows(sql, name = nil, binds = [])
70
- execute_and_clear(sql, name, binds) do |result|
71
- result.values
72
- end
73
- end
74
-
75
- # Executes an INSERT query and returns the new record's ID
76
- def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
77
- unless pk
78
- # Extract the table from the insert sql. Yuck.
79
- table_ref = extract_table_ref_from_insert_sql(sql)
80
- pk = primary_key(table_ref) if table_ref
81
- end
82
-
83
- if pk && use_insert_returning?
84
- select_value("#{sql} RETURNING #{quote_column_name(pk)}")
85
- elsif pk
86
- super
87
- last_insert_id_value(sequence_name || default_sequence_name(table_ref, pk))
88
- else
89
- super
90
- end
91
- end
92
-
93
- def create
94
- super.insert
9
+ PostgreSQL::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", binds))
95
10
  end
96
11
 
97
12
  # The internal PostgreSQL identifier of the money data type.
@@ -133,9 +48,9 @@ module ActiveRecord
133
48
  # (2) $12.345.678,12
134
49
  case data
135
50
  when /^-?\D+[\d,]+\.\d{2}$/ # (1)
136
- data.gsub!(/[^-\d.]/, '')
51
+ data.gsub!(/[^-\d.]/, "")
137
52
  when /^-?\D+[\d.]+,\d{2}$/ # (2)
138
- data.gsub!(/[^-\d,]/, '').sub!(/,/, '.')
53
+ data.gsub!(/[^-\d,]/, "").sub!(/,/, ".")
139
54
  end
140
55
  end
141
56
  end
@@ -144,20 +59,26 @@ module ActiveRecord
144
59
  # Queries the database and returns the results in an Array-like object
145
60
  def query(sql, name = nil) #:nodoc:
146
61
  log(sql, name) do
147
- result_as_array @connection.async_exec(sql)
62
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
63
+ result_as_array @connection.async_exec(sql)
64
+ end
148
65
  end
149
66
  end
150
67
 
151
- # Executes an SQL statement, returning a PGresult object on success
152
- # or raising a PGError exception otherwise.
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.
153
72
  def execute(sql, name = nil)
154
73
  log(sql, name) do
155
- @connection.async_exec(sql)
74
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
75
+ @connection.async_exec(sql)
76
+ end
156
77
  end
157
78
  end
158
79
 
159
- def exec_query(sql, name = 'SQL', binds = [])
160
- execute_and_clear(sql, name, binds) do |result|
80
+ def exec_query(sql, name = "SQL", binds = [], prepare: false)
81
+ execute_and_clear(sql, name, binds, prepare: prepare) do |result|
161
82
  types = {}
162
83
  fields = result.fields
163
84
  fields.each_with_index do |fname, i|
@@ -169,44 +90,44 @@ module ActiveRecord
169
90
  end
170
91
  end
171
92
 
172
- def exec_delete(sql, name = 'SQL', binds = [])
173
- execute_and_clear(sql, name, binds) {|result| result.cmd_tuples }
93
+ def exec_delete(sql, name = nil, binds = [])
94
+ execute_and_clear(sql, name, binds) { |result| result.cmd_tuples }
174
95
  end
175
96
  alias :exec_update :exec_delete
176
97
 
177
- def sql_for_insert(sql, pk, id_value, sequence_name, binds)
178
- unless pk
98
+ def sql_for_insert(sql, pk, id_value, sequence_name, binds) # :nodoc:
99
+ if pk.nil?
179
100
  # Extract the table from the insert sql. Yuck.
180
101
  table_ref = extract_table_ref_from_insert_sql(sql)
181
102
  pk = primary_key(table_ref) if table_ref
182
103
  end
183
104
 
184
- if pk && use_insert_returning?
105
+ if pk = suppress_composite_primary_key(pk)
185
106
  sql = "#{sql} RETURNING #{quote_column_name(pk)}"
186
107
  end
187
108
 
188
- [sql, binds]
109
+ super
189
110
  end
111
+ private :sql_for_insert
190
112
 
191
- def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
192
- val = exec_query(sql, name, binds)
193
- if !use_insert_returning? && pk
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)
194
118
  unless sequence_name
195
119
  table_ref = extract_table_ref_from_insert_sql(sql)
196
- sequence_name = default_sequence_name(table_ref, pk)
197
- return val unless sequence_name
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
198
126
  end
199
127
  last_insert_id_result(sequence_name)
200
- else
201
- val
202
128
  end
203
129
  end
204
130
 
205
- # Executes an UPDATE query and returns the number of affected tuples.
206
- def update_sql(sql, name = nil)
207
- super.cmd_tuples
208
- end
209
-
210
131
  # Begins a transaction.
211
132
  def begin_db_transaction
212
133
  execute "BEGIN"
@@ -226,6 +147,16 @@ module ActiveRecord
226
147
  def exec_rollback_db_transaction
227
148
  execute "ROLLBACK"
228
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
229
160
  end
230
161
  end
231
162
  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