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 |