activerecord 3.0.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

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