activerecord 1.15.6 → 2.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 (185) hide show
  1. data/CHANGELOG +2454 -34
  2. data/README +1 -1
  3. data/RUNNING_UNIT_TESTS +3 -34
  4. data/Rakefile +98 -77
  5. data/install.rb +1 -1
  6. data/lib/active_record.rb +13 -22
  7. data/lib/active_record/aggregations.rb +38 -49
  8. data/lib/active_record/associations.rb +452 -333
  9. data/lib/active_record/associations/association_collection.rb +66 -20
  10. data/lib/active_record/associations/association_proxy.rb +9 -8
  11. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +46 -51
  12. data/lib/active_record/associations/has_many_association.rb +21 -57
  13. data/lib/active_record/associations/has_many_through_association.rb +38 -18
  14. data/lib/active_record/associations/has_one_association.rb +30 -14
  15. data/lib/active_record/attribute_methods.rb +253 -0
  16. data/lib/active_record/base.rb +719 -494
  17. data/lib/active_record/calculations.rb +62 -63
  18. data/lib/active_record/callbacks.rb +57 -83
  19. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +38 -9
  20. data/lib/active_record/connection_adapters/abstract/database_statements.rb +56 -15
  21. data/lib/active_record/connection_adapters/abstract/query_cache.rb +87 -0
  22. data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -12
  23. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +191 -62
  24. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +37 -34
  25. data/lib/active_record/connection_adapters/abstract_adapter.rb +28 -17
  26. data/lib/active_record/connection_adapters/mysql_adapter.rb +119 -37
  27. data/lib/active_record/connection_adapters/postgresql_adapter.rb +473 -210
  28. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
  29. data/lib/active_record/connection_adapters/sqlite_adapter.rb +91 -107
  30. data/lib/active_record/fixtures.rb +503 -113
  31. data/lib/active_record/locking/optimistic.rb +72 -34
  32. data/lib/active_record/migration.rb +80 -57
  33. data/lib/active_record/observer.rb +13 -10
  34. data/lib/active_record/query_cache.rb +16 -57
  35. data/lib/active_record/reflection.rb +35 -38
  36. data/lib/active_record/schema.rb +5 -5
  37. data/lib/active_record/schema_dumper.rb +35 -13
  38. data/lib/active_record/serialization.rb +98 -0
  39. data/lib/active_record/serializers/json_serializer.rb +71 -0
  40. data/lib/active_record/{xml_serialization.rb → serializers/xml_serializer.rb} +90 -83
  41. data/lib/active_record/timestamp.rb +20 -21
  42. data/lib/active_record/transactions.rb +39 -43
  43. data/lib/active_record/validations.rb +256 -107
  44. data/lib/active_record/version.rb +3 -3
  45. data/lib/activerecord.rb +1 -0
  46. data/test/aaa_create_tables_test.rb +15 -2
  47. data/test/abstract_unit.rb +24 -17
  48. data/test/active_schema_test_mysql.rb +20 -8
  49. data/test/adapter_test.rb +23 -5
  50. data/test/adapter_test_sqlserver.rb +15 -1
  51. data/test/aggregations_test.rb +16 -1
  52. data/test/all.sh +2 -2
  53. data/test/associations/ar_joins_test.rb +0 -0
  54. data/test/associations/callbacks_test.rb +51 -30
  55. data/test/associations/cascaded_eager_loading_test.rb +1 -29
  56. data/test/associations/eager_singularization_test.rb +145 -0
  57. data/test/associations/eager_test.rb +42 -6
  58. data/test/associations/extension_test.rb +6 -1
  59. data/test/associations/inner_join_association_test.rb +88 -0
  60. data/test/associations/join_model_test.rb +47 -16
  61. data/test/associations_test.rb +449 -226
  62. data/test/attribute_methods_test.rb +97 -0
  63. data/test/base_test.rb +251 -105
  64. data/test/binary_test.rb +22 -27
  65. data/test/calculations_test.rb +37 -5
  66. data/test/callbacks_test.rb +23 -0
  67. data/test/connection_test_firebird.rb +2 -2
  68. data/test/connection_test_mysql.rb +30 -0
  69. data/test/connections/native_mysql/connection.rb +3 -0
  70. data/test/connections/native_sqlite/connection.rb +5 -14
  71. data/test/connections/native_sqlite3/connection.rb +5 -14
  72. data/test/connections/native_sqlite3/in_memory_connection.rb +1 -1
  73. data/test/{copy_table_sqlite.rb → copy_table_test_sqlite.rb} +8 -3
  74. data/test/datatype_test_postgresql.rb +178 -27
  75. data/test/{empty_date_time_test.rb → date_time_test.rb} +13 -1
  76. data/test/defaults_test.rb +8 -1
  77. data/test/deprecated_finder_test.rb +7 -128
  78. data/test/finder_test.rb +192 -54
  79. data/test/fixtures/all/developers.yml +0 -0
  80. data/test/fixtures/all/people.csv +0 -0
  81. data/test/fixtures/all/tasks.yml +0 -0
  82. data/test/fixtures/author.rb +12 -5
  83. data/test/fixtures/binaries.yml +130 -435
  84. data/test/fixtures/category.rb +6 -0
  85. data/test/fixtures/company.rb +8 -1
  86. data/test/fixtures/computer.rb +1 -0
  87. data/test/fixtures/contact.rb +16 -0
  88. data/test/fixtures/customer.rb +2 -2
  89. data/test/fixtures/db_definitions/db2.drop.sql +1 -0
  90. data/test/fixtures/db_definitions/db2.sql +4 -0
  91. data/test/fixtures/db_definitions/firebird.drop.sql +3 -1
  92. data/test/fixtures/db_definitions/firebird.sql +6 -0
  93. data/test/fixtures/db_definitions/frontbase.drop.sql +1 -0
  94. data/test/fixtures/db_definitions/frontbase.sql +5 -0
  95. data/test/fixtures/db_definitions/openbase.sql +41 -25
  96. data/test/fixtures/db_definitions/oracle.drop.sql +2 -0
  97. data/test/fixtures/db_definitions/oracle.sql +5 -0
  98. data/test/fixtures/db_definitions/postgresql.drop.sql +7 -0
  99. data/test/fixtures/db_definitions/postgresql.sql +87 -58
  100. data/test/fixtures/db_definitions/postgresql2.sql +1 -2
  101. data/test/fixtures/db_definitions/schema.rb +280 -0
  102. data/test/fixtures/db_definitions/schema2.rb +11 -0
  103. data/test/fixtures/db_definitions/sqlite.drop.sql +1 -0
  104. data/test/fixtures/db_definitions/sqlite.sql +4 -0
  105. data/test/fixtures/db_definitions/sybase.drop.sql +1 -0
  106. data/test/fixtures/db_definitions/sybase.sql +4 -0
  107. data/test/fixtures/developer.rb +10 -0
  108. data/test/fixtures/example.log +1 -0
  109. data/test/fixtures/flowers.jpg +0 -0
  110. data/test/fixtures/item.rb +7 -0
  111. data/test/fixtures/items.yml +4 -0
  112. data/test/fixtures/joke.rb +0 -3
  113. data/test/fixtures/matey.rb +4 -0
  114. data/test/fixtures/mateys.yml +4 -0
  115. data/test/fixtures/minimalistic.rb +2 -0
  116. data/test/fixtures/minimalistics.yml +2 -0
  117. data/test/fixtures/mixins.yml +2 -100
  118. data/test/fixtures/parrot.rb +13 -0
  119. data/test/fixtures/parrots.yml +27 -0
  120. data/test/fixtures/parrots_pirates.yml +7 -0
  121. data/test/fixtures/pirate.rb +5 -0
  122. data/test/fixtures/pirates.yml +9 -0
  123. data/test/fixtures/post.rb +1 -0
  124. data/test/fixtures/project.rb +3 -2
  125. data/test/fixtures/reserved_words/distinct.yml +5 -0
  126. data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
  127. data/test/fixtures/reserved_words/group.yml +14 -0
  128. data/test/fixtures/reserved_words/select.yml +8 -0
  129. data/test/fixtures/reserved_words/values.yml +7 -0
  130. data/test/fixtures/ship.rb +3 -0
  131. data/test/fixtures/ships.yml +5 -0
  132. data/test/fixtures/tagging.rb +4 -0
  133. data/test/fixtures/taggings.yml +8 -1
  134. data/test/fixtures/topic.rb +13 -1
  135. data/test/fixtures/treasure.rb +4 -0
  136. data/test/fixtures/treasures.yml +10 -0
  137. data/test/fixtures_test.rb +205 -24
  138. data/test/inheritance_test.rb +7 -1
  139. data/test/json_serialization_test.rb +180 -0
  140. data/test/lifecycle_test.rb +1 -1
  141. data/test/locking_test.rb +85 -2
  142. data/test/migration_test.rb +206 -40
  143. data/test/mixin_test.rb +13 -515
  144. data/test/pk_test.rb +3 -6
  145. data/test/query_cache_test.rb +104 -0
  146. data/test/reflection_test.rb +16 -0
  147. data/test/reserved_word_test_mysql.rb +177 -0
  148. data/test/schema_dumper_test.rb +38 -3
  149. data/test/serialization_test.rb +47 -0
  150. data/test/transactions_test.rb +74 -23
  151. data/test/unconnected_test.rb +1 -1
  152. data/test/validations_test.rb +322 -32
  153. data/test/xml_serialization_test.rb +121 -44
  154. metadata +48 -41
  155. data/examples/associations.rb +0 -87
  156. data/examples/shared_setup.rb +0 -15
  157. data/examples/validation.rb +0 -85
  158. data/lib/active_record/acts/list.rb +0 -256
  159. data/lib/active_record/acts/nested_set.rb +0 -211
  160. data/lib/active_record/acts/tree.rb +0 -96
  161. data/lib/active_record/connection_adapters/db2_adapter.rb +0 -228
  162. data/lib/active_record/connection_adapters/firebird_adapter.rb +0 -728
  163. data/lib/active_record/connection_adapters/frontbase_adapter.rb +0 -861
  164. data/lib/active_record/connection_adapters/openbase_adapter.rb +0 -350
  165. data/lib/active_record/connection_adapters/oracle_adapter.rb +0 -690
  166. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +0 -591
  167. data/lib/active_record/connection_adapters/sybase_adapter.rb +0 -662
  168. data/lib/active_record/deprecated_associations.rb +0 -104
  169. data/lib/active_record/deprecated_finders.rb +0 -44
  170. data/lib/active_record/vendor/simple.rb +0 -693
  171. data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
  172. data/lib/active_record/wrappings.rb +0 -58
  173. data/test/connections/native_sqlserver/connection.rb +0 -23
  174. data/test/connections/native_sqlserver_odbc/connection.rb +0 -25
  175. data/test/deprecated_associations_test.rb +0 -396
  176. data/test/fixtures/db_definitions/mysql.drop.sql +0 -32
  177. data/test/fixtures/db_definitions/mysql.sql +0 -234
  178. data/test/fixtures/db_definitions/mysql2.drop.sql +0 -2
  179. data/test/fixtures/db_definitions/mysql2.sql +0 -5
  180. data/test/fixtures/db_definitions/sqlserver.drop.sql +0 -34
  181. data/test/fixtures/db_definitions/sqlserver.sql +0 -243
  182. data/test/fixtures/db_definitions/sqlserver2.drop.sql +0 -2
  183. data/test/fixtures/db_definitions/sqlserver2.sql +0 -5
  184. data/test/fixtures/mixin.rb +0 -63
  185. data/test/mixin_nested_set_test.rb +0 -196
@@ -12,29 +12,205 @@ module ActiveRecord
12
12
  username = config[:username].to_s
13
13
  password = config[:password].to_s
14
14
 
15
- min_messages = config[:min_messages]
16
-
17
15
  if config.has_key?(:database)
18
16
  database = config[:database]
19
17
  else
20
18
  raise ArgumentError, "No database specified. Missing argument: database."
21
19
  end
22
20
 
23
- pga = ConnectionAdapters::PostgreSQLAdapter.new(
24
- PGconn.connect(host, port, "", "", database, username, password), logger, config
25
- )
21
+ # The postgres drivers don't allow the creation of an unconnected PGconn object,
22
+ # so just pass a nil connection object for the time being.
23
+ ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, [host, port, nil, nil, database, username, password], config)
24
+ end
25
+ end
26
26
 
27
- PGconn.translate_results = false if PGconn.respond_to? :translate_results=
27
+ module ConnectionAdapters
28
+ # PostgreSQL-specific extensions to column definitions in a table.
29
+ class PostgreSQLColumn < Column #:nodoc:
30
+ # Instantiates a new PostgreSQL column definition in a table.
31
+ def initialize(name, default, sql_type = nil, null = true)
32
+ super(name, self.class.extract_value_from_default(default), sql_type, null)
33
+ end
28
34
 
29
- pga.schema_search_path = config[:schema_search_path] || config[:schema_order]
35
+ private
36
+ # Extracts the scale from PostgreSQL-specific data types.
37
+ def extract_scale(sql_type)
38
+ # Money type has a fixed scale of 2.
39
+ sql_type =~ /^money/ ? 2 : super
40
+ end
30
41
 
31
- pga
42
+ # Extracts the precision from PostgreSQL-specific data types.
43
+ def extract_precision(sql_type)
44
+ # Actual code is defined dynamically in PostgreSQLAdapter.connect
45
+ # depending on the server specifics
46
+ super
47
+ end
48
+
49
+ # Escapes binary strings for bytea input to the database.
50
+ def self.string_to_binary(value)
51
+ if PGconn.respond_to?(:escape_bytea)
52
+ self.class.module_eval do
53
+ define_method(:string_to_binary) do |value|
54
+ PGconn.escape_bytea(value) if value
55
+ end
56
+ end
57
+ else
58
+ self.class.module_eval do
59
+ define_method(:string_to_binary) do |value|
60
+ if value
61
+ result = ''
62
+ value.each_byte { |c| result << sprintf('\\\\%03o', c) }
63
+ result
64
+ end
65
+ end
66
+ end
67
+ end
68
+ self.class.string_to_binary(value)
69
+ end
70
+
71
+ # Unescapes bytea output from a database to the binary string it represents.
72
+ def self.binary_to_string(value)
73
+ # In each case, check if the value actually is escaped PostgreSQL bytea output
74
+ # or an unescaped Active Record attribute that was just written.
75
+ if PGconn.respond_to?(:unescape_bytea)
76
+ self.class.module_eval do
77
+ define_method(:binary_to_string) do |value|
78
+ if value =~ /\\\d{3}/
79
+ PGconn.unescape_bytea(value)
80
+ else
81
+ value
82
+ end
83
+ end
84
+ end
85
+ else
86
+ self.class.module_eval do
87
+ define_method(:binary_to_string) do |value|
88
+ if value =~ /\\\d{3}/
89
+ result = ''
90
+ i, max = 0, value.size
91
+ while i < max
92
+ char = value[i]
93
+ if char == ?\\
94
+ if value[i+1] == ?\\
95
+ char = ?\\
96
+ i += 1
97
+ else
98
+ char = value[i+1..i+3].oct
99
+ i += 3
100
+ end
101
+ end
102
+ result << char
103
+ i += 1
104
+ end
105
+ result
106
+ else
107
+ value
108
+ end
109
+ end
110
+ end
111
+ end
112
+ self.class.binary_to_string(value)
113
+ end
114
+
115
+ # Maps PostgreSQL-specific data types to logical Rails types.
116
+ def simplified_type(field_type)
117
+ case field_type
118
+ # Numeric and monetary types
119
+ when /^(?:real|double precision)$/
120
+ :float
121
+ # Monetary types
122
+ when /^money$/
123
+ :decimal
124
+ # Character types
125
+ when /^(?:character varying|bpchar)(?:\(\d+\))?$/
126
+ :string
127
+ # Binary data types
128
+ when /^bytea$/
129
+ :binary
130
+ # Date/time types
131
+ when /^timestamp with(?:out)? time zone$/
132
+ :datetime
133
+ when /^interval$/
134
+ :string
135
+ # Geometric types
136
+ when /^(?:point|line|lseg|box|"?path"?|polygon|circle)$/
137
+ :string
138
+ # Network address types
139
+ when /^(?:cidr|inet|macaddr)$/
140
+ :string
141
+ # Bit strings
142
+ when /^bit(?: varying)?(?:\(\d+\))?$/
143
+ :string
144
+ # XML type
145
+ when /^xml$/
146
+ :string
147
+ # Arrays
148
+ when /^\D+\[\]$/
149
+ :string
150
+ # Object identifier types
151
+ when /^oid$/
152
+ :integer
153
+ # Pass through all types that are not specific to PostgreSQL.
154
+ else
155
+ super
156
+ end
157
+ end
158
+
159
+ # Extracts the value from a PostgreSQL column default definition.
160
+ def self.extract_value_from_default(default)
161
+ case default
162
+ # Numeric types
163
+ when /\A-?\d+(\.\d*)?\z/
164
+ default
165
+ # Character types
166
+ when /\A'(.*)'::(?:character varying|bpchar|text)\z/m
167
+ $1
168
+ # Character types (8.1 formatting)
169
+ when /\AE'(.*)'::(?:character varying|bpchar|text)\z/m
170
+ $1.gsub(/\\(\d\d\d)/) { $1.oct.chr }
171
+ # Binary data types
172
+ when /\A'(.*)'::bytea\z/m
173
+ $1
174
+ # Date/time types
175
+ when /\A'(.+)'::(?:time(?:stamp)? with(?:out)? time zone|date)\z/
176
+ $1
177
+ when /\A'(.*)'::interval\z/
178
+ $1
179
+ # Boolean type
180
+ when 'true'
181
+ true
182
+ when 'false'
183
+ false
184
+ # Geometric types
185
+ when /\A'(.*)'::(?:point|line|lseg|box|"?path"?|polygon|circle)\z/
186
+ $1
187
+ # Network address types
188
+ when /\A'(.*)'::(?:cidr|inet|macaddr)\z/
189
+ $1
190
+ # Bit string types
191
+ when /\AB'(.*)'::"?bit(?: varying)?"?\z/
192
+ $1
193
+ # XML type
194
+ when /\A'(.*)'::xml\z/m
195
+ $1
196
+ # Arrays
197
+ when /\A'(.*)'::"?\D+"?\[\]\z/
198
+ $1
199
+ # Object identifier types
200
+ when /\A-?\d+\z/
201
+ $1
202
+ else
203
+ # Anything else is blank, some user type, or some function
204
+ # and we can't know the value of that, so return nil.
205
+ nil
206
+ end
207
+ end
32
208
  end
33
209
  end
34
210
 
35
211
  module ConnectionAdapters
36
- # The PostgreSQL adapter works both with the C-based (http://www.postgresql.jp/interfaces/ruby/) and the Ruby-base
37
- # (available both as gem and from http://rubyforge.org/frs/?group_id=234&release_id=1145) drivers.
212
+ # The PostgreSQL adapter works both with the native C (http://ruby.scripting.ca/postgres/) and the pure
213
+ # Ruby (available both as gem and from http://rubyforge.org/frs/?group_id=234&release_id=1944) drivers.
38
214
  #
39
215
  # Options:
40
216
  #
@@ -44,19 +220,21 @@ module ActiveRecord
44
220
  # * <tt>:password</tt> -- Defaults to nothing
45
221
  # * <tt>:database</tt> -- The name of the database. No default, must be provided.
46
222
  # * <tt>:schema_search_path</tt> -- An optional schema search path for the connection given as a string of comma-separated schema names. This is backward-compatible with the :schema_order option.
47
- # * <tt>:encoding</tt> -- An optional client encoding that is using in a SET client_encoding TO <encoding> call on connection.
48
- # * <tt>:min_messages</tt> -- An optional client min messages that is using in a SET client_min_messages TO <min_messages> call on connection.
223
+ # * <tt>:encoding</tt> -- An optional client encoding that is used in a SET client_encoding TO <encoding> call on the connection.
224
+ # * <tt>:min_messages</tt> -- An optional client min messages that is used in a SET client_min_messages TO <min_messages> call on the connection.
49
225
  # * <tt>:allow_concurrency</tt> -- If true, use async query methods so Ruby threads don't deadlock; otherwise, use blocking query methods.
50
226
  class PostgreSQLAdapter < AbstractAdapter
227
+ # Returns 'PostgreSQL' as adapter name for identification purposes.
51
228
  def adapter_name
52
229
  'PostgreSQL'
53
230
  end
54
231
 
55
- def initialize(connection, logger, config = {})
232
+ # Initializes and connects a PostgreSQL adapter.
233
+ def initialize(connection, logger, connection_parameters, config)
56
234
  super(connection, logger)
57
- @config = config
58
- @async = config[:allow_concurrency]
59
- configure_connection
235
+ @connection_parameters, @config = connection_parameters, config
236
+
237
+ connect
60
238
  end
61
239
 
62
240
  # Is this connection alive and ready for queries?
@@ -64,29 +242,32 @@ module ActiveRecord
64
242
  if @connection.respond_to?(:status)
65
243
  @connection.status == PGconn::CONNECTION_OK
66
244
  else
245
+ # We're asking the driver, not ActiveRecord, so use @connection.query instead of #query
67
246
  @connection.query 'SELECT 1'
68
247
  true
69
248
  end
70
- # postgres-pr raises a NoMethodError when querying if no conn is available
249
+ # postgres-pr raises a NoMethodError when querying if no connection is available.
71
250
  rescue PGError, NoMethodError
72
251
  false
73
252
  end
74
253
 
75
254
  # Close then reopen the connection.
76
255
  def reconnect!
77
- # TODO: postgres-pr doesn't have PGconn#reset.
78
256
  if @connection.respond_to?(:reset)
79
257
  @connection.reset
80
258
  configure_connection
259
+ else
260
+ disconnect!
261
+ connect
81
262
  end
82
263
  end
83
264
 
265
+ # Close the connection.
84
266
  def disconnect!
85
- # Both postgres and postgres-pr respond to :close
86
267
  @connection.close rescue nil
87
268
  end
88
269
 
89
- def native_database_types
270
+ def native_database_types #:nodoc:
90
271
  {
91
272
  :primary_key => "serial primary key",
92
273
  :string => { :name => "character varying", :limit => 255 },
@@ -103,41 +284,113 @@ module ActiveRecord
103
284
  }
104
285
  end
105
286
 
287
+ # Does PostgreSQL support migrations?
106
288
  def supports_migrations?
107
289
  true
108
290
  end
109
291
 
292
+ # Does PostgreSQL support standard conforming strings?
293
+ def supports_standard_conforming_strings?
294
+ # Temporarily set the client message level above error to prevent unintentional
295
+ # error messages in the logs when working on a PostgreSQL database server that
296
+ # does not support standard conforming strings.
297
+ client_min_messages_old = client_min_messages
298
+ self.client_min_messages = 'panic'
299
+
300
+ # postgres-pr does not raise an exception when client_min_messages is set higher
301
+ # than error and "SHOW standard_conforming_strings" fails, but returns an empty
302
+ # PGresult instead.
303
+ has_support = execute('SHOW standard_conforming_strings')[0][0] rescue false
304
+ self.client_min_messages = client_min_messages_old
305
+ has_support
306
+ end
307
+
308
+ # Returns the configured supported identifier length supported by PostgreSQL,
309
+ # or report the default of 63 on PostgreSQL 7.x.
110
310
  def table_alias_length
111
- 63
311
+ @table_alias_length ||= (postgresql_version >= 80000 ? query('SHOW max_identifier_length')[0][0].to_i : 63)
112
312
  end
113
313
 
114
314
  # QUOTING ==================================================
115
315
 
116
- def quote(value, column = nil)
316
+ # Quotes PostgreSQL-specific data types for SQL input.
317
+ def quote(value, column = nil) #:nodoc:
117
318
  if value.kind_of?(String) && column && column.type == :binary
118
- "'#{escape_bytea(value)}'"
319
+ "#{quoted_string_prefix}'#{column.class.string_to_binary(value)}'"
320
+ elsif value.kind_of?(String) && column && column.sql_type =~ /^xml$/
321
+ "xml '#{quote_string(value)}'"
322
+ elsif value.kind_of?(Numeric) && column && column.sql_type =~ /^money$/
323
+ # Not truly string input, so doesn't require (or allow) escape string syntax.
324
+ "'#{value.to_s}'"
325
+ elsif value.kind_of?(String) && column && column.sql_type =~ /^bit/
326
+ case value
327
+ when /^[01]*$/
328
+ "B'#{value}'" # Bit-string notation
329
+ when /^[0-9A-F]*$/i
330
+ "X'#{value}'" # Hexadecimal notation
331
+ end
119
332
  else
120
333
  super
121
334
  end
122
335
  end
123
336
 
124
- def quote_column_name(name)
337
+ # Quotes strings for use in SQL input in the postgres driver for better performance.
338
+ def quote_string(s) #:nodoc:
339
+ if PGconn.respond_to?(:escape)
340
+ self.class.instance_eval do
341
+ define_method(:quote_string) do |s|
342
+ PGconn.escape(s)
343
+ end
344
+ end
345
+ else
346
+ # There are some incorrectly compiled postgres drivers out there
347
+ # that don't define PGconn.escape.
348
+ self.class.instance_eval do
349
+ undef_method(:quote_string)
350
+ end
351
+ end
352
+ quote_string(s)
353
+ end
354
+
355
+ # Quotes column names for use in SQL queries.
356
+ def quote_column_name(name) #:nodoc:
125
357
  %("#{name}")
126
358
  end
127
359
 
128
- def quoted_date(value)
129
- value.strftime("%Y-%m-%d %H:%M:%S.#{sprintf("%06d", value.usec)}")
360
+ # Quote date/time values for use in SQL input. Includes microseconds
361
+ # if the value is a Time responding to usec.
362
+ def quoted_date(value) #:nodoc:
363
+ if value.acts_like?(:time) && value.respond_to?(:usec)
364
+ "#{super}.#{sprintf("%06d", value.usec)}"
365
+ else
366
+ super
367
+ end
130
368
  end
131
369
 
370
+ # REFERENTIAL INTEGRITY ====================================
371
+
372
+ def disable_referential_integrity(&block) #:nodoc:
373
+ execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
374
+ yield
375
+ ensure
376
+ execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";"))
377
+ end
132
378
 
133
379
  # DATABASE STATEMENTS ======================================
134
380
 
135
- def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
136
- execute(sql, name)
381
+ # Executes a SELECT query and returns an array of rows. Each row is an
382
+ # array of field values.
383
+ def select_rows(sql, name = nil)
384
+ select_raw(sql, name).last
385
+ end
386
+
387
+ # Executes an INSERT query and returns the new record's ID
388
+ def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
137
389
  table = sql.split(" ", 4)[2]
138
- id_value || last_insert_id(table, sequence_name || default_sequence_name(table, pk))
390
+ super || last_insert_id(table, sequence_name || default_sequence_name(table, pk))
139
391
  end
140
392
 
393
+ # Queries the database and returns the results in an Array or nil otherwise.
141
394
  def query(sql, name = nil) #:nodoc:
142
395
  log(sql, name) do
143
396
  if @async
@@ -148,7 +401,9 @@ module ActiveRecord
148
401
  end
149
402
  end
150
403
 
151
- def execute(sql, name = nil) #:nodoc:
404
+ # Executes an SQL statement, returning a PGresult object on success
405
+ # or raising a PGError exception otherwise.
406
+ def execute(sql, name = nil)
152
407
  log(sql, name) do
153
408
  if @async
154
409
  @connection.async_exec(sql)
@@ -158,26 +413,30 @@ module ActiveRecord
158
413
  end
159
414
  end
160
415
 
161
- def update(sql, name = nil) #:nodoc:
162
- execute(sql, name).cmdtuples
416
+ # Executes an UPDATE query and returns the number of affected tuples.
417
+ def update_sql(sql, name = nil)
418
+ super.cmdtuples
163
419
  end
164
420
 
165
- def begin_db_transaction #:nodoc:
421
+ # Begins a transaction.
422
+ def begin_db_transaction
166
423
  execute "BEGIN"
167
424
  end
168
425
 
169
- def commit_db_transaction #:nodoc:
426
+ # Commits a transaction.
427
+ def commit_db_transaction
170
428
  execute "COMMIT"
171
429
  end
172
430
 
173
- def rollback_db_transaction #:nodoc:
431
+ # Aborts a transaction.
432
+ def rollback_db_transaction
174
433
  execute "ROLLBACK"
175
434
  end
176
435
 
177
436
  # SCHEMA STATEMENTS ========================================
178
437
 
179
- # Return the list of all tables in the schema search path.
180
- def tables(name = nil) #:nodoc:
438
+ # Returns the list of all tables in the schema search path or a specified schema.
439
+ def tables(name = nil)
181
440
  schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',')
182
441
  query(<<-SQL, name).map { |row| row[0] }
183
442
  SELECT tablename
@@ -186,7 +445,8 @@ module ActiveRecord
186
445
  SQL
187
446
  end
188
447
 
189
- def indexes(table_name, name = nil) #:nodoc:
448
+ # Returns the list of all indexes for a table.
449
+ def indexes(table_name, name = nil)
190
450
  result = query(<<-SQL, name)
191
451
  SELECT i.relname, d.indisunique, a.attname
192
452
  FROM pg_class t, pg_class i, pg_index d, pg_attribute a
@@ -219,34 +479,49 @@ module ActiveRecord
219
479
  indexes
220
480
  end
221
481
 
222
- def columns(table_name, name = nil) #:nodoc:
223
- column_definitions(table_name).collect do |name, type, default, notnull, typmod|
224
- # typmod now unused as limit, precision, scale all handled by superclass
225
- Column.new(name, default_value(default), translate_field_type(type), notnull == "f")
482
+ # Returns the list of all column definitions for a table.
483
+ def columns(table_name, name = nil)
484
+ # Limit, precision, and scale are all handled by the superclass.
485
+ column_definitions(table_name).collect do |name, type, default, notnull|
486
+ PostgreSQLColumn.new(name, default, type, notnull == 'f')
226
487
  end
227
488
  end
228
489
 
229
- # Set the schema search path to a string of comma-separated schema names.
230
- # Names beginning with $ are quoted (e.g. $user => '$user')
231
- # See http://www.postgresql.org/docs/8.0/interactive/ddl-schemas.html
232
- def schema_search_path=(schema_csv) #:nodoc:
490
+ # Sets the schema search path to a string of comma-separated schema names.
491
+ # Names beginning with $ have to be quoted (e.g. $user => '$user').
492
+ # See: http://www.postgresql.org/docs/current/static/ddl-schemas.html
493
+ #
494
+ # This should be not be called manually but set in database.yml.
495
+ def schema_search_path=(schema_csv)
233
496
  if schema_csv
234
497
  execute "SET search_path TO #{schema_csv}"
235
- @schema_search_path = nil
498
+ @schema_search_path = schema_csv
236
499
  end
237
500
  end
238
501
 
239
- def schema_search_path #:nodoc:
502
+ # Returns the active schema search path.
503
+ def schema_search_path
240
504
  @schema_search_path ||= query('SHOW search_path')[0][0]
241
505
  end
242
506
 
243
- def default_sequence_name(table_name, pk = nil)
507
+ # Returns the current client message level.
508
+ def client_min_messages
509
+ query('SHOW client_min_messages')[0][0]
510
+ end
511
+
512
+ # Set the client message level.
513
+ def client_min_messages=(level)
514
+ execute("SET client_min_messages TO '#{level}'")
515
+ end
516
+
517
+ # Returns the sequence name for a table's primary key or some other specified key.
518
+ def default_sequence_name(table_name, pk = nil) #:nodoc:
244
519
  default_pk, default_seq = pk_and_sequence_for(table_name)
245
520
  default_seq || "#{table_name}_#{pk || default_pk || 'id'}_seq"
246
521
  end
247
522
 
248
- # Resets sequence to the max value of the table's pk if present.
249
- def reset_pk_sequence!(table, pk = nil, sequence = nil)
523
+ # Resets the sequence of a table's primary key to the maximum value.
524
+ def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc:
250
525
  unless pk and sequence
251
526
  default_pk, default_sequence = pk_and_sequence_for(table)
252
527
  pk ||= default_pk
@@ -263,19 +538,18 @@ module ActiveRecord
263
538
  end
264
539
  end
265
540
 
266
- # Find a table's primary key and sequence.
267
- def pk_and_sequence_for(table)
541
+ # Returns a table's primary key and belonging sequence.
542
+ def pk_and_sequence_for(table) #:nodoc:
268
543
  # First try looking for a sequence with a dependency on the
269
544
  # given table's primary key.
270
545
  result = query(<<-end_sql, 'PK and serial sequence')[0]
271
- SELECT attr.attname, name.nspname, seq.relname
546
+ SELECT attr.attname, seq.relname
272
547
  FROM pg_class seq,
273
548
  pg_attribute attr,
274
549
  pg_depend dep,
275
550
  pg_namespace name,
276
551
  pg_constraint cons
277
552
  WHERE seq.oid = dep.objid
278
- AND seq.relnamespace = name.oid
279
553
  AND seq.relkind = 'S'
280
554
  AND attr.attrelid = dep.refobjid
281
555
  AND attr.attnum = dep.refobjsubid
@@ -289,11 +563,9 @@ module ActiveRecord
289
563
  # If that fails, try parsing the primary key's default value.
290
564
  # Support the 7.x and 8.0 nextval('foo'::text) as well as
291
565
  # the 8.1+ nextval('foo'::regclass).
292
- # TODO: assumes sequence is in same schema as table.
293
566
  result = query(<<-end_sql, 'PK and custom sequence')[0]
294
- SELECT attr.attname, name.nspname, split_part(def.adsrc, '''', 2)
567
+ SELECT attr.attname, split_part(def.adsrc, '''', 2)
295
568
  FROM pg_class t
296
- JOIN pg_namespace name ON (t.relnamespace = name.oid)
297
569
  JOIN pg_attribute attr ON (t.oid = attrelid)
298
570
  JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
299
571
  JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
@@ -302,68 +574,72 @@ module ActiveRecord
302
574
  AND def.adsrc ~* 'nextval'
303
575
  end_sql
304
576
  end
305
- # check for existence of . in sequence name as in public.foo_sequence. if it does not exist, return unqualified sequence
306
- # We cannot qualify unqualified sequences, as rails doesn't qualify any table access, using the search path
577
+ # [primary_key, sequence]
307
578
  [result.first, result.last]
308
579
  rescue
309
580
  nil
310
581
  end
311
582
 
583
+ # Renames a table.
312
584
  def rename_table(name, new_name)
313
585
  execute "ALTER TABLE #{name} RENAME TO #{new_name}"
314
586
  end
315
587
 
588
+ # Adds a column to a table.
316
589
  def add_column(table_name, column_name, type, options = {})
317
590
  default = options[:default]
318
591
  notnull = options[:null] == false
319
592
 
320
593
  # Add the column.
321
- execute("ALTER TABLE #{table_name} ADD COLUMN #{column_name} #{type_to_sql(type, options[:limit])}")
594
+ execute("ALTER TABLE #{table_name} ADD COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit])}")
322
595
 
323
- # Set optional default. If not null, update nulls to the new default.
324
- if options_include_default?(options)
325
- change_column_default(table_name, column_name, default)
326
- if notnull
327
- execute("UPDATE #{table_name} SET #{column_name}=#{quote(default, options[:column])} WHERE #{column_name} IS NULL")
328
- end
329
- end
330
-
331
- if notnull
332
- execute("ALTER TABLE #{table_name} ALTER #{column_name} SET NOT NULL")
333
- end
596
+ change_column_default(table_name, column_name, default) if options_include_default?(options)
597
+ change_column_null(table_name, column_name, false, default) if notnull
334
598
  end
335
599
 
336
- def change_column(table_name, column_name, type, options = {}) #:nodoc:
600
+ # Changes the column of a table.
601
+ def change_column(table_name, column_name, type, options = {})
337
602
  begin
338
- execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} TYPE #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
603
+ execute "ALTER TABLE #{table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
339
604
  rescue ActiveRecord::StatementInvalid
340
- # This is PG7, so we use a more arcane way of doing it.
605
+ # This is PostgreSQL 7.x, so we have to use a more arcane way of doing it.
341
606
  begin_db_transaction
342
- add_column(table_name, "#{column_name}_ar_tmp", type, options)
343
- execute "UPDATE #{table_name} SET #{column_name}_ar_tmp = CAST(#{column_name} AS #{type_to_sql(type, options[:limit], options[:precision], options[:scale])})"
607
+ tmp_column_name = "#{column_name}_ar_tmp"
608
+ add_column(table_name, tmp_column_name, type, options)
609
+ execute "UPDATE #{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])})"
344
610
  remove_column(table_name, column_name)
345
- rename_column(table_name, "#{column_name}_ar_tmp", column_name)
611
+ rename_column(table_name, tmp_column_name, column_name)
346
612
  commit_db_transaction
347
613
  end
348
614
 
349
- if options_include_default?(options)
350
- change_column_default(table_name, column_name, options[:default])
351
- end
615
+ change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
616
+ change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
352
617
  end
353
618
 
354
- def change_column_default(table_name, column_name, default) #:nodoc:
619
+ # Changes the default value of a table column.
620
+ def change_column_default(table_name, column_name, default)
355
621
  execute "ALTER TABLE #{table_name} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote(default)}"
356
622
  end
357
623
 
358
- def rename_column(table_name, column_name, new_column_name) #:nodoc:
624
+ def change_column_null(table_name, column_name, null, default = nil)
625
+ unless null || default.nil?
626
+ execute("UPDATE #{table_name} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
627
+ end
628
+ execute("ALTER TABLE #{table_name} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
629
+ end
630
+
631
+ # Renames a column in a table.
632
+ def rename_column(table_name, column_name, new_column_name)
359
633
  execute "ALTER TABLE #{table_name} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
360
634
  end
361
635
 
362
- def remove_index(table_name, options) #:nodoc:
636
+ # Drops an index from a table.
637
+ def remove_index(table_name, options = {})
363
638
  execute "DROP INDEX #{index_name(table_name, options)}"
364
639
  end
365
640
 
366
- def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
641
+ # Maps logical Rails types to PostgreSQL-specific data types.
642
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil)
367
643
  return super unless type.to_s == 'integer'
368
644
 
369
645
  if limit.nil? || limit == 4
@@ -375,32 +651,32 @@ module ActiveRecord
375
651
  end
376
652
  end
377
653
 
378
- # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
654
+ # Returns a SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
379
655
  #
380
656
  # PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
381
657
  # requires that the ORDER BY include the distinct column.
382
658
  #
383
659
  # distinct("posts.id", "posts.created_at desc")
384
- def distinct(columns, order_by)
660
+ def distinct(columns, order_by) #:nodoc:
385
661
  return "DISTINCT #{columns}" if order_by.blank?
386
662
 
387
- # construct a clean list of column names from the ORDER BY clause, removing
388
- # any asc/desc modifiers
663
+ # Construct a clean list of column names from the ORDER BY clause, removing
664
+ # any ASC/DESC modifiers
389
665
  order_columns = order_by.split(',').collect { |s| s.split.first }
390
666
  order_columns.delete_if &:blank?
391
667
  order_columns = order_columns.zip((0...order_columns.size).to_a).map { |s,i| "#{s} AS alias_#{i}" }
392
668
 
393
- # return a DISTINCT ON() clause that's distinct on the columns we want but includes
394
- # all the required columns for the ORDER BY to work properly
669
+ # Return a DISTINCT ON() clause that's distinct on the columns we want but includes
670
+ # all the required columns for the ORDER BY to work properly.
395
671
  sql = "DISTINCT ON (#{columns}) #{columns}, "
396
672
  sql << order_columns * ', '
397
673
  end
398
674
 
399
- # ORDER BY clause for the passed order option.
675
+ # Returns an ORDER BY clause for the passed order option.
400
676
  #
401
677
  # PostgreSQL does not allow arbitrary ordering when using DISTINCT ON, so we work around this
402
678
  # by wrapping the sql as a sub-select and ordering in that query.
403
- def add_order_by_for_association_limiting!(sql, options)
679
+ def add_order_by_for_association_limiting!(sql, options) #:nodoc:
404
680
  return sql if options[:order].blank?
405
681
 
406
682
  order = options[:order].split(',').collect { |s| s.strip }.reject(&:blank?)
@@ -410,103 +686,135 @@ module ActiveRecord
410
686
  sql.replace "SELECT * FROM (#{sql}) AS id_list ORDER BY #{order}"
411
687
  end
412
688
 
689
+ protected
690
+ # Returns the version of the connected PostgreSQL version.
691
+ def postgresql_version
692
+ @postgresql_version ||=
693
+ if @connection.respond_to?(:server_version)
694
+ @connection.server_version
695
+ else
696
+ # Mimic PGconn.server_version behavior
697
+ begin
698
+ query('SELECT version()')[0][0] =~ /PostgreSQL (\d+)\.(\d+)\.(\d+)/
699
+ ($1.to_i * 10000) + ($2.to_i * 100) + $3.to_i
700
+ rescue
701
+ 0
702
+ end
703
+ end
704
+ end
705
+
413
706
  private
414
- BYTEA_COLUMN_TYPE_OID = 17
415
- NUMERIC_COLUMN_TYPE_OID = 1700
416
- TIMESTAMPOID = 1114
417
- TIMESTAMPTZOID = 1184
707
+ # The internal PostgreSQL identifer of the money data type.
708
+ MONEY_COLUMN_TYPE_OID = 790 #:nodoc:
709
+
710
+ # Connects to a PostgreSQL server and sets up the adapter depending on the
711
+ # connected server's characteristics.
712
+ def connect
713
+ @connection = PGconn.connect(*@connection_parameters)
714
+ PGconn.translate_results = false if PGconn.respond_to?(:translate_results=)
715
+
716
+ # Ignore async_exec and async_query when using postgres-pr.
717
+ @async = @config[:allow_concurrency] && @connection.respond_to?(:async_exec)
718
+
719
+ # Use escape string syntax if available. We cannot do this lazily when encountering
720
+ # the first string, because that could then break any transactions in progress.
721
+ # See: http://www.postgresql.org/docs/current/static/runtime-config-compatible.html
722
+ # If PostgreSQL doesn't know the standard_conforming_strings parameter then it doesn't
723
+ # support escape string syntax. Don't override the inherited quoted_string_prefix.
724
+ if supports_standard_conforming_strings?
725
+ self.class.instance_eval do
726
+ define_method(:quoted_string_prefix) { 'E' }
727
+ end
728
+ end
418
729
 
730
+ # Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of
731
+ # PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision
732
+ # should know about this but can't detect it there, so deal with it here.
733
+ money_precision = (postgresql_version >= 80300) ? 19 : 10
734
+ PostgreSQLColumn.module_eval(<<-end_eval)
735
+ def extract_precision(sql_type)
736
+ if sql_type =~ /^money$/
737
+ #{money_precision}
738
+ else
739
+ super
740
+ end
741
+ end
742
+ end_eval
743
+
744
+ configure_connection
745
+ end
746
+
747
+ # Configures the encoding, verbosity, and schema search path of the connection.
748
+ # This is called by #connect and should not be called manually.
419
749
  def configure_connection
420
750
  if @config[:encoding]
421
- execute("SET client_encoding TO '#{@config[:encoding]}'")
422
- end
423
- if @config[:min_messages]
424
- execute("SET client_min_messages TO '#{@config[:min_messages]}'")
751
+ if @connection.respond_to?(:set_client_encoding)
752
+ @connection.set_client_encoding(@config[:encoding])
753
+ else
754
+ execute("SET client_encoding TO '#{@config[:encoding]}'")
755
+ end
425
756
  end
757
+ self.client_min_messages = @config[:min_messages] if @config[:min_messages]
758
+ self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
426
759
  end
427
760
 
428
- def last_insert_id(table, sequence_name)
761
+ # Returns the current ID of a table's sequence.
762
+ def last_insert_id(table, sequence_name) #:nodoc:
429
763
  Integer(select_value("SELECT currval('#{sequence_name}')"))
430
764
  end
431
765
 
766
+ # Executes a SELECT query and returns the results, performing any data type
767
+ # conversions that are required to be performed here instead of in PostgreSQLColumn.
432
768
  def select(sql, name = nil)
769
+ fields, rows = select_raw(sql, name)
770
+ result = []
771
+ for row in rows
772
+ row_hash = {}
773
+ fields.each_with_index do |f, i|
774
+ row_hash[f] = row[i]
775
+ end
776
+ result << row_hash
777
+ end
778
+ result
779
+ end
780
+
781
+ def select_raw(sql, name = nil)
433
782
  res = execute(sql, name)
434
783
  results = res.result
784
+ fields = []
435
785
  rows = []
436
786
  if results.length > 0
437
787
  fields = res.fields
438
788
  results.each do |row|
439
789
  hashed_row = {}
440
- row.each_index do |cel_index|
441
- column = row[cel_index]
442
-
443
- case res.type(cel_index)
444
- when BYTEA_COLUMN_TYPE_OID
445
- column = unescape_bytea(column)
446
- when TIMESTAMPTZOID, TIMESTAMPOID
447
- column = cast_to_time(column)
448
- when NUMERIC_COLUMN_TYPE_OID
449
- column = column.to_d if column.respond_to?(:to_d)
790
+ row.each_index do |cell_index|
791
+ # If this is a money type column and there are any currency symbols,
792
+ # then strip them off. Indeed it would be prettier to do this in
793
+ # PostgreSQLColumn.string_to_decimal but would break form input
794
+ # fields that call value_before_type_cast.
795
+ if res.type(cell_index) == MONEY_COLUMN_TYPE_OID
796
+ # Because money output is formatted according to the locale, there are two
797
+ # cases to consider (note the decimal separators):
798
+ # (1) $12,345,678.12
799
+ # (2) $12.345.678,12
800
+ case column = row[cell_index]
801
+ when /^-?\D+[\d,]+\.\d{2}$/ # (1)
802
+ row[cell_index] = column.gsub(/[^-\d\.]/, '')
803
+ when /^-?\D+[\d\.]+,\d{2}$/ # (2)
804
+ row[cell_index] = column.gsub(/[^-\d,]/, '').sub(/,/, '.')
805
+ end
450
806
  end
451
807
 
452
- hashed_row[fields[cel_index]] = column
808
+ hashed_row[fields[cell_index]] = column
453
809
  end
454
- rows << hashed_row
810
+ rows << row
455
811
  end
456
812
  end
457
813
  res.clear
458
- return rows
459
- end
460
-
461
- def escape_bytea(s)
462
- if PGconn.respond_to? :escape_bytea
463
- self.class.send(:define_method, :escape_bytea) do |s|
464
- PGconn.escape_bytea(s) if s
465
- end
466
- else
467
- self.class.send(:define_method, :escape_bytea) do |s|
468
- if s
469
- result = ''
470
- s.each_byte { |c| result << sprintf('\\\\%03o', c) }
471
- result
472
- end
473
- end
474
- end
475
- escape_bytea(s)
476
- end
477
-
478
- def unescape_bytea(s)
479
- if PGconn.respond_to? :unescape_bytea
480
- self.class.send(:define_method, :unescape_bytea) do |s|
481
- PGconn.unescape_bytea(s) if s
482
- end
483
- else
484
- self.class.send(:define_method, :unescape_bytea) do |s|
485
- if s
486
- result = ''
487
- i, max = 0, s.size
488
- while i < max
489
- char = s[i]
490
- if char == ?\\
491
- if s[i+1] == ?\\
492
- char = ?\\
493
- i += 1
494
- else
495
- char = s[i+1..i+3].oct
496
- i += 3
497
- end
498
- end
499
- result << char
500
- i += 1
501
- end
502
- result
503
- end
504
- end
505
- end
506
- unescape_bytea(s)
814
+ return fields, rows
507
815
  end
508
816
 
509
- # Query a table's column names, default values, and types.
817
+ # Returns the list of a table's column names, data types, and default values.
510
818
  #
511
819
  # The underlying query is roughly:
512
820
  # SELECT column.name, column.type, default.value
@@ -524,7 +832,7 @@ module ActiveRecord
524
832
  # Query implementation notes:
525
833
  # - format_type includes the column size constraint, e.g. varchar(50)
526
834
  # - ::regclass is a function that gives the id for a table name
527
- def column_definitions(table_name)
835
+ def column_definitions(table_name) #:nodoc:
528
836
  query <<-end_sql
529
837
  SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull
530
838
  FROM pg_attribute a LEFT JOIN pg_attrdef d
@@ -534,51 +842,6 @@ module ActiveRecord
534
842
  ORDER BY a.attnum
535
843
  end_sql
536
844
  end
537
-
538
- # Translate PostgreSQL-specific types into simplified SQL types.
539
- # These are special cases; standard types are handled by
540
- # ConnectionAdapters::Column#simplified_type.
541
- def translate_field_type(field_type)
542
- # Match the beginning of field_type since it may have a size constraint on the end.
543
- case field_type
544
- # PostgreSQL array data types.
545
- when /\[\]$/i then 'string'
546
- when /^timestamp/i then 'datetime'
547
- when /^real|^money/i then 'float'
548
- when /^interval/i then 'string'
549
- # geometric types (the line type is currently not implemented in postgresql)
550
- when /^(?:point|lseg|box|"?path"?|polygon|circle)/i then 'string'
551
- when /^bytea/i then 'binary'
552
- else field_type # Pass through standard types.
553
- end
554
- end
555
-
556
- def default_value(value)
557
- # Boolean types
558
- return "t" if value =~ /true/i
559
- return "f" if value =~ /false/i
560
-
561
- # Char/String/Bytea type values
562
- return $1 if value =~ /^'(.*)'::(bpchar|text|character varying|bytea)$/
563
-
564
- # Numeric values
565
- return value if value =~ /^-?[0-9]+(\.[0-9]*)?/
566
-
567
- # Fixed dates / times
568
- return $1 if value =~ /^'(.+)'::(date|timestamp)/
569
-
570
- # Anything else is blank, some user type, or some function
571
- # and we can't know the value of that, so return nil.
572
- return nil
573
- end
574
-
575
- # Only needed for DateTime instances
576
- def cast_to_time(value)
577
- return value unless value.class == DateTime
578
- v = value
579
- time_array = [v.year, v.month, v.day, v.hour, v.min, v.sec, v.usec]
580
- Time.send(Base.default_timezone, *time_array) rescue nil
581
- end
582
845
  end
583
846
  end
584
847
  end