activerecord 4.2.0 → 5.2.8.1

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 +640 -928
  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 +264 -247
  8. data/lib/active_record/association_relation.rb +24 -6
  9. data/lib/active_record/associations/alias_tracker.rb +29 -35
  10. data/lib/active_record/associations/association.rb +87 -41
  11. data/lib/active_record/associations/association_scope.rb +106 -132
  12. data/lib/active_record/associations/belongs_to_association.rb +55 -36
  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 +14 -23
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +50 -39
  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 +145 -266
  22. data/lib/active_record/associations/collection_proxy.rb +242 -138
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +35 -75
  25. data/lib/active_record/associations/has_many_through_association.rb +51 -69
  26. data/lib/active_record/associations/has_one_association.rb +39 -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 -81
  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 +134 -154
  32. data/lib/active_record/associations/preloader/association.rb +85 -116
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -74
  34. data/lib/active_record/associations/preloader.rb +83 -93
  35. data/lib/active_record/associations/singular_association.rb +27 -40
  36. data/lib/active_record/associations/through_association.rb +48 -23
  37. data/lib/active_record/associations.rb +1732 -1596
  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 +12 -5
  41. data/lib/active_record/attribute_methods/dirty.rb +94 -125
  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 +62 -36
  47. data/lib/active_record/attribute_methods/write.rb +31 -46
  48. data/lib/active_record/attribute_methods.rb +170 -117
  49. data/lib/active_record/attributes.rb +201 -74
  50. data/lib/active_record/autosave_association.rb +118 -45
  51. data/lib/active_record/base.rb +60 -48
  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 +37 -13
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -284
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +254 -87
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +72 -22
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -52
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +6 -4
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -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 +617 -212
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +139 -75
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +332 -191
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +567 -563
  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 +42 -195
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +46 -115
  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 -57
  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 +5 -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 -13
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +7 -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 -1
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +65 -51
  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 +466 -280
  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 +439 -330
  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 -324
  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 +205 -202
  129. data/lib/active_record/counter_cache.rb +80 -37
  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 +136 -90
  133. data/lib/active_record/errors.rb +180 -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 +11 -6
  137. data/lib/active_record/fixture_set/file.rb +35 -9
  138. data/lib/active_record/fixtures.rb +193 -135
  139. data/lib/active_record/gem_version.rb +5 -3
  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 +48 -0
  144. data/lib/active_record/locale/en.yml +3 -2
  145. data/lib/active_record/locking/optimistic.rb +92 -98
  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 +594 -267
  152. data/lib/active_record/model_schema.rb +292 -111
  153. data/lib/active_record/nested_attributes.rb +266 -214
  154. data/lib/active_record/no_touching.rb +8 -2
  155. data/lib/active_record/null_relation.rb +24 -37
  156. data/lib/active_record/persistence.rb +350 -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 +117 -35
  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 +160 -174
  163. data/lib/active_record/readonly_attributes.rb +5 -4
  164. data/lib/active_record/reflection.rb +447 -288
  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 +259 -244
  168. data/lib/active_record/relation/delegation.rb +67 -60
  169. data/lib/active_record/relation/finder_methods.rb +290 -253
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +91 -68
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -23
  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 +118 -92
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +446 -389
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +18 -16
  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 -339
  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 -19
  193. data/lib/active_record/scoping/default.rb +102 -84
  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 +136 -95
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +59 -89
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -31
  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 +208 -123
  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 -41
  212. data/lib/active_record/type/date_time.rb +4 -38
  213. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  214. data/lib/active_record/type/hash_lookup_type_map.rb +13 -5
  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 +30 -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 +41 -32
  231. data/lib/active_record/validations.rb +38 -35
  232. data/lib/active_record/version.rb +3 -1
  233. data/lib/active_record.rb +36 -21
  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 -6
  238. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -7
  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.rb +7 -5
  243. metadata +77 -53
  244. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  245. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  246. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  247. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  248. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  249. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  250. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  251. data/lib/active_record/attribute.rb +0 -149
  252. data/lib/active_record/attribute_set/builder.rb +0 -86
  253. data/lib/active_record/attribute_set.rb +0 -77
  254. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  255. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  256. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  257. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  258. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  259. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  260. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  261. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  262. data/lib/active_record/type/big_integer.rb +0 -13
  263. data/lib/active_record/type/binary.rb +0 -50
  264. data/lib/active_record/type/boolean.rb +0 -30
  265. data/lib/active_record/type/decimal.rb +0 -40
  266. data/lib/active_record/type/decorator.rb +0 -14
  267. data/lib/active_record/type/float.rb +0 -19
  268. data/lib/active_record/type/integer.rb +0 -55
  269. data/lib/active_record/type/mutable.rb +0 -16
  270. data/lib/active_record/type/numeric.rb +0 -36
  271. data/lib/active_record/type/string.rb +0 -36
  272. data/lib/active_record/type/time_value.rb +0 -38
  273. data/lib/active_record/type/value.rb +0 -101
  274. /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -1,31 +1,34 @@
1
- require 'active_record/connection_adapters/abstract_adapter'
2
- require 'active_record/connection_adapters/statement_pool'
1
+ # frozen_string_literal: true
3
2
 
4
- require 'active_record/connection_adapters/postgresql/utils'
5
- require 'active_record/connection_adapters/postgresql/column'
6
- require 'active_record/connection_adapters/postgresql/oid'
7
- require 'active_record/connection_adapters/postgresql/quoting'
8
- require 'active_record/connection_adapters/postgresql/referential_integrity'
9
- require 'active_record/connection_adapters/postgresql/schema_definitions'
10
- require 'active_record/connection_adapters/postgresql/schema_statements'
11
- require 'active_record/connection_adapters/postgresql/database_statements'
3
+ # Make sure we're using pg high enough for type casts and Ruby 2.2+ compatibility
4
+ gem "pg", ">= 0.18", "< 2.0"
5
+ require "pg"
12
6
 
13
- require 'arel/visitors/bind_visitor'
14
-
15
- # Make sure we're using pg high enough for PGResult#values
16
- gem 'pg', '~> 0.15'
17
- require 'pg'
7
+ # Use async_exec instead of exec_params on pg versions before 1.1
8
+ class ::PG::Connection
9
+ unless self.public_method_defined?(:async_exec_params)
10
+ remove_method :exec_params
11
+ alias exec_params async_exec
12
+ end
13
+ end
18
14
 
19
- require 'ipaddr'
15
+ require "active_record/connection_adapters/abstract_adapter"
16
+ require "active_record/connection_adapters/statement_pool"
17
+ require "active_record/connection_adapters/postgresql/column"
18
+ require "active_record/connection_adapters/postgresql/database_statements"
19
+ require "active_record/connection_adapters/postgresql/explain_pretty_printer"
20
+ require "active_record/connection_adapters/postgresql/oid"
21
+ require "active_record/connection_adapters/postgresql/quoting"
22
+ require "active_record/connection_adapters/postgresql/referential_integrity"
23
+ require "active_record/connection_adapters/postgresql/schema_creation"
24
+ require "active_record/connection_adapters/postgresql/schema_definitions"
25
+ require "active_record/connection_adapters/postgresql/schema_dumper"
26
+ require "active_record/connection_adapters/postgresql/schema_statements"
27
+ require "active_record/connection_adapters/postgresql/type_metadata"
28
+ require "active_record/connection_adapters/postgresql/utils"
20
29
 
21
30
  module ActiveRecord
22
31
  module ConnectionHandling # :nodoc:
23
- VALID_CONN_PARAMS = [:host, :hostaddr, :port, :dbname, :user, :password, :connect_timeout,
24
- :client_encoding, :options, :application_name, :fallback_application_name,
25
- :keepalives, :keepalives_idle, :keepalives_interval, :keepalives_count,
26
- :tty, :sslmode, :requiressl, :sslcompression, :sslcert, :sslkey,
27
- :sslrootcert, :sslcrl, :requirepeer, :krbsrvname, :gsslib, :service]
28
-
29
32
  # Establishes a connection to the database that's used by all Active Record objects
30
33
  def postgresql_connection(config)
31
34
  conn_params = config.symbolize_keys
@@ -36,10 +39,11 @@ module ActiveRecord
36
39
  conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
37
40
  conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
38
41
 
39
- # Forward only valid config params to PGconn.connect.
40
- conn_params.keep_if { |k, _| VALID_CONN_PARAMS.include?(k) }
42
+ # Forward only valid config params to PG::Connection.connect.
43
+ valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
44
+ conn_params.slice!(*valid_conn_param_keys)
41
45
 
42
- # The postgres drivers don't allow the creation of an unconnected PGconn object,
46
+ # The postgres drivers don't allow the creation of an unconnected PG::Connection object,
43
47
  # so just pass a nil connection object for the time being.
44
48
  ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, conn_params, config)
45
49
  end
@@ -64,24 +68,23 @@ module ActiveRecord
64
68
  # <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
65
69
  # * <tt>:variables</tt> - An optional hash of additional parameters that
66
70
  # will be used in <tt>SET SESSION key = val</tt> calls on the connection.
67
- # * <tt>:insert_returning</tt> - An optional boolean to control the use or <tt>RETURNING</tt> for <tt>INSERT</tt> statements
71
+ # * <tt>:insert_returning</tt> - An optional boolean to control the use of <tt>RETURNING</tt> for <tt>INSERT</tt> statements
68
72
  # defaults to true.
69
73
  #
70
74
  # Any further options are used as connection parameters to libpq. See
71
- # http://www.postgresql.org/docs/9.1/static/libpq-connect.html for the
75
+ # https://www.postgresql.org/docs/current/static/libpq-connect.html for the
72
76
  # list of parameters.
73
77
  #
74
78
  # In addition, default connection parameters of libpq can be set per environment variables.
75
- # See http://www.postgresql.org/docs/9.1/static/libpq-envars.html .
79
+ # See https://www.postgresql.org/docs/current/static/libpq-envars.html .
76
80
  class PostgreSQLAdapter < AbstractAdapter
77
- ADAPTER_NAME = 'PostgreSQL'.freeze
81
+ ADAPTER_NAME = "PostgreSQL".freeze
78
82
 
79
83
  NATIVE_DATABASE_TYPES = {
80
- primary_key: "serial primary key",
81
- bigserial: "bigserial",
84
+ primary_key: "bigserial primary key",
82
85
  string: { name: "character varying" },
83
86
  text: { name: "text" },
84
- integer: { name: "integer" },
87
+ integer: { name: "integer", limit: 4 },
85
88
  float: { name: "float" },
86
89
  decimal: { name: "decimal" },
87
90
  datetime: { name: "timestamp" },
@@ -95,7 +98,6 @@ module ActiveRecord
95
98
  int8range: { name: "int8range" },
96
99
  binary: { name: "bytea" },
97
100
  boolean: { name: "boolean" },
98
- bigint: { name: "bigint" },
99
101
  xml: { name: "xml" },
100
102
  tsvector: { name: "tsvector" },
101
103
  hstore: { name: "hstore" },
@@ -108,9 +110,17 @@ module ActiveRecord
108
110
  ltree: { name: "ltree" },
109
111
  citext: { name: "citext" },
110
112
  point: { name: "point" },
113
+ line: { name: "line" },
114
+ lseg: { name: "lseg" },
115
+ box: { name: "box" },
116
+ path: { name: "path" },
117
+ polygon: { name: "polygon" },
118
+ circle: { name: "circle" },
111
119
  bit: { name: "bit" },
112
120
  bit_varying: { name: "bit varying" },
113
121
  money: { name: "money" },
122
+ interval: { name: "interval" },
123
+ oid: { name: "oid" },
114
124
  }
115
125
 
116
126
  OID = PostgreSQL::OID #:nodoc:
@@ -119,143 +129,119 @@ module ActiveRecord
119
129
  include PostgreSQL::ReferentialIntegrity
120
130
  include PostgreSQL::SchemaStatements
121
131
  include PostgreSQL::DatabaseStatements
122
- include Savepoints
123
132
 
124
- def schema_creation # :nodoc:
125
- PostgreSQL::SchemaCreation.new self
133
+ def supports_bulk_alter?
134
+ true
126
135
  end
127
136
 
128
- # Adds +:array+ option to the default set provided by the
129
- # AbstractAdapter
130
- def prepare_column_options(column, types) # :nodoc:
131
- spec = super
132
- spec[:array] = 'true' if column.respond_to?(:array) && column.array
133
- spec[:default] = "\"#{column.default_function}\"" if column.default_function
134
- spec
137
+ def supports_index_sort_order?
138
+ true
135
139
  end
136
140
 
137
- # Adds +:array+ as a valid migration key
138
- def migration_keys
139
- super + [:array]
141
+ def supports_partial_index?
142
+ true
140
143
  end
141
144
 
142
- # Returns +true+, since this connection adapter supports prepared statement
143
- # caching.
144
- def supports_statement_cache?
145
+ def supports_expression_index?
145
146
  true
146
147
  end
147
148
 
148
- def supports_index_sort_order?
149
+ def supports_transaction_isolation?
149
150
  true
150
151
  end
151
152
 
152
- def supports_partial_index?
153
+ def supports_foreign_keys?
153
154
  true
154
155
  end
155
156
 
156
- def supports_transaction_isolation?
157
+ def supports_validate_constraints?
157
158
  true
158
159
  end
159
160
 
160
- def supports_foreign_keys?
161
+ def supports_views?
161
162
  true
162
163
  end
163
164
 
164
- def supports_views?
165
+ def supports_datetime_with_precision?
166
+ true
167
+ end
168
+
169
+ def supports_json?
170
+ postgresql_version >= 90200
171
+ end
172
+
173
+ def supports_comments?
174
+ true
175
+ end
176
+
177
+ def supports_savepoints?
165
178
  true
166
179
  end
167
180
 
168
181
  def index_algorithms
169
- { concurrently: 'CONCURRENTLY' }
182
+ { concurrently: "CONCURRENTLY" }
170
183
  end
171
184
 
172
- class StatementPool < ConnectionAdapters::StatementPool
185
+ class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
173
186
  def initialize(connection, max)
174
- super
187
+ super(max)
188
+ @connection = connection
175
189
  @counter = 0
176
- @cache = Hash.new { |h,pid| h[pid] = {} }
177
190
  end
178
191
 
179
- def each(&block); cache.each(&block); end
180
- def key?(key); cache.key?(key); end
181
- def [](key); cache[key]; end
182
- def length; cache.length; end
183
-
184
192
  def next_key
185
193
  "a#{@counter + 1}"
186
194
  end
187
195
 
188
196
  def []=(sql, key)
189
- while @max <= cache.size
190
- dealloc(cache.shift.last)
191
- end
192
- @counter += 1
193
- cache[sql] = key
194
- end
195
-
196
- def clear
197
- cache.each_value do |stmt_key|
198
- dealloc stmt_key
199
- end
200
- cache.clear
201
- end
202
-
203
- def delete(sql_key)
204
- dealloc cache[sql_key]
205
- cache.delete sql_key
197
+ super.tap { @counter += 1 }
206
198
  end
207
199
 
208
200
  private
209
-
210
- def cache
211
- @cache[Process.pid]
212
- end
213
-
214
201
  def dealloc(key)
215
202
  @connection.query "DEALLOCATE #{key}" if connection_active?
203
+ rescue PG::Error
216
204
  end
217
205
 
218
206
  def connection_active?
219
- @connection.status == PGconn::CONNECTION_OK
220
- rescue PGError
207
+ @connection.status == PG::CONNECTION_OK
208
+ rescue PG::Error
221
209
  false
222
210
  end
223
211
  end
224
212
 
225
213
  # Initializes and connects a PostgreSQL adapter.
226
214
  def initialize(connection, logger, connection_parameters, config)
227
- super(connection, logger)
215
+ super(connection, logger, config)
228
216
 
229
- @visitor = Arel::Visitors::PostgreSQL.new self
230
- if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
231
- @prepared_statements = true
232
- else
233
- @prepared_statements = false
234
- end
235
-
236
- @connection_parameters, @config = connection_parameters, config
217
+ @connection_parameters = connection_parameters
237
218
 
238
219
  # @local_tz is initialized as nil to avoid warnings when connect tries to use it
239
220
  @local_tz = nil
240
- @table_alias_length = nil
221
+ @max_identifier_length = nil
241
222
 
242
223
  connect
224
+ add_pg_encoders
243
225
  @statements = StatementPool.new @connection,
244
- self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 })
226
+ self.class.type_cast_config_to_integer(config[:statement_limit])
245
227
 
246
- if postgresql_version < 80200
247
- raise "Your version of PostgreSQL (#{postgresql_version}) is too old, please upgrade!"
228
+ if postgresql_version < 90100
229
+ raise "Your version of PostgreSQL (#{postgresql_version}) is too old. Active Record supports PostgreSQL >= 9.1."
248
230
  end
249
231
 
232
+ add_pg_decoders
233
+
250
234
  @type_map = Type::HashLookupTypeMap.new
251
- initialize_type_map(type_map)
252
- @local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
235
+ initialize_type_map
236
+ @local_tz = execute("SHOW TIME ZONE", "SCHEMA").first["TimeZone"]
253
237
  @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
254
238
  end
255
239
 
256
240
  # Clears the prepared statements cache.
257
241
  def clear_cache!
258
- @statements.clear
242
+ @lock.synchronize do
243
+ @statements.clear
244
+ end
259
245
  end
260
246
 
261
247
  def truncate(table_name, name = nil)
@@ -264,59 +250,62 @@ module ActiveRecord
264
250
 
265
251
  # Is this connection alive and ready for queries?
266
252
  def active?
267
- @connection.query 'SELECT 1'
253
+ @lock.synchronize do
254
+ @connection.query "SELECT 1"
255
+ end
268
256
  true
269
- rescue PGError
257
+ rescue PG::Error
270
258
  false
271
259
  end
272
260
 
273
261
  # Close then reopen the connection.
274
262
  def reconnect!
275
- super
276
- @connection.reset
277
- configure_connection
263
+ @lock.synchronize do
264
+ super
265
+ @connection.reset
266
+ configure_connection
267
+ end
278
268
  end
279
269
 
280
270
  def reset!
281
- clear_cache!
282
- reset_transaction
283
- unless @connection.transaction_status == ::PG::PQTRANS_IDLE
284
- @connection.query 'ROLLBACK'
271
+ @lock.synchronize do
272
+ clear_cache!
273
+ reset_transaction
274
+ unless @connection.transaction_status == ::PG::PQTRANS_IDLE
275
+ @connection.query "ROLLBACK"
276
+ end
277
+ @connection.query "DISCARD ALL"
278
+ configure_connection
285
279
  end
286
- @connection.query 'DISCARD ALL'
287
- configure_connection
288
280
  end
289
281
 
290
282
  # Disconnects from the database if already connected. Otherwise, this
291
283
  # method does nothing.
292
284
  def disconnect!
293
- super
294
- @connection.close rescue nil
285
+ @lock.synchronize do
286
+ super
287
+ @connection.close rescue nil
288
+ end
289
+ end
290
+
291
+ def discard! # :nodoc:
292
+ @connection.socket_io.reopen(IO::NULL) rescue nil
293
+ @connection = nil
295
294
  end
296
295
 
297
296
  def native_database_types #:nodoc:
298
297
  NATIVE_DATABASE_TYPES
299
298
  end
300
299
 
301
- # Returns true, since this connection adapter supports migrations.
302
- def supports_migrations?
303
- true
300
+ def set_standard_conforming_strings
301
+ execute("SET standard_conforming_strings = on", "SCHEMA")
304
302
  end
305
303
 
306
- # Does PostgreSQL support finding primary key on non-Active Record tables?
307
- def supports_primary_key? #:nodoc:
304
+ def supports_ddl_transactions?
308
305
  true
309
306
  end
310
307
 
311
- # Enable standard-conforming strings if available.
312
- def set_standard_conforming_strings
313
- old, self.client_min_messages = client_min_messages, 'panic'
314
- execute('SET standard_conforming_strings = on', 'SCHEMA') rescue nil
315
- ensure
316
- self.client_min_messages = old
317
- end
318
-
319
- def supports_ddl_transactions?
308
+ def supports_advisory_locks?
320
309
  true
321
310
  end
322
311
 
@@ -324,13 +313,12 @@ module ActiveRecord
324
313
  true
325
314
  end
326
315
 
327
- # Returns true if pg > 9.1
328
316
  def supports_extensions?
329
- postgresql_version >= 90100
317
+ true
330
318
  end
331
319
 
332
- # Range datatypes weren't introduced until PostgreSQL 9.2
333
320
  def supports_ranges?
321
+ # Range datatypes weren't introduced until PostgreSQL 9.2
334
322
  postgresql_version >= 90200
335
323
  end
336
324
 
@@ -338,6 +326,28 @@ module ActiveRecord
338
326
  postgresql_version >= 90300
339
327
  end
340
328
 
329
+ def supports_foreign_tables?
330
+ postgresql_version >= 90300
331
+ end
332
+
333
+ def supports_pgcrypto_uuid?
334
+ postgresql_version >= 90400
335
+ end
336
+
337
+ def get_advisory_lock(lock_id) # :nodoc:
338
+ unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
339
+ raise(ArgumentError, "PostgreSQL requires advisory lock ids to be a signed 64 bit integer")
340
+ end
341
+ query_value("SELECT pg_try_advisory_lock(#{lock_id})")
342
+ end
343
+
344
+ def release_advisory_lock(lock_id) # :nodoc:
345
+ unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
346
+ raise(ArgumentError, "PostgreSQL requires advisory lock ids to be a signed 64 bit integer")
347
+ end
348
+ query_value("SELECT pg_advisory_unlock(#{lock_id})")
349
+ end
350
+
341
351
  def enable_extension(name)
342
352
  exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
343
353
  reload_type_map
@@ -351,49 +361,31 @@ module ActiveRecord
351
361
  end
352
362
 
353
363
  def extension_enabled?(name)
354
- if supports_extensions?
355
- res = exec_query "SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled",
356
- 'SCHEMA'
357
- res.cast_values.first
358
- end
364
+ res = exec_query("SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled", "SCHEMA")
365
+ res.cast_values.first
359
366
  end
360
367
 
361
368
  def extensions
362
- if supports_extensions?
363
- exec_query("SELECT extname from pg_extension", "SCHEMA").cast_values
364
- else
365
- super
366
- end
369
+ exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
367
370
  end
368
371
 
369
372
  # Returns the configured supported identifier length supported by PostgreSQL
370
- def table_alias_length
371
- @table_alias_length ||= query('SHOW max_identifier_length', 'SCHEMA')[0][0].to_i
373
+ def max_identifier_length
374
+ @max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
372
375
  end
376
+ alias table_alias_length max_identifier_length
377
+ alias index_name_length max_identifier_length
373
378
 
374
379
  # Set the authorized user for this session
375
380
  def session_auth=(user)
376
381
  clear_cache!
377
- exec_query "SET SESSION AUTHORIZATION #{user}"
382
+ execute("SET SESSION AUTHORIZATION #{user}")
378
383
  end
379
384
 
380
385
  def use_insert_returning?
381
386
  @use_insert_returning
382
387
  end
383
388
 
384
- def valid_type?(type)
385
- !native_database_types[type].nil?
386
- end
387
-
388
- def update_table_definition(table_name, base) #:nodoc:
389
- PostgreSQL::Table.new(table_name, base)
390
- end
391
-
392
- def lookup_cast_type(sql_type) # :nodoc:
393
- oid = execute("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").first['oid'].to_i
394
- super(oid)
395
- end
396
-
397
389
  def column_name_for_operation(operation, node) # :nodoc:
398
390
  OPERATION_ALIASES.fetch(operation) { operation.downcase }
399
391
  end
@@ -404,94 +396,115 @@ module ActiveRecord
404
396
  "average" => "avg",
405
397
  }
406
398
 
407
- protected
399
+ # Returns the version of the connected PostgreSQL server.
400
+ def postgresql_version
401
+ @connection.server_version
402
+ end
408
403
 
409
- # Returns the version of the connected PostgreSQL server.
410
- def postgresql_version
411
- @connection.server_version
412
- end
404
+ def default_index_type?(index) # :nodoc:
405
+ index.using == :btree || super
406
+ end
413
407
 
414
- # See http://www.postgresql.org/docs/9.1/static/errcodes-appendix.html
408
+ private
409
+ # See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
410
+ VALUE_LIMIT_VIOLATION = "22001"
411
+ NUMERIC_VALUE_OUT_OF_RANGE = "22003"
412
+ NOT_NULL_VIOLATION = "23502"
415
413
  FOREIGN_KEY_VIOLATION = "23503"
416
414
  UNIQUE_VIOLATION = "23505"
415
+ SERIALIZATION_FAILURE = "40001"
416
+ DEADLOCK_DETECTED = "40P01"
417
+ LOCK_NOT_AVAILABLE = "55P03"
418
+ QUERY_CANCELED = "57014"
417
419
 
418
420
  def translate_exception(exception, message)
419
421
  return exception unless exception.respond_to?(:result)
420
422
 
421
- case exception.result.try(:error_field, PGresult::PG_DIAG_SQLSTATE)
423
+ case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
422
424
  when UNIQUE_VIOLATION
423
- RecordNotUnique.new(message, exception)
425
+ RecordNotUnique.new(message)
424
426
  when FOREIGN_KEY_VIOLATION
425
- InvalidForeignKey.new(message, exception)
427
+ InvalidForeignKey.new(message)
428
+ when VALUE_LIMIT_VIOLATION
429
+ ValueTooLong.new(message)
430
+ when NUMERIC_VALUE_OUT_OF_RANGE
431
+ RangeError.new(message)
432
+ when NOT_NULL_VIOLATION
433
+ NotNullViolation.new(message)
434
+ when SERIALIZATION_FAILURE
435
+ SerializationFailure.new(message)
436
+ when DEADLOCK_DETECTED
437
+ Deadlocked.new(message)
438
+ when LOCK_NOT_AVAILABLE
439
+ LockWaitTimeout.new(message)
440
+ when QUERY_CANCELED
441
+ QueryCanceled.new(message)
426
442
  else
427
443
  super
428
444
  end
429
445
  end
430
446
 
431
- private
432
-
433
- def get_oid_type(oid, fmod, column_name, sql_type = '') # :nodoc:
447
+ def get_oid_type(oid, fmod, column_name, sql_type = "".freeze)
434
448
  if !type_map.key?(oid)
435
- load_additional_types(type_map, [oid])
449
+ load_additional_types([oid])
436
450
  end
437
451
 
438
452
  type_map.fetch(oid, fmod, sql_type) {
439
453
  warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
440
- Type::Value.new.tap do |cast_type|
454
+ Type.default_value.tap do |cast_type|
441
455
  type_map.register_type(oid, cast_type)
442
456
  end
443
457
  }
444
458
  end
445
459
 
446
- def initialize_type_map(m) # :nodoc:
447
- register_class_with_limit m, 'int2', OID::Integer
448
- m.alias_type 'int4', 'int2'
449
- m.alias_type 'int8', 'int2'
450
- m.alias_type 'oid', 'int2'
451
- m.register_type 'float4', OID::Float.new
452
- m.alias_type 'float8', 'float4'
453
- m.register_type 'text', Type::Text.new
454
- register_class_with_limit m, 'varchar', Type::String
455
- m.alias_type 'char', 'varchar'
456
- m.alias_type 'name', 'varchar'
457
- m.alias_type 'bpchar', 'varchar'
458
- m.register_type 'bool', Type::Boolean.new
459
- register_class_with_limit m, 'bit', OID::Bit
460
- register_class_with_limit m, 'varbit', OID::BitVarying
461
- m.alias_type 'timestamptz', 'timestamp'
462
- m.register_type 'date', OID::Date.new
463
- m.register_type 'time', OID::Time.new
464
-
465
- m.register_type 'money', OID::Money.new
466
- m.register_type 'bytea', OID::Bytea.new
467
- m.register_type 'point', OID::Point.new
468
- m.register_type 'hstore', OID::Hstore.new
469
- m.register_type 'json', OID::Json.new
470
- m.register_type 'jsonb', OID::Jsonb.new
471
- m.register_type 'cidr', OID::Cidr.new
472
- m.register_type 'inet', OID::Inet.new
473
- m.register_type 'uuid', OID::Uuid.new
474
- m.register_type 'xml', OID::Xml.new
475
- m.register_type 'tsvector', OID::SpecializedString.new(:tsvector)
476
- m.register_type 'macaddr', OID::SpecializedString.new(:macaddr)
477
- m.register_type 'citext', OID::SpecializedString.new(:citext)
478
- m.register_type 'ltree', OID::SpecializedString.new(:ltree)
479
-
480
- # FIXME: why are we keeping these types as strings?
481
- m.alias_type 'interval', 'varchar'
482
- m.alias_type 'path', 'varchar'
483
- m.alias_type 'line', 'varchar'
484
- m.alias_type 'polygon', 'varchar'
485
- m.alias_type 'circle', 'varchar'
486
- m.alias_type 'lseg', 'varchar'
487
- m.alias_type 'box', 'varchar'
488
-
489
- m.register_type 'timestamp' do |_, _, sql_type|
460
+ def initialize_type_map(m = type_map)
461
+ m.register_type "int2", Type::Integer.new(limit: 2)
462
+ m.register_type "int4", Type::Integer.new(limit: 4)
463
+ m.register_type "int8", Type::Integer.new(limit: 8)
464
+ m.register_type "oid", OID::Oid.new
465
+ m.register_type "float4", Type::Float.new
466
+ m.alias_type "float8", "float4"
467
+ m.register_type "text", Type::Text.new
468
+ register_class_with_limit m, "varchar", Type::String
469
+ m.alias_type "char", "varchar"
470
+ m.alias_type "name", "varchar"
471
+ m.alias_type "bpchar", "varchar"
472
+ m.register_type "bool", Type::Boolean.new
473
+ register_class_with_limit m, "bit", OID::Bit
474
+ register_class_with_limit m, "varbit", OID::BitVarying
475
+ m.alias_type "timestamptz", "timestamp"
476
+ m.register_type "date", OID::Date.new
477
+
478
+ m.register_type "money", OID::Money.new
479
+ m.register_type "bytea", OID::Bytea.new
480
+ m.register_type "point", OID::Point.new
481
+ m.register_type "hstore", OID::Hstore.new
482
+ m.register_type "json", Type::Json.new
483
+ m.register_type "jsonb", OID::Jsonb.new
484
+ m.register_type "cidr", OID::Cidr.new
485
+ m.register_type "inet", OID::Inet.new
486
+ m.register_type "uuid", OID::Uuid.new
487
+ m.register_type "xml", OID::Xml.new
488
+ m.register_type "tsvector", OID::SpecializedString.new(:tsvector)
489
+ m.register_type "macaddr", OID::SpecializedString.new(:macaddr)
490
+ m.register_type "citext", OID::SpecializedString.new(:citext)
491
+ m.register_type "ltree", OID::SpecializedString.new(:ltree)
492
+ m.register_type "line", OID::SpecializedString.new(:line)
493
+ m.register_type "lseg", OID::SpecializedString.new(:lseg)
494
+ m.register_type "box", OID::SpecializedString.new(:box)
495
+ m.register_type "path", OID::SpecializedString.new(:path)
496
+ m.register_type "polygon", OID::SpecializedString.new(:polygon)
497
+ m.register_type "circle", OID::SpecializedString.new(:circle)
498
+
499
+ m.register_type "interval" do |_, _, sql_type|
490
500
  precision = extract_precision(sql_type)
491
- OID::DateTime.new(precision: precision)
501
+ OID::SpecializedString.new(:interval, precision: precision)
492
502
  end
493
503
 
494
- m.register_type 'numeric' do |_, fmod, sql_type|
504
+ register_class_with_precision m, "time", Type::Time
505
+ register_class_with_precision m, "timestamp", OID::DateTime
506
+
507
+ m.register_type "numeric" do |_, fmod, sql_type|
495
508
  precision = extract_precision(sql_type)
496
509
  scale = extract_scale(sql_type)
497
510
 
@@ -511,51 +524,47 @@ module ActiveRecord
511
524
  end
512
525
  end
513
526
 
514
- load_additional_types(m)
515
- end
516
-
517
- def extract_limit(sql_type) # :nodoc:
518
- case sql_type
519
- when /^bigint/i, /^int8/i
520
- 8
521
- when /^smallint/i
522
- 2
523
- else
524
- super
525
- end
527
+ load_additional_types
526
528
  end
527
529
 
528
530
  # Extracts the value from a PostgreSQL column default definition.
529
- def extract_value_from_default(oid, default) # :nodoc:
531
+ def extract_value_from_default(default)
530
532
  case default
531
533
  # Quoted types
532
- when /\A[\(B]?'(.*)'::/m
533
- $1.gsub(/''/, "'")
534
+ when /\A[\(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
535
+ # The default 'now'::date is CURRENT_DATE
536
+ if $1 == "now".freeze && $2 == "date".freeze
537
+ nil
538
+ else
539
+ $1.gsub("''".freeze, "'".freeze)
540
+ end
534
541
  # Boolean types
535
- when 'true', 'false'
536
- default
542
+ when "true".freeze, "false".freeze
543
+ default
537
544
  # Numeric types
538
- when /\A\(?(-?\d+(\.\d*)?)\)?(::bigint)?\z/
539
- $1
545
+ when /\A\(?(-?\d+(\.\d*)?)\)?(::bigint)?\z/
546
+ $1
540
547
  # Object identifier types
541
- when /\A-?\d+\z/
542
- $1
543
- else
544
- # Anything else is blank, some user type, or some function
545
- # and we can't know the value of that, so return nil.
546
- nil
548
+ when /\A-?\d+\z/
549
+ $1
550
+ else
551
+ # Anything else is blank, some user type, or some function
552
+ # and we can't know the value of that, so return nil.
553
+ nil
547
554
  end
548
555
  end
549
556
 
550
- def extract_default_function(default_value, default) # :nodoc:
557
+ def extract_default_function(default_value, default)
551
558
  default if has_default_function?(default_value, default)
552
559
  end
553
560
 
554
- def has_default_function?(default_value, default) # :nodoc:
555
- !default_value && (%r{\w+\(.*\)} === default)
561
+ def has_default_function?(default_value, default)
562
+ !default_value && %r{\w+\(.*\)|\(.*\)::\w+|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
556
563
  end
557
564
 
558
- def load_additional_types(type_map, oids = nil) # :nodoc:
565
+ def load_additional_types(oids = nil)
566
+ initializer = OID::TypeMapInitializer.new(type_map)
567
+
559
568
  if supports_ranges?
560
569
  query = <<-SQL
561
570
  SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
@@ -571,56 +580,86 @@ module ActiveRecord
571
580
 
572
581
  if oids
573
582
  query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
583
+ else
584
+ query += initializer.query_conditions_for_initial_load
574
585
  end
575
586
 
576
- initializer = OID::TypeMapInitializer.new(type_map)
577
- records = execute(query, 'SCHEMA')
578
- initializer.run(records)
587
+ execute_and_clear(query, "SCHEMA", []) do |records|
588
+ initializer.run(records)
589
+ end
579
590
  end
580
591
 
581
592
  FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
582
593
 
583
- def execute_and_clear(sql, name, binds)
584
- result = without_prepared_statement?(binds) ? exec_no_cache(sql, name, binds) :
585
- exec_cache(sql, name, binds)
594
+ def execute_and_clear(sql, name, binds, prepare: false)
595
+ if without_prepared_statement?(binds)
596
+ result = exec_no_cache(sql, name, [])
597
+ elsif !prepare
598
+ result = exec_no_cache(sql, name, binds)
599
+ else
600
+ result = exec_cache(sql, name, binds)
601
+ end
586
602
  ret = yield result
587
603
  result.clear
588
604
  ret
589
605
  end
590
606
 
591
607
  def exec_no_cache(sql, name, binds)
592
- log(sql, name, binds) { @connection.async_exec(sql, []) }
608
+ type_casted_binds = type_casted_binds(binds)
609
+ log(sql, name, binds, type_casted_binds) do
610
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
611
+ @connection.exec_params(sql, type_casted_binds)
612
+ end
613
+ end
593
614
  end
594
615
 
595
616
  def exec_cache(sql, name, binds)
596
617
  stmt_key = prepare_statement(sql)
597
- type_casted_binds = binds.map { |col, val|
598
- [col, type_cast(val, col)]
599
- }
618
+ type_casted_binds = type_casted_binds(binds)
600
619
 
601
- log(sql, name, type_casted_binds, stmt_key) do
602
- @connection.exec_prepared(stmt_key, type_casted_binds.map { |_, val| val })
620
+ log(sql, name, binds, type_casted_binds, stmt_key) do
621
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
622
+ @connection.exec_prepared(stmt_key, type_casted_binds)
623
+ end
603
624
  end
604
625
  rescue ActiveRecord::StatementInvalid => e
605
- pgerror = e.original_exception
606
-
607
- # Get the PG code for the failure. Annoyingly, the code for
608
- # prepared statements whose return value may have changed is
609
- # FEATURE_NOT_SUPPORTED. Check here for more details:
610
- # http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
611
- begin
612
- code = pgerror.result.result_error_field(PGresult::PG_DIAG_SQLSTATE)
613
- rescue
614
- raise e
615
- end
616
- if FEATURE_NOT_SUPPORTED == code
617
- @statements.delete sql_key(sql)
618
- retry
626
+ raise unless is_cached_plan_failure?(e)
627
+
628
+ # Nothing we can do if we are in a transaction because all commands
629
+ # will raise InFailedSQLTransaction
630
+ if in_transaction?
631
+ raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)
619
632
  else
620
- raise e
633
+ @lock.synchronize do
634
+ # outside of transactions we can simply flush this query and retry
635
+ @statements.delete sql_key(sql)
636
+ end
637
+ retry
621
638
  end
622
639
  end
623
640
 
641
+ # Annoyingly, the code for prepared statements whose return value may
642
+ # have changed is FEATURE_NOT_SUPPORTED.
643
+ #
644
+ # This covers various different error types so we need to do additional
645
+ # work to classify the exception definitively as a
646
+ # ActiveRecord::PreparedStatementCacheExpired
647
+ #
648
+ # Check here for more details:
649
+ # https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
650
+ CACHED_PLAN_HEURISTIC = "cached plan must not change result type".freeze
651
+ def is_cached_plan_failure?(e)
652
+ pgerror = e.cause
653
+ code = pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE)
654
+ code == FEATURE_NOT_SUPPORTED && pgerror.message.include?(CACHED_PLAN_HEURISTIC)
655
+ rescue
656
+ false
657
+ end
658
+
659
+ def in_transaction?
660
+ open_transactions > 0
661
+ end
662
+
624
663
  # Returns the statement identifier for the client side cache
625
664
  # of statements
626
665
  def sql_key(sql)
@@ -630,35 +669,31 @@ module ActiveRecord
630
669
  # Prepare the statement if it hasn't been prepared, return
631
670
  # the statement key.
632
671
  def prepare_statement(sql)
633
- sql_key = sql_key(sql)
634
- unless @statements.key? sql_key
635
- nextkey = @statements.next_key
636
- begin
637
- @connection.prepare nextkey, sql
638
- rescue => e
639
- raise translate_exception_class(e, sql)
672
+ @lock.synchronize do
673
+ sql_key = sql_key(sql)
674
+ unless @statements.key? sql_key
675
+ nextkey = @statements.next_key
676
+ begin
677
+ @connection.prepare nextkey, sql
678
+ rescue => e
679
+ raise translate_exception_class(e, sql)
680
+ end
681
+ # Clear the queue
682
+ @connection.get_last_result
683
+ @statements[sql_key] = nextkey
640
684
  end
641
- # Clear the queue
642
- @connection.get_last_result
643
- @statements[sql_key] = nextkey
685
+ @statements[sql_key]
644
686
  end
645
- @statements[sql_key]
646
687
  end
647
688
 
648
689
  # Connects to a PostgreSQL server and sets up the adapter depending on the
649
690
  # connected server's characteristics.
650
691
  def connect
651
- @connection = PGconn.connect(@connection_parameters)
652
-
653
- # Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of
654
- # PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision
655
- # should know about this but can't detect it there, so deal with it here.
656
- OID::Money.precision = (postgresql_version >= 80300) ? 19 : 10
657
-
692
+ @connection = PG.connect(@connection_parameters)
658
693
  configure_connection
659
694
  rescue ::PG::Error => error
660
695
  if error.message.include?("does not exist")
661
- raise ActiveRecord::NoDatabaseError.new(error.message, error)
696
+ raise ActiveRecord::NoDatabaseError
662
697
  else
663
698
  raise
664
699
  end
@@ -670,51 +705,40 @@ module ActiveRecord
670
705
  if @config[:encoding]
671
706
  @connection.set_client_encoding(@config[:encoding])
672
707
  end
673
- self.client_min_messages = @config[:min_messages] || 'warning'
708
+ self.client_min_messages = @config[:min_messages] || "warning"
674
709
  self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
675
710
 
676
- # Use standard-conforming strings if available so we don't have to do the E'...' dance.
711
+ # Use standard-conforming strings so we don't have to do the E'...' dance.
677
712
  set_standard_conforming_strings
678
713
 
714
+ variables = @config.fetch(:variables, {}).stringify_keys
715
+
679
716
  # If using Active Record's time zone support configure the connection to return
680
717
  # TIMESTAMP WITH ZONE types in UTC.
681
- # (SET TIME ZONE does not use an equals sign like other SET variables)
682
- if ActiveRecord::Base.default_timezone == :utc
683
- execute("SET time zone 'UTC'", 'SCHEMA')
684
- elsif @local_tz
685
- execute("SET time zone '#{@local_tz}'", 'SCHEMA')
718
+ unless variables["timezone"]
719
+ if ActiveRecord::Base.default_timezone == :utc
720
+ variables["timezone"] = "UTC"
721
+ elsif @local_tz
722
+ variables["timezone"] = @local_tz
723
+ end
686
724
  end
687
725
 
688
726
  # SET statements from :variables config hash
689
- # http://www.postgresql.org/docs/8.3/static/sql-set.html
690
- variables = @config[:variables] || {}
727
+ # https://www.postgresql.org/docs/current/static/sql-set.html
691
728
  variables.map do |k, v|
692
- if v == ':default' || v == :default
729
+ if v == ":default" || v == :default
693
730
  # Sets the value to the global or compile default
694
- execute("SET SESSION #{k} TO DEFAULT", 'SCHEMA')
731
+ execute("SET SESSION #{k} TO DEFAULT", "SCHEMA")
695
732
  elsif !v.nil?
696
- execute("SET SESSION #{k} TO #{quote(v)}", 'SCHEMA')
733
+ execute("SET SESSION #{k} TO #{quote(v)}", "SCHEMA")
697
734
  end
698
735
  end
699
736
  end
700
737
 
701
- # Returns the current ID of a table's sequence.
702
- def last_insert_id(sequence_name) #:nodoc:
703
- Integer(last_insert_id_value(sequence_name))
704
- end
705
-
706
- def last_insert_id_value(sequence_name)
707
- last_insert_id_result(sequence_name).rows.first.first
708
- end
709
-
710
- def last_insert_id_result(sequence_name) #:nodoc:
711
- exec_query("SELECT currval('#{sequence_name}')", 'SQL')
712
- end
713
-
714
738
  # Returns the list of a table's column names, data types, and default values.
715
739
  #
716
740
  # The underlying query is roughly:
717
- # SELECT column.name, column.type, default.value
741
+ # SELECT column.name, column.type, default.value, column.comment
718
742
  # FROM column LEFT JOIN default
719
743
  # ON column.table_id = default.table_id
720
744
  # AND column.num = default.column_num
@@ -729,26 +753,111 @@ module ActiveRecord
729
753
  # Query implementation notes:
730
754
  # - format_type includes the column size constraint, e.g. varchar(50)
731
755
  # - ::regclass is a function that gives the id for a table name
732
- def column_definitions(table_name) # :nodoc:
733
- exec_query(<<-end_sql, 'SCHEMA').rows
756
+ def column_definitions(table_name)
757
+ query(<<-end_sql, "SCHEMA")
734
758
  SELECT a.attname, format_type(a.atttypid, a.atttypmod),
735
- pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod
736
- FROM pg_attribute a LEFT JOIN pg_attrdef d
737
- ON a.attrelid = d.adrelid AND a.attnum = d.adnum
738
- WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
759
+ pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
760
+ c.collname, col_description(a.attrelid, a.attnum) AS comment
761
+ FROM pg_attribute a
762
+ LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
763
+ LEFT JOIN pg_type t ON a.atttypid = t.oid
764
+ LEFT JOIN pg_collation c ON a.attcollation = c.oid AND a.attcollation <> t.typcollation
765
+ WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass
739
766
  AND a.attnum > 0 AND NOT a.attisdropped
740
767
  ORDER BY a.attnum
741
768
  end_sql
742
769
  end
743
770
 
744
- def extract_table_ref_from_insert_sql(sql) # :nodoc:
745
- sql[/into\s+([^\(]*).*values\s*\(/im]
771
+ def extract_table_ref_from_insert_sql(sql)
772
+ sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
746
773
  $1.strip if $1
747
774
  end
748
775
 
749
- def create_table_definition(name, temporary, options, as = nil) # :nodoc:
750
- PostgreSQL::TableDefinition.new native_database_types, name, temporary, options, as
776
+ def arel_visitor
777
+ Arel::Visitors::PostgreSQL.new(self)
778
+ end
779
+
780
+ def can_perform_case_insensitive_comparison_for?(column)
781
+ @case_insensitive_cache ||= {}
782
+ @case_insensitive_cache[column.sql_type] ||= begin
783
+ sql = <<-end_sql
784
+ SELECT exists(
785
+ SELECT * FROM pg_proc
786
+ WHERE proname = 'lower'
787
+ AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
788
+ ) OR exists(
789
+ SELECT * FROM pg_proc
790
+ INNER JOIN pg_cast
791
+ ON ARRAY[casttarget]::oidvector = proargtypes
792
+ WHERE proname = 'lower'
793
+ AND castsource = #{quote column.sql_type}::regtype
794
+ )
795
+ end_sql
796
+ execute_and_clear(sql, "SCHEMA", []) do |result|
797
+ result.getvalue(0, 0)
798
+ end
799
+ end
800
+ end
801
+
802
+ def add_pg_encoders
803
+ map = PG::TypeMapByClass.new
804
+ map[Integer] = PG::TextEncoder::Integer.new
805
+ map[TrueClass] = PG::TextEncoder::Boolean.new
806
+ map[FalseClass] = PG::TextEncoder::Boolean.new
807
+ @connection.type_map_for_queries = map
751
808
  end
809
+
810
+ def add_pg_decoders
811
+ coders_by_name = {
812
+ "int2" => PG::TextDecoder::Integer,
813
+ "int4" => PG::TextDecoder::Integer,
814
+ "int8" => PG::TextDecoder::Integer,
815
+ "oid" => PG::TextDecoder::Integer,
816
+ "float4" => PG::TextDecoder::Float,
817
+ "float8" => PG::TextDecoder::Float,
818
+ "bool" => PG::TextDecoder::Boolean,
819
+ }
820
+ known_coder_types = coders_by_name.keys.map { |n| quote(n) }
821
+ query = <<-SQL % known_coder_types.join(", ")
822
+ SELECT t.oid, t.typname
823
+ FROM pg_type as t
824
+ WHERE t.typname IN (%s)
825
+ SQL
826
+ coders = execute_and_clear(query, "SCHEMA", []) do |result|
827
+ result
828
+ .map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
829
+ .compact
830
+ end
831
+
832
+ map = PG::TypeMapByOid.new
833
+ coders.each { |coder| map.add_coder(coder) }
834
+ @connection.type_map_for_results = map
835
+ end
836
+
837
+ def construct_coder(row, coder_class)
838
+ return unless coder_class
839
+ coder_class.new(oid: row["oid"].to_i, name: row["typname"])
840
+ end
841
+
842
+ ActiveRecord::Type.add_modifier({ array: true }, OID::Array, adapter: :postgresql)
843
+ ActiveRecord::Type.add_modifier({ range: true }, OID::Range, adapter: :postgresql)
844
+ ActiveRecord::Type.register(:bit, OID::Bit, adapter: :postgresql)
845
+ ActiveRecord::Type.register(:bit_varying, OID::BitVarying, adapter: :postgresql)
846
+ ActiveRecord::Type.register(:binary, OID::Bytea, adapter: :postgresql)
847
+ ActiveRecord::Type.register(:cidr, OID::Cidr, adapter: :postgresql)
848
+ ActiveRecord::Type.register(:date, OID::Date, adapter: :postgresql)
849
+ ActiveRecord::Type.register(:datetime, OID::DateTime, adapter: :postgresql)
850
+ ActiveRecord::Type.register(:decimal, OID::Decimal, adapter: :postgresql)
851
+ ActiveRecord::Type.register(:enum, OID::Enum, adapter: :postgresql)
852
+ ActiveRecord::Type.register(:hstore, OID::Hstore, adapter: :postgresql)
853
+ ActiveRecord::Type.register(:inet, OID::Inet, adapter: :postgresql)
854
+ ActiveRecord::Type.register(:jsonb, OID::Jsonb, adapter: :postgresql)
855
+ ActiveRecord::Type.register(:money, OID::Money, adapter: :postgresql)
856
+ ActiveRecord::Type.register(:point, OID::Point, adapter: :postgresql)
857
+ ActiveRecord::Type.register(:legacy_point, OID::LegacyPoint, adapter: :postgresql)
858
+ ActiveRecord::Type.register(:uuid, OID::Uuid, adapter: :postgresql)
859
+ ActiveRecord::Type.register(:vector, OID::Vector, adapter: :postgresql)
860
+ ActiveRecord::Type.register(:xml, OID::Xml, adapter: :postgresql)
752
861
  end
753
862
  end
754
863
  end