Skip to content

File query_rewrite_term.c

File List > cubrid > src > optimizer > rewriter > query_rewrite_term.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.
 *
 */

/*
 * query_rewrite_predicate.c
 */

#ident "$Id$"

#include <assert.h>
#include "query_rewrite.h"

static void qo_converse_sarg_terms (PARSER_CONTEXT * parser, PT_NODE * where);
static void qo_reduce_comp_pair_terms (PARSER_CONTEXT * parser, PT_NODE ** wherep);
static void qo_rewrite_like_terms (PARSER_CONTEXT * parser, PT_NODE ** wherep);
static void qo_convert_to_range (PARSER_CONTEXT * parser, PT_NODE ** wherep);
static void qo_apply_range_intersection (PARSER_CONTEXT * parser, PT_NODE ** wherep);
static void qo_fold_is_and_not_null (PARSER_CONTEXT * parser, PT_NODE * from, PT_NODE ** wherep);

/*
 * qo_rewrite_terms () - checks all subqueries for rewrite optimizations
 *   return: PT_NODE *
 *   parser(in): parser environment
 *   node(in): possible query
 *   arg(in):
 *   continue_walk(in):
 *   
 *   Verify correctness before modifying previous steps
 */
void
qo_rewrite_terms (PARSER_CONTEXT * parser, PT_NODE * nodes, PT_NODE ** terms)
{
  if (*terms)
    {
      qo_converse_sarg_terms (parser, *terms);
      qo_reduce_comp_pair_terms (parser, terms);
      qo_rewrite_like_terms (parser, terms);
      qo_convert_to_range (parser, terms);
      qo_apply_range_intersection (parser, terms);
      qo_fold_is_and_not_null (parser, nodes, terms);
    }
}


/*
 * qo_collect_name_spec () -
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in):
 *   continue_walk(in):
 */
static PT_NODE *
qo_collect_name_spec (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  PT_NAME_SPEC_INFO *info = (PT_NAME_SPEC_INFO *) arg;

  /* To fall through from PT_DOT to PT_NAME, the `node` is changed in PT_DOT.
   * The original `node` needs to be backed up in order to return it later. */
  PT_NODE *backup_node = node;

  *continue_walk = PT_CONTINUE_WALK;

  switch (node->node_type)
    {
    case PT_DOT_:
      node = pt_get_end_path_node (node);
      if (node->node_type != PT_NAME)
    {
      break;        /* impossible case, give up */
    }
      [[fallthrough]];

    case PT_NAME:
      if (info->c_name->info.name.location > 0 && info->c_name->info.name.location < node->info.name.location)
    {
      /* next outer join location */
    }
      else
    {
      if (node->info.name.spec_id == info->c_name->info.name.spec_id)
        {
          /* check for name spec is same */
          if (pt_name_equal (parser, node, info->c_name))
        {
          info->c_name_num++;   /* found reduced attr */
        }
        }
      else
        {
          PT_NODE *point, *s_name;

          /* check for spec in other spec */
          for (point = info->s_point_list; point; point = point->next)
        {
          s_name = point;
          CAST_POINTER_TO_NODE (s_name);
          if (s_name->info.name.spec_id == node->info.name.spec_id)
            break;
        }

          /* not found */
          if (!point)
        {
          info->s_point_list = parser_append_node (pt_point (parser, node), info->s_point_list);
        }
        }
    }

      *continue_walk = PT_LIST_WALK;
      break;

    case PT_SELECT:
    case PT_UNION:
    case PT_DIFFERENCE:
    case PT_INTERSECTION:
      /* simply give up when we find query in predicate */
      info->query_serial_num++;
      break;

    case PT_EXPR:
      if (node->info.expr.op == PT_NEXT_VALUE || node->info.expr.op == PT_CURRENT_VALUE)
    {
      /* simply give up when we find serial */
      info->query_serial_num++;
      break;
    }

      if (PT_HAS_COLLATION (info->c_name->type_enum) && node->info.expr.op == PT_CAST
      && PT_HAS_COLLATION (node->type_enum) && node->info.expr.arg1 != NULL
      && node->info.expr.arg1->node_type == PT_NAME
      && node->info.expr.arg1->info.name.spec_id == info->c_name->info.name.spec_id)
    {
      int cast_coll = LANG_SYS_COLLATION;
      int name_coll = LANG_SYS_COLLATION;

      name_coll = PT_GET_COLLATION_MODIFIER (info->c_name);

      if (!PT_HAS_COLLATION_MODIFIER (info->c_name) && info->c_name->data_type != NULL)
        {
          name_coll = info->c_name->data_type->info.data_type.collation_id;
        }

      if (PT_EXPR_INFO_IS_FLAGED (node, PT_EXPR_INFO_CAST_COLL_MODIFIER))
        {
          cast_coll = PT_GET_COLLATION_MODIFIER (node);
        }
      else if (node->data_type != NULL)
        {
          cast_coll = node->data_type->info.data_type.collation_id;
        }

      if (cast_coll != name_coll)
        {
          /* predicate evaluates with different collation */
          info->query_serial_num++;
        }
    }
      break;
    default:
      break;
    }               /* switch (node->node_type) */

  if (info->query_serial_num > 0)
    {
      *continue_walk = PT_STOP_WALK;
    }

  return backup_node;
}

/*
 * qo_collect_name_spec_post () -
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in):
 *   continue_walk(in):
 */
static PT_NODE *
qo_collect_name_spec_post (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  PT_NAME_SPEC_INFO *info = (PT_NAME_SPEC_INFO *) arg;

  *continue_walk = PT_CONTINUE_WALK;

  if (info->query_serial_num > 0)
    {
      *continue_walk = PT_STOP_WALK;
    }

  return node;
}

/* 
 * qo_get_name_by_spec_id () - looks for a name with a matching id
 *   return: PT_NODE *
 *   parser(in): parser environment
 *   node(in): (name) node to compare id's with
 *   arg(in): info of spec and result
 *   continue_walk(in):
 */
PT_NODE *
qo_get_name_by_spec_id (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  SPEC_ID_INFO *info = (SPEC_ID_INFO *) arg;

  if (node->node_type == PT_NAME && node->info.name.spec_id == info->id)
    {
      *continue_walk = PT_STOP_WALK;
      info->appears = true;
    }

  return node;
}

/*
 * qo_check_nullable_expr () -
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in):
 *   continue_walk(in):
 */
PT_NODE *
qo_check_nullable_expr (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  int *nullable_cntp = (int *) arg;

  if (node->node_type == PT_EXPR)
    {
      /* check for nullable term: expr(..., NULL, ...) can be non-NULL */
      switch (node->info.expr.op)
    {
    case PT_IS_NULL:
    case PT_CASE:
    case PT_COALESCE:
    case PT_NVL:
    case PT_NVL2:
    case PT_DECODE:
    case PT_IF:
    case PT_IFNULL:
    case PT_ISNULL:
    case PT_CONCAT_WS:
    case PT_NULLSAFE_EQ:
      /* NEED FUTURE OPTIMIZATION */
      (*nullable_cntp)++;
      break;
    default:
      break;
    }
    }

  return node;
}

/*
 * qo_replace_spec_name_null () - replace spec names with PT_TYPE_NULL pt_values
 *   return: PT_NODE *
 *   parser(in): parser environment
 *   node(in): (name) node to compare id's with
 *   arg(in): spec
 *   continue_walk(in):
 */
static PT_NODE *
qo_replace_spec_name_null (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  PT_NODE *spec = (PT_NODE *) arg;
  PT_NODE *name;

  if (node->node_type == PT_NAME && node->info.name.spec_id == spec->info.spec.id)
    {
      node->node_type = PT_VALUE;
      node->type_enum = PT_TYPE_NULL;
    }

  if (node->node_type == PT_DOT_ && (name = node->info.dot.arg2) && name->info.name.spec_id == spec->info.spec.id)
    {
      parser_free_tree (parser, name);
      parser_free_tree (parser, node->info.expr.arg1);
      node->node_type = PT_VALUE;
      node->type_enum = PT_TYPE_NULL;
      /* By changing this node, we need to null the value container so that we protect parts of the code that ignore
       * type_enum set to PT_TYPE_NULL.  This is particularly problematic on PCs since they have different alignment
       * requirements. */
      node->info.value.data_value.set = NULL;
    }

  return node;
}

/*
 * qo_check_condition_null () -
 *   return:
 *   parser(in): parser environment
 *   path_spec(in): to test attributes as NULL
 *   query_where(in): clause to evaluate
 */
bool
qo_check_condition_null (PARSER_CONTEXT * parser, PT_NODE * path_spec, PT_NODE * query_where)
{
  PT_NODE *where;
  bool result = false;
  SEMANTIC_CHK_INFO sc_info = { NULL, NULL, 0, 0, 0, false, false };

  if (query_where == NULL)
    {
      return result;
    }

  where = parser_copy_tree_list (parser, query_where);
  where = parser_walk_tree (parser, where, qo_replace_spec_name_null, path_spec, NULL, NULL);

  sc_info.top_node = where;
  sc_info.donot_fold = false;
  where = pt_semantic_type (parser, where, &sc_info);
  result = pt_false_search_condition (parser, where);
  parser_free_tree (parser, where);

  /*
   * Ignore any error returned from semantic type check.
   * Just wanted to evaluate where clause with nulled spec names.
   */
  if (pt_has_error (parser))
    {
      parser_free_tree (parser, parser->error_msgs);
      parser->error_msgs = NULL;
    }

  return result;
}

/*
 * qo_check_nullable_expr_with_spec () -
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in):
 *   continue_walk(in):
 */
PT_NODE *
qo_check_nullable_expr_with_spec (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  SPEC_ID_INFO *info = (SPEC_ID_INFO *) arg;

  if (node->node_type == PT_EXPR)
    {
      /* check for nullable term: expr(..., NULL, ...) can be non-NULL */
      switch (node->info.expr.op)
    {
    case PT_IS_NULL:
    case PT_CASE:
    case PT_COALESCE:
    case PT_NVL:
    case PT_NVL2:
    case PT_DECODE:
    case PT_IF:
    case PT_IFNULL:
    case PT_ISNULL:
    case PT_CONCAT_WS:
      info->appears = false;
      parser_walk_tree (parser, node, qo_get_name_by_spec_id, info, NULL, NULL);
      if (info->appears)
        {
          info->nullable = true;
          *continue_walk = PT_STOP_WALK;
        }
      break;
    default:
      break;
    }
    }

  return node;
}


/*
 * qo_is_cast_attr () -
 *   return:
 *   expr(in):
 */
static int
qo_is_cast_attr (PT_NODE * expr)
{
  PT_NODE *arg1;

  /* check for CAST-expr */
  if (!expr || expr->node_type != PT_EXPR || expr->info.expr.op != PT_CAST || !(arg1 = expr->info.expr.arg1))
    {
      return 0;
    }

  return pt_is_attr (arg1);
}

/*
 * qo_is_reduceable_const () -
 *   return:
 *   expr(in):
 */
int
qo_is_reduceable_const (PT_NODE * expr)
{
  while (expr && expr->node_type == PT_EXPR)
    {
      if (expr->info.expr.op == PT_CAST || expr->info.expr.op == PT_TO_ENUMERATION_VALUE)
    {
      expr = expr->info.expr.arg1;
    }
      else
    {
      return false;     /* give up */
    }
    }

  return PT_IS_CONST_INPUT_HOSTVAR (expr);
}

/*
 * qo_reduce_equality_terms () -
 *   return:
 *   parser(in):
 *   node(in):
 *   wherep(in):
 *
 *  Obs: modified to support PRIOR operator as follows:
 *    -> PRIOR field = exp1 AND PRIOR field = exp2 =>
 *   PRIOR field = exp1 AND exp1 = exp2
 *    -> PRIOR ? -> replace with ?
 */
void
qo_reduce_equality_terms (PARSER_CONTEXT * parser, PT_NODE * node, PT_NODE ** wherep)
{
  PT_NODE *from;
  PT_NODE **orgp;
  PT_NODE *accumulator, *expr, *arg1, *arg2, *temp, *next;
  PT_NODE *join_term, *join_term_list, *s_name1, *s_name2;
  PT_NAME_SPEC_INFO info1, info2;
  int spec1_cnt, spec2_cnt;
  bool found_equality_term, found_join_term;
  PT_NODE *spec, *derived_table, *attr, *col;
  int i, num_check, idx;
  PT_NODE *save_where_next;
  bool copy_arg2;
  PT_NODE *dt1, *dt2;
  bool cut_off;
  PT_NODE *expr_prev = NULL;
  PT_NODE *opd1, *opd2;
  DB_VALUE *dbv1, *dbv2;

  /* init */
  orgp = wherep;
  accumulator = NULL;
  join_term_list = NULL;

  while ((expr = *wherep))
    {
      col = NULL;       /* init - reserve for constant column of derived-table */

      /* check for 1st phase; keep out OR conjunct; 1st init */
      found_equality_term = (expr->or_next == NULL) ? true : false;

      if (found_equality_term != true)
    {
      wherep = &(*wherep)->next;
      continue;     /* give up */
    }

      /* check for 2nd phase; '=', 'range ( =)' keep out function index expr = const */
      found_equality_term = false;  /* 2nd init */

      if (expr->info.expr.op == PT_EQ && expr->info.expr.arg1 && expr->info.expr.arg2
      && !(pt_is_function_index_expression (expr->info.expr.arg1) && qo_is_reduceable_const (expr->info.expr.arg2))
      && !(pt_is_function_index_expression (expr->info.expr.arg2) && qo_is_reduceable_const (expr->info.expr.arg1)))
    {           /* 'opd = opd' */
      found_equality_term = true;   /* pass 2nd phase */
      num_check = 2;
    }
      else if (expr->info.expr.op == PT_RANGE)
    {           /* 'opd range (opd =)' */
      PT_NODE *between_and;

      between_and = expr->info.expr.arg2;
      if (between_and->or_next == NULL  /* has only one range */
          && between_and->info.expr.op == PT_BETWEEN_EQ_NA)
        {
          found_equality_term = true;   /* pass 2nd phase */
          num_check = 1;
        }
    }

      if (found_equality_term != true)
    {
      wherep = &(*wherep)->next;
      continue;     /* give up */
    }

      /* check for 3rd phase; 'attr = const', 'attr range (const =)' */
      found_equality_term = false;  /* 3rd init */

      for (i = 0; i < num_check; i++)
    {
      arg1 = (i == 0) ? expr->info.expr.arg1 : expr->info.expr.arg2;
      arg2 = (i == 0) ? expr->info.expr.arg2 : expr->info.expr.arg1;

      if (expr->info.expr.op == PT_RANGE)
        {
          arg2 = arg2->info.expr.arg1;
        }

      /* if arg1 is expression with PRIOR, move arg1 to the arg1 of PRIOR */

      if (arg1->node_type == PT_EXPR && arg1->info.expr.op == PT_PRIOR && pt_is_attr (arg1->info.expr.arg1))
        {
          arg1 = arg1->info.expr.arg1;
        }

      if (pt_is_attr (arg1) || pt_is_function_index_expression (arg1))
        {
          if (qo_is_reduceable_const (arg2))
        {
          found_equality_term = true;
          break;    /* immediately break */
        }
          else if (pt_is_attr (arg2))
        {
          ;     /* nop */
        }
          else if (qo_is_cast_attr (arg2))
        {
          arg2 = arg2->info.expr.arg1;
        }
          else
        {
          continue; /* not found. step to next */
        }

          if (node->node_type == PT_SELECT)
        {
          from = node->info.query.q.select.from;
        }
          else if (node->node_type == PT_DELETE)
        {
          from = node->info.delete_.spec;
        }
          else if (node->node_type == PT_UPDATE)
        {
          from = node->info.update.spec;
        }
          else
        {
          from = NULL;  /* not found. step to next */
        }

          for (spec = from; spec; spec = spec->next)
        {
          if (spec->info.spec.id == arg2->info.name.spec_id)
            break;  /* found match */
        }

          /* if arg2 is derived alias col, get its corresponding constant column from derived-table */
          if (spec && spec->info.spec.derived_table_type == PT_IS_SUBQUERY
          && (derived_table = spec->info.spec.derived_table) && derived_table->node_type == PT_SELECT
          && !derived_table->info.query.q.select.single_table_opt)
        {
          /* traverse as_attr_list */
          for (attr = spec->info.spec.as_attr_list, idx = 0; attr; attr = attr->next, idx++)
            {
              if (pt_name_equal (parser, attr, arg2))
            break;  /* found match */
            }       /* for (attr = ...) */

          /* get corresponding column */
          col = pt_get_select_list (parser, derived_table);
          for (; col && idx; col = col->next, idx--)
            {
              ;     /* step to next */
            }
          /* replace a constant value for a substitutable node which is set to PT_NAME_INFO_CONSTANT */
          if (col->node_type == PT_NAME || col->node_type == PT_DOT_)
            {
              col = pt_get_end_path_node (col);
              if (PT_NAME_INFO_IS_FLAGED (col, PT_NAME_INFO_CONSTANT))
            {
              col = col->info.name.constant_value;
            }
            }
          /* do not reduce PT_NAME that belongs to PT_NODE_LIST to PT_VALUE */
          if (attr && col && !PT_IS_VALUE_QUERY (col) && qo_is_reduceable_const (col))
            {
              /* add additional equailty-term; is reduced */
              PT_NODE *expr_copy = parser_copy_tree (parser, expr);
              PT_EXPR_INFO_SET_FLAG (expr_copy, PT_EXPR_INFO_DO_NOT_AUTOPARAM);
              *wherep = parser_append_node (expr_copy, *wherep);

              /* select-list's PT_NODE can have next PT_NODEs. so copy select_list to col node */
              col = parser_copy_tree (parser, col);

              /* reset arg1, arg2 */
              arg1 = arg2;
              arg2 = col;

              found_equality_term = true;
              break;    /* immediately break */
            }
        }       /* if arg2 is derived alias-column */
        }           /* if (pt_is_attr(arg1)) */
    }           /* for (i = 0; ...) */

      if (found_equality_term != true)
    {
      wherep = &(*wherep)->next;
      continue;     /* give up */
    }

      /*
       * now, finally pass all check
       */

      save_where_next = (*wherep)->next;

      if (pt_is_attr (arg2))
    {
      temp = arg1;
      arg1 = arg2;
      arg2 = temp;
    }

      /* at here, arg1 is reduced attr */

      *wherep = expr->next;
      if (col)
    {
      ;         /* corresponding constant column of derived-table */
    }
      else
    {
      expr->next = accumulator;
      accumulator = expr;
    }

      /* Restart where at beginning of WHERE clause because we may find new terms after substitution, and must
       * substitute entire where clause because incoming order is arbitrary. */
      wherep = orgp;

      temp = pt_get_end_path_node (arg1);

      info1.c_name = temp;
      info2.c_name = temp;

      /* save reduced join terms */
      for (temp = *wherep; temp; temp = temp->next)
    {
      if (temp == expr)
        {
          /* this is the working equality_term, skip and go ahead */
          continue;
        }

      if (temp->node_type != PT_EXPR || !pt_is_symmetric_op (temp->info.expr.op))
        {
          /* skip and go ahead */
          continue;
        }

      next = temp->next;    /* save and cut-off link */
      temp->next = NULL;

      /* check for already added join term */
      for (join_term = join_term_list; join_term; join_term = join_term->next)
        {
          if (join_term->etc == (void *) temp)
        {
          break;    /* found */
        }
        }

      /* check for not added join terms */
      if (join_term == NULL)
        {

          found_join_term = false;  /* init */

          /* check for attr of other specs */
          if (temp->or_next == NULL)
        {
          info1.c_name_num = 0;
          info1.query_serial_num = 0;
          info1.s_point_list = NULL;
          (void) parser_walk_tree (parser, temp->info.expr.arg1, qo_collect_name_spec, &info1,
                       qo_collect_name_spec_post, &info1);

          info2.c_name_num = 0;
          info2.query_serial_num = 0;
          info2.s_point_list = NULL;
          if (info1.query_serial_num == 0)
            {
              (void) parser_walk_tree (parser, temp->info.expr.arg2, qo_collect_name_spec, &info2,
                           qo_collect_name_spec_post, &info2);
            }

          if (info1.query_serial_num == 0 && info2.query_serial_num == 0)
            {
              /* check for join term related to reduced attr lhs and rhs has name of other spec CASE 1:
               * X.c_name = Y.attr CASE 2: X.c_name + Y.attr = ? CASE 3: Y.attr = X.c_name CASE 4: ? = Y.attr +
               * X.c_name */

              spec1_cnt = pt_length_of_list (info1.s_point_list);
              spec2_cnt = pt_length_of_list (info2.s_point_list);

              if (info1.c_name_num)
            {
              if (spec1_cnt == 0)
                {   /* CASE 1 */
                  if (spec2_cnt == 1)
                {
                  found_join_term = true;
                }
                }
              else if (spec1_cnt == 1)
                {   /* CASE 2 */
                  if (spec2_cnt == 0)
                {
                  found_join_term = true;
                }
                  else if (spec2_cnt == 1)
                {
                  s_name1 = info1.s_point_list;
                  s_name2 = info2.s_point_list;
                  CAST_POINTER_TO_NODE (s_name1);
                  CAST_POINTER_TO_NODE (s_name2);
                  if (s_name1->info.name.spec_id == s_name2->info.name.spec_id)
                    {
                      /* X.c_name + Y.attr = Y.attr */
                      found_join_term = true;
                    }
                  else
                    {
                      /* X.c_name + Y.attr = Z.attr */
                      ; /* nop */
                    }
                }
                }
            }
              else if (info2.c_name_num)
            {
              if (spec2_cnt == 0)
                {   /* CASE 3 */
                  if (spec1_cnt == 1)
                {
                  found_join_term = true;
                }
                }
              else if (spec2_cnt == 1)
                {   /* CASE 4 */
                  if (spec1_cnt == 0)
                {
                  found_join_term = true;
                }
                  else if (spec1_cnt == 1)
                {
                  s_name1 = info1.s_point_list;
                  s_name2 = info2.s_point_list;
                  CAST_POINTER_TO_NODE (s_name1);
                  CAST_POINTER_TO_NODE (s_name2);
                  if (s_name1->info.name.spec_id == s_name2->info.name.spec_id)
                    {
                      /* Y.attr = Y.attr + X.c_name */
                      found_join_term = true;
                    }
                  else
                    {
                      /* Z.attr = Y.attr + X.c_name */
                      ; /* nop */
                    }
                }
                }
            }
            }

          /* free name list */
          if (info1.s_point_list)
            {
              parser_free_tree (parser, info1.s_point_list);
            }
          if (info2.s_point_list)
            {
              parser_free_tree (parser, info2.s_point_list);
            }
        }       /* if (temp->or_next == NULL) */

          if (found_join_term)
        {
          join_term = parser_copy_tree (parser, temp);

          if (join_term != NULL)
            {
              join_term->etc = (void *) temp;   /* mark as added */
              join_term_list = parser_append_node (join_term, join_term_list);
            }
        }

        }           /* if (join_term == NULL) */

      temp->next = next;    /* restore link */
    }           /* for (term = *wherep; term; term = term->next) */

      copy_arg2 = false;    /* init */

      if (PT_IS_PARAMETERIZED_TYPE (arg1->type_enum))
    {
      DB_VALUE *dbval, dbval_res;
      TP_DOMAIN *dom;

      /* don't replace node's data type precision, scale */
      if (PT_IS_CONST_NOT_HOSTVAR (arg2))
        {
          dom = pt_node_to_db_domain (parser, arg1, NULL);
          dom = tp_domain_cache (dom);
          if (dom->precision <= DB_MAX_LITERAL_PRECISION)
        {
          if ((dbval = pt_value_to_db (parser, arg2)) == NULL)
            {
              *wherep = save_where_next;
              continue; /* give up */
            }
          db_make_null (&dbval_res);
          if (tp_value_cast_force (dbval, &dbval_res, dom, false) != DOMAIN_COMPATIBLE)
            {
              PT_ERRORmf2 (parser, arg2, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CANT_COERCE_TO,
                   pt_short_print (parser, arg2), pt_show_type_enum (arg1->type_enum));
              *wherep = save_where_next;
              continue; /* give up */
            }
          temp = pt_dbval_to_value (parser, &dbval_res);
          pr_clear_value (&dbval_res);
        }
          else
        {       /* too big literal string */
          PT_NODE *dt = NULL;
          if (arg1->type_enum == PT_TYPE_ENUMERATION)
            {
              /* be sure to cast to the same enumeration type */
              dt = arg1->data_type;
            }

          temp =
            pt_wrap_with_cast_op (parser, parser_copy_tree_list (parser, arg2), arg1->type_enum,
                      TP_FLOATING_PRECISION_VALUE, 0, dt);
          if (temp == NULL)
            {
              PT_ERRORm (parser, arg2, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
              *wherep = save_where_next;
              continue; /* give up */
            }
        }
        }
      else
        {           /* is CAST expr */
          if ((temp = parser_copy_tree_list (parser, arg2)) == NULL)
        {
          PT_ERRORm (parser, arg2, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          *wherep = save_where_next;
          continue; /* give up */
        }
        }

      arg2 = temp;

      copy_arg2 = true; /* mark as copy */
    }

      /* replace 'arg1' in '*wherep' with 'arg2' with location checking */
      temp = pt_get_end_path_node (arg1);

      if (node->node_type == PT_SELECT)
    {
      /* query with WHERE condition */
      node->info.query.q.select.list = pt_lambda_with_arg (parser, node->info.query.q.select.list, arg1, arg2,
                                   (temp->info.name.location > 0 ? true : false), 1,
                                   true /* dont_replace */ );
    }
      *wherep = pt_lambda_with_arg (parser, *wherep, arg1, arg2, (temp->info.name.location > 0 ? true : false), 1,
                    false /* dont_replace: DEFAULT */ );

      /* Leave "wherep" pointing at the begining of the rest of the predicate. We still gurantee loop termination
       * because we have removed a term. future iterations which do not fall into this case will advance to the next
       * term. */

      /* free copied constant column */
      if (copy_arg2)
    {
      parser_free_tree (parser, arg2);
    }
    }

  *orgp = parser_append_node (accumulator, *orgp);

  if (join_term_list)
    {
      /* mark as transitive join terms and append to the WHERE clause */
      for (join_term = join_term_list; join_term; join_term = join_term->next)
    {
      PT_EXPR_INFO_SET_FLAG (join_term, PT_EXPR_INFO_TRANSITIVE);
      join_term->etc = (void *) NULL;   /* clear */
    }

      *orgp = parser_append_node (join_term_list, *orgp);
    }

  /* remove always-true term */
  while ((expr = ((expr_prev) ? expr_prev->next : *orgp)))
    {
      PT_OP_TYPE op = expr->info.expr.op;
      cut_off = false;
      opd1 = expr->info.expr.arg1;
      opd2 = expr->info.expr.arg2;

      if (expr->or_next == NULL)
    {
      if (opd1 && opd2 && op == PT_EQ && opd1->node_type == PT_VALUE && opd2->node_type == PT_VALUE)
        {
          dbv1 = pt_value_to_db (parser, opd1);
          dbv2 = pt_value_to_db (parser, opd2);
          if (db_value_compare (dbv1, dbv2) == DB_EQ)
        {
          cut_off = true;
        }
        }
    }
      else
    {
      /*
       * give up
       */
      ;
    }

      if (cut_off)
    {
      /* cut if off from CNF list */
      if (expr_prev)
        {
          expr_prev->next = expr->next;
        }
      else
        {
          *orgp = expr->next;
        }
      expr->next = NULL;
      parser_free_tree (parser, expr);
    }
      else
    {
      expr_prev = expr;
    }
    }


}

/*
 * qo_reduce_equality_terms_post ()
 *   return: PT_NODE *
 *   parser(in): parser environment
 *   node(in): (name) node to compare id's with
 *   arg(in): info of spec and result
 *   continue_walk(in):
 */
PT_NODE *
qo_reduce_equality_terms_post (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  PT_NODE **wherep;

  if (node->node_type == PT_SELECT)
    {
      wherep = &node->info.query.q.select.where;
      QO_CHECK_AND_REDUCE_EQUALITY_TERMS (parser, node, wherep);
    }

  return node;
}

/*
 * qo_converse_sarg_terms () -
 *   return:
 *   parser(in):
 *   where(in): CNF list of WHERE clause
 *
 * Note:
 *      Convert terms of the form 'constant op attr' to 'attr op constant'
 *      by traversing expression tree with prefix order (left child,
 *      right child, and then parent). Convert 'attr op attr' so, LHS has more
 *      common attribute.
 *
 *  examples:
 *      0. where 5 = a                     -->  where a = 5
 *      1. where -5 = -a                   -->  where a = 5
 *      2. where -5 = -(-a)                -->  where a = -5
 *      3. where 5 = -a                    -->  where a = -5
 *      4. where 5 = -(-a)                 -->  where a = 5
 *      5. where 5 > x.a and/or x.a = y.b  -->  where x.a < 5 and/or x.a = y.b
 *      6. where b = a or c = a            -->  where a = b or a = c
 *      7. where b = -a or c = a           -->  where a = -b or a = c
 *      8. where b = a or c = a            -->  where a = b or a = c
 *      9. where a = b or b = c or d = b   -->  where b = a or b = c or b = d
 *
 * Obs: modified to support PRIOR
 *  examples:
 *      0. connect by 5 = prior a          -->  connect by prior a = 5
 *      1. connect by -5 = prior (-a)      -->  connect by prior a = 5
 *  ...
 *  prior(-attr) between opd1 and opd2 -->
 *      prior(-attr) >= opd1 AND prior(-attr) <= opd2 -->
 *  prior (attr) <= -opd1 AND prior(attr) >= -opd2 -->
 *  prior (attr) between -opd2 and -opd1
 */
static void
qo_converse_sarg_terms (PARSER_CONTEXT * parser, PT_NODE * where)
{
  PT_NODE *cnf_node, *dnf_node, *arg1, *arg2, *arg1_arg1, *arg2_arg1;
  PT_NODE *arg1_prior_father, *arg2_prior_father;
  PT_OP_TYPE op_type;
  PT_NODE *attr, *attr_list;
  int arg1_cnt, arg2_cnt;


  /* traverse CNF list */
  for (cnf_node = where; cnf_node; cnf_node = cnf_node->next)
    {
      attr_list = NULL;     /* init */

      /* STEP 1: traverse DNF list to generate attr_list */
      for (dnf_node = cnf_node; dnf_node; dnf_node = dnf_node->or_next)
    {

      if (dnf_node->node_type != PT_EXPR)
        {
          continue;
        }

      op_type = dnf_node->info.expr.op;
      /* not CNF/DNF form; give up */
      if (op_type == PT_AND || op_type == PT_OR)
        {
          if (attr_list)
        {
          parser_free_tree (parser, attr_list);
          attr_list = NULL;
        }

          break;        /* immediately, exit loop */
        }

      arg1_prior_father = arg2_prior_father = NULL;

      arg1 = dnf_node->info.expr.arg1;
      /* go in PRIOR argument but memorize it for further node manag */
      if (pt_is_expr_node (arg1) && arg1->info.expr.op == PT_PRIOR)
        {
          arg1_prior_father = arg1;
          arg1 = arg1->info.expr.arg1;
        }

      arg1_arg1 = ((pt_is_expr_node (arg1) && arg1->info.expr.op == PT_UNARY_MINUS) ? arg1->info.expr.arg1 : NULL);
      while (pt_is_expr_node (arg1) && arg1->info.expr.op == PT_UNARY_MINUS)
        {
          arg1 = arg1->info.expr.arg1;
        }

      if (op_type == PT_BETWEEN && arg1_arg1 && (pt_is_attr (arg1) || pt_is_function_index_expression (arg1)))
        {
          /* term in the form of '-attr between opd1 and opd2' convert to '-attr >= opd1 and -attr <= opd2' */

          /* check for one range spec */
          if (cnf_node == dnf_node && dnf_node->or_next == NULL)
        {
          arg2 = dnf_node->info.expr.arg2;
          assert (arg2->node_type == PT_EXPR);
          /* term of '-attr >= opd1' */
          dnf_node->info.expr.arg2 = arg2->info.expr.arg1;
          op_type = dnf_node->info.expr.op = PT_GE;
          /* term of '-attr <= opd2' */
          arg2->info.expr.arg1 = parser_copy_tree (parser, dnf_node->info.expr.arg1);
          arg2->info.expr.op = PT_LE;
          /* term of 'and' */
          arg2->next = dnf_node->next;
          dnf_node->next = arg2;
        }
        }

      arg2 = dnf_node->info.expr.arg2;

      /* go in PRIOR argument but memorize it for further node manag */
      if (pt_is_expr_node (arg2) && arg2->info.expr.op == PT_PRIOR)
        {
          arg2_prior_father = arg2;
          arg2 = arg2->info.expr.arg1;
        }

      while (pt_is_expr_node (arg2) && arg2->info.expr.op == PT_UNARY_MINUS)
        {
          arg2 = arg2->info.expr.arg1;
        }

      /* add sargable attribute to attr_list */
      if (arg1 && arg2 && pt_converse_op (op_type) != 0)
        {
          if (pt_is_attr (arg1) || pt_is_function_index_expression (arg1))
        {
          for (attr = attr_list; attr; attr = attr->next)
            {
              if (pt_check_path_eq (parser, attr, arg1) == 0)
            {
              attr->line_number++;  /* increase attribute count */
              break;
            }
            }

          /* not found; add new attribute */
          if (attr == NULL)
            {
              attr = pt_point (parser, arg1);
              if (attr != NULL)
            {
              attr->line_number = 1;    /* set attribute count */

              attr_list = parser_append_node (attr_list, attr);
            }
            }
        }

          if (pt_is_attr (arg2) || pt_is_function_index_expression (arg2))
        {
          for (attr = attr_list; attr; attr = attr->next)
            {
              if (pt_check_path_eq (parser, attr, arg2) == 0)
            {
              attr->line_number++;  /* increase attribute count */
              break;
            }
            }

          /* not found; add new attribute */
          if (attr == NULL)
            {
              attr = pt_point (parser, arg2);

              if (attr != NULL)
            {
              attr->line_number = 1;    /* set attribute count */

              attr_list = parser_append_node (attr_list, attr);
            }
            }
        }
        }
    }

      /* STEP 2: re-traverse DNF list to converse sargable terms */
      for (dnf_node = cnf_node; dnf_node; dnf_node = dnf_node->or_next)
    {
      if (dnf_node->node_type != PT_EXPR)
        continue;

      arg1_prior_father = arg2_prior_father = NULL;

      /* filter out unary minus nodes */
      while ((arg1 = dnf_node->info.expr.arg1) && (arg2 = dnf_node->info.expr.arg2))
        {
          /* go in PRIOR argument but memorize it for further node manag */
          if (pt_is_expr_node (arg1) && arg1->info.expr.op == PT_PRIOR)
        {
          arg1_prior_father = arg1;
          arg1 = arg1->info.expr.arg1;
        }

          if (pt_is_expr_node (arg2) && arg2->info.expr.op == PT_PRIOR)
        {
          arg2_prior_father = arg2;
          arg2 = arg2->info.expr.arg1;
        }

          op_type = pt_converse_op (dnf_node->info.expr.op);
          arg1_arg1 =
        ((pt_is_expr_node (arg1) && arg1->info.expr.op == PT_UNARY_MINUS) ? arg1->info.expr.arg1 : NULL);
          arg2_arg1 =
        ((pt_is_expr_node (arg2) && arg2->info.expr.op == PT_UNARY_MINUS) ? arg2->info.expr.arg1 : NULL);

          if (arg1_arg1 && arg2_arg1)
        {
          /* Delete both minus from prior also. */
          if (arg1_prior_father)
            {
              arg1_prior_father->info.expr.arg1 = arg1_prior_father->info.expr.arg1->info.expr.arg1;
            }
          if (arg2_prior_father)
            {
              arg2_prior_father->info.expr.arg1 = arg2_prior_father->info.expr.arg1->info.expr.arg1;
            }

          /* term in the form of '-something op -something' */
          dnf_node->info.expr.arg1 = arg1->info.expr.arg1;
          arg1->info.expr.arg1 = NULL;
          parser_free_tree (parser, arg1);
          dnf_node->info.expr.arg2 = arg2->info.expr.arg1;
          arg2->info.expr.arg1 = NULL;
          parser_free_tree (parser, arg2);

          /* both minus operators are gone but they were written over the prior operator so we must add them
           * again. */
          if (arg1_prior_father)
            {
              dnf_node->info.expr.arg1 = arg1_prior_father;
            }
          if (arg2_prior_father)
            {
              dnf_node->info.expr.arg2 = arg2_prior_father;
            }
        }
          else if (op_type != 0 && arg1_arg1
               && ((pt_is_attr (arg1_arg1) || pt_is_function_index_expression (arg1_arg1))
               || (pt_is_expr_node (arg1_arg1) && arg1_arg1->info.expr.op == PT_UNARY_MINUS))
               && pt_is_const (arg2))
        {
          /* arg1 was with prior, make the modifications in prior and move the prior to
           * dnf_node->info.expr.arg2 */

          /* prior (-attr) op const => prior attr op -const */
          if (arg1_prior_father)
            {
              /* cut - from prior -attr */
              arg1_prior_father->info.expr.arg1 = arg1->info.expr.arg1;

              dnf_node->info.expr.arg1 = arg1_prior_father;
              arg1->info.expr.arg1 = arg2;
              dnf_node->info.expr.arg2 = arg1;
            }
          else
            {
              /* term in the form of '-attr op const' or '-(-something) op const' */
              dnf_node->info.expr.arg1 = arg1->info.expr.arg1;
              arg1->info.expr.arg1 = arg2;
              dnf_node->info.expr.arg2 = arg1;
            }
        }
          else if (op_type != 0 && arg2_arg1
               && ((pt_is_attr (arg2_arg1) || pt_is_function_index_expression (arg2_arg1))
               || (pt_is_expr_node (arg2_arg1) && arg2_arg1->info.expr.op == PT_UNARY_MINUS))
               && pt_is_const (arg1))
        {
          /* arg2 was with prior, make the modifications in prior and move the prior to
           * dnf_node->info.expr.arg1 */

          /* const op prior (-attr) => -const op prior attr */
          if (arg2_prior_father)
            {
              /* cut - from prior -attr */
              arg2_prior_father->info.expr.arg1 = arg2_prior_father->info.expr.arg1->info.expr.arg1;

              dnf_node->info.expr.arg2 = arg2_prior_father;
              arg2->info.expr.arg1 = arg1;
              dnf_node->info.expr.arg1 = arg2;
            }
          else
            {
              /* term in the form of 'const op -attr' or 'const op -(-something)' */
              dnf_node->info.expr.arg2 = arg2->info.expr.arg1;
              arg2->info.expr.arg1 = arg1;
              dnf_node->info.expr.arg1 = arg2;
            }
        }
          else
        {
          break;
        }

          /* swap term's operator */
          dnf_node->info.expr.op = op_type;
        }

      op_type = dnf_node->info.expr.op;
      arg1 = dnf_node->info.expr.arg1;
      arg2 = dnf_node->info.expr.arg2;

      arg1_prior_father = arg2_prior_father = NULL;
      /* if arg1 or arg2 is PT_PRIOR, go in its argument */
      if (pt_is_expr_node (arg1) && arg1->info.expr.op == PT_PRIOR)
        {
          /* keep its parent so when swapping the two elements, swap with its father */
          arg1_prior_father = arg1;
          arg1 = arg1->info.expr.arg1;
        }
      if (pt_is_expr_node (arg2) && arg2->info.expr.op == PT_PRIOR)
        {
          arg2_prior_father = arg2;
          arg2 = arg2->info.expr.arg1;
        }

      if (op_type == PT_AND)
        {
          /* not CNF form; what do I have to do? */

          /* traverse left child */
          qo_converse_sarg_terms (parser, arg1);
          /* traverse right child */
          qo_converse_sarg_terms (parser, arg2);

        }
      else if (op_type == PT_OR)
        {
          /* not DNF form; what do I have to do? */

          /* traverse left child */
          qo_converse_sarg_terms (parser, arg1);
          /* traverse right child */
          qo_converse_sarg_terms (parser, arg2);

        }
      /* sargable term, where 'op_type' is one of '=', '<' '<=', '>', or '>=' */
      else if (arg1 && arg2 && (op_type = pt_converse_op (op_type)) != 0
           && (pt_is_attr (arg2) || pt_is_function_index_expression (arg2)))
        {
          if (pt_is_attr (arg1) || pt_is_function_index_expression (arg1))
        {
          /* term in the form of 'attr op attr' */

          arg1_cnt = arg2_cnt = 0;  /* init */
          for (attr = attr_list; attr; attr = attr->next)
            {
              if (pt_check_path_eq (parser, attr, arg1) == 0)
            {
              arg1_cnt = attr->line_number;
            }
              else if (pt_check_path_eq (parser, attr, arg2) == 0)
            {
              arg2_cnt = attr->line_number;
            }

              if (arg1_cnt && arg2_cnt)
            {
              break;    /* already found both arg1, arg2 */
            }
            }

          if (!arg1_cnt || !arg2_cnt)
            {
              /* something wrong; skip and go ahead */
              continue;
            }

          /* swap */
          if (arg1_cnt < arg2_cnt)
            {
              /* check if arg1 and/or arg2 have PRIOR above them. If so, swap the arg with the prior also */
              if (arg1_prior_father)
            {
              arg1 = arg1_prior_father;
            }
              if (arg2_prior_father)
            {
              arg2 = arg2_prior_father;
            }

              dnf_node->info.expr.arg1 = arg2;
              dnf_node->info.expr.arg2 = arg1;
              dnf_node->info.expr.op = op_type;

              /* change back arg1 and arg2 */
              if (arg1_prior_father)
            {
              arg1 = arg1_prior_father->info.expr.arg1;
            }
              if (arg2_prior_father)
            {
              arg2 = arg2_prior_father->info.expr.arg1;
            }
            }
        }
          else
        {
          /* term in the form of 'non-attr op attr' */

          /* swap */

          /* check if arg1 and/or arg2 have PRIOR above them. If so, swap the arg with the prior also */
          if (arg1_prior_father)
            {
              arg1 = arg1_prior_father;
            }
          if (arg2_prior_father)
            {
              arg2 = arg2_prior_father;
            }

          dnf_node->info.expr.arg1 = arg2;
          dnf_node->info.expr.arg2 = arg1;
          dnf_node->info.expr.op = op_type;

          /* change back arg1 and arg2 */
          if (arg1_prior_father)
            {
              arg1 = arg1_prior_father->info.expr.arg1;
            }
          if (arg2_prior_father)
            {
              arg2 = arg2_prior_father->info.expr.arg1;
            }
        }
        }
    }

      if (attr_list)
    {
      parser_free_tree (parser, attr_list);
      attr_list = NULL;
    }
    }
}

/*
 * qo_fold_is_and_not_null () - Make IS NOT NULL node that is always true as 1
 *               and make IS NULL node that is always false as 0
 *   return:
 *   parser(in):
 *   wherep(in): pointer to WHERE list
 */
static void
qo_fold_is_and_not_null (PARSER_CONTEXT * parser, PT_NODE * from, PT_NODE ** wherep)
{
  PT_NODE *node, *sibling, *prev, *fold;
  DB_VALUE value;
  bool found;
  PT_NODE *node_prior, *sibling_prior;
  PT_NODE *spec = NULL;

  /* traverse CNF list and keep track of the pointer to previous node */
  prev = NULL;
  while ((node = (prev ? prev->next : *wherep)))
    {
      if (node->node_type != PT_EXPR || (node->info.expr.op != PT_IS_NULL && node->info.expr.op != PT_IS_NOT_NULL)
      || node->or_next != NULL)
    {
      /* neither expression node, IS NULL/IS NOT NULL node nor one predicate term */
      prev = prev ? prev->next : node;
      continue;
    }

      node_prior = pt_get_first_arg_ignore_prior (node);
      if (!pt_is_attr (node_prior) && !qo_is_cast_attr (node_prior))
    {
      /* LHS is not an attribute */
      prev = prev ? prev->next : node;
      continue;
    }

      /* search if there's a term that make this IS NULL/IS NOT NULL node meaningless; that is, a term that has the
       * same attribute */
      found = false;
      for (sibling = *wherep; sibling; sibling = sibling->next)
    {
      if (sibling == node || sibling->node_type != PT_EXPR || sibling->or_next != NULL)
        {
          continue;
        }

      if (sibling->info.expr.location != node->info.expr.location)
        {
          continue;
        }

      sibling_prior = pt_get_first_arg_ignore_prior (sibling);

      /* just one node from node and sibling contains the PRIOR -> do nothing, they are not comparable */
      if ((PT_IS_EXPR_WITH_PRIOR_ARG (node) && !PT_IS_EXPR_WITH_PRIOR_ARG (sibling))
          || (!PT_IS_EXPR_WITH_PRIOR_ARG (node) && PT_IS_EXPR_WITH_PRIOR_ARG (sibling)))
        {
          continue;
        }

      if (pt_check_path_eq (parser, node_prior, sibling_prior) == 0
          || pt_check_path_eq (parser, node_prior, sibling->info.expr.arg2) == 0)
        {
          found = true;
          break;
        }
    }

      if (found)
    {
      int truefalse;

      if (sibling->info.expr.op == PT_IS_NULL || sibling->info.expr.op == PT_IS_NOT_NULL)
        {
          /* a IS NULL(IS NOT NULL) AND a IS NULL(IS NOT NULL) case */
          truefalse = (node->info.expr.op == sibling->info.expr.op);
        }
      else if (sibling->info.expr.op == PT_NULLSAFE_EQ)
        {
          if (PT_IS_NULL_NODE (sibling->info.expr.arg1) || PT_IS_NULL_NODE (sibling->info.expr.arg2))
        {
          /* a IS NULL(IS NOT NULL) AND a <=> NULL case */
          truefalse = (node->info.expr.op == PT_IS_NULL);
        }
          else
        {
          /* a IS NULL(IS NOT NULL) AND a <=> expr(except NULL) case */

          /* We may optimize (a is null and a <=> expr) as (a is null and expr is null), (a is not null and a
           * <=> expr) as (a = expr) in the near future. */
          break;
        }
        }
      else
        {
          /* a IS NULL(IS NOT NULL) AND a < 10 case */
          truefalse = (node->info.expr.op == PT_IS_NOT_NULL);
        }

      db_make_int (&value, truefalse);
    }
      else
    {
      spec = NULL;
      if (PT_IS_EXPR_NODE_WITH_OPERATOR (node_prior, PT_CAST))
        {
          node_prior = node_prior->info.expr.arg1;
        }
      else
        {
          node_prior = pt_get_end_path_node (node_prior);
        }

      if (PT_IS_NAME_NODE (node_prior))
        {
          spec = pt_find_entity (parser, from, node_prior->info.name.spec_id);
        }

      if (node->info.expr.op == PT_IS_NOT_NULL && spec && !mq_is_outer_join_spec (parser, spec)
          && pt_check_not_null_constraint (parser, from, node_prior))
        {
          db_make_int (&value, true);
        }
      else
        {
          prev = prev ? prev->next : node;
          continue;
        }
    }

      fold = pt_dbval_to_value (parser, &value);
      if (fold == NULL)
    {
      return;
    }

      fold->type_enum = node->type_enum;
      fold->info.value.location = node->info.expr.location;
      pr_clear_value (&value);
      /* replace IS NULL/IS NOT NULL node with newly created VALUE node */
      if (prev)
    {
      prev->next = fold;
    }
      else
    {
      *wherep = fold;
    }
      fold->next = node->next;
      node->next = NULL;
      /* node->or_next == NULL */
      parser_free_tree (parser, node);
      node = fold->next;

      prev = prev ? prev->next : node;
    }
}

/*
 * qo_search_comp_pair_term () -
 *   return:
 *   parser(in):
 *   start(in):
 */
static PT_NODE *
qo_search_comp_pair_term (PARSER_CONTEXT * parser, PT_NODE * start)
{
  PT_NODE *node, *arg2;
  PT_OP_TYPE op_type1, op_type2;
  int find_const, find_attr;
  PT_NODE *arg_prior, *arg_prior_start;

  arg_prior = arg_prior_start = NULL;

  switch (start->info.expr.op)
    {
    case PT_GE:
    case PT_GT:
      op_type1 = PT_LE;
      op_type2 = PT_LT;
      break;
    case PT_LE:
    case PT_LT:
      op_type1 = PT_GE;
      op_type2 = PT_GT;
      break;
    default:
      return NULL;
    }
  /* skip out unary minus expr */
  arg2 = start->info.expr.arg2;
  while (pt_is_expr_node (arg2) && arg2->info.expr.op == PT_UNARY_MINUS)
    {
      arg2 = arg2->info.expr.arg1;
    }
  find_const = pt_is_const_expr_node (arg2);
  find_attr = (pt_is_attr (start->info.expr.arg2) || pt_is_function_index_expression (start->info.expr.arg2));

  arg_prior_start = start->info.expr.arg1;  /* original value */
  if (arg_prior_start->info.expr.op == PT_PRIOR)
    {
      arg_prior_start = arg_prior_start->info.expr.arg1;
    }

  /* search CNF list */
  for (node = start; node; node = node->next)
    {
      if (node->node_type != PT_EXPR || node->or_next != NULL)
    {
      /* neither expression node nor one predicate term */
      continue;
    }

      if (node->info.expr.location != start->info.expr.location)
    {
      continue;
    }

      arg_prior = pt_get_first_arg_ignore_prior (node);

      if (node->info.expr.op == op_type1 || node->info.expr.op == op_type2)
    {
      if (find_const && (pt_is_attr (arg_prior) || pt_is_function_index_expression (arg_prior))
          && (pt_check_path_eq (parser, arg_prior_start, arg_prior) == 0))
        {
          /* skip out unary minus expr */
          arg2 = node->info.expr.arg2;
          while (pt_is_expr_node (arg2) && arg2->info.expr.op == PT_UNARY_MINUS)
        {
          arg2 = arg2->info.expr.arg1;
        }
          if (pt_is_const_expr_node (arg2))
        {
          /* found 'attr op const' term */
          break;
        }
        }
      if (find_attr && (pt_is_attr (arg_prior) || pt_is_function_index_expression (arg_prior))
          && (pt_is_attr (node->info.expr.arg2) || pt_is_function_index_expression (node->info.expr.arg2))
          && (pt_check_path_eq (parser, arg_prior_start, node->info.expr.arg1) == 0)
          && (pt_check_class_eq (parser, start->info.expr.arg2, node->info.expr.arg2) == 0))
        {
          /* found 'attr op attr' term */
          break;
        }
    }
    }

  return node;
}

/*
 * qo_reduce_comp_pair_terms () - Convert a pair of comparison terms to one
 *                 BETWEEN term
 *   return:
 *   parser(in):
 *   wherep(in): pointer to WHERE
 *
 * Note:
 *  examples:
 *      1) where a<=20 and a=>10        -->  where a between 10 and(ge_le) 20
 *      2) where a<20 and a>10          -->  where a between 10 gt_lt 20
 *      3) where a<B.b and a>=B.c       -->  where a between B.c ge_lt B.b
 */
static void
qo_reduce_comp_pair_terms (PARSER_CONTEXT * parser, PT_NODE ** wherep)
{
  PT_NODE *node, *pair, *lower, *upper, *prev, *next, *arg1, *arg2;
  int location;
  DB_VALUE *lower_val, *upper_val;
  DB_VALUE_COMPARE_RESULT cmp;

  /* traverse CNF list */
  for (node = *wherep; node; node = node->next)
    {
      if (node->node_type != PT_EXPR || node->or_next != NULL)
    {
      /* neither expression node nor one predicate term */
      continue;
    }

      arg1 = pt_get_first_arg_ignore_prior (node);

      if (!pt_is_attr (arg1) && !pt_is_function_index_expression (arg1))
    {
      /* LHS is not an attribute */
      continue;
    }

      switch (node->info.expr.op)
    {
    case PT_GT:
    case PT_GE:
      lower = node;
      upper = pair = qo_search_comp_pair_term (parser, node);
      break;
    case PT_LT:
    case PT_LE:
      lower = pair = qo_search_comp_pair_term (parser, node);
      upper = node;
      break;
    default:
      /* not comparison term; continue to next node */
      continue;
    }
      if (!pair)
    {
      /* there's no pair comparison term having the same attribute */
      continue;
    }

      if ((PT_IS_EXPR_WITH_PRIOR_ARG (lower) && !PT_IS_EXPR_WITH_PRIOR_ARG (upper))
      || (!PT_IS_EXPR_WITH_PRIOR_ARG (lower) && PT_IS_EXPR_WITH_PRIOR_ARG (upper)))
    {
      /* one of the bounds does not contain prior */
      continue;
    }

      /* the node will be converted to BETWEEN node and the pair node will be converted to the right operand(arg2) of
       * BETWEEN node denoting the range of BETWEEN such as BETWEEN_GE_LE, BETWEEN_GE_LT, BETWEEN_GT_LE, and
       * BETWEEN_GT_LT */

      /* make the pair node to the right operand of BETWEEN node */
      if (pt_comp_to_between_op (lower->info.expr.op, upper->info.expr.op, PT_REDUCE_COMP_PAIR_TERMS,
                 &pair->info.expr.op) != 0)
    {
      /* cannot be occurred but something wrong */
      continue;
    }
      parser_free_tree (parser, pair->info.expr.arg1);
      pair->info.expr.arg1 = lower->info.expr.arg2;
      pair->info.expr.arg2 = upper->info.expr.arg2;
      /* should set pair->info.expr.arg1 before pair->info.expr.arg2 */
      /* make the node to BETWEEN node */
      node->info.expr.op = PT_BETWEEN;
      /* revert BETWEEN_GE_LE to BETWEEN_AND */
      if (pair->info.expr.op == PT_BETWEEN_GE_LE)
    {
      pair->info.expr.op = PT_BETWEEN_AND;
    }
      node->info.expr.arg2 = pair;

      /* adjust linked list */
      for (prev = node; prev->next != pair; prev = prev->next)
    ;
      prev->next = pair->next;
      pair->next = NULL;

      /* check if the between range is valid */
      arg2 = node->info.expr.arg2;

      lower = arg2->info.expr.arg1;
      upper = arg2->info.expr.arg2;
      if (pt_is_const_not_hostvar (lower) && pt_is_const_not_hostvar (upper))
    {
      lower_val = pt_value_to_db (parser, lower);
      upper_val = pt_value_to_db (parser, upper);
      cmp = (DB_VALUE_COMPARE_RESULT) db_value_compare (lower_val, upper_val);
      if (cmp == DB_GT
          || (cmp == DB_EQ
          && (arg2->info.expr.op == PT_BETWEEN_GE_LT || arg2->info.expr.op == PT_BETWEEN_GT_LE
              || arg2->info.expr.op == PT_BETWEEN_GT_LT)))
        {
          /* lower bound is greater than upper bound */

          location = node->info.expr.location;  /* save location */

          if (location == 0)
        {
          /* empty conjuctive make whole condition always false */
          /* NOTICE: that is valid only when we handle one predicate terms in this function */
          parser_free_tree (parser, *wherep);

          /* make a single false node */
          node = parser_new_node (parser, PT_VALUE);
          if (node == NULL)
            {
              PT_INTERNAL_ERROR (parser, "allocate new node");
              return;
            }

          node->type_enum = PT_TYPE_LOGICAL;
          node->info.value.data_value.i = 0;
          node->info.value.location = location;
          (void) pt_value_to_db (parser, node);
          *wherep = node;
        }
          else
        {
          /* empty conjunctive is outer join ON condition. remove all nodes which have same location number */
          prev = NULL;
          node = *wherep;
          while (node)
            {
              if ((node->node_type == PT_EXPR && node->info.expr.location == location)
              || (node->node_type == PT_VALUE && node->info.value.location == location))
            {
              next = node->next;
              node->next = NULL;
              parser_free_tree (parser, node);
              if (prev)
                {
                  prev->next = next;
                }
              else
                {
                  *wherep = next;
                }
              node = next;
            }
              else
            {
              prev = node;
              node = node->next;
            }
            }

          /* make a single false node and append it to WHERE list */
          node = parser_new_node (parser, PT_VALUE);
          if (node == NULL)
            {
              PT_INTERNAL_ERROR (parser, "allocate new node");
              return;
            }

          node->type_enum = PT_TYPE_LOGICAL;
          node->info.value.data_value.i = 0;
          node->info.value.location = location;
          (void) pt_value_to_db (parser, node);
          node->next = *wherep;
          *wherep = node;
        }

          return;
        }
    }
    }
}

/*
 * qo_find_like_rewrite_bound () -
 * return: the lower or upper bound for the LIKE query rewrite (depending on
 *         the value of the compute_lower_bound parameter), NULL on error.
 *         See qo_rewrite_one_like_term for details.
 *  parser(in):
 *  pattern(in): the pattern tree node
 *  pattern_str(in): a DB_VALUE of the string in the pattern argument
 *  has_escape_char(in): whether the LIKE pattern can use an escape character
 *  escape_str(in):if has_escape_char is true this is the escaping character
 *                 used in the pattern, otherwise the parameter has no
 *                 meaning and should have the value NULL
 *  compute_lower_bound(in): whether to compute the lower or the upper bound
 *  last_safe_logical_pos(in): the value returned by a
 *                             db_get_info_for_like_optimization call
 */
static PT_NODE *
qo_find_like_rewrite_bound (PARSER_CONTEXT * const parser, PT_NODE * const pattern, DB_VALUE * const pattern_str,
                const bool has_escape_char, const char *escape_str, const bool compute_lower_bound,
                const int last_safe_logical_pos)
{
  int error_code = NO_ERROR;
  PT_NODE *bound;
  DB_VALUE tmp_result;

  db_make_null (&tmp_result);

  assert (parser != NULL);
  if (parser == NULL)
    {
      return NULL;
    }

  assert (has_escape_char ^ (escape_str == NULL));

  bound = parser_new_node (parser, PT_VALUE);
  if (bound == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      goto error_exit;
    }

  error_code =
    db_get_like_optimization_bounds (pattern_str, &tmp_result, has_escape_char, escape_str, compute_lower_bound,
                     last_safe_logical_pos);
  if (error_code != NO_ERROR)
    {
      PT_INTERNAL_ERROR (parser, "db_get_like_optimization_bounds");
      goto error_exit;
    }

  bound->type_enum = pattern->type_enum;
  if (pattern->data_type != NULL)
    {
      bound->data_type = parser_copy_tree (parser, pattern->data_type);
    }
  bound->info.value.data_value.str =
    pt_append_bytes (parser, NULL, db_get_string (&tmp_result), db_get_string_size (&tmp_result));
  PT_NODE_PRINT_VALUE_TO_TEXT (parser, bound);
  (void) pt_value_to_db (parser, bound);

  assert (bound->info.value.db_value_is_initialized);
  assert (PT_HAS_COLLATION (pattern->type_enum));

  db_string_put_cs_and_collation (&(bound->info.value.db_value), db_get_string_codeset (&tmp_result),
                  db_get_string_collation (&tmp_result));

  db_value_clear (&tmp_result);
  return bound;

error_exit:
  if (bound != NULL)
    {
      parser_free_tree (parser, bound);
    }

  db_value_clear (&tmp_result);
  return NULL;
}

/*
 * qo_rewrite_one_like_term () - Convert a leftmost LIKE term to a BETWEEN
 *                   (GE_LT) term to increase the chance of using
 *                               an index.
 *   parser(in):
 *   like(in):
 *   pattern(in):
 *   escape(in):
 *   perform_generic_rewrite(out): true if this function did not perform a
 *                                 rewrite, but the expression will benefit
 *                                 from the more generic rewrite performed by
 *                                 qo_rewrite_like_for_index_scan
 *
 * Note: See the notes of the db_get_info_for_like_optimization function for
 *       details on what rewrites can be performed.
 *       This function will only be applied to pattern values known at
 *       compile-time. It will only perform a rewrite if the LIKE predicate
 *       can be fully expressed with other predicates (cases 1, 2 and 3.2
 *       described in db_get_info_for_like_optimization).
 *       If this function cannot perform the above rewrites, but the rewrite
 *       of form 3.1 would benefit from an index scan
 */
static void
qo_rewrite_one_like_term (PARSER_CONTEXT * const parser, PT_NODE * const like, PT_NODE * const pattern,
              PT_NODE * const escape, bool * const perform_generic_rewrite)
{
  int error_code = NO_ERROR;
  bool has_escape_char = false;
  const char *escape_str = NULL;
  const char *pattern_str = NULL;
  int pattern_size = 0;
  int pattern_length = 0;
  bool uses_escaping = false;
  int num_logical_chars = 0;
  int last_safe_logical_pos = 0;
  int num_match_many = 0;
  int num_match_one = 0;
  DB_VALUE compressed_pattern;
  int collation_id;
  INTL_CODESET codeset;

  db_make_null (&compressed_pattern);

  *perform_generic_rewrite = false;

  assert (pattern != NULL && parser != NULL);
  if (pattern == NULL || parser == NULL)
    {
      return;
    }

  assert (TP_IS_CHAR_TYPE (DB_VALUE_DOMAIN_TYPE (&pattern->info.value.db_value)));

  collation_id = db_get_string_collation (&pattern->info.value.db_value);
  codeset = db_get_string_codeset (&pattern->info.value.db_value);

  if (escape != NULL)
    {
      if (PT_IS_NULL_NODE (escape))
    {
      has_escape_char = true;
      escape_str = "\\";
    }
      else
    {
      int esc_char_len = 0;

      assert (pt_is_ascii_string_value_node (escape));

      escape_str = (const char *) escape->info.value.data_value.str->bytes;
      intl_char_count ((unsigned char *) escape_str, escape->info.value.data_value.str->length, codeset,
               &esc_char_len);
      if (esc_char_len != 1)
        {
          PT_ERRORm (parser, escape, MSGCAT_SET_ERROR, -(ER_QSTR_INVALID_ESCAPE_SEQUENCE));
          goto error_exit;
        }
      has_escape_char = true;
    }
    }
  else if (prm_get_bool_value (PRM_ID_REQUIRE_LIKE_ESCAPE_CHARACTER))
    {
      assert (escape == NULL);
      assert (!prm_get_bool_value (PRM_ID_NO_BACKSLASH_ESCAPES));
      has_escape_char = true;
      escape_str = "\\";
    }
  else
    {
      has_escape_char = false;
      escape_str = NULL;
    }

  error_code =
    db_compress_like_pattern (&pattern->info.value.db_value, &compressed_pattern, has_escape_char, escape_str);
  if (error_code != NO_ERROR)
    {
      PT_INTERNAL_ERROR (parser, "db_compress_like_pattern");
      goto error_exit;
    }

  pattern->info.value.data_value.str =
    pt_append_bytes (parser, NULL, db_get_string (&compressed_pattern), db_get_string_size (&compressed_pattern));
  pattern_str = (char *) pattern->info.value.data_value.str->bytes;
  pattern_size = pattern->info.value.data_value.str->length;
  intl_char_count ((unsigned char *) pattern_str, pattern_size, codeset, &pattern_length);
  PT_NODE_PRINT_VALUE_TO_TEXT (parser, pattern);

  error_code =
    db_get_info_for_like_optimization (&compressed_pattern, has_escape_char, escape_str, &num_logical_chars,
                       &last_safe_logical_pos, &num_match_many, &num_match_one);
  if (error_code != NO_ERROR)
    {
      PT_INTERNAL_ERROR (parser, "db_get_info_for_like_optimization");
      goto error_exit;
    }

  assert (pattern_length >= num_logical_chars);
  uses_escaping = (num_logical_chars != pattern_length);

  if (num_match_many == 0 && num_match_one == 0)
    {
      /* The pattern does not contain wildcards. */

      if (uses_escaping)
    {
      /* TODO also support this scenario by eliminating the no longer needed escape characters. When this is
       * implemented, we will no longer need to perform the generic rewrite. Rewriting to PT_EQ will result in
       * faster execution, so this specific rewrite is preferable. */
      *perform_generic_rewrite = true;
      goto fast_exit;
    }

      if (escape != NULL)
    {
      pt_free_escape_char (parser, like, pattern, escape);
    }

      if (pattern_length == 0)
    {
      /* Rewrite this term as equal predicate. */
      like->info.expr.op = PT_EQ;
    }
      else if (pattern_str[pattern_size - 1] == ' ')
    {
      /* If the rightmost character in the pattern is a space we cannot rewrite this term. */
      /* TODO It is not clear why this case is not handled. Clarify this issue and improve the comment. It is
       * possible that the index ordering of strings with trailing spaces is inconsistent with LIKE comparison
       * semantics. Another issue is that the successor of the space character should be the character with the
       * code 1 (as space is sorted before any other character) and character code 1 is (incorrectly) used as a
       * dummy escape character in qstr_eval_like when there is no other escape character given. */
      if (last_safe_logical_pos >= 0)
        {
          /* We can perform the generic rewrite as the string contains non-space characters. */
          *perform_generic_rewrite = true;
        }
    }
      else
    {
      /* Rewrite this term as equal predicate. */
      like->info.expr.op = PT_EQ;
    }
      goto fast_exit;
    }

  if (pattern_length == 1 && num_match_many == 1)
    {
      /* LIKE '%' predicate that matches any non-null string. */
      assert (num_logical_chars == 1);
      assert (pattern_str[0] == LIKE_WILDCARD_MATCH_MANY && pattern_str[1] == 0);

      /* We change the node to a IS NOT NULL node. */
      parser_free_tree (parser, like->info.expr.arg2);
      like->info.expr.arg2 = NULL;
      like->info.expr.op = PT_IS_NOT_NULL;
      goto fast_exit;
    }

  if (num_match_many == 1 && num_match_one == 0 && last_safe_logical_pos >= 0
      && last_safe_logical_pos == num_logical_chars - 2)
    {
      PT_NODE *lower = NULL;
      PT_NODE *upper = NULL;
      PT_NODE *between_and = NULL;

      assert (pattern_length >= 2 && pattern_str[pattern_size - 1] == LIKE_WILDCARD_MATCH_MANY);

      /* do not rewrite for collations with LIKE disabled optimization */
      if (!(lang_get_collation (collation_id)->options.allow_like_rewrite))
    {
      *perform_generic_rewrite = true;
      goto fast_exit;
    }

      between_and = pt_expression_2 (parser, PT_BETWEEN_GE_LT, NULL, NULL);
      if (between_and == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      goto error_exit;
    }

      between_and->type_enum = PT_TYPE_LOGICAL;
      between_and->info.expr.location = like->info.expr.location;

      lower =
    qo_find_like_rewrite_bound (parser, pattern, &compressed_pattern, has_escape_char, escape_str, true,
                    last_safe_logical_pos);
      if (lower == NULL)
    {
      parser_free_tree (parser, between_and);
      between_and = NULL;
      goto error_exit;
    }

      between_and->info.expr.arg1 = lower;

      upper =
    qo_find_like_rewrite_bound (parser, pattern, &compressed_pattern, has_escape_char, escape_str, false,
                    last_safe_logical_pos);
      if (upper == NULL)
    {
      parser_free_tree (parser, between_and);
      between_and = NULL;
      goto error_exit;
    }

      between_and->info.expr.arg2 = upper;

      /* We replace the LIKE node with a BETWEEN node. */
      like->info.expr.op = PT_BETWEEN;
      parser_free_tree (parser, like->info.expr.arg2);
      like->info.expr.arg2 = between_and;
    }
  else if (last_safe_logical_pos >= 0)
    {
      *perform_generic_rewrite = true;
    }

fast_exit:
  db_value_clear (&compressed_pattern);

  return;

error_exit:
  db_value_clear (&compressed_pattern);

  return;
}

static PT_NODE *
qo_allocate_like_bound_for_index_scan (PARSER_CONTEXT * const parser, PT_NODE * const like, PT_NODE * const pattern,
                       PT_NODE * const escape, const bool allocate_lower_bound)
{
  PT_NODE *bound = NULL;
  PT_NODE *expr_pattern = NULL;
  PT_NODE *expr_escape = NULL;

  bound = pt_expression_2 (parser, allocate_lower_bound ? PT_LIKE_LOWER_BOUND : PT_LIKE_UPPER_BOUND, NULL, NULL);
  if (bound == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      goto error_exit;
    }
  bound->info.expr.location = like->info.expr.location;

  bound->type_enum = pattern->type_enum;

  expr_pattern = parser_copy_tree (parser, pattern);
  if (expr_pattern == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      goto error_exit;
    }

  bound->info.expr.arg1 = expr_pattern;

  if (prm_get_bool_value (PRM_ID_REQUIRE_LIKE_ESCAPE_CHARACTER) && escape == NULL)
    {
      assert (!prm_get_bool_value (PRM_ID_NO_BACKSLASH_ESCAPES));
      expr_escape = pt_make_string_value (parser, "\\");
      if (expr_escape == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      goto error_exit;
    }
    }
  else if (escape != NULL)
    {
      if (PT_IS_NULL_NODE (escape))
    {
      expr_escape = pt_make_string_value (parser, "\\");
      if (expr_escape == NULL)
        {
          PT_INTERNAL_ERROR (parser, "allocate new node");
          goto error_exit;
        }
    }
      else
    {
      expr_escape = parser_copy_tree (parser, escape);
      if (expr_escape == NULL)
        {
          PT_INTERNAL_ERROR (parser, "allocate new node");
          goto error_exit;
        }
    }
    }
  else
    {
      expr_escape = NULL;
    }

  bound->info.expr.arg2 = expr_escape;

  /* copy data type */
  assert (bound->data_type == NULL);
  bound->data_type = parser_copy_tree (parser, pattern->data_type);

  return bound;

error_exit:
  if (bound != NULL)
    {
      parser_free_tree (parser, bound);
    }
  return NULL;
}

/*
 * qo_rewrite_like_for_index_scan ()
 *   parser(in):
 *   like(in):
 *   pattern(in):
 *   escape(in):
 *
 * Note: See the notes of the db_get_info_for_like_optimization function for
 *       details on what rewrites can be performed. This function will always
 *       rewrite to form 3.1.
 */
static PT_NODE *
qo_rewrite_like_for_index_scan (PARSER_CONTEXT * const parser, PT_NODE * like, PT_NODE * const pattern,
                PT_NODE * const escape)
{
  PT_NODE *between = NULL;
  PT_NODE *between_and = NULL;
  PT_NODE *lower = NULL;
  PT_NODE *upper = NULL;
  PT_NODE *match_col = NULL;
  PT_NODE *like_save = NULL;

  between = pt_expression_1 (parser, PT_BETWEEN, NULL);
  if (between == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      goto error_exit;
    }

  between->type_enum = PT_TYPE_LOGICAL;
  between->info.expr.location = like->info.expr.location;

  match_col = parser_copy_tree (parser, like->info.expr.arg1);
  if (match_col == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      goto error_exit;
    }

  between->info.expr.arg1 = match_col;

  between_and = pt_expression_2 (parser, PT_BETWEEN_GE_LT, NULL, NULL);
  if (between_and == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      goto error_exit;
    }

  between->info.expr.arg2 = between_and;

  between_and->type_enum = PT_TYPE_LOGICAL;
  between_and->info.expr.location = like->info.expr.location;

  lower = qo_allocate_like_bound_for_index_scan (parser, like, pattern, escape, true);
  if (lower == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      goto error_exit;
    }

  between_and->info.expr.arg1 = lower;

  upper = qo_allocate_like_bound_for_index_scan (parser, like, pattern, escape, false);
  if (upper == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      goto error_exit;
    }

  between_and->info.expr.arg2 = upper;

  between->next = like->next;
  like->next = between;

  /* fold range bounds : this will allow auto-parametrization */
  like_save = parser_copy_tree_list (parser, like);
  if (like_save == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      goto error_exit;
    }

  /* if success, use like_save. Otherwise, keep like. */
  like_save = pt_semantic_type (parser, like_save, NULL);
  if (like_save == NULL || er_errid () != NO_ERROR || pt_has_error (parser))
    {
      like->next = between->next;
      between->next = NULL;

      /* clear error */
      if (er_errid () != NO_ERROR)
    {
      er_clear ();
    }

      if (pt_has_error (parser))
    {
      pt_reset_error (parser);
    }
      goto error_exit;
    }

  /* success: use like_save. */
  return like_save;

error_exit:
  if (between != NULL)
    {
      parser_free_tree (parser, between);
      between = NULL;
    }

  if (like_save != NULL)
    {
      parser_free_tree (parser, like_save);
    }

  return like;
}

/*
 * qo_check_like_expression_pre - Checks to see if an expression is safe to
 *                                use in the LIKE rewrite optimization
 *                                performed by qo_rewrite_like_for_index_scan
 *
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in/out): A pointer to a bool value that represents whether the
 *                expression is safe for the rewrite.
 *   continue_walk(in/out):
 *
 * Note: Expressions are first filtered by the pt_is_pseudo_const function.
 *       However, in addition to what that function considers a "constant"
 *       for index scans, we also include PT_NAME and PT_DOT nodes and query
 *       nodes. Some of them might be pseudo-constant and usable during the
 *       index scan, but since we have no easy way to tell we prefer to
 *       exclude them.
 */
static PT_NODE *
qo_check_like_expression_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  bool *const like_expression_not_safe = (bool *) arg;

  if (node == NULL)
    {
      return node;
    }

  if (PT_IS_QUERY (node) || PT_IS_DOT_NODE (node))
    {
      *like_expression_not_safe = true;
      *continue_walk = PT_STOP_WALK;
      return node;
    }
  else if (PT_IS_NAME_NODE (node) && node->info.name.correlation_level == 0)
    {
      *like_expression_not_safe = true;
      *continue_walk = PT_STOP_WALK;
      return node;
    }

  return node;
}

/*
 * qo_rewrite_like_terms ()
 *   return:
 *   parser(in):
 *   cnf_list(in):
 */
static void
qo_rewrite_like_terms (PARSER_CONTEXT * parser, PT_NODE ** cnf_list)
{
  bool error_saved = false;
  PT_NODE *cnf_node = NULL;
  /* prev node in list which linked by next pointer. */
  PT_NODE *prev = NULL;
  /* prev node in list which linked by or_next pointer. */
  PT_NODE *or_prev = NULL;

  if (er_errid () != NO_ERROR)
    {
      er_stack_push ();
      error_saved = true;
    }

  for (cnf_node = *cnf_list; cnf_node != NULL; cnf_node = cnf_node->next)
    {
      PT_NODE *crt_expr = NULL;

      or_prev = NULL;
      for (crt_expr = cnf_node; crt_expr != NULL; crt_expr = crt_expr->or_next)
    {
      PT_NODE *compared_expr = NULL;
      PT_NODE *pattern = NULL;
      PT_NODE *escape = NULL;
      PT_NODE *arg2 = NULL;
      bool perform_generic_rewrite = false;
      PT_TYPE_ENUM pattern_type, escape_type = PT_TYPE_NONE;

      if (!PT_IS_EXPR_NODE_WITH_OPERATOR (crt_expr, PT_LIKE))
        {
          /* TODO Investigate optimizing PT_NOT_LIKE expressions also. */
          continue;
        }

      compared_expr = pt_get_first_arg_ignore_prior (crt_expr);
      if (!pt_is_attr (compared_expr) && !qo_is_cast_attr (compared_expr)
          && !pt_is_function_index_expr (parser, compared_expr, false))
        {
          /* LHS is not an attribute or an expression supported as function index so it cannot currently have an
           * index. The transformation could still be useful as it might provide faster execution time in some
           * scenarios. */
          continue;
        }

      arg2 = crt_expr->info.expr.arg2;
      if (PT_IS_EXPR_NODE_WITH_OPERATOR (arg2, PT_LIKE_ESCAPE))
        {
          /* TODO LIKE handling might be easier if the parser saved the escape sequence in arg3 of the PT_LIKE
           * node. */
          pattern = arg2->info.expr.arg1;
          escape = arg2->info.expr.arg2;
          assert (escape != NULL);
        }
      else
        {
          pattern = arg2;
          escape = NULL;
        }

      pattern_type = pattern->type_enum;

      if (pattern_type == PT_TYPE_MAYBE && pattern->expected_domain)
        {
          pattern_type = pt_db_to_type_enum (TP_DOMAIN_TYPE (pattern->expected_domain));
        }

      if (escape != NULL)
        {
          escape_type = escape->type_enum;
          if (escape_type == PT_TYPE_MAYBE && escape->expected_domain)
        {
          escape_type = pt_db_to_type_enum (TP_DOMAIN_TYPE (escape->expected_domain));
        }
        }

      if (pt_is_ascii_string_value_node (pattern)
          && (escape == NULL || PT_IS_NULL_NODE (escape) || pt_is_ascii_string_value_node (escape)))
        {
          qo_rewrite_one_like_term (parser, crt_expr, pattern, escape, &perform_generic_rewrite);
          if (!perform_generic_rewrite)
        {
          continue;
        }
        }
      if (crt_expr == cnf_node && crt_expr->or_next == NULL)
        {
          /* The LIKE predicate in CNF is not chained in an OR list, so we can easily split it into several
           * predicates chained with AND. Supporting the case: col LIKE expr1 OR predicate would make it difficult
           * to rewrite the query because we need to preserve the CNF. */
          /* TODO We should check that the column is indexed. Otherwise it might not be worth the effort to do this
           * rewrite. */
          if (pt_is_pseudo_const (pattern)
          && (escape == NULL || PT_IS_NULL_NODE (escape) || pt_is_pseudo_const (escape)))
        {
          bool like_expression_not_safe = false;

          (void *) parser_walk_tree (parser, pattern, qo_check_like_expression_pre, &like_expression_not_safe,
                         NULL, NULL);
          if (like_expression_not_safe)
            {
              continue;
            }
          (void *) parser_walk_tree (parser, escape, qo_check_like_expression_pre, &like_expression_not_safe,
                         NULL, NULL);
          if (like_expression_not_safe)
            {
              continue;
            }
          crt_expr = qo_rewrite_like_for_index_scan (parser, crt_expr, pattern, escape);
          /* rebuild link list. */
          if (or_prev != NULL)
            {
              or_prev->or_next = crt_expr;
            }
          else if (prev != NULL)
            {
              /* The first node in cnf_node */
              prev->next = crt_expr;
              cnf_node = crt_expr;
            }
          else
            {
              /* The first node in cnf_list */
              *cnf_list = crt_expr;
              cnf_node = crt_expr;
            }


        }
        }
      or_prev = crt_expr;
    }
      prev = cnf_node;
    }

  if (error_saved)
    {
      er_stack_pop ();
    }
}

/*
 * qo_set_value_to_range_list () -
 *   return:
 *   parser(in):
 *   node(in):
 */
static PT_NODE *
qo_set_value_to_range_list (PARSER_CONTEXT * parser, PT_NODE * node)
{
  PT_NODE *set_val, *list, *last, *range;

  list = last = NULL;
  if (node->node_type == PT_VALUE)
    {
      set_val = node->info.value.data_value.set;
    }
  else if (node->node_type == PT_FUNCTION)
    {
      set_val = node->info.function.arg_list;
    }
  else if (node->node_type == PT_NAME && !PT_IS_COLLECTION_TYPE (node->type_enum))
    {
      set_val = node;
    }
  else
    {
      set_val = NULL;
    }

  while (set_val)
    {
      range = parser_new_node (parser, PT_EXPR);
      if (!range)
    goto error;
      range->type_enum = PT_TYPE_LOGICAL;
      range->info.expr.op = PT_BETWEEN_EQ_NA;
      range->info.expr.arg1 = parser_copy_tree (parser, set_val);
      range->info.expr.arg2 = NULL;
      range->info.expr.location = set_val->info.expr.location;
#if defined(CUBRID_DEBUG)
      range->next = NULL;
      range->or_next = NULL;
#endif /* CUBRID_DEBUG */
      if (last)
    {
      last->or_next = range;
    }
      else
    {
      list = range;
    }
      last = range;
      set_val = set_val->next;
    }

  return list;

error:
  if (list)
    parser_free_tree (parser, list);
  return NULL;
}


/*
 * qo_convert_to_range_helper () -
 *   return:
 *   parser(in):
 *   node(in):
 */
static void
qo_convert_to_range_helper (PARSER_CONTEXT * parser, PT_NODE * node)
{
  PT_NODE *between_and, *sibling, *last, *prev, *in_arg2;
  PT_OP_TYPE op_type;
  PT_NODE *node_prior = NULL;
  PT_NODE *sibling_prior = NULL;

  assert (PT_IS_EXPR_NODE (node));
  node_prior = pt_get_first_arg_ignore_prior (node);

  assert (node_prior != NULL);
  if (node_prior == NULL)
    {
      return;
    }

  /* convert the given node to RANGE node */

  /* construct BETWEEN_AND node as arg2(RHS) of RANGE node */
  op_type = node->info.expr.op;
  switch (op_type)
    {
    case PT_EQ:
      between_and = parser_new_node (parser, PT_EXPR);
      if (!between_and)
    {
      return;       /* error; stop converting */
    }
      between_and->type_enum = PT_TYPE_LOGICAL;
      between_and->info.expr.op = PT_BETWEEN_EQ_NA;
      between_and->info.expr.arg1 = node->info.expr.arg2;
      between_and->info.expr.arg2 = NULL;
      between_and->info.expr.location = node->info.expr.location;
#if defined(CUBRID_DEBUG)
      between_and->next = NULL;
      between_and->or_next = NULL;
#endif /* CUBRID_DEBUG */
      break;
    case PT_GT:
    case PT_GE:
    case PT_LT:
    case PT_LE:
      between_and = parser_new_node (parser, PT_EXPR);
      if (!between_and)
    {
      return;       /* error; stop converting */
    }
      between_and->type_enum = PT_TYPE_LOGICAL;
      if (op_type == PT_GT)
    {
      between_and->info.expr.op = PT_BETWEEN_GT_INF;
    }
      else if (op_type == PT_GE)
    {
      between_and->info.expr.op = PT_BETWEEN_GE_INF;
    }
      else if (op_type == PT_LT)
    {
      between_and->info.expr.op = PT_BETWEEN_INF_LT;
    }
      else
    {
      between_and->info.expr.op = PT_BETWEEN_INF_LE;
    }

      between_and->info.expr.arg1 = node->info.expr.arg2;
      between_and->info.expr.arg2 = NULL;
      between_and->info.expr.location = node->info.expr.location;
#if defined(CUBRID_DEBUG)
      between_and->next = NULL;
      between_and->or_next = NULL;
#endif
      break;
    case PT_BETWEEN:
      between_and = node->info.expr.arg2;
      assert (between_and->node_type == PT_EXPR);
      /* replace PT_BETWEEN_AND with PT_BETWEEN_GE_LE */
      if (between_and->info.expr.op == PT_BETWEEN_AND)
    {
      between_and->info.expr.op = PT_BETWEEN_GE_LE;
    }
      break;
    case PT_IS_IN:
      in_arg2 = node->info.expr.arg2;
      if (PT_IS_COLLECTION_TYPE (node->type_enum) || PT_IS_QUERY_NODE_TYPE (in_arg2->node_type)
      || !PT_IS_COLLECTION_TYPE (in_arg2->type_enum))
    {
      /* subquery cannot be converted to RANGE */
      return;
    }
      between_and = qo_set_value_to_range_list (parser, in_arg2);
      if (!between_and)
    {
      return;       /* error; stop converting */
    }
      /* free the converted set value node, which is the operand of IN */
      parser_free_tree (parser, in_arg2);
      break;
    case PT_RANGE:
      /* already converted. do nothing */
      return;
    default:
      /* unsupported operator; only PT_EQ, PT_GT, PT_GE, PT_LT, PT_LE, and PT_BETWEEN can be converted to RANGE */
      return;           /* error; stop converting */
    }
#if 0
  between_and->next = between_and->or_next = NULL;
#endif
  /* change the node to RANGE */
  node->info.expr.op = PT_RANGE;
  node->info.expr.arg2 = last = between_and;
  while (last->or_next)
    {
      last = last->or_next;
    }


  /* link all nodes in the list whose LHS is the same attribute with the RANGE node */

  /* search DNF list from the next to the node and keep track of the pointer to previous node */
  prev = node;
  while ((sibling = prev->or_next))
    {
      if (sibling->node_type != PT_EXPR)
    {
      /* sibling is not an expression node */
      prev = prev->or_next;
      continue;
    }

      sibling_prior = pt_get_first_arg_ignore_prior (sibling);
      if (PT_IS_EXPR_WITH_PRIOR_ARG (sibling))
    {
      if (!PT_IS_EXPR_WITH_PRIOR_ARG (node))
        {
          /* sibling has prior, node hasn't */
          prev = prev->or_next;
          continue;
        }
    }
      else
    {
      if (PT_IS_EXPR_WITH_PRIOR_ARG (node))
        {
          /* sibling hasn't prior, node has */
          prev = prev->or_next;
          continue;
        }
    }
      /* if node had prior check that sibling also contains prior and vice-versa */

      if (!pt_is_attr (sibling_prior) && !pt_is_function_index_expression (sibling_prior)
      && !pt_is_instnum (sibling_prior))
    {
      /* LHS is not an attribute */
      prev = prev->or_next;
      continue;
    }

      if (node_prior->node_type != sibling_prior->node_type)
    {
      prev = prev->or_next;
      continue;
    }

      if ((pt_is_attr (node_prior) || pt_is_function_index_expression (node_prior))
      && (pt_is_attr (sibling_prior) || pt_is_function_index_expression (sibling_prior))
      && pt_check_path_eq (parser, node_prior, sibling_prior))
    {
      /* pt_check_path_eq() return non-zero if two are different */
      prev = prev->or_next;
      continue;
    }

      if ((pt_is_instnum (node_prior) && !pt_is_instnum (sibling_prior))
      || (!pt_is_instnum (node_prior) && pt_is_instnum (sibling_prior)))
    {
      prev = prev->or_next;
      continue;
    }

      /* found a node of the same attribute */

      /* construct BETWEEN_AND node as the tail of RANGE node's range list */
      op_type = sibling->info.expr.op;
      switch (op_type)
    {
    case PT_EQ:
      between_and = parser_new_node (parser, PT_EXPR);
      if (!between_and)
        {
          return;       /* error; stop converting */
        }
      between_and->type_enum = PT_TYPE_LOGICAL;
      between_and->info.expr.op = PT_BETWEEN_EQ_NA;
      between_and->info.expr.arg1 = sibling->info.expr.arg2;
      between_and->info.expr.arg2 = NULL;
      between_and->info.expr.location = sibling->info.expr.location;
#if defined(CUBRID_DEBUG)
      between_and->next = NULL;
      between_and->or_next = NULL;
#endif /* CUBRID_DEBUG */
      break;
    case PT_GT:
    case PT_GE:
    case PT_LT:
    case PT_LE:
      between_and = parser_new_node (parser, PT_EXPR);
      if (!between_and)
        {
          return;       /* error; stop converting */
        }
      between_and->type_enum = PT_TYPE_LOGICAL;
      if (op_type == PT_GT)
        {
          between_and->info.expr.op = PT_BETWEEN_GT_INF;
        }
      else if (op_type == PT_GE)
        {
          between_and->info.expr.op = PT_BETWEEN_GE_INF;
        }
      else if (op_type == PT_LT)
        {
          between_and->info.expr.op = PT_BETWEEN_INF_LT;
        }
      else
        {
          between_and->info.expr.op = PT_BETWEEN_INF_LE;
        }
      between_and->info.expr.arg1 = sibling->info.expr.arg2;
      between_and->info.expr.arg2 = NULL;
      between_and->info.expr.location = sibling->info.expr.location;
#if defined(CUBRID_DEBUG)
      between_and->next = NULL;
      between_and->or_next = NULL;
#endif
      break;
    case PT_BETWEEN:
      between_and = sibling->info.expr.arg2;
      assert (between_and->node_type == PT_EXPR);
      /* replace PT_BETWEEN_AND with PT_BETWEEN_GE_LE */
      if (between_and->info.expr.op == PT_BETWEEN_AND)
        {
          between_and->info.expr.op = PT_BETWEEN_GE_LE;
        }
      break;
    case PT_IS_IN:
      in_arg2 = sibling->info.expr.arg2;
      if (PT_IS_COLLECTION_TYPE (sibling->type_enum) || PT_IS_QUERY_NODE_TYPE (in_arg2->node_type)
          || !PT_IS_COLLECTION_TYPE (in_arg2->type_enum))
        {
          /* subquery cannot be converted to RANGE */
          prev = prev->or_next;
          continue;
        }
      between_and = qo_set_value_to_range_list (parser, in_arg2);
      if (!between_and)
        {
          prev = prev->or_next;
          continue;
        }
      /* free the converted set value node, which is the operand of IN */
      parser_free_tree (parser, in_arg2);
      break;
    default:
      /* unsupported operator; continue to next node */
      prev = prev->or_next;
      continue;
    }           /* switch (op_type) */
#if 0
      between_and->next = between_and->or_next = NULL;
#endif
      /* append to the range list */
      last->or_next = between_and;
      last = between_and;
      while (last->or_next)
    {
      last = last->or_next;
    }

      /* delete the node and its arg1(LHS), and adjust linked list */
      prev->or_next = sibling->or_next;
      sibling->next = sibling->or_next = NULL;
      sibling->info.expr.arg2 = NULL;   /* parser_free_tree() will handle 'arg1' */
      parser_free_tree (parser, sibling);
    }
}

/*
 * qo_compare_dbvalue_with_optype () - compare two DB_VALUEs specified
 *                  by range operator
 *   return:
 *   val1(in):
 *   op1(in):
 *   val2(in):
 *   op2(in):
 */
static COMP_DBVALUE_WITH_OPTYPE_RESULT
qo_compare_dbvalue_with_optype (DB_VALUE * val1, PT_OP_TYPE op1, DB_VALUE * val2, PT_OP_TYPE op2)
{
  DB_VALUE_COMPARE_RESULT rc;

  switch (op1)
    {
    case PT_EQ:
    case PT_GE:
    case PT_GT:
    case PT_LT:
    case PT_LE:
    case PT_GT_INF:
    case PT_LT_INF:
      break;
    default:
      return CompResultError;
    }
  switch (op2)
    {
    case PT_EQ:
    case PT_GE:
    case PT_GT:
    case PT_LT:
    case PT_LE:
    case PT_GT_INF:
    case PT_LT_INF:
      break;
    default:
      return CompResultError;
    }

  if (op1 == PT_GT_INF)     /* val1 is -INF */
    {
      return (op1 == op2) ? CompResultEqual : CompResultLess;
    }
  if (op1 == PT_LT_INF)     /* val1 is +INF */
    {
      return (op1 == op2) ? CompResultEqual : CompResultGreater;
    }
  if (op2 == PT_GT_INF)     /* val2 is -INF */
    {
      return (op2 == op1) ? CompResultEqual : CompResultGreater;
    }
  if (op2 == PT_LT_INF)     /* va2 is +INF */
    {
      return (op2 == op1) ? CompResultEqual : CompResultLess;
    }

  rc = tp_value_compare (val1, val2, 1, 1);
  if (rc == DB_EQ)
    {
      /* (val1, op1) == (val2, op2) */
      if (op1 == op2)
    {
      return CompResultEqual;
    }
      if (op1 == PT_EQ || op1 == PT_GE || op1 == PT_LE)
    {
      if (op2 == PT_EQ || op2 == PT_GE || op2 == PT_LE)
        {
          return CompResultEqual;
        }
      return (op2 == PT_GT) ? CompResultLessAdj : CompResultGreaterAdj;
    }
      if (op1 == PT_GT)
    {
      if (op2 == PT_EQ || op2 == PT_GE || op2 == PT_LE)
        {
          return CompResultGreaterAdj;
        }
      return (op2 == PT_LT) ? CompResultGreater : CompResultEqual;
    }
      if (op1 == PT_LT)
    {
      if (op2 == PT_EQ || op2 == PT_GE || op2 == PT_LE)
        {
          return CompResultLessAdj;
        }
      return (op2 == PT_GT) ? CompResultLess : CompResultEqual;
    }
    }
  else if (rc == DB_LT)
    {
      /* (val1, op1) < (val2, op2) */
      return CompResultLess;
    }
  else if (rc == DB_GT)
    {
      /* (val1, op1) > (val2, op2) */
      return CompResultGreater;
    }

  /* tp_value_compare() returned error? */
  return CompResultError;
}

/*
 * qo_range_optype_rank () -
 *   return:
 *   op(in):
 * description:
 *   a, x = 1
 *   b, x < 1
 *   c, x <= 1
 *  apparently, the rank: a < b < c
 */
static int
qo_range_optype_rank (PT_OP_TYPE op)
{
  switch (op)
    {
    case PT_EQ:
      return 1;
    case PT_GT:
    case PT_LT:
      return 2;
    case PT_GE:
    case PT_LE:
      return 3;
    case PT_GT_INF:
    case PT_LT_INF:
      return 4;
    default:
      assert (false);
      return 1;
    }
  return 1;
}

/*
 * qo_merge_range_helper () -
 *   return: valid, always false or always true
 *   parser(in):
 *   node(in):
 */
static DNF_MERGE_RANGE_RESULT
qo_merge_range_helper (PARSER_CONTEXT * parser, PT_NODE * node)
{
  PT_NODE *range, *sibling, *current, *prev = NULL;
  PT_OP_TYPE r_op, r_lop, r_uop, s_op, s_lop, s_uop;
  DB_VALUE *r_lv, *r_uv, *s_lv, *s_uv;
  bool r_lv_copied = false, r_uv_copied = false;
  COMP_DBVALUE_WITH_OPTYPE_RESULT cmp1, cmp2, cmp3, cmp4, cmp5, cmp6;
  bool need_to_determine_upper_bound;

  int r_rank;
  int s_rank;

  if (node->info.expr.arg2->or_next == NULL)
    {
      /* one range spec; nothing to merge */
      return DNF_RANGE_VALID;
    }

  r_lv = r_uv = s_lv = s_uv = NULL;
  current = NULL;
  range = node->info.expr.arg2;
  prev = NULL;
  while (range)
    {
      if (!pt_is_const_not_hostvar (range->info.expr.arg1)
      || (range->info.expr.arg2 && !pt_is_const_not_hostvar (range->info.expr.arg2)))
    {
      /* not constant; cannot be merged */
      prev = range;
      range = range->or_next;
      continue;
    }

      r_op = range->info.expr.op;
      if (pt_between_to_comp_op (r_op, &r_lop, &r_uop) != 0)
    {
      /* something wrong; continue to next range spec */
      prev = range;
      range = range->or_next;
      continue;
    }

      /* search DNF list from the next to the node and keep track of the pointer to previous node */
      current = range;
      while ((sibling = current->or_next))
    {
      if (!pt_is_const_not_hostvar (sibling->info.expr.arg1)
          || (sibling->info.expr.arg2 && !pt_is_const_not_hostvar (sibling->info.expr.arg2)))
        {
          /* not constant; cannot be merged */
          current = current->or_next;
          continue;
        }

      s_op = sibling->info.expr.op;
      if (pt_between_to_comp_op (s_op, &s_lop, &s_uop) != 0)
        {
          /* something wrong; continue to next range spec */
          current = current->or_next;
          continue;
        }

      if (r_lop == PT_GT_INF)
        {
          /* PT_BETWEEN_INF_LE or PT_BETWEEN_INF_LT */
          if (r_lv_copied && r_lv)
        {
          pr_free_value (r_lv);
          r_lv_copied = false;
        }
          if (r_uv_copied && r_uv)
        {
          pr_free_value (r_uv);
          r_uv_copied = false;
        }
          r_lv = NULL;
          r_uv = pt_value_to_db (parser, range->info.expr.arg1);
        }
      else if (r_uop == PT_LT_INF)
        {
          /* PT_BETWEEN_GE_INF or PT_BETWEEN_GT_INF */
          if (r_lv_copied && r_lv)
        {
          pr_free_value (r_lv);
          r_lv_copied = false;
        }
          if (r_uv_copied && r_uv)
        {
          pr_free_value (r_uv);
          r_uv_copied = false;
        }
          r_lv = pt_value_to_db (parser, range->info.expr.arg1);
          r_uv = NULL;
        }
      else if (r_lop == PT_EQ)
        {
          /* PT_BETWEEN_EQ_NA */
          if (r_lv_copied && r_lv)
        {
          pr_free_value (r_lv);
          r_lv_copied = false;
        }
          if (r_uv_copied && r_uv)
        {
          pr_free_value (r_uv);
          r_uv_copied = false;
        }
          r_lv = pt_value_to_db (parser, range->info.expr.arg1);
          r_uv = r_lv;
        }
      else
        {
          /* PT_BETWEEN_GE_LE, PT_BETWEEN_GE_LT, PT_BETWEEN_GT_LE, or PT_BETWEEN_GT_LT */
          if (r_lv_copied && r_lv)
        {
          pr_free_value (r_lv);
          r_lv_copied = false;
        }
          if (r_uv_copied && r_uv)
        {
          pr_free_value (r_uv);
          r_uv_copied = false;
        }
          r_lv = pt_value_to_db (parser, range->info.expr.arg1);
          r_uv = pt_value_to_db (parser, range->info.expr.arg2);
        }

      if (s_lop == PT_GT_INF)
        {
          /* PT_BETWEEN_INF_LE or PT_BETWEEN_INF_LT */
          s_lv = NULL;
          s_uv = pt_value_to_db (parser, sibling->info.expr.arg1);
        }
      else if (s_uop == PT_LT_INF)
        {
          /* PT_BETWEEN_GE_INF or PT_BETWEEN_GT_INF */
          s_lv = pt_value_to_db (parser, sibling->info.expr.arg1);
          s_uv = NULL;
        }
      else if (s_lop == PT_EQ)
        {
          /* PT_BETWEEN_EQ_NA */
          s_lv = pt_value_to_db (parser, sibling->info.expr.arg1);
          s_uv = s_lv;
        }
      else
        {
          /* PT_BETWEEN_GE_LE, PT_BETWEEN_GE_LT, PT_BETWEEN_GT_LE, or PT_BETWEEN_GT_LT */
          s_lv = pt_value_to_db (parser, sibling->info.expr.arg1);
          s_uv = pt_value_to_db (parser, sibling->info.expr.arg2);
        }

      PT_EXPR_INFO_CLEAR_FLAG (node, PT_EXPR_INFO_EMPTY_RANGE);
      /* check if the two range specs are mergable */
      cmp1 = qo_compare_dbvalue_with_optype (r_lv, r_lop, s_lv, s_lop);
      cmp2 = qo_compare_dbvalue_with_optype (r_lv, r_lop, s_uv, s_uop);
      cmp3 = qo_compare_dbvalue_with_optype (r_uv, r_uop, s_lv, s_lop);
      cmp4 = qo_compare_dbvalue_with_optype (r_uv, r_uop, s_uv, s_uop);

      /* make more compare to detect something like "a>1 or a between 1 and 0" */
      cmp5 = qo_compare_dbvalue_with_optype (r_lv, r_lop, r_uv, r_uop);
      cmp6 = qo_compare_dbvalue_with_optype (s_lv, s_lop, s_uv, s_uop);

      if (cmp1 == CompResultError || cmp2 == CompResultError || cmp3 == CompResultError || cmp4 == CompResultError)
        {
          /* somthine wrong; continue to next range spec */
          current = current->or_next;
          continue;
        }
      if (((cmp1 == CompResultLess || cmp1 == CompResultGreater) && cmp1 == cmp2 && cmp1 == cmp3 && cmp1 == cmp4)
          || cmp5 == CompResultGreater || cmp6 == CompResultGreater)
        {
          /* they are disjoint; continue to next range spec */
          current = current->or_next;
          continue;
        }

      /* merge the two range specs */
      /* swap arg1 and arg2 if op type is INF_LT or INF_LE to make easy the following merge algorithm */
      if (r_op == PT_BETWEEN_INF_LT || r_op == PT_BETWEEN_INF_LE)
        {
          range->info.expr.arg2 = range->info.expr.arg1;
          range->info.expr.arg1 = NULL;
        }
      if (s_op == PT_BETWEEN_INF_LT || s_op == PT_BETWEEN_INF_LE)
        {
          sibling->info.expr.arg2 = sibling->info.expr.arg1;
          sibling->info.expr.arg1 = NULL;
        }
      /* determine the lower bound of the merged range spec */
      need_to_determine_upper_bound = true;
      if (cmp1 == CompResultGreaterAdj || cmp1 == CompResultGreater)
        {
          parser_free_tree (parser, range->info.expr.arg1);
          if (s_op == PT_BETWEEN_EQ_NA)
        {
          range->info.expr.arg1 = parser_copy_tree (parser, sibling->info.expr.arg1);
        }
          else
        {
          range->info.expr.arg1 = sibling->info.expr.arg1;
        }
          r_lop = s_lop;
          if (r_lv_copied && r_lv)
        {
          pr_free_value (r_lv);
          r_lv_copied = false;
        }
          if (s_lv)
        {
          r_lv = pr_copy_value (s_lv);
          r_lv_copied = true;
        }
          else
        {
          r_lv = s_lv;
        }

          sibling->info.expr.arg1 = NULL;
          if (r_op == PT_BETWEEN_EQ_NA)
        {       /* PT_BETWEEN_EQ_NA */
          parser_free_tree (parser, range->info.expr.arg2);
          if (s_op == PT_BETWEEN_EQ_NA)
            {
              range->info.expr.arg2 = parser_copy_tree (parser, sibling->info.expr.arg1);
            }
          else
            {
              range->info.expr.arg2 = sibling->info.expr.arg2;
            }
          sibling->info.expr.arg2 = NULL;
          r_uop = PT_LE;
          need_to_determine_upper_bound = false;
        }

          if (r_lop == PT_EQ)
        {       /* PT_BETWEEN_EQ_NA */
          r_lop = PT_GE;
        }
        }
      else if (cmp1 == CompResultEqual)
        {
          /* There are two groups to reach here. 1. Both operators are identical(EQ, GE, LE, GT_INF, LT_INF) 2.
           * non-identical operators combination among (EQ, GE, LE).  GE for (EQ-GE), GE of (GE-EQ), LE for
           * (EQ-LE), LE for (LE-EQ) */
          r_rank = qo_range_optype_rank (r_lop);
          s_rank = qo_range_optype_rank (s_lop);

          if (r_rank < s_rank)
        {
          r_lop = s_lop;
        }
        }

      /* determine the upper bound of the merged range spec */
      if (cmp4 == CompResultLess || cmp4 == CompResultLessAdj)
        {
          if (need_to_determine_upper_bound == true)
        {
          parser_free_tree (parser, range->info.expr.arg2);
          if (s_op == PT_BETWEEN_EQ_NA)
            {
              range->info.expr.arg2 = parser_copy_tree (parser, sibling->info.expr.arg1);
            }
          else
            {
              range->info.expr.arg2 = sibling->info.expr.arg2;
            }
          sibling->info.expr.arg2 = NULL;
        }
          r_uop = s_uop;
          if (r_uv_copied && r_uv)
        {
          pr_free_value (r_uv);
          r_uv_copied = false;
        }
          if (s_uv)
        {
          r_uv = pr_copy_value (s_uv);
          r_uv_copied = true;
        }
          else
        {
          r_uv = s_uv;
        }

          if (r_uop == PT_EQ)
        {       /* PT_BETWEEN_EQ_NA */
          r_uop = PT_LE;
        }
        }
      else if (cmp4 == CompResultEqual)
        {
          /* There are two groups to reach here. 1. Both operators are identical(EQ, GE, LE, GT_INF, LT_INF) 2.
           * non-identical operators combination among (EQ, GE, LE).  GE for (EQ-GE), GE of (GE-EQ), LE for
           * (EQ-LE), LE for (LE-EQ) */
          r_rank = qo_range_optype_rank (r_uop);
          s_rank = qo_range_optype_rank (s_uop);

          if (r_rank < s_rank)
        {
          r_uop = s_uop;
        }
        }

      /* determine the new range type */
      if (pt_comp_to_between_op (r_lop, r_uop, PT_RANGE_MERGE, &r_op) != 0)
        {
          /* the merge result is unbound range spec, INF_INF; this means that this RANGE node is always true and
           * meaningless */
          return DNF_RANGE_ALWAYS_TRUE;
        }
      /* check if the range is invalid, that is, lower bound is greater than upper bound */
      cmp1 = qo_compare_dbvalue_with_optype (r_lv, r_lop, r_uv, r_uop);
      if (cmp1 == CompResultGreaterAdj || cmp1 == CompResultGreater)
        {
          /* this is always false */
          r_op = (PT_OP_TYPE) 0;
        }
      else if (cmp1 == CompResultEqual)
        {
          if (r_op == PT_BETWEEN_GE_LE)
        {       /* convert to PT_EQ */
          r_lop = r_uop = PT_EQ;

          r_op = PT_BETWEEN_EQ_NA;
          parser_free_tree (parser, range->info.expr.arg2);
          range->info.expr.arg2 = NULL;
        }
        }

      range->info.expr.op = r_op;
      /* recover arg1 and arg2 for the type of INF_LT and INF_LE */
      if (r_op == PT_BETWEEN_INF_LT || r_op == PT_BETWEEN_INF_LE)
        {
          range->info.expr.arg1 = range->info.expr.arg2;
          range->info.expr.arg2 = NULL;
        }
      /* no need to recover the sibling because it is to be deleted */

      /* delete the sibling node and adjust linked list */
      current->or_next = sibling->or_next;
      sibling->next = sibling->or_next = NULL;
      parser_free_tree (parser, sibling);

      if (r_op == 0)
        {
          /* We determined that this range is always false. If we successfully merged all ranges in this DNF and
           * the final result is false, we can return false. If we haven't reached the end yet or we found disjoint
           * ranges along the way, we need to remove this node from the DNF. */
          if (prev == NULL && range->or_next == NULL)
        {
          return DNF_RANGE_ALWAYS_FALSE;
        }
          current = range->or_next;
          range->or_next = NULL;
          parser_free_tree (parser, range);
          range = current;
          if (prev == NULL)
        {
          /* first node */
          node->info.expr.arg2 = range;
          range = NULL;
        }
          else
        {
          prev->or_next = range;
          /* go to next node */
          range = prev;
        }
          /* no sense in handling siblings since current range was invalidated */
          break;
        }

      /* with merged range, search DNF list from the next to the node and keep track of the pointer to previous
       * node */
      current = range;
    }
      if (range == NULL)
    {
      range = node->info.expr.arg2;
    }
      else
    {
      prev = range;
      range = range->or_next;
    }
    }

  if (r_lv_copied && r_lv)
    {
      pr_free_value (r_lv);
    }
  if (r_uv_copied && r_uv)
    {
      pr_free_value (r_uv);
    }

  for (range = node->info.expr.arg2; range; range = range->or_next)
    {
      if (range->info.expr.op == PT_BETWEEN_EQ_NA && range->info.expr.arg2 != NULL)
    {
      parser_free_tree (parser, range->info.expr.arg2);
      range->info.expr.arg2 = NULL;
    }
    }
  return DNF_RANGE_VALID;
}

/*
 * qo_convert_to_range () - Convert comparison term to RANGE term
 *   return:
 *   parser(in):
 *   wherep(in): pointer to WHERE list
 *
 * Note:
 *  examples:
 *      1. WHERE a<=20 AND a=>10   -->  WHERE a RANGE(10 GE_LE 20)
 *      2. WHERE a<10              -->  WHERE a RANGE(10 INF_LT)
 *      3. WHERE a>=20             -->  WHERE a RANGE(20 GE_INF)
 *      4. WHERE a<10 OR a>=20     -->  WHERE a RANGE(10 INF_LT, 20 GE_INF)
 */
static void
qo_convert_to_range (PARSER_CONTEXT * parser, PT_NODE ** wherep)
{
  PT_NODE *cnf_node, *dnf_node, *cnf_prev, *dnf_prev;
  PT_NODE *arg1_prior, *func_arg;
  DNF_MERGE_RANGE_RESULT result;
  int is_attr;
  bool is_all_constant;

  /* traverse CNF list and keep track of the pointer to previous node */
  cnf_prev = NULL;
  while ((cnf_node = (cnf_prev ? cnf_prev->next : *wherep)))
    {

      /* traverse DNF list and keep track of the pointer to previous node */
      dnf_prev = NULL;
      while ((dnf_node = (dnf_prev ? dnf_prev->or_next : cnf_node)))
    {
      if (dnf_node->node_type != PT_EXPR)
        {
          /* dnf_node is not an expression node */
          dnf_prev = dnf_prev ? dnf_prev->or_next : dnf_node;
          continue;
        }

      arg1_prior = pt_get_first_arg_ignore_prior (dnf_node);

      is_attr = true;
      is_all_constant = true;
      if (pt_is_multi_col_term (arg1_prior))
        {
          /* multi_col_term can convert to range if arg1 is (attr,func_idx_expr,constant) */
          func_arg = arg1_prior->info.function.arg_list;
          for ( /* none */ ; func_arg; func_arg = func_arg->next)
        {
          if (!pt_is_attr (func_arg) && !pt_is_function_index_expression (func_arg) && !pt_is_const (func_arg))
            {
              is_attr = false;
              break;
            }
          else if (!pt_is_const (func_arg))
            {
              is_all_constant = false;
            }
        }
          /* if multi_col_term's columns are all constant value then NOT convert to range for constant folding */
          if (is_all_constant)
        {
          is_attr = false;
        }
        }
      else
        {
          is_attr = (pt_is_attr (arg1_prior) || pt_is_function_index_expression (arg1_prior));
        }

      if (!is_attr && !pt_is_instnum (arg1_prior))
        {
          /* LHS is not an attribute */
          dnf_prev = dnf_prev ? dnf_prev->or_next : dnf_node;
          continue;
        }

      if (dnf_node == cnf_node && dnf_node->or_next == NULL && dnf_node->info.expr.op == PT_EQ
          && !pt_is_instnum (arg1_prior))
        {
          /* do not convert one predicate '=' term to RANGE */
          dnf_prev = dnf_prev ? dnf_prev->or_next : dnf_node;
          continue;
        }

      switch (dnf_node->info.expr.op)
        {
        case PT_EQ:
        case PT_GT:
        case PT_GE:
        case PT_LT:
        case PT_LE:
        case PT_BETWEEN:
        case PT_IS_IN:
        case PT_RANGE:

          /* should be pure constant in list */
          if (dnf_node->info.expr.op == PT_IS_IN && PT_IS_SET_TYPE (dnf_node->info.expr.arg2)
          && dnf_node->or_next == NULL)
        {
          /*
           * skip merge in list
           * server will eliminate duplicate keys
           * this is because merging huge in list takes
           * too much time.
           */
          qo_convert_to_range_helper (parser, dnf_node);
          break;
        }

          /* convert all comparison nodes in the DNF list which have the same attribute as its LHS into one RANGE
           * node containing multi-range spec */
          qo_convert_to_range_helper (parser, dnf_node);

          if (dnf_node->info.expr.op == PT_RANGE)
        {
          /* merge range specs in the RANGE node */
          result = qo_merge_range_helper (parser, dnf_node);
          if (result == DNF_RANGE_ALWAYS_FALSE)
            {
              /* An empty range is always false so change it to 0<>0 */
              DB_VALUE db_zero;
              parser_free_tree (parser, dnf_node->info.expr.arg1);
              parser_free_tree (parser, dnf_node->info.expr.arg2);
              db_make_int (&db_zero, 0);

              dnf_node->info.expr.arg1 = pt_dbval_to_value (parser, &db_zero);
              dnf_node->info.expr.arg2 = pt_dbval_to_value (parser, &db_zero);
              dnf_node->info.expr.op = PT_NE;

            }
          else if (result == DNF_RANGE_ALWAYS_TRUE)
            {
              /* change unbound range spec to IS NOT NULL node */
              parser_free_tree (parser, dnf_node->info.expr.arg2);
              dnf_node->info.expr.arg2 = NULL;
              dnf_node->info.expr.op = PT_IS_NOT_NULL;
            }
        }
          break;
        default:
          break;
        }
      dnf_prev = dnf_prev ? dnf_prev->or_next : dnf_node;
    }
      cnf_prev = cnf_prev ? cnf_prev->next : cnf_node;
    }
}

/*
 * qo_apply_range_intersection_helper () -
 *   return:
 *   parser(in):
 *   node1(in):
 *   node2(in):
 */
static void
qo_apply_range_intersection_helper (PARSER_CONTEXT * parser, PT_NODE * node1, PT_NODE * node2)
{
  PT_NODE *range, *sibling, *prev, *new_range, *temp1, *temp2;
  PT_OP_TYPE r_op, r_lop, r_uop, s_op, s_lop, s_uop, new_op, new_lop, new_uop;
  DB_VALUE *r_lv, *r_uv, *s_lv, *s_uv, *new_lv, *new_uv;
  COMP_DBVALUE_WITH_OPTYPE_RESULT cmp1, cmp2, cmp3, cmp4, new_cmp;
  bool dont_remove_sibling = false;
  bool include_nonvalue;

  assert (parser != NULL);
  if (parser == NULL)
    {
      return;
    }

  /* for each range spec of the node1 */
  prev = NULL;
  while ((range = (prev ? prev->or_next : node1->info.expr.arg2)))
    {
      if (!pt_is_const_not_hostvar (range->info.expr.arg1)
      || (range->info.expr.arg2 && !pt_is_const_not_hostvar (range->info.expr.arg2)))
    {
      /* not constant; cannot be merged */
      prev = prev ? prev->or_next : range;
      dont_remove_sibling = true;
      continue;
    }

      r_op = range->info.expr.op;
      if (pt_between_to_comp_op (r_op, &r_lop, &r_uop) != 0)
    {
      /* something wrong; continue to next range spec */
      prev = prev ? prev->or_next : range;
      dont_remove_sibling = true;
      continue;
    }

      if (r_lop == PT_GT_INF)
    {
      /* PT_BETWEEN_INF_LE or PT_BETWEEN_INF_LT */
      r_lv = NULL;
      r_uv = pt_value_to_db (parser, range->info.expr.arg1);
    }
      else if (r_uop == PT_LT_INF)
    {
      /* PT_BETWEEN_GE_INF or PT_BETWEEN_GT_INF */
      r_lv = pt_value_to_db (parser, range->info.expr.arg1);
      r_uv = NULL;
    }
      else if (r_lop == PT_EQ)
    {
      /* PT_BETWEEN_EQ_NA */
      r_lv = pt_value_to_db (parser, range->info.expr.arg1);
      r_uv = r_lv;
    }
      else
    {
      /* PT_BETWEEN_GE_LE, PT_BETWEEN_GE_LT, PT_BETWEEN_GT_LE, or PT_BETWEEN_GT_LT */
      r_lv = pt_value_to_db (parser, range->info.expr.arg1);
      r_uv = pt_value_to_db (parser, range->info.expr.arg2);
    }

      if (DB_IS_NULL (r_lv) && DB_IS_NULL (r_uv))
    {
      /* if both are null, this expr is false. */
      prev = prev ? prev->or_next : range;
      dont_remove_sibling = true;
      continue;
    }

      /* for each range spec of the node2 */
      include_nonvalue = false;
      for (sibling = node2->info.expr.arg2; sibling; sibling = sibling->or_next)
    {
      if (!pt_is_const_not_hostvar (sibling->info.expr.arg1)
          || (sibling->info.expr.arg2 && !pt_is_const_not_hostvar (sibling->info.expr.arg2)))
        {
          /* not constant; cannot be merged */
          include_nonvalue = true;
          break;
        }
    }

      if (include_nonvalue == true)
    {
      /* there was no application */
      prev = prev ? prev->or_next : range;
      continue;
    }

      new_range = NULL;

      /* for each range spec of the node2 */
      for (sibling = node2->info.expr.arg2; sibling; sibling = sibling->or_next)
    {
      assert (pt_is_const_not_hostvar (sibling->info.expr.arg1)
          && (sibling->info.expr.arg2 == NULL || pt_is_const_not_hostvar (sibling->info.expr.arg2)));

      s_op = sibling->info.expr.op;
      if (pt_between_to_comp_op (s_op, &s_lop, &s_uop) != 0)
        {
          /* something wrong; continue to next range spec */
          continue;
        }

      if (s_lop == PT_GT_INF)
        {
          /* PT_BETWEEN_INF_LE or PT_BETWEEN_INF_LT */
          s_lv = NULL;
          s_uv = pt_value_to_db (parser, sibling->info.expr.arg1);
        }
      else if (s_uop == PT_LT_INF)
        {
          /* PT_BETWEEN_GE_INF or PT_BETWEEN_GT_INF */
          s_lv = pt_value_to_db (parser, sibling->info.expr.arg1);
          s_uv = NULL;
        }
      else if (s_lop == PT_EQ)
        {
          /* PT_BETWEEN_EQ_NA */
          s_lv = pt_value_to_db (parser, sibling->info.expr.arg1);
          s_uv = s_lv;
        }
      else
        {
          /* PT_BETWEEN_GE_LE, PT_BETWEEN_GE_LT, PT_BETWEEN_GT_LE, or PT_BETWEEN_GT_LT */
          s_lv = pt_value_to_db (parser, sibling->info.expr.arg1);
          s_uv = pt_value_to_db (parser, sibling->info.expr.arg2);
        }

      if (DB_IS_NULL (s_lv) && DB_IS_NULL (s_uv))
        {
          /* if both are null, this expr is false. */
          PT_EXPR_INFO_SET_FLAG (sibling, PT_EXPR_INFO_EMPTY_RANGE);
          dont_remove_sibling = true;
          continue;
        }

      PT_EXPR_INFO_CLEAR_FLAG (sibling, PT_EXPR_INFO_EMPTY_RANGE);
      /* check if the two range specs are mergable */
      cmp1 = qo_compare_dbvalue_with_optype (r_lv, r_lop, s_lv, s_lop);
      cmp2 = qo_compare_dbvalue_with_optype (r_lv, r_lop, s_uv, s_uop);
      cmp3 = qo_compare_dbvalue_with_optype (r_uv, r_uop, s_lv, s_lop);
      cmp4 = qo_compare_dbvalue_with_optype (r_uv, r_uop, s_uv, s_uop);
      if (cmp1 == CompResultError || cmp2 == CompResultError || cmp3 == CompResultError || cmp4 == CompResultError)
        {
          /* somthine wrong; continue to next range spec */
          continue;
        }
      if (!new_range)
        {
          new_range = range;
        }
      if (!((cmp1 == CompResultLess || cmp1 == CompResultGreater) && cmp1 == cmp2 && cmp1 == cmp3 && cmp1 == cmp4))
        {
          /* they are not disjoint; apply intersection to the two range specs */

          /* allocate new range spec node */
          temp1 = range->or_next;
          range->or_next = NULL;
          temp2 = parser_copy_tree (parser, range);
          new_op = r_op;
          if (r_op == PT_BETWEEN_EQ_NA)
        {
          parser_free_tree (parser, temp2->info.expr.arg2);
          temp2->info.expr.arg2 = parser_copy_tree (parser, temp2->info.expr.arg1);
        }
          new_lop = r_lop;
          new_uop = r_uop;
          temp2->or_next = (new_range == range) ? NULL : new_range;
          new_range = temp2;
          range->or_next = temp1;
          /* swap arg1 and arg2 if op type is INF_LT or INF_LE to make easy the following merge algorithm */
          if (new_op == PT_BETWEEN_INF_LT || new_op == PT_BETWEEN_INF_LE)
        {
          new_range->info.expr.arg2 = new_range->info.expr.arg1;
          new_range->info.expr.arg1 = NULL;
        }
          if (s_op == PT_BETWEEN_INF_LT || s_op == PT_BETWEEN_INF_LE)
        {
          sibling->info.expr.arg2 = sibling->info.expr.arg1;
          sibling->info.expr.arg1 = NULL;
        }
          /* determine the lower bound of the merged range spec */
          if (cmp1 == CompResultLess || cmp1 == CompResultLessAdj)
        {
          parser_free_tree (parser, new_range->info.expr.arg1);
          new_range->info.expr.arg1 = parser_copy_tree (parser, sibling->info.expr.arg1);
          new_lop = s_lop;
          if (cmp3 == CompResultEqual && cmp4 == CompResultEqual)
            {
              new_uop = PT_EQ;
            }
        }
          /* determine the upper bound of the merged range spec */
          if (cmp4 == CompResultGreaterAdj || cmp4 == CompResultGreater)
        {
          parser_free_tree (parser, new_range->info.expr.arg2);
          new_range->info.expr.arg2 = parser_copy_tree (parser, sibling->info.expr.arg2);
          new_uop = s_uop;
        }
          /* determine the new range type */
          if (pt_comp_to_between_op (new_lop, new_uop, PT_RANGE_INTERSECTION, &new_op) != 0)
        {
          /* they are not disjoint; remove empty range */
          if (new_range->or_next == NULL)
            {
              parser_free_tree (parser, new_range);
              new_range = range;
            }
          else
            {
              temp1 = new_range->or_next;
              new_range->or_next = NULL;
              parser_free_tree (parser, new_range);
              new_range = temp1;
            }
        }
          else
        {       /* merged range is empty */
          new_range->info.expr.op = new_op;
          /* check if the new range is valid */
          if (new_range->info.expr.arg1 && new_range->info.expr.arg2)
            {
              if (pt_between_to_comp_op (new_op, &new_lop, &new_uop) != 0)
            {
              /* must be be impossible; skip and go ahead */
            }
              else
            {
              new_lv = pt_value_to_db (parser, new_range->info.expr.arg1);
              new_uv = pt_value_to_db (parser, new_range->info.expr.arg2);
              new_cmp = qo_compare_dbvalue_with_optype (new_lv, new_lop, new_uv, new_uop);
              if (new_cmp == CompResultGreater || new_cmp == CompResultGreaterAdj)
                {
                  /* they are not disjoint; remove empty range */
                  if (new_range->or_next == NULL)
                {
                  parser_free_tree (parser, new_range);
                  new_range = range;
                }
                  else
                {
                  temp1 = new_range->or_next;
                  new_range->or_next = NULL;
                  parser_free_tree (parser, new_range);
                  new_range = temp1;
                }
                }
            }
            }
        }       /* merged range is empty */

          /* recover arg1 and arg2 for the type of INF_LT, INF_LE */
          if (new_op == PT_BETWEEN_INF_LT || new_op == PT_BETWEEN_INF_LE)
        {
          if (new_range->info.expr.arg1 == NULL && new_range->info.expr.arg2 != NULL)
            {
              new_range->info.expr.arg1 = new_range->info.expr.arg2;
              new_range->info.expr.arg2 = NULL;
            }
        }
          if (s_op == PT_BETWEEN_INF_LT || s_op == PT_BETWEEN_INF_LE)
        {
          if (sibling->info.expr.arg1 == NULL && sibling->info.expr.arg2 != NULL)
            {
              sibling->info.expr.arg1 = sibling->info.expr.arg2;
              sibling->info.expr.arg2 = NULL;
            }
        }
        }

      /* mark this sibling node to be deleted */
      PT_EXPR_INFO_SET_FLAG (sibling, PT_EXPR_INFO_EMPTY_RANGE);
    }

      if (new_range == NULL)
    {
      /* there was no application */
      prev = prev ? prev->or_next : range;
      continue;
    }

      /* replace the range node with the new_range node */
      if (new_range != range)
    {
      if (prev)
        {
          prev->or_next = new_range;
        }
      else
        {
          node1->info.expr.arg2 = new_range;
        }
      for (prev = new_range; prev->or_next; prev = prev->or_next)
        {
          ;
        }
      prev->or_next = range->or_next;
    }
      else
    {
      /* the result is empty range */
      if (prev)
        {
          prev->or_next = range->or_next;
        }
      else
        {
          node1->info.expr.arg2 = range->or_next;
        }
    }
      /* range->next == NULL */
      range->or_next = NULL;
      parser_free_tree (parser, range);
    }


  if (dont_remove_sibling != true)
    {
      /* remove nodes marked as to be deleted while applying intersction */
      prev = NULL;
      while ((sibling = (prev ? prev->or_next : node2->info.expr.arg2)))
    {
      if (PT_EXPR_INFO_IS_FLAGED (sibling, PT_EXPR_INFO_EMPTY_RANGE))
        {
          if (prev)
        {
          prev->or_next = sibling->or_next;
        }
          else
        {
          node2->info.expr.arg2 = sibling->or_next;
        }
          /* sibling->next == NULL */
          sibling->or_next = NULL;
          parser_free_tree (parser, sibling);
        }
      else
        {
          prev = prev ? prev->or_next : sibling;
        }
    }
    }

  for (range = node1->info.expr.arg2; range; range = range->or_next)
    {
      if (range->info.expr.op == PT_BETWEEN_EQ_NA && range->info.expr.arg2 != NULL)
    {
      parser_free_tree (parser, range->info.expr.arg2);
      range->info.expr.arg2 = NULL;
    }
    }
  for (range = node2->info.expr.arg2; range; range = range->or_next)
    {
      if (range->info.expr.op == PT_BETWEEN_EQ_NA && range->info.expr.arg2 != NULL)
    {
      parser_free_tree (parser, range->info.expr.arg2);
      range->info.expr.arg2 = NULL;
    }
    }
}

/*
 * qo_apply_range_intersection () - Apply range intersection
 *   return:
 *   parser(in):
 *   wherep(in): pointer to WHERE list
 */
static void
qo_apply_range_intersection (PARSER_CONTEXT * parser, PT_NODE ** wherep)
{
  PT_NODE *node, *sibling, *node_prev, *sibling_prev;
  int location;
  PT_NODE *arg1_prior, *sibling_prior;

  /* traverse CNF list and keep track of the pointer to previous node */
  node_prev = NULL;
  while ((node = (node_prev ? node_prev->next : *wherep)))
    {
      if (node->node_type != PT_EXPR || node->info.expr.op != PT_RANGE || node->or_next != NULL)
    {
      /* NOTE: Due to implementation complexity, handle one predicate term only. */
      /* neither expression node, RANGE node, nor one predicate term */
      node_prev = node_prev ? node_prev->next : *wherep;
      continue;
    }

      arg1_prior = pt_get_first_arg_ignore_prior (node);

      if (!pt_is_attr (arg1_prior) && !pt_is_function_index_expression (arg1_prior) && !pt_is_instnum (arg1_prior))
    {
      /* LHS is not an attribute */
      node_prev = node_prev ? node_prev->next : *wherep;
      continue;
    }

      if (node->next == NULL)
    {           /* one range spec; nothing to intersect */
      PT_NODE *range;
      PT_OP_TYPE r_lop, r_uop;
      DB_VALUE *r_lv, *r_uv;
      COMP_DBVALUE_WITH_OPTYPE_RESULT cmp;

      range = node->info.expr.arg2;
      if (range->info.expr.arg2 && pt_is_const_not_hostvar (range->info.expr.arg1)
          && pt_is_const_not_hostvar (range->info.expr.arg2))
        {
          /* both constant; check range spec */
          if (!pt_between_to_comp_op (range->info.expr.op, &r_lop, &r_uop))
        {
          r_lv = pt_value_to_db (parser, range->info.expr.arg1);
          r_uv = pt_value_to_db (parser, range->info.expr.arg2);
          /* check if the range spec is valid */
          cmp = qo_compare_dbvalue_with_optype (r_lv, r_lop, r_uv, r_uop);
          if (cmp == CompResultGreaterAdj || cmp == CompResultGreater)
            {
              /* the range is invalid, that is, lower bound is greater than upper bound */
              if (range->or_next == NULL)
            {
              node->info.expr.arg2 = NULL;
            }
              else
            {
              node->info.expr.arg2 = range->or_next;
              range->or_next = NULL;
            }
              parser_free_tree (parser, range);
            }
          else if (cmp == CompResultError)
            {
              ;     /* something wrong; do nothing */
            }
        }
        }
    }

      /* search CNF list from the next to the node and keep track of the pointer to previous node */
      sibling_prev = node;

      while ((sibling = sibling_prev->next))
    {
      if (sibling->node_type != PT_EXPR || sibling->info.expr.op != PT_RANGE || sibling->or_next != NULL)
        {
          /* neither an expression node, RANGE node, nor one predicate term */
          sibling_prev = sibling_prev->next;
          continue;
        }

      sibling_prior = pt_get_first_arg_ignore_prior (sibling);
      if (PT_IS_EXPR_WITH_PRIOR_ARG (sibling))
        {
          if (!PT_IS_EXPR_WITH_PRIOR_ARG (node))
        {
          /* sibling has prior, node hasn't */
          sibling_prev = sibling_prev->next;
          continue;
        }
        }
      else
        {
          if (PT_IS_EXPR_WITH_PRIOR_ARG (node))
        {
          /* sibling hasn't prior, node has */
          sibling_prev = sibling_prev->next;
          continue;
        }
        }
      /* if node had prior check that sibling also contains prior and vice-versa */

      if (!pt_is_attr (sibling_prior) && !pt_is_function_index_expression (sibling_prior)
          && !pt_is_instnum (sibling_prior))
        {
          /* LHS is not an attribute */
          sibling_prev = sibling_prev->next;
          continue;
        }

      if (sibling->info.expr.location != node->info.expr.location)
        {
          sibling_prev = sibling_prev->next;
          continue;
        }

      if (arg1_prior->node_type != sibling_prior->node_type)
        {
          sibling_prev = sibling_prev->next;
          continue;
        }

      if ((pt_is_attr (arg1_prior) || pt_is_function_index_expression (arg1_prior))
          && (pt_is_attr (sibling_prior) || pt_is_function_index_expression (sibling_prior))
          && pt_check_path_eq (parser, arg1_prior, sibling_prior))
        {
          /* pt_check_path_eq() return non-zero if two are different */
          sibling_prev = sibling_prev->next;
          continue;
        }

      if ((pt_is_instnum (arg1_prior) && !pt_is_instnum (sibling_prior))
          || (!pt_is_instnum (arg1_prior) && pt_is_instnum (sibling_prior)))
        {
          sibling_prev = sibling_prev->next;
          continue;
        }

      /* found a node of the same attribute */

      /* combine each range specs of two RANGE nodes */
      qo_apply_range_intersection_helper (parser, node, sibling);

      /* remove the sibling node if its range is empty */
      if (sibling->info.expr.arg2 == NULL)
        {
          sibling_prev->next = sibling->next;
          sibling->next = NULL;
          /* sibling->or_next == NULL */
          parser_free_tree (parser, sibling);
        }
      else
        {
          sibling_prev = sibling_prev->next;
        }

      if (node->info.expr.arg2 == NULL)
        {
          break;
        }
    }

      /* remove the node if its range is empty */
      if (node->info.expr.arg2 == NULL)
    {
      if (node_prev)
        {
          node_prev->next = node->next;
        }
      else
        {
          *wherep = node->next;
        }

      node->next = NULL;
      location = node->info.expr.location;  /* save location */

      /* node->or_next == NULL */
      parser_free_tree (parser, node);

      if (location == 0)
        {
          /* empty conjuctive make whole condition always false */
          /* NOTICE: that is valid only when we handle one predicate terms in this function */
          parser_free_tree (parser, *wherep);

          /* make a single false node */
          node = parser_new_node (parser, PT_VALUE);
          if (node == NULL)
        {
          PT_INTERNAL_ERROR (parser, "allocate new node");
          return;
        }

          node->type_enum = PT_TYPE_LOGICAL;
          node->info.value.data_value.i = 0;
          node->info.value.location = location;
          (void) pt_value_to_db (parser, node);
          *wherep = node;

          return;
        }
      else
        {
          PT_NODE *prev, *next;

          /* empty conjunctive is outer join ON condition. remove all nodes which have same location number */
          prev = NULL;
          node = *wherep;
          while (node)
        {
          if ((node->node_type == PT_EXPR && node->info.expr.location == location)
              || (node->node_type == PT_VALUE && node->info.value.location == location))
            {
              next = node->next;
              node->next = NULL;
              parser_free_tree (parser, node);
              if (prev)
            {
              prev->next = next;
            }
              else
            {
              *wherep = next;
            }
              node = next;
            }
          else
            {
              prev = node;
              node = node->next;
            }
        }

          /* make a single false node and append it to WHERE list */
          node = parser_new_node (parser, PT_VALUE);
          if (node == NULL)
        {
          PT_INTERNAL_ERROR (parser, "allocate new node");
          return;
        }

          node->type_enum = PT_TYPE_LOGICAL;
          node->info.value.data_value.i = 0;
          node->info.value.location = location;
          (void) pt_value_to_db (parser, node);
          node->next = *wherep;
          *wherep = node;

          /* re-traverse CNF list */
          node_prev = node;
        }
    }
      else
    {
      node_prev = (node_prev) ? node_prev->next : *wherep;
    }
    }
}