From 2e1ed5f33cf5756b52a10ceadd0ce8e9ecf989e3 Mon Sep 17 00:00:00 2001 From: Shai Berger Date: Wed, 15 Oct 2014 02:52:21 +0300 Subject: [PATCH] Fixed #23649 -- Made Oracle test-db creation drop test-user earlier when needed Checked database destruction for ORA-29857, which happens when objects of some types are left over in an old test database (notably, such objects are created by the GIS backend). When this happens, we can resolve the issue by dropping the test-user (if we are allowed to). An alternative approach, to just switch the order of creation (so that, if the test user exists and can be dropped, it is always dropped before the tablespace) was considered; but since the user creation depends on the tablespace, this would necessitate separating the dropping of the user from its creation -- a change I am reluctant to make. Thanks Josh Smeaton and Tim Graham for reviews. --- django/db/backends/oracle/creation.py | 51 +++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/django/db/backends/oracle/creation.py b/django/db/backends/oracle/creation.py index ca3a255c41..d470d5ac7c 100644 --- a/django/db/backends/oracle/creation.py +++ b/django/db/backends/oracle/creation.py @@ -75,10 +75,22 @@ class DatabaseCreation(BaseDatabaseCreation): "It appears the test database, %s, already exists. " "Type 'yes' to delete it, or 'no' to cancel: " % parameters['user']) if autoclobber or confirm == 'yes': + if verbosity >= 1: + print("Destroying old test database '%s'..." % self.connection.alias) try: - if verbosity >= 1: - print("Destroying old test database '%s'..." % self.connection.alias) self._execute_test_db_destruction(cursor, parameters, verbosity) + except DatabaseError as e: + if 'ORA-29857' in str(e): + self._handle_objects_preventing_db_destruction(cursor, parameters, + verbosity, autoclobber) + else: + # Ran into a database error that isn't about leftover objects in the tablespace + sys.stderr.write("Got an error destroying the old test database: %s\n" % e) + sys.exit(2) + except Exception as e: + sys.stderr.write("Got an error destroying the old test database: %s\n" % e) + sys.exit(2) + try: self._execute_test_db_creation(cursor, parameters, verbosity) except Exception as e: sys.stderr.write("Got an error recreating the test database: %s\n" % e) @@ -128,6 +140,41 @@ class DatabaseCreation(BaseDatabaseCreation): return self.connection.settings_dict['NAME'] + def _handle_objects_preventing_db_destruction(self, cursor, parameters, verbosity, autoclobber): + # There are objects in the test tablespace which prevent dropping it + # The easy fix is to drop the test user -- but are we allowed to do so? + print("There are objects in the old test database which prevent its destruction.") + print("If they belong to the test user, deleting the user will allow the test " + "database to be recreated.") + print("Otherwise, you will need to find and remove each of these objects, " + "or use a different tablespace.\n") + if self._test_user_create(): + if not autoclobber: + confirm = input("Type 'yes' to delete user %s: " % parameters['user']) + if autoclobber or confirm == 'yes': + try: + if verbosity >= 1: + print("Destroying old test user...") + self._destroy_test_user(cursor, parameters, verbosity) + except Exception as e: + sys.stderr.write("Got an error destroying the test user: %s\n" % e) + sys.exit(2) + try: + if verbosity >= 1: + print("Destroying old test database '%s'..." % self.connection.alias) + self._execute_test_db_destruction(cursor, parameters, verbosity) + except Exception as e: + sys.stderr.write("Got an error destroying the test database: %s\n" % e) + sys.exit(2) + else: + print("Tests cancelled -- test database cannot be recreated.") + sys.exit(1) + else: + print("Django is configured to use pre-existing test user '%s'," + " and will not attempt to delete it.\n" % parameters['user']) + print("Tests cancelled -- test database cannot be recreated.") + sys.exit(1) + def _destroy_test_db(self, test_database_name, verbosity=1): """ Destroy a test database, prompting the user for confirmation if the