File trigger_description.cpp¶
File List > cubrid > src > object > trigger_description.cpp
Go to the documentation of this file
/*
* Copyright 2008 Search Solution Corporation
* Copyright 2016 CUBRID Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
/*
* trigger_description.cpp
*/
#include "trigger_description.hpp"
#include "authenticate.h"
#include "dbi.h"
#include "dbtype_function.h"
#include "object_accessor.h"
#include "object_print.h"
#include "object_printer.hpp"
#include "object_print_util.hpp"
#include "printer.hpp"
#include "set_object.h"
#include "schema_manager.h"
#include "trigger_manager.h"
#include "work_space.h"
/* safe string free */
#define STRFREE_W(string) \
if (string != NULL) db_string_free((char *) (string))
static int is_required_trigger (TR_TRIGGER *trigger, DB_OBJLIST *classes);
static char *get_user_name (DB_OBJECT *user);
trigger_description::trigger_description ()
: name (0)
, event (0)
, class_name (0)
, attribute (0)
, full_event (0)
, status (0)
, priority (0)
, condition_time (0)
, condition (0)
, action_time (0)
, action (0)
, comment (0)
{
}
int trigger_description::init (const char *name)
{
struct db_object *trobj = tr_find_trigger (name);
if (trobj == NULL)
{
return ER_FAILED;
}
return init (trobj);
}
int trigger_description::init (struct db_object *trobj)
{
assert (trobj != NULL);
char *condition = NULL, *action = NULL, *classname;
TR_TRIGGER *trigger;
trigger = tr_map_trigger (trobj, 1);
if (trigger == NULL)
{
return ER_FAILED;
}
/* even though we have the trigger, use these to get the expressions translated into a simple string */
if (db_trigger_condition (trobj, &condition) != NO_ERROR)
{
ws_free_string (condition);
return ER_FAILED;
}
if (db_trigger_action (trobj, &action) != NO_ERROR)
{
ws_free_string (action);
return ER_FAILED;
}
/* copy these */
this->name = object_print::copy_string (trigger->name);
this->attribute = object_print::copy_string (trigger->attribute);
this->comment = object_print::copy_string (trigger->comment);
/* these are already copies */
this->condition = condition;
this->action = action;
/* these are constant strings that don't need to ever change */
this->event = tr_event_as_string (trigger->event);
this->condition_time = object_printer::describe_trigger_condition_time (*trigger);
this->action_time = object_printer::describe_trigger_action_time (*trigger);
/* only show status if its inactive */
if (trigger->status != TR_STATUS_ACTIVE)
{
this->status = tr_status_as_string (trigger->status);
}
/* if its 0, leave it out */
if (trigger->priority != 0.0)
{
char temp_buffer[64];
sprintf (temp_buffer, "%f", trigger->priority);
this->priority = object_print::copy_string (temp_buffer);
}
if (trigger->class_mop != NULL)
{
classname = (char *) sm_get_ch_name (trigger->class_mop);
if (classname != NULL)
{
this->class_name = object_print::copy_string ((char *) classname);
}
else
{
this->class_name = object_print::copy_string ("*** deleted class ***");
}
/* format the full event specification so csql can display it without being dependent on syntax */
char buffer[SM_MAX_IDENTIFIER_LENGTH * 2 + 32];
if (this->attribute != NULL)
{
sprintf (buffer, "%s ON %s(%s)", this->event, this->class_name, this->attribute);
}
else
{
sprintf (buffer, "%s ON %s", this->event, this->class_name);
}
this->full_event = object_print::copy_string (buffer);
}
else
{
/* just make a copy of this so csql can simply use it without thinking */
this->full_event = object_print::copy_string ((char *) this->event);
}
return NO_ERROR;
}
trigger_description::~trigger_description ()
{
/* these were allocated by this module and can be freed with free_and_init() */
free (name);
free (attribute);
free (class_name);
free (full_event);
free (priority);
if (comment)
{
free ((void *) comment);
}
/* these were returned by the trigger manager and must be freed with db_string_free() */
STRFREE_W (condition);
STRFREE_W (action);
}
/*
* help_print_trigger () - Debug function, primarily for help_print_info,
* can be useful in the debugger as well.
* Display the description of a trigger to stdout.
* return: none
* name(in): trigger name
* file(in):
*/
void trigger_description::fprint (FILE *file)
{
if (name != NULL)
{
fprintf (file, "Trigger : %s\n", name);
if (status != NULL)
{
fprintf (file, "Status : %s\n", status);
}
if (priority != NULL)
{
fprintf (file, "Priority : %s\n", priority);
}
fprintf (file, "Event : %s %s\n", condition_time, full_event);
if (condition != NULL)
{
fprintf (file, "Condition : %s\n", condition);
}
if (condition_time != action_time)
{
fprintf (file, "Action : %s %s\n", action_time, action);
}
else
{
fprintf (file, "Action : %s\n", action);
}
if (comment != NULL)
{
fprintf (file, "Comment '%s'\n", comment);
}
}
}
/*
* tr_dump_trigger() - This function is used to dump a trigger definition in ASCII format so that it can be read and
* re-defined from the csql interpreter.
* It is intended to support the unloaddb/loadbdb migration utilities.
* return: error code
* output_ctx(in/out): output context
* trigger_object(in): trigger object
* quoted_id_flag(in):
*/
int
tr_dump_trigger (extract_context &ctxt, print_output &output_ctx, DB_OBJECT *trigger_object)
{
int error = NO_ERROR;
TR_TRIGGER *trigger;
DB_TRIGGER_TIME time;
int save;
const char *name;
char owner_name[DB_MAX_USER_LENGTH] = { '\0' };
const char *trigger_name = NULL;
const char *class_name = NULL;
PARSER_CONTEXT *parser;
PT_NODE **action_node = nullptr, **condition_node = nullptr;
char *query_action_result, *query_condition_result;
AU_DISABLE (save);
trigger = tr_map_trigger (trigger_object, 1);
if (trigger == NULL)
{
ASSERT_ERROR_AND_SET (error);
}
else if (trigger->status != TR_STATUS_INVALID)
{
if (trigger->class_mop != NULL)
{
name = db_get_class_name (trigger->class_mop);
if (sm_qualifier_name (name, owner_name, DB_MAX_USER_LENGTH) == NULL)
{
ASSERT_ERROR_AND_SET (error);
return error;
}
class_name = sm_remove_qualifier_name (name);
}
/* automatically filter out invalid triggers */
output_ctx ("CREATE TRIGGER ");
if (ctxt.is_dba_user || ctxt.is_dba_group_member)
{
output_ctx ("[%s].[%s]\n", owner_name, sm_remove_qualifier_name (trigger->name));
}
else
{
output_ctx ("[%s]\n", sm_remove_qualifier_name (trigger->name));
}
output_ctx (" STATUS %s\n", tr_status_as_string (trigger->status));
output_ctx (" PRIORITY %f\n", trigger->priority);
time = TR_TIME_BEFORE;
if (trigger->condition != NULL)
{
time = trigger->condition->time;
}
else if (trigger->action != NULL)
{
time = trigger->action->time;
}
/* BEFORE UPDATE etc. */
output_ctx (" %s %s", tr_time_as_string (time), tr_event_as_string (trigger->event));
if (trigger->class_mop != NULL)
{
output_ctx (" ON ");
if (ctxt.is_dba_user || ctxt.is_dba_group_member)
{
output_ctx ("[%s].[%s]", owner_name, class_name);
}
else
{
output_ctx ("[%s]", class_name);
}
if (trigger->attribute != NULL)
{
output_ctx ("([%s])", trigger->attribute);
}
}
output_ctx ("\n");
if (trigger->condition != NULL)
{
char *text;
int length;
length = strlen (EVAL_PREFIX) + strlen (trigger->condition->source) + strlen (EVAL_SUFFIX) + 1;
text = (char *) malloc (length);
if (text == NULL)
{
output_ctx ("/* ERROR : IF %s */\n", trigger->condition->source);
error = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (size_t) length);
return error;
}
strcpy (text, EVAL_PREFIX);
strcat (text, trigger->condition->source);
strcat (text, EVAL_SUFFIX);
parser = parser_create_parser ();
if (parser == NULL)
{
output_ctx ("/* ERROR : IF %s */\n", trigger->condition->source);
ASSERT_ERROR_AND_SET (error);
return error;
}
if (ctxt.is_dba_user == false && ctxt.is_dba_group_member == false)
{
parser->custom_print |= PT_PRINT_NO_CURRENT_USER_NAME;
}
condition_node = parser_parse_string (parser, text);
if (condition_node != NULL)
{
query_condition_result = parser_print_tree_with_quotes (parser, *condition_node);
/* remove appended trigger evaluate info */
query_condition_result = remove_appended_trigger_evaluate (query_condition_result, 1);
if (query_condition_result == NULL)
{
output_ctx ("/* ERROR : IF %s */\n", trigger->condition->source);
ASSERT_ERROR_AND_SET (error);
return error;
}
output_ctx ("IF %s\n", query_condition_result);
}
else
{
output_ctx ("/* ERROR : IF %s */\n", trigger->condition->source);
}
parser_free_parser (parser);
free_and_init (text);
}
if (trigger->action != NULL)
{
output_ctx (" EXECUTE ");
if (trigger->action->time != time)
{
output_ctx ("%s ", tr_time_as_string (trigger->action->time));
}
switch (trigger->action->type)
{
case TR_ACT_EXPRESSION:
parser = parser_create_parser ();
if (parser == NULL)
{
output_ctx ("\n/* ERROR : EXECUTE %s */\n", trigger->action->source);
ASSERT_ERROR_AND_SET (error);
return error;
}
if (ctxt.is_dba_user == false && ctxt.is_dba_group_member == false)
{
parser->custom_print |= PT_PRINT_NO_CURRENT_USER_NAME;
}
action_node = parser_parse_string (parser, trigger->action->source);
if (action_node != NULL)
{
query_action_result = parser_print_tree_with_quotes (parser, *action_node);
output_ctx ("%s", query_action_result);
}
else
{
output_ctx ("\n/* ERROR : EXECUTE %s */\n", trigger->action->source);
}
parser_free_parser (parser);
break;
case TR_ACT_REJECT:
output_ctx ("REJECT");
break;
case TR_ACT_INVALIDATE:
output_ctx ("INVALIDATE TRANSACTION");
break;
case TR_ACT_PRINT:
output_ctx ("PRINT '%s'", trigger->action->source);
break;
default:
output_ctx ("???");
break;
}
}
if (trigger->comment != NULL && trigger->comment[0] != '\0')
{
output_ctx (" ");
help_print_describe_comment (output_ctx, trigger->comment);
}
output_ctx (";\n\n");
}
AU_ENABLE (save);
return error;
}
/*
* tr_dump_selective_triggers() -
* return: error code
* output_ctx(in/out):
* quoted_id_flag(in):
* classes(in):
*/
int
tr_dump_selective_triggers (extract_context &ctxt, print_output &output_ctx, DB_OBJLIST *classes)
{
int error = NO_ERROR;
TR_TRIGGER *trigger;
DB_SET *table;
DB_VALUE value;
DB_OBJECT *trigger_object;
int max, i;
if (Au_root == NULL)
{
return NO_ERROR;
}
error = obj_get (Au_root, "triggers", &value);
if (error != NO_ERROR)
{
return error;
}
if (DB_IS_NULL (&value))
{
table = NULL;
}
else
{
table = db_get_set (&value);
}
if (table == NULL)
{
return NO_ERROR;
}
error = set_filter (table);
max = set_size (table);
for (i = 1; i < max && error == NO_ERROR; i += 2)
{
error = set_get_element (table, i, &value);
if (error == NO_ERROR)
{
if (DB_VALUE_TYPE (&value) == DB_TYPE_OBJECT && !DB_IS_NULL (&value) && db_get_object (&value) != NULL)
{
trigger_object = db_get_object (&value);
trigger = tr_map_trigger (trigger_object, 1);
if (trigger == NULL)
{
ASSERT_ERROR_AND_SET (error);
}
else
{
int is_system_class = 0;
if (trigger->class_mop != NULL && !is_required_trigger (trigger, classes))
{
continue;
}
/* don't dump system class triggers */
if (trigger->class_mop != NULL)
{
is_system_class = sm_is_system_class (trigger->class_mop);
}
if (is_system_class == 0)
{
if (trigger->status != TR_STATUS_INVALID)
{
tr_dump_trigger (ctxt, output_ctx, trigger_object);
}
}
else if (is_system_class < 0)
{
error = is_system_class;
}
}
}
}
}
set_free (table);
return error;
}
/*
* is_required_trigger() -
* return: int
* trigger(in):
* classes(in):
*/
static int
is_required_trigger (TR_TRIGGER *trigger, DB_OBJLIST *classes)
{
DB_OBJLIST *cl;
for (cl = classes; cl != NULL; cl = cl->next)
{
if (trigger->class_mop == cl->op)
{
return 1;
}
}
return 0;
}
/*
* get_user_name() - Shorthand function for getting the user name out of a user object.
* The name is stored in a static array so we don't have to worry about freeing it.
* return: user name
* user(in): user object
*/
static char *
get_user_name (DB_OBJECT *user)
{
#define MAX_USER_NAME 32 /* actually its 8 */
static char namebuf[MAX_USER_NAME];
DB_VALUE value;
const char *tmp;
if (db_get (user, "name", &value))
{
/* error */
strcpy (namebuf, "???");
return namebuf;
}
if (DB_VALUE_TYPE (&value) != DB_TYPE_STRING || DB_IS_NULL (&value) || db_get_string (&value) == NULL)
{
strcpy (namebuf, "???");
}
else
{
tmp = db_get_string (&value);
if (tmp)
{
strncpy (namebuf, tmp, sizeof (namebuf) - 1);
}
namebuf[MAX_USER_NAME - 1] = '\0';
}
db_value_clear (&value);
return namebuf;
#undef MAX_USER_NAME
}