Update of /usr/cvs/Public/pygresql/module
In directory druid.net:/tmp/cvs-serv22355/module
Modified Files:
TEST_PyGreSQL_classic.py pg.py pgdb.py test_pg.py
Log Message:
Added support for PQescapeStringConn() and PQescapeByteaConn(). Promoted the
_quote() functions to methods of the connection. Made them use the former
functions instead of manually and faulitly handling backslashes and quotes.
To see the diffs for this commit:
http://www.druid.net/pygresql/viewcvs.cgi/cvs/pygresql/module/TEST_PyGreSQL_classic.py.diff?r1=1.13&r2=1.14
Index: TEST_PyGreSQL_classic.py
===================================================================
RCS file: /usr/cvs/Public/pygresql/module/TEST_PyGreSQL_classic.py,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -r1.13 -r1.14
--- TEST_PyGreSQL_classic.py 21 Nov 2008 13:10:51 -0000 1.13
+++ TEST_PyGreSQL_classic.py 21 Nov 2008 21:17:53 -0000 1.14
@@ -138,7 +138,7 @@
self.assertEqual(r['dvar'], 456)
def test_quote(self):
- from pg import _quote
+ _quote = db._quote
self.assertEqual(_quote(1, 'int'), "1")
self.assertEqual(_quote(1, 'text'), "'1'")
self.assertEqual(_quote(1, 'num'), "1")
http://www.druid.net/pygresql/viewcvs.cgi/cvs/pygresql/module/pg.py.diff?r1=1.62&r2=1.63
Index: pg.py
===================================================================
RCS file: /usr/cvs/Public/pygresql/module/pg.py,v
retrieving revision 1.62
retrieving revision 1.63
diff -u -r1.62 -r1.63
--- pg.py 21 Nov 2008 19:25:27 -0000 1.62
+++ pg.py 21 Nov 2008 21:17:53 -0000 1.63
@@ -5,7 +5,7 @@
# Written by D'Arcy J.M. Cain
# Improved by Christoph Zwerschke
#
-# $Id: pg.py,v 1.62 2008/11/21 19:25:27 cito Exp $
+# $Id: pg.py,v 1.63 2008/11/21 21:17:53 cito Exp $
#
"""PyGreSQL classic interface.
@@ -32,58 +32,6 @@
# Auxiliary functions which are independent from a DB connection:
-def _quote_text(d):
- """Quote text value."""
- if not isinstance(d, basestring):
- d = str(d)
- return "'%s'" % d.replace("\\", "\\\\").replace("'", "''")
-
-_bool_true = frozenset('t true 1 y yes on'.split())
-
-def _quote_bool(d):
- """Quote boolean value."""
- if isinstance(d, basestring):
- if not d:
- return 'NULL'
- d = d.lower() in _bool_true
- else:
- d = bool(d)
- return ("'f'", "'t'")[d]
-
-_date_literal = frozenset('current_date current_time'
- ' current_timestamp localtime localtimestamp'.split())
-
-def _quote_date(d):
- """Quote date value."""
- if not d:
- return 'NULL'
- if isinstance(d, basestring) and d.lower() in _date_literal:
- return d
- return _quote_text(d)
-
-def _quote_num(d):
- """Quote numeric value."""
- if not d:
- return 'NULL'
- return str(d)
-
-def _quote_money(d):
- """Quote money value."""
- if not d:
- return 'NULL'
- return "'%.2f'" % float(d)
-
-_quote_funcs = dict( # quote functions for each type
- text=_quote_text, bool=_quote_bool, date=_quote_date,
- int=_quote_num, num=_quote_num, float=_quote_num, money=_quote_money)
-
-def _quote(d, t):
- """Return quotes if needed."""
- if d is None:
- return 'NULL'
- else:
- return _quote_funcs.get(t, _quote_text)(d)
-
def _is_quoted(s):
"""Check whether this string is a quoted identifier."""
s = s.replace('_', 'a')
@@ -188,9 +136,7 @@
else:
raise InternalError('Connection is not valid')
- # escape_string and escape_bytea exist as methods,
- # so we define unescape_bytea as a method as well
- unescape_bytea = staticmethod(unescape_bytea)
+ # Auxiliary methods
def _do_debug(self, s):
"""Print a debug message."""
@@ -202,46 +148,61 @@
elif callable(self.debug):
self.debug(s)
- def close(self):
- """Close the database connection."""
- # Wraps shared library function so we can track state.
- if self._closeable:
- if self.db:
- self.db.close()
- self.db = None
- else:
- raise InternalError('Connection already closed')
+ def _quote_text(self, d):
+ """Quote text value."""
+ if not isinstance(d, basestring):
+ d = str(d)
+ return "'%s'" % self.escape_string(d)
+
+ _bool_true = frozenset('t true 1 y yes on'.split())
+
+ def _quote_bool(self, d):
+ """Quote boolean value."""
+ if isinstance(d, basestring):
+ if not d:
+ return 'NULL'
+ d = d.lower() in self._bool_true
+ else:
+ d = bool(d)
+ return ("'f'", "'t'")[d]
- def reopen(self):
- """Reopen connection to the database.
+ _date_literals = frozenset('current_date current_time'
+ ' current_timestamp localtime localtimestamp'.split())
- Used in case we need another connection to the same database.
- Note that we can still reopen a database that we have closed.
+ def _quote_date(self, d):
+ """Quote date value."""
+ if not d:
+ return 'NULL'
+ if isinstance(d, basestring) and d.lower() in self._date_literals:
+ return d
+ return self._quote_text(d)
- """
- # There is no such shared library function.
- if self._closeable:
- db = connect(*self._args[0], **self._args[1])
- if self.db:
- self.db.close()
- self.db = db
+ def _quote_num(self, d):
+ """Quote numeric value."""
+ if not d:
+ return 'NULL'
+ return str(d)
- def query(self, qstr):
- """Executes a SQL command string.
+ def _quote_money(self, d):
+ """Quote money value."""
+ if not d:
+ return 'NULL'
+ return "'%.2f'" % float(d)
- This method simply sends a SQL query to the database. If the query is
- an insert statement, the return value is the OID of the newly
- inserted row. If it is otherwise a query that does not return a result
- (ie. is not a some kind of SELECT statement), it returns None.
- Otherwise, it returns a pgqueryobject that can be accessed via the
- getresult or dictresult method or simply printed.
+ _quote_funcs = dict( # quote methods for each type
+ text=_quote_text, bool=_quote_bool, date=_quote_date,
+ int=_quote_num, num=_quote_num, float=_quote_num,
+ money=_quote_money)
- """
- # Wraps shared library function for debugging.
- if not self.db:
- raise InternalError('Connection is not valid')
- self._do_debug(qstr)
- return self.db.query(qstr)
+ def _quote(self, d, t):
+ """Return quotes if needed."""
+ if d is None:
+ return 'NULL'
+ try:
+ quote_func = self._quote_funcs[t]
+ except KeyError:
+ quote_func = self._quote_funcs['text']
+ return quote_func(self, d)
def _split_schema(self, cl):
"""Return schema and name of object separately.
@@ -282,6 +243,53 @@
schema = 'public'
return schema, cl
+ # Public methods
+
+ # escape_string and escape_bytea exist as methods,
+ # so we define unescape_bytea as a method as well
+ unescape_bytea = staticmethod(unescape_bytea)
+
+ def close(self):
+ """Close the database connection."""
+ # Wraps shared library function so we can track state.
+ if self._closeable:
+ if self.db:
+ self.db.close()
+ self.db = None
+ else:
+ raise InternalError('Connection already closed')
+
+ def reopen(self):
+ """Reopen connection to the database.
+
+ Used in case we need another connection to the same database.
+ Note that we can still reopen a database that we have closed.
+
+ """
+ # There is no such shared library function.
+ if self._closeable:
+ db = connect(*self._args[0], **self._args[1])
+ if self.db:
+ self.db.close()
+ self.db = db
+
+ def query(self, qstr):
+ """Executes a SQL command string.
+
+ This method simply sends a SQL query to the database. If the query is
+ an insert statement, the return value is the OID of the newly
+ inserted row. If it is otherwise a query that does not return a result
+ (ie. is not a some kind of SELECT statement), it returns None.
+ Otherwise, it returns a pgqueryobject that can be accessed via the
+ getresult or dictresult method or simply printed.
+
+ """
+ # Wraps shared library function for debugging.
+ if not self.db:
+ raise InternalError('Connection is not valid')
+ self._do_debug(qstr)
+ return self.db.query(qstr)
+
def pkey(self, cl, newpkey = None):
"""This method gets or sets the primary key of a class.
@@ -451,12 +459,12 @@
else:
q = 'SELECT %s FROM %s WHERE %s=%s LIMIT 1' % \
(','.join(fnames.keys()), qcl, \
- keyname, _quote(k, fnames[keyname]))
+ keyname, self._quote(k, fnames[keyname]))
self._do_debug(q)
res = self.db.query(q).dictresult()
if not res:
raise DatabaseError('No such record in %s where %s=%s'
- % (qcl, keyname, _quote(k, fnames[keyname])))
+ % (qcl, keyname, self._quote(k, fnames[keyname])))
for k, d in res[0].items():
if k == 'oid':
k = foid
@@ -488,7 +496,7 @@
n = []
for f in fnames.keys():
if f != 'oid' and f in a:
- t.append(_quote(a[f], fnames[f]))
+ t.append(self._quote(a[f], fnames[f]))
n.append('"%s"' % f)
q = 'INSERT INTO %s (%s) VALUES (%s)' % \
(qcl, ','.join(n), ','.join(t))
@@ -548,7 +556,7 @@
fnames = self.get_attnames(qcl)
for ff in fnames.keys():
if ff != 'oid' and ff in a:
- v.append('%s=%s' % (ff, _quote(a[ff], fnames[ff])))
+ v.append('%s=%s' % (ff, self._quote(a[ff], fnames[ff])))
if v == []:
return None
q = 'UPDATE %s SET %s WHERE %s' % (qcl, ','.join(v), where)
http://www.druid.net/pygresql/viewcvs.cgi/cvs/pygresql/module/pgdb.py.diff?r1=1.47&r2=1.48
Index: pgdb.py
===================================================================
RCS file: /usr/cvs/Public/pygresql/module/pgdb.py,v
retrieving revision 1.47
retrieving revision 1.48
diff -u -r1.47 -r1.48
--- pgdb.py 21 Nov 2008 13:10:51 -0000 1.47
+++ pgdb.py 21 Nov 2008 21:17:53 -0000 1.48
@@ -4,7 +4,7 @@
#
# Written by D'Arcy J.M. Cain
#
-# $Id: pgdb.py,v 1.47 2008/11/21 13:10:51 cito Exp $
+# $Id: pgdb.py,v 1.48 2008/11/21 21:17:53 cito Exp $
#
"""pgdb - DB-API 2.0 compliant module for PygreSQL.
@@ -145,47 +145,15 @@
return res
-class _quoteitem(dict):
- """Dictionary with auto quoting of its items."""
+class _quotedict(dict):
+ """Dictionary with auto quoting of its items.
- def __getitem__(self, key):
- return _quote(super(_quoteitem, self).__getitem__(key))
-
-
-def _quote(val):
- """Quote value depending on its type."""
- if isinstance(val, datetime):
- val = str(val)
- elif isinstance(val, unicode):
- val = val.encode( 'utf-8' )
- if isinstance(val, str):
- val = "'%s'" % str(val).replace("\\", "\\\\").replace("'", "''")
- elif isinstance(val, (int, long, float)):
- pass
- elif val is None:
- val = 'NULL'
- elif isinstance(val, (list, tuple)):
- val = '(%s)' % ','.join(map(lambda v: str(_quote(v)), val))
- elif Decimal is not float and isinstance(val, Decimal):
- pass
- elif hasattr(val, '__pg_repr__'):
- val = val.__pg_repr__()
- else:
- raise InterfaceError('do not know how to handle type %s' % type(val))
- return val
-
-
-def _quoteparams(string, params):
- """Quote parameters.
-
- This function works for both mappings and sequences.
+ The quote attribute must be set to the desired quote function.
"""
- if hasattr(params, 'has_key'):
- params = _quoteitem(params)
- else:
- params = tuple(map(_quote, params))
- return string % params
+
+ def __getitem__(self, key):
+ return self.quote(super(_quotedict, self).__getitem__(key))
### Cursor Object
@@ -204,6 +172,41 @@
self.arraysize = 1
self.lastrowid = None
+ def _quote(self, val):
+ """Quote value depending on its type."""
+ if isinstance(val, datetime):
+ val = str(val)
+ elif isinstance(val, unicode):
+ val = val.encode( 'utf8' )
+ if isinstance(val, str):
+ val = "'%s'" % self._cnx.escape_string(val)
+ elif isinstance(val, (int, long, float)):
+ pass
+ elif val is None:
+ val = 'NULL'
+ elif isinstance(val, (list, tuple)):
+ val = '(%s)' % ','.join(map(lambda v: str(self._quote(v)), val))
+ elif Decimal is not float and isinstance(val, Decimal):
+ pass
+ elif hasattr(val, '__pg_repr__'):
+ val = val.__pg_repr__()
+ else:
+ raise InterfaceError('do not know how to handle type %s' %
type(val))
+ return val
+
+ def _quoteparams(self, string, params):
+ """Quote parameters.
+
+ This function works for both mappings and sequences.
+
+ """
+ if isinstance(params, dict):
+ params = _quotedict(params)
+ params.quote = self._quote
+ else:
+ params = tuple(map(self._quote, params))
+ return string % params
+
def row_factory(row):
"""Process rows before they are returned.
@@ -260,7 +263,7 @@
self._dbcnx._tnx = True
for params in param_seq:
if params:
- sql = _quoteparams(operation, params)
+ sql = self._quoteparams(operation, params)
else:
sql = operation
rows = self._src.execute(sql)
http://www.druid.net/pygresql/viewcvs.cgi/cvs/pygresql/module/test_pg.py.diff?r1=1.16&r2=1.17
Index: test_pg.py
===================================================================
RCS file: /usr/cvs/Public/pygresql/module/test_pg.py,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -r1.16 -r1.17
--- test_pg.py 21 Nov 2008 19:25:27 -0000 1.16
+++ test_pg.py 21 Nov 2008 21:17:53 -0000 1.17
@@ -4,7 +4,7 @@
#
# Written by Christoph Zwerschke
#
-# $Id: test_pg.py,v 1.16 2008/11/21 19:25:27 cito Exp $
+# $Id: test_pg.py,v 1.17 2008/11/21 21:17:53 cito Exp $
#
"""Test the classic PyGreSQL interface in the pg module.
@@ -27,15 +27,17 @@
import pg
import unittest
+debug = 0
+
# Try to load german locale for Umlaut tests
german = 1
try:
import locale
locale.setlocale(locale.LC_ALL, ('de', 'latin1'))
-except:
+except Exception:
try:
locale.setlocale(locale.LC_ALL, 'german')
- except:
+ except Exception:
german = 0
try:
@@ -68,71 +70,6 @@
class TestAuxiliaryFunctions(unittest.TestCase):
"""Test the auxiliary functions external to the connection class."""
- def testQuote(self):
- f = pg._quote
- self.assertEqual(f(None, None), 'NULL')
- self.assertEqual(f(None, 'int'), 'NULL')
- self.assertEqual(f(None, 'float'), 'NULL')
- self.assertEqual(f(None, 'num'), 'NULL')
- self.assertEqual(f(None, 'money'), 'NULL')
- self.assertEqual(f(None, 'bool'), 'NULL')
- self.assertEqual(f(None, 'date'), 'NULL')
- self.assertEqual(f('', 'int'), 'NULL')
- self.assertEqual(f('', 'float'), 'NULL')
- self.assertEqual(f('', 'num'), 'NULL')
- self.assertEqual(f('', 'money'), 'NULL')
- self.assertEqual(f('', 'bool'), 'NULL')
- self.assertEqual(f('', 'date'), 'NULL')
- self.assertEqual(f('', 'text'), "''")
- self.assertEqual(f(123456789, 'int'), '123456789')
- self.assertEqual(f(123456987, 'num'), '123456987')
- self.assertEqual(f(1.23654789, 'num'), '1.23654789')
- self.assertEqual(f(12365478.9, 'num'), '12365478.9')
- self.assertEqual(f('123456789', 'num'), '123456789')
- self.assertEqual(f('1.23456789', 'num'), '1.23456789')
- self.assertEqual(f('12345678.9', 'num'), '12345678.9')
- self.assertEqual(f(123, 'money'), "'123.00'")
- self.assertEqual(f('123', 'money'), "'123.00'")
- self.assertEqual(f(123.45, 'money'), "'123.45'")
- self.assertEqual(f('123.45', 'money'), "'123.45'")
- self.assertEqual(f(123.454, 'money'), "'123.45'")
- self.assertEqual(f('123.454', 'money'), "'123.45'")
- self.assertEqual(f(123.456, 'money'), "'123.46'")
- self.assertEqual(f('123.456', 'money'), "'123.46'")
- self.assertEqual(f('f', 'bool'), "'f'")
- self.assertEqual(f('F', 'bool'), "'f'")
- self.assertEqual(f('false', 'bool'), "'f'")
- self.assertEqual(f('False', 'bool'), "'f'")
- self.assertEqual(f('FALSE', 'bool'), "'f'")
- self.assertEqual(f(0, 'bool'), "'f'")
- self.assertEqual(f('0', 'bool'), "'f'")
- self.assertEqual(f('-', 'bool'), "'f'")
- self.assertEqual(f('n', 'bool'), "'f'")
- self.assertEqual(f('N', 'bool'), "'f'")
- self.assertEqual(f('no', 'bool'), "'f'")
- self.assertEqual(f('off', 'bool'), "'f'")
- self.assertEqual(f('t', 'bool'), "'t'")
- self.assertEqual(f('T', 'bool'), "'t'")
- self.assertEqual(f('true', 'bool'), "'t'")
- self.assertEqual(f('True', 'bool'), "'t'")
- self.assertEqual(f('TRUE', 'bool'), "'t'")
- self.assertEqual(f(1, 'bool'), "'t'")
- self.assertEqual(f(2, 'bool'), "'t'")
- self.assertEqual(f(-1, 'bool'), "'t'")
- self.assertEqual(f(0.5, 'bool'), "'t'")
- self.assertEqual(f('1', 'bool'), "'t'")
- self.assertEqual(f('y', 'bool'), "'t'")
- self.assertEqual(f('Y', 'bool'), "'t'")
- self.assertEqual(f('yes', 'bool'), "'t'")
- self.assertEqual(f('on', 'bool'), "'t'")
- self.assertEqual(f('01.01.2000', 'date'), "'01.01.2000'")
- self.assertEqual(f(123, 'text'), "'123'")
- self.assertEqual(f(1.23, 'text'), "'1.23'")
- self.assertEqual(f('abc', 'text'), "'abc'")
- self.assertEqual(f("ab'c", 'text'), "'ab''c'")
- self.assertEqual(f('ab\\c', 'text'), "'ab\\\\c'")
- self.assertEqual(f("a\\b'c", 'text'), "'a\\\\b''c'")
-
def testIsQuoted(self):
f = pg._is_quoted
self.assert_(f('A'))
@@ -383,11 +320,11 @@
dbname = 'template1'
try:
connection = pg.connect(dbname)
- except:
+ except Exception:
self.fail('Cannot connect to database ' + dbname)
try:
connection.close()
- except:
+ except Exception:
self.fail('Cannot close the database connection')
@@ -465,13 +402,13 @@
try:
self.connection.reset()
fail('Reset should give an error for a closed connection')
- except:
+ except Exception:
pass
self.assertRaises(pg.InternalError, self.connection.close)
try:
self.connection.query('select 1')
self.fail('Query should give an error for a closed connection')
- except:
+ except Exception:
pass
self.connection = pg.connect(self.dbname)
@@ -597,7 +534,7 @@
stdout, sys.stdout = sys.stdout, s
try:
print r
- except:
+ except Exception:
pass
sys.stdout = stdout
s.close()
@@ -775,7 +712,7 @@
try:
self.db.reset()
fail('Reset should give an error for a closed connection')
- except:
+ except Exception:
pass
self.assertRaises(pg.InternalError, self.db.close)
self.assertRaises(pg.InternalError, self.db.query, 'select 1')
@@ -812,6 +749,8 @@
dbname = DBTestSuite.dbname
self.dbname = dbname
self.db = pg.DB(dbname)
+ if debug:
+ self.db.debug = 'DEBUG: %s'
def tearDown(self):
self.db.close()
@@ -838,6 +777,71 @@
self.assertEqual(pg.unescape_bytea(
r'O\000ps\377!'), 'O\x00ps\xff!')
+ def testQuote(self):
+ f = self.db._quote
+ self.assertEqual(f(None, None), 'NULL')
+ self.assertEqual(f(None, 'int'), 'NULL')
+ self.assertEqual(f(None, 'float'), 'NULL')
+ self.assertEqual(f(None, 'num'), 'NULL')
+ self.assertEqual(f(None, 'money'), 'NULL')
+ self.assertEqual(f(None, 'bool'), 'NULL')
+ self.assertEqual(f(None, 'date'), 'NULL')
+ self.assertEqual(f('', 'int'), 'NULL')
+ self.assertEqual(f('', 'float'), 'NULL')
+ self.assertEqual(f('', 'num'), 'NULL')
+ self.assertEqual(f('', 'money'), 'NULL')
+ self.assertEqual(f('', 'bool'), 'NULL')
+ self.assertEqual(f('', 'date'), 'NULL')
+ self.assertEqual(f('', 'text'), "''")
+ self.assertEqual(f(123456789, 'int'), '123456789')
+ self.assertEqual(f(123456987, 'num'), '123456987')
+ self.assertEqual(f(1.23654789, 'num'), '1.23654789')
+ self.assertEqual(f(12365478.9, 'num'), '12365478.9')
+ self.assertEqual(f('123456789', 'num'), '123456789')
+ self.assertEqual(f('1.23456789', 'num'), '1.23456789')
+ self.assertEqual(f('12345678.9', 'num'), '12345678.9')
+ self.assertEqual(f(123, 'money'), "'123.00'")
+ self.assertEqual(f('123', 'money'), "'123.00'")
+ self.assertEqual(f(123.45, 'money'), "'123.45'")
+ self.assertEqual(f('123.45', 'money'), "'123.45'")
+ self.assertEqual(f(123.454, 'money'), "'123.45'")
+ self.assertEqual(f('123.454', 'money'), "'123.45'")
+ self.assertEqual(f(123.456, 'money'), "'123.46'")
+ self.assertEqual(f('123.456', 'money'), "'123.46'")
+ self.assertEqual(f('f', 'bool'), "'f'")
+ self.assertEqual(f('F', 'bool'), "'f'")
+ self.assertEqual(f('false', 'bool'), "'f'")
+ self.assertEqual(f('False', 'bool'), "'f'")
+ self.assertEqual(f('FALSE', 'bool'), "'f'")
+ self.assertEqual(f(0, 'bool'), "'f'")
+ self.assertEqual(f('0', 'bool'), "'f'")
+ self.assertEqual(f('-', 'bool'), "'f'")
+ self.assertEqual(f('n', 'bool'), "'f'")
+ self.assertEqual(f('N', 'bool'), "'f'")
+ self.assertEqual(f('no', 'bool'), "'f'")
+ self.assertEqual(f('off', 'bool'), "'f'")
+ self.assertEqual(f('t', 'bool'), "'t'")
+ self.assertEqual(f('T', 'bool'), "'t'")
+ self.assertEqual(f('true', 'bool'), "'t'")
+ self.assertEqual(f('True', 'bool'), "'t'")
+ self.assertEqual(f('TRUE', 'bool'), "'t'")
+ self.assertEqual(f(1, 'bool'), "'t'")
+ self.assertEqual(f(2, 'bool'), "'t'")
+ self.assertEqual(f(-1, 'bool'), "'t'")
+ self.assertEqual(f(0.5, 'bool'), "'t'")
+ self.assertEqual(f('1', 'bool'), "'t'")
+ self.assertEqual(f('y', 'bool'), "'t'")
+ self.assertEqual(f('Y', 'bool'), "'t'")
+ self.assertEqual(f('yes', 'bool'), "'t'")
+ self.assertEqual(f('on', 'bool'), "'t'")
+ self.assertEqual(f('01.01.2000', 'date'), "'01.01.2000'")
+ self.assertEqual(f(123, 'text'), "'123'")
+ self.assertEqual(f(1.23, 'text'), "'1.23'")
+ self.assertEqual(f('abc', 'text'), "'abc'")
+ self.assertEqual(f("ab'c", 'text'), "'ab''c'")
+ self.assertEqual(f('ab\\c', 'text'), "'ab\\\\c'")
+ self.assertEqual(f("a\\b'c", 'text'), "'a\\\\b''c'")
+
def testPkey(self):
smart_ddl(self.db, 'drop table pkeytest0')
smart_ddl(self.db, "create table pkeytest0 ("
_______________________________________________
PyGreSQL mailing list
[email protected]
http://mailman.vex.net/mailman/listinfo/pygresql