activerecord 1.4.0 → 1.5.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.
- data/CHANGELOG +98 -0
- data/install.rb +1 -0
- data/lib/active_record.rb +1 -0
- data/lib/active_record/acts/list.rb +19 -16
- data/lib/active_record/associations.rb +164 -164
- data/lib/active_record/associations/association_collection.rb +44 -71
- data/lib/active_record/associations/association_proxy.rb +76 -0
- data/lib/active_record/associations/belongs_to_association.rb +74 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +34 -21
- data/lib/active_record/associations/has_many_association.rb +34 -30
- data/lib/active_record/associations/has_one_association.rb +48 -0
- data/lib/active_record/base.rb +62 -18
- data/lib/active_record/callbacks.rb +17 -8
- data/lib/active_record/connection_adapters/abstract_adapter.rb +11 -10
- data/lib/active_record/connection_adapters/mysql_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +29 -1
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +94 -73
- data/lib/active_record/deprecated_associations.rb +46 -8
- data/lib/active_record/fixtures.rb +1 -1
- data/lib/active_record/observer.rb +5 -1
- data/lib/active_record/support/binding_of_caller.rb +72 -68
- data/lib/active_record/support/breakpoint.rb +526 -524
- data/lib/active_record/support/class_inheritable_attributes.rb +105 -29
- data/lib/active_record/support/core_ext.rb +1 -0
- data/lib/active_record/support/core_ext/hash.rb +5 -0
- data/lib/active_record/support/core_ext/hash/keys.rb +35 -0
- data/lib/active_record/support/core_ext/numeric.rb +7 -0
- data/lib/active_record/support/core_ext/numeric/bytes.rb +33 -0
- data/lib/active_record/support/core_ext/numeric/time.rb +59 -0
- data/lib/active_record/support/core_ext/string.rb +5 -0
- data/lib/active_record/support/core_ext/string/inflections.rb +41 -0
- data/lib/active_record/support/dependencies.rb +1 -14
- data/lib/active_record/support/inflector.rb +6 -6
- data/lib/active_record/support/misc.rb +0 -24
- data/lib/active_record/validations.rb +34 -1
- data/lib/active_record/vendor/mysql411.rb +305 -0
- data/rakefile +11 -2
- data/test/abstract_unit.rb +1 -2
- data/test/associations_test.rb +234 -23
- data/test/base_test.rb +50 -1
- data/test/callbacks_test.rb +16 -0
- data/test/connections/native_mysql/connection.rb +2 -2
- data/test/connections/native_sqlite3/connection.rb +34 -0
- data/test/deprecated_associations_test.rb +36 -2
- data/test/fixtures/company.rb +2 -0
- data/test/fixtures/computer.rb +3 -0
- data/test/fixtures/computers.yml +3 -0
- data/test/fixtures/db_definitions/db2.sql +5 -0
- data/test/fixtures/db_definitions/mysql.sql +5 -0
- data/test/fixtures/db_definitions/postgresql.sql +5 -0
- data/test/fixtures/db_definitions/sqlite.sql +5 -0
- data/test/fixtures/db_definitions/sqlserver.sql +5 -1
- data/test/fixtures/fixture_database.sqlite +0 -0
- data/test/validations_test.rb +21 -0
- metadata +22 -2
@@ -2,37 +2,17 @@ module ActiveRecord
|
|
2
2
|
module Associations
|
3
3
|
class HasManyAssociation < AssociationCollection #:nodoc:
|
4
4
|
def initialize(owner, association_name, association_class_name, association_class_primary_key_name, options)
|
5
|
-
super
|
5
|
+
super
|
6
6
|
@conditions = sanitize_sql(options[:conditions])
|
7
7
|
|
8
|
-
|
9
|
-
@finder_sql = interpolate_sql(options[:finder_sql])
|
10
|
-
else
|
11
|
-
@finder_sql = "#{@association_class_primary_key_name} = #{@owner.quoted_id}"
|
12
|
-
@finder_sql << " AND #{@conditions}" if @conditions
|
13
|
-
end
|
14
|
-
|
15
|
-
if options[:counter_sql]
|
16
|
-
@counter_sql = interpolate_sql(options[:counter_sql])
|
17
|
-
elsif options[:finder_sql]
|
18
|
-
options[:counter_sql] = options[:finder_sql].gsub(/SELECT (.*) FROM/i, "SELECT COUNT(*) FROM")
|
19
|
-
@counter_sql = interpolate_sql(options[:counter_sql])
|
20
|
-
else
|
21
|
-
@counter_sql = "#{@association_class_primary_key_name} = #{@owner.quoted_id}#{@conditions ? " AND " + interpolate_sql(@conditions) : ""}"
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def create(attributes = {})
|
26
|
-
# Can't use Base.create since the foreign key may be a protected attribute.
|
27
|
-
record = build(attributes)
|
28
|
-
record.save
|
29
|
-
@collection << record if loaded?
|
30
|
-
record
|
8
|
+
construct_sql
|
31
9
|
end
|
32
10
|
|
33
11
|
def build(attributes = {})
|
12
|
+
load_target
|
34
13
|
record = @association_class.new(attributes)
|
35
|
-
record[@association_class_primary_key_name] = @owner.id
|
14
|
+
record[@association_class_primary_key_name] = @owner.id unless @owner.new_record?
|
15
|
+
@target << record
|
36
16
|
record
|
37
17
|
end
|
38
18
|
|
@@ -77,10 +57,10 @@ module ActiveRecord
|
|
77
57
|
elsif @options[:finder_sql]
|
78
58
|
if ids.size == 1
|
79
59
|
id = ids.first
|
80
|
-
record =
|
60
|
+
record = load_target.detect { |record| id == record.id }
|
81
61
|
expects_array? ? [record] : record
|
82
62
|
else
|
83
|
-
|
63
|
+
load_target.select { |record| ids.include?(record.id) }
|
84
64
|
end
|
85
65
|
|
86
66
|
# Otherwise, delegate to association class with conditions.
|
@@ -94,12 +74,12 @@ module ActiveRecord
|
|
94
74
|
# method calls may be chained.
|
95
75
|
def clear
|
96
76
|
@association_class.update_all("#{@association_class_primary_key_name} = NULL", "#{@association_class_primary_key_name} = #{@owner.quoted_id}")
|
97
|
-
@
|
77
|
+
@target = []
|
98
78
|
self
|
99
79
|
end
|
100
80
|
|
101
81
|
protected
|
102
|
-
def
|
82
|
+
def find_target
|
103
83
|
find_all
|
104
84
|
end
|
105
85
|
|
@@ -122,7 +102,8 @@ module ActiveRecord
|
|
122
102
|
end
|
123
103
|
|
124
104
|
def insert_record(record)
|
125
|
-
record
|
105
|
+
record[@association_class_primary_key_name] = @owner.id
|
106
|
+
record.save
|
126
107
|
end
|
127
108
|
|
128
109
|
def delete_records(records)
|
@@ -132,6 +113,29 @@ module ActiveRecord
|
|
132
113
|
"#{@association_class_primary_key_name} = #{@owner.quoted_id} AND #{@association_class.primary_key} IN (#{ids})"
|
133
114
|
)
|
134
115
|
end
|
116
|
+
|
117
|
+
def target_obsolete?
|
118
|
+
false
|
119
|
+
end
|
120
|
+
|
121
|
+
def construct_sql
|
122
|
+
if @options[:finder_sql]
|
123
|
+
@finder_sql = interpolate_sql(@options[:finder_sql])
|
124
|
+
else
|
125
|
+
@finder_sql = "#{@association_class_primary_key_name} = #{@owner.quoted_id}"
|
126
|
+
@finder_sql << " AND #{interpolate_sql(@conditions)}" if @conditions
|
127
|
+
end
|
128
|
+
|
129
|
+
if @options[:counter_sql]
|
130
|
+
@counter_sql = interpolate_sql(@options[:counter_sql])
|
131
|
+
elsif @options[:finder_sql]
|
132
|
+
@options[:counter_sql] = @options[:finder_sql].gsub(/SELECT (.*) FROM/i, "SELECT COUNT(*) FROM")
|
133
|
+
@counter_sql = interpolate_sql(@options[:counter_sql])
|
134
|
+
else
|
135
|
+
@counter_sql = "#{@association_class_primary_key_name} = #{@owner.quoted_id}"
|
136
|
+
@counter_sql << " AND #{interpolate_sql(@conditions)}" if @conditions
|
137
|
+
end
|
138
|
+
end
|
135
139
|
end
|
136
140
|
end
|
137
141
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Associations
|
3
|
+
class HasOneAssociation < BelongsToAssociation #:nodoc:
|
4
|
+
def initialize(owner, association_name, association_class_name, association_class_primary_key_name, options)
|
5
|
+
super
|
6
|
+
|
7
|
+
construct_sql
|
8
|
+
end
|
9
|
+
|
10
|
+
def replace(obj, dont_save = false)
|
11
|
+
load_target
|
12
|
+
unless @target.nil?
|
13
|
+
@target[@association_class_primary_key_name] = nil
|
14
|
+
@target.save unless @owner.new_record?
|
15
|
+
end
|
16
|
+
|
17
|
+
if obj.nil?
|
18
|
+
@target = nil
|
19
|
+
else
|
20
|
+
raise_on_type_mismatch(obj)
|
21
|
+
|
22
|
+
obj[@association_class_primary_key_name] = @owner.id unless @owner.new_record?
|
23
|
+
@target = obj
|
24
|
+
end
|
25
|
+
|
26
|
+
@loaded = true
|
27
|
+
unless @owner.new_record? or obj.nil? or dont_save
|
28
|
+
return (obj.save ? obj : false)
|
29
|
+
else
|
30
|
+
return obj
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
def find_target
|
36
|
+
@association_class.find_first(@finder_sql, @options[:order])
|
37
|
+
end
|
38
|
+
|
39
|
+
def target_obsolete?
|
40
|
+
false
|
41
|
+
end
|
42
|
+
|
43
|
+
def construct_sql
|
44
|
+
@finder_sql = "#{@association_class_primary_key_name} = #{@owner.quoted_id}#{@options[:conditions] ? " AND " + @options[:conditions] : ""}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/active_record/base.rb
CHANGED
@@ -789,7 +789,6 @@ module ActiveRecord #:nodoc:
|
|
789
789
|
# * A record does exist: Updates the record with values matching those of the object attributes.
|
790
790
|
def save
|
791
791
|
create_or_update
|
792
|
-
return true
|
793
792
|
end
|
794
793
|
|
795
794
|
# Deletes the record in the database and freezes this instance to reflect that no changes should
|
@@ -808,17 +807,7 @@ module ActiveRecord #:nodoc:
|
|
808
807
|
|
809
808
|
# Returns a clone of the record that hasn't been assigned an id yet and is treated as a new record.
|
810
809
|
def clone
|
811
|
-
|
812
|
-
|
813
|
-
self.attribute_names.each do |name|
|
814
|
-
begin
|
815
|
-
attr[name] = read_attribute(name).clone
|
816
|
-
rescue TypeError
|
817
|
-
attr[name] = read_attribute(name)
|
818
|
-
end
|
819
|
-
end
|
820
|
-
|
821
|
-
cloned_record = self.class.new(attr)
|
810
|
+
cloned_record = self.class.new(self.attributes)
|
822
811
|
cloned_record.instance_variable_set "@new_record", true
|
823
812
|
cloned_record.id = nil
|
824
813
|
cloned_record
|
@@ -829,7 +818,7 @@ module ActiveRecord #:nodoc:
|
|
829
818
|
# doesn't get subjected to validation checks. Hence, attributes can be updated even if the full object isn't valid.
|
830
819
|
def update_attribute(name, value)
|
831
820
|
self[name] = value
|
832
|
-
|
821
|
+
save
|
833
822
|
end
|
834
823
|
|
835
824
|
# Updates all the attributes in from the passed hash and saves the record. If the object is invalid, the saving will
|
@@ -839,17 +828,59 @@ module ActiveRecord #:nodoc:
|
|
839
828
|
return save
|
840
829
|
end
|
841
830
|
|
831
|
+
# Initializes the +attribute+ to zero if nil and adds one. Only makes sense for number-based attributes. Returns self.
|
832
|
+
def increment(attribute)
|
833
|
+
self[attribute] ||= 0
|
834
|
+
self[attribute] += 1
|
835
|
+
self
|
836
|
+
end
|
837
|
+
|
838
|
+
# Increments the +attribute+ and saves the record.
|
839
|
+
def increment!(attribute)
|
840
|
+
increment(attribute).update_attribute(attribute, self[attribute])
|
841
|
+
end
|
842
|
+
|
843
|
+
# Initializes the +attribute+ to zero if nil and subtracts one. Only makes sense for number-based attributes. Returns self.
|
844
|
+
def decrement(attribute)
|
845
|
+
self[attribute] ||= 0
|
846
|
+
self[attribute] -= 1
|
847
|
+
self
|
848
|
+
end
|
849
|
+
|
850
|
+
# Decrements the +attribute+ and saves the record.
|
851
|
+
def decrement!(attribute)
|
852
|
+
decrement(attribute).update_attribute(attribute, self[attribute])
|
853
|
+
end
|
854
|
+
|
855
|
+
# Turns an +attribute+ that's currently true into false and vice versa. Returns self.
|
856
|
+
def toggle(attribute)
|
857
|
+
self[attribute] = quote(!send("#{attribute}?", column_for_attribute(attribute)))
|
858
|
+
self
|
859
|
+
end
|
860
|
+
|
861
|
+
# Toggles the +attribute+ and saves the record.
|
862
|
+
def toggle!(attribute)
|
863
|
+
toggle(attribute).update_attribute(attribute, self[attribute])
|
864
|
+
end
|
865
|
+
|
866
|
+
# Reloads the attributes of this object from the database.
|
867
|
+
def reload
|
868
|
+
clear_association_cache
|
869
|
+
@attributes.update(self.class.find(self.id).instance_variable_get('@attributes'))
|
870
|
+
return self
|
871
|
+
end
|
872
|
+
|
842
873
|
# Returns the value of attribute identified by <tt>attr_name</tt> after it has been type cast (for example,
|
843
874
|
# "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
|
844
875
|
# (Alias for the protected read_attribute method).
|
845
876
|
def [](attr_name)
|
846
|
-
read_attribute(attr_name)
|
877
|
+
read_attribute(attr_name.to_s)
|
847
878
|
end
|
848
879
|
|
849
880
|
# Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
|
850
881
|
# (Alias for the protected write_attribute method).
|
851
882
|
def []= (attr_name, value)
|
852
|
-
write_attribute(attr_name, value)
|
883
|
+
write_attribute(attr_name.to_s, value)
|
853
884
|
end
|
854
885
|
|
855
886
|
# Allows you to set all the attributes at once by passing in a hash with keys
|
@@ -867,6 +898,18 @@ module ActiveRecord #:nodoc:
|
|
867
898
|
assign_multiparameter_attributes(multi_parameter_attributes)
|
868
899
|
end
|
869
900
|
|
901
|
+
# Returns a hash of all the attributes with their names as keys and clones of their objects as values.
|
902
|
+
def attributes
|
903
|
+
self.attribute_names.inject({}) do |attributes, name|
|
904
|
+
begin
|
905
|
+
attributes[name] = read_attribute(name).clone
|
906
|
+
rescue TypeError
|
907
|
+
attributes[name] = read_attribute(name)
|
908
|
+
end
|
909
|
+
attributes
|
910
|
+
end
|
911
|
+
end
|
912
|
+
|
870
913
|
# Returns true if the specified +attribute+ has been set by the user or by a database load and is neither
|
871
914
|
# nil nor empty? (the latter only applies to objects that responds to empty?, most notably Strings).
|
872
915
|
def attribute_present?(attribute)
|
@@ -881,12 +924,12 @@ module ActiveRecord #:nodoc:
|
|
881
924
|
|
882
925
|
# Returns the column object for the named attribute.
|
883
926
|
def column_for_attribute(name)
|
884
|
-
self.class.columns_hash[name]
|
927
|
+
self.class.columns_hash[name.to_s]
|
885
928
|
end
|
886
929
|
|
887
|
-
# Returns true if the +comparison_object+ is of the same type and has the same id.
|
930
|
+
# Returns true if the +comparison_object+ is the same object, or is of the same type and has the same id.
|
888
931
|
def ==(comparison_object)
|
889
|
-
comparison_object.instance_of?(self.class)
|
932
|
+
comparison_object.equal?(self) or (comparison_object.instance_of?(self.class) and comparison_object.id == id)
|
890
933
|
end
|
891
934
|
|
892
935
|
# Delegates to ==
|
@@ -912,6 +955,7 @@ module ActiveRecord #:nodoc:
|
|
912
955
|
private
|
913
956
|
def create_or_update
|
914
957
|
if new_record? then create else update end
|
958
|
+
return true
|
915
959
|
end
|
916
960
|
|
917
961
|
# Updates the associated record with values matching those of the instant attributes.
|
@@ -153,6 +153,12 @@ module ActiveRecord
|
|
153
153
|
# to implement a simple performance constraint (50% more speed on a simple test case). Unlike all the other callbacks, after_find and
|
154
154
|
# after_initialize can only be declared using an explicit implementation. So using the inheritable callback queue for after_find and
|
155
155
|
# after_initialize won't work.
|
156
|
+
#
|
157
|
+
# == Cancelling callbacks
|
158
|
+
#
|
159
|
+
# If a before_* callback returns false, all the later callbacks and the associated action are cancelled. If an after_* callback returns
|
160
|
+
# false, all the later callbacks are cancelled. Callbacks are generally run in the order they are defined, with the exception of callbacks
|
161
|
+
# defined as methods on the model, which are called last.
|
156
162
|
module Callbacks
|
157
163
|
CALLBACKS = %w(
|
158
164
|
after_find after_initialize before_save after_save before_create after_create before_update after_update before_validation
|
@@ -227,7 +233,7 @@ module ActiveRecord
|
|
227
233
|
# Is called _after_ Base.save (regardless of whether it's a create or update save).
|
228
234
|
def after_save() end
|
229
235
|
def create_or_update_with_callbacks #:nodoc:
|
230
|
-
callback(:before_save)
|
236
|
+
return false if callback(:before_save) == false
|
231
237
|
result = create_or_update_without_callbacks
|
232
238
|
callback(:after_save)
|
233
239
|
result
|
@@ -239,7 +245,7 @@ module ActiveRecord
|
|
239
245
|
# Is called _after_ Base.save on new objects that haven't been saved yet (no record exists).
|
240
246
|
def after_create() end
|
241
247
|
def create_with_callbacks #:nodoc:
|
242
|
-
callback(:before_create)
|
248
|
+
return false if callback(:before_create) == false
|
243
249
|
result = create_without_callbacks
|
244
250
|
callback(:after_create)
|
245
251
|
result
|
@@ -252,7 +258,7 @@ module ActiveRecord
|
|
252
258
|
def after_update() end
|
253
259
|
|
254
260
|
def update_with_callbacks #:nodoc:
|
255
|
-
callback(:before_update)
|
261
|
+
return false if callback(:before_update) == false
|
256
262
|
result = update_without_callbacks
|
257
263
|
callback(:after_update)
|
258
264
|
result
|
@@ -281,8 +287,9 @@ module ActiveRecord
|
|
281
287
|
def after_validation_on_update() end
|
282
288
|
|
283
289
|
def valid_with_callbacks #:nodoc:
|
284
|
-
callback(:before_validation)
|
285
|
-
if new_record? then callback(:before_validation_on_create) else callback(:before_validation_on_update) end
|
290
|
+
return false if callback(:before_validation) == false
|
291
|
+
if new_record? then result = callback(:before_validation_on_create) else result = callback(:before_validation_on_update) end
|
292
|
+
return false if result == false
|
286
293
|
|
287
294
|
result = valid_without_callbacks
|
288
295
|
|
@@ -298,7 +305,7 @@ module ActiveRecord
|
|
298
305
|
# Is called _after_ Base.destroy (and all the attributes have been frozen).
|
299
306
|
def after_destroy() end
|
300
307
|
def destroy_with_callbacks #:nodoc:
|
301
|
-
callback(:before_destroy)
|
308
|
+
return false if callback(:before_destroy) == false
|
302
309
|
result = destroy_without_callbacks
|
303
310
|
callback(:after_destroy)
|
304
311
|
result
|
@@ -307,7 +314,7 @@ module ActiveRecord
|
|
307
314
|
private
|
308
315
|
def callback(method)
|
309
316
|
callbacks_for(method).each do |callback|
|
310
|
-
case callback
|
317
|
+
result = case callback
|
311
318
|
when Symbol
|
312
319
|
self.send(callback)
|
313
320
|
when String
|
@@ -321,9 +328,11 @@ module ActiveRecord
|
|
321
328
|
raise ActiveRecordError, "Callbacks must be a symbol denoting the method to call, a string to be evaluated, a block to be invoked, or an object responding to the callback method."
|
322
329
|
end
|
323
330
|
end
|
331
|
+
return false if result == false
|
324
332
|
end
|
325
333
|
|
326
334
|
invoke_and_notify(method)
|
335
|
+
true
|
327
336
|
end
|
328
337
|
|
329
338
|
def callbacks_for(method)
|
@@ -340,4 +349,4 @@ module ActiveRecord
|
|
340
349
|
self.class.notify_observers(method, self)
|
341
350
|
end
|
342
351
|
end
|
343
|
-
end
|
352
|
+
end
|
@@ -89,7 +89,7 @@ module ActiveRecord
|
|
89
89
|
raise AdapterNotSpecified, "#{spec} database is not configured"
|
90
90
|
end
|
91
91
|
else
|
92
|
-
spec =
|
92
|
+
spec = spec.symbolize_keys
|
93
93
|
unless spec.key?(:adapter) then raise AdapterNotSpecified, "database configuration does not specify adapter" end
|
94
94
|
adapter_method = "#{spec[:adapter]}_connection"
|
95
95
|
unless respond_to?(adapter_method) then raise AdapterNotFound, "database configuration specifies nonexistent #{spec[:adapter]} adapter" end
|
@@ -152,10 +152,7 @@ module ActiveRecord
|
|
152
152
|
|
153
153
|
# Converts all strings in a hash to symbols.
|
154
154
|
def self.symbolize_strings_in_hash(hash)
|
155
|
-
hash.
|
156
|
-
hash_with_symbolized_strings[pair.first.to_sym] = pair.last
|
157
|
-
hash_with_symbolized_strings
|
158
|
-
end
|
155
|
+
hash.symbolize_keys
|
159
156
|
end
|
160
157
|
end
|
161
158
|
|
@@ -356,7 +353,7 @@ module ActiveRecord
|
|
356
353
|
end
|
357
354
|
|
358
355
|
def quote_column_name(name)
|
359
|
-
|
356
|
+
name
|
360
357
|
end
|
361
358
|
|
362
359
|
# Returns a string of the CREATE TABLE SQL statements for recreating the entire structure of the database.
|
@@ -367,16 +364,20 @@ module ActiveRecord
|
|
367
364
|
end
|
368
365
|
|
369
366
|
protected
|
370
|
-
def log(sql, name, connection
|
367
|
+
def log(sql, name, connection = nil)
|
368
|
+
connection ||= @connection
|
371
369
|
begin
|
372
370
|
if @logger.nil? || @logger.level > Logger::INFO
|
373
|
-
|
374
|
-
|
371
|
+
yield connection
|
372
|
+
elsif block_given?
|
375
373
|
result = nil
|
376
|
-
bm = measure { result =
|
374
|
+
bm = measure { result = yield connection }
|
377
375
|
@runtime += bm.real
|
378
376
|
log_info(sql, name, bm.real)
|
379
377
|
result
|
378
|
+
else
|
379
|
+
log_info(sql, name, 0)
|
380
|
+
nil
|
380
381
|
end
|
381
382
|
rescue => e
|
382
383
|
log_info("#{e.message}: #{sql}", name, 0)
|
@@ -37,6 +37,7 @@ module ActiveRecord
|
|
37
37
|
end
|
38
38
|
|
39
39
|
module ConnectionAdapters
|
40
|
+
|
40
41
|
class PostgreSQLAdapter < AbstractAdapter # :nodoc:
|
41
42
|
def select_all(sql, name = nil)
|
42
43
|
select(sql, name)
|
@@ -76,6 +77,14 @@ module ActiveRecord
|
|
76
77
|
def commit_db_transaction() execute "COMMIT" end
|
77
78
|
def rollback_db_transaction() execute "ROLLBACK" end
|
78
79
|
|
80
|
+
def quote(value, column = nil)
|
81
|
+
if value.class == String && column && column.type == :binary
|
82
|
+
quote_bytea(value)
|
83
|
+
else
|
84
|
+
super
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
79
88
|
def quote_column_name(name)
|
80
89
|
return "\"#{name}\""
|
81
90
|
end
|
@@ -96,13 +105,31 @@ module ActiveRecord
|
|
96
105
|
fields = res.fields
|
97
106
|
results.each do |row|
|
98
107
|
hashed_row = {}
|
99
|
-
row.each_index
|
108
|
+
row.each_index do |cel_index|
|
109
|
+
column = row[cel_index]
|
110
|
+
if res.type(cel_index) == 17 # type oid for bytea
|
111
|
+
column = unescape_bytea(column)
|
112
|
+
end
|
113
|
+
hashed_row[fields[cel_index]] = column
|
114
|
+
end
|
100
115
|
rows << hashed_row
|
101
116
|
end
|
102
117
|
end
|
103
118
|
return rows
|
104
119
|
end
|
105
120
|
|
121
|
+
def quote_bytea(s)
|
122
|
+
"'#{escape_bytea(s)}'"
|
123
|
+
end
|
124
|
+
|
125
|
+
def escape_bytea(s)
|
126
|
+
s.gsub(/\\/) { '\\\\\\\\' }.gsub(/[^\\]/) { |c| sprintf('\\\\%03o', c[0].to_i) }
|
127
|
+
end
|
128
|
+
|
129
|
+
def unescape_bytea(s)
|
130
|
+
s.gsub(/\\([0-9][0-9][0-9])/) { $1.oct.chr }.gsub(/\\\\/) { '\\' }
|
131
|
+
end
|
132
|
+
|
106
133
|
def split_table_schema(table_name)
|
107
134
|
schema_split = table_name.split('.')
|
108
135
|
schema_name = "public"
|
@@ -141,6 +168,7 @@ module ActiveRecord
|
|
141
168
|
when 'numeric', 'real', 'money' then 'float'
|
142
169
|
when 'character varying', 'interval' then 'string'
|
143
170
|
when 'timestamp without time zone' then 'datetime'
|
171
|
+
when 'bytea' then 'binary'
|
144
172
|
else field_type
|
145
173
|
end
|
146
174
|
|