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