Why errors and warnings are good for you
The value of rigorous
error checking is exemplified in any of the several catastrophes arising
from poor software engineering. Examples abound, but a few are
particularly illustrative of what happens when bad data and design go
unchallenged.
On 4 June 1996, the first
test flight of the Ariane 5 rocket self-destructed 37 seconds after its
launch. The navigation code from Ariane 4 was reused in Ariane 5. The
faster processing speed on the newer rocket caused an operand error. The
conversion of a 64-bit floating-point value resulted in a
larger-than-expected and unsupported 16-bit signed integer. The result
was an overflow that scrambled the flight's computer, causing too much
thrust to be passed by the rocket itself, resulting in the crash of
US$370 million worth of technology. Widely considered to be one of the
most expensive computer bugs in history, the crash arose due to mistakes
in design and in subsequent error checking.
On 15 January 1990, the American
telecommunications company AT&T installed a new system on the
switches that controlled their long-distance service. A bug in the
software caused the computers to crash every time they received a
message from one of their neighboring switches. The message in question
just happened to be the same one that the switches send out when they
recover from a system crash. The result: Within a short time, 114
switches across New York City were rebooting every six seconds, leaving
around 60,000 people without long distance service for nine hours. The
system ultimately had to be fixed by reinstalling the old software.
On the Internet, a lack of
proper error-checking still makes it possible for a malformed ping
request to crash a server anywhere in the world. The Computer Emergency Response Team (CERT)
Advisory on this bug, CA-1996-26, was released in 1996, but the bug
persists. The original denial-of-service attack has thus evolved into
the distributed denial-of-service attack employing botnets of zombie
machines worldwide.
More than any other part of a
computing system, errors cost significantly more to fix later than if
they were resolved earlier in the development process. It is
specifically for this reason that Python outputs error messages to the
screen, unless such errors are explicitly handled otherwise.
A basic dynamic of
computing is that the computer does not let anyone know what is
happening inside itself. A simple illustration of this dynamic is as
follows:
Knowing Python and reading the code, we understand that the value of x is now 4. But the computer has provided us no indication of the value of x.
What's more, it will not tell us anything unless we explicitly tell it
to do so. Generally speaking, there are two ways you can ask Python to
tell you what it's thinking:
Here, a simple print statement would tell us the value of x.
Output displayed
on the screen or saved to a file are the most common ways for programs
to report their status to users. However, the similar effect is done by
indicator lights and other non-verbal forms of communication. The type
of output is necessarily dependent on the hardware being used.
By default, Python outputs all errors and warnings to the screen. As MySQL for Python is interpreted by Python, errors passed by MySQLdb
are no different. This naturally gives the debugging programmer
information for ironing out the performance of the
program—whether determining why a program is not executing as
planned or how to make it execute faster or more reliably. However, it
also means that any information needed for tracing the error, along with
parts of the code, is passed to the user, whoever they may be.
This is great for debugging, but makes for terrible security. That is why the Zen of Python reads:
Errors should never pass silently.
Unless explicitly silenced.
One needs the error
messages to know why the program fails, but it is a security hazard to
pass raw error messages to the user. If one wants the user to handle an
error message, it should be sanitized of information that may compromise
the security of the system.
Further, the more
complicated an application, the more robust the error-handling should
be. Ultimately, every kind of error is covered by one of the several
types that can be thrown by MySQL for Python. Each one of them allows
for customized error messages to be passed to the user.
With a bit of further
coding, one can check the authentication level of the user and pass
error messages according to their level of authorization. This can be
done through a flag system or by using modules from the Python library.
If the former is used, one must ensure that knowledge of the flag(s)
used is guarded from unauthorized users. Alternatively, one can employ
both systems by checking the authentication level of users or programs
that pass a particular flag to the program.
Errors versus warnings: There's a big difference
As with Python in general, the
main difference between errors and warnings is that warnings do not
cause a program to terminate. Errors do. Warnings provide notice of
something we should note; errors indicate the reason the program cannot
continue. If not handled appropriately, warnings therefore pass process
information to the user without interrupting the execution of the
program. This lack of detectability makes warnings more dangerous to the
security of an application, and the system in general, than errors.
Consequently, the error-handling process of an application must account
for both errors and warnings.
While Python handles
warnings and exceptions differently by default, especially with regard
to program execution, both are written to stderr. Therefore, one handles them the same way that one handles standard errors (see Handling exceptions passed from MySQL in the later sections).
Additionally, one can set
warnings to be silenced altogether or to carry the same gravity as an
error. This level of functionality was introduced in Python 2.1. We will
discuss this more later.
The two main errors in MySQLdb
Python generally
supports several kinds of errors, and MySQL for Python is no different.
The obvious difference between the two is that MySQLdb's errors deal exclusively with the database connection. Where MySQLdb passes warnings that are not MySQL-specific, all exceptions are related to MySQL.
The MySQL-specific exceptions are then classified as either warnings or errors. There is only one kind of warning, but MySQLdb allows two categories of errors—DatabaseError and InterfaceError. Of the former, there are six types that we will discuss here.
DatabaseError
When there is a problem with the MySQL database itself, a DatabaseError
is thrown. This is an intermediate catch-all category of exceptions
that deal with everything from how the data is processed (for example,
errors arising from division by zero), to problems in the SQL syntax, to
internal problems within MySQL itself. Essentially, if a connection is
made and a problem arises, the DatabaseError will catch it.
Several types of exceptions are contained by the DatabaseError type. We look at each of these in the section Handling exceptions passed from MySQL.
InterfaceError
When the database connection fails for some reason, MySQLdb will raise an InterfaceError. This may be caused from problems in the interface anywhere in the connection process.
Warnings in MySQL for Python
In addition to errors, MySQL for Python also supports a Warning class. This exception is raised for warnings like data truncation when executing an INSERT error, but otherwise will not interrupt the flow of a program.
statement. It may be caught just like an