Error Handling
Errors are raised as exceptions. There is no return value that means failure, and nothing is committed after an error.
try:
uid = db.insert('users', {'email': '[email protected]'})
except Exception:
... # the row was NOT inserted and NOT committed
What is raised, and when
| Situation | Result |
|---|---|
| SQL error (constraint, syntax, type) | the driver's exception (pymysql.MySQLError / psycopg2.Error) |
| Connection closed | RuntimeError |
condition of an invalid type | TypeError |
Empty WHERE in update() / delete() | ValueError |
Empty data in insert() / update() | ValueError |
NaN or Inf as a value | ValueError |
str() or an f-string over a Condition | TypeError |
and, or, not or bool() over a Condition | TypeError |
resetSession() with a transaction open | RuntimeError |
reconnect() or connect() with a transaction open | RuntimeError |
| Any operation from a thread that does not own the open transaction | RuntimeError |
CRUD, a batch, begin(), truncate(), reconnect or reset with pending work outside transaction() | RuntimeError |
Any operation after a failed begin, commit, rollback or savepoint | RuntimeError, until rollback() or close() |
for_update=True outside a transaction | RuntimeError |
| Rows of a batch with different keys, or a non-dict row | ValueError |
A str, bytes or dict as the batch container | TypeError |
conflict_columns on MySQL, or missing on PostgreSQL | ValueError |
Nothing is retried
A statement is sent once. After a driver exception there is no automatic reconnection and no second send, inside or outside a transaction. A failed commit is not repeated either.
try:
db.insert('users', {'email': '[email protected]'})
except Exception:
... # sent once
db.reconnect() # explicit, when the session has to be replaced
A lost response does not mean the write or the commit failed on the server. Check the state from another connection before repeating the operation.
What is not an error
A SELECT with no matches returns []. That means
zero rows, never a failure:
rows = db.select('users', {'email': '[email protected]'})
if not rows:
... # no user with that address; the query itself worked
Logging
Library messages go through the standard logging module under the
easymysql logger. Nothing is printed to stdout.
import logging
logging.basicConfig(level=logging.DEBUG)
logging.getLogger('easymysql').setLevel(logging.DEBUG)
Counters are invalidated by a failure
count() and getLastId() are reset before every
operation, and again if it fails at any point — argument validation, the statement itself,
the fetch, or the commit. After a failure they return 0 and None:
db.insert('users', {'email': '[email protected]'})
db.getLastId() # the new id
try:
db.insert('users', {'email': '[email protected]'}) # violates UNIQUE
except Exception:
db.getLastId() # None, not the earlier id
count() == 0 and getLastId() is None mean the metadata is
unavailable. They are not evidence that the server applied nothing.
Errors inside a transaction
If the exception escapes the with block, everything is rolled back:
with db.transaction():
db.insert('orders', {'customer_id': 7})
db.update('stock', {'quantity': 0}, {'id': 3}) # if this fails,
# ...the insert above is rolled back too