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?
supports_index_column_ordering = True
# Does the backend support introspection of materialized views?
can_introspect_materialized_views = False
# Support for the DISTINCT ON clause
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:
set_password = 'ALTER USER %(user)s IDENTIFIED BY "%(password)s"'
self._execute_statements(cursor, [set_password], parameters, verbosity)
# Most test-suites can be run without the create-view privilege. But some need it.
extra = "GRANT CREATE VIEW TO %(user)s"
# Most test suites can be run without "create view" and
# "create materialized view" privileges. But some need it.
for object_type in ('VIEW', 'MATERIALIZED VIEW'):
extra = 'GRANT CREATE %(object_type)s TO %(user)s'
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 VIEW permission to test user. This may be ok.')
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):
if verbosity >= 2:

View File

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

View File

@ -48,8 +48,20 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
def get_table_list(self, cursor):
"""Return a list of table and view names in the current database."""
cursor.execute("SELECT TABLE_NAME, 't' FROM USER_TABLES UNION ALL "
"SELECT VIEW_NAME, 'v' FROM USER_VIEWS")
cursor.execute("""
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()]
def get_table_description(self, cursor, table_name):

View File

@ -21,6 +21,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
supports_transactions = True
can_introspect_autofield = True
can_introspect_ip_address_field = True
can_introspect_materialized_views = True
can_introspect_small_integer_field = True
can_distinct_on_fields = 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
``RESOURCE WITH ADMIN OPTION`` is not usually sufficient for running tests.
Some test suites also create views; to run these, the user also needs
the ``CREATE VIEW WITH ADMIN OPTION`` privilege. In particular, this is needed
for Django's own test suite.
Some test suites also create views or materialized views; to run these, the
user also needs ``CREATE VIEW WITH ADMIN OPTION`` and
``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
for use on a private developer's database.

View File

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

View File

@ -182,7 +182,7 @@ Management Commands
* :djadmin:`inspectdb` now creates models for foreign tables on PostgreSQL.
* :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
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:
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):
"""inspectdb --include-views creates models for database materialized views."""
"""inspectdb --include-views creates models for materialized views."""
with connection.cursor() as cursor:
cursor.execute(
'CREATE MATERIALIZED VIEW inspectdb_people_materialized_view AS '
'CREATE MATERIALIZED VIEW inspectdb_people_materialized AS '
'SELECT id, name FROM inspectdb_people'
)
out = StringIO()
view_model = 'class InspectdbPeopleMaterializedView(models.Model):'
view_model = 'class InspectdbPeopleMaterialized(models.Model):'
view_managed = 'managed = False # Created from a view.'
try:
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)
finally:
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')
@skipUnlessDBFeature('supports_table_partitions')