From 343d163c881e978b31f30e6ecdde73286f241bdc Mon Sep 17 00:00:00 2001 From: David Wobrock Date: Tue, 8 Jun 2021 23:19:20 +0200 Subject: [PATCH] Add Postgresql warning when using CREATE INDEX without CONCURRENTLY keyword --- .../sql_analyser/analyser.py | 2 +- .../sql_analyser/postgresql.py | 13 +++++++++++- tests/unit/test_sql_analyser.py | 20 +++++++++++++++++-- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/django_migration_linter/sql_analyser/analyser.py b/django_migration_linter/sql_analyser/analyser.py index d69f873..d98610a 100644 --- a/django_migration_linter/sql_analyser/analyser.py +++ b/django_migration_linter/sql_analyser/analyser.py @@ -29,4 +29,4 @@ def analyse_sql_statements( ): sql_analyser = get_sql_analyser(database_vendor, exclude_migration_tests) sql_analyser.analyse(sql_statements) - return sql_analyser.errors, sql_analyser.ignored + return sql_analyser.errors, sql_analyser.ignored, sql_analyser.warnings diff --git a/django_migration_linter/sql_analyser/postgresql.py b/django_migration_linter/sql_analyser/postgresql.py index 5da16c2..4ff31fe 100644 --- a/django_migration_linter/sql_analyser/postgresql.py +++ b/django_migration_linter/sql_analyser/postgresql.py @@ -1,5 +1,16 @@ +import re + from .base import BaseAnalyser class PostgresqlAnalyser(BaseAnalyser): - migration_tests = [] + migration_tests = [ + { + "code": "CREATE_INDEX", + "fn": lambda sql, **kw: re.search("CREATE (UNIQUE )?INDEX", sql) + and not re.search("INDEX CONCURRENTLY", sql), + "msg": "CREATE INDEX locks table", + "mode": "one_liner", + "type": "warning", + }, + ] diff --git a/tests/unit/test_sql_analyser.py b/tests/unit/test_sql_analyser.py index 4b646ca..07a6246 100644 --- a/tests/unit/test_sql_analyser.py +++ b/tests/unit/test_sql_analyser.py @@ -14,13 +14,17 @@ class SqlAnalyserTestCase(unittest.TestCase): ) def assertValidSql(self, sql): - errors, _ = self.analyse_sql(sql) + errors, _, _ = self.analyse_sql(sql) self.assertEqual(0, len(errors), "Found errors in sql: {}".format(errors)) def assertBackwardIncompatibleSql(self, sql): - errors, _ = self.analyse_sql(sql) + errors, _, _ = self.analyse_sql(sql) self.assertNotEqual(0, len(errors), "Found no errors in sql") + def assertWarningSql(self, sql): + _, _, warnings = self.analyse_sql(sql) + self.assertNotEqual(0, len(warnings), "Found no warnings in sql") + class MySqlAnalyserTestCase(SqlAnalyserTestCase): database_vendor = "mysql" @@ -203,3 +207,15 @@ class PostgresqlAnalyserTestCase(SqlAnalyserTestCase): 'ALTER TABLE "app_drop_default_a" ALTER COLUMN "col" SET DEFAULT `\'empty\';', ] self.assertValidSql(sql) + + def test_create_index_non_concurrently(self): + sql = "CREATE INDEX ON films ((lower(titre)));" + self.assertWarningSql(sql) + sql = "CREATE UNIQUE INDEX title_idx ON films (title);" + self.assertWarningSql(sql) + + def test_create_index_concurrently(self): + sql = "CREATE INDEX CONCURRENTLY ON films ((lower(titre)));" + self.assertValidSql(sql) + sql = "CREATE UNIQUE INDEX CONCURRENTLY title_idx ON films (title);" + self.assertValidSql(sql)