Skip to content

Commit

Permalink
Fixed table renames not updating references indexes
Browse files Browse the repository at this point in the history
  • Loading branch information
mpscholten committed May 11, 2022
1 parent 3d7e071 commit 1dacc7d
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 31 deletions.
33 changes: 2 additions & 31 deletions IHP/IDE/SchemaDesigner/Controller/Tables.hs
Original file line number Diff line number Diff line change
Expand Up @@ -62,19 +62,14 @@ instance Controller TablesController where
statements <- readSchema
let tableName = param "tableName"
let tableId = param "tableId"
let oldTableName = (get #name . unsafeGetCreateTable) (statements !! tableId)

let validationResult = tableName |> validateTable statements (Just oldTableName)
case validationResult of
Failure message -> do
setErrorMessage message
redirectTo ShowTableAction { tableName = oldTableName }
Success -> do
let updateConstraintsStatement =
referencingTableForeignKeyConstraints oldTableName statements
|> map (\constraint -> updateReferenceTableOfForeignKeyConstraint constraint tableName statements)

forEach updateConstraintsStatement updateSchema
updateSchema (updateTable tableId tableName)
updateSchema (SchemaOperations.updateTable tableId tableName)
redirectTo ShowTableAction { .. }

action DeleteTableAction { .. } = do
Expand All @@ -83,33 +78,9 @@ instance Controller TablesController where
updateSchema (SchemaOperations.deleteTable tableName)
redirectTo TablesAction



updateTable :: Int -> Text -> [Statement] -> [Statement]
updateTable tableId tableName list = replace tableId (StatementCreateTable CreateTable { name = tableName, columns = get #columns table, primaryKeyConstraint = get #primaryKeyConstraint table, constraints = get #constraints table }) list
where table = unsafeGetCreateTable (list !! tableId)

validateTable :: [Statement] -> Maybe Text -> Validator Text
validateTable statements = validateNameInSchema "table name" (getAllObjectNames statements)

referencingTableForeignKeyConstraints tableName statements =
filter (\statement ->
statement ==
AddConstraint
{ tableName = (get #tableName statement)
, constraint =
ForeignKeyConstraint
{ name = Just (get #constraintName statement)
, columnName = (get #columnName (get #constraint statement))
, referenceTable = tableName
, referenceColumn = (get #referenceColumn (get #constraint statement))
, onDelete = (get #onDelete (get #constraint statement))
}
, deferrable = Nothing
, deferrableType = Nothing
}
) statements

updateReferenceTableOfForeignKeyConstraint constraint newTableName statements =
let Just constraintId = elemIndex constraint statements
tableName = get #tableName constraint
Expand Down
16 changes: 16 additions & 0 deletions IHP/IDE/SchemaDesigner/SchemaOperations.hs
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,22 @@ deleteTable tableName statements =
CreateTrigger { tableName = triggerTable } | triggerTable == tableName -> False
otherwise -> True

updateTable :: Int -> Text -> Schema -> Schema
updateTable tableId tableName statements =
let
oldTableName = (get #name . unsafeGetCreateTable) (statements !! tableId)
in
statements
|> map \case
(StatementCreateTable table@(CreateTable { name })) | name == oldTableName -> StatementCreateTable (table { name = tableName })
constraint@(AddConstraint { tableName = constraintTable, constraint = c }) | constraintTable == oldTableName -> (constraint :: Statement) { tableName, constraint = c { name = Text.replace oldTableName tableName <$> (get #name c) } }
index@(CreateIndex { tableName = indexTable, indexName }) | indexTable == oldTableName -> (index :: Statement) { tableName, indexName = Text.replace oldTableName tableName indexName }
rls@(EnableRowLevelSecurity { tableName = rlsTable }) | rlsTable == oldTableName -> (rls :: Statement) { tableName }
policy@(CreatePolicy { tableName = policyTable, name }) | policyTable == oldTableName -> (policy :: Statement) { tableName, name = Text.replace oldTableName tableName name }
trigger@(CreateTrigger { tableName = triggerTable, name }) | triggerTable == oldTableName -> (trigger :: Statement) { tableName, name = Text.replace oldTableName tableName name }
otherwise -> otherwise


updatedAtTriggerName :: Text -> Text
updatedAtTriggerName tableName = "update_" <> tableName <> "_updated_at"

Expand Down
20 changes: 20 additions & 0 deletions Test/IDE/SchemaDesigner/SchemaOperationsSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,26 @@ tests = do
}

(SchemaOperations.updateColumn options inputSchema) `shouldBe` expectedSchema
describe "updateTable" do
it "renames a table with all it's indices, constraints, policies, enable RLS statements, triggers" do
let inputSchema = parseSqlStatements [trimming|
CREATE TABLE tasks ();
CREATE INDEX tasks_user_id_index ON tasks (user_id);
ALTER TABLE tasks ADD CONSTRAINT tasks_ref_user_id FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE NO ACTION;
CREATE POLICY "Users can manage their tasks" ON tasks USING (user_id = ihp_user_id()) WITH CHECK (user_id = ihp_user_id());
ALTER TABLE tasks ENABLE ROW LEVEL SECURITY;
CREATE TRIGGER update_tasks_updated_at BEFORE UPDATE ON tasks FOR EACH ROW EXECUTE FUNCTION set_updated_at_to_now();
|]
let outputSchema = parseSqlStatements [trimming|
CREATE TABLE todos ();
CREATE INDEX todos_user_id_index ON todos (user_id);
ALTER TABLE todos ADD CONSTRAINT todos_ref_user_id FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE NO ACTION;
CREATE POLICY "Users can manage their todos" ON todos USING (user_id = ihp_user_id()) WITH CHECK (user_id = ihp_user_id());
ALTER TABLE todos ENABLE ROW LEVEL SECURITY;
CREATE TRIGGER update_todos_updated_at BEFORE UPDATE ON todos FOR EACH ROW EXECUTE FUNCTION set_updated_at_to_now();
|]

SchemaOperations.updateTable 0 "todos" inputSchema `shouldBe` outputSchema

parseSqlStatements :: Text -> [Statement]
parseSqlStatements sql =
Expand Down

0 comments on commit 1dacc7d

Please sign in to comment.