File px_heap_scan_checker.cpp¶
File List > cubrid > src > query > parallel > px_heap_scan > px_heap_scan_checker.cpp
Go to the documentation of this file
/*
*
* 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.
*
*/
/*
* px_heap_scan_checker.cpp - module that checks whether parallel heap scan is possible.
*/
#include "px_heap_scan_checker.hpp"
#include "dbtype_def.h"
#include "regu_var.hpp"
#include "storage_common.h"
#include "xasl_predicate.hpp"
#include "xasl.h"
#include "xasl_aggregate.hpp"
#include <unordered_map>
#include <unordered_set>
// XXX: SHOULD BE THE LAST INCLUDE HEADER
#include "memory_wrapper.hpp"
namespace parallel_heap_scan
{
/*
* Parallel heap scan has two execution modes:
* 1. list_merge: Fast mode that merges sorted partial results from workers
* 2. row_by_row: Slower mode that processes rows individually, but covers all cases
*
* When list_merge is not possible, row_by_row is always available as a fallback.
*
* Buildvalue optimization (buildvalue_opt) can be applied when:
* - The select list contains only aggregate functions suitable for buildvalue optimization
* - list_merge mode: buildvalue_opt is always possible
* - row_by_row mode: buildvalue_opt may not be possible due to constraints like ROWNUM
*
* The BUILDVALUE_OPT_POSSIBLE flag specifically checks:
* 1. Whether the select list consists solely of suitable aggregate functions
* 2. Whether row_by_row constraints (e.g., ROWNUM usage) prevent buildvalue optimization
*/
using possible_flags = uint32_t;
const possible_flags CANNOT_PARALLEL_HEAP_SCAN = 0x1 << 0;
const possible_flags CANNOT_LIST_MERGE = 0x1 << 1;
const possible_flags CANNOT_BUILDVALUE_OPT = 0x1 << 2;
static bool
is_buildvalue_opt_supported_function (FUNC_CODE function)
{
switch (function)
{
case PT_COUNT_STAR:
case PT_COUNT:
case PT_MIN:
case PT_MAX:
case PT_SUM:
case PT_AVG:
case PT_STDDEV:
case PT_STDDEV_POP:
case PT_STDDEV_SAMP:
case PT_VARIANCE:
case PT_VAR_POP:
case PT_VAR_SAMP:
return true;
default:
return false;
}
}
// Thread-local map to cache check results for XASL_NODE and prevent infinite recursion
// This is used to detect circular references in XASL structures and reuse computed results
thread_local std::unordered_map<XASL_NODE *, possible_flags> xasl_check_cache;
// Thread-local set to track XASL_NODEs being processed to prevent infinite recursion in process functions
thread_local std::unordered_set<XASL_NODE *> xasl_processing_set;
inline void set_flag (possible_flags &flags, possible_flags flag)
{
flags |= flag;
}
inline void clear_flag (possible_flags &flags, possible_flags flag)
{
flags &= ~flag;
}
inline bool is_flag_set (possible_flags flags, possible_flags flag)
{
return (flags & flag) != 0;
}
using rv_list_node = struct regu_variable_list_node;
/* prototypes */
template <typename T, bool is_outptr_list>
possible_flags check (T *arg);
template <bool is_outptr_list>
possible_flags check (REGU_VARIABLE *arg);
template <bool is_outptr_list>
possible_flags check (PRED_EXPR *arg);
template <bool is_outptr_list>
possible_flags check (rv_list_node *arg);
template <bool is_outptr_list>
possible_flags check (ARITH_TYPE *arg);
template <bool is_outptr_list>
possible_flags check (PRED *arg);
template <bool is_outptr_list>
possible_flags check (EVAL_TERM *arg);
template <bool is_outptr_list>
possible_flags check (COMP_EVAL_TERM *arg);
template <bool is_outptr_list>
possible_flags check (ALSM_EVAL_TERM *arg);
template <bool is_outptr_list>
possible_flags check (LIKE_EVAL_TERM *arg);
template <bool is_outptr_list>
possible_flags check (RLIKE_EVAL_TERM *arg);
template <bool is_outptr_list>
possible_flags check (ACCESS_SPEC_TYPE *arg);
template <bool is_outptr_list>
possible_flags check (XASL_NODE *arg);
template <bool is_outptr_list>
possible_flags sibling_check (XASL_NODE *sibling);
template <bool is_outptr_list>
possible_flags sibling_check (ACCESS_SPEC_TYPE *arg);
void process_xasl_node_recursive (XASL_NODE *arg);
void process_xasl_node_recursive_force_cannot_parallel (XASL_NODE *arg);
template <bool is_outptr_list>
possible_flags check (REGU_VARIABLE *arg)
{
possible_flags result = 0, temp = 0;
if (!arg)
{
return result;
}
if (arg->xasl)
{
temp = sibling_check<is_outptr_list> (arg->xasl);
if (is_flag_set (temp, CANNOT_PARALLEL_HEAP_SCAN))
{
set_flag (result, CANNOT_PARALLEL_HEAP_SCAN);
}
}
switch (arg->type)
{
case TYPE_ATTR_ID: /* fetch object attribute value */
case TYPE_SHARED_ATTR_ID:
case TYPE_CLASS_ATTR_ID:
if (arg->value.attr_descr.type == DB_TYPE_OBJECT || arg->value.attr_descr.type == DB_TYPE_OID)
{
set_flag (result, CANNOT_PARALLEL_HEAP_SCAN);
}
break;
case TYPE_CONSTANT:
case TYPE_OID:
case TYPE_DBVAL:
case TYPE_POSITION:
case TYPE_POS_VALUE:
case TYPE_LIST_ID:
/* can execute with constants */
break;
case TYPE_ORDERBY_NUM:
case TYPE_CLASSOID:
case TYPE_REGUVAL_LIST:
/* cannot execute with this regu-variable */
set_flag (result, CANNOT_PARALLEL_HEAP_SCAN);
break;
case TYPE_INARITH:
case TYPE_OUTARITH:
temp = check<is_outptr_list> (arg->value.arithptr);
result |= temp;
break;
case TYPE_SP:
result |= check<is_outptr_list> (arg->value.sp_ptr->args);
/* cannot execute sp in child threads */
if (is_outptr_list)
{
set_flag (result, CANNOT_LIST_MERGE);
}
else
{
set_flag (result, CANNOT_PARALLEL_HEAP_SCAN);
}
break;
case TYPE_FUNC:
temp = check<is_outptr_list> (arg->value.funcp->operand);
result |= temp;
break;
case TYPE_REGU_VAR_LIST:
temp = check<is_outptr_list> (arg->value.regu_var_list);
if (is_flag_set (temp, CANNOT_PARALLEL_HEAP_SCAN))
{
set_flag (result, CANNOT_PARALLEL_HEAP_SCAN);
}
else
{
set_flag (result, CANNOT_LIST_MERGE);
}
break;
default:
set_flag (result, CANNOT_PARALLEL_HEAP_SCAN);
break;
}
return result;
}
template <bool is_outptr_list>
possible_flags check (PRED_EXPR *arg)
{
if (!arg)
{
return 0;
}
switch (arg->type)
{
case T_PRED:
return check<is_outptr_list> (&arg->pe.m_pred);
case T_EVAL_TERM:
return check<is_outptr_list> (&arg->pe.m_eval_term);
case T_NOT_TERM:
return check<is_outptr_list> (arg->pe.m_not_term);
default:
return 0;
}
}
template <bool is_outptr_list>
possible_flags check (rv_list_node *arg)
{
if (!arg)
{
return 0;
}
possible_flags result = 0;
rv_list_node *curr = arg;
while (curr)
{
result |= check<is_outptr_list> (&curr->value);
curr = curr->next;
}
return result;
}
template <bool is_outptr_list>
possible_flags check (ARITH_TYPE *arg)
{
if (!arg)
{
return 0;
}
possible_flags result = 0;
result |= check<is_outptr_list> (arg->leftptr);
result |= check<is_outptr_list> (arg->rightptr);
result |= check<is_outptr_list> (arg->thirdptr);
result |= check<is_outptr_list> (arg->pred);
if (arg->opcode == T_TRACE_STATS || arg->opcode == T_EVALUATE_VARIABLE || arg->opcode == T_DEFINE_VARIABLE
|| arg->opcode == T_CURRENT_VALUE || arg->opcode == T_NEXT_VALUE)
{
set_flag (result, CANNOT_PARALLEL_HEAP_SCAN);
}
return result;
}
template <bool is_outptr_list>
possible_flags check (PRED *arg)
{
if (!arg)
{
return 0;
}
possible_flags result = 0;
result |= check<is_outptr_list> (arg->lhs);
result |= check<is_outptr_list> (arg->rhs);
return result;
}
template <bool is_outptr_list>
possible_flags check (EVAL_TERM *arg)
{
if (!arg)
{
return 0;
}
switch (arg->et_type)
{
case T_COMP_EVAL_TERM:
return check<is_outptr_list> (&arg->et.et_comp);
break;
case T_ALSM_EVAL_TERM:
return check<is_outptr_list> (&arg->et.et_alsm);
break;
case T_LIKE_EVAL_TERM:
return check<is_outptr_list> (&arg->et.et_like);
break;
case T_RLIKE_EVAL_TERM:
return check<is_outptr_list> (&arg->et.et_rlike);
break;
default:
return 0;
}
}
template <bool is_outptr_list>
possible_flags check (COMP_EVAL_TERM *arg)
{
if (!arg)
{
return 0;
}
return check<is_outptr_list> (arg->lhs) | check<is_outptr_list> (arg->rhs);
}
template <bool is_outptr_list>
possible_flags check (ALSM_EVAL_TERM *arg)
{
if (!arg)
{
return 0;
}
return check<is_outptr_list> (arg->elem) | check<is_outptr_list> (arg->elemset);
}
template <bool is_outptr_list>
possible_flags check (LIKE_EVAL_TERM *arg)
{
if (!arg)
{
return 0;
}
return check<is_outptr_list> (arg->src) | check<is_outptr_list> (arg->pattern) | check<is_outptr_list> (arg->esc_char);
}
template <bool is_outptr_list>
possible_flags check (RLIKE_EVAL_TERM *arg)
{
if (!arg)
{
return 0;
}
return check<is_outptr_list> (arg->src) | check<is_outptr_list> (arg->pattern) | check<is_outptr_list>
(arg->case_sensitive);
}
template <>
possible_flags check<false> (ACCESS_SPEC_TYPE *arg)
{
possible_flags result = 0;
if (!arg)
{
return 0;
}
if (arg->access != ACCESS_METHOD_SEQUENTIAL || arg->type != TARGET_CLASS)
{
set_flag (result, CANNOT_PARALLEL_HEAP_SCAN);
return result;
}
if (arg->next)
{
set_flag (result, CANNOT_PARALLEL_HEAP_SCAN);
return result;
}
result |= check<false> (arg->s.cls_node.cls_regu_list_pred);
result |= check<false> (arg->s.cls_node.cls_regu_list_rest);
result |= check<false> (arg->where_pred);
if (!arg->s.cls_node.cls_regu_list_pred && !arg->s.cls_node.cls_regu_list_rest)
{
set_flag (result, CANNOT_LIST_MERGE);
}
return result;
}
template <bool is_outptr_list>
possible_flags sibling_check (ACCESS_SPEC_TYPE *arg)
{
possible_flags result = 0;
if (!arg)
{
return 0;
}
if (arg->next)
{
set_flag (result, CANNOT_PARALLEL_HEAP_SCAN);
return result;
}
if (arg->type != TARGET_CLASS && arg->type != TARGET_LIST)
{
set_flag (result, CANNOT_LIST_MERGE);
}
else
{
result |= check<false> (arg->s.cls_node.cls_regu_list_pred);
result |= check<false> (arg->s.cls_node.cls_regu_list_rest);
result |= check<false> (arg->where_pred);
}
return result;
}
template <bool is_outptr_list>
possible_flags sibling_check (XASL_NODE *sibling)
{
if (!sibling)
{
return 0;
}
possible_flags result = 0, temp = 0;
if (sibling->selected_upd_list || sibling->scan_op_type != S_SELECT || sibling->upd_del_class_cnt > 0
|| XASL_IS_FLAGED (sibling, XASL_MULTI_UPDATE_AGG))
{
set_flag (result, CANNOT_PARALLEL_HEAP_SCAN);
}
for (XASL_NODE *xaslp = sibling->aptr_list; xaslp; xaslp = xaslp->next)
{
result |= check<is_outptr_list> (xaslp);
}
if (sibling->bptr_list || sibling->fptr_list)
{
set_flag (result, CANNOT_PARALLEL_HEAP_SCAN);
}
for (XASL_NODE *xaslp = sibling->dptr_list; xaslp; xaslp = xaslp->next)
{
temp = sibling_check<false> (xaslp);
if (is_flag_set (temp, CANNOT_PARALLEL_HEAP_SCAN))
{
set_flag (result, CANNOT_PARALLEL_HEAP_SCAN);
}
}
if (sibling->connect_by_ptr)
{
set_flag (result, CANNOT_LIST_MERGE);
}
if (sibling->if_pred)
{
temp = check<is_outptr_list> (sibling->if_pred);
if (is_flag_set (temp, CANNOT_PARALLEL_HEAP_SCAN))
{
set_flag (result, CANNOT_PARALLEL_HEAP_SCAN);
}
}
if (sibling->instnum_pred || sibling->instnum_val)
{
set_flag (result, CANNOT_LIST_MERGE);
}
for (ACCESS_SPEC_TYPE *specp = sibling->spec_list; specp; specp = specp->next)
{
result |= sibling_check<is_outptr_list> (specp);
}
return result;
}
template <bool is_outptr_list>
possible_flags check (XASL_NODE *arg)
{
if (!arg)
{
return 0;
}
// Check if this XASL_NODE has already been checked
auto it = xasl_check_cache.find (arg);
if (it != xasl_check_cache.end ())
{
// Return cached result
return it->second;
}
// Mark as being visited (with temporary result 0) to prevent infinite recursion
xasl_check_cache[arg] = 0;
possible_flags result = 0, temp = 0;
bool buildvalue_opt = false;
switch (arg->type)
{
case BUILDLIST_PROC:
break;
case BUILDVALUE_PROC:
if (arg->proc.buildvalue.agg_list)
{
set_flag (result, CANNOT_LIST_MERGE);
buildvalue_opt = true;
AGGREGATE_TYPE *agg_it = arg->proc.buildvalue.agg_list;
int agg_cnt = 0;
temp = 0;
for (; agg_it; agg_it = agg_it->next)
{
agg_cnt++;
if (!is_buildvalue_opt_supported_function (agg_it->function))
{
buildvalue_opt = false;
break;
}
temp |= check<false> (agg_it->operands);
if (is_flag_set (temp, CANNOT_PARALLEL_HEAP_SCAN))
{
buildvalue_opt = false;
break;
}
}
if (agg_cnt != arg->outptr_list->valptr_cnt)
{
buildvalue_opt = false;
}
}
break;
case CTE_PROC:
if (arg->proc.cte.recursive_part)
{
set_flag (result, CANNOT_PARALLEL_HEAP_SCAN);
}
break;
case MERGE_PROC:
set_flag (result, CANNOT_PARALLEL_HEAP_SCAN);
break;
case HASHJOIN_PROC:
case UNION_PROC:
case DIFFERENCE_PROC:
case INTERSECTION_PROC:
case INSERT_PROC:
case MERGELIST_PROC:
break;
case OBJFETCH_PROC:
case UPDATE_PROC:
case DELETE_PROC:
case CONNECTBY_PROC:
case DO_PROC:
case BUILD_SCHEMA_PROC:
case SCAN_PROC:
default:
set_flag (result, CANNOT_PARALLEL_HEAP_SCAN);
break;
}
if (arg->selected_upd_list || arg->scan_op_type != S_SELECT || arg->upd_del_class_cnt > 0
|| XASL_IS_FLAGED (arg, XASL_MULTI_UPDATE_AGG))
{
set_flag (result, CANNOT_PARALLEL_HEAP_SCAN);
}
for (XASL_NODE *xaslp = arg->aptr_list; xaslp; xaslp = xaslp->next)
{
if (XASL_IS_FLAGED (xaslp, XASL_LINK_TO_REGU_VARIABLE))
{
temp = sibling_check<false> (xaslp);
if (is_flag_set (temp, CANNOT_PARALLEL_HEAP_SCAN))
{
set_flag (result, CANNOT_PARALLEL_HEAP_SCAN);
}
}
else
{
result |= check<is_outptr_list> (xaslp);
}
}
if (arg->bptr_list || arg->fptr_list || arg->connect_by_ptr)
{
set_flag (result, CANNOT_PARALLEL_HEAP_SCAN);
}
for (XASL_NODE *xaslp = arg->dptr_list; xaslp; xaslp = xaslp->next)
{
temp = sibling_check<false> (xaslp);
if (is_flag_set (temp, CANNOT_PARALLEL_HEAP_SCAN))
{
set_flag (result, CANNOT_PARALLEL_HEAP_SCAN);
}
}
if (arg->scan_ptr)
{
for (XASL_NODE *xaslp = arg->scan_ptr; xaslp; xaslp = xaslp->scan_ptr)
{
result |= sibling_check<is_outptr_list> (arg->scan_ptr);
}
}
if (arg->connect_by_ptr)
{
set_flag (result, CANNOT_LIST_MERGE);
buildvalue_opt = false;
}
if (arg->if_pred)
{
temp = check<is_outptr_list> (arg->if_pred);
if (is_flag_set (temp, CANNOT_PARALLEL_HEAP_SCAN))
{
set_flag (result, CANNOT_PARALLEL_HEAP_SCAN);
}
}
if (arg->instnum_pred || arg->instnum_val)
{
set_flag (result, CANNOT_LIST_MERGE);
buildvalue_opt = false;
}
if (arg->outptr_list)
{
result |= check<true> (arg->outptr_list->valptrp);
}
for (ACCESS_SPEC_TYPE *specp = arg->spec_list; specp; specp = specp->next)
{
result |= check<false> (specp);
}
for (ACCESS_SPEC_TYPE *specp = arg->merge_spec; specp; specp = specp->next)
{
result |= check<false> (specp);
}
if (!buildvalue_opt)
{
set_flag (result, CANNOT_BUILDVALUE_OPT);
}
// Update cache with computed result
xasl_check_cache[arg] = result;
return result;
}
void process_xasl_node_recursive (XASL_NODE *arg)
{
if (!arg)
{
return;
}
// Check if this XASL_NODE is already being processed to prevent infinite recursion
if (xasl_processing_set.find (arg) != xasl_processing_set.end ())
{
return;
}
xasl_processing_set.insert (arg);
possible_flags result = 0;
switch (arg->type)
{
case CTE_PROC:
if (arg->proc.cte.recursive_part)
{
process_xasl_node_recursive_force_cannot_parallel (arg->proc.cte.recursive_part);
set_flag (result, CANNOT_PARALLEL_HEAP_SCAN);
}
if (arg->proc.cte.non_recursive_part)
{
process_xasl_node_recursive (arg->proc.cte.non_recursive_part);
}
break;
case MERGE_PROC:
if (arg->proc.merge.insert_xasl)
{
process_xasl_node_recursive_force_cannot_parallel (arg->proc.merge.insert_xasl);
}
if (arg->proc.merge.update_xasl)
{
process_xasl_node_recursive_force_cannot_parallel (arg->proc.merge.update_xasl);
}
set_flag (result, CANNOT_PARALLEL_HEAP_SCAN);
break;
case BUILDLIST_PROC:
case BUILDVALUE_PROC:
case HASHJOIN_PROC:
case UNION_PROC:
case DIFFERENCE_PROC:
case INTERSECTION_PROC:
case INSERT_PROC:
case MERGELIST_PROC:
break;
case OBJFETCH_PROC:
case UPDATE_PROC:
case DELETE_PROC:
case CONNECTBY_PROC:
case DO_PROC:
case BUILD_SCHEMA_PROC:
case SCAN_PROC:
default:
process_xasl_node_recursive_force_cannot_parallel (arg);
set_flag (result, CANNOT_PARALLEL_HEAP_SCAN);
break;
}
for (XASL_NODE *xaslp = arg->aptr_list; xaslp; xaslp = xaslp->next)
{
process_xasl_node_recursive (xaslp);
}
for (XASL_NODE *xaslp = arg->bptr_list; xaslp; xaslp = xaslp->next)
{
process_xasl_node_recursive_force_cannot_parallel (xaslp);
}
for (XASL_NODE *xaslp = arg->dptr_list; xaslp; xaslp = xaslp->next)
{
process_xasl_node_recursive_force_cannot_parallel (xaslp);
}
for (XASL_NODE *xaslp = arg->fptr_list; xaslp; xaslp = xaslp->next)
{
process_xasl_node_recursive_force_cannot_parallel (xaslp);
}
for (XASL_NODE *xaslp = arg->scan_ptr; xaslp; xaslp = xaslp->scan_ptr)
{
process_xasl_node_recursive_force_cannot_parallel (xaslp);
}
for (XASL_NODE *xaslp = arg->connect_by_ptr; xaslp; xaslp = xaslp->next)
{
process_xasl_node_recursive_force_cannot_parallel (xaslp);
}
result |= check<false> (arg);
if (is_flag_set (result, CANNOT_PARALLEL_HEAP_SCAN))
{
for (ACCESS_SPEC_TYPE *specp = arg->spec_list; specp; specp = specp->next)
{
ACCESS_SPEC_SET_FLAG (specp, ACCESS_SPEC_FLAG_NO_PARALLEL_HEAP_SCAN);
}
}
else
{
/* parallel heap scan is possible */
if (!is_flag_set (result, CANNOT_BUILDVALUE_OPT))
{
/* buildvalue optimization is possible */
for (ACCESS_SPEC_TYPE *specp = arg->spec_list; specp; specp = specp->next)
{
ACCESS_SPEC_SET_FLAG (specp, ACCESS_SPEC_FLAG_BUILDVALUE_OPT);
}
}
else
{
if (!is_flag_set (result, CANNOT_LIST_MERGE))
{
/* list merge is possible */
for (ACCESS_SPEC_TYPE *specp = arg->spec_list; specp; specp = specp->next)
{
ACCESS_SPEC_SET_FLAG (specp, ACCESS_SPEC_FLAG_MERGEABLE_LIST);
}
}
else
{
/* list merge is not possible, try row by row mode */
for (ACCESS_SPEC_TYPE *specp = arg->spec_list; specp; specp = specp->next)
{
ACCESS_SPEC_UNSET_FLAG (specp, ACCESS_SPEC_FLAG_MERGEABLE_LIST);
}
}
}
}
}
void process_xasl_node_recursive_force_cannot_parallel (XASL_NODE *arg)
{
if (!arg)
{
return;
}
// Check if this XASL_NODE is already being processed to prevent infinite recursion
if (xasl_processing_set.find (arg) != xasl_processing_set.end ())
{
return;
}
xasl_processing_set.insert (arg);
// Mark all access specs as cannot parallel
for (ACCESS_SPEC_TYPE *specp = arg->spec_list; specp; specp = specp->next)
{
ACCESS_SPEC_SET_FLAG (specp, ACCESS_SPEC_FLAG_NO_PARALLEL_HEAP_SCAN);
}
// Recursively process all child nodes
for (XASL_NODE *xaslp = arg->aptr_list; xaslp; xaslp = xaslp->next)
{
process_xasl_node_recursive_force_cannot_parallel (xaslp);
}
for (XASL_NODE *xaslp = arg->bptr_list; xaslp; xaslp = xaslp->next)
{
process_xasl_node_recursive_force_cannot_parallel (xaslp);
}
for (XASL_NODE *xaslp = arg->dptr_list; xaslp; xaslp = xaslp->next)
{
process_xasl_node_recursive_force_cannot_parallel (xaslp);
}
for (XASL_NODE *xaslp = arg->fptr_list; xaslp; xaslp = xaslp->next)
{
process_xasl_node_recursive_force_cannot_parallel (xaslp);
}
for (XASL_NODE *xaslp = arg->scan_ptr; xaslp; xaslp = xaslp->scan_ptr)
{
process_xasl_node_recursive_force_cannot_parallel (xaslp);
}
for (XASL_NODE *xaslp = arg->connect_by_ptr; xaslp; xaslp = xaslp->next)
{
process_xasl_node_recursive_force_cannot_parallel (xaslp);
}
// Process special node types
switch (arg->type)
{
case CTE_PROC:
if (arg->proc.cte.recursive_part)
{
process_xasl_node_recursive_force_cannot_parallel (arg->proc.cte.recursive_part);
}
if (arg->proc.cte.non_recursive_part)
{
process_xasl_node_recursive_force_cannot_parallel (arg->proc.cte.non_recursive_part);
}
break;
case MERGE_PROC:
if (arg->proc.merge.insert_xasl)
{
process_xasl_node_recursive_force_cannot_parallel (arg->proc.merge.insert_xasl);
}
if (arg->proc.merge.update_xasl)
{
process_xasl_node_recursive_force_cannot_parallel (arg->proc.merge.update_xasl);
}
break;
default:
break;
}
}
}
extern int
scan_check_parallel_heap_scan_possible (XASL_NODE *xasl)
{
// Clear caches to start fresh for each top-level check
parallel_heap_scan::xasl_check_cache.clear ();
parallel_heap_scan::xasl_processing_set.clear ();
parallel_heap_scan::process_xasl_node_recursive (xasl);
// Clear caches after processing to free memory
parallel_heap_scan::xasl_check_cache.clear ();
parallel_heap_scan::xasl_processing_set.clear ();
return NO_ERROR;
}