DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `account`.`role_syncPrivileges`()
BEGIN
/**
 * Synchronizes permissions of MySQL role users based on role hierarchy.
 * The computed role users of permission mix will be named according to
 * pattern [vGenRolePrefix][roleName].
 *
 * If [vTplUser]@[vRoleHost] user exists, it will be taken as a template for
 * basic attributes.
 *
 * Warning! This procedure should only be called when MySQL privileges
 * are modified. If role hierarchy is modified, you must call the role_sync()
 * procedure wich calls this internally.
 */
	DECLARE vIsMariaDb BOOL DEFAULT VERSION() LIKE '%MariaDB%';
	DECLARE vVersion INT DEFAULT SUBSTRING_INDEX(VERSION(), '.', 1);
	DECLARE vTplUser VARCHAR(255);
	DECLARE vRoleHost VARCHAR(255);
	DECLARE vUserPrefix VARCHAR(2);
	DECLARE vUserHost VARCHAR(255);
	DECLARE vGenRolePrefix VARCHAR(2);
	DECLARE vGenRoleHost VARCHAR(255);
	DECLARE vPrefixedLike VARCHAR(255);
	DECLARE vPassword VARCHAR(255) DEFAULT '';

	SELECT rolePrefix, userPrefix, userHost, tplUser
		INTO vGenRolePrefix, vUserPrefix, vUserHost, vTplUser
		FROM roleConfig;

	-- Deletes computed role users

	SET vPrefixedLike = CONCAT(vGenRolePrefix, '%');
	SET vGenRoleHost = vUserHost;

	IF vIsMariaDb THEN
		SET vRoleHost = '';

		DELETE FROM mysql.global_priv
			WHERE `User` LIKE vPrefixedLike;

		DELETE FROM mysql.roles_mapping
			WHERE `User` LIKE vPrefixedLike
				OR (
					`Role` LIKE vPrefixedLike
					AND (`User`, `Host`) = ('root', 'localhost')
				);
	ELSE
		SET vRoleHost = 'localhost';

		DELETE FROM mysql.user
			WHERE `User` LIKE vPrefixedLike;
	END IF;

	DELETE FROM mysql.db
		WHERE `User` LIKE vPrefixedLike;

	DELETE FROM mysql.tables_priv
		WHERE `User` LIKE vPrefixedLike;

	DELETE FROM mysql.columns_priv
		WHERE `User` LIKE vPrefixedLike;

	DELETE FROM mysql.procs_priv
		WHERE `User` LIKE vPrefixedLike;

	DELETE FROM mysql.proxies_priv
		WHERE `Proxied_user` LIKE vPrefixedLike;

	-- Temporary tables

	DROP TEMPORARY TABLE IF EXISTS tRole;
	CREATE TEMPORARY TABLE tRole
		(INDEX (id))
		ENGINE = MEMORY
		SELECT
			id,
			`name` `role`,
			CONCAT(vGenRolePrefix, `name`) prefixedRole
			FROM `role`
			WHERE hasLogin;

	-- Get role inheritance

	DROP TEMPORARY TABLE IF EXISTS tRoleInherit;
	CREATE TEMPORARY TABLE tRoleInherit
		(INDEX (inheritsFrom))
		ENGINE = MEMORY
		SELECT
			r.prefixedRole,
			ri.`name` inheritsFrom
			FROM tRole r
				JOIN `roleRole` rr ON rr.role = r.id
				JOIN `role` ri ON ri.id = rr.inheritsFrom
				JOIN mysql.`user` u
					ON u.`User` = ri.`name` AND u.`Host` = vRoleHost;

	-- Recreate role users

	IF vIsMariaDb THEN
		-- Roles

		INSERT INTO mysql.global_priv (`Host`, `User`, `Priv`)
			SELECT vRoleHost, prefixedRole,
					JSON_OBJECT(
						'is_role', TRUE,
						'access', 0
					)
				FROM tRole r
			UNION
			SELECT vRoleHost, CONCAT(vGenRolePrefix, 'root'),
				JSON_OBJECT(
					'is_role', TRUE,
					'access', 0
				);

		INSERT INTO mysql.roles_mapping (`Host`, `User`, `Role`, `Admin_option`)
			SELECT vRoleHost, prefixedRole, inheritsFrom, 'N'
				FROM tRoleInherit
			UNION
			SELECT vRoleHost, CONCAT(vGenRolePrefix, 'root'), prefixedRole, 'Y'
				FROM tRole;

		INSERT INTO mysql.roles_mapping (`Host`, `User`, `Role`, `Admin_option`)
			SELECT 'localhost', 'root', prefixedRole, 'Y'
				FROM tRole
			UNION
			SELECT 'localhost', 'root', CONCAT(vGenRolePrefix, 'root'), 'Y';

		-- Role users

		INSERT INTO mysql.global_priv (
				`User`,
				`Host`,
				`Priv`
			)
			SELECT
				r.prefixedRole,
				vGenRoleHost,
				JSON_MERGE_PATCH(
					IFNULL(t.`Priv`, '{}'),
					IFNULL(u.`Priv`, '{}'),
					JSON_OBJECT(
						'mysql_old_password', JSON_VALUE(t.`Priv`, '$.mysql_old_password'),
						'mysql_native_password', JSON_VALUE(t.`Priv`, '$.mysql_native_password'),
						'authentication_string', JSON_VALUE(t.`Priv`, '$.authentication_string'),
						'ssl_type', JSON_VALUE(t.`Priv`, '$.ssl_type'),
						'default_role', r.prefixedRole,
						'access', 0,
						'is_role', FALSE
					)
				)
				FROM tRole r
					LEFT JOIN mysql.global_priv t
						ON (t.`User`, t.Host) = (vTplUser, vRoleHost)
					LEFT JOIN mysql.global_priv u
						ON (u.`User`, u.Host) = (r.`role`, vRoleHost);

		INSERT INTO mysql.roles_mapping (`Host`, `User`, `Role`, `Admin_option`)
			SELECT vGenRoleHost, prefixedRole, prefixedRole, 'N'
				FROM tRole;

		-- Accounts

		DROP TEMPORARY TABLE IF EXISTS tAccounts;
		CREATE TEMPORARY TABLE tAccounts
			(INDEX (`name`))
			ENGINE = MEMORY
			SELECT p.`User` `name`, r.prefixedRole
				FROM mysql.global_priv p
					JOIN `user` u ON u.`name` = p.`User`
					JOIN tRole r ON r.`id` = u.`role`
				WHERE JSON_EXTRACT(p.`Priv`, '$.autogenerated') = 'true';

		UPDATE mysql.global_priv p
				JOIN tAccounts c ON c.`name` = p.`User`
			SET p.`Priv` = JSON_SET(p.`Priv`, '$.default_role', c.prefixedRole)
				WHERE p.`Host` = vUserHost;

		UPDATE IGNORE mysql.roles_mapping m
				JOIN tAccounts c ON c.`name` = m.`User`
			SET m.`Role` = c.prefixedRole
				WHERE m.`Host` = vUserHost;

		DROP TEMPORARY TABLE tAccounts;
	ELSE
		DROP TEMPORARY TABLE IF EXISTS tUser;
		CREATE TEMPORARY TABLE tUser
			SELECT
				r.prefixedRole `User`,
				vGenRoleHost `Host`,
				IFNULL(t.`authentication_string`,
					'') `authentication_string`,
				IFNULL(t.`plugin`,
					'mysql_native_password') `plugin`,
				IFNULL(IF('' != u.`ssl_type`,
					u.`ssl_type`, t.`ssl_type`),
					'') `ssl_type`,
				IFNULL(IF('' != u.`ssl_cipher`,
					u.`ssl_cipher`, t.`ssl_cipher`),
					'') `ssl_cipher`,
				IFNULL(IF('' != u.`x509_issuer`,
					u.`x509_issuer`, t.`x509_issuer`),
					'') `x509_issuer`,
				IFNULL(IF('' != u.`x509_subject`,
					u.`x509_subject`, t.`x509_subject`),
					'') `x509_subject`,
				IFNULL(IF(0 != u.`max_questions`,
					u.`max_questions`, t.`max_questions`),
					0) `max_questions`,
				IFNULL(IF(0 != u.`max_updates`,
					u.`max_updates`, t.`max_updates`),
					0) `max_updates`,
				IFNULL(IF(0 != u.`max_connections`,
					u.`max_connections`, t.`max_connections`),
					0) `max_connections`,
				IFNULL(IF(0 != u.`max_user_connections`,
					u.`max_user_connections`, t.`max_user_connections`),
					0) `max_user_connections`
				FROM tRole r
					LEFT JOIN mysql.user t
						ON t.`User` = vTplUser
							AND t.`Host` = vRoleHost
					LEFT JOIN mysql.user u
						ON u.`User` = r.role
							AND u.`Host` = vRoleHost;

		IF vVersion <= 5 THEN
			SELECT `Password` INTO vPassword
				FROM mysql.user
				WHERE `User` = vTplUser
					AND `Host` = vRoleHost;

			INSERT INTO mysql.user (
					`User`,
					`Host`,
					`Password`,
					`authentication_string`,
					`plugin`,
					`ssl_type`,
					`ssl_cipher`,
					`x509_issuer`,
					`x509_subject`,
					`max_questions`,
					`max_updates`,
					`max_connections`,
					`max_user_connections`
				)
				SELECT
					`User`,
					`Host`,
					vPassword,
					`authentication_string`,
					`plugin`,
					`ssl_type`,
					`ssl_cipher`,
					`x509_issuer`,
					`x509_subject`,
					`max_questions`,
					`max_updates`,
					`max_connections`,
					`max_user_connections`
					FROM tUser;
		ELSE
			INSERT INTO mysql.user (
					`User`,
					`Host`,
					`authentication_string`,
					`plugin`,
					`ssl_type`,
					`ssl_cipher`,
					`x509_issuer`,
					`x509_subject`,
					`max_questions`,
					`max_updates`,
					`max_connections`,
					`max_user_connections`
				)
				SELECT
					`User`,
					`Host`,
					`authentication_string`,
					`plugin`,
					`ssl_type`,
					`ssl_cipher`,
					`x509_issuer`,
					`x509_subject`,
					`max_questions`,
					`max_updates`,
					`max_connections`,
					`max_user_connections`
					FROM tUser;
		END IF;

		DROP TEMPORARY TABLE tUser;

		-- Proxy privileges

		INSERT INTO mysql.proxies_priv (
			`User`,
			`Host`,
			`Proxied_user`,
			`Proxied_host`,
			`Grantor`
		)
		SELECT
			'',
			vUserHost,
			prefixedRole,
			vGenRoleHost,
			CONCAT(prefixedRole, '@', vGenRoleHost)
			FROM tRole;

		-- Global privileges

		DROP TEMPORARY TABLE IF EXISTS tUserPriv;
		CREATE TEMPORARY TABLE tUserPriv
			(INDEX (prefixedRole))
			ENGINE = MEMORY
			SELECT
				r.prefixedRole,
				MAX(u.`Select_priv`) `Select_priv`,
				MAX(u.`Insert_priv`) `Insert_priv`,
				MAX(u.`Update_priv`) `Update_priv`,
				MAX(u.`Delete_priv`) `Delete_priv`,
				MAX(u.`Create_priv`) `Create_priv`,
				MAX(u.`Drop_priv`) `Drop_priv`,
				MAX(u.`Reload_priv`) `Reload_priv`,
				MAX(u.`Shutdown_priv`) `Shutdown_priv`,
				MAX(u.`Process_priv`) `Process_priv`,
				MAX(u.`File_priv`) `File_priv`,
				MAX(u.`Grant_priv`) `Grant_priv`,
				MAX(u.`References_priv`) `References_priv`,
				MAX(u.`Index_priv`) `Index_priv`,
				MAX(u.`Alter_priv`) `Alter_priv`,
				MAX(u.`Show_db_priv`) `Show_db_priv`,
				MAX(u.`Super_priv`) `Super_priv`,
				MAX(u.`Create_tmp_table_priv`) `Create_tmp_table_priv`,
				MAX(u.`Lock_tables_priv`) `Lock_tables_priv`,
				MAX(u.`Execute_priv`) `Execute_priv`,
				MAX(u.`Repl_slave_priv`) `Repl_slave_priv`,
				MAX(u.`Repl_client_priv`) `Repl_client_priv`,
				MAX(u.`Create_view_priv`) `Create_view_priv`,
				MAX(u.`Show_view_priv`) `Show_view_priv`,
				MAX(u.`Create_routine_priv`) `Create_routine_priv`,
				MAX(u.`Alter_routine_priv`) `Alter_routine_priv`,
				MAX(u.`Create_user_priv`) `Create_user_priv`,
				MAX(u.`Event_priv`) `Event_priv`,
				MAX(u.`Trigger_priv`) `Trigger_priv`,
				MAX(u.`Create_tablespace_priv`) `Create_tablespace_priv`
				FROM tRoleInherit r
					JOIN mysql.user u
						ON u.`User` = r.inheritsFrom
							AND u.`Host`= vRoleHost
				GROUP BY r.prefixedRole;

		UPDATE mysql.user u
			JOIN tUserPriv t
				ON u.`User` = t.prefixedRole
					AND u.`Host` = vGenRoleHost
			SET
				u.`Select_priv`
					= t.`Select_priv`,
				u.`Insert_priv`
					= t.`Insert_priv`,
				u.`Update_priv`
					= t.`Update_priv`,
				u.`Delete_priv`
					= t.`Delete_priv`,
				u.`Create_priv`
					= t.`Create_priv`,
				u.`Drop_priv`
					= t.`Drop_priv`,
				u.`Reload_priv`
					= t.`Reload_priv`,
				u.`Shutdown_priv`
					= t.`Shutdown_priv`,
				u.`Process_priv`
					= t.`Process_priv`,
				u.`File_priv`
					= t.`File_priv`,
				u.`Grant_priv`
					= t.`Grant_priv`,
				u.`References_priv`
					= t.`References_priv`,
				u.`Index_priv`
					= t.`Index_priv`,
				u.`Alter_priv`
					= t.`Alter_priv`,
				u.`Show_db_priv`
					= t.`Show_db_priv`,
				u.`Super_priv`
					= t.`Super_priv`,
				u.`Create_tmp_table_priv`
					= t.`Create_tmp_table_priv`,
				u.`Lock_tables_priv`
					= t.`Lock_tables_priv`,
				u.`Execute_priv`
					= t.`Execute_priv`,
				u.`Repl_slave_priv`
					= t.`Repl_slave_priv`,
				u.`Repl_client_priv`
					= t.`Repl_client_priv`,
				u.`Create_view_priv`
					= t.`Create_view_priv`,
				u.`Show_view_priv`
					= t.`Show_view_priv`,
				u.`Create_routine_priv`
					= t.`Create_routine_priv`,
				u.`Alter_routine_priv`
					= t.`Alter_routine_priv`,
				u.`Create_user_priv`
					= t.`Create_user_priv`,
				u.`Event_priv`
					= t.`Event_priv`,
				u.`Trigger_priv`
					= t.`Trigger_priv`,
				u.`Create_tablespace_priv`
					= t.`Create_tablespace_priv`;

		DROP TEMPORARY TABLE tUserPriv;

		-- Schema level privileges

		INSERT INTO mysql.db (
				`User`,
				`Host`,
				`Db`,
				`Select_priv`,
				`Insert_priv`,
				`Update_priv`,
				`Delete_priv`,
				`Create_priv`,
				`Drop_priv`,
				`Grant_priv`,
				`References_priv`,
				`Index_priv`,
				`Alter_priv`,
				`Create_tmp_table_priv`,
				`Lock_tables_priv`,
				`Create_view_priv`,
				`Show_view_priv`,
				`Create_routine_priv`,
				`Alter_routine_priv`,
				`Execute_priv`,
				`Event_priv`,
				`Trigger_priv`
			)
			SELECT
				r.prefixedRole,
				vGenRoleHost,
				t.`Db`,
				MAX(t.`Select_priv`),
				MAX(t.`Insert_priv`),
				MAX(t.`Update_priv`),
				MAX(t.`Delete_priv`),
				MAX(t.`Create_priv`),
				MAX(t.`Drop_priv`),
				MAX(t.`Grant_priv`),
				MAX(t.`References_priv`),
				MAX(t.`Index_priv`),
				MAX(t.`Alter_priv`),
				MAX(t.`Create_tmp_table_priv`),
				MAX(t.`Lock_tables_priv`),
				MAX(t.`Create_view_priv`),
				MAX(t.`Show_view_priv`),
				MAX(t.`Create_routine_priv`),
				MAX(t.`Alter_routine_priv`),
				MAX(t.`Execute_priv`),
				MAX(t.`Event_priv`),
				MAX(t.`Trigger_priv`)
				FROM tRoleInherit r
					JOIN mysql.db t
						ON t.`User` = r.inheritsFrom
							AND t.`Host`= vRoleHost
				GROUP BY r.prefixedRole, t.`Db`;

		-- Table level privileges

		INSERT INTO mysql.tables_priv (
				`User`,
				`Host`,
				`Db`,
				`Table_name`,
				`Grantor`,
				`Timestamp`,
				`Table_priv`,
				`Column_priv`
			)
			SELECT
				r.prefixedRole,
				vGenRoleHost,
				t.`Db`,
				t.`Table_name`,
				t.`Grantor`,
				MAX(t.`Timestamp`),
				IFNULL(GROUP_CONCAT(NULLIF(t.`Table_priv`, '')), ''),
				IFNULL(GROUP_CONCAT(NULLIF(t.`Column_priv`, '')), '')
				FROM tRoleInherit r
					JOIN mysql.tables_priv t
						ON t.`User` = r.inheritsFrom
							AND t.`Host`= vRoleHost
				GROUP BY r.prefixedRole, t.`Db`, t.`Table_name`;

		-- Column level privileges

		INSERT INTO mysql.columns_priv (
				`User`,
				`Host`,
				`Db`,
				`Table_name`,
				`Column_name`,
				`Timestamp`,
				`Column_priv`
			)
			SELECT
				r.prefixedRole,
				vGenRoleHost,
				t.`Db`,
				t.`Table_name`,
				t.`Column_name`,
				MAX(t.`Timestamp`),
				IFNULL(GROUP_CONCAT(NULLIF(t.`Column_priv`, '')), '')
				FROM tRoleInherit r
					JOIN mysql.columns_priv t
						ON t.`User` = r.inheritsFrom
							AND t.`Host`= vRoleHost
				GROUP BY r.prefixedRole, t.`Db`, t.`Table_name`, t.`Column_name`;

		-- Routine privileges

		INSERT IGNORE INTO mysql.procs_priv (
				`User`,
				`Host`,
				`Db`,
				`Routine_name`,
				`Routine_type`,
				`Grantor`,
				`Timestamp`,
				`Proc_priv`
			)
			SELECT
				r.prefixedRole,
				vGenRoleHost,
				t.`Db`,
				t.`Routine_name`,
				t.`Routine_type`,
				t.`Grantor`,
				t.`Timestamp`,
				t.`Proc_priv`
				FROM tRoleInherit r
					JOIN mysql.procs_priv t
						ON t.`User` = r.inheritsFrom
							AND t.`Host`= vRoleHost;
	END IF;

	-- Cleanup

	DROP TEMPORARY TABLE
		tRole,
		tRoleInherit;

	FLUSH PRIVILEGES;
END$$
DELIMITER ;
