From a5e2f6cd28710e4a81e850a77d5d0833cb1379de Mon Sep 17 00:00:00 2001
From: Jeremy Kemper <jeremy@bitsweat.net>
Date: Mon, 26 Sep 2005 20:29:01 +0000
Subject:  r3590@asus:  jeremy | 2005-09-26 17:00:53 -0700  Correct and
 optimize PostgreSQL bytea escaping.  This is a blend of four patches, each
 providing a bit to the solution.

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@2347 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
---
 .../connection_adapters/postgresql_adapter.rb      | 50 +++++++++++++++++++---
 1 file changed, 43 insertions(+), 7 deletions(-)

diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 254b1a04a8..037f5f6869 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -79,7 +79,7 @@ module ActiveRecord
 
       def quote(value, column = nil)
         if value.class == String && column && column.type == :binary
-          quote_bytea(value)
+          "'#{escape_bytea(value)}'"
         else
           super
         end
@@ -266,16 +266,52 @@ module ActiveRecord
           return rows
         end
 
-        def quote_bytea(s)
-          "'#{escape_bytea(s)}'"
-        end
-
         def escape_bytea(s)
-          s.gsub(/\\/) { '\\\\\\\\' }.gsub(/[^\\]/) { |c| sprintf('\\\\%03o', c[0].to_i) } unless s.nil?
+          if PGconn.respond_to? :escape_bytea
+            self.class.send(:define_method, :escape_bytea) do |s|
+              PGconn.escape_bytea(s) if s
+            end
+          else
+            self.class.send(:define_method, :escape_bytea) do |s|
+              if s
+                result = ''
+                s.each_byte { |c| result << sprintf('\\\\%03o', c) }
+                result
+              end
+            end
+          end
+          escape_bytea(s)
         end
 
         def unescape_bytea(s)
-          s.gsub(/\\([0-9][0-9][0-9])/) { $1.oct.chr }.gsub(/\\\\/) { '\\' } unless s.nil?
+          if PGconn.respond_to? :unescape_bytea
+            self.class.send(:define_method, :unescape_bytea) do |s|
+              PGconn.unescape_bytea(s) if s
+            end
+          else
+            self.class.send(:define_method, :unescape_bytea) do |s|
+              if s
+                result = ''
+                i, max = 0, s.size
+                while i < max
+                  char = s[i]
+                  if char == ?\\
+                    if s[i+1] == ?\\
+                      char = ?\\
+                      i += 1
+                    else
+                      char = s[i+1..i+3].oct
+                      i += 3
+                    end
+                  end
+                  result << char
+                  i += 1
+                end
+                result
+              end
+            end
+          end
+          unescape_bytea(s)
         end
         
         # Query a table's column names, default values, and types.
-- 
cgit v1.2.3