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.
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