From 43ce4e91922e54e09071415d601bb28fab6e9487 Mon Sep 17 00:00:00 2001 From: Adrian Liechti Date: Tue, 20 Oct 2015 15:03:39 +0200 Subject: [PATCH] Fixes #6876: Fixed RBAC migration MSSQL cascade problem --- framework/CHANGELOG.md | 1 + .../rbac/migrations/m140506_102106_rbac_init.php | 60 ++++++++++++++++++++-- framework/rbac/migrations/schema-mssql.sql | 41 +++++++++++++-- 3 files changed, 95 insertions(+), 7 deletions(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 97f43bc..fbc834e 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -6,6 +6,7 @@ Yii Framework 2 Change Log - Bug #6351: Find MySQL FK constraints from `information_schema` tables instead of `SHOW CREATE TABLE` to improve reliability (nineinchnick) - Bug #6363, #8301, #8582, #9566: Fixed data methods and PJAX issues when used together (derekisbusy) +- Bug #6876: Fixed RBAC migration MSSQL cascade problem (thejahweh) - Bug #8723: Fixed `yii\helpers\VarDumper::export()` unable to export circle referenced objects with `Closure` (klimov-paul) - Bug #9108: Negative number resulted in no formatting when using `Formatter::asSize()` or `Formatter::asShortSize` (nxnx, cebe) - Bug #9288: Fixed `FileHelper::createDirectory` directory creation to be concurrency friendly (dynasource) diff --git a/framework/rbac/migrations/m140506_102106_rbac_init.php b/framework/rbac/migrations/m140506_102106_rbac_init.php index b91d979..9aff418 100644 --- a/framework/rbac/migrations/m140506_102106_rbac_init.php +++ b/framework/rbac/migrations/m140506_102106_rbac_init.php @@ -30,6 +30,14 @@ class m140506_102106_rbac_init extends \yii\db\Migration } /** + * @return bool + */ + protected function isMSSQL() + { + return $this->db->driverName === 'mssql' || $this->db->driverName === 'sqlsrv' || $this->db->driverName === 'dblib'; + } + + /** * @inheritdoc */ public function up() @@ -60,7 +68,8 @@ class m140506_102106_rbac_init extends \yii\db\Migration 'created_at' => $this->integer(), 'updated_at' => $this->integer(), 'PRIMARY KEY (name)', - 'FOREIGN KEY (rule_name) REFERENCES ' . $authManager->ruleTable . ' (name) ON DELETE SET NULL ON UPDATE CASCADE', + 'FOREIGN KEY (rule_name) REFERENCES ' . $authManager->ruleTable . ' (name)'. + ($this->isMSSQL() ? '' : ' ON DELETE SET NULL ON UPDATE CASCADE'), ], $tableOptions); $this->createIndex('idx-auth_item-type', $authManager->itemTable, 'type'); @@ -68,8 +77,10 @@ class m140506_102106_rbac_init extends \yii\db\Migration 'parent' => $this->string(64)->notNull(), 'child' => $this->string(64)->notNull(), 'PRIMARY KEY (parent, child)', - 'FOREIGN KEY (parent) REFERENCES ' . $authManager->itemTable . ' (name) ON DELETE CASCADE ON UPDATE CASCADE', - 'FOREIGN KEY (child) REFERENCES ' . $authManager->itemTable . ' (name) ON DELETE CASCADE ON UPDATE CASCADE', + 'FOREIGN KEY (parent) REFERENCES ' . $authManager->itemTable . ' (name)'. + ($this->isMSSQL() ? '' : ' ON DELETE CASCADE ON UPDATE CASCADE'), + 'FOREIGN KEY (child) REFERENCES ' . $authManager->itemTable . ' (name)'. + ($this->isMSSQL() ? '' : ' ON DELETE CASCADE ON UPDATE CASCADE'), ], $tableOptions); $this->createTable($authManager->assignmentTable, [ @@ -79,6 +90,43 @@ class m140506_102106_rbac_init extends \yii\db\Migration 'PRIMARY KEY (item_name, user_id)', 'FOREIGN KEY (item_name) REFERENCES ' . $authManager->itemTable . ' (name) ON DELETE CASCADE ON UPDATE CASCADE', ], $tableOptions); + + if($this->isMsSQL()) { + $this->execute("CREATE TRIGGER dbo.trigger_auth_item_child + ON dbo.{$authManager->itemTable} + INSTEAD OF DELETE, UPDATE + AS + DECLARE @old_name VARCHAR (64) = (SELECT name FROM deleted) + DECLARE @new_name VARCHAR (64) = (SELECT name FROM inserted) + BEGIN + IF COLUMNS_UPDATED() > 0 + BEGIN + IF @old_name <> @new_name + BEGIN + ALTER TABLE auth_item_child NOCHECK CONSTRAINT FK__auth_item__child; + UPDATE auth_item_child SET child = @new_name WHERE child = @old_name; + END + UPDATE auth_item + SET name = (SELECT name FROM inserted), + type = (SELECT type FROM inserted), + description = (SELECT description FROM inserted), + rule_name = (SELECT rule_name FROM inserted), + data = (SELECT data FROM inserted), + created_at = (SELECT created_at FROM inserted), + updated_at = (SELECT updated_at FROM inserted) + WHERE name IN (SELECT name FROM deleted) + IF @old_name <> @new_name + BEGIN + ALTER TABLE auth_item_child CHECK CONSTRAINT FK__auth_item__child; + END + END + ELSE + BEGIN + DELETE FROM dbo.{$authManager->itemChildTable} WHERE parent IN (SELECT name FROM deleted) OR child IN (SELECT name FROM deleted); + DELETE FROM dbo.{$authManager->itemTable} WHERE name IN (SELECT name FROM deleted); + END + END;"); + } } /** @@ -89,9 +137,13 @@ class m140506_102106_rbac_init extends \yii\db\Migration $authManager = $this->getAuthManager(); $this->db = $authManager->db; + if($this->isMsSQL()) { + $this->execute('DROP dbo.trigger_auth_item_child;'); + } + $this->dropTable($authManager->assignmentTable); $this->dropTable($authManager->itemChildTable); $this->dropTable($authManager->itemTable); $this->dropTable($authManager->ruleTable); } -} +} \ No newline at end of file diff --git a/framework/rbac/migrations/schema-mssql.sql b/framework/rbac/migrations/schema-mssql.sql index 5e1596f..4bc940d 100644 --- a/framework/rbac/migrations/schema-mssql.sql +++ b/framework/rbac/migrations/schema-mssql.sql @@ -33,7 +33,7 @@ create table [auth_item] [created_at] integer, [updated_at] integer, primary key ([name]), - foreign key ([rule_name]) references [auth_rule] ([name]) on delete set null on update cascade + foreign key ([rule_name]) references [auth_rule] ([name]) ); create index [idx-auth_item-type] on [auth_item] ([type]); @@ -43,8 +43,8 @@ create table [auth_item_child] [parent] varchar(64) not null, [child] varchar(64) not null, primary key ([parent],[child]), - foreign key ([parent]) references [auth_item] ([name]) on delete cascade on update cascade, - foreign key ([child]) references [auth_item] ([name]) on delete cascade on update cascade + foreign key ([parent]) references [auth_item] ([name]), + foreign key ([child]) references [auth_item] ([name]) ); create table [auth_assignment] @@ -55,3 +55,38 @@ create table [auth_assignment] primary key ([item_name], [user_id]), foreign key ([item_name]) references [auth_item] ([name]) on delete cascade on update cascade ); + +CREATE TRIGGER dbo.trigger_auth_item_child + ON dbo.[auth_item] + INSTEAD OF DELETE, UPDATE + AS + DECLARE @old_name VARCHAR (64) = (SELECT name FROM deleted) + DECLARE @new_name VARCHAR (64) = (SELECT name FROM inserted) + BEGIN + IF COLUMNS_UPDATED() > 0 + BEGIN + IF @old_name <> @new_name + BEGIN + ALTER TABLE auth_item_child NOCHECK CONSTRAINT FK__auth_item__child; + UPDATE auth_item_child SET child = @new_name WHERE child = @old_name; + END + UPDATE auth_item + SET name = (SELECT name FROM inserted), + type = (SELECT type FROM inserted), + description = (SELECT description FROM inserted), + rule_name = (SELECT rule_name FROM inserted), + data = (SELECT data FROM inserted), + created_at = (SELECT created_at FROM inserted), + updated_at = (SELECT updated_at FROM inserted) + WHERE name IN (SELECT name FROM deleted) + IF @old_name <> @new_name + BEGIN + ALTER TABLE auth_item_child CHECK CONSTRAINT FK__auth_item__child; + END + END + ELSE + BEGIN + DELETE FROM dbo.[auth_item_child] WHERE parent IN (SELECT name FROM deleted) OR child IN (SELECT name FROM deleted); + DELETE FROM dbo.[auth_item] WHERE name IN (SELECT name FROM deleted); + END + END; \ No newline at end of file