activerecord 3.2.19 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (264) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1715 -604
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +40 -45
  5. data/examples/performance.rb +33 -22
  6. data/examples/simple.rb +3 -4
  7. data/lib/active_record/aggregations.rb +76 -51
  8. data/lib/active_record/association_relation.rb +35 -0
  9. data/lib/active_record/associations/alias_tracker.rb +54 -40
  10. data/lib/active_record/associations/association.rb +76 -56
  11. data/lib/active_record/associations/association_scope.rb +125 -93
  12. data/lib/active_record/associations/belongs_to_association.rb +57 -28
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -2
  14. data/lib/active_record/associations/builder/association.rb +120 -32
  15. data/lib/active_record/associations/builder/belongs_to.rb +115 -62
  16. data/lib/active_record/associations/builder/collection_association.rb +61 -53
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +117 -43
  18. data/lib/active_record/associations/builder/has_many.rb +9 -65
  19. data/lib/active_record/associations/builder/has_one.rb +18 -52
  20. data/lib/active_record/associations/builder/singular_association.rb +18 -19
  21. data/lib/active_record/associations/collection_association.rb +268 -186
  22. data/lib/active_record/associations/collection_proxy.rb +1003 -63
  23. data/lib/active_record/associations/foreign_association.rb +11 -0
  24. data/lib/active_record/associations/has_many_association.rb +81 -41
  25. data/lib/active_record/associations/has_many_through_association.rb +76 -55
  26. data/lib/active_record/associations/has_one_association.rb +51 -21
  27. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  28. data/lib/active_record/associations/join_dependency/join_association.rb +83 -108
  29. data/lib/active_record/associations/join_dependency/join_base.rb +7 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +30 -37
  31. data/lib/active_record/associations/join_dependency.rb +239 -155
  32. data/lib/active_record/associations/preloader/association.rb +97 -62
  33. data/lib/active_record/associations/preloader/collection_association.rb +2 -8
  34. data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
  35. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  36. data/lib/active_record/associations/preloader/singular_association.rb +3 -3
  37. data/lib/active_record/associations/preloader/through_association.rb +75 -33
  38. data/lib/active_record/associations/preloader.rb +111 -79
  39. data/lib/active_record/associations/singular_association.rb +35 -13
  40. data/lib/active_record/associations/through_association.rb +41 -19
  41. data/lib/active_record/associations.rb +727 -501
  42. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  43. data/lib/active_record/attribute.rb +213 -0
  44. data/lib/active_record/attribute_assignment.rb +32 -162
  45. data/lib/active_record/attribute_decorators.rb +67 -0
  46. data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
  47. data/lib/active_record/attribute_methods/dirty.rb +101 -61
  48. data/lib/active_record/attribute_methods/primary_key.rb +50 -36
  49. data/lib/active_record/attribute_methods/query.rb +7 -6
  50. data/lib/active_record/attribute_methods/read.rb +56 -117
  51. data/lib/active_record/attribute_methods/serialization.rb +43 -96
  52. data/lib/active_record/attribute_methods/time_zone_conversion.rb +93 -42
  53. data/lib/active_record/attribute_methods/write.rb +34 -45
  54. data/lib/active_record/attribute_methods.rb +333 -144
  55. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  56. data/lib/active_record/attribute_set/builder.rb +108 -0
  57. data/lib/active_record/attribute_set.rb +108 -0
  58. data/lib/active_record/attributes.rb +265 -0
  59. data/lib/active_record/autosave_association.rb +285 -223
  60. data/lib/active_record/base.rb +95 -490
  61. data/lib/active_record/callbacks.rb +95 -61
  62. data/lib/active_record/coders/json.rb +13 -0
  63. data/lib/active_record/coders/yaml_column.rb +28 -19
  64. data/lib/active_record/collection_cache_key.rb +40 -0
  65. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +724 -277
  66. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  67. data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -192
  68. data/lib/active_record/connection_adapters/abstract/query_cache.rb +31 -26
  69. data/lib/active_record/connection_adapters/abstract/quoting.rb +140 -57
  70. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +147 -0
  72. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +419 -276
  73. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +105 -0
  74. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +963 -276
  75. data/lib/active_record/connection_adapters/abstract/transaction.rb +232 -0
  76. data/lib/active_record/connection_adapters/abstract_adapter.rb +397 -106
  77. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +643 -342
  78. data/lib/active_record/connection_adapters/column.rb +30 -259
  79. data/lib/active_record/connection_adapters/connection_specification.rb +263 -0
  80. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  81. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  82. data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
  83. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  84. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  85. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  86. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  87. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  88. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  89. data/lib/active_record/connection_adapters/mysql2_adapter.rb +47 -196
  90. data/lib/active_record/connection_adapters/postgresql/column.rb +15 -0
  91. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +170 -0
  92. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +70 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +48 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +21 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +10 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +39 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +93 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  110. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  111. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  112. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  113. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  114. data/lib/active_record/connection_adapters/postgresql/oid.rb +31 -0
  115. data/lib/active_record/connection_adapters/postgresql/quoting.rb +116 -0
  116. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +49 -0
  117. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +180 -0
  118. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  119. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +682 -0
  120. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  121. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  122. data/lib/active_record/connection_adapters/postgresql_adapter.rb +558 -1039
  123. data/lib/active_record/connection_adapters/schema_cache.rb +74 -36
  124. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  125. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  126. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  127. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  128. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +538 -24
  129. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  130. data/lib/active_record/connection_handling.rb +155 -0
  131. data/lib/active_record/core.rb +561 -0
  132. data/lib/active_record/counter_cache.rb +146 -105
  133. data/lib/active_record/dynamic_matchers.rb +101 -64
  134. data/lib/active_record/enum.rb +234 -0
  135. data/lib/active_record/errors.rb +153 -56
  136. data/lib/active_record/explain.rb +15 -63
  137. data/lib/active_record/explain_registry.rb +30 -0
  138. data/lib/active_record/explain_subscriber.rb +10 -6
  139. data/lib/active_record/fixture_set/file.rb +77 -0
  140. data/lib/active_record/fixtures.rb +355 -232
  141. data/lib/active_record/gem_version.rb +15 -0
  142. data/lib/active_record/inheritance.rb +144 -79
  143. data/lib/active_record/integration.rb +66 -13
  144. data/lib/active_record/internal_metadata.rb +56 -0
  145. data/lib/active_record/legacy_yaml_adapter.rb +46 -0
  146. data/lib/active_record/locale/en.yml +9 -1
  147. data/lib/active_record/locking/optimistic.rb +77 -56
  148. data/lib/active_record/locking/pessimistic.rb +6 -6
  149. data/lib/active_record/log_subscriber.rb +53 -28
  150. data/lib/active_record/migration/command_recorder.rb +166 -33
  151. data/lib/active_record/migration/compatibility.rb +126 -0
  152. data/lib/active_record/migration/join_table.rb +15 -0
  153. data/lib/active_record/migration.rb +792 -264
  154. data/lib/active_record/model_schema.rb +192 -130
  155. data/lib/active_record/nested_attributes.rb +238 -145
  156. data/lib/active_record/no_touching.rb +52 -0
  157. data/lib/active_record/null_relation.rb +89 -0
  158. data/lib/active_record/persistence.rb +357 -157
  159. data/lib/active_record/query_cache.rb +22 -43
  160. data/lib/active_record/querying.rb +34 -23
  161. data/lib/active_record/railtie.rb +88 -48
  162. data/lib/active_record/railties/console_sandbox.rb +3 -4
  163. data/lib/active_record/railties/controller_runtime.rb +5 -4
  164. data/lib/active_record/railties/databases.rake +170 -422
  165. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  166. data/lib/active_record/readonly_attributes.rb +2 -5
  167. data/lib/active_record/reflection.rb +715 -189
  168. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  169. data/lib/active_record/relation/batches.rb +203 -50
  170. data/lib/active_record/relation/calculations.rb +203 -194
  171. data/lib/active_record/relation/delegation.rb +103 -25
  172. data/lib/active_record/relation/finder_methods.rb +457 -261
  173. data/lib/active_record/relation/from_clause.rb +32 -0
  174. data/lib/active_record/relation/merger.rb +167 -0
  175. data/lib/active_record/relation/predicate_builder/array_handler.rb +43 -0
  176. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  177. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  178. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  179. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  180. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  181. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  182. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  183. data/lib/active_record/relation/predicate_builder.rb +153 -48
  184. data/lib/active_record/relation/query_attribute.rb +19 -0
  185. data/lib/active_record/relation/query_methods.rb +1019 -194
  186. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  187. data/lib/active_record/relation/spawn_methods.rb +46 -150
  188. data/lib/active_record/relation/where_clause.rb +174 -0
  189. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  190. data/lib/active_record/relation.rb +450 -245
  191. data/lib/active_record/result.rb +104 -12
  192. data/lib/active_record/runtime_registry.rb +22 -0
  193. data/lib/active_record/sanitization.rb +120 -94
  194. data/lib/active_record/schema.rb +28 -18
  195. data/lib/active_record/schema_dumper.rb +141 -74
  196. data/lib/active_record/schema_migration.rb +50 -0
  197. data/lib/active_record/scoping/default.rb +64 -57
  198. data/lib/active_record/scoping/named.rb +93 -108
  199. data/lib/active_record/scoping.rb +73 -121
  200. data/lib/active_record/secure_token.rb +38 -0
  201. data/lib/active_record/serialization.rb +7 -5
  202. data/lib/active_record/statement_cache.rb +113 -0
  203. data/lib/active_record/store.rb +173 -15
  204. data/lib/active_record/suppressor.rb +58 -0
  205. data/lib/active_record/table_metadata.rb +68 -0
  206. data/lib/active_record/tasks/database_tasks.rb +313 -0
  207. data/lib/active_record/tasks/mysql_database_tasks.rb +151 -0
  208. data/lib/active_record/tasks/postgresql_database_tasks.rb +110 -0
  209. data/lib/active_record/tasks/sqlite_database_tasks.rb +59 -0
  210. data/lib/active_record/timestamp.rb +42 -24
  211. data/lib/active_record/touch_later.rb +58 -0
  212. data/lib/active_record/transactions.rb +233 -105
  213. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  214. data/lib/active_record/type/date.rb +7 -0
  215. data/lib/active_record/type/date_time.rb +7 -0
  216. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  217. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  218. data/lib/active_record/type/internal/timezone.rb +15 -0
  219. data/lib/active_record/type/serialized.rb +63 -0
  220. data/lib/active_record/type/time.rb +20 -0
  221. data/lib/active_record/type/type_map.rb +64 -0
  222. data/lib/active_record/type.rb +72 -0
  223. data/lib/active_record/type_caster/connection.rb +29 -0
  224. data/lib/active_record/type_caster/map.rb +19 -0
  225. data/lib/active_record/type_caster.rb +7 -0
  226. data/lib/active_record/validations/absence.rb +23 -0
  227. data/lib/active_record/validations/associated.rb +33 -18
  228. data/lib/active_record/validations/length.rb +24 -0
  229. data/lib/active_record/validations/presence.rb +66 -0
  230. data/lib/active_record/validations/uniqueness.rb +128 -68
  231. data/lib/active_record/validations.rb +48 -40
  232. data/lib/active_record/version.rb +5 -7
  233. data/lib/active_record.rb +71 -47
  234. data/lib/rails/generators/active_record/migration/migration_generator.rb +56 -8
  235. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +24 -0
  236. data/lib/rails/generators/active_record/migration/templates/migration.rb +28 -16
  237. data/lib/rails/generators/active_record/migration.rb +18 -8
  238. data/lib/rails/generators/active_record/model/model_generator.rb +38 -16
  239. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  240. data/lib/rails/generators/active_record/model/templates/model.rb +7 -6
  241. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  242. data/lib/rails/generators/active_record.rb +3 -11
  243. metadata +188 -134
  244. data/examples/associations.png +0 -0
  245. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  246. data/lib/active_record/associations/join_helper.rb +0 -55
  247. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  248. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  249. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  250. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -441
  251. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  252. data/lib/active_record/dynamic_finder_match.rb +0 -68
  253. data/lib/active_record/dynamic_scope_match.rb +0 -23
  254. data/lib/active_record/fixtures/file.rb +0 -65
  255. data/lib/active_record/identity_map.rb +0 -162
  256. data/lib/active_record/observer.rb +0 -121
  257. data/lib/active_record/serializers/xml_serializer.rb +0 -203
  258. data/lib/active_record/session_store.rb +0 -360
  259. data/lib/active_record/test_case.rb +0 -73
  260. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
  261. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  262. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  263. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  264. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,42 +1,63 @@
1
1
  require 'active_record/connection_adapters/abstract_mysql_adapter'
2
+ require 'active_record/connection_adapters/mysql/database_statements'
2
3
 
3
- gem 'mysql2', '~> 0.3.10'
4
+ gem 'mysql2', '>= 0.3.18', '< 0.5'
4
5
  require 'mysql2'
6
+ raise 'mysql2 0.4.3 is not supported. Please upgrade to 0.4.4+' if Mysql2::VERSION == '0.4.3'
5
7
 
6
8
  module ActiveRecord
7
- class Base
9
+ module ConnectionHandling # :nodoc:
8
10
  # Establishes a connection to the database that's used by all Active Record objects.
9
- def self.mysql2_connection(config)
11
+ def mysql2_connection(config)
12
+ config = config.symbolize_keys
13
+
10
14
  config[:username] = 'root' if config[:username].nil?
15
+ config[:flags] ||= 0
11
16
 
12
17
  if Mysql2::Client.const_defined? :FOUND_ROWS
13
- config[:flags] = Mysql2::Client::FOUND_ROWS
18
+ if config[:flags].kind_of? Array
19
+ config[:flags].push "FOUND_ROWS".freeze
20
+ else
21
+ config[:flags] |= Mysql2::Client::FOUND_ROWS
22
+ end
14
23
  end
15
24
 
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)
25
+ client = Mysql2::Client.new(config)
26
+ ConnectionAdapters::Mysql2Adapter.new(client, logger, nil, config)
27
+ rescue Mysql2::Error => error
28
+ if error.message.include?("Unknown database")
29
+ raise ActiveRecord::NoDatabaseError
30
+ else
31
+ raise
32
+ end
19
33
  end
20
34
  end
21
35
 
22
36
  module ConnectionAdapters
23
37
  class Mysql2Adapter < AbstractMysqlAdapter
38
+ ADAPTER_NAME = 'Mysql2'.freeze
24
39
 
25
- class Column < AbstractMysqlAdapter::Column # :nodoc:
26
- def adapter
27
- Mysql2Adapter
28
- end
29
- end
30
-
31
- ADAPTER_NAME = 'Mysql2'
40
+ include MySQL::DatabaseStatements
32
41
 
33
42
  def initialize(connection, logger, connection_options, config)
34
43
  super
35
- @visitor = BindSubstitution.new self
44
+ @prepared_statements = false unless config.key?(:prepared_statements)
36
45
  configure_connection
37
46
  end
38
47
 
39
- def supports_explain?
48
+ def supports_json?
49
+ !mariadb? && version >= '5.7.8'
50
+ end
51
+
52
+ def supports_comments?
53
+ true
54
+ end
55
+
56
+ def supports_comments_in_create?
57
+ true
58
+ end
59
+
60
+ def supports_savepoints?
40
61
  true
41
62
  end
42
63
 
@@ -52,21 +73,21 @@ module ActiveRecord
52
73
  end
53
74
  end
54
75
 
55
- def new_column(field, default, type, null, collation) # :nodoc:
56
- Column.new(field, default, type, null, collation)
57
- end
58
-
59
76
  def error_number(exception)
60
77
  exception.error_number if exception.respond_to?(:error_number)
61
78
  end
62
79
 
80
+ #--
63
81
  # QUOTING ==================================================
82
+ #++
64
83
 
65
84
  def quote_string(string)
66
85
  @connection.escape(string)
67
86
  end
68
87
 
88
+ #--
69
89
  # CONNECTION MANAGEMENT ====================================
90
+ #++
70
91
 
71
92
  def active?
72
93
  return false unless @connection
@@ -74,178 +95,22 @@ module ActiveRecord
74
95
  end
75
96
 
76
97
  def reconnect!
98
+ super
77
99
  disconnect!
78
100
  connect
79
101
  end
102
+ alias :reset! :reconnect!
80
103
 
81
104
  # Disconnects from the database if already connected.
82
105
  # Otherwise, this method does nothing.
83
106
  def disconnect!
107
+ super
84
108
  unless @connection.nil?
85
109
  @connection.close
86
110
  @connection = nil
87
111
  end
88
112
  end
89
113
 
90
- def reset!
91
- disconnect!
92
- connect
93
- end
94
-
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)
104
- end
105
-
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
154
-
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(' | ') + ' |'
163
- end
164
-
165
- def build_footer(nrows, elapsed)
166
- rows_label = nrows == 1 ? 'row' : 'rows'
167
- "#{nrows} #{rows_label} in set (%.2f sec)" % elapsed
168
- end
169
- end
170
-
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
211
- 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
114
  private
250
115
 
251
116
  def connect
@@ -255,25 +120,11 @@ module ActiveRecord
255
120
 
256
121
  def configure_connection
257
122
  @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)
123
+ super
273
124
  end
274
125
 
275
- def version
276
- @version ||= @connection.info[:version].scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
126
+ def full_version
127
+ @full_version ||= @connection.server_info[:version]
277
128
  end
278
129
  end
279
130
  end
@@ -0,0 +1,15 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ # PostgreSQL-specific extensions to column definitions in a table.
4
+ class PostgreSQLColumn < Column #:nodoc:
5
+ delegate :array, :oid, :fmod, to: :sql_type_metadata
6
+ alias :array? :array
7
+
8
+ def serial?
9
+ return unless default_function
10
+
11
+ %r{\Anextval\('"?#{table_name}_#{name}_seq"?'::regclass\)\z} === default_function
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,170 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module DatabaseStatements
5
+ def explain(arel, binds = [])
6
+ sql = "EXPLAIN #{to_sql(arel, binds)}"
7
+ PostgreSQL::ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', binds))
8
+ end
9
+
10
+ def select_value(arel, name = nil, binds = [])
11
+ arel, binds = binds_from_relation arel, binds
12
+ sql = to_sql(arel, binds)
13
+ execute_and_clear(sql, name, binds) do |result|
14
+ result.getvalue(0, 0) if result.ntuples > 0 && result.nfields > 0
15
+ end
16
+ end
17
+
18
+ def select_values(arel, name = nil, binds = [])
19
+ arel, binds = binds_from_relation arel, binds
20
+ sql = to_sql(arel, binds)
21
+ execute_and_clear(sql, name, binds) do |result|
22
+ if result.nfields > 0
23
+ result.column_values(0)
24
+ else
25
+ []
26
+ end
27
+ end
28
+ end
29
+
30
+ # Executes a SELECT query and returns an array of rows. Each row is an
31
+ # array of field values.
32
+ def select_rows(sql, name = nil, binds = [])
33
+ execute_and_clear(sql, name, binds) do |result|
34
+ result.values
35
+ end
36
+ end
37
+
38
+ # The internal PostgreSQL identifier of the money data type.
39
+ MONEY_COLUMN_TYPE_OID = 790 #:nodoc:
40
+ # The internal PostgreSQL identifier of the BYTEA data type.
41
+ BYTEA_COLUMN_TYPE_OID = 17 #:nodoc:
42
+
43
+ # create a 2D array representing the result set
44
+ def result_as_array(res) #:nodoc:
45
+ # check if we have any binary column and if they need escaping
46
+ ftypes = Array.new(res.nfields) do |i|
47
+ [i, res.ftype(i)]
48
+ end
49
+
50
+ rows = res.values
51
+ return rows unless ftypes.any? { |_, x|
52
+ x == BYTEA_COLUMN_TYPE_OID || x == MONEY_COLUMN_TYPE_OID
53
+ }
54
+
55
+ typehash = ftypes.group_by { |_, type| type }
56
+ binaries = typehash[BYTEA_COLUMN_TYPE_OID] || []
57
+ monies = typehash[MONEY_COLUMN_TYPE_OID] || []
58
+
59
+ rows.each do |row|
60
+ # unescape string passed BYTEA field (OID == 17)
61
+ binaries.each do |index, _|
62
+ row[index] = unescape_bytea(row[index])
63
+ end
64
+
65
+ # If this is a money type column and there are any currency symbols,
66
+ # then strip them off. Indeed it would be prettier to do this in
67
+ # PostgreSQLColumn.string_to_decimal but would break form input
68
+ # fields that call value_before_type_cast.
69
+ monies.each do |index, _|
70
+ data = row[index]
71
+ # Because money output is formatted according to the locale, there are two
72
+ # cases to consider (note the decimal separators):
73
+ # (1) $12,345,678.12
74
+ # (2) $12.345.678,12
75
+ case data
76
+ when /^-?\D+[\d,]+\.\d{2}$/ # (1)
77
+ data.gsub!(/[^-\d.]/, '')
78
+ when /^-?\D+[\d.]+,\d{2}$/ # (2)
79
+ data.gsub!(/[^-\d,]/, '').sub!(/,/, '.')
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ # Queries the database and returns the results in an Array-like object
86
+ def query(sql, name = nil) #:nodoc:
87
+ log(sql, name) do
88
+ result_as_array @connection.async_exec(sql)
89
+ end
90
+ end
91
+
92
+ # Executes an SQL statement, returning a PGresult object on success
93
+ # or raising a PGError exception otherwise.
94
+ # Note: the PGresult object is manually memory managed; if you don't
95
+ # need it specifically, you many want consider the exec_query wrapper.
96
+ def execute(sql, name = nil)
97
+ log(sql, name) do
98
+ @connection.async_exec(sql)
99
+ end
100
+ end
101
+
102
+ def exec_query(sql, name = 'SQL', binds = [], prepare: false)
103
+ execute_and_clear(sql, name, binds, prepare: prepare) do |result|
104
+ types = {}
105
+ fields = result.fields
106
+ fields.each_with_index do |fname, i|
107
+ ftype = result.ftype i
108
+ fmod = result.fmod i
109
+ types[fname] = get_oid_type(ftype, fmod, fname)
110
+ end
111
+ ActiveRecord::Result.new(fields, result.values, types)
112
+ end
113
+ end
114
+
115
+ def exec_delete(sql, name = 'SQL', binds = [])
116
+ execute_and_clear(sql, name, binds) {|result| result.cmd_tuples }
117
+ end
118
+ alias :exec_update :exec_delete
119
+
120
+ def sql_for_insert(sql, pk, id_value, sequence_name, binds) # :nodoc:
121
+ if pk.nil?
122
+ # Extract the table from the insert sql. Yuck.
123
+ table_ref = extract_table_ref_from_insert_sql(sql)
124
+ pk = primary_key(table_ref) if table_ref
125
+ end
126
+
127
+ if pk && use_insert_returning?
128
+ sql = "#{sql} RETURNING #{quote_column_name(pk)}"
129
+ end
130
+
131
+ super
132
+ end
133
+
134
+ def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
135
+ val = exec_query(sql, name, binds)
136
+ if !use_insert_returning? && pk
137
+ unless sequence_name
138
+ table_ref = extract_table_ref_from_insert_sql(sql)
139
+ sequence_name = default_sequence_name(table_ref, pk)
140
+ return val unless sequence_name
141
+ end
142
+ last_insert_id_result(sequence_name)
143
+ else
144
+ val
145
+ end
146
+ end
147
+
148
+ # Begins a transaction.
149
+ def begin_db_transaction
150
+ execute "BEGIN"
151
+ end
152
+
153
+ def begin_isolated_db_transaction(isolation)
154
+ begin_db_transaction
155
+ execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
156
+ end
157
+
158
+ # Commits a transaction.
159
+ def commit_db_transaction
160
+ execute "COMMIT"
161
+ end
162
+
163
+ # Aborts a transaction.
164
+ def exec_rollback_db_transaction
165
+ execute "ROLLBACK"
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,42 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ class ExplainPrettyPrinter # :nodoc:
5
+ # Pretty prints the result of an EXPLAIN in a way that resembles the output of the
6
+ # PostgreSQL shell:
7
+ #
8
+ # QUERY PLAN
9
+ # ------------------------------------------------------------------------------
10
+ # Nested Loop Left Join (cost=0.00..37.24 rows=8 width=0)
11
+ # Join Filter: (posts.user_id = users.id)
12
+ # -> Index Scan using users_pkey on users (cost=0.00..8.27 rows=1 width=4)
13
+ # Index Cond: (id = 1)
14
+ # -> Seq Scan on posts (cost=0.00..28.88 rows=8 width=4)
15
+ # Filter: (posts.user_id = 1)
16
+ # (6 rows)
17
+ #
18
+ def pp(result)
19
+ header = result.columns.first
20
+ lines = result.rows.map(&:first)
21
+
22
+ # We add 2 because there's one char of padding at both sides, note
23
+ # the extra hyphens in the example above.
24
+ width = [header, *lines].map(&:length).max + 2
25
+
26
+ pp = []
27
+
28
+ pp << header.center(width).rstrip
29
+ pp << '-' * width
30
+
31
+ pp += lines.map {|line| " #{line}"}
32
+
33
+ nrows = result.rows.length
34
+ rows_label = nrows == 1 ? 'row' : 'rows'
35
+ pp << "(#{nrows} #{rows_label})"
36
+
37
+ pp.join("\n") + "\n"
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,70 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class Array < Type::Value # :nodoc:
6
+ include Type::Helpers::Mutable
7
+
8
+ attr_reader :subtype, :delimiter
9
+ delegate :type, :user_input_in_time_zone, :limit, to: :subtype
10
+
11
+ def initialize(subtype, delimiter = ',')
12
+ @subtype = subtype
13
+ @delimiter = delimiter
14
+
15
+ @pg_encoder = PG::TextEncoder::Array.new name: "#{type}[]", delimiter: delimiter
16
+ @pg_decoder = PG::TextDecoder::Array.new name: "#{type}[]", delimiter: delimiter
17
+ end
18
+
19
+ def deserialize(value)
20
+ if value.is_a?(::String)
21
+ type_cast_array(@pg_decoder.decode(value), :deserialize)
22
+ else
23
+ super
24
+ end
25
+ end
26
+
27
+ def cast(value)
28
+ if value.is_a?(::String)
29
+ value = @pg_decoder.decode(value)
30
+ end
31
+ type_cast_array(value, :cast)
32
+ end
33
+
34
+ def serialize(value)
35
+ if value.is_a?(::Array)
36
+ @pg_encoder.encode(type_cast_array(value, :serialize))
37
+ else
38
+ super
39
+ end
40
+ end
41
+
42
+ def ==(other)
43
+ other.is_a?(Array) &&
44
+ subtype == other.subtype &&
45
+ delimiter == other.delimiter
46
+ end
47
+
48
+ def type_cast_for_schema(value)
49
+ return super unless value.is_a?(::Array)
50
+ "[" + value.map { |v| subtype.type_cast_for_schema(v) }.join(", ") + "]"
51
+ end
52
+
53
+ def map(value, &block)
54
+ value.map(&block)
55
+ end
56
+
57
+ private
58
+
59
+ def type_cast_array(value, method)
60
+ if value.is_a?(::Array)
61
+ value.map { |item| type_cast_array(item, method) }
62
+ else
63
+ @subtype.public_send(method, value)
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,52 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class Bit < Type::Value # :nodoc:
6
+ def type
7
+ :bit
8
+ end
9
+
10
+ def cast(value)
11
+ if ::String === value
12
+ case value
13
+ when /^0x/i
14
+ value[2..-1].hex.to_s(2) # Hexadecimal notation
15
+ else
16
+ value # Bit-string notation
17
+ end
18
+ else
19
+ value
20
+ end
21
+ end
22
+
23
+ def serialize(value)
24
+ Data.new(super) if value
25
+ end
26
+
27
+ class Data
28
+ def initialize(value)
29
+ @value = value
30
+ end
31
+
32
+ def to_s
33
+ value
34
+ end
35
+
36
+ def binary?
37
+ /\A[01]*\Z/ === value
38
+ end
39
+
40
+ def hex?
41
+ /\A[0-9A-F]*\Z/i === value
42
+ end
43
+
44
+ protected
45
+
46
+ attr_reader :value
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,13 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class BitVarying < OID::Bit # :nodoc:
6
+ def type
7
+ :bit_varying
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class Bytea < Type::Binary # :nodoc:
6
+ def deserialize(value)
7
+ return if value.nil?
8
+ return value.to_s if value.is_a?(Type::Binary::Data)
9
+ PGconn.unescape_bytea(super)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end