Fix parsing of UC errors in sqlite 3.7.16+/3.8.2+

The new version of libsqlite changed the format of error messages.
Code of oslo.db helpers must be updated appropriately. This was meant
to be fixed by I86e9673ac409b3b3e881bea7cb8b3516f320388d, but that
change doesn't cope well with the new format of column names.

Closes-Bug: #1259915

Change-Id: Iec39c8804ea47307a98d1b79899ff74af9404bf6
This commit is contained in:
Roman Podoliaka 2014-01-15 17:53:36 +02:00
parent 9fd406bf9a
commit 3728fbf0ae
2 changed files with 49 additions and 3 deletions

View File

@ -473,9 +473,9 @@ def get_session(autocommit=True, expire_on_commit=False, sqlite_fk=False,
# N columns - (IntegrityError) column c1, c2, ..., N are not unique
#
# sqlite since 3.7.16:
# 1 column - (IntegrityError) UNIQUE constraint failed: k1
# 1 column - (IntegrityError) UNIQUE constraint failed: tbl.k1
#
# N columns - (IntegrityError) UNIQUE constraint failed: k1, k2
# N columns - (IntegrityError) UNIQUE constraint failed: tbl.k1, tbl.k2
#
# postgres:
# 1 column - (IntegrityError) duplicate key value violates unique
@ -532,7 +532,7 @@ def _raise_if_duplicate_entry_error(integrity_error, engine_name):
columns = match.group(1)
if engine_name == "sqlite":
columns = columns.strip().split(", ")
columns = [c.split('.')[-1] for c in columns.strip().split(", ")]
else:
columns = get_columns_from_uniq_cons_or_name(columns)
raise exception.DBDuplicateEntry(columns, integrity_error)

View File

@ -28,7 +28,9 @@ from sqlalchemy.sql import select
from sqlalchemy.types import UserDefinedType, NullType
from openstack.common.py3kcompat import urlutils
from openstack.common.db import exception
from openstack.common.db.sqlalchemy import migration
from openstack.common.db.sqlalchemy import session
from openstack.common.db.sqlalchemy import test_migrations
from openstack.common.db.sqlalchemy import utils
from openstack.common.fixture import moxstubout
@ -593,3 +595,47 @@ class TestConnectionUtils(test_utils.BaseTestCase):
conn_pieces = urlutils.urlparse(self.connect_string)
self.assertEqual(utils.get_db_connection_info(conn_pieces),
('dude', 'pass', 'test', 'localhost'))
class TestRaiseDuplicateEntryError(test.BaseTestCase):
def _test_impl(self, engine_name, error_msg):
try:
error = sqlalchemy.exc.IntegrityError('test', 'test', error_msg)
session._raise_if_duplicate_entry_error(error, engine_name)
except exception.DBDuplicateEntry as e:
self.assertEqual(e.columns, ['a', 'b'])
else:
self.fail('DBDuplicateEntry was not raised')
def test_sqlite(self):
self._test_impl(
'sqlite',
'(IntegrityError) column a, b are not unique'
)
def test_sqlite_3_7_16_or_3_8_2_and_higher(self):
self._test_impl(
'sqlite',
'(IntegrityError) UNIQUE constraint failed: tbl.a, tbl.b'
)
def test_mysql(self):
self._test_impl(
'mysql',
'(IntegrityError) (1062, "Duplicate entry '
'\'2-3\' for key \'uniq_tbl0a0b\'")'
)
def test_postgresql(self):
self._test_impl(
'postgresql',
'(IntegrityError) duplicate key value violates unique constraint'
'"uniq_tbl0a0b"'
'\nDETAIL: Key (a, b)=(2, 3) already exists.\n'
)
def test_unsupported_backend_returns_none(self):
error = sqlalchemy.exc.IntegrityError('test', 'test', 'test')
rv = session._raise_if_duplicate_entry_error('oracle', error)
self.assertIsNone(rv)