Skip to content

Commit

Permalink
When dropping a column, also drop related policies
Browse files Browse the repository at this point in the history
Fixes #1550
  • Loading branch information
mpscholten committed Oct 18, 2022
1 parent 928cacd commit 67ac12c
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 1 deletion.
32 changes: 32 additions & 0 deletions IHP/IDE/SchemaDesigner/SchemaOperations.hs
Original file line number Diff line number Diff line change
Expand Up @@ -552,11 +552,43 @@ deleteColumn DeleteColumnOptions { .. } schema =
then deleteTriggerIfExists (updatedAtTriggerName tableName)
else \schema -> schema
)
|> filter deletePolicyReferencingPolicy
where
deleteColumnInTable :: Statement -> Statement
deleteColumnInTable (StatementCreateTable table@CreateTable { name, columns }) | name == tableName = StatementCreateTable $ table { columns = delete (columns !! columnId) columns}
deleteColumnInTable statement = statement

deletePolicyReferencingPolicy :: Statement -> Bool
deletePolicyReferencingPolicy CreatePolicy { tableName = policyTable, using, check } | policyTable == tableName =
case (using, check) of
(Just using, Nothing) -> not (isRef using)
(Nothing, Just check) -> not (isRef check)
(Just using, Just check) -> not (isRef using && isRef check)
where
isRef :: Expression -> Bool
isRef (TextExpression {}) = False
isRef (VarExpression var) = var == columnName
isRef (CallExpression _ args) = foldl' (||) False (map isRef args)
isRef (NotEqExpression a b) = isRef a || isRef b
isRef (EqExpression a b) = isRef a || isRef b
isRef (AndExpression a b) = isRef a || isRef b
isRef (IsExpression a b) = isRef a || isRef b
isRef (InExpression a b) = isRef a || isRef b
isRef (NotExpression a) = isRef a
isRef (ExistsExpression a) = isRef a
isRef (OrExpression a b) = isRef a || isRef b
isRef (LessThanExpression a b) = isRef a || isRef b
isRef (LessThanOrEqualToExpression a b) = isRef a || isRef b
isRef (GreaterThanExpression a b) = isRef a || isRef b
isRef (GreaterThanOrEqualToExpression a b) = isRef a || isRef b
isRef (DoubleExpression _) = False
isRef (IntExpression _) = False
isRef (TypeCastExpression a _) = isRef a
isRef (SelectExpression _) = False
isRef (DotExpression a _) = isRef a
isRef (ConcatenationExpression a b) = isRef a || isRef b
deletePolicyReferencingPolicy otherwise = True

-- | Returns True if a CreateIndex statement references a specific column
--
-- E.g. given a schema like this:
Expand Down
27 changes: 26 additions & 1 deletion Test/IDE/CodeGeneration/MigrationGenerator.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1204,8 +1204,33 @@ CREATE POLICY "Users can read and edit their own record" ON public.users USING (
|]

diffSchemas targetSchema actualSchema `shouldBe` migration

it "should delete policies when the column is deleted" do
-- https://github.com/digitallyinduced/ihp/issues/1480
let targetSchema = sql $ cs [plain|
CREATE TABLE artefacts (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL
);
ALTER TABLE artefacts ENABLE ROW LEVEL SECURITY;
|]
let actualSchema = sql $ cs [plain|
CREATE TABLE artefacts (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL,
user_id UUID DEFAULT ihp_user_id() NOT NULL
);
ALTER TABLE artefacts ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Users can manage their artefacts" ON artefacts USING (user_id = ihp_user_id()) WITH CHECK (user_id = ihp_user_id());
|]
let migration = sql [i|
ALTER TABLE artefacts DROP COLUMN user_id;
DROP POLICY "Users can manage their artefacts" ON artefacts;
|]


diffSchemas targetSchema actualSchema `shouldBe` migration
sql :: Text -> [Statement]
sql code = case Megaparsec.runParser Parser.parseDDL "" code of
Left parsingFailed -> error (cs $ Megaparsec.errorBundlePretty parsingFailed)
Expand Down
29 changes: 29 additions & 0 deletions Test/IDE/SchemaDesigner/SchemaOperationsSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,35 @@ tests = do
}

(SchemaOperations.deleteColumn options inputSchema) `shouldBe` expectedSchema

it "should delete an referenced policy" do
let tableAWithUserId = StatementCreateTable CreateTable
{ name = "a"
, columns = [
Column
{ name = "user_id"
, columnType = PUUID
, defaultValue = Just (CallExpression "ihp_user_id" [])
, notNull = True
, isUnique = False
, generator = Nothing
}
]
, primaryKeyConstraint = PrimaryKeyConstraint []
, constraints = []
}
let policy = CreatePolicy { name = "a_policy", tableName = "a", action = Nothing, using = Just (EqExpression (VarExpression "user_id") (CallExpression "ihp_user_id" [])), check = Nothing }

let inputSchema = [tableAWithUserId, policy]
let expectedSchema = [tableA]

let options = SchemaOperations.DeleteColumnOptions
{ tableName = "a"
, columnName = "user_id"
, columnId = 0
}

(SchemaOperations.deleteColumn options inputSchema) `shouldBe` expectedSchema
describe "update" do
it "update a column's name, type, default value and not null" do
let tableAWithCreatedAt = StatementCreateTable CreateTable
Expand Down

0 comments on commit 67ac12c

Please sign in to comment.