activerecord 3.2.22.5 → 5.2.8

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

Potentially problematic release.


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

Files changed (275) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +657 -621
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +41 -46
  5. data/examples/performance.rb +55 -42
  6. data/examples/simple.rb +6 -5
  7. data/lib/active_record/aggregations.rb +264 -236
  8. data/lib/active_record/association_relation.rb +40 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -42
  10. data/lib/active_record/associations/association.rb +127 -75
  11. data/lib/active_record/associations/association_scope.rb +126 -92
  12. data/lib/active_record/associations/belongs_to_association.rb +78 -27
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +9 -4
  14. data/lib/active_record/associations/builder/association.rb +117 -32
  15. data/lib/active_record/associations/builder/belongs_to.rb +135 -60
  16. data/lib/active_record/associations/builder/collection_association.rb +61 -54
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +120 -42
  18. data/lib/active_record/associations/builder/has_many.rb +10 -64
  19. data/lib/active_record/associations/builder/has_one.rb +19 -51
  20. data/lib/active_record/associations/builder/singular_association.rb +28 -18
  21. data/lib/active_record/associations/collection_association.rb +226 -293
  22. data/lib/active_record/associations/collection_proxy.rb +1067 -69
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +83 -47
  25. data/lib/active_record/associations/has_many_through_association.rb +98 -65
  26. data/lib/active_record/associations/has_one_association.rb +57 -20
  27. data/lib/active_record/associations/has_one_through_association.rb +18 -9
  28. data/lib/active_record/associations/join_dependency/join_association.rb +48 -126
  29. data/lib/active_record/associations/join_dependency/join_base.rb +11 -12
  30. data/lib/active_record/associations/join_dependency/join_part.rb +35 -42
  31. data/lib/active_record/associations/join_dependency.rb +212 -164
  32. data/lib/active_record/associations/preloader/association.rb +95 -89
  33. data/lib/active_record/associations/preloader/through_association.rb +84 -44
  34. data/lib/active_record/associations/preloader.rb +123 -111
  35. data/lib/active_record/associations/singular_association.rb +33 -24
  36. data/lib/active_record/associations/through_association.rb +60 -26
  37. data/lib/active_record/associations.rb +1759 -1506
  38. data/lib/active_record/attribute_assignment.rb +60 -193
  39. data/lib/active_record/attribute_decorators.rb +90 -0
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +55 -8
  41. data/lib/active_record/attribute_methods/dirty.rb +113 -74
  42. data/lib/active_record/attribute_methods/primary_key.rb +106 -77
  43. data/lib/active_record/attribute_methods/query.rb +8 -5
  44. data/lib/active_record/attribute_methods/read.rb +63 -114
  45. data/lib/active_record/attribute_methods/serialization.rb +60 -90
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +69 -43
  47. data/lib/active_record/attribute_methods/write.rb +43 -45
  48. data/lib/active_record/attribute_methods.rb +366 -149
  49. data/lib/active_record/attributes.rb +266 -0
  50. data/lib/active_record/autosave_association.rb +312 -225
  51. data/lib/active_record/base.rb +114 -505
  52. data/lib/active_record/callbacks.rb +145 -67
  53. data/lib/active_record/coders/json.rb +15 -0
  54. data/lib/active_record/coders/yaml_column.rb +32 -23
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +883 -284
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +16 -2
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +350 -200
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -27
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +150 -65
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +146 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +477 -284
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +95 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1100 -310
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +283 -0
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +450 -118
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +657 -446
  69. data/lib/active_record/connection_adapters/column.rb +50 -255
  70. data/lib/active_record/connection_adapters/connection_specification.rb +287 -0
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +59 -210
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +44 -0
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +163 -0
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +56 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +111 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +168 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +206 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +774 -0
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +620 -1080
  117. data/lib/active_record/connection_adapters/schema_cache.rb +85 -36
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +545 -27
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +145 -0
  128. data/lib/active_record/core.rb +559 -0
  129. data/lib/active_record/counter_cache.rb +200 -105
  130. data/lib/active_record/define_callbacks.rb +22 -0
  131. data/lib/active_record/dynamic_matchers.rb +107 -69
  132. data/lib/active_record/enum.rb +244 -0
  133. data/lib/active_record/errors.rb +245 -60
  134. data/lib/active_record/explain.rb +35 -71
  135. data/lib/active_record/explain_registry.rb +32 -0
  136. data/lib/active_record/explain_subscriber.rb +18 -9
  137. data/lib/active_record/fixture_set/file.rb +82 -0
  138. data/lib/active_record/fixtures.rb +418 -275
  139. data/lib/active_record/gem_version.rb +17 -0
  140. data/lib/active_record/inheritance.rb +209 -100
  141. data/lib/active_record/integration.rb +116 -21
  142. data/lib/active_record/internal_metadata.rb +45 -0
  143. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  144. data/lib/active_record/locale/en.yml +9 -1
  145. data/lib/active_record/locking/optimistic.rb +107 -94
  146. data/lib/active_record/locking/pessimistic.rb +20 -8
  147. data/lib/active_record/log_subscriber.rb +99 -34
  148. data/lib/active_record/migration/command_recorder.rb +199 -64
  149. data/lib/active_record/migration/compatibility.rb +217 -0
  150. data/lib/active_record/migration/join_table.rb +17 -0
  151. data/lib/active_record/migration.rb +893 -296
  152. data/lib/active_record/model_schema.rb +328 -175
  153. data/lib/active_record/nested_attributes.rb +338 -242
  154. data/lib/active_record/no_touching.rb +58 -0
  155. data/lib/active_record/null_relation.rb +68 -0
  156. data/lib/active_record/persistence.rb +557 -170
  157. data/lib/active_record/query_cache.rb +14 -43
  158. data/lib/active_record/querying.rb +36 -24
  159. data/lib/active_record/railtie.rb +147 -52
  160. data/lib/active_record/railties/console_sandbox.rb +5 -4
  161. data/lib/active_record/railties/controller_runtime.rb +13 -6
  162. data/lib/active_record/railties/databases.rake +206 -488
  163. data/lib/active_record/readonly_attributes.rb +4 -6
  164. data/lib/active_record/reflection.rb +734 -228
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  166. data/lib/active_record/relation/batches.rb +249 -52
  167. data/lib/active_record/relation/calculations.rb +330 -284
  168. data/lib/active_record/relation/delegation.rb +135 -37
  169. data/lib/active_record/relation/finder_methods.rb +450 -287
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +193 -0
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  173. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  174. data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
  179. data/lib/active_record/relation/predicate_builder.rb +132 -43
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +1037 -221
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +48 -151
  184. data/lib/active_record/relation/where_clause.rb +186 -0
  185. data/lib/active_record/relation/where_clause_factory.rb +34 -0
  186. data/lib/active_record/relation.rb +451 -359
  187. data/lib/active_record/result.rb +129 -20
  188. data/lib/active_record/runtime_registry.rb +24 -0
  189. data/lib/active_record/sanitization.rb +164 -136
  190. data/lib/active_record/schema.rb +31 -19
  191. data/lib/active_record/schema_dumper.rb +154 -107
  192. data/lib/active_record/schema_migration.rb +56 -0
  193. data/lib/active_record/scoping/default.rb +108 -98
  194. data/lib/active_record/scoping/named.rb +125 -112
  195. data/lib/active_record/scoping.rb +77 -123
  196. data/lib/active_record/secure_token.rb +40 -0
  197. data/lib/active_record/serialization.rb +10 -6
  198. data/lib/active_record/statement_cache.rb +121 -0
  199. data/lib/active_record/store.rb +175 -16
  200. data/lib/active_record/suppressor.rb +61 -0
  201. data/lib/active_record/table_metadata.rb +82 -0
  202. data/lib/active_record/tasks/database_tasks.rb +337 -0
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +143 -0
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +83 -0
  206. data/lib/active_record/timestamp.rb +80 -41
  207. data/lib/active_record/touch_later.rb +64 -0
  208. data/lib/active_record/transactions.rb +240 -119
  209. data/lib/active_record/translation.rb +2 -0
  210. data/lib/active_record/type/adapter_specific_registry.rb +136 -0
  211. data/lib/active_record/type/date.rb +9 -0
  212. data/lib/active_record/type/date_time.rb +9 -0
  213. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  214. data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
  215. data/lib/active_record/type/internal/timezone.rb +17 -0
  216. data/lib/active_record/type/json.rb +30 -0
  217. data/lib/active_record/type/serialized.rb +71 -0
  218. data/lib/active_record/type/text.rb +11 -0
  219. data/lib/active_record/type/time.rb +21 -0
  220. data/lib/active_record/type/type_map.rb +62 -0
  221. data/lib/active_record/type/unsigned_integer.rb +17 -0
  222. data/lib/active_record/type.rb +79 -0
  223. data/lib/active_record/type_caster/connection.rb +33 -0
  224. data/lib/active_record/type_caster/map.rb +23 -0
  225. data/lib/active_record/type_caster.rb +9 -0
  226. data/lib/active_record/validations/absence.rb +25 -0
  227. data/lib/active_record/validations/associated.rb +35 -18
  228. data/lib/active_record/validations/length.rb +26 -0
  229. data/lib/active_record/validations/presence.rb +68 -0
  230. data/lib/active_record/validations/uniqueness.rb +133 -75
  231. data/lib/active_record/validations.rb +53 -43
  232. data/lib/active_record/version.rb +7 -7
  233. data/lib/active_record.rb +89 -57
  234. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  235. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  236. data/lib/rails/generators/active_record/migration/migration_generator.rb +61 -8
  237. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  238. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +46 -0
  239. data/lib/rails/generators/active_record/migration.rb +28 -8
  240. data/lib/rails/generators/active_record/model/model_generator.rb +23 -22
  241. data/lib/rails/generators/active_record/model/templates/model.rb.tt +13 -0
  242. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +1 -1
  243. data/lib/rails/generators/active_record.rb +10 -16
  244. metadata +141 -62
  245. data/examples/associations.png +0 -0
  246. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  247. data/lib/active_record/associations/join_helper.rb +0 -55
  248. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  249. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  250. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  251. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  252. data/lib/active_record/associations/preloader/has_many_through.rb +0 -15
  253. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  254. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  255. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  256. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  257. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  258. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -441
  259. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  260. data/lib/active_record/dynamic_finder_match.rb +0 -68
  261. data/lib/active_record/dynamic_scope_match.rb +0 -23
  262. data/lib/active_record/fixtures/file.rb +0 -65
  263. data/lib/active_record/identity_map.rb +0 -162
  264. data/lib/active_record/observer.rb +0 -121
  265. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  266. data/lib/active_record/serializers/xml_serializer.rb +0 -203
  267. data/lib/active_record/session_store.rb +0 -360
  268. data/lib/active_record/test_case.rb +0 -73
  269. data/lib/rails/generators/active_record/migration/templates/migration.rb +0 -34
  270. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
  271. data/lib/rails/generators/active_record/model/templates/model.rb +0 -12
  272. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  273. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  274. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  275. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,1196 +1,663 @@
1
- require 'active_record/connection_adapters/abstract_adapter'
2
- require 'active_support/core_ext/object/blank'
3
- require 'active_record/connection_adapters/statement_pool'
4
- require 'arel/visitors/bind_visitor'
1
+ # frozen_string_literal: true
5
2
 
6
- # Make sure we're using pg high enough for PGResult#values
7
- gem 'pg', '~> 0.11'
8
- require 'pg'
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"
9
6
 
10
- module ActiveRecord
11
- class Base
12
- # Establishes a connection to the database that's used by all Active Record objects
13
- def self.postgresql_connection(config) # :nodoc:
14
- config = config.symbolize_keys
15
- host = config[:host]
16
- port = config[:port] || 5432
17
- username = config[:username].to_s if config[:username]
18
- password = config[:password].to_s if config[:password]
19
-
20
- if config.key?(:database)
21
- database = config[:database]
22
- else
23
- raise ArgumentError, "No database specified. Missing argument: database."
24
- end
25
-
26
- # The postgres drivers don't allow the creation of an unconnected PGconn object,
27
- # so just pass a nil connection object for the time being.
28
- ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, [host, port, nil, nil, database, username, password], config)
29
- end
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
30
12
  end
13
+ end
31
14
 
32
- module ConnectionAdapters
33
- # PostgreSQL-specific extensions to column definitions in a table.
34
- class PostgreSQLColumn < Column #:nodoc:
35
- # Instantiates a new PostgreSQL column definition in a table.
36
- def initialize(name, default, sql_type = nil, null = true)
37
- super(name, self.class.extract_value_from_default(default), sql_type, null)
38
- end
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"
39
29
 
40
- # :stopdoc:
41
- class << self
42
- attr_accessor :money_precision
43
- def string_to_time(string)
44
- return string unless String === string
30
+ module ActiveRecord
31
+ module ConnectionHandling # :nodoc:
32
+ # Establishes a connection to the database that's used by all Active Record objects
33
+ def postgresql_connection(config)
34
+ conn_params = config.symbolize_keys
45
35
 
46
- case string
47
- when 'infinity' then 1.0 / 0.0
48
- when '-infinity' then -1.0 / 0.0
49
- else
50
- super
51
- end
52
- end
53
- end
54
- # :startdoc:
36
+ conn_params.delete_if { |_, v| v.nil? }
55
37
 
56
- private
57
- def extract_limit(sql_type)
58
- case sql_type
59
- when /^bigint/i; 8
60
- when /^smallint/i; 2
61
- else super
62
- end
63
- end
38
+ # Map ActiveRecords param names to PGs.
39
+ conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
40
+ conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
64
41
 
65
- # Extracts the scale from PostgreSQL-specific data types.
66
- def extract_scale(sql_type)
67
- # Money type has a fixed scale of 2.
68
- sql_type =~ /^money/ ? 2 : super
69
- end
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)
70
45
 
71
- # Extracts the precision from PostgreSQL-specific data types.
72
- def extract_precision(sql_type)
73
- if sql_type == 'money'
74
- self.class.money_precision
75
- else
76
- super
77
- end
78
- end
79
-
80
- # Maps PostgreSQL-specific data types to logical Rails types.
81
- def simplified_type(field_type)
82
- case field_type
83
- # Numeric and monetary types
84
- when /^(?:real|double precision)$/
85
- :float
86
- # Monetary types
87
- when 'money'
88
- :decimal
89
- # Character types
90
- when /^(?:character varying|bpchar)(?:\(\d+\))?$/
91
- :string
92
- # Binary data types
93
- when 'bytea'
94
- :binary
95
- # Date/time types
96
- when /^timestamp with(?:out)? time zone$/
97
- :datetime
98
- when 'interval'
99
- :string
100
- # Geometric types
101
- when /^(?:point|line|lseg|box|"?path"?|polygon|circle)$/
102
- :string
103
- # Network address types
104
- when /^(?:cidr|inet|macaddr)$/
105
- :string
106
- # Bit strings
107
- when /^bit(?: varying)?(?:\(\d+\))?$/
108
- :string
109
- # XML type
110
- when 'xml'
111
- :xml
112
- # tsvector type
113
- when 'tsvector'
114
- :tsvector
115
- # Arrays
116
- when /^\D+\[\]$/
117
- :string
118
- # Object identifier types
119
- when 'oid'
120
- :integer
121
- # UUID type
122
- when 'uuid'
123
- :string
124
- # Small and big integer types
125
- when /^(?:small|big)int$/
126
- :integer
127
- # Pass through all types that are not specific to PostgreSQL.
128
- else
129
- super
130
- end
131
- end
132
-
133
- # Extracts the value from a PostgreSQL column default definition.
134
- def self.extract_value_from_default(default)
135
- case default
136
- # This is a performance optimization for Ruby 1.9.2 in development.
137
- # If the value is nil, we return nil straight away without checking
138
- # the regular expressions. If we check each regular expression,
139
- # Regexp#=== will call NilClass#to_str, which will trigger
140
- # method_missing (defined by whiny nil in ActiveSupport) which
141
- # makes this method very very slow.
142
- when NilClass
143
- nil
144
- # Numeric types
145
- when /\A\(?(-?\d+(\.\d*)?\)?(::bigint)?)\z/
146
- $1
147
- # Character types
148
- when /\A\(?'(.*)'::.*\b(?:character varying|bpchar|text)\z/m
149
- $1
150
- # Binary data types
151
- when /\A'(.*)'::bytea\z/m
152
- $1
153
- # Date/time types
154
- when /\A'(.+)'::(?:time(?:stamp)? with(?:out)? time zone|date)\z/
155
- $1
156
- when /\A'(.*)'::interval\z/
157
- $1
158
- # Boolean type
159
- when 'true'
160
- true
161
- when 'false'
162
- false
163
- # Geometric types
164
- when /\A'(.*)'::(?:point|line|lseg|box|"?path"?|polygon|circle)\z/
165
- $1
166
- # Network address types
167
- when /\A'(.*)'::(?:cidr|inet|macaddr)\z/
168
- $1
169
- # Bit string types
170
- when /\AB'(.*)'::"?bit(?: varying)?"?\z/
171
- $1
172
- # XML type
173
- when /\A'(.*)'::xml\z/m
174
- $1
175
- # Arrays
176
- when /\A'(.*)'::"?\D+"?\[\]\z/
177
- $1
178
- # Object identifier types
179
- when /\A-?\d+\z/
180
- $1
181
- else
182
- # Anything else is blank, some user type, or some function
183
- # and we can't know the value of that, so return nil.
184
- nil
185
- end
186
- end
46
+ # The postgres drivers don't allow the creation of an unconnected PG::Connection object,
47
+ # so just pass a nil connection object for the time being.
48
+ ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, conn_params, config)
187
49
  end
50
+ end
188
51
 
189
- # The PostgreSQL adapter works both with the native C (http://ruby.scripting.ca/postgres/) and the pure
190
- # Ruby (available both as gem and from http://rubyforge.org/frs/?group_id=234&release_id=1944) drivers.
52
+ module ConnectionAdapters
53
+ # The PostgreSQL adapter works with the native C (https://bitbucket.org/ged/ruby-pg) driver.
191
54
  #
192
55
  # Options:
193
56
  #
194
- # * <tt>:host</tt> - Defaults to "localhost".
57
+ # * <tt>:host</tt> - Defaults to a Unix-domain socket in /tmp. On machines without Unix-domain sockets,
58
+ # the default is to connect to localhost.
195
59
  # * <tt>:port</tt> - Defaults to 5432.
196
- # * <tt>:username</tt> - Defaults to nothing.
197
- # * <tt>:password</tt> - Defaults to nothing.
198
- # * <tt>:database</tt> - The name of the database. No default, must be provided.
60
+ # * <tt>:username</tt> - Defaults to be the same as the operating system name of the user running the application.
61
+ # * <tt>:password</tt> - Password to be used if the server demands password authentication.
62
+ # * <tt>:database</tt> - Defaults to be the same as the user name.
199
63
  # * <tt>:schema_search_path</tt> - An optional schema search path for the connection given
200
64
  # as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
201
65
  # * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO
202
66
  # <encoding></tt> call on the connection.
203
67
  # * <tt>:min_messages</tt> - An optional client min messages that is used in a
204
68
  # <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
69
+ # * <tt>:variables</tt> - An optional hash of additional parameters that
70
+ # will be used in <tt>SET SESSION key = val</tt> calls on the connection.
71
+ # * <tt>:insert_returning</tt> - An optional boolean to control the use of <tt>RETURNING</tt> for <tt>INSERT</tt> statements
72
+ # defaults to true.
73
+ #
74
+ # Any further options are used as connection parameters to libpq. See
75
+ # https://www.postgresql.org/docs/current/static/libpq-connect.html for the
76
+ # list of parameters.
77
+ #
78
+ # In addition, default connection parameters of libpq can be set per environment variables.
79
+ # See https://www.postgresql.org/docs/current/static/libpq-envars.html .
205
80
  class PostgreSQLAdapter < AbstractAdapter
206
- class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
207
- def xml(*args)
208
- options = args.extract_options!
209
- column(args[0], 'xml', options)
210
- end
81
+ ADAPTER_NAME = "PostgreSQL".freeze
211
82
 
212
- def tsvector(*args)
213
- options = args.extract_options!
214
- column(args[0], 'tsvector', options)
215
- end
83
+ NATIVE_DATABASE_TYPES = {
84
+ primary_key: "bigserial primary key",
85
+ string: { name: "character varying" },
86
+ text: { name: "text" },
87
+ integer: { name: "integer", limit: 4 },
88
+ float: { name: "float" },
89
+ decimal: { name: "decimal" },
90
+ datetime: { name: "timestamp" },
91
+ time: { name: "time" },
92
+ date: { name: "date" },
93
+ daterange: { name: "daterange" },
94
+ numrange: { name: "numrange" },
95
+ tsrange: { name: "tsrange" },
96
+ tstzrange: { name: "tstzrange" },
97
+ int4range: { name: "int4range" },
98
+ int8range: { name: "int8range" },
99
+ binary: { name: "bytea" },
100
+ boolean: { name: "boolean" },
101
+ xml: { name: "xml" },
102
+ tsvector: { name: "tsvector" },
103
+ hstore: { name: "hstore" },
104
+ inet: { name: "inet" },
105
+ cidr: { name: "cidr" },
106
+ macaddr: { name: "macaddr" },
107
+ uuid: { name: "uuid" },
108
+ json: { name: "json" },
109
+ jsonb: { name: "jsonb" },
110
+ ltree: { name: "ltree" },
111
+ citext: { name: "citext" },
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" },
119
+ bit: { name: "bit" },
120
+ bit_varying: { name: "bit varying" },
121
+ money: { name: "money" },
122
+ interval: { name: "interval" },
123
+ oid: { name: "oid" },
124
+ }
125
+
126
+ OID = PostgreSQL::OID #:nodoc:
127
+
128
+ include PostgreSQL::Quoting
129
+ include PostgreSQL::ReferentialIntegrity
130
+ include PostgreSQL::SchemaStatements
131
+ include PostgreSQL::DatabaseStatements
132
+
133
+ def supports_bulk_alter?
134
+ true
216
135
  end
217
136
 
218
- ADAPTER_NAME = 'PostgreSQL'
137
+ def supports_index_sort_order?
138
+ true
139
+ end
219
140
 
220
- NATIVE_DATABASE_TYPES = {
221
- :primary_key => "serial primary key",
222
- :string => { :name => "character varying", :limit => 255 },
223
- :text => { :name => "text" },
224
- :integer => { :name => "integer" },
225
- :float => { :name => "float" },
226
- :decimal => { :name => "decimal" },
227
- :datetime => { :name => "timestamp" },
228
- :timestamp => { :name => "timestamp" },
229
- :time => { :name => "time" },
230
- :date => { :name => "date" },
231
- :binary => { :name => "bytea" },
232
- :boolean => { :name => "boolean" },
233
- :xml => { :name => "xml" },
234
- :tsvector => { :name => "tsvector" }
235
- }
141
+ def supports_partial_index?
142
+ true
143
+ end
236
144
 
237
- # Returns 'PostgreSQL' as adapter name for identification purposes.
238
- def adapter_name
239
- ADAPTER_NAME
145
+ def supports_expression_index?
146
+ true
240
147
  end
241
148
 
242
- # Returns +true+, since this connection adapter supports prepared statement
243
- # caching.
244
- def supports_statement_cache?
149
+ def supports_transaction_isolation?
245
150
  true
246
151
  end
247
152
 
248
- def supports_index_sort_order?
153
+ def supports_foreign_keys?
154
+ true
155
+ end
156
+
157
+ def supports_validate_constraints?
158
+ true
159
+ end
160
+
161
+ def supports_views?
162
+ true
163
+ end
164
+
165
+ def supports_datetime_with_precision?
249
166
  true
250
167
  end
251
168
 
252
- class StatementPool < ConnectionAdapters::StatementPool
169
+ def supports_json?
170
+ postgresql_version >= 90200
171
+ end
172
+
173
+ def supports_comments?
174
+ true
175
+ end
176
+
177
+ def supports_savepoints?
178
+ true
179
+ end
180
+
181
+ def index_algorithms
182
+ { concurrently: "CONCURRENTLY" }
183
+ end
184
+
185
+ class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
253
186
  def initialize(connection, max)
254
- super
187
+ super(max)
188
+ @connection = connection
255
189
  @counter = 0
256
- @cache = Hash.new { |h,pid| h[pid] = {} }
257
190
  end
258
191
 
259
- def each(&block); cache.each(&block); end
260
- def key?(key); cache.key?(key); end
261
- def [](key); cache[key]; end
262
- def length; cache.length; end
263
-
264
192
  def next_key
265
193
  "a#{@counter + 1}"
266
194
  end
267
195
 
268
196
  def []=(sql, key)
269
- while @max <= cache.size
270
- dealloc(cache.shift.last)
271
- end
272
- @counter += 1
273
- cache[sql] = key
274
- end
275
-
276
- def clear
277
- cache.each_value do |stmt_key|
278
- dealloc stmt_key
279
- end
280
- cache.clear
281
- end
282
-
283
- def delete(sql_key)
284
- dealloc cache[sql_key]
285
- cache.delete sql_key
197
+ super.tap { @counter += 1 }
286
198
  end
287
199
 
288
200
  private
289
- def cache
290
- @cache[$$]
291
- end
292
-
293
- def dealloc(key)
294
- @connection.query "DEALLOCATE #{key}" if connection_active?
295
- end
296
-
297
- def connection_active?
298
- @connection.status == PGconn::CONNECTION_OK
299
- rescue PGError
300
- false
301
- end
302
- end
201
+ def dealloc(key)
202
+ @connection.query "DEALLOCATE #{key}" if connection_active?
203
+ rescue PG::Error
204
+ end
303
205
 
304
- class BindSubstitution < Arel::Visitors::PostgreSQL # :nodoc:
305
- include Arel::Visitors::BindVisitor
206
+ def connection_active?
207
+ @connection.status == PG::CONNECTION_OK
208
+ rescue PG::Error
209
+ false
210
+ end
306
211
  end
307
212
 
308
213
  # Initializes and connects a PostgreSQL adapter.
309
214
  def initialize(connection, logger, connection_parameters, config)
310
- super(connection, logger)
311
-
312
- if config.fetch(:prepared_statements) { true }
313
- @visitor = Arel::Visitors::PostgreSQL.new self
314
- else
315
- @visitor = BindSubstitution.new self
316
- end
215
+ super(connection, logger, config)
317
216
 
318
- @connection_parameters, @config = connection_parameters, config
217
+ @connection_parameters = connection_parameters
319
218
 
320
219
  # @local_tz is initialized as nil to avoid warnings when connect tries to use it
321
220
  @local_tz = nil
322
- @table_alias_length = nil
221
+ @max_identifier_length = nil
323
222
 
324
223
  connect
224
+ add_pg_encoders
325
225
  @statements = StatementPool.new @connection,
326
- config.fetch(:statement_limit) { 1000 }
226
+ self.class.type_cast_config_to_integer(config[:statement_limit])
327
227
 
328
- if postgresql_version < 80200
329
- 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."
330
230
  end
331
231
 
332
- @local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
232
+ add_pg_decoders
233
+
234
+ @type_map = Type::HashLookupTypeMap.new
235
+ initialize_type_map
236
+ @local_tz = execute("SHOW TIME ZONE", "SCHEMA").first["TimeZone"]
237
+ @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
333
238
  end
334
239
 
335
240
  # Clears the prepared statements cache.
336
241
  def clear_cache!
337
- @statements.clear
242
+ @lock.synchronize do
243
+ @statements.clear
244
+ end
245
+ end
246
+
247
+ def truncate(table_name, name = nil)
248
+ exec_query "TRUNCATE TABLE #{quote_table_name(table_name)}", name, []
338
249
  end
339
250
 
340
251
  # Is this connection alive and ready for queries?
341
252
  def active?
342
- @connection.query 'SELECT 1'
253
+ @lock.synchronize do
254
+ @connection.query "SELECT 1"
255
+ end
343
256
  true
344
- rescue PGError
257
+ rescue PG::Error
345
258
  false
346
259
  end
347
260
 
348
261
  # Close then reopen the connection.
349
262
  def reconnect!
350
- clear_cache!
351
- @connection.reset
352
- @open_transactions = 0
353
- configure_connection
263
+ @lock.synchronize do
264
+ super
265
+ @connection.reset
266
+ configure_connection
267
+ end
354
268
  end
355
269
 
356
270
  def reset!
357
- clear_cache!
358
- super
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
279
+ end
359
280
  end
360
281
 
361
282
  # Disconnects from the database if already connected. Otherwise, this
362
283
  # method does nothing.
363
284
  def disconnect!
364
- clear_cache!
365
- @connection.close rescue nil
366
- end
367
-
368
- def native_database_types #:nodoc:
369
- NATIVE_DATABASE_TYPES
285
+ @lock.synchronize do
286
+ super
287
+ @connection.close rescue nil
288
+ end
370
289
  end
371
290
 
372
- # Returns true, since this connection adapter supports migrations.
373
- def supports_migrations?
374
- true
291
+ def discard! # :nodoc:
292
+ @connection.socket_io.reopen(IO::NULL) rescue nil
293
+ @connection = nil
375
294
  end
376
295
 
377
- # Does PostgreSQL support finding primary key on non-Active Record tables?
378
- def supports_primary_key? #:nodoc:
379
- true
296
+ def native_database_types #:nodoc:
297
+ NATIVE_DATABASE_TYPES
380
298
  end
381
299
 
382
- # Enable standard-conforming strings if available.
383
300
  def set_standard_conforming_strings
384
- old, self.client_min_messages = client_min_messages, 'panic'
385
- execute('SET standard_conforming_strings = on', 'SCHEMA') rescue nil
386
- ensure
387
- self.client_min_messages = old
301
+ execute("SET standard_conforming_strings = on", "SCHEMA")
388
302
  end
389
303
 
390
- def supports_insert_with_returning?
304
+ def supports_ddl_transactions?
391
305
  true
392
306
  end
393
307
 
394
- def supports_ddl_transactions?
308
+ def supports_advisory_locks?
395
309
  true
396
310
  end
397
311
 
398
- # Returns true, since this connection adapter supports savepoints.
399
- def supports_savepoints?
312
+ def supports_explain?
400
313
  true
401
314
  end
402
315
 
403
- # Returns true.
404
- def supports_explain?
316
+ def supports_extensions?
405
317
  true
406
318
  end
407
319
 
408
- # Returns the configured supported identifier length supported by PostgreSQL
409
- def table_alias_length
410
- @table_alias_length ||= query('SHOW max_identifier_length')[0][0].to_i
320
+ def supports_ranges?
321
+ # Range datatypes weren't introduced until PostgreSQL 9.2
322
+ postgresql_version >= 90200
411
323
  end
412
324
 
413
- # QUOTING ==================================================
325
+ def supports_materialized_views?
326
+ postgresql_version >= 90300
327
+ end
414
328
 
415
- # Escapes binary strings for bytea input to the database.
416
- def escape_bytea(value)
417
- @connection.escape_bytea(value) if value
329
+ def supports_foreign_tables?
330
+ postgresql_version >= 90300
418
331
  end
419
332
 
420
- # Unescapes bytea output from a database to the binary string it represents.
421
- # NOTE: This is NOT an inverse of escape_bytea! This is only to be used
422
- # on escaped binary output from database drive.
423
- def unescape_bytea(value)
424
- @connection.unescape_bytea(value) if value
333
+ def supports_pgcrypto_uuid?
334
+ postgresql_version >= 90400
425
335
  end
426
336
 
427
- # Quotes PostgreSQL-specific data types for SQL input.
428
- def quote(value, column = nil) #:nodoc:
429
- return super unless column
430
-
431
- case value
432
- when Float
433
- return super unless value.infinite? && column.type == :datetime
434
- "'#{value.to_s.downcase}'"
435
- when Numeric
436
- return super unless column.sql_type == 'money'
437
- # Not truly string input, so doesn't require (or allow) escape string syntax.
438
- "'#{value}'"
439
- when String
440
- case column.sql_type
441
- when 'bytea' then "'#{escape_bytea(value)}'"
442
- when 'xml' then "xml '#{quote_string(value)}'"
443
- when /^bit/
444
- case value
445
- when /\A[01]*\Z/ then "B'#{value}'" # Bit-string notation
446
- when /\A[0-9A-F]*\Z/i then "X'#{value}'" # Hexadecimal notation
447
- end
448
- else
449
- super
450
- end
451
- else
452
- super
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")
453
340
  end
341
+ query_value("SELECT pg_try_advisory_lock(#{lock_id})")
454
342
  end
455
343
 
456
- def type_cast(value, column)
457
- return super unless column
458
-
459
- case value
460
- when String
461
- return super unless 'bytea' == column.sql_type
462
- { :value => value, :format => 1 }
463
- else
464
- super
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")
465
347
  end
348
+ query_value("SELECT pg_advisory_unlock(#{lock_id})")
349
+ end
350
+
351
+ def enable_extension(name)
352
+ exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
353
+ reload_type_map
354
+ }
466
355
  end
467
356
 
468
- # Quotes strings for use in SQL input.
469
- def quote_string(s) #:nodoc:
470
- @connection.escape(s)
357
+ def disable_extension(name)
358
+ exec_query("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE").tap {
359
+ reload_type_map
360
+ }
471
361
  end
472
362
 
473
- # Checks the following cases:
474
- #
475
- # - table_name
476
- # - "table.name"
477
- # - schema_name.table_name
478
- # - schema_name."table.name"
479
- # - "schema.name".table_name
480
- # - "schema.name"."table.name"
481
- def quote_table_name(name)
482
- schema, name_part = extract_pg_identifier_from_name(name.to_s)
483
-
484
- unless name_part
485
- quote_column_name(schema)
486
- else
487
- table_name, name_part = extract_pg_identifier_from_name(name_part)
488
- "#{quote_column_name(schema)}.#{quote_column_name(table_name)}"
489
- end
363
+ def extension_enabled?(name)
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
490
366
  end
491
367
 
492
- # Quotes column names for use in SQL queries.
493
- def quote_column_name(name) #:nodoc:
494
- PGconn.quote_ident(name.to_s)
368
+ def extensions
369
+ exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
495
370
  end
496
371
 
497
- # Quote date/time values for use in SQL input. Includes microseconds
498
- # if the value is a Time responding to usec.
499
- def quoted_date(value) #:nodoc:
500
- if value.acts_like?(:time) && value.respond_to?(:usec)
501
- "#{super}.#{sprintf("%06d", value.usec)}"
502
- else
503
- super
504
- end
372
+ # Returns the configured supported identifier length supported by PostgreSQL
373
+ def max_identifier_length
374
+ @max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
505
375
  end
376
+ alias table_alias_length max_identifier_length
377
+ alias index_name_length max_identifier_length
506
378
 
507
379
  # Set the authorized user for this session
508
380
  def session_auth=(user)
509
381
  clear_cache!
510
- exec_query "SET SESSION AUTHORIZATION #{user}"
382
+ execute("SET SESSION AUTHORIZATION #{user}")
511
383
  end
512
384
 
513
- # REFERENTIAL INTEGRITY ====================================
514
-
515
- def supports_disable_referential_integrity? #:nodoc:
516
- true
385
+ def use_insert_returning?
386
+ @use_insert_returning
517
387
  end
518
388
 
519
- def disable_referential_integrity #:nodoc:
520
- if supports_disable_referential_integrity? then
521
- execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
522
- end
523
- yield
524
- ensure
525
- if supports_disable_referential_integrity? then
526
- execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";"))
527
- end
389
+ def column_name_for_operation(operation, node) # :nodoc:
390
+ OPERATION_ALIASES.fetch(operation) { operation.downcase }
528
391
  end
529
392
 
530
- # DATABASE STATEMENTS ======================================
531
-
532
- def explain(arel, binds = [])
533
- sql = "EXPLAIN #{to_sql(arel, binds)}"
534
- ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', binds))
535
- end
536
-
537
- class ExplainPrettyPrinter # :nodoc:
538
- # Pretty prints the result of a EXPLAIN in a way that resembles the output of the
539
- # PostgreSQL shell:
540
- #
541
- # QUERY PLAN
542
- # ------------------------------------------------------------------------------
543
- # Nested Loop Left Join (cost=0.00..37.24 rows=8 width=0)
544
- # Join Filter: (posts.user_id = users.id)
545
- # -> Index Scan using users_pkey on users (cost=0.00..8.27 rows=1 width=4)
546
- # Index Cond: (id = 1)
547
- # -> Seq Scan on posts (cost=0.00..28.88 rows=8 width=4)
548
- # Filter: (posts.user_id = 1)
549
- # (6 rows)
550
- #
551
- def pp(result)
552
- header = result.columns.first
553
- lines = result.rows.map(&:first)
554
-
555
- # We add 2 because there's one char of padding at both sides, note
556
- # the extra hyphens in the example above.
557
- width = [header, *lines].map(&:length).max + 2
558
-
559
- pp = []
560
-
561
- pp << header.center(width).rstrip
562
- pp << '-' * width
563
-
564
- pp += lines.map {|line| " #{line}"}
565
-
566
- nrows = result.rows.length
567
- rows_label = nrows == 1 ? 'row' : 'rows'
568
- pp << "(#{nrows} #{rows_label})"
393
+ OPERATION_ALIASES = { # :nodoc:
394
+ "maximum" => "max",
395
+ "minimum" => "min",
396
+ "average" => "avg",
397
+ }
569
398
 
570
- pp.join("\n") + "\n"
571
- end
399
+ # Returns the version of the connected PostgreSQL server.
400
+ def postgresql_version
401
+ @connection.server_version
572
402
  end
573
403
 
574
- # Executes a SELECT query and returns an array of rows. Each row is an
575
- # array of field values.
576
- def select_rows(sql, name = nil)
577
- select_raw(sql, name).last
404
+ def default_index_type?(index) # :nodoc:
405
+ index.using == :btree || super
578
406
  end
579
407
 
580
- # Executes an INSERT query and returns the new record's ID
581
- def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
582
- unless pk
583
- # Extract the table from the insert sql. Yuck.
584
- table_ref = extract_table_ref_from_insert_sql(sql)
585
- pk = primary_key(table_ref) if table_ref
586
- end
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"
413
+ FOREIGN_KEY_VIOLATION = "23503"
414
+ UNIQUE_VIOLATION = "23505"
415
+ SERIALIZATION_FAILURE = "40001"
416
+ DEADLOCK_DETECTED = "40P01"
417
+ LOCK_NOT_AVAILABLE = "55P03"
418
+ QUERY_CANCELED = "57014"
587
419
 
588
- if pk
589
- select_value("#{sql} RETURNING #{quote_column_name(pk)}")
590
- else
591
- super
592
- end
593
- end
594
- alias :create :insert
420
+ def translate_exception(exception, message)
421
+ return exception unless exception.respond_to?(:result)
595
422
 
596
- # create a 2D array representing the result set
597
- def result_as_array(res) #:nodoc:
598
- # check if we have any binary column and if they need escaping
599
- ftypes = Array.new(res.nfields) do |i|
600
- [i, res.ftype(i)]
423
+ case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
424
+ when UNIQUE_VIOLATION
425
+ RecordNotUnique.new(message)
426
+ when FOREIGN_KEY_VIOLATION
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)
442
+ else
443
+ super
444
+ end
601
445
  end
602
446
 
603
- rows = res.values
604
- return rows unless ftypes.any? { |_, x|
605
- x == BYTEA_COLUMN_TYPE_OID || x == MONEY_COLUMN_TYPE_OID
606
- }
607
-
608
- typehash = ftypes.group_by { |_, type| type }
609
- binaries = typehash[BYTEA_COLUMN_TYPE_OID] || []
610
- monies = typehash[MONEY_COLUMN_TYPE_OID] || []
611
-
612
- rows.each do |row|
613
- # unescape string passed BYTEA field (OID == 17)
614
- binaries.each do |index, _|
615
- row[index] = unescape_bytea(row[index])
447
+ def get_oid_type(oid, fmod, column_name, sql_type = "".freeze)
448
+ if !type_map.key?(oid)
449
+ load_additional_types([oid])
616
450
  end
617
451
 
618
- # If this is a money type column and there are any currency symbols,
619
- # then strip them off. Indeed it would be prettier to do this in
620
- # PostgreSQLColumn.string_to_decimal but would break form input
621
- # fields that call value_before_type_cast.
622
- monies.each do |index, _|
623
- data = row[index]
624
- # Because money output is formatted according to the locale, there are two
625
- # cases to consider (note the decimal separators):
626
- # (1) $12,345,678.12
627
- # (2) $12.345.678,12
628
- case data
629
- when /^-?\D+[\d,]+\.\d{2}$/ # (1)
630
- data.gsub!(/[^-\d.]/, '')
631
- when /^-?\D+[\d.]+,\d{2}$/ # (2)
632
- data.gsub!(/[^-\d,]/, '').sub!(/,/, '.')
452
+ type_map.fetch(oid, fmod, sql_type) {
453
+ warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
454
+ Type.default_value.tap do |cast_type|
455
+ type_map.register_type(oid, cast_type)
633
456
  end
457
+ }
458
+ end
459
+
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|
500
+ precision = extract_precision(sql_type)
501
+ OID::SpecializedString.new(:interval, precision: precision)
634
502
  end
635
- end
636
- end
637
-
638
-
639
- # Queries the database and returns the results in an Array-like object
640
- def query(sql, name = nil) #:nodoc:
641
- log(sql, name) do
642
- result_as_array @connection.async_exec(sql)
643
- end
644
- end
645
503
 
646
- # Executes an SQL statement, returning a PGresult object on success
647
- # or raising a PGError exception otherwise.
648
- def execute(sql, name = nil)
649
- log(sql, name) do
650
- @connection.async_exec(sql)
651
- end
652
- end
653
-
654
- def substitute_at(column, index)
655
- Arel::Nodes::BindParam.new "$#{index + 1}"
656
- end
657
-
658
- def exec_query(sql, name = 'SQL', binds = [])
659
- log(sql, name, binds) do
660
- result = binds.empty? ? exec_no_cache(sql, binds) :
661
- exec_cache(sql, binds)
662
-
663
- ret = ActiveRecord::Result.new(result.fields, result_as_array(result))
664
- result.clear
665
- return ret
666
- end
667
- end
668
-
669
- def exec_delete(sql, name = 'SQL', binds = [])
670
- log(sql, name, binds) do
671
- result = binds.empty? ? exec_no_cache(sql, binds) :
672
- exec_cache(sql, binds)
673
- affected = result.cmd_tuples
674
- result.clear
675
- affected
676
- end
677
- end
678
- alias :exec_update :exec_delete
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|
508
+ precision = extract_precision(sql_type)
509
+ scale = extract_scale(sql_type)
510
+
511
+ # The type for the numeric depends on the width of the field,
512
+ # so we'll do something special here.
513
+ #
514
+ # When dealing with decimal columns:
515
+ #
516
+ # places after decimal = fmod - 4 & 0xffff
517
+ # places before decimal = (fmod - 4) >> 16 & 0xffff
518
+ if fmod && (fmod - 4 & 0xffff).zero?
519
+ # FIXME: Remove this class, and the second argument to
520
+ # lookups on PG
521
+ Type::DecimalWithoutScale.new(precision: precision)
522
+ else
523
+ OID::Decimal.new(precision: precision, scale: scale)
524
+ end
525
+ end
679
526
 
680
- def sql_for_insert(sql, pk, id_value, sequence_name, binds)
681
- unless pk
682
- # Extract the table from the insert sql. Yuck.
683
- table_ref = extract_table_ref_from_insert_sql(sql)
684
- pk = primary_key(table_ref) if table_ref
527
+ load_additional_types
685
528
  end
686
529
 
687
- sql = "#{sql} RETURNING #{quote_column_name(pk)}" if pk
688
-
689
- [sql, binds]
690
- end
691
-
692
- # Executes an UPDATE query and returns the number of affected tuples.
693
- def update_sql(sql, name = nil)
694
- super.cmd_tuples
695
- end
696
-
697
- # Begins a transaction.
698
- def begin_db_transaction
699
- execute "BEGIN"
700
- end
701
-
702
- # Commits a transaction.
703
- def commit_db_transaction
704
- execute "COMMIT"
705
- end
706
-
707
- # Aborts a transaction.
708
- def rollback_db_transaction
709
- execute "ROLLBACK"
710
- end
711
-
712
- def outside_transaction?
713
- @connection.transaction_status == PGconn::PQTRANS_IDLE
714
- end
715
-
716
- def create_savepoint
717
- execute("SAVEPOINT #{current_savepoint_name}")
718
- end
719
-
720
- def rollback_to_savepoint
721
- execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
722
- end
723
-
724
- def release_savepoint
725
- execute("RELEASE SAVEPOINT #{current_savepoint_name}")
726
- end
727
-
728
- # SCHEMA STATEMENTS ========================================
729
-
730
- # Drops the database specified on the +name+ attribute
731
- # and creates it again using the provided +options+.
732
- def recreate_database(name, options = {}) #:nodoc:
733
- drop_database(name)
734
- create_database(name, options)
735
- end
736
-
737
- # Create a new PostgreSQL database. Options include <tt>:owner</tt>, <tt>:template</tt>,
738
- # <tt>:encoding</tt>, <tt>:tablespace</tt>, and <tt>:connection_limit</tt> (note that MySQL uses
739
- # <tt>:charset</tt> while PostgreSQL uses <tt>:encoding</tt>).
740
- #
741
- # Example:
742
- # create_database config[:database], config
743
- # create_database 'foo_development', :encoding => 'unicode'
744
- def create_database(name, options = {})
745
- options = options.reverse_merge(:encoding => "utf8")
746
-
747
- option_string = options.symbolize_keys.sum do |key, value|
748
- case key
749
- when :owner
750
- " OWNER = \"#{value}\""
751
- when :template
752
- " TEMPLATE = \"#{value}\""
753
- when :encoding
754
- " ENCODING = '#{value}'"
755
- when :tablespace
756
- " TABLESPACE = \"#{value}\""
757
- when :connection_limit
758
- " CONNECTION LIMIT = #{value}"
530
+ # Extracts the value from a PostgreSQL column default definition.
531
+ def extract_value_from_default(default)
532
+ case default
533
+ # Quoted types
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
541
+ # Boolean types
542
+ when "true".freeze, "false".freeze
543
+ default
544
+ # Numeric types
545
+ when /\A\(?(-?\d+(\.\d*)?)\)?(::bigint)?\z/
546
+ $1
547
+ # Object identifier types
548
+ when /\A-?\d+\z/
549
+ $1
759
550
  else
760
- ""
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
761
554
  end
762
555
  end
763
556
 
764
- execute "CREATE DATABASE #{quote_table_name(name)}#{option_string}"
765
- end
766
-
767
- # Drops a PostgreSQL database.
768
- #
769
- # Example:
770
- # drop_database 'matt_development'
771
- def drop_database(name) #:nodoc:
772
- execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
773
- end
774
-
775
- # Returns the list of all tables in the schema search path or a specified schema.
776
- def tables(name = nil)
777
- query(<<-SQL, 'SCHEMA').map { |row| row[0] }
778
- SELECT tablename
779
- FROM pg_tables
780
- WHERE schemaname = ANY (current_schemas(false))
781
- SQL
782
- end
783
-
784
- # Returns true if table exists.
785
- # If the schema is not specified as part of +name+ then it will only find tables within
786
- # the current schema search path (regardless of permissions to access tables in other schemas)
787
- def table_exists?(name)
788
- schema, table = Utils.extract_schema_and_table(name.to_s)
789
- return false unless table
790
-
791
- binds = [[nil, table]]
792
- binds << [nil, schema] if schema
793
-
794
- exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
795
- SELECT COUNT(*)
796
- FROM pg_class c
797
- LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
798
- WHERE c.relkind in ('v','r')
799
- AND c.relname = '#{table.gsub(/(^"|"$)/,'')}'
800
- AND n.nspname = #{schema ? "'#{schema}'" : 'ANY (current_schemas(false))'}
801
- SQL
802
- end
803
-
804
- # Returns true if schema exists.
805
- def schema_exists?(name)
806
- exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
807
- SELECT COUNT(*)
808
- FROM pg_namespace
809
- WHERE nspname = '#{name}'
810
- SQL
811
- end
812
-
813
- # Returns an array of indexes for the given table.
814
- def indexes(table_name, name = nil)
815
- result = query(<<-SQL, 'SCHEMA')
816
- SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
817
- FROM pg_class t
818
- INNER JOIN pg_index d ON t.oid = d.indrelid
819
- INNER JOIN pg_class i ON d.indexrelid = i.oid
820
- WHERE i.relkind = 'i'
821
- AND d.indisprimary = 'f'
822
- AND t.relname = '#{table_name}'
823
- AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (current_schemas(false)) )
824
- ORDER BY i.relname
825
- SQL
826
-
827
-
828
- result.map do |row|
829
- index_name = row[0]
830
- unique = row[1] == 't'
831
- indkey = row[2].split(" ")
832
- inddef = row[3]
833
- oid = row[4]
834
-
835
- columns = Hash[query(<<-SQL, "SCHEMA")]
836
- SELECT a.attnum, a.attname
837
- FROM pg_attribute a
838
- WHERE a.attrelid = #{oid}
839
- AND a.attnum IN (#{indkey.join(",")})
840
- SQL
841
-
842
- column_names = columns.values_at(*indkey).compact
843
-
844
- # add info on sort order for columns (only desc order is explicitly specified, asc is the default)
845
- desc_order_columns = inddef.scan(/(\w+) DESC/).flatten
846
- orders = desc_order_columns.any? ? Hash[desc_order_columns.map {|order_column| [order_column, :desc]}] : {}
847
-
848
- column_names.empty? ? nil : IndexDefinition.new(table_name, index_name, unique, column_names, [], orders)
849
- end.compact
850
- end
851
-
852
- # Returns the list of all column definitions for a table.
853
- def columns(table_name, name = nil)
854
- # Limit, precision, and scale are all handled by the superclass.
855
- column_definitions(table_name).collect do |column_name, type, default, notnull|
856
- PostgreSQLColumn.new(column_name, default, type, notnull == 'f')
857
- end
858
- end
859
-
860
- # Returns the current database name.
861
- def current_database
862
- query('select current_database()', 'SCHEMA')[0][0]
863
- end
864
-
865
- # Returns the current schema name.
866
- def current_schema
867
- query('SELECT current_schema', 'SCHEMA')[0][0]
868
- end
869
-
870
- # Returns the current database encoding format.
871
- def encoding
872
- query(<<-end_sql, 'SCHEMA')[0][0]
873
- SELECT pg_encoding_to_char(pg_database.encoding) FROM pg_database
874
- WHERE pg_database.datname LIKE '#{current_database}'
875
- end_sql
876
- end
877
-
878
- # Sets the schema search path to a string of comma-separated schema names.
879
- # Names beginning with $ have to be quoted (e.g. $user => '$user').
880
- # See: http://www.postgresql.org/docs/current/static/ddl-schemas.html
881
- #
882
- # This should be not be called manually but set in database.yml.
883
- def schema_search_path=(schema_csv)
884
- if schema_csv
885
- execute("SET search_path TO #{schema_csv}", 'SCHEMA')
886
- @schema_search_path = schema_csv
557
+ def extract_default_function(default_value, default)
558
+ default if has_default_function?(default_value, default)
887
559
  end
888
- end
889
-
890
- # Returns the active schema search path.
891
- def schema_search_path
892
- @schema_search_path ||= query('SHOW search_path', 'SCHEMA')[0][0]
893
- end
894
-
895
- # Returns the current client message level.
896
- def client_min_messages
897
- query('SHOW client_min_messages', 'SCHEMA')[0][0]
898
- end
899
-
900
- # Set the client message level.
901
- def client_min_messages=(level)
902
- execute("SET client_min_messages TO '#{level}'", 'SCHEMA')
903
- end
904
-
905
- # Returns the sequence name for a table's primary key or some other specified key.
906
- def default_sequence_name(table_name, pk = nil) #:nodoc:
907
- serial_sequence(table_name, pk || 'id').split('.').last
908
- rescue ActiveRecord::StatementInvalid
909
- "#{table_name}_#{pk || 'id'}_seq"
910
- end
911
-
912
- def serial_sequence(table, column)
913
- result = exec_query(<<-eosql, 'SCHEMA')
914
- SELECT pg_get_serial_sequence('#{table}', '#{column}')
915
- eosql
916
- result.rows.first.first
917
- end
918
-
919
- # Resets the sequence of a table's primary key to the maximum value.
920
- def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc:
921
- unless pk and sequence
922
- default_pk, default_sequence = pk_and_sequence_for(table)
923
560
 
924
- pk ||= default_pk
925
- sequence ||= default_sequence
561
+ def has_default_function?(default_value, default)
562
+ !default_value && %r{\w+\(.*\)|\(.*\)::\w+|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
926
563
  end
927
564
 
928
- if @logger && pk && !sequence
929
- @logger.warn "#{table} has primary key #{pk} with no default sequence"
930
- end
931
-
932
- if pk && sequence
933
- quoted_sequence = quote_table_name(sequence)
934
-
935
- select_value <<-end_sql, 'SCHEMA'
936
- SELECT setval('#{quoted_sequence}', (SELECT COALESCE(MAX(#{quote_column_name pk})+(SELECT increment_by FROM #{quoted_sequence}), (SELECT min_value FROM #{quoted_sequence})) FROM #{quote_table_name(table)}), false)
937
- end_sql
938
- end
939
- end
940
-
941
- # Returns a table's primary key and belonging sequence.
942
- def pk_and_sequence_for(table) #:nodoc:
943
- # First try looking for a sequence with a dependency on the
944
- # given table's primary key.
945
- result = query(<<-end_sql, 'SCHEMA')[0]
946
- SELECT attr.attname, seq.relname
947
- FROM pg_class seq,
948
- pg_attribute attr,
949
- pg_depend dep,
950
- pg_namespace name,
951
- pg_constraint cons
952
- WHERE seq.oid = dep.objid
953
- AND seq.relkind = 'S'
954
- AND attr.attrelid = dep.refobjid
955
- AND attr.attnum = dep.refobjsubid
956
- AND attr.attrelid = cons.conrelid
957
- AND attr.attnum = cons.conkey[1]
958
- AND cons.contype = 'p'
959
- AND dep.refobjid = '#{quote_table_name(table)}'::regclass
960
- end_sql
961
-
962
- if result.nil? or result.empty?
963
- result = query(<<-end_sql, 'SCHEMA')[0]
964
- SELECT attr.attname,
965
- CASE
966
- WHEN split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2) ~ '.' THEN
967
- substr(split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2),
968
- strpos(split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2), '.')+1)
969
- ELSE split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2)
970
- END
971
- FROM pg_class t
972
- JOIN pg_attribute attr ON (t.oid = attrelid)
973
- JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
974
- JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
975
- WHERE t.oid = '#{quote_table_name(table)}'::regclass
976
- AND cons.contype = 'p'
977
- AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval'
978
- end_sql
979
- end
980
-
981
- [result.first, result.last]
982
- rescue
983
- nil
984
- end
565
+ def load_additional_types(oids = nil)
566
+ initializer = OID::TypeMapInitializer.new(type_map)
985
567
 
986
- # Returns just a table's primary key
987
- def primary_key(table)
988
- row = exec_query(<<-end_sql, 'SCHEMA').rows.first
989
- SELECT attr.attname
990
- FROM pg_attribute attr
991
- INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1]
992
- WHERE cons.contype = 'p'
993
- AND cons.conrelid = '#{quote_table_name(table)}'::regclass
994
- end_sql
995
-
996
- row && row.first
997
- end
998
-
999
- # Renames a table.
1000
- # Also renames a table's primary key sequence if the sequence name matches the
1001
- # Active Record default.
1002
- #
1003
- # Example:
1004
- # rename_table('octopuses', 'octopi')
1005
- def rename_table(name, new_name)
1006
- clear_cache!
1007
- execute "ALTER TABLE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}"
1008
- pk, seq = pk_and_sequence_for(new_name)
1009
- if seq == "#{name}_#{pk}_seq"
1010
- new_seq = "#{new_name}_#{pk}_seq"
1011
- execute "ALTER TABLE #{quote_table_name(seq)} RENAME TO #{quote_table_name(new_seq)}"
1012
- end
1013
- end
1014
-
1015
- # Adds a new column to the named table.
1016
- # See TableDefinition#column for details of the options you can use.
1017
- def add_column(table_name, column_name, type, options = {})
1018
- clear_cache!
1019
- add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
1020
- add_column_options!(add_column_sql, options)
1021
-
1022
- execute add_column_sql
1023
- end
1024
-
1025
- # Changes the column of a table.
1026
- def change_column(table_name, column_name, type, options = {})
1027
- clear_cache!
1028
- quoted_table_name = quote_table_name(table_name)
1029
-
1030
- execute "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
1031
-
1032
- change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
1033
- change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
1034
- end
1035
-
1036
- # Changes the default value of a table column.
1037
- def change_column_default(table_name, column_name, default)
1038
- clear_cache!
1039
- execute "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote(default)}"
1040
- end
1041
-
1042
- def change_column_null(table_name, column_name, null, default = nil)
1043
- clear_cache!
1044
- unless null || default.nil?
1045
- execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
1046
- end
1047
- execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
1048
- end
1049
-
1050
- # Renames a column in a table.
1051
- def rename_column(table_name, column_name, new_column_name)
1052
- clear_cache!
1053
- execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
1054
- end
1055
-
1056
- def remove_index!(table_name, index_name) #:nodoc:
1057
- execute "DROP INDEX #{quote_table_name(index_name)}"
1058
- end
1059
-
1060
- def rename_index(table_name, old_name, new_name)
1061
- execute "ALTER INDEX #{quote_column_name(old_name)} RENAME TO #{quote_table_name(new_name)}"
1062
- end
1063
-
1064
- def index_name_length
1065
- 63
1066
- end
1067
-
1068
- # Maps logical Rails types to PostgreSQL-specific data types.
1069
- def type_to_sql(type, limit = nil, precision = nil, scale = nil)
1070
- case type.to_s
1071
- when 'binary'
1072
- # PostgreSQL doesn't support limits on binary (bytea) columns.
1073
- # The hard limit is 1Gb, because of a 32-bit size field, and TOAST.
1074
- case limit
1075
- when nil, 0..0x3fffffff; super(type)
1076
- else raise(ActiveRecordError, "No binary type has byte size #{limit}.")
568
+ if supports_ranges?
569
+ query = <<-SQL
570
+ SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
571
+ FROM pg_type as t
572
+ LEFT JOIN pg_range as r ON oid = rngtypid
573
+ SQL
574
+ else
575
+ query = <<-SQL
576
+ SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype
577
+ FROM pg_type as t
578
+ SQL
1077
579
  end
1078
- when 'text'
1079
- # PostgreSQL doesn't support limits on text columns.
1080
- # The hard limit is 1Gb, according to section 8.3 in the manual.
1081
- case limit
1082
- when nil, 0..0x3fffffff; super(type)
1083
- else raise(ActiveRecordError, "The limit on text can be at most 1GB - 1byte.")
580
+
581
+ if oids
582
+ query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
583
+ else
584
+ query += initializer.query_conditions_for_initial_load
1084
585
  end
1085
- when 'integer'
1086
- return 'integer' unless limit
1087
-
1088
- case limit
1089
- when 1, 2; 'smallint'
1090
- when 3, 4; 'integer'
1091
- when 5..8; 'bigint'
1092
- else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
586
+
587
+ execute_and_clear(query, "SCHEMA", []) do |records|
588
+ initializer.run(records)
1093
589
  end
1094
- else
1095
- super
1096
590
  end
1097
- end
1098
-
1099
- # Returns a SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
1100
- #
1101
- # PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
1102
- # requires that the ORDER BY include the distinct column.
1103
- #
1104
- # distinct("posts.id", "posts.created_at desc")
1105
- def distinct(columns, orders) #:nodoc:
1106
- return "DISTINCT #{columns}" if orders.empty?
1107
-
1108
- # Construct a clean list of column names from the ORDER BY clause, removing
1109
- # any ASC/DESC modifiers
1110
- order_columns = orders.collect { |s| s.gsub(/\s+(ASC|DESC)\s*(NULLS\s+(FIRST|LAST)\s*)?/i, '') }
1111
- order_columns.delete_if { |c| c.blank? }
1112
- order_columns = order_columns.zip((0...order_columns.size).to_a).map { |s,i| "#{s} AS alias_#{i}" }
1113
-
1114
- "DISTINCT #{columns}, #{order_columns * ', '}"
1115
- end
1116
591
 
1117
- module Utils
1118
- extend self
592
+ FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
1119
593
 
1120
- # Returns an array of <tt>[schema_name, table_name]</tt> extracted from +name+.
1121
- # +schema_name+ is nil if not specified in +name+.
1122
- # +schema_name+ and +table_name+ exclude surrounding quotes (regardless of whether provided in +name+)
1123
- # +name+ supports the range of schema/table references understood by PostgreSQL, for example:
1124
- #
1125
- # * <tt>table_name</tt>
1126
- # * <tt>"table.name"</tt>
1127
- # * <tt>schema_name.table_name</tt>
1128
- # * <tt>schema_name."table.name"</tt>
1129
- # * <tt>"schema.name"."table name"</tt>
1130
- def extract_schema_and_table(name)
1131
- table, schema = name.scan(/[^".\s]+|"[^"]*"/)[0..1].collect{|m| m.gsub(/(^"|"$)/,'') }.reverse
1132
- [schema, table]
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
602
+ ret = yield result
603
+ result.clear
604
+ ret
1133
605
  end
1134
- end
1135
606
 
1136
- protected
1137
- # Returns the version of the connected PostgreSQL server.
1138
- def postgresql_version
1139
- @connection.server_version
607
+ def exec_no_cache(sql, name, binds)
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
1140
614
  end
1141
615
 
1142
- # See http://www.postgresql.org/docs/9.1/static/errcodes-appendix.html
1143
- FOREIGN_KEY_VIOLATION = "23503"
1144
- UNIQUE_VIOLATION = "23505"
616
+ def exec_cache(sql, name, binds)
617
+ stmt_key = prepare_statement(sql)
618
+ type_casted_binds = type_casted_binds(binds)
1145
619
 
1146
- def translate_exception(exception, message)
1147
- return exception unless exception.respond_to?(:result)
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
624
+ end
625
+ rescue ActiveRecord::StatementInvalid => e
626
+ raise unless is_cached_plan_failure?(e)
1148
627
 
1149
- case exception.result.try(:error_field, PGresult::PG_DIAG_SQLSTATE)
1150
- when UNIQUE_VIOLATION
1151
- RecordNotUnique.new(message, exception)
1152
- when FOREIGN_KEY_VIOLATION
1153
- InvalidForeignKey.new(message, exception)
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)
1154
632
  else
1155
- super
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
1156
638
  end
1157
639
  end
1158
640
 
1159
- private
1160
- FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
1161
-
1162
- def exec_no_cache(sql, binds)
1163
- @connection.async_exec(sql, [])
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
1164
657
  end
1165
658
 
1166
- def exec_cache(sql, binds)
1167
- begin
1168
- stmt_key = prepare_statement sql
1169
-
1170
- # Clear the queue
1171
- @connection.get_last_result
1172
- @connection.send_query_prepared(stmt_key, binds.map { |col, val|
1173
- type_cast(val, col)
1174
- })
1175
- @connection.block
1176
- @connection.get_last_result
1177
- rescue PGError => e
1178
- # Get the PG code for the failure. Annoyingly, the code for
1179
- # prepared statements whose return value may have changed is
1180
- # FEATURE_NOT_SUPPORTED. Check here for more details:
1181
- # http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
1182
- begin
1183
- code = e.result.result_error_field(PGresult::PG_DIAG_SQLSTATE)
1184
- rescue
1185
- raise e
1186
- end
1187
- if FEATURE_NOT_SUPPORTED == code
1188
- @statements.delete sql_key(sql)
1189
- retry
1190
- else
1191
- raise e
1192
- end
1193
- end
659
+ def in_transaction?
660
+ open_transactions > 0
1194
661
  end
1195
662
 
1196
663
  # Returns the statement identifier for the client side cache
@@ -1202,31 +669,34 @@ module ActiveRecord
1202
669
  # Prepare the statement if it hasn't been prepared, return
1203
670
  # the statement key.
1204
671
  def prepare_statement(sql)
1205
- sql_key = sql_key(sql)
1206
- unless @statements.key? sql_key
1207
- nextkey = @statements.next_key
1208
- @connection.prepare nextkey, sql
1209
- @statements[sql_key] = nextkey
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
684
+ end
685
+ @statements[sql_key]
1210
686
  end
1211
- @statements[sql_key]
1212
687
  end
1213
688
 
1214
- # The internal PostgreSQL identifier of the money data type.
1215
- MONEY_COLUMN_TYPE_OID = 790 #:nodoc:
1216
- # The internal PostgreSQL identifier of the BYTEA data type.
1217
- BYTEA_COLUMN_TYPE_OID = 17 #:nodoc:
1218
-
1219
689
  # Connects to a PostgreSQL server and sets up the adapter depending on the
1220
690
  # connected server's characteristics.
1221
691
  def connect
1222
- @connection = PGconn.connect(*@connection_parameters)
1223
-
1224
- # Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of
1225
- # PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision
1226
- # should know about this but can't detect it there, so deal with it here.
1227
- PostgreSQLColumn.money_precision = (postgresql_version >= 80300) ? 19 : 10
1228
-
692
+ @connection = PG.connect(@connection_parameters)
1229
693
  configure_connection
694
+ rescue ::PG::Error => error
695
+ if error.message.include?("does not exist")
696
+ raise ActiveRecord::NoDatabaseError
697
+ else
698
+ raise
699
+ end
1230
700
  end
1231
701
 
1232
702
  # Configures the encoding, verbosity, schema search path, and time zone of the connection.
@@ -1235,45 +705,40 @@ module ActiveRecord
1235
705
  if @config[:encoding]
1236
706
  @connection.set_client_encoding(@config[:encoding])
1237
707
  end
1238
- self.client_min_messages = @config[:min_messages] if @config[:min_messages]
708
+ self.client_min_messages = @config[:min_messages] || "warning"
1239
709
  self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
1240
710
 
1241
- # 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.
1242
712
  set_standard_conforming_strings
1243
713
 
714
+ variables = @config.fetch(:variables, {}).stringify_keys
715
+
1244
716
  # If using Active Record's time zone support configure the connection to return
1245
717
  # TIMESTAMP WITH ZONE types in UTC.
1246
- if ActiveRecord::Base.default_timezone == :utc
1247
- execute("SET time zone 'UTC'", 'SCHEMA')
1248
- elsif @local_tz
1249
- 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
1250
724
  end
1251
- end
1252
-
1253
- # Returns the current ID of a table's sequence.
1254
- def last_insert_id(sequence_name) #:nodoc:
1255
- r = exec_query("SELECT currval('#{sequence_name}')", 'SQL')
1256
- Integer(r.rows.first.first)
1257
- end
1258
725
 
1259
- # Executes a SELECT query and returns the results, performing any data type
1260
- # conversions that are required to be performed here instead of in PostgreSQLColumn.
1261
- def select(sql, name = nil, binds = [])
1262
- exec_query(sql, name, binds).to_a
1263
- end
1264
-
1265
- def select_raw(sql, name = nil)
1266
- res = execute(sql, name)
1267
- results = result_as_array(res)
1268
- fields = res.fields
1269
- res.clear
1270
- return fields, results
726
+ # SET statements from :variables config hash
727
+ # https://www.postgresql.org/docs/current/static/sql-set.html
728
+ variables.map do |k, v|
729
+ if v == ":default" || v == :default
730
+ # Sets the value to the global or compile default
731
+ execute("SET SESSION #{k} TO DEFAULT", "SCHEMA")
732
+ elsif !v.nil?
733
+ execute("SET SESSION #{k} TO #{quote(v)}", "SCHEMA")
734
+ end
735
+ end
1271
736
  end
1272
737
 
1273
738
  # Returns the list of a table's column names, data types, and default values.
1274
739
  #
1275
740
  # The underlying query is roughly:
1276
- # SELECT column.name, column.type, default.value
741
+ # SELECT column.name, column.type, default.value, column.comment
1277
742
  # FROM column LEFT JOIN default
1278
743
  # ON column.table_id = default.table_id
1279
744
  # AND column.num = default.column_num
@@ -1288,36 +753,111 @@ module ActiveRecord
1288
753
  # Query implementation notes:
1289
754
  # - format_type includes the column size constraint, e.g. varchar(50)
1290
755
  # - ::regclass is a function that gives the id for a table name
1291
- def column_definitions(table_name) #:nodoc:
1292
- exec_query(<<-end_sql, 'SCHEMA').rows
1293
- SELECT a.attname, format_type(a.atttypid, a.atttypmod),
1294
- pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod
1295
- FROM pg_attribute a LEFT JOIN pg_attrdef d
1296
- ON a.attrelid = d.adrelid AND a.attnum = d.adnum
1297
- WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
1298
- AND a.attnum > 0 AND NOT a.attisdropped
1299
- ORDER BY a.attnum
756
+ def column_definitions(table_name)
757
+ query(<<-end_sql, "SCHEMA")
758
+ SELECT a.attname, format_type(a.atttypid, a.atttypmod),
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
766
+ AND a.attnum > 0 AND NOT a.attisdropped
767
+ ORDER BY a.attnum
1300
768
  end_sql
1301
769
  end
1302
770
 
1303
- def extract_pg_identifier_from_name(name)
1304
- match_data = name.start_with?('"') ? name.match(/\"([^\"]+)\"/) : name.match(/([^\.]+)/)
1305
-
1306
- if match_data
1307
- rest = name[match_data[0].length, name.length]
1308
- rest = rest[1, rest.length] if rest.start_with? "."
1309
- [match_data[1], (rest.length > 0 ? rest : nil)]
1310
- end
1311
- end
1312
-
1313
771
  def extract_table_ref_from_insert_sql(sql)
1314
- sql[/into\s+([^\(]*).*values\s*\(/i]
772
+ sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
1315
773
  $1.strip if $1
1316
774
  end
1317
775
 
1318
- def table_definition
1319
- TableDefinition.new(self)
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
1320
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
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)
1321
861
  end
1322
862
  end
1323
863
  end