Query goal: | Find duplicate constraints, which make it more difficult to maintain the constraints. Do remember that the same task can be solved in SQL usually in multiple different ways. Thus, the exact copies are not the only possible duplication. |
Notes about the query: | The query finds primary key/unique columns and foreign key columns that reference these that have the same CHECK constraint expression. Finds cases where both the key column and the foreign key column have a constraint that is directly attached to the table or if the columns have been defined based on different domains that have the same constraint. The latter (defining candidate key and foreign key columns based on different domains) is bad because the change of a constraint in one domain requires corresponding change in another domain as well. The referential constraint creates an implicit dependency between the domains. The query relies on using the internal representation of the check constraint. Thus, if in one case the representation of the expression uses type casting but in another case does not, then in terms of the query these are two different expressions. Therefore, the query may have false negative results if one one of the checks is associated directly with a column but another is associated with the domain. |
Query type: | Problem detection (Each row in the result could represent a flaw in the design) |
Query reliability: | Medium (Medium number of false-positive results) |
Query license: | MIT License |
Fixing suggestion: | Drop the check constraint from the foreign key columns. If the foreign column is associated with a domain, then one may want to consider removing the domain from the column and associating the column directly with a data type. |
Data source: | INFORMATION_SCHEMA+system catalog |
SQL query: | Click on query to copy it
WITH f AS (select (select nspname from pg_namespace where oid=f.relnamespace) as foreign_schema, f.relname as foreign_table, (select a.attname from pg_attribute a where a.attrelid = f.oid and a.attnum = o.confkey[1] and a.attisdropped = false) as foreign_colname, (select nspname from pg_namespace where oid=c.relnamespace) as target_schema, c.relname as target_table, (select a.attname from pg_attribute a where a.attrelid = c.oid and a.attnum = o.conkey[1] and a.attisdropped = false) as target_colname from pg_constraint o inner join pg_class c on c.oid = o.conrelid inner join pg_class f on f.oid = o.confrelid where o.contype = 'f' AND cardinality(o.conkey)=1), c AS (SELECT table_schema, table_name, column_name, regexp_replace(check_clause, '([(| ])(' || column_name || ')([) | ])', E'\\1VALUE\\3') AS check_clause, domain_schema, domain_name, 'TABLE CHECK' AS check_type FROM (select (select nspname from pg_namespace where oid=c.relnamespace) as table_schema, c.relname as table_name, (select a.attname from pg_attribute a where a.attrelid = c.oid and a.attnum = o.conkey[1] and a.attisdropped = false) as column_name, substring(pg_get_constraintdef(o.oid),7) AS check_clause, NULL AS domain_schema, NULL AS domain_name from pg_constraint o inner join pg_class c on c.oid = o.conrelid where o.contype = 'c' AND cardinality(o.conkey)=1) AS chk WHERE table_schema NOT IN (SELECT schema_name FROM INFORMATION_SCHEMA.schemata WHERE schema_name<>'public' AND schema_owner='postgres' AND schema_name IS NOT NULL) UNION SELECT cdu.table_schema, cdu.table_name, cdu.column_name, cc.check_clause, cdu.domain_schema, cdu.domain_name, 'DOMAIN CHECK' AS check_type FROM INFORMATION_SCHEMA.column_domain_usage AS cdu INNER JOIN INFORMATION_SCHEMA.tables AS t USING (table_schema, table_name) INNER JOIN INFORMATION_SCHEMA.domain_constraints AS dc USING (domain_schema, domain_name) INNER JOIN INFORMATION_SCHEMA.check_constraints AS cc USING (constraint_schema, constraint_name) WHERE t.table_type IN ('BASE TABLE', 'FOREIGN') AND cc.check_clause NOT LIKE '%IS NOT NULL' AND cdu.table_schema NOT IN (SELECT schema_name FROM INFORMATION_SCHEMA.schemata WHERE schema_name<>'public' AND schema_owner='postgres' AND schema_name IS NOT NULL)) SELECT f.*, cd.check_clause AS check_on_target_column, cd.check_type, cd.domain_schema AS domain_schema_target, cd.domain_name AS domain_name_target FROM f, c AS cp, c AS cd WHERE f.foreign_schema=cp.table_schema AND f.foreign_table=cp.table_name AND f.foreign_colname=cp.column_name AND f.target_schema = cd.table_schema AND f.target_table = cd.table_name AND f.target_colname = cd.column_name AND cd.check_clause=cp.check_clause AND ((cd.domain_name IS DISTINCT FROM cp.domain_name) OR (cd.domain_name IS NULL AND cp.domain_name IS NULL)) ORDER BY target_schema, target_table; |
SQL query | Description |
---|---|
WITH f AS (select (select nspname from pg_namespace where oid=f.relnamespace) as foreign_schema, f.relname as foreign_table, (select a.attname from pg_attribute a where a.attrelid = f.oid and a.attnum = o.confkey[1] and a.attisdropped = false) as foreign_colname, (select nspname from pg_namespace where oid=c.relnamespace) as target_schema, c.relname as target_table, (select a.attname from pg_attribute a where a.attrelid = c.oid and a.attnum = o.conkey[1] and a.attisdropped = false) as target_colname from pg_constraint o inner join pg_class c on c.oid = o.conrelid inner join pg_class f on f.oid = o.confrelid where o.contype = 'f' AND cardinality(o.conkey)=1), c AS (SELECT table_schema, table_name, column_name, constraint_name, regexp_replace(check_clause, '([(| ])(' || column_name || ')([) | ])', E'\\1VALUE\\3') AS check_clause, domain_schema, domain_name, 'TABLE CHECK' AS check_type FROM (select o.conname as constraint_name, (select nspname from pg_namespace where oid=c.relnamespace) as table_schema, c.relname as table_name, (select a.attname from pg_attribute a where a.attrelid = c.oid and a.attnum = o.conkey[1] and a.attisdropped = false) as column_name, substring(pg_get_constraintdef(o.oid),7) AS check_clause, NULL AS domain_schema, NULL AS domain_name from pg_constraint o inner join pg_class c on c.oid = o.conrelid where o.contype = 'c' AND cardinality(o.conkey)=1) AS chk WHERE table_schema NOT IN (SELECT schema_name FROM INFORMATION_SCHEMA.schemata WHERE schema_name<>'public' AND schema_owner='postgres' AND schema_name IS NOT NULL) UNION SELECT cdu.table_schema, cdu.table_name, cdu.column_name, dc.constraint_name, cc.check_clause, cdu.domain_schema, cdu.domain_name, 'DOMAIN CHECK' AS check_type FROM INFORMATION_SCHEMA.column_domain_usage AS cdu INNER JOIN INFORMATION_SCHEMA.tables AS t USING (table_schema, table_name) INNER JOIN INFORMATION_SCHEMA.domain_constraints AS dc USING (domain_schema, domain_name) INNER JOIN INFORMATION_SCHEMA.check_constraints AS cc USING (constraint_schema, constraint_name) WHERE t.table_type IN ('BASE TABLE', 'FOREIGN') AND cc.check_clause NOT LIKE '%IS NOT NULL' AND cdu.table_schema NOT IN (SELECT schema_name FROM INFORMATION_SCHEMA.schemata WHERE schema_name<>'public' AND schema_owner='postgres' AND schema_name IS NOT NULL)), redundant_constraints AS (SELECT f.target_schema, f.target_table, cd.constraint_name, cd.check_type, cd.domain_schema AS domain_schema_target, cd.domain_name AS domain_name_target FROM f, c AS cp, c AS cd WHERE f.foreign_schema=cp.table_schema AND f.foreign_table=cp.table_name AND f.foreign_colname=cp.column_name AND f.target_schema = cd.table_schema AND f.target_table = cd.table_name AND f.target_colname = cd.column_name AND cd.check_clause=cp.check_clause AND ((cd.domain_name IS DISTINCT FROM cp.domain_name) OR (cd.domain_name IS NULL AND cp.domain_name IS NULL))) SELECT DISTINCT CASE WHEN check_type='TABLE CHECK' THEN format('ALTER TABLE %1$I.%2$I DROP CONSTRAINT %3$I;', target_schema, target_table, constraint_name) WHEN check_type='DOMAIN CHECK' THEN format('ALTER DOMAIN %1$I.%2$I DROP CONSTRAINT %3$I;', domain_schema_target, domain_name_target, constraint_name) END AS statements FROM redundant_constraints ORDER BY statements; | Drop the constraint. |
Collection name | Collection description |
---|---|
Find problems automatically | Queries, that results point to problems in the database. Each query in the collection produces an initial assessment. However, a human reviewer has the final say as to whether there is a problem or not . |
Category name | Category description |
---|---|
CHECK constraints | Queries of this category provide information about CHECK constraints. |
Duplication of implementation elements | Queries of this catergory provide information about the duplication of the database objects. |
Reference |
---|
https://refactoring.guru/smells/alternative-classes-with-different-interfaces |
https://refactoring.guru/smells/duplicate-code |
The corresponding code smell in case of cleaning code is "G5: Duplication". (Robert C. Martin, Clean Code) |