:meta-keywords: cubrid trigger, database trigger, trigger condition, trigger action, trigger debugging, trigger example
:meta-description: CUBRID trigger definition, manipulation and mechanics.

******
트리거
******

.. _create-trigger:

CREATE TRIGGER
==============

트리거 정의를 위한 가이드라인
-----------------------------

트리거 정의로 다양하고 강력한 기능을 만들 수 있다. 트리거를 생성하기 전에 다음과 같은 정의 사항을 고려해야 한다.

*   **트리거의 조건 영역 표현식이 데이터베이스에 예측할 수 없는 결과(side effect)를 가져오지는 않는가?**

    SQL 문을 예측이 가능한 범위 내에서 사용해야 한다.

*   **트리거의 실행 영역이 트리거의 이벤트 대상으로 주어진 테이블을 변경하지는 않는가?**

    이러한 유형의 설계가 트리거의 정의에서 사용이 금지되어 있지는 않지만, 무한 루프로 빠지는 트리거가 만들어질 수 있어 주의해서 사용해야 한다. 트리거 실행 영역이 이벤트 대상 테이블을 수정할 때, 같은 트리거가 다시 불려질 수 있다. 또한 **WHERE** 절을 포함하는 문장에 의해 트리거가 발생하면, 해당 트리거는 **WHERE** 절에 의해 수행되는 테이블에는 일반적으로 부작용이 없다.

*   **트리거가 불필요한 오버헤드를 만들어 내고 있지는 않는가?**

    원하는 동작이 소스 내에서 조금 더 효과적으로 표현될 수 있다면 직접 소스 내에서 구현하도록 한다.

*   **트리거가 재귀적으로 실행되고 있지는 않는가?**

    트리거의 실행 영역이 트리거를 부르고 이 트리거가 다시 처음 트리거를 부르면 재귀 루프(recursive loop)가 데이터베이스에 만들어진다. 재귀 루프가 만들어지면, 트리거가 정확히 수행되지 않거나 진행 중인 루프를 단절하기 위해 현재 세션을 강제로 종료해야 할 수도 있다.

*   **트리거의 정의는 유일한가?**

    동일한 테이블에서 정의된 트리거나, 동일한 실행 영역에서 시작된 트리거는 복구할 수 없는 에러의 원인이 된다. 동일한 테이블에 있는 트리거는 다른 트리거 이벤트를 가져야 한다. 또한, 트리거의 우선순위는 명시적으로 정의되어 있거나 모호하지 않아야 한다.

트리거 정의 구문
----------------

**CREATE TRIGGER** 문을 사용하여 새로운 트리거를 생성하고, 트리거 대상, 실행 조건과 수행할 내용을 정의할 수 있다. 트리거는 데이터베이스 객체로서, 특정 이벤트가 대상 테이블에 대해 발생하면 정의된 동작을 수행한다. ::

    CREATE TRIGGER [schema_name.]trigger_name
    [ STATUS { ACTIVE | INACTIVE } ]
    [ PRIORITY key ]
    <event_time> <event_type> [<event_target>]
    [ IF condition ]
    EXECUTE [ AFTER | DEFERRED ] action 
    [COMMENT 'trigger_comment'];
     
    <event_time> ::=
        BEFORE |
        AFTER  |
        DEFERRED
     
    <event_type> ::=
        INSERT |
        STATEMENT INSERT |
        UPDATE |
        STATEMENT UPDATE |
        DELETE |
        STATEMENT DELETE |
        ROLLBACK |
        COMMIT
     
    <event_target> ::=
        ON [schema_name.]table_name |
        ON [schema_name.]table_name [ (column_name) ]
     
    <condition> ::=
        expression
     
    <action> ::=
        REJECT |
        INVALIDATE TRANSACTION |
        PRINT message_string |
        INSERT statement |
        UPDATE statement |
        DELETE statement

*   *schema_name*: 트리거의 스키마 이름을 지정한다. 생략하면 현재 세션의 스키마 이름을 사용한다.
*   *trigger_name*: 정의하려는 트리거의 이름을 지정한다.
*   [ **STATUS** { **ACTIVE** | **INACTIVE** } ]: 트리거의 상태를 정의한다(정의하지 않을 경우 기본값은 **ACTIVE** ).

    *   **ACTIVE** 상태인 경우 관련 이벤트가 발생할 때마다 트리거를 실행한다.
    *   **INACTIVE** 상태인 경우 관련 이벤트가 발생하여도 트리거를 실행하지 않는다. 트리거의 활성 여부는 변경할 수 있다. 자세한 내용은 :ref:`alter-trigger` 을 참조한다.

*   [ **PRIORITY** *key* ]: 하나의 이벤트에 대해서 다수의 트리거가 불려질 경우 실행되는 우선순위를 부여한다. *key* 값은 반드시 음수가 아닌 부동 소수점 값이어야 한다. 우선순위를 정의하지 않을 경우 가장 낮은 우선순위인 0을 할당한다. 같은 우선순위를 가지는 트리거는 임의의 순서로 실행된다. 트리거의 우선순위는 변경할 수 있다. 자세한 내용은 :ref:`alter-trigger` 을 참조한다.

*   <*event_time*>: 트리거의 조건 영역과 실행 영역이 실행되는 시점을 지정하며 **BEFORE**, **AFTER**, **DEFERRED** 가 있다. 자세한 내용은 :ref:`trigger-event-time` 을 참조한다.
*   <*event_type*>: 트리거 타입은 사용자 트리거와 테이블 트리거로 나뉜다. 자세한 내용은 :ref:`trigger-event-type` 을 참조한다.
*   <*event_target*>: 이벤트 대상은 트리거가 호출되기 위한 대상을 지정할 때 쓰인다. 자세한 내용은 :ref:`trigger-event-target` 을 참조한다.

*   <*condition*>: 트리거의 조건영역을 지정한다. 자세한 내용은 :ref:`trigger-condition` 을 참조한다.
*   <*action*>: 트리거의 실행영역을 지정한다. 자세한 내용은 :ref:`trigger-action` 을 참조한다.
*   *trigger_comment*: 트리거의 커멘트를 지정한다.

.. note::

    *   **DBA** 와 **DBA** 멤버는 다른 스키마에 트리거를 생성할 수 있다. 사용자가 **DBA** 도 아니고 **DBA** 멤버도 아니면 해당 사용자의 스키마에서만 트리거를 생성할 수 있다.

다음은 *participant* 테이블의 레코드를 갱신할 때 획득 메달의 개수가 0보다 작을 경우 갱신을 거절하는 트리거를 생성하는 예제이다.
2004년도 올림픽에 한국이 획득한 금메달의 개수를 음수로 갱신할 경우 갱신이 거절되는 것을 알 수 있다.

.. code-block:: sql

    CREATE TRIGGER medal_trigger
    BEFORE UPDATE ON participant
    IF new.gold < 0 OR new.silver < 0 OR new.bronze < 0
    EXECUTE REJECT;
     
    UPDATE participant SET gold = -5 WHERE nation_code = 'KOR'
    AND host_year = 2004;

::

    ERROR: The operation has been rejected by trigger "medal_trigger".

.. _trigger-event-time:

이벤트 시점
-----------

트리거의 조건 영역과 실행 영역이 실행되는 시점을 지정한다. 이벤트 시점의 종류에는 **BEFORE**, **AFTER**, **DEFERRED** 가 있다.

*   **BEFORE**: 이벤트가 처리되기 전에 조건을 검사한다.
*   **AFTER**: 이벤트가 처리된 후에 조건을 검사한다.
*   **DEFERRED**: 이벤트에 대한 트랜잭션의 끝에서 조건을 검사한다. **DEFERRED** 로 지정할 경우 이벤트 타입에 **COMMIT** 이나 **ROLLBACK** 을 사용할 수 없다.

트리거 타입
-----------

**사용자 트리거(User Trigger)**

*   데이터베이스의 특정 사용자와 관련된 트리거를 사용자 트리거(user trigger)라고 한다.
*   사용자 트리거는 이벤트 대상이 없으며 트리거의 소유자(트리거를 생성한 사용자)에 의해서만 실행된다.
*   사용자 트리거를 정의하는 이벤트 타입은 **COMMIT** 과 **ROLLBACK** 이 있다.

**테이블 트리거(Table Trigger)**

*   특정 테이블을 이벤트 대상으로 가지는 트리거를 테이블 트리거(클래스 트리거)라 한다.
*   테이블 트리거는 대상 테이블에 **SELECT** 권한을 가지는 모든 사용자가 볼 수 있다.
*   테이블 트리거를 정의하는 이벤트 타입은 인스턴스 이벤트와 문장 이벤트가 있다.

.. _trigger-event-type:

트리거 이벤트 타입
------------------

*   인스턴스 이벤트(instance event) : 인스턴스 이벤트는 이벤트 연산의 단위가 인스턴스(레코드)인 이벤트 타입을 말한다. 인스턴스 이벤트의 종류는 다음과 같다.

    *   **INSERT**
    *   **UPDATE**
    *   **DELETE**

*   문장 이벤트(statement event): 이벤트 타입을 문장 이벤트로 정의하면 주어진 문장(이벤트)에 의해 영향을 받는 객체(인스턴스)가 많더라도, 트리거는 문장이 시작할 때 한 번만 불려지게 된다. 문장 이벤트의 종류는 다음과 같다.

    *   **STATEMENT INSERT**
    *   **STATEMENT UPDATE**
    *   **STATEMENT DELETE**

*   기타 이벤트: **COMMIT** 과 **ROLLBACK** 은 개별적인 인스턴스에는 적용할 수 없다.

    *   **COMMIT**
    *   **ROLLBACK**

다음은 인스턴스 이벤트를 사용하는 예제이다. *example* 트리거는 데이터베이스 갱신에 의해 영향을 받는 각각의 인스턴스에 대해서 한번씩 불려진다. 예를 들어, *history* 테이블의 다섯 개 인스턴스의 *score* 를 변경했다면, 이 트리거는 다섯 번 불려진다.

.. code-block:: sql

    CREATE TABLE update_logs(event_code INTEGER, score VARCHAR(10), dt DATETIME);
    
    CREATE TRIGGER example
    BEFORE UPDATE ON history(score)
    EXECUTE INSERT INTO update_logs VALUES (obj.event_code, obj.score, SYSDATETIME);

만약 *score* 칼럼의 첫 번째 인스턴스가 갱신되기 전에 트리거가 한 번만 호출되게 하려면, 아래의 예와 같이 **STATEMENT UPDATE** 형식을 사용한다.

다음은 문장 이벤트를 사용하는 예제이다. 문장 이벤트를 지정하면 갱신의 영향을 받는 인스턴스가 많더라도, 첫 번째 인스턴스가 갱신되기 전에 트리거가 한 번만 불려지게 된다.

.. code-block:: sql

    CREATE TRIGGER example
    BEFORE STATEMENT UPDATE ON history(score)
    EXECUTE PRINT 'There was an update on history table';

.. note::

    *   이벤트 타입으로 인스턴스 이벤트와 문장 이벤트를 지정할 경우에는 반드시 이벤트 대상을 명시해야 한다.
    *   **COMMIT** 과 **ROLLBACK** 은 이벤트 대상을 가질 수 없다.

.. _trigger-event-target:

트리거 이벤트 대상
------------------

이벤트 대상은 트리거가 호출되기 위한 대상을 지정할 때 쓰인다. 트리거 이벤트의 대상은 테이블명 혹은 테이블명과 칼럼명으로 지정할 수 있으며 칼럼명을 지정하면 해당 칼럼이 이벤트의 영향을 받을 때에만 트리거가 불려진다. 만약 칼럼을 지정하지 않으면 지정된 테이블 내에 어떤 칼럼이 영향을 받더라도 트리거가 호출된다. 오직 **UPDATE**, **STATEMENT UPDATE** 이벤트만이 이벤트 대상에 칼럼을 지정할 수 있다.

다음은 *example* 트리거의 이벤트 대상을 *history* 테이블의 *score* 칼럼으로 지정한 예제이다.

.. code-block:: sql

    CREATE TABLE update_logs(event_code INTEGER, score VARCHAR(10), dt DATETIME);
    
    CREATE TRIGGER example
    BEFORE UPDATE ON history(score)
    EXECUTE INSERT INTO update_logs VALUES (obj.event_code, obj.score, SYSDATETIME);

이벤트 타입과 대상 조합
-----------------------

트리거를 호출하는 데이터베이스 이벤트는 트리거 이벤트 타입과 트리거 정의 내의 이벤트 대상에 의해 식별된다. 다음은 트리거 이벤트 타입과 대상 조합, 트리거 이벤트가 나타내는 CUBRID 데이터베이스 이벤트의 활동을 표로 정리한 것이다.

+--------------+--------------+-----------------------------------------------------------------+
| 이벤트 타입  | 이벤트 대상  | 대응되는 데이터베이스 활동                                      |
+==============+==============+=================================================================+
| **UPDATE**   | 테이블       | 테이블에 **UPDATE** 문이 실행되었을 때 트리거가 호출된다.       |
+--------------+--------------+-----------------------------------------------------------------+
| **INSERT**   | 테이블       | 테이블에 **INSERT** 문이 실행되었을 때 트리거가 호출된다.       |
+--------------+--------------+-----------------------------------------------------------------+
| **DELETE**   | 테이블       | 테이블에 **DELETE** 문이 실행되었을 때 트리거가 호출된다.       |
+--------------+--------------+-----------------------------------------------------------------+
| **COMMIT**   | 없음         | 데이터베이스 트랜잭션이 커밋되었을 때 트리거가 호출된다.        |
+--------------+--------------+-----------------------------------------------------------------+
| **ROLLBACK** | 없음         | 데이터베이스의 트랜잭션이 롤백되었을 때 트리거가 호출된다.      |
+--------------+--------------+-----------------------------------------------------------------+

.. _trigger-condition:

트리거 조건 영역
----------------

트리거를 정의할 때 조건 영역을 정의하여 트리거의 수행 영역에 대한 수행 여부를 결정한다.

*   트리거 조건 영역이 기술된다면, 참 또는 거짓을 평가할 수 있는 단독적인 복합 표현식으로 쓰여질 수 있다. 이 경우에 표현식은 **SELECT** 문의 **WHERE** 절에 허용되는 산술 연산자와 논리 연산자를 포함할 수 있다. 조건 영역이 참이면, 트리거 실행 영역이 수행되고, 거짓이면 실행되지 않는다.

*   트리거의 조건 영역을 생략하면 조건 없는 트리거(unconditional trigger)가 되며 트리거가 호출될 때 항상 트리거의 실행 영역이 수행된다.

다음은 조건 영역 내의 표현식에 상관명을 이용한 예제이다. 이벤트 타입이 **INSERT**, **UPDATE**, **DELETE** 인 경우에, 조건 영역 내의 표현식은 특정 칼럼 값에 접근하기 위하여 상관명 **obj**, **new**, **old** 를 사용할 수 있다. 예제에서 *example* 트리거는 칼럼의 새로운 값을 이용해서 조건 영역을 검사하기 위해 트리거 조건 영역에 **new** 를 칼럼 이름 앞에 사용하였다.

.. code-block:: sql

    CREATE TRIGGER example
    BEFORE UPDATE ON participant
    IF new.gold < 0 OR new.silver < 0 OR new.bronze < 0
    EXECUTE REJECT;

다음은 조건 영역 내의 표현식에 **SELECT** 문을 사용한 예제이다. 예제의 트리거는 집계함수 **COUNT** (\*)를 사용하는 **SELECT** 문을 사용하여 그 값과 상수를 비교한다. **SELECT** 문은 반드시 괄호로 싸여 있어야 하고, 표현식의 마지막에 위치해야 한다.

.. code-block:: sql

    CREATE TRIGGER example
    BEFORE INSERT ON participant
    IF 1000 >  (SELECT COUNT(*) FROM participant)
    EXECUTE REJECT;

.. note::

    트리거 조건 영역에 주어진 표현식은 조건 영역이 수행되는 동안에 메서드가 호출되면 데이터베이스에 부작용을 초래할 수 있다. 트리거 조건 영역은 데이터베이스에 생각지 못한 부작용이 발생하지 않도록 구성해야 한다.

상관명(correlation name)
------------------------

트리거를 정의할 때 상관명을 사용하여 대상 테이블의 칼럼 값에 접근할 수 있다. 상관명은 실제적으로 트리거를 부르는 데이터베이스 연산에 의해 영향을 받는 인스턴스를 나타낸다. 상관명은 트리거의 조건 영역이나 실행 영역에도 기술할 수 있다.

상관명의 종류에는 **new**, **old**, **obj** 가 있으며 이러한 상관명은 인스턴스 트리거에서 **INSERT**, **UPDATE**, **DELETE** 의 이벤트 타입을 가지고 있는 트리거에서만 사용할 수 있다.

상관명의 사용은 아래 표와 같이 트리거 조건 영역에 정의된 이벤트 시점에 의해 더욱 제한된다.

+------------+------------+-----------------------+
|            | BEFORE     | AFTER or DERERRED     |
+============+============+=======================+
| **INSERT** | **new**    | **obj**               |
+------------+------------+-----------------------+
| **UPDATE** | **obj**    | **obj**               |
|            |            |                       |
|            | **new**    | **old** (AFTER)       |
+------------+------------+-----------------------+
| **DELETE** | **obj**    | N/A                   |
+------------+------------+-----------------------+

+---------+-------------------------------------------------------------------------------------------------------------+
| 상관명  | 대표 속성 값                                                                                                |
+=========+=============================================================================================================+
| **obj** | 인스턴스의 현재 속성 값을 나타낸다. 인스턴스가 갱신되거나 삭제되기 전에 속성값에 접근하기 위해서 사용한다.  |
|         | 그리고 인스턴스가 갱신되거나 삽입된 후에 속성 값에 접근하기 위해 사용한다.                                  |
+---------+-------------------------------------------------------------------------------------------------------------+
| **new** | 삽입이나 갱신 연산에 의해 제시되는 속성값을 나타낸다.                                                       |
|         | 새로운 값은 인스턴스가 실제적으로 삽입되거나 갱신되기 전에만 접근할 수 있다.                                |
+---------+-------------------------------------------------------------------------------------------------------------+
| **old** | 갱신 연산의 완료 전에 존재하던 속성값을 나타낸다. 이 값은 트리거가 수행되는 동안만 유지된다.                |
|         | 트리거가 종료되면 **old** 값은 잃어버리게 된다.                                                             |
+---------+-------------------------------------------------------------------------------------------------------------+

.. _trigger-action:

트리거 실행 영역
----------------

트리거 실행 영역은 트리거의 조건 영역이 참이거나 조건 영역이 생략된 경우 수행될 내용을 기술하는 영역이다. 실행 영역 절에 특정 시점(**AFTER** 나 **DEFERRED**)이 주어지지 않으면, 실행 영역은 트리거 이벤트와 같은 시점에서 수행된다.

아래 목록은 트리거를 정의할 때 사용할 수 있는 실행 영역의 목록이다.

*   **REJECT**: 트리거에서 조건 영역이 참이 아닌 경우 트리거를 발동시킨 연산은 거절되고 데이터베이스의 예전 상태를 그대로 유지한다. 연산이 수행된 후에는 거절할 수 없기 때문에 **REJECT** 는 실행 시점이 **BEFORE** 일 때만 허용된다. 따라서 실행 시점이 **AFTER** 나 **DERERRED** 인 경우 **REJECT** 를 사용해서는 안 된다.

*   **INVALIDATE TRANSACTION**: 트리거를 부른 이벤트 연산은 수행되지만, 커밋을 포함하고 있는 트랜잭션은 수행되지 않도록 한다. 트랜잭션이 유효하지 않으면 반드시 **ROLLBACK** 문으로 취소시켜야 한다. 이러한 실행은 데이터를 변경하는 이벤트가 발생한 후에 유효하지 않은 데이터를 가지는 것으로부터 데이터베이스를 보호하기 위해 사용된다.

*   **PRINT**: 터미널 화면에 텍스트 메시지로 트리거 활동을 가시적으로 보여주기 때문에 트리거의 개발이나 시험하는 도중에 사용될 수 있다. 이벤트 연산의 결과를 거절하거나 무효화시키지는 않는다.
*   **INSERT**: 테이블에 하나 혹은 그 이상의 새로운 인스턴스를 추가한다.
*   **UPDATE**: 테이블에 있는 하나 혹은 그 이상의 칼럼 값을 변경한다.
*   **DELETE**: 테이블로부터 하나 혹은 그 이상의 인스턴스를 제거한다.

다음은 트리거 생성 시에 실행영역의 정의 방법을 보여주는 예제이다. *medal_trig* 트리거는 실행 영역에 **REJECT** 를 지정하였다. **REJECT** 는 실행 시점이 **BEFORE** 일 때만 지정 가능하다.

.. code-block:: sql

    CREATE TRIGGER medal_trig
    BEFORE UPDATE ON participant
    IF new.gold < 0 OR new.silver < 0 OR new.bronze < 0
    EXECUTE REJECT;

.. note::

    *   **INSERT** 이벤트가 정의된 트리거의 실행 영역에 **INSERT** 를 사용할 때는 트리거가 무한 루프에 빠질 수 있으므로 주의해야 한다.
    *   **UPDATE** 이벤트가 정의된 트리거가 분할된 테이블에서 동작하는 경우, 정의된 분할이 깨지거나 의도하지 않은 오동작이 발생할 수 있으므로 주의해야 한다. 이를 방지하기 위해 CUBRID는 트리거가 동작중인 경우 분할 변경을 야기하는 **UPDATE** 가 실행되지 않도록 오류 처리한다. **UPDATE** 이벤트가 정의된 트리거의 실행 영역에 **UPDATE** 를 사용할 때는 무한 루프에 빠질 수 있으므로 주의해야 한다.

트리거의 커멘트
---------------

트리의 커멘트를 다음과 같이 명시할 수 있다. 

.. code-block:: sql

    CREATE TRIGGER trg_ab BEFORE UPDATE on abc(c) EXECUTE UPDATE cube_ab SET sumc = sumc + 1
    COMMENT 'test trigger comment';

트리거의 커멘트는 다음 구문에서 확인할 수 있다.

.. code-block:: sql

	SELECT name, comment FROM db_trigger;
	SELECT trigger_name, comment FROM db_trig;

또는 CSQL 인터프리터에서 스키마를 출력하는 ;sc 명령으로 트리거의 커멘트를 확인할 수 있다.

.. code-block:: sql

    $ csql -u dba demodb
    
    csql> ;sc tbl

트리거 커멘트의 변경은 아래의 **ALTER TRIGGER** 문을 참고한다.

.. _alter-trigger:

ALTER TRIGGER
=============

트리거 정의에서 **STATUS** 와 **PRIORITY** 옵션에 대해 **ALTER** 구문을 이용하여 변경할 수 있다. 만약 트리거의 다른 부분에 대해 변경(이벤트 대상 또는 조건 표현식)이 필요하면, 트리거를 삭제한 후 재생성해야 한다. 

::

    ALTER TRIGGER [schema_name.]trigger_name <trigger_option> ;

    <trigger_option> ::=
        STATUS { ACTIVE | INACTIVE } |
        PRIORITY key

*   *schema_name*: 트리거의 스키마 이름을 지정한다. 생략하면 현재 세션의 스키마 이름을 사용한다.
*   *trigger_name*: 변경할 트리거의 이름을 지정한다.
*   **STATUS** { **ACTIVE** | **INACTIVE** }: 트리거의 상태를 변경한다.
*   **PRIORITY** *key*: 우선순위를 변경한다.

다음은 medal_trig 트리거를 생성하고 트리거의 상태를 **INACTIVE** 로, 우선순위를 0.7로 변경하는 예제이다.

.. code-block:: sql

    CREATE TRIGGER medal_trig
    STATUS ACTIVE
    BEFORE UPDATE ON participant
    IF new.gold < 0 OR new.silver < 0 OR new.bronze < 0
    EXECUTE REJECT;

    ALTER TRIGGER medal_trig STATUS INACTIVE;
    ALTER TRIGGER medal_trig PRIORITY 0.7;

.. note::

    *   같은 **ALTER TRIGGER** 문 내에서는 한 개의 *trigger_option* 만 기술할 수 있다.
    *   만약 테이블 트리거를 변경하려면, 해당 트리거의 소유자이거나, 해당 트리거가 있는 테이블에 대해 **ALTER** 권한이 부여되어 있어야 한다.
    *   사용자 트리거를 변경하기 위해서는 반드시 해당 트리거의 소유자여야 한다. *trigger_option* 에 대한 자세한 내용은 :ref:`create-trigger` 을 참조한다. **PRIORITY** 옵션과 같이 기술하는 key는 반드시 음이 아닌 부동 소수점 값(non-negative floating point value)이어야 한다.

트리거 커멘트
-------------

트리거 커멘트는 **ALTER TRIGGER** 문을 실행하여 다음과 같이 변경할 수 있다.

::

    ALTER TRIGGER [schema_name.]trigger_name [trigger_option] 
    [COMMENT ‘comment_string’];

*   *schema_name*: 트리거의 스키마 이름을 지정한다. 생략하면 현재 세션의 스키마 이름을 사용한다.
*   *trigger_name*: 변경할 트리거의 이름을 지정한다.
*   *comment_string*: 트리거의 커멘트를 지정한다.

트리거의 커멘트만 변경하는 경우 트리거 옵션(trigger_option)을 생략할 수 있다.

*trigger_option*\은 위의 :ref:`alter-trigger` 구문을 참고한다.

.. code-block:: sql

    ALTER TRIGGER trg_ab COMMENT 'new trigger comment';

DROP TRIGGER
============

**DROP TRIGGER** 구문을 이용하여 트리거를 삭제한다. ::

    DROP TRIGGER [schema_name.]trigger_name ; 

*   *schema_name*: 트리거의 스키마 이름을 지정한다. 생략하면 현재 세션의 스키마 이름을 사용한다.
*   *trigger_name*: 삭제할 트리거의 이름을 지정한다.

다음은 medal_trig 트리거를 삭제하는 예제이다.

.. code-block:: sql

    DROP TRIGGER medal_trig;

.. note::

    *   트리거가 사용자 트리거(즉 트리거 이벤트가 **COMMIT** 이거나 **ROLLBACK**)이면, 트리거의 소유자만 볼 수 있고 소유자만 제거할 수 있다.
    *   한 개의 **DROP TRIGGER** 문에서는 한 개의 트리거만 제거할 수 있다.테이블 트리거는 트리거가 속해 있는 테이블에 대해 **ALTER** 권한이 있는 사용자에 의해 제거될 수 있다.

RENAME TRIGGER
==============

트리거의 이름은 **RENAME** 구문의 **TRIGGER** 예약어를 이용해서 변경한다. ::

    RENAME TRIGGER [schema_name.]old_trigger_name {AS | TO} [schema_name.]new_trigger_name ;

*   *schema_name*: 트리거의 스키마 이름을 지정한다. 생략하면 현재 세션의 스키마 이름을 사용한다. 현재 트리거의 스키마와 변경할 트리거의 스키마가 동일해야 한다.
*   *old_trigger_name*: 트리거의 현재 이름을 입력한다.
*   *new_trigger_name*: 변경할 트리거의 이름을 지정한다.

.. code-block:: sql

    RENAME TRIGGER medal_trigger AS medal_trig;

.. note::

    *   트리거 이름은 사용자가 소유하고 있는 트리거 중에서 유일해야 한다. 하지만 데이터베이스 내의 테이블 이름과 같거나 다른 소유자가 소유하고 있는 트리거의 이름과는 같을 수 있다.
    *   만약 테이블 트리거의 이름을 변경하려면, 트리거의 소유자이거나, 해당 트리거가 있는 테이블에 대해 **ALTER** 권한이 부여되어 있어야 한다. 사용자 트리거는 트리거의 소유자만 이름을 변경할 수 있다.

지연된 트리거
=============

지연된 트리거 실행영역과 조건 영역은 나중에 실행되거나 취소될 수 있다. 이러한 트리거들은 이벤트 시점(event time)이나 실행 영역(action) 절에 **DEFERRED** 시간 옵션을 포함하고 있다. **DEFERRED** 옵션이 이벤트 시점에 기술되고, 실행 영역 앞에 시간이 생략되었다면, 실행 영역은 자동으로 지연된다.

지연된 영역 실행
----------------

지연된 트리거의 조건 영역이나 실행 영역을 즉시 실행시킨다. ::

    EXECUTE DEFERRED TRIGGER <trigger_identifier> ;

    <trigger_identifier> ::=
        [schema_name.]trigger_name |
        ALL TRIGGERS

*   *schema_name*: 트리거의 스키마 이름을 지정한다. 생략하면 현재 세션의 스키마 이름을 사용한다.
*   *trigger_name*: 트리거의 이름을 지정하면 지정된 트리거의 지연된 활동이 실행된다.
*   **ALL TRIGGERS**: 현재 모든 지연된 활동이 실행된다.

지연된 영역 취소
----------------

지연된 트리거의 조건 영역과 실행 영역을 취소한다. ::

    DROP DEFERRED TRIGGER <trigger_identifier> ;

    <trigger_identifier> ::=
        [schema_name.]trigger_name |
        ALL TRIGGERS

*   *schema_name*: 트리거의 스키마 이름을 지정한다. 생략하면 현재 세션의 스키마 이름을 사용한다.
*   *trigger_name*: 트리거의 이름을 지정하면 지정된 트리거의 지연된 활동이 취소된다.
*   **ALL TRIGGERS**: 현재 모든 지연된 활동이 취소된다.

트리거 권한 부여
----------------

트리거에 대한 권한은 명시적으로 부여되지 않는다. 트리거의 정의에 기술된 이벤트 대상 테이블에 권한이 부여되었을 때 사용자는 테이블 트리거에 대한 권한을 자동적으로 획득한다. 다시 말하자면, 테이블 대상(**INSERT**, **UPDATE** 등)을 가지는 트리거는 해당 테이블에 적절한 권한을 가지는 모든 사용자에게 보인다. 사용자 트리거(**COMMIT** 과 **ROLLBACK**)는 트리거를 정의한 사용자만 볼 수 있다. 트리거의 소유자이면 모든 권한은 자동적으로 부여된다.

.. note::

    *   테이블 트리거를 정의하기 위해서는 관련된 테이블에 **ALTER** 권한이 반드시 있어야 한다.
    *   사용자 트리거를 정의하기 위해서는 유효한 사용자를 이용하여 데이터베이스에 접근하는 것이 필요하다.

REPLACE와 INSERT ... ON DUPLICATE KEY UPDATE에서의 트리거
=========================================================

CUBRID에서는 **REPLACE** 문과 **INSERT ... ON DUPLICATE KEY UPDATE** 문 실행 시 내부적으로 **DELETE**, **UPDATE**, **INSERT** 작업이 발생하면서 해당 트리거가 실행된다. 다음 표는 **REPLACE** 혹은 **INSERT ... ON DUPLICATE KEY UPDATE** 문이 수행될 때 발생하는 이벤트에 따라 CUBRID에서 트리거가 어떤 순서로 동작하는지를 나타낸다. **REPLACE** 문과 **INSERT ... ON DUPLICATE KEY UPDATE** 문 모두 상속받은 클래스(테이블)에서는 트리거가 동작하지 않는다.

**REPLACE와 INSERT ... ON DUPLICATE KEY UPDATE 문에서 트리거의 동작 순서**

+--------------------------------------------+------------------+
| 이벤트                                     | 트리거 동작 순서 |
+============================================+==================+
| REPLACE                                    | BEFORE DELETE >  |
| 레코드가 삭제되고 삽입될 때                | AFTER DELETE >   |
|                                            | BEFORE INSERT >  |
|                                            | AFTER INSERT     |
+--------------------------------------------+------------------+
| INSERT ... ON DUPLICATE KEY UPDATE         | BEFORE UPDATE >  |
| 레코드가 업데이트될 때                     | AFTER UPDATE     |
+--------------------------------------------+------------------+
| REPLACE, INSERT ... ON DUPLCATE KEY UPDATE | BEFORE INSERT >  |
| 레코드가 삽입만 될 때                      | AFTER INSERT     |
+--------------------------------------------+------------------+

다음은 *with_trigger* 테이블에 **INSERT ... ON DUPLICATE KEY UPDATE** 와 **RELPACE** 를 수행하면 트리거가 동작하여 *trigger_actions* 테이블에 레코드를 삽입하는 예제이다.

.. code-block:: sql

    CREATE TABLE with_trigger (id INT UNIQUE);
    INSERT INTO with_trigger VALUES (11);
     
    CREATE TABLE trigger_actions (val INT);
     
    CREATE TRIGGER trig_1 BEFORE INSERT ON with_trigger EXECUTE INSERT INTO trigger_actions VALUES (1);
    CREATE TRIGGER trig_2 BEFORE UPDATE ON with_trigger EXECUTE INSERT INTO trigger_actions VALUES (2);
    CREATE TRIGGER trig_3 BEFORE DELETE ON with_trigger EXECUTE INSERT INTO trigger_actions VALUES (3);
     
    INSERT INTO with_trigger VALUES (11) ON DUPLICATE KEY UPDATE id=22;
     
    SELECT * FROM trigger_actions;

::
    
              va
    ==============
                2
     
.. code-block:: sql

    REPLACE INTO with_trigger VALUES (22);
     
    SELECT * FROM trigger_actions;
    
::
    
              va
    ==============
                2
                3
                1

트리거 디버깅
=============

트리거를 정의한 후에는 트리거가 의도한 대로 동작하는지 검사하는 것이 좋다. 종종 트리거가 기대했던 것보다 처리하는데 오랜 시간이 걸리는 경우가 있다. 이는 시스템에 너무 많은 오버헤드를 주거나, 재귀적 루프에 빠졌다는 뜻이다. 이 절에서는 트리거를 디버그하는 몇 가지 방법을 설명한다.

다음은 호출되면 재귀적으로 루프에 빠지도록 정의한 트리거이다. *loop_trg* 트리거는 목적이 다소 인위적이지만 트리거를 디버그하기 위한 예제로 사용될 수 있다.

.. code-block:: sql

    CREATE TRIGGER loop_tgr
    BEFORE UPDATE ON participant(gold)
    IF new.gold > 0
    EXECUTE UPDATE participant
            SET gold = new.gold - 1
            WHERE nation_code = obj.nation_code AND host_year = obj.host_year;

트리거 실행 로그 보기
---------------------

**SET TRIGGER TRACE** 문을 이용하여 터미널에서 트리거의 실행 로그를 볼 수 있다. ::

    SET TRIGGER TRACE <switch> ;

    <switch> ::=
        ON |
        OFF

*   **ON**: **TRACE** 가 작동되며 **OFF** 하거나 현재 데이터베이스 세션을 종료할 때까지 계속 유지된다.
*   **OFF**: **TRACE** 의 작동을 멈춘다.

다음 예제는 트리거의 실행 로그를 보기 위해 **TRACE** 를 작동시키고 *loop_trg* 트리거를 작동시키는 예제이다. 트리거가 호출될 때 수행된 각각의 조건 영역과 실행 영역에 대한 추적을 식별하기 위한 메시지가 터미널에 나타난다. *loop_trg* 트리거는 *gold* 값이 0이 될 때까지 실행되므로 예제에서는 아래의 메세지가 15번 나타난다.

.. code-block:: sql

    SET TRIGGER TRACE ON;
    UPDATE participant SET gold = 15 WHERE nation_code = 'KOR' AND host_year = 1988;

::

    TRACE: Evaluating condition for trigger "loop".
    TRACE: Executing action for trigger "loop".

중첩된 트리거 제한
------------------

**SET TRIGGER** 문의 **MAXIMUM DEPTH** 키워드를 이용하여 단계적으로 발동되는 트리거 수를 제한할 수 있다. 이를 이용하면 재귀적으로 호출되는 트리거가 무한루프에 빠지는 것을 막을 수 있다. ::

    SET TRIGGER [ MAXIMUM ] DEPTH count ;

*   *count*: 양의 정수값으로 트리거가 다른 트리거나 자신을 재귀적으로 발동할 수 있는 횟수를 지정한다. 트리거의 수가 최대 깊이에 도달하면 데이터베이스 요청은 중단되고 트랜잭션은 유효하지 않은 것처럼 표시된다. 설정된 **DEPTH** 는 현재 세션을 제외한 나머지 모든 트리거에 적용된다. 최대값은 32이다.

다음은 재귀적 트리거 호출의 최대 값을 10으로 설정하는 예제이다. 이는 이후에 발동하는 모든 트리거에 적용된다. 이 예제에서 *gold* 칼럼에 대한 값은 15로 갱신되어 트리거는 총 16번 불려지게 된다. 이는 현재 설정된 최대 깊이를 초과하게 되고 아래와 같은 에러 메시지가 발생한다.

.. code-block:: sql

    SET TRIGGER MAXIMUM DEPTH 10;
    UPDATE participant SET gold = 15 WHERE nation_code = 'KOR' AND host_year = 1988;
     
::

    ERROR: Maximum trigger depth 10 exceeded at trigger "loop_tgr".

트리거를 이용한 응용
====================

여기에서는 데모 데이터베이스에 있는 트리거 정의에 대해 알아본다. *demodb* 데이터베이스에 생성되어 있는 트리거는 그리 복잡하지는 않지만 CUBRID에서 사용할 수 있는 대부분의 기능을 활용한다. 이러한 트리거를 테스트할 때, *demodb* 데이터베이스의 원형을 유지하고 싶다면 데이터에 변경이 발생한 후 롤백을 수행해야 한다.

사용자 데이터베이스에 직접 생성한 트리거는 사용자가 만든 응용 프로그램만큼이나 강력할 수 있다.

*participant* 테이블에 만들어진 아래 트리거는 제시된 값이 0보다 작을 때 메달 칼럼(*gold*, *silver*, *bronze*)에 대한 업데이트를 거절한다. 트리거의 조건에 상관명 new가 사용되었기 때문에 시작 시점(evaluation time)은 반드시 **BEFORE** 가 되어야 한다. 비록 기술하지는 않았지만, 이 트리거에서 실행 시점(action time) 또한 **BEFORE** 이다.

.. code-block:: sql

    CREATE TRIGGER medal_trigger
    BEFORE UPDATE ON participant
    IF new.gold < 0 OR new.silver < 0 OR new.bronze < 0
    EXECUTE REJECT;

국가 코드가 'BLA'인 나라의 금메달(*gold*) 수를 업데이트 할 때, *medal_trigger* 트리거가 발동한다. 금메달 수가 음수인 경우를 허용하지 않도록 트리거를 생성하였으므로, 업데이트를 허용하지 않는다.

.. code-block:: sql

    UPDATE participant
    SET gold = -10
    WHERE nation_code = 'BLA';

아래 트리거는 위의 예제와 같은 조건인데, **STATUS ACTIVE** 가 추가된 경우이다. **STATUS** 문이 생략될 경우 기본값은 **ACTIVE** 이며, **ALTER TRIGGER** 문에 의해 **STATUS** 를 **INACTIVE** 로 변경할 수 있다. 

**STATUS** 의 값에 따라 트리거의 실행 여부를 지정할 수 있다.

.. code-block:: sql

    CREATE TRIGGER medal_trig
    STATUS ACTIVE
    BEFORE UPDATE ON participant
    IF new.gold < 0 OR new.silver < 0 OR new.bronze < 0
    EXECUTE REJECT;
     
    ALTER TRIGGER medal_trig
    STATUS INACTIVE;

다음 트리거는 트랜잭션이 커밋되었을 때 어떻게 무결성 제약 조건을 강제적으로 수행하는지 보여 준다. 하나의 트리거가 여러 테이블에 대해 지정 조건을 넣을 수 있다는 점이 앞의 예제와 다르다.

.. code-block:: sql

    CREATE TRIGGER check_null_first
    BEFORE COMMIT
    IF 0 < (SELECT count(*) FROM athlete WHERE gender IS NULL)
    OR 0 < (SELECT count(*) FROM game WHERE nation_code IS NULL)
    EXECUTE REJECT;

다음 트리거는 *record* 테이블에 대해서 트랜잭션이 커밋될 때까지 업데이트 무결성 제약조건 검사를 지연시킨다. **DEFERRED** 키워드가 이벤트 시점으로 주어졌기 때문에 업데이트 실행 시점에 즉시 트리거가 실행되지는 않는다.

.. code-block:: sql

    CREATE TRIGGER deferred_check_on_record
    DEFERRED UPDATE ON record
    IF obj.score = '100'
    EXECUTE INVALIDATE TRANSACTION;

*record* 테이블에서 업데이트가 완료되었을 때, 해당 업데이트는 현재 트랜잭션의 마지막(커밋이나 롤백할 때)에 확인하게 된다. 상관명 **old** 는 **DEFERRED UPDATE** 를 사용하는 트리거의 조건 절에 사용할 수 없다. 따라서 아래와 같은 트리거는 생성할 수 없다.

.. code-block:: sql

    CREATE TABLE foo (n int);
    CREATE TRIGGER foo_trigger
        DEFERRED UPDATE ON foo
        IF old.n = 100
        EXECUTE PRINT 'foo_trigger';

위와 같이 트리거를 생성하려고 하면 다음과 같은 에러 메시지를 보여주고, 실패한다. 

::

    ERROR: Error compiling condition for 'foo_trigger' : old.n is not defined.

상관명 **old** 는 트리거의 조건 시간이 **AFTER** 일 때에만 사용될 수 있다.