# Ticket #5552 (reopened defect)

Opened 4 years ago

## Reviewer unable to approve review: "ProgrammingError: Cannot operate on a closed cursor."

Reported by: Assigned to: thomas.peiselt@nickwoods.de rjollos normal PeerReviewPlugin normal closed cursor hasienda 0.11

### Description

played around with different editions from subversion, but to no avail:

Sysinfo:

Trac:  	0.11.5
Python: 	2.4.4 (#71, Oct 18 2006, 08:34:43) [MSC v.1310 32 bit (Intel)]
setuptools: 	0.6c9
SQLite: 	3.6.11
pysqlite: 	2.5.5
Genshi: 	0.5.1
jQuery:	1.2.6


This is running on a Windows XP 64 SP2, but to me it seems like a database issue.

Traceback:

18:36:26 Trac[main] ERROR: Internal Server Error:
Traceback (most recent call last):
File "C:\Program Files (x86)\Python2.4\Lib\site-packages\trac\web\main.py", l
ne 444, in _dispatch_request
dispatcher.dispatch(req)
File "C:\Program Files (x86)\Python2.4\Lib\site-packages\trac\web\main.py", l
ne 205, in dispatch
resp = chosen_handler.process_request(req)
File "build\bdist.win32\egg\codereview\peerReviewView.py", line 165, in proce
s_request
File "build\bdist.win32\egg\codereview\peerReviewView.py", line 209, in vote
File "build\bdist.win32\egg\codereview\ReviewerStruct.py", line 49, in save
File "C:\Program Files (x86)\Python2.4\Lib\site-packages\trac\db\util.py", li
e 59, in execute
return self.cursor.execute(sql_escape_percent(sql), args)
File "C:\Program Files (x86)\Python2.4\Lib\site-packages\trac\db\sqlite_backe
d.py", line 58, in execute
args or [])
File "C:\Program Files (x86)\Python2.4\Lib\site-packages\trac\db\sqlite_backe
d.py", line 50, in _rollback_on_error
return function(self, *args, **kwargs)
ProgrammingError: Cannot operate on a closed cursor.


## Change History

### 08/31/09 04:22:48 changed by isaac.to@gmail.com

I see the same problem. If I move the cursor = db.cursor(); line in codereview.ReviewerStruct.save() into both the try and except block, the problem goes away. Is that a right fix?

### (follow-up: ↓ 3 ) 10/06/10 22:56:29 changed by quinn@strangecode.com

I have the same issue with Trac 0.11.6 and the latest PeerReviewPlugin from http://trac-hacks.org/browser/peerreviewplugin/0.11.

Is development on the plugin dead? What is trac-hacks.org's process for submitting patches into release versions?

Quinn

### (in reply to: ↑ 2 ) 10/09/10 00:17:41 changed by rjollos

Is development on the plugin dead? What is trac-hacks.org's process for submitting patches into release versions?

Alternatively, we can just apply some patches if no response from the author for 2 weeks. Ping me with a reminder if we don't hear anything and you'd like to proceed.

### 11/24/10 03:13:54 changed by rjollos

#6184 closed as a duplicate.

### 12/08/10 10:55:57 changed by rjollos

• status changed from new to assigned.
• owner changed from proofek to rjollos.

Checking in a proposed fix. Please reopen if you continue to have issues after installing from the trunk.

### 12/08/10 10:56:48 changed by rjollos

• status changed from assigned to closed.
• resolution set to fixed.

(In [9639]) Attempt to prevent cursor from being closed before it is used. Fixes #5552.

### 09/16/12 21:32:05 changed by rjollos

• keywords set to closed cursor.

### 09/16/12 21:45:34 changed by rjollos

• status changed from closed to reopened.
• resolution deleted.

The 2.2-dev branch has been moved to the trunk, and the change in [9639] has not yet been applied there: /peerreviewplugin/trunk/codereview/ReviewerStruct.py@3503.

### (follow-up: ↓ 11 ) 09/16/12 21:59:37 changed by rjollos

• cc set to hasienda.

Comments in comment:13:ticket:9521 lead me to believe that the fix in [9639] is not correct, because the cursor would be closed when the except block is executed. It looks like we must update the code to obtain a new cursor in each block:

    def save(self, db):
try:
#Add information to a new database entry
cursor = db.cursor();
cursor.execute("""
INSERT INTO Reviewers (IDReview, Reviewer, Status, Vote)
VALUES (%s, %s, %s, %s)""",
(self.IDReview, self.Reviewer, self.Status, self.Vote))
db.commit()
except:
#Update information in existing database entry
cursor = db.cursor();
cursor.execute("""
UPDATE Reviewers SET Status=%s, Vote=%s
WHERE IDReview=%s AND Reviewer=%s""",
(self.Status, self.Vote, self.IDReview, self.Reviewer))
db.commit()


However, I question whether we really want to be using a try/except here for handling an UPDATE vs INSERT. At a minimum, we should be handling a specific exception for this except case, and then add a generic exception handler for unexpected exceptions. I'll search around the Trac codebase a bit and see if some similar patterns exist.

### (in reply to: ↑ 10 ) 09/16/12 23:41:55 changed by hasienda

Comments in comment:13:ticket:9521 lead me to believe that the fix in [9639] is not correct, because the cursor would be closed when the except block is executed.

...

However, I question whether we really want to be using a try/except here for handling an UPDATE vs INSERT. At a minimum, we should be handling a specific exception for this except case, and then add a generic exception handler for unexpected exceptions. I'll search around the Trac codebase a bit and see if some similar patterns exist.

Yes, indeed. The try-except shouldn't be needed here. I've taken advise from rblank to use a pattern of SQL statements, that he called UPSERT. It's an atomic transaction containing the following common structure:

• unconditional UPDATE (will silently fail, if dataset isn't there
• SELECT and check, if updated dataset exists
• if no-match, the INSERT is safe

all done in one move - "atomic" transaction - and without exception handler. I've used that i.e. in AcctMgr, but it's done this way in many more plugins, and Trac core of course. It seems rather big at first sight, but it's performance is ok, and I've never had issues with code, that implements this strategy. Only the 'no-match' check is a bit trick to get, because different backends return different empty cursors. I prefer a two-step approach like (pre-0.12 db query style)

    db = _get_db(env, db)
cursor = db.cursor()
cursor.execute("""
SELECT COUNT(*)
FROM ...
""")
exists = cursor.fetchone()
if not exists[0]:
cursor.execute("""
INSERT INTO ...
""", (*args))


### Add/Change #5552 (Reviewer unable to approve review: "ProgrammingError: Cannot operate on a closed cursor.")

Change Properties