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
@@ -0,0 +1,374 @@
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 Bit < Type
22
+ def type_cast(value)
23
+ if String === value
24
+ ConnectionAdapters::PostgreSQLColumn.string_to_bit value
25
+ else
26
+ value
27
+ end
28
+ end
29
+ end
30
+
31
+ class Bytea < Type
32
+ def type_cast(value)
33
+ return if value.nil?
34
+ # This is a flawed heuristic, but it avoids truncation;
35
+ # we really shouldn’t be calling this with already-unescaped values
36
+ return value if value.dup.force_encoding("BINARY") =~ /\x00/
37
+ PGconn.unescape_bytea value
38
+ end
39
+ end
40
+
41
+ class Money < Type
42
+ def type_cast(value)
43
+ return if value.nil?
44
+ return value unless String === value
45
+
46
+ # Because money output is formatted according to the locale, there are two
47
+ # cases to consider (note the decimal separators):
48
+ # (1) $12,345,678.12
49
+ # (2) $12.345.678,12
50
+ # Negative values are represented as follows:
51
+ # (3) -$2.55
52
+ # (4) ($2.55)
53
+
54
+ value.sub!(/^\((.+)\)$/, '-\1') # (4)
55
+ case value
56
+ when /^-?\D+[\d,]+\.\d{2}$/ # (1)
57
+ value.gsub!(/[^-\d.]/, '')
58
+ when /^-?\D+[\d.]+,\d{2}$/ # (2)
59
+ value.gsub!(/[^-\d,]/, '').sub!(/,/, '.')
60
+ end
61
+
62
+ ConnectionAdapters::Column.value_to_decimal value
63
+ end
64
+ end
65
+
66
+ class Vector < Type
67
+ attr_reader :delim, :subtype
68
+
69
+ # +delim+ corresponds to the `typdelim` column in the pg_types
70
+ # table. +subtype+ is derived from the `typelem` column in the
71
+ # pg_types table.
72
+ def initialize(delim, subtype)
73
+ @delim = delim
74
+ @subtype = subtype
75
+ end
76
+
77
+ # FIXME: this should probably split on +delim+ and use +subtype+
78
+ # to cast the values. Unfortunately, the current Rails behavior
79
+ # is to just return the string.
80
+ def type_cast(value)
81
+ value
82
+ end
83
+ end
84
+
85
+ class Point < Type
86
+ def type_cast(value)
87
+ if String === value
88
+ ConnectionAdapters::PostgreSQLColumn.string_to_point value
89
+ else
90
+ value
91
+ end
92
+ end
93
+ end
94
+
95
+ class Array < Type
96
+ attr_reader :subtype
97
+ def initialize(subtype)
98
+ @subtype = subtype
99
+ end
100
+
101
+ def type_cast(value)
102
+ if String === value
103
+ ConnectionAdapters::PostgreSQLColumn.string_to_array value, @subtype
104
+ else
105
+ value
106
+ end
107
+ end
108
+ end
109
+
110
+ class Range < Type
111
+ attr_reader :subtype
112
+ def initialize(subtype)
113
+ @subtype = subtype
114
+ end
115
+
116
+ def extract_bounds(value)
117
+ from, to = value[1..-2].split(',')
118
+ {
119
+ from: (value[1] == ',' || from == '-infinity') ? infinity(:negative => true) : from,
120
+ to: (value[-2] == ',' || to == 'infinity') ? infinity : to,
121
+ exclude_start: (value[0] == '('),
122
+ exclude_end: (value[-1] == ')')
123
+ }
124
+ end
125
+
126
+ def infinity(options = {})
127
+ ::Float::INFINITY * (options[:negative] ? -1 : 1)
128
+ end
129
+
130
+ def infinity?(value)
131
+ value.respond_to?(:infinite?) && value.infinite?
132
+ end
133
+
134
+ def to_integer(value)
135
+ infinity?(value) ? value : value.to_i
136
+ end
137
+
138
+ def type_cast(value)
139
+ return if value.nil? || value == 'empty'
140
+ return value if value.is_a?(::Range)
141
+
142
+ extracted = extract_bounds(value)
143
+
144
+ case @subtype
145
+ when :date
146
+ from = ConnectionAdapters::Column.value_to_date(extracted[:from])
147
+ from -= 1.day if extracted[:exclude_start]
148
+ to = ConnectionAdapters::Column.value_to_date(extracted[:to])
149
+ when :decimal
150
+ from = BigDecimal.new(extracted[:from].to_s)
151
+ # FIXME: add exclude start for ::Range, same for timestamp ranges
152
+ to = BigDecimal.new(extracted[:to].to_s)
153
+ when :time
154
+ from = ConnectionAdapters::Column.string_to_time(extracted[:from])
155
+ to = ConnectionAdapters::Column.string_to_time(extracted[:to])
156
+ when :integer
157
+ from = to_integer(extracted[:from]) rescue value ? 1 : 0
158
+ from -= 1 if extracted[:exclude_start]
159
+ to = to_integer(extracted[:to]) rescue value ? 1 : 0
160
+ else
161
+ return value
162
+ end
163
+
164
+ ::Range.new(from, to, extracted[:exclude_end])
165
+ end
166
+ end
167
+
168
+ class Integer < Type
169
+ def type_cast(value)
170
+ return if value.nil?
171
+
172
+ ConnectionAdapters::Column.value_to_integer value
173
+ end
174
+ end
175
+
176
+ class Boolean < Type
177
+ def type_cast(value)
178
+ return if value.nil?
179
+
180
+ ConnectionAdapters::Column.value_to_boolean value
181
+ end
182
+ end
183
+
184
+ class Timestamp < Type
185
+ def type; :timestamp; end
186
+
187
+ def type_cast(value)
188
+ return if value.nil?
189
+
190
+ # FIXME: probably we can improve this since we know it is PG
191
+ # specific
192
+ ConnectionAdapters::PostgreSQLColumn.string_to_time value
193
+ end
194
+ end
195
+
196
+ class Date < Type
197
+ def type; :datetime; end
198
+
199
+ def type_cast(value)
200
+ return if value.nil?
201
+
202
+ # FIXME: probably we can improve this since we know it is PG
203
+ # specific
204
+ ConnectionAdapters::Column.value_to_date value
205
+ end
206
+ end
207
+
208
+ class Time < Type
209
+ def type_cast(value)
210
+ return if value.nil?
211
+
212
+ # FIXME: probably we can improve this since we know it is PG
213
+ # specific
214
+ ConnectionAdapters::Column.string_to_dummy_time value
215
+ end
216
+ end
217
+
218
+ class Float < Type
219
+ def type_cast(value)
220
+ return if value.nil?
221
+
222
+ value.to_f
223
+ end
224
+ end
225
+
226
+ class Decimal < Type
227
+ def type_cast(value)
228
+ return if value.nil?
229
+
230
+ ConnectionAdapters::Column.value_to_decimal value
231
+ end
232
+ end
233
+
234
+ class Hstore < Type
235
+ def type_cast(value)
236
+ return if value.nil?
237
+
238
+ ConnectionAdapters::PostgreSQLColumn.string_to_hstore value
239
+ end
240
+ end
241
+
242
+ class Cidr < Type
243
+ def type_cast(value)
244
+ return if value.nil?
245
+
246
+ ConnectionAdapters::PostgreSQLColumn.string_to_cidr value
247
+ end
248
+ end
249
+
250
+ class Json < Type
251
+ def type_cast(value)
252
+ return if value.nil?
253
+
254
+ ConnectionAdapters::PostgreSQLColumn.string_to_json value
255
+ end
256
+ end
257
+
258
+ class TypeMap
259
+ def initialize
260
+ @mapping = {}
261
+ end
262
+
263
+ def []=(oid, type)
264
+ @mapping[oid] = type
265
+ end
266
+
267
+ def [](oid)
268
+ @mapping[oid]
269
+ end
270
+
271
+ def clear
272
+ @mapping.clear
273
+ end
274
+
275
+ def key?(oid)
276
+ @mapping.key? oid
277
+ end
278
+
279
+ def fetch(ftype, fmod)
280
+ # The type for the numeric depends on the width of the field,
281
+ # so we'll do something special here.
282
+ #
283
+ # When dealing with decimal columns:
284
+ #
285
+ # places after decimal = fmod - 4 & 0xffff
286
+ # places before decimal = (fmod - 4) >> 16 & 0xffff
287
+ if ftype == 1700 && (fmod - 4 & 0xffff).zero?
288
+ ftype = 23
289
+ end
290
+
291
+ @mapping.fetch(ftype) { |oid| yield oid, fmod }
292
+ end
293
+ end
294
+
295
+ TYPE_MAP = TypeMap.new # :nodoc:
296
+
297
+ # When the PG adapter connects, the pg_type table is queried. The
298
+ # key of this hash maps to the `typname` column from the table.
299
+ # TYPE_MAP is then dynamically built with oids as the key and type
300
+ # objects as values.
301
+ NAMES = Hash.new { |h,k| # :nodoc:
302
+ h[k] = OID::Identity.new
303
+ }
304
+
305
+ # Register an OID type named +name+ with a typcasting object in
306
+ # +type+. +name+ should correspond to the `typname` column in
307
+ # the `pg_type` table.
308
+ def self.register_type(name, type)
309
+ NAMES[name] = type
310
+ end
311
+
312
+ # Alias the +old+ type to the +new+ type.
313
+ def self.alias_type(new, old)
314
+ NAMES[new] = NAMES[old]
315
+ end
316
+
317
+ # Is +name+ a registered type?
318
+ def self.registered_type?(name)
319
+ NAMES.key? name
320
+ end
321
+
322
+ register_type 'int2', OID::Integer.new
323
+ alias_type 'int4', 'int2'
324
+ alias_type 'int8', 'int2'
325
+ alias_type 'oid', 'int2'
326
+
327
+ register_type 'daterange', OID::Range.new(:date)
328
+ register_type 'numrange', OID::Range.new(:decimal)
329
+ register_type 'tsrange', OID::Range.new(:time)
330
+ register_type 'int4range', OID::Range.new(:integer)
331
+ alias_type 'tstzrange', 'tsrange'
332
+ alias_type 'int8range', 'int4range'
333
+
334
+ register_type 'numeric', OID::Decimal.new
335
+ register_type 'text', OID::Identity.new
336
+ alias_type 'varchar', 'text'
337
+ alias_type 'char', 'text'
338
+ alias_type 'bpchar', 'text'
339
+ alias_type 'xml', 'text'
340
+
341
+ # FIXME: why are we keeping these types as strings?
342
+ alias_type 'tsvector', 'text'
343
+ alias_type 'interval', 'text'
344
+ alias_type 'macaddr', 'text'
345
+ alias_type 'uuid', 'text'
346
+
347
+ register_type 'money', OID::Money.new
348
+ register_type 'bytea', OID::Bytea.new
349
+ register_type 'bool', OID::Boolean.new
350
+ register_type 'bit', OID::Bit.new
351
+ register_type 'varbit', OID::Bit.new
352
+
353
+ register_type 'float4', OID::Float.new
354
+ alias_type 'float8', 'float4'
355
+
356
+ register_type 'timestamp', OID::Timestamp.new
357
+ register_type 'timestamptz', OID::Timestamp.new
358
+ register_type 'date', OID::Date.new
359
+ register_type 'time', OID::Time.new
360
+
361
+ register_type 'path', OID::Identity.new
362
+ register_type 'point', OID::Point.new
363
+ register_type 'polygon', OID::Identity.new
364
+ register_type 'circle', OID::Identity.new
365
+ register_type 'hstore', OID::Hstore.new
366
+ register_type 'json', OID::Json.new
367
+ register_type 'ltree', OID::Identity.new
368
+
369
+ register_type 'cidr', OID::Cidr.new
370
+ alias_type 'inet', 'cidr'
371
+ end
372
+ end
373
+ end
374
+ end
@@ -0,0 +1,183 @@
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 && column.type
20
+
21
+ sql_type = type_to_sql(column.type, column.limit, column.precision, column.scale)
22
+
23
+ case value
24
+ when Range
25
+ if /range$/ =~ sql_type
26
+ escaped = quote_string(PostgreSQLColumn.range_to_string(value))
27
+ "'#{escaped}'::#{sql_type}"
28
+ else
29
+ super
30
+ end
31
+ when Array
32
+ case sql_type
33
+ when 'point' then super(PostgreSQLColumn.point_to_string(value))
34
+ when 'json' then super(PostgreSQLColumn.json_to_string(value))
35
+ else
36
+ if column.array
37
+ "'#{PostgreSQLColumn.array_to_string(value, column, self).gsub(/'/, "''")}'"
38
+ else
39
+ super
40
+ end
41
+ end
42
+ when Hash
43
+ case sql_type
44
+ when 'hstore' then super(PostgreSQLColumn.hstore_to_string(value), column)
45
+ when 'json' then super(PostgreSQLColumn.json_to_string(value), column)
46
+ else super
47
+ end
48
+ when IPAddr
49
+ case sql_type
50
+ when 'inet', 'cidr' then super(PostgreSQLColumn.cidr_to_string(value), column)
51
+ else super
52
+ end
53
+ when Float
54
+ if value.infinite? && column.type == :datetime
55
+ "'#{value.to_s.downcase}'"
56
+ elsif value.infinite? || value.nan?
57
+ "'#{value.to_s}'"
58
+ else
59
+ super
60
+ end
61
+ when Numeric
62
+ if sql_type == 'money' || [:string, :text].include?(column.type)
63
+ # Not truly string input, so doesn't require (or allow) escape string syntax.
64
+ "'#{value}'"
65
+ else
66
+ super
67
+ end
68
+ when String
69
+ case sql_type
70
+ when 'bytea' then "'#{escape_bytea(value)}'"
71
+ when 'xml' then "xml '#{quote_string(value)}'"
72
+ when /^bit/
73
+ case value
74
+ when /\A[01]*\Z/ then "B'#{value}'" # Bit-string notation
75
+ when /\A[0-9A-F]*\Z/i then "X'#{value}'" # Hexadecimal notation
76
+ end
77
+ else
78
+ super
79
+ end
80
+ else
81
+ super
82
+ end
83
+ end
84
+
85
+ def type_cast(value, column, array_member = false)
86
+ return super(value, column) unless column
87
+
88
+ case value
89
+ when Range
90
+ return super(value, column) unless /range$/ =~ column.sql_type
91
+ PostgreSQLColumn.range_to_string(value)
92
+ when NilClass
93
+ if column.array && array_member
94
+ 'NULL'
95
+ elsif column.array
96
+ value
97
+ else
98
+ super(value, column)
99
+ end
100
+ when Array
101
+ case column.sql_type
102
+ when 'point' then PostgreSQLColumn.point_to_string(value)
103
+ when 'json' then PostgreSQLColumn.json_to_string(value)
104
+ else
105
+ return super(value, column) unless column.array
106
+ PostgreSQLColumn.array_to_string(value, column, self)
107
+ end
108
+ when String
109
+ return super(value, column) unless 'bytea' == column.sql_type
110
+ { :value => value, :format => 1 }
111
+ when Hash
112
+ case column.sql_type
113
+ when 'hstore' then PostgreSQLColumn.hstore_to_string(value, array_member)
114
+ when 'json' then PostgreSQLColumn.json_to_string(value)
115
+ else super(value, column)
116
+ end
117
+ when IPAddr
118
+ return super(value, column) unless ['inet','cidr'].include? column.sql_type
119
+ PostgreSQLColumn.cidr_to_string(value)
120
+ else
121
+ super(value, column)
122
+ end
123
+ end
124
+
125
+ # Quotes strings for use in SQL input.
126
+ def quote_string(s) #:nodoc:
127
+ @connection.escape(s)
128
+ end
129
+
130
+ # Checks the following cases:
131
+ #
132
+ # - table_name
133
+ # - "table.name"
134
+ # - schema_name.table_name
135
+ # - schema_name."table.name"
136
+ # - "schema.name".table_name
137
+ # - "schema.name"."table.name"
138
+ def quote_table_name(name)
139
+ schema, name_part = extract_pg_identifier_from_name(name.to_s)
140
+
141
+ unless name_part
142
+ quote_column_name(schema)
143
+ else
144
+ table_name, name_part = extract_pg_identifier_from_name(name_part)
145
+ "#{quote_column_name(schema)}.#{quote_column_name(table_name)}"
146
+ end
147
+ end
148
+
149
+ def quote_table_name_for_assignment(table, attr)
150
+ quote_column_name(attr)
151
+ end
152
+
153
+ # Quotes column names for use in SQL queries.
154
+ def quote_column_name(name) #:nodoc:
155
+ PGconn.quote_ident(name.to_s)
156
+ end
157
+
158
+ # Quote date/time values for use in SQL input. Includes microseconds
159
+ # if the value is a Time responding to usec.
160
+ def quoted_date(value) #:nodoc:
161
+ result = super
162
+ if value.acts_like?(:time) && value.respond_to?(:usec)
163
+ result = "#{result}.#{sprintf("%06d", value.usec)}"
164
+ end
165
+
166
+ if value.year < 0
167
+ result = result.sub(/^-/, "") + " BC"
168
+ end
169
+ result
170
+ end
171
+
172
+ # Does not quote function default values for UUID columns
173
+ def quote_default_value(value, column) #:nodoc:
174
+ if column.type == :uuid && value =~ /\(\)/
175
+ value
176
+ else
177
+ quote(value, column)
178
+ end
179
+ end
180
+ end
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,30 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ class PostgreSQLAdapter < AbstractAdapter
4
+ module ReferentialIntegrity
5
+ def supports_disable_referential_integrity? #:nodoc:
6
+ true
7
+ end
8
+
9
+ def disable_referential_integrity #:nodoc:
10
+ if supports_disable_referential_integrity?
11
+ begin
12
+ execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
13
+ rescue
14
+ execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER USER" }.join(";"))
15
+ end
16
+ end
17
+ yield
18
+ ensure
19
+ if supports_disable_referential_integrity?
20
+ begin
21
+ execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";"))
22
+ rescue
23
+ execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER USER" }.join(";"))
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end