|
From: Billy G. A. <bal...@us...> - 2002-12-01 04:59:31
|
Update of /cvsroot/pypgsql/pypgsql/pyPgSQL
In directory sc8-pr-cvs1:/tmp/cvs-serv16107/pyPgSQL
Modified Files:
PgSQL.py
Log Message:
30NOV2002 bga Updated the test cases to account for changes in PostgreSQL 7.3.
28NOV2002 bga Fixed changed PG_TIMESTAMP oid, added PG_TIMESTAMPTZ and
PG_REFCURSOR oids. [Bug #845360] |
--- Reference cursors are now type-casted into cursor objects.
27NOV2002 bga Completed the emulation of a String object for the PgBytea and
PgOther classes. This corrects several problems with PgBytea
concerning comparisons, using PgBytea types as keys in
dictionaries, etc.
Index: PgSQL.py
===================================================================
RCS file: /cvsroot/pypgsql/pypgsql/pyPgSQL/PgSQL.py,v
retrieving revision 1.22
retrieving revision 1.23
diff -C2 -d -r1.22 -r1.23
*** PgSQL.py 11 Nov 2002 03:56:42 -0000 1.22
--- PgSQL.py 1 Dec 2002 04:59:25 -0000 1.23
***************
*** 30,33 ****
--- 30,41 ----
# Date Ini Description |
# --------- --- ------------------------------------------------------- |
+ # 28NOV2002 bga - Fixed changed PG_TIMESTAMP oid, added PG_TIMESTAMPTZ |
+ # and PG_REFCURSOR oids. [Bug #845360] |
+ # - Reference cursors are now type-casted into cursor ob- |
+ # jects.
+ # 27NOV2002 bga - Completed the emulation of a String object for the |
+ # PgBytea and PgOther classes. This corrects several |
+ # problems with PgBytea concerning comparisons, using |
+ # PgBytea types as keys in dictionaries, etc. |
# 10NOV2002 bga - Added the __hash__ function to the PgNumeric class. |
# Cleaned up the code in PgNumeric class and made some |
***************
*** 85,199 ****
# operand. This is no longer the case all precision is |
# retained for the +, -, and * operations. |
! # 03AUG2002 gh Fixed problem that occurs when a query on an OID field |
! # doesn't return any rows. [Bug #589370]. |
! # 29JUL2002 gh Applied patch #569203 and also added __pos__ and |
! # __abs__ special methods to PgNumeric. |
! # 15MAY2002 gh Got rid of redundant building and storing of the |
! # mapping of column names to column positions in the |
! # PgResultSet class. Now, rows of the same query are |
! # instances of a dynamically created class, which has |
! # this mapping as a class attribute instead of an attri- |
! # bute of the instance. This saves a lot of space, and |
! # also slightly increases performance of cursor fetches. |
! # 21APR2002 gh Improved the array parsing, so that it now passes all |
! # the new mean testcases. Added support for parsing |
! # multidimensional arrays. Eventually, the array parsing |
! # code should go as a support function into libpq. |
! # --- Replaced all typechecks with "is" operators instead |
! # of equals. Mark McEahern had a problem with using |
! # pyPgSQL in combination with a FixedPoint class where |
! # the reason was that the FixedPoint class was not com- |
! # parable to None. The consensus on python-list was that |
! # None and all types are singletons, so they should be |
! # checked using "is", which is also faster, because it |
! # only checks for object identity. |
! # 16APR2002 gh Fix the array parsing so it also works with PostgreSQL |
! # versions 7.2.x. The PostgreSQL developers changed the |
! # quoting in the string representation of arrays in 7.2 |
! # beta cycle: strings are now only quoted when otherwise |
! # the array representation would be ambiguous. The new |
! # parseArray() method should handle the old and new way |
! # of quoting in arrays. [Bug #539769]. |
! # 01FEB2002 bga Fixed a couple of problems found by Steven D. Arnold: |
! # 1. A undefined variable was used in a few places where |
! # notices were popped from the notice list. |
! # 2. Comparisons between 2 PgNumerics gave the wrong re- |
! # sult. |
! # 18JAN2002 bga Fixed problem that occurs when the sum() aggregate re- |
! # turns a NULL. [Bug #505162]. |
! # 04DEC2001 bga Added code to implement support for setting PostgreSQL |
! # transaction levels. [Feature Request #481727]. |
! # 03NOV2001 bga It appears that under certain circumstances, PostgreSQL |
! # will not return a precision/scale value for a numeric |
! # column when no result rows are returned [Bug #477792]. |
! # The problem is fixed by using a precision of (30,6) in |
! # case this condition occurs. |
! # 21OCT2001 gh Change the import of DateTime to avoid conflicts with |
! # ZOPE's builtin DateTime module. |
! # 17OCT2001 bga Added code to ensure that creation of a binary object |
! # via the binary() method always occurs within the con- |
! # text of a transaction. |
! # 27SEP2001 bga Change code so that the escaping/quoting is different |
! # if the result is to be used to build PostgreSQL arrays. |
! # --- Modified all the Object._quote methods so that they now |
! # accept an option argument, forArray, which if set to 1 |
! # will cause escaping/quoting for PostgreSQL array use. |
! # --- Re-organized the code use to build PostgreSQL arrays. |
! # 26SEP2001 bga Added additional PostgreSQL types to BINARY, ROWID, etc |
! # --- Fixed problems associated with type casting. |
! # 24SEP2001 bga Changed the method used to quote strings. This new way |
! # does not require the services of pgFixEsc, which was |
! # removed from pgconnection.c. |
! # --- Added support for the PostgreSQL type BYTEA. |
! # --- Improved support for the BYTEA type. |
! # 19SEP2001 bga Cleaned up the logic in hadleArray(). |
! # --- Updated the PgSQL.__doc__ string. |
! # 18SEP2001 bga [Feature Request #462588] Cursor.close() no longer ends |
! # (rollback) the open transaction when the last cursor is |
! # closed. |
! # --- On Connection.close(), reset all the attributes to a |
! # sane state (i.e. None or 0, as appropiate). |
! # --- [Bug #462589] In a fit of optimization, I had intro- |
! # duced a bug that caused comparisons of PgResultSets to |
! # anything to fail. This bug has been squashed. |
! # 03SEP2001 gh Fixed several cases of references to non-existing |
! # globals in PgMoney. |
! # 30AUG2001 bga Previously, I was not building up the description |
! # attibute if no rows were returned. The description |
! # attribute is now built on the first successful select |
! # or cursor fetch. |
! # 23AUG2001 bga [Bug #454653] Change the code to rollback an open trans-|
! # action, if any, when the last cursor of a connection is |
! # closed. |
! # --- Added code that will, if weak references are not avail- |
! # able, remove any cursors that are only referenced from |
! # the connection.cursors list. This is in effect garbage |
! # collection for deleted cursors. The gabage collection |
! # of orphaned cursor will occur at the following points: |
! # 1. After a new cursor is created. |
! # 2. When a cursor is closed. |
! # 3. When the connection.autocommit value is changed. |
! # --- Changed cursor.isClosed to cursor.closed. Why, closed |
! # is used in other object (file and PgLargeObject, for |
! # example) and I thought I would follow the trend (i.e. |
! # for no good reason). |
! # --- Change from the use of hash() to generate the portal |
! # name to the use of id(). |
! # 10AUG2001 bga [Bug #449743]Added code to trap a failure to connect to |
! # a database and delete the Connection object on failure. |
! # This resolves the problem reported by Adam Buraczewski. |
! # --- Fixed a bug in fetchmany() that could return more than |
! # the requested rows if PostgreSQL Portals aren't used in |
! # the query. |
! # 03AUG2001 bga The PgVer object is now implemented in C (as PgVersion) |
! # and been moved to the libpq module. This was done to |
! # support handling of large objects in libpq based on the |
! # PostgreSQL version. |
! # 01AUG2001 bga Changed the code so that execute will not attempt to do |
! # parameter subsitution on the query string if no addi- |
! # tional parameters are passed to execute. |
! # Modify PgSQL to reflect that fact. |
# --------- bga Remove prior comments to reduce the size of the flower |
! # box. See revision 1.19 for earlier comments. |
#--(H-)-----------------------------------------------------------------+
"""
--- 93,122 ----
# operand. This is no longer the case all precision is |
# retained for the +, -, and * operations. |
! # 03AUG2002 gh - Fixed problem that occurs when a query on an OID |
! # field doesn't return any rows. [Bug #589370]. |
! # 29JUL2002 gh - Applied patch #569203 and also added __pos__ and |
! # __abs__ special methods to PgNumeric. |
! # 15MAY2002 gh - Got rid of redundant building and storing of the |
! # mapping of column names to column positions in the |
! # PgResultSet class. Now, rows of the same query are |
! # instances of a dynamically created class, which has |
! # this mapping as a class attribute instead of an at- |
! # tribute of the instance. This saves a lot of space, |
! # and also slightly increases performance of cursor |
! # fetches. |
! # 21APR2002 gh - Improved the array parsing, so that it now passes all |
! # the new mean testcases. Added support for parsing |
! # multidimensional arrays. Eventually, the array par- |
! # sing code should go as a support function into libpq. |
! # - Replaced all typechecks with "is" operators instead |
! # of equals. Mark McEahern had a problem with using |
! # pyPgSQL in combination with a FixedPoint class where |
! # the reason was that the FixedPoint class was not com- |
! # parable to None. The consensus on python-list was |
! # that None and all types are singletons, so they |
! # should be checked using "is", which is also faster, |
! # because it only checks for object identity. |
# --------- bga Remove prior comments to reduce the size of the flower |
! # box. See revision 1.22 for earlier comments. |
#--(H-)-----------------------------------------------------------------+
"""
***************
*** 478,483 ****
DATETIME = DBAPITypeObject('DATETIME', PG_DATE, PG_TIME, PG_TIMESTAMP,
! PG_ABSTIME, PG_RELTIME, PG_INTERVAL,
! PG_TINTERVAL)
NUMBER = DBAPITypeObject('NUMBER', PG_INT8, PG_INT2, PG_INT4, PG_FLOAT4,
--- 401,406 ----
DATETIME = DBAPITypeObject('DATETIME', PG_DATE, PG_TIME, PG_TIMESTAMP,
! PG_TIMESTAMPTZ, PG_ABSTIME, PG_RELTIME,
! PG_INTERVAL, PG_TINTERVAL)
NUMBER = DBAPITypeObject('NUMBER', PG_INT8, PG_INT2, PG_INT4, PG_FLOAT4,
***************
*** 497,501 ****
OTHER = DBAPITypeObject('OTHER', PG_POINT, PG_LSEG, PG_PATH, PG_BOX,
PG_POLYGON, PG_LINE, PG_CIDR, PG_CIRCLE,
! PG_INET, PG_MACADDR, PG_ACLITEM)
#-----------------------------------------------------------------------+
--- 420,425 ----
OTHER = DBAPITypeObject('OTHER', PG_POINT, PG_LSEG, PG_PATH, PG_BOX,
PG_POLYGON, PG_LINE, PG_CIDR, PG_CIRCLE,
! PG_INET, PG_MACADDR, PG_ACLITEM,
! PG_REFCURSOR)
#-----------------------------------------------------------------------+
***************
*** 790,793 ****
--- 714,719 ----
else:
return PgBytea(value)
+ elif _ftv == PG_REFCURSOR:
+ return self.__conn.cursor(value, isRefCursor=PG_True)
elif _ftv == OTHER:
if isinstance(value, PgOther):
***************
*** 898,901 ****
--- 824,856 ----
return (self, other)
+ def __getitem__(self, index):
+ if type(index) is SliceType:
+ if index.step is None:
+ return PgOther(self.value[index.start:index.stop])
+ else:
+ return PgOther(self.value[index.start:index.stop:index.step])
+
+ return self.value[index];
+
+ def __setitem__(self, index, item):
+ raise TypeError, "object doesn't support slice assignment"
+
+ def __delitem__(self, index):
+ raise TypeError, "object doesn't support slice deletion"
+
+ def __contains__(self, item):
+ return (item in self.value)
+
+ if sys.version_info < (2, 0):
+ # They won't be defined if version is at least 2.0 final
+ def __getslice__(self, i, j):
+ return PgOther(self.value[max(0, i):max(0, j)])
+
+ def __setslice__(self, i, j, seq):
+ raise TypeError, "object doesn't support slice assignment"
+
+ def __delslice__(self, i, j):
+ raise TypeError, "object doesn't support slice deletion"
+
# NOTE: A string is being concatenated to a PgOther, so the result type
# is a PgOther
***************
*** 920,923 ****
--- 875,905 ----
return str(self.value)
+ def __hash__(self):
+ return hash(self.value)
+
+ def __cmp__(self, other):
+ return cmp(self.value, other)
+
+ def __rcmp__(self, other):
+ return cmp(other, self.value)
+
+ def __lt__(self, other):
+ return self.value < other
+
+ def __le__(self, other):
+ return self.value <= other
+
+ def __eq__(self, other):
+ return self.value == other
+
+ def __ne__(self, other):
+ return self.value != other
+
+ def __gt__(self, other):
+ return self.value > other
+
+ def __ge__(self, other):
+ return self.value >= other
+
# NOTE: A PgOther object will use the PgQuoteString() function in libpq.
def __quote__(self, forArray=0):
***************
*** 1116,1119 ****
--- 1098,1130 ----
return (self, other)
+ def __getitem__(self, index):
+ if type(index) is SliceType:
+ if index.step is None:
+ return PgBytea(self.value[index.start:index.stop])
+ else:
+ return PgBytea(self.value[index.start:index.stop:index.step])
+
+ return self.value[index];
+
+ def __setitem__(self, index, item):
+ raise TypeError, "object doesn't support slice assignment"
+
+ def __delitem__(self, index):
+ raise TypeError, "object doesn't support slice deletion"
+
+ def __contains__(self, item):
+ return (item in self.value)
+
+ if sys.version_info < (2, 0):
+ # They won't be defined if version is at least 2.0 final
+ def __getslice__(self, i, j):
+ return PgBytea(self.value[max(0, i):max(0, j)])
+
+ def __setslice__(self, i, j, seq):
+ raise TypeError, "object doesn't support slice assignment"
+
+ def __delslice__(self, i, j):
+ raise TypeError, "object doesn't support slice deletion"
+
def __add__(self, other):
return PgBytea((self.value + other))
***************
*** 1134,1137 ****
--- 1145,1175 ----
return str(self.value)
+ def __hash__(self):
+ return hash(self.value)
+
+ def __cmp__(self, other):
+ return cmp(self.value, other)
+
+ def __rcmp__(self, other):
+ return cmp(other, self.value)
+
+ def __lt__(self, other):
+ return self.value < other
+
+ def __le__(self, other):
+ return self.value <= other
+
+ def __eq__(self, other):
+ return self.value == other
+
+ def __ne__(self, other):
+ return self.value != other
+
+ def __gt__(self, other):
+ return self.value > other
+
+ def __ge__(self, other):
+ return self.value >= other
+
# NOTE: A PgBytea object will use the PgQuoteBytea() function in libpq
def __quote__(self, forArray=0):
***************
*** 2318,2322 ****
"Rollback failed - %s" % res.resultErrorMessage
! def cursor(self, name=None):
"""
cursor([name])
--- 2356,2360 ----
"Rollback failed - %s" % res.resultErrorMessage
! def cursor(self, name=None, isRefCursor=PG_False):
"""
cursor([name])
***************
*** 2327,2331 ****
"Create cursor failed - Connection is not open."
! return Cursor(self, name)
def binary(self, string=None):
--- 2365,2369 ----
"Create cursor failed - Connection is not open."
! return Cursor(self, name, isRefCursor)
def binary(self, string=None):
***************
*** 2396,2400 ****
return res
-
#-----------------------------------------------------------------------+
# Name: Cursor |
--- 2434,2437 ----
***************
*** 2407,2411 ****
"""Python DB-API 2.0 Cursor Object."""
! def __init__(self, conn, name):
if not isinstance(conn, Connection):
raise TypeError, "Cursor requires a connection."
--- 2444,2448 ----
"""Python DB-API 2.0 Cursor Object."""
! def __init__(self, conn, name, isRefCursor=PG_False):
if not isinstance(conn, Connection):
raise TypeError, "Cursor requires a connection."
***************
*** 2413,2416 ****
--- 2450,2455 ----
# Generate a unique name for the cursor is one is not given.
if name is None:
+ if isRefCursor:
+ raise TypeError, "Reference cursor requires a name."
name = "PgSQL_%08X" % id(self)
elif type(name) is not StringType:
***************
*** 2429,2435 ****
# This needs to be defined here sot that the initial call to __reset()
# will work.
! self.__dict__["closed"] = None
!
! self.__reset()
# _varhdrsz is the length (in bytes) of the header for variable
--- 2468,2473 ----
# This needs to be defined here sot that the initial call to __reset()
# will work.
! self.__dict__["closed"] = None
! self.__reset()
# _varhdrsz is the length (in bytes) of the header for variable
***************
*** 2450,2464 ****
# Only the first created cursor begins the transaction.
if not self.conn.inTransaction:
! _nl = len(self.conn.notices)
! conn.conn.query("BEGIN WORK")
! if self.conn.TransactionLevel != "":
! self.conn.conn.query('SET TRANSACTION ISOLATION LEVEL %s' %
! self.conn.TransactionLevel)
! if len(self.conn.notices) != _nl:
! raise Warning, self.conn.notices.pop()
! self.conn.__dict__["inTransaction"] = 1
self.__dict__["PgResultSetClass"] = None
!
def __del__(self):
# Ensure that the cursor is closed when it is deleted. This takes
--- 2488,2515 ----
# Only the first created cursor begins the transaction.
if not self.conn.inTransaction:
! self.__setupTransaction()
self.__dict__["PgResultSetClass"] = None
+
+ if isRefCursor:
+ # Ok -- we've created a cursor, we will pre-fetch the first row in
+ # order to make the description array. Note: the first call to
+ # fetchXXX will return the pre-fetched record.
+ self.__dict__["closed"] = 0
+ self.res = self.conn.conn.query('FETCH 1 FROM "%s"' % self.name)
+ self._rows_ = self.res.ntuples
+ self._idx_ = 0
+ self.__makedesc__()
! def __setupTransaction(self):
! _nl = len(self.conn.notices)
! self.conn.conn.query("BEGIN WORK")
! if self.conn.TransactionLevel != "":
! self.conn.conn.query('SET TRANSACTION ISOLATION LEVEL %s' %
! self.conn.TransactionLevel)
! if len(self.conn.notices) != _nl:
! raise Warning, self.conn.notices.pop()
! self.conn.__dict__["inTransaction"] = 1
!
!
def __del__(self):
# Ensure that the cursor is closed when it is deleted. This takes
***************
*** 2682,2693 ****
else:
if not self.conn.inTransaction:
! _nl = len(self.conn.notices)
! self.conn.conn.query('BEGIN WORK')
! if self.conn.TransactionLevel != "":
! self.conn.conn.query('SET TRANSACTION ISOLATION LEVEL %s' %
! self.conn.TransactionLevel)
! if len(self.conn.notices) != _nl:
! raise Warning, self.conn.notices.pop()
! self.conn.__dict__["inTransaction"] = 1
proc = self.__unicodeConvert(proc)
--- 2733,2737 ----
else:
if not self.conn.inTransaction:
! self.__setupTransaction()
proc = self.__unicodeConvert(proc)
***************
*** 2792,2808 ****
# so DROP TABLE/INDEX is ok.
else:
! _nl = len(self.conn.notices)
! self.conn.conn.query('BEGIN WORK')
! if self.conn.TransactionLevel != "":
! self.conn.conn.query(
! 'SET TRANSACTION ISOLATION LEVEL %s' %
! self.conn.TransactionLevel)
! if len(self.conn.notices) != _nl:
! raise Warning, self.conn.notices.pop()
! self.conn.__dict__["inTransaction"] = 1
if re_DQL.search(query) and \
not (noPostgresCursor or re_4UP.search(query)):
! _qstr = "DECLARE %s CURSOR FOR %s" % (self.name, query)
self.closed = 0
elif _badQuery and self.conn.inTransaction:
--- 2836,2844 ----
# so DROP TABLE/INDEX is ok.
else:
! self.__setupTransaction()
if re_DQL.search(query) and \
not (noPostgresCursor or re_4UP.search(query)):
! _qstr = 'DECLARE "%s" CURSOR FOR %s' % (self.name, query)
self.closed = 0
elif _badQuery and self.conn.inTransaction:
***************
*** 2813,2825 ****
pass # not in transaction so DROP TABLE/INDEX is ok.
else:
! _nl = len(self.conn.notices)
! self.conn.conn.query('BEGIN WORK')
! if self.conn.TransactionLevel != "":
! self.conn.conn.query(
! 'SET TRANSACTION ISOLATION LEVEL %s' %
! self.conn.TransactionLevel)
! if len(self.conn.notices) != _nl:
! raise Warning, self.conn.notices.pop()
! self.conn.__dict__["inTransaction"] = 1
_nl = len(self.conn.notices)
--- 2849,2853 ----
pass # not in transaction so DROP TABLE/INDEX is ok.
else:
! self.__setupTransaction()
_nl = len(self.conn.notices)
***************
*** 2882,2886 ****
# order to make the description array. Note: the first call to
# fetchXXX will return the pre-fetched record.
! self.res = self.conn.conn.query("FETCH 1 FROM %s" % self.name)
self._rows_ = self.res.ntuples
self._idx_ = 0
--- 2910,2914 ----
# order to make the description array. Note: the first call to
# fetchXXX will return the pre-fetched record.
! self.res = self.conn.conn.query('FETCH 1 FROM "%s"' % self.name)
self._rows_ = self.res.ntuples
self._idx_ = 0
***************
*** 2921,2925 ****
elif self.closed == 0:
_nl = len(self.conn.notices)
! self.res = self.conn.conn.query("FETCH 1 FROM %s" % self.name)
self._rows_ = self.res.ntuples
self._idx_ = 0
--- 2949,2953 ----
elif self.closed == 0:
_nl = len(self.conn.notices)
! self.res = self.conn.conn.query('FETCH 1 FROM "%s"' % self.name)
self._rows_ = self.res.ntuples
self._idx_ = 0
***************
*** 2967,2971 ****
if self.closed == 0 and sz > 0:
_nl = len(self.conn.notices)
! self.res = self.conn.conn.query("FETCH %d FROM %s" %
(sz, self.name))
self._rows_ = self.res.ntuples
--- 2995,2999 ----
if self.closed == 0 and sz > 0:
_nl = len(self.conn.notices)
! self.res = self.conn.conn.query('FETCH %d FROM "%s"' %
(sz, self.name))
self._rows_ = self.res.ntuples
***************
*** 3004,3008 ****
if self.closed == 0:
_nl = len(self.conn.notices)
! self.res = self.conn.conn.query("FETCH ALL FROM %s" % self.name)
self._rows_ = self.res.ntuples
self._idx_ = 0
--- 3032,3036 ----
if self.closed == 0:
_nl = len(self.conn.notices)
! self.res = self.conn.conn.query('FETCH ALL FROM "%s"' % self.name)
self._rows_ = self.res.ntuples
self._idx_ = 0
|