activerecord 3.2.22.5 → 4.0.0.beta1

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 (162) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1024 -543
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +20 -29
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +55 -44
  7. data/lib/active_record/aggregations.rb +40 -34
  8. data/lib/active_record/associations.rb +204 -276
  9. data/lib/active_record/associations/alias_tracker.rb +1 -1
  10. data/lib/active_record/associations/association.rb +30 -35
  11. data/lib/active_record/associations/association_scope.rb +40 -40
  12. data/lib/active_record/associations/belongs_to_association.rb +15 -2
  13. data/lib/active_record/associations/builder/association.rb +81 -28
  14. data/lib/active_record/associations/builder/belongs_to.rb +35 -57
  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 +92 -88
  21. data/lib/active_record/associations/collection_proxy.rb +913 -63
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +12 -10
  23. data/lib/active_record/associations/has_many_association.rb +35 -9
  24. data/lib/active_record/associations/has_many_through_association.rb +24 -14
  25. data/lib/active_record/associations/has_one_association.rb +33 -13
  26. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  27. data/lib/active_record/associations/join_dependency.rb +2 -2
  28. data/lib/active_record/associations/join_dependency/join_association.rb +17 -22
  29. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  30. data/lib/active_record/associations/join_helper.rb +1 -11
  31. data/lib/active_record/associations/preloader.rb +14 -17
  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 +1 -1
  35. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  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/singular_association.rb +11 -11
  39. data/lib/active_record/associations/through_association.rb +2 -2
  40. data/lib/active_record/attribute_assignment.rb +133 -153
  41. data/lib/active_record/attribute_methods.rb +196 -93
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
  43. data/lib/active_record/attribute_methods/dirty.rb +31 -28
  44. data/lib/active_record/attribute_methods/primary_key.rb +38 -30
  45. data/lib/active_record/attribute_methods/query.rb +5 -4
  46. data/lib/active_record/attribute_methods/read.rb +62 -91
  47. data/lib/active_record/attribute_methods/serialization.rb +97 -66
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -45
  49. data/lib/active_record/attribute_methods/write.rb +32 -39
  50. data/lib/active_record/autosave_association.rb +56 -70
  51. data/lib/active_record/base.rb +53 -450
  52. data/lib/active_record/callbacks.rb +53 -18
  53. data/lib/active_record/coders/yaml_column.rb +11 -9
  54. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +353 -197
  55. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  56. data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -131
  57. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -19
  58. data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -3
  59. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +101 -91
  60. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +59 -0
  61. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +225 -96
  62. data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
  63. data/lib/active_record/connection_adapters/abstract_adapter.rb +99 -46
  64. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +114 -36
  65. data/lib/active_record/connection_adapters/column.rb +46 -24
  66. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  67. data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
  68. data/lib/active_record/connection_adapters/mysql_adapter.rb +181 -64
  69. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
  70. data/lib/active_record/connection_adapters/postgresql/cast.rb +132 -0
  71. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid.rb +347 -0
  73. data/lib/active_record/connection_adapters/postgresql/quoting.rb +158 -0
  74. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  75. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +448 -0
  76. data/lib/active_record/connection_adapters/postgresql_adapter.rb +454 -885
  77. data/lib/active_record/connection_adapters/schema_cache.rb +48 -16
  78. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +574 -13
  79. data/lib/active_record/connection_handling.rb +98 -0
  80. data/lib/active_record/core.rb +428 -0
  81. data/lib/active_record/counter_cache.rb +106 -108
  82. data/lib/active_record/dynamic_matchers.rb +110 -63
  83. data/lib/active_record/errors.rb +25 -8
  84. data/lib/active_record/explain.rb +8 -58
  85. data/lib/active_record/explain_subscriber.rb +6 -3
  86. data/lib/active_record/fixture_set/file.rb +56 -0
  87. data/lib/active_record/fixtures.rb +146 -148
  88. data/lib/active_record/inheritance.rb +77 -59
  89. data/lib/active_record/integration.rb +5 -5
  90. data/lib/active_record/locale/en.yml +8 -1
  91. data/lib/active_record/locking/optimistic.rb +38 -42
  92. data/lib/active_record/locking/pessimistic.rb +4 -4
  93. data/lib/active_record/log_subscriber.rb +19 -9
  94. data/lib/active_record/migration.rb +318 -153
  95. data/lib/active_record/migration/command_recorder.rb +90 -31
  96. data/lib/active_record/migration/join_table.rb +15 -0
  97. data/lib/active_record/model_schema.rb +69 -92
  98. data/lib/active_record/nested_attributes.rb +113 -148
  99. data/lib/active_record/null_relation.rb +65 -0
  100. data/lib/active_record/persistence.rb +188 -97
  101. data/lib/active_record/query_cache.rb +18 -36
  102. data/lib/active_record/querying.rb +19 -15
  103. data/lib/active_record/railtie.rb +91 -36
  104. data/lib/active_record/railties/console_sandbox.rb +0 -2
  105. data/lib/active_record/railties/controller_runtime.rb +2 -2
  106. data/lib/active_record/railties/databases.rake +90 -309
  107. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  108. data/lib/active_record/readonly_attributes.rb +7 -3
  109. data/lib/active_record/reflection.rb +72 -56
  110. data/lib/active_record/relation.rb +241 -157
  111. data/lib/active_record/relation/batches.rb +25 -22
  112. data/lib/active_record/relation/calculations.rb +143 -121
  113. data/lib/active_record/relation/delegation.rb +96 -18
  114. data/lib/active_record/relation/finder_methods.rb +117 -183
  115. data/lib/active_record/relation/merger.rb +133 -0
  116. data/lib/active_record/relation/predicate_builder.rb +90 -42
  117. data/lib/active_record/relation/query_methods.rb +666 -136
  118. data/lib/active_record/relation/spawn_methods.rb +43 -150
  119. data/lib/active_record/result.rb +33 -6
  120. data/lib/active_record/sanitization.rb +24 -50
  121. data/lib/active_record/schema.rb +19 -12
  122. data/lib/active_record/schema_dumper.rb +31 -39
  123. data/lib/active_record/schema_migration.rb +36 -0
  124. data/lib/active_record/scoping.rb +0 -124
  125. data/lib/active_record/scoping/default.rb +48 -45
  126. data/lib/active_record/scoping/named.rb +74 -103
  127. data/lib/active_record/serialization.rb +6 -2
  128. data/lib/active_record/serializers/xml_serializer.rb +9 -15
  129. data/lib/active_record/store.rb +119 -15
  130. data/lib/active_record/tasks/database_tasks.rb +158 -0
  131. data/lib/active_record/tasks/mysql_database_tasks.rb +138 -0
  132. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  133. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  134. data/lib/active_record/test_case.rb +61 -38
  135. data/lib/active_record/timestamp.rb +8 -9
  136. data/lib/active_record/transactions.rb +65 -51
  137. data/lib/active_record/validations.rb +17 -15
  138. data/lib/active_record/validations/associated.rb +20 -14
  139. data/lib/active_record/validations/presence.rb +65 -0
  140. data/lib/active_record/validations/uniqueness.rb +93 -52
  141. data/lib/active_record/version.rb +4 -4
  142. data/lib/rails/generators/active_record.rb +3 -5
  143. data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -7
  144. data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
  145. data/lib/rails/generators/active_record/model/model_generator.rb +4 -3
  146. data/lib/rails/generators/active_record/model/templates/model.rb +1 -6
  147. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  148. metadata +53 -46
  149. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  150. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  151. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  152. data/lib/active_record/dynamic_finder_match.rb +0 -68
  153. data/lib/active_record/dynamic_scope_match.rb +0 -23
  154. data/lib/active_record/fixtures/file.rb +0 -65
  155. data/lib/active_record/identity_map.rb +0 -162
  156. data/lib/active_record/observer.rb +0 -121
  157. data/lib/active_record/session_store.rb +0 -360
  158. data/lib/rails/generators/active_record/migration.rb +0 -15
  159. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  160. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  161. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  162. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -0,0 +1,347 @@
1
+ require 'active_record/connection_adapters/abstract_adapter'
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ class PostgreSQLAdapter < AbstractAdapter
6
+ module OID
7
+ class Type
8
+ def type; end
9
+
10
+ def type_cast_for_write(value)
11
+ value
12
+ end
13
+ end
14
+
15
+ class Identity < Type
16
+ def type_cast(value)
17
+ value
18
+ end
19
+ end
20
+
21
+ class Bytea < Type
22
+ def type_cast(value)
23
+ PGconn.unescape_bytea value
24
+ end
25
+ end
26
+
27
+ class Money < Type
28
+ def type_cast(value)
29
+ return if value.nil?
30
+
31
+ # Because money output is formatted according to the locale, there are two
32
+ # cases to consider (note the decimal separators):
33
+ # (1) $12,345,678.12
34
+ # (2) $12.345.678,12
35
+
36
+ case value
37
+ when /^-?\D+[\d,]+\.\d{2}$/ # (1)
38
+ value.gsub!(/[^-\d.]/, '')
39
+ when /^-?\D+[\d.]+,\d{2}$/ # (2)
40
+ value.gsub!(/[^-\d,]/, '').sub!(/,/, '.')
41
+ end
42
+
43
+ ConnectionAdapters::Column.value_to_decimal value
44
+ end
45
+ end
46
+
47
+ class Vector < Type
48
+ attr_reader :delim, :subtype
49
+
50
+ # +delim+ corresponds to the `typdelim` column in the pg_types
51
+ # table. +subtype+ is derived from the `typelem` column in the
52
+ # pg_types table.
53
+ def initialize(delim, subtype)
54
+ @delim = delim
55
+ @subtype = subtype
56
+ end
57
+
58
+ # FIXME: this should probably split on +delim+ and use +subtype+
59
+ # to cast the values. Unfortunately, the current Rails behavior
60
+ # is to just return the string.
61
+ def type_cast(value)
62
+ value
63
+ end
64
+ end
65
+
66
+ class Array < Type
67
+ attr_reader :subtype
68
+ def initialize(subtype)
69
+ @subtype = subtype
70
+ end
71
+
72
+ def type_cast(value)
73
+ if String === value
74
+ ConnectionAdapters::PostgreSQLColumn.string_to_array value, @subtype
75
+ else
76
+ value
77
+ end
78
+ end
79
+ end
80
+
81
+ class Range < Type
82
+ attr_reader :subtype
83
+ def initialize(subtype)
84
+ @subtype = subtype
85
+ end
86
+
87
+ def extract_bounds(value)
88
+ from, to = value[1..-2].split(',')
89
+ {
90
+ from: (value[1] == ',' || from == '-infinity') ? infinity(:negative => true) : from,
91
+ to: (value[-2] == ',' || to == 'infinity') ? infinity : to,
92
+ exclude_start: (value[0] == '('),
93
+ exclude_end: (value[-1] == ')')
94
+ }
95
+ end
96
+
97
+ def infinity(options = {})
98
+ ::Float::INFINITY * (options[:negative] ? -1 : 1)
99
+ end
100
+
101
+ def infinity?(value)
102
+ value.respond_to?(:infinite?) && value.infinite?
103
+ end
104
+
105
+ def to_integer(value)
106
+ infinity?(value) ? value : value.to_i
107
+ end
108
+
109
+ def type_cast(value)
110
+ return if value.nil? || value == 'empty'
111
+ return value if value.is_a?(::Range)
112
+
113
+ extracted = extract_bounds(value)
114
+
115
+ case @subtype
116
+ when :date
117
+ from = ConnectionAdapters::Column.value_to_date(extracted[:from])
118
+ from -= 1.day if extracted[:exclude_start]
119
+ to = ConnectionAdapters::Column.value_to_date(extracted[:to])
120
+ when :decimal
121
+ from = BigDecimal.new(extracted[:from].to_s)
122
+ # FIXME: add exclude start for ::Range, same for timestamp ranges
123
+ to = BigDecimal.new(extracted[:to].to_s)
124
+ when :time
125
+ from = ConnectionAdapters::Column.string_to_time(extracted[:from])
126
+ to = ConnectionAdapters::Column.string_to_time(extracted[:to])
127
+ when :integer
128
+ from = to_integer(extracted[:from]) rescue value ? 1 : 0
129
+ from -= 1 if extracted[:exclude_start]
130
+ to = to_integer(extracted[:to]) rescue value ? 1 : 0
131
+ else
132
+ return value
133
+ end
134
+
135
+ ::Range.new(from, to, extracted[:exclude_end])
136
+ end
137
+ end
138
+
139
+ class Integer < Type
140
+ def type_cast(value)
141
+ return if value.nil?
142
+
143
+ ConnectionAdapters::Column.value_to_integer value
144
+ end
145
+ end
146
+
147
+ class Boolean < Type
148
+ def type_cast(value)
149
+ return if value.nil?
150
+
151
+ ConnectionAdapters::Column.value_to_boolean value
152
+ end
153
+ end
154
+
155
+ class Timestamp < Type
156
+ def type; :timestamp; end
157
+
158
+ def type_cast(value)
159
+ return if value.nil?
160
+
161
+ # FIXME: probably we can improve this since we know it is PG
162
+ # specific
163
+ ConnectionAdapters::PostgreSQLColumn.string_to_time value
164
+ end
165
+ end
166
+
167
+ class Date < Type
168
+ def type; :datetime; end
169
+
170
+ def type_cast(value)
171
+ return if value.nil?
172
+
173
+ # FIXME: probably we can improve this since we know it is PG
174
+ # specific
175
+ ConnectionAdapters::Column.value_to_date value
176
+ end
177
+ end
178
+
179
+ class Time < Type
180
+ def type_cast(value)
181
+ return if value.nil?
182
+
183
+ # FIXME: probably we can improve this since we know it is PG
184
+ # specific
185
+ ConnectionAdapters::Column.string_to_dummy_time value
186
+ end
187
+ end
188
+
189
+ class Float < Type
190
+ def type_cast(value)
191
+ return if value.nil?
192
+
193
+ value.to_f
194
+ end
195
+ end
196
+
197
+ class Decimal < Type
198
+ def type_cast(value)
199
+ return if value.nil?
200
+
201
+ ConnectionAdapters::Column.value_to_decimal value
202
+ end
203
+ end
204
+
205
+ class Hstore < Type
206
+ def type_cast(value)
207
+ return if value.nil?
208
+
209
+ ConnectionAdapters::PostgreSQLColumn.string_to_hstore value
210
+ end
211
+ end
212
+
213
+ class Cidr < Type
214
+ def type_cast(value)
215
+ return if value.nil?
216
+
217
+ ConnectionAdapters::PostgreSQLColumn.string_to_cidr value
218
+ end
219
+ end
220
+
221
+ class Json < Type
222
+ def type_cast(value)
223
+ return if value.nil?
224
+
225
+ ConnectionAdapters::PostgreSQLColumn.string_to_json value
226
+ end
227
+ end
228
+
229
+ class TypeMap
230
+ def initialize
231
+ @mapping = {}
232
+ end
233
+
234
+ def []=(oid, type)
235
+ @mapping[oid] = type
236
+ end
237
+
238
+ def [](oid)
239
+ @mapping[oid]
240
+ end
241
+
242
+ def clear
243
+ @mapping.clear
244
+ end
245
+
246
+ def key?(oid)
247
+ @mapping.key? oid
248
+ end
249
+
250
+ def fetch(ftype, fmod)
251
+ # The type for the numeric depends on the width of the field,
252
+ # so we'll do something special here.
253
+ #
254
+ # When dealing with decimal columns:
255
+ #
256
+ # places after decimal = fmod - 4 & 0xffff
257
+ # places before decimal = (fmod - 4) >> 16 & 0xffff
258
+ if ftype == 1700 && (fmod - 4 & 0xffff).zero?
259
+ ftype = 23
260
+ end
261
+
262
+ @mapping.fetch(ftype) { |oid| yield oid, fmod }
263
+ end
264
+ end
265
+
266
+ TYPE_MAP = TypeMap.new # :nodoc:
267
+
268
+ # When the PG adapter connects, the pg_type table is queried. The
269
+ # key of this hash maps to the `typname` column from the table.
270
+ # TYPE_MAP is then dynamically built with oids as the key and type
271
+ # objects as values.
272
+ NAMES = Hash.new { |h,k| # :nodoc:
273
+ h[k] = OID::Identity.new
274
+ }
275
+
276
+ # Register an OID type named +name+ with a typcasting object in
277
+ # +type+. +name+ should correspond to the `typname` column in
278
+ # the `pg_type` table.
279
+ def self.register_type(name, type)
280
+ NAMES[name] = type
281
+ end
282
+
283
+ # Alias the +old+ type to the +new+ type.
284
+ def self.alias_type(new, old)
285
+ NAMES[new] = NAMES[old]
286
+ end
287
+
288
+ # Is +name+ a registered type?
289
+ def self.registered_type?(name)
290
+ NAMES.key? name
291
+ end
292
+
293
+ register_type 'int2', OID::Integer.new
294
+ alias_type 'int4', 'int2'
295
+ alias_type 'int8', 'int2'
296
+ alias_type 'oid', 'int2'
297
+
298
+ register_type 'daterange', OID::Range.new(:date)
299
+ register_type 'numrange', OID::Range.new(:decimal)
300
+ register_type 'tsrange', OID::Range.new(:time)
301
+ register_type 'int4range', OID::Range.new(:integer)
302
+ alias_type 'tstzrange', 'tsrange'
303
+ alias_type 'int8range', 'int4range'
304
+
305
+ register_type 'numeric', OID::Decimal.new
306
+ register_type 'text', OID::Identity.new
307
+ alias_type 'varchar', 'text'
308
+ alias_type 'char', 'text'
309
+ alias_type 'bpchar', 'text'
310
+ alias_type 'xml', 'text'
311
+
312
+ # FIXME: why are we keeping these types as strings?
313
+ alias_type 'tsvector', 'text'
314
+ alias_type 'interval', 'text'
315
+ alias_type 'bit', 'text'
316
+ alias_type 'varbit', 'text'
317
+ alias_type 'macaddr', 'text'
318
+ alias_type 'uuid', 'text'
319
+
320
+ # FIXME: I don't think this is correct. We should probably be returning a parsed date,
321
+ # but the tests pass with a string returned.
322
+ register_type 'timestamptz', OID::Identity.new
323
+
324
+ register_type 'money', OID::Money.new
325
+ register_type 'bytea', OID::Bytea.new
326
+ register_type 'bool', OID::Boolean.new
327
+
328
+ register_type 'float4', OID::Float.new
329
+ alias_type 'float8', 'float4'
330
+
331
+ register_type 'timestamp', OID::Timestamp.new
332
+ register_type 'date', OID::Date.new
333
+ register_type 'time', OID::Time.new
334
+
335
+ register_type 'path', OID::Identity.new
336
+ register_type 'polygon', OID::Identity.new
337
+ register_type 'circle', OID::Identity.new
338
+ register_type 'hstore', OID::Hstore.new
339
+ register_type 'json', OID::Json.new
340
+ register_type 'ltree', OID::Identity.new
341
+
342
+ register_type 'cidr', OID::Cidr.new
343
+ alias_type 'inet', 'cidr'
344
+ end
345
+ end
346
+ end
347
+ end
@@ -0,0 +1,158 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ class PostgreSQLAdapter < AbstractAdapter
4
+ module Quoting
5
+ # Escapes binary strings for bytea input to the database.
6
+ def escape_bytea(value)
7
+ PGconn.escape_bytea(value) if value
8
+ end
9
+
10
+ # Unescapes bytea output from a database to the binary string it represents.
11
+ # NOTE: This is NOT an inverse of escape_bytea! This is only to be used
12
+ # on escaped binary output from database drive.
13
+ def unescape_bytea(value)
14
+ PGconn.unescape_bytea(value) if value
15
+ end
16
+
17
+ # Quotes PostgreSQL-specific data types for SQL input.
18
+ def quote(value, column = nil) #:nodoc:
19
+ return super unless column
20
+
21
+ case value
22
+ when Range
23
+ if /range$/ =~ column.sql_type
24
+ "'#{PostgreSQLColumn.range_to_string(value)}'::#{column.sql_type}"
25
+ else
26
+ super
27
+ end
28
+ when Array
29
+ if column.array
30
+ "'#{PostgreSQLColumn.array_to_string(value, column, self)}'"
31
+ else
32
+ super
33
+ end
34
+ when Hash
35
+ case column.sql_type
36
+ when 'hstore' then super(PostgreSQLColumn.hstore_to_string(value), column)
37
+ when 'json' then super(PostgreSQLColumn.json_to_string(value), column)
38
+ else super
39
+ end
40
+ when IPAddr
41
+ case column.sql_type
42
+ when 'inet', 'cidr' then super(PostgreSQLColumn.cidr_to_string(value), column)
43
+ else super
44
+ end
45
+ when Float
46
+ if value.infinite? && column.type == :datetime
47
+ "'#{value.to_s.downcase}'"
48
+ elsif value.infinite? || value.nan?
49
+ "'#{value.to_s}'"
50
+ else
51
+ super
52
+ end
53
+ when Numeric
54
+ return super unless column.sql_type == 'money'
55
+ # Not truly string input, so doesn't require (or allow) escape string syntax.
56
+ "'#{value}'"
57
+ when String
58
+ case column.sql_type
59
+ when 'bytea' then "'#{escape_bytea(value)}'"
60
+ when 'xml' then "xml '#{quote_string(value)}'"
61
+ when /^bit/
62
+ case value
63
+ when /^[01]*$/ then "B'#{value}'" # Bit-string notation
64
+ when /^[0-9A-F]*$/i then "X'#{value}'" # Hexadecimal notation
65
+ end
66
+ else
67
+ super
68
+ end
69
+ else
70
+ super
71
+ end
72
+ end
73
+
74
+ def type_cast(value, column, array_member = false)
75
+ return super(value, column) unless column
76
+
77
+ case value
78
+ when Range
79
+ return super(value, column) unless /range$/ =~ column.sql_type
80
+ PostgreSQLColumn.range_to_string(value)
81
+ when NilClass
82
+ if column.array && array_member
83
+ 'NULL'
84
+ elsif column.array
85
+ value
86
+ else
87
+ super(value, column)
88
+ end
89
+ when Array
90
+ return super(value, column) unless column.array
91
+ PostgreSQLColumn.array_to_string(value, column, self)
92
+ when String
93
+ return super(value, column) unless 'bytea' == column.sql_type
94
+ { :value => value, :format => 1 }
95
+ when Hash
96
+ case column.sql_type
97
+ when 'hstore' then PostgreSQLColumn.hstore_to_string(value)
98
+ when 'json' then PostgreSQLColumn.json_to_string(value)
99
+ else super(value, column)
100
+ end
101
+ when IPAddr
102
+ return super(value, column) unless ['inet','cidr'].include? column.sql_type
103
+ PostgreSQLColumn.cidr_to_string(value)
104
+ else
105
+ super(value, column)
106
+ end
107
+ end
108
+
109
+ # Quotes strings for use in SQL input.
110
+ def quote_string(s) #:nodoc:
111
+ @connection.escape(s)
112
+ end
113
+
114
+ # Checks the following cases:
115
+ #
116
+ # - table_name
117
+ # - "table.name"
118
+ # - schema_name.table_name
119
+ # - schema_name."table.name"
120
+ # - "schema.name".table_name
121
+ # - "schema.name"."table.name"
122
+ def quote_table_name(name)
123
+ schema, name_part = extract_pg_identifier_from_name(name.to_s)
124
+
125
+ unless name_part
126
+ quote_column_name(schema)
127
+ else
128
+ table_name, name_part = extract_pg_identifier_from_name(name_part)
129
+ "#{quote_column_name(schema)}.#{quote_column_name(table_name)}"
130
+ end
131
+ end
132
+
133
+ def quote_table_name_for_assignment(table, attr)
134
+ quote_column_name(attr)
135
+ end
136
+
137
+ # Quotes column names for use in SQL queries.
138
+ def quote_column_name(name) #:nodoc:
139
+ PGconn.quote_ident(name.to_s)
140
+ end
141
+
142
+ # Quote date/time values for use in SQL input. Includes microseconds
143
+ # if the value is a Time responding to usec.
144
+ def quoted_date(value) #:nodoc:
145
+ result = super
146
+ if value.acts_like?(:time) && value.respond_to?(:usec)
147
+ result = "#{result}.#{sprintf("%06d", value.usec)}"
148
+ end
149
+
150
+ if value.year < 0
151
+ result = result.sub(/^-/, "") + " BC"
152
+ end
153
+ result
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end