Author: cito
Date: Thu Nov 26 01:33:35 2015
New Revision: 629

Log:
Improve unicode support, particularly for pgdb

The pgdb source object now accepts unicode queries and returns results
decoded with the proper encoding from the connection.
Proper handling of binary objects, particularly accept inner NUL bytes.
The escape/unescape methods now also encode and decode with the proper
encoding; before they had assumed utf8 in Python3 and ascii in Python2.

Also some more whitespace and code cleanup.

Modified:
   trunk/module/TEST_PyGreSQL_classic.py
   trunk/module/TEST_PyGreSQL_classic_connection.py
   trunk/module/TEST_PyGreSQL_classic_dbwrapper.py
   trunk/module/TEST_PyGreSQL_classic_functions.py
   trunk/module/TEST_PyGreSQL_dbapi20.py
   trunk/module/dbapi20.py
   trunk/module/pgdb.py
   trunk/module/pgmodule.c

Modified: trunk/module/TEST_PyGreSQL_classic.py
==============================================================================
--- trunk/module/TEST_PyGreSQL_classic.py       Wed Nov 25 12:48:37 2015        
(r628)
+++ trunk/module/TEST_PyGreSQL_classic.py       Thu Nov 26 01:33:35 2015        
(r629)
@@ -1,4 +1,5 @@
 #! /usr/bin/python
+# -*- coding: utf-8 -*-
 
 from __future__ import print_function
 

Modified: trunk/module/TEST_PyGreSQL_classic_connection.py
==============================================================================
--- trunk/module/TEST_PyGreSQL_classic_connection.py    Wed Nov 25 12:48:37 
2015        (r628)
+++ trunk/module/TEST_PyGreSQL_classic_connection.py    Thu Nov 26 01:33:35 
2015        (r629)
@@ -1531,38 +1531,36 @@
     def testEscapeString(self):
         f = pg.escape_string
         r = f(b'plain')
-        self.assertIsInstance(r, str)
-        self.assertEqual(r, 'plain')
+        self.assertIsInstance(r, bytes)
+        self.assertEqual(r, b'plain')
         r = f(u'plain')
-        self.assertIsInstance(r, str)
-        self.assertEqual(r, 'plain')
+        self.assertIsInstance(r, unicode)
+        self.assertEqual(r, u'plain')
         r = f(u"das is' käse".encode('utf-8'))
-        self.assertIsInstance(r, str)
-        self.assertEqual(r, "das is'' käse")
-        if unicode_strings:
-            r = f("das is' käse")
-            self.assertIsInstance(r, str)
-            self.assertEqual(r, "das is'' käse")
+        self.assertIsInstance(r, bytes)
+        self.assertEqual(r, u"das is'' käse".encode('utf-8'))
+        r = f(u"that's cheesy")
+        self.assertIsInstance(r, unicode)
+        self.assertEqual(r, u"that''s cheesy")
         r = f(r"It's bad to have a \ inside.")
         self.assertEqual(r, r"It''s bad to have a \\ inside.")
 
     def testEscapeBytea(self):
         f = pg.escape_bytea
         r = f(b'plain')
-        self.assertIsInstance(r, str)
-        self.assertEqual(r, 'plain')
+        self.assertIsInstance(r, bytes)
+        self.assertEqual(r, b'plain')
         r = f(u'plain')
-        self.assertIsInstance(r, str)
-        self.assertEqual(r, 'plain')
+        self.assertIsInstance(r, unicode)
+        self.assertEqual(r, u'plain')
         r = f(u"das is' käse".encode('utf-8'))
-        self.assertIsInstance(r, str)
-        self.assertEqual(r, r"das is'' k\\303\\244se")
-        if unicode_strings:
-            r = f("das is' käse")
-            self.assertIsInstance(r, str)
-            self.assertEqual(r, r"das is'' k\\303\\244se")
+        self.assertIsInstance(r, bytes)
+        self.assertEqual(r, b"das is'' k\\\\303\\\\244se")
+        r = f(u"that's cheesy")
+        self.assertIsInstance(r, unicode)
+        self.assertEqual(r, u"that''s cheesy")
         r = f(b'O\x00ps\xff!')
-        self.assertEqual(r, r'O\\000ps\\377!')
+        self.assertEqual(r, b'O\\\\000ps\\\\377!')
 
 
 if __name__ == '__main__':

Modified: trunk/module/TEST_PyGreSQL_classic_dbwrapper.py
==============================================================================
--- trunk/module/TEST_PyGreSQL_classic_dbwrapper.py     Wed Nov 25 12:48:37 
2015        (r628)
+++ trunk/module/TEST_PyGreSQL_classic_dbwrapper.py     Thu Nov 26 01:33:35 
2015        (r629)
@@ -40,6 +40,11 @@
 except NameError:  # Python >= 3.0
     long = int
 
+try:
+    unicode
+except NameError:  # Python >= 3.0
+    unicode = str
+
 windows = os.name == 'nt'
 
 # There is a known a bug in libpq under Windows which can cause
@@ -212,7 +217,7 @@
             '\\x', '').replace('\\', ''), '')
 
     def testMethodUnescapeBytea(self):
-        self.assertEqual(self.db.unescape_bytea(''), '')
+        self.assertEqual(self.db.unescape_bytea(''), b'')
 
     def testMethodQuery(self):
         query = self.db.query
@@ -313,8 +318,18 @@
 
     def testEscapeLiteral(self):
         f = self.db.escape_literal
-        self.assertEqual(f("plain"), "'plain'")
-        self.assertEqual(f("that's k\xe4se"), "'that''s k\xe4se'")
+        r = f(b"plain")
+        self.assertIsInstance(r, bytes)
+        self.assertEqual(r, b"'plain'")
+        r = f(u"plain")
+        self.assertIsInstance(r, unicode)
+        self.assertEqual(r, u"'plain'")
+        r = f(u"that's käse".encode('utf-8'))
+        self.assertIsInstance(r, bytes)
+        self.assertEqual(r, u"'that''s käse'".encode('utf-8'))
+        r = f(u"that's käse")
+        self.assertIsInstance(r, unicode)
+        self.assertEqual(r, u"'that''s käse'")
         self.assertEqual(f(r"It's fine to have a \ inside."),
             r" E'It''s fine to have a \\ inside.'")
         self.assertEqual(f('No "quotes" must be escaped.'),
@@ -322,8 +337,18 @@
 
     def testEscapeIdentifier(self):
         f = self.db.escape_identifier
-        self.assertEqual(f("plain"), '"plain"')
-        self.assertEqual(f("that's k\xe4se"), '"that\'s k\xe4se"')
+        r = f(b"plain")
+        self.assertIsInstance(r, bytes)
+        self.assertEqual(r, b'"plain"')
+        r = f(u"plain")
+        self.assertIsInstance(r, unicode)
+        self.assertEqual(r, u'"plain"')
+        r = f(u"that's käse".encode('utf-8'))
+        self.assertIsInstance(r, bytes)
+        self.assertEqual(r, u'"that\'s käse"'.encode('utf-8'))
+        r = f(u"that's käse")
+        self.assertIsInstance(r, unicode)
+        self.assertEqual(r, u'"that\'s käse"')
         self.assertEqual(f(r"It's fine to have a \ inside."),
             '"It\'s fine to have a \\ inside."')
         self.assertEqual(f('All "quotes" must be escaped.'),
@@ -331,8 +356,18 @@
 
     def testEscapeString(self):
         f = self.db.escape_string
-        self.assertEqual(f("plain"), "plain")
-        self.assertEqual(f("that's k\xe4se"), "that''s k\xe4se")
+        r = f(b"plain")
+        self.assertIsInstance(r, bytes)
+        self.assertEqual(r, b"plain")
+        r = f(u"plain")
+        self.assertIsInstance(r, unicode)
+        self.assertEqual(r, u"plain")
+        r = f(u"that's käse".encode('utf-8'))
+        self.assertIsInstance(r, bytes)
+        self.assertEqual(r, u"that''s käse".encode('utf-8'))
+        r = f(u"that's käse")
+        self.assertIsInstance(r, unicode)
+        self.assertEqual(r, u"that''s käse")
         self.assertEqual(f(r"It's fine to have a \ inside."),
             r"It''s fine to have a \ inside.")
 
@@ -341,38 +376,38 @@
         # note that escape_byte always returns hex output since Pg 9.0,
         # regardless of the bytea_output setting
         r = f(b'plain')
-        self.assertIsInstance(r, str)
-        self.assertEqual(r, r'\x706c61696e')
+        self.assertIsInstance(r, bytes)
+        self.assertEqual(r, b'\\x706c61696e')
         r = f(u'plain')
-        self.assertIsInstance(r, str)
-        self.assertEqual(r, r'\x706c61696e')
+        self.assertIsInstance(r, unicode)
+        self.assertEqual(r, u'\\x706c61696e')
         r = f(u"das is' käse".encode('utf-8'))
-        self.assertIsInstance(r, str)
-        self.assertEqual(r, r'\x64617320697327206bc3a47365')
-        r = f("das is' käse")
-        self.assertIsInstance(r, str)
-        self.assertEqual(r, r'\x64617320697327206bc3a47365')
-        self.assertEqual(f(b'O\x00ps\xff!'), r'\x4f007073ff21')
+        self.assertIsInstance(r, bytes)
+        self.assertEqual(r, b'\\x64617320697327206bc3a47365')
+        r = f(u"das is' käse")
+        self.assertIsInstance(r, unicode)
+        self.assertEqual(r, u'\\x64617320697327206bc3a47365')
+        self.assertEqual(f(b'O\x00ps\xff!'), b'\\x4f007073ff21')
 
     def testUnescapeBytea(self):
         f = self.db.unescape_bytea
         r = f(b'plain')
-        self.assertIsInstance(r, str)
-        self.assertEqual(r, 'plain')
+        self.assertIsInstance(r, bytes)
+        self.assertEqual(r, b'plain')
         r = f(u'plain')
-        self.assertIsInstance(r, str)
-        self.assertEqual(r, 'plain')
+        self.assertIsInstance(r, bytes)
+        self.assertEqual(r, b'plain')
         r = f(b"das is' k\\303\\244se")
-        self.assertIsInstance(r, str)
-        self.assertEqual(r, "das is' käse")
+        self.assertIsInstance(r, bytes)
+        self.assertEqual(r, u"das is' käse".encode('utf8'))
         r = f(u"das is' k\\303\\244se")
-        self.assertIsInstance(r, str)
-        self.assertEqual(r, "das is' käse")
-        self.assertEqual(f(r'O\\000ps\\377!'), r'O\000ps\377!')
-        self.assertEqual(f(r'\\x706c61696e'), r'\x706c61696e')
+        self.assertIsInstance(r, bytes)
+        self.assertEqual(r, u"das is' käse".encode('utf8'))
+        self.assertEqual(f(r'O\\000ps\\377!'), b'O\\000ps\\377!')
+        self.assertEqual(f(r'\\x706c61696e'), b'\\x706c61696e')
         self.assertEqual(f(r'\\x746861742773206be47365'),
-            r'\x746861742773206be47365')
-        self.assertEqual(f(r'\\x4f007073ff21'), r'\x4f007073ff21')
+            b'\\x746861742773206be47365')
+        self.assertEqual(f(r'\\x4f007073ff21'), b'\\x4f007073ff21')
 
     def testQuote(self):
         f = self.db._quote
@@ -1034,16 +1069,16 @@
         query('drop table if exists bytea_test')
         query('create table bytea_test ('
             'data bytea)')
-        s = "It's all \\ kinds \x00 of\r nasty \xff stuff!\n"
+        s = b"It's all \\ kinds \x00 of\r nasty \xff stuff!\n"
         r = self.db.escape_bytea(s)
-        query('insert into bytea_test values('
-            "'%s')" % r)
+        query('insert into bytea_test values($1)', (r,))
         r = query('select * from bytea_test').getresult()
         self.assertTrue(len(r) == 1)
         r = r[0]
         self.assertTrue(len(r) == 1)
         r = r[0]
         r = self.db.unescape_bytea(r)
+        self.assertIsInstance(r, bytes)
         self.assertEqual(r, s)
         query('drop table bytea_test')
 

Modified: trunk/module/TEST_PyGreSQL_classic_functions.py
==============================================================================
--- trunk/module/TEST_PyGreSQL_classic_functions.py     Wed Nov 25 12:48:37 
2015        (r628)
+++ trunk/module/TEST_PyGreSQL_classic_functions.py     Thu Nov 26 01:33:35 
2015        (r629)
@@ -26,7 +26,10 @@
 except NameError:  # Python >= 3.0
     long = int
 
-unicode_strings = str is not bytes
+try:
+    unicode
+except NameError:  # Python >= 3.0
+    unicode = str
 
 
 class TestAuxiliaryFunctions(unittest.TestCase):
@@ -274,11 +277,11 @@
     def testEscapeString(self):
         f = pg.escape_string
         r = f(b'plain')
-        self.assertIsInstance(r, str)
-        self.assertEqual(r, 'plain')
+        self.assertIsInstance(r, bytes)
+        self.assertEqual(r, b'plain')
         r = f(u'plain')
-        self.assertIsInstance(r, str)
-        self.assertEqual(r, 'plain')
+        self.assertIsInstance(r, unicode)
+        self.assertEqual(r, u'plain')
         r = f("that's cheese")
         self.assertIsInstance(r, str)
         self.assertEqual(r, "that''s cheese")
@@ -286,11 +289,11 @@
     def testEscapeBytea(self):
         f = pg.escape_bytea
         r = f(b'plain')
-        self.assertIsInstance(r, str)
-        self.assertEqual(r, 'plain')
+        self.assertIsInstance(r, bytes)
+        self.assertEqual(r, b'plain')
         r = f(u'plain')
-        self.assertIsInstance(r, str)
-        self.assertEqual(r, 'plain')
+        self.assertIsInstance(r, unicode)
+        self.assertEqual(r, u'plain')
         r = f("that's cheese")
         self.assertIsInstance(r, str)
         self.assertEqual(r, "that''s cheese")
@@ -298,19 +301,21 @@
     def testUnescapeBytea(self):
         f = pg.unescape_bytea
         r = f(b'plain')
-        self.assertIsInstance(r, str)
-        self.assertEqual(r, 'plain')
+        self.assertIsInstance(r, bytes)
+        self.assertEqual(r, b'plain')
         r = f(u'plain')
-        self.assertIsInstance(r, str)
-        self.assertEqual(r, 'plain')
+        self.assertIsInstance(r, bytes)
+        self.assertEqual(r, b'plain')
         r = f(b"das is' k\\303\\244se")
-        self.assertIsInstance(r, str)
-        self.assertEqual(r, "das is' käse")
+        self.assertIsInstance(r, bytes)
+        self.assertEqual(r, u"das is' käse".encode('utf-8'))
         r = f(u"das is' k\\303\\244se")
-        self.assertIsInstance(r, str)
-        self.assertEqual(r, "das is' käse")
-        r = f(r'O\\000ps\\377!')
-        self.assertEqual(r, r'O\000ps\377!')
+        self.assertIsInstance(r, bytes)
+        self.assertEqual(r, u"das is' käse".encode('utf-8'))
+        r = f(b'O\\000ps\\377!')
+        self.assertEqual(r, b'O\x00ps\xff!')
+        r = f(u'O\\000ps\\377!')
+        self.assertEqual(r, b'O\x00ps\xff!')
 
 
 class TestConfigFunctions(unittest.TestCase):

Modified: trunk/module/TEST_PyGreSQL_dbapi20.py
==============================================================================
--- trunk/module/TEST_PyGreSQL_dbapi20.py       Wed Nov 25 12:48:37 2015        
(r628)
+++ trunk/module/TEST_PyGreSQL_dbapi20.py       Thu Nov 26 01:33:35 2015        
(r629)
@@ -1,4 +1,5 @@
 #! /usr/bin/python
+# -*- coding: utf-8 -*-
 # $Id$
 
 import unittest
@@ -16,6 +17,11 @@
 except ImportError:
     pass
 
+try:
+    long
+except NameError:  # Python >= 3.0
+    long = int
+
 
 class test_PyGreSQL(dbapi20.DatabaseAPI20Test):
 
@@ -60,7 +66,7 @@
 
     def test_fetch_2_rows(self):
         Decimal = pgdb.decimal_type()
-        values = ['test', pgdb.Binary('\xff\x52\xb2'),
+        values = ['test', pgdb.Binary(b'\xff\x52\xb2'),
             True, 5, 6, 5.7, Decimal('234.234234'), Decimal('75.45'),
             '2011-07-17', '15:47:42', '2008-10-20 15:25:35', '15:31:05',
             7897234]
@@ -91,8 +97,21 @@
             cur.execute("select * from %s" % table)
             rows = cur.fetchall()
             self.assertEqual(len(rows), 2)
-            self.assertEqual(rows[0], values)
-            self.assertEqual(rows[0], rows[1])
+            row0 = rows[0]
+            self.assertEqual(row0, values)
+            self.assertEqual(row0, rows[1])
+            self.assertIsInstance(row0[0], str)
+            self.assertIsInstance(row0[1], bytes)
+            self.assertIsInstance(row0[2], bool)
+            self.assertIsInstance(row0[3], int)
+            self.assertIsInstance(row0[4], long)
+            self.assertIsInstance(row0[5], float)
+            self.assertIsInstance(row0[6], Decimal)
+            self.assertIsInstance(row0[7], Decimal)
+            self.assertIsInstance(row0[8], str)
+            self.assertIsInstance(row0[9], str)
+            self.assertIsInstance(row0[10], str)
+            self.assertIsInstance(row0[11], str)
         finally:
             con.close()
 
@@ -119,7 +138,7 @@
             cur.execute(
                 "create table %s (n smallint, floattest float)" % table)
             params = enumerate(values)
-            cur.executemany("insert into %s values(%%s,%%s)" % table, params)
+            cur.executemany("insert into %s values (%%s,%%s)" % table, params)
             cur.execute("select * from %s order by 1" % table)
             rows = cur.fetchall()
         finally:
@@ -138,6 +157,72 @@
             else:
                 self.assertEqual(inval, outval)
 
+    def test_unicode_with_utf8(self):
+        table = self.table_prefix + 'booze'
+        input = u"He wes Leovenaðes sone — liðe him be Drihten"
+        con = self._connect()
+        try:
+            cur = con.cursor()
+            cur.execute("create table %s (t text)" % table)
+            try:
+                cur.execute("set client_encoding=utf8")
+                cur.execute(u"select '%s'" % input)
+            except Exception:
+                self.skipTest("database does not support utf8")
+            output1 = cur.fetchone()[0]
+            cur.execute("insert into %s values (%%s)" % table, (input,))
+            cur.execute("select * from %s" % table)
+            output2 = cur.fetchone()[0]
+            cur.execute("select t = '%s' from %s" % (input, table))
+            output3 = cur.fetchone()[0]
+            cur.execute("select t = %%s from %s" % table, (input,))
+            output4 = cur.fetchone()[0]
+        finally:
+            con.close()
+        if str is bytes:  # Python < 3.0
+            input = input.encode('utf8')
+        self.assertIsInstance(output1, str)
+        self.assertEqual(output1, input)
+        self.assertIsInstance(output2, str)
+        self.assertEqual(output2, input)
+        self.assertIsInstance(output3, bool)
+        self.assertTrue(output3)
+        self.assertIsInstance(output4, bool)
+        self.assertTrue(output4)
+
+    def test_unicode_with_latin1(self):
+        table = self.table_prefix + 'booze'
+        input = u"Ehrt den König seine Würde, ehret uns der Hände Fleiß."
+        con = self._connect()
+        try:
+            cur = con.cursor()
+            cur.execute("create table %s (t text)" % table)
+            try:
+                cur.execute("set client_encoding=latin1")
+                cur.execute(u"select '%s'" % input)
+            except Exception:
+                self.skipTest("database does not support latin1")
+            output1 = cur.fetchone()[0]
+            cur.execute("insert into %s values (%%s)" % table, (input,))
+            cur.execute("select * from %s" % table)
+            output2 = cur.fetchone()[0]
+            cur.execute("select t = '%s' from %s" % (input, table))
+            output3 = cur.fetchone()[0]
+            cur.execute("select t = %%s from %s" % table, (input,))
+            output4 = cur.fetchone()[0]
+        finally:
+            con.close()
+        if str is bytes:  # Python < 3.0
+            input = input.encode('latin1')
+        self.assertIsInstance(output1, str)
+        self.assertEqual(output1, input)
+        self.assertIsInstance(output2, str)
+        self.assertEqual(output2, input)
+        self.assertIsInstance(output3, bool)
+        self.assertTrue(output3)
+        self.assertIsInstance(output4, bool)
+        self.assertTrue(output4)
+
     def test_set_decimal_type(self):
         decimal_type = pgdb.decimal_type()
         self.assertTrue(decimal_type is not None and callable(decimal_type))

Modified: trunk/module/dbapi20.py
==============================================================================
--- trunk/module/dbapi20.py     Wed Nov 25 12:48:37 2015        (r628)
+++ trunk/module/dbapi20.py     Thu Nov 26 01:33:35 2015        (r629)
@@ -827,8 +827,8 @@
         # self.assertEqual(str(t1),str(t2))
 
     def test_Binary(self):
-        b = self.driver.Binary('Something')
-        b = self.driver.Binary('')
+        b = self.driver.Binary(b'Something')
+        b = self.driver.Binary(b'')
 
     def test_STRING(self):
         self.assertTrue(hasattr(self.driver,'STRING'),

Modified: trunk/module/pgdb.py
==============================================================================
--- trunk/module/pgdb.py        Wed Nov 25 12:48:37 2015        (r628)
+++ trunk/module/pgdb.py        Thu Nov 26 01:33:35 2015        (r629)
@@ -78,6 +78,11 @@
     long = int
 
 try:
+    unicode
+except NameError:  # Python >= 3.0
+    unicode = str
+
+try:
     basestring
 except NameError:  # Python >= 3.0
     basestring = (str, bytes)
@@ -240,11 +245,11 @@
         """Quote value depending on its type."""
         if isinstance(val, (datetime, date, time, timedelta)):
             val = str(val)
-        elif isinstance(val, str) and str is not bytes:
-            val = val.encode('utf8')
-        if isinstance(val, bytes):
+        if isinstance(val, basestring):
             if isinstance(val, Binary):
                 val = self._cnx.escape_bytea(val)
+                if bytes is not str:  # Python >= 3.0
+                    val = val.decode('ascii')
             else:
                 val = self._cnx.escape_string(val)
             val = "'%s'" % val
@@ -351,7 +356,7 @@
         except DatabaseError:
             raise
         except Error as err:
-            raise _db_error("error '%s' in '%s'" % (err, sql))
+            raise _db_error("error in '%s': '%s' " % (sql, err))
         except Exception as err:
             raise _op_error("internal error in '%s': %s" % (sql, err))
         # then initialize result raw count and description
@@ -685,7 +690,7 @@
     return Timestamp(*localtime(ticks)[:6])
 
 
-class Binary(str):
+class Binary(bytes):
     """construct an object capable of holding a binary (long) string value."""
 
 

Modified: trunk/module/pgmodule.c
==============================================================================
--- trunk/module/pgmodule.c     Wed Nov 25 12:48:37 2015        (r628)
+++ trunk/module/pgmodule.c     Thu Nov 26 01:33:35 2015        (r629)
@@ -147,7 +147,7 @@
 static void notice_receiver(void *, const PGresult *);
 
 /* --------------------------------------------------------------------- */
-/* Object declarations */
+/* Object declarations                                                         
                                         */
 /* --------------------------------------------------------------------- */
 typedef struct
 {
@@ -164,6 +164,7 @@
        int                     valid;                  /* validity flag */
        connObject      *pgcnx;                 /* parent connection object */
        PGresult        *result;                /* result content */
+       int                     encoding;               /* client encoding */
        int                     result_type;    /* result type (DDL/DML/DQL) */
        long            arraysize;              /* array size for fetch method 
*/
        int                     current_row;    /* current selected row */
@@ -199,10 +200,49 @@
 #define is_largeObject(v) (PyType(v) == &largeType)
 #endif /* LARGE_OBJECTS */
 
+/* define internal types */
+
+#define PYGRES_INT 1
+#define PYGRES_LONG 2
+#define PYGRES_FLOAT 3
+#define PYGRES_DECIMAL 4
+#define PYGRES_MONEY 5
+#define PYGRES_DEFAULT 6
 
 /* --------------------------------------------------------------------- */
-/* Internal Functions */
+/* Internal Functions                                                          
                                         */
 /* --------------------------------------------------------------------- */
+
+/* shared function for encoding and decoding strings */
+
+PyObject *
+get_decoded_string(char *str, Py_ssize_t size, int encoding)
+{
+       if (encoding == pg_encoding_utf8)
+               return PyUnicode_DecodeUTF8(str, size, "strict");
+       if (encoding == pg_encoding_latin1)
+               return PyUnicode_DecodeLatin1(str, size, "strict");
+       if (encoding == pg_encoding_ascii)
+               return PyUnicode_DecodeASCII(str, size, "strict");
+       /* encoding name should be properly translated to Python here */
+       return PyUnicode_Decode(str, size,
+               pg_encoding_to_char(encoding), "strict");
+}
+
+PyObject *
+get_encoded_string(PyObject *unicode_obj, int encoding)
+ {
+       if (encoding == pg_encoding_utf8)
+               return PyUnicode_AsUTF8String(unicode_obj);
+       if (encoding == pg_encoding_latin1)
+               return PyUnicode_AsLatin1String(unicode_obj);
+       if (encoding == pg_encoding_ascii)
+               return PyUnicode_AsASCIIString(unicode_obj);
+       /* encoding name should be properly translated to Python here */
+       return PyUnicode_AsEncodedString(unicode_obj,
+               pg_encoding_to_char(encoding), "strict");
+}
+
 /* shared functions for converting PG types to Python types */
 static int *
 get_type_array(PGresult *result, int nfields)
@@ -223,29 +263,28 @@
                        case INT2OID:
                        case INT4OID:
                        case OIDOID:
-                               typ[j] = 1;
+                               typ[j] = PYGRES_INT;
                                break;
 
                        case INT8OID:
-                               typ[j] = 2;
+                               typ[j] = PYGRES_LONG;
                                break;
 
                        case FLOAT4OID:
                        case FLOAT8OID:
-                               typ[j] = 3;
+                               typ[j] = PYGRES_FLOAT;
                                break;
 
                        case NUMERICOID:
-                               typ[j] = 4;
+                               typ[j] = PYGRES_DECIMAL;
                                break;
 
                        case CASHOID:
-                               typ[j] = 5;
+                               typ[j] = PYGRES_MONEY;
                                break;
 
                        default:
-                               typ[j] = 6;
-                               break;
+                               typ[j] = PYGRES_DEFAULT;
                }
        }
 
@@ -387,7 +426,6 @@
                                                        break;
                                                default:
                                                        aligns[j] = 'l';
-                                                       break;
                                        }
                                }
                        }
@@ -500,7 +538,7 @@
 }
 
 /* --------------------------------------------------------------------- */
-/* large objects                                                         */
+/* large objects                                                               
                                                 */
 /* --------------------------------------------------------------------- */
 #ifdef LARGE_OBJECTS
 
@@ -665,7 +703,7 @@
        buffer = PyBytes_FromStringAndSize((char *) NULL, size);
 
        if ((size = lo_read(self->pgcnx->cnx, self->lo_fd,
-           PyBytes_AS_STRING((PyBytesObject *)(buffer)), size)) < 0)
+               PyBytes_AS_STRING((PyBytesObject *)(buffer)), size)) < 0)
        {
                PyErr_SetString(PyExc_IOError, "error while reading.");
                Py_XDECREF(buffer);
@@ -900,7 +938,8 @@
 
 /* get the list of large object attributes */
 static PyObject *
-largeDir(largeObject *self) {
+largeDir(largeObject *self)
+{
        PyObject *attrs;
 
        attrs = PyObject_Dir(PyObject_Type((PyObject *)self));
@@ -995,25 +1034,25 @@
        0,                                                              /* 
tp_as_sequence */
        0,                                                              /* 
tp_as_mapping */
        0,                                                              /* 
tp_hash */
-       0,                              /* tp_call */
+       0,                                                              /* 
tp_call */
        (reprfunc) largeStr,                    /* tp_str */
        (getattrofunc) largeGetAttr,    /* tp_getattro */
-       0,                              /* tp_setattro */
-       0,                              /* tp_as_buffer */
-       Py_TPFLAGS_DEFAULT,             /* tp_flags */
+       0,                                                              /* 
tp_setattro */
+       0,                                                              /* 
tp_as_buffer */
+       Py_TPFLAGS_DEFAULT,                             /* tp_flags */
        large__doc__,                                   /* tp_doc */
-       0,                              /* tp_traverse */
-       0,                              /* tp_clear */
-       0,                              /* tp_richcompare */
-       0,                              /* tp_weaklistoffset */
-       0,                              /* tp_iter */
-       0,                              /* tp_iternext */
+       0,                                                              /* 
tp_traverse */
+       0,                                                              /* 
tp_clear */
+       0,                                                              /* 
tp_richcompare */
+       0,                                                              /* 
tp_weaklistoffset */
+       0,                                                              /* 
tp_iter */
+       0,                                                              /* 
tp_iternext */
        largeMethods,                                   /* tp_methods */
 };
 #endif /* LARGE_OBJECTS */
 
 /* --------------------------------------------------------------------- */
-/* connection object */
+/* connection object                                                           
                                         */
 /* --------------------------------------------------------------------- */
 static void
 connDelete(connObject *self)
@@ -1078,7 +1117,6 @@
        char            *query = NULL;
        PGresult        *result;
        queryObject *npgobj;
-       const char*     encoding_name=NULL;
        int                     encoding,
                                status,
                                nparms = 0;
@@ -1096,10 +1134,6 @@
        }
 
        encoding = PQclientEncoding(self->cnx);
-       if (encoding != pg_encoding_utf8 && encoding != pg_encoding_latin1
-                       && encoding != pg_encoding_ascii)
-               /* should be translated to Python here */
-               encoding_name = pg_encoding_to_char(encoding);
 
        if (PyBytes_Check(query_obj))
        {
@@ -1107,19 +1141,12 @@
        }
        else if (PyUnicode_Check(query_obj))
        {
-               if (encoding == pg_encoding_utf8)
-                       query_obj = PyUnicode_AsUTF8String(query_obj);
-               else if (encoding == pg_encoding_latin1)
-                       query_obj = PyUnicode_AsLatin1String(query_obj);
-               else if (encoding == pg_encoding_ascii)
-                       query_obj = PyUnicode_AsASCIIString(query_obj);
-               else
-                       query_obj = PyUnicode_AsEncodedString(query_obj,
-                               encoding_name, "strict");
+               query_obj = get_encoded_string(query_obj, encoding);
                if (!query_obj) return NULL; /* pass the UnicodeEncodeError */
                query = PyBytes_AsString(query_obj);
        }
-       if (!query) {
+       if (!query)
+       {
                PyErr_SetString(PyExc_TypeError,
                        "query command must be a string.");
                return NULL;
@@ -1176,21 +1203,11 @@
                        }
                        else if (PyBytes_Check(obj))
                        {
-                               *s = obj;
-                               *p = PyBytes_AsString(*s);
-                               *l = (int)PyBytes_Size(*s);
+                               PyBytes_AsStringAndSize(*s = obj, p, 
(Py_ssize_t *)l);
                        }
                        else if (PyUnicode_Check(obj))
                        {
-                               if (encoding == pg_encoding_utf8)
-                                       *s = PyUnicode_AsUTF8String(obj);
-                               else if (encoding == pg_encoding_latin1)
-                                       *s = PyUnicode_AsLatin1String(obj);
-                               else if (encoding == pg_encoding_ascii)
-                                       *s = PyUnicode_AsASCIIString(obj);
-                               else
-                                       *s = PyUnicode_AsEncodedString(obj,
-                                               encoding_name, "strict");
+                               *s = get_encoded_string(obj, encoding);
                                if (!*s)
                                {
                                        free(lparms); free(parms); free(str);
@@ -1203,8 +1220,7 @@
                                        }
                                        return NULL; /* pass the 
UnicodeEncodeError */
                                }
-                               *p = PyBytes_AsString(*s);
-                               *l = (int)PyBytes_Size(*s);
+                               PyBytes_AsStringAndSize(*s, p, (Py_ssize_t *)l);
                        }
                        else
                        {
@@ -1298,7 +1314,6 @@
                        default:
                                set_dberror(InternalError,
                                        "internal error: unknown result 
status.", result);
-                               break;
                }
 
                PQclear(result);
@@ -1445,7 +1460,6 @@
        char            *table,
                                *buffer,
                                *bufpt;
-       const char *encoding_name=NULL;
        int                     encoding;
        size_t          bufsiz;
        PyObject        *list,
@@ -1514,10 +1528,6 @@
        }
 
        encoding = PQclientEncoding(self->cnx);
-       if (encoding != pg_encoding_utf8 && encoding != pg_encoding_latin1
-                       && encoding != pg_encoding_ascii)
-               /* should be translated to Python here */
-               encoding_name = pg_encoding_to_char(encoding);
 
        PQclear(result);
 
@@ -1597,16 +1607,7 @@
                        }
                        else if (PyUnicode_Check(item))
                        {
-                               PyObject *s;
-                               if (encoding == pg_encoding_utf8)
-                                       s = PyUnicode_AsUTF8String(item);
-                               else if (encoding == pg_encoding_latin1)
-                                       s = PyUnicode_AsLatin1String(item);
-                               else if (encoding == pg_encoding_ascii)
-                                       s = PyUnicode_AsASCIIString(item);
-                               else
-                                       s = PyUnicode_AsEncodedString(item,
-                                               encoding_name, "strict");
+                               PyObject *s = get_encoded_string(item, 
encoding);
                                if (!s)
                                {
                                        free(buffer);
@@ -1761,20 +1762,44 @@
 static PyObject *
 connEscapeLiteral(connObject *self, PyObject *args)
 {
-       char *str; /* our string argument */
-       int str_length; /* length of string */
-       char *esc; /* the escaped version of the string */
-       PyObject *ret; /* string object to return */
-
-       if (!PyArg_ParseTuple(args, "s#", &str, &str_length))
-               return NULL;
-       esc = PQescapeLiteral(self->cnx, str, (size_t)str_length);
-       ret = Py_BuildValue("s", esc);
-       if (esc)
-               PQfreemem(esc);
-       if (!ret) /* pass on exception */
+       PyObject   *from_obj, /* the object that was passed in */
+                          *to_obj; /* string object to return */
+       char       *from=NULL, /* our string argument as encoded string */
+                          *to; /* the result as encoded string */
+       Py_ssize_t      from_length; /* length of string */
+       size_t          to_length; /* length of result */
+       int                     encoding = -1; /* client encoding */
+
+       if (!PyArg_ParseTuple(args, "O", &from_obj))
                return NULL;
-       return ret;
+
+       if (PyBytes_Check(from_obj))
+       {
+               PyBytes_AsStringAndSize(from_obj, &from, &from_length);
+       }
+       else if (PyUnicode_Check(from_obj))
+       {
+               encoding = PQclientEncoding(self->cnx);
+               from_obj = get_encoded_string(from_obj, encoding);
+               if (!from_obj) return NULL; /* pass the UnicodeEncodeError */
+               PyBytes_AsStringAndSize(from_obj, &from, &from_length);
+       }
+       if (!from)
+       {
+               PyErr_SetString(PyExc_TypeError, "escape_literal() expects a 
string.");
+               return NULL;
+       }
+
+       to = PQescapeLiteral(self->cnx, from, (size_t)from_length);
+       to_length = strlen(to);
+
+       if (encoding == -1)
+               to_obj = PyBytes_FromStringAndSize(to, to_length);
+       else
+               to_obj = get_decoded_string(to, to_length, encoding);
+       if (to)
+               PQfreemem(to);
+       return to_obj;
 }
 
 /* escape identifier */
@@ -1784,20 +1809,45 @@
 static PyObject *
 connEscapeIdentifier(connObject *self, PyObject *args)
 {
-       char *str; /* our string argument */
-       int str_length; /* length of string */
-       char *esc; /* the escaped version of the string */
-       PyObject *ret; /* string object to return */
-
-       if (!PyArg_ParseTuple(args, "s#", &str, &str_length))
-               return NULL;
-       esc = PQescapeIdentifier(self->cnx, str, (size_t)str_length);
-       ret = Py_BuildValue("s", esc);
-       if (esc)
-               PQfreemem(esc);
-       if (!ret) /* pass on exception */
+       PyObject   *from_obj, /* the object that was passed in */
+                          *to_obj; /* string object to return */
+       char       *from=NULL, /* our string argument as encoded string */
+                          *to; /* the result as encoded string */
+       Py_ssize_t      from_length; /* length of string */
+       size_t          to_length; /* length of result */
+       int                     encoding = -1; /* client encoding */
+
+       if (!PyArg_ParseTuple(args, "O", &from_obj))
                return NULL;
-       return ret;
+
+       if (PyBytes_Check(from_obj))
+       {
+               PyBytes_AsStringAndSize(from_obj, &from, &from_length);
+       }
+       else if (PyUnicode_Check(from_obj))
+       {
+               encoding = PQclientEncoding(self->cnx);
+               from_obj = get_encoded_string(from_obj, encoding);
+               if (!from_obj) return NULL; /* pass the UnicodeEncodeError */
+               PyBytes_AsStringAndSize(from_obj, &from, &from_length);
+       }
+       if (!from)
+       {
+               PyErr_SetString(PyExc_TypeError,
+                       "escape_identifier() expects a string.");
+               return NULL;
+       }
+
+       to = PQescapeIdentifier(self->cnx, from, (size_t)from_length);
+       to_length = strlen(to);
+
+       if (encoding == -1)
+               to_obj = PyBytes_FromStringAndSize(to, to_length);
+       else
+               to_obj = get_decoded_string(to, to_length, encoding);
+       if (to)
+               PQfreemem(to);
+       return to_obj;
 }
 
 #endif /* ESCAPING_FUNCS */
@@ -1809,14 +1859,34 @@
 static PyObject *
 connEscapeString(connObject *self, PyObject *args)
 {
-       char *from; /* our string argument */
-       char *to=NULL; /* the result */
-       int from_length; /* length of string */
-       int to_length; /* length of result */
-       PyObject *ret; /* string object to return */
+       PyObject   *from_obj, /* the object that was passed in */
+                          *to_obj; /* string object to return */
+       char       *from=NULL, /* our string argument as encoded string */
+                          *to; /* the result as encoded string */
+       Py_ssize_t      from_length; /* length of string */
+       size_t          to_length; /* length of result */
+       int                     encoding = -1; /* client encoding */
 
-       if (!PyArg_ParseTuple(args, "s#", &from, &from_length))
+       if (!PyArg_ParseTuple(args, "O", &from_obj))
                return NULL;
+
+       if (PyBytes_Check(from_obj))
+       {
+               PyBytes_AsStringAndSize(from_obj, &from, &from_length);
+       }
+       else if (PyUnicode_Check(from_obj))
+       {
+               encoding = PQclientEncoding(self->cnx);
+               from_obj = get_encoded_string(from_obj, encoding);
+               if (!from_obj) return NULL; /* pass the UnicodeEncodeError */
+               PyBytes_AsStringAndSize(from_obj, &from, &from_length);
+       }
+       if (!from)
+       {
+               PyErr_SetString(PyExc_TypeError, "escape_string() expects a 
string.");
+               return NULL;
+       }
+
        to_length = 2*from_length + 1;
        if (to_length < from_length) /* overflow */
        {
@@ -1824,14 +1894,16 @@
                from_length = (from_length - 1)/2;
        }
        to = (char *)malloc(to_length);
-       to_length = (int)PQescapeStringConn(self->cnx,
+       to_length = PQescapeStringConn(self->cnx,
                to, from, (size_t)from_length, NULL);
-       ret = Py_BuildValue("s#", to, to_length);
+
+       if (encoding == -1)
+               to_obj = PyBytes_FromStringAndSize(to, to_length);
+       else
+               to_obj = get_decoded_string(to, to_length, encoding);
        if (to)
                free(to);
-       if (!ret) /* pass on exception */
-               return NULL;
-       return ret;
+       return to_obj;
 }
 
 /* escape bytea */
@@ -1841,21 +1913,44 @@
 static PyObject *
 connEscapeBytea(connObject *self, PyObject *args)
 {
-       unsigned char *from; /* our string argument */
-       unsigned char *to; /* the result */
-       int from_length; /* length of string */
-       size_t to_length; /* length of result */
-       PyObject *ret; /* string object to return */
+       PyObject   *from_obj, /* the object that was passed in */
+                          *to_obj; /* string object to return */
+       char       *from=NULL, /* our string argument as encoded string */
+                          *to; /* the result as encoded string */
+       Py_ssize_t      from_length; /* length of string */
+       size_t          to_length; /* length of result */
+       int                     encoding = -1; /* client encoding */
 
-       if (!PyArg_ParseTuple(args, "s#", &from, &from_length))
+       if (!PyArg_ParseTuple(args, "O", &from_obj))
                return NULL;
-       to = PQescapeByteaConn(self->cnx, from, (int)from_length, &to_length);
-       ret = Py_BuildValue("s", to);
-       if (to)
-               PQfreemem((void *)to);
-       if (!ret) /* pass on exception */
+
+       if (PyBytes_Check(from_obj))
+       {
+               PyBytes_AsStringAndSize(from_obj, &from, &from_length);
+       }
+       else if (PyUnicode_Check(from_obj))
+       {
+               encoding = PQclientEncoding(self->cnx);
+               from_obj = get_encoded_string(from_obj, encoding);
+               if (!from_obj) return NULL; /* pass the UnicodeEncodeError */
+               PyBytes_AsStringAndSize(from_obj, &from, &from_length);
+       }
+       if (!from)
+       {
+               PyErr_SetString(PyExc_TypeError, "escape_bytea() expects a 
string.");
                return NULL;
-       return ret;
+       }
+
+       to = (char *)PQescapeByteaConn(self->cnx,
+               (unsigned char *)from, (size_t)from_length, &to_length);
+
+       if (encoding == -1)
+               to_obj = PyBytes_FromStringAndSize(to, to_length - 1);
+       else
+               to_obj = get_decoded_string(to, to_length - 1, encoding);
+       if (to)
+               PQfreemem(to);
+       return to_obj;
 }
 
 #ifdef LARGE_OBJECTS
@@ -2187,7 +2282,8 @@
 
 /* get the list of connection attributes */
 static PyObject *
-connDir(connObject *self) {
+connDir(connObject *self)
+{
        PyObject *attrs;
 
        attrs = PyObject_Dir(PyObject_Type((PyObject *)self));
@@ -2341,7 +2437,7 @@
        (getattrofunc) connGetAttr,     /* tp_getattro */
        0,                                                      /* tp_setattro 
*/
        0,                                                      /* tp_as_buffer 
*/
-       Py_TPFLAGS_DEFAULT,         /* tp_flags */
+       Py_TPFLAGS_DEFAULT,                     /* tp_flags */
        0,                                                      /* tp_doc */
        0,                                                      /* tp_traverse 
*/
        0,                                                      /* tp_clear */
@@ -2353,7 +2449,7 @@
 };
 
 /* --------------------------------------------------------------------- */
-/* source object */
+/* source object                                                               
                                                 */
 /* --------------------------------------------------------------------- */
 /* checks source object validity */
 static int
@@ -2436,7 +2532,9 @@
 static PyObject *
 sourceExecute(sourceObject *self, PyObject *args)
 {
-       char            *query;
+       PyObject        *query_obj;
+       char            *query = NULL;
+       int                     encoding;
 
        /* checks validity */
        if (!check_source_obj(self, CHECK_CNX))
@@ -2447,9 +2545,26 @@
                return NULL;
 
        /* get query args */
-       if (!PyArg_ParseTuple(args, "s", &query))
+       if (!PyArg_ParseTuple(args, "O", &query_obj))
+       {
+               return NULL;
+       }
+
+       encoding = PQclientEncoding(self->pgcnx->cnx);
+
+       if (PyBytes_Check(query_obj))
+       {
+               query = PyBytes_AsString(query_obj);
+       }
+       else if (PyUnicode_Check(query_obj))
+       {
+               query_obj = get_encoded_string(query_obj, encoding);
+               if (!query_obj) return NULL; /* pass the UnicodeEncodeError */
+               query = PyBytes_AsString(query_obj);
+       }
+       if (!query)
        {
-               PyErr_SetString(PyExc_TypeError, "execute(sql), with sql 
(string).");
+               PyErr_SetString(PyExc_TypeError, "executed sql must be a 
string.");
                return NULL;
        }
 
@@ -2462,6 +2577,7 @@
        self->max_row = 0;
        self->current_row = 0;
        self->num_fields = 0;
+       self->encoding = encoding;
 
        /* gets result */
        Py_BEGIN_ALLOW_THREADS
@@ -2514,7 +2630,6 @@
                default:
                        set_dberror(InternalError, "internal error: "
                                "unknown result status.", self->result);
-                       break;
        }
 
        /* frees result and returns error */
@@ -2564,12 +2679,13 @@
 static PyObject *
 sourceFetch(sourceObject *self, PyObject *args)
 {
-       PyObject   *rowtuple,
-                          *reslist,
-                          *str;
+       PyObject   *reslist;
        int                     i,
-                               j;
+                               k;
        long            size;
+#if IS_PY3
+       int                     encoding;
+#endif
 
        /* checks validity */
        if (!check_source_obj(self, CHECK_RESULT | CHECK_DQL))
@@ -2593,9 +2709,16 @@
        if (!(reslist = PyList_New(0)))
                return NULL;
 
+#if IS_PY3
+       encoding = self->encoding;
+#endif
+
        /* builds result */
-       for (i = 0; i < size; i++)
+       for (i = 0, k = self->current_row; i < size; i++, k++)
        {
+               PyObject   *rowtuple;
+               int                     j;
+
                if (!(rowtuple = PyTuple_New(self->num_fields)))
                {
                        Py_DECREF(reslist);
@@ -2604,23 +2727,35 @@
 
                for (j = 0; j < self->num_fields; j++)
                {
-                       if (PQgetisnull(self->result, self->current_row, j))
+                       PyObject   *str;
+
+                       if (PQgetisnull(self->result, k, j))
                        {
                                Py_INCREF(Py_None);
                                str = Py_None;
                        }
-                       else
-                               str = PyStr_FromString(
-                                       PQgetvalue(self->result, 
self->current_row, j));
-
+                       else {
+                               char *s = PQgetvalue(self->result, k, j);
+                               Py_ssize_t size = PQgetlength(self->result, k, 
j);
+#if IS_PY3
+                               if (PQfformat(self->result, j) == 0) /* textual 
format */
+                               {
+                                       str = get_decoded_string(s, size, 
encoding);
+                                       if (!str) /* cannot decode */
+                                               str = 
PyBytes_FromStringAndSize(s, size);
+                               }
+                               else
+#endif
+                               str = PyBytes_FromStringAndSize(s, size);
+                       }
                        PyTuple_SET_ITEM(rowtuple, j, str);
                }
 
                PyList_Append(reslist, rowtuple);
                Py_DECREF(rowtuple);
-               self->current_row++;
        }
 
+       self->current_row = k;
        return reslist;
 }
 
@@ -2857,7 +2992,8 @@
 
 /* get the list of source object attributes */
 static PyObject *
-sourceDir(connObject *self) {
+sourceDir(connObject *self)
+{
        PyObject *attrs;
 
        attrs = PyObject_Dir(PyObject_Type((PyObject *)self));
@@ -3228,17 +3364,13 @@
 static PyObject *
 queryGetResult(queryObject *self, PyObject *args)
 {
-       PyObject   *rowtuple,
-                          *reslist,
-                          *val;
+       PyObject   *reslist;
        int                     i,
-                               j,
                                m,
                                n,
-                          *typ;
+                          *coltypes;
 #if IS_PY3
        int                     encoding;
-       const char *encoding_name=NULL;
 #endif
 
        /* checks args (args == NULL for an internal call) */
@@ -3249,23 +3381,23 @@
                return NULL;
        }
 
-#if IS_PY3
-       encoding = self->encoding;
-       if (encoding != pg_encoding_utf8 && encoding != pg_encoding_latin1
-                       && encoding != pg_encoding_ascii)
-               /* should be translated to Python here */
-               encoding_name = pg_encoding_to_char(encoding);
-#endif
-
        /* stores result in tuple */
        m = PQntuples(self->result);
        n = PQnfields(self->result);
-       reslist = PyList_New(m);
+       if (!(reslist = PyList_New(m)))
+               return NULL;
 
-       typ = get_type_array(self->result, n);
+#if IS_PY3
+       encoding = self->encoding;
+#endif
+
+       coltypes = get_type_array(self->result, n);
 
        for (i = 0; i < m; i++)
        {
+               PyObject   *rowtuple;
+               int                     j;
+
                if (!(rowtuple = PyTuple_New(n)))
                {
                        Py_DECREF(reslist);
@@ -3275,10 +3407,7 @@
 
                for (j = 0; j < n; j++)
                {
-                       int                     k;
-                       char       *s = PQgetvalue(self->result, i, j);
-                       char            cashbuf[64];
-                       PyObject   *tmp_obj;
+                       PyObject * val;
 
                        if (PQgetisnull(self->result, i, j))
                        {
@@ -3286,17 +3415,24 @@
                                val = Py_None;
                        }
                        else
-                               switch (typ[j])
+                       {
+                               char       *s = PQgetvalue(self->result, i, j);
+                               char            cashbuf[64];
+                               int                     k;
+                               Py_ssize_t      size;
+                               PyObject   *tmp_obj;
+
+                               switch (coltypes[j])
                                {
-                                       case 1:  /* int2/4 */
+                                       case PYGRES_INT:
                                                val = PyInt_FromString(s, NULL, 
10);
                                                break;
 
-                                       case 2:  /* int8 */
+                                       case PYGRES_LONG:
                                                val = PyLong_FromString(s, 
NULL, 10);
                                                break;
 
-                                       case 3:  /* float/double */
+                                       case PYGRES_FLOAT:
                                                tmp_obj = PyStr_FromString(s);
 #if IS_PY3
                                                val = 
PyFloat_FromString(tmp_obj);
@@ -3306,13 +3442,13 @@
                                                Py_DECREF(tmp_obj);
                                                break;
 
-                                       case 5:  /* money */
+                                       case PYGRES_MONEY:
                                                /* convert to decimal only if 
decimal point is set */
                                                if (!decimal_point) goto 
default_case;
 
                                                for (k = 0;
-                                                        *s && k < 
sizeof(cashbuf) / sizeof(cashbuf[0]) - 1;
-                                                        s++)
+                                                       *s && k < 
sizeof(cashbuf)/sizeof(cashbuf[0]) - 1;
+                                                       s++)
                                                {
                                                        if (*s >= '0' && *s <= 
'9')
                                                                cashbuf[k++] = 
*s;
@@ -3325,7 +3461,7 @@
                                                s = cashbuf;
 
                                        /* FALLTHROUGH */ /* no break */
-                                       case 4:  /* numeric */
+                                       case PYGRES_DECIMAL:
                                                if (decimal)
                                                {
                                                        tmp_obj = 
Py_BuildValue("(s)", s);
@@ -3345,23 +3481,19 @@
 
                                        default:
                                        default_case:
+                                               size = 
PQgetlength(self->result, i, j);
 #if IS_PY3
-                                               if (encoding == 
pg_encoding_utf8)
-                                                       val = 
PyUnicode_DecodeUTF8(s, strlen(s), "strict");
-                                               else if (encoding == 
pg_encoding_latin1)
-                                                       val = 
PyUnicode_DecodeLatin1(s, strlen(s), "strict");
-                                               else if (encoding == 
pg_encoding_ascii)
-                                                       val = 
PyUnicode_DecodeASCII(s, strlen(s), "strict");
+                                               if (PQfformat(self->result, j) 
== 0) /* text */
+                                               {
+                                                       val = 
get_decoded_string(s, size, encoding);
+                                                       if (!val) /* cannot 
decode */
+                                                               val = 
PyBytes_FromStringAndSize(s, size);
+                                               }
                                                else
-                                                       val = 
PyUnicode_Decode(s, strlen(s),
-                                                               encoding_name, 
"strict");
-                                               if (!val)
-                                                       val = 
PyBytes_FromString(s);
-#else
-                                               val = PyBytes_FromString(s);
 #endif
-                                               break;
+                                               val = 
PyBytes_FromStringAndSize(s, size);
                                }
+                       }
 
                        if (!val)
                        {
@@ -3378,7 +3510,7 @@
        }
 
 exit:
-       free(typ);
+       free(coltypes);
 
        /* returns list */
        return reslist;
@@ -3393,17 +3525,13 @@
 static PyObject *
 queryDictResult(queryObject *self, PyObject *args)
 {
-       PyObject   *dict,
-                          *reslist,
-                          *val;
+       PyObject   *reslist;
        int                     i,
-                               j,
                                m,
                                n,
-                          *typ;
+                          *coltypes;
 #if IS_PY3
        int                     encoding;
-       const char *encoding_name=NULL;
 #endif
 
        /* checks args (args == NULL for an internal call) */
@@ -3414,23 +3542,23 @@
                return NULL;
        }
 
-#if IS_PY3
-       encoding = self->encoding;
-       if (encoding != pg_encoding_utf8 && encoding != pg_encoding_latin1
-                       && encoding != pg_encoding_ascii)
-               /* should be translated to Python here */
-               encoding_name = pg_encoding_to_char(encoding);
-#endif
-
        /* stores result in list */
        m = PQntuples(self->result);
        n = PQnfields(self->result);
-       reslist = PyList_New(m);
+       if (!(reslist = PyList_New(m)))
+               return NULL;
+
+#if IS_PY3
+       encoding = self->encoding;
+#endif
 
-       typ = get_type_array(self->result, n);
+       coltypes = get_type_array(self->result, n);
 
        for (i = 0; i < m; i++)
        {
+               PyObject   *dict;
+               int                     j;
+
                if (!(dict = PyDict_New()))
                {
                        Py_DECREF(reslist);
@@ -3440,10 +3568,7 @@
 
                for (j = 0; j < n; j++)
                {
-                       int                     k;
-                       char       *s = PQgetvalue(self->result, i, j);
-                       char            cashbuf[64];
-                       PyObject   *tmp_obj;
+                       PyObject * val;
 
                        if (PQgetisnull(self->result, i, j))
                        {
@@ -3451,17 +3576,24 @@
                                val = Py_None;
                        }
                        else
-                               switch (typ[j])
+                       {
+                               char       *s = PQgetvalue(self->result, i, j);
+                               char            cashbuf[64];
+                               int                     k;
+                               Py_ssize_t      size;
+                               PyObject   *tmp_obj;
+
+                               switch (coltypes[j])
                                {
-                                       case 1:  /* int2/4 */
+                                       case PYGRES_INT:
                                                val = PyInt_FromString(s, NULL, 
10);
                                                break;
 
-                                       case 2:  /* int8 */
+                                       case PYGRES_LONG:
                                                val = PyLong_FromString(s, 
NULL, 10);
                                                break;
 
-                                       case 3:  /* float/double */
+                                       case PYGRES_FLOAT:
                                                tmp_obj = PyBytes_FromString(s);
 #if IS_PY3
                                                val = 
PyFloat_FromString(tmp_obj);
@@ -3471,13 +3603,13 @@
                                                Py_DECREF(tmp_obj);
                                                break;
 
-                                       case 5:  /* money */
+                                       case PYGRES_MONEY:
                                                /* convert to decimal only if 
decimal point is set */
                                                if (!decimal_point) goto 
default_case;
 
                                                for (k = 0;
-                                                        *s && k < 
sizeof(cashbuf) / sizeof(cashbuf[0]) - 1;
-                                                        s++)
+                                                       *s && k < 
sizeof(cashbuf)/sizeof(cashbuf[0]) - 1;
+                                                       s++)
                                                {
                                                        if (*s >= '0' && *s <= 
'9')
                                                                cashbuf[k++] = 
*s;
@@ -3490,7 +3622,7 @@
                                                s = cashbuf;
 
                                        /* FALLTHROUGH */ /* no break */
-                                       case 4:  /* numeric */
+                                       case PYGRES_DECIMAL:
                                                if (decimal)
                                                {
                                                        tmp_obj = 
Py_BuildValue("(s)", s);
@@ -3510,23 +3642,19 @@
 
                                        default:
                                        default_case:
+                                               size = 
PQgetlength(self->result, i, j);
 #if IS_PY3
-                                               if (encoding == 
pg_encoding_utf8)
-                                                       val = 
PyUnicode_DecodeUTF8(s, strlen(s), "strict");
-                                               else if (encoding == 
pg_encoding_latin1)
-                                                       val = 
PyUnicode_DecodeLatin1(s, strlen(s), "strict");
-                                               else if (encoding == 
pg_encoding_ascii)
-                                                       val = 
PyUnicode_DecodeASCII(s, strlen(s), "strict");
+                                               if (PQfformat(self->result, j) 
== 0) /* text */
+                                               {
+                                                       val = 
get_decoded_string(s, size, encoding);
+                                                       if (!val) /* cannot 
decode */
+                                                               val = 
PyBytes_FromStringAndSize(s, size);
+                                               }
                                                else
-                                                       val = 
PyUnicode_Decode(s, strlen(s),
-                                                               encoding_name, 
"strict");
-                                               if (!val)
-                                                       val = 
PyBytes_FromString(s);
-#else
-                                               val = PyBytes_FromString(s);
 #endif
-                                               break;
+                                               val = 
PyBytes_FromStringAndSize(s, size);
                                }
+                       }
 
                        if (!val)
                        {
@@ -3544,7 +3672,7 @@
        }
 
 exit:
-       free(typ);
+       free(coltypes);
 
        /* returns list */
        return reslist;
@@ -3653,7 +3781,8 @@
 
 /* get the list of notice attributes */
 static PyObject *
-noticeDir(noticeObject *self) {
+noticeDir(noticeObject *self)
+{
        PyObject *attrs;
 
        attrs = PyObject_Dir(PyObject_Type((PyObject *)self));
@@ -3765,14 +3894,34 @@
 static PyObject *
 pgEscapeString(PyObject *self, PyObject *args)
 {
-       char *from; /* our string argument */
-       char *to=NULL; /* the result */
-       int from_length; /* length of string */
-       int to_length; /* length of result */
-       PyObject *ret; /* string object to return */
+       PyObject   *from_obj, /* the object that was passed in */
+                          *to_obj; /* string object to return */
+       char       *from=NULL, /* our string argument as encoded string */
+                          *to; /* the result as encoded string */
+       Py_ssize_t      from_length; /* length of string */
+       size_t          to_length; /* length of result */
+       int                     encoding = -1; /* client encoding */
 
-       if (!PyArg_ParseTuple(args, "s#", &from, &from_length))
+       if (!PyArg_ParseTuple(args, "O", &from_obj))
+               return NULL;
+
+       if (PyBytes_Check(from_obj))
+       {
+               PyBytes_AsStringAndSize(from_obj, &from, &from_length);
+       }
+       else if (PyUnicode_Check(from_obj))
+       {
+               encoding = pg_encoding_ascii;
+               from_obj = get_encoded_string(from_obj, encoding);
+               if (!from_obj) return NULL; /* pass the UnicodeEncodeError */
+               PyBytes_AsStringAndSize(from_obj, &from, &from_length);
+       }
+       if (!from)
+       {
+               PyErr_SetString(PyExc_TypeError, "escape_string() expects a 
string.");
                return NULL;
+       }
+
        to_length = 2*from_length + 1;
        if (to_length < from_length) /* overflow */
        {
@@ -3781,12 +3930,14 @@
        }
        to = (char *)malloc(to_length);
        to_length = (int)PQescapeString(to, from, (size_t)from_length);
-       ret = Py_BuildValue("s#", to, to_length);
+
+       if (encoding == -1)
+               to_obj = PyBytes_FromStringAndSize(to, to_length);
+       else
+               to_obj = get_decoded_string(to, to_length, encoding);
        if (to)
                free(to);
-       if (!ret) /* pass on exception */
-               return NULL;
-       return ret;
+       return to_obj;
 }
 
 /* escape bytea */
@@ -3796,21 +3947,44 @@
 static PyObject *
 pgEscapeBytea(PyObject *self, PyObject *args)
 {
-       unsigned char *from; /* our string argument */
-       unsigned char *to; /* the result */
-       int from_length; /* length of string */
-       size_t to_length; /* length of result */
-       PyObject *ret; /* string object to return */
+       PyObject   *from_obj, /* the object that was passed in */
+                          *to_obj; /* string object to return */
+       char       *from=NULL, /* our string argument as encoded string */
+                          *to; /* the result as encoded string */
+       Py_ssize_t      from_length; /* length of string */
+       size_t          to_length; /* length of result */
+       int                     encoding = -1; /* client encoding */
 
-       if (!PyArg_ParseTuple(args, "s#", &from, &from_length))
+       if (!PyArg_ParseTuple(args, "O", &from_obj))
                return NULL;
-       to = PQescapeBytea(from, (int)from_length, &to_length);
-       ret = Py_BuildValue("s", to);
-       if (to)
-               PQfreemem((void *)to);
-       if (!ret) /* pass on exception */
+
+       if (PyBytes_Check(from_obj))
+       {
+               PyBytes_AsStringAndSize(from_obj, &from, &from_length);
+       }
+       else if (PyUnicode_Check(from_obj))
+       {
+               encoding = pg_encoding_ascii;
+               from_obj = get_encoded_string(from_obj, encoding);
+               if (!from_obj) return NULL; /* pass the UnicodeEncodeError */
+               PyBytes_AsStringAndSize(from_obj, &from, &from_length);
+       }
+       if (!from)
+       {
+               PyErr_SetString(PyExc_TypeError, "escape_bytea() expects a 
string.");
                return NULL;
-       return ret;
+       }
+
+       to = (char *)PQescapeBytea(
+               (unsigned char*)from, (size_t)from_length, &to_length);
+
+       if (encoding == -1)
+               to_obj = PyBytes_FromStringAndSize(to, to_length - 1);
+       else
+               to_obj = get_decoded_string(to, to_length - 1, encoding);
+       if (to)
+               PQfreemem(to);
+       return to_obj;
 }
 
 /* unescape bytea */
@@ -3820,23 +3994,38 @@
 static PyObject
 *pgUnescapeBytea(PyObject *self, PyObject *args)
 {
-       unsigned char *from; /* our string argument */
-       unsigned char *to; /* the result */
-       int from_length; /* length of string */
-       size_t to_length; /* length of result string */
-       PyObject *ret; /* string object to return */
-
-       if (!PyArg_ParseTuple(args, "s#", &from, &from_length))
-               return NULL;
-       to = PQunescapeBytea(from, &to_length);
-       if (!to)
-           return NULL;
-       ret = Py_BuildValue("s#", to, (int)to_length);
-       if (to)
-           PQfreemem((void *)to);
-       if (!ret) /* pass on exception */
+       PyObject   *from_obj, /* the object that was passed in */
+                          *to_obj; /* string object to return */
+       char       *from=NULL, /* our string argument as encoded string */
+                          *to; /* the result as encoded string */
+       Py_ssize_t      from_length; /* length of string */
+       size_t          to_length; /* length of result */
+
+       if (!PyArg_ParseTuple(args, "O", &from_obj))
                return NULL;
-       return ret;
+
+       if (PyBytes_Check(from_obj))
+       {
+               PyBytes_AsStringAndSize(from_obj, &from, &from_length);
+       }
+       else if (PyUnicode_Check(from_obj))
+       {
+               from_obj = get_encoded_string(from_obj, pg_encoding_ascii);
+               if (!from_obj) return NULL; /* pass the UnicodeEncodeError */
+               PyBytes_AsStringAndSize(from_obj, &from, &from_length);
+       }
+       if (!from)
+       {
+               PyErr_SetString(PyExc_TypeError, "unescape_bytea() expects a 
string.");
+               return NULL;
+       }
+
+       to = (char *)PQunescapeBytea((unsigned char*)from, &to_length);
+
+       to_obj = PyBytes_FromStringAndSize(to, to_length);
+       if (to)
+               PQfreemem(to);
+       return to_obj;
 }
 
 /* set decimal point */
@@ -3850,14 +4039,16 @@
        char *s = NULL;
 
        /* gets arguments */
-       if (PyArg_ParseTuple(args, "z", &s)) {
+       if (PyArg_ParseTuple(args, "z", &s))
+       {
                if (!s)
                        s = "\0";
                else if (*s && (*(s+1) || !strchr(".,;: '*/_`|", *s)))
                        s = NULL;
        }
 
-       if (s) {
+       if (s)
+       {
                decimal_point = *s;
                Py_INCREF(Py_None); ret = Py_None;
        } else {
@@ -3880,7 +4071,8 @@
 
        if (PyArg_ParseTuple(args, ""))
        {
-               if (decimal_point) {
+               if (decimal_point)
+               {
                        s[0] = decimal_point; s[1] = '\0';
                        ret = PyStr_FromString(s);
                } else {
_______________________________________________
PyGreSQL mailing list
[email protected]
https://mail.vex.net/mailman/listinfo.cgi/pygresql

Reply via email to