activerecord 3.2.19 → 5.0.0

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

Potentially problematic release.


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

Files changed (264) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1715 -604
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +40 -45
  5. data/examples/performance.rb +33 -22
  6. data/examples/simple.rb +3 -4
  7. data/lib/active_record/aggregations.rb +76 -51
  8. data/lib/active_record/association_relation.rb +35 -0
  9. data/lib/active_record/associations/alias_tracker.rb +54 -40
  10. data/lib/active_record/associations/association.rb +76 -56
  11. data/lib/active_record/associations/association_scope.rb +125 -93
  12. data/lib/active_record/associations/belongs_to_association.rb +57 -28
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -2
  14. data/lib/active_record/associations/builder/association.rb +120 -32
  15. data/lib/active_record/associations/builder/belongs_to.rb +115 -62
  16. data/lib/active_record/associations/builder/collection_association.rb +61 -53
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +117 -43
  18. data/lib/active_record/associations/builder/has_many.rb +9 -65
  19. data/lib/active_record/associations/builder/has_one.rb +18 -52
  20. data/lib/active_record/associations/builder/singular_association.rb +18 -19
  21. data/lib/active_record/associations/collection_association.rb +268 -186
  22. data/lib/active_record/associations/collection_proxy.rb +1003 -63
  23. data/lib/active_record/associations/foreign_association.rb +11 -0
  24. data/lib/active_record/associations/has_many_association.rb +81 -41
  25. data/lib/active_record/associations/has_many_through_association.rb +76 -55
  26. data/lib/active_record/associations/has_one_association.rb +51 -21
  27. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  28. data/lib/active_record/associations/join_dependency/join_association.rb +83 -108
  29. data/lib/active_record/associations/join_dependency/join_base.rb +7 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +30 -37
  31. data/lib/active_record/associations/join_dependency.rb +239 -155
  32. data/lib/active_record/associations/preloader/association.rb +97 -62
  33. data/lib/active_record/associations/preloader/collection_association.rb +2 -8
  34. data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
  35. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  36. data/lib/active_record/associations/preloader/singular_association.rb +3 -3
  37. data/lib/active_record/associations/preloader/through_association.rb +75 -33
  38. data/lib/active_record/associations/preloader.rb +111 -79
  39. data/lib/active_record/associations/singular_association.rb +35 -13
  40. data/lib/active_record/associations/through_association.rb +41 -19
  41. data/lib/active_record/associations.rb +727 -501
  42. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  43. data/lib/active_record/attribute.rb +213 -0
  44. data/lib/active_record/attribute_assignment.rb +32 -162
  45. data/lib/active_record/attribute_decorators.rb +67 -0
  46. data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
  47. data/lib/active_record/attribute_methods/dirty.rb +101 -61
  48. data/lib/active_record/attribute_methods/primary_key.rb +50 -36
  49. data/lib/active_record/attribute_methods/query.rb +7 -6
  50. data/lib/active_record/attribute_methods/read.rb +56 -117
  51. data/lib/active_record/attribute_methods/serialization.rb +43 -96
  52. data/lib/active_record/attribute_methods/time_zone_conversion.rb +93 -42
  53. data/lib/active_record/attribute_methods/write.rb +34 -45
  54. data/lib/active_record/attribute_methods.rb +333 -144
  55. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  56. data/lib/active_record/attribute_set/builder.rb +108 -0
  57. data/lib/active_record/attribute_set.rb +108 -0
  58. data/lib/active_record/attributes.rb +265 -0
  59. data/lib/active_record/autosave_association.rb +285 -223
  60. data/lib/active_record/base.rb +95 -490
  61. data/lib/active_record/callbacks.rb +95 -61
  62. data/lib/active_record/coders/json.rb +13 -0
  63. data/lib/active_record/coders/yaml_column.rb +28 -19
  64. data/lib/active_record/collection_cache_key.rb +40 -0
  65. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +724 -277
  66. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  67. data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -192
  68. data/lib/active_record/connection_adapters/abstract/query_cache.rb +31 -26
  69. data/lib/active_record/connection_adapters/abstract/quoting.rb +140 -57
  70. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +147 -0
  72. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +419 -276
  73. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +105 -0
  74. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +963 -276
  75. data/lib/active_record/connection_adapters/abstract/transaction.rb +232 -0
  76. data/lib/active_record/connection_adapters/abstract_adapter.rb +397 -106
  77. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +643 -342
  78. data/lib/active_record/connection_adapters/column.rb +30 -259
  79. data/lib/active_record/connection_adapters/connection_specification.rb +263 -0
  80. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  81. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  82. data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
  83. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  84. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  85. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  86. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  87. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  88. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  89. data/lib/active_record/connection_adapters/mysql2_adapter.rb +47 -196
  90. data/lib/active_record/connection_adapters/postgresql/column.rb +15 -0
  91. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +170 -0
  92. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +70 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +48 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +21 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +10 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +39 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +93 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  110. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  111. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  112. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  113. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  114. data/lib/active_record/connection_adapters/postgresql/oid.rb +31 -0
  115. data/lib/active_record/connection_adapters/postgresql/quoting.rb +116 -0
  116. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +49 -0
  117. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +180 -0
  118. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  119. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +682 -0
  120. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  121. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  122. data/lib/active_record/connection_adapters/postgresql_adapter.rb +558 -1039
  123. data/lib/active_record/connection_adapters/schema_cache.rb +74 -36
  124. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  125. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  126. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  127. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  128. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +538 -24
  129. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  130. data/lib/active_record/connection_handling.rb +155 -0
  131. data/lib/active_record/core.rb +561 -0
  132. data/lib/active_record/counter_cache.rb +146 -105
  133. data/lib/active_record/dynamic_matchers.rb +101 -64
  134. data/lib/active_record/enum.rb +234 -0
  135. data/lib/active_record/errors.rb +153 -56
  136. data/lib/active_record/explain.rb +15 -63
  137. data/lib/active_record/explain_registry.rb +30 -0
  138. data/lib/active_record/explain_subscriber.rb +10 -6
  139. data/lib/active_record/fixture_set/file.rb +77 -0
  140. data/lib/active_record/fixtures.rb +355 -232
  141. data/lib/active_record/gem_version.rb +15 -0
  142. data/lib/active_record/inheritance.rb +144 -79
  143. data/lib/active_record/integration.rb +66 -13
  144. data/lib/active_record/internal_metadata.rb +56 -0
  145. data/lib/active_record/legacy_yaml_adapter.rb +46 -0
  146. data/lib/active_record/locale/en.yml +9 -1
  147. data/lib/active_record/locking/optimistic.rb +77 -56
  148. data/lib/active_record/locking/pessimistic.rb +6 -6
  149. data/lib/active_record/log_subscriber.rb +53 -28
  150. data/lib/active_record/migration/command_recorder.rb +166 -33
  151. data/lib/active_record/migration/compatibility.rb +126 -0
  152. data/lib/active_record/migration/join_table.rb +15 -0
  153. data/lib/active_record/migration.rb +792 -264
  154. data/lib/active_record/model_schema.rb +192 -130
  155. data/lib/active_record/nested_attributes.rb +238 -145
  156. data/lib/active_record/no_touching.rb +52 -0
  157. data/lib/active_record/null_relation.rb +89 -0
  158. data/lib/active_record/persistence.rb +357 -157
  159. data/lib/active_record/query_cache.rb +22 -43
  160. data/lib/active_record/querying.rb +34 -23
  161. data/lib/active_record/railtie.rb +88 -48
  162. data/lib/active_record/railties/console_sandbox.rb +3 -4
  163. data/lib/active_record/railties/controller_runtime.rb +5 -4
  164. data/lib/active_record/railties/databases.rake +170 -422
  165. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  166. data/lib/active_record/readonly_attributes.rb +2 -5
  167. data/lib/active_record/reflection.rb +715 -189
  168. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  169. data/lib/active_record/relation/batches.rb +203 -50
  170. data/lib/active_record/relation/calculations.rb +203 -194
  171. data/lib/active_record/relation/delegation.rb +103 -25
  172. data/lib/active_record/relation/finder_methods.rb +457 -261
  173. data/lib/active_record/relation/from_clause.rb +32 -0
  174. data/lib/active_record/relation/merger.rb +167 -0
  175. data/lib/active_record/relation/predicate_builder/array_handler.rb +43 -0
  176. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  177. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  178. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  179. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  180. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  181. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  182. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  183. data/lib/active_record/relation/predicate_builder.rb +153 -48
  184. data/lib/active_record/relation/query_attribute.rb +19 -0
  185. data/lib/active_record/relation/query_methods.rb +1019 -194
  186. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  187. data/lib/active_record/relation/spawn_methods.rb +46 -150
  188. data/lib/active_record/relation/where_clause.rb +174 -0
  189. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  190. data/lib/active_record/relation.rb +450 -245
  191. data/lib/active_record/result.rb +104 -12
  192. data/lib/active_record/runtime_registry.rb +22 -0
  193. data/lib/active_record/sanitization.rb +120 -94
  194. data/lib/active_record/schema.rb +28 -18
  195. data/lib/active_record/schema_dumper.rb +141 -74
  196. data/lib/active_record/schema_migration.rb +50 -0
  197. data/lib/active_record/scoping/default.rb +64 -57
  198. data/lib/active_record/scoping/named.rb +93 -108
  199. data/lib/active_record/scoping.rb +73 -121
  200. data/lib/active_record/secure_token.rb +38 -0
  201. data/lib/active_record/serialization.rb +7 -5
  202. data/lib/active_record/statement_cache.rb +113 -0
  203. data/lib/active_record/store.rb +173 -15
  204. data/lib/active_record/suppressor.rb +58 -0
  205. data/lib/active_record/table_metadata.rb +68 -0
  206. data/lib/active_record/tasks/database_tasks.rb +313 -0
  207. data/lib/active_record/tasks/mysql_database_tasks.rb +151 -0
  208. data/lib/active_record/tasks/postgresql_database_tasks.rb +110 -0
  209. data/lib/active_record/tasks/sqlite_database_tasks.rb +59 -0
  210. data/lib/active_record/timestamp.rb +42 -24
  211. data/lib/active_record/touch_later.rb +58 -0
  212. data/lib/active_record/transactions.rb +233 -105
  213. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  214. data/lib/active_record/type/date.rb +7 -0
  215. data/lib/active_record/type/date_time.rb +7 -0
  216. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  217. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  218. data/lib/active_record/type/internal/timezone.rb +15 -0
  219. data/lib/active_record/type/serialized.rb +63 -0
  220. data/lib/active_record/type/time.rb +20 -0
  221. data/lib/active_record/type/type_map.rb +64 -0
  222. data/lib/active_record/type.rb +72 -0
  223. data/lib/active_record/type_caster/connection.rb +29 -0
  224. data/lib/active_record/type_caster/map.rb +19 -0
  225. data/lib/active_record/type_caster.rb +7 -0
  226. data/lib/active_record/validations/absence.rb +23 -0
  227. data/lib/active_record/validations/associated.rb +33 -18
  228. data/lib/active_record/validations/length.rb +24 -0
  229. data/lib/active_record/validations/presence.rb +66 -0
  230. data/lib/active_record/validations/uniqueness.rb +128 -68
  231. data/lib/active_record/validations.rb +48 -40
  232. data/lib/active_record/version.rb +5 -7
  233. data/lib/active_record.rb +71 -47
  234. data/lib/rails/generators/active_record/migration/migration_generator.rb +56 -8
  235. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +24 -0
  236. data/lib/rails/generators/active_record/migration/templates/migration.rb +28 -16
  237. data/lib/rails/generators/active_record/migration.rb +18 -8
  238. data/lib/rails/generators/active_record/model/model_generator.rb +38 -16
  239. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  240. data/lib/rails/generators/active_record/model/templates/model.rb +7 -6
  241. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  242. data/lib/rails/generators/active_record.rb +3 -11
  243. metadata +188 -134
  244. data/examples/associations.png +0 -0
  245. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  246. data/lib/active_record/associations/join_helper.rb +0 -55
  247. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  248. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  249. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  250. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -441
  251. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  252. data/lib/active_record/dynamic_finder_match.rb +0 -68
  253. data/lib/active_record/dynamic_scope_match.rb +0 -23
  254. data/lib/active_record/fixtures/file.rb +0 -65
  255. data/lib/active_record/identity_map.rb +0 -162
  256. data/lib/active_record/observer.rb +0 -121
  257. data/lib/active_record/serializers/xml_serializer.rb +0 -203
  258. data/lib/active_record/session_store.rb +0 -360
  259. data/lib/active_record/test_case.rb +0 -73
  260. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
  261. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  262. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  263. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  264. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,245 +1,132 @@
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'
5
-
6
- # Make sure we're using pg high enough for PGResult#values
7
- gem 'pg', '~> 0.11'
1
+ # Make sure we're using pg high enough for type casts and Ruby 2.2+ compatibility
2
+ gem 'pg', '~> 0.18'
8
3
  require 'pg'
9
4
 
5
+ require "active_record/connection_adapters/abstract_adapter"
6
+ require "active_record/connection_adapters/postgresql/column"
7
+ require "active_record/connection_adapters/postgresql/database_statements"
8
+ require "active_record/connection_adapters/postgresql/explain_pretty_printer"
9
+ require "active_record/connection_adapters/postgresql/oid"
10
+ require "active_record/connection_adapters/postgresql/quoting"
11
+ require "active_record/connection_adapters/postgresql/referential_integrity"
12
+ require "active_record/connection_adapters/postgresql/schema_definitions"
13
+ require "active_record/connection_adapters/postgresql/schema_dumper"
14
+ require "active_record/connection_adapters/postgresql/schema_statements"
15
+ require "active_record/connection_adapters/postgresql/type_metadata"
16
+ require "active_record/connection_adapters/postgresql/utils"
17
+ require "active_record/connection_adapters/statement_pool"
18
+
10
19
  module ActiveRecord
11
- class Base
20
+ module ConnectionHandling # :nodoc:
12
21
  # 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
22
+ def postgresql_connection(config)
23
+ conn_params = config.symbolize_keys
24
+
25
+ conn_params.delete_if { |_, v| v.nil? }
26
+
27
+ # Map ActiveRecords param names to PGs.
28
+ conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
29
+ conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
30
+
31
+ # Forward only valid config params to PGconn.connect.
32
+ valid_conn_param_keys = PGconn.conndefaults_hash.keys + [:requiressl]
33
+ conn_params.slice!(*valid_conn_param_keys)
25
34
 
26
35
  # The postgres drivers don't allow the creation of an unconnected PGconn object,
27
36
  # 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)
37
+ ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, conn_params, config)
29
38
  end
30
39
  end
31
40
 
32
41
  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
39
-
40
- # :stopdoc:
41
- class << self
42
- attr_accessor :money_precision
43
- def string_to_time(string)
44
- return string unless String === string
45
-
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:
55
-
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
64
-
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
70
-
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
187
- end
188
-
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.
42
+ # The PostgreSQL adapter works with the native C (https://bitbucket.org/ged/ruby-pg) driver.
191
43
  #
192
44
  # Options:
193
45
  #
194
- # * <tt>:host</tt> - Defaults to "localhost".
46
+ # * <tt>:host</tt> - Defaults to a Unix-domain socket in /tmp. On machines without Unix-domain sockets,
47
+ # the default is to connect to localhost.
195
48
  # * <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.
49
+ # * <tt>:username</tt> - Defaults to be the same as the operating system name of the user running the application.
50
+ # * <tt>:password</tt> - Password to be used if the server demands password authentication.
51
+ # * <tt>:database</tt> - Defaults to be the same as the user name.
199
52
  # * <tt>:schema_search_path</tt> - An optional schema search path for the connection given
200
53
  # as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
201
54
  # * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO
202
55
  # <encoding></tt> call on the connection.
203
56
  # * <tt>:min_messages</tt> - An optional client min messages that is used in a
204
57
  # <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
58
+ # * <tt>:variables</tt> - An optional hash of additional parameters that
59
+ # will be used in <tt>SET SESSION key = val</tt> calls on the connection.
60
+ # * <tt>:insert_returning</tt> - An optional boolean to control the use of <tt>RETURNING</tt> for <tt>INSERT</tt> statements
61
+ # defaults to true.
62
+ #
63
+ # Any further options are used as connection parameters to libpq. See
64
+ # http://www.postgresql.org/docs/current/static/libpq-connect.html for the
65
+ # list of parameters.
66
+ #
67
+ # In addition, default connection parameters of libpq can be set per environment variables.
68
+ # See http://www.postgresql.org/docs/current/static/libpq-envars.html .
205
69
  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
211
-
212
- def tsvector(*args)
213
- options = args.extract_options!
214
- column(args[0], 'tsvector', options)
215
- end
216
- end
217
-
218
- ADAPTER_NAME = 'PostgreSQL'
70
+ ADAPTER_NAME = 'PostgreSQL'.freeze
219
71
 
220
72
  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" }
73
+ primary_key: "serial primary key",
74
+ string: { name: "character varying" },
75
+ text: { name: "text" },
76
+ integer: { name: "integer" },
77
+ float: { name: "float" },
78
+ decimal: { name: "decimal" },
79
+ datetime: { name: "timestamp" },
80
+ time: { name: "time" },
81
+ date: { name: "date" },
82
+ daterange: { name: "daterange" },
83
+ numrange: { name: "numrange" },
84
+ tsrange: { name: "tsrange" },
85
+ tstzrange: { name: "tstzrange" },
86
+ int4range: { name: "int4range" },
87
+ int8range: { name: "int8range" },
88
+ binary: { name: "bytea" },
89
+ boolean: { name: "boolean" },
90
+ xml: { name: "xml" },
91
+ tsvector: { name: "tsvector" },
92
+ hstore: { name: "hstore" },
93
+ inet: { name: "inet" },
94
+ cidr: { name: "cidr" },
95
+ macaddr: { name: "macaddr" },
96
+ uuid: { name: "uuid" },
97
+ json: { name: "json" },
98
+ jsonb: { name: "jsonb" },
99
+ ltree: { name: "ltree" },
100
+ citext: { name: "citext" },
101
+ point: { name: "point" },
102
+ line: { name: "line" },
103
+ lseg: { name: "lseg" },
104
+ box: { name: "box" },
105
+ path: { name: "path" },
106
+ polygon: { name: "polygon" },
107
+ circle: { name: "circle" },
108
+ bit: { name: "bit" },
109
+ bit_varying: { name: "bit varying" },
110
+ money: { name: "money" },
235
111
  }
236
112
 
237
- # Returns 'PostgreSQL' as adapter name for identification purposes.
238
- def adapter_name
239
- ADAPTER_NAME
113
+ OID = PostgreSQL::OID #:nodoc:
114
+
115
+ include PostgreSQL::Quoting
116
+ include PostgreSQL::ReferentialIntegrity
117
+ include PostgreSQL::SchemaStatements
118
+ include PostgreSQL::DatabaseStatements
119
+ include PostgreSQL::ColumnDumper
120
+
121
+ def schema_creation # :nodoc:
122
+ PostgreSQL::SchemaCreation.new self
240
123
  end
241
124
 
242
- # Returns +true+, since this connection adapter supports prepared statement
125
+ def arel_visitor # :nodoc:
126
+ Arel::Visitors::PostgreSQL.new(self)
127
+ end
128
+
129
+ # Returns true, since this connection adapter supports prepared statement
243
130
  # caching.
244
131
  def supports_statement_cache?
245
132
  true
@@ -249,87 +136,99 @@ module ActiveRecord
249
136
  true
250
137
  end
251
138
 
139
+ def supports_partial_index?
140
+ true
141
+ end
142
+
143
+ def supports_expression_index?
144
+ true
145
+ end
146
+
147
+ def supports_transaction_isolation?
148
+ true
149
+ end
150
+
151
+ def supports_foreign_keys?
152
+ true
153
+ end
154
+
155
+ def supports_views?
156
+ true
157
+ end
158
+
159
+ def supports_datetime_with_precision?
160
+ true
161
+ end
162
+
163
+ def supports_json?
164
+ postgresql_version >= 90200
165
+ end
166
+
167
+ def supports_comments?
168
+ true
169
+ end
170
+
171
+ def supports_savepoints?
172
+ true
173
+ end
174
+
175
+ def index_algorithms
176
+ { concurrently: 'CONCURRENTLY' }
177
+ end
178
+
252
179
  class StatementPool < ConnectionAdapters::StatementPool
253
180
  def initialize(connection, max)
254
- super
181
+ super(max)
182
+ @connection = connection
255
183
  @counter = 0
256
- @cache = Hash.new { |h,pid| h[pid] = {} }
257
184
  end
258
185
 
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
186
  def next_key
265
187
  "a#{@counter + 1}"
266
188
  end
267
189
 
268
190
  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
191
+ super.tap { @counter += 1 }
286
192
  end
287
193
 
288
194
  private
289
- def cache
290
- @cache[$$]
291
- end
292
-
293
- def dealloc(key)
294
- @connection.query "DEALLOCATE #{key}" if connection_active?
295
- end
296
195
 
297
- def connection_active?
298
- @connection.status == PGconn::CONNECTION_OK
299
- rescue PGError
300
- false
301
- end
302
- end
196
+ def dealloc(key)
197
+ @connection.query "DEALLOCATE #{key}" if connection_active?
198
+ end
303
199
 
304
- class BindSubstitution < Arel::Visitors::PostgreSQL # :nodoc:
305
- include Arel::Visitors::BindVisitor
200
+ def connection_active?
201
+ @connection.status == PGconn::CONNECTION_OK
202
+ rescue PGError
203
+ false
204
+ end
306
205
  end
307
206
 
308
207
  # Initializes and connects a PostgreSQL adapter.
309
208
  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
209
+ super(connection, logger, config)
317
210
 
318
- @connection_parameters, @config = connection_parameters, config
211
+ @connection_parameters = connection_parameters
319
212
 
320
213
  # @local_tz is initialized as nil to avoid warnings when connect tries to use it
321
214
  @local_tz = nil
322
215
  @table_alias_length = nil
323
216
 
324
217
  connect
218
+ add_pg_encoders
325
219
  @statements = StatementPool.new @connection,
326
- config.fetch(:statement_limit) { 1000 }
220
+ self.class.type_cast_config_to_integer(config[:statement_limit])
327
221
 
328
- if postgresql_version < 80200
329
- raise "Your version of PostgreSQL (#{postgresql_version}) is too old, please upgrade!"
222
+ if postgresql_version < 90100
223
+ raise "Your version of PostgreSQL (#{postgresql_version}) is too old. Active Record supports PostgreSQL >= 9.1."
330
224
  end
331
225
 
226
+ add_pg_decoders
227
+
228
+ @type_map = Type::HashLookupTypeMap.new
229
+ initialize_type_map(type_map)
332
230
  @local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
231
+ @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
333
232
  end
334
233
 
335
234
  # Clears the prepared statements cache.
@@ -337,6 +236,10 @@ module ActiveRecord
337
236
  @statements.clear
338
237
  end
339
238
 
239
+ def truncate(table_name, name = nil)
240
+ exec_query "TRUNCATE TABLE #{quote_table_name(table_name)}", name, []
241
+ end
242
+
340
243
  # Is this connection alive and ready for queries?
341
244
  def active?
342
245
  @connection.query 'SELECT 1'
@@ -347,21 +250,25 @@ module ActiveRecord
347
250
 
348
251
  # Close then reopen the connection.
349
252
  def reconnect!
350
- clear_cache!
253
+ super
351
254
  @connection.reset
352
- @open_transactions = 0
353
255
  configure_connection
354
256
  end
355
257
 
356
258
  def reset!
357
259
  clear_cache!
358
- super
260
+ reset_transaction
261
+ unless @connection.transaction_status == ::PG::PQTRANS_IDLE
262
+ @connection.query 'ROLLBACK'
263
+ end
264
+ @connection.query 'DISCARD ALL'
265
+ configure_connection
359
266
  end
360
267
 
361
268
  # Disconnects from the database if already connected. Otherwise, this
362
269
  # method does nothing.
363
270
  def disconnect!
364
- clear_cache!
271
+ super
365
272
  @connection.close rescue nil
366
273
  end
367
274
 
@@ -379,818 +286,359 @@ module ActiveRecord
379
286
  true
380
287
  end
381
288
 
382
- # Enable standard-conforming strings if available.
383
289
  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
388
- end
389
-
390
- def supports_insert_with_returning?
391
- true
290
+ execute('SET standard_conforming_strings = on', 'SCHEMA')
392
291
  end
393
292
 
394
293
  def supports_ddl_transactions?
395
294
  true
396
295
  end
397
296
 
398
- # Returns true, since this connection adapter supports savepoints.
399
- def supports_savepoints?
297
+ def supports_advisory_locks?
400
298
  true
401
299
  end
402
300
 
403
- # Returns true.
404
301
  def supports_explain?
405
302
  true
406
303
  end
407
304
 
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
305
+ def supports_extensions?
306
+ true
411
307
  end
412
308
 
413
- # QUOTING ==================================================
414
-
415
- # Escapes binary strings for bytea input to the database.
416
- def escape_bytea(value)
417
- @connection.escape_bytea(value) if value
309
+ # Range datatypes weren't introduced until PostgreSQL 9.2
310
+ def supports_ranges?
311
+ postgresql_version >= 90200
418
312
  end
419
313
 
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
314
+ def supports_materialized_views?
315
+ postgresql_version >= 90300
425
316
  end
426
317
 
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
318
+ def get_advisory_lock(lock_id) # :nodoc:
319
+ unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
320
+ raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer")
453
321
  end
322
+ select_value("SELECT pg_try_advisory_lock(#{lock_id});")
454
323
  end
455
324
 
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
325
+ def release_advisory_lock(lock_id) # :nodoc:
326
+ unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
327
+ raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer")
465
328
  end
329
+ select_value("SELECT pg_advisory_unlock(#{lock_id})")
466
330
  end
467
331
 
468
- # Quotes strings for use in SQL input.
469
- def quote_string(s) #:nodoc:
470
- @connection.escape(s)
332
+ def enable_extension(name)
333
+ exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
334
+ reload_type_map
335
+ }
471
336
  end
472
337
 
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
338
+ def disable_extension(name)
339
+ exec_query("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE").tap {
340
+ reload_type_map
341
+ }
490
342
  end
491
343
 
492
- # Quotes column names for use in SQL queries.
493
- def quote_column_name(name) #:nodoc:
494
- PGconn.quote_ident(name.to_s)
344
+ def extension_enabled?(name)
345
+ if supports_extensions?
346
+ res = exec_query "SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled",
347
+ 'SCHEMA'
348
+ res.cast_values.first
349
+ end
495
350
  end
496
351
 
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)}"
352
+ def extensions
353
+ if supports_extensions?
354
+ exec_query("SELECT extname from pg_extension", "SCHEMA").cast_values
502
355
  else
503
356
  super
504
357
  end
505
358
  end
506
359
 
360
+ # Returns the configured supported identifier length supported by PostgreSQL
361
+ def table_alias_length
362
+ @table_alias_length ||= query('SHOW max_identifier_length', 'SCHEMA')[0][0].to_i
363
+ end
364
+
507
365
  # Set the authorized user for this session
508
366
  def session_auth=(user)
509
367
  clear_cache!
510
368
  exec_query "SET SESSION AUTHORIZATION #{user}"
511
369
  end
512
370
 
513
- # REFERENTIAL INTEGRITY ====================================
514
-
515
- def supports_disable_referential_integrity? #:nodoc:
516
- true
371
+ def use_insert_returning?
372
+ @use_insert_returning
517
373
  end
518
374
 
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
375
+ def valid_type?(type)
376
+ !native_database_types[type].nil?
528
377
  end
529
378
 
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))
379
+ def update_table_definition(table_name, base) #:nodoc:
380
+ PostgreSQL::Table.new(table_name, base)
535
381
  end
536
382
 
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})"
569
-
570
- pp.join("\n") + "\n"
571
- end
383
+ def lookup_cast_type(sql_type) # :nodoc:
384
+ oid = execute("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").first['oid'].to_i
385
+ super(oid)
572
386
  end
573
387
 
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
388
+ def column_name_for_operation(operation, node) # :nodoc:
389
+ OPERATION_ALIASES.fetch(operation) { operation.downcase }
578
390
  end
579
391
 
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
392
+ OPERATION_ALIASES = { # :nodoc:
393
+ "maximum" => "max",
394
+ "minimum" => "min",
395
+ "average" => "avg",
396
+ }
587
397
 
588
- if pk
589
- select_value("#{sql} RETURNING #{quote_column_name(pk)}")
590
- else
591
- super
592
- end
398
+ # Returns the version of the connected PostgreSQL server.
399
+ def postgresql_version
400
+ @connection.server_version
593
401
  end
594
- alias :create :insert
595
402
 
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)]
601
- end
602
-
603
- rows = res.values
604
- return rows unless ftypes.any? { |_, x|
605
- x == BYTEA_COLUMN_TYPE_OID || x == MONEY_COLUMN_TYPE_OID
606
- }
403
+ protected
607
404
 
608
- typehash = ftypes.group_by { |_, type| type }
609
- binaries = typehash[BYTEA_COLUMN_TYPE_OID] || []
610
- monies = typehash[MONEY_COLUMN_TYPE_OID] || []
405
+ # See http://www.postgresql.org/docs/current/static/errcodes-appendix.html
406
+ VALUE_LIMIT_VIOLATION = "22001"
407
+ FOREIGN_KEY_VIOLATION = "23503"
408
+ UNIQUE_VIOLATION = "23505"
611
409
 
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])
616
- end
410
+ def translate_exception(exception, message)
411
+ return exception unless exception.respond_to?(:result)
617
412
 
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!(/,/, '.')
633
- end
413
+ case exception.result.try(:error_field, PGresult::PG_DIAG_SQLSTATE)
414
+ when UNIQUE_VIOLATION
415
+ RecordNotUnique.new(message)
416
+ when FOREIGN_KEY_VIOLATION
417
+ InvalidForeignKey.new(message)
418
+ when VALUE_LIMIT_VIOLATION
419
+ ValueTooLong.new(message)
420
+ else
421
+ super
634
422
  end
635
423
  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
424
 
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)
425
+ private
662
426
 
663
- ret = ActiveRecord::Result.new(result.fields, result_as_array(result))
664
- result.clear
665
- return ret
666
- end
667
- end
427
+ def get_oid_type(oid, fmod, column_name, sql_type = '') # :nodoc:
428
+ if !type_map.key?(oid)
429
+ load_additional_types(type_map, [oid])
430
+ end
668
431
 
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
432
+ type_map.fetch(oid, fmod, sql_type) {
433
+ warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
434
+ Type::Value.new.tap do |cast_type|
435
+ type_map.register_type(oid, cast_type)
436
+ end
437
+ }
438
+ end
439
+
440
+ def initialize_type_map(m) # :nodoc:
441
+ register_class_with_limit m, 'int2', Type::Integer
442
+ register_class_with_limit m, 'int4', Type::Integer
443
+ register_class_with_limit m, 'int8', Type::Integer
444
+ m.alias_type 'oid', 'int2'
445
+ m.register_type 'float4', Type::Float.new
446
+ m.alias_type 'float8', 'float4'
447
+ m.register_type 'text', Type::Text.new
448
+ register_class_with_limit m, 'varchar', Type::String
449
+ m.alias_type 'char', 'varchar'
450
+ m.alias_type 'name', 'varchar'
451
+ m.alias_type 'bpchar', 'varchar'
452
+ m.register_type 'bool', Type::Boolean.new
453
+ register_class_with_limit m, 'bit', OID::Bit
454
+ register_class_with_limit m, 'varbit', OID::BitVarying
455
+ m.alias_type 'timestamptz', 'timestamp'
456
+ m.register_type 'date', Type::Date.new
457
+
458
+ m.register_type 'money', OID::Money.new
459
+ m.register_type 'bytea', OID::Bytea.new
460
+ m.register_type 'point', OID::Point.new
461
+ m.register_type 'hstore', OID::Hstore.new
462
+ m.register_type 'json', OID::Json.new
463
+ m.register_type 'jsonb', OID::Jsonb.new
464
+ m.register_type 'cidr', OID::Cidr.new
465
+ m.register_type 'inet', OID::Inet.new
466
+ m.register_type 'uuid', OID::Uuid.new
467
+ m.register_type 'xml', OID::Xml.new
468
+ m.register_type 'tsvector', OID::SpecializedString.new(:tsvector)
469
+ m.register_type 'macaddr', OID::SpecializedString.new(:macaddr)
470
+ m.register_type 'citext', OID::SpecializedString.new(:citext)
471
+ m.register_type 'ltree', OID::SpecializedString.new(:ltree)
472
+ m.register_type 'line', OID::SpecializedString.new(:line)
473
+ m.register_type 'lseg', OID::SpecializedString.new(:lseg)
474
+ m.register_type 'box', OID::SpecializedString.new(:box)
475
+ m.register_type 'path', OID::SpecializedString.new(:path)
476
+ m.register_type 'polygon', OID::SpecializedString.new(:polygon)
477
+ m.register_type 'circle', OID::SpecializedString.new(:circle)
478
+
479
+ # FIXME: why are we keeping these types as strings?
480
+ m.alias_type 'interval', 'varchar'
481
+
482
+ register_class_with_precision m, 'time', Type::Time
483
+ register_class_with_precision m, 'timestamp', OID::DateTime
484
+
485
+ m.register_type 'numeric' do |_, fmod, sql_type|
486
+ precision = extract_precision(sql_type)
487
+ scale = extract_scale(sql_type)
488
+
489
+ # The type for the numeric depends on the width of the field,
490
+ # so we'll do something special here.
491
+ #
492
+ # When dealing with decimal columns:
493
+ #
494
+ # places after decimal = fmod - 4 & 0xffff
495
+ # places before decimal = (fmod - 4) >> 16 & 0xffff
496
+ if fmod && (fmod - 4 & 0xffff).zero?
497
+ # FIXME: Remove this class, and the second argument to
498
+ # lookups on PG
499
+ Type::DecimalWithoutScale.new(precision: precision)
500
+ else
501
+ OID::Decimal.new(precision: precision, scale: scale)
502
+ end
503
+ end
679
504
 
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
505
+ load_additional_types(m)
685
506
  end
686
507
 
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}"
508
+ def extract_limit(sql_type) # :nodoc:
509
+ case sql_type
510
+ when /^bigint/i, /^int8/i
511
+ 8
512
+ when /^smallint/i
513
+ 2
759
514
  else
760
- ""
515
+ super
761
516
  end
762
517
  end
763
518
 
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
887
- 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
-
924
- pk ||= default_pk
925
- sequence ||= default_sequence
926
- end
927
-
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
519
+ # Extracts the value from a PostgreSQL column default definition.
520
+ def extract_value_from_default(default) # :nodoc:
521
+ case default
522
+ # Quoted types
523
+ when /\A[\(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
524
+ # The default 'now'::date is CURRENT_DATE
525
+ if $1 == "now".freeze && $2 == "date".freeze
526
+ nil
527
+ else
528
+ $1.gsub("''".freeze, "'".freeze)
529
+ end
530
+ # Boolean types
531
+ when 'true'.freeze, 'false'.freeze
532
+ default
533
+ # Numeric types
534
+ when /\A\(?(-?\d+(\.\d*)?)\)?(::bigint)?\z/
535
+ $1
536
+ # Object identifier types
537
+ when /\A-?\d+\z/
538
+ $1
539
+ else
540
+ # Anything else is blank, some user type, or some function
541
+ # and we can't know the value of that, so return nil.
542
+ nil
543
+ end
979
544
  end
980
545
 
981
- [result.first, result.last]
982
- rescue
983
- nil
984
- end
985
-
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)}"
546
+ def extract_default_function(default_value, default) # :nodoc:
547
+ default if has_default_function?(default_value, default)
1012
548
  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
549
 
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")
550
+ def has_default_function?(default_value, default) # :nodoc:
551
+ !default_value && (%r{\w+\(.*\)|\(.*\)::\w+} === default)
1046
552
  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
553
 
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
554
+ def load_additional_types(type_map, oids = nil) # :nodoc:
555
+ initializer = OID::TypeMapInitializer.new(type_map)
1067
556
 
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}.")
557
+ if supports_ranges?
558
+ query = <<-SQL
559
+ SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
560
+ FROM pg_type as t
561
+ LEFT JOIN pg_range as r ON oid = rngtypid
562
+ SQL
563
+ else
564
+ query = <<-SQL
565
+ SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype
566
+ FROM pg_type as t
567
+ SQL
1077
568
  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.")
569
+
570
+ if oids
571
+ query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
572
+ else
573
+ query += initializer.query_conditions_for_initial_load(type_map)
1084
574
  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.")
575
+
576
+ execute_and_clear(query, 'SCHEMA', []) do |records|
577
+ initializer.run(records)
1093
578
  end
1094
- else
1095
- super
1096
579
  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
580
 
1117
- module Utils
1118
- extend self
581
+ FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
1119
582
 
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]
583
+ def execute_and_clear(sql, name, binds, prepare: false)
584
+ if without_prepared_statement?(binds)
585
+ result = exec_no_cache(sql, name, [])
586
+ elsif !prepare
587
+ result = exec_no_cache(sql, name, binds)
588
+ else
589
+ result = exec_cache(sql, name, binds)
590
+ end
591
+ ret = yield result
592
+ result.clear
593
+ ret
1133
594
  end
1134
- end
1135
595
 
1136
- protected
1137
- # Returns the version of the connected PostgreSQL server.
1138
- def postgresql_version
1139
- @connection.server_version
596
+ def exec_no_cache(sql, name, binds)
597
+ type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) }
598
+ log(sql, name, binds) { @connection.async_exec(sql, type_casted_binds) }
1140
599
  end
1141
600
 
1142
- # See http://www.postgresql.org/docs/9.1/static/errcodes-appendix.html
1143
- FOREIGN_KEY_VIOLATION = "23503"
1144
- UNIQUE_VIOLATION = "23505"
601
+ def exec_cache(sql, name, binds)
602
+ stmt_key = prepare_statement(sql)
603
+ type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) }
1145
604
 
1146
- def translate_exception(exception, message)
1147
- return exception unless exception.respond_to?(:result)
605
+ log(sql, name, binds, stmt_key) do
606
+ @connection.exec_prepared(stmt_key, type_casted_binds)
607
+ end
608
+ rescue ActiveRecord::StatementInvalid => e
609
+ raise unless is_cached_plan_failure?(e)
1148
610
 
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)
611
+ # Nothing we can do if we are in a transaction because all commands
612
+ # will raise InFailedSQLTransaction
613
+ if in_transaction?
614
+ raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)
1154
615
  else
1155
- super
616
+ # outside of transactions we can simply flush this query and retry
617
+ @statements.delete sql_key(sql)
618
+ retry
1156
619
  end
1157
620
  end
1158
621
 
1159
- private
1160
- FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
1161
-
1162
- def exec_no_cache(sql, binds)
1163
- @connection.async_exec(sql, [])
622
+ # Annoyingly, the code for prepared statements whose return value may
623
+ # have changed is FEATURE_NOT_SUPPORTED.
624
+ #
625
+ # This covers various different error types so we need to do additional
626
+ # work to classify the exception definitively as a
627
+ # ActiveRecord::PreparedStatementCacheExpired
628
+ #
629
+ # Check here for more details:
630
+ # http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
631
+ CACHED_PLAN_HEURISTIC = 'cached plan must not change result type'.freeze
632
+ def is_cached_plan_failure?(e)
633
+ pgerror = e.cause
634
+ code = pgerror.result.result_error_field(PGresult::PG_DIAG_SQLSTATE)
635
+ code == FEATURE_NOT_SUPPORTED && pgerror.message.include?(CACHED_PLAN_HEURISTIC)
636
+ rescue
637
+ false
1164
638
  end
1165
639
 
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
640
+ def in_transaction?
641
+ open_transactions > 0
1194
642
  end
1195
643
 
1196
644
  # Returns the statement identifier for the client side cache
@@ -1205,28 +653,29 @@ module ActiveRecord
1205
653
  sql_key = sql_key(sql)
1206
654
  unless @statements.key? sql_key
1207
655
  nextkey = @statements.next_key
1208
- @connection.prepare nextkey, sql
656
+ begin
657
+ @connection.prepare nextkey, sql
658
+ rescue => e
659
+ raise translate_exception_class(e, sql)
660
+ end
661
+ # Clear the queue
662
+ @connection.get_last_result
1209
663
  @statements[sql_key] = nextkey
1210
664
  end
1211
665
  @statements[sql_key]
1212
666
  end
1213
667
 
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
668
  # Connects to a PostgreSQL server and sets up the adapter depending on the
1220
669
  # connected server's characteristics.
1221
670
  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
-
671
+ @connection = PGconn.connect(@connection_parameters)
1229
672
  configure_connection
673
+ rescue ::PG::Error => error
674
+ if error.message.include?("does not exist")
675
+ raise ActiveRecord::NoDatabaseError
676
+ else
677
+ raise
678
+ end
1230
679
  end
1231
680
 
1232
681
  # Configures the encoding, verbosity, schema search path, and time zone of the connection.
@@ -1235,45 +684,43 @@ module ActiveRecord
1235
684
  if @config[:encoding]
1236
685
  @connection.set_client_encoding(@config[:encoding])
1237
686
  end
1238
- self.client_min_messages = @config[:min_messages] if @config[:min_messages]
687
+ self.client_min_messages = @config[:min_messages] || 'warning'
1239
688
  self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
1240
689
 
1241
- # Use standard-conforming strings if available so we don't have to do the E'...' dance.
690
+ # Use standard-conforming strings so we don't have to do the E'...' dance.
1242
691
  set_standard_conforming_strings
1243
692
 
1244
693
  # If using Active Record's time zone support configure the connection to return
1245
694
  # TIMESTAMP WITH ZONE types in UTC.
695
+ # (SET TIME ZONE does not use an equals sign like other SET variables)
1246
696
  if ActiveRecord::Base.default_timezone == :utc
1247
697
  execute("SET time zone 'UTC'", 'SCHEMA')
1248
698
  elsif @local_tz
1249
699
  execute("SET time zone '#{@local_tz}'", 'SCHEMA')
1250
700
  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
701
 
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
702
+ # SET statements from :variables config hash
703
+ # http://www.postgresql.org/docs/current/static/sql-set.html
704
+ variables = @config[:variables] || {}
705
+ variables.map do |k, v|
706
+ if v == ':default' || v == :default
707
+ # Sets the value to the global or compile default
708
+ execute("SET SESSION #{k} TO DEFAULT", 'SCHEMA')
709
+ elsif !v.nil?
710
+ execute("SET SESSION #{k} TO #{quote(v)}", 'SCHEMA')
711
+ end
712
+ end
1263
713
  end
1264
714
 
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
715
+ # Returns the current ID of a table's sequence.
716
+ def last_insert_id_result(sequence_name) # :nodoc:
717
+ exec_query("SELECT currval('#{sequence_name}')", 'SQL')
1271
718
  end
1272
719
 
1273
720
  # Returns the list of a table's column names, data types, and default values.
1274
721
  #
1275
722
  # The underlying query is roughly:
1276
- # SELECT column.name, column.type, default.value
723
+ # SELECT column.name, column.type, default.value, column.comment
1277
724
  # FROM column LEFT JOIN default
1278
725
  # ON column.table_id = default.table_id
1279
726
  # AND column.num = default.column_num
@@ -1288,36 +735,108 @@ module ActiveRecord
1288
735
  # Query implementation notes:
1289
736
  # - format_type includes the column size constraint, e.g. varchar(50)
1290
737
  # - ::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
738
+ def column_definitions(table_name) # :nodoc:
739
+ query(<<-end_sql, 'SCHEMA')
740
+ SELECT a.attname, format_type(a.atttypid, a.atttypmod),
741
+ pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
742
+ (SELECT c.collname FROM pg_collation c, pg_type t
743
+ WHERE c.oid = a.attcollation AND t.oid = a.atttypid AND a.attcollation <> t.typcollation),
744
+ col_description(a.attrelid, a.attnum) AS comment
745
+ FROM pg_attribute a LEFT JOIN pg_attrdef d
746
+ ON a.attrelid = d.adrelid AND a.attnum = d.adnum
747
+ WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
748
+ AND a.attnum > 0 AND NOT a.attisdropped
749
+ ORDER BY a.attnum
1300
750
  end_sql
1301
751
  end
1302
752
 
1303
- def extract_pg_identifier_from_name(name)
1304
- match_data = name.start_with?('"') ? name.match(/\"([^\"]+)\"/) : name.match(/([^\.]+)/)
753
+ def extract_table_ref_from_insert_sql(sql) # :nodoc:
754
+ sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
755
+ $1.strip if $1
756
+ end
1305
757
 
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)]
758
+ def create_table_definition(*args) # :nodoc:
759
+ PostgreSQL::TableDefinition.new(*args)
760
+ end
761
+
762
+ def can_perform_case_insensitive_comparison_for?(column)
763
+ @case_insensitive_cache ||= {}
764
+ @case_insensitive_cache[column.sql_type] ||= begin
765
+ sql = <<-end_sql
766
+ SELECT exists(
767
+ SELECT * FROM pg_proc
768
+ INNER JOIN pg_cast
769
+ ON casttarget::text::oidvector = proargtypes
770
+ WHERE proname = 'lower'
771
+ AND castsource = '#{column.sql_type}'::regtype::oid
772
+ )
773
+ end_sql
774
+ execute_and_clear(sql, "SCHEMA", []) do |result|
775
+ result.getvalue(0, 0)
776
+ end
1310
777
  end
1311
778
  end
1312
779
 
1313
- def extract_table_ref_from_insert_sql(sql)
1314
- sql[/into\s+([^\(]*).*values\s*\(/i]
1315
- $1.strip if $1
1316
- end
780
+ def add_pg_encoders
781
+ map = PG::TypeMapByClass.new
782
+ map[Integer] = PG::TextEncoder::Integer.new
783
+ map[TrueClass] = PG::TextEncoder::Boolean.new
784
+ map[FalseClass] = PG::TextEncoder::Boolean.new
785
+ map[Float] = PG::TextEncoder::Float.new
786
+ @connection.type_map_for_queries = map
787
+ end
788
+
789
+ def add_pg_decoders
790
+ coders_by_name = {
791
+ 'int2' => PG::TextDecoder::Integer,
792
+ 'int4' => PG::TextDecoder::Integer,
793
+ 'int8' => PG::TextDecoder::Integer,
794
+ 'oid' => PG::TextDecoder::Integer,
795
+ 'float4' => PG::TextDecoder::Float,
796
+ 'float8' => PG::TextDecoder::Float,
797
+ 'bool' => PG::TextDecoder::Boolean,
798
+ }
799
+ known_coder_types = coders_by_name.keys.map { |n| quote(n) }
800
+ query = <<-SQL % known_coder_types.join(", ")
801
+ SELECT t.oid, t.typname
802
+ FROM pg_type as t
803
+ WHERE t.typname IN (%s)
804
+ SQL
805
+ coders = execute_and_clear(query, "SCHEMA", []) do |result|
806
+ result
807
+ .map { |row| construct_coder(row, coders_by_name[row['typname']]) }
808
+ .compact
809
+ end
1317
810
 
1318
- def table_definition
1319
- TableDefinition.new(self)
1320
- end
811
+ map = PG::TypeMapByOid.new
812
+ coders.each { |coder| map.add_coder(coder) }
813
+ @connection.type_map_for_results = map
814
+ end
815
+
816
+ def construct_coder(row, coder_class)
817
+ return unless coder_class
818
+ coder_class.new(oid: row['oid'].to_i, name: row['typname'])
819
+ end
820
+
821
+ ActiveRecord::Type.add_modifier({ array: true }, OID::Array, adapter: :postgresql)
822
+ ActiveRecord::Type.add_modifier({ range: true }, OID::Range, adapter: :postgresql)
823
+ ActiveRecord::Type.register(:bit, OID::Bit, adapter: :postgresql)
824
+ ActiveRecord::Type.register(:bit_varying, OID::BitVarying, adapter: :postgresql)
825
+ ActiveRecord::Type.register(:binary, OID::Bytea, adapter: :postgresql)
826
+ ActiveRecord::Type.register(:cidr, OID::Cidr, adapter: :postgresql)
827
+ ActiveRecord::Type.register(:datetime, OID::DateTime, adapter: :postgresql)
828
+ ActiveRecord::Type.register(:decimal, OID::Decimal, adapter: :postgresql)
829
+ ActiveRecord::Type.register(:enum, OID::Enum, adapter: :postgresql)
830
+ ActiveRecord::Type.register(:hstore, OID::Hstore, adapter: :postgresql)
831
+ ActiveRecord::Type.register(:inet, OID::Inet, adapter: :postgresql)
832
+ ActiveRecord::Type.register(:json, OID::Json, adapter: :postgresql)
833
+ ActiveRecord::Type.register(:jsonb, OID::Jsonb, adapter: :postgresql)
834
+ ActiveRecord::Type.register(:money, OID::Money, adapter: :postgresql)
835
+ ActiveRecord::Type.register(:point, OID::Rails51Point, adapter: :postgresql)
836
+ ActiveRecord::Type.register(:legacy_point, OID::Point, adapter: :postgresql)
837
+ ActiveRecord::Type.register(:uuid, OID::Uuid, adapter: :postgresql)
838
+ ActiveRecord::Type.register(:vector, OID::Vector, adapter: :postgresql)
839
+ ActiveRecord::Type.register(:xml, OID::Xml, adapter: :postgresql)
1321
840
  end
1322
841
  end
1323
842
  end