Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CBRD-25709] Trigger DEFERRED 구문에서 재귀 구조 발생 시 무한 루프 발생(Maximum trigger depth 미작동) #5676

Open
wants to merge 11 commits into
base: develop
Choose a base branch
from
97 changes: 59 additions & 38 deletions src/object/trigger_manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -6010,9 +6010,8 @@ tr_execute_deferred_activities (DB_OBJECT * trigger_object, DB_OBJECT * target)
{
int error = NO_ERROR;
TR_DEFERRED_CONTEXT *c, *c_next;
TR_TRIGLIST *t, *next;
TR_TRIGLIST *t, *next, *tail;
TR_TRIGGER *trigger;
TR_STATE *state_p;
int status;
bool rejected;

Expand All @@ -6025,57 +6024,78 @@ tr_execute_deferred_activities (DB_OBJECT * trigger_object, DB_OBJECT * target)
return NO_ERROR;
}

/*
* Before and after triggers resemble a DFS (Depth-First Search) structure,
* as they execute immediately and deeply within the transaction, processing related operations
* in a depth-first manner.
*
* In contrast, a deferred trigger resembles a BFS (Breadth-First Search) structure,
* as it delays execution until the end of the transaction, processing all triggers layer by layer.
*/

for (c = tr_Deferred_activities, c_next = NULL; c != NULL && !error; c = c_next)
{
c_next = c->next;
tr_Current_depth = 1;

for (t = c->head, next = NULL; t != NULL && !error; t = next)
for (t = c->head; t != NULL && !error; t = c->head)
{
next = t->next;
trigger = t->trigger;
tr_Current_depth++;

if ((trigger_object == NULL || trigger->object == trigger_object) && (target == NULL || t->target == target))
if (compare_recursion_levels (tr_Current_depth, tr_Maximum_depth) > 0)
{
if (its_deleted (t->target))
{
/*
* Somewhere along the line, the target object was deleted, quietly ignore the deferred activity.
* If it turns out that we really want to keep these active, we'll have to contend with
* what pt_exec_trigger_stmt is going to do when we pass it deleted objects.
*/
remove_deferred_activity (c, t);
}
else
{
state_p = NULL;
if (start_state (&state_p, t->trigger->name) == NULL)
{
ASSERT_ERROR_AND_SET (error);
break;
}

status = execute_activity (trigger, TR_TIME_DEFERRED, t->target, NULL, &rejected);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TR_EXCEEDS_MAX_REC_LEVEL, 2, tr_Maximum_depth,
t->trigger->name);
ASSERT_ERROR_AND_SET (error);
break;
}

tr_finish (state_p);
/* range [head, tail] */
for (tail = c->tail; t != NULL && !error; t = next)
{
next = t->next;
trigger = t->trigger;

/* execute_activity() maybe include trigger and change the next pointer. we need get it again. */
next = t->next;
if (status == TR_RETURN_TRUE)
if ((trigger_object == NULL || trigger->object == trigger_object)
&& (target == NULL || t->target == target))
{
if (its_deleted (t->target))
{
/* successful processing, remove it from the list */
/*
* Somewhere along the line, the target object was deleted, quietly ignore the deferred activity.
* If it turns out that we really want to keep these active, we'll have to contend with
* what pt_exec_trigger_stmt is going to do when we pass it deleted objects.
*/
remove_deferred_activity (c, t);

/* reject can't happen here, even if it does, it is unclear what it would mean */
}
else if (status == TR_RETURN_ERROR)
else
{
/*
* if an error happens, should we invalidate the transaction ?
*/
ASSERT_ERROR_AND_SET (error);
status = execute_activity (trigger, TR_TIME_DEFERRED, t->target, NULL, &rejected);

/* execute_activity() maybe include trigger and change the next pointer. we need get it again. */
next = t->next;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assert(next == NULL || next == t->next);
next = t->next;

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

코드 리뷰 감사합니다.
춘택님께서는 execute_activity 에 의해 기존의 next 를 잃어버리는 상황이 희박하거나 불가능하다고 하더라도, assert 코드를 작성하여 좀 더 명확하게 표현하는 것이 더 나은 방법이라고 생각하시는 것이 맞을까요?

if (status == TR_RETURN_TRUE)
{
/* successful processing, remove it from the list */
remove_deferred_activity (c, t);

/* reject can't happen here, even if it does, it is unclear what it would mean */
}
else if (status == TR_RETURN_ERROR)
{
/*
* if an error happens, should we invalidate the transaction ?
*/
ASSERT_ERROR_AND_SET (error);
}

/* else, thinks the trigger can't be evaluated yet, shouldn't happen */
}
}

/* else, thinks the trigger can't be evaluated yet, shouldn't happen */
if (t == tail)
{
break;
}
}
}
Expand All @@ -6088,6 +6108,7 @@ tr_execute_deferred_activities (DB_OBJECT * trigger_object, DB_OBJECT * target)
remove_deferred_context (c);
}
}
tr_Current_depth = 0;

return error;
}
Expand Down
Loading