Refs #29722 -- Added introspection of materialized views for Oracle.
Thanks Tim Graham for the review.
This commit is contained in:
parent
26c2a6ff88
commit
f091ea3515
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
^^^^^^^^^^
|
^^^^^^^^^^
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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')
|
||||||
|
|
Loading…
Reference in New Issue