activerecord 3.2.22.4 → 4.0.13

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