Skip to content

File parse_evaluate.c

File List > cubrid > src > parser > parse_evaluate.c

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.
 *
 */

/*
 * parse_evaluate.c - Helper functions to interprets tree and
 *                returns its result as a DB_VALUE
 */

#ident "$Id$"

#include "config.h"

#include <assert.h>

#include "porting.h"
#include "error_manager.h"
#include "parser.h"
#include "cursor.h"
#include "memory_alloc.h"
#include "memory_hash.h"
#include "parser_message.h"
#include "execute_statement.h"
#include "object_domain.h"
#include "object_primitive.h"
#include "object_template.h"
#include "work_space.h"
#include "virtual_object.h"
#include "server_interface.h"
#include "arithmetic.h"
#include "parser_support.h"
#include "view_transform.h"
#include "network_interface_cl.h"
#include "schema_system_catalog_constants.h"
#include "dbtype.h"
#include "optimizer.h"      /* qo_need_skip_execution () */

/* associates labels with DB_VALUES */
static MHT_TABLE *pt_Label_table = NULL;

static int pt_is_table_op (const PT_OP_TYPE op);
static PT_NODE *pt_query_to_set_table (PARSER_CONTEXT * parser, PT_NODE * node);
static DB_VALUE *pt_set_table_to_db (PARSER_CONTEXT * parser, PT_NODE * subquery_in, DB_VALUE * db_value, int is_seq);
#if defined(ENABLE_UNUSED_FUNCTION)
static int pt_make_label_list (const void *key, void *data, void *args);
#endif /* ENABLE_UNUSED_FUNCTION */
static int pt_free_label (const void *key, void *data, void *args);
static int pt_associate_label_with_value (const char *label, DB_VALUE * val);
static void pt_evaluate_tree_internal (PARSER_CONTEXT * parser, PT_NODE * tree, DB_VALUE * db_values, int values_count,
                       bool having_serial);


/*
 * pt_is_table_op () -
 *   return:  1, if its a table operator, 0 otherwise.
 *   op(in): an expression operator
 */

static int
pt_is_table_op (const PT_OP_TYPE op)
{
  switch (op)
    {
    case PT_GE_SOME:
    case PT_GT_SOME:
    case PT_LT_SOME:
    case PT_LE_SOME:
    case PT_GE_ALL:
    case PT_GT_ALL:
    case PT_LT_ALL:
    case PT_LE_ALL:
    case PT_EQ_SOME:
    case PT_NE_SOME:
    case PT_EQ_ALL:
    case PT_NE_ALL:
    case PT_IS_IN:
    case PT_IS_NOT_IN:
      return 1;

    default:
      return 0;
    }
}

/*
 * pt_query_to_set_table () -
 *   return:  if node is a query, returns a table to set function
 *        on that query. Otherwise, returns node.
 *   parser(in):
 *   node(in):
 */

static PT_NODE *
pt_query_to_set_table (PARSER_CONTEXT * parser, PT_NODE * node)
{
  PT_NODE *result;

  result = node;
  if (node && PT_IS_QUERY_NODE_TYPE (node->node_type))
    {
      result = parser_new_node (parser, PT_FUNCTION);
      result->line_number = node->line_number;
      result->column_number = node->column_number;
      result->info.function.function_type = F_TABLE_SET;
      result->info.function.arg_list = node;
    }

  return result;
}

/*
 * pt_set_table_to_db () -  add subquery elements into a DB_VALUE set/multiset
 *   return:  db_value if all OK, NULL otherwise.
 *   parser(in): handle to parser context
 *   subquery(in): the subquery to be inserted
 *   db_value(out): a set or multiset value container
 */

static DB_VALUE *
pt_set_table_to_db (PARSER_CONTEXT * parser, PT_NODE * subquery_in, DB_VALUE * db_value, int is_seq)
{
  DB_VALUE *vals = NULL, *e_val;
  QFILE_LIST_ID *list_id = NULL;
  CURSOR_ID cursor_id;
  int cursor_status, degree = 0, col;
  PT_NODE *select_list = NULL, *subquery;
  QUERY_ID query_id_self = parser->query_id;
  int error = NO_ERROR;

  subquery = parser_copy_tree (parser, subquery_in);

  subquery = mq_translate (parser, subquery);
  if (subquery == NULL)
    {
      return NULL;
    }

  query_id_self = parser->query_id;
  parser->query_id = NULL_QUERY_ID;
  error = do_select (parser, subquery);
  if (error == NO_ERROR)
    {
      list_id = (QFILE_LIST_ID *) subquery->etc;
      select_list = pt_get_select_list (parser, subquery);
    }
  else
    {
      PT_ERRORc (parser, subquery, db_error_string (3));
      error = -1;
    }

  if (error == NO_ERROR)
    {
      degree = pt_length_of_select_list (select_list, EXCLUDE_HIDDEN_COLUMNS);
      if ((vals = (DB_VALUE *) malloc (degree * sizeof (DB_VALUE))) == NULL)
    {
      PT_ERRORmf (parser, subquery, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_RUNTIME_OUT_OF_MEMORY, sizeof (DB_VALUE));
      error = -1;
    }
      for (col = 0; vals && col < degree; col++)
    {
      db_make_null (&vals[col]);
    }
    }

  if (!(error < 0))
    {
      /*
       * the  above select resulted in a list file put on subquery->etc
       * open it and add the elements to the set.
       */
      if (cursor_open (&cursor_id, list_id, false, false))
    {
      int idx;
      DB_SET *collection;

      cursor_id.query_id = parser->query_id;

      cursor_status = cursor_next_tuple (&cursor_id);

      idx = 0;
      collection = db_get_set (db_value);

      while (error >= 0 && cursor_status == DB_CURSOR_SUCCESS)
        {
          error = cursor_get_tuple_value_list (&cursor_id, degree, vals);

          for (e_val = vals, col = 0; error >= 0 && col < degree; e_val++, col++, idx++)
        {
          if (is_seq)
            {
              error = db_seq_put (collection, idx, e_val);
            }
          else
            {
              error = db_set_add (collection, e_val);
            }
        }

          if (error >= 0)
        {
          cursor_status = cursor_next_tuple (&cursor_id);
        }
        }

      if (error >= 0 && cursor_status != DB_CURSOR_END)
        {
          error = ER_GENERIC_ERROR;
        }
      cursor_close (&cursor_id);
    }
    }

  cursor_free_self_list_id (list_id);

  pt_end_query (parser, query_id_self);

  if (vals)
    {
      for (col = 0; col < degree; col++)
    {
      pr_clear_value (&vals[col]);
    }
      free_and_init (vals);
    }

  if (error != NO_ERROR)
    {
      PT_ERRORc (parser, subquery, db_error_string (3));
      db_value = NULL;
    }

  parser_free_tree (parser, subquery);

  return db_value;
}

/*
 * pt_eval_path_expr () -  evaluate a path expr into a db_value
 *   return:  1 if all OK, 0 otherwise
 *   parser(in): the parser context
 *   tree(in): a path expression
 *   val(out): a newly set DB_VALUE if successful, untouched otherwise
 */
bool
pt_eval_path_expr (PARSER_CONTEXT * parser, PT_NODE * tree, DB_VALUE * val)
{
  bool result = true;
  PT_NODE *arg1, *arg2;
  DB_VALUE val1, *valp;
  DB_OBJECT *obj1;
  const char *nam2;
  const char *label;
  int is_class = 0;

  assert (parser != NULL);
  db_make_null (&val1);

  if (tree == NULL || val == NULL)
    {
      return false;
    }

  switch (tree->node_type)
    {
    case PT_DOT_:
      if ((arg1 = tree->info.dot.arg1) != NULL && (arg2 = tree->info.dot.arg2) != NULL && arg2->node_type == PT_NAME
      && (nam2 = arg2->info.name.original) != NULL)
    {
      pt_evaluate_tree (parser, arg1, &val1, 1);
      if (pt_has_error (parser))
        {
          pt_report_to_ersys (parser, PT_SEMANTIC);
          result = false;
        }
      else if (DB_VALUE_TYPE (&val1) == DB_TYPE_NULL)
        {
          result = true;
        }
      else if (DB_VALUE_TYPE (&val1) == DB_TYPE_OBJECT && (obj1 = db_get_object (&val1)) != NULL)
        {
          int error;

          error = db_get (obj1, nam2, val);
          if (error == ER_AU_SELECT_FAILURE || error == ER_AU_AUTHORIZATION_FAILURE)
        {
          DB_OBJECT *class_op;

          class_op = db_get_class (obj1);
          PT_ERRORmf2 (parser, arg1, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_IS_NOT_AUTHORIZED_ON, "Select",
                   ((class_op) ? db_get_class_name (class_op) : pt_short_print (parser, arg1)));
        }

          result = (error == NO_ERROR);
        }
      else
        {
          result = false;
        }
    }
      break;

    case PT_NAME:
      /* find & return obj associated with this label */
      label = tree->info.name.original;
      if (!label)
    {
      PT_ERRORf (parser, tree, "Internal error evaluate(%d)", __LINE__);
      break;
    }
      obj1 = tree->info.name.db_object;

      switch (tree->info.name.meta_class)
    {
    case PT_PARAMETER:
      valp = pt_find_value_of_label (label);
      if (valp)
        {
          (void) db_value_clone (valp, val);
        }
      else
        {
          PT_ERRORmf (parser, tree, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_UNKNOWN_VARIABLE, label);
        }
      break;

    case PT_OID_ATTR:
    case PT_VID_ATTR:
      db_make_object (val, obj1);
      break;

    case PT_TRIGGER_OID:
      db_make_object (val, obj1);
      /* check if this is really a path expression */
      if (((nam2 = tree->info.name.original) != NULL) && (nam2[0] != '\0'))
        {
          result = (db_get (obj1, nam2, val) == NO_ERROR);
        }
      break;

    case PT_META_CLASS:
    case PT_CLASSOID_ATTR:
      /* object is the class itself */
      db_make_object (val, obj1);
      is_class = db_is_any_class (obj1);
      if (is_class < 0)
        {
          result = false;
        }
      else if (!is_class)
        {
          PT_ERRORmf (parser, tree, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME__CAN_NOT_EVALUATE,
              pt_short_print (parser, tree));
        }
      break;

    case PT_SHARED:
      {
        DB_ATTRIBUTE *s_attr;

        s_attr = (DB_ATTRIBUTE *) db_get_shared_attribute (obj1, label);
        if (s_attr)
          {
        valp = (DB_VALUE *) db_attribute_default (s_attr);
          }
        else
          {
        valp = NULL;
          }

        if (valp != NULL)
          {
        (void) db_value_clone (valp, val);
          }
        else
          {
        PT_ERRORmf (parser, tree, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_UNKNOWN_SHARED_ATTR, label);
          }
      }
      break;

    case PT_META_ATTR:
    case PT_NORMAL:
    default:
      if (db_get (obj1, label, val) != NO_ERROR)
        {
          PT_ERRORmf (parser, tree, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME__CAN_NOT_EVALUATE,
              pt_short_print (parser, tree));
        }
    }
      break;

    default:
      PT_INTERNAL_ERROR (parser, "name evaluate");
      break;
    }

  return (result && !pt_has_error (parser));
}

/*
 * pt_get_one_tuple_from_list_id () -  return 1st tuple of a given list_id
 *   return:  1 if all OK, 0 otherwise
 *   parser(in): the parser context
 *   tree(in): a select/union/difference/intersection
 *   vals(out): an array of DB_VALUEs
 *   cnt(in): number of columns in the requested tuple
 */
int
pt_get_one_tuple_from_list_id (PARSER_CONTEXT * parser, PT_NODE * tree, DB_VALUE * vals, int cnt)
{
  QFILE_LIST_ID *list_id;
  CURSOR_ID cursor_id;
  int result = 0;
  PT_NODE *select_list;

  assert (parser != NULL);

  if (!tree || !vals || !(list_id = (QFILE_LIST_ID *) (tree->etc))
      || !(select_list = pt_get_select_list (parser, tree)))
    return result;

  if (cursor_open (&cursor_id, list_id, false, tree->info.query.oids_included))
    {
      /* succesfully opened a cursor */
      cursor_id.query_id = parser->query_id;

      if (cursor_next_tuple (&cursor_id) != DB_CURSOR_SUCCESS
      || cursor_get_tuple_value_list (&cursor_id, cnt, vals) != NO_ERROR)
    {
      /*
       * This isn't really an error condition, especially when we are in an
       * esql context.  Just say that we didn't succeed, which should be
       * enough to keep upper levels from trying to do anything with the
       * result, but don't report an error.
       */
      result = 0;
    }
      else if (cursor_next_tuple (&cursor_id) == DB_CURSOR_SUCCESS)
    {
      char query_prefix[65], *p;

      p = parser_print_tree (parser, tree);
      if (p == NULL)
        {
          query_prefix[0] = '\0';
        }
      else
        {
          strncpy (query_prefix, p, 60);
          if (query_prefix[59])
        {
          query_prefix[60] = '\0';
          strcat (query_prefix, "...");
        }
        }

      PT_ERRORmf (parser, select_list, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_YIELDS_GT_ONE_ROW, query_prefix);
    }
      else
    {
      result = 1;       /* all OK */
    }

      cursor_close (&cursor_id);
    }

  return result;
}

/*
 * pt_associate_label_with_value () - enter a label with associated value
 *                                    into label_table
 *   return: NO_ERROR on success, non-zero for ERROR
 *   label(in): a string (aka interpreter variable) to be associated with val
 *   val(in): the DB_VALUE to be associated with label
 */

static int
pt_associate_label_with_value (const char *label, DB_VALUE * val)
{
  const char *key;
  DB_VALUE *oldval;
  MOP mop;

  /* create label table if non-existent */
  if (!pt_Label_table)
    {
      pt_Label_table =
    mht_create ("Interpreter labels", 70, intl_identifier_mht_1strlowerhash, mht_compare_identifiers_equal);

      if (!pt_Label_table)
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }
    }

  assert (label != NULL && val != NULL);

  /* see if we already have a value for this label */
  oldval = (DB_VALUE *) mht_get (pt_Label_table, label);
  if (oldval == NULL)
    {
      /* create a copy of the label */
      key = ws_copy_string ((char *) label);
      if (key == NULL)
    {
      return ER_OUT_OF_VIRTUAL_MEMORY;
    }

      /* enter {label, val} as a {key, value} pair into label_table */
      if (mht_put (pt_Label_table, (char *) key, (void *) val) == NULL)
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

      if (DB_VALUE_TYPE ((DB_VALUE *) val) == DB_TYPE_OBJECT)
    {
      mop = db_get_object (val);
      ws_add_label_value_to_mop (mop, val);
    }
    }
  else
    {
      /* Sigh, the old key value was allocated too and needs to be freed or reused. We don't currently have a way to
       * get the current key pointer in the table. mht_put has the undocumented behavior that if the key already exists
       * in the table, it will continue to use the old key and ignore the one passed in. We rely on this here by
       * passing in the label string which we don't own. If this mht_put behavior changes, then the only safe way will
       * be to add a new mht_ function that allows us to get a pointer to the key so we can free it. */
      if (mht_put_data (pt_Label_table, (char *) label, (void *) val) == NULL)
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

      if (DB_VALUE_TYPE ((DB_VALUE *) val) == DB_TYPE_OBJECT)
    {
      mop = db_get_object (val);
      if (ws_add_label_value_to_mop (mop, val) != NO_ERROR)
        {
          assert (er_errid () != NO_ERROR);
          return er_errid ();
        }
    }

      if (DB_VALUE_TYPE (oldval) == DB_TYPE_OBJECT)
    {
      mop = db_get_object (oldval);
      ws_remove_label_value_from_mop (mop, oldval);
    }

      /* if the insertion went well, free the old value */
      db_value_free (oldval);
    }

  return NO_ERROR;
}

/*
 * pt_associate_label_with_value_check_reference () - enter a label with
 *                                     associated value into label_table
 *   return: NO_ERROR on success, non-zero for ERROR
 *   label(in): a string (aka interpreter variable) to be associated with val
 *   val(in): the DB_VALUE to be associated with label
 *
 * Note:
 * If it is passed an instance pointer in the val parameter the function also
 * checks that it does not point to a reusable OID class instance. This is
 * done in order to prevent accidental modifications on a different instance
 * if the OID is reused.
 */

int
pt_associate_label_with_value_check_reference (const char *label, DB_VALUE * val)
{
  int is_ref = pt_is_reference_to_reusable_oid (val);
  if (is_ref < 0)
    {
      return is_ref;
    }
  if (is_ref > 0)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_REFERENCE_TO_NON_REFERABLE_NOT_ALLOWED, 0);
      return ER_REFERENCE_TO_NON_REFERABLE_NOT_ALLOWED;
    }

  if (val->domain.general_info.type == DB_TYPE_STRING)
    {
      if (pr_Enable_string_compression)
    {
      if (val->data.ch.medium.compressed_size == DB_UNCOMPRESSABLE)
        {
          assert (val->data.ch.medium.compressed_buf == NULL);
          val->data.ch.medium.compressed_size = DB_NOT_YET_COMPRESSED;
        }
    }
      else if (val->data.ch.medium.compressed_size > 0)
    {
      assert (val->data.ch.medium.compressed_buf != NULL);
      db_private_free_and_init (NULL, val->data.ch.medium.compressed_buf);
      val->data.ch.medium.compressed_buf = NULL;
      val->data.ch.medium.compressed_size = DB_NOT_YET_COMPRESSED;
    }
    }

  return pt_associate_label_with_value (label, val);
}

/*
 * pt_is_reference_to_reusable_oid () - Returns true if the value passed in
 *                                      is an instance of a reusable OID class
 *   return:  < 0 if error, > 0 if the value is an instance of a reusable OID
 *        class, == 0 otherwise
 *   val(in):
 * Note:
 * Instances of reusable OID classes are non-referable. References to such
 * instances should be only used internally by the server/client process with
 * great care.
 */
int
pt_is_reference_to_reusable_oid (DB_VALUE * val)
{
  DB_OBJECT *obj_class = NULL;
  DB_TYPE vtype = DB_TYPE_NULL;

  int ret_val = false;

  if (val == NULL)
    {
      return 0;
    }

  vtype = DB_VALUE_TYPE (val);

  if (vtype == DB_TYPE_OBJECT)
    {
      DB_OBJECT *obj = db_get_object (val);
      int is_class = 0;

      if (obj == NULL)
    {
      return 0;
    }

      is_class = db_is_class (obj);
      if (is_class < 0)
    {
      return is_class;
    }
      if (is_class > 0)
    {
      return 0;
    }

      obj_class = db_get_class (obj);
      if (obj_class == NULL)
    {
      return 0;
    }
    }
  else if (vtype == DB_TYPE_POINTER)
    {
      DB_OTMPL *obj_tmpl = (DB_OTMPL *) db_get_pointer (val);

      if (obj_tmpl == NULL)
    {
      return 0;
    }

      obj_class = obj_tmpl->classobj;
      if (obj_class == NULL)
    {
      return 0;
    }
    }
  else
    {
      return 0;
    }

  ret_val = sm_check_reuse_oid_class (obj_class);

  if (ret_val < NO_ERROR)
    {
      return ret_val;
    }

  if (ret_val != 0)
    {
      return 1;
    }

  return 0;
}

/*
 * pt_find_value_of_label () - find label in label_table &
 *                             return its associated value
 *   return: the DB_VALUE associated with label if found, NULL otherwise.
 *   label(in): a string (aka interpreter variable) to be used as a search key
 *
 * Note :
 * The value returned here is "owned" by the table and will be freed
 * if another assignment is made to this label. If the lifespan of
 * the reference in the calling function is long and can span statement
 * boundaries, you may need to copy the value.
 */

DB_VALUE *
pt_find_value_of_label (const char *label)
{
  DB_VALUE *db_valp;

  if (!pt_Label_table || !label)
    {
      return NULL;
    }
  else
    {
      db_valp = (DB_VALUE *) mht_get (pt_Label_table, label);
      if (db_valp != NULL && DB_VALUE_TYPE (db_valp) == DB_TYPE_OBJECT)
    {
      db_valp = vid_flush_and_rehash_lbl (db_valp);
    }
      return db_valp;
    }
}

#if defined(ENABLE_UNUSED_FUNCTION)
/*
 * pt_make_label_list () -  adds key value to a list of names
 *   return:  NO_ERROR (keep going) or allocation error
 *   key(in): search key to the interpreter variable table
 *   data(in): a DB_VALUE associated with the key (not used)
 *   args(in): generic pointer to the list
 *
 * Note :
 * called by pt_find_labels() and should not be called directly.
 */

static int
pt_make_label_list (const void *key, void *data, void *args)
{
  DB_NAMELIST **list = (DB_NAMELIST **) args;
  DB_NAMELIST *new_;

  assert (key != NULL);

  new_ = (DB_NAMELIST *) db_ws_alloc (sizeof (DB_NAMELIST));
  if (new_ == NULL)
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

  new_->name = ws_copy_string ((char *) key);
  if (new_->name == NULL)
    {
      db_ws_free (new_);

      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

  new_->next = *list;
  *list = new_;

  return (NO_ERROR);
}

/*
 * pt_find_labels () - Sets a pointer to a list of all session interpreter
 *                     variable labels
 *   return: NO_ERROR on success, non-zero for ERROR
 *   list(in): address of pointer to list of interpreter variable labels
 *
 * Note :
 * The list returned by this function must later be freed by nlist_free.
 */

int
pt_find_labels (DB_NAMELIST ** list)
{
  int error = NO_ERROR;

  *list = NULL;

  if (pt_Label_table)
    {
      /* notice that we are checking for true instead of NO_ERROR.  The function mht_map() requires true for normal
       * processing.  Non-true values should be considered errors.  We change true to NO_ERROR for our return value
       * here so the caller doesn't get confused. */
      if ((error = mht_map (pt_Label_table, pt_make_label_list, (void *) list)) != NO_ERROR)
    {
      nlist_free (*list);
      *list = NULL;
    }
      else
    {
      error = NO_ERROR;
    }
    }

  return (error);
}
#endif /* ENABLE_UNUSED_FUNCTION */

/*
 * do_drop_variable () - remove interpreter variable(s) from the label table
 *   return: NO_ERROR on success, non-zero for ERROR
 *   parser(in): the parser context
 *   stmt(in): a drop variable statement
 */

int
do_drop_variable (PARSER_CONTEXT * parser, PT_NODE * stmt)
{
  PT_NODE *lbl;

  assert (parser != NULL);

  if (!stmt || stmt->node_type != PT_DROP_VARIABLE)
    {
      return NO_ERROR;
    }

  for (lbl = stmt->info.drop_variable.var_names; lbl && lbl->node_type == PT_NAME; lbl = lbl->next)
    {
      if (!pt_Label_table || (mht_rem (pt_Label_table, lbl->info.name.original, pt_free_label, NULL) != NO_ERROR))
    {
      PT_ERRORmf (parser, lbl, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_VAR_NOT_DEFINED, lbl->info.name.original);
    }
    }

  return NO_ERROR;
}

/*
 * pt_free_label () - release all memory occupied by an interpreter variable
 *   return:  NO_ERROR
 *   key(in): an interpreter variable used as a search key
 *   data(in): a DB_VALUE associated with the key
 *   args(in): NULL (not used here, but needed by mht_map)
 */

static int
pt_free_label (const void *key, void *data, void *args)
{
  DB_VALUE *val = (DB_VALUE *) data;
  MOP mop;

  if (key != NULL)
    {
      ws_free_string ((char *) key);
      if (DB_VALUE_TYPE (val) == DB_TYPE_OBJECT)
    {
      mop = db_get_object (val);
      ws_remove_label_value_from_mop (mop, val);
    }
      db_value_free ((DB_VALUE *) data);
    }

  return NO_ERROR;
}

/*
 * pt_free_label_table () - release all memory occupied by the label_table
 *   return: none
 */

void
pt_free_label_table (void)
{
  if (!pt_Label_table)
    {
      return;
    }

  mht_map (pt_Label_table, pt_free_label, NULL);
  mht_destroy (pt_Label_table);
  pt_Label_table = NULL;
}

/*
 * pt_final () - deallocate all resources used by the parser
 *   return: none
 */
void
parser_final (void)
{
  pt_free_label_table ();
  pt_final_packing_buf ();
}

/*
 * pt_evaluate_tree () - interprets tree & returns its result as a DB_VALUE
 *   return:  none
 *   parser(in): handle to the parser used to process & derive tree
 *   tree(in): an abstract syntax tree form of a CUBRID insert_value
 *   db_values(out): array of newly set DB_VALUEs if successful, untouched
 *          otherwise
 *   values_count(in): number of values to store in db_value array.
 */

void
pt_evaluate_tree (PARSER_CONTEXT * parser, PT_NODE * tree, DB_VALUE * db_values, int values_count)
{
  pt_evaluate_tree_internal (parser, tree, db_values, values_count, false);
}

/*
 * pt_evaluate_tree_internal () -
 *   return: none
 *   parser(in):
 *   tree(in):
 *   db_values(in): array of newly set DB_VALUEs if successful, untouched
 *          otherwise
 *   values_count(in): Number of values to store in db_value.
 *   set_insert(in):
 */
void
pt_evaluate_tree_internal (PARSER_CONTEXT * parser, PT_NODE * tree, DB_VALUE * db_values, int values_count,
               bool having_serial)
{
  int error = NO_ERROR;
  PT_NODE *arg1, *arg2, *arg3, *temp;
  PT_NODE *or_next;
  DB_VALUE *val, opd1, opd2, opd3;
  PT_OP_TYPE op;
  PT_TYPE_ENUM type1 = PT_TYPE_NONE, type2 = PT_TYPE_NONE, type3 = PT_TYPE_NONE, common_type = PT_TYPE_NONE;
  TP_DOMAIN *domain;
  PT_MISC_TYPE qualifier = (PT_MISC_TYPE) 0;
  QUERY_ID query_id_self = parser->query_id;
  MOP serial_mop;
  DB_IDENTIFIER *serial_oid_p;
  int cached_num;
  int r = 0;
  int error_code;
  bool opd2_set_null = false;

  assert (parser != NULL);

  if (tree == NULL || db_values == NULL || pt_has_error (parser))
    {
      return;
    }
  if (values_count > 1 && tree->node_type != PT_SELECT && tree->node_type != PT_INTERSECTION
      && tree->node_type != PT_UNION && tree->node_type != PT_DIFFERENCE)
    {
      /* Only a query can return more than one value */
      PT_ERRORmf (parser, tree, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME__CAN_NOT_EVALUATE,
          pt_short_print (parser, tree));
      return;
    }

  switch (tree->node_type)
    {
    case PT_HOST_VAR:
    case PT_VALUE:
      val = pt_value_to_db (parser, tree);
      if (val)
    {
      (void) db_value_clone (val, db_values);
    }
      break;

    case PT_DOT_:
    case PT_NAME:
      if (!pt_eval_path_expr (parser, tree, db_values) && !pt_has_error (parser))
    {
      PT_ERRORmf (parser, tree, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME__CAN_NOT_EVALUATE,
              pt_short_print (parser, tree));
    }
      break;

    case PT_TUPLE_VALUE:
      {
    CURSOR_ID *cursor_id = tree->info.tuple_value.cursor_p;
    int index = tree->info.tuple_value.index;
    if (cursor_id == NULL)
      {
        PT_ERRORmf (parser, tree, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME__CAN_NOT_EVALUATE,
            pt_short_print (parser, tree));
        break;
      }
    error = cursor_get_tuple_value (cursor_id, index, db_values);
    if (error != NO_ERROR)
      {
        PT_ERRORc (parser, tree, er_msg ());
      }
    break;
      }

    case PT_INSERT_VALUE:
      if (tree->info.insert_value.is_evaluated)
    {
      /* Node was already evaluated, just copy the stored value */
      (void) db_value_clone (&tree->info.insert_value.value, db_values);
    }
      else
    {
      /* evaluate original node */
      pt_evaluate_tree_having_serial (parser, tree->info.insert_value.original_node, db_values, values_count);
    }
      break;

    case PT_EXPR:
      if (tree->info.expr.op == PT_FUNCTION_HOLDER)
    {
      if (pt_evaluate_function (parser, tree->info.expr.arg1, db_values) != NO_ERROR)
        {
          PT_ERRORmf (parser, tree, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME__CAN_NOT_EVALUATE,
              pt_short_print (parser, tree));
        }
      break;
    }
      if (tree->or_next)
    {
      /* The expression tree has 'or_next' filed. Evaluate it after converting to 'OR' tree. */

      /* save 'or_next' */
      or_next = tree->or_next;
      tree->or_next = NULL;

      /* make 'OR' tree */
      temp = parser_new_node (parser, PT_EXPR);
      temp->type_enum = PT_TYPE_LOGICAL;
      temp->info.expr.op = PT_OR;
      temp->info.expr.arg1 = tree;
      temp->info.expr.arg2 = or_next;

      /* evaluate the 'OR' tree */
      pt_evaluate_tree (parser, temp, db_values, 1);

      /* delete 'OR' node */
      temp->info.expr.arg1 = temp->info.expr.arg2 = NULL;
      temp->next = temp->or_next = NULL;
      parser_free_tree (parser, temp);

      /* restore 'or_next' */
      tree->or_next = or_next;
    }
      else
    {           /* if (tree->or_next) */
      op = tree->info.expr.op;

      /* If the an operand is a query, and the context is a table query (quantified comparison), rewrite the query
       * into something we can actually handle here, a table to set conversion. */
      if (pt_is_table_op (op))
        {
          tree->info.expr.arg2 = pt_query_to_set_table (parser, tree->info.expr.arg2);
        }
      else if (op == PT_EXISTS)
        {
          tree->info.expr.arg1 = pt_query_to_set_table (parser, tree->info.expr.arg1);
        }

      arg1 = tree->info.expr.arg1;
      if (op == PT_BETWEEN || op == PT_NOT_BETWEEN)
        {
          /* special handling for PT_BETWEEN and PT_NOT_BETWEEN */
          assert (tree->info.expr.arg2->node_type == PT_EXPR);
          arg2 = tree->info.expr.arg2->info.expr.arg1;
          arg3 = tree->info.expr.arg2->info.expr.arg2;
        }
      else
        {
          arg2 = tree->info.expr.arg2;
          arg3 = tree->info.expr.arg3;
        }

      if (op == PT_NEXT_VALUE || op == PT_CURRENT_VALUE)
        {
          if (!having_serial)
        {
          /* Serial not allowed in current context */
          PT_ERRORmf (parser, tree, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME__CAN_NOT_EVALUATE,
                  pt_short_print (parser, tree));
          break;
        }
          serial_mop = pt_resolve_serial (parser, arg1);
          if (serial_mop != NULL)
        {
          serial_oid_p = db_identifier (serial_mop);
          if (serial_oid_p != NULL)
            {
              error = do_get_serial_cached_num (&cached_num, serial_mop);
            }
          else
            {
              error = ER_FAILED;
            }
          if (error == NO_ERROR)
            {
              if (op == PT_CURRENT_VALUE)
            {
              error = serial_get_current_value (db_values, serial_oid_p, cached_num);
            }
              else
            {
              int num_alloc;

              val = pt_value_to_db (parser, arg2);
              num_alloc = db_get_int (val);
              if (num_alloc < 1)
                {
                  PT_ERRORm (parser, tree, MSGCAT_SET_PARSER_SEMANTIC,
                     MSGCAT_SEMANTIC_SERIAL_NUM_ALLOC_INVALID);
                  return;
                }
              else
                {
                  error =
                serial_get_next_value (db_values, serial_oid_p, cached_num, num_alloc, GENERATE_SERIAL);
                }
            }
            }

          if (error != NO_ERROR)
            {
              switch (er_errid ())
            {
            case ER_DB_NO_MODIFICATIONS:
              PT_ERRORm (parser, tree, MSGCAT_SET_ERROR, -(ER_DB_NO_MODIFICATIONS));
              return;
            case ER_QPROC_DB_SERIAL_NOT_FOUND:
              error_code = MSGCAT_SEMANTIC_SERIAL_NOT_DEFINED;
              break;
            case ER_QPROC_SERIAL_ALREADY_EXIST:
              error_code = MSGCAT_SEMANTIC_SERIAL_ALREADY_EXIST;
              break;
            case ER_QPROC_SERIAL_RANGE_OVERFLOW:
              error_code = MSGCAT_SEMANTIC_SERIAL_VALUE_OVERFLOW;
              break;
            case ER_QPROC_CANNOT_FETCH_SERIAL:
            case ER_QPROC_CANNOT_UPDATE_SERIAL:
            default:
              error_code = MSGCAT_SEMANTIC_SERIAL_IO_ERROR;
              break;
            }

              PT_ERRORmf (parser, tree, MSGCAT_SET_PARSER_SEMANTIC, error_code, arg1->info.name.original);
            }
        }
          else
        {
          PT_ERRORmf (parser, tree, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SERIAL_NOT_DEFINED,
                  arg1->info.name.original);
        }
          return;
        }

      db_make_null (&opd1);
      db_make_null (&opd2);
      db_make_null (&opd3);

      /* evaluate operands */
      if (!arg1 && op == PT_UNIX_TIMESTAMP)
        {
          pr_clone_value (&parser->sys_datetime, &opd1);
        }
      else
        {
          pt_evaluate_tree_having_serial (parser, arg1, &opd1, 1);
        }
      type1 = pt_db_to_type_enum ((DB_TYPE) opd1.domain.general_info.type);
      if (arg2 && !pt_has_error (parser))
        {
          pt_evaluate_tree_having_serial (parser, arg2, &opd2, 1);
          type2 = arg2->type_enum;
        }
      else
        {
          INTL_CODESET opd1_cs = DB_IS_NULL (&opd1) ? LANG_SYS_CODESET : db_get_string_codeset (&opd1);
          int opd1_coll = DB_IS_NULL (&opd1) ? LANG_SYS_COLLATION : db_get_string_collation (&opd1);

          switch (op)
        {
        case PT_TRIM:
        case PT_LTRIM:
        case PT_RTRIM:
          db_make_varchar (&opd2, 1, " ", 1, opd1_cs, opd1_coll);
          type2 = PT_TYPE_VARCHAR;
          break;
        case PT_FROM_UNIXTIME:
          db_make_null (&opd2);
          opd2_set_null = true;
          break;
        default:
          db_make_null (&opd2);
          break;
        }       /* switch (op) */
        }
      if (arg3 && !pt_has_error (parser))
        {
          pt_evaluate_tree_having_serial (parser, arg3, &opd3, 1);
          type3 = arg3->type_enum;
        }
      else
        {
          INTL_CODESET opd1_cs = DB_IS_NULL (&opd1) ? LANG_SYS_CODESET : db_get_string_codeset (&opd1);
          int opd1_coll = DB_IS_NULL (&opd1) ? LANG_SYS_COLLATION : db_get_string_collation (&opd1);

          switch (op)
        {
        case PT_REPLACE:
        case PT_TRANSLATE:
          db_make_varchar (&opd3, 1, "", 0, opd1_cs, opd1_coll);
          type3 = PT_TYPE_VARCHAR;
          break;
        case PT_LPAD:
        case PT_RPAD:
          db_make_varchar (&opd3, 1, " ", 1, opd1_cs, opd1_coll);
          type2 = PT_TYPE_VARCHAR;
          break;
        default:
          db_make_null (&opd3);
          break;
        }       /* switch (op) */
        }
      if (pt_has_error (parser))
        {
          pr_clear_value (&opd1);
          pr_clear_value (&opd2);
          pr_clear_value (&opd3);
          break;
        }

      /* try to evaluate the expression */
      if (op == PT_TRIM || op == PT_EXTRACT || op == PT_SUBSTRING || op == PT_EQ)
        {
          qualifier = tree->info.expr.qualifier;
        }
      domain = pt_node_to_db_domain (parser, tree, NULL);
      domain = tp_domain_cache (domain);

      int type_arg[2];
      type_arg[0] = PT_HOST_VAR;
      type_arg[1] = 0;

      (void) parser_walk_tree (parser, tree, pt_find_node_type_pre, type_arg, NULL, NULL);

      /* recheck type of expr for host_var */
      if (domain && domain->type && domain->type->id == DB_TYPE_NULL && (type_arg[1] != 0))
        {
          common_type = pt_common_type (type1, type2);
          if (type3 != PT_TYPE_NONE)
        {
          common_type = pt_common_type (common_type, type3);
        }
          domain = pt_type_enum_to_db_domain (common_type);
          domain = tp_domain_cache (domain);
        }

      /* PT_BETWEEN_xxxx, PT_ASSIGN, PT_LIKE_ESCAPE do not need to be evaluated and will return 0 from
       * 'pt_evaluate_db_value_expr()' */
      if (!pt_is_between_range_op (op) && op != PT_ASSIGN && op != PT_LIKE_ESCAPE && op != PT_CURRENT_VALUE)
        {
          if (!pt_evaluate_db_value_expr (parser, tree, op, &opd1, opd2_set_null ? NULL : &opd2, &opd3, db_values,
                          domain, arg1, arg2, arg3, qualifier))
        {
          PT_ERRORmf (parser, tree, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME__CAN_NOT_EVALUATE,
                  pt_short_print (parser, tree));
        }
        }

      db_value_clear (&opd1);
      db_value_clear (&opd2);
      db_value_clear (&opd3);
    }           /* if (tree->or_next) */
      break;

    case PT_SELECT:
    case PT_UNION:
    case PT_DIFFERENCE:
    case PT_INTERSECTION:
      /* cannot directly evaluate tree, since this may modify it */
      temp = parser_copy_tree (parser, tree);

      temp = mq_translate (parser, temp);

      if (temp == NULL)
    {
      if (!pt_has_error (parser))
        {
          PT_ERRORc (parser, tree, db_error_string (3));
        }
      break;
    }

      query_id_self = parser->query_id;
      parser->query_id = NULL_QUERY_ID;
      error = do_select (parser, temp);
      if (error == NO_ERROR)
    {
      /* If there isn't a value from the select, but the select succeeded, return a NULL instead of an error. It
       * might break something if an error were returned.  */
      if (pt_get_one_tuple_from_list_id (parser, temp, db_values, values_count) == 0)
        {
          for (r = 0; r < values_count; r++)
        {
          db_make_null (&db_values[r]);
        }
        }

      cursor_free_self_list_id (temp->etc);
      pt_end_query (parser, query_id_self);
    }
      else
    {
      if (!pt_has_error (parser))
        {
          PT_ERRORc (parser, tree, db_error_string (3));
        }
      parser->query_id = query_id_self;
    }

      parser_free_tree (parser, temp);
      break;

    case PT_INSERT:
      assert (tree->info.insert.value_clauses != NULL);
      if (tree->info.insert.value_clauses->next != NULL)
    {
      error = ER_DO_INSERT_TOO_MANY;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
      if (!pt_has_error (parser))
        {
          PT_ERRORc (parser, tree, db_error_string (3));
        }
      break;
    }

      /* Execute insert */
      error = do_insert (parser, tree);
      if (error >= NO_ERROR)
    {
      if ((val = (DB_VALUE *) (tree->etc)) == NULL)
        {
          PT_INTERNAL_ERROR (parser, "do_insert returns NULL result");
        }
      else
        {
          /* do_insert returns at most one value, inserts with selects are not allowed to be nested */
          (void) db_value_clone (val, db_values);
        }
      db_value_free (val);
      tree->etc = NULL;
    }
      else if (!pt_has_error (parser))
    {
      PT_ERRORc (parser, tree, db_error_string (3));
    }
      break;

    case PT_METHOD_CALL:

      if (!PT_IS_METHOD (tree)
      && !(tree->info.method_call.call_or_expr == PT_IS_CALL_STMT) && do_Trigger_involved == false)
    {
      // do not perform constant folding
      break;
    }

      if (qo_need_skip_execution ())
    {
      // It is for the get_query_info.
      // Do not call method by constant folding
      db_make_null (db_values);
      error = NO_ERROR;
    }
      else
    {
      error = do_call_method (parser, tree);
    }
      if (error >= NO_ERROR)
    {
      if ((val = (DB_VALUE *) (tree->etc)) != NULL)
        {           /* do_call_method returns at most one value */
          if (db_value_clone (val, db_values) != NO_ERROR)
        {
          db_make_null (db_values);
        }
          pr_free_ext_value (val);
        }
      else
        {
          db_make_null (db_values);
        }
    }
      else if (!pt_has_error (parser))
    {
      PT_ERRORc (parser, tree, db_error_string (3));
    }
      break;

    case PT_FUNCTION:
      switch (tree->info.function.function_type)
    {
      /* we have a set/multiset/sequence constructor function call. build the set/multiset/sequence using the
       * function arguments as the set/multiset/sequence element building blocks. */
    case F_SET:
      db_make_set (db_values, db_set_create_basic (NULL, NULL));
      if (!db_get_set (db_values))
        {
          PT_ERRORm (parser, tree, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          return;
        }

      if (pt_set_value_to_db (parser, &tree->info.function.arg_list, db_values, &tree->data_type) == NULL
          && !pt_has_error (parser))
        {
          PT_ERRORc (parser, tree, db_error_string (3));
        }
      return;

    case F_MULTISET:
      db_make_multiset (db_values, db_set_create_multi (NULL, NULL));
      if (!db_get_set (db_values))
        {
          PT_ERRORm (parser, tree, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          return;
        }

      if (pt_set_value_to_db (parser, &tree->info.function.arg_list, db_values, &tree->data_type) == NULL
          && !pt_has_error (parser))
        {
          PT_ERRORc (parser, tree, db_error_string (3));
        }
      return;

    case F_SEQUENCE:
      db_make_sequence (db_values, db_seq_create (NULL, NULL, 0));
      if (!db_get_set (db_values))
        {
          PT_ERRORm (parser, tree, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          return;
        }

      if (pt_seq_value_to_db (parser, tree->info.function.arg_list, db_values, &tree->data_type) == NULL
          && !pt_has_error (parser))
        {
          PT_ERRORc (parser, tree, db_error_string (3));
        }
      return;

    case F_TABLE_SET:
      db_make_set (db_values, db_set_create_basic (NULL, NULL));
      if (!db_get_set (db_values))
        {
          PT_ERRORm (parser, tree, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          return;
        }

      if (pt_set_table_to_db (parser, tree->info.function.arg_list, db_values, 0) == NULL && !pt_has_error (parser))
        {
          PT_ERRORc (parser, tree, db_error_string (3));
        }
      return;

    case F_TABLE_MULTISET:
      db_make_multiset (db_values, db_set_create_multi (NULL, NULL));
      if (!db_get_set (db_values))
        {
          PT_ERRORm (parser, tree, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          return;
        }
      if (pt_set_table_to_db (parser, tree->info.function.arg_list, db_values, 0) == NULL && !pt_has_error (parser))
        {
          PT_ERRORc (parser, tree, db_error_string (3));
        }
      return;

    case F_TABLE_SEQUENCE:
      db_make_sequence (db_values, db_seq_create (NULL, NULL, 0));
      if (!db_get_set (db_values))
        {
          PT_ERRORm (parser, tree, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          return;
        }
      if (pt_set_table_to_db (parser, tree->info.function.arg_list, db_values, 1) == NULL && !pt_has_error (parser))
        {
          PT_ERRORc (parser, tree, db_error_string (3));
        }
      return;

    default:
      PT_ERRORmf (parser, tree, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME__CAN_NOT_EVALUATE,
              pt_short_print (parser, tree));
      break;
    }
      break;

    default:
      PT_ERRORmf (parser, tree, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME__CAN_NOT_EVALUATE,
          pt_short_print (parser, tree));
      break;
    }
}

/*
 * pt_evaluate_tree_having_serial () -
 *   return:
 *   parser(in):
 *   tree(in):
 *   db_values(in):
 *   values_count(in):
 */
void
pt_evaluate_tree_having_serial (PARSER_CONTEXT * parser, PT_NODE * tree, DB_VALUE * db_value, int vals_cnt)
{
  pt_evaluate_tree_internal (parser, tree, db_value, vals_cnt, true);
}