Skip to content

File method_transform.c

File List > cubrid > src > parser > method_transform.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.
 *
 */

/*
 * method_transform.c - Rewrite queries that contain method calls
 */

#ident "$Id$"

#include "config.h"

#include "authenticate.h"
#include "porting.h"
#include "error_manager.h"
#include "parser.h"
#include "parser_message.h"
#include "view_transform.h"
#include "dbtype.h"

typedef struct meth_lambda METH_LAMBDA;
struct meth_lambda
{
  UINTPTR method_id;        /* method id of calls to replace */
  PT_NODE *replacement;     /* node to replace method calls with */
  PT_NODE *new_spec;        /* the new spec that was generated for the call */
};

typedef struct meth_stmt_info METH_STMT_INFO;
struct meth_stmt_info
{
  PT_NODE *root;        /* ptr to original statement */
  PT_NODE *select_statement;    /* ptr to current statement */
};

typedef struct meth_corr_info METH_CORR_INFO;
struct meth_corr_info
{
  UINTPTR spec_id;      /* spec id of spec we are expanding/collapsing */
  int corr_step;        /* amount to bump correlation */
  int corr_threshold;       /* correlation threshold which defines which correlations need to be bumped. */
};

typedef struct meth_info METH_INFO;
struct meth_info
{
  PT_NODE *method;      /* method we are finding the entity of */
  PT_NODE *entity_for_method;   /* the entity where we'll hang the method */
  int nesting_depth;        /* depth of current nesting level */
  int entities_nesting_depth;   /* depth of the entity */
};

typedef struct meth_info1 METH_INFO1;
struct meth_info1
{
  UINTPTR id;           /* spec_id or method_id we're looking for */
  int found;            /* whether we've found it */
};

typedef struct meth_info2 METH_INFO2;
struct meth_info2
{
  PT_NODE *root;        /* top node for the statement */
  int methods_to_translate; /* whether there are methods to translate */
};

typedef struct meth_info3 METH_INFO3;
struct meth_info3
{
  PT_NODE *entity;      /* ptr to entity if found, NULL otherwise */
  UINTPTR id;           /* id of entity to find */
};

typedef struct meth_info4 METH_INFO4;
struct meth_info4
{
  PT_NODE *spec_list;       /* specs at current level */
  UINTPTR id;           /* id of entity we're interested in */
  int found;            /* whether we've found it */
};

typedef struct meth_info5 METH_INFO5;
struct meth_info5
{
  PT_NODE *new_where;       /* where clause we are building */
  UINTPTR spec_id;      /* id of conjuncts we are interested in */
};

typedef struct meth_info6 METH_INFO6;
struct meth_info6
{
  UINTPTR old_id;       /* id to replace */
  UINTPTR new_id;       /* id to replace it with */
};

typedef struct meth_info7 METH_INFO7;
struct meth_info7
{
  UINTPTR id;           /* spec_id or method_id we're looking for */
  int found;            /* whether we've found it */
  int check_method_calls;   /* dive into method calls to look? */
};

static int meth_table_number = 0;   /* for unique table names */
static int meth_attr_number = 0;    /* for unique attribute names */

static PT_NODE *meth_translate_helper (PARSER_CONTEXT * parser, PT_NODE * node);
static PT_NODE *meth_translate_local (PARSER_CONTEXT * parser, PT_NODE * statement, void *void_arg, int *continue_walk);
static PT_NODE *meth_create_method_list (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static PT_NODE *meth_translate_select (PARSER_CONTEXT * parser, PT_NODE * select_statement, PT_NODE * root);
static PT_NODE *meth_translate_spec (PARSER_CONTEXT * parser, PT_NODE * spec, void *void_arg, int *continue_walk);
static PT_NODE *meth_collapse_nodes (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static PT_NODE *meth_get_method_params (PARSER_CONTEXT * parser, UINTPTR spec_id, PT_NODE * method_list, int *num);
static PT_NODE *meth_make_unique_range_var (PARSER_CONTEXT * parser, PT_NODE * spec);
static PT_NODE *meth_gen_as_attr_list (PARSER_CONTEXT * parser, PT_NODE * range_var, UINTPTR spec_id,
                       PT_NODE * attr_list);
static void meth_replace_method_params (PARSER_CONTEXT * parser, UINTPTR spec_id, PT_NODE * method_list,
                    PT_NODE * as_attr_list);
static void meth_replace_method_calls (PARSER_CONTEXT * parser, PT_NODE * root, PT_NODE * method_list,
                       PT_NODE * as_attr_list, PT_NODE * new_spec, int num_methods);
static PT_NODE *meth_replace_call (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static void meth_replace_referenced_attrs (PARSER_CONTEXT * parser, PT_NODE * root, PT_NODE * attr_list,
                       PT_NODE * as_attr_list, int num);
static PT_NODE *meth_find_last_entity (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static PT_NODE *meth_find_last_entity_post (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg,
                        int *continue_walk);
static PT_NODE *meth_match_entity (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static PT_NODE *meth_find_outside_refs (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static void meth_bump_correlation_level (PARSER_CONTEXT * parser, PT_NODE * node, int increment, int threshold,
                     UINTPTR spec_id);
static PT_NODE *meth_bump_corr_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static PT_NODE *meth_bump_corr_post (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static PT_NODE *meth_find_merge (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static PT_NODE *meth_is_method (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static PT_NODE *meth_method_path_entities (PARSER_CONTEXT * parser, PT_NODE * paths);
static PT_NODE *meth_non_method_path_entities (PARSER_CONTEXT * parser, PT_NODE * paths);
static PT_NODE *meth_find_entity (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static PT_NODE *meth_find_method (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static PT_NODE *meth_find_outside_refs_subquery (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg,
                         int *continue_walk);
static PT_NODE *meth_push_conjuncts (PARSER_CONTEXT * parser, UINTPTR spec_id, PT_NODE ** where);
static PT_NODE *meth_grab_conj (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static void meth_grab_cnf_conj (PARSER_CONTEXT * parser, PT_NODE ** where, METH_INFO5 * info5);
static PT_NODE *meth_add_conj (PARSER_CONTEXT * parser, PT_NODE * where, PT_NODE * new_conj);
static PT_NODE *meth_replace_id_in_method_names (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg,
                         int *continue_walk);
static int meth_refs_to_scope (PARSER_CONTEXT * parser, PT_NODE * scope, PT_NODE * tree);
static PT_NODE *meth_have_methods (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static PT_NODE *meth_find_hierarchical_op (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static void meth_find_hierarchical_in_method_list (PARSER_CONTEXT * parser, PT_NODE * method_list,
                           bool * has_hierarchical_expr);
static void meth_copy_hierarchical_expr_to_list (PARSER_CONTEXT * parser, PT_NODE * src_list, PT_NODE ** dst_list_p,
                         int *copy_count);
static void meth_move_hierarchical_to_derived (PARSER_CONTEXT * parser, PT_SELECT_INFO * statement_info,
                           PT_SELECT_INFO * derived_info);
static void meth_replace_hierarchical_exprs (PARSER_CONTEXT * parser, PT_NODE ** select_list_p, PT_NODE * ref_attrs,
                         int num_methods);

/*
 * meth_have_methods() -
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in/out):
 *   continue_walk(in/out):
 */
static PT_NODE *
meth_have_methods (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  int *have_method = (int *) arg;

  *continue_walk = PT_CONTINUE_WALK;
  if (PT_IS_METHOD (node))
    {
      *have_method = 1;
      *continue_walk = PT_STOP_WALK;
    }

  return node;
}


/*
 * pt_statement_have_methods() -
 *   return: 1 on methods exists
 *   parser(in):
 *   statement(in):
 */
int
pt_statement_have_methods (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int have_method = 0;

  if (!statement)
    {
      return 0;
    }

  parser_walk_tree (parser, statement, NULL, NULL, meth_have_methods, &have_method);

  return have_method;
}

/*
 * meth_translate() - Recursively translates queries with methods
 *   return:
 *   parser(in):
 *   node(in/out): Query to translate
 */
PT_NODE *
meth_translate (PARSER_CONTEXT * parser, PT_NODE * volatile node)
{
  int hand_rewritten;
  PT_NODE *next;

  if (!node)
    {
      return NULL;
    }

  /* set spec_ident for terms */
  node = parser_walk_tree (parser, node, NULL, NULL, pt_do_cnf, NULL);

  /* don't translate if it is a hand re-written query using our exposed syntax for debugging purposes.  */
  hand_rewritten = 0;
  (void) parser_walk_tree (parser, node, meth_find_merge, &hand_rewritten, NULL, NULL);

  if (hand_rewritten)
    {
      goto exit_on_done;
    }

  /* set up an environment for longjump to return to if there is an out of memory error in pt_memory.c. DO NOT RETURN
   * unless PT_CLEAR_JMP_ENV is called to clear the environment. */
  PT_SET_JMP_ENV (parser);

  /* save next link */
  next = node->next;
  node->next = NULL;

  node = meth_translate_helper (parser, node);
  if (node)
    {
      if (pt_has_error (parser))
    {
      node = NULL;
    }
      else
    {
      node->next = next;    /* restore link */
    }
    }

  PT_CLEAR_JMP_ENV (parser);

exit_on_done:

  return node;
}

/*
 * meth_translate_helper() -
 *   return:
 *   parser(in):
 *   node(in/out):
 */
static PT_NODE *
meth_translate_helper (PARSER_CONTEXT * parser, PT_NODE * node)
{
  PT_NODE *spec, *derived_table, *elm;
  METH_INFO2 info2;

  if (!node)
    {
      return NULL;
    }

  /* only translate translatable statements */
  switch (node->node_type)
    {
    case PT_UNION:
    case PT_DIFFERENCE:
    case PT_INTERSECTION:
      node->info.query.q.union_.arg1 = meth_translate_helper (parser, node->info.query.q.union_.arg1);
      node->info.query.q.union_.arg2 = meth_translate_helper (parser, node->info.query.q.union_.arg2);
      break;

    case PT_SELECT:
      /* get entity list */
      for (spec = node->info.query.q.select.from; spec; spec = spec->next)
    {
      derived_table = spec->info.spec.derived_table;
      if (derived_table)
        {
          switch (spec->info.spec.derived_table_type)
        {
        case PT_IS_SUBQUERY:
          elm = derived_table;
          break;

        case PT_IS_SET_EXPR:
          if (pt_is_query (derived_table))
            {
              elm = derived_table;
            }
          else if (derived_table->node_type == PT_VALUE)
            {
              elm = derived_table->info.value.data_value.set;
            }
          else if (derived_table->node_type == PT_FUNCTION)
            {
              elm = derived_table->info.function.arg_list;
            }
          else
            {
              /* skip and go ahead for example: use parameter case create class x; -- set audit all on x;
               * create class z (i int, xs sequence(x)); select xs into p from z where i = 312; select s from
               * table(p) as t(s); ---> at here, the second select statement is in here */
              elm = NULL;
            }
          break;

        default:
          elm = NULL;
          break;
        }       /* switch (spec->info.spec.derived_table_type) */

          /* dive into the derived-subquery */
          for (; elm; elm = elm->next)
        {
          elm = meth_translate_helper (parser, elm);
          if (!elm || pt_has_error (parser))
            {
              /* exit immediately */
              goto exit_on_error;
            }
        }
        }
    }

    default:
      break;
    }               /* switch (node->node_type) */

  /* METHOD TRANSLATE */
  /* put methods on the target entity spec's method list */
  info2.methods_to_translate = 0;
  info2.root = node;
  (void) parser_walk_tree (parser, node, NULL, NULL, meth_create_method_list, &info2);

  if (!info2.methods_to_translate)
    {               /* not found method, do nothing */
      return node;
    }

  while (info2.methods_to_translate)
    {
      /* translate statement */
      node = parser_walk_tree (parser, node, NULL, NULL, meth_translate_local, node);
      /* error check */
      if (pt_has_error (parser))
    {           /* exit immediately */
      goto exit_on_error;
    }

      /* Recalculate method_lists for nested method calls */
      info2.methods_to_translate = 0;
      info2.root = node;
      (void) parser_walk_tree (parser, node, NULL, NULL, meth_create_method_list, &info2);
    }               /* while (info2.methods_to_translate) */

  /* collapse unnecessary SELECT/MERGE combinations */
  node = parser_walk_tree (parser, node, NULL, NULL, meth_collapse_nodes, NULL);

  return node;

exit_on_error:

  return NULL;
}


/*
 * meth_translate_local() -
 *   return:
 *   parser(in):
 *   statement(in/out): statement to translate
 *   void_arg(in): a PT_NODE that is the root of the whole statement
 *   continue_walk(in/out):
 */
static PT_NODE *
meth_translate_local (PARSER_CONTEXT * parser, PT_NODE * statement, void *void_arg, int *continue_walk)
{
  PT_NODE *root = (PT_NODE *) void_arg;
  int line, column;
  PT_NODE *save_statement;

  /* we only translate SELECTS */
  if (!statement || (statement->node_type != PT_SELECT))
    {
      return statement;
    }

  /* try to track original source line and column */
  line = statement->line_number;
  column = statement->column_number;
  save_statement = statement;

  statement = meth_translate_select (parser, statement, root);

  if (statement == NULL || pt_has_error (parser))
    {
      statement = save_statement;   /* restore to old parse tree */
      *continue_walk = PT_STOP_WALK;
    }
  else
    {
      statement->line_number = line;
      statement->column_number = column;
    }

  return statement;
}


/*
 * meth_create_method_list() - Put method calls on the method_list for
 *                     the entity their target resolved to
 *   return:
 *   parser(in):
 *   node(in/out):
 *   void_arg(in):
 *   continue_walk(in/out):
 */
static PT_NODE *
meth_create_method_list (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
  METH_INFO2 *info2 = (METH_INFO2 *) void_arg;
  PT_NODE *new_method;
  PT_NODE *arg1;
  METH_INFO info;
  int nested_methods;
  METH_INFO3 info3;

  *continue_walk = PT_CONTINUE_WALK;

  /* don't walk CSELECT lists */
  if (node->node_type == PT_SPEC && node->info.spec.derived_table
      && node->info.spec.derived_table_type == PT_IS_CSELECT)
    {
      *continue_walk = PT_LIST_WALK;
    }

  if (node->node_type == PT_DOT_ && (arg1 = node->info.dot.arg1) && PT_IS_METHOD (arg1)
      && arg1->info.method_call.call_or_expr == PT_PARAMETER && node->info.dot.arg2)
    {
      /* this is a path expression rooted in a constant method call. We need to tag it as such for xasl generation */
      node->info.dot.arg2->info.name.meta_class = PT_PARAMETER;
    }

  if (!PT_IS_METHOD (node) || (node->info.method_call.method_name->info.name.spec_id == 0))
    {
      return node;
    }


  /* check for nested method calls */
  nested_methods = 0;
  parser_walk_leaves (parser, node, meth_is_method, &nested_methods, NULL, NULL);

  if (nested_methods)
    {
      return node;
    }

  new_method = parser_copy_tree (parser, node);
  if (new_method == NULL)
    {
      return NULL;
    }

  /* don't keep finding this method, since we are copying it */
  new_method->info.method_call.method_name->info.name.spec_id = 0;

  info3.entity = NULL;
  info3.id = node->info.method_call.method_name->info.name.spec_id;
  (void) parser_walk_tree (parser, info2->root, meth_find_entity, &info3, NULL, NULL);

  info.method = new_method;
  info.entity_for_method = NULL;
  info.nesting_depth = 0;
  info.entities_nesting_depth = 0;
  (void) parser_walk_tree (parser, info2->root, meth_find_last_entity, &info, meth_find_last_entity_post, &info);

  if (!info.entity_for_method)
    {
      /* This case can arise when the target of the method call is a parameter and the arg_list contains PT_VALUE
       * and/or parameter nodes.  In this case use the entity the method name resolves to for the method. */
      info.entity_for_method = info3.entity;
    }

  if (!info.entity_for_method)
    {
      node->info.method_call.call_or_expr = PT_PARAMETER;
      if (new_method)
    {
      parser_free_tree (parser, new_method);
    }
    }
  else
    {
      info2->methods_to_translate = 1;  /* we found at least one */

      info.entity_for_method->info.spec.method_list =
    parser_append_node (new_method, info.entity_for_method->info.spec.method_list);
    }

  return node;
}


/*
 * meth_translate_select() - Translate the select statement with method calls
 *   return:
 *   parser(in):
 *   select_statement(in/out): select statement to translate
 *   root(in): top node of the whole statement
 */
static PT_NODE *
meth_translate_select (PARSER_CONTEXT * parser, PT_NODE * select_statement, PT_NODE * root)
{
  METH_STMT_INFO info;
  PT_NODE *save_from;

  info.root = root;
  info.select_statement = select_statement;

  /* translate any entity spec with method calls to a MERGE */
  save_from = select_statement->info.query.q.select.from;   /* save */
  select_statement->info.query.q.select.from =
    parser_walk_tree (parser, select_statement->info.query.q.select.from, NULL, NULL, meth_translate_spec, &info);

  if (pt_has_error (parser))
    {
      /* restore to old parse tree */
      select_statement->info.query.q.select.from = save_from;
      return NULL;
    }
  return select_statement;
}


/*
 * meth_translate_spec() - Replaces entity specs whose objects are the
 *                         targets for method calls with a MERGE statement
 *   return:
 *   parser(in):
 *   spec(in/out): entity spec to translate
 *   void_arg(in): root of the whole statement to allow us to replace
 *                 method calls with their new derived table attributes
 *   continue_walk(in/out): flag to control tree walking
 */
static PT_NODE *
meth_translate_spec (PARSER_CONTEXT * parser, PT_NODE * spec, void *void_arg, int *continue_walk)
{
  METH_STMT_INFO *info = (METH_STMT_INFO *) void_arg;
  PT_NODE *merge;
  PT_NODE *table1, *table2;
  PT_NODE *derived1;
  PT_NODE *new_spec;
  PT_NODE *tmp;
  PT_NODE *save_referenced_attrs;
  int i;
  int num_methods;
  int num_method_params = 0;
  int num_referenced_attrs;
  int num_hierarchical_exprs = 0;
  METH_INFO1 info1;
  METH_INFO6 info6;
  unsigned short derived1_correlation_level;
  unsigned short merge_correlation_level;
  unsigned short correlation_level;
  PT_NODE *dummy_set_tbl;
  PT_NODE *spec_list, **where;
  bool has_hierarchical_expr = false;

  info1.found = 0;
  *continue_walk = PT_LIST_WALK;

  if ((info->select_statement == NULL) || (spec->node_type != PT_SPEC) || (spec->info.spec.method_list == NULL))
    {
      return spec;      /* nothing to translate */
    }
  correlation_level = info->select_statement->info.query.correlation_level;
  spec_list = info->select_statement->info.query.q.select.from;
  where = &info->select_statement->info.query.q.select.where;

  /* squirrel away the referenced_attrs from the original tree */
  save_referenced_attrs = mq_get_references (parser, info->root, spec);

  /* check method calls for hierarchical expressions in arguments */
  meth_find_hierarchical_in_method_list (parser, spec->info.spec.method_list, &has_hierarchical_expr);

  num_methods = pt_length_of_list (spec->info.spec.method_list);
  num_referenced_attrs = pt_length_of_list (save_referenced_attrs);

  dummy_set_tbl = NULL;     /* init */

  /* newly create additional dummy_set_tbl as derived1 for instance method and stored precdure. check for path-expr. */
  if (spec->info.spec.meta_class == PT_CLASS && spec->info.spec.path_entities == NULL && !has_hierarchical_expr)
    {               /* can't handle path-expr */
      DB_VALUE val;
      PT_NODE *arg, *set;

      /* not derived-table spec and not meta class spec */
      db_make_int (&val, true);
      arg = pt_dbval_to_value (parser, &val);

      set = parser_new_node (parser, PT_FUNCTION);
      if (set == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      return NULL;
    }

      set->info.function.function_type = F_SEQUENCE;
      set->info.function.arg_list = arg;
      set->type_enum = PT_TYPE_SEQUENCE;

      dummy_set_tbl = parser_new_node (parser, PT_SPEC);
      if (dummy_set_tbl == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      return NULL;
    }

      dummy_set_tbl->info.spec.id = (UINTPTR) dummy_set_tbl;    /* set id */
      dummy_set_tbl->info.spec.derived_table = set;
      dummy_set_tbl->info.spec.derived_table_type = PT_IS_SET_EXPR;
      dummy_set_tbl->info.spec.range_var = meth_make_unique_range_var (parser, dummy_set_tbl);
      dummy_set_tbl->info.spec.as_attr_list =
    meth_gen_as_attr_list (parser, dummy_set_tbl->info.spec.range_var, dummy_set_tbl->info.spec.id, arg);
    }
  else
    {
      /* check for outside references for correlation level determination */
      info1.id = spec->info.spec.id;
      info1.found = 0;
      for (tmp = spec->info.spec.method_list; tmp != NULL; tmp = tmp->next)
    {
      (void) parser_walk_tree (parser, tmp->info.method_call.arg_list, meth_find_outside_refs, &info1, NULL, NULL);
      if (info1.found)
        {
          break;
        }
      (void) parser_walk_tree (parser, tmp->info.method_call.on_call_target, meth_find_outside_refs, &info1, NULL,
                   NULL);
      if (info1.found)
        {
          break;
        }
    }
    }

  /* create and fill in table2 of the merge */
  table2 = parser_new_node (parser, PT_SPEC);
  if (table2 == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      return NULL;
    }

  table2->info.spec.id = (UINTPTR) table2;
  table2->info.spec.derived_table = spec->info.spec.method_list;
  spec->info.spec.method_list = NULL;   /* take it out of main tree */
  table2->info.spec.range_var = meth_make_unique_range_var (parser, table2);
  table2->info.spec.as_attr_list =
    meth_gen_as_attr_list (parser, table2->info.spec.range_var, table2->info.spec.id, table2->info.spec.derived_table);
  table2->info.spec.referenced_attrs = parser_copy_tree_list (parser, table2->info.spec.as_attr_list);
  for (tmp = table2->info.spec.as_attr_list; tmp != NULL; tmp = tmp->next)
    {
      tmp->info.name.resolved = NULL;
    }
  table2->info.spec.derived_table_type = PT_IS_CSELECT;

  /* create and fill in the innermost derived statement */
  derived1 = parser_new_node (parser, PT_SELECT);
  if (derived1 == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      return NULL;
    }

  derived1->info.query.q.select.flavor = PT_USER_SELECT;
  derived1->info.query.is_subquery = PT_IS_SUBQUERY;

  if (dummy_set_tbl)
    {               /* now, generate new spec */
      derived1->info.query.q.select.from = dummy_set_tbl;

      derived1_correlation_level = 2;
    }
  else
    {
      derived1->info.query.q.select.from = parser_copy_tree (parser, spec);

      if (info1.found)
    {           /* found outside references */
      derived1_correlation_level = 2;
    }
      else
    {
      PT_NODE *sub_der;
      unsigned short sub_corr_level = 0;    /* init */

      sub_der = spec->info.spec.derived_table;
      if (sub_der != NULL)
        {
          switch (spec->info.spec.derived_table_type)
        {
        case PT_IS_SUBQUERY:
          sub_corr_level = sub_der->info.query.correlation_level;
          break;
        case PT_IS_SET_EXPR:
          /* KLUDGE ALERT!!! Until we have correlation level info for set expression derived tables, we can
           * check for no correlation, and correlation to this level. Correlation to outer scopes we can't
           * handle. */
          if (sub_der->node_type == PT_SELECT)
            {
              sub_corr_level = sub_der->info.query.correlation_level;
            }
          else if (meth_refs_to_scope (parser, spec_list, sub_der))
            {
              sub_corr_level = 1;
            }
          else
            {       /* check for outside refs */
              METH_INFO4 info4;

              info4.found = 0;
              info4.id = spec->info.spec.id;
              info4.spec_list = spec_list;
              (void) parser_walk_tree (parser, sub_der, meth_find_outside_refs_subquery, &info4, NULL, NULL);
              if (info4.found)
            {
              /* raise error -- can't currently deal with this */
              PT_ERRORm (parser, spec, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_METH_CORR_SET_EXPR);

              parser_free_tree (parser, derived1);
              parser_free_tree (parser, table2);
              parser_free_tree (parser, save_referenced_attrs);

              *continue_walk = PT_STOP_WALK;
              return spec;
            }
            }
          break;
        default:
          break;
        }
        }

      if (sub_corr_level)
        {
          derived1_correlation_level = sub_corr_level + 1;
        }
      else
        {
          derived1_correlation_level = correlation_level;
        }
    }
    }
  merge_correlation_level = MAX (derived1_correlation_level - 1, 0);

  /* remove un-replaced outer join relation; recover later */
  derived1->info.query.q.select.from->info.spec.join_type = PT_JOIN_NONE;
  if (derived1->info.query.q.select.from->info.spec.on_cond)
    {
      parser_free_tree (parser, derived1->info.query.q.select.from->info.spec.on_cond);
      derived1->info.query.q.select.from->info.spec.on_cond = NULL;
    }
  derived1->info.query.q.select.from->info.spec.method_list = NULL;
  derived1->info.query.q.select.list =
    meth_get_method_params (parser, spec->info.spec.id, table2->info.spec.derived_table, &num_method_params);
  if (has_hierarchical_expr)
    {
      /* copy all hierarchical expressions from select list to derived */
      meth_copy_hierarchical_expr_to_list (parser, info->select_statement->info.query.q.select.list,
                       &derived1->info.query.q.select.list, &num_hierarchical_exprs);
    }
  derived1->info.query.q.select.list =
    parser_append_node (parser_copy_tree_list (parser, save_referenced_attrs), derived1->info.query.q.select.list);
  derived1->info.query.q.select.from->info.spec.path_entities =
    meth_non_method_path_entities (parser, spec->info.spec.path_entities);
  derived1->info.query.correlation_level = derived1_correlation_level;
  if (has_hierarchical_expr)
    {
      /* move hierarchical query to derived */
      meth_move_hierarchical_to_derived (parser, &info->select_statement->info.query.q.select,
                     &derived1->info.query.q.select);
    }

  /* create and fill in table1 of the merge */
  table1 = parser_new_node (parser, PT_SPEC);
  if (table1 == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      return NULL;
    }

  table1->next = table2;
  table1->info.spec.id = (UINTPTR) table1;
  table1->info.spec.derived_table = derived1;
  table1->info.spec.range_var = meth_make_unique_range_var (parser, table1);
  table1->info.spec.as_attr_list =
    meth_gen_as_attr_list (parser, table1->info.spec.range_var, table1->info.spec.id,
               derived1->info.query.q.select.list);
  table1->info.spec.referenced_attrs = parser_copy_tree_list (parser, table1->info.spec.as_attr_list);
  for (tmp = table1->info.spec.as_attr_list; tmp != NULL; tmp = tmp->next)
    {
      tmp->info.name.resolved = NULL;
    }
  table1->info.spec.derived_table_type = PT_IS_SUBQUERY;

  /* If original spec was a derived table, we need to adjust correlations of nested queries.  Also need to adjust
   * correlations of queries that were subqueries of the method.  These are found on the select list of derived1. */
  if (dummy_set_tbl == NULL && derived1_correlation_level)
    {
      meth_bump_correlation_level (parser, derived1->info.query.q.select.from->info.spec.derived_table,
                   derived1_correlation_level - correlation_level, 1, spec->info.spec.id);
      meth_bump_correlation_level (parser, derived1->info.query.q.select.list,
                   derived1_correlation_level - correlation_level, 1, spec->info.spec.id);
    }

  /* create and fill in the merge node */
  merge = parser_new_node (parser, PT_SELECT);
  if (merge == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      return NULL;
    }

  merge->info.query.q.select.flavor = PT_MERGE_SELECT;
  merge->info.query.correlation_level = merge_correlation_level;
  merge->info.query.is_subquery = PT_IS_SUBQUERY;
  merge->info.query.q.select.from = table1;

  merge->info.query.q.select.list = parser_copy_tree_list (parser, table2->info.spec.referenced_attrs);
  tmp = table1->info.spec.referenced_attrs;
  for (i = 0; i < num_method_params; i++)
    {
      tmp = tmp->next;      /* skip params */
    }

  merge->info.query.q.select.list =
    parser_append_node (parser_copy_tree_list (parser, tmp), merge->info.query.q.select.list);
  merge->type_enum = merge->info.query.q.select.list->type_enum;
  if (merge->info.query.q.select.list->data_type)
    {
      merge->data_type = parser_copy_tree_list (parser, merge->info.query.q.select.list->data_type);
    }

  /* create and fill in the new_spec */
  new_spec = parser_new_node (parser, PT_SPEC);
  if (new_spec == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      return NULL;
    }

  new_spec->next = spec->next;  /* don't loose the list */
  new_spec->info.spec.id = (UINTPTR) new_spec;
  new_spec->info.spec.derived_table = merge;
  new_spec->info.spec.range_var = meth_make_unique_range_var (parser, new_spec);
  new_spec->info.spec.as_attr_list =
    meth_gen_as_attr_list (parser, new_spec->info.spec.range_var, new_spec->info.spec.id,
               merge->info.query.q.select.list);
  new_spec->info.spec.referenced_attrs = parser_copy_tree_list (parser, new_spec->info.spec.as_attr_list);
  for (tmp = new_spec->info.spec.as_attr_list; tmp != NULL; tmp = tmp->next)
    {
      tmp->info.name.resolved = NULL;
    }
  new_spec->info.spec.derived_table_type = PT_IS_SUBQUERY;
  new_spec->info.spec.path_entities = meth_non_method_path_entities (parser, spec->info.spec.path_entities);
  new_spec->info.spec.path_entities =
    parser_append_node (meth_method_path_entities (parser, spec->info.spec.path_entities),
            new_spec->info.spec.path_entities);

  /* replace the non-PT_NAME parameters of the method calls with the derived attributes from the first table of the
   * merge.  We use the referenced_attrs list instead of the as_attr_list because it has the resolved field filled in. */
  meth_replace_method_params (parser, spec->info.spec.id, table2->info.spec.derived_table,
                  table1->info.spec.referenced_attrs);

  /* replace the PT_NAME parameters of the method calls with the derived attributes from the first table of the merge.
   * We use the referenced attrs list instead of the as_attr_list because it has the resolved field filled in.  We must
   * first skip over the non-PT_NAME parameters. */
  tmp = table1->info.spec.referenced_attrs;
  for (i = 0; i < num_method_params + num_hierarchical_exprs; i++)
    {
      tmp = tmp->next;
    }
  meth_replace_referenced_attrs (parser, table2->info.spec.derived_table, save_referenced_attrs, tmp,
                 num_referenced_attrs);

  /* replace all method calls from this spec in the original statement with their new derived attributes which are
   * first on the new_spec's as_attr_list.  We use the referenced_attrs list instead of the as_attr_list because it has
   * the resolved field filled in.
   *
   * * Since new_spec->info.spec.path_entities is a copy of the original spec's, we also need to replace any method
   * calls in this tree. */
  meth_replace_method_calls (parser, info->root, table2->info.spec.derived_table, new_spec->info.spec.referenced_attrs,
                 new_spec, num_methods);
  /* replace hierarchical exprs in select list with derived attrs */
  if (has_hierarchical_expr)
    {
      meth_replace_hierarchical_exprs (parser, &info->select_statement->info.query.q.select.list,
                       new_spec->info.spec.referenced_attrs, num_methods);
    }
  meth_replace_method_calls (parser, new_spec->info.spec.path_entities, table2->info.spec.derived_table,
                 new_spec->info.spec.referenced_attrs, new_spec, num_methods);

  if (dummy_set_tbl == NULL)
    {
      /* after all methods are replaced, but before we replace references to the old spec, push the conjuncts for this
       * spec down. */
      derived1->info.query.q.select.where = meth_push_conjuncts (parser, spec->info.spec.id, where);
    }

  /* now that we've finished copy stuff to the derived table, reset ids */
  derived1 = mq_reset_paths (parser, derived1, derived1->info.query.q.select.from);

  /* replace references to the old spec's referenced_attr list with derived attrs from the new_spec's as_attr_list.  We
   * need to skip over derived attrs for the method calls.  Again, we use the referenced_attrs list instead of the
   * as_attr_list because it has the resolved field filled in.
   *
   * * Since table2 and new_spec path_entities are a copy of the original spec's, we also need to replace any
   * referenced attrs in these trees. */
  tmp = new_spec->info.spec.referenced_attrs;
  for (i = 0; i < num_methods + num_hierarchical_exprs; i++)
    {
      tmp = tmp->next;
    }
  if (dummy_set_tbl == NULL)
    {
      meth_replace_referenced_attrs (parser, info->root, save_referenced_attrs, tmp, num_referenced_attrs);
    }
  meth_replace_referenced_attrs (parser, table2->info.spec.path_entities, save_referenced_attrs, tmp,
                 num_referenced_attrs);
  meth_replace_referenced_attrs (parser, new_spec->info.spec.path_entities, save_referenced_attrs, tmp,
                 num_referenced_attrs);

  /* By convention, references are now computed as needed, to allow transformations and optimizations to proceed
   * without the restriction of constantly maintaining the referenced attributes lists. */
  parser_free_tree (parser, new_spec->info.spec.referenced_attrs);
  new_spec->info.spec.referenced_attrs = NULL;
  parser_free_tree (parser, table1->info.spec.referenced_attrs);
  table1->info.spec.referenced_attrs = NULL;
  parser_free_tree (parser, table2->info.spec.referenced_attrs);
  table2->info.spec.referenced_attrs = NULL;

  /* re-resolve methods whose method name node resolves to the translated spec, but which didn't get translated this
   * time.  The only case where this can happen is when there is a method that doesn't reference any spec (it is a
   * constant for the query), that has nested method calls.  These methods should now be resolved to new_spec. */
  info6.old_id = spec->info.spec.id;
  info6.new_id = new_spec->info.spec.id;
  parser_walk_leaves (parser, info->root, meth_replace_id_in_method_names, &info6, NULL, NULL);

  if (dummy_set_tbl)
    {
      /* We attach the new_spec to the end of the from list. */
      new_spec->next = NULL;
      for (tmp = spec; tmp->next != NULL; tmp = tmp->next)
    {
      /* No body. */
    }

      tmp->next = new_spec;
      new_spec = spec;
    }
  else
    {
      /* recover replaced outer join relation */
      new_spec->info.spec.join_type = spec->info.spec.join_type;
      spec->info.spec.join_type = PT_JOIN_NONE;
      new_spec->info.spec.on_cond = spec->info.spec.on_cond;
      spec->info.spec.on_cond = NULL;
      new_spec->info.spec.location = spec->info.spec.location;
    }

  parser_free_tree (parser, save_referenced_attrs);

  return new_spec;      /* replace the spec with the new_spec */
}


/*
 * meth_collapse_nodes() -
 *   return:
 *   parser(in):
 *   node(in/out):
 *   void_arg(in):
 *   continue_walk(in):
 */
static PT_NODE *
meth_collapse_nodes (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
  PT_NODE *merge;
  PT_NODE *as_attrs, *sel_attrs;
  int num;

  if ((node->node_type != PT_SELECT) || !node->info.query.q.select.from
      || !(merge = node->info.query.q.select.from->info.spec.derived_table)
      || (node->info.query.q.select.from->info.spec.derived_table_type != PT_IS_SUBQUERY)
      || (merge->info.query.q.select.flavor != PT_MERGE_SELECT) || node->info.query.q.select.from->next)
    {
      return node;
    }

  /* if we get here its a collapsible node, so collapse it reduce the correlation level of subqueries */
  meth_bump_correlation_level (parser, merge->info.query.q.select.from, -1, 1,
                   node->info.query.q.select.from->info.spec.id);

  /* move any path entity from the MERGE spec to the CSELECT spec */
  merge->info.query.q.select.from->next->info.spec.path_entities =
    parser_append_node (node->info.query.q.select.from->info.spec.path_entities,
            merge->info.query.q.select.from->next->info.spec.path_entities);

  /* Replace attributes that reference the derived table with the corresponding derived table's select list attribute.
   * Don't forget about the path conjuncts in the path entities. */
  as_attrs = node->info.query.q.select.from->info.spec.as_attr_list;
  sel_attrs = merge->info.query.q.select.list;
  num = pt_length_of_list (as_attrs);
  node->info.query.q.select.from = NULL;
  meth_replace_referenced_attrs (parser, node, as_attrs, sel_attrs, num);
  meth_replace_referenced_attrs (parser, merge->info.query.q.select.from->next->info.spec.path_entities, as_attrs,
                 sel_attrs, num);

  node->info.query.q.select.from = merge->info.query.q.select.from;
  node->info.query.q.select.where =
    meth_add_conj (parser, node->info.query.q.select.where, merge->info.query.q.select.where);
  node->info.query.q.select.flavor = PT_MERGE_SELECT;

  /* redo cnf, since collapsing may have munged the conjunct tags */
  node = pt_do_cnf (parser, node, void_arg, continue_walk);

  return node;
}


/*
 * meth_get_method_params() - return a list of their parameters and targets
 *   return:
 *   parser(in):
 *   spec_id(in): entity spec id of the entity we're replacing
 *   method_list(out): list of methods from which to get their parameters
 *   num(out): number of method parameters that we are adding to the list
 */
static PT_NODE *
meth_get_method_params (PARSER_CONTEXT * parser, UINTPTR spec_id, PT_NODE * method_list, int *num)
{
  PT_NODE *params = NULL;
  PT_NODE *arg = NULL;
  PT_NODE *method;

  if (!method_list)
    {
      return NULL;
    }

  for (method = method_list; method != NULL; method = method->next)
    {
      if (!PT_IS_METHOD (method))
    {
      PT_INTERNAL_ERROR (parser, "translate");
      return NULL;
    }
      for (arg = method->info.method_call.arg_list; arg != NULL; arg = arg->next)
    {
      if ((arg->node_type != PT_NAME) || (arg->info.name.spec_id != spec_id))
        {
          params = parser_append_node (parser_copy_tree (parser, arg), params);
        }
    }

      /* don't forget the method's target */
      if (method->info.method_call.on_call_target != NULL
      && ((method->info.method_call.on_call_target->node_type != PT_NAME)
          || (method->info.method_call.on_call_target->info.name.spec_id != spec_id)))
    {
      params = parser_append_node (parser_copy_tree (parser, method->info.method_call.on_call_target), params);
    }
    }

  *num = pt_length_of_list (params);    /* set this for calling routine */
  return params;
}


/*
 * meth_make_unique_range_var() - Create a new range variable with a unique name
 *   return:
 *   parser(in):
 *   spec(in): corresponding spec node
 */
static PT_NODE *
meth_make_unique_range_var (PARSER_CONTEXT * parser, PT_NODE * spec)
{
  PT_NODE *node = parser_new_node (parser, PT_NAME);
  if (node == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      return NULL;
    }

  node->info.name.original = mq_generate_name (parser, "t", &meth_table_number);
  node->info.name.meta_class = PT_CLASS;
  node->info.name.spec_id = spec->info.spec.id;

  return node;
}


/*
 * meth_gen_as_attr_list() - Create an as_attr_list from the list of
 *                           attributes for the given spec_id and range_var
 *   return:
 *   parser(in):
 *   range_var(in): range var of the entity for the resolution of the node
 *   spec_id(in): entity spec id for the resolution of the node
 *   attr_list(in): list of attributes for the as_attr_list
 */
static PT_NODE *
meth_gen_as_attr_list (PARSER_CONTEXT * parser, PT_NODE * range_var, UINTPTR spec_id, PT_NODE * attr_list)
{
  PT_NODE *node_list = NULL;
  PT_NODE *attr, *new_attr;

  for (attr = attr_list; attr != NULL; attr = attr->next)
    {
      new_attr = parser_new_node (parser, PT_NAME);
      if (new_attr == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      return NULL;
    }

      new_attr->type_enum = attr->type_enum;
      if (attr->data_type)
    {
      new_attr->data_type = parser_copy_tree (parser, attr->data_type);
    }
      new_attr->info.name.original = mq_generate_name (parser, "c", &meth_attr_number);
      new_attr->info.name.resolved = range_var->info.name.original;
      new_attr->info.name.spec_id = spec_id;
      new_attr->info.name.meta_class = PT_NORMAL;
      node_list = parser_append_node (new_attr, node_list);
    }

  return node_list;
}


/*
 * meth_replace_method_params() -
 *   return:
 *   parser(in):
 *   spec_id(in): current spec_id
 *   method_list(in): methods whose parameters need to be replaced
 *   as_attr_list(in): list of replacement nodes for the parameters
 */
static void
meth_replace_method_params (PARSER_CONTEXT * parser, UINTPTR spec_id, PT_NODE * method_list, PT_NODE * as_attr_list)
{
  PT_NODE *arg;
  PT_NODE *method;
  PT_NODE *prev_node, *node_next, *tmp;
  PT_NODE *attr_list;

  if (!method_list)
    {
      PT_INTERNAL_ERROR (parser, "translate");  /* life is really screwed up */
      return;
    }

  attr_list = as_attr_list;
  for (method = method_list; method != NULL; method = method->next)
    {
      if (!PT_IS_METHOD (method))
    {
      PT_INTERNAL_ERROR (parser, "translate");
      return;
    }
      prev_node = NULL;
      for (arg = method->info.method_call.arg_list, node_next = arg ? arg->next : NULL; arg != NULL;
       arg = node_next, node_next = arg ? arg->next : NULL)
    {
      if ((arg->node_type != PT_NAME) || (arg->info.name.spec_id != spec_id))
        {
          /* replace with copy of next node on as_attr_list */
          tmp = parser_copy_tree (parser, attr_list);
          if (tmp == NULL)
        {
          PT_INTERNAL_ERROR (parser, "parser_copy_tree");
          return;
        }

          tmp->next = arg->next;
          if (!prev_node)
        {
          /* this is the first parameter */
          method->info.method_call.arg_list = tmp;
        }
          else
        {
          prev_node->next = tmp;
        }
          attr_list = attr_list->next;
          /* could (should?) recover param here */

          prev_node = tmp;
        }
      else
        {
          prev_node = arg;
        }
    }

      /* don't forget the method's target */
      if (method->info.method_call.on_call_target != NULL
      && ((method->info.method_call.on_call_target->node_type != PT_NAME)
          || (method->info.method_call.on_call_target->info.name.spec_id != spec_id)))
    {
      /* replace with copy of next node on as_attr_list */
      tmp = parser_copy_tree (parser, attr_list);
      method->info.method_call.on_call_target = tmp;
      attr_list = attr_list->next;
      /* could (should?) recover old target here */
    }
    }
}


/*
 * meth_replace_method_calls() - Replace all method calls to methods in the
 *      method_list in the root statement with their derived table attribute
 *   return: none
 *   parser(in):
 *   root(in): root statement containing method calls
 *   method_list(in): list of method calls to replace in the root statement
 *   as_attr_list(in): list of replacement nodes (PT_NAME) to replace the
 *                    method calls with
 *   new_spec(in): the spec that was generated for the method call
 *   num_methods(in): number of original method calls used as a sanity check
 */
static void
meth_replace_method_calls (PARSER_CONTEXT * parser, PT_NODE * root, PT_NODE * method_list, PT_NODE * as_attr_list,
               PT_NODE * new_spec, int num_methods)
{
  PT_NODE *method;
  PT_NODE *attr_list;
  METH_LAMBDA lambda;

  if (num_methods != pt_length_of_list (method_list))
    {
      PT_INTERNAL_ERROR (parser, "translate");  /* life is really screwed up */
      return;
    }

  lambda.new_spec = new_spec;

  attr_list = as_attr_list;
  for (method = method_list; method != NULL; method = method->next)
    {
      if (!PT_IS_METHOD (method))
    {
      PT_INTERNAL_ERROR (parser, "translate");
      return;
    }
      lambda.method_id = method->info.method_call.method_id;
      lambda.replacement = attr_list;
      (void) parser_walk_tree (parser, root, meth_replace_call, &lambda, NULL, NULL);
      attr_list = attr_list->next;
    }
}


/*
 * meth_replace_call() - replaces a method call with the correct method id
 *                       with the replacement node
 *   return:
 *   parser(in):
 *   node(in):
 *   void_arg(in):
 *   continue_walk(in):
 */
static PT_NODE *
meth_replace_call (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
  METH_LAMBDA *lambda = (METH_LAMBDA *) void_arg;
  PT_NODE *new_node;
  METH_INFO1 info;

  if (!PT_IS_METHOD (node))
    {
      return node;
    }

  if (node->info.method_call.method_id == lambda->method_id)
    {
      /* now we have a method call to replace */
      new_node = parser_copy_tree (parser, lambda->replacement);
      if (new_node == NULL)
    {
      PT_INTERNAL_ERROR (parser, "parser_copy_tree");
      return NULL;
    }

      new_node->next = node->next;  /* don't loose the list */
      return new_node;
    }
  else
    {
      /* Check if the method we are replacing is a nested method call for the node.  If so, set the resolution of the
       * enclosing method call to the merge of the nested method_call. */
      info.id = lambda->method_id;
      info.found = 0;
      parser_walk_leaves (parser, node, meth_find_method, &info, NULL, NULL);

      if (info.found)
    {
      node->info.method_call.method_name->info.name.spec_id = lambda->new_spec->info.spec.id;
    }

      return node;
    }
}


/*
 * meth_replace_referenced_attrs() - Replace all attribute references in the
 *      attr_list in the root statement with their derived table attribute
 *   return: none
 *   parser(in):
 *   root(in): root statement which has referenced attrs to be replaced
 *   attr_list(in): list of attributes to be replaced
 *   as_attr_list(in): list of replacement nodes for the attrs
 *   num(in): number of original referenced attrs used as a sanity check
 */
static void
meth_replace_referenced_attrs (PARSER_CONTEXT * parser, PT_NODE * root, PT_NODE * attr_list, PT_NODE * as_attr_list,
                   int num)
{
  PT_NODE *attr;
  PT_NODE *as_attr;
  PT_NODE *next, *node_next;

  if ((num != pt_length_of_list (attr_list)) || (num != pt_length_of_list (as_attr_list)))
    {
      PT_INTERNAL_ERROR (parser, "translate");  /* life is really screwed up */
      return;
    }

  as_attr = as_attr_list;
  for (attr = attr_list, node_next = attr ? attr->next : NULL; attr != NULL;
       attr = node_next, node_next = attr ? attr->next : NULL)
    {
      if (attr->node_type != PT_NAME)
    {
      PT_INTERNAL_ERROR (parser, "translate");
      return;
    }

      /* save next link, pt_lambda will replace with whole as_attr list if we don't NULL the current next link. */
      next = as_attr->next;
      as_attr->next = NULL;

      root = pt_lambda (parser, root, attr, as_attr);

      /* restore next link */
      as_attr->next = next;
      as_attr = as_attr->next;
    }
}


/*
 * meth_find_last_entity() - See if entity is the resolution for any parameter
 *                           in the method
 *   return:
 *   parser(in):
 *   node(in):
 *   void_arg(in): METH_INFO
 *   continue_walk(in/out):
 */
static PT_NODE *
meth_find_last_entity (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
  METH_INFO *info = (METH_INFO *) void_arg;
  METH_INFO7 info7;
  PT_NODE *method = info->method;

  *continue_walk = PT_CONTINUE_WALK;

  if (node->node_type == PT_SELECT)
    {
      info->nesting_depth++;    /* going down */
    }

  /* don't walk into the method you're checking with */
  if (PT_IS_METHOD (node) && (node->info.method_call.method_id == info->method->info.method_call.method_id))
    {
      *continue_walk = PT_LIST_WALK;
    }

  if (node->node_type != PT_SPEC)
    {
      return node;
    }

  /* don't check if the nesting depth is less that the best entity so far */
  if (info->nesting_depth < info->entities_nesting_depth)
    {
      return node;
    }

  info7.id = node->info.spec.id;
  info7.found = 0;
  info7.check_method_calls = 0;
  parser_walk_leaves (parser, method, meth_match_entity, &info7, NULL, NULL);

  if (info7.found)
    {
      info->entity_for_method = node;
      info->entities_nesting_depth = info->nesting_depth;
    }

  /* don't walk down if this is a translated method call spec (a MERGE) */
  if (node->info.spec.derived_table && node->info.spec.derived_table_type == PT_IS_SUBQUERY
      && (node->info.spec.derived_table->info.query.q.select.flavor == PT_MERGE_SELECT))
    {
      *continue_walk = PT_LIST_WALK;
    }

  return node;
}


/*
 * meth_find_last_entity_post() - Maintain nesting depth level on the
 *                way back up the walk
 *   return:
 *   parser(in):
 *   node(in):
 *   void_arg(in):
 *   continue_walk(in):
 */
static PT_NODE *
meth_find_last_entity_post (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
  METH_INFO *info = (METH_INFO *) void_arg;

  if (node->node_type == PT_SELECT)
    {
      info->nesting_depth--;    /* going up */
    }

  return node;
}


/*
 * meth_match_entity() - See if this node resolves to the given entity spec id
 *   return:
 *   parser(in):
 *   node(in):
 *   void_arg(in): METH_INFO1
 *   continue_walk(in/out):
 */
static PT_NODE *
meth_match_entity (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
  METH_INFO7 *info7 = (METH_INFO7 *) void_arg;
  PT_NODE *root;

  *continue_walk = PT_CONTINUE_WALK;


  /* check to see if we want to dive into nested method calls. don't dive into data type nodes */
  if (!info7->check_method_calls && (PT_IS_METHOD (node) || (node->node_type == PT_DATA_TYPE)))
    {
      *continue_walk = PT_LIST_WALK;
      return node;
    }

  if (node->node_type == PT_DOT_)
    {
      for (root = node->info.dot.arg1; root->node_type == PT_DOT_; root = root->info.dot.arg1)
    {
      ;         /* purposely blank */
    }
      if (root->info.name.spec_id == info7->id)
    {
      info7->found = 1;
    }
      *continue_walk = PT_LIST_WALK;
    }

  if ((node->node_type == PT_NAME) && (node->info.name.spec_id == info7->id))
    {
      info7->found = 1;
    }

  return node;
}


/*
 * meth_find_outside_refs() - See if this node resolves to an entity spec
 *                            different than the one given
 *   return:
 *   parser(in):
 *   node(in):
 *   void_arg(in):
 *   continue_walk(in/out):
 */
static PT_NODE *
meth_find_outside_refs (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
  METH_INFO1 *info1 = (METH_INFO1 *) void_arg;
  PT_NODE *root;
  METH_INFO4 info4;

  *continue_walk = PT_CONTINUE_WALK;

  /* don't walk data_types */
  if (node->node_type == PT_DATA_TYPE)
    {
      *continue_walk = PT_LIST_WALK;
      return node;
    }

  /* check path expressions */
  if (node->node_type == PT_DOT_)
    {
      for (root = node->info.dot.arg1; root->node_type == PT_DOT_; root = root->info.dot.arg1)
    {
      ;         /* purposely blank */
    }
      *continue_walk = PT_LIST_WALK;
      if (root->node_type != PT_NAME || root->info.name.meta_class == PT_PARAMETER
      || root->info.name.meta_class == PT_META_CLASS || root->info.name.meta_class == PT_META_ATTR
      || root->info.name.meta_class == PT_METHOD)
    {
      /* do nothing */
    }
      else if (root->info.name.spec_id != info1->id)
    {
      info1->found = 1;
      /* immediately, stop walking */
      *continue_walk = PT_STOP_WALK;
    }
    }

  /* watch out for sub queries--we're only interested in references outside the scope of the sub query. */
  if (node->node_type == PT_SELECT)
    {
      if (node->info.query.correlation_level == 1)
    {
      /* it may only be correlated to the current spec--check */
      info4.found = 0;
      info4.id = info1->id;
      info4.spec_list = node->info.query.q.select.from;
      parser_walk_leaves (parser, node, meth_find_outside_refs_subquery, &info4, NULL, NULL);
      if (info4.found)
        {
          info1->found = 1;
          /* immediately, stop walking */
          *continue_walk = PT_STOP_WALK;
        }
    }
      else if (node->info.query.correlation_level != 0)
    {
      info1->found = 1;
      /* immediately, stop walking */
      *continue_walk = PT_STOP_WALK;
    }
      *continue_walk = PT_LIST_WALK;
    }

  /* watch out for parameters of type PT_OBJECT, we don't want to look at their datatype nodes. */
  if (node->node_type == PT_VALUE)
    {
      *continue_walk = PT_LIST_WALK;
    }

  /* watch out ROWNUM predicate */
  if (node->node_type == PT_EXPR)
    {
      switch (node->info.expr.op)
    {
    case PT_ROWNUM:
    case PT_INST_NUM:
      info1->found = 1;
      /* immediately, stop walking */
      *continue_walk = PT_STOP_WALK;
      break;

    default:
      break;
    }           /* switch */
    }               /* if (node->node_type == PT_EXPR) */

  if (node->node_type != PT_NAME)
    {
      return node;
    }

  *continue_walk = PT_LIST_WALK;

  /* Parameters are no longer bound at compilation time (they're not PT_VALUES they're still PT_NAME nodes. Also
   * parameters are not correlated to any outside scope so we can skip them. Also method names, or meta class
   * attributes are not correllated names. */
  if (node->info.name.meta_class == PT_PARAMETER || node->info.name.meta_class == PT_META_CLASS
      || node->info.name.meta_class == PT_META_ATTR || node->info.name.meta_class == PT_METHOD)
    {
      return node;
    }

  if (node->info.name.spec_id != info1->id)
    {
      info1->found = 1;
      /* immediately, stop walking */
      *continue_walk = PT_STOP_WALK;
    }

  return node;
}


/*
 * meth_bump_correlation_level() - Bump the correlation level of all
 *                                 correlated queries
 *   return:
 *   parser(in):
 *   node(in):
 *   increment(in): amount to bump correlation
 *   threshold(in): used to determine which correlations to bump
 *   spec_id(in): used to determine which correlations to bump
 */
static void
meth_bump_correlation_level (PARSER_CONTEXT * parser, PT_NODE * node, int increment, int threshold, UINTPTR spec_id)
{
  METH_CORR_INFO info;
  info.corr_step = increment;
  info.corr_threshold = threshold;
  info.spec_id = spec_id;

  (void) parser_walk_tree (parser, node, meth_bump_corr_pre, &info, meth_bump_corr_post, &info);
}


/*
 * meth_bump_corr_pre() - Bump the correlation level of all queries
 *   return:
 *   parser(in):
 *   node(in):
 *   void_arg(in):
 *   continue_walk(in/out):
 */
static PT_NODE *
meth_bump_corr_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
  METH_CORR_INFO *corr_info = (METH_CORR_INFO *) void_arg;
  METH_INFO1 info1;
  METH_INFO7 info7;
  PT_NODE *next;

  *continue_walk = PT_CONTINUE_WALK;

  if (!PT_IS_QUERY_NODE_TYPE (node->node_type))
    {
      return node;
    }

  /* Can not increment threshold for list portion of walk. Since those queries are not sub-queries of this query.
   * Consequently, we recurse separately for the list leading from a query. */
  if (node->next)
    {
      meth_bump_correlation_level (parser, node->next, corr_info->corr_step, corr_info->corr_threshold,
                   corr_info->spec_id);
    }

  *continue_walk = PT_LEAF_WALK;

  if (node->info.query.correlation_level != 0)
    {
      if (node->info.query.correlation_level == corr_info->corr_threshold)
    {
      if (corr_info->corr_step < 0)
        {
          /* always bump correlation_level if this is a collapse */
          node->info.query.correlation_level += corr_info->corr_step;
        }
      else
        {
          next = node->next;
          node->next = NULL;

          /* check for outside refs */
          info1.id = corr_info->spec_id;
          info1.found = 0;
          parser_walk_leaves (parser, node, meth_find_outside_refs, &info1, NULL, NULL);

          /* check if there are refs to the spec we are expanding */
          info7.id = corr_info->spec_id;
          info7.found = 0;
          info7.check_method_calls = 0;
          parser_walk_leaves (parser, node, meth_match_entity, &info7, NULL, NULL);

          /* bump correlation_level if there are outside refs and there are no refs to our spec. */
          if (info1.found && !info7.found)
        {
          node->info.query.correlation_level += corr_info->corr_step;
        }
          node->next = next;
        }
    }
      else if (node->info.query.correlation_level > corr_info->corr_threshold)
    node->info.query.correlation_level += corr_info->corr_step;
    }
  else
    {
      /* if the correlation level is 0, there cannot be correlated subqueries crossing this level */
      *continue_walk = PT_STOP_WALK;
    }

  /* increment threshold as we dive into subqueries */
  corr_info->corr_threshold++;

  return node;
}


/*
 * meth_bump_corr_post() - Decrement the corr_threshold on the way up
 *   return:
 *   parser(in):
 *   node(in):
 *   void_arg(in):
 *   continue_walk(in):
 */
static PT_NODE *
meth_bump_corr_post (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
  METH_CORR_INFO *corr_info = (METH_CORR_INFO *) void_arg;

  if (!PT_IS_QUERY_NODE_TYPE (node->node_type))
    {
      return node;
    }

  corr_info->corr_threshold--;

  return node;
}


/*
 * meth_find_merge() - Check if the node is a MERGE node
 *   return:
 *   parser(in):
 *   node(in):
 *   void_arg(out):
 *   continue_walk(in):
 */
static PT_NODE *
meth_find_merge (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
  int *hand_rewritten = (int *) void_arg;

  if (node->node_type == PT_SELECT && node->info.query.q.select.flavor == PT_MERGE_SELECT)
    {
      *hand_rewritten = 1;
    }

  return node;
}


/*
 * meth_is_method() - Is this node a method call
 *   return:
 *   parser(in):
 *   node(in):
 *   void_arg(out):
 *   continue_walk(in/out):
 */
static PT_NODE *
meth_is_method (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
  int *is_a_method = (int *) void_arg;

  *continue_walk = PT_CONTINUE_WALK;

  if (PT_IS_METHOD (node) && node->info.method_call.call_or_expr != PT_PARAMETER)
    {
      *is_a_method = 1;
    }

  /* we don't want to look for methods that have already been translated. they will be found in the leaves of merge
   * nodes. */
  if (node->node_type == PT_SELECT && node->info.query.q.select.flavor == PT_MERGE_SELECT)
    {
      *continue_walk = PT_LIST_WALK;
    }

  return node;
}


/*
 * meth_method_path_entities() - return a list of all special method path
 *                               entities in paths
 *   return:
 *   parser(in):
 *   paths(out): a list of path entities
 */
static PT_NODE *
meth_method_path_entities (PARSER_CONTEXT * parser, PT_NODE * paths)
{
  PT_NODE *path;
  PT_NODE *spec;
  PT_NODE *list = NULL;

  for (path = paths; path != NULL; path = path->next)
    {
      if (path->info.spec.flavor == PT_METHOD_ENTITY)
    {
      spec = parser_copy_tree (parser, path);

      /* as we move it up, it becomes a regular path entity */
      spec->info.spec.flavor = (PT_MISC_TYPE) 0;
      list = parser_append_node (spec, list);
    }
    }

  return list;
}

/*
 * meth_non_method_path_entities() - return a list of all path enties which
 *      are not special method path entities in paths
 *   return:
 *   parser(in):
 *   paths(out): a list of path entities
 */
static PT_NODE *
meth_non_method_path_entities (PARSER_CONTEXT * parser, PT_NODE * paths)
{
  PT_NODE *path;
  PT_NODE *spec;
  PT_NODE *list = NULL;

  for (path = paths; path != NULL; path = path->next)
    {
      if (path->info.spec.flavor != PT_METHOD_ENTITY)
    {
      spec = parser_copy_tree (parser, path);
      list = parser_append_node (spec, list);
    }
    }

  return list;
}


/*
 * meth_find_entity() - find entity that matches the given id
 *   return:
 *   parser(in):
 *   node(in):
 *   void_arg(in):
 *   continue_walk(in):
 */
static PT_NODE *
meth_find_entity (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
  METH_INFO3 *info3 = (METH_INFO3 *) void_arg;

  if (node->node_type == PT_SPEC && node->info.spec.id == info3->id)
    {
      info3->entity = node;
    }

  return node;
}


/*
 * meth_find_method() - Check if the given method is found
 *   return:
 *   parser(in):
 *   node(in):
 *   void_arg(in/out):
 *   continue_walk(in/out):
 *
 * Note:
 *  If the method_id == 0, match any method and look for methods in subqueries.
 *  If method_id != 0, we are looking for a specific method and do not look
 *  for that method in subqueries
 */
static PT_NODE *
meth_find_method (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
  METH_INFO1 *info = (METH_INFO1 *) void_arg;

  *continue_walk = PT_CONTINUE_WALK;

  if (PT_IS_METHOD (node) && (info->id == 0 || node->info.method_call.method_id == info->id))
    {
      info->found = 1;
    }

  /* prune walk at selects */
  if (node->node_type == PT_SELECT && info->id != 0)
    {
      *continue_walk = PT_LIST_WALK;
    }

  return node;
}


/*
 * meth_find_outside_refs_subquery() - Check if outside refs match given
 *                     entity spec id
 *   return:
 *   parser(in):
 *   node(in):
 *   void_arg(in/out):
 *   continue_walk(in/out):
 */
static PT_NODE *
meth_find_outside_refs_subquery (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
  METH_INFO4 *info4 = (METH_INFO4 *) void_arg;
  PT_NODE *root;

  *continue_walk = PT_CONTINUE_WALK;

  /* don't walk data_types */
  if (node->node_type == PT_DATA_TYPE)
    {
      *continue_walk = PT_LIST_WALK;
      return node;
    }

  /* check path expressions */
  if (node->node_type == PT_DOT_)
    {
      for (root = node->info.dot.arg1; root->node_type == PT_DOT_; root = root->info.dot.arg1)
    {
      ;         /* purposely blank */
    }
      *continue_walk = PT_LIST_WALK;
      if (!pt_find_entity (parser, info4->spec_list, root->info.name.spec_id) && root->info.name.spec_id != info4->id)
    {
      info4->found = 1;
      /* immediately, stop walking */
      *continue_walk = PT_STOP_WALK;
    }
    }

  if ((node->node_type == PT_SELECT) || (node->node_type == PT_VALUE))
    {
      *continue_walk = PT_LIST_WALK;
    }

  if (node->node_type != PT_NAME)
    {
      return node;
    }
  /* Parameters are no longer bound at compilation time (they're not PT_VALUES they're still PT_NAME nodes. Also
   * parameters are not correlated to any outside scope so we can skip them. */
  if (node->info.name.meta_class == PT_PARAMETER)
    {
      *continue_walk = PT_LIST_WALK;
      return node;
    }
  /* don't look at class attributes, their spec ids are not real */
  if (node->info.name.meta_class != PT_META_CLASS && node->info.name.meta_class != PT_META_ATTR
      && !pt_find_entity (parser, info4->spec_list, node->info.name.spec_id) && node->info.name.spec_id != info4->id)
    {
      info4->found = 1;
      /* immediately, stop walking */
      *continue_walk = PT_STOP_WALK;
    }

  return node;
}


/*
 * meth_push_conjuncts() -
 *   return:
 *   parser(in):
 *   spec_id(in): entity spec id of the conjuncts we want to push
 *   where(in): where clause that contains conjuncts that might be able
 *              to be pushed down
 */
static PT_NODE *
meth_push_conjuncts (PARSER_CONTEXT * parser, UINTPTR spec_id, PT_NODE ** where)
{
  METH_INFO5 info5;
  METH_INFO1 info1;
  int outside_refs, nested_meths;
  SEMANTIC_CHK_INFO sc_info = { NULL, NULL, 0, 0, 0, false, false };

  info5.new_where = NULL;
  info5.spec_id = spec_id;

  if (!(*where))
    {
      return NULL;      /* there are no conjuncts to push */
    }

  sc_info.top_node = *where;
  sc_info.donot_fold = false;

  if ((*where)->node_type == PT_EXPR && (*where)->info.expr.op == PT_AND)
    {
      *where = parser_walk_tree (parser, *where, NULL, NULL, meth_grab_conj, &info5);

      /* check top conjunct */
      if ((*where)->node_type == PT_EXPR && (*where)->info.expr.op != PT_AND && (*where)->spec_ident == spec_id)
    {
      /* we can't push if there are outside refs */
      info1.id = spec_id;
      info1.found = 0;
      (void) parser_walk_tree (parser, *where, meth_find_outside_refs, &info1, NULL, NULL);
      outside_refs = info1.found;

      /* we can't push if there are nested method calls */
      info1.id = 0;     /* match any method--even in nested subqueries */
      info1.found = 0;
      (void) parser_walk_tree (parser, *where, meth_find_method, &info1, NULL, NULL);
      nested_meths = info1.found;

      if (!outside_refs && !nested_meths)
        {
          info5.new_where = meth_add_conj (parser, info5.new_where, *where);
          *where = NULL;
        }
    }

      /* need to fold where clause since pushing conjuncts can introduce true nodes into the tree. */
      *where = pt_semantic_type (parser, *where, &sc_info);

      /* check if the where clause = true */
      *where = pt_where_type (parser, *where);
    }
  else
    {
      /* WHERE is cnf list */
      meth_grab_cnf_conj (parser, where, &info5);
    }

  return info5.new_where;
}


/*
 * meth_grab_conj() - Put conjuncts that match the given spec id on the
 *                    new_where clause and replace it in original tree
 *   return:
 *   parser(in):
 *   node(in):
 *   void_arg(in/out):
 *   continue_walk(in/out):
 */
static PT_NODE *
meth_grab_conj (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
  METH_INFO5 *info5 = (METH_INFO5 *) void_arg;
  PT_NODE *true_node;
  METH_INFO1 info1;
  int arg1_outside_refs, arg2_outside_refs;
  int arg1_nested_meths, arg2_nested_meths;

  *continue_walk = PT_CONTINUE_WALK;

  if ((node->node_type != PT_EXPR) || (node->info.expr.op != PT_AND))
    {
      return node;
    }

  /* we can't push if there are outside refs */
  info1.id = info5->spec_id;
  info1.found = 0;
  (void) parser_walk_tree (parser, node->info.expr.arg1, meth_find_outside_refs, &info1, NULL, NULL);
  arg1_outside_refs = info1.found;

  info1.id = info5->spec_id;
  info1.found = 0;
  (void) parser_walk_tree (parser, node->info.expr.arg2, meth_find_outside_refs, &info1, NULL, NULL);
  arg2_outside_refs = info1.found;

  /* we can't push if there are nested method calls */
  info1.id = 0;         /* match any method--even in nested subqueries */
  info1.found = 0;
  (void) parser_walk_tree (parser, node->info.expr.arg1, meth_find_method, &info1, NULL, NULL);
  arg1_nested_meths = info1.found;

  info1.id = 0;         /* match any method--even in nested subqueries */
  info1.found = 0;
  (void) parser_walk_tree (parser, node->info.expr.arg2, meth_find_method, &info1, NULL, NULL);
  arg2_nested_meths = info1.found;

  if (node->info.expr.arg1->spec_ident == info5->spec_id && node->info.expr.arg2->spec_ident == info5->spec_id
      && !arg1_outside_refs && !arg2_outside_refs && !arg1_nested_meths && !arg2_nested_meths)
    {
      info5->new_where = meth_add_conj (parser, info5->new_where, node->info.expr.arg1);
      info5->new_where = meth_add_conj (parser, info5->new_where, node->info.expr.arg2);

      /* create a true node to replace the current node */
      true_node = parser_new_node (parser, PT_VALUE);
      if (true_node == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      return NULL;
    }

      true_node->type_enum = PT_TYPE_LOGICAL;
      true_node->info.value.data_value.i = 1;

      return true_node;     /* AND node collapses */
    }

  if (node->info.expr.arg1->spec_ident == info5->spec_id && !arg1_outside_refs && !arg1_nested_meths)
    {
      info5->new_where = meth_add_conj (parser, info5->new_where, node->info.expr.arg1);

      return node->info.expr.arg2;  /* AND node collapses */
    }

  if (node->info.expr.arg2->spec_ident == info5->spec_id && !arg2_outside_refs && !arg2_nested_meths)
    {
      info5->new_where = meth_add_conj (parser, info5->new_where, node->info.expr.arg2);

      return node->info.expr.arg1;  /* AND node collapses */
    }

  return node;
}

/*
 * meth_grab_cnf_conj() - Put conjuncts that match the given spec id on
 *                        the new_where clause and replace it in original tree
 *   return:
 *   parser(in):
 *   where(in):
 *   info5(out):
 */
static void
meth_grab_cnf_conj (PARSER_CONTEXT * parser, PT_NODE ** where, METH_INFO5 * info5)
{
  PT_NODE *conj, *prev = NULL, *next;
  METH_INFO1 info1;
  int outside_refs;
  int nested_meths;

  conj = *where;
  while (conj)
    {
      next = conj->next;
      conj->next = NULL;    /* cut */

      /* we can't push if there are outside refs */
      info1.id = info5->spec_id;
      info1.found = 0;
      (void) parser_walk_tree (parser, conj, meth_find_outside_refs, &info1, NULL, NULL);
      outside_refs = info1.found;

      /* we can't push if there are nested method calls */
      info1.id = 0;     /* match any method - even in nested subqueries */
      info1.found = 0;
      (void) parser_walk_tree (parser, conj, meth_find_method, &info1, NULL, NULL);
      nested_meths = info1.found;

      if (!outside_refs && !nested_meths)
    {
      /* found pushable conjuncts */
      if (conj == *where)
        {           /* first node of cnf list */
          *where = next;    /* remove conj from where */
        }
      else
        {
          prev->next = next;    /* remove conj from where */
        }
      /* append conj to new_where */
      info5->new_where = parser_append_node (conj, info5->new_where);
    }
      else
    {
      conj->next = next;    /* restore next link */
      prev = conj;      /* save prev conj */
    }

      conj = next;
    }
}

/*
 * meth_add_conj() - add the conjunct to the where clause
 *   return:
 *   parser(in):
 *   where(in): where clause to add the conjunct to
 *   new_conj(in): conjunct to add to the where clause
 */
static PT_NODE *
meth_add_conj (PARSER_CONTEXT * parser, PT_NODE * where, PT_NODE * new_conj)
{
  PT_NODE *conj;

  if (where == NULL)
    {
      return new_conj;
    }
  else if (new_conj == NULL)
    {
      return where;
    }
  else
    {
      conj = parser_new_node (parser, PT_EXPR);
      if (conj == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      return NULL;
    }

      conj->type_enum = PT_TYPE_LOGICAL;
      conj->info.expr.op = PT_AND;
      conj->info.expr.arg1 = where;
      conj->info.expr.arg2 = new_conj;

      return conj;
    }
}


/*
 * meth_replace_id_in_method_names() - Re-resolve method name nodes that used
 *                                     to resolve to the old_id to the new_id
 *   return:
 *   parser(in):
 *   node(in):
 *   void_arg(in):
 *   continue_walk(in):
 */
static PT_NODE *
meth_replace_id_in_method_names (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
  METH_INFO6 *info6 = (METH_INFO6 *) void_arg;

  if (PT_IS_METHOD (node) && (node->info.method_call.method_name->info.name.spec_id == info6->old_id))
    {
      node->info.method_call.method_name->info.name.spec_id = info6->new_id;
    }

  return node;
}


/*
 * meth_refs_to_scope() - See if a name node resolves to the given scope
 *   return: 1 on found
 *   parser(in):
 *   scope(in): scope to check refs for
 *   tree(in): tree to check refs in
 */
static int
meth_refs_to_scope (PARSER_CONTEXT * parser, PT_NODE * scope, PT_NODE * tree)
{
  int found = 0;
  PT_NODE *spec;
  METH_INFO7 info7;

  for (spec = scope; spec != NULL; spec = spec->next)
    {
      info7.id = spec->info.spec.id;
      info7.found = 0;
      info7.check_method_calls = 1;
      tree = parser_walk_tree (parser, tree, meth_match_entity, &info7, NULL, NULL);

      if (info7.found)
    {
      found = 1;
    }
    }

  return found;
}

/*
 * meth_find_hierarchical_op() - Check expression tree for hierarchical op
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in/out):
 *   continue_walk(in):
 */
static PT_NODE *
meth_find_hierarchical_op (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  bool *is_hierarchical_op = (bool *) arg;

  if (node->node_type != PT_EXPR)
    {
      *continue_walk = PT_STOP_WALK;
    }
  else
    {
      if (PT_REQUIRES_HIERARCHICAL_QUERY (node->info.expr.op))
    {
      *is_hierarchical_op = true;
      *continue_walk = PT_STOP_WALK;
    }
    }

  return node;
}

/*
 * meth_find_hierarchical_in_method_list() - Check method list for hierarchical
 *                       expressions in arguments
 *   return:
 *   parser(in):
 *   method_list(in):
 *   has_hierarchical_expr(out):
 */
static void
meth_find_hierarchical_in_method_list (PARSER_CONTEXT * parser, PT_NODE * method_list, bool * has_hierarchical_expr)
{
  PT_NODE *node, *arg, *save_next;

  for (node = method_list; node != NULL && !(*has_hierarchical_expr); node = node->next)
    {
      if (PT_IS_METHOD (node))
    {
      for (arg = node->info.method_call.arg_list; arg != NULL && !(*has_hierarchical_expr); arg = arg->next)
        {
          save_next = arg->next;
          arg->next = NULL;
          (void) parser_walk_tree (parser, arg, meth_find_hierarchical_op, has_hierarchical_expr, NULL, NULL);
          arg->next = save_next;
        }
    }
    }
}

/*
 * meth_copy_hierarchical_expr_to_list() - Copy and hierarchical expressions
 *                     from source to destination list
 *   return:
 *   parser(in):
 *   src_list(in):
 *   dst_list(in):
 *   copy_count(in/out):
 */
static void
meth_copy_hierarchical_expr_to_list (PARSER_CONTEXT * parser, PT_NODE * src_list, PT_NODE ** dst_list_p,
                     int *copy_count)
{
  PT_NODE *node, *temp, *save_next;
  bool found, has_hierarchical_expr;

  for (node = src_list; node != NULL; node = node->next)
    {
      has_hierarchical_expr = false;
      save_next = node->next;
      node->next = NULL;
      (void) parser_walk_tree (parser, node, meth_find_hierarchical_op, &has_hierarchical_expr, NULL, NULL);
      node->next = save_next;
      if (has_hierarchical_expr)
    {
      /* don't copy if it's already there */
      found = false;
      for (temp = *dst_list_p; temp != NULL && !found; temp = temp->next)
        {
          if (node == temp)
        {
          found = true;
        }
        }
      if (!found)
        {
          *dst_list_p = parser_append_node (parser_copy_tree (parser, node), *dst_list_p);
          (*copy_count)++;
        }
    }
    }
}

/*
 * meth_move_hierarchical_to_derived() - Move hierarchical statement info to
 *                   derived
 *   return:
 *   parser(in):
 *   statement_info(in):
 *   derived_info(in):
 */
static void
meth_move_hierarchical_to_derived (PARSER_CONTEXT * parser, PT_SELECT_INFO * statement_info,
                   PT_SELECT_INFO * derived_info)
{
  /* copy predicates */
  derived_info->connect_by = parser_copy_tree_list (parser, statement_info->connect_by);
  derived_info->start_with = parser_copy_tree_list (parser, statement_info->start_with);
  derived_info->after_cb_filter = parser_copy_tree_list (parser, statement_info->after_cb_filter);

  /* copy flags */
  derived_info->check_cycles = statement_info->check_cycles;
  derived_info->single_table_opt = statement_info->single_table_opt;

  /* clear predicates */
  parser_free_tree (parser, statement_info->connect_by);
  parser_free_tree (parser, statement_info->start_with);
  parser_free_tree (parser, statement_info->after_cb_filter);
  statement_info->connect_by = NULL;
  statement_info->start_with = NULL;
  statement_info->after_cb_filter = NULL;

  /* reset flags */
  statement_info->check_cycles = CONNECT_BY_CYCLES_ERROR;
  statement_info->single_table_opt = 0;
}

/*
 * meth_replace_hierarchical_exprs() - Replace hierarchical exprs in list with
 *                     derived attrs
 *   return:
 *   parser(in):
 *   statement_info(in):
 *   derived_info(in):
 */
static void
meth_replace_hierarchical_exprs (PARSER_CONTEXT * parser, PT_NODE ** select_list_p, PT_NODE * ref_attrs,
                 int num_methods)
{
  PT_NODE *node, *prev_node, *save_next, *new_node;
  bool has_hierarchical_expr;
  int i;

  for (i = 0; i < num_methods; i++)
    {
      ref_attrs = ref_attrs->next;
    }

  for (node = *select_list_p, prev_node = NULL; node != NULL; node = node->next)
    {
      has_hierarchical_expr = false;
      save_next = node->next;
      node->next = NULL;
      (void) parser_walk_tree (parser, node, meth_find_hierarchical_op, &has_hierarchical_expr, NULL, NULL);
      node->next = save_next;
      if (has_hierarchical_expr)
    {
      new_node = parser_copy_tree (parser, ref_attrs);
      new_node->next = node->next;
      if (prev_node)
        {
          prev_node->next = new_node;
        }
      else
        {
          *select_list_p = new_node;
        }
      node->next = NULL;
      parser_free_tree (parser, node);
      node = new_node;
      ref_attrs = ref_attrs->next;
    }
      prev_node = node;
    }
}