views:

165

answers:

3

Hello,

for some reason I'm experiencing the Operational Error with "can't rollback" message when I attempt to roll back my transaction in the following context:

try:
    cursors[instance].execute("lock revision, app, timeout IN SHARE MODE")
    cursors[instance].execute("insert into app (type, active, active_revision, contents, z) values ('session', true, %s, %s, 0) returning id", (cRevision, sessionId))
    sAppId = cursors[instance].fetchone()[0]
    cursors[instance].execute("insert into revision (app_id, type) values (%s, 'active')", (sAppId,))
    cursors[instance].execute("insert into timeout (app_id, last_seen) values (%s, now())", (sAppId,))
    connections[instance].commit()
except pgdb.DatabaseError, e:
    connections[instance].rollback()
    return "{status: 'error', errno:4, errmsg: \"%s\"}"%(str(e).replace('\"', '\\"').replace('\n', '\\n').replace('\r', '\\r'))

The driver in use is PGDB.

What is fundamentally wrong here?

A: 

What happens if you exclude the lock statement?

This is what's happening inside pgdb.py:

def rollback(self):
    """Roll back to the start of any pending transaction."""
    if self._cnx:
        if self._tnx:
            self._tnx = False
            try:
                self._cnx.source().execute("ROLLBACK")
            except Exception:
                raise OperationalError("can't rollback")
    else:
        raise OperationalError("connection has been closed")

So I suggest you replace your connections[instance].rollback() call with:

connections[instance]._tnx = False
connections[instance]._cnx.source().execute("ROLLBACK")

to see if that gives you a more informative error message (the except clause inside pgdb is greedy).

Also: check the Postgresql log, it will have probably logged the reason!

ChristopheD
Why? Please explain. Also, it doesn't work.
David Parunakian
Hmm, sorry; you're right, it shouldn't matter. Do you have the same problems if you exclude the `lock` statement?
ChristopheD
I haven't tried; however, I seriously doubt that the logic of my application allows to operate on tables without locking them first. Would you like me to test anyway?
David Parunakian
See my suggestions added to my answer;
ChristopheD
@David Parunakian: a single underscore is an indication that the variable is for private use but you should be able to alter it without trouble. A double underscore does the same (but with some name mangling). Python does *not* intrinsically have the concept of private variables.
ChristopheD
A: 

Where do you start the transaction? I do see a COMMIT but don't see a BEGIN or START TRANSACTION.

Frank Heikens
iirc the dbapi2 spec (which pgdb / pygresql implements) implies transactions in the scope of the connection object by default
ChristopheD
Ok, thanks for the info. I'm a SQL-guy, that's why I expect plain SQL.
Frank Heikens
+1  A: 

You are looking in the wrong place. What does the PostgreSQL log say about what you are doing?

Joshua D. Drake