activerecord 3.1.10 → 4.2.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

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