mirror of
https://github.com/django/django.git
synced 2025-03-31 19:46:42 +00:00
Refs #27098 -- Added introspection for expression-based index on PostgreSQL
Also test it on PostGIS raster fields.
This commit is contained in:
parent
0c6fbea59b
commit
a1ad896422
@ -152,7 +152,9 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
|
|||||||
|
|
||||||
def get_constraints(self, cursor, table_name):
|
def get_constraints(self, cursor, table_name):
|
||||||
"""
|
"""
|
||||||
Retrieves any constraints or keys (unique, pk, fk, check, index) across one or more columns.
|
Retrieve any constraints or keys (unique, pk, fk, check, index) across
|
||||||
|
one or more columns. Also retrieve the definition of expression-based
|
||||||
|
indexes.
|
||||||
"""
|
"""
|
||||||
constraints = {}
|
constraints = {}
|
||||||
# Loop over the key table, collecting things as constraints. The column
|
# Loop over the key table, collecting things as constraints. The column
|
||||||
@ -191,15 +193,20 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
|
|||||||
"foreign_key": tuple(used_cols.split(".", 1)) if kind == "f" else None,
|
"foreign_key": tuple(used_cols.split(".", 1)) if kind == "f" else None,
|
||||||
"check": kind == "c",
|
"check": kind == "c",
|
||||||
"index": False,
|
"index": False,
|
||||||
|
"definition": None,
|
||||||
}
|
}
|
||||||
# Now get indexes
|
# Now get indexes
|
||||||
cursor.execute("""
|
cursor.execute("""
|
||||||
SELECT
|
SELECT
|
||||||
indexname, array_agg(attname), indisunique, indisprimary,
|
indexname, array_agg(attname), indisunique, indisprimary,
|
||||||
array_agg(ordering), amname
|
array_agg(ordering), amname, exprdef
|
||||||
FROM (
|
FROM (
|
||||||
SELECT
|
SELECT
|
||||||
c2.relname as indexname, idx.*, attr.attname, am.amname,
|
c2.relname as indexname, idx.*, attr.attname, am.amname,
|
||||||
|
CASE
|
||||||
|
WHEN idx.indexprs IS NOT NULL THEN
|
||||||
|
pg_get_indexdef(idx.indexrelid)
|
||||||
|
END AS exprdef,
|
||||||
CASE
|
CASE
|
||||||
WHEN am.amcanorder THEN
|
WHEN am.amcanorder THEN
|
||||||
CASE (option & 1)
|
CASE (option & 1)
|
||||||
@ -217,18 +224,19 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
|
|||||||
LEFT JOIN pg_attribute attr ON attr.attrelid = c.oid AND attr.attnum = idx.key
|
LEFT JOIN pg_attribute attr ON attr.attrelid = c.oid AND attr.attnum = idx.key
|
||||||
WHERE c.relname = %s
|
WHERE c.relname = %s
|
||||||
) s2
|
) s2
|
||||||
GROUP BY indexname, indisunique, indisprimary, amname;
|
GROUP BY indexname, indisunique, indisprimary, amname, exprdef;
|
||||||
""", [table_name])
|
""", [table_name])
|
||||||
for index, columns, unique, primary, orders, type_ in cursor.fetchall():
|
for index, columns, unique, primary, orders, type_, definition in cursor.fetchall():
|
||||||
if index not in constraints:
|
if index not in constraints:
|
||||||
constraints[index] = {
|
constraints[index] = {
|
||||||
"columns": columns,
|
"columns": columns if columns != [None] else [],
|
||||||
"orders": orders,
|
"orders": orders if orders != [None] else [],
|
||||||
"primary_key": primary,
|
"primary_key": primary,
|
||||||
"unique": unique,
|
"unique": unique,
|
||||||
"foreign_key": None,
|
"foreign_key": None,
|
||||||
"check": False,
|
"check": False,
|
||||||
"index": True,
|
"index": True,
|
||||||
"type": type_,
|
"type": type_,
|
||||||
|
"definition": definition,
|
||||||
}
|
}
|
||||||
return constraints
|
return constraints
|
||||||
|
@ -67,9 +67,16 @@ class OperationTests(TransactionTestCase):
|
|||||||
expected_count
|
expected_count
|
||||||
)
|
)
|
||||||
|
|
||||||
def assertSpatialIndexExists(self, table, column):
|
def assertSpatialIndexExists(self, table, column, raster=False):
|
||||||
with connection.cursor() as cursor:
|
with connection.cursor() as cursor:
|
||||||
constraints = connection.introspection.get_constraints(cursor, table)
|
constraints = connection.introspection.get_constraints(cursor, table)
|
||||||
|
if raster:
|
||||||
|
self.assertTrue(any(
|
||||||
|
'st_convexhull(%s)' % column in c['definition']
|
||||||
|
for c in constraints.values()
|
||||||
|
if c['definition'] is not None
|
||||||
|
))
|
||||||
|
else:
|
||||||
self.assertIn([column], [c['columns'] for c in constraints.values()])
|
self.assertIn([column], [c['columns'] for c in constraints.values()])
|
||||||
|
|
||||||
def alter_gis_model(self, migration_class, model_name, field_name,
|
def alter_gis_model(self, migration_class, model_name, field_name,
|
||||||
@ -111,7 +118,7 @@ class OperationTests(TransactionTestCase):
|
|||||||
|
|
||||||
# Test spatial indices when available
|
# Test spatial indices when available
|
||||||
if self.has_spatial_indexes:
|
if self.has_spatial_indexes:
|
||||||
self.assertSpatialIndexExists('gis_neighborhood', 'heatmap')
|
self.assertSpatialIndexExists('gis_neighborhood', 'heatmap', raster=True)
|
||||||
|
|
||||||
@skipIfDBFeature('supports_raster')
|
@skipIfDBFeature('supports_raster')
|
||||||
def test_create_raster_model_on_db_without_raster_support(self):
|
def test_create_raster_model_on_db_without_raster_support(self):
|
||||||
@ -159,7 +166,7 @@ class OperationTests(TransactionTestCase):
|
|||||||
|
|
||||||
# Test spatial indices when available
|
# Test spatial indices when available
|
||||||
if self.has_spatial_indexes:
|
if self.has_spatial_indexes:
|
||||||
self.assertSpatialIndexExists('gis_neighborhood', 'heatmap')
|
self.assertSpatialIndexExists('gis_neighborhood', 'heatmap', raster=True)
|
||||||
|
|
||||||
def test_remove_geom_field(self):
|
def test_remove_geom_field(self):
|
||||||
"""
|
"""
|
||||||
@ -189,7 +196,7 @@ class OperationTests(TransactionTestCase):
|
|||||||
self.assertSpatialIndexExists('gis_neighborhood', 'geom')
|
self.assertSpatialIndexExists('gis_neighborhood', 'geom')
|
||||||
|
|
||||||
if connection.features.supports_raster:
|
if connection.features.supports_raster:
|
||||||
self.assertSpatialIndexExists('gis_neighborhood', 'rast')
|
self.assertSpatialIndexExists('gis_neighborhood', 'rast', raster=True)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def has_spatial_indexes(self):
|
def has_spatial_indexes(self):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user