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