diff --git a/src/parser/view_transform.c b/src/parser/view_transform.c index 3d9b9751164..40a3ea25d96 100644 --- a/src/parser/view_transform.c +++ b/src/parser/view_transform.c @@ -425,7 +425,6 @@ static PT_NODE *mq_update_analytic_sort_spec_expr (PARSER_CONTEXT * parser, PT_N static PT_NODE *mq_inline_cte_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk); static PT_NODE *mq_rewrite_cte_as_derived (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk); static PT_NODE *mq_count_cte_references (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk); -static PT_NODE *mq_check_inline_cte (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk); static void mq_check_cte_inline_or_materialize (PARSER_CONTEXT * parser, PT_NODE * node); /* * mq_is_outer_join_spec () - determine if a spec is outer joined in a spec list @@ -5292,7 +5291,7 @@ mq_check_cte_inline_or_materialize (PARSER_CONTEXT * parser, PT_NODE * node) { PT_NODE *cte; PT_HINT_ENUM hint; - bool is_inlinable = true; + bool has_click_counter = false; assert (node->node_type == PT_WITH_CLAUSE); @@ -5311,14 +5310,14 @@ mq_check_cte_inline_or_materialize (PARSER_CONTEXT * parser, PT_NODE * node) continue; } - is_inlinable = true; + has_click_counter = false; /* CTE containing functions like incr, rownum etc. cannot be rewritten as inline view * since it may change the query results. Handle it same as CTE with materialize hint. */ (void) parser_walk_tree (parser, cte->info.cte.non_recursive_part, - mq_check_inline_cte, &is_inlinable, NULL, NULL); + mq_has_click_counter, &has_click_counter, NULL, NULL); /* false subquery cannot be rewritten as inline view */ - if (is_inlinable && pt_is_query (cte->info.cte.non_recursive_part)) + if (!has_click_counter && pt_is_query (cte->info.cte.non_recursive_part)) { hint = pt_get_hint_from_query (parser, cte->info.cte.non_recursive_part); @@ -5398,16 +5397,16 @@ mq_count_cte_references (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int } /* - * mq_check_inline_cte () - + * mq_has_click_counter () - * return: * parser(in): * node(in): * arg(in): */ -static PT_NODE * -mq_check_inline_cte (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk) +PT_NODE * +mq_has_click_counter (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk) { - bool *can_inlining = (bool *) arg; + bool *has_click_counter = (bool *) arg; if (node == NULL) { @@ -5416,11 +5415,10 @@ mq_check_inline_cte (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *co switch (node->node_type) { - /* CTE cannot contain WITH clause inside, so we don't need to handle this case */ case PT_EXPR: if (node->info.expr.op == PT_INCR || node->info.expr.op == PT_DECR) { - *can_inlining = false; + *has_click_counter = true; *continue_walk = PT_STOP_WALK; } break; diff --git a/src/parser/view_transform.h b/src/parser/view_transform.h index 07ecb9cedb7..090063c3151 100644 --- a/src/parser/view_transform.h +++ b/src/parser/view_transform.h @@ -98,4 +98,6 @@ extern PT_NODE *mq_rewrite_aggregate_as_derived (PARSER_CONTEXT * parser, PT_NOD extern PT_NODE *mq_rewrite_query_as_derived (PARSER_CONTEXT * parser, PT_NODE * query); extern int mq_copypush_sargable_terms (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * spec); + +extern PT_NODE *mq_has_click_counter (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk); #endif /* _VIEW_TRANSFORM_H_ */ diff --git a/src/parser/xasl_generation.c b/src/parser/xasl_generation.c index b54915d004e..14b46f29ee6 100644 --- a/src/parser/xasl_generation.c +++ b/src/parser/xasl_generation.c @@ -639,6 +639,8 @@ static PT_NODE *pt_check_corr_subquery_not_cachable_expr (PARSER_CONTEXT * parse static PT_NODE *pt_make_result_ref (PARSER_CONTEXT * parser, PT_NODE * node, PT_NODE * groupby_list, VAL_LIST * vallist); +static int pt_check_analytic_limit_optimization (XASL_NODE * xasl, ANALYTIC_EVAL_TYPE * eval_list); + static void pt_init_xasl_supp_info () { @@ -16595,6 +16597,17 @@ pt_to_buildlist_proc (PARSER_CONTEXT * parser, PT_NODE * select_node, QO_PLAN * goto analytic_exit_on_error; } + int *attr_offsets; + attr_offsets = pt_make_identity_offsets (select_list_ex); + + buildlist->a_scan_regu_list = + pt_to_regu_variable_list (parser, select_list_ex, UNBOX_AS_VALUE, buildlist->a_val_list, attr_offsets); + + if (attr_offsets != NULL) + { + free_and_init (attr_offsets); + } + /* generate regu list (identity fetching from temp tuple) */ buildlist->a_regu_list = pt_to_position_regu_variable_list (parser, select_list_ex, buildlist->a_val_list, NULL); @@ -16640,6 +16653,52 @@ pt_to_buildlist_proc (PARSER_CONTEXT * parser, PT_NODE * select_node, QO_PLAN * /* optimize analytic function list */ xasl->proc.buildlist.a_eval_list = pt_optimize_analytic_list (parser, &analytic_info, &no_optimization_done); + if (pt_check_analytic_limit_optimization (xasl, xasl->proc.buildlist.a_eval_list) != NO_ERROR) + { + PT_ERRORm (parser, select_list_ex, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY); + goto analytic_exit_on_error; + } + + + if (XASL_IS_FLAGED (xasl, XASL_ANALYTIC_USES_LIMIT_OPT)) + { + PT_NODE *final_node = NULL; + REGU_VARIABLE_LIST regu_list_new, prev = NULL; + REGU_VARIABLE *regu_var_temp; + + for (final_node = select_list_final; final_node != NULL; final_node = final_node->next) + { + if (pt_is_instnum (final_node)) + { + buildlist->a_outptr_list_ex->valptr_cnt++; + + regu_alloc (regu_list_new); + if (regu_list_new == NULL) + { + goto analytic_exit_on_error; + } + + regu_var_temp = pt_make_regu_numbering (parser, final_node); + regu_list_new->value = *regu_var_temp; + + if (prev == NULL) + { + prev = regu_list_new; + regu_list_new->next = buildlist->a_outptr_list_ex->valptrp; + buildlist->a_outptr_list_ex->valptrp = regu_list_new; + } + else + { + regu_list_new->next = prev->next; + prev->next = regu_list_new; + } + prev = prev->next; + } + + prev = (prev != NULL) ? prev->next : buildlist->a_outptr_list_ex->valptrp; + } + } + /* FIXME - Fix it with pt_build_analytic_eval_list (). */ if (no_optimization_done == true) { @@ -16739,7 +16798,14 @@ pt_to_buildlist_proc (PARSER_CONTEXT * parser, PT_NODE * select_node, QO_PLAN * select_list_ex = NULL; /* register initial outlist */ - xasl->outptr_list = buildlist->a_outptr_list_ex; + if (XASL_IS_FLAGED (xasl, XASL_ANALYTIC_USES_LIMIT_OPT)) + { + xasl->outptr_list = buildlist->a_outptr_list; + } + else + { + xasl->outptr_list = buildlist->a_outptr_list_ex; + } /* all done */ goto analytic_exit; @@ -16935,7 +17001,7 @@ pt_to_buildlist_proc (PARSER_CONTEXT * parser, PT_NODE * select_node, QO_PLAN * } if ((xasl->instnum_pred != NULL || xasl->instnum_flag & XASL_INSTNUM_FLAG_EVAL_DEFER) - && pt_has_analytic (parser, select_node)) + && pt_has_analytic (parser, select_node) && !XASL_IS_FLAGED (xasl, XASL_ANALYTIC_USES_LIMIT_OPT)) { /* we have an inst_num() which should not get evaluated in the initial fetch(processing stage) * qexec_execute_analytic(post-processing stage) will use it in the final sort */ @@ -24428,6 +24494,11 @@ pt_to_analytic_final_node (PARSER_CONTEXT * parser, PT_NODE * tree, PT_NODE ** e return NULL; } + if (PT_IS_VALUE_NODE (tree)) + { + return tree; + } + if (PT_IS_ANALYTIC_NODE (tree)) { /* select ntile(select stddev(...)...)... from ... is allowed */ @@ -25050,6 +25121,11 @@ pt_substitute_analytic_references (PARSER_CONTEXT * parser, PT_NODE * node, PT_N return node; } + if (PT_IS_VALUE_NODE (node)) + { + return node; + } + if (PT_IS_POINTER_REF_NODE (node)) { PT_NODE *real_node = node->info.pointer.node; @@ -28241,3 +28317,62 @@ pt_make_result_ref (PARSER_CONTEXT * parser, PT_NODE * node, PT_NODE * groupby_l return new_node; } + +/* + * pt_check_analytic_limit_optimization () - + * Check if analytic functions are optimizable for limit optimization. + * If optimizable, set the XASL_ANALYTIC_USES_LIMIT_OPT flag. + * + * return : + * xasl (in) : + * eval_list (in) : + */ +static int +pt_check_analytic_limit_optimization (XASL_NODE * xasl, ANALYTIC_EVAL_TYPE * eval_list) +{ + ANALYTIC_EVAL_TYPE *eval; + ANALYTIC_TYPE *a_func_list; + bool is_optimizable = false; + + if (!xasl->instnum_pred && !xasl->instnum_val) + { + return NO_ERROR; + } + + /* NOTE: If eval->sort_list is NULL, eval_list length is always 1. + * If eval_list length >= 2, all eval entries have non-NULL sort_list. + * Since we check sort_list == NULL here, checking only one eval is sufficient. */ + for (eval = eval_list; eval != NULL; eval = eval->next) + { + is_optimizable = !(eval->sort_list) ? true : false; + + for (a_func_list = eval->head; a_func_list && is_optimizable; a_func_list = a_func_list->next) + { + switch (a_func_list->function) + { + case PT_FIRST_VALUE: + if (a_func_list->ignore_nulls) + { + is_optimizable = false; + break; + } + + case PT_ROW_NUMBER: + case PT_RANK: + case PT_DENSE_RANK: + break; + + default: + is_optimizable = false; + break; + } + } + } + + if (is_optimizable) + { + XASL_SET_FLAG (xasl, XASL_ANALYTIC_USES_LIMIT_OPT); + } + + return NO_ERROR; +} diff --git a/src/query/parallel/px_heap_scan/px_heap_scan_checker.cpp b/src/query/parallel/px_heap_scan/px_heap_scan_checker.cpp index 8a4776e6b09..0c9eec7340e 100644 --- a/src/query/parallel/px_heap_scan/px_heap_scan_checker.cpp +++ b/src/query/parallel/px_heap_scan/px_heap_scan_checker.cpp @@ -503,7 +503,14 @@ namespace parallel_heap_scan } if (xasl->proc.buildlist.a_eval_list) { - result = CHECK_RESULT::PARALLEL_LIST_MERGE; + if (XASL_IS_FLAGED (xasl, XASL_ANALYTIC_USES_LIMIT_OPT)) + { + result = CHECK_RESULT::PARALLEL_PAGE_BY_PAGE; + } + else + { + result = CHECK_RESULT::PARALLEL_LIST_MERGE; + } } break; case BUILDVALUE_PROC: diff --git a/src/query/query_executor.c b/src/query/query_executor.c index 99dd9cae838..255c60b85a4 100644 --- a/src/query/query_executor.c +++ b/src/query/query_executor.c @@ -730,6 +730,7 @@ static int qexec_build_agg_hkey (THREAD_ENTRY * thread_p, XASL_STATE * xasl_stat static int qexec_locate_agg_hentry_in_list (THREAD_ENTRY * thread_p, AGGREGATE_HASH_CONTEXT * context, AGGREGATE_HASH_KEY * key, bool * found); static int qexec_get_attr_default (THREAD_ENTRY * thread_p, OR_ATTRIBUTE * attr, DB_VALUE * default_val); +static int qexec_analytic_eval_in_processing (THREAD_ENTRY * thread_p, XASL_NODE * xasl, XASL_STATE * xasl_state); /* * Utility routines @@ -1187,6 +1188,11 @@ qexec_end_one_iteration (THREAD_ENTRY * thread_p, XASL_NODE * xasl, XASL_STATE * GOTO_EXIT_ON_ERROR; } + if (qexec_analytic_eval_in_processing (thread_p, xasl, xasl_state) != NO_ERROR) + { + GOTO_EXIT_ON_ERROR; + } + tpldescr_status = qexec_generate_tuple_descriptor (thread_p, xasl->list_id, xasl->outptr_list, &xasl_state->vd); if (tpldescr_status == QPROC_TPLDESCR_FAILURE) { @@ -2571,6 +2577,7 @@ qexec_clear_xasl (THREAD_ENTRY * thread_p, xasl_node * xasl, bool is_final, bool /* analytic functions */ pg_cnt += qexec_clear_analytic_function_list (thread_p, xasl, buildlist->a_eval_list, is_final); pg_cnt += qexec_clear_regu_list (thread_p, xasl, buildlist->a_regu_list, is_final, false); + pg_cnt += qexec_clear_regu_list (thread_p, xasl, buildlist->a_scan_regu_list, is_final, true); /* group by regu list */ if (buildlist->g_scan_regu_list) @@ -3076,6 +3083,7 @@ qexec_clear_xasl_for_parallel_aptr (THREAD_ENTRY * thread_p, XASL_NODE * xasl, b /* analytic functions */ pg_cnt += qexec_clear_analytic_function_list (thread_p, xasl, buildlist->a_eval_list, is_final); pg_cnt += qexec_clear_regu_list (thread_p, xasl, buildlist->a_regu_list, is_final, true); + pg_cnt += qexec_clear_regu_list (thread_p, xasl, buildlist->a_scan_regu_list, is_final, true); /* group by regu list */ if (buildlist->g_scan_regu_list) @@ -9242,6 +9250,19 @@ qexec_intprt_fnc (THREAD_ENTRY * thread_p, XASL_NODE * xasl, XASL_STATE * xasl_s { return S_SUCCESS; } + + /* Evaluate analytic functions for tuples that don't satisfy instnum_pred yet. + * For instnum_pred like "inst_num() > 10 AND inst_num() < 100", when inst_num() <= 10, + * XASL_INSTNUM_FLAG_SCAN_CHECK flag is not set, but analytic functions must still be evaluated + * to get correct results. Since analytic functions are evaluable in processing, we don't read all tuples, + * so analytic functions must be evaluated at this point. */ + if (ev_res == V_FALSE && !(xasl->instnum_flag & XASL_INSTNUM_FLAG_SCAN_CHECK)) + { + if (qexec_analytic_eval_in_processing (thread_p, xasl, xasl_state) != NO_ERROR) + { + return S_ERROR; + } + } } qualified = (xasl->instnum_pred == NULL || ev_res == V_TRUE); @@ -15170,6 +15191,8 @@ qexec_execute_mainblock_internal (THREAD_ENTRY * thread_p, XASL_NODE * xasl, XAS if (xasl->type == BUILDLIST_PROC) { AGGREGATE_TYPE *agg_p; + ANALYTIC_EVAL_TYPE *a_eval_list; + ANALYTIC_TYPE *a_func_list; /* prepare hash table for aggregate evaluation */ if (xasl->proc.buildlist.g_hash_eligible) @@ -15194,6 +15217,20 @@ qexec_execute_mainblock_internal (THREAD_ENTRY * thread_p, XASL_NODE * xasl, XAS /* domains not resolved */ xasl->proc.buildlist.g_agg_domains_resolved = 0; + + if (XASL_IS_FLAGED (xasl, XASL_ANALYTIC_USES_LIMIT_OPT)) + { + for (a_eval_list = xasl->proc.buildlist.a_eval_list; a_eval_list; a_eval_list = a_eval_list->next) + { + for (a_func_list = a_eval_list->head; a_func_list; a_func_list = a_func_list->next) + { + if (qdata_initialize_analytic_func (thread_p, a_func_list, xasl_state->query_id) != NO_ERROR) + { + GOTO_EXIT_ON_ERROR; + } + } + } + } } else if (xasl->type == BUILDVALUE_PROC) { @@ -15845,7 +15882,8 @@ qexec_execute_mainblock_internal (THREAD_ENTRY * thread_p, XASL_NODE * xasl, XAS } /* process analytic functions */ - if (xasl->type == BUILDLIST_PROC && xasl->proc.buildlist.a_eval_list) + if (xasl->type == BUILDLIST_PROC && xasl->proc.buildlist.a_eval_list + && !XASL_IS_FLAGED (xasl, XASL_ANALYTIC_USES_LIMIT_OPT)) { ANALYTIC_EVAL_TYPE *eval_list; for (eval_list = xasl->proc.buildlist.a_eval_list; eval_list; eval_list = eval_list->next) @@ -22903,6 +22941,54 @@ qexec_analytic_update_group_result (THREAD_ENTRY * thread_p, ANALYTIC_STATE * an return rc; } +/* + * qexec_analytic_eval_in_processing () - evaluate analytic functions in processing + * return: NO_ERROR, or ER_code + * thread_p (in) : thread pointer + * xasl (in) : xasl tree + * xasl_state (in) : xasl state + */ +static int +qexec_analytic_eval_in_processing (THREAD_ENTRY * thread_p, XASL_NODE * xasl, XASL_STATE * xasl_state) +{ + BUILDLIST_PROC_NODE *buildlist; + ANALYTIC_EVAL_TYPE *a_eval_list; + ANALYTIC_TYPE *a_func_list; + + if (XASL_IS_FLAGED (xasl, XASL_ANALYTIC_USES_LIMIT_OPT)) + { + assert (xasl->type == BUILDLIST_PROC); + assert (xasl->proc.buildlist.a_eval_list); + assert (xasl->proc.buildlist.a_eval_list->next == NULL); + + buildlist = &xasl->proc.buildlist; + for (a_eval_list = buildlist->a_eval_list; a_eval_list; a_eval_list = a_eval_list->next) + { + if (fetch_val_list + (thread_p, buildlist->a_scan_regu_list, &xasl_state->vd, NULL, NULL, NULL, PEEK) != NO_ERROR) + { + return ER_FAILED; + } + + for (a_func_list = a_eval_list->head; a_func_list; a_func_list = a_func_list->next) + { + ANALYTIC_FUNC_SET_FLAG (a_func_list, ANALYTIC_KEEP_RANK); + if (qdata_evaluate_analytic_func (thread_p, a_func_list, &xasl_state->vd) != NO_ERROR) + { + return ER_FAILED; + } + + if (a_func_list->function != PT_ROW_NUMBER) + { + pr_clone_value (a_func_list->value, a_func_list->out_value); + } + } + } + } + + return NO_ERROR; +} + /* * qexec_clear_pred_context () - clear the predicate * return: int diff --git a/src/query/stream_to_xasl.c b/src/query/stream_to_xasl.c index 53aff043b8a..f4e66c9fbdb 100644 --- a/src/query/stream_to_xasl.c +++ b/src/query/stream_to_xasl.c @@ -2947,6 +2947,21 @@ stx_build_buildlist_proc (THREAD_ENTRY * thread_p, char *ptr, BUILDLIST_PROC_NOD } } + ptr = or_unpack_int (ptr, &offset); + if (offset == 0) + { + stx_build_list_proc->a_scan_regu_list = NULL; + } + else + { + stx_build_list_proc->a_scan_regu_list = + stx_restore_regu_variable_list (thread_p, &xasl_unpack_info->packed_xasl[offset]); + if (stx_build_list_proc->a_scan_regu_list == NULL) + { + goto error; + } + } + ptr = or_unpack_int (ptr, &offset); if (offset == 0) { diff --git a/src/query/xasl.h b/src/query/xasl.h index c945c4ddc4b..3f426729707 100644 --- a/src/query/xasl.h +++ b/src/query/xasl.h @@ -329,6 +329,7 @@ struct buildlist_proc_node REGU_VARIABLE_LIST g_scan_regu_list; /* group_by regulist during scan */ ANALYTIC_EVAL_TYPE *a_eval_list; /* analytic functions evaluation groups */ REGU_VARIABLE_LIST a_regu_list; /* analytic regu list */ + REGU_VARIABLE_LIST a_scan_regu_list; /* analytic regulist during scan */ OUTPTR_LIST *a_outptr_list; /* analytic output ptr list */ OUTPTR_LIST *a_outptr_list_ex; /* ext output ptr list */ OUTPTR_LIST *a_outptr_list_interm; /* intermediate output list */ @@ -515,6 +516,7 @@ struct cte_proc_node #define XASL_SAMPLING_SCAN (0x1 << 17) /* is sampling scan */ #define XASL_USES_SQ_CACHE (0x1 << 18) /* subquery uses result cache */ #define XASL_NO_PARALLEL_SUBQUERY (0x1 << 19) /* disable parallel subquery */ +#define XASL_ANALYTIC_USES_LIMIT_OPT (0x1 << 20) /* analytic uses limit optimization */ #define XASL_IS_FLAGED(x, f) (((x)->flag & (int) (f)) != 0) #define XASL_SET_FLAG(x, f) (x)->flag |= (int) (f) diff --git a/src/query/xasl_to_stream.c b/src/query/xasl_to_stream.c index b32c6774b00..b5b63c82854 100644 --- a/src/query/xasl_to_stream.c +++ b/src/query/xasl_to_stream.c @@ -3468,6 +3468,13 @@ xts_process_buildlist_proc (char *ptr, const BUILDLIST_PROC_NODE * build_list_pr } ptr = or_pack_int (ptr, offset); + offset = xts_save_regu_variable_list (build_list_proc->a_scan_regu_list); + if (offset == ER_FAILED) + { + return NULL; + } + ptr = or_pack_int (ptr, offset); + offset = xts_save_outptr_list (build_list_proc->a_outptr_list); if (offset == ER_FAILED) { @@ -6237,6 +6244,7 @@ xts_sizeof_buildlist_proc (const BUILDLIST_PROC_NODE * build_list) + PTR_SIZE /* g_agg_list */ + PTR_SIZE /* a_func_list */ + PTR_SIZE /* a_regu_list */ + + PTR_SIZE /* a_scan_regu_list */ + PTR_SIZE /* a_outptr_list */ + PTR_SIZE /* a_outptr_list_ex */ + PTR_SIZE /* a_outptr_list_interm */