Repository: rucio/rucio
Issue: Internal error during adding duplicate attribute to account

Motivation
----------
While testing I encurred the following autotest failing:

```
2020-06-29T12:23:54.1758267Z ======================================================================
2020-06-29T12:23:54.1758524Z ERROR: ACCOUNT (CORE): Test adding attribute to account
2020-06-29T12:23:54.1759599Z ----------------------------------------------------------------------
2020-06-29T12:23:54.1759842Z Traceback (most recent call last):
2020-06-29T12:23:54.1760414Z   File "/usr/lib/python2.7/site-packages/nose/case.py", line 197, in runTest
2020-06-29T12:23:54.1760755Z     self.test(*self.arg)
2020-06-29T12:23:54.1760888Z   File "/opt/rucio/lib/rucio/tests/test_account.py", line 89, in test_add_account_attribute
2020-06-29T12:23:54.1761017Z     add_account_attribute(account, key, value)
2020-06-29T12:23:54.1761148Z   File "/opt/rucio/lib/rucio/db/sqla/session.py", line 366, in new_funct
2020-06-29T12:23:54.1761431Z     session.commit()  # pylint: disable=maybe-no-member
2020-06-29T12:23:54.1761733Z   File "/usr/lib64/python2.7/site-packages/sqlalchemy/orm/scoping.py", line 162, in do
2020-06-29T12:23:54.1762103Z     return getattr(self.registry(), name)(*args, **kwargs)
2020-06-29T12:23:54.1762429Z   File "/usr/lib64/python2.7/site-packages/sqlalchemy/orm/session.py", line 1027, in commit
2020-06-29T12:23:54.1762573Z     self.transaction.commit()
2020-06-29T12:23:54.1762876Z   File "/usr/lib64/python2.7/site-packages/sqlalchemy/orm/session.py", line 492, in commit
2020-06-29T12:23:54.1763013Z     self._assert_active(prepared_ok=True)
2020-06-29T12:23:54.1763312Z   File "/usr/lib64/python2.7/site-packages/sqlalchemy/orm/session.py", line 295, in _assert_active
2020-06-29T12:23:54.1763447Z     code="7s2a",
2020-06-29T12:23:54.1763937Z InvalidRequestError: This Session's transaction has been rolled back due to a previous exception during flush. To begin a new transaction with this Session, first issue Session.rollback(). Original exception was: (sqlite3.IntegrityError) columns account, key are not unique
2020-06-29T12:23:54.1764136Z [SQL: INSERT INTO account_attr_map (account, "key", value, updated_at, created_at) VALUES (?, ?, ?, ?, ?)]
2020-06-29T12:23:54.1764511Z [parameters: ('root', 'jdoe-29b888b8f89c44e4', 'true', '2020-06-29 12:17:36.381848', '2020-06-29 12:17:36.381854')]
2020-06-29T12:23:54.1764746Z (Background on this error at: http://sqlalche.me/e/gkpj) (Background on this error at: http://sqlalche.me/e/7s2a)
```

The problem is not with the autotest, but the code checking for the wrong error string from sqlite to transform it to a "Duplicate" type error.


Modification
------------
Modify the checks in `add_account_attribute` in `lib/rucio/core/account.py` to include the new sqlite IntegrityError message: »*(sqlite3.IntegrityError) columns account, key are not unique*«

I already have this prepared and tested.

Changed files:
lib/rucio/core/did.py, lib/rucio/core/rule.py, lib/rucio/core/replica.py, lib/rucio/core/account.py, lib/rucio/core/lifetime_exception.py, lib/rucio/core/meta.py, lib/rucio/core/scope.py, lib/rucio/core/subscription.py

diff --git a/lib/rucio/core/account.py b/lib/rucio/core/account.py
index b48b813e5..6174be818 100644
--- a/lib/rucio/core/account.py
+++ b/lib/rucio/core/account.py
@@ -1,4 +1,4 @@
-# Copyright 2012-2019 CERN for the benefit of the ATLAS collaboration.
+# Copyright 2012-2020 CERN for the benefit of the ATLAS collaboration.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@
 # - Joaquin Bogado <jbogado@linti.unlp.edu.ar>, 2015
 # - Hannes Hansen <hannes.jakob.hansen@cern.ch>, 2018-2019
 # - Andrew Lister <andrew.lister@stfc.ac.uk>, 2019
+# - Benedikt Ziemons <benedikt.ziemons@cern.ch>, 2020
 #
 # PY3K COMPATIBLE
 
@@ -261,7 +262,7 @@ def add_account_attribute(account, key, value, session=None):
         if match('.*IntegrityError.*ORA-00001: unique constraint.*ACCOUNT_ATTR_MAP_PK.*violated.*', error.args[0]) \\
            or match('.*IntegrityError.*1062.*Duplicate entry.*for key.*', error.args[0]) \\
            or match('.*IntegrityError.*UNIQUE constraint failed: account_attr_map.account, account_attr_map.key.*', error.args[0]) \\
-           or error.args[0] == "(IntegrityError) column account/key is not unique" \\
+           or match('.*IntegrityError.*columns? account.*key.*not unique.*', error.args[0]) \\
            or match('.*IntegrityError.*duplicate key value violates unique constraint.*', error.args[0]) \\
            or match('.*UniqueViolation.*duplicate key value violates unique constraint.*', error.args[0]):
             raise exception.Duplicate('Key {0} already exist for account {1}!'.format(key, account))
diff --git a/lib/rucio/core/did.py b/lib/rucio/core/did.py
index 159a7fb8f..1dc45a24c 100644
--- a/lib/rucio/core/did.py
+++ b/lib/rucio/core/did.py
@@ -1,4 +1,4 @@
-# Copyright 2013-2018 CERN for the benefit of the ATLAS collaboration.
+# Copyright 2013-2020 CERN for the benefit of the ATLAS collaboration.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@
 # - Ruturaj Gujar <ruturaj.gujar23@gmail.com>, 2019
 # - Brandon White <bjwhite@fnal.gov>, 2019
 # - Luc Goossens <luc.goossens@cern.ch>, 2020
+# - Benedikt Ziemons <benedikt.ziemons@cern.ch>, 2020
 #
 # PY3K COMPATIBLE
 
@@ -209,7 +210,7 @@ def add_dids(dids, account, session=None):
                 or match('.*IntegrityError.*1062.*Duplicate entry.*for key.*', error.args[0]) \\
                 or match('.*IntegrityError.*duplicate key value violates unique constraint.*', error.args[0]) \\
                 or match('.*UniqueViolation.*duplicate key value violates unique constraint.*', error.args[0]) \\
-                or match('.*sqlite3.IntegrityError.*are not unique.*', error.args[0]):
+                or match('.*IntegrityError.*columns? .*not unique.*', error.args[0]):
             raise exception.DataIdentifierAlreadyExists('Data Identifier already exists!')
 
         if match('.*IntegrityError.*02291.*integrity constraint.*DIDS_SCOPE_FK.*violated - parent key not found.*', error.args[0]) \\
@@ -218,7 +219,7 @@ def add_dids(dids, account, session=None):
                 or match('.*IntegrityError.*02291.*integrity constraint.*DIDS_SCOPE_FK.*violated - parent key not found.*', error.args[0]) \\
                 or match('.*IntegrityError.*insert or update on table.*violates foreign key constraint.*', error.args[0]) \\
                 or match('.*ForeignKeyViolation.*insert or update on table.*violates foreign key constraint.*', error.args[0]) \\
-                or match('.*sqlite3.IntegrityError.*foreign key constraint failed', error.args[0]):
+                or match('.*IntegrityError.*foreign key constraints? failed.*', error.args[0]):
             raise exception.ScopeNotFound('Scope not found!')
 
         raise exception.RucioException(error.args)
@@ -386,7 +387,7 @@ def __add_files_to_dataset(scope, name, files, account, rse_id, ignore_duplicate
     except IntegrityError as error:
         if match('.*IntegrityError.*ORA-02291: integrity constraint .*CONTENTS_CHILD_ID_FK.*violated - parent key not found.*', error.args[0]) \\
                 or match('.*IntegrityError.*1452.*Cannot add or update a child row: a foreign key constraint fails.*', error.args[0]) \\
-                or error.args[0] == "(IntegrityError) foreign key constraint failed" \\
+                or match('.*IntegrityError.*foreign key constraints? failed.*', error.args[0]) \\
                 or match('.*IntegrityError.*insert or update on table.*violates foreign key constraint.*', error.args[0]):
             raise exception.DataIdentifierNotFound("Data identifier not found")
         elif match('.*IntegrityError.*ORA-00001: unique constraint .*CONTENTS_PK.*violated.*', error.args[0]) \\
@@ -395,7 +396,7 @@ def __add_files_to_dataset(scope, name, files, account, rse_id, ignore_duplicate
                 or match('.*UniqueViolation.*duplicate key value violates unique constraint.*', error.args[0]) \\
                 or match('.*IntegrityError.*1062.*Duplicate entry .*for key.*PRIMARY.*', error.args[0]) \\
                 or match('.*duplicate entry.*key.*PRIMARY.*', error.args[0]) \\
-                or match('.*sqlite3.IntegrityError.*are not unique.*', error.args[0]):
+                or match('.*IntegrityError.*columns? .*not unique.*', error.args[0]):
             raise exception.FileAlreadyExists(error.args)
         else:
             raise exception.RucioException(error.args)
@@ -461,13 +462,13 @@ def __add_collections_to_container(scope, name, collections, account, session):
         session.flush()
     except IntegrityError as error:
         if match('.*IntegrityError.*ORA-02291: integrity constraint .*CONTENTS_CHILD_ID_FK.*violated - parent key not found.*', error.args[0]) \\
-           or match('.*IntegrityError.*1452.*Cannot add or update a child row: a foreign key constraint fails.*', error.args[0]) \\
-           or error.args[0] == "(IntegrityError) foreign key constraint failed" \\
-           or match('.*IntegrityError.*insert or update on table.*violates foreign key constraint.*', error.args[0]):
+                or match('.*IntegrityError.*1452.*Cannot add or update a child row: a foreign key constraint fails.*', error.args[0]) \\
+                or match('.*IntegrityError.*foreign key constraints? failed.*', error.args[0]) \\
+                or match('.*IntegrityError.*insert or update on table.*violates foreign key constraint.*', error.args[0]):
             raise exception.DataIdentifierNotFound("Data identifier not found")
         elif match('.*IntegrityError.*ORA-00001: unique constraint .*CONTENTS_PK.*violated.*', error.args[0]) \\
                 or match('.*IntegrityError.*1062.*Duplicate entry .*for key.*PRIMARY.*', error.args[0]) \\
-                or match('.*columns scope, name, child_scope, child_name are not unique.*', error.args[0]) \\
+                or match('.*IntegrityError.*columns? scope.*name.*child_scope.*child_name.*not unique.*', error.args[0]) \\
                 or match('.*IntegrityError.*duplicate key value violates unique constraint.*', error.args[0]) \\
                 or match('.*UniqueViolation.*duplicate key value violates unique constraint.*', error.args[0]) \\
                 or match('.*IntegrityError.* UNIQUE constraint failed: contents.scope, contents.name, contents.child_scope, contents.child_name.*', error.args[0]):
diff --git a/lib/rucio/core/lifetime_exception.py b/lib/rucio/core/lifetime_exception.py
index 2233449df..26c736f2a 100644
--- a/lib/rucio/core/lifetime_exception.py
+++ b/lib/rucio/core/lifetime_exception.py
@@ -1,15 +1,23 @@
-#  Copyright European Organization for Nuclear Research (CERN)
+# Copyright 2017-2020 CERN for the benefit of the ATLAS collaboration.
 #
-#  Licensed under the Apache License, Version 2.0 (the "License");
-#  You may not use this file except in compliance with the License.
-#  You may obtain a copy of the License at
-#  http://www.apache.org/licenses/LICENSE-2.0
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
 #
-#  Authors:
-#  - Cedric Serfon, <cedric.serfon@cern.ch>, 2016-2018
-#  - Dimitrios Christidis, <dimitrios.christidis@cern.ch> 2018
-#  - Hannes Hansen, <hannes.jakob.hansen@cern.ch>, 2018
-#  - Andrew Lister, <andrew.lister@stfc.ac.uk>, 2019
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Authors:
+# - Cedric Serfon, <cedric.serfon@cern.ch>, 2016-2018
+# - Dimitrios Christidis, <dimitrios.christidis@cern.ch> 2018
+# - Hannes Hansen, <hannes.jakob.hansen@cern.ch>, 2018
+# - Andrew Lister, <andrew.lister@stfc.ac.uk>, 2019
+# - Benedikt Ziemons <benedikt.ziemons@cern.ch>, 2020
 #
 # PY3K COMPATIBLE
 
@@ -115,10 +123,10 @@ def add_exception(dids, account, pattern, comments, expires_at, session=None):
         try:
             new_exception.save(session=session, flush=False)
         except IntegrityError as error:
-            if match('.*ORA-00001.*', str(error.args[0]))\\
-               or match('.*IntegrityError.*UNIQUE constraint failed.*', str(error.args[0]))\\
-               or match('.*1062.*Duplicate entry.*for key.*', str(error.args[0]))\\
-               or match('.*sqlite3.IntegrityError.*are not unique.*', error.args[0]):
+            if match('.*ORA-00001.*', str(error.args[0])) \\
+                    or match('.*IntegrityError.*UNIQUE constraint failed.*', str(error.args[0])) \\
+                    or match('.*1062.*Duplicate entry.*for key.*', str(error.args[0])) \\
+                    or match('.*IntegrityError.*columns? .*not unique.*', error.args[0]):
                 raise LifetimeExceptionDuplicate()
             raise RucioException(error.args[0])
     if truncated_message:
diff --git a/lib/rucio/core/meta.py b/lib/rucio/core/meta.py
index 643f3b16f..7f0436ef8 100644
--- a/lib/rucio/core/meta.py
+++ b/lib/rucio/core/meta.py
@@ -1,18 +1,24 @@
-'''
-  Copyright European Organization for Nuclear Research (CERN)
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  You may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-  http://www.apache.org/licenses/LICENSE-2.0
-
-  Authors:
-  - Vincent Garonne, <vincent.garonne@cern.ch>, 2012-2015
-  - Mario Lassnig, <mario.lassnig@cern.ch>, 2013
-  - Hannes Hansen, <hannes.jakob.hansen@cern.ch>, 2018
-
-  PY3K COMPATIBLE
-'''
+# Copyright 2012-2020 CERN for the benefit of the ATLAS collaboration.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Authors:
+# - Vincent Garonne, <vincent.garonne@cern.ch>, 2012-2015
+# - Mario Lassnig, <mario.lassnig@cern.ch>, 2013
+# - Hannes Hansen, <hannes.jakob.hansen@cern.ch>, 2018
+# - Benedikt Ziemons <benedikt.ziemons@cern.ch>, 2020
+#
+# PY3K COMPATIBLE
 
 from __future__ import print_function
 from re import match
@@ -63,7 +69,7 @@ def add_key(key, key_type, value_type=None, value_regexp=None, session=None):
     try:
         new_key.save(session=session)
     except IntegrityError as error:
-        if error.args[0] == "(IntegrityError) column key is not unique":
+        if match('.*IntegrityError.*columns? key.*not unique.*', error.args[0]):
             raise Duplicate('key \\'%(key)s\\' already exists!' % locals())
         raise
 
@@ -108,10 +114,9 @@ def add_value(key, value, session=None):
     try:
         new_value.save(session=session)
     except IntegrityError as error:
-        if error.args[0] == "(IntegrityError) columns key, value are not unique":
+        if match('.*IntegrityError.*columns? key.*value.*not unique.*', error.args[0]):
             raise Duplicate('key-value \\'%(key)s-%(value)s\\' already exists!' % locals())
-
-        if error.args[0] == "(IntegrityError) foreign key constraint failed":
+        if match('.*IntegrityError.*foreign key constraints? failed.*', error.args[0]):
             raise KeyNotFound("key '%(key)s' does not exist!" % locals())
         if match('.*IntegrityError.*ORA-02291: integrity constraint.*DID_MAP_KEYS_FK.*violated.*', error.args[0]):
             raise KeyNotFound("key '%(key)s' does not exist!" % locals())
diff --git a/lib/rucio/core/replica.py b/lib/rucio/core/replica.py
index 673c125be..f5cc56f61 100644
--- a/lib/rucio/core/replica.py
+++ b/lib/rucio/core/replica.py
@@ -29,6 +29,7 @@
 # - Brandon White, <bjwhite@fnal.gov>, 2019
 # - Luc Goossens <luc.goossens@cern.ch>, 2020
 # - Patrick Austin <patrick.austin@stfc.ac.uk>, 2020
+# - Benedikt Ziemons <benedikt.ziemons@cern.ch>, 2020
 #
 # PY3K COMPATIBLE
 
@@ -1263,9 +1264,9 @@ def __bulk_add_replicas(rse_id, files, account, session=None):
         return nbfiles, bytes
     except IntegrityError as error:
         if match('.*IntegrityError.*ORA-00001: unique constraint .*REPLICAS_PK.*violated.*', error.args[0]) \\
-           or match('.*IntegrityError.*1062.*Duplicate entry.*', error.args[0]) \\
-           or error.args[0] == '(IntegrityError) columns rse_id, scope, name are not unique' \\
-           or match('.*IntegrityError.*duplicate key value violates unique constraint.*', error.args[0]):
+                or match('.*IntegrityError.*1062.*Duplicate entry.*', error.args[0]) \\
+                or match('.*IntegrityError.*columns? rse_id.*scope.*name.*not unique.*', error.args[0]) \\
+                or match('.*IntegrityError.*duplicate key value violates unique constraint.*', error.args[0]):
             raise exception.Duplicate("File replica already exists!")
         raise exception.RucioException(error.args)
     except DatabaseError as error:
diff --git a/lib/rucio/core/rule.py b/lib/rucio/core/rule.py
index 4dfee5cc9..56d0e3c1a 100644
--- a/lib/rucio/core/rule.py
+++ b/lib/rucio/core/rule.py
@@ -25,6 +25,7 @@
 # - Dimitrios Christidis <dimitrios.christidis@cern.ch>, 2019
 # - Brandon White <bjwhite@fnal.gov>, 2019-2020
 # - Luc Goossens <luc.goossens@cern.ch>, 2020
+# - Benedikt Ziemons <benedikt.ziemons@cern.ch>, 2020
 #
 # PY3K COMPATIBLE
 
@@ -266,12 +267,12 @@ def add_rule(dids, account, copies, rse_expression, grouping, weight, lifetime,
                 try:
                     new_rule.save(session=session)
                 except IntegrityError as error:
-                    if match('.*ORA-00001.*', str(error.args[0]))\\
-                       or match('.*IntegrityError.*UNIQUE constraint failed.*', str(error.args[0]))\\
-                       or match('.*1062.*Duplicate entry.*for key.*', str(error.args[0]))\\
-                       or match('.*IntegrityError.*duplicate key value violates unique constraint.*', error.args[0]) \\
-                       or match('.*UniqueViolation.*duplicate key value violates unique constraint.*', error.args[0]) \\
-                       or match('.*sqlite3.IntegrityError.*are not unique.*', error.args[0]):
+                    if match('.*ORA-00001.*', str(error.args[0])) \\
+                            or match('.*IntegrityError.*UNIQUE constraint failed.*', str(error.args[0])) \\
+                            or match('.*1062.*Duplicate entry.*for key.*', str(error.args[0])) \\
+                            or match('.*IntegrityError.*duplicate key value violates unique constraint.*', error.args[0]) \\
+                            or match('.*UniqueViolation.*duplicate key value violates unique constraint.*', error.args[0]) \\
+                            or match('.*IntegrityError.*columns? .*not unique.*', error.args[0]):
                         raise DuplicateRule(error.args[0])
                     raise InvalidReplicationRule(error.args[0])
                 rule_ids.append(new_rule.id)
@@ -1365,10 +1366,10 @@ def update_rule(rule_id, options, session=None):
             insert_rule_history(rule=rule, recent=True, longterm=False, session=session)
 
     except IntegrityError as error:
-        if match('.*ORA-00001.*', str(error.args[0]))\\
-           or match('.*IntegrityError.*UNIQUE constraint failed.*', str(error.args[0]))\\
-           or match('.*1062.*Duplicate entry.*for key.*', str(error.args[0]))\\
-           or match('.*sqlite3.IntegrityError.*are not unique.*', error.args[0]):
+        if match('.*ORA-00001.*', str(error.args[0])) \\
+                or match('.*IntegrityError.*UNIQUE constraint failed.*', str(error.args[0])) \\
+                or match('.*1062.*Duplicate entry.*for key.*', str(error.args[0])) \\
+                or match('.*IntegrityError.*columns? .*not unique.*', str(error.args[0])):
             raise DuplicateRule(error.args[0])
         else:
             raise error
diff --git a/lib/rucio/core/scope.py b/lib/rucio/core/scope.py
index 944b9e9d1..297db72db 100644
--- a/lib/rucio/core/scope.py
+++ b/lib/rucio/core/scope.py
@@ -1,9 +1,16 @@
-# Copyright European Organization for Nuclear Research (CERN)
+# Copyright 2012-2020 CERN for the benefit of the ATLAS collaboration.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
-# You may not use this file except in compliance with the License.
+# you may not use this file except in compliance with the License.
 # You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
 #
 # Authors:
 # - Thomas Beermann, <thomas.beermann@cern.ch>, 2012
@@ -12,6 +19,7 @@
 # - Vincent Garonne, <vincent.garonne@cern.ch>, 2012-2015
 # - Cedric Serfon, <cedric.serfon@cern.ch>, 2015
 # - Hannes Hansen, <hannes.jakob.hansen@cern.ch>, 2019
+# - Benedikt Ziemons <benedikt.ziemons@cern.ch>, 2020
 #
 # PY3K COMPATIBLE
 
@@ -43,11 +51,11 @@ def add_scope(scope, account, session=None):
         new_scope.save(session=session)
     except IntegrityError as e:
         if match('.*IntegrityError.*ORA-00001: unique constraint.*SCOPES_PK.*violated.*', e.args[0]) \\
-           or match('.*IntegrityError.*Duplicate entry.*for key.*', e.args[0]) \\
-           or match('.*IntegrityError.*UNIQUE constraint failed: scopes.scope.*', e.args[0]) \\
-           or match('.*IntegrityError.*duplicate key value violates unique constraint.*', e.args[0]) \\
-           or match('.*UniqueViolation.*duplicate key value violates unique constraint.*', e.args[0]) \\
-           or match('.*sqlite3.IntegrityError.*is not unique.*', e.args[0]):
+                or match('.*IntegrityError.*Duplicate entry.*for key.*', e.args[0]) \\
+                or match('.*IntegrityError.*UNIQUE constraint failed: scopes.scope.*', e.args[0]) \\
+                or match('.*IntegrityError.*duplicate key value violates unique constraint.*', e.args[0]) \\
+                or match('.*UniqueViolation.*duplicate key value violates unique constraint.*', e.args[0]) \\
+                or match('.*IntegrityError.*columns? .*not unique.*', e.args[0]):
             raise Duplicate('Scope \\'%s\\' already exists!' % scope)
         else:
             raise RucioException(e)
diff --git a/lib/rucio/core/subscription.py b/lib/rucio/core/subscription.py
index 3c9980788..d1942480c 100644
--- a/lib/rucio/core/subscription.py
+++ b/lib/rucio/core/subscription.py
@@ -1,4 +1,4 @@
-# Copyright 2013-2018 CERN for the benefit of the ATLAS collaboration.
+# Copyright 2013-2020 CERN for the benefit of the ATLAS collaboration.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@
 # - Thomas Beermann, <thomas.beermann@cern.ch>, 2014
 # - Hannes Hansen, <hannes.jakob.hansen@cern.ch>, 2018
 # - Andrew Lister, <andrew.lister@stfc.ac.uk>, 2019
+# - Benedikt Ziemons <benedikt.ziemons@cern.ch>, 2020
 #
 # PY3K COMPATIBLE
 
@@ -109,7 +110,7 @@ def add_subscription(name, account, filter, replication_rules, comments, lifetim
     except IntegrityError as error:
         if re.match('.*IntegrityError.*ORA-00001: unique constraint.*SUBSCRIPTIONS_PK.*violated.*', error.args[0])\\
            or re.match(".*IntegrityError.*UNIQUE constraint failed: subscriptions.name, subscriptions.account.*", error.args[0])\\
-           or re.match(".*columns name, account are not unique.*", error.args[0])\\
+           or re.match('.*IntegrityError.*columns? name.*account.*not unique.*', error.args[0]) \\
            or re.match('.*IntegrityError.*ORA-00001: unique constraint.*SUBSCRIPTIONS_NAME_ACCOUNT_UQ.*violated.*', error.args[0])\\
            or re.match('.*IntegrityError.*1062.*Duplicate entry.*', error.args[0]) \\
            or re.match('.*IntegrityError.*duplicate key value violates unique constraint.*', error.args[0]) \\