problem with bytea and undecorated literals

Peter Eisentraut <[email protected]> Thu, 29 Apr 2010 11:33:07 +0300
Newsgroups gmane.comp.python.db.psycopg.devel
Message-ID <[email protected]>
Consider this example:

create function bar1(x bytea) returns text language sql
  as $$ select 'bytea version'::text $$;  -- I want to call this one.

create function bar1(x text) returns text language sql
  as $$ select 'text version'::text $$;


import psycopg2
from psycopg2 import Binary

conn = psycopg2.connect("dbname=test")
cursor = conn.cursor()

a = Binary('abc')
print cursor.mogrify('SELECT bar1(%s)', [a])
cursor.execute('SELECT bar1(%s)', [a])
print cursor.fetchall()


Output:

SELECT bar1(E'abc')
[('text version',)]   # WRONG


This would work correctly if the substitution of the Binary type created
something like:

SELECT bar1(E'abc'::bytea)


I think this might be a more widespread problem.  A cursory test with
the Date type revealed a similar problem.  PostgreSQL is going to
default unknown literals to text, so adding an explicit type decoration
is advisable.


Another instance of this problem is that array support with types like
these is broken:

create function foo(x bytea[]) returns int language sql
  as $$ select 123 $$;

import psycopg2
from psycopg2 import Binary

conn = psycopg2.connect("dbname=test")
cursor = conn.cursor()

b = [Binary('abc\000\123'), Binary('def')]
cursor.execute('SELECT foo (%s)', [b])
print cursor.fetchall()

psycopg2.ProgrammingError: function foo(text[]) does not exist


For the bytea type, the following patch will fix both of these problems.
But again, this might affect other data types as well.

diff --git a/psycopg/adapter_binary.c b/psycopg/adapter_binary.c
index ba39ebc..599580d 100644
--- a/psycopg/adapter_binary.c
+++ b/psycopg/adapter_binary.c
@@ -157,7 +157,7 @@ binary_quote(binaryObject *self)
         if (len > 0)
             self->buffer = PyString_FromFormat(
                 (self->conn && ((connectionObject*)self->conn)->equote)
-                    ? "E'%s'" : "'%s'" , to);
+                    ? "E'%s'::bytea" : "'%s'::bytea" , to);
         else
             self->buffer = PyString_FromString("''");

(patch is against python2 branch HEAD)