Refs #29722 -- Added introspection of materialized views for Oracle.

Thanks Tim Graham for the review.
This commit is contained in:
Mariusz Felisiak 2018-11-26 19:45:05 +01:00 committed by GitHub
parent 26c2a6ff88
commit f091ea3515
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 43 additions and 16 deletions

View File

@ -151,6 +151,9 @@ class BaseDatabaseFeatures:
# Can the backend introspect the column order (ASC/DESC) for indexes? # Can the backend introspect the column order (ASC/DESC) for indexes?
supports_index_column_ordering = True supports_index_column_ordering = True
# Does the backend support introspection of materialized views?
can_introspect_materialized_views = False
# Support for the DISTINCT ON clause # Support for the DISTINCT ON clause
can_distinct_on_fields = False can_distinct_on_fields = False

View File

@ -241,11 +241,14 @@ class DatabaseCreation(BaseDatabaseCreation):
if not success and self._test_settings_get('PASSWORD') is None: if not success and self._test_settings_get('PASSWORD') is None:
set_password = 'ALTER USER %(user)s IDENTIFIED BY "%(password)s"' set_password = 'ALTER USER %(user)s IDENTIFIED BY "%(password)s"'
self._execute_statements(cursor, [set_password], parameters, verbosity) self._execute_statements(cursor, [set_password], parameters, verbosity)
# Most test-suites can be run without the create-view privilege. But some need it. # Most test suites can be run without "create view" and
extra = "GRANT CREATE VIEW TO %(user)s" # "create materialized view" privileges. But some need it.
success = self._execute_allow_fail_statements(cursor, [extra], parameters, verbosity, 'ORA-01031') for object_type in ('VIEW', 'MATERIALIZED VIEW'):
if not success and verbosity >= 2: extra = 'GRANT CREATE %(object_type)s TO %(user)s'
self.log('Failed to grant CREATE VIEW permission to test user. This may be ok.') parameters['object_type'] = object_type
success = self._execute_allow_fail_statements(cursor, [extra], parameters, verbosity, 'ORA-01031')
if not success and verbosity >= 2:
self.log('Failed to grant CREATE %s permission to test user. This may be ok.' % object_type)
def _execute_test_db_destruction(self, cursor, parameters, verbosity): def _execute_test_db_destruction(self, cursor, parameters, verbosity):
if verbosity >= 2: if verbosity >= 2:

View File

@ -21,6 +21,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
truncates_names = True truncates_names = True
supports_tablespaces = True supports_tablespaces = True
supports_sequence_reset = False supports_sequence_reset = False
can_introspect_materialized_views = True
can_introspect_time_field = False can_introspect_time_field = False
atomic_transactions = False atomic_transactions = False
supports_combined_alters = False supports_combined_alters = False

View File

@ -48,8 +48,20 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
def get_table_list(self, cursor): def get_table_list(self, cursor):
"""Return a list of table and view names in the current database.""" """Return a list of table and view names in the current database."""
cursor.execute("SELECT TABLE_NAME, 't' FROM USER_TABLES UNION ALL " cursor.execute("""
"SELECT VIEW_NAME, 'v' FROM USER_VIEWS") SELECT table_name, 't'
FROM user_tables
WHERE
NOT EXISTS (
SELECT 1
FROM user_mviews
WHERE user_mviews.mview_name = user_tables.table_name
)
UNION ALL
SELECT view_name, 'v' FROM user_views
UNION ALL
SELECT mview_name, 'v' FROM user_mviews
""")
return [TableInfo(self.identifier_converter(row[0]), row[1]) for row in cursor.fetchall()] return [TableInfo(self.identifier_converter(row[0]), row[1]) for row in cursor.fetchall()]
def get_table_description(self, cursor, table_name): def get_table_description(self, cursor, table_name):

View File

@ -21,6 +21,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
supports_transactions = True supports_transactions = True
can_introspect_autofield = True can_introspect_autofield = True
can_introspect_ip_address_field = True can_introspect_ip_address_field = True
can_introspect_materialized_views = True
can_introspect_small_integer_field = True can_introspect_small_integer_field = True
can_distinct_on_fields = True can_distinct_on_fields = True
can_rollback_ddl = True can_rollback_ddl = True

View File

@ -764,9 +764,10 @@ and a user granted ``RESOURCE WITH ADMIN OPTION`` can grant ``RESOURCE``, such
a user cannot grant the individual privileges (e.g. ``CREATE TABLE``), and thus a user cannot grant the individual privileges (e.g. ``CREATE TABLE``), and thus
``RESOURCE WITH ADMIN OPTION`` is not usually sufficient for running tests. ``RESOURCE WITH ADMIN OPTION`` is not usually sufficient for running tests.
Some test suites also create views; to run these, the user also needs Some test suites also create views or materialized views; to run these, the
the ``CREATE VIEW WITH ADMIN OPTION`` privilege. In particular, this is needed user also needs ``CREATE VIEW WITH ADMIN OPTION`` and
for Django's own test suite. ``CREATE MATERIALIZED VIEW WITH ADMIN OPTION`` privileges. In particular, this
is needed for Django's own test suite.
All of these privileges are included in the DBA role, which is appropriate All of these privileges are included in the DBA role, which is appropriate
for use on a private developer's database. for use on a private developer's database.

View File

@ -399,6 +399,12 @@ it because ``True`` is its default value).
Database-specific notes Database-specific notes
~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~
Oracle
^^^^^^
* Models are created for materialized views if :option:`--include-views` is
used.
PostgreSQL PostgreSQL
^^^^^^^^^^ ^^^^^^^^^^

View File

@ -182,7 +182,7 @@ Management Commands
* :djadmin:`inspectdb` now creates models for foreign tables on PostgreSQL. * :djadmin:`inspectdb` now creates models for foreign tables on PostgreSQL.
* :option:`inspectdb --include-views` now creates models for materialized views * :option:`inspectdb --include-views` now creates models for materialized views
on PostgreSQL. on Oracle and PostgreSQL.
* The new :option:`inspectdb --include-partitions` option allows creating * The new :option:`inspectdb --include-partitions` option allows creating
models for partition tables on PostgreSQL. In older versions, models are models for partition tables on PostgreSQL. In older versions, models are

View File

@ -310,16 +310,16 @@ class InspectDBTransactionalTests(TransactionTestCase):
with connection.cursor() as cursor: with connection.cursor() as cursor:
cursor.execute('DROP VIEW inspectdb_people_view') cursor.execute('DROP VIEW inspectdb_people_view')
@skipUnless(connection.vendor == 'postgresql', 'PostgreSQL specific SQL') @skipUnlessDBFeature('can_introspect_materialized_views')
def test_include_materialized_views(self): def test_include_materialized_views(self):
"""inspectdb --include-views creates models for database materialized views.""" """inspectdb --include-views creates models for materialized views."""
with connection.cursor() as cursor: with connection.cursor() as cursor:
cursor.execute( cursor.execute(
'CREATE MATERIALIZED VIEW inspectdb_people_materialized_view AS ' 'CREATE MATERIALIZED VIEW inspectdb_people_materialized AS '
'SELECT id, name FROM inspectdb_people' 'SELECT id, name FROM inspectdb_people'
) )
out = StringIO() out = StringIO()
view_model = 'class InspectdbPeopleMaterializedView(models.Model):' view_model = 'class InspectdbPeopleMaterialized(models.Model):'
view_managed = 'managed = False # Created from a view.' view_managed = 'managed = False # Created from a view.'
try: try:
call_command('inspectdb', table_name_filter=inspectdb_tables_only, stdout=out) call_command('inspectdb', table_name_filter=inspectdb_tables_only, stdout=out)
@ -332,7 +332,7 @@ class InspectDBTransactionalTests(TransactionTestCase):
self.assertIn(view_managed, with_views_output) self.assertIn(view_managed, with_views_output)
finally: finally:
with connection.cursor() as cursor: with connection.cursor() as cursor:
cursor.execute('DROP MATERIALIZED VIEW IF EXISTS inspectdb_people_materialized_view') cursor.execute('DROP MATERIALIZED VIEW inspectdb_people_materialized')
@skipUnless(connection.vendor == 'postgresql', 'PostgreSQL specific SQL') @skipUnless(connection.vendor == 'postgresql', 'PostgreSQL specific SQL')
@skipUnlessDBFeature('supports_table_partitions') @skipUnlessDBFeature('supports_table_partitions')