mirror of
https://github.com/weewx/weewx.git
synced 2026-05-24 01:35:18 -04:00
Ported MySQLdb to the new database exception hierarchy.
This commit is contained in:
@@ -12,9 +12,9 @@ This table shows how the various MySQLdb and sqlite exceptions are mapped to a w
|
||||
| `OperationalError` | *N/A* | `OperationalError` | 1049 | Open non-existent database |
|
||||
| `DatabaseExists` | *N/A* | `ProgrammingError` | 1007 | Database already exists |
|
||||
| `OperationalError` | `OperationalError` | `OperationalError` | 1050 | Table already exists |
|
||||
| `ProgrammingError` | *N/A* | `ProgrammingError` | 1146 | SELECT on non-existing database |
|
||||
| `ProgrammingError` | `OperationalError` | `ProgrammingError` | 1146 | SELECT non-existing table |
|
||||
| `OperationalError` | `OperationalError` | `OperationalError` | 1054 | SELECT non-existing column |
|
||||
| `ProgrammingError` | *N/A* | `ProgrammingError` | 1146 | SELECT on non-existing database |
|
||||
| `IntegrityError` | `IntegrityError` | `IntegrityError` | 1062 | Duplicate key |
|
||||
|
||||
###V3.6 Exception hierarchy
|
||||
@@ -42,9 +42,9 @@ StandardError
|
||||
| `NoDatabaseError` | *N/A* | `OperationalError` | 1049 | Open non-existent database |
|
||||
| `DatabaseExistsError` | *N/A* | `ProgrammingError` | 1007 | Database already exists |
|
||||
| `TableExistsError` | `OperationalError` | `OperationalError` | 1050 | Table already exists |
|
||||
| `NoTableError` | *N/A* | `ProgrammingError` | 1146 | SELECT on non-existing database |
|
||||
| `NoTableError` | `OperationalError` | `ProgrammingError` | 1146 | SELECT non-existing table |
|
||||
| `NoColumnError` | `OperationalError` | `OperationalError` | 1054 | SELECT non-existing column |
|
||||
| `ProgrammingError` | *N/A* | `ProgrammingError` | 1146 | SELECT on non-existing database |
|
||||
| `IntegrityError` | `IntegrityError` | `IntegrityError` | 1062 | Duplicate key |
|
||||
|
||||
###V3.7 Exception hierarchy
|
||||
|
||||
@@ -1,32 +1,49 @@
|
||||
#
|
||||
# Copyright (c) 2009-2015 Tom Keffer <tkeffer@gmail.com>
|
||||
# Copyright (c) 2009-2017 Tom Keffer <tkeffer@gmail.com>
|
||||
#
|
||||
# See the file LICENSE.txt for your full rights.
|
||||
#
|
||||
"""Driver for the MySQL database"""
|
||||
"""weedb driver for the MySQL database"""
|
||||
|
||||
import decimal
|
||||
|
||||
import MySQLdb
|
||||
from _mysql_exceptions import IntegrityError, ProgrammingError, OperationalError
|
||||
from _mysql_exceptions import DatabaseError, IntegrityError, ProgrammingError, OperationalError
|
||||
|
||||
from weeutil.weeutil import to_bool
|
||||
import weedb
|
||||
|
||||
DEFAULT_ENGINE = 'INNODB'
|
||||
|
||||
exception_map = {
|
||||
1007: weedb.DatabaseExistsError,
|
||||
1008: weedb.NoDatabaseError,
|
||||
1044: weedb.PermissionError,
|
||||
1045: weedb.BadPasswordError,
|
||||
1049: weedb.NoDatabaseError,
|
||||
1050: weedb.TableExistsError,
|
||||
1054: weedb.NoColumnError,
|
||||
1062: weedb.IntegrityError,
|
||||
1146: weedb.NoTableError,
|
||||
2002: weedb.CannotConnectError,
|
||||
2005: weedb.CannotConnectError,
|
||||
None: weedb.DatabaseError
|
||||
}
|
||||
|
||||
def guard(fn):
|
||||
"""Decorator function that converts MySQL exceptions into weedb exceptions."""
|
||||
|
||||
def guarded_fn(*args, **kwargs):
|
||||
try:
|
||||
return fn(*args, **kwargs)
|
||||
except IntegrityError, e:
|
||||
raise weedb.IntegrityError(e)
|
||||
except ProgrammingError, e:
|
||||
raise weedb.ProgrammingError(e)
|
||||
except OperationalError, e:
|
||||
raise weedb.OperationalError(e)
|
||||
except DatabaseError, e:
|
||||
# Default exception is weedb.DatabaseError
|
||||
try:
|
||||
errno = e[0]
|
||||
except IndexError:
|
||||
errno = None
|
||||
klass = exception_map.get(errno, weedb.DatabaseError)
|
||||
raise klass(e)
|
||||
|
||||
return guarded_fn
|
||||
|
||||
@@ -34,37 +51,26 @@ def guard(fn):
|
||||
def connect(host='localhost', user='', password='', database_name='',
|
||||
driver='', engine=DEFAULT_ENGINE, **kwargs):
|
||||
"""Connect to the specified database"""
|
||||
if host not in ('localhost', '127.0.0.1'):
|
||||
kwargs.setdefault('port', 3306)
|
||||
return Connection(host=host, user=user, password=password,
|
||||
database_name=database_name, engine=engine, **kwargs)
|
||||
|
||||
def create(host='localhost', user='', password='', database_name='',
|
||||
driver='', engine=DEFAULT_ENGINE, **kwargs):
|
||||
"""Create the specified database. If it already exists,
|
||||
an exception of type weedb.DatabaseExists will be thrown."""
|
||||
an exception of type weedb.DatabaseExistsError will be thrown."""
|
||||
# Open up a connection w/o specifying the database.
|
||||
if host not in ('localhost', '127.0.0.1'):
|
||||
kwargs.setdefault('port', 3306)
|
||||
connect = Connection(host=host,
|
||||
user=user,
|
||||
password=password,
|
||||
**kwargs)
|
||||
cursor = connect.cursor()
|
||||
|
||||
try:
|
||||
connect = MySQLdb.connect(host=host,
|
||||
user=user,
|
||||
passwd=password,
|
||||
**kwargs)
|
||||
set_engine(connect, engine)
|
||||
cursor = connect.cursor()
|
||||
# An exception will get thrown if the database already exists.
|
||||
try:
|
||||
# Now create the database.
|
||||
cursor.execute("CREATE DATABASE %s" % (database_name,))
|
||||
except ProgrammingError:
|
||||
# The database already exists. Change the type of exception.
|
||||
raise weedb.DatabaseExists("Database %s already exists" % (database_name,))
|
||||
finally:
|
||||
cursor.close()
|
||||
connect.close()
|
||||
except OperationalError, e:
|
||||
raise weedb.OperationalError(e)
|
||||
# Now create the database.
|
||||
cursor.execute("CREATE DATABASE %s" % (database_name,))
|
||||
finally:
|
||||
cursor.close()
|
||||
connect.close()
|
||||
|
||||
|
||||
def drop(host='localhost', user='', password='', database_name='',
|
||||
@@ -73,23 +79,19 @@ def drop(host='localhost', user='', password='', database_name='',
|
||||
if host not in ('localhost', '127.0.0.1'):
|
||||
kwargs.setdefault('port', 3306)
|
||||
# Open up a connection
|
||||
connect = Connection(host=host,
|
||||
user=user,
|
||||
password=password,
|
||||
**kwargs)
|
||||
cursor = connect.cursor()
|
||||
|
||||
try:
|
||||
connect = MySQLdb.connect(host=host,
|
||||
user=user,
|
||||
passwd=password,
|
||||
**kwargs)
|
||||
cursor = connect.cursor()
|
||||
try:
|
||||
cursor.execute("DROP DATABASE %s" % database_name)
|
||||
except OperationalError:
|
||||
raise weedb.NoDatabase("""Attempt to drop non-existent database %s""" % (database_name,))
|
||||
finally:
|
||||
cursor.close()
|
||||
connect.close()
|
||||
except OperationalError, e:
|
||||
raise weedb.OperationalError(e)
|
||||
|
||||
cursor.execute("DROP DATABASE %s" % database_name)
|
||||
finally:
|
||||
cursor.close()
|
||||
connect.close()
|
||||
|
||||
@guard
|
||||
class Connection(weedb.Connection):
|
||||
"""A wrapper around a MySQL connection object."""
|
||||
|
||||
@@ -111,16 +113,9 @@ class Connection(weedb.Connection):
|
||||
"""
|
||||
if host not in ('localhost', '127.0.0.1'):
|
||||
kwargs.setdefault('port', 3306)
|
||||
try:
|
||||
connection = MySQLdb.connect(host=host, user=user, passwd=password, db=database_name, **kwargs)
|
||||
except OperationalError, e:
|
||||
# The MySQL driver does not include the database in the
|
||||
# exception information. Tack it on, in case it might be useful.
|
||||
msg = str(e) + " while opening database '%s'" % (database_name,)
|
||||
if e.args[0] == 2002:
|
||||
raise weedb.CannotConnect(msg)
|
||||
else:
|
||||
raise weedb.OperationalError(msg)
|
||||
|
||||
connection = MySQLdb.connect(host=host, user=user, passwd=password,
|
||||
db=database_name, **kwargs)
|
||||
|
||||
weedb.Connection.__init__(self, connection, database_name, 'mysql')
|
||||
|
||||
@@ -223,6 +218,7 @@ class Connection(weedb.Connection):
|
||||
class Cursor(object):
|
||||
"""A wrapper around the MySQLdb cursor object"""
|
||||
|
||||
@guard
|
||||
def __init__(self, connection):
|
||||
"""Initialize a Cursor from a connection.
|
||||
|
||||
@@ -251,9 +247,9 @@ class Cursor(object):
|
||||
return self
|
||||
|
||||
def fetchone(self):
|
||||
# Get a result from the MySQL cursor, then run it through the massage
|
||||
# Get a result from the MySQL cursor, then run it through the _massage
|
||||
# filter below
|
||||
return massage(self.cursor.fetchone())
|
||||
return _massage(self.cursor.fetchone())
|
||||
|
||||
def close(self):
|
||||
try:
|
||||
@@ -279,8 +275,8 @@ class Cursor(object):
|
||||
# This is a utility function for converting a result set that might contain
|
||||
# longs or decimal.Decimals (which MySQLdb uses) to something containing just ints.
|
||||
#
|
||||
def massage(seq):
|
||||
# Return the massaged sequence if it exists, otherwise, return None
|
||||
def _massage(seq):
|
||||
# Return the _massaged sequence if it exists, otherwise, return None
|
||||
if seq is not None:
|
||||
return [int(i) if isinstance(i, long) or isinstance(i, decimal.Decimal) else i for i in seq]
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"""Test the weedb exception hierarchy"""
|
||||
from __future__ import with_statement
|
||||
import os
|
||||
import stat
|
||||
import unittest
|
||||
|
||||
import MySQLdb
|
||||
@@ -47,22 +49,15 @@ class Cursor(object):
|
||||
class Common(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Drop the old databases, in preparation for running a test."""
|
||||
try:
|
||||
weedb.drop(mysql1_dict)
|
||||
except weedb.NoDatabase:
|
||||
pass
|
||||
try:
|
||||
weedb.drop(mysql2_dict)
|
||||
except weedb.NoDatabase:
|
||||
pass
|
||||
try:
|
||||
weedb.drop(sqdb1_dict)
|
||||
except weedb.NoDatabase:
|
||||
pass
|
||||
try:
|
||||
weedb.drop(sqdb2_dict)
|
||||
except weedb.NoDatabase:
|
||||
pass
|
||||
|
||||
def test_bad_host(self):
|
||||
mysql_dict = dict(mysql1_dict)
|
||||
@@ -86,7 +81,10 @@ class Common(unittest.TestCase):
|
||||
weedb.create(mysql1_dict)
|
||||
with self.assertRaises(weedb.PermissionError):
|
||||
weedb.drop(mysql2_dict)
|
||||
with self.assertRaises(weedb.PermissionError):
|
||||
weedb.create(sqdb1_dict)
|
||||
# Can't really test this one without setting up a file where
|
||||
# we have no write permission
|
||||
with self.assertRaises(weedb.NoDatabaseError):
|
||||
weedb.drop(sqdb2_dict)
|
||||
|
||||
def test_create_nopermission(self):
|
||||
@@ -114,7 +112,7 @@ class Common(unittest.TestCase):
|
||||
mysql_dict.pop('database_name')
|
||||
connect = weedb.connect(mysql_dict)
|
||||
cursor = connect.cursor()
|
||||
with self.assertRaises(weedb.NoDatabaseError):
|
||||
with self.assertRaises(weedb.NoTableError):
|
||||
cursor.execute("SELECT foo from test_weewx1.bar")
|
||||
cursor.close()
|
||||
connect.close()
|
||||
|
||||
@@ -127,6 +127,9 @@ with option retry_login. Fixes issue #212.
|
||||
The test suites now use dedicated users 'weewx1' and 'weewx2'. A shell script
|
||||
has been included to setup these users.
|
||||
|
||||
A more formal exception hierarchy has been adopted for the internal
|
||||
database library weedb. See weedb/NOTES.md
|
||||
|
||||
|
||||
3.6.2 11/08/2016
|
||||
|
||||
|
||||
Reference in New Issue
Block a user