aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Majer <adamm@zombino.com>2008-09-19 21:38:39 -0500
committerMichael Koziarski <michael@koziarski.com>2008-10-25 12:54:48 +0200
commit932dffc559ef188eb31d0223116e9da361833488 (patch)
tree3b0afabc3f70537b143b8c468182bcc329bbb4c9
parent5c97d4ff29cfd944da751f01177a3024626d57bb (diff)
downloadrails-932dffc559ef188eb31d0223116e9da361833488.tar.gz
rails-932dffc559ef188eb31d0223116e9da361833488.tar.bz2
rails-932dffc559ef188eb31d0223116e9da361833488.zip
Fix binary data corruption bug in PostgreSQL adaptor
1. Move the binary escape/unescape from column to the driver - we should store binary data AR just like most other adaptors 2. check to make sure we only unescape bytea data PGresult.ftype( column ) == 17 that is passed to us in escaped format PGresult.fformat( column ) == 0 Signed-off-by: Michael Koziarski <michael@koziarski.com> [#1063 state:committed]
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb147
1 files changed, 79 insertions, 68 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index bebab5d05d..ccde8a63a9 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -68,72 +68,6 @@ module ActiveRecord
super
end
- # Escapes binary strings for bytea input to the database.
- def self.string_to_binary(value)
- if PGconn.respond_to?(:escape_bytea)
- self.class.module_eval do
- define_method(:string_to_binary) do |value|
- PGconn.escape_bytea(value) if value
- end
- end
- else
- self.class.module_eval do
- define_method(:string_to_binary) do |value|
- if value
- result = ''
- value.each_byte { |c| result << sprintf('\\\\%03o', c) }
- result
- end
- end
- end
- end
- self.class.string_to_binary(value)
- end
-
- # Unescapes bytea output from a database to the binary string it represents.
- def self.binary_to_string(value)
- # In each case, check if the value actually is escaped PostgreSQL bytea output
- # or an unescaped Active Record attribute that was just written.
- if PGconn.respond_to?(:unescape_bytea)
- self.class.module_eval do
- define_method(:binary_to_string) do |value|
- if value =~ /\\\d{3}/
- PGconn.unescape_bytea(value)
- else
- value
- end
- end
- end
- else
- self.class.module_eval do
- define_method(:binary_to_string) do |value|
- if value =~ /\\\d{3}/
- result = ''
- i, max = 0, value.size
- while i < max
- char = value[i]
- if char == ?\\
- if value[i+1] == ?\\
- char = ?\\
- i += 1
- else
- char = value[i+1..i+3].oct
- i += 3
- end
- end
- result << char
- i += 1
- end
- result
- else
- value
- end
- end
- end
- end
- self.class.binary_to_string(value)
- end
-
# Maps PostgreSQL-specific data types to logical Rails types.
def simplified_type(field_type)
case field_type
@@ -347,10 +281,78 @@ module ActiveRecord
# QUOTING ==================================================
+ # Escapes binary strings for bytea input to the database.
+ def escape_bytea(value)
+ if PGconn.respond_to?(:escape_bytea)
+ self.class.instance_eval do
+ define_method(:escape_bytea) do |value|
+ PGconn.escape_bytea(value) if value
+ end
+ end
+ else
+ self.class.instance_eval do
+ define_method(:escape_bytea) do |value|
+ if value
+ result = ''
+ value.each_byte { |c| result << sprintf('\\\\%03o', c) }
+ result
+ end
+ end
+ end
+ end
+ escape_bytea(value)
+ end
+
+ # Unescapes bytea output from a database to the binary string it represents.
+ # NOTE: This is NOT an inverse of escape_bytea! This is only to be used
+ # on escaped binary output from database drive.
+ def unescape_bytea(value)
+ # In each case, check if the value actually is escaped PostgreSQL bytea output
+ # or an unescaped Active Record attribute that was just written.
+ if PGconn.respond_to?(:unescape_bytea)
+ self.class.instance_eval do
+ define_method(:unescape_bytea) do |value|
+ if value =~ /\\\d{3}/
+ PGconn.unescape_bytea(value)
+ else
+ value
+ end
+ end
+ end
+ else
+ self.class.instance_eval do
+ define_method(:unescape_bytea) do |value|
+ if value =~ /\\\d{3}/
+ result = ''
+ i, max = 0, value.size
+ while i < max
+ char = value[i]
+ if char == ?\\
+ if value[i+1] == ?\\
+ char = ?\\
+ i += 1
+ else
+ char = value[i+1..i+3].oct
+ i += 3
+ end
+ end
+ result << char
+ i += 1
+ end
+ result
+ else
+ value
+ end
+ end
+ end
+ end
+ unescape_bytea(value)
+ end
+
# Quotes PostgreSQL-specific data types for SQL input.
def quote(value, column = nil) #:nodoc:
if value.kind_of?(String) && column && column.type == :binary
- "#{quoted_string_prefix}'#{column.class.string_to_binary(value)}'"
+ "#{quoted_string_prefix}'#{escape_bytea(value)}'"
elsif value.kind_of?(String) && column && column.sql_type =~ /^xml$/
"xml '#{quote_string(value)}'"
elsif value.kind_of?(Numeric) && column && column.sql_type =~ /^money$/
@@ -463,11 +465,20 @@ module ActiveRecord
# create a 2D array representing the result set
def result_as_array(res) #:nodoc:
+ # check if we have any binary column and if they need escaping
+ unescape_col = []
+ for j in 0...res.nfields do
+ # unescape string passed BYTEA field (OID == 17)
+ unescape_col << ( res.fformat(j)==0 and res.ftype(j)==17 )
+ end
+
ary = []
for i in 0...res.ntuples do
ary << []
for j in 0...res.nfields do
- ary[i] << res.getvalue(i,j)
+ data = res.getvalue(i,j)
+ data = unescape_bytea(data) if unescape_col[j] and data.is_a?(String)
+ ary[i] << data
end
end
return ary