Handle ibm_db_sa DBDuplicateEntry integrity errors

This patch adds duplicate entry integrity error handling for the
ibm_db_sa driver to the common sqlalchemy db session.

Closes-Bug: #1227321
Part of nova blueprint: db2-database

Change-Id: I2f0d3d660373e4f647615f121fed0da9442f8cd2
This commit is contained in:
Matt Riedemann 2013-09-18 13:51:17 -07:00 committed by Brant Knudson
parent 0f24d82afc
commit 8dccc7bbb6
2 changed files with 52 additions and 3 deletions

View File

@ -332,11 +332,20 @@ class SqliteForeignKeysListener(PoolListener):
# 'c1'")
# N columns - (IntegrityError) (1062, "Duplicate entry 'values joined
# with -' for key 'name_of_our_constraint'")
#
# ibm_db_sa:
# N columns - (IntegrityError) SQL0803N One or more values in the INSERT
# statement, UPDATE statement, or foreign key update caused by a
# DELETE statement are not valid because the primary key, unique
# constraint or unique index identified by "2" constrains table
# "NOVA.KEY_PAIRS" from having duplicate values for the index
# key.
_DUP_KEY_RE_DB = {
"sqlite": (re.compile(r"^.*columns?([^)]+)(is|are)\s+not\s+unique$"),
re.compile(r"^.*UNIQUE\s+constraint\s+failed:\s+(.+)$")),
"postgresql": (re.compile(r"^.*duplicate\s+key.*\"([^\"]+)\"\s*\n.*$"),),
"mysql": (re.compile(r"^.*\(1062,.*'([^\']+)'\"\)$"),)
"mysql": (re.compile(r"^.*\(1062,.*'([^\']+)'\"\)$"),),
"ibm_db_sa": (re.compile(r"^.*SQL0803N.*$"),),
}
@ -358,7 +367,7 @@ def _raise_if_duplicate_entry_error(integrity_error, engine_name):
return [columns]
return columns[len(uniqbase):].split("0")[1:]
if engine_name not in ["mysql", "sqlite", "postgresql"]:
if engine_name not in ["ibm_db_sa", "mysql", "sqlite", "postgresql"]:
return
# FIXME(johannes): The usage of the .message attribute has been
@ -373,7 +382,12 @@ def _raise_if_duplicate_entry_error(integrity_error, engine_name):
else:
return
columns = match.group(1)
# NOTE(mriedem): The ibm_db_sa integrity error message doesn't provide the
# columns so we have to omit that from the DBDuplicateEntry error.
columns = ''
if engine_name != 'ibm_db_sa':
columns = match.group(1)
if engine_name == "sqlite":
columns = [c.split('.')[-1] for c in columns.strip().split(", ")]

View File

@ -22,6 +22,7 @@ import mock
import sqlalchemy
from sqlalchemy import Column, MetaData, Table, UniqueConstraint
from sqlalchemy import DateTime, Integer, String
from sqlalchemy import exc as sqla_exc
from sqlalchemy.exc import DataError
from sqlalchemy.ext.declarative import declarative_base
@ -84,6 +85,40 @@ class SessionErrorWrapperTestCase(test_base.DbTestCase):
self.assertRaises(db_exc.DBDuplicateEntry,
method, {'foo': 20})
def test_ibm_db_sa_raise_if_duplicate_entry_error_duplicate(self):
# Tests that the session._raise_if_duplicate_entry_error method
# translates the duplicate entry integrity error for the DB2 engine.
statement = ('INSERT INTO key_pairs (created_at, updated_at, '
'deleted_at, deleted, name, user_id, fingerprint) VALUES '
'(?, ?, ?, ?, ?, ?, ?)')
params = ['20130918001123627099', None, None, 0, 'keypair-23474772',
'974a7c9ffde6419f9811fcf94a917f47',
'7d:2c:58:7f:97:66:14:3f:27:c7:09:3c:26:95:66:4d']
orig = sqla_exc.SQLAlchemyError(
'SQL0803N One or more values in the INSERT statement, UPDATE '
'statement, or foreign key update caused by a DELETE statement are'
' not valid because the primary key, unique constraint or unique '
'index identified by "2" constrains table "NOVA.KEY_PAIRS" from '
'having duplicate values for the index key.')
integrity_error = sqla_exc.IntegrityError(statement, params, orig)
self.assertRaises(db_exc.DBDuplicateEntry,
session._raise_if_duplicate_entry_error,
integrity_error, 'ibm_db_sa')
def test_ibm_db_sa_raise_if_duplicate_entry_error_no_match(self):
# Tests that the session._raise_if_duplicate_entry_error method
# does not raise a DBDuplicateEntry exception when it's not a matching
# integrity error.
statement = ('ALTER TABLE instance_types ADD CONSTRAINT '
'uniq_name_x_deleted UNIQUE (name, deleted)')
params = None
orig = sqla_exc.SQLAlchemyError(
'SQL0542N The column named "NAME" cannot be a column of a '
'primary key or unique key constraint because it can contain null '
'values.')
integrity_error = sqla_exc.IntegrityError(statement, params, orig)
session._raise_if_duplicate_entry_error(integrity_error, 'ibm_db_sa')
_REGEXP_TABLE_NAME = _TABLE_NAME + "regexp"