web-dev-qa-db-ja.com

トリガー内の動的SQLの代替?

私のデータベースには次のテーブルがあります。

    TAG
    ----------------------
    | tag_id | tag_name  |
    ----------------------

    TAG_VALUE: Stores values associated to each tag
    ----------------------------------------
    | tag_id | insertion_timestamp | value |
    ----------------------------------------

    ALARM: Defines alarms for each tag
    -------------------------------------
    | alarm_id | tag_id | function_name |
    -------------------------------------

    ALARM ACTIVATION: Stores information regarding each time the alarms were triggered
    -----------------------------------------------------
    | alarm_id | activation_timestamp | activation_value|
    -----------------------------------------------------

したがって、新しい値がTAG_VALUEに挿入されるたびに、その新しい値がそのタグに関連付けられているアラームをトリガーするかどうかを確認する必要があります。アラーム間に共通の基準がないので、アラームをトリガーするかどうかを決定するために後で使用される関数名を保存しています。

TAG_VALUEには次のAFTER_INSERTトリガーがあります:

CREATE DEFINER=`root`@`localhost` TRIGGER `mydb`.`tag_value_AFTER_INSERT` AFTER INSERT ON `tag_value` FOR EACH ROW
BEGIN
call sp_alarm_handler(NEW.tag_value, NEW.tag_id);
END

また、sp_alarm_handlerは次のようにコーディングされています。

CREATE DEFINER=`root`@`localhost` PROCEDURE `sp_alarm_handler`(IN tag_value VARCHAR(255), IN value_tag_id INT(11))
    BEGIN

    DECLARE exit_loop BOOLEAN;
    DECLARE v_alarm_id INT(11);
    DECLARE function_name VARCHAR(255);
    DECLARE value_triggers_alarm TINYINT(1);

    DECLARE custom_alarm_cur CURSOR FOR
    Select alarm_id, function_name From vw_custom_alarms where tag_id = value_tag_id;


    DECLARE CONTINUE HANDLER FOR NOT FOUND SET exit_loop = TRUE;

    OPEN custom_alarm_cur;

    custom_alarm_lp: LOOP

        FETCH custom_alarm_cur into v_alarm_id, function_name;

        IF exit_loop THEN
        leave custom_alarm_lp;
        END IF;

        #************************************************
        #*********HERE'S THE DYNAMIC SQL PIECE'**********
        #************************************************
        set @query = CONCAT('Select ',function_name,'(',tag_value,')', 'into @value_triggers_alarm');
        PREPARE stmt FROM @query;
        Execute stmt;

        IF fn_is_alarm_active(v_alarm_id) = 0 THEN

            If @value_triggers_alarm = 1 THEN
                    INSERT INTO alarm_activation(alarm_id, activation_timestamp, activation_value)
                    VALUES (v_alarm_id, NOW(), tag_value);
            end if;

        ELSE IF @value_triggers_alarm = 0 THEN
            call sp_deactivate_alarm(v_alarm_id);

            END IF;
        END IF;



    END LOOP custom_alarm_lp;
    Close custom_alarm_cur;

    END

これによりエラー1336:1336:動的SQLはストアドファンクションまたはトリガーでは許可されません私の場合、回避策はありますか?

3
minusnine

チェックする項目のリストが一定である場合は、テストを構築するのではなく、それらを詳しく説明してください。 (実行不可能)

しかし、おそらくそうではありません...

プランA:トリガーを呼び出す必要があるINSERTPROCEDURE呼び出しに置き換えます。このルーチンには、INSERTに加えて、提示した残りのコードが含まれています。

プランB:プランAと同じですが、アプリケーションコードで行います。

注:AまたはBのどちらかをBEGIN...COMMITでラップして、「アトミック」にします。

結論:トリガーはすべてを実行できるわけではありません。

Aの適用(これにより、他のユーザーがストアドプロシージャをバイパスするのを防ぐことができると思います。)

  • CREATE USER special@... ...-新しいユーザー
  • GRANT INSERT ON db.tbl TO special@...-入る
  • ストアドプロシージャはSECURITY special@...-procを「特別な」ものとして実行し、INSERTsを実行できるようにします
  • 削除する INSERT PRIVILEGE他のテーブルから。 (これは乱雑になるか、おそらく不要になります。)(テーブルが現在誰もアクセスできない別のデータベースにある場合、may役立ちます。)
  • (私の直感は、もう1つのステップがあると言います。「読者のための演習」として残しておきます。)
2
Rick James