/*
 * Copyright (c) 2025, Oracle and/or its affiliates.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License, version 2.0,
 * as published by the Free Software Foundation.
 *
 * This program is designed to work with certain software (including
 * but not limited to OpenSSL) that is licensed under separate terms, as
 * designated in a particular file or component or in included license
 * documentation.  The authors of MySQL hereby grant you an additional
 * permission to link the program and your derivative works with the
 * separately licensed software that they have either included with
 * the program or referenced in the documentation.
 *
 * This program is distributed in the hope that it will be useful,  but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 * the GNU General Public License, version 2.0, for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

-- #############################################################################
-- MSM Section 003: Database Schema Deployment Script
-- -----------------------------------------------------------------------------
-- This script either creates or updates the database schema
-- `mysql_rest_service_metadata` to version 4.1.3
-- #############################################################################


-- #############################################################################
-- MSM Section 010: Server Variable Settings
-- -----------------------------------------------------------------------------
-- Set server variables, remember their state to be able to restore accordingly.
-- #############################################################################

SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,'
    'NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,'
    'NO_ENGINE_SUBSTITUTION';


-- #############################################################################
-- MSM Section 110: Database Schema Creation
-- -----------------------------------------------------------------------------
-- CREATE SCHEMA statement
-- #############################################################################

CREATE SCHEMA IF NOT EXISTS `mysql_rest_service_metadata`
    DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;

USE `mysql_rest_service_metadata`;


-- #############################################################################
-- MSM Section 340: Non-Idempotent Deployment Procedures
-- -----------------------------------------------------------------------------
-- Create the stored procedures used for schema TABLE installation and updates.
-- These procedures will only be used during the installation and update
-- process and will be dropped afterwards.
-- #############################################################################

USE `mysql_rest_service_metadata`;

DELIMITER %%

-- -----------------------------------------------------------------------------
-- PROCEDURE `mysql_rest_service_metadata`.`msm_update_4.0.0_to_4.0.1`
-- -----------------------------------------------------------------------------
-- Stored procedure to update TABLEs and VIEWs
-- from version 4.0.0 to 4.0.1
-- -----------------------------------------------------------------------------
DROP PROCEDURE IF EXISTS `msm_update_4.0.0_to_4.0.1`%%
CREATE PROCEDURE `msm_update_4.0.0_to_4.0.1`()
SQL SECURITY INVOKER
BEGIN
    -- -----------------------------------------------------------------------------
    -- ALTER TABLE `mysql_rest_service_metadata`.`my_table`
    -- -----------------------------------------------------------------------------

    ALTER TABLE `mysql_rest_service_metadata`.`router`
        CHANGE COLUMN `router_name` `router_name` VARCHAR(255) NOT NULL
        COMMENT 'A user specified name for an instance of the router. Should default to address:port, where port is the http server port of the router. Set via --name during router bootstrap.';
END%%

-- -----------------------------------------------------------------------------
-- PROCEDURE `mysql_rest_service_metadata`.`msm_update_4.0.1_to_4.0.2`
-- -----------------------------------------------------------------------------
-- Stored procedure to update TABLEs and VIEWs
-- from version 4.0.1 to 4.0.2
-- -----------------------------------------------------------------------------
DROP PROCEDURE IF EXISTS `msm_update_4.0.1_to_4.0.2`%%
CREATE PROCEDURE `msm_update_4.0.1_to_4.0.2`()
SQL SECURITY INVOKER
BEGIN
    ALTER TABLE `mysql_rest_service_metadata`.`router_general_log`
        CHANGE COLUMN `log_type` `log_type` ENUM("INFO", "WARNING", "DEBUG", "ERROR", "FATAL", "SYSTEM", "NOTE") NOT NULL,
        CHANGE COLUMN `code` `code` INT UNSIGNED NULL,
        CHANGE COLUMN `message` `message` VARCHAR(4096) NULL,
        ADD COLUMN `domain` VARCHAR(255) NULL,
        ADD COLUMN `thread_id` INT UNSIGNED NULL,
        ADD INDEX `router_log_type` (`log_type` ASC) VISIBLE,
        ADD INDEX `router_log_thread_id` (`thread_id` ASC) VISIBLE;
END%%

-- -----------------------------------------------------------------------------
-- PROCEDURE `mysql_rest_service_metadata`.`msm_update_4.0.2_to_4.0.3`
-- -----------------------------------------------------------------------------
-- Stored procedure to update TABLEs and VIEWs
-- from version 4.0.2 to 4.0.3
-- -----------------------------------------------------------------------------
DROP PROCEDURE IF EXISTS `msm_update_4.0.2_to_4.0.3`%%
CREATE PROCEDURE `msm_update_4.0.2_to_4.0.3`()
SQL SECURITY INVOKER
BEGIN
END%%

-- -----------------------------------------------------------------------------
-- PROCEDURE `mysql_rest_service_metadata`.`msm_update_4.0.3_to_4.0.4`
-- -----------------------------------------------------------------------------
-- Stored procedure to update TABLEs and VIEWs
-- from version 4.0.3 to 4.0.4
-- -----------------------------------------------------------------------------
DROP PROCEDURE IF EXISTS `msm_update_4.0.3_to_4.0.4`%%
CREATE PROCEDURE `msm_update_4.0.3_to_4.0.4`()
SQL SECURITY INVOKER
BEGIN
    -- -----------------------------------------------------------------------------
    -- ALTER TABLE `mysql_rest_service_metadata`.`audit_log`
    -- -----------------------------------------------------------------------------

    ALTER TABLE `mysql_rest_service_metadata`.`audit_log`
        CHANGE COLUMN `changed_at` `changed_at` TIMESTAMP(6) NOT NULL;

    -- -----------------------------------------------------------------------------
    -- ALTER TABLE `mysql_rest_service_metadata`.`audit_log`
    -- -----------------------------------------------------------------------------

    ALTER TABLE `mysql_rest_service_metadata`.`router_general_log`
        CHANGE COLUMN `log_time` `log_time` TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6);
END%%

-- -----------------------------------------------------------------------------
-- PROCEDURE `mysql_rest_service_metadata`.`msm_update_4.0.4_to_4.1.0`
-- -----------------------------------------------------------------------------
-- Stored procedure to update TABLEs and VIEWs
-- from version 4.0.4 to 4.1.0
-- -----------------------------------------------------------------------------
DROP PROCEDURE IF EXISTS `msm_update_4.0.4_to_4.1.0`%%
CREATE PROCEDURE `msm_update_4.0.4_to_4.1.0`()
SQL SECURITY INVOKER
BEGIN
END%%

-- -----------------------------------------------------------------------------
-- PROCEDURE `mysql_rest_service_metadata`.`msm_update_4.1.0_to_4.1.1`
-- -----------------------------------------------------------------------------
-- Stored procedure to update TABLEs and VIEWs
-- from version 4.1.0 to 4.1.1
-- -----------------------------------------------------------------------------
DROP PROCEDURE IF EXISTS `msm_update_4.1.0_to_4.1.1`%%
CREATE PROCEDURE `msm_update_4.1.0_to_4.1.1`()
SQL SECURITY INVOKER
BEGIN
END%%

-- -----------------------------------------------------------------------------
-- PROCEDURE `mysql_rest_service_metadata`.`msm_update_4.1.1_to_4.1.2`
-- -----------------------------------------------------------------------------
-- Stored procedure to update TABLEs and VIEWs
-- from version 4.1.1 to 4.1.2
-- -----------------------------------------------------------------------------
DROP PROCEDURE IF EXISTS `msm_update_4.1.1_to_4.1.2`%%
CREATE PROCEDURE `msm_update_4.1.1_to_4.1.2`()
SQL SECURITY INVOKER
BEGIN
END%%

-- -----------------------------------------------------------------------------
-- PROCEDURE `mysql_rest_service_metadata`.`msm_update_4.1.2_to_4.1.3`
-- -----------------------------------------------------------------------------
-- Stored procedure to update TABLEs and VIEWs
-- from version 4.1.2 to 4.1.3
-- -----------------------------------------------------------------------------
DROP PROCEDURE IF EXISTS `msm_update_4.1.2_to_4.1.3`%%
CREATE PROCEDURE `msm_update_4.1.2_to_4.1.3`()
SQL SECURITY INVOKER
BEGIN
END%%

-- -----------------------------------------------------------------------------
-- PROCEDURE `mysql_rest_service_metadata`.`msm_create_4.1.3`
-- -----------------------------------------------------------------------------
-- Stored procedure to install schema TABLEs for version 4.1.3
-- -----------------------------------------------------------------------------
DROP PROCEDURE IF EXISTS `msm_create_4.1.3`%%
CREATE PROCEDURE `msm_create_4.1.3`()
SQL SECURITY INVOKER
BEGIN
    -- -----------------------------------------------------
    -- Table `url_host`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `url_host` (
      `id` BINARY(16) NOT NULL,
      `name` VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'Specifies the host name of the MRS as represented in the request URLs. Example: example.com',
      `comments` VARCHAR(512) NULL,
      PRIMARY KEY (`id`),
      UNIQUE INDEX `name_UNIQUE` (`name` ASC) VISIBLE)
    ENGINE = InnoDB;


    -- -----------------------------------------------------
    -- Table `service`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `service` (
      `id` BINARY(16) NOT NULL,
      `parent_id` BINARY(16) NULL,
      `url_host_id` BINARY(16) NOT NULL,
      `url_context_root` VARCHAR(255) NOT NULL DEFAULT '/mrs' COMMENT 'Specifies context root of the MRS as represented in the request URLs, default being /mrs. URL Example: https://www.example.com/mrs',
      `url_protocol` SET('HTTP', 'HTTPS') NOT NULL DEFAULT 'HTTPS',
      `name` VARCHAR(255) NOT NULL,
      `enabled` TINYINT NOT NULL DEFAULT 1,
      `published` TINYINT NOT NULL DEFAULT 0,
      `in_development` JSON NULL COMMENT 'If not NULL, this column indicates that the REST service is currently \"in development\" and holds the name(s) of the developer(s) who is(/are) allowed to work with the service in the \"$.developers\" string array. REST services with this column not being NULL may use the same url_host+url_context_root context path as existing services. Routers only serve REST services with this column being NULL, unless they are bootstrapped with --mrs-development <user> which sets `router`.`option`->>\"$.developer\". When bootstrapped with the --mrs-development <user> option the Router also serves REST services marked \"in development\" with this column\'s \"$.developers\" including the same name as the <user> specified during bootstrap, while these REST services marked \"in development\" take priority over services with the same url_host+url_context_root context path and this column being NULL.',
      `comments` VARCHAR(512) NULL,
      `options` JSON NULL,
      `auth_path` VARCHAR(255) NOT NULL DEFAULT '/authentication' COMMENT 'The path used for authentication. The following sub-paths will be made available for <service_path>/<auth_path>:  /login /status /logout /completed',
      `auth_completed_url` VARCHAR(255) NULL COMMENT 'The authentication workflow will redirect to this URL after successful- or failed login. If this field is not set, the workflow will redirect to <service_path>/<auth_path>/completed if the <service_path>/<auth_path>/login?onCompletionRedirect parameter has not been set.',
      `auth_completed_url_validation` VARCHAR(512) NULL COMMENT 'A regular expression to validate the <service_path>/<auth_path>/login?onCompletionRedirect parameter. If set, this allows to limit the possible URLs an application can specify for this parameter.',
      `auth_completed_page_content` TEXT NULL COMMENT 'If this field is set its content will replace the page content of the /completed page.',
      `enable_sql_endpoint` TINYINT NOT NULL DEFAULT 0,
      `custom_metadata_schema` VARCHAR(255) NULL,
      `metadata` JSON NULL,
      PRIMARY KEY (`id`),
      INDEX `fk_service_url_host1_idx` (`url_host_id` ASC) VISIBLE,
      INDEX `fk_service_service1_idx` (`parent_id` ASC) VISIBLE,
      CONSTRAINT `fk_service_url_host1`
        FOREIGN KEY (`url_host_id`)
        REFERENCES `url_host` (`id`)
        ON DELETE RESTRICT
        ON UPDATE NO ACTION,
      CONSTRAINT `fk_service_service1`
        FOREIGN KEY (`parent_id`)
        REFERENCES `service` (`id`)
        ON DELETE RESTRICT
        ON UPDATE NO ACTION)
    ENGINE = InnoDB;


    -- -----------------------------------------------------
    -- Table `db_schema`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `db_schema` (
      `id` BINARY(16) NOT NULL,
      `service_id` BINARY(16) NOT NULL,
      `name` VARCHAR(255) NOT NULL,
      `schema_type` ENUM('DATABASE_SCHEMA', 'SCRIPT_MODULE') NOT NULL DEFAULT 'DATABASE_SCHEMA',
      `request_path` VARCHAR(255) NOT NULL,
      `requires_auth` TINYINT NOT NULL DEFAULT 0,
      `enabled` TINYINT NOT NULL DEFAULT 1,
      `internal` TINYINT NOT NULL DEFAULT 0,
      `items_per_page` INT UNSIGNED NULL DEFAULT 25,
      `comments` VARCHAR(512) NULL,
      `options` JSON NULL,
      `metadata` JSON NULL,
      PRIMARY KEY (`id`),
      INDEX `fk_db_schema_service1_idx` (`service_id` ASC) VISIBLE,
      CONSTRAINT `fk_db_schema_service1`
        FOREIGN KEY (`service_id`)
        REFERENCES `service` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION)
    ENGINE = InnoDB;


    -- -----------------------------------------------------
    -- Table `db_object`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `db_object` (
      `id` BINARY(16) NOT NULL,
      `db_schema_id` BINARY(16) NOT NULL,
      `name` VARCHAR(255) NOT NULL,
      `request_path` VARCHAR(255) NOT NULL,
      `enabled` TINYINT NOT NULL DEFAULT 1,
      `internal` TINYINT NOT NULL DEFAULT 0,
      `object_type` ENUM('TABLE', 'VIEW', 'PROCEDURE', 'FUNCTION', 'SCRIPT') NOT NULL,
      `crud_operations` SET('CREATE', 'READ', 'UPDATE', 'DELETE') NOT NULL DEFAULT '' COMMENT 'Calculated by the duality view options of object and object_reference table, always UPDATE for procedures and functions.',
      `format` ENUM('FEED', 'ITEM', 'MEDIA') NOT NULL DEFAULT 'FEED' COMMENT 'The HTTP request method for this handler. \'feed\' executes the source query and returns the result set in JSON representation, \'item\' returns a single row instead, \'media\' turns the result set into a binary representation with accompanying HTTP Content-Type header.',
      `items_per_page` INT UNSIGNED NULL,
      `media_type` VARCHAR(45) NULL,
      `auto_detect_media_type` TINYINT NOT NULL DEFAULT 0,
      `requires_auth` TINYINT NOT NULL DEFAULT 0,
      `auth_stored_procedure` VARCHAR(255) NULL DEFAULT 0 COMMENT 'Specifies the STORE PROCEDURE that should be called to identify if the given user is allowed to perform the given CRUD operation. The SP has to be in the same schema as the schema object and it has to accept the following parameters: (user_id, schema, object, crud_operation).  It returns true or false.',
      `options` JSON NULL COMMENT 'Holds additional options for the db_object, e.g. {\"id_generation\": \"auto_increment\"}. \"id_generation\" can be undefined or \"auto_increment\" for tables using AUTO_INCREMENT or \"reverse_uuid\" for tables using DECIMAL(16) for the primary key.',
      `details` JSON NULL,
      `comments` VARCHAR(512) NULL,
      `metadata` JSON NULL,
      PRIMARY KEY (`id`),
      INDEX `fk_db_objects_db_schema1_idx` (`db_schema_id` ASC) INVISIBLE,
      CONSTRAINT `fk_db_objects_db_schema1`
        FOREIGN KEY (`db_schema_id`)
        REFERENCES `db_schema` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION)
    ENGINE = InnoDB;


    -- -----------------------------------------------------
    -- Table `auth_vendor`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `auth_vendor` (
      `id` BINARY(16) NOT NULL,
      `name` VARCHAR(65) NOT NULL,
      `validation_url` VARCHAR(255) NULL COMMENT 'URL used to validate the access_token provided by the client. Example: https://graph.facebook.com/debug_token?input_token=%access_token%&access_token=%app_access_token%',
      `enabled` TINYINT NOT NULL DEFAULT 1,
      `comments` VARCHAR(512) NULL,
      `options` JSON NULL,
      PRIMARY KEY (`id`))
    ENGINE = InnoDB;


    -- -----------------------------------------------------
    -- Table `auth_app`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `auth_app` (
      `id` BINARY(16) NOT NULL,
      `auth_vendor_id` BINARY(16) NOT NULL,
      `name` VARCHAR(45) NOT NULL,
      `description` VARCHAR(512) NULL,
      `url` VARCHAR(255) NULL,
      `url_direct_auth` VARCHAR(255) NULL,
      `access_token` VARCHAR(1024) NULL COMMENT 'The app access token to validate the user login.',
      `app_id` VARCHAR(1024) NULL,
      `enabled` TINYINT NULL,
      `limit_to_registered_users` TINYINT NOT NULL DEFAULT 1 COMMENT 'Limit the users that can log in to the list of users in the auth_user table. The auth_user table can be pre-filled with users by specifying the name and email only. The vendor_user_id will be added on the first login automatically.',
      `default_role_id` BINARY(16) NULL COMMENT 'If set, a new user that has not any auth_roles assigned will get this role assigned when he logs in the first time.',
      `options` JSON NULL,
      PRIMARY KEY (`id`),
      INDEX `fk_auth_app_auth_vendor1_idx` (`auth_vendor_id` ASC) VISIBLE,
      UNIQUE INDEX `name_UNIQUE` (`name` ASC) VISIBLE,
      CONSTRAINT `fk_auth_app_auth_vendor1`
        FOREIGN KEY (`auth_vendor_id`)
        REFERENCES `auth_vendor` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION)
    ENGINE = InnoDB;


    -- -----------------------------------------------------
    -- Table `mrs_user`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `mrs_user` (
      `id` BINARY(16) NOT NULL,
      `auth_app_id` BINARY(16) NOT NULL,
      `name` VARCHAR(225) NULL,
      `email` VARCHAR(255) NULL,
      `vendor_user_id` VARCHAR(255) NULL,
      `login_permitted` TINYINT NOT NULL DEFAULT 0,
      `mapped_user_id` VARCHAR(255) NULL,
      `app_options` JSON NULL,
      `auth_string` TEXT NULL,
      `options` JSON NULL,
      PRIMARY KEY (`id`),
      INDEX `fk_auth_user_auth_app1_idx` (`auth_app_id` ASC) VISIBLE,
      INDEX `mrs_use_vendor_user_id` (`vendor_user_id` ASC) VISIBLE,
      INDEX `mrs_user_name` (`name` ASC) VISIBLE,
      INDEX `mrs_user_email` (`email` ASC) VISIBLE,
      CONSTRAINT `fk_auth_user_auth_app1`
        FOREIGN KEY (`auth_app_id`)
        REFERENCES `auth_app` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION)
    ENGINE = InnoDB;


    -- -----------------------------------------------------
    -- Table `config`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `config` (
      `id` TINYINT NOT NULL DEFAULT 1,
      `service_enabled` TINYINT NULL,
      `data` JSON NULL,
      PRIMARY KEY (`id`))
    ENGINE = InnoDB;


    -- -----------------------------------------------------
    -- Table `redirect`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `redirect` (
      `id` BINARY(16) NOT NULL,
      `pattern` VARCHAR(1024) NOT NULL,
      `target` VARCHAR(1024) NOT NULL,
      `kind` ENUM('REDIRECT', 'REWRITE') NOT NULL DEFAULT 'REDIRECT',
      `in_development` JSON NULL,
      PRIMARY KEY (`id`))
    ENGINE = InnoDB;


    -- -----------------------------------------------------
    -- Table `url_host_alias`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `url_host_alias` (
      `id` BINARY(16) NOT NULL,
      `url_host_id` BINARY(16) NOT NULL,
      `alias` VARCHAR(255) NOT NULL COMMENT 'Specifies additional aliases for the given host, e.g. www.example.com',
      PRIMARY KEY (`id`),
      INDEX `fk_url_host_alias_url_host1_idx` (`url_host_id` ASC) VISIBLE,
      CONSTRAINT `fk_url_host_alias_url_host1`
        FOREIGN KEY (`url_host_id`)
        REFERENCES `url_host` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION)
    ENGINE = InnoDB;


    -- -----------------------------------------------------
    -- Table `content_set`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `content_set` (
      `id` BINARY(16) NOT NULL,
      `service_id` BINARY(16) NOT NULL,
      `content_type` ENUM('STATIC', 'SCRIPTS') NOT NULL DEFAULT 'STATIC',
      `request_path` VARCHAR(255) NOT NULL,
      `requires_auth` TINYINT NOT NULL DEFAULT 0,
      `enabled` TINYINT NOT NULL DEFAULT 0,
      `internal` TINYINT NOT NULL DEFAULT 0,
      `comments` VARCHAR(512) NULL,
      `options` JSON NULL,
      PRIMARY KEY (`id`),
      INDEX `fk_static_content_version_service1_idx` (`service_id` ASC) VISIBLE,
      CONSTRAINT `fk_static_content_version_service1`
        FOREIGN KEY (`service_id`)
        REFERENCES `service` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION)
    ENGINE = InnoDB;


    -- -----------------------------------------------------
    -- Table `content_file`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `content_file` (
      `id` BINARY(16) NOT NULL,
      `content_set_id` BINARY(16) NOT NULL,
      `request_path` VARCHAR(255) NOT NULL DEFAULT '/',
      `requires_auth` TINYINT NOT NULL DEFAULT 0,
      `enabled` TINYINT NOT NULL DEFAULT 1,
      `content` LONGBLOB NOT NULL,
      `size` BIGINT GENERATED ALWAYS AS (LENGTH(content)) STORED,
      `options` JSON NULL,
      PRIMARY KEY (`id`),
      INDEX `fk_content_content_set1_idx` (`content_set_id` ASC) VISIBLE,
      CONSTRAINT `fk_content_content_set1`
        FOREIGN KEY (`content_set_id`)
        REFERENCES `content_set` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION)
    ENGINE = InnoDB;


    -- -----------------------------------------------------
    -- Table `audit_log`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `audit_log` (
      `id` INT NOT NULL AUTO_INCREMENT,
      `schema_name` VARCHAR(255) NULL,
      `table_name` VARCHAR(255) NOT NULL,
      `dml_type` ENUM('INSERT', 'UPDATE', 'DELETE') NOT NULL,
      `old_row_data` JSON NULL,
      `new_row_data` JSON NULL,
      `changed_by` VARCHAR(255) NOT NULL,
      `changed_at` TIMESTAMP(6) NOT NULL,
      `old_row_id` BINARY(16) NULL,
      `new_row_id` BINARY(16) NULL,
      PRIMARY KEY (`id`),
      INDEX `idx_table_name` (`table_name` ASC) VISIBLE,
      INDEX `idx_changed_at` (`changed_at` ASC) VISIBLE,
      INDEX `idx_changed_by` (`changed_by` ASC) VISIBLE,
      INDEX `idx_new_row_id` (`new_row_id` ASC) VISIBLE,
      INDEX `idx_old_row_id` (`old_row_id` ASC) VISIBLE)
    ENGINE = InnoDB;


    -- -----------------------------------------------------
    -- Table `mrs_role`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `mrs_role` (
      `id` BINARY(16) NOT NULL,
      `derived_from_role_id` BINARY(16) NULL,
      `specific_to_service_id` BINARY(16) NULL,
      `caption` VARCHAR(150) NOT NULL,
      `description` VARCHAR(512) NULL,
      `options` JSON NULL,
      PRIMARY KEY (`id`),
      INDEX `fk_priv_role_priv_role1_idx` (`derived_from_role_id` ASC) VISIBLE,
      INDEX `fk_auth_role_service1_idx` (`specific_to_service_id` ASC) VISIBLE,
      UNIQUE INDEX `unique_caption_per_service` (`specific_to_service_id` ASC, `caption` ASC) VISIBLE,
      CONSTRAINT `fk_priv_role_priv_role1`
        FOREIGN KEY (`derived_from_role_id`)
        REFERENCES `mrs_role` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION,
      CONSTRAINT `fk_auth_role_service1`
        FOREIGN KEY (`specific_to_service_id`)
        REFERENCES `service` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION)
    ENGINE = InnoDB;


    -- -----------------------------------------------------
    -- Table `mrs_user_has_role`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `mrs_user_has_role` (
      `user_id` BINARY(16) NOT NULL,
      `role_id` BINARY(16) NOT NULL,
      `comments` VARCHAR(512) NULL,
      `options` JSON NULL,
      PRIMARY KEY (`user_id`, `role_id`),
      INDEX `fk_auth_user_has_privilege_role_privilege_role1_idx` (`role_id` ASC) VISIBLE,
      INDEX `fk_auth_user_has_privilege_role_auth_user1_idx` (`user_id` ASC) VISIBLE,
      CONSTRAINT `fk_auth_user_has_privilege_role_auth_user1`
        FOREIGN KEY (`user_id`)
        REFERENCES `mrs_user` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION,
      CONSTRAINT `fk_auth_user_has_privilege_role_privilege_role1`
        FOREIGN KEY (`role_id`)
        REFERENCES `mrs_role` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION)
    ENGINE = InnoDB;


    -- -----------------------------------------------------
    -- Table `mrs_user_hierarchy_type`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `mrs_user_hierarchy_type` (
      `id` BINARY(16) NOT NULL,
      `caption` VARCHAR(150) NULL,
      `description` VARCHAR(512) NULL,
      `specific_to_service_id` BINARY(16) NULL,
      `options` JSON NULL,
      PRIMARY KEY (`id`),
      INDEX `fk_user_hierarchy_type_service1_idx` (`specific_to_service_id` ASC) VISIBLE,
      CONSTRAINT `fk_user_hierarchy_type_service1`
        FOREIGN KEY (`specific_to_service_id`)
        REFERENCES `service` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION)
    ENGINE = InnoDB;


    -- -----------------------------------------------------
    -- Table `mrs_user_hierarchy`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `mrs_user_hierarchy` (
      `user_id` BINARY(16) NOT NULL,
      `reporting_to_user_id` BINARY(16) NOT NULL,
      `user_hierarchy_type_id` BINARY(16) NOT NULL,
      `options` JSON NULL,
      PRIMARY KEY (`user_id`, `reporting_to_user_id`, `user_hierarchy_type_id`),
      INDEX `fk_user_hierarchy_auth_user2_idx` (`reporting_to_user_id` ASC) VISIBLE,
      INDEX `fk_user_hierarchy_hierarchy_type1_idx` (`user_hierarchy_type_id` ASC) VISIBLE,
      CONSTRAINT `fk_user_hierarchy_auth_user1`
        FOREIGN KEY (`user_id`)
        REFERENCES `mrs_user` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION,
      CONSTRAINT `fk_user_hierarchy_auth_user2`
        FOREIGN KEY (`reporting_to_user_id`)
        REFERENCES `mrs_user` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION,
      CONSTRAINT `fk_user_hierarchy_hierarchy_type1`
        FOREIGN KEY (`user_hierarchy_type_id`)
        REFERENCES `mrs_user_hierarchy_type` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION)
    ENGINE = InnoDB;


    -- -----------------------------------------------------
    -- Table `mrs_privilege`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `mrs_privilege` (
      `id` BINARY(16) NOT NULL,
      `role_id` BINARY(16) NOT NULL,
      `crud_operations` SET('CREATE', 'READ', 'UPDATE', 'DELETE') NOT NULL DEFAULT '',
      `service_path` VARCHAR(512) NOT NULL DEFAULT '*',
      `schema_path` VARCHAR(255) NOT NULL DEFAULT '*',
      `object_path` VARCHAR(255) NOT NULL DEFAULT '*',
      `options` JSON NULL,
      PRIMARY KEY (`id`),
      CONSTRAINT `fk_priv_on_schema_auth_role1`
        FOREIGN KEY (`role_id`)
        REFERENCES `mrs_role` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION)
    ENGINE = InnoDB;


    -- -----------------------------------------------------
    -- Table `mrs_user_group`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `mrs_user_group` (
      `id` BINARY(16) NOT NULL,
      `specific_to_service_id` BINARY(16) NULL,
      `caption` VARCHAR(45) NULL,
      `description` VARCHAR(512) NULL,
      `options` JSON NULL,
      PRIMARY KEY (`id`),
      INDEX `fk_user_group_service1_idx` (`specific_to_service_id` ASC) VISIBLE,
      CONSTRAINT `fk_user_group_service1`
        FOREIGN KEY (`specific_to_service_id`)
        REFERENCES `service` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION)
    ENGINE = InnoDB;


    -- -----------------------------------------------------
    -- Table `mrs_user_group_has_role`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `mrs_user_group_has_role` (
      `user_group_id` BINARY(16) NOT NULL,
      `role_id` BINARY(16) NOT NULL,
      `options` JSON NULL,
      PRIMARY KEY (`user_group_id`, `role_id`),
      INDEX `fk_user_group_has_auth_role_auth_role1_idx` (`role_id` ASC) VISIBLE,
      INDEX `fk_user_group_has_auth_role_user_group1_idx` (`user_group_id` ASC) VISIBLE,
      CONSTRAINT `fk_user_group_has_auth_role_user_group1`
        FOREIGN KEY (`user_group_id`)
        REFERENCES `mrs_user_group` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION,
      CONSTRAINT `fk_user_group_has_auth_role_auth_role1`
        FOREIGN KEY (`role_id`)
        REFERENCES `mrs_role` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION)
    ENGINE = InnoDB;


    -- -----------------------------------------------------
    -- Table `mrs_user_has_group`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `mrs_user_has_group` (
      `user_id` BINARY(16) NOT NULL,
      `user_group_id` BINARY(16) NOT NULL,
      `comments` VARCHAR(512) NULL,
      `options` JSON NULL,
      PRIMARY KEY (`user_id`, `user_group_id`),
      INDEX `fk_auth_user_has_user_group_user_group1_idx` (`user_group_id` ASC) VISIBLE,
      INDEX `fk_auth_user_has_user_group_auth_user1_idx` (`user_id` ASC) VISIBLE,
      CONSTRAINT `fk_auth_user_has_user_group_auth_user1`
        FOREIGN KEY (`user_id`)
        REFERENCES `mrs_user` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION,
      CONSTRAINT `fk_auth_user_has_user_group_user_group1`
        FOREIGN KEY (`user_group_id`)
        REFERENCES `mrs_user_group` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION)
    ENGINE = InnoDB;


    -- -----------------------------------------------------
    -- Table `mrs_group_hierarchy_type`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `mrs_group_hierarchy_type` (
      `id` BINARY(16) NOT NULL,
      `caption` VARCHAR(150) NULL,
      `description` VARCHAR(512) NULL,
      `options` JSON NULL,
      PRIMARY KEY (`id`))
    ENGINE = InnoDB;


    -- -----------------------------------------------------
    -- Table `mrs_user_group_hierarchy`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `mrs_user_group_hierarchy` (
      `user_group_id` BINARY(16) NOT NULL,
      `parent_group_id` BINARY(16) NOT NULL,
      `group_hierarchy_type_id` BINARY(16) NOT NULL,
      `level` INT UNSIGNED NOT NULL DEFAULT 0,
      `options` JSON NULL,
      PRIMARY KEY (`user_group_id`, `parent_group_id`, `group_hierarchy_type_id`),
      INDEX `fk_user_group_has_user_group_user_group2_idx` (`parent_group_id` ASC) VISIBLE,
      INDEX `fk_user_group_has_user_group_user_group1_idx` (`user_group_id` ASC) VISIBLE,
      INDEX `fk_user_group_hierarchy_group_hierarchy_type1_idx` (`group_hierarchy_type_id` ASC) VISIBLE,
      CONSTRAINT `fk_user_group_has_user_group_user_group1`
        FOREIGN KEY (`user_group_id`)
        REFERENCES `mrs_user_group` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION,
      CONSTRAINT `fk_user_group_has_user_group_user_group2`
        FOREIGN KEY (`parent_group_id`)
        REFERENCES `mrs_user_group` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION,
      CONSTRAINT `fk_user_group_hierarchy_group_hierarchy_type1`
        FOREIGN KEY (`group_hierarchy_type_id`)
        REFERENCES `mrs_group_hierarchy_type` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION)
    ENGINE = InnoDB;


    -- -----------------------------------------------------
    -- Table `mrs_db_object_row_group_security`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `mrs_db_object_row_group_security` (
      `db_object_id` BINARY(16) NOT NULL,
      `group_hierarchy_type_id` BINARY(16) NOT NULL,
      `row_group_ownership_column` VARCHAR(255) NOT NULL,
      `level` INT UNSIGNED NOT NULL DEFAULT 0,
      `match_level` ENUM('HIGHER', 'EQUAL OR HIGHER', 'EQUAL', 'LOWER OR EQUAL', 'LOWER') NOT NULL DEFAULT 'HIGHER',
      `options` JSON NULL,
      INDEX `fk_table1_db_object1_idx` (`db_object_id` ASC) VISIBLE,
      INDEX `fk_db_object_row_security_group_hierarchy_type1_idx` (`group_hierarchy_type_id` ASC) VISIBLE,
      PRIMARY KEY (`db_object_id`, `group_hierarchy_type_id`),
      CONSTRAINT `fk_table1_db_object1`
        FOREIGN KEY (`db_object_id`)
        REFERENCES `db_object` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION,
      CONSTRAINT `fk_db_object_row_security_group_hierarchy_type1`
        FOREIGN KEY (`group_hierarchy_type_id`)
        REFERENCES `mrs_group_hierarchy_type` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION)
    ENGINE = InnoDB;


    -- -----------------------------------------------------
    -- Table `router`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `router` (
      `id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'The ID of the router instance that uniquely identifies the router on this MySQL REST Service setup.',
      `router_name` VARCHAR(255) NOT NULL COMMENT 'A user specified name for an instance of the router. Should default to address:port, where port is the http server port of the router. Set via --name during router bootstrap.',
      `address` VARCHAR(255) CHARACTER SET 'ascii' COLLATE 'ascii_general_ci' NOT NULL COMMENT 'Network address of the host the Router is running on. Set via --report--host during bootstrap.',
      `product_name` VARCHAR(128) NOT NULL COMMENT 'The product name of the routing component, e.g. \'MySQL Router\'',
      `version` VARCHAR(12) NULL COMMENT 'The version of the router instance. Updated on bootstrap and each startup of the router instance. Format: x.y.z, 3 digits for each component. Managed by Router.',
      `last_check_in` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'A timestamp updated by the router every hour with the current time. This timestamp is used to detect routers that are no longer used or stalled. Managed by Router.',
      `attributes` JSON NULL COMMENT 'Router specific custom attributes. Managed by Router.',
      `options` JSON NULL COMMENT 'Router instance specific configuration options.',
      PRIMARY KEY (`id`),
      UNIQUE INDEX `address_router_name` (`address` ASC, `router_name` ASC) VISIBLE)
    ENGINE = InnoDB
    COMMENT = 'no_audit_log';


    -- -----------------------------------------------------
    -- Table `router_status`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `router_status` (
      `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
      `router_id` INT UNSIGNED NOT NULL,
      `status_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'The time the status was reported',
      `timespan` INT UNSIGNED NOT NULL COMMENT 'The timespan of the measuring interval',
      `mysql_connections` INT UNSIGNED NOT NULL DEFAULT 0,
      `mysql_queries` INT UNSIGNED NOT NULL DEFAULT 0,
      `http_requests_get` INT UNSIGNED NOT NULL DEFAULT 0,
      `http_requests_post` INT UNSIGNED NOT NULL DEFAULT 0,
      `http_requests_put` INT UNSIGNED NOT NULL DEFAULT 0,
      `http_requests_delete` INT UNSIGNED NOT NULL DEFAULT 0,
      `active_mysql_connections` INT UNSIGNED NOT NULL DEFAULT 0,
      `details` JSON NULL COMMENT 'More detailed status information',
      PRIMARY KEY (`id`),
      INDEX `fk_router_status_router1_idx` (`router_id` ASC) VISIBLE,
      INDEX `status_time` (`status_time` ASC) VISIBLE,
      CONSTRAINT `fk_router_status_router1`
        FOREIGN KEY (`router_id`)
        REFERENCES `router` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION)
    ENGINE = InnoDB
    COMMENT = 'no_audit_log';


    -- -----------------------------------------------------
    -- Table `router_session`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `router_session` (
      `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
      `user_id` BINARY(16) NOT NULL,
      `service_id` BINARY(16) NOT NULL,
      `expires` DATETIME NOT NULL,
      PRIMARY KEY (`id`))
    ENGINE = InnoDB
    COMMENT = 'no_audit_log';


    -- -----------------------------------------------------
    -- Table `router_general_log`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `router_general_log` (
      `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
      `router_id` INT UNSIGNED NOT NULL,
      `router_session_id` INT UNSIGNED NULL,
      `log_time` TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
      `log_type` ENUM("INFO", "WARNING", "DEBUG", "ERROR", "FATAL", "SYSTEM", "NOTE") NOT NULL,
      `code` INT UNSIGNED NULL,
      `domain` VARCHAR(255) NULL,
      `message` VARCHAR(4096) NULL,
      `thread_id` INT UNSIGNED NULL,
      `data` JSON NULL,
      PRIMARY KEY (`id`),
      INDEX `fk_router_general_log_router1_idx` (`router_id` ASC) VISIBLE,
      INDEX `log_time` (`log_time` ASC) VISIBLE,
      INDEX `fk_router_general_log_router_session1_idx` (`router_session_id` ASC) VISIBLE,
      INDEX `router_log_type` (`log_type` ASC) VISIBLE,
      INDEX `router_log_thread_id` (`thread_id` ASC) VISIBLE,
      CONSTRAINT `fk_router_general_log_router1`
        FOREIGN KEY (`router_id`)
        REFERENCES `router` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION,
      CONSTRAINT `fk_router_general_log_router_session1`
        FOREIGN KEY (`router_session_id`)
        REFERENCES `router_session` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION)
    ENGINE = InnoDB
    COMMENT = 'no_audit_log';


    -- -----------------------------------------------------
    -- Table `object`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `object` (
      `id` BINARY(16) NOT NULL,
      `db_object_id` BINARY(16) NOT NULL,
      `name` VARCHAR(255) NOT NULL,
      `kind` ENUM('RESULT', 'PARAMETERS', 'INTERFACE') NOT NULL DEFAULT 'RESULT',
      `position` INT NOT NULL DEFAULT 0,
      `row_ownership_field_id` BINARY(16) NULL,
      `options` JSON NULL COMMENT 'Holds data mapping view options for INSERT, UPDATE, DELETE and CHECK, e.g. { dataMappingViewInsert: true, dataMappingViewUpdate: true, dataMappingViewDelete: false, dataMappingViewNoCheck: false }',
      `sdk_options` JSON NULL,
      `comments` VARCHAR(512) NULL,
      PRIMARY KEY (`id`),
      INDEX `fk_result_db_object1_idx` (`db_object_id` ASC) VISIBLE,
      INDEX `row_ownership_object_idx` (`row_ownership_field_id` ASC) VISIBLE,
      CONSTRAINT `fk_result_db_object1`
        FOREIGN KEY (`db_object_id`)
        REFERENCES `db_object` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION)
    ENGINE = InnoDB;


    -- -----------------------------------------------------
    -- Table `object_reference`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `object_reference` (
      `id` BINARY(16) NOT NULL,
      `reduce_to_value_of_field_id` BINARY(16) NULL COMMENT 'If set to an object_field, this reference will be reduced to the value of the given field. Example: \"films\": [ { \"categories\": [ \"Thriller\", \"Action\"] } ] instead of \"films\": [ { \"categories\": [ { \"name\": \"Thriller\" }, { \"name\": \"Action\" } ] } ],',
      `row_ownership_field_id` BINARY(16) NULL,
      `reference_mapping` JSON NOT NULL COMMENT 'Holds all column mappings of the FK, {kind:\"n:1\", constraint: \"constraint_name\", referenced_schema: \"schema_name\", referenced_table: \"table_name\", column_mapping: [{\"column_name\": \"referenced_column_name\"}, \"to_many\": true, \"id_generation\": \"auto_increment\"}. \"id_generation\" can be undefined or \"auto_increment\" for tables using AUTO_INCREMENT or \"reverse_uuid\" for tables using BINARY(16) for the primary key.',
      `unnest` BIT(1) NOT NULL DEFAULT 0 COMMENT 'If set to TRUE, the properties will be directly added to the parent',
      `options` JSON NULL COMMENT 'Holds data mapping view options for INSERT, UPDATE, DELETE and CHECK, e.g. { dataMappingViewInsert: true, dataMappingViewUpdate: true, dataMappingViewDelete: false, dataMappingViewNoCheck: false }',
      `sdk_options` JSON NULL,
      `comments` VARCHAR(512) NULL,
      PRIMARY KEY (`id`),
      INDEX `reduce_to_idx` (`reduce_to_value_of_field_id` ASC) VISIBLE,
      INDEX `row_ownership_object_reference_idx` (`row_ownership_field_id` ASC) VISIBLE)
    ENGINE = InnoDB;


    -- -----------------------------------------------------
    -- Table `object_field`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `object_field` (
      `id` BINARY(16) NOT NULL,
      `object_id` BINARY(16) NOT NULL,
      `parent_reference_id` BINARY(16) NULL,
      `represents_reference_id` BINARY(16) NULL,
      `name` VARCHAR(255) NOT NULL COMMENT 'The name of the field as returned in the JSON',
      `position` INT NOT NULL,
      `db_column` JSON NULL COMMENT 'Holds information about the original database column, e.g. {\"name\": \"first_name\", \"datatype\":\"VARCHAR(45)\", \"not_null\": true, \"is_primary\": false, \"is_unique\": false, \"is_generated\": false, \"auto_inc\": false}. When representing a STORED PROCEDURE parameter, two optional fields can be set, {\"in\": true, \"out\": false}',
      `enabled` BIT(1) NOT NULL DEFAULT 1 COMMENT 'When set to FALSE, the property is hidden from the result',
      `allow_filtering` BIT(1) NOT NULL DEFAULT 1 COMMENT 'When set to FALSE the property is not available for filtering',
      `allow_sorting` BIT(1) NOT NULL DEFAULT 0 COMMENT 'When set to TRUE the field can be used for ordering',
      `no_check` BIT(1) NOT NULL DEFAULT 0 COMMENT 'Specifies whether the field should be ignored in the scope of concurrency control',
      `no_update` BIT(1) NOT NULL DEFAULT 0 COMMENT 'If set to 1 then no updates of this field are allowed.',
      `json_schema` JSON NULL,
      `options` JSON NULL,
      `sdk_options` JSON NULL,
      `comments` VARCHAR(512) NULL,
      PRIMARY KEY (`id`),
      INDEX `fk_properties_result1_idx` (`object_id` ASC) VISIBLE,
      INDEX `fk_result_property_result_reference1_idx` (`parent_reference_id` ASC) VISIBLE,
      INDEX `fk_result_property_result_reference2_idx` (`represents_reference_id` ASC) VISIBLE,
      CONSTRAINT `fk_properties_result1`
        FOREIGN KEY (`object_id`)
        REFERENCES `object` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION,
      CONSTRAINT `fk_result_property_result_reference1`
        FOREIGN KEY (`parent_reference_id`)
        REFERENCES `object_reference` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION,
      CONSTRAINT `fk_result_property_result_reference2`
        FOREIGN KEY (`represents_reference_id`)
        REFERENCES `object_reference` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION)
    ENGINE = InnoDB;


    -- -----------------------------------------------------
    -- Table `service_has_auth_app`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `service_has_auth_app` (
      `service_id` BINARY(16) NOT NULL,
      `auth_app_id` BINARY(16) NOT NULL,
      `options` JSON NULL,
      PRIMARY KEY (`service_id`, `auth_app_id`),
      INDEX `fk_service_has_auth_app_auth_app1_idx` (`auth_app_id` ASC) VISIBLE,
      INDEX `fk_service_has_auth_app_service1_idx` (`service_id` ASC) VISIBLE,
      CONSTRAINT `fk_service_has_auth_app_service1`
        FOREIGN KEY (`service_id`)
        REFERENCES `service` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION,
      CONSTRAINT `fk_service_has_auth_app_auth_app1`
        FOREIGN KEY (`auth_app_id`)
        REFERENCES `auth_app` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION)
    ENGINE = InnoDB;


    -- -----------------------------------------------------
    -- Table `content_set_has_obj_def`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `content_set_has_obj_def` (
      `content_set_id` BINARY(16) NOT NULL,
      `db_object_id` BINARY(16) NOT NULL,
      `kind` ENUM('Script', 'BeforeCreate', 'BeforeRead', 'BeforeUpdate', 'BeforeDelete', 'AfterCreate', 'AfterRead', 'AfterUpdate', 'AfterDelete') NOT NULL,
      `priority` INT NOT NULL DEFAULT 0,
      `language` VARCHAR(45) NOT NULL,
      `name` VARCHAR(255) NOT NULL,
      `class_name` VARCHAR(255) NULL,
      `comments` VARCHAR(512) NULL,
      `options` JSON NULL,
      INDEX `fk_content_set_has_obj_dev_db_object1_idx` (`db_object_id` ASC) VISIBLE,
      INDEX `fk_content_set_has_obj_dev_content_set1_idx` (`content_set_id` ASC) VISIBLE,
      INDEX `content_set_has_obj_dev_priority` (`priority` ASC) VISIBLE,
      PRIMARY KEY (`content_set_id`, `db_object_id`, `kind`, `priority`),
      INDEX `content_set_has_obj_dev_method_type` (`kind` ASC) VISIBLE,
      CONSTRAINT `fk_content_set_has_db_object_content_set1`
        FOREIGN KEY (`content_set_id`)
        REFERENCES `content_set` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION,
      CONSTRAINT `fk_content_set_has_db_object_db_object1`
        FOREIGN KEY (`db_object_id`)
        REFERENCES `db_object` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION)
    ENGINE = InnoDB;


    -- -----------------------------------------------------
    -- Table `audit_log_status`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `audit_log_status` (
      `id` TINYINT NOT NULL,
      `last_dump_at` TIMESTAMP NULL,
      `data` JSON NULL,
      PRIMARY KEY (`id`))
    ENGINE = InnoDB
    COMMENT = 'no_audit_log';


    -- -----------------------------------------------------
    -- Additional SQL

    -- Ensure only one row in `config`
    ALTER TABLE `config`
    	ADD CONSTRAINT Config_OnlyOneRow CHECK (id = 1);

    -- Ensure only one row in `audit_log_status`
    ALTER TABLE `audit_log_status`
    	ADD CONSTRAINT AuditLogStatus_OnlyOneRow CHECK (id = 1);

    -- Ensure there is a default for service.name taken from url_context_root
    ALTER TABLE `service`
        CHANGE COLUMN name name VARCHAR(255) NOT NULL DEFAULT (REGEXP_REPLACE(url_context_root, '[^0-9a-zA-Z ]', ''));

    -- Ensure page size is within 16K limit
    ALTER TABLE `db_schema`
    	ADD CONSTRAINT db_schema_max_page_size CHECK (items_per_page IS NULL OR items_per_page < 16384);
    ALTER TABLE `db_object`
    	ADD CONSTRAINT db_object_max_page_size CHECK (items_per_page IS NULL OR items_per_page < 16384);

    -- Ensure an email cannot be used as user name and a user name cannot be used as email
    ALTER TABLE `mrs_user`
        ADD CONSTRAINT `mrs_user_no_at_symbol_in_user_name` CHECK (INSTR(name, '@') = 0),
        ADD CONSTRAINT `mrs_user_at_symbol_in_email` CHECK (INSTR(email, '@') > 0 OR email IS NULL OR email = '');

    -- Ensure that for STORED PROCEDURE parameters at least one of the 'in' and 'out' flag is set to true
    ALTER TABLE `object_field`
      ADD CONSTRAINT param_mode_not_false CHECK (
        (db_column->"$.in" IS NULL AND db_column->"$.out" IS NULL) OR
        (db_column->"$.in" + db_column->"$.out" >= 1));

    -- Ensure the service.in_development->>$.developers is a list that only holds unique strings
    ALTER TABLE `service`
      ADD CONSTRAINT in_development_developers_check CHECK(
        JSON_SCHEMA_VALID('{
        "id": "https://dev.mysql.com/mrs/service/in_development",
        "type": "object",
        "properties": {
            "developers": {
                "type": "array",
                "items": {
                    "type": "string"
                },
                "minItems": 1,
                "uniqueItems": true
            }
        },
        "required": [ "developers" ]
        }', s.in_development)
    );

    -- -----------------------------------------------------
    -- INSERTs

    -- -----------------------------------------------------------------------------
    -- Host filter that matches any string
    INSERT INTO `mysql_rest_service_metadata`.`url_host` (`id`, `name`, `comments`)
    VALUES (0x31, '', 'Match any host');

    -- -----------------------------------------------------------------------------
    -- MRS Authentication Vendors
    INSERT INTO `mysql_rest_service_metadata`.`auth_vendor` (
        `id`, `name`, `validation_url`, `enabled`, `comments`, `options`)
    VALUES (0x30, 'MRS', NULL, 1, 'Built-in user management of MRS', NULL);

    INSERT INTO `mysql_rest_service_metadata`.`auth_vendor` (
        `id`, `name`, `validation_url`, `enabled`, `comments`, `options`)
    VALUES (0x31, 'MySQL Internal', NULL, 1,
        'Provides basic authentication via MySQL Server accounts', NULL);

    INSERT INTO `mysql_rest_service_metadata`.`auth_vendor` (
        `id`, `name`, `validation_url`, `enabled`, `comments`, `options`)
    VALUES (0x32, 'Facebook', NULL, 1, 'Uses the Facebook Login OAuth2 service',
        NULL);

    INSERT INTO `mysql_rest_service_metadata`.`auth_vendor` (
        `id`, `name`, `validation_url`, `enabled`, `comments`, `options`)
    VALUES (0x34, 'Google', NULL, 1, 'Uses the Google OAuth2 service', NULL);

    INSERT INTO `mysql_rest_service_metadata`.`auth_vendor` (
        `id`, `name`, `validation_url`, `enabled`, `comments`, `options`)
    VALUES (0x35, 'OCI OAuth2', NULL, 1, 'Uses the OCI OAuth2 service', NULL);

    -- Default MySQL auth_app for MySQL Internal Authentication
    INSERT INTO `mysql_rest_service_metadata`.`auth_app` (
        `id`, `auth_vendor_id`, `name`, `description`, `enabled`,
        `limit_to_registered_users`, `default_role_id`, `options`)
    VALUES (0x31, 0x31, 'MySQL',
        'Provide login capabilities for MySQL Server user accounts.',
        TRUE, FALSE, 0x31, NULL);

    -- Default role for full access
    INSERT INTO `mysql_rest_service_metadata`.`mrs_role` (
        `id`, `derived_from_role_id`, `specific_to_service_id`, `caption`,
        `description`, `options`)
    VALUES (0x31, NULL, NULL, 'Full Access', 'Full access to all db_objects', NULL);

    -- Default privilege that defines full access
    INSERT INTO `mysql_rest_service_metadata`.`mrs_privilege` (
        `id`, `role_id`, `crud_operations`, `service_path`, `schema_path`,
        `object_path`, `options`)
    VALUES (0x31, 0x31, 'CREATE,READ,UPDATE,DELETE', DEFAULT, DEFAULT, DEFAULT,
        NULL);

    -- User hierarchy types
    INSERT INTO `mysql_rest_service_metadata`.`mrs_user_hierarchy_type` (
        `id`, `caption`, `description`, `specific_to_service_id`, `options`)
    VALUES (0x31, 'Direct Report', 'An employee directly reporting to the user',
        NULL, NULL);
    INSERT INTO `mysql_rest_service_metadata`.`mrs_user_hierarchy_type` (
        `id`, `caption`, `description`, `specific_to_service_id`, `options`)
    VALUES (0x32, 'Dotted Line Report',
        'An employee reporting to the user via a dotted line relationship',
        NULL, NULL);

    -- -----------------------------------------------------------------------------
    -- Base entry for audit_log_status, indicating that no dump has yet happened
    INSERT INTO `mysql_rest_service_metadata`.`audit_log_status` (
        `id`, `last_dump_at`, `data`) VALUES (1, NULL, NULL);

    -- -----------------------------------------------------
    -- Config

    DELETE FROM `mysql_rest_service_metadata`.`config`;
    INSERT INTO `mysql_rest_service_metadata`.`config` (`id`, `service_enabled`, `data`) VALUES (1, 1, '{ "defaultStaticContent": { "index.html": "[encoded data, 26492 characters]", "favicon.ico": "AAABAAMAMDAQAAEABABoBgAANgAAACAgEAABAAQA6AIAAJ4GAAAQEBAAAQAEACgBAACGCQAAKAAAADAAAABgAAAAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIVaCACOZAAAkWsgAJ17LwCoiUMAsJRbALykcADHs4oAz7+ZANrNsgDm3MgA7ujZAPXx6wD++/YA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGcgAAAAAAAAAAAAAAAAAAAAAAAAAAAAK+7CAAAnYAAAAAAAAAAAAAAAAAAAAAABzYzlAAfHAAAAAAAAAAAAAAAAAAAAAAAJ5QvmAq5wAAAAAAAAAAAAAAAAAAAAAABOkAzmCuYAAAAAAAAAAAAAAAAAAAAAAACuMA7ljYAAAAAAAAAAAAAAAAAAAAAAAALZAA3q6gAAAAAAAAAAAAAAAAAAAAAAAAblAAvuwQAAAAAAAAAAAAAAAAAAAAAAAAnhAAnuUAAAAAAAAAAAAAAAAAAAAAAAAAygAATpAAAAAAAAAAAAAAAAAAAAAAAAAB6QAACiAAAAAAAAAAAAAAAAAAAAAAAAAC5wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC5gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC5wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAngAAAAAAAAAAAAAAAgAAAAAAAAAAAAAG6wAAAAAAAAAAAAAAOgAAAAAAAAAAAABewwAAAAAAAAAAAAADxwAAAAAAAAAAAATrIAAAAAAAAAAAAAAb5AAAAAAAAAAAABvCAAAAAAAAAAAAAACOsAAAAAAAAAAAAH5gAAAAAAAAACVmQgB+UAAAAAAAAAAAA9sAAAAAAAAAa+yqzqQCAAAAAAAAAAAACeUAAAAAAAAqxiAAA36RAAAAAAAAAAAAPqAAAAAAAAPJEAAAAAKrAAAAAAAAAAAArjAAABAAABuABagAqUAakAAAAAAAAAAF6AAAAKIAAHsQXoMAScIC1QAAAAAAAAA8wgAACOIAAuMAbiAABOQAawAAAAAAAAPMUAAAfoAABrAAbgAAA+QAHjAAAAAAADzlAAAAAgAACXAAjgAABOUACmAAAAAABM5QAAAAAAAACmAXyQAAALtgCXAAAAAALeQAAAAAAAAAC1A9owAAAEvQCIAAAAAAjlAAAAFlRDR3CmACrAAAAtgQCXAAAAAAywAAAmze7t7sCJAAfgAABOUADFAAAAAAvDJGne7Jq7uXBMEAbgAAA+QAPhAAAAAAbu7e7sYQAAAAALcAbTAABeMAqAAAAAAABr3LdAAAAAAAAE0wLMcAnKAGwgAAAAAAAAAAAAAAAAAAAAjDAFQAVABdUAAAAAAAAAAAAAAAAAAAAAB+YAAAACjlAAAAAAAAAAAAAAAAAAAAAAAFu4U0WcowAAAAAAAAAAAAAAAAAAAAAAAAFZvduEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAIAAAAEAAAAABAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhVoIAIthAACQah8AmnYnAKCANwCtkFIAuKBsAMCpdwDEsYYAz7+aANfIqgDe07oA597OAPDp2gD///8AAAAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpAAAMAAAAAAAAAAAAAAAfs0AS0AAAAAAAAAAAAAAA+OeBeQAAAAAAAAAAAAAAAuAnV5QAAAAAAAAAAAAAAAuEJ7lAAAAAAAAAAAAAAAAawB+sAAAAAAAAAAAAAAAAJgALiAAAAAAAAAAAAAAAAC1AAIAAAAAAAAAAAAAAAAAtQAAAAAAAAAAAAAAAAAAAKYAAAAAAAAAAAAAAAAAAACoAAAAAAAAAAAAAAAAAAAAigAAAAAAAAABAAAAAAAABNgAAAAAAAAAVwAAAAAAAC2AAAAAAAAAA+QAAAAAAAC6AAAAAAAAAAzAAAAAAAAF4QAAAAA6zMpCQAAAAAAADXAAAAAIxAADqhAAAAAAAG0AAAAAiQVwVhagAAAAAALVAAVgArBNMCtglQAAAAAtoABOMAlQWwAIgCsAAAAC2gAAAQAMAGoABqANAAAAHaAAAAAADAbTAALKCxAAAHwAAEqHijwAigAGoBwAAACLE2ztztxJQFsACIArAAAAPu7sYAAAA7BNMCtglQAAAAJUEAAAAACJBnBXBqAAAAAAAAAAAAAACLQAA5oQAAAAAAAAAAAAAABJzMtQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAEAAAACAAAAABAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhVoIAIxiAACQayAAmnYhAJ59MgCnhz4Aq5BRALGVXQC3nGYAuqJvAMGqeADHs4wA0cKeANrQtgDv7uIAAAAAAAAAAAAAAAAAAAAALVBQAAAAAADHvBAAAAAAArXiAAAAAAAHQGAAAAAAAAcwAAAAAAAABVAAAAAAAAAMIAAABjAAAKQAAGg7AAACsAAqQWgAAAwRoJKQtEAAwwAAiUCzgAWTvdmEcLGAAbtgAGSSeRAAAAAAB4mTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", "favicon.svg": "[encoded data, 7760 characters]", "sakila.svg": "[encoded data, 5244 characters]", "standalone-preact.js": "[encoded data, 15668 characters]" }, "directoryIndexDirective": [ "index.html" ] }');
END%%

-- -----------------------------------------------------------------------------
-- PROCEDURE `mysql_rest_service_metadata`.`msm_create_or_update`
-- -----------------------------------------------------------------------------
-- Stored procedure to perform the version sensitive creation or update of
-- schema TABLEs
-- -----------------------------------------------------------------------------

DROP PROCEDURE IF EXISTS `msm_create_or_update`%%
CREATE PROCEDURE `msm_create_or_update`()
SQL SECURITY INVOKER
BEGIN
    DECLARE item_count INT;
    DECLARE version_str VARCHAR(255);
    DECLARE signal_msg VARCHAR(128);

    -- Check if the schema is yet empty
    SELECT COUNT(*) +
        (SELECT COUNT(*) FROM `information_schema`.`ROUTINES`
            WHERE ROUTINE_SCHEMA = 'mysql_rest_service_metadata'
                AND ROUTINE_NAME NOT LIKE 'msm_%') +
        (SELECT COUNT(*) FROM `information_schema`.`EVENTS`
            WHERE `EVENT_SCHEMA` = 'mysql_rest_service_metadata') INTO item_count
    FROM `information_schema`.`TABLES`
    WHERE `TABLE_SCHEMA` = 'mysql_rest_service_metadata';

    -- If the schema is empty, create the schema from scratch
    IF item_count = 0 THEN
        -- Set the @msm_schema_init variable to indicate a fresh creation of the
        -- schema. This session variable is later used when creating roles
        SET @msm_schema_init = 1;

        -- Set `mysql_rest_service_metadata`.`msm_schema_version` VIEW
        -- to 0, 0, 0 to indicate start of update process
        CREATE OR REPLACE SQL SECURITY INVOKER
            VIEW `mysql_rest_service_metadata`.`msm_schema_version` (
                `major`,`minor`,`patch`) AS
                SELECT 0, 0, 0;

        CALL `mysql_rest_service_metadata`.`msm_create_4.1.3`();
    ELSE
        -- If the schema is not empty, update the schema
        SET @msm_schema_init = 0;

        -- Check if the schema is a managed one
        SELECT COUNT(*) INTO item_count FROM information_schema.`TABLES`
            WHERE `TABLE_SCHEMA` = 'mysql_rest_service_metadata'
                AND `TABLE_NAME` = 'msm_schema_version'
                AND `TABLE_TYPE` = 'VIEW';
        IF item_count = 0 THEN
            SELECT COUNT(*) INTO item_count FROM `information_schema`.`TABLES`
                WHERE `TABLE_SCHEMA` = 'mysql_rest_service_metadata';
            IF item_count > 0 THEN
                SET signal_msg = LEFT(CONCAT(
                    'The schema `mysql_rest_service_metadata` already exists but ',
                    'misses a msm_schema_version view ',
                    'and cannot be updated.'), 128);
                SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = signal_msg,
                    MYSQL_ERRNO = 32100;
            END IF;
        END IF;

        -- Get the current version of the schema
        SELECT CONCAT(v.major, '.', v.minor, '.', v.patch) INTO `version_str`
        FROM `mysql_rest_service_metadata`.`msm_schema_version` AS v;

        SET @msm_current_version = version_str;

        -- Check if the current version needs to be updated
        IF version_str != '4.1.3' THEN
            IF version_str = '0.0.0' THEN
                SET signal_msg = LEFT(CONCAT(
                    'The schema `mysql_rest_service_metadata` is already being updated ',
                    'as its version is set to 0.0.0.'), 128);
                SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = signal_msg,
                    MYSQL_ERRNO = 32101;
            END IF;

            -- Set `mysql_rest_service_metadata`.`msm_schema_version` VIEW
            -- to 0, 0, 0 to indicate start of update process
            CREATE OR REPLACE SQL SECURITY INVOKER
                VIEW `mysql_rest_service_metadata`.`msm_schema_version` (
                    `major`,`minor`,`patch`) AS
                    SELECT 0, 0, 0;


            -- Check if the current version can be updated by this script
            IF NOT JSON_CONTAINS('["4.0.0", "4.0.1", "4.0.2", "4.0.3", "4.0.4", "4.1.0", "4.1.1", "4.1.2"]',
                JSON_QUOTE(version_str), '$') THEN
                SET signal_msg = LEFT(CONCAT(
                    'The schema `mysql_rest_service_metadata` is on version ', version_str,
                    ' which cannot be updated by this script.'), 128);
                SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = signal_msg,
                    MYSQL_ERRNO = 32102;
            END IF;

            -- Run the update scripts
            IF version_str = '4.0.0' THEN
                CALL `mysql_rest_service_metadata`.`msm_update_4.0.0_to_4.0.1`();
                SET version_str = '4.0.1';
            END IF;

            IF version_str = '4.0.1' THEN
                CALL `mysql_rest_service_metadata`.`msm_update_4.0.1_to_4.0.2`();
                SET version_str = '4.0.2';
            END IF;

            IF version_str = '4.0.2' THEN
                CALL `mysql_rest_service_metadata`.`msm_update_4.0.2_to_4.0.3`();
                SET version_str = '4.0.3';
            END IF;

            IF version_str = '4.0.3' THEN
                CALL `mysql_rest_service_metadata`.`msm_update_4.0.3_to_4.0.4`();
                SET version_str = '4.0.4';
            END IF;

            IF version_str = '4.0.4' THEN
                CALL `mysql_rest_service_metadata`.`msm_update_4.0.4_to_4.1.0`();
                SET version_str = '4.1.0';
            END IF;

            IF version_str = '4.1.0' THEN
                CALL `mysql_rest_service_metadata`.`msm_update_4.1.0_to_4.1.1`();
                SET version_str = '4.1.1';
            END IF;

            IF version_str = '4.1.1' THEN
                CALL `mysql_rest_service_metadata`.`msm_update_4.1.1_to_4.1.2`();
                SET version_str = '4.1.2';
            END IF;

            IF version_str = '4.1.2' THEN
                CALL `mysql_rest_service_metadata`.`msm_update_4.1.2_to_4.1.3`();
                SET version_str = '4.1.3';
            END IF;

        END IF;
    END IF;
END%%

DELIMITER ;

-- -----------------------------------------------------------------------------
-- Execute the installation/upgrade procedure for schema tables
-- -----------------------------------------------------------------------------

CALL `msm_create_or_update`();

-- -----------------------------------------------------------------------------
-- Drop the stored procedures used for schema TABLE installation and updates.
-- -----------------------------------------------------------------------------

USE `mysql_rest_service_metadata`;
DROP PROCEDURE `msm_update_4.0.0_to_4.0.1`;
USE `mysql_rest_service_metadata`;
DROP PROCEDURE `msm_update_4.0.1_to_4.0.2`;
USE `mysql_rest_service_metadata`;
DROP PROCEDURE `msm_update_4.0.2_to_4.0.3`;
USE `mysql_rest_service_metadata`;
DROP PROCEDURE `msm_update_4.0.3_to_4.0.4`;
USE `mysql_rest_service_metadata`;
DROP PROCEDURE `msm_update_4.0.4_to_4.1.0`;
USE `mysql_rest_service_metadata`;
DROP PROCEDURE `msm_update_4.1.0_to_4.1.1`;
USE `mysql_rest_service_metadata`;
DROP PROCEDURE `msm_update_4.1.1_to_4.1.2`;
USE `mysql_rest_service_metadata`;
DROP PROCEDURE `msm_update_4.1.2_to_4.1.3`;
USE `mysql_rest_service_metadata`;
DROP PROCEDURE `msm_create_4.1.3`;
DROP PROCEDURE `msm_create_or_update`;


-- #############################################################################
-- MSM Section 150: Idempotent Schema Objects
-- -----------------------------------------------------------------------------
-- This section contains the creation of all schema objects except TABLEs.
-- All objects must be created under the assumption that an object of the same
-- name is already present. Therefore explicit DROP IF EXISTS statements or
-- CREATE OR REPLACE clauses must be used when creating the objects.
-- -----------------------------------------------------------------------------
-- All other schema object definitions (VIEWS, PROCEDUREs, FUNCTIONs, TRIGGERs,
-- EVENTS, ...)
-- #############################################################################

-- -----------------------------------------------------
-- VIEWs

-- -----------------------------------------------------------------------------
-- View `mysql_rest_service_metadata`.`mrs_user_schema_version`
-- -----------------------------------------------------------------------------
CREATE OR REPLACE SQL SECURITY INVOKER
VIEW `mrs_user_schema_version` (
    major, minor, patch) AS
SELECT 4, 0, 0;

-- -----------------------------------------------------------------------------
-- View `mysql_rest_service_metadata`.`object_fields_with_references`
-- -----------------------------------------------------------------------------
CREATE OR REPLACE SQL SECURITY INVOKER VIEW `object_fields_with_references` AS
WITH RECURSIVE obj_fields (
    caption, lev, position, id, represents_reference_id, parent_reference_id, object_id,
    name, db_column, enabled,
    allow_filtering, allow_sorting, no_check, no_update, options, sdk_options, comments,
    object_reference) AS
(
    SELECT CONCAT('- ', f.name) as caption, 1 AS lev, f.position, f.id,
		f.represents_reference_id, f.parent_reference_id, f.object_id, f.name,
        f.db_column, f.enabled, f.allow_filtering, f.allow_sorting, f.no_check, f.no_update,
        f.options, f.sdk_options, f.comments,
        IF(ISNULL(f.represents_reference_id), NULL, JSON_OBJECT(
            'reduce_to_value_of_field_id', TO_BASE64(r.reduce_to_value_of_field_id),
            'row_ownership_field_id', TO_BASE64(r.row_ownership_field_id),
            'reference_mapping', r.reference_mapping,
            'unnest', (r.unnest = 1),
            'options', r.options,
            'sdk_options', r.sdk_options,
            'comments', r.comments
        )) AS object_reference
    FROM `mysql_rest_service_metadata`.`object_field` f
        LEFT OUTER JOIN `mysql_rest_service_metadata`.`object_reference` AS r
            ON r.id = f.represents_reference_id
    WHERE ISNULL(parent_reference_id)
    UNION ALL
    SELECT CONCAT(REPEAT('  ', p.lev), '- ', f.name) as caption, p.lev+1 AS lev, f.position,
        f.id, f.represents_reference_id, f.parent_reference_id, f.object_id, f.name,
        f.db_column, f.enabled, f.allow_filtering, f.allow_sorting, f.no_check, f.no_update,
        f.options, f.sdk_options, f.comments,
        IF(ISNULL(f.represents_reference_id), NULL, JSON_OBJECT(
            'reduce_to_value_of_field_id', TO_BASE64(rc.reduce_to_value_of_field_id),
            'row_ownership_field_id', TO_BASE64(rc.row_ownership_field_id),
            'reference_mapping', rc.reference_mapping,
            'unnest', (rc.unnest = 1),
            'options', rc.options,
            'sdk_options', rc.sdk_options,
            'comments', rc.comments
        )) AS object_reference
    FROM obj_fields AS p JOIN `mysql_rest_service_metadata`.`object_reference` AS r
            ON r.id = p.represents_reference_id
        LEFT OUTER JOIN `mysql_rest_service_metadata`.`object_field` AS f
            ON r.id = f.parent_reference_id
        LEFT OUTER JOIN `mysql_rest_service_metadata`.`object_reference` AS rc
            ON rc.id = f.represents_reference_id
	WHERE f.id IS NOT NULL
)
SELECT * FROM obj_fields;

-- -----------------------------------------------------------------------------
-- View `mysql_rest_service_metadata`.`router_services`
-- -----------------------------------------------------------------------------
CREATE OR REPLACE SQL SECURITY INVOKER
VIEW `router_services` AS
SELECT r.id AS router_id, r.router_name, r.address, r.attributes->>'$.developer' AS router_developer,
    s.id as service_id, h.name AS service_url_host_name,
    s.url_context_root AS service_url_context_root,
    CONCAT(h.name, s.url_context_root) AS service_host_ctx,
    s.published, s.in_development,
    (SELECT GROUP_CONCAT(IF(item REGEXP '^[A-Za-z0-9_]+$', item, QUOTE(item)) ORDER BY item)
        FROM JSON_TABLE(
        s.in_development->>'$.developers', '$[*]' COLUMNS (item text path '$')
    ) AS jt) AS sorted_developers
FROM `mysql_rest_service_metadata`.`service` s
    LEFT JOIN `mysql_rest_service_metadata`.`url_host` h
        ON s.url_host_id = h.id
    JOIN `mysql_rest_service_metadata`.`router` r
WHERE
    (enabled = 1)
    AND (
    ((published = 1) AND (NOT EXISTS (select s2.id from `mysql_rest_service_metadata`.`service` s2 where s.url_host_id=s2.url_host_id AND s.url_context_root=s2.url_context_root
        AND JSON_OVERLAPS(r.attributes->'$.developer', s2.in_development->>'$.developers'))))
    OR
    ((published = 0) AND (s.id IN (select s2.id from `mysql_rest_service_metadata`.`service` s2 where s.url_host_id=s2.url_host_id AND s.url_context_root=s2.url_context_root
        AND JSON_OVERLAPS(r.attributes->'$.developer', s2.in_development->>'$.developers'))))
    OR
    ((published = 0) AND (r.options->'$.developer' IS NOT NULL
        OR r.attributes->'$.developer' IS NOT NULL) AND s.in_development IS NULL)
    );

-- -----------------------------------------------------
-- PROCEDURES and FUNCTIONs

DELIMITER %%

-- -----------------------------------------------------------------------------
-- CREATE PROCEDURE `msm_instance_demoted`
-- -----------------------------------------------------------------------------

DROP PROCEDURE IF EXISTS `msm_instance_demoted`%%
CREATE PROCEDURE `msm_instance_demoted`()
SQL SECURITY DEFINER
COMMENT 'This procedure needs to be called on a primary instance in an InnoDB Cluster setup before it is demoted to
    become a secondary.'
BEGIN
    ALTER EVENT `mysql_rest_service_metadata`.`delete_old_audit_log_entries` DISABLE;
    ALTER EVENT `mysql_rest_service_metadata`.`router_status_cleanup` DISABLE;
    ALTER EVENT `mysql_rest_service_metadata`.`router_log_cleanup` DISABLE;
END%%

-- -----------------------------------------------------------------------------
-- CREATE PROCEDURE `msm_instance_promoted`
-- -----------------------------------------------------------------------------

DROP PROCEDURE IF EXISTS `msm_instance_promoted`%%
CREATE PROCEDURE `msm_instance_promoted`()
SQL SECURITY DEFINER
COMMENT 'This procedure needs to be called on an instance in an InnoDB Cluster setup when it is promoted to
    become the primary.'
BEGIN
    ALTER EVENT `mysql_rest_service_metadata`.`delete_old_audit_log_entries` ENABLE;
    ALTER EVENT `mysql_rest_service_metadata`.`router_status_cleanup` ENABLE;
    ALTER EVENT `mysql_rest_service_metadata`.`router_log_cleanup` ENABLE;
END%%


-- -----------------------------------------------------------------------------
-- CREATE FUNCTIONs
-- -----------------------------------------------------------------------------

DROP FUNCTION IF EXISTS `get_sequence_id`%%
CREATE FUNCTION `get_sequence_id`() RETURNS BINARY(16) SQL SECURITY INVOKER NOT DETERMINISTIC NO SQL
RETURN UUID_TO_BIN(UUID(), 1)%%

DROP FUNCTION IF EXISTS `valid_request_path`%%
CREATE FUNCTION `valid_request_path`(path VARCHAR(255))
RETURNS TINYINT(1) NOT DETERMINISTIC READS SQL DATA
BEGIN
    SET @valid := (SELECT COUNT(*) = 0 AS valid FROM
        (SELECT CONCAT(COALESCE(se.in_development->>'$.developers', ''), h.name,
            se.url_context_root) as full_request_path
        FROM `mysql_rest_service_metadata`.service se
            LEFT JOIN `mysql_rest_service_metadata`.url_host h
                ON se.url_host_id = h.id
        WHERE CONCAT(COALESCE(se.in_development->>'$.developers', ''), h.name, se.url_context_root) = path
            AND se.enabled = TRUE
        UNION
        SELECT CONCAT(COALESCE(se.in_development->>'$.developers', ''), h.name, se.url_context_root,
            sc.request_path) as full_request_path
        FROM `mysql_rest_service_metadata`.db_schema sc
            LEFT OUTER JOIN `mysql_rest_service_metadata`.service se
                ON se.id = sc.service_id
            LEFT JOIN `mysql_rest_service_metadata`.url_host h
                ON se.url_host_id = h.id
        WHERE CONCAT(COALESCE(se.in_development->>'$.developers', ''), h.name, se.url_context_root,
                sc.request_path) = path
            AND se.enabled = TRUE
        UNION
        SELECT CONCAT(COALESCE(se.in_development->>'$.developers', ''), h.name, se.url_context_root,
            sc.request_path, o.request_path) as full_request_path
        FROM `mysql_rest_service_metadata`.db_object o
            LEFT OUTER JOIN `mysql_rest_service_metadata`.db_schema sc
                ON sc.id = o.db_schema_id
            LEFT OUTER JOIN `mysql_rest_service_metadata`.service se
                ON se.id = sc.service_id
            LEFT JOIN `mysql_rest_service_metadata`.url_host h
                ON se.url_host_id = h.id
        WHERE CONCAT(COALESCE(se.in_development->>'$.developers', ''), h.name, se.url_context_root,
                sc.request_path, o.request_path) = path
            AND se.enabled = TRUE
        UNION
        SELECT CONCAT(COALESCE(se.in_development->>'$.developers', ''), h.name, se.url_context_root,
            co.request_path) as full_request_path
        FROM `mysql_rest_service_metadata`.content_set co
            LEFT OUTER JOIN `mysql_rest_service_metadata`.service se
                ON se.id = co.service_id
            LEFT JOIN `mysql_rest_service_metadata`.url_host h
                ON se.url_host_id = h.id
        WHERE CONCAT(COALESCE(se.in_development->>'$.developers', ''), h.name, se.url_context_root,
                co.request_path) = path
            AND se.enabled = TRUE) AS p);

    RETURN @valid;
END%%


-- -----------------------------------------------------------------------------
-- CREATE PROCEDUREs
-- -----------------------------------------------------------------------------

DROP PROCEDURE IF EXISTS `dump_audit_log`%%
CREATE PROCEDURE `dump_audit_log`()
SQL SECURITY DEFINER
COMMENT 'The dump_audit_log procedure allows the audit_log table to be exported to a file
    Please note that the secure_file_priv global variable must be set for this to work in the my.ini / my.cnf file
    [mysqld]
    secure-file-priv="/usr/local/mysql/outfiles"'
BEGIN
    DECLARE dump_from TIMESTAMP;
    DECLARE dump_until TIMESTAMP;
    DECLARE event_count INT;

    -- Only perform the dump if the secure_file_priv global is set, otherwise the file cannot be written
    IF @@secure_file_priv IS NOT NULL THEN
        SELECT IFNULL(last_dump_at, '2025-01-01 00:00:00') INTO dump_from
        FROM `mysql_rest_service_metadata`.`audit_log_status`
        WHERE `id` = 1;

        SET dump_until = NOW();

        SELECT COUNT(*) INTO event_count
        FROM `mysql_rest_service_metadata`.`audit_log`
        WHERE `changed_at` BETWEEN dump_from AND dump_until;

        IF event_count > 0 THEN
            -- Export all audit_log entries that occurred since the last dump
            SET @sql = CONCAT(
                'SELECT JSON_OBJECT("changed_at", changed_at, "id", id, "server_uuid", @@server_uuid, ',
                '    "schema_name", schema_name, "table_name", table_name, "dm_type", dml_type, "changed_by", changed_by, '
                '    "old_row_data", JSON_REPLACE(old_row_data, "$.data.defaultStaticContent", "BINARY_DATA"), ',
                '    "new_row_data", JSON_REPLACE(new_row_data, "$.data.defaultStaticContent", "BINARY_DATA")) ',
                'INTO OUTFILE "', TRIM(TRAILING '/' FROM @@secure_file_priv), '/mrs/mrs_audit_log_',
                DATE_FORMAT(dump_until, '%Y-%m-%d_%H-%i-%s'),
                '.log" LINES TERMINATED BY "\\\n" ',
                'FROM `mysql_rest_service_metadata`.`audit_log` ',
                'WHERE `changed_at` BETWEEN CAST("', DATE_FORMAT(dump_from, '%Y-%m-%d %H:%i:%s'), '" AS DATETIME) ',
                '    AND CAST("', DATE_FORMAT(dump_until, '%Y-%m-%d %H:%i:%s'), '" AS DATETIME) ',
                'ORDER BY `id`');

            CALL sys.execute_prepared_stmt(@sql);
        END IF;

        UPDATE `mysql_rest_service_metadata`.`audit_log_status`
        SET `last_dump_at` = dump_until
        WHERE `id` = 1;
    ELSE
        SIGNAL SQLSTATE '45000'
            SET MESSAGE_TEXT = 'Please configure the secure-file-priv variable in the [mysqld] section of my.cnf.',
            MYSQL_ERRNO = 5400;
    END IF;
END%%

-- Procedure to fetch all table columns as well as references to related tables

DROP PROCEDURE IF EXISTS `table_columns_with_references`%%
CREATE PROCEDURE `table_columns_with_references`(
    p_schema_name VARCHAR(64), p_table_name VARCHAR(64))
BEGIN
    SELECT f.*, js.json_schema_def FROM (
        -- Get the table columns
        SELECT c.ORDINAL_POSITION AS position, c.COLUMN_NAME AS name,
            NULL AS ref_column_names,
            JSON_OBJECT(
                'name', c.COLUMN_NAME,
                'datatype', c.COLUMN_TYPE,
                'not_null', c.IS_NULLABLE = 'NO',
                'is_primary', c.COLUMN_KEY = 'PRI',
                'is_unique', c.COLUMN_KEY = 'UNI',
                'is_generated', c.GENERATION_EXPRESSION <> '',
                'id_generation', IF(c.EXTRA = 'auto_increment', 'auto_inc',
                    IF(c.COLUMN_KEY = 'PRI' AND c.DATA_TYPE = 'binary' AND c.CHARACTER_MAXIMUM_LENGTH = 16,
                        'rev_uuid', NULL)),
                'comment', c.COLUMN_COMMENT,
                'srid', c.SRS_ID,
                'column_default', c.COLUMN_DEFAULT,
                'charset', c.CHARACTER_SET_NAME,
                'collation', c.COLLATION_NAME
                ) AS db_column,
            NULL AS reference_mapping,
            c.TABLE_SCHEMA as table_schema, c.TABLE_NAME as table_name
        FROM INFORMATION_SCHEMA.COLUMNS AS c
            LEFT OUTER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS k
                ON c.TABLE_SCHEMA = k.TABLE_SCHEMA AND c.TABLE_NAME = k.TABLE_NAME
                    AND c.COLUMN_NAME=k.COLUMN_NAME
                    AND NOT ISNULL(k.POSITION_IN_UNIQUE_CONSTRAINT)
        WHERE c.TABLE_SCHEMA COLLATE utf8mb3_general_ci = p_schema_name AND c.TABLE_NAME COLLATE utf8mb3_general_ci = p_table_name
        -- Union with the references that point from the table to other tables (n:1)
        UNION
        SELECT MAX(c.ORDINAL_POSITION) + 100 AS position, MAX(k.REFERENCED_TABLE_NAME) AS name,
            GROUP_CONCAT(c.COLUMN_NAME SEPARATOR ', ') AS ref_column_names,
            NULL AS db_column,
            JSON_MERGE_PRESERVE(
                JSON_OBJECT('kind', 'n:1'),
                JSON_OBJECT('constraint',
                    CONCAT(MAX(k.CONSTRAINT_SCHEMA), '.', MAX(k.CONSTRAINT_NAME))),
                JSON_OBJECT('to_many', FALSE),
                JSON_OBJECT('referenced_schema', MAX(k.REFERENCED_TABLE_SCHEMA)),
                JSON_OBJECT('referenced_table', MAX(k.REFERENCED_TABLE_NAME)),
                JSON_OBJECT('column_mapping',
                    JSON_ARRAYAGG(JSON_OBJECT(
                        'base', c.COLUMN_NAME,
                        'ref', k.REFERENCED_COLUMN_NAME)))
            ) AS reference_mapping,
            MAX(c.TABLE_SCHEMA) AS table_schema, MAX(c.TABLE_NAME) AS table_name
        FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS k
            JOIN INFORMATION_SCHEMA.COLUMNS AS c
                ON c.TABLE_SCHEMA = k.TABLE_SCHEMA AND c.TABLE_NAME = k.TABLE_NAME
                    AND c.COLUMN_NAME=k.COLUMN_NAME
                    AND c.TABLE_SCHEMA COLLATE utf8mb3_general_ci = p_schema_name AND c.TABLE_NAME COLLATE utf8mb3_general_ci = p_table_name
        WHERE NOT ISNULL(k.REFERENCED_TABLE_NAME)
        GROUP BY k.CONSTRAINT_NAME, k.table_schema, k.table_name
        UNION
        -- Union with the references that point from other tables to the table (1:1 and 1:n)
        SELECT MAX(c.ORDINAL_POSITION) + 1000 AS position,
            MAX(c.TABLE_NAME) AS name,
            GROUP_CONCAT(k.COLUMN_NAME SEPARATOR ', ') AS ref_column_names,
            NULL AS db_column,
            JSON_MERGE_PRESERVE(
                -- If the PKs of the table and the referred table are exactly the same,
                -- this is a 1:1 relationship, otherwise an 1:n
                JSON_OBJECT('kind', IF(JSON_CONTAINS(MAX(PK_TABLE.PK), MAX(PK_REF.PK)) = 1,
                    '1:1', '1:n')),
                JSON_OBJECT('constraint',
                    CONCAT(MAX(k.CONSTRAINT_SCHEMA), '.', MAX(k.CONSTRAINT_NAME))),
                JSON_OBJECT('to_many', JSON_CONTAINS(MAX(PK_TABLE.PK), MAX(PK_REF.PK)) = 0),
                JSON_OBJECT('referenced_schema', MAX(c.TABLE_SCHEMA)),
                JSON_OBJECT('referenced_table', MAX(c.TABLE_NAME)),
                JSON_OBJECT('column_mapping',
                    JSON_ARRAYAGG(JSON_OBJECT(
                        'base', k.REFERENCED_COLUMN_NAME,
                        'ref', c.COLUMN_NAME)))
            ) AS reference_mapping,
            MAX(k.REFERENCED_TABLE_SCHEMA) AS table_schema,
            MAX(k.REFERENCED_TABLE_NAME) AS table_name
        FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS k
            JOIN INFORMATION_SCHEMA.COLUMNS AS c
                ON c.TABLE_SCHEMA = k.TABLE_SCHEMA AND c.TABLE_NAME = k.TABLE_NAME
                    AND c.COLUMN_NAME=k.COLUMN_NAME
            -- The PK columns of the table, e.g. ['test_fk.product.id']
            JOIN (SELECT JSON_ARRAYAGG(CONCAT(c2.TABLE_SCHEMA, '.',
                        c2.TABLE_NAME, '.', c2.COLUMN_NAME)) AS PK,
                    c2.TABLE_SCHEMA, c2.TABLE_NAME
                    FROM INFORMATION_SCHEMA.COLUMNS AS c2
                    WHERE c2.COLUMN_KEY = 'PRI'
                    GROUP BY c2.COLUMN_KEY, c2.TABLE_SCHEMA, c2.TABLE_NAME) AS PK_TABLE
                ON PK_TABLE.TABLE_SCHEMA = k.REFERENCED_TABLE_SCHEMA
                    AND PK_TABLE.TABLE_NAME = k.REFERENCED_TABLE_NAME
            -- The PK columns of the referenced table,
            -- e.g. ['test_fk.product_part.id', 'test_fk.product.id']
            JOIN (SELECT JSON_ARRAYAGG(PK2.PK_COL) AS PK, PK2.TABLE_SCHEMA, PK2.TABLE_NAME
                FROM (SELECT IFNULL(
                    CONCAT(MAX(k1.REFERENCED_TABLE_SCHEMA), '.',
                        MAX(k1.REFERENCED_TABLE_NAME), '.', MAX(k1.REFERENCED_COLUMN_NAME)),
                    CONCAT(c1.TABLE_SCHEMA, '.', c1.TABLE_NAME, '.', c1.COLUMN_NAME)) AS PK_COL,
                    c1.TABLE_SCHEMA AS TABLE_SCHEMA, c1.TABLE_NAME AS TABLE_NAME
                    FROM INFORMATION_SCHEMA.COLUMNS AS c1
                        JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS k1
                            ON k1.TABLE_SCHEMA = c1.TABLE_SCHEMA
                                AND k1.TABLE_NAME = c1.TABLE_NAME
                                AND k1.COLUMN_NAME = c1.COLUMN_NAME
                    WHERE c1.COLUMN_KEY = 'PRI'
                    GROUP BY c1.COLUMN_NAME, c1.TABLE_SCHEMA, c1.TABLE_NAME) AS PK2
                    GROUP BY PK2.TABLE_SCHEMA, PK2.TABLE_NAME) AS PK_REF
                ON PK_REF.TABLE_SCHEMA = k.TABLE_SCHEMA AND PK_REF.TABLE_NAME = k.TABLE_NAME
        WHERE k.REFERENCED_TABLE_SCHEMA COLLATE utf8mb3_general_ci = p_schema_name AND k.REFERENCED_TABLE_NAME COLLATE utf8mb3_general_ci = p_table_name
        GROUP BY k.CONSTRAINT_NAME, c.TABLE_SCHEMA, c.TABLE_NAME
        ) AS f
        -- LEFT JOIN with possible JSON_SCHEMA CHECK constraint for the given column
        LEFT OUTER JOIN (
            SELECT co.TABLE_SCHEMA, co.TABLE_NAME, co.COLUMN_NAME, MAX(co.JSON_SCHEMA_DEF) AS json_schema_def
            FROM (SELECT tc.TABLE_SCHEMA, tc.TABLE_NAME, TRIM('`' FROM TRIM(TRAILING ')' FROM
                    REGEXP_SUBSTR(REGEXP_SUBSTR(cc.CHECK_CLAUSE, 'json_schema_valid\s*\\(.*,\s*`[^`]*`\s*\\)'), '`[^`]*`\\)')
                    )) AS COLUMN_NAME,
                    tc.ENFORCED, cc.CONSTRAINT_NAME,
                    REPLACE(TRIM('\\''' FROM REGEXP_REPLACE(SUBSTRING(cc.CHECK_CLAUSE FROM LOCATE('{', cc.CHECK_CLAUSE)), '\s*,\s*`[^`]*`\\).*', '')), '\\\\n', '\n') AS JSON_SCHEMA_DEF
                FROM `information_schema`.`TABLE_CONSTRAINTS` AS tc
                    LEFT OUTER JOIN information_schema.CHECK_CONSTRAINTS AS cc
                        ON cc.CONSTRAINT_SCHEMA = tc.TABLE_SCHEMA AND cc.CONSTRAINT_NAME = tc.CONSTRAINT_NAME
                ) AS co
            WHERE co.COLUMN_NAME IS NOT NULL AND co.ENFORCED = 'YES' AND JSON_VALID(co.JSON_SCHEMA_DEF) AND co.TABLE_SCHEMA COLLATE utf8mb3_general_ci = p_schema_name AND co.TABLE_NAME COLLATE utf8mb3_general_ci = p_table_name
            GROUP BY co.TABLE_SCHEMA, co.TABLE_NAME, co.COLUMN_NAME) AS js
        ON f.TABLE_SCHEMA = js.TABLE_SCHEMA AND f.TABLE_NAME = js.TABLE_NAME AND f.name = js.COLUMN_NAME
    ORDER BY f.position;
END%%


-- sub-minute data is kept for 4h, then down-sampled to 1 minute samples
-- sub-hourly data is kept for 24h, then down-sampled to 1 hour samples
-- sub-daily data is kept for 7 days, then down-sampled to 1 day samples
-- daily data is kept indefinitely

DROP PROCEDURE IF EXISTS `router_status_downsample`%%
CREATE PROCEDURE `router_status_downsample`(
    time TIMESTAMP,
    router_version VARCHAR(12),
    status_variables JSON,
    target_interval CHAR)
    SQL SECURITY INVOKER
here:BEGIN
    DECLARE max_interval INT DEFAULT 1;
    DECLARE time_point_format VARCHAR(40) DEFAULT '';
    DECLARE done INT DEFAULT FALSE;
    DECLARE direct_columns TEXT DEFAULT '';
    DECLARE direct_query TEXT DEFAULT '';
    DECLARE details_query TEXT DEFAULT '';
    DECLARE before_time TIMESTAMP;
    DECLARE compress_older_than TIMESTAMP;
    DECLARE attr_name VARCHAR(80);
    DECLARE attr_non_resettable BOOLEAN;
    DECLARE attr_column VARCHAR(32);
    DECLARE cur1 CURSOR FOR SELECT vars.`name`, vars.`resettable`, vars.`column` FROM
        JSON_TABLE(status_variables,
         '$[*]' COLUMNS (
            `name` VARCHAR(80) PATH '$.name',
            `resettable` BOOLEAN PATH '$.nonResettable' DEFAULT 'false' ON EMPTY,
            `column` VARCHAR(32) PATH '$.column'
            )) vars;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
    DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN
        DROP TABLE IF EXISTS `aggregated`;
        RESIGNAL;
    END;

    -- target_interval must be either M (60s or 1min) H (60*60s or 1 hour) or D (24*60*60s or 1 day)
    IF target_interval = 'M' THEN
        SET max_interval = 60000;
        SET time_point_format = '%Y-%m-%d %H:%i:00';
        SET compress_older_than = DATE_SUB(time, INTERVAL 4 HOUR);
    ELSEIF target_interval = 'H' THEN
        SET max_interval = 60*60000;
        SET time_point_format = '%Y-%m-%d %H:00:00';
        SET compress_older_than = DATE_SUB(time, INTERVAL 24 HOUR);
    ELSEIF target_interval = 'D' THEN
        SET max_interval = 24*60*60000;
        SET time_point_format = '%Y-%m-%d 00:00:00';
        SET compress_older_than = DATE_SUB(time, INTERVAL 1 DAY);
    ELSE
        SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Invalid value for target_interval, must be M, H or D.', MYSQL_ERRNO = 5400;
    END IF;
    SET before_time = DATE_FORMAT(compress_older_than, time_point_format);

    START TRANSACTION;
    -- generate aggregation query
    OPEN cur1;

    SET details_query = '';
    variable_loop: LOOP
        FETCH cur1 INTO attr_name, attr_non_resettable, attr_column;
        IF done THEN
            LEAVE variable_loop;
        END IF;
        IF attr_non_resettable THEN
             IF attr_column IS NOT NULL THEN
                SET direct_query = CONCAT(direct_query, 'MAX(', attr_column, ') as ', attr_column, ', ');
                SET direct_columns = CONCAT(direct_columns, attr_column, ',');
            ELSE
                SET details_query = CONCAT(details_query, quote(attr_name), ',MAX(details->''$.', attr_name, '''), ');
            END IF;
        ELSE
            IF attr_column IS NOT NULL THEN
                SET direct_query = CONCAT(direct_query, 'SUM(', attr_column, ') as ', attr_column, ', ');
                SET direct_columns = CONCAT(direct_columns, attr_column, ',');
            ELSE
                SET details_query = CONCAT(details_query, quote(attr_name), ',SUM(details->''$.', attr_name, '''), ');
             END IF;
        END IF;
    END LOOP;
    SET details_query = CONCAT('JSON_OBJECT(', SUBSTR(details_query, 1, GREATEST(0, LENGTH(details_query)-2)), ') as details');

    CLOSE cur1;
    DROP TABLE IF EXISTS `aggregated`;
    CREATE TEMPORARY TABLE `aggregated` LIKE mysql_rest_service_metadata.router_status;

    -- aggregate rows with interval < target_interval at the same time
    SET @query = CONCAT('INSERT INTO `aggregated` ',
        '(id, router_id, `timespan`, status_time, ', direct_columns, '`details`)',
        ' SELECT min(rs.id) as id, rs.router_id, ', max_interval, ' as `timespan`, ',
        'DATE_FORMAT(rs.status_time, ', quote(time_point_format), ') as status_time_rounded, ',
        direct_query, details_query,
        ' FROM mysql_rest_service_metadata.router_status rs JOIN mysql_rest_service_metadata.router r ON rs.router_id=r.id WHERE r.version=', quote(router_version),
        ' AND rs.status_time < ', quote(before_time), ' AND rs.`timespan` < ', max_interval);
    SET @query = CONCAT(@query, ' GROUP BY rs.router_id, DATE_FORMAT(rs.status_time, ',quote(time_point_format),') ORDER BY status_time_rounded ASC');

    PREPARE stmt FROM @query;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;

    DELETE FROM mysql_rest_service_metadata.router_status r
        WHERE r.router_id=router_id AND r.status_time < before_time AND r.`timespan` < max_interval;

    INSERT INTO mysql_rest_service_metadata.router_status SELECT * FROM `aggregated`;

    COMMIT;
END%%

DROP PROCEDURE IF EXISTS `router_status_do_cleanup`%%
CREATE PROCEDURE `router_status_do_cleanup`(time TIMESTAMP)
    SQL SECURITY INVOKER
BEGIN
    DECLARE version VARCHAR(12);
    DECLARE old_status_variables JSON DEFAULT '[{"name":"httpRequestGet","column":"http_requests_get"},
{"name":"httpRequestPost","column":"http_requests_post"},
{"name":"httpRequestPut","column":"http_requests_put"},
{"name":"httpRequestDelete","column":"http_requests_delete"},
{"name":"kEntityCounterHttpRequestOptions"},
{"name":"httpConnectionsReused"},
{"name":"httpConnectionsCreated"},
{"name":"httpConnectionsClosed"},
{"name":"mysqlConnectionsReused"},
{"name":"mysqlConnectionsCreated","column":"mysql_connections"},
{"name":"mysqlConnectionsClosed"},
{"name":"mysqlConnectionsActive","column":"active_mysql_connections","nonResettable":true},
{"name":"mysqlQueries","column":"mysql_queries"},
{"name":"mysqlChangeUser"},
{"name":"mysqlPrepareStmt"},
{"name":"mysqlExecuteStmt"},
{"name":"mysqlRemoveStmt"},
{"name":"restReturnedItems"},
{"name":"restAffectedItems"},
{"name":"restCacheItemLoads"},
{"name":"restCacheItemEjects"},
{"name":"restCacheItemHits"},
{"name":"restCacheItemMisses"},
{"name":"restCachedItems","nonResettable":true},
{"name":"restCacheFileLoads"},
{"name":"restCacheFileEjects"},
{"name":"restCacheFileHits"},
{"name":"restCacheFileMisses"},
{"name":"restCachedFiles","nonResettable":true},
{"name":"restCachedEndpoints","nonResettable":true},
{"name":"changesHosts","nonResettable":true},
{"name":"changesServices","nonResettable":true},
{"name":"changesSchemas","nonResettable":true},
{"name":"changesContentSets","nonResettable":true},
{"name":"changesObjects","nonResettable":true},
{"name":"changesFiles","nonResettable":true},
{"name":"changesAuthentications","nonResettable":true},
{"name":"restAsofUsesRo"},
{"name":"restAsofUsesRw"},
{"name":"restAsofSwitchesFromRo2Rw"},
{"name":"restAsofNumberOfTimeouts"},
{"name":"restMetadataGtids"},
{"name":"sqlQueryTimeouts"}]';
    DECLARE status_variables JSON;
    DECLARE done INT DEFAULT FALSE;
    DECLARE cur1 CURSOR FOR SELECT router.version, JSON_EXTRACT(ANY_VALUE(router.attributes), '$.statusVariables')
        FROM mysql_rest_service_metadata.router
        GROUP BY router.version;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

    OPEN cur1;

    variable_loop: LOOP
        FETCH cur1 INTO version, status_variables;
        IF done THEN
            LEAVE variable_loop;
        END IF;
        IF status_variables IS NULL THEN
            SET status_variables = old_status_variables;
        END IF;
        CALL mysql_rest_service_metadata.router_status_downsample(time, version, status_variables, 'M');
        CALL mysql_rest_service_metadata.router_status_downsample(time, version, status_variables, 'H');
        CALL mysql_rest_service_metadata.router_status_downsample(time, version, status_variables, 'D');
    END LOOP;

    CLOSE cur1;
END%%


DROP PROCEDURE IF EXISTS `sdk_service_data`%%
CREATE PROCEDURE `sdk_service_data`(IN service_id BINARY(16))
BEGIN
    DECLARE service_res JSON;
    DECLARE schema_id BINARY(16);
    DECLARE schema_res JSON;

    -- Get all db_schemas of the given service, fetch the id to do the nested SELECTs and
    -- the data as JSON
    DECLARE schema_loop_done TINYINT DEFAULT FALSE;
    DECLARE schema_cursor CURSOR FOR
        SELECT s.id,
            JSON_OBJECT(
                'id', s.id,
                'name', s.name,
                'schema_type', s.schema_type,
                'request_path', s.request_path,
                'requires_auth', s.requires_auth,
                'internal', s.internal,
                'options', s.options
            )
        FROM mysql_rest_service_metadata.db_schema AS s
        WHERE s.service_id = service_id AND s.enabled = 1;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET schema_loop_done = 1;

    -- Get the service data as JSON
    SELECT
        JSON_OBJECT(
            'id', s.id,
            'url_context_root', s.url_context_root,
            'name', s.name,
            'enabled', s.enabled,
            'published', s.published,
            'options', s.options,
            'auth_path', s.auth_path,
            'auth_completed_url_validation', s.auth_completed_url_validation
        )
        INTO service_res
    FROM mysql_rest_service_metadata.service AS s
    WHERE s.id = service_id;

    -- Initiate the list of db_schemas with an empty JSON array
    SET service_res = JSON_SET(service_res, '$.db_schemas', json_array());

    -- Loop over all db_schema of the given service
    OPEN schema_cursor;
    schema_loop: LOOP
        -- Get the next db_schema of the service
        FETCH NEXT FROM schema_cursor INTO schema_id, schema_res;

        IF schema_loop_done THEN
            LEAVE schema_loop;
        ELSE schema_block: BEGIN
            -- Get all db_objects of the given db_schema, fetch the id to do the nested SELECTs and
            -- the data as JSON
            DECLARE db_object_id BINARY(16);
            DECLARE db_object_res JSON;
            DECLARE db_object_loop_done TINYINT DEFAULT FALSE;
            DECLARE db_object_cursor CURSOR FOR
                SELECT o.id,
                    JSON_OBJECT(
                        'id', o.id,
                        'name', o.name,
                        'request_path', o.request_path,
                        'internal', o.internal,
                        'object_type', o.object_type,
                        'crud_operations', o.crud_operations,
                        'format', o.format,
                        'requires_auth', o.requires_auth,
                        'options', o.options
                    )
                FROM mysql_rest_service_metadata.db_object AS o
                WHERE o.db_schema_id = schema_id AND o.enabled = 1;
            DECLARE CONTINUE HANDLER FOR NOT FOUND SET db_object_loop_done = 1;

            -- Initiate the list of db_objects with an empty JSON array
            SET schema_res = JSON_SET(schema_res, '$.db_objects', json_array());

            -- Loop over all db_objects of the given db_schema
            OPEN db_object_cursor;
            db_object_loop: LOOP
                FETCH NEXT FROM db_object_cursor INTO db_object_id, db_object_res;

                IF db_object_loop_done THEN
                    LEAVE db_object_loop;
                ELSE db_object_block: BEGIN
                    DECLARE object_id BINARY(16);
                    DECLARE object_res JSON;
                    DECLARE object_loop_done TINYINT DEFAULT FALSE;
                    DECLARE object_cursor CURSOR FOR
                        SELECT o.id,
                          JSON_OBJECT(
                            'id', o.id,
                            'db_object_id', o.db_object_id,
                            'name', name,
                            'kind', kind,
                            'position', position,
                            'row_ownership_field_id', row_ownership_field_id,
                            'options', options,
                            'sdk_options', sdk_options
                          )
                      FROM mysql_rest_service_metadata.object AS o
                      WHERE o.db_object_id = db_object_id;
                    DECLARE CONTINUE HANDLER FOR NOT FOUND SET object_loop_done = 1;

                    -- Initiate the list of objects with an empty JSON array
                    SET db_object_res = JSON_SET(db_object_res, '$.objects', json_array());

                    -- Loop over all SDK object instances of the given db_object
                    OPEN object_cursor;
                    object_loop: LOOP
                        FETCH NEXT FROM object_cursor INTO object_id, object_res;

                        IF object_loop_done THEN
                            LEAVE object_loop;
                        ELSE object_block: BEGIN
                            DECLARE field_id BINARY(16);
                            DECLARE field_res JSON;
                            DECLARE field_loop_done TINYINT DEFAULT FALSE;
                            DECLARE field_cursor CURSOR FOR
                                SELECT f.id,
                                    JSON_OBJECT(
                                        'caption', f.caption,
                                        'lev', f.lev,
                                        'position', f.position,
                                        'id', f.id,
                                        'represents_reference_id', f.represents_reference_id,
                                        'parent_reference_id', f.parent_reference_id,
                                        'object_id', f.object_id,
                                        'name', f.name,
                                        'db_column', f.db_column,
                                        'enabled', f.enabled,
                                        'allow_filtering', f.allow_filtering,
                                        'allow_sorting', f.allow_sorting,
                                        'no_check', f.no_check,
                                        'no_update', f.no_update,
                                        'options', f.options,
                                        'sdk_options', f.sdk_options,
                                        'object_reference', f.object_reference
                                    )
                                FROM mysql_rest_service_metadata.object_fields_with_references AS f
                                WHERE f.object_id = object_id;
                            DECLARE CONTINUE HANDLER FOR NOT FOUND SET field_loop_done = 1;

                            -- Initiate the list of fields with an empty JSON array
                            SET object_res = JSON_SET(object_res, '$.fields', json_array());

                            -- Loop over all fields of the given SDK object
                            OPEN field_cursor;
                            field_loop: LOOP
                                FETCH NEXT FROM field_cursor INTO field_id, field_res;

                                IF field_loop_done THEN
                                    LEAVE field_loop;
                                ELSE field_block: BEGIN
                                    -- Append the field JSON data to the object's fields array
                                    SET object_res = JSON_ARRAY_APPEND(object_res, '$.fields', field_res);
                                END field_block; END IF;
                            END LOOP field_loop;

                            -- Append the SDK object JSON data to the db_objects's objects array
                            SET db_object_res = JSON_ARRAY_APPEND(db_object_res, '$.objects', object_res);
                        END object_block; END IF;
                    END LOOP object_loop;

                    -- Append the db_object JSON data to the db_schema's db_objects array
                    SET schema_res = JSON_ARRAY_APPEND(schema_res, '$.db_objects', db_object_res);

                END db_object_block; END IF;
            END LOOP db_object_loop;

            -- Append the db_schema JSON data to the service's db_schemas array
            SET service_res = JSON_ARRAY_APPEND(service_res, '$.db_schemas', schema_res);

            CLOSE db_object_cursor;
        END schema_block; END IF;
    END LOOP schema_loop;

    CLOSE schema_cursor;

    -- Return the JSON data as a result set
    SELECT service_res;
END%%

DELIMITER ;

-- -----------------------------------------------------
-- TRIGGERs

DELIMITER %%

DROP TRIGGER IF EXISTS `router_AFTER_INSERT_AUDIT_LOG`%%
CREATE TRIGGER `router_AFTER_INSERT_AUDIT_LOG` AFTER INSERT ON `router` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "router",
        "INSERT",
        NULL,
        JSON_OBJECT(
            "id", NEW.id,
            "options", NEW.options),
        NULL,
        UNHEX(LPAD(CONV(NEW.id, 10, 16), 32, '0')),
        CURRENT_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `router_AFTER_UPDATE_AUDIT_LOG`%%
CREATE TRIGGER `router_AFTER_UPDATE_AUDIT_LOG` AFTER UPDATE ON `router` FOR EACH ROW
BEGIN
    IF (COALESCE(OLD.options, '') <> COALESCE(NEW.options, '')) THEN
        INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
            table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
        VALUES (
            "router",
            "UPDATE",
            JSON_OBJECT(
                "id", OLD.id,
                "options", OLD.options),
            JSON_OBJECT(
                "id", NEW.id,
                "options", NEW.options),
            UNHEX(LPAD(CONV(OLD.id, 10, 16), 32, '0')),
            UNHEX(LPAD(CONV(NEW.id, 10, 16), 32, '0')),
            CURRENT_USER(),
            CURRENT_TIMESTAMP
        );
    END IF;
END%%

DROP TRIGGER IF EXISTS `url_host_BEFORE_DELETE`%%
CREATE DEFINER = CURRENT_USER TRIGGER `url_host_BEFORE_DELETE` BEFORE DELETE ON `url_host` FOR EACH ROW
BEGIN
	DELETE FROM `mysql_rest_service_metadata`.`url_host_alias` WHERE `url_host_id` = OLD.`id`;
END%%

DROP TRIGGER IF EXISTS `service_BEFORE_INSERT`%%
CREATE DEFINER = CURRENT_USER TRIGGER `service_BEFORE_INSERT` BEFORE INSERT ON `service` FOR EACH ROW
BEGIN
    # Check if the full service request_path (including the optional developer setting) already exists
    IF NEW.enabled = TRUE THEN
        SET @host_name := (SELECT h.name FROM `mysql_rest_service_metadata`.url_host h WHERE h.id = NEW.url_host_id);
        SET @request_path := CONCAT(COALESCE(NEW.in_development->>'$.developers', ''), @host_name, NEW.url_context_root);
        SET @validPath := (SELECT `mysql_rest_service_metadata`.`valid_request_path`(@request_path));

        IF @validPath = 0 THEN
            SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = "The request_path is already used by another entity.";
        END IF;

        # Check if the same developer is already registered in the in_development->>'$.developers' of a service with the very same host_ctx
        SET @validDeveloperList := (SELECT MAX(COALESCE(
                JSON_OVERLAPS(s.in_development->>'$.developers', NEW.in_development->>'$.developers'), FALSE)) AS overlap
            FROM `mysql_rest_service_metadata`.`service` AS s JOIN
                `mysql_rest_service_metadata`.`url_host` AS h ON s.url_host_id = h.id JOIN
                `mysql_rest_service_metadata`.`url_host` AS h2 ON h2.id = NEW.url_host_id
            WHERE CONCAT(h.name, s.url_context_root) = CONCAT(h2.name, NEW.url_context_root) AND s.enabled = TRUE
            GROUP BY CONCAT(h.name, s.url_context_root));

        IF COALESCE(@validDeveloperList, FALSE) = TRUE THEN
            SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = "This developer is already registered for a REST service with the same host/url_context_root path.";
        END IF;
    END IF;

    IF NEW.in_development IS NOT NULL THEN
        SET NEW.published = 0;
    END IF;
END%%

DROP TRIGGER IF EXISTS `service_BEFORE_UPDATE`%%
CREATE DEFINER = CURRENT_USER TRIGGER `service_BEFORE_UPDATE` BEFORE UPDATE ON `service` FOR EACH ROW
BEGIN
    # Check if the full service request_path (including the optional developer setting) already exists,
    # but only when the service is enabled and either of those values was actually changed
    IF NEW.enabled = TRUE AND (COALESCE(NEW.in_development, '') <> COALESCE(OLD.in_development, '')
		OR NEW.url_host_id <> OLD.url_host_id OR NEW.url_context_root <> OLD.url_context_root) THEN

        SET @host_name := (SELECT h.name FROM `mysql_rest_service_metadata`.url_host h WHERE h.id = NEW.url_host_id);
        SET @request_path := CONCAT(COALESCE(NEW.in_development->>'$.developers', ''), @host_name, NEW.url_context_root);
        SET @validPath := (SELECT `mysql_rest_service_metadata`.`valid_request_path`(@request_path));

        IF @validPath = 0 THEN
            SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = "The request_path is already used by another entity.";
        END IF;

        # Check if the same developer is already registered in the in_development->>'$.developers' of a service with the very same host_ctx
        SET @validDeveloperList := (SELECT MAX(COALESCE(
                JSON_OVERLAPS(s.in_development->>'$.developers', NEW.in_development->>'$.developers'), FALSE)) AS overlap
            FROM `mysql_rest_service_metadata`.`service` AS s JOIN
                `mysql_rest_service_metadata`.`url_host` AS h ON s.url_host_id = h.id JOIN
                `mysql_rest_service_metadata`.`url_host` AS h2 ON h2.id = NEW.url_host_id
            WHERE CONCAT(h.name, s.url_context_root) = CONCAT(h2.name, NEW.url_context_root) AND s.enabled = TRUE
                AND s.id <> NEW.id
            GROUP BY CONCAT(h.name, s.url_context_root));

        IF COALESCE(@validDeveloperList, FALSE) = TRUE THEN
            SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = "This developer is already registered for a REST service with the same host/url_context_root path.";
        END IF;
    END IF;

    IF OLD.in_development IS NULL AND NEW.in_development IS NOT NULL AND NEW.published = 1 THEN
        SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = "A REST service that is in development cannot be published. Please reset the development state first.";
    END IF;
END%%

DROP TRIGGER IF EXISTS `service_BEFORE_DELETE`%%
CREATE DEFINER = CURRENT_USER TRIGGER `service_BEFORE_DELETE` BEFORE DELETE ON `service` FOR EACH ROW
BEGIN
	# Since FKs do not fire the triggers on the related tables, manually trigger the DELETEs
	DELETE FROM `mysql_rest_service_metadata`.`content_set` WHERE `service_id` = OLD.`id`;
	DELETE FROM `mysql_rest_service_metadata`.`db_schema` WHERE `service_id` = OLD.`id`;
    DELETE FROM `mysql_rest_service_metadata`.`service_has_auth_app` WHERE `service_id` = OLD.`id`;
    DELETE FROM `mysql_rest_service_metadata`.`mrs_role` WHERE `specific_to_service_id` = OLD.`id`;
    DELETE FROM `mysql_rest_service_metadata`.`mrs_user_hierarchy_type` WHERE `specific_to_service_id` = OLD.`id`;
    DELETE FROM `mysql_rest_service_metadata`.`mrs_user_group` WHERE `specific_to_service_id` = OLD.`id`;
END%%

DROP TRIGGER IF EXISTS `db_schema_BEFORE_INSERT`%%
CREATE DEFINER = CURRENT_USER TRIGGER `db_schema_BEFORE_INSERT` BEFORE INSERT ON `db_schema` FOR EACH ROW
BEGIN
	SET @service_path := (SELECT CONCAT(COALESCE(se.in_development->>'$.developers', ''), h.name, se.url_context_root) AS path
		FROM `mysql_rest_service_metadata`.service se
            LEFT JOIN `mysql_rest_service_metadata`.url_host h
                ON se.url_host_id = h.id
		WHERE se.id = NEW.service_id);
	SET @validPath := (SELECT `mysql_rest_service_metadata`.`valid_request_path`(CONCAT(@service_path, NEW.request_path)));

    IF @validPath = 0 THEN
		SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = "The request_path is already used by another entity.";
    END IF;
END%%

DROP TRIGGER IF EXISTS `db_schema_BEFORE_UPDATE`%%
CREATE DEFINER = CURRENT_USER TRIGGER `db_schema_BEFORE_UPDATE` BEFORE UPDATE ON `db_schema` FOR EACH ROW
BEGIN
	IF (NEW.request_path <> OLD.request_path OR NEW.service_id <> OLD.service_id) THEN
		SET @service_path := (SELECT CONCAT(COALESCE(se.in_development->>'$.developers', ''), h.name, se.url_context_root) AS path
			FROM `mysql_rest_service_metadata`.service se
				LEFT JOIN `mysql_rest_service_metadata`.url_host h
					ON se.url_host_id = h.id
			WHERE se.id = NEW.service_id);
		SET @validPath := (SELECT `mysql_rest_service_metadata`.`valid_request_path`(CONCAT(@service_path, NEW.request_path)));

		IF @validPath = 0 THEN
			SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = "The request_path is already used by another entity.";
		END IF;
    END IF;
END%%

DROP TRIGGER IF EXISTS `db_schema_BEFORE_DELETE`%%
CREATE DEFINER = CURRENT_USER TRIGGER `db_schema_BEFORE_DELETE` BEFORE DELETE ON `db_schema` FOR EACH ROW
BEGIN
	DELETE FROM `mysql_rest_service_metadata`.`db_object` WHERE `db_schema_id` = OLD.`id`;
END%%

DROP TRIGGER IF EXISTS `db_object_BEFORE_INSERT`%%
CREATE DEFINER = CURRENT_USER TRIGGER `db_object_BEFORE_INSERT` BEFORE INSERT ON `db_object` FOR EACH ROW
BEGIN
    SET @schema_path := (SELECT CONCAT(COALESCE(se.in_development->>'$.developers', ''), h.name, se.url_context_root, sc.request_path) AS path
        FROM `mysql_rest_service_metadata`.db_schema sc
            LEFT OUTER JOIN `mysql_rest_service_metadata`.service se
                ON se.id = sc.service_id
            LEFT JOIN `mysql_rest_service_metadata`.url_host h
                ON se.url_host_id = h.id
        WHERE sc.id = NEW.db_schema_id);
    SET @validPath := (SELECT `mysql_rest_service_metadata`.`valid_request_path`(CONCAT(@schema_path, NEW.request_path)));

    IF @validPath = 0 THEN
        SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = "The request_path is already used by another entity.";
    END IF;
END%%

DROP TRIGGER IF EXISTS `db_object_BEFORE_UPDATE`%%
CREATE DEFINER = CURRENT_USER TRIGGER `db_object_BEFORE_UPDATE` BEFORE UPDATE ON `db_object` FOR EACH ROW
BEGIN
    IF (NEW.request_path <> OLD.request_path OR NEW.db_schema_id <> OLD.db_schema_id) THEN
        SET @schema_path := (SELECT CONCAT(COALESCE(se.in_development->>'$.developers', ''), h.name, se.url_context_root, sc.request_path) AS path
            FROM `mysql_rest_service_metadata`.db_schema sc
                LEFT OUTER JOIN `mysql_rest_service_metadata`.service se
                    ON se.id = sc.service_id
                LEFT JOIN `mysql_rest_service_metadata`.url_host h
                    ON se.url_host_id = h.id
            WHERE sc.id = NEW.db_schema_id);
        SET @validPath := (SELECT `mysql_rest_service_metadata`.`valid_request_path`(CONCAT(@schema_path, NEW.request_path)));

        IF @validPath = 0 THEN
            SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = "The request_path is already used by another entity.";
        END IF;
    END IF;
END%%

DROP TRIGGER IF EXISTS `db_object_BEFORE_DELETE`%%
CREATE DEFINER = CURRENT_USER TRIGGER `db_object_BEFORE_DELETE` BEFORE DELETE ON `db_object` FOR EACH ROW
BEGIN
    DELETE FROM `mysql_rest_service_metadata`.`mrs_db_object_row_group_security` WHERE `db_object_id` = OLD.`id`;
    DELETE FROM `mysql_rest_service_metadata`.`object` WHERE `db_object_id` = OLD.`id`;
END%%

DROP TRIGGER IF EXISTS `auth_vendor_BEFORE_DELETE`%%
CREATE DEFINER = CURRENT_USER TRIGGER `auth_vendor_BEFORE_DELETE` BEFORE DELETE ON `auth_vendor` FOR EACH ROW
BEGIN
	DELETE FROM `mysql_rest_service_metadata`.`auth_app` WHERE `auth_vendor_id` = OLD.`id`;
END%%

DROP TRIGGER IF EXISTS `auth_app_BEFORE_DELETE`%%
CREATE DEFINER = CURRENT_USER TRIGGER `auth_app_BEFORE_DELETE` BEFORE DELETE ON `auth_app` FOR EACH ROW
BEGIN
	DELETE FROM `mysql_rest_service_metadata`.`mrs_user` WHERE `auth_app_id` = OLD.`id`;
END%%

DROP TRIGGER IF EXISTS `mrs_user_BEFORE_INSERT`%%
CREATE DEFINER = CURRENT_USER TRIGGER `mrs_user_BEFORE_INSERT` BEFORE INSERT ON `mrs_user` FOR EACH ROW
BEGIN
	IF NEW.name IS NOT NULL AND (SELECT COUNT(*) FROM `mysql_rest_service_metadata`.`mrs_user` AS u
		WHERE UPPER(u.name) = UPPER(NEW.name) AND u.auth_app_id = NEW.auth_app_id AND NEW.id <> u.id) > 0
	THEN
		SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = "This name has already been used.";
	END IF;
	IF NEW.email IS NOT NULL AND (SELECT COUNT(*) FROM `mysql_rest_service_metadata`.`mrs_user` AS u
		WHERE UPPER(u.email) = UPPER(NEW.email) AND u.auth_app_id = NEW.auth_app_id AND NEW.id <> u.id) > 0
	THEN
		SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = "This email has already been used.";
    END IF;
    IF (NEW.auth_string IS NULL AND
        (SELECT a.auth_vendor_id FROM `mysql_rest_service_metadata`.`auth_app` AS a WHERE a.id = NEW.auth_app_id) = 0x30000000000000000000000000000000)
    THEN
		SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = "A this account requires a password to be set.";
    END IF;
    IF JSON_STORAGE_SIZE(NEW.app_options) > 16384 THEN
		SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = "The JSON value stored in app_options must not be bigger than 16KB.";
    END IF;
END%%

DROP TRIGGER IF EXISTS `mrs_user_BEFORE_UPDATE`%%
CREATE DEFINER = CURRENT_USER TRIGGER `mrs_user_BEFORE_UPDATE` BEFORE UPDATE ON `mrs_user` FOR EACH ROW
BEGIN
	IF NEW.name IS NOT NULL AND (SELECT COUNT(*) FROM `mysql_rest_service_metadata`.`mrs_user` AS u
		WHERE UPPER(u.name) = UPPER(NEW.name) AND u.auth_app_id = NEW.auth_app_id AND NEW.id <> u.id) > 0
	THEN
		SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = "This name has already been used.";
	END IF;
	IF NEW.email IS NOT NULL AND (SELECT COUNT(*) FROM `mysql_rest_service_metadata`.`mrs_user` AS u
		WHERE UPPER(u.email) = UPPER(NEW.email) AND u.auth_app_id = NEW.auth_app_id AND NEW.id <> u.id) > 0
	THEN
		SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = "This email has already been used.";
    END IF;
    IF (NEW.auth_string IS NULL AND
        (SELECT a.auth_vendor_id FROM `mysql_rest_service_metadata`.`auth_app` AS a WHERE a.id = NEW.auth_app_id) = 0x30000000000000000000000000000000)
    THEN
		SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = "A this account requires a password to be set.";
    END IF;
    IF JSON_STORAGE_SIZE(NEW.app_options) > 16384 THEN
		SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = "The JSON value stored in app_options must not be bigger than 16KB.";
    END IF;
END%%

DROP TRIGGER IF EXISTS `mrs_user_BEFORE_DELETE`%%
CREATE DEFINER = CURRENT_USER TRIGGER `mrs_user_BEFORE_DELETE` BEFORE DELETE ON `mrs_user` FOR EACH ROW
BEGIN
	DELETE FROM `mysql_rest_service_metadata`.`mrs_user_hierarchy` WHERE `user_id` = OLD.`id` OR `reporting_to_user_id` = OLD.`id`;
    DELETE FROM `mysql_rest_service_metadata`.`mrs_user_has_role` WHERE `user_id` = OLD.`id`;
    DELETE FROM `mysql_rest_service_metadata`.`mrs_user_has_group` WHERE `user_id` = OLD.`id`;
END%%

DROP TRIGGER IF EXISTS `content_set_BEFORE_INSERT`%%
CREATE DEFINER = CURRENT_USER TRIGGER `content_set_BEFORE_INSERT` BEFORE INSERT ON `content_set` FOR EACH ROW
BEGIN
	SET @service_path := (SELECT CONCAT(COALESCE(se.in_development->>'$.developers', ''), h.name, se.url_context_root) AS path
		FROM `mysql_rest_service_metadata`.service se
            LEFT JOIN `mysql_rest_service_metadata`.url_host h
                ON se.url_host_id = h.id
		WHERE se.id = NEW.service_id);
	SET @validPath := (SELECT `mysql_rest_service_metadata`.`valid_request_path`(CONCAT(@service_path, NEW.request_path)));

    IF @validPath = 0 THEN
		SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = "The request_path is already used by another entity.";
    END IF;
END%%

DROP TRIGGER IF EXISTS `content_set_BEFORE_UPDATE`%%
CREATE DEFINER = CURRENT_USER TRIGGER `content_set_BEFORE_UPDATE` BEFORE UPDATE ON `content_set` FOR EACH ROW
BEGIN
	IF (NEW.request_path <> OLD.request_path OR NEW.service_id <> OLD.service_id) THEN
		SET @service_path := (SELECT CONCAT(COALESCE(se.in_development->>'$.developers', ''), h.name, se.url_context_root) AS path
			FROM `mysql_rest_service_metadata`.service se
				LEFT JOIN `mysql_rest_service_metadata`.url_host h
					ON se.url_host_id = h.id
			WHERE se.id = NEW.service_id);
		SET @validPath := (SELECT `mysql_rest_service_metadata`.`valid_request_path`(CONCAT(@service_path, NEW.request_path)));

		IF @validPath = 0 THEN
			SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = "The request_path is already used by another entity.";
		END IF;
    END IF;
END%%

DROP TRIGGER IF EXISTS `content_set_BEFORE_DELETE`%%
CREATE DEFINER = CURRENT_USER TRIGGER `content_set_BEFORE_DELETE` BEFORE DELETE ON `content_set` FOR EACH ROW
BEGIN
	DELETE FROM `mysql_rest_service_metadata`.`content_file`
	WHERE `content_set_id` = OLD.`id`;
	DELETE FROM `mysql_rest_service_metadata`.`content_set_has_obj_def`
	WHERE `content_set_id` = OLD.`id`;
END%%

DROP TRIGGER IF EXISTS `content_file_BEFORE_INSERT`%%
CREATE DEFINER = CURRENT_USER TRIGGER `content_file_BEFORE_INSERT` BEFORE INSERT ON `content_file` FOR EACH ROW
BEGIN
    SET @content_set_path := (SELECT CONCAT(h.name, se.url_context_root, co.request_path) AS path
        FROM `mysql_rest_service_metadata`.content_set co
            LEFT OUTER JOIN `mysql_rest_service_metadata`.service se
                ON se.id = co.service_id
            LEFT JOIN `mysql_rest_service_metadata`.url_host h
                ON se.url_host_id = h.id
        WHERE co.id = NEW.content_set_id);
    SET @validPath := (SELECT `mysql_rest_service_metadata`.`valid_request_path`(CONCAT(@content_set_path, NEW.request_path)));

    IF @validPath = 0 THEN
        SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = "The request_path is already used by another entity.";
    END IF;
END%%

DROP TRIGGER IF EXISTS `content_file_BEFORE_UPDATE`%%
CREATE DEFINER = CURRENT_USER TRIGGER `content_file_BEFORE_UPDATE` BEFORE UPDATE ON `content_file` FOR EACH ROW
BEGIN
    IF (NEW.request_path <> OLD.request_path OR NEW.content_set_id <> OLD.content_set_id) THEN
        SET @content_set_path := (SELECT CONCAT(h.name, se.url_context_root, co.request_path) AS path
            FROM `mysql_rest_service_metadata`.content_set co
                LEFT OUTER JOIN `mysql_rest_service_metadata`.service se
                    ON se.id = co.service_id
                LEFT JOIN `mysql_rest_service_metadata`.url_host h
                    ON se.url_host_id = h.id
            WHERE co.id = NEW.content_set_id);
        SET @validPath := (SELECT `mysql_rest_service_metadata`.`valid_request_path`(CONCAT(@content_set_path, NEW.request_path)));

        IF @validPath = 0 THEN
            SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = "The request_path is already used by another entity.";
        END IF;
    END IF;
END%%

DROP TRIGGER IF EXISTS `mrs_role_BEFORE_DELETE`%%
CREATE DEFINER = CURRENT_USER TRIGGER `mrs_role_BEFORE_DELETE` BEFORE DELETE ON `mrs_role` FOR EACH ROW
BEGIN
	DELETE FROM `mysql_rest_service_metadata`.`mrs_user_has_role` WHERE `role_id` = OLD.`id`;
    -- Workaround to fix issue with recursive delete
	IF OLD.id <> NULL THEN
		DELETE FROM `mysql_rest_service_metadata`.`mrs_role` WHERE `derived_from_role_id` = OLD.`id`;
	END IF;
    DELETE FROM `mysql_rest_service_metadata`.`mrs_privilege` WHERE `role_id` = OLD.`id`;
    DELETE FROM `mysql_rest_service_metadata`.`mrs_user_group_has_role` WHERE `role_id` = OLD.`id`;
END%%

DROP TRIGGER IF EXISTS `mrs_user_hierarchy_type_BEFORE_DELETE`%%
CREATE DEFINER = CURRENT_USER TRIGGER `mrs_user_hierarchy_type_BEFORE_DELETE` BEFORE DELETE ON `mrs_user_hierarchy_type` FOR EACH ROW
BEGIN
	DELETE FROM `mysql_rest_service_metadata`.`mrs_user_hierarchy` WHERE `user_hierarchy_type_id` = OLD.`id`;
END%%

DROP TRIGGER IF EXISTS `mrs_user_group_BEFORE_DELETE`%%
CREATE DEFINER = CURRENT_USER TRIGGER `mrs_user_group_BEFORE_DELETE` BEFORE DELETE ON `mrs_user_group` FOR EACH ROW
BEGIN
	DELETE FROM `mysql_rest_service_metadata`.`mrs_user_has_group` WHERE `user_group_id` = OLD.`id`;
    DELETE FROM `mysql_rest_service_metadata`.`mrs_user_group_hierarchy` WHERE `user_group_id` = OLD.`id` OR `parent_group_id` = OLD.`id`;
    DELETE FROM `mysql_rest_service_metadata`.`mrs_user_group_has_role` WHERE `user_group_id` = OLD.`id`;
END%%

DROP TRIGGER IF EXISTS `mrs_group_hierarchy_type_BEFORE_DELETE`%%
CREATE DEFINER = CURRENT_USER TRIGGER `mrs_group_hierarchy_type_BEFORE_DELETE` BEFORE DELETE ON `mrs_group_hierarchy_type` FOR EACH ROW
BEGIN
	DELETE FROM `mysql_rest_service_metadata`.`mrs_user_group_hierarchy` WHERE `group_hierarchy_type_id` = OLD.`id`;
    DELETE FROM `mysql_rest_service_metadata`.`mrs_db_object_row_group_security` WHERE `group_hierarchy_type_id` = OLD.`id`;
END%%

DROP TRIGGER IF EXISTS `router_BEFORE_DELETE`%%
CREATE DEFINER = CURRENT_USER TRIGGER `router_BEFORE_DELETE` BEFORE DELETE ON `router` FOR EACH ROW
BEGIN
	DELETE FROM `mysql_rest_service_metadata`.`router_status` WHERE `router_id` = OLD.`id`;
    DELETE FROM `mysql_rest_service_metadata`.`router_general_log` WHERE `router_id` = OLD.`id`;
END%%

DROP TRIGGER IF EXISTS `router_session_BEFORE_DELETE`%%
CREATE DEFINER = CURRENT_USER TRIGGER `router_session_BEFORE_DELETE` BEFORE DELETE ON `router_session` FOR EACH ROW
BEGIN
	DELETE FROM `mysql_rest_service_metadata`.`router_general_log` WHERE `router_session_id` = OLD.`id`;
END%%

DROP TRIGGER IF EXISTS `object_BEFORE_DELETE`%%
CREATE DEFINER = CURRENT_USER TRIGGER `object_BEFORE_DELETE` BEFORE DELETE ON `object` FOR EACH ROW
BEGIN
	DELETE FROM `mysql_rest_service_metadata`.`object_field` WHERE `object_id` = OLD.`id`;
END%%

DROP TRIGGER IF EXISTS `object_field_BEFORE_DELETE`%%
CREATE DEFINER = CURRENT_USER TRIGGER `object_field_BEFORE_DELETE` BEFORE DELETE ON `object_field` FOR EACH ROW
BEGIN
	SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
	DELETE FROM `mysql_rest_service_metadata`.`object_reference` WHERE `id` = OLD.`represents_reference_id`;
    SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
END%%

DROP TRIGGER IF EXISTS `content_set_has_obj_def_AFTER_DELETE`%%
CREATE DEFINER = CURRENT_USER TRIGGER `content_set_has_obj_def_AFTER_DELETE` AFTER DELETE ON `content_set_has_obj_def` FOR EACH ROW
BEGIN
	DELETE FROM `mysql_rest_service_metadata`.`db_object` dbo
    WHERE OLD.kind = "Script" AND dbo.id = OLD.db_object_id;
END%%

DELIMITER ;

-- -----------------------------------------------------
-- Create audit_log triggers

DELIMITER %%

DROP TRIGGER IF EXISTS `db_schema_AFTER_INSERT_AUDIT_LOG`%%
CREATE TRIGGER `db_schema_AFTER_INSERT_AUDIT_LOG`
    AFTER INSERT ON `db_schema` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "db_schema",
        "INSERT",
        NULL,
        JSON_OBJECT(
            "id", NEW.id,
            "service_id", NEW.service_id,
            "name", NEW.name,
            "schema_type", NEW.schema_type,
            "request_path", NEW.request_path,
            "requires_auth", NEW.requires_auth,
            "enabled", NEW.enabled,
            "internal", NEW.internal,
            "items_per_page", NEW.items_per_page,
            "comments", NEW.comments,
            "options", NEW.options,
            "metadata", NEW.metadata),
        NULL,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `db_schema_AFTER_UPDATE_AUDIT_LOG`%%
CREATE TRIGGER `db_schema_AFTER_UPDATE_AUDIT_LOG`
    AFTER UPDATE ON `db_schema` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "db_schema",
        "UPDATE",
        JSON_OBJECT(
            "id", OLD.id,
            "service_id", OLD.service_id,
            "name", OLD.name,
            "schema_type", OLD.schema_type,
            "request_path", OLD.request_path,
            "requires_auth", OLD.requires_auth,
            "enabled", OLD.enabled,
            "internal", OLD.internal,
            "items_per_page", OLD.items_per_page,
            "comments", OLD.comments,
            "options", OLD.options,
            "metadata", OLD.metadata),
        JSON_OBJECT(
            "id", NEW.id,
            "service_id", NEW.service_id,
            "name", NEW.name,
            "schema_type", NEW.schema_type,
            "request_path", NEW.request_path,
            "requires_auth", NEW.requires_auth,
            "enabled", NEW.enabled,
            "internal", NEW.internal,
            "items_per_page", NEW.items_per_page,
            "comments", NEW.comments,
            "options", NEW.options,
            "metadata", NEW.metadata),
        OLD.id,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `db_schema_AFTER_DELETE_AUDIT_LOG`%%
CREATE TRIGGER `db_schema_AFTER_DELETE_AUDIT_LOG`
    AFTER DELETE ON `db_schema` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "db_schema",
        "DELETE",
        JSON_OBJECT(
            "id", OLD.id,
            "service_id", OLD.service_id,
            "name", OLD.name,
            "schema_type", OLD.schema_type,
            "request_path", OLD.request_path,
            "requires_auth", OLD.requires_auth,
            "enabled", OLD.enabled,
            "internal", OLD.internal,
            "items_per_page", OLD.items_per_page,
            "comments", OLD.comments,
            "options", OLD.options,
            "metadata", OLD.metadata),
        NULL,
        OLD.id,
        NULL,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `service_AFTER_INSERT_AUDIT_LOG`%%
CREATE TRIGGER `service_AFTER_INSERT_AUDIT_LOG`
    AFTER INSERT ON `service` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "service",
        "INSERT",
        NULL,
        JSON_OBJECT(
            "id", NEW.id,
            "parent_id", NEW.parent_id,
            "url_host_id", NEW.url_host_id,
            "url_context_root", NEW.url_context_root,
            "url_protocol", NEW.url_protocol,
            "name", NEW.name,
            "enabled", NEW.enabled,
            "published", NEW.published,
            "in_development", NEW.in_development,
            "comments", NEW.comments,
            "options", NEW.options,
            "auth_path", NEW.auth_path,
            "auth_completed_url", NEW.auth_completed_url,
            "auth_completed_url_validation", NEW.auth_completed_url_validation,
            "enable_sql_endpoint", NEW.enable_sql_endpoint,
            "custom_metadata_schema", NEW.custom_metadata_schema,
            "metadata", NEW.metadata),
        NULL,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `service_AFTER_UPDATE_AUDIT_LOG`%%
CREATE TRIGGER `service_AFTER_UPDATE_AUDIT_LOG`
    AFTER UPDATE ON `service` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "service",
        "UPDATE",
        JSON_OBJECT(
            "id", OLD.id,
            "parent_id", OLD.parent_id,
            "url_host_id", OLD.url_host_id,
            "url_context_root", OLD.url_context_root,
            "url_protocol", OLD.url_protocol,
            "name", OLD.name,
            "enabled", OLD.enabled,
            "published", OLD.published,
            "in_development", OLD.in_development,
            "comments", OLD.comments,
            "options", OLD.options,
            "auth_path", OLD.auth_path,
            "auth_completed_url", OLD.auth_completed_url,
            "auth_completed_url_validation", OLD.auth_completed_url_validation,
            "enable_sql_endpoint", OLD.enable_sql_endpoint,
            "custom_metadata_schema", OLD.custom_metadata_schema,
            "metadata", OLD.metadata),
        JSON_OBJECT(
            "id", NEW.id,
            "parent_id", NEW.parent_id,
            "url_host_id", NEW.url_host_id,
            "url_context_root", NEW.url_context_root,
            "url_protocol", NEW.url_protocol,
            "name", NEW.name,
            "enabled", NEW.enabled,
            "published", NEW.published,
            "in_development", NEW.in_development,
            "comments", NEW.comments,
            "options", NEW.options,
            "auth_path", NEW.auth_path,
            "auth_completed_url", NEW.auth_completed_url,
            "auth_completed_url_validation", NEW.auth_completed_url_validation,
            "enable_sql_endpoint", NEW.enable_sql_endpoint,
            "custom_metadata_schema", NEW.custom_metadata_schema,
            "metadata", NEW.metadata),
        OLD.id,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `service_AFTER_DELETE_AUDIT_LOG`%%
CREATE TRIGGER `service_AFTER_DELETE_AUDIT_LOG`
    AFTER DELETE ON `service` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "service",
        "DELETE",
        JSON_OBJECT(
            "id", OLD.id,
            "parent_id", OLD.parent_id,
            "url_host_id", OLD.url_host_id,
            "url_context_root", OLD.url_context_root,
            "url_protocol", OLD.url_protocol,
            "name", OLD.name,
            "enabled", OLD.enabled,
            "published", OLD.published,
            "in_development", OLD.in_development,
            "comments", OLD.comments,
            "options", OLD.options,
            "auth_path", OLD.auth_path,
            "auth_completed_url", OLD.auth_completed_url,
            "auth_completed_url_validation", OLD.auth_completed_url_validation,
            "enable_sql_endpoint", OLD.enable_sql_endpoint,
            "custom_metadata_schema", OLD.custom_metadata_schema,
            "metadata", OLD.metadata),
        NULL,
        OLD.id,
        NULL,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `db_object_AFTER_INSERT_AUDIT_LOG`%%
CREATE TRIGGER `db_object_AFTER_INSERT_AUDIT_LOG`
    AFTER INSERT ON `db_object` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "db_object",
        "INSERT",
        NULL,
        JSON_OBJECT(
            "id", NEW.id,
            "db_schema_id", NEW.db_schema_id,
            "name", NEW.name,
            "request_path", NEW.request_path,
            "enabled", NEW.enabled,
            "internal", NEW.internal,
            "object_type", NEW.object_type,
            "crud_operations", NEW.crud_operations,
            "format", NEW.format,
            "items_per_page", NEW.items_per_page,
            "media_type", NEW.media_type,
            "auto_detect_media_type", NEW.auto_detect_media_type,
            "requires_auth", NEW.requires_auth,
            "auth_stored_procedure", NEW.auth_stored_procedure,
            "options", NEW.options,
            "details", NEW.details,
            "comments", NEW.comments,
            "metadata", NEW.metadata),
        NULL,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `db_object_AFTER_UPDATE_AUDIT_LOG`%%
CREATE TRIGGER `db_object_AFTER_UPDATE_AUDIT_LOG`
    AFTER UPDATE ON `db_object` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "db_object",
        "UPDATE",
        JSON_OBJECT(
            "id", OLD.id,
            "db_schema_id", OLD.db_schema_id,
            "name", OLD.name,
            "request_path", OLD.request_path,
            "enabled", OLD.enabled,
            "internal", OLD.internal,
            "object_type", OLD.object_type,
            "crud_operations", OLD.crud_operations,
            "format", OLD.format,
            "items_per_page", OLD.items_per_page,
            "media_type", OLD.media_type,
            "auto_detect_media_type", OLD.auto_detect_media_type,
            "requires_auth", OLD.requires_auth,
            "auth_stored_procedure", OLD.auth_stored_procedure,
            "options", OLD.options,
            "details", OLD.details,
            "comments", OLD.comments,
            "metadata", OLD.metadata),
        JSON_OBJECT(
            "id", NEW.id,
            "db_schema_id", NEW.db_schema_id,
            "name", NEW.name,
            "request_path", NEW.request_path,
            "enabled", NEW.enabled,
            "internal", NEW.internal,
            "object_type", NEW.object_type,
            "crud_operations", NEW.crud_operations,
            "format", NEW.format,
            "items_per_page", NEW.items_per_page,
            "media_type", NEW.media_type,
            "auto_detect_media_type", NEW.auto_detect_media_type,
            "requires_auth", NEW.requires_auth,
            "auth_stored_procedure", NEW.auth_stored_procedure,
            "options", NEW.options,
            "details", NEW.details,
            "comments", NEW.comments,
            "metadata", NEW.metadata),
        OLD.id,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `db_object_AFTER_DELETE_AUDIT_LOG`%%
CREATE TRIGGER `db_object_AFTER_DELETE_AUDIT_LOG`
    AFTER DELETE ON `db_object` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "db_object",
        "DELETE",
        JSON_OBJECT(
            "id", OLD.id,
            "db_schema_id", OLD.db_schema_id,
            "name", OLD.name,
            "request_path", OLD.request_path,
            "enabled", OLD.enabled,
            "internal", OLD.internal,
            "object_type", OLD.object_type,
            "crud_operations", OLD.crud_operations,
            "format", OLD.format,
            "items_per_page", OLD.items_per_page,
            "media_type", OLD.media_type,
            "auto_detect_media_type", OLD.auto_detect_media_type,
            "requires_auth", OLD.requires_auth,
            "auth_stored_procedure", OLD.auth_stored_procedure,
            "options", OLD.options,
            "details", OLD.details,
            "comments", OLD.comments,
            "metadata", OLD.metadata),
        NULL,
        OLD.id,
        NULL,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_user_AFTER_INSERT_AUDIT_LOG`%%
CREATE TRIGGER `mrs_user_AFTER_INSERT_AUDIT_LOG`
    AFTER INSERT ON `mrs_user` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_user",
        "INSERT",
        NULL,
        JSON_OBJECT(
            "id", NEW.id,
            "auth_app_id", NEW.auth_app_id,
            "name", NEW.name,
            "email", NEW.email,
            "vendor_user_id", NEW.vendor_user_id,
            "login_permitted", NEW.login_permitted,
            "mapped_user_id", NEW.mapped_user_id,
            "app_options", NEW.app_options,
            "options", NEW.options),
        NULL,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_user_AFTER_UPDATE_AUDIT_LOG`%%
CREATE TRIGGER `mrs_user_AFTER_UPDATE_AUDIT_LOG`
    AFTER UPDATE ON `mrs_user` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_user",
        "UPDATE",
        JSON_OBJECT(
            "id", OLD.id,
            "auth_app_id", OLD.auth_app_id,
            "name", OLD.name,
            "email", OLD.email,
            "vendor_user_id", OLD.vendor_user_id,
            "login_permitted", OLD.login_permitted,
            "mapped_user_id", OLD.mapped_user_id,
            "app_options", OLD.app_options,
            "options", OLD.options),
        JSON_OBJECT(
            "id", NEW.id,
            "auth_app_id", NEW.auth_app_id,
            "name", NEW.name,
            "email", NEW.email,
            "vendor_user_id", NEW.vendor_user_id,
            "login_permitted", NEW.login_permitted,
            "mapped_user_id", NEW.mapped_user_id,
            "app_options", NEW.app_options,
            "options", NEW.options),
        OLD.id,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_user_AFTER_DELETE_AUDIT_LOG`%%
CREATE TRIGGER `mrs_user_AFTER_DELETE_AUDIT_LOG`
    AFTER DELETE ON `mrs_user` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_user",
        "DELETE",
        JSON_OBJECT(
            "id", OLD.id,
            "auth_app_id", OLD.auth_app_id,
            "name", OLD.name,
            "email", OLD.email,
            "vendor_user_id", OLD.vendor_user_id,
            "login_permitted", OLD.login_permitted,
            "mapped_user_id", OLD.mapped_user_id,
            "app_options", OLD.app_options,
            "options", OLD.options),
        NULL,
        OLD.id,
        NULL,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `auth_vendor_AFTER_INSERT_AUDIT_LOG`%%
CREATE TRIGGER `auth_vendor_AFTER_INSERT_AUDIT_LOG`
    AFTER INSERT ON `auth_vendor` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "auth_vendor",
        "INSERT",
        NULL,
        JSON_OBJECT(
            "id", NEW.id,
            "name", NEW.name,
            "validation_url", NEW.validation_url,
            "enabled", NEW.enabled,
            "comments", NEW.comments,
            "options", NEW.options),
        NULL,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `auth_vendor_AFTER_UPDATE_AUDIT_LOG`%%
CREATE TRIGGER `auth_vendor_AFTER_UPDATE_AUDIT_LOG`
    AFTER UPDATE ON `auth_vendor` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "auth_vendor",
        "UPDATE",
        JSON_OBJECT(
            "id", OLD.id,
            "name", OLD.name,
            "validation_url", OLD.validation_url,
            "enabled", OLD.enabled,
            "comments", OLD.comments,
            "options", OLD.options),
        JSON_OBJECT(
            "id", NEW.id,
            "name", NEW.name,
            "validation_url", NEW.validation_url,
            "enabled", NEW.enabled,
            "comments", NEW.comments,
            "options", NEW.options),
        OLD.id,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `auth_vendor_AFTER_DELETE_AUDIT_LOG`%%
CREATE TRIGGER `auth_vendor_AFTER_DELETE_AUDIT_LOG`
    AFTER DELETE ON `auth_vendor` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "auth_vendor",
        "DELETE",
        JSON_OBJECT(
            "id", OLD.id,
            "name", OLD.name,
            "validation_url", OLD.validation_url,
            "enabled", OLD.enabled,
            "comments", OLD.comments,
            "options", OLD.options),
        NULL,
        OLD.id,
        NULL,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `auth_app_AFTER_INSERT_AUDIT_LOG`%%
CREATE TRIGGER `auth_app_AFTER_INSERT_AUDIT_LOG`
    AFTER INSERT ON `auth_app` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "auth_app",
        "INSERT",
        NULL,
        JSON_OBJECT(
            "id", NEW.id,
            "auth_vendor_id", NEW.auth_vendor_id,
            "name", NEW.name,
            "description", NEW.description,
            "url", NEW.url,
            "url_direct_auth", NEW.url_direct_auth,
            "access_token", NEW.access_token,
            "app_id", NEW.app_id,
            "enabled", NEW.enabled,
            "limit_to_registered_users", NEW.limit_to_registered_users,
            "default_role_id", NEW.default_role_id,
            "options", NEW.options),
        NULL,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `auth_app_AFTER_UPDATE_AUDIT_LOG`%%
CREATE TRIGGER `auth_app_AFTER_UPDATE_AUDIT_LOG`
    AFTER UPDATE ON `auth_app` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "auth_app",
        "UPDATE",
        JSON_OBJECT(
            "id", OLD.id,
            "auth_vendor_id", OLD.auth_vendor_id,
            "name", OLD.name,
            "description", OLD.description,
            "url", OLD.url,
            "url_direct_auth", OLD.url_direct_auth,
            "access_token", OLD.access_token,
            "app_id", OLD.app_id,
            "enabled", OLD.enabled,
            "limit_to_registered_users", OLD.limit_to_registered_users,
            "default_role_id", OLD.default_role_id,
            "options", OLD.options),
        JSON_OBJECT(
            "id", NEW.id,
            "auth_vendor_id", NEW.auth_vendor_id,
            "name", NEW.name,
            "description", NEW.description,
            "url", NEW.url,
            "url_direct_auth", NEW.url_direct_auth,
            "access_token", NEW.access_token,
            "app_id", NEW.app_id,
            "enabled", NEW.enabled,
            "limit_to_registered_users", NEW.limit_to_registered_users,
            "default_role_id", NEW.default_role_id,
            "options", NEW.options),
        OLD.id,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `auth_app_AFTER_DELETE_AUDIT_LOG`%%
CREATE TRIGGER `auth_app_AFTER_DELETE_AUDIT_LOG`
    AFTER DELETE ON `auth_app` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "auth_app",
        "DELETE",
        JSON_OBJECT(
            "id", OLD.id,
            "auth_vendor_id", OLD.auth_vendor_id,
            "name", OLD.name,
            "description", OLD.description,
            "url", OLD.url,
            "url_direct_auth", OLD.url_direct_auth,
            "access_token", OLD.access_token,
            "app_id", OLD.app_id,
            "enabled", OLD.enabled,
            "limit_to_registered_users", OLD.limit_to_registered_users,
            "default_role_id", OLD.default_role_id,
            "options", OLD.options),
        NULL,
        OLD.id,
        NULL,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `config_AFTER_INSERT_AUDIT_LOG`%%
CREATE TRIGGER `config_AFTER_INSERT_AUDIT_LOG`
    AFTER INSERT ON `config` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "config",
        "INSERT",
        NULL,
        JSON_OBJECT(
            "id", NEW.id,
            "service_enabled", NEW.service_enabled,
            "data", NEW.data),
        NULL,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `config_AFTER_UPDATE_AUDIT_LOG`%%
CREATE TRIGGER `config_AFTER_UPDATE_AUDIT_LOG`
    AFTER UPDATE ON `config` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "config",
        "UPDATE",
        JSON_OBJECT(
            "id", OLD.id,
            "service_enabled", OLD.service_enabled,
            "data", OLD.data),
        JSON_OBJECT(
            "id", NEW.id,
            "service_enabled", NEW.service_enabled,
            "data", NEW.data),
        OLD.id,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `config_AFTER_DELETE_AUDIT_LOG`%%
CREATE TRIGGER `config_AFTER_DELETE_AUDIT_LOG`
    AFTER DELETE ON `config` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "config",
        "DELETE",
        JSON_OBJECT(
            "id", OLD.id,
            "service_enabled", OLD.service_enabled,
            "data", OLD.data),
        NULL,
        OLD.id,
        NULL,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `redirect_AFTER_INSERT_AUDIT_LOG`%%
CREATE TRIGGER `redirect_AFTER_INSERT_AUDIT_LOG`
    AFTER INSERT ON `redirect` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "redirect",
        "INSERT",
        NULL,
        JSON_OBJECT(
            "id", NEW.id,
            "pattern", NEW.pattern,
            "target", NEW.target,
            "kind", NEW.kind,
            "in_development", NEW.in_development),
        NULL,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `redirect_AFTER_UPDATE_AUDIT_LOG`%%
CREATE TRIGGER `redirect_AFTER_UPDATE_AUDIT_LOG`
    AFTER UPDATE ON `redirect` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "redirect",
        "UPDATE",
        JSON_OBJECT(
            "id", OLD.id,
            "pattern", OLD.pattern,
            "target", OLD.target,
            "kind", OLD.kind,
            "in_development", OLD.in_development),
        JSON_OBJECT(
            "id", NEW.id,
            "pattern", NEW.pattern,
            "target", NEW.target,
            "kind", NEW.kind,
            "in_development", NEW.in_development),
        OLD.id,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `redirect_AFTER_DELETE_AUDIT_LOG`%%
CREATE TRIGGER `redirect_AFTER_DELETE_AUDIT_LOG`
    AFTER DELETE ON `redirect` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "redirect",
        "DELETE",
        JSON_OBJECT(
            "id", OLD.id,
            "pattern", OLD.pattern,
            "target", OLD.target,
            "kind", OLD.kind,
            "in_development", OLD.in_development),
        NULL,
        OLD.id,
        NULL,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `url_host_alias_AFTER_INSERT_AUDIT_LOG`%%
CREATE TRIGGER `url_host_alias_AFTER_INSERT_AUDIT_LOG`
    AFTER INSERT ON `url_host_alias` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "url_host_alias",
        "INSERT",
        NULL,
        JSON_OBJECT(
            "id", NEW.id,
            "url_host_id", NEW.url_host_id,
            "alias", NEW.alias),
        NULL,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `url_host_alias_AFTER_UPDATE_AUDIT_LOG`%%
CREATE TRIGGER `url_host_alias_AFTER_UPDATE_AUDIT_LOG`
    AFTER UPDATE ON `url_host_alias` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "url_host_alias",
        "UPDATE",
        JSON_OBJECT(
            "id", OLD.id,
            "url_host_id", OLD.url_host_id,
            "alias", OLD.alias),
        JSON_OBJECT(
            "id", NEW.id,
            "url_host_id", NEW.url_host_id,
            "alias", NEW.alias),
        OLD.id,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `url_host_alias_AFTER_DELETE_AUDIT_LOG`%%
CREATE TRIGGER `url_host_alias_AFTER_DELETE_AUDIT_LOG`
    AFTER DELETE ON `url_host_alias` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "url_host_alias",
        "DELETE",
        JSON_OBJECT(
            "id", OLD.id,
            "url_host_id", OLD.url_host_id,
            "alias", OLD.alias),
        NULL,
        OLD.id,
        NULL,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `url_host_AFTER_INSERT_AUDIT_LOG`%%
CREATE TRIGGER `url_host_AFTER_INSERT_AUDIT_LOG`
    AFTER INSERT ON `url_host` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "url_host",
        "INSERT",
        NULL,
        JSON_OBJECT(
            "id", NEW.id,
            "name", NEW.name,
            "comments", NEW.comments),
        NULL,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `url_host_AFTER_UPDATE_AUDIT_LOG`%%
CREATE TRIGGER `url_host_AFTER_UPDATE_AUDIT_LOG`
    AFTER UPDATE ON `url_host` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "url_host",
        "UPDATE",
        JSON_OBJECT(
            "id", OLD.id,
            "name", OLD.name,
            "comments", OLD.comments),
        JSON_OBJECT(
            "id", NEW.id,
            "name", NEW.name,
            "comments", NEW.comments),
        OLD.id,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `url_host_AFTER_DELETE_AUDIT_LOG`%%
CREATE TRIGGER `url_host_AFTER_DELETE_AUDIT_LOG`
    AFTER DELETE ON `url_host` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "url_host",
        "DELETE",
        JSON_OBJECT(
            "id", OLD.id,
            "name", OLD.name,
            "comments", OLD.comments),
        NULL,
        OLD.id,
        NULL,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `content_file_AFTER_INSERT_AUDIT_LOG`%%
CREATE TRIGGER `content_file_AFTER_INSERT_AUDIT_LOG`
    AFTER INSERT ON `content_file` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "content_file",
        "INSERT",
        NULL,
        JSON_OBJECT(
            "id", NEW.id,
            "content_set_id", NEW.content_set_id,
            "request_path", NEW.request_path,
            "requires_auth", NEW.requires_auth,
            "enabled", NEW.enabled,
            "size", NEW.size,
            "options", NEW.options),
        NULL,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `content_file_AFTER_UPDATE_AUDIT_LOG`%%
CREATE TRIGGER `content_file_AFTER_UPDATE_AUDIT_LOG`
    AFTER UPDATE ON `content_file` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "content_file",
        "UPDATE",
        JSON_OBJECT(
            "id", OLD.id,
            "content_set_id", OLD.content_set_id,
            "request_path", OLD.request_path,
            "requires_auth", OLD.requires_auth,
            "enabled", OLD.enabled,
            "size", OLD.size,
            "options", OLD.options),
        JSON_OBJECT(
            "id", NEW.id,
            "content_set_id", NEW.content_set_id,
            "request_path", NEW.request_path,
            "requires_auth", NEW.requires_auth,
            "enabled", NEW.enabled,
            "size", NEW.size,
            "options", NEW.options),
        OLD.id,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `content_file_AFTER_DELETE_AUDIT_LOG`%%
CREATE TRIGGER `content_file_AFTER_DELETE_AUDIT_LOG`
    AFTER DELETE ON `content_file` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "content_file",
        "DELETE",
        JSON_OBJECT(
            "id", OLD.id,
            "content_set_id", OLD.content_set_id,
            "request_path", OLD.request_path,
            "requires_auth", OLD.requires_auth,
            "enabled", OLD.enabled,
            "size", OLD.size,
            "options", OLD.options),
        NULL,
        OLD.id,
        NULL,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `content_set_AFTER_INSERT_AUDIT_LOG`%%
CREATE TRIGGER `content_set_AFTER_INSERT_AUDIT_LOG`
    AFTER INSERT ON `content_set` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "content_set",
        "INSERT",
        NULL,
        JSON_OBJECT(
            "id", NEW.id,
            "service_id", NEW.service_id,
            "content_type", NEW.content_type,
            "request_path", NEW.request_path,
            "requires_auth", NEW.requires_auth,
            "enabled", NEW.enabled,
            "internal", NEW.internal,
            "comments", NEW.comments,
            "options", NEW.options),
        NULL,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `content_set_AFTER_UPDATE_AUDIT_LOG`%%
CREATE TRIGGER `content_set_AFTER_UPDATE_AUDIT_LOG`
    AFTER UPDATE ON `content_set` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "content_set",
        "UPDATE",
        JSON_OBJECT(
            "id", OLD.id,
            "service_id", OLD.service_id,
            "content_type", OLD.content_type,
            "request_path", OLD.request_path,
            "requires_auth", OLD.requires_auth,
            "enabled", OLD.enabled,
            "internal", OLD.internal,
            "comments", OLD.comments,
            "options", OLD.options),
        JSON_OBJECT(
            "id", NEW.id,
            "service_id", NEW.service_id,
            "content_type", NEW.content_type,
            "request_path", NEW.request_path,
            "requires_auth", NEW.requires_auth,
            "enabled", NEW.enabled,
            "internal", NEW.internal,
            "comments", NEW.comments,
            "options", NEW.options),
        OLD.id,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `content_set_AFTER_DELETE_AUDIT_LOG`%%
CREATE TRIGGER `content_set_AFTER_DELETE_AUDIT_LOG`
    AFTER DELETE ON `content_set` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "content_set",
        "DELETE",
        JSON_OBJECT(
            "id", OLD.id,
            "service_id", OLD.service_id,
            "content_type", OLD.content_type,
            "request_path", OLD.request_path,
            "requires_auth", OLD.requires_auth,
            "enabled", OLD.enabled,
            "internal", OLD.internal,
            "comments", OLD.comments,
            "options", OLD.options),
        NULL,
        OLD.id,
        NULL,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_role_AFTER_INSERT_AUDIT_LOG`%%
CREATE TRIGGER `mrs_role_AFTER_INSERT_AUDIT_LOG`
    AFTER INSERT ON `mrs_role` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_role",
        "INSERT",
        NULL,
        JSON_OBJECT(
            "id", NEW.id,
            "derived_from_role_id", NEW.derived_from_role_id,
            "specific_to_service_id", NEW.specific_to_service_id,
            "caption", NEW.caption,
            "description", NEW.description,
            "options", NEW.options),
        NULL,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_role_AFTER_UPDATE_AUDIT_LOG`%%
CREATE TRIGGER `mrs_role_AFTER_UPDATE_AUDIT_LOG`
    AFTER UPDATE ON `mrs_role` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_role",
        "UPDATE",
        JSON_OBJECT(
            "id", OLD.id,
            "derived_from_role_id", OLD.derived_from_role_id,
            "specific_to_service_id", OLD.specific_to_service_id,
            "caption", OLD.caption,
            "description", OLD.description,
            "options", OLD.options),
        JSON_OBJECT(
            "id", NEW.id,
            "derived_from_role_id", NEW.derived_from_role_id,
            "specific_to_service_id", NEW.specific_to_service_id,
            "caption", NEW.caption,
            "description", NEW.description,
            "options", NEW.options),
        OLD.id,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_role_AFTER_DELETE_AUDIT_LOG`%%
CREATE TRIGGER `mrs_role_AFTER_DELETE_AUDIT_LOG`
    AFTER DELETE ON `mrs_role` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_role",
        "DELETE",
        JSON_OBJECT(
            "id", OLD.id,
            "derived_from_role_id", OLD.derived_from_role_id,
            "specific_to_service_id", OLD.specific_to_service_id,
            "caption", OLD.caption,
            "description", OLD.description,
            "options", OLD.options),
        NULL,
        OLD.id,
        NULL,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_user_has_role_AFTER_INSERT_AUDIT_LOG`%%
CREATE TRIGGER `mrs_user_has_role_AFTER_INSERT_AUDIT_LOG`
    AFTER INSERT ON `mrs_user_has_role` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_user_has_role",
        "INSERT",
        NULL,
        JSON_OBJECT(
            "user_id", NEW.user_id,
            "role_id", NEW.role_id,
            "comments", NEW.comments,
            "options", NEW.options),
        NULL,
        NEW.user_id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_user_has_role_AFTER_UPDATE_AUDIT_LOG`%%
CREATE TRIGGER `mrs_user_has_role_AFTER_UPDATE_AUDIT_LOG`
    AFTER UPDATE ON `mrs_user_has_role` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_user_has_role",
        "UPDATE",
        JSON_OBJECT(
            "user_id", OLD.user_id,
            "role_id", OLD.role_id,
            "comments", OLD.comments,
            "options", OLD.options),
        JSON_OBJECT(
            "user_id", NEW.user_id,
            "role_id", NEW.role_id,
            "comments", NEW.comments,
            "options", NEW.options),
        OLD.user_id,
        NEW.user_id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_user_has_role_AFTER_DELETE_AUDIT_LOG`%%
CREATE TRIGGER `mrs_user_has_role_AFTER_DELETE_AUDIT_LOG`
    AFTER DELETE ON `mrs_user_has_role` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_user_has_role",
        "DELETE",
        JSON_OBJECT(
            "user_id", OLD.user_id,
            "role_id", OLD.role_id,
            "comments", OLD.comments,
            "options", OLD.options),
        NULL,
        OLD.user_id,
        NULL,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_user_hierarchy_AFTER_INSERT_AUDIT_LOG`%%
CREATE TRIGGER `mrs_user_hierarchy_AFTER_INSERT_AUDIT_LOG`
    AFTER INSERT ON `mrs_user_hierarchy` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_user_hierarchy",
        "INSERT",
        NULL,
        JSON_OBJECT(
            "user_id", NEW.user_id,
            "reporting_to_user_id", NEW.reporting_to_user_id,
            "user_hierarchy_type_id", NEW.user_hierarchy_type_id,
            "options", NEW.options),
        NULL,
        NEW.user_id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_user_hierarchy_AFTER_UPDATE_AUDIT_LOG`%%
CREATE TRIGGER `mrs_user_hierarchy_AFTER_UPDATE_AUDIT_LOG`
    AFTER UPDATE ON `mrs_user_hierarchy` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_user_hierarchy",
        "UPDATE",
        JSON_OBJECT(
            "user_id", OLD.user_id,
            "reporting_to_user_id", OLD.reporting_to_user_id,
            "user_hierarchy_type_id", OLD.user_hierarchy_type_id,
            "options", OLD.options),
        JSON_OBJECT(
            "user_id", NEW.user_id,
            "reporting_to_user_id", NEW.reporting_to_user_id,
            "user_hierarchy_type_id", NEW.user_hierarchy_type_id,
            "options", NEW.options),
        OLD.user_id,
        NEW.user_id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_user_hierarchy_AFTER_DELETE_AUDIT_LOG`%%
CREATE TRIGGER `mrs_user_hierarchy_AFTER_DELETE_AUDIT_LOG`
    AFTER DELETE ON `mrs_user_hierarchy` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_user_hierarchy",
        "DELETE",
        JSON_OBJECT(
            "user_id", OLD.user_id,
            "reporting_to_user_id", OLD.reporting_to_user_id,
            "user_hierarchy_type_id", OLD.user_hierarchy_type_id,
            "options", OLD.options),
        NULL,
        OLD.user_id,
        NULL,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_user_hierarchy_type_AFTER_INSERT_AUDIT_LOG`%%
CREATE TRIGGER `mrs_user_hierarchy_type_AFTER_INSERT_AUDIT_LOG`
    AFTER INSERT ON `mrs_user_hierarchy_type` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_user_hierarchy_type",
        "INSERT",
        NULL,
        JSON_OBJECT(
            "id", NEW.id,
            "caption", NEW.caption,
            "description", NEW.description,
            "specific_to_service_id", NEW.specific_to_service_id,
            "options", NEW.options),
        NULL,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_user_hierarchy_type_AFTER_UPDATE_AUDIT_LOG`%%
CREATE TRIGGER `mrs_user_hierarchy_type_AFTER_UPDATE_AUDIT_LOG`
    AFTER UPDATE ON `mrs_user_hierarchy_type` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_user_hierarchy_type",
        "UPDATE",
        JSON_OBJECT(
            "id", OLD.id,
            "caption", OLD.caption,
            "description", OLD.description,
            "specific_to_service_id", OLD.specific_to_service_id,
            "options", OLD.options),
        JSON_OBJECT(
            "id", NEW.id,
            "caption", NEW.caption,
            "description", NEW.description,
            "specific_to_service_id", NEW.specific_to_service_id,
            "options", NEW.options),
        OLD.id,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_user_hierarchy_type_AFTER_DELETE_AUDIT_LOG`%%
CREATE TRIGGER `mrs_user_hierarchy_type_AFTER_DELETE_AUDIT_LOG`
    AFTER DELETE ON `mrs_user_hierarchy_type` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_user_hierarchy_type",
        "DELETE",
        JSON_OBJECT(
            "id", OLD.id,
            "caption", OLD.caption,
            "description", OLD.description,
            "specific_to_service_id", OLD.specific_to_service_id,
            "options", OLD.options),
        NULL,
        OLD.id,
        NULL,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_privilege_AFTER_INSERT_AUDIT_LOG`%%
CREATE TRIGGER `mrs_privilege_AFTER_INSERT_AUDIT_LOG`
    AFTER INSERT ON `mrs_privilege` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_privilege",
        "INSERT",
        NULL,
        JSON_OBJECT(
            "id", NEW.id,
            "role_id", NEW.role_id,
            "crud_operations", NEW.crud_operations,
            "service_path", NEW.service_path,
            "schema_path", NEW.schema_path,
            "object_path", NEW.object_path,
            "options", NEW.options),
        NULL,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_privilege_AFTER_UPDATE_AUDIT_LOG`%%
CREATE TRIGGER `mrs_privilege_AFTER_UPDATE_AUDIT_LOG`
    AFTER UPDATE ON `mrs_privilege` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_privilege",
        "UPDATE",
        JSON_OBJECT(
            "id", OLD.id,
            "role_id", OLD.role_id,
            "crud_operations", OLD.crud_operations,
            "service_path", OLD.service_path,
            "schema_path", OLD.schema_path,
            "object_path", OLD.object_path,
            "options", OLD.options),
        JSON_OBJECT(
            "id", NEW.id,
            "role_id", NEW.role_id,
            "crud_operations", NEW.crud_operations,
            "service_path", NEW.service_path,
            "schema_path", NEW.schema_path,
            "object_path", NEW.object_path,
            "options", NEW.options),
        OLD.id,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_privilege_AFTER_DELETE_AUDIT_LOG`%%
CREATE TRIGGER `mrs_privilege_AFTER_DELETE_AUDIT_LOG`
    AFTER DELETE ON `mrs_privilege` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_privilege",
        "DELETE",
        JSON_OBJECT(
            "id", OLD.id,
            "role_id", OLD.role_id,
            "crud_operations", OLD.crud_operations,
            "service_path", OLD.service_path,
            "schema_path", OLD.schema_path,
            "object_path", OLD.object_path,
            "options", OLD.options),
        NULL,
        OLD.id,
        NULL,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_user_group_AFTER_INSERT_AUDIT_LOG`%%
CREATE TRIGGER `mrs_user_group_AFTER_INSERT_AUDIT_LOG`
    AFTER INSERT ON `mrs_user_group` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_user_group",
        "INSERT",
        NULL,
        JSON_OBJECT(
            "id", NEW.id,
            "specific_to_service_id", NEW.specific_to_service_id,
            "caption", NEW.caption,
            "description", NEW.description,
            "options", NEW.options),
        NULL,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_user_group_AFTER_UPDATE_AUDIT_LOG`%%
CREATE TRIGGER `mrs_user_group_AFTER_UPDATE_AUDIT_LOG`
    AFTER UPDATE ON `mrs_user_group` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_user_group",
        "UPDATE",
        JSON_OBJECT(
            "id", OLD.id,
            "specific_to_service_id", OLD.specific_to_service_id,
            "caption", OLD.caption,
            "description", OLD.description,
            "options", OLD.options),
        JSON_OBJECT(
            "id", NEW.id,
            "specific_to_service_id", NEW.specific_to_service_id,
            "caption", NEW.caption,
            "description", NEW.description,
            "options", NEW.options),
        OLD.id,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_user_group_AFTER_DELETE_AUDIT_LOG`%%
CREATE TRIGGER `mrs_user_group_AFTER_DELETE_AUDIT_LOG`
    AFTER DELETE ON `mrs_user_group` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_user_group",
        "DELETE",
        JSON_OBJECT(
            "id", OLD.id,
            "specific_to_service_id", OLD.specific_to_service_id,
            "caption", OLD.caption,
            "description", OLD.description,
            "options", OLD.options),
        NULL,
        OLD.id,
        NULL,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_user_group_has_role_AFTER_INSERT_AUDIT_LOG`%%
CREATE TRIGGER `mrs_user_group_has_role_AFTER_INSERT_AUDIT_LOG`
    AFTER INSERT ON `mrs_user_group_has_role` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_user_group_has_role",
        "INSERT",
        NULL,
        JSON_OBJECT(
            "user_group_id", NEW.user_group_id,
            "role_id", NEW.role_id,
            "options", NEW.options),
        NULL,
        NEW.user_group_id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_user_group_has_role_AFTER_UPDATE_AUDIT_LOG`%%
CREATE TRIGGER `mrs_user_group_has_role_AFTER_UPDATE_AUDIT_LOG`
    AFTER UPDATE ON `mrs_user_group_has_role` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_user_group_has_role",
        "UPDATE",
        JSON_OBJECT(
            "user_group_id", OLD.user_group_id,
            "role_id", OLD.role_id,
            "options", OLD.options),
        JSON_OBJECT(
            "user_group_id", NEW.user_group_id,
            "role_id", NEW.role_id,
            "options", NEW.options),
        OLD.user_group_id,
        NEW.user_group_id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_user_group_has_role_AFTER_DELETE_AUDIT_LOG`%%
CREATE TRIGGER `mrs_user_group_has_role_AFTER_DELETE_AUDIT_LOG`
    AFTER DELETE ON `mrs_user_group_has_role` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_user_group_has_role",
        "DELETE",
        JSON_OBJECT(
            "user_group_id", OLD.user_group_id,
            "role_id", OLD.role_id,
            "options", OLD.options),
        NULL,
        OLD.user_group_id,
        NULL,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_user_has_group_AFTER_INSERT_AUDIT_LOG`%%
CREATE TRIGGER `mrs_user_has_group_AFTER_INSERT_AUDIT_LOG`
    AFTER INSERT ON `mrs_user_has_group` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_user_has_group",
        "INSERT",
        NULL,
        JSON_OBJECT(
            "user_id", NEW.user_id,
            "user_group_id", NEW.user_group_id,
            "comments", NEW.comments,
            "options", NEW.options),
        NULL,
        NEW.user_id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_user_has_group_AFTER_UPDATE_AUDIT_LOG`%%
CREATE TRIGGER `mrs_user_has_group_AFTER_UPDATE_AUDIT_LOG`
    AFTER UPDATE ON `mrs_user_has_group` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_user_has_group",
        "UPDATE",
        JSON_OBJECT(
            "user_id", OLD.user_id,
            "user_group_id", OLD.user_group_id,
            "comments", OLD.comments,
            "options", OLD.options),
        JSON_OBJECT(
            "user_id", NEW.user_id,
            "user_group_id", NEW.user_group_id,
            "comments", NEW.comments,
            "options", NEW.options),
        OLD.user_id,
        NEW.user_id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_user_has_group_AFTER_DELETE_AUDIT_LOG`%%
CREATE TRIGGER `mrs_user_has_group_AFTER_DELETE_AUDIT_LOG`
    AFTER DELETE ON `mrs_user_has_group` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_user_has_group",
        "DELETE",
        JSON_OBJECT(
            "user_id", OLD.user_id,
            "user_group_id", OLD.user_group_id,
            "comments", OLD.comments,
            "options", OLD.options),
        NULL,
        OLD.user_id,
        NULL,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_group_hierarchy_type_AFTER_INSERT_AUDIT_LOG`%%
CREATE TRIGGER `mrs_group_hierarchy_type_AFTER_INSERT_AUDIT_LOG`
    AFTER INSERT ON `mrs_group_hierarchy_type` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_group_hierarchy_type",
        "INSERT",
        NULL,
        JSON_OBJECT(
            "id", NEW.id,
            "caption", NEW.caption,
            "description", NEW.description,
            "options", NEW.options),
        NULL,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_group_hierarchy_type_AFTER_UPDATE_AUDIT_LOG`%%
CREATE TRIGGER `mrs_group_hierarchy_type_AFTER_UPDATE_AUDIT_LOG`
    AFTER UPDATE ON `mrs_group_hierarchy_type` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_group_hierarchy_type",
        "UPDATE",
        JSON_OBJECT(
            "id", OLD.id,
            "caption", OLD.caption,
            "description", OLD.description,
            "options", OLD.options),
        JSON_OBJECT(
            "id", NEW.id,
            "caption", NEW.caption,
            "description", NEW.description,
            "options", NEW.options),
        OLD.id,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_group_hierarchy_type_AFTER_DELETE_AUDIT_LOG`%%
CREATE TRIGGER `mrs_group_hierarchy_type_AFTER_DELETE_AUDIT_LOG`
    AFTER DELETE ON `mrs_group_hierarchy_type` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_group_hierarchy_type",
        "DELETE",
        JSON_OBJECT(
            "id", OLD.id,
            "caption", OLD.caption,
            "description", OLD.description,
            "options", OLD.options),
        NULL,
        OLD.id,
        NULL,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_user_group_hierarchy_AFTER_INSERT_AUDIT_LOG`%%
CREATE TRIGGER `mrs_user_group_hierarchy_AFTER_INSERT_AUDIT_LOG`
    AFTER INSERT ON `mrs_user_group_hierarchy` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_user_group_hierarchy",
        "INSERT",
        NULL,
        JSON_OBJECT(
            "user_group_id", NEW.user_group_id,
            "parent_group_id", NEW.parent_group_id,
            "group_hierarchy_type_id", NEW.group_hierarchy_type_id,
            "level", NEW.level,
            "options", NEW.options),
        NULL,
        NEW.user_group_id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_user_group_hierarchy_AFTER_UPDATE_AUDIT_LOG`%%
CREATE TRIGGER `mrs_user_group_hierarchy_AFTER_UPDATE_AUDIT_LOG`
    AFTER UPDATE ON `mrs_user_group_hierarchy` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_user_group_hierarchy",
        "UPDATE",
        JSON_OBJECT(
            "user_group_id", OLD.user_group_id,
            "parent_group_id", OLD.parent_group_id,
            "group_hierarchy_type_id", OLD.group_hierarchy_type_id,
            "level", OLD.level,
            "options", OLD.options),
        JSON_OBJECT(
            "user_group_id", NEW.user_group_id,
            "parent_group_id", NEW.parent_group_id,
            "group_hierarchy_type_id", NEW.group_hierarchy_type_id,
            "level", NEW.level,
            "options", NEW.options),
        OLD.user_group_id,
        NEW.user_group_id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_user_group_hierarchy_AFTER_DELETE_AUDIT_LOG`%%
CREATE TRIGGER `mrs_user_group_hierarchy_AFTER_DELETE_AUDIT_LOG`
    AFTER DELETE ON `mrs_user_group_hierarchy` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_user_group_hierarchy",
        "DELETE",
        JSON_OBJECT(
            "user_group_id", OLD.user_group_id,
            "parent_group_id", OLD.parent_group_id,
            "group_hierarchy_type_id", OLD.group_hierarchy_type_id,
            "level", OLD.level,
            "options", OLD.options),
        NULL,
        OLD.user_group_id,
        NULL,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_db_object_row_group_security_AFTER_INSERT_AUDIT_LOG`%%
CREATE TRIGGER `mrs_db_object_row_group_security_AFTER_INSERT_AUDIT_LOG`
    AFTER INSERT ON `mrs_db_object_row_group_security` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_db_object_row_group_security",
        "INSERT",
        NULL,
        JSON_OBJECT(
            "db_object_id", NEW.db_object_id,
            "group_hierarchy_type_id", NEW.group_hierarchy_type_id,
            "row_group_ownership_column", NEW.row_group_ownership_column,
            "level", NEW.level,
            "match_level", NEW.match_level,
            "options", NEW.options),
        NULL,
        NEW.db_object_id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_db_object_row_group_security_AFTER_UPDATE_AUDIT_LOG`%%
CREATE TRIGGER `mrs_db_object_row_group_security_AFTER_UPDATE_AUDIT_LOG`
    AFTER UPDATE ON `mrs_db_object_row_group_security` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_db_object_row_group_security",
        "UPDATE",
        JSON_OBJECT(
            "db_object_id", OLD.db_object_id,
            "group_hierarchy_type_id", OLD.group_hierarchy_type_id,
            "row_group_ownership_column", OLD.row_group_ownership_column,
            "level", OLD.level,
            "match_level", OLD.match_level,
            "options", OLD.options),
        JSON_OBJECT(
            "db_object_id", NEW.db_object_id,
            "group_hierarchy_type_id", NEW.group_hierarchy_type_id,
            "row_group_ownership_column", NEW.row_group_ownership_column,
            "level", NEW.level,
            "match_level", NEW.match_level,
            "options", NEW.options),
        OLD.db_object_id,
        NEW.db_object_id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `mrs_db_object_row_group_security_AFTER_DELETE_AUDIT_LOG`%%
CREATE TRIGGER `mrs_db_object_row_group_security_AFTER_DELETE_AUDIT_LOG`
    AFTER DELETE ON `mrs_db_object_row_group_security` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "mrs_db_object_row_group_security",
        "DELETE",
        JSON_OBJECT(
            "db_object_id", OLD.db_object_id,
            "group_hierarchy_type_id", OLD.group_hierarchy_type_id,
            "row_group_ownership_column", OLD.row_group_ownership_column,
            "level", OLD.level,
            "match_level", OLD.match_level,
            "options", OLD.options),
        NULL,
        OLD.db_object_id,
        NULL,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `object_AFTER_INSERT_AUDIT_LOG`%%
CREATE TRIGGER `object_AFTER_INSERT_AUDIT_LOG`
    AFTER INSERT ON `object` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "object",
        "INSERT",
        NULL,
        JSON_OBJECT(
            "id", NEW.id,
            "db_object_id", NEW.db_object_id,
            "name", NEW.name,
            "kind", NEW.kind,
            "position", NEW.position,
            "row_ownership_field_id", NEW.row_ownership_field_id,
            "options", NEW.options,
            "sdk_options", NEW.sdk_options,
            "comments", NEW.comments),
        NULL,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `object_AFTER_UPDATE_AUDIT_LOG`%%
CREATE TRIGGER `object_AFTER_UPDATE_AUDIT_LOG`
    AFTER UPDATE ON `object` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "object",
        "UPDATE",
        JSON_OBJECT(
            "id", OLD.id,
            "db_object_id", OLD.db_object_id,
            "name", OLD.name,
            "kind", OLD.kind,
            "position", OLD.position,
            "row_ownership_field_id", OLD.row_ownership_field_id,
            "options", OLD.options,
            "sdk_options", OLD.sdk_options,
            "comments", OLD.comments),
        JSON_OBJECT(
            "id", NEW.id,
            "db_object_id", NEW.db_object_id,
            "name", NEW.name,
            "kind", NEW.kind,
            "position", NEW.position,
            "row_ownership_field_id", NEW.row_ownership_field_id,
            "options", NEW.options,
            "sdk_options", NEW.sdk_options,
            "comments", NEW.comments),
        OLD.id,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `object_AFTER_DELETE_AUDIT_LOG`%%
CREATE TRIGGER `object_AFTER_DELETE_AUDIT_LOG`
    AFTER DELETE ON `object` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "object",
        "DELETE",
        JSON_OBJECT(
            "id", OLD.id,
            "db_object_id", OLD.db_object_id,
            "name", OLD.name,
            "kind", OLD.kind,
            "position", OLD.position,
            "row_ownership_field_id", OLD.row_ownership_field_id,
            "options", OLD.options,
            "sdk_options", OLD.sdk_options,
            "comments", OLD.comments),
        NULL,
        OLD.id,
        NULL,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `object_field_AFTER_INSERT_AUDIT_LOG`%%
CREATE TRIGGER `object_field_AFTER_INSERT_AUDIT_LOG`
    AFTER INSERT ON `object_field` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "object_field",
        "INSERT",
        NULL,
        JSON_OBJECT(
            "id", NEW.id,
            "object_id", NEW.object_id,
            "parent_reference_id", NEW.parent_reference_id,
            "represents_reference_id", NEW.represents_reference_id,
            "name", NEW.name,
            "position", NEW.position,
            "db_column", NEW.db_column,
            "enabled", NEW.enabled,
            "allow_filtering", NEW.allow_filtering,
            "allow_sorting", NEW.allow_sorting,
            "no_check", NEW.no_check,
            "no_update", NEW.no_update,
            "json_schema", NEW.json_schema,
            "options", NEW.options,
            "sdk_options", NEW.sdk_options,
            "comments", NEW.comments),
        NULL,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `object_field_AFTER_UPDATE_AUDIT_LOG`%%
CREATE TRIGGER `object_field_AFTER_UPDATE_AUDIT_LOG`
    AFTER UPDATE ON `object_field` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "object_field",
        "UPDATE",
        JSON_OBJECT(
            "id", OLD.id,
            "object_id", OLD.object_id,
            "parent_reference_id", OLD.parent_reference_id,
            "represents_reference_id", OLD.represents_reference_id,
            "name", OLD.name,
            "position", OLD.position,
            "db_column", OLD.db_column,
            "enabled", OLD.enabled,
            "allow_filtering", OLD.allow_filtering,
            "allow_sorting", OLD.allow_sorting,
            "no_check", OLD.no_check,
            "no_update", OLD.no_update,
            "json_schema", OLD.json_schema,
            "options", OLD.options,
            "sdk_options", OLD.sdk_options,
            "comments", OLD.comments),
        JSON_OBJECT(
            "id", NEW.id,
            "object_id", NEW.object_id,
            "parent_reference_id", NEW.parent_reference_id,
            "represents_reference_id", NEW.represents_reference_id,
            "name", NEW.name,
            "position", NEW.position,
            "db_column", NEW.db_column,
            "enabled", NEW.enabled,
            "allow_filtering", NEW.allow_filtering,
            "allow_sorting", NEW.allow_sorting,
            "no_check", NEW.no_check,
            "no_update", NEW.no_update,
            "json_schema", NEW.json_schema,
            "options", NEW.options,
            "sdk_options", NEW.sdk_options,
            "comments", NEW.comments),
        OLD.id,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `object_field_AFTER_DELETE_AUDIT_LOG`%%
CREATE TRIGGER `object_field_AFTER_DELETE_AUDIT_LOG`
    AFTER DELETE ON `object_field` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "object_field",
        "DELETE",
        JSON_OBJECT(
            "id", OLD.id,
            "object_id", OLD.object_id,
            "parent_reference_id", OLD.parent_reference_id,
            "represents_reference_id", OLD.represents_reference_id,
            "name", OLD.name,
            "position", OLD.position,
            "db_column", OLD.db_column,
            "enabled", OLD.enabled,
            "allow_filtering", OLD.allow_filtering,
            "allow_sorting", OLD.allow_sorting,
            "no_check", OLD.no_check,
            "no_update", OLD.no_update,
            "json_schema", OLD.json_schema,
            "options", OLD.options,
            "sdk_options", OLD.sdk_options,
            "comments", OLD.comments),
        NULL,
        OLD.id,
        NULL,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `object_reference_AFTER_INSERT_AUDIT_LOG`%%
CREATE TRIGGER `object_reference_AFTER_INSERT_AUDIT_LOG`
    AFTER INSERT ON `object_reference` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "object_reference",
        "INSERT",
        NULL,
        JSON_OBJECT(
            "id", NEW.id,
            "reduce_to_value_of_field_id", NEW.reduce_to_value_of_field_id,
            "row_ownership_field_id", NEW.row_ownership_field_id,
            "reference_mapping", NEW.reference_mapping,
            "unnest", NEW.unnest,
            "options", NEW.options,
            "sdk_options", NEW.sdk_options,
            "comments", NEW.comments),
        NULL,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `object_reference_AFTER_UPDATE_AUDIT_LOG`%%
CREATE TRIGGER `object_reference_AFTER_UPDATE_AUDIT_LOG`
    AFTER UPDATE ON `object_reference` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "object_reference",
        "UPDATE",
        JSON_OBJECT(
            "id", OLD.id,
            "reduce_to_value_of_field_id", OLD.reduce_to_value_of_field_id,
            "row_ownership_field_id", OLD.row_ownership_field_id,
            "reference_mapping", OLD.reference_mapping,
            "unnest", OLD.unnest,
            "options", OLD.options,
            "sdk_options", OLD.sdk_options,
            "comments", OLD.comments),
        JSON_OBJECT(
            "id", NEW.id,
            "reduce_to_value_of_field_id", NEW.reduce_to_value_of_field_id,
            "row_ownership_field_id", NEW.row_ownership_field_id,
            "reference_mapping", NEW.reference_mapping,
            "unnest", NEW.unnest,
            "options", NEW.options,
            "sdk_options", NEW.sdk_options,
            "comments", NEW.comments),
        OLD.id,
        NEW.id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `object_reference_AFTER_DELETE_AUDIT_LOG`%%
CREATE TRIGGER `object_reference_AFTER_DELETE_AUDIT_LOG`
    AFTER DELETE ON `object_reference` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "object_reference",
        "DELETE",
        JSON_OBJECT(
            "id", OLD.id,
            "reduce_to_value_of_field_id", OLD.reduce_to_value_of_field_id,
            "row_ownership_field_id", OLD.row_ownership_field_id,
            "reference_mapping", OLD.reference_mapping,
            "unnest", OLD.unnest,
            "options", OLD.options,
            "sdk_options", OLD.sdk_options,
            "comments", OLD.comments),
        NULL,
        OLD.id,
        NULL,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `service_has_auth_app_AFTER_INSERT_AUDIT_LOG`%%
CREATE TRIGGER `service_has_auth_app_AFTER_INSERT_AUDIT_LOG`
    AFTER INSERT ON `service_has_auth_app` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "service_has_auth_app",
        "INSERT",
        NULL,
        JSON_OBJECT(
            "service_id", NEW.service_id,
            "auth_app_id", NEW.auth_app_id,
            "options", NEW.options),
        NULL,
        NEW.service_id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `service_has_auth_app_AFTER_UPDATE_AUDIT_LOG`%%
CREATE TRIGGER `service_has_auth_app_AFTER_UPDATE_AUDIT_LOG`
    AFTER UPDATE ON `service_has_auth_app` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "service_has_auth_app",
        "UPDATE",
        JSON_OBJECT(
            "service_id", OLD.service_id,
            "auth_app_id", OLD.auth_app_id,
            "options", OLD.options),
        JSON_OBJECT(
            "service_id", NEW.service_id,
            "auth_app_id", NEW.auth_app_id,
            "options", NEW.options),
        OLD.service_id,
        NEW.service_id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `service_has_auth_app_AFTER_DELETE_AUDIT_LOG`%%
CREATE TRIGGER `service_has_auth_app_AFTER_DELETE_AUDIT_LOG`
    AFTER DELETE ON `service_has_auth_app` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "service_has_auth_app",
        "DELETE",
        JSON_OBJECT(
            "service_id", OLD.service_id,
            "auth_app_id", OLD.auth_app_id,
            "options", OLD.options),
        NULL,
        OLD.service_id,
        NULL,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `content_set_has_obj_def_AFTER_INSERT_AUDIT_LOG`%%
CREATE TRIGGER `content_set_has_obj_def_AFTER_INSERT_AUDIT_LOG`
    AFTER INSERT ON `content_set_has_obj_def` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "content_set_has_obj_def",
        "INSERT",
        NULL,
        JSON_OBJECT(
            "content_set_id", NEW.content_set_id,
            "db_object_id", NEW.db_object_id,
            "kind", NEW.kind,
            "priority", NEW.priority,
            "language", NEW.language,
            "name", NEW.name,
            "class_name", NEW.class_name,
            "comments", NEW.comments,
            "options", NEW.options),
        NULL,
        NEW.content_set_id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `content_set_has_obj_def_AFTER_UPDATE_AUDIT_LOG`%%
CREATE TRIGGER `content_set_has_obj_def_AFTER_UPDATE_AUDIT_LOG`
    AFTER UPDATE ON `content_set_has_obj_def` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "content_set_has_obj_def",
        "UPDATE",
        JSON_OBJECT(
            "content_set_id", OLD.content_set_id,
            "db_object_id", OLD.db_object_id,
            "kind", OLD.kind,
            "priority", OLD.priority,
            "language", OLD.language,
            "name", OLD.name,
            "class_name", OLD.class_name,
            "comments", OLD.comments,
            "options", OLD.options),
        JSON_OBJECT(
            "content_set_id", NEW.content_set_id,
            "db_object_id", NEW.db_object_id,
            "kind", NEW.kind,
            "priority", NEW.priority,
            "language", NEW.language,
            "name", NEW.name,
            "class_name", NEW.class_name,
            "comments", NEW.comments,
            "options", NEW.options),
        OLD.content_set_id,
        NEW.content_set_id,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DROP TRIGGER IF EXISTS `content_set_has_obj_def_AFTER_DELETE_AUDIT_LOG`%%
CREATE TRIGGER `content_set_has_obj_def_AFTER_DELETE_AUDIT_LOG`
    AFTER DELETE ON `content_set_has_obj_def` FOR EACH ROW
BEGIN
    INSERT INTO `mysql_rest_service_metadata`.`audit_log` (
        table_name, dml_type, old_row_data, new_row_data, old_row_id, new_row_id, changed_by, changed_at)
    VALUES (
        "content_set_has_obj_def",
        "DELETE",
        JSON_OBJECT(
            "content_set_id", OLD.content_set_id,
            "db_object_id", OLD.db_object_id,
            "kind", OLD.kind,
            "priority", OLD.priority,
            "language", OLD.language,
            "name", OLD.name,
            "class_name", OLD.class_name,
            "comments", OLD.comments,
            "options", OLD.options),
        NULL,
        OLD.content_set_id,
        NULL,
        SESSION_USER(),
        CURRENT_TIMESTAMP
    );
END%%

DELIMITER ;

-- -----------------------------------------------------
-- EVENTs

DELIMITER %%

-- Create an event to delete old audit_log entries that are older than 14 days

DROP EVENT IF EXISTS `delete_old_audit_log_entries`%%
CREATE EVENT `delete_old_audit_log_entries`
ON SCHEDULE EVERY 1 DAY ENABLE
DO BEGIN
    DELETE FROM `mysql_rest_service_metadata`.`audit_log`
        WHERE changed_at < NOW() - INTERVAL 14 DAY;
END%%

-- Create an event to dump the audit log every 15 minutes

DROP EVENT IF EXISTS `audit_log_dump_event`%%
CREATE EVENT `audit_log_dump_event`
ON SCHEDULE EVERY 15 MINUTE
  STARTS '2025-01-01 00:00:00'
ON COMPLETION PRESERVE DISABLE
DO BEGIN
    CALL `mysql_rest_service_metadata`.`dump_audit_log`();
END%%

-- Periodically down-sample router_status rows to keep its size under control.

DROP EVENT IF EXISTS `router_status_cleanup`%%
CREATE EVENT `router_status_cleanup` ON SCHEDULE EVERY 1 HOUR
ON COMPLETION NOT PRESERVE ENABLE COMMENT 'Aggregate and clean up router_status entries' DO
    CALL mysql_rest_service_metadata.router_status_do_cleanup(NOW())%%


-- Periodically delete the router_general_log

DROP EVENT IF EXISTS `router_log_cleanup`%%
CREATE EVENT `router_log_cleanup`
ON SCHEDULE EVERY 1 HOUR
ON COMPLETION NOT PRESERVE ENABLE COMMENT 'Clean up router_general_log entries'
DO
    DELETE FROM `mysql_rest_service_metadata`.`router_general_log`
        WHERE `log_time` <= NOW() - INTERVAL 1 DAY%%

DELIMITER ;

DELIMITER %%

-- -----------------------------------------------------------------------------
-- CREATE PROCEDURE `mysql_rest_service_metadata`.`restore_roles`
-- -----------------------------------------------------------------------------

DROP PROCEDURE IF EXISTS `mysql_rest_service_metadata`.`restore_roles`%%
CREATE PROCEDURE `mysql_rest_service_metadata`.`restore_roles`()
SQL SECURITY DEFINER
COMMENT 'This procedure restores all the ROLEs required by the MySQL REST Service.'
BEGIN
    -- -----------------------------------------------------
    -- Create roles for the MySQL REST Service

    -- The mysql_rest_service_admin ROLE allows to fully manage the REST services
    -- The mysql_rest_service_schema_admin ROLE allows to manage the database schemas assigned to REST services
    -- The mysql_rest_service_dev ROLE allows to develop new REST objects for given REST services and upload static files
    -- The mysql_rest_service_user ROLE can be assigned to MySQL users that are granted access via MySQL Internal authentication.
    -- The mysql_rest_service_meta_provider ROLE is used by the MySQL Router to read the mrs metadata and make inserts into the auth_user table
    -- The mysql_rest_service_data_provider ROLE is used by the MySQL Router to read the actual schema data that is exposed via REST

    CREATE ROLE IF NOT EXISTS 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev', 'mysql_rest_service_user',
        'mysql_rest_service_meta_provider', 'mysql_rest_service_data_provider';

    -- Allow the 'mysql_rest_service_user' role to access the same data as 'mysql_rest_service_data_provider'
    GRANT 'mysql_rest_service_data_provider' TO 'mysql_rest_service_user';

    -- Allow the creation of temporary tables
    GRANT CREATE TEMPORARY TABLES ON `mysql_rest_service_metadata`.*
        TO 'mysql_rest_service_data_provider';

    -- `mysql_rest_service_metadata`.`msm_schema_version`
    GRANT SELECT ON `mysql_rest_service_metadata`.`msm_schema_version`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev', 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`audit_log`
    GRANT SELECT ON `mysql_rest_service_metadata`.`audit_log`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev', 'mysql_rest_service_meta_provider';

    -- -----------------------------------------------------
    -- Config

    -- `mysql_rest_service_metadata`.`config`
    GRANT SELECT, UPDATE
        ON `mysql_rest_service_metadata`.`config`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin';
    GRANT SELECT ON `mysql_rest_service_metadata`.`config`
        TO 'mysql_rest_service_meta_provider', 'mysql_rest_service_dev';

    -- `mysql_rest_service_metadata`.`redirect`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`redirect`
        TO 'mysql_rest_service_admin';
    GRANT SELECT ON `mysql_rest_service_metadata`.`redirect`
        TO 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev', 'mysql_rest_service_meta_provider';

    -- -----------------------------------------------------
    -- Service

    -- `mysql_rest_service_metadata`.`url_host`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`url_host`
        TO 'mysql_rest_service_admin';
    GRANT SELECT ON `mysql_rest_service_metadata`.`url_host`
        TO 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev', 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`url_host_alias`
    GRANT SELECT, INSERT, DELETE
        ON `mysql_rest_service_metadata`.`url_host_alias`
        TO 'mysql_rest_service_admin';
    GRANT SELECT ON `mysql_rest_service_metadata`.`url_host_alias`
        TO 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev', 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`service`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`service`
        TO 'mysql_rest_service_admin';
    GRANT SELECT ON `mysql_rest_service_metadata`.`service`
        TO 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev', 'mysql_rest_service_meta_provider';

    -- -----------------------------------------------------
    -- Schema Objects

    -- `mysql_rest_service_metadata`.`db_schema`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`db_schema`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin';
    GRANT SELECT ON `mysql_rest_service_metadata`.`db_schema`
        TO 'mysql_rest_service_meta_provider', 'mysql_rest_service_dev';

    -- `mysql_rest_service_metadata`.`db_object`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`db_object`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev';
    GRANT SELECT ON `mysql_rest_service_metadata`.`db_object`
        TO 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`mrs_db_object_row_group_security`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`mrs_db_object_row_group_security`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev';
    GRANT SELECT ON `mysql_rest_service_metadata`.`mrs_db_object_row_group_security`
        TO 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`object`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`object`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev';
    GRANT SELECT ON `mysql_rest_service_metadata`.`object`
        TO 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`object_field`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`object_field`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev';
    GRANT SELECT ON `mysql_rest_service_metadata`.`object_field`
        TO 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`object_reference`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`object_reference`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev';
    GRANT SELECT ON `mysql_rest_service_metadata`.`object_reference`
        TO 'mysql_rest_service_meta_provider';

    -- -----------------------------------------------------
    -- Static Content

    -- `mysql_rest_service_metadata`.`content_set`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`content_set`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev';
    GRANT SELECT ON `mysql_rest_service_metadata`.`content_set`
        TO 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`content_file`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`content_file`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev';
    GRANT SELECT ON `mysql_rest_service_metadata`.`content_file`
        TO 'mysql_rest_service_meta_provider';


    -- `mysql_rest_service_metadata`.`content_set_has_obj_def`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`content_set_has_obj_def`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev';
    GRANT SELECT ON `mysql_rest_service_metadata`.`content_set_has_obj_def`
        TO 'mysql_rest_service_meta_provider';

    -- -----------------------------------------------------
    -- User Authentication

    -- `mysql_rest_service_metadata`.`auth_app`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`auth_app`
        TO 'mysql_rest_service_admin';
    GRANT SELECT ON `mysql_rest_service_metadata`.`auth_app`
        TO 'mysql_rest_service_meta_provider', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev';

    -- `mysql_rest_service_metadata`.`service_has_auth_app`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`service_has_auth_app`
        TO 'mysql_rest_service_admin';
    GRANT SELECT ON `mysql_rest_service_metadata`.`service_has_auth_app`
        TO 'mysql_rest_service_meta_provider', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev';

    -- `mysql_rest_service_metadata`.`auth_vendor`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`auth_vendor`
        TO 'mysql_rest_service_admin';
    GRANT SELECT ON `mysql_rest_service_metadata`.`auth_vendor`
        TO 'mysql_rest_service_meta_provider', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev';

    -- `mysql_rest_service_metadata`.`mrs_user`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`mrs_user`
        TO 'mysql_rest_service_admin';
    GRANT SELECT, INSERT, UPDATE ON `mysql_rest_service_metadata`.`mrs_user`
        TO 'mysql_rest_service_meta_provider';
    GRANT SELECT ON `mysql_rest_service_metadata`.`mrs_user`
        TO 'mysql_rest_service_data_provider';

    -- -----------------------------------------------------
    -- User Hierarchy

    -- `mysql_rest_service_metadata`.`mrs_user_hierarchy`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`mrs_user_hierarchy`
        TO 'mysql_rest_service_admin';
    GRANT SELECT, INSERT ON `mysql_rest_service_metadata`.`mrs_user_hierarchy`
        TO 'mysql_rest_service_meta_provider';
    GRANT SELECT ON `mysql_rest_service_metadata`.`mrs_user_hierarchy`
        TO 'mysql_rest_service_data_provider';

    -- `mysql_rest_service_metadata`.`mrs_user_hierarchy_type`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`mrs_user_hierarchy_type`
        TO 'mysql_rest_service_admin';
    GRANT SELECT, INSERT ON `mysql_rest_service_metadata`.`mrs_user_hierarchy_type`
        TO 'mysql_rest_service_meta_provider';

    -- -----------------------------------------------------
    -- User Roles

    -- `mysql_rest_service_metadata`.`mrs_user_has_role`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`mrs_user_has_role`
        TO 'mysql_rest_service_admin';
    GRANT SELECT, INSERT ON `mysql_rest_service_metadata`.`mrs_user_has_role`
        TO 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`mrs_role`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`mrs_role`
        TO 'mysql_rest_service_admin';
    GRANT SELECT, INSERT ON `mysql_rest_service_metadata`.`mrs_role`
        TO 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`mrs_privilege`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`mrs_privilege`
        TO 'mysql_rest_service_admin';
    GRANT SELECT, INSERT ON `mysql_rest_service_metadata`.`mrs_privilege`
        TO 'mysql_rest_service_meta_provider';

    -- -----------------------------------------------------
    -- User Group Management

    -- `mysql_rest_service_metadata`.`mrs_user_has_group`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`mrs_user_has_group`
        TO 'mysql_rest_service_admin';
    GRANT SELECT, INSERT ON `mysql_rest_service_metadata`.`mrs_user_has_group`
        TO 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`mrs_user_group`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`mrs_user_group`
        TO 'mysql_rest_service_admin';
    GRANT SELECT, INSERT ON `mysql_rest_service_metadata`.`mrs_user_group`
        TO 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`mrs_user_group_has_role`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`mrs_user_group_has_role`
        TO 'mysql_rest_service_admin';
    GRANT SELECT, INSERT ON `mysql_rest_service_metadata`.`mrs_user_group_has_role`
        TO 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`mrs_group_hierarchy_type`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`mrs_group_hierarchy_type`
        TO 'mysql_rest_service_admin';
    GRANT SELECT, INSERT ON `mysql_rest_service_metadata`.`mrs_group_hierarchy_type`
        TO 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`mrs_user_group_hierarchy`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`mrs_user_group_hierarchy`
        TO 'mysql_rest_service_admin';
    GRANT SELECT, INSERT ON `mysql_rest_service_metadata`.`mrs_user_group_hierarchy`
        TO 'mysql_rest_service_meta_provider';
    GRANT SELECT ON `mysql_rest_service_metadata`.`mrs_user_group_hierarchy`
        TO 'mysql_rest_service_data_provider';

    -- -----------------------------------------------------
    -- Router Management

    -- `mysql_rest_service_metadata`.`router`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`router`
        TO 'mysql_rest_service_admin';
    GRANT SELECT, INSERT, UPDATE ON `mysql_rest_service_metadata`.`router`
        TO 'mysql_rest_service_meta_provider';
    GRANT SELECT
        ON `mysql_rest_service_metadata`.`router`
        TO 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev';

    -- `mysql_rest_service_metadata`.`router_status`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`router_status`
        TO 'mysql_rest_service_admin';
    GRANT SELECT, INSERT, UPDATE ON `mysql_rest_service_metadata`.`router_status`
        TO 'mysql_rest_service_meta_provider';
    GRANT SELECT ON `mysql_rest_service_metadata`.`router_status`
        TO 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev';

    -- `mysql_rest_service_metadata`.`router_general_log`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`router_general_log`
        TO 'mysql_rest_service_admin';
    GRANT INSERT ON `mysql_rest_service_metadata`.`router_general_log`
        TO 'mysql_rest_service_meta_provider';
    GRANT SELECT ON `mysql_rest_service_metadata`.`router_general_log`
        TO 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev';

    -- `mysql_rest_service_metadata`.`router_session`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`router_session`
        TO 'mysql_rest_service_admin';
    GRANT SELECT, INSERT ON `mysql_rest_service_metadata`.`router_session`
        TO 'mysql_rest_service_meta_provider';
    GRANT SELECT ON `mysql_rest_service_metadata`.`router_session`
        TO 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev';

    -- `mysql_rest_service_metadata`.`router_services`
    GRANT SELECT ON `mysql_rest_service_metadata`.`router_services`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev', 'mysql_rest_service_meta_provider';

    -- -----------------------------------------------------
    -- Procedures and Functions

    -- `mysql_rest_service_metadata`.`get_sequence_id`

    GRANT EXECUTE ON FUNCTION `mysql_rest_service_metadata`.`get_sequence_id`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev', 'mysql_rest_service_meta_provider', 'mysql_rest_service_data_provider';

    -- `mysql_rest_service_metadata`.`table_columns_with_references`
    GRANT EXECUTE ON PROCEDURE `mysql_rest_service_metadata`.`table_columns_with_references`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev', 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`sdk_service_data`
    GRANT EXECUTE ON PROCEDURE `mysql_rest_service_metadata`.`sdk_service_data`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev', 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`restore_roles`
    GRANT EXECUTE ON PROCEDURE `mysql_rest_service_metadata`.`restore_roles`
        TO 'mysql_rest_service_admin';


    -- -----------------------------------------------------
    -- Views

    -- `mysql_rest_service_metadata`.`mrs_user_schema_version`
    GRANT SELECT
      ON `mysql_rest_service_metadata`.`mrs_user_schema_version`
      TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev', 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`object_fields_with_references`
    GRANT SELECT
        ON `mysql_rest_service_metadata`.`object_fields_with_references`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev', 'mysql_rest_service_meta_provider';

    -- -----------------------------------------------------
    -- Grant the necessary mysql_tasks privileges to the MySQL REST Service roles
    GRANT 'mysql_task_user' TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev', 'mysql_rest_service_user',
    	'mysql_rest_service_meta_provider', 'mysql_rest_service_data_provider';
END%%

DELIMITER ;


-- #############################################################################
-- MSM Section 370: Authorization
-- -----------------------------------------------------------------------------
-- This section is used to define or update ROLEs and GRANTs.
-- #############################################################################

USE `mysql_rest_service_metadata`;

DELIMITER %%

-- -----------------------------------------------------------------------------
-- PROCEDURE `mysql_rest_service_metadata`.`msm_auth_4.0.0_to_4.0.1`
-- -----------------------------------------------------------------------------
-- Stored procedure to perform the authorization changes from version 4.0.0
-- to 4.0.1
-- -----------------------------------------------------------------------------
DROP PROCEDURE IF EXISTS `msm_auth_4.0.0_to_4.0.1`%%
CREATE PROCEDURE `msm_auth_4.0.0_to_4.0.1`()
SQL SECURITY INVOKER
BEGIN
END%%

-- -----------------------------------------------------------------------------
-- PROCEDURE `mysql_rest_service_metadata`.`msm_auth_4.0.1_to_4.0.2`
-- -----------------------------------------------------------------------------
-- Stored procedure to perform the authorization changes from version 4.0.1
-- to 4.0.2
-- -----------------------------------------------------------------------------
DROP PROCEDURE IF EXISTS `msm_auth_4.0.1_to_4.0.2`%%
CREATE PROCEDURE `msm_auth_4.0.1_to_4.0.2`()
SQL SECURITY INVOKER
BEGIN
    -- `mysql_rest_service_metadata`.`msm_schema_version`
    GRANT SELECT ON `mysql_rest_service_metadata`.`msm_schema_version`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev', 'mysql_rest_service_meta_provider';
END%%

-- -----------------------------------------------------------------------------
-- PROCEDURE `mysql_rest_service_metadata`.`msm_auth_4.0.2_to_4.0.3`
-- -----------------------------------------------------------------------------
-- Stored procedure to perform the authorization changes from version 4.0.2
-- to 4.0.3
-- -----------------------------------------------------------------------------
DROP PROCEDURE IF EXISTS `msm_auth_4.0.2_to_4.0.3`%%
CREATE PROCEDURE `msm_auth_4.0.2_to_4.0.3`()
SQL SECURITY INVOKER
BEGIN
    -- Change ROLEs and perform the required GRANT/REVOKE statements.

    REVOKE CREATE TEMPORARY TABLES ON *.* FROM 'mysql_rest_service_data_provider';

    GRANT CREATE TEMPORARY TABLES ON `mysql_rest_service_metadata`.*
        TO 'mysql_rest_service_data_provider';
END%%

-- -----------------------------------------------------------------------------
-- PROCEDURE `mysql_rest_service_metadata`.`msm_auth_4.0.3_to_4.0.4`
-- -----------------------------------------------------------------------------
-- Stored procedure to perform the authorization changes from version 4.0.3
-- to 4.0.4
-- -----------------------------------------------------------------------------
DROP PROCEDURE IF EXISTS `msm_auth_4.0.3_to_4.0.4`%%
CREATE PROCEDURE `msm_auth_4.0.3_to_4.0.4`()
SQL SECURITY INVOKER
BEGIN
END%%

-- -----------------------------------------------------------------------------
-- PROCEDURE `mysql_rest_service_metadata`.`msm_auth_4.0.4_to_4.1.0`
-- -----------------------------------------------------------------------------
-- Stored procedure to perform the authorization changes from version 4.0.4
-- to 4.1.0
-- -----------------------------------------------------------------------------
DROP PROCEDURE IF EXISTS `msm_auth_4.0.4_to_4.1.0`%%
CREATE PROCEDURE `msm_auth_4.0.4_to_4.1.0`()
SQL SECURITY INVOKER
BEGIN
END%%

-- -----------------------------------------------------------------------------
-- PROCEDURE `mysql_rest_service_metadata`.`msm_auth_4.1.0_to_4.1.1`
-- -----------------------------------------------------------------------------
-- Stored procedure to perform the authorization changes from version 4.1.0
-- to 4.1.1
-- -----------------------------------------------------------------------------
DROP PROCEDURE IF EXISTS `msm_auth_4.1.0_to_4.1.1`%%
CREATE PROCEDURE `msm_auth_4.1.0_to_4.1.1`()
SQL SECURITY INVOKER
BEGIN
END%%

-- -----------------------------------------------------------------------------
-- PROCEDURE `mysql_rest_service_metadata`.`msm_auth_4.1.1_to_4.1.2`
-- -----------------------------------------------------------------------------
-- Stored procedure to perform the authorization changes from version 4.1.1
-- to 4.1.2
-- -----------------------------------------------------------------------------
DROP PROCEDURE IF EXISTS `msm_auth_4.1.1_to_4.1.2`%%
CREATE PROCEDURE `msm_auth_4.1.1_to_4.1.2`()
SQL SECURITY INVOKER
BEGIN
    -- Change ROLEs and perform the required GRANT/REVOKE statements.

    -- `mysql_rest_service_metadata`.`sdk_service_data`
    GRANT EXECUTE ON PROCEDURE `mysql_rest_service_metadata`.`sdk_service_data`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev', 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`restore_roles`
    GRANT EXECUTE ON PROCEDURE `mysql_rest_service_metadata`.`restore_roles`
        TO 'mysql_rest_service_admin';
END%%

-- -----------------------------------------------------------------------------
-- PROCEDURE `mysql_rest_service_metadata`.`msm_auth_4.1.2_to_4.1.3`
-- -----------------------------------------------------------------------------
-- Stored procedure to perform the authorization changes from version 4.1.2
-- to 4.1.3
-- -----------------------------------------------------------------------------
DROP PROCEDURE IF EXISTS `msm_auth_4.1.2_to_4.1.3`%%
CREATE PROCEDURE `msm_auth_4.1.2_to_4.1.3`()
SQL SECURITY INVOKER
BEGIN
END%%

-- -----------------------------------------------------------------------------
-- PROCEDURE `mysql_rest_service_metadata`.`msm_auth_4.1.3`
-- -----------------------------------------------------------------------------
-- Stored procedure to setup authorization for version 4.1.3
-- -----------------------------------------------------------------------------
DROP PROCEDURE IF EXISTS `msm_auth_4.1.3`%%
CREATE PROCEDURE `msm_auth_4.1.3`()
SQL SECURITY INVOKER
BEGIN
    -- -----------------------------------------------------
    -- Create roles for the MySQL REST Service

    -- The mysql_rest_service_admin ROLE allows to fully manage the REST services
    -- The mysql_rest_service_schema_admin ROLE allows to manage the database schemas assigned to REST services
    -- The mysql_rest_service_dev ROLE allows to develop new REST objects for given REST services and upload static files
    -- The mysql_rest_service_user ROLE can be assigned to MySQL users that are granted access via MySQL Internal authentication.
    -- The mysql_rest_service_meta_provider ROLE is used by the MySQL Router to read the mrs metadata and make inserts into the auth_user table
    -- The mysql_rest_service_data_provider ROLE is used by the MySQL Router to read the actual schema data that is exposed via REST

    CREATE ROLE IF NOT EXISTS 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev', 'mysql_rest_service_user',
        'mysql_rest_service_meta_provider', 'mysql_rest_service_data_provider';

    -- Allow the 'mysql_rest_service_user' role to access the same data as 'mysql_rest_service_data_provider'
    GRANT 'mysql_rest_service_data_provider' TO 'mysql_rest_service_user';

    -- Allow the creation of temporary tables
    GRANT CREATE TEMPORARY TABLES ON `mysql_rest_service_metadata`.*
        TO 'mysql_rest_service_data_provider';

    -- `mysql_rest_service_metadata`.`msm_schema_version`
    GRANT SELECT ON `mysql_rest_service_metadata`.`msm_schema_version`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev', 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`audit_log`
    GRANT SELECT ON `mysql_rest_service_metadata`.`audit_log`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev', 'mysql_rest_service_meta_provider';

    -- -----------------------------------------------------
    -- Config

    -- `mysql_rest_service_metadata`.`config`
    GRANT SELECT, UPDATE
        ON `mysql_rest_service_metadata`.`config`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin';
    GRANT SELECT ON `mysql_rest_service_metadata`.`config`
        TO 'mysql_rest_service_meta_provider', 'mysql_rest_service_dev';

    -- `mysql_rest_service_metadata`.`redirect`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`redirect`
        TO 'mysql_rest_service_admin';
    GRANT SELECT ON `mysql_rest_service_metadata`.`redirect`
        TO 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev', 'mysql_rest_service_meta_provider';

    -- -----------------------------------------------------
    -- Service

    -- `mysql_rest_service_metadata`.`url_host`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`url_host`
        TO 'mysql_rest_service_admin';
    GRANT SELECT ON `mysql_rest_service_metadata`.`url_host`
        TO 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev', 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`url_host_alias`
    GRANT SELECT, INSERT, DELETE
        ON `mysql_rest_service_metadata`.`url_host_alias`
        TO 'mysql_rest_service_admin';
    GRANT SELECT ON `mysql_rest_service_metadata`.`url_host_alias`
        TO 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev', 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`service`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`service`
        TO 'mysql_rest_service_admin';
    GRANT SELECT ON `mysql_rest_service_metadata`.`service`
        TO 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev', 'mysql_rest_service_meta_provider';

    -- -----------------------------------------------------
    -- Schema Objects

    -- `mysql_rest_service_metadata`.`db_schema`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`db_schema`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin';
    GRANT SELECT ON `mysql_rest_service_metadata`.`db_schema`
        TO 'mysql_rest_service_meta_provider', 'mysql_rest_service_dev';

    -- `mysql_rest_service_metadata`.`db_object`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`db_object`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev';
    GRANT SELECT ON `mysql_rest_service_metadata`.`db_object`
        TO 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`mrs_db_object_row_group_security`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`mrs_db_object_row_group_security`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev';
    GRANT SELECT ON `mysql_rest_service_metadata`.`mrs_db_object_row_group_security`
        TO 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`object`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`object`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev';
    GRANT SELECT ON `mysql_rest_service_metadata`.`object`
        TO 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`object_field`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`object_field`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev';
    GRANT SELECT ON `mysql_rest_service_metadata`.`object_field`
        TO 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`object_reference`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`object_reference`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev';
    GRANT SELECT ON `mysql_rest_service_metadata`.`object_reference`
        TO 'mysql_rest_service_meta_provider';

    -- -----------------------------------------------------
    -- Static Content

    -- `mysql_rest_service_metadata`.`content_set`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`content_set`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev';
    GRANT SELECT ON `mysql_rest_service_metadata`.`content_set`
        TO 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`content_file`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`content_file`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev';
    GRANT SELECT ON `mysql_rest_service_metadata`.`content_file`
        TO 'mysql_rest_service_meta_provider';


    -- `mysql_rest_service_metadata`.`content_set_has_obj_def`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`content_set_has_obj_def`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev';
    GRANT SELECT ON `mysql_rest_service_metadata`.`content_set_has_obj_def`
        TO 'mysql_rest_service_meta_provider';

    -- -----------------------------------------------------
    -- User Authentication

    -- `mysql_rest_service_metadata`.`auth_app`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`auth_app`
        TO 'mysql_rest_service_admin';
    GRANT SELECT ON `mysql_rest_service_metadata`.`auth_app`
        TO 'mysql_rest_service_meta_provider', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev';

    -- `mysql_rest_service_metadata`.`service_has_auth_app`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`service_has_auth_app`
        TO 'mysql_rest_service_admin';
    GRANT SELECT ON `mysql_rest_service_metadata`.`service_has_auth_app`
        TO 'mysql_rest_service_meta_provider', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev';

    -- `mysql_rest_service_metadata`.`auth_vendor`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`auth_vendor`
        TO 'mysql_rest_service_admin';
    GRANT SELECT ON `mysql_rest_service_metadata`.`auth_vendor`
        TO 'mysql_rest_service_meta_provider', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev';

    -- `mysql_rest_service_metadata`.`mrs_user`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`mrs_user`
        TO 'mysql_rest_service_admin';
    GRANT SELECT, INSERT, UPDATE ON `mysql_rest_service_metadata`.`mrs_user`
        TO 'mysql_rest_service_meta_provider';
    GRANT SELECT ON `mysql_rest_service_metadata`.`mrs_user`
        TO 'mysql_rest_service_data_provider';

    -- -----------------------------------------------------
    -- User Hierarchy

    -- `mysql_rest_service_metadata`.`mrs_user_hierarchy`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`mrs_user_hierarchy`
        TO 'mysql_rest_service_admin';
    GRANT SELECT, INSERT ON `mysql_rest_service_metadata`.`mrs_user_hierarchy`
        TO 'mysql_rest_service_meta_provider';
    GRANT SELECT ON `mysql_rest_service_metadata`.`mrs_user_hierarchy`
        TO 'mysql_rest_service_data_provider';

    -- `mysql_rest_service_metadata`.`mrs_user_hierarchy_type`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`mrs_user_hierarchy_type`
        TO 'mysql_rest_service_admin';
    GRANT SELECT, INSERT ON `mysql_rest_service_metadata`.`mrs_user_hierarchy_type`
        TO 'mysql_rest_service_meta_provider';

    -- -----------------------------------------------------
    -- User Roles

    -- `mysql_rest_service_metadata`.`mrs_user_has_role`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`mrs_user_has_role`
        TO 'mysql_rest_service_admin';
    GRANT SELECT, INSERT ON `mysql_rest_service_metadata`.`mrs_user_has_role`
        TO 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`mrs_role`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`mrs_role`
        TO 'mysql_rest_service_admin';
    GRANT SELECT, INSERT ON `mysql_rest_service_metadata`.`mrs_role`
        TO 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`mrs_privilege`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`mrs_privilege`
        TO 'mysql_rest_service_admin';
    GRANT SELECT, INSERT ON `mysql_rest_service_metadata`.`mrs_privilege`
        TO 'mysql_rest_service_meta_provider';

    -- -----------------------------------------------------
    -- User Group Management

    -- `mysql_rest_service_metadata`.`mrs_user_has_group`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`mrs_user_has_group`
        TO 'mysql_rest_service_admin';
    GRANT SELECT, INSERT ON `mysql_rest_service_metadata`.`mrs_user_has_group`
        TO 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`mrs_user_group`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`mrs_user_group`
        TO 'mysql_rest_service_admin';
    GRANT SELECT, INSERT ON `mysql_rest_service_metadata`.`mrs_user_group`
        TO 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`mrs_user_group_has_role`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`mrs_user_group_has_role`
        TO 'mysql_rest_service_admin';
    GRANT SELECT, INSERT ON `mysql_rest_service_metadata`.`mrs_user_group_has_role`
        TO 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`mrs_group_hierarchy_type`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`mrs_group_hierarchy_type`
        TO 'mysql_rest_service_admin';
    GRANT SELECT, INSERT ON `mysql_rest_service_metadata`.`mrs_group_hierarchy_type`
        TO 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`mrs_user_group_hierarchy`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`mrs_user_group_hierarchy`
        TO 'mysql_rest_service_admin';
    GRANT SELECT, INSERT ON `mysql_rest_service_metadata`.`mrs_user_group_hierarchy`
        TO 'mysql_rest_service_meta_provider';
    GRANT SELECT ON `mysql_rest_service_metadata`.`mrs_user_group_hierarchy`
        TO 'mysql_rest_service_data_provider';

    -- -----------------------------------------------------
    -- Router Management

    -- `mysql_rest_service_metadata`.`router`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`router`
        TO 'mysql_rest_service_admin';
    GRANT SELECT, INSERT, UPDATE ON `mysql_rest_service_metadata`.`router`
        TO 'mysql_rest_service_meta_provider';
    GRANT SELECT
        ON `mysql_rest_service_metadata`.`router`
        TO 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev';

    -- `mysql_rest_service_metadata`.`router_status`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`router_status`
        TO 'mysql_rest_service_admin';
    GRANT SELECT, INSERT, UPDATE ON `mysql_rest_service_metadata`.`router_status`
        TO 'mysql_rest_service_meta_provider';
    GRANT SELECT ON `mysql_rest_service_metadata`.`router_status`
        TO 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev';

    -- `mysql_rest_service_metadata`.`router_general_log`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`router_general_log`
        TO 'mysql_rest_service_admin';
    GRANT INSERT ON `mysql_rest_service_metadata`.`router_general_log`
        TO 'mysql_rest_service_meta_provider';
    GRANT SELECT ON `mysql_rest_service_metadata`.`router_general_log`
        TO 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev';

    -- `mysql_rest_service_metadata`.`router_session`
    GRANT SELECT, INSERT, UPDATE, DELETE
        ON `mysql_rest_service_metadata`.`router_session`
        TO 'mysql_rest_service_admin';
    GRANT SELECT, INSERT ON `mysql_rest_service_metadata`.`router_session`
        TO 'mysql_rest_service_meta_provider';
    GRANT SELECT ON `mysql_rest_service_metadata`.`router_session`
        TO 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev';

    -- `mysql_rest_service_metadata`.`router_services`
    GRANT SELECT ON `mysql_rest_service_metadata`.`router_services`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev', 'mysql_rest_service_meta_provider';

    -- -----------------------------------------------------
    -- Procedures and Functions

    -- `mysql_rest_service_metadata`.`get_sequence_id`

    GRANT EXECUTE ON FUNCTION `mysql_rest_service_metadata`.`get_sequence_id`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev', 'mysql_rest_service_meta_provider', 'mysql_rest_service_data_provider';

    -- `mysql_rest_service_metadata`.`table_columns_with_references`
    GRANT EXECUTE ON PROCEDURE `mysql_rest_service_metadata`.`table_columns_with_references`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev', 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`sdk_service_data`
    GRANT EXECUTE ON PROCEDURE `mysql_rest_service_metadata`.`sdk_service_data`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev', 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`restore_roles`
    GRANT EXECUTE ON PROCEDURE `mysql_rest_service_metadata`.`restore_roles`
        TO 'mysql_rest_service_admin';


    -- -----------------------------------------------------
    -- Views

    -- `mysql_rest_service_metadata`.`mrs_user_schema_version`
    GRANT SELECT
      ON `mysql_rest_service_metadata`.`mrs_user_schema_version`
      TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev', 'mysql_rest_service_meta_provider';

    -- `mysql_rest_service_metadata`.`object_fields_with_references`
    GRANT SELECT
        ON `mysql_rest_service_metadata`.`object_fields_with_references`
        TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev', 'mysql_rest_service_meta_provider';

    -- -----------------------------------------------------
    -- Grant the necessary mysql_tasks privileges to the MySQL REST Service roles
    GRANT 'mysql_task_user' TO 'mysql_rest_service_admin', 'mysql_rest_service_schema_admin', 'mysql_rest_service_dev', 'mysql_rest_service_user',
    	'mysql_rest_service_meta_provider', 'mysql_rest_service_data_provider';
END%%

-- -----------------------------------------------------------------------------
-- PROCEDURE `mysql_rest_service_metadata`.`msm_auth`
-- -----------------------------------------------------------------------------
-- Stored procedure to setup authorization
-- -----------------------------------------------------------------------------
DROP PROCEDURE IF EXISTS `msm_auth`%%
CREATE PROCEDURE `msm_auth`()
SQL SECURITY INVOKER
BEGIN
    DECLARE version_str VARCHAR(255);

    SET version_str = @msm_current_version;

    IF @msm_schema_init THEN
        CALL `mysql_rest_service_metadata`.`msm_auth_4.1.3`();
    ELSE
        IF version_str = '4.0.0' THEN
            CALL `mysql_rest_service_metadata`.`msm_auth_4.0.0_to_4.0.1`();
            SET version_str = '4.0.1';
        END IF;

        IF version_str = '4.0.1' THEN
            CALL `mysql_rest_service_metadata`.`msm_auth_4.0.1_to_4.0.2`();
            SET version_str = '4.0.2';
        END IF;

        IF version_str = '4.0.2' THEN
            CALL `mysql_rest_service_metadata`.`msm_auth_4.0.2_to_4.0.3`();
            SET version_str = '4.0.3';
        END IF;

        IF version_str = '4.0.3' THEN
            CALL `mysql_rest_service_metadata`.`msm_auth_4.0.3_to_4.0.4`();
            SET version_str = '4.0.4';
        END IF;

        IF version_str = '4.0.4' THEN
            CALL `mysql_rest_service_metadata`.`msm_auth_4.0.4_to_4.1.0`();
            SET version_str = '4.1.0';
        END IF;

        IF version_str = '4.1.0' THEN
            CALL `mysql_rest_service_metadata`.`msm_auth_4.1.0_to_4.1.1`();
            SET version_str = '4.1.1';
        END IF;

        IF version_str = '4.1.1' THEN
            CALL `mysql_rest_service_metadata`.`msm_auth_4.1.1_to_4.1.2`();
            SET version_str = '4.1.2';
        END IF;

        IF version_str = '4.1.2' THEN
            CALL `mysql_rest_service_metadata`.`msm_auth_4.1.2_to_4.1.3`();
            SET version_str = '4.1.3';
        END IF;

        CALL `mysql_rest_service_metadata`.`msm_auth_4.1.3`();
    END IF;
END%%

DELIMITER ;

-- -----------------------------------------------------------------------------
-- Execute the installation/upgrade procedure to setup authorization
-- -----------------------------------------------------------------------------

CALL `msm_auth`();

-- -----------------------------------------------------------------------------
-- Drop the stored procedures used for authorization setup and updates.
-- -----------------------------------------------------------------------------

USE `mysql_rest_service_metadata`;
DROP PROCEDURE `msm_auth_4.0.0_to_4.0.1`;
USE `mysql_rest_service_metadata`;
DROP PROCEDURE `msm_auth_4.0.1_to_4.0.2`;
USE `mysql_rest_service_metadata`;
DROP PROCEDURE `msm_auth_4.0.2_to_4.0.3`;
USE `mysql_rest_service_metadata`;
DROP PROCEDURE `msm_auth_4.0.3_to_4.0.4`;
USE `mysql_rest_service_metadata`;
DROP PROCEDURE `msm_auth_4.0.4_to_4.1.0`;
USE `mysql_rest_service_metadata`;
DROP PROCEDURE `msm_auth_4.1.0_to_4.1.1`;
USE `mysql_rest_service_metadata`;
DROP PROCEDURE `msm_auth_4.1.1_to_4.1.2`;
USE `mysql_rest_service_metadata`;
DROP PROCEDURE `msm_auth_4.1.2_to_4.1.3`;
USE `mysql_rest_service_metadata`;
DROP PROCEDURE `msm_auth_4.1.3`;
DROP PROCEDURE `msm_auth`;


-- #############################################################################
-- MSM Section 910: Database Schema Version
-- -----------------------------------------------------------------------------
-- Setting the correct database schema version
-- #############################################################################

USE `mysql_rest_service_metadata`;

CREATE OR REPLACE SQL SECURITY INVOKER
VIEW `msm_schema_version` (`major`,`minor`,`patch`) AS
SELECT 4, 1, 3;


-- #############################################################################
-- MSM Section 920: Server Variable Restoration
-- -----------------------------------------------------------------------------
-- Restore the modified server variables to their original state.
-- #############################################################################

SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
