I got an error as follows in my code of python (which collects twitter statuses and store in database).
sqlalchemy.exc.InvalidRequestError: This Session's transaction has been rolled back by a nested rollback() call. To begin a new transaction, issue Session.rollback() first.
I want to know what is the problem, why does it occur, and how can I solve it.
I have no idea about nested rollback. Is there any simple example which occurs nested rollback?
The problem was solved.
The point, in this case, is that rollback is not executed until we call rollback explicitly, so when we include commit(), we should write it in a try statement, and write rollback() in the exception statement (in most case) as written in https://docs.sqlalchemy.org/en/13/faq/sessions.html#this-session-s-transaction-has-been-rolled-back-due-to-a-previous-exception-during-flush-or-similar
And, here is the correct code example. I quoted this from the link above.
try:
<use session>
session.commit()
except:
session.rollback()
raise
finally:
session.close() # optional, depends on use case
As identified by #fbessho above, this is indeed the correct pattern:
try:
<use session>
session.commit()
except:
session.rollback()
However, there are some subtleties that can derail the error handling.
In this example (an imaginary unique constraint violation), the rollback does not occur:
class Thing1(Base):
id = Column(BigInteger, primary_key=True)
class Thing2(Base):
id = Column(BigInteger, primary_key=True)
def do_something(s: session, thing_1: Thing1, duplicate_id):
# imagine this violates a unique constraint on Thing2
thing_2 = Thing2(id=duplicate_id)
s.add(thing_2)
try:
# the exception will occur when the commit statement is executed
s.commit()
except Exception as ex:
# this will log details of the exception
logger.error(f"{ex.__class__.__name__}: {ex}")
# referencing thing_1.id will raise a second exception
logger.error(f"Commit failed. Thing1 id was {thing_1.id}.")
s.rollback()
This second Exception occurs even though thing_1 has nothing to do with the failed insert. Merely referencing thing_1 raises a second Exception which prevents the rollback from being executed.
Solution 1
This requires a little more overhead, but will always work.
def do_something_1(s: session, thing_1: Thing1, duplicate_id):
# create a reference that does not rely on the data object
id_for_thing = thing_1.id
# imagine this violates a unique constraint on Thing2
thing_2 = Thing2(id=duplicate_id)
s.add(thing_2)
try:
# the exception will occur when the commit statement is executed
s.commit()
except Exception as ex:
logger.error(f"{ex.__class__.__name__}: {ex}")
# no direct reference to thing_1
logger.error(f"Commit failed. Thing1 id was {id_for_thing}.")
s.rollback()
Solution 2
This will work as long as thing_1 is not affected by the rollback.
def do_something_2(s: session, thing_1: Thing1, duplicate_id):
# imagine this violates a unique constraint on Thing2
thing_2 = Thing2(id=duplicate_id)
s.add(thing_2)
try:
# the exception will occur when the commit statement is executed
s.commit()
except Exception as ex:
logger.error(f"{ex.__class__.__name__}: {ex}")
s.rollback()
# thing_1.id can be referenced after rollback
logger.error(f"Commit failed. Thing1 id was {thing_1.id}.")
Related
I have a list of employees and I want to create tables for them. I want to continue creating tables even if one of the table creations fail. I am using try/except block but when a table creation fails, it fails totally. I am trying below code. Appreciate your help!
employees=[1,2,3,4]
for p in employees:
create_table = f"""create table dim_{p}"""
try:
conn.execute(create_table)
print(f"success for {p}")
except Exception as e:
raise Exception(f"Failed to create table for {p} with error : {{e}}")
I think you can save the exception object for later without raising it.
employees=[1,2,3,4]
exceptions = []
for p in employees:
create_table = f"""create table dim_{p}"""
try:
conn.execute(create_table)
print(f"success for {p}")
except Exception as e:
exceptions.append((p, e)) # save info as a tuple
if exceptions: # after your loop over employees is complete
# handle your errors how you like
fail_msg = '\n'.join([f"{exception[0]} ; {exception[1]}" for exception in exceptions])
raise Exception(f"Failed to create table for the following employees:\n{fail_msg}")
When I run this (since conn is not defined via any imports) I get:
Exception: Failed to create table for the following employees:
1 ; name 'conn' is not defined
2 ; name 'conn' is not defined
3 ; name 'conn' is not defined
4 ; name 'conn' is not defined
I am using try/except block but when a table creation fails, it fails totally.
this is because you have used raise Exception(f"Failed to create table for {p} with error : {{e}}")
raising an exception, leading to the program interrupting. To avoid that, simply don't use raise.
For better understanding, look at the example below:
employees=["1","2",[1,2],"4"]
for i in employees :
try :
print(int(i))
except :
print("error on" , i)
This will output :
1
2
error on [1, 2]
4
I'd like to access a field called timeCreated on a mapped object. The field is instantiated via a utcnow() FunctionElement (taken from this example).
After doing a merge or add call, committing, then closing the session, I've noticed that I get the below error when accessing the field. I have expire_on_commit set to False.
sqlalchemy.orm.exc.DetachedInstanceError: Instance <User at 0x102046710> is not bound to a Session; attribute refresh operation cannot proceed (Background on this error at: http://sqlalche.me/e/13/bhk3)
Example code:
def write(obj):
with sessionScope() as session:
obj.timeCreated = utcnow()
ret = session.merge(obj)
return ret
user = User(name='Totoro')
savedUser = write(user)
# Error occurs when accessing timeCreated
print(savedUser.timeCreated)
SessionScope() is taken from these docs, it's defined as:
sessionFactory = sessionmaker(bind=engine)
#contextmanager
def sessionScope():
try:
session = sessionFactory()
yield session
session.commit()
except Exception as e:
session.rollback()
raise
finally:
session.close()
return
Is there a reason why timeCreated is not resolved after commit()? If, after committing, but before closing, I access timeCreated, then subsequent accesses after close still work.
Is there a way to "eager" load this type of column?
The problem is that when timeCreated is assigned to the result of calling utcnow, SQLAlchemy doesn't assign the result from the server immediately; instead a placeholder value is assigned, as we can see in the debugger:
(Pdb) obj.__dict__
{..., 'timeCreated': <__main__.utcnow at 0x7f237d0a90a0; utcnow object>}
When the session is closed, this placeholder value is expired:
(Pdb) sa.inspect(savedUser).expired_attributes
{'timeCreated'}
So, as stated at the end of the question, the value of timeCreated must be loaded before the session is closed to prevent a DetachedInstanceError if it is accessed later.
Based on the documentation for Fetching Server-Generated Defaults (case 1), this can be done by by setting timeCreated's server_default attribute to a FetchedValue, and setting eager_defaults to True in the mapper args.
Here is an example model (tested on Mariadb and Postgresql):
from sqlalchemy.schema import FetchedValue
class User(Base):
...
timeCreated = Column(DateTime, server_default=FetchedValue())
__mapper_args__ = {'eager_defaults': True}
For what it's worth, setting server_default=utcnow() in combination with the mapper args would work just as well, and avoid the need to set timeCreated explicitly; but perhaps the OP has their own reason for doing this.
Here are data in MySQL databaseļ¼
python3 tableļ¼users_info
But when i use fetchall to get all data in tables it always return none!!
I really really dont konw how to fix it,can someone has met this problem?
the followings are the code files.
encapsulation.py
import MySQLdb
class mysql_encapsulation(object):
def __init__(self,host,port,user,passwd,db,charset):
self.host = host
self.port = port
self.user = user
self.passwd = passwd
self.db = db
self.charset = charset
def open(self):
self.conn = MySQLdb.connect(host=self.host,port=self.port,user=self.user,passwd=self.passwd,db=self.db,charset=self.charset)
self.cursor = self.conn.cursor()
def close(self):
self.cursor.close()
self.conn.close()
def operate(self,sql,params):
try:
self.open()
self.cursor.execute(sql,params)
self.conn.commit()
print(' operate ok')
self.close()
except Exception,e:
print(e.message)
self.conn.rollback()
def get_result_set(self,sql,params=[]):
result=None
try:
self.open()
self.cursor.execute(sql,params)
result = self.cursor.fetchall()
self.close()
except Exception,e:
print('error!')
print(e.message)
return result
use.py(problem in this file)
#coding=utf-8
from encapsulation import *
mysql = mysql_encapsulation(port=3306,host='localhost',user='root',passwd='mysql',
db='python3',charset='utf8')
sql='select id,name from users_info where id=3'
result=mysql.get_result_set(sql)
print (result)
The problem happens in this method;
def get_result_set(self,sql,params=[]):
result=None
try:
self.open()
self.cursor.execute(sql,params)
result = self.cursor.fetchall()
self.close()
except Exception,e:
print('error!')
print(e.message)
return result
One of the first 3 lines in the try block (self.open / self.cursor.exec / result = self.cursor.fetch) generate an error, which you are then catching in the except block and (you can see that it prints "error!"). That's why the result always stays in it's default None value. Remove the except block and it will tell you what kind of error occured.
You should almost never catch the bare Exception but instead catch specific kinds of exception and handle each of them correctly, this problem is a prefect example why.
The error in question probably happens because in your SQL query you are selecting id and name, when the columns in your table are actually id and user_name. So your SQL query should be like this;
sql = 'select id, user_name from users_info where id = 3'
Did you check your sql statement? Based on your table your columns are "id", "user_name" and "passwd", but in your sql you are searching for "id" and "name", and "name" isn't a column so that will throw an error. Change your sql to "sql='select id,user_name from users_info where id=3'"
As ruohola already explained in his answer, your exception handler is hiding all the important informations about what really went wrong - FWIW, there's the "Error !" string printed in your screen capture just above the None. The point is: your try/except block is not only useless, it's actually harmful - it prevents some other code up the call stack to even be aware that there was a problem (and eventually solve it), it also prevents you from knowing what went wrong. As a general rule, only catch exception the exact exception that you expect AND can effectively handle at this point in the code - if you can't fix the error at this point, let the exception propagate and let the calling code deal with it. Also, you want to have as few code as possible in your try clause (the only exception being the application's top-level exception handler of course).
This being said, there is indeed a very valid reason for wanting to be warned of exception, which is to make sure you free resources (and eventually rollback a transaction), but then you want 1/ to use a finally clause for resources cleanup (a finally block is always executed, whatever happens) and 2/ for the rollback part, use an except clause but re-raise the exception, ie:
# leave this one out of the try block -
# there's nothing you can do here about a connection error
# so let the calling code deal with it
self.connect()
try:
self.cursor.execute(sql, params)
except Exception as e:
self.connection.rollback()
finally:
# this will ALWAYS be executed (unless `self.connect()` raised
# but then you don't even want to close the connection <g>
self.close()
wrt/ the real cause of your problem, Kurose's answer is certainly the right one.
Also, there are a quite a few other things that are debatable with your "mysql_encapsulation" class (the naming to start with but let ignore this ATM), the main one being to open and close the connection for each query. Opening a database connection has a rather high cost, so you want to keep it opened as long as possible (and eventually reconnect if you get a "2006 mysql has gone away" error).
A function A() is enabled for manual transaction. It has DB transactions and rolls back in case of any error. This function calls another function which is not enabled for manual transactions but has db transactions too raises an error in case of an exception . Will rollback in the parent function rollback db edit statements in the called function in this case??
I am using Mysql
import sys
import os
from polls.models import Choice
from django.db import transaction
def s1():
with transaction.commit_manually():
try:
print "First Fun"
choice_obj = Choice.objects.create(
choice_text="heyaa", votes='1', question_id='1')
s2()
transaction.commit()
except:
print "Roll Fun"
transaction.commit()
transaction.rollback()
def s2():
try:
print "Second Fun"
choice_obj = Choice.objects.create(
choice_text="abcaa", votes='1', question_id='1')
print a
except:
raise
Yes, it will be rolled back as well, though not if you commit it first in your except block and there is no database-related exception that prevents commits.
The very definition of a database transaction (atomic, consistent, isolated, durable) prevents that it can be nested. It is possible to emulate a sort of nested transaction using savepoints (which Django 1.6+ uses), though these are not true transactions. In any case, if the outer-most transaction is rolled back (i.e. the only true database-transaction), the whole transaction is rolled back.
Just a quick note:
except:
transaction.commit()
This is probably undesired behaviour on any exception, but this will raise another error if the exception is a database-related error that marks the transaction as broken. In that case, a rollback is required.
Suppose, I have a modifying statement:
cursor = conn.cursor()
# some code
affected_rows1 = cursor.execute(update_statement1, params1)
# some code
conn.commit()
cursor.close()
Should I wrap the block of code with a try ... except and explicitly rollback a transaction when an exception is raised, and which MySQLdb exceptions should I catch to rollback? I used to catch any StandardError in this case, but now I have a hesitation that the block of code would even need an explicit rollback at all.
The following example is slightly more difficult, and I understand that it does require an explicit rollback if the first update statement succeeded. Still, which exceptions should I catch in this case:
cursor = conn.cursor()
# some code
affected_rows1 = cursor.execute(update_statement1, params1)
# some code
affected_rows2 = cursor.execute(update_statement2, params2)
#some code
conn.commit()
cursor.close()
This link shows the various types of Errors that you can catch. MySQLdb.Error is the standard base class from which all other MySQL Errors are derived.
I usually use MySQLdb.Error because it lets you focus on errors relating to MySQLdb itself. By contrast StandardError will catch almost all the exceptions (not something you want if you want better debugging capability). Plus the use of MySQLdb.Error allows you to display the exact error message (MySQL error number and all) so that you can debug it faster.
Coming to the first part of the question, in case of database statements it is (usually) necessary to rollback transactions (if they are supported) in case of error.
The methodology that I follow is to wrap each execute statement in a try except clause (catching MySQLdb.Error) and using rollback if there is an an error before printing the error message and exiting.
However, there is a catch. In MySQLdb the changes that you make to DB are not actually written to the database until you explicilty call commit. So, logically, rollback is not necessary.
As an example,
conn = MySQLdb.connection(db=, host=, passwd=, user=)
cur = conn.cursor()
#Say you have a table X with one entry id = 1 and total = 50
cur.execute("update X set total = 70 where id = 1")
#Actual DB has not yet changed
cur.execute("update X set total = 80 where id = 1")
#Actual DB has still not changed
If you exit the program without commiting, the value in DB will still be 50 because you never called commit().
This is how you would ideally do it:
conn = MySQLdb.connection(db=, host=, passwd=, user=)
cur = conn.cursor()
#Say you have a table X with one entry id = 1 and total = 50
try:
cur.execute("update X set total = 70 where id = 1")
except MySQLdb.Error,e:
print e[0], e[1]
conn.rollback()
cur.close()
conn.close()
#print lengthy error description!!
sys.exit(2)
#Note: Value in table is still 50
#If you do conn.commit() here, value becomes 70 in table too!!
try:
cur.execute("update X set total = 80 where id = 1")
except MySQLdb.Error,e:
print e[0], e[1]
conn.rollback()
cur.close()
conn.close()
#print lengthy error description!!
sys.exit(2)
#Value in DB will be
#a) 50 if you didn't commit anywhere
#b) 70 if you committed after first execute statement
conn.commit()
#Now value in DB is 80!!
cur.close()
conn.close()
IMHO, you should rollback transactions if you continue to use the same connection. Else everything before the error will get commit when you finish the transactions.
For the exception to catch, I always use MySQLdb.Error but I'm not sure that's correct.
Its advised to wrap execute() in a sub. This is how i do it.
def executeSQL(self, stmt):
cursor = self.dbHand.cursor()
if not stmt.endswith(";"):
stmt += ';'
try:
cursor.execute(stmt)
except MySQLdb.Error, e:
self.logger.error("Caught MYSQL exception :%s: while executing stmt :%s:.\n"%(e,stmt))
return False