已合并
add the index parallel scannning function #9321
add the index parallel scannning function #9321
已合并
xiahanzhi创建于 6月15日
44 个文件变更+3413-295
@@ -25,9 +25,9 @@
25#include <stdlib.h>25#include <stdlib.h>
26#include "pg_config.h"26#include "pg_config.h"
27 27 
28-#define FILE_NAME_MAX_LEN (32)28+#define FILE_NAME_MAX_LEN (64)
29-#define ERR_LOCATION_MAX_NUM (128)29+#define ERR_LOCATION_MAX_NUM (256)
30-#define ERRMSG_MAX_LEN (512)30+#define ERRMSG_MAX_LEN (768)
31#define SQLSTATE_MAX_LEN (6)31#define SQLSTATE_MAX_LEN (6)
32 32 
33/* Identify the location info of errmsg. */33/* Identify the location info of errmsg. */
@@ -213,6 +213,13 @@ static void PrintFileLeakWarning(File file);
213 */213 */
214ResourceOwner ResourceOwnerCreate(ResourceOwner parent, const char* name, MemoryContext memCxt)214ResourceOwner ResourceOwnerCreate(ResourceOwner parent, const char* name, MemoryContext memCxt)
215{215{
216+ if (parent == NULL && strcmp(name, "TopTransaction") != 0 && strcmp(name, "InitLocalSysCache") != 0 &&
217+ strcmp(name, "ThreadRootResourceOwner") != 0) {
218+ Assert(t_thrd.utils_cxt.ThreadRootResourceOwner == t_thrd.utils_cxt.CurrentResourceOwner);
219+ Assert(strcmp(t_thrd.utils_cxt.ThreadRootResourceOwner->name, "ThreadRootResourceOwner") == 0);
220+ t_thrd.utils_cxt.ThreadRootResourceOwner->name = name;
221+ return t_thrd.utils_cxt.ThreadRootResourceOwner;
222+ }
216 ResourceOwner owner;223 ResourceOwner owner;
217 224 
218 MemoryContext context = AllocSetContextCreate(memCxt,225 MemoryContext context = AllocSetContextCreate(memCxt,
@@ -531,6 +538,11 @@ void ResourceOwnerDelete(ResourceOwner owner)
531 IsolatedResourceOwner = NULL;538 IsolatedResourceOwner = NULL;
532 Assert(t_thrd.lsc_cxt.local_sysdb_resowner != owner);539 Assert(t_thrd.lsc_cxt.local_sysdb_resowner != owner);
533 540 
541+ Assert(t_thrd.utils_cxt.ThreadRootResourceOwner != owner);
542+ if (t_thrd.utils_cxt.ThreadRootResourceOwner == owner) {
543+ return;
544+ }
545+ 
534 while (owner->firstchild != NULL)546 while (owner->firstchild != NULL)
535 ResourceOwnerDelete(owner->firstchild);547 ResourceOwnerDelete(owner->firstchild);
536 548 
@@ -2715,6 +2727,7 @@ void ResourceOwnerReleaseAllPlanCacheRefs(ResourceOwner owner)
2715 ResourceOwnerDecrementNPlanRefs(owner, true);2727 ResourceOwnerDecrementNPlanRefs(owner, true);
2716 t_thrd.utils_cxt.CurrentResourceOwner = save;2728 t_thrd.utils_cxt.CurrentResourceOwner = save;
2717}2729}
2730+ 
2718void ReleaseResownerOutOfTransaction()2731void ReleaseResownerOutOfTransaction()
2719{2732{
2720 if (likely(t_thrd.utils_cxt.CurrentResourceOwner == NULL)) {2733 if (likely(t_thrd.utils_cxt.CurrentResourceOwner == NULL)) {
@@ -2735,10 +2748,43 @@ void ReleaseResownerOutOfTransaction()
2735 if (unlikely(strcmp(root->name, "TopTransaction") == 0)) {2748 if (unlikely(strcmp(root->name, "TopTransaction") == 0)) {
2736 return;2749 return;
2737 }2750 }
2738- 2751+ Assert(root == t_thrd.utils_cxt.ThreadRootResourceOwner);
2752+ t_thrd.utils_cxt.CurrentResourceOwner = t_thrd.utils_cxt.ThreadRootResourceOwner;
2739 ResourceOwnerRelease(root, RESOURCE_RELEASE_BEFORE_LOCKS, false, true);2753 ResourceOwnerRelease(root, RESOURCE_RELEASE_BEFORE_LOCKS, false, true);
2740 ResourceOwnerRelease(root, RESOURCE_RELEASE_LOCKS, false, true);2754 ResourceOwnerRelease(root, RESOURCE_RELEASE_LOCKS, false, true);
2741 ResourceOwnerRelease(root, RESOURCE_RELEASE_AFTER_LOCKS, false, true);2755 ResourceOwnerRelease(root, RESOURCE_RELEASE_AFTER_LOCKS, false, true);
2756+ while (t_thrd.utils_cxt.ThreadRootResourceOwner->firstchild != NULL) {
2757+ ResourceOwnerDelete(t_thrd.utils_cxt.ThreadRootResourceOwner->firstchild);
2758+ }
2759+}
2760+ 
2761+bool ResourceOwnerExists(ResourceOwner owner, ResourceOwner root)
2762+{
2763+ /* resouceowner exists is a check before release, root onwer's release shouldn't call ResourceOwnerExists */
2764+ if (owner == NULL || owner->parent == NULL || root == NULL) {
2765+ return false;
2766+ }
2767+ return root == owner || (root->firstchild != NULL && ResourceOwnerExists(owner, root->firstchild)) ||
2768+ (root->nextchild != NULL && ResourceOwnerExists(owner, root->nextchild));
2769+}
2770+ 
2771+void ReleaseResownerForStreamError()
2772+{
2773+ ResourceOwner toSetCurrentResourceOwner = NULL;
2774+ if (t_thrd.utils_cxt.CurrentResourceOwner != NULL && t_thrd.utils_cxt.CurrentResourceOwner->parent != NULL &&
2775+ ResourceOwnerExists(t_thrd.utils_cxt.CurrentResourceOwner, t_thrd.utils_cxt.ThreadRootResourceOwner)) {
2776+ toSetCurrentResourceOwner = t_thrd.utils_cxt.ThreadRootResourceOwner;
2777+ } else {
2778+ toSetCurrentResourceOwner = t_thrd.utils_cxt.CurrentResourceOwner;
2779+ }
2780+ t_thrd.utils_cxt.CurrentResourceOwner = t_thrd.utils_cxt.ThreadRootResourceOwner;
2781+ ResourceOwnerRelease(t_thrd.utils_cxt.CurrentResourceOwner, RESOURCE_RELEASE_BEFORE_LOCKS, false, true);
2782+ ResourceOwnerRelease(t_thrd.utils_cxt.CurrentResourceOwner, RESOURCE_RELEASE_LOCKS, false, true);
2783+ ResourceOwnerRelease(t_thrd.utils_cxt.CurrentResourceOwner, RESOURCE_RELEASE_AFTER_LOCKS, false, true);
2784+ while (t_thrd.utils_cxt.ThreadRootResourceOwner->firstchild != NULL) {
2785+ ResourceOwnerDelete(t_thrd.utils_cxt.ThreadRootResourceOwner->firstchild);
2786+ }
2787+ t_thrd.utils_cxt.CurrentResourceOwner = toSetCurrentResourceOwner;
2742}2788}
2743 2789 
2744FORCE_INLINE2790FORCE_INLINE
@@ -265,7 +265,7 @@ static void InitInstrWorkloadTransactionUser(void)
265 265 
266 ResourceOwner currentOwner = t_thrd.utils_cxt.CurrentResourceOwner;266 ResourceOwner currentOwner = t_thrd.utils_cxt.CurrentResourceOwner;
267 ResourceOwner tmpOwner;267 ResourceOwner tmpOwner;
268- t_thrd.utils_cxt.CurrentResourceOwner = ResourceOwnerCreate(NULL, "ForWorkloadTransaction",268+ t_thrd.utils_cxt.CurrentResourceOwner = ResourceOwnerCreate(currentOwner, "ForWorkloadTransaction",
269 THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_CBB));269 THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_CBB));
270 270 
271 Relation relation = heap_open(AuthIdRelationId, AccessShareLock);271 Relation relation = heap_open(AuthIdRelationId, AccessShareLock);
@@ -2508,7 +2508,6 @@ void DmsCallbackThreadShmemInit(unsigned char need_startup, char **reg_data)
2508 }2508 }
2509 CreateLocalSysDBCache();2509 CreateLocalSysDBCache();
2510 InitShmemForDmsCallBack();2510 InitShmemForDmsCallBack();
2511- Assert(t_thrd.utils_cxt.CurrentResourceOwner == NULL);
2512 t_thrd.utils_cxt.CurrentResourceOwner =2511 t_thrd.utils_cxt.CurrentResourceOwner =
2513 ResourceOwnerCreate(NULL, "dms worker", THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE));2512 ResourceOwnerCreate(NULL, "dms worker", THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE));
2514 2513 
@@ -907,11 +907,11 @@ static List* build_index_paths(PlannerInfo* root, RelOptInfo* rel, IndexOptInfo*
907 bool found_clause = false;907 bool found_clause = false;
908 bool found_lower_saop_clause = false;908 bool found_lower_saop_clause = false;
909 bool pathkeys_possibly_useful = false;909 bool pathkeys_possibly_useful = false;
910+ bool with_array_keys = false;
910 bool index_is_ordered = false;911 bool index_is_ordered = false;
911 bool index_only_scan = false;912 bool index_only_scan = false;
912 int indexcol;913 int indexcol;
913- bool can_parallel = IS_STREAM_PLAN && (u_sess->opt_cxt.query_dop > 1) && (ST_BITMAPSCAN != scantype) &&914+ bool can_parallel = IS_STREAM_PLAN && (u_sess->opt_cxt.query_dop > 1) && (ST_BITMAPSCAN != scantype);
914- (!rel->isPartitionedTable) && !index->rel->is_ustore;
915 915 
916 if (index->isAnnIndex && IsExtremeRedo()) {916 if (index->isAnnIndex && IsExtremeRedo()) {
917 if (ST_BITMAPSCAN != scantype) {917 if (ST_BITMAPSCAN != scantype) {
@@ -976,6 +976,7 @@ static List* build_index_paths(PlannerInfo* root, RelOptInfo* rel, IndexOptInfo*
976 RestrictInfo* rinfo = (RestrictInfo*)lfirst(lc);976 RestrictInfo* rinfo = (RestrictInfo*)lfirst(lc);
977 977 
978 if (IsA(rinfo->clause, ScalarArrayOpExpr)) {978 if (IsA(rinfo->clause, ScalarArrayOpExpr)) {
979+ with_array_keys = true;
979 /* Ignore if not supported by index */980 /* Ignore if not supported by index */
980 if (saop_control == SAOP_PER_AM && !index->amsearcharray)981 if (saop_control == SAOP_PER_AM && !index->amsearcharray)
981 continue;982 continue;
@@ -1002,7 +1003,7 @@ static List* build_index_paths(PlannerInfo* root, RelOptInfo* rel, IndexOptInfo*
1002 if (index_clauses == NIL && !index->amoptionalkey)1003 if (index_clauses == NIL && !index->amoptionalkey)
1003 return NIL;1004 return NIL;
1004 }1005 }
1005- 1006+ can_parallel = can_parallel && !with_array_keys;
1006 /* We do not want the index's rel itself listed in outer_relids */1007 /* We do not want the index's rel itself listed in outer_relids */
1007 outer_relids = bms_del_member(outer_relids, rel->relid);1008 outer_relids = bms_del_member(outer_relids, rel->relid);
1008 /* Enforce convention that outer_relids is exactly NULL if empty */1009 /* Enforce convention that outer_relids is exactly NULL if empty */
@@ -83,7 +83,7 @@
83 83 
84#define EQUALJOINVARRATIO ((2.0) / (3.0))84#define EQUALJOINVARRATIO ((2.0) / (3.0))
85 85 
86-static Plan* create_plan_recurse(PlannerInfo* root, Path* best_path);86+static Plan* create_plan_recurse(PlannerInfo* root, Path* best_path, bool *may_change = NULL);
87static List* build_path_tlist(PlannerInfo* root, Path* path);87static List* build_path_tlist(PlannerInfo* root, Path* path);
88static Plan* create_scan_plan(PlannerInfo* root, Path* best_path);88static Plan* create_scan_plan(PlannerInfo* root, Path* best_path);
89static List* build_relation_tlist(RelOptInfo* rel);89static List* build_relation_tlist(RelOptInfo* rel);
@@ -160,7 +160,7 @@ static Plan* setPartitionParam(PlannerInfo* root, Plan* plan, RelOptInfo* rel);
160#ifdef ENABLE_MULTIPLE_NODES160#ifdef ENABLE_MULTIPLE_NODES
161static Plan* setBucketInfoParam(PlannerInfo* root, Plan* plan, RelOptInfo* rel);161static Plan* setBucketInfoParam(PlannerInfo* root, Plan* plan, RelOptInfo* rel);
162#endif162#endif
163-Plan* create_globalpartInterator_plan(PlannerInfo* root, PartIteratorPath* pIterpath);163+Plan* create_globalpartInterator_plan(PlannerInfo* root, PartIteratorPath* pIterpath, bool* may_change = NULL);
164static bool is_ann_partiterator_local_limit_safe(Node* limitCount);164static bool is_ann_partiterator_local_limit_safe(Node* limitCount);
165static bool need_ann_partiterator_local_limit(PlannerInfo* root, PartIteratorPath* pIterpath);165static bool need_ann_partiterator_local_limit(PlannerInfo* root, PartIteratorPath* pIterpath);
166 166 
@@ -601,10 +601,10 @@ Plan* create_plan(PlannerInfo* root, Path* best_path)
601 * create_plan_recurse601 * create_plan_recurse
602 * Recursive guts of create_plan().602 * Recursive guts of create_plan().
603 */603 */
604-static Plan* create_plan_recurse(PlannerInfo* root, Path* best_path)604+static Plan* create_plan_recurse(PlannerInfo* root, Path* best_path, bool *may_change)
605{605{
606 Plan* plan = NULL;606 Plan* plan = NULL;
607- 607+ bool change = false;
608 /* Guard against stack overflow due to overly complex plans */608 /* Guard against stack overflow due to overly complex plans */
609 check_stack_depth();609 check_stack_depth();
610 610 
@@ -674,7 +674,13 @@ static Plan* create_plan_recurse(PlannerInfo* root, Path* best_path)
674 plan = create_unique_plan(root, (UniquePath*)best_path);674 plan = create_unique_plan(root, (UniquePath*)best_path);
675 break;675 break;
676 case T_PartIterator:676 case T_PartIterator:
677- plan = (Plan*)create_globalpartInterator_plan(root, (PartIteratorPath*)best_path);677+ if (may_change == NULL) {
678+ plan = (Plan*)create_globalpartInterator_plan(root, (PartIteratorPath*)best_path, &change);
679+ } else {
680+ plan = (Plan*)create_globalpartInterator_plan(root, (PartIteratorPath*)best_path, may_change);
681+ change = *may_change;
682+ }
683+
678 break;684 break;
679#ifdef PGXC685#ifdef PGXC
680 case T_RemoteQuery:686 case T_RemoteQuery:
@@ -708,8 +714,11 @@ static Plan* create_plan_recurse(PlannerInfo* root, Path* best_path)
708 /*714 /*
709 * Set smp info for Plan.715 * Set smp info for Plan.
710 * If the plan is on CN, we should not parallelize.716 * If the plan is on CN, we should not parallelize.
717+ * For append path, we have manage it before, do not bother here.
711 */718 */
712- plan->dop = is_execute_on_datanodes(plan) ? SET_DOP(best_path->dop) : 1;719+ if (!change) {
720+ plan->dop = is_execute_on_datanodes(plan) ? SET_DOP(best_path->dop) : 1;
721+ }
713 722 
714 return plan;723 return plan;
715}724}
@@ -727,8 +736,8 @@ Plan* create_stream_plan(PlannerInfo* root, StreamPath* best_path)
727 Stream* stream = NULL;736 Stream* stream = NULL;
728 Plan* subplan = NULL;737 Plan* subplan = NULL;
729 Plan* plan = NULL;738 Plan* plan = NULL;
730- 739+ bool may_change = false;
731- subplan = create_plan_recurse(root, best_path->subpath);740+ subplan = create_plan_recurse(root, best_path->subpath, &may_change);
732 741 
733 if (is_execute_on_coordinator(subplan)) {742 if (is_execute_on_coordinator(subplan)) {
734 return subplan;743 return subplan;
@@ -800,7 +809,9 @@ Plan* create_stream_plan(PlannerInfo* root, StreamPath* best_path)
800 /* Copy the smpDesc from path */809 /* Copy the smpDesc from path */
801 if (best_path->smpDesc) {810 if (best_path->smpDesc) {
802 stream->smpDesc.consumerDop = best_path->smpDesc->consumerDop > 1 ? best_path->smpDesc->consumerDop : 1;811 stream->smpDesc.consumerDop = best_path->smpDesc->consumerDop > 1 ? best_path->smpDesc->consumerDop : 1;
803- stream->smpDesc.producerDop = best_path->smpDesc->producerDop > 1 ? best_path->smpDesc->producerDop : 1;812+ if (may_change == false) {
813+ stream->smpDesc.producerDop = best_path->smpDesc->producerDop > 1 ? best_path->smpDesc->producerDop : 1;
814+ }
804 plan->dop = stream->smpDesc.consumerDop;815 plan->dop = stream->smpDesc.consumerDop;
805 stream->smpDesc.distriType = best_path->smpDesc->distriType;816 stream->smpDesc.distriType = best_path->smpDesc->distriType;
806 817 
@@ -6799,7 +6810,7 @@ SubqueryScan* make_subqueryscan(List* qptlist, List* qpqual, Index scanrelid, Pl
6799 * Hypothetical index does not support partition index unusable.6810 * Hypothetical index does not support partition index unusable.
6800 *6811 *
6801 */6812 */
6802-Plan* create_globalpartInterator_plan(PlannerInfo* root, PartIteratorPath* pIterpath)6813+Plan* create_globalpartInterator_plan(PlannerInfo* root, PartIteratorPath* pIterpath, bool* may_change)
6803{6814{
6804 Plan* plan = NULL;6815 Plan* plan = NULL;
6805 6816 
@@ -6835,6 +6846,9 @@ Plan* create_globalpartInterator_plan(PlannerInfo* root, PartIteratorPath* pIter
6835 6846 
6836 switch (usable_type) {6847 switch (usable_type) {
6837 case INDEXES_FULL_USABLE: {6848 case INDEXES_FULL_USABLE: {
6849+ if (may_change != NULL) {
6850+ *may_change = false;
6851+ }
6838 /* Create partition iterator with index scan plan. */6852 /* Create partition iterator with index scan plan. */
6839 GlobalPartIterator* gpIter = (GlobalPartIterator*)palloc(sizeof(GlobalPartIterator));6853 GlobalPartIterator* gpIter = (GlobalPartIterator*)palloc(sizeof(GlobalPartIterator));
6840 gpIter->curItrs = pIterpath->subPath->parent->partItrs;6854 gpIter->curItrs = pIterpath->subPath->parent->partItrs;
@@ -6842,6 +6856,9 @@ Plan* create_globalpartInterator_plan(PlannerInfo* root, PartIteratorPath* pIter
6842 plan = (Plan*)create_partIterator_plan(root, pIterpath, gpIter);6856 plan = (Plan*)create_partIterator_plan(root, pIterpath, gpIter);
6843 } break;6857 } break;
6844 case INDEXES_NONE_USABLE: {6858 case INDEXES_NONE_USABLE: {
6859+ if (may_change != NULL) {
6860+ *may_change = true;
6861+ }
6845 /* Create partition iterator with seq scan plan. */6862 /* Create partition iterator with seq scan plan. */
6846 GlobalPartIterator* gpIter = (GlobalPartIterator*)palloc(sizeof(GlobalPartIterator));6863 GlobalPartIterator* gpIter = (GlobalPartIterator*)palloc(sizeof(GlobalPartIterator));
6847 gpIter->curItrs = pIterpath->subPath->parent->partItrs_for_index_unusable;6864 gpIter->curItrs = pIterpath->subPath->parent->partItrs_for_index_unusable;
@@ -6850,6 +6867,9 @@ Plan* create_globalpartInterator_plan(PlannerInfo* root, PartIteratorPath* pIter
6850 plan = (Plan*)create_partIterator_plan(root, pIterpath, gpIter);6867 plan = (Plan*)create_partIterator_plan(root, pIterpath, gpIter);
6851 } break;6868 } break;
6852 case INDEXES_PARTIAL_USABLE: {6869 case INDEXES_PARTIAL_USABLE: {
6870+ if (may_change != NULL) {
6871+ *may_change = true;
6872+ }
6853 /* Create partition iterator with partial index and partial seq scan plan. */6873 /* Create partition iterator with partial index and partial seq scan plan. */
6854 Append* appendPlan = NULL;6874 Append* appendPlan = NULL;
6855 List* subplans = NIL;6875 List* subplans = NIL;
@@ -6881,10 +6901,19 @@ Plan* create_globalpartInterator_plan(PlannerInfo* root, PartIteratorPath* pIter
6881 plan = (Plan*)appendPlan;6901 plan = (Plan*)appendPlan;
6882 6902 
6883#ifdef STREAMPLAN6903#ifdef STREAMPLAN
6904+ int record_dop = plan->dop;
6884 inherit_plan_locator_info(plan, piterIndexPlan);6905 inherit_plan_locator_info(plan, piterIndexPlan);
6906+ /*
6907+ * For append path, it could be paralleized only if all the paths are parallelized,
6908+ * we have managed it in optplan_make_append(), do not bother here.
6909+ */
6910+ plan->dop = record_dop;
6885#endif6911#endif
6886 } break;6912 } break;
6887 default:6913 default:
6914+ if (may_change != NULL) {
6915+ *may_change = false;
6916+ }
6888 break;6917 break;
6889 }6918 }
6890 } else if (is_pwj_path((Path*)pIterpath)) {6919 } else if (is_pwj_path((Path*)pIterpath)) {
@@ -9450,6 +9479,10 @@ static Plan* parallel_limit_sort(
9450 plan = (Plan*)make_limit(root, lefttree, limitOffset, limitCount, offset_est, count_est, false);9479 plan = (Plan*)make_limit(root, lefttree, limitOffset, limitCount, offset_est, count_est, false);
9451 plan = create_local_gather(plan);9480 plan = create_local_gather(plan);
9452 plan = (Plan*)make_sort_from_pathkeys(root, plan, root->sort_pathkeys, -1.0);9481 plan = (Plan*)make_sort_from_pathkeys(root, plan, root->sort_pathkeys, -1.0);
9482+ } else if (root->sort_pathkeys && (IsA(lefttree, IndexOnlyScan) || IsA(lefttree, IndexScan))) {
9483+ plan = (Plan*)make_limit(root, lefttree, limitOffset, limitCount, offset_est, count_est, false);
9484+ plan = create_local_gather(plan);
9485+ plan = (Plan*)make_sort_from_pathkeys(root, plan, root->sort_pathkeys, -1.0);
9453 } else {9486 } else {
9454#ifdef ENABLE_MULTIPLE_NODES9487#ifdef ENABLE_MULTIPLE_NODES
9455 plan = create_local_gather(lefttree);9488 plan = create_local_gather(lefttree);
@@ -349,6 +349,11 @@ StreamNodeGroup::StreamNodeGroup()
349 pthread_mutex_init(&m_mutex, NULL);349 pthread_mutex_init(&m_mutex, NULL);
350 pthread_mutex_init(&m_recursiveMutex, NULL);350 pthread_mutex_init(&m_recursiveMutex, NULL);
351 pthread_cond_init(&m_cond, NULL);351 pthread_cond_init(&m_cond, NULL);
352+ pthread_mutex_init(&m_index_smp_mutex, NULL);
353+ pthread_condattr_t attr;
354+ pthread_condattr_init(&attr);
355+ pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
356+ pthread_cond_init(&m_index_smp_cond, &attr);
352 m_pid = gs_thread_self();357 m_pid = gs_thread_self();
353 m_streamPairList = NULL;358 m_streamPairList = NULL;
354 m_streamConsumerList = NULL;359 m_streamConsumerList = NULL;
@@ -363,6 +368,8 @@ StreamNodeGroup::StreamNodeGroup()
363 m_portal = NULL;368 m_portal = NULL;
364#endif369#endif
365 m_spiLevel = u_sess->SPI_cxt._connected;370 m_spiLevel = u_sess->SPI_cxt._connected;
371+ parallel_indexscan_map = NULL;
372+ parallel_indexscan_size = 0;
366}373}
367 374 
368StreamNodeGroup::~StreamNodeGroup()375StreamNodeGroup::~StreamNodeGroup()
@@ -1145,7 +1152,15 @@ void StreamNodeGroup::deInit(StreamObjStatus status)
1145 }1152 }
1146 1153 
1147 m_streamRuntimeContext = NULL;1154 m_streamRuntimeContext = NULL;
1148- 1155+ for (int i = 0; i < parallel_indexscan_size; i++) {
1156+ if (parallel_indexscan_map != NULL && parallel_indexscan_map[i] != nullptr) {
1157+ pfree_ext(parallel_indexscan_map[i]);
1158+ }
1159+ }
1160+ if (parallel_indexscan_map != NULL) {
1161+ pfree_ext(parallel_indexscan_map);
1162+ }
1163+ parallel_indexscan_size = 0;
1149 /*1164 /*
1150 * 1. If length of m_streamPairList is not the same as m_size(number of stream exists in plan tree),1165 * 1. If length of m_streamPairList is not the same as m_size(number of stream exists in plan tree),
1151 * it means that stream connection and stream thread initialization may not finish yet due to1166 * it means that stream connection and stream thread initialization may not finish yet due to
@@ -1166,8 +1181,10 @@ void StreamNodeGroup::deInit(StreamObjStatus status)
1166 streamLock2.unLock();1181 streamLock2.unLock();
1167#endif1182#endif
1168 pthread_cond_destroy(&m_cond);1183 pthread_cond_destroy(&m_cond);
1184+ pthread_cond_destroy(&m_index_smp_cond);
1169 pthread_mutex_destroy(&m_mutex);1185 pthread_mutex_destroy(&m_mutex);
1170 pthread_mutex_destroy(&m_recursiveMutex);1186 pthread_mutex_destroy(&m_recursiveMutex);
1187+ pthread_mutex_destroy(&m_index_smp_mutex);
1171}1188}
1172 1189 
1173/*1190/*
@@ -388,7 +388,8 @@ static void HandleStreamSigjmp()
388 AtEOXact_SysDBCache(false);388 AtEOXact_SysDBCache(false);
389 389 
390 LWLockReleaseAll();390 LWLockReleaseAll();
391- 391+ ReleaseResownerOutOfTransaction();
392+ ReleaseResownerForStreamError();
392 if (u_sess->stream_cxt.producer_obj != NULL) {393 if (u_sess->stream_cxt.producer_obj != NULL) {
393 u_sess->stream_cxt.producer_obj->reportError();394 u_sess->stream_cxt.producer_obj->reportError();
394 }395 }
@@ -9267,7 +9267,9 @@ int PostgresMain(int argc, char* argv[], const char* dbname, const char* usernam
9267 AbortCurrentTransaction();9267 AbortCurrentTransaction();
9268 }9268 }
9269 }9269 }
9270- 9270+ if (t_thrd.utils_cxt.CurrentResourceOwner == NULL) {
9271+ t_thrd.utils_cxt.CurrentResourceOwner = t_thrd.utils_cxt.ThreadRootResourceOwner;
9272+ }
9271 ReleaseResownerOutOfTransaction();9273 ReleaseResownerOutOfTransaction();
9272 9274 
9273 /* release resource held by lsc */9275 /* release resource held by lsc */
@@ -9697,7 +9699,7 @@ int PostgresMain(int argc, char* argv[], const char* dbname, const char* usernam
9697 /* we use t_thrd.top_mem_cxt to remember all node info in this cluster. */9699 /* we use t_thrd.top_mem_cxt to remember all node info in this cluster. */
9698 MemoryContext old = MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt);9700 MemoryContext old = MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt);
9699 9701 
9700- t_thrd.utils_cxt.CurrentResourceOwner = ResourceOwnerCreate(NULL, "ForPGXCNodes",9702+ t_thrd.utils_cxt.CurrentResourceOwner = ResourceOwnerCreate(currentOwner, "ForPGXCNodes",
9701 THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_COMMUNICATION));9703 THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_COMMUNICATION));
9702 9704 
9703 /* Update node table in the shared memory */9705 /* Update node table in the shared memory */
@@ -1113,7 +1113,11 @@ static void knl_t_utils_init(knl_t_utils_context* utils_cxt)
1113 int rc = memset_s(1113 int rc = memset_s(
1114 utils_cxt->valueItemArr, MAX_PARTKEY_NUMS * sizeof(Const*), 0, MAX_PARTKEY_NUMS * sizeof(Const*));1114 utils_cxt->valueItemArr, MAX_PARTKEY_NUMS * sizeof(Const*), 0, MAX_PARTKEY_NUMS * sizeof(Const*));
1115 securec_check(rc, "\0", "\0");1115 securec_check(rc, "\0", "\0");
1116- utils_cxt->CurrentResourceOwner = NULL;1116+ utils_cxt->ThreadRootResourceOwner = NULL;
1117+ utils_cxt->ThreadRootResourceOwner =
1118+ ResourceOwnerCreate(NULL, "ThreadRootResourceOwner", THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DEFAULT));
1119+ utils_cxt->CurrentResourceOwner = utils_cxt->ThreadRootResourceOwner;
1120+ utils_cxt->OutOfTransResourceOwner = utils_cxt->ThreadRootResourceOwner;
1117 utils_cxt->STPSavedResourceOwner = NULL;1121 utils_cxt->STPSavedResourceOwner = NULL;
1118 utils_cxt->CurTransactionResourceOwner = NULL;1122 utils_cxt->CurTransactionResourceOwner = NULL;
1119 utils_cxt->TopTransactionResourceOwner = NULL;1123 utils_cxt->TopTransactionResourceOwner = NULL;
@@ -428,7 +428,7 @@ void ExecReScanIndexOnlyScan(IndexOnlyScanState* node)
428 node->ioss_ScanKeys,428 node->ioss_ScanKeys,
429 node->ioss_NumScanKeys,429 node->ioss_NumScanKeys,
430 node->ioss_OrderByKeys,430 node->ioss_OrderByKeys,
431- node->ioss_NumOrderByKeys);431+ node->ioss_NumOrderByKeys, node->m_dop, node->m_plan_node_id);
432 432 
433 ExecScanReScan(&node->ss);433 ExecScanReScan(&node->ss);
434}434}
@@ -577,7 +577,9 @@ IndexOnlyScanState* ExecInitIndexOnlyScan(IndexOnlyScan* node, EState* estate, i
577 * create expression context for node577 * create expression context for node
578 */578 */
579 ExecAssignExprContext(estate, &indexstate->ss.ps);579 ExecAssignExprContext(estate, &indexstate->ss.ps);
580- 580+ int dop = node->scan.plan.dop;
581+ indexstate->m_dop = dop > 1 ? dop : 1;
582+ indexstate->m_plan_node_id = indexstate->ss.ps.plan->plan_node_id;
581 indexstate->ss.ps.ps_vec_TupFromTlist = false;583 indexstate->ss.ps.ps_vec_TupFromTlist = false;
582 584 
583 /*585 /*
@@ -770,7 +772,7 @@ IndexOnlyScanState* ExecInitIndexOnlyScan(IndexOnlyScan* node, EState* estate, i
770 scanSnap,772 scanSnap,
771 indexstate->ioss_NumScanKeys,773 indexstate->ioss_NumScanKeys,
772 indexstate->ioss_NumOrderByKeys,774 indexstate->ioss_NumOrderByKeys,
773- (ScanState*)indexstate);775+ (ScanState*)indexstate, NULL, dop, indexstate->ss.ps.plan->plan_node_id);
774 }776 }
775 }777 }
776 } else {778 } else {
@@ -813,7 +815,7 @@ IndexOnlyScanState* ExecInitIndexOnlyScan(IndexOnlyScan* node, EState* estate, i
813 indexstate->ioss_NumScanKeys,815 indexstate->ioss_NumScanKeys,
814 indexstate->ioss_NumOrderByKeys,816 indexstate->ioss_NumOrderByKeys,
815 (ScanState*)indexstate,817 (ScanState*)indexstate,
816- paralleDesc);818+ paralleDesc, dop, indexstate->ss.ps.plan->plan_node_id);
817 }819 }
818 820 
819 /*821 /*
@@ -833,7 +835,7 @@ IndexOnlyScanState* ExecInitIndexOnlyScan(IndexOnlyScan* node, EState* estate, i
833 indexstate->ioss_ScanKeys,835 indexstate->ioss_ScanKeys,
834 indexstate->ioss_NumScanKeys,836 indexstate->ioss_NumScanKeys,
835 indexstate->ioss_OrderByKeys,837 indexstate->ioss_OrderByKeys,
836- indexstate->ioss_NumOrderByKeys);838+ indexstate->ioss_NumOrderByKeys, dop, indexstate->ss.ps.plan->plan_node_id);
837 } else {839 } else {
838 indexstate->ss.ps.stubType = PST_Scan;840 indexstate->ss.ps.stubType = PST_Scan;
839 }841 }
@@ -889,7 +891,7 @@ static void ExecInitNextIndexPartitionForIndexScanOnly(IndexOnlyScanState* node)
889 node->ss.currentSlot = (int)param->value;891 node->ss.currentSlot = (int)param->value;
890 subPartParamno = plan->scan.plan.subparamno;892 subPartParamno = plan->scan.plan.subparamno;
891 subPartParam = &(node->ss.ps.state->es_param_exec_vals[subPartParamno]);893 subPartParam = &(node->ss.ps.state->es_param_exec_vals[subPartParamno]);
892- 894+ int dop = plan->scan.plan.dop > 1 ? plan->scan.plan.dop : 1;
893 /* construct a dummy table relation with the next table partition*/895 /* construct a dummy table relation with the next table partition*/
894 currentpartition = (Partition)list_nth(node->ss.partitions, node->ss.currentSlot);896 currentpartition = (Partition)list_nth(node->ss.partitions, node->ss.currentSlot);
895 currentpartitionrel = partitionGetRelation(node->ss.ss_currentRelation, currentpartition);897 currentpartitionrel = partitionGetRelation(node->ss.ss_currentRelation, currentpartition);
@@ -924,20 +926,14 @@ static void ExecInitNextIndexPartitionForIndexScanOnly(IndexOnlyScanState* node)
924 node->ioss_CurrentIndexPartition = currentindexpartitionrel;926 node->ioss_CurrentIndexPartition = currentindexpartitionrel;
925 927 
926 /* Initialize scan descriptor. */928 /* Initialize scan descriptor. */
927- node->ioss_ScanDesc = scan_handler_idx_beginscan(node->ss.ss_currentPartition,929+ node->ioss_ScanDesc = scan_handler_idx_beginscan(node->ss.ss_currentPartition, node->ioss_CurrentIndexPartition,
928- node->ioss_CurrentIndexPartition,930+ scanSnap, node->ioss_NumScanKeys, node->ioss_NumOrderByKeys,
929- scanSnap,931+ (ScanState*)node, NULL, dop, node->ss.ps.plan->plan_node_id);
930- node->ioss_NumScanKeys,
931- node->ioss_NumOrderByKeys,
932- (ScanState*)node);
933 GetIndexScanDesc(node->ioss_ScanDesc)->xs_want_itup = true;932 GetIndexScanDesc(node->ioss_ScanDesc)->xs_want_itup = true;
934- scan_handler_idx_rescan_local(node->ioss_ScanDesc,933+ scan_handler_idx_rescan_local(node->ioss_ScanDesc, node->ioss_ScanKeys, node->ioss_NumScanKeys,
935- node->ioss_ScanKeys,934+ node->ioss_OrderByKeys, node->ioss_NumOrderByKeys, dop,
936- node->ioss_NumScanKeys,935+ node->ss.ps.plan->plan_node_id);
937- node->ioss_OrderByKeys,
938- node->ioss_NumOrderByKeys);
939 heap_close(heapRelation, AccessShareLock);936 heap_close(heapRelation, AccessShareLock);
940- 
941}937}
942 938 
943/*939/*
@@ -569,8 +569,8 @@ void ExecReScanIndexScan(IndexScanState* node)
569 }569 }
570 570 
571 /* reset index scan */571 /* reset index scan */
572- scan_handler_idx_rescan(572+ scan_handler_idx_rescan(node->iss_ScanDesc, node->iss_ScanKeys, node->iss_NumScanKeys, node->iss_OrderByKeys,
573- node->iss_ScanDesc, node->iss_ScanKeys, node->iss_NumScanKeys, node->iss_OrderByKeys, node->iss_NumOrderByKeys);573+ node->iss_NumOrderByKeys, node->m_dop, node->m_plan_node_id);
574 574 
575 scan_handler_idx_rescan_parallel(node->iss_ScanDesc);575 scan_handler_idx_rescan_parallel(node->iss_ScanDesc);
576 576 
@@ -861,7 +861,7 @@ void ExecInitIndexRelation(IndexScanState* node, EState* estate, int eflags)
861 Snapshot scanSnap;861 Snapshot scanSnap;
862 Relation current_relation = index_state->ss.ss_currentRelation;862 Relation current_relation = index_state->ss.ss_currentRelation;
863 IndexScan *index_scan = (IndexScan *)node->ss.ps.plan;863 IndexScan *index_scan = (IndexScan *)node->ss.ps.plan;
864- 864+ int dop = index_scan->scan.plan.dop;
865 /*865 /*
866 * Choose user-specified snapshot if TimeCapsule clause exists, otherwise 866 * Choose user-specified snapshot if TimeCapsule clause exists, otherwise
867 * estate->es_snapshot instead.867 * estate->es_snapshot instead.
@@ -925,7 +925,7 @@ void ExecInitIndexRelation(IndexScanState* node, EState* estate, int eflags)
925 scanSnap,925 scanSnap,
926 index_state->iss_NumScanKeys,926 index_state->iss_NumScanKeys,
927 index_state->iss_NumOrderByKeys,927 index_state->iss_NumOrderByKeys,
928- (ScanState*)index_state);928+ (ScanState*)index_state, NULL, dop, node->ss.ps.plan->plan_node_id);
929 }929 }
930 }930 }
931 } else {931 } else {
@@ -968,7 +968,7 @@ void ExecInitIndexRelation(IndexScanState* node, EState* estate, int eflags)
968 index_state->iss_NumScanKeys,968 index_state->iss_NumScanKeys,
969 index_state->iss_NumOrderByKeys,969 index_state->iss_NumOrderByKeys,
970 (ScanState*)index_state,970 (ScanState*)index_state,
971- paralleDesc);971+ paralleDesc, dop, node->ss.ps.plan->plan_node_id);
972 }972 }
973 973 
974 return;974 return;
@@ -1176,7 +1176,8 @@ IndexScanState* ExecInitIndexScan(IndexScan* node, EState* estate, int eflags)
1176 1176 
1177 /* deal with partition info */1177 /* deal with partition info */
1178 ExecInitIndexRelation(index_state, estate, eflags);1178 ExecInitIndexRelation(index_state, estate, eflags);
1179- 1179+ index_state->m_dop = index_state->ss.ps.plan->dop > 1 ? index_state->ss.ps.plan->dop : 1;
1180+ index_state->m_plan_node_id = index_state->ss.ps.plan->plan_node_id;
1180 /*1181 /*
1181 * If no run-time keys to calculate, go ahead and pass the scankeys to the1182 * If no run-time keys to calculate, go ahead and pass the scankeys to the
1182 * index AM.1183 * index AM.
@@ -1188,7 +1189,7 @@ IndexScanState* ExecInitIndexScan(IndexScan* node, EState* estate, int eflags)
1188 index_state->iss_ScanKeys,1189 index_state->iss_ScanKeys,
1189 index_state->iss_NumScanKeys,1190 index_state->iss_NumScanKeys,
1190 index_state->iss_OrderByKeys,1191 index_state->iss_OrderByKeys,
1191- index_state->iss_NumOrderByKeys);1192+ index_state->iss_NumOrderByKeys, node->scan.plan.dop, node->scan.plan.plan_node_id);
1192 }1193 }
1193 1194 
1194 /*1195 /*
@@ -1768,19 +1769,21 @@ static void ExecInitNextPartitionForIndexScan(IndexScanState* node)
1768 /* update scan-related partition */1769 /* update scan-related partition */
1769 releaseDummyRelation(&(node->ss.ss_currentPartition));1770 releaseDummyRelation(&(node->ss.ss_currentPartition));
1770 node->ss.ss_currentPartition = current_partition_rel;1771 node->ss.ss_currentPartition = current_partition_rel;
1771- 1772+ int dop = plan->scan.plan.dop > 1 ? plan->scan.plan.dop : 1;
1773+ node->m_dop = dop;
1774+ node->m_plan_node_id = node->ss.ps.plan->plan_node_id;
1772 /* Initialize scan descriptor. */1775 /* Initialize scan descriptor. */
1773 node->iss_ScanDesc = scan_handler_idx_beginscan(node->ss.ss_currentPartition,1776 node->iss_ScanDesc = scan_handler_idx_beginscan(node->ss.ss_currentPartition,
1774 node->iss_CurrentIndexPartition,1777 node->iss_CurrentIndexPartition,
1775 scanSnap,1778 scanSnap,
1776 node->iss_NumScanKeys,1779 node->iss_NumScanKeys,
1777 node->iss_NumOrderByKeys,1780 node->iss_NumOrderByKeys,
1778- (ScanState*)node);1781+ (ScanState*)node, NULL, dop, node->ss.ps.plan->plan_node_id);
1779 1782 
1780 if (node->iss_ScanDesc != NULL) {1783 if (node->iss_ScanDesc != NULL) {
1781 scan_handler_idx_rescan_local(1784 scan_handler_idx_rescan_local(
1782 node->iss_ScanDesc, node->iss_ScanKeys, node->iss_NumScanKeys,1785 node->iss_ScanDesc, node->iss_ScanKeys, node->iss_NumScanKeys,
1783- node->iss_OrderByKeys, node->iss_NumOrderByKeys);1786+ node->iss_OrderByKeys, node->iss_NumOrderByKeys, dop, node->ss.ps.plan->plan_node_id);
1784 }1787 }
1785 1788 
1786 heap_close(heapRelation, AccessShareLock);1789 heap_close(heapRelation, AccessShareLock);
@@ -258,7 +258,7 @@ CStoreIndexScanState* ExecInitCstoreIndexScan(CStoreIndexScan* node, EState* est
258 CStoreScan* indexScan = NULL;258 CStoreScan* indexScan = NULL;
259 CStoreScanState* scanstate = NULL;259 CStoreScanState* scanstate = NULL;
260 errno_t rc = EOK;260 errno_t rc = EOK;
261- 261+ int dop = node->scan.plan.dop;
262 // Sanity checks262 // Sanity checks
263 //263 //
264 if (node->indexorderby != NULL || node->indexorderbyorig != NULL) {264 if (node->indexorderby != NULL || node->indexorderbyorig != NULL) {
@@ -383,13 +383,8 @@ CStoreIndexScanState* ExecInitCstoreIndexScan(CStoreIndexScan* node, EState* est
383 /* cbtree index scan */383 /* cbtree index scan */
384 CBTreeScanState* btreeIndexScan = makeNode(CBTreeScanState);384 CBTreeScanState* btreeIndexScan = makeNode(CBTreeScanState);
385 btreeIndexScan->m_indexScanTList = indexScanTList;385 btreeIndexScan->m_indexScanTList = indexScanTList;
386- BuildCBtreeIndexScan(btreeIndexScan,386+ BuildCBtreeIndexScan(btreeIndexScan, (ScanState*)scanstate, (Scan*)node, estate, indexRel, node->indexqual,
387- (ScanState*)scanstate,387+ node->indexorderby, dop);
388- (Scan*)node,
389- estate,
390- indexRel,
391- node->indexqual,
392- node->indexorderby);
393 indexstate->m_btreeIndexScan = btreeIndexScan;388 indexstate->m_btreeIndexScan = btreeIndexScan;
394 indexstate->part_id = indexstate->m_btreeIndexScan->ss.part_id;389 indexstate->part_id = indexstate->m_btreeIndexScan->ss.part_id;
395 indexstate->m_btreeIndexOnlyScan = NULL;390 indexstate->m_btreeIndexOnlyScan = NULL;
@@ -398,13 +393,8 @@ CStoreIndexScanState* ExecInitCstoreIndexScan(CStoreIndexScan* node, EState* est
398 /* cbtree index only scan */393 /* cbtree index only scan */
399 CBTreeOnlyScanState* btreeIndexOnlyScan = makeNode(CBTreeOnlyScanState);394 CBTreeOnlyScanState* btreeIndexOnlyScan = makeNode(CBTreeOnlyScanState);
400 btreeIndexOnlyScan->m_indexScanTList = indexScanTList;395 btreeIndexOnlyScan->m_indexScanTList = indexScanTList;
401- BuildCBtreeIndexOnlyScan(btreeIndexOnlyScan,396+ BuildCBtreeIndexOnlyScan(btreeIndexOnlyScan, (ScanState*)scanstate, (Scan*)node, estate, indexRel,
402- (ScanState*)scanstate,397+ node->indexqual, node->indexorderby, dop);
403- (Scan*)node,
404- estate,
405- indexRel,
406- node->indexqual,
407- node->indexorderby);
408 indexstate->m_btreeIndexOnlyScan = btreeIndexOnlyScan;398 indexstate->m_btreeIndexOnlyScan = btreeIndexOnlyScan;
409 indexstate->part_id = indexstate->m_btreeIndexOnlyScan->ss.part_id;399 indexstate->part_id = indexstate->m_btreeIndexOnlyScan->ss.part_id;
410 indexstate->m_btreeIndexScan = NULL;400 indexstate->m_btreeIndexScan = NULL;
@@ -624,7 +614,7 @@ static List* FixIndexScanTargetList(CStoreIndexScan* node, CStoreIndexScanState*
624 * @IN param indexorderby: the ordered qual on the index columns614 * @IN param indexorderby: the ordered qual on the index columns
625 */615 */
626void BuildCBtreeIndexScan(CBTreeScanState* btreeIndexScan, ScanState* scanstate, Scan* node, EState* estate,616void BuildCBtreeIndexScan(CBTreeScanState* btreeIndexScan, ScanState* scanstate, Scan* node, EState* estate,
627- Relation indexRel, List* indexqual, List* indexorderby)617+ Relation indexRel, List* indexqual, List* indexorderby, int dop)
628{618{
629 int sortMem = SET_NODEMEM(node->plan.operatorMemKB[0], node->plan.dop);619 int sortMem = SET_NODEMEM(node->plan.operatorMemKB[0], node->plan.dop);
630 int maxMem = (node->plan.operatorMaxMem > 0) ? (node->plan.operatorMaxMem / SET_DOP(node->plan.dop)) : 0;620 int maxMem = (node->plan.operatorMaxMem > 0) ? (node->plan.operatorMaxMem / SET_DOP(node->plan.dop)) : 0;
@@ -735,19 +725,17 @@ void BuildCBtreeIndexScan(CBTreeScanState* btreeIndexScan, ScanState* scanstate,
735 partitionGetRelation(btreeIndexScan->iss_RelationDesc, currentindex);725 partitionGetRelation(btreeIndexScan->iss_RelationDesc, currentindex);
736 726 
737 /* Initialize scan descriptor for partitioned table */727 /* Initialize scan descriptor for partitioned table */
738- btreeIndexScan->iss_ScanDesc = index_beginscan(btreeIndexScan->ss.ss_currentPartition,728+ btreeIndexScan->iss_ScanDesc =
739- btreeIndexScan->iss_CurrentIndexPartition,729+ index_beginscan(btreeIndexScan->ss.ss_currentPartition, btreeIndexScan->iss_CurrentIndexPartition,
740- estate->es_snapshot,730+ estate->es_snapshot, btreeIndexScan->iss_NumScanKeys,
741- btreeIndexScan->iss_NumScanKeys,731+ btreeIndexScan->iss_NumOrderByKeys, NULL, NULL, dop, node->plan.plan_node_id);
742- btreeIndexScan->iss_NumOrderByKeys);
743 Assert(PointerIsValid(btreeIndexScan->iss_ScanDesc));732 Assert(PointerIsValid(btreeIndexScan->iss_ScanDesc));
744 }733 }
745 } else {734 } else {
746- btreeIndexScan->iss_ScanDesc = index_beginscan(btreeIndexScan->ss.ss_currentPartition,735+ btreeIndexScan->iss_ScanDesc =
747- btreeIndexScan->iss_RelationDesc,736+ index_beginscan(btreeIndexScan->ss.ss_currentPartition, btreeIndexScan->iss_RelationDesc,
748- estate->es_snapshot,737+ estate->es_snapshot, btreeIndexScan->iss_NumScanKeys, btreeIndexScan->iss_NumOrderByKeys,
749- btreeIndexScan->iss_NumScanKeys,738+ NULL, NULL, dop, node->plan.plan_node_id);
750- btreeIndexScan->iss_NumOrderByKeys);
751 }739 }
752 740 
753 GetIndexScanDesc(btreeIndexScan->iss_ScanDesc)->xs_want_itup = false;741 GetIndexScanDesc(btreeIndexScan->iss_ScanDesc)->xs_want_itup = false;
@@ -757,11 +745,9 @@ void BuildCBtreeIndexScan(CBTreeScanState* btreeIndexScan, ScanState* scanstate,
757 * index AM.745 * index AM.
758 */746 */
759 if (btreeIndexScan->iss_NumRuntimeKeys == 0 && PointerIsValid(btreeIndexScan->iss_ScanDesc))747 if (btreeIndexScan->iss_NumRuntimeKeys == 0 && PointerIsValid(btreeIndexScan->iss_ScanDesc))
760- scan_handler_idx_rescan(btreeIndexScan->iss_ScanDesc,748+ scan_handler_idx_rescan(btreeIndexScan->iss_ScanDesc, btreeIndexScan->iss_ScanKeys,
761- btreeIndexScan->iss_ScanKeys,749+ btreeIndexScan->iss_NumScanKeys, btreeIndexScan->iss_OrderByKeys,
762- btreeIndexScan->iss_NumScanKeys,750+ btreeIndexScan->iss_NumOrderByKeys, dop, node->plan.plan_node_id);
763- btreeIndexScan->iss_OrderByKeys,
764- btreeIndexScan->iss_NumOrderByKeys);
765}751}
766 752 
767/*753/*
@@ -775,7 +761,7 @@ void BuildCBtreeIndexScan(CBTreeScanState* btreeIndexScan, ScanState* scanstate,
775 * @IN param indexorderby: the ordered qual on the index columns761 * @IN param indexorderby: the ordered qual on the index columns
776 */762 */
777void BuildCBtreeIndexOnlyScan(CBTreeOnlyScanState* btreeIndexOnlyScan, ScanState* scanstate, Scan* node, EState* estate,763void BuildCBtreeIndexOnlyScan(CBTreeOnlyScanState* btreeIndexOnlyScan, ScanState* scanstate, Scan* node, EState* estate,
778- Relation indexRel, List* indexqual, List* indexorderby)764+ Relation indexRel, List* indexqual, List* indexorderby, int dop)
779{765{
780 int sortMem = u_sess->attr.attr_memory.work_mem;766 int sortMem = u_sess->attr.attr_memory.work_mem;
781 int maxMem = 0;767 int maxMem = 0;
@@ -894,20 +880,17 @@ void BuildCBtreeIndexOnlyScan(CBTreeOnlyScanState* btreeIndexOnlyScan, ScanState
894 partitionGetRelation(btreeIndexOnlyScan->ioss_RelationDesc, currentindex);880 partitionGetRelation(btreeIndexOnlyScan->ioss_RelationDesc, currentindex);
895 881 
896 /* Initialize scan descriptor for partitioned table */882 /* Initialize scan descriptor for partitioned table */
897- btreeIndexOnlyScan->ioss_ScanDesc =883+ btreeIndexOnlyScan->ioss_ScanDesc = index_beginscan(
898- index_beginscan(btreeIndexOnlyScan->ss.ss_currentPartition,884+ btreeIndexOnlyScan->ss.ss_currentPartition, btreeIndexOnlyScan->ioss_CurrentIndexPartition,
899- btreeIndexOnlyScan->ioss_CurrentIndexPartition,885+ estate->es_snapshot, btreeIndexOnlyScan->ioss_NumScanKeys, btreeIndexOnlyScan->ioss_NumOrderByKeys,
900- estate->es_snapshot,886+ NULL, NULL, dop, node->plan.plan_node_id);
901- btreeIndexOnlyScan->ioss_NumScanKeys,
902- btreeIndexOnlyScan->ioss_NumOrderByKeys);
903 Assert(PointerIsValid(btreeIndexOnlyScan->ioss_ScanDesc));887 Assert(PointerIsValid(btreeIndexOnlyScan->ioss_ScanDesc));
904 }888 }
905 } else {889 } else {
906- btreeIndexOnlyScan->ioss_ScanDesc = index_beginscan(btreeIndexOnlyScan->ss.ss_currentPartition,890+ btreeIndexOnlyScan->ioss_ScanDesc =
907- btreeIndexOnlyScan->ioss_RelationDesc,891+ index_beginscan(btreeIndexOnlyScan->ss.ss_currentPartition, btreeIndexOnlyScan->ioss_RelationDesc,
908- estate->es_snapshot,892+ estate->es_snapshot, btreeIndexOnlyScan->ioss_NumScanKeys,
909- btreeIndexOnlyScan->ioss_NumScanKeys,893+ btreeIndexOnlyScan->ioss_NumOrderByKeys, NULL, NULL, dop, node->plan.plan_node_id);
910- btreeIndexOnlyScan->ioss_NumOrderByKeys);
911 }894 }
912 895 
913 /*896 /*
@@ -923,11 +906,9 @@ void BuildCBtreeIndexOnlyScan(CBTreeOnlyScanState* btreeIndexOnlyScan, ScanState
923 * index AM.906 * index AM.
924 */907 */
925 if (btreeIndexOnlyScan->ioss_NumRuntimeKeys == 0)908 if (btreeIndexOnlyScan->ioss_NumRuntimeKeys == 0)
926- scan_handler_idx_rescan(btreeIndexOnlyScan->ioss_ScanDesc,909+ scan_handler_idx_rescan(btreeIndexOnlyScan->ioss_ScanDesc, btreeIndexOnlyScan->ioss_ScanKeys,
927- btreeIndexOnlyScan->ioss_ScanKeys,910+ btreeIndexOnlyScan->ioss_NumScanKeys, btreeIndexOnlyScan->ioss_OrderByKeys,
928- btreeIndexOnlyScan->ioss_NumScanKeys,911+ btreeIndexOnlyScan->ioss_NumOrderByKeys, dop, node->plan.plan_node_id);
929- btreeIndexOnlyScan->ioss_OrderByKeys,
930- btreeIndexOnlyScan->ioss_NumOrderByKeys);
931 }912 }
932}913}
933 914 
@@ -546,12 +546,13 @@ static HeapTuple cross_level_index_getnext(IndexScanDesc scan, ScanDirection dir
546 */546 */
547 547 
548IndexScanDesc scan_handler_idx_beginscan(Relation heap_relation, Relation index_relation, Snapshot snapshot,548IndexScanDesc scan_handler_idx_beginscan(Relation heap_relation, Relation index_relation, Snapshot snapshot,
549- int nkeys, int norderbys, ScanState* scan_state, ParallelIndexScanDesc pscan)549+ int nkeys, int norderbys, ScanState* scan_state, ParallelIndexScanDesc pscan, int dop, int nodeid)
550{550{
551 if (unlikely(RELATION_OWN_BUCKET(heap_relation))) {551 if (unlikely(RELATION_OWN_BUCKET(heap_relation))) {
552 return hbkt_idx_beginscan(heap_relation, index_relation, snapshot, nkeys, norderbys, scan_state);552 return hbkt_idx_beginscan(heap_relation, index_relation, snapshot, nkeys, norderbys, scan_state);
553 } else {553 } else {
554- return index_beginscan(heap_relation, index_relation, snapshot, nkeys, norderbys, scan_state, pscan);554+ return index_beginscan(heap_relation, index_relation, snapshot, nkeys, norderbys, scan_state, pscan, dop,
555+ nodeid);
555 }556 }
556}557}
557 558 
@@ -570,14 +571,15 @@ IndexScanDesc scan_handler_idx_beginscan_bitmap(Relation indexRelation, Snapshot
570 }571 }
571}572}
572 573 
573-void scan_handler_idx_rescan(IndexScanDesc scan, ScanKey key, int nkeys, ScanKey orderbys, int norderbys)574+void scan_handler_idx_rescan(IndexScanDesc scan, ScanKey key, int nkeys, ScanKey orderbys, int norderbys, int dop,
575+ int plan_nodeid)
574{576{
575 Assert(scan != NULL);577 Assert(scan != NULL);
576 578 
577 if (unlikely(RELATION_OWN_BUCKET(scan->indexRelation))) {579 if (unlikely(RELATION_OWN_BUCKET(scan->indexRelation))) {
578 hbkt_idx_rescan(scan, key, nkeys, orderbys, norderbys);580 hbkt_idx_rescan(scan, key, nkeys, orderbys, norderbys);
579 } else {581 } else {
580- index_rescan(scan, key, nkeys, orderbys, norderbys);582+ index_rescan(scan, key, nkeys, orderbys, norderbys, dop, plan_nodeid);
581 }583 }
582}584}
583 585 
@@ -587,14 +589,15 @@ void scan_handler_idx_rescan_parallel(IndexScanDesc scan)
587 IndexRescanParallel(scan);589 IndexRescanParallel(scan);
588}590}
589 591 
590-void scan_handler_idx_rescan_local(IndexScanDesc scan, ScanKey key, int nkeys, ScanKey orderbys, int norderbys)592+void scan_handler_idx_rescan_local(IndexScanDesc scan, ScanKey key, int nkeys, ScanKey orderbys, int norderbys, int dop,
593+ int plan_nodeid)
591{594{
592 Assert(scan != NULL);595 Assert(scan != NULL);
593 596 
594 if (unlikely(RELATION_OWN_BUCKET(scan->indexRelation))) {597 if (unlikely(RELATION_OWN_BUCKET(scan->indexRelation))) {
595 index_rescan(((HBktIdxScanDesc)scan)->currBktIdxScan, key, nkeys, orderbys, norderbys);598 index_rescan(((HBktIdxScanDesc)scan)->currBktIdxScan, key, nkeys, orderbys, norderbys);
596 } else {599 } else {
597- index_rescan(scan, key, nkeys, orderbys, norderbys);600+ index_rescan(scan, key, nkeys, orderbys, norderbys, dop, plan_nodeid);
598 }601 }
599}602}
600 603 
@@ -323,7 +323,7 @@ bool index_insert(Relation index_relation, Datum *values, const bool *isnull, It
323 */323 */
324IndexScanDesc index_beginscan(324IndexScanDesc index_beginscan(
325 Relation heap_relation, Relation index_relation, Snapshot snapshot, int nkeys, int norderbys, ScanState* scan_state,325 Relation heap_relation, Relation index_relation, Snapshot snapshot, int nkeys, int norderbys, ScanState* scan_state,
326- ParallelIndexScanDesc pscan)326+ ParallelIndexScanDesc pscan, int dop, int nodeid)
327{327{
328 IndexScanDesc scan;328 IndexScanDesc scan;
329 329 
@@ -339,8 +339,14 @@ IndexScanDesc index_beginscan(
339 if (scan->xs_want_ext_oid) {339 if (scan->xs_want_ext_oid) {
340 scan->xs_gpi_scan->parentRelation = heap_relation;340 scan->xs_gpi_scan->parentRelation = heap_relation;
341 }341 }
342- 342+ if (dop > 1 && INDEX_TYPE_CAN_PARALLEL(index_relation->rd_rel->relam)) {
343- /* prepare to fetch index matches from table */343+ scan->dop = dop;
344+ scan->btps_end_block = InvalidBlockNumber;
345+ if (nodeid != -1) {
346+ scan->plan_nodeid = static_cast<uint32>(nodeid);
347+ }
348+ }
349+ /* prepare to fetch index matches from table */
344 scan->xs_heapfetch = tableam_scan_index_fetch_begin(heap_relation);350 scan->xs_heapfetch = tableam_scan_index_fetch_begin(heap_relation);
345 351 
346 return scan;352 return scan;
@@ -430,7 +436,8 @@ static IndexScanDesc index_beginscan_internal(Relation index_relation, int nkeys
430 * scan->numberOfKeys is zero.)436 * scan->numberOfKeys is zero.)
431 * ----------------437 * ----------------
432 */438 */
433-void index_rescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys, int norderbys)439+void index_rescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys, int norderbys, int dop,
440+ int plan_nodeid)
434{441{
435 FmgrInfo *procedure = NULL;442 FmgrInfo *procedure = NULL;
436 443 
@@ -452,7 +459,8 @@ void index_rescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys,
452 scan->xs_continue_hot = false;459 scan->xs_continue_hot = false;
453 460 
454 scan->kill_prior_tuple = false; /* for safety */461 scan->kill_prior_tuple = false; /* for safety */
455- 462+ scan->dop = dop;
463+ scan->plan_nodeid = plan_nodeid;
456 if (scan->indexRelation->rd_rel->relam == BTREE_AM_OID) {464 if (scan->indexRelation->rd_rel->relam == BTREE_AM_OID) {
457 btrescan_internal(scan, keys, nkeys, orderbys, norderbys);465 btrescan_internal(scan, keys, nkeys, orderbys, norderbys);
458 } else if (use_index_am_routine(scan->indexRelation)) {466 } else if (use_index_am_routine(scan->indexRelation)) {
@@ -10,6 +10,7 @@ ifneq "$(MAKECMDGOALS)" "clean"
10 endif10 endif
11endif11endif
12OBJS = nbtcompare.o nbtdedup.o nbtinsert.o nbtpage.o nbtree.o nbtsearch.o \12OBJS = nbtcompare.o nbtdedup.o nbtinsert.o nbtpage.o nbtree.o nbtsearch.o \
13- nbtutils.o nbtsort.o nbtxlog.o spq_btbuild.o13+ nbtutils.o nbtsort.o nbtxlog.o spq_btbuild.o nbtsearch_parallel.o \
14+ parallel_indexscan_scankey_procs.o parallel_indexscan_thread_proc.o
14 15 
15include $(top_srcdir)/src/gausskernel/common.mk16include $(top_srcdir)/src/gausskernel/common.mk
@@ -1330,7 +1330,9 @@ static Buffer _bt_split(Relation rel, BTScanInsert itup_key, Buffer buf, Buffer
1330 /* Since we already have write-lock on both pages, ok to read cycleid */1330 /* Since we already have write-lock on both pages, ok to read cycleid */
1331 lopaque->btpo_cycleid = _bt_vacuum_cycleid(rel);1331 lopaque->btpo_cycleid = _bt_vacuum_cycleid(rel);
1332 ropaque->btpo_cycleid = lopaque->btpo_cycleid;1332 ropaque->btpo_cycleid = lopaque->btpo_cycleid;
1333- 1333+ if (P_PARALLEL_SCAN_END(oopaque)) {
1334+ ((BTPageOpaque)lopaque)->xact = ((BTPageOpaque)oopaque)->xact;
1335+ }
1334 /*1336 /*
1335 * If the page we're splitting is not the rightmost page at its level in1337 * If the page we're splitting is not the rightmost page at its level in
1336 * the tree, then the first entry on the page is the high key for the1338 * the tree, then the first entry on the page is the high key for the
@@ -789,7 +789,7 @@ static void BtRootbufCacheEnsureSessionInit(void)
789 u_sess->storage_cxt.btMetaCache->lastHitSlot = -1;789 u_sess->storage_cxt.btMetaCache->lastHitSlot = -1;
790 u_sess->storage_cxt.btMetaCache->reformVer = g_instance.dms_cxt.SSReformInfo.reform_ver;790 u_sess->storage_cxt.btMetaCache->reformVer = g_instance.dms_cxt.SSReformInfo.reform_ver;
791 u_sess->storage_cxt.btMetaCacheResOwner =791 u_sess->storage_cxt.btMetaCacheResOwner =
792- ResourceOwnerCreate(NULL, "BtMetaCache", allocCxt);792+ ResourceOwnerCreate(t_thrd.utils_cxt.ThreadRootResourceOwner, "BtMetaCache", allocCxt);
793 BtRootbufCacheRegisterRelcacheCallback();793 BtRootbufCacheRegisterRelcacheCallback();
794 (void)MemoryContextSwitchTo(oldCxt);794 (void)MemoryContextSwitchTo(oldCxt);
795}795}
@@ -3171,7 +3171,10 @@ static bool _bt_mark_page_halfdead(Relation rel, Buffer leafbuf, BTStack stack)
3171 3171 
3172 page = BufferGetPage(leafbuf);3172 page = BufferGetPage(leafbuf);
3173 opaque = (BTPageOpaqueInternal)PageGetSpecialPointer(page);3173 opaque = (BTPageOpaqueInternal)PageGetSpecialPointer(page);
3174- 3174+ if (P_PARALLEL_SCAN_END(opaque) &&
3175+ !(TransactionIdPrecedes(((BTPageOpaque)opaque)->xact, u_sess->utils_cxt.RecentGlobalXmin))) {
3176+ return false;
3177+ }
3175 Assert(!P_RIGHTMOST(opaque) && !P_ISROOT(opaque) && !P_ISDELETED(opaque) && !P_ISHALFDEAD(opaque) &&3178 Assert(!P_RIGHTMOST(opaque) && !P_ISROOT(opaque) && !P_ISDELETED(opaque) && !P_ISHALFDEAD(opaque) &&
3176 P_ISLEAF(opaque) && P_FIRSTDATAKEY(opaque) > PageGetMaxOffsetNumber(page));3179 P_ISLEAF(opaque) && P_FIRSTDATAKEY(opaque) > PageGetMaxOffsetNumber(page));
3177 3180 
@@ -32,7 +32,6 @@
32#include "catalog/pg_proc.h"32#include "catalog/pg_proc.h"
33 33 
34static int32 btree_compare_heap_tid(Relation rel, BTScanInsert itup_key, IndexTuple itup, int num_tuple_attrs);34static int32 btree_compare_heap_tid(Relation rel, BTScanInsert itup_key, IndexTuple itup, int num_tuple_attrs);
35-static bool _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum);
36static void _bt_saveitem(BTScanOpaque so, int itemIndex, OffsetNumber offnum, IndexTuple itup, Oid partOid,35static void _bt_saveitem(BTScanOpaque so, int itemIndex, OffsetNumber offnum, IndexTuple itup, Oid partOid,
37 int2 bucketid);36 int2 bucketid);
38static bool _bt_steppage(IndexScanDesc scan, ScanDirection dir);37static bool _bt_steppage(IndexScanDesc scan, ScanDirection dir);
@@ -286,7 +285,7 @@ static void _bt_prefetch_local_heap_pages(IndexScanDesc scan, ScanDirection dir)
286 * to be created and returned. When access = BT_READ, an empty index285 * to be created and returned. When access = BT_READ, an empty index
287 * will result in *bufP being set to InvalidBuffer.286 * will result in *bufP being set to InvalidBuffer.
288 */287 */
289-BTStack _bt_search(Relation rel, BTScanInsert key, Buffer *bufP, int access, bool needStack)288+BTStack _bt_search(Relation rel, BTScanInsert key, Buffer* bufP, int access, bool needStack, BlockNumber parallel_end)
290{289{
291 BTStack stack_in = NULL;290 BTStack stack_in = NULL;
292 bool borrowedRoot = false;291 bool borrowedRoot = false;
@@ -321,7 +320,7 @@ BTStack _bt_search(Relation rel, BTScanInsert key, Buffer *bufP, int access, boo
321 * if the leaf page is split and we insert to the parent page). But320 * if the leaf page is split and we insert to the parent page). But
322 * this is a good opportunity to finish splits of internal pages too.321 * this is a good opportunity to finish splits of internal pages too.
323 */322 */
324- *bufP = _bt_moveright(rel, key, *bufP, (access == BT_WRITE), stack_in, page_access);323+ *bufP = _bt_moveright(rel, key, *bufP, (access == BT_WRITE), stack_in, page_access, parallel_end);
325 borrowedRoot = BtRootbufIsBorrowed(rel, *bufP);324 borrowedRoot = BtRootbufIsBorrowed(rel, *bufP);
326 325 
327 /* if this is a leaf page, we're done */326 /* if this is a leaf page, we're done */
@@ -395,7 +394,7 @@ BTStack _bt_search(Relation rel, BTScanInsert key, Buffer *bufP, int access, boo
395 * but before we acquired a write lock. If it has, we may need to394 * but before we acquired a write lock. If it has, we may need to
396 * move right to its new sibling. Do that.395 * move right to its new sibling. Do that.
397 */396 */
398- *bufP = _bt_moveright(rel, key, *bufP, true, stack_in, BT_WRITE);397+ *bufP = _bt_moveright(rel, key, *bufP, true, stack_in, BT_WRITE, parallel_end);
399 }398 }
400 399 
401 return stack_in;400 return stack_in;
@@ -432,7 +431,7 @@ BTStack _bt_search(Relation rel, BTScanInsert key, Buffer *bufP, int access, boo
432 * the same on the right sibling. Return value is the buffer we stop at.431 * the same on the right sibling. Return value is the buffer we stop at.
433 */432 */
434Buffer _bt_moveright(Relation rel, BTScanInsert key, Buffer buf, bool forupdate, BTStack stack,433Buffer _bt_moveright(Relation rel, BTScanInsert key, Buffer buf, bool forupdate, BTStack stack,
435- int access)434+ int access, BlockNumber parallel_end)
436{435{
437 Page page;436 Page page;
438 BTPageOpaqueInternal opaque;437 BTPageOpaqueInternal opaque;
@@ -466,8 +465,9 @@ Buffer _bt_moveright(Relation rel, BTScanInsert key, Buffer buf, bool forupdate,
466 /*465 /*
467 * Finish any incomplete splits we encounter along the way.466 * Finish any incomplete splits we encounter along the way.
468 */467 */
468+ BlockNumber blkno = InvalidBlockNumber;
469 if (forupdate && P_INCOMPLETE_SPLIT(opaque)) {469 if (forupdate && P_INCOMPLETE_SPLIT(opaque)) {
470- BlockNumber blkno = BufferGetBlockNumber(buf);470+ blkno = BufferGetBlockNumber(buf);
471 471 
472 /* upgrade our lock if necessary */472 /* upgrade our lock if necessary */
473 if (access == BT_READ) {473 if (access == BT_READ) {
@@ -487,6 +487,10 @@ Buffer _bt_moveright(Relation rel, BTScanInsert key, Buffer buf, bool forupdate,
487 }487 }
488 488 
489 if (P_IGNORE(opaque) || _bt_compare(rel, key, page, P_HIKEY) >= cmpval) {489 if (P_IGNORE(opaque) || _bt_compare(rel, key, page, P_HIKEY) >= cmpval) {
490+ blkno = opaque->btpo_next;
491+ if (parallel_end != InvalidBlockNumber && blkno == parallel_end) {
492+ return buf;
493+ }
490 /* step right one page */494 /* step right one page */
491 if (borrowedRoot) {495 if (borrowedRoot) {
492 BtRootbufReleaseBorrowed(rel, buf);496 BtRootbufReleaseBorrowed(rel, buf);
@@ -879,29 +883,8 @@ bool _bt_first(IndexScanDesc scan, ScanDirection dir)
879 if (!so->qual_ok)883 if (!so->qual_ok)
880 return false;884 return false;
881 885 
882-#ifdef ENABLE_NEON886+ if (scan->dop > 1) {
883- _bt_init_prefetch_state(so, scan);887+ return _bt_parallel_first(scan, dir);
884-#endif
885- 
886- /*
887- * For parallel scans, get the starting page from shared state. If the
888- * scan has not started, proceed to find out first leaf page in the usual
889- * way while keeping other participating processes waiting. If the scan
890- * has already begun, use the page number from the shared structure.
891- */
892- if (scan->parallelScan != NULL) {
893- status = _bt_parallel_seize(scan, &blkno);
894- if (!status) {
895- return false;
896- } else if (blkno == P_NONE) {
897- _bt_parallel_done(scan);
898- return false;
899- } else if (blkno != InvalidBlockNumber) {
900- if (!_bt_parallel_readpage(scan, blkno, dir)) {
901- return false;
902- }
903- goto readcomplete;
904- }
905 }888 }
906 889 
907 /* ----------890 /* ----------
@@ -1372,8 +1355,6 @@ bool _bt_first(IndexScanDesc scan, ScanDirection dir)
1372 /* Drop the lock, but not pin, on the current page */1355 /* Drop the lock, but not pin, on the current page */
1373 LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);1356 LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
1374 }1357 }
1375- 
1376-readcomplete:
1377 /* OK, itemIndex says what to return */1358 /* OK, itemIndex says what to return */
1378 currItem = &so->currPos.items[so->currPos.itemIndex];1359 currItem = &so->currPos.items[so->currPos.itemIndex];
1379 scan->xs_ctup.t_self = currItem->heapTid;1360 scan->xs_ctup.t_self = currItem->heapTid;
@@ -1475,7 +1456,7 @@ bool _bt_next(IndexScanDesc scan, ScanDirection dir)
1475 *1456 *
1476 * Returns true if any matching items found on the page, false if none.1457 * Returns true if any matching items found on the page, false if none.
1477 */1458 */
1478-static bool _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)1459+bool _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
1479{1460{
1480 BTScanOpaque so = (BTScanOpaque)scan->opaque;1461 BTScanOpaque so = (BTScanOpaque)scan->opaque;
1481 Page page;1462 Page page;
@@ -1747,39 +1728,13 @@ static bool _bt_steppage(IndexScanDesc scan, ScanDirection dir)
1747 if (ScanDirectionIsForward(dir)) {1728 if (ScanDirectionIsForward(dir)) {
1748 so->currPos.buf = InvalidBuffer;1729 so->currPos.buf = InvalidBuffer;
1749 1730 
1750- /* Walk right to the next page with data */1731+ blkno = so->currPos.nextPage;
1751- if (scan->parallelScan != NULL) {
1752- /*
1753- * Seize the scan to get the next block number; if the scan has
1754- * ended already, bail out.
1755- */
1756- status = _bt_parallel_seize(scan, &blkno);
1757- if (!status) {
1758- return false;
1759- }
1760- } else {
1761- /* Not parallel, so use the previously-saved nextPage link. */
1762- blkno = so->currPos.nextPage;
1763- }
1764 /* Remember we left a page with data */1732 /* Remember we left a page with data */
1765 so->currPos.moreLeft = true;1733 so->currPos.moreLeft = true;
1766 } else {1734 } else {
1767 /* Remember we left a page with data */1735 /* Remember we left a page with data */
1768 so->currPos.moreRight = true;1736 so->currPos.moreRight = true;
1769- if (scan->parallelScan != NULL) {1737+ blkno = so->currPos.currPage;
1770- /*
1771- * Seize the scan to get the current block number; if the scan has
1772- * ended already, bail out.
1773- */
1774- status = _bt_parallel_seize(scan, &blkno);
1775- if (!status) {
1776- so->currPos.buf = InvalidBuffer;
1777- return false;
1778- }
1779- } else {
1780- /* Not parallel, so just use our own notion of the current page */
1781- blkno = so->currPos.currPage;
1782- }
1783 }1738 }
1784 1739 
1785 return _bt_readnextpage(scan, blkno, dir);1740 return _bt_readnextpage(scan, blkno, dir);
@@ -1830,9 +1785,6 @@ static bool _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno,
1830 if (_bt_readpage(scan, dir, P_FIRSTDATAKEY(opaque))) {1785 if (_bt_readpage(scan, dir, P_FIRSTDATAKEY(opaque))) {
1831 break;1786 break;
1832 }1787 }
1833- } else if (scan->parallelScan != NULL) {
1834- /* allow next page be processed by parallel worker */
1835- _bt_parallel_release(scan, opaque->btpo_next);
1836 }1788 }
1837 1789 
1838 /* release the previous buffer */1790 /* release the previous buffer */
@@ -1840,14 +1792,7 @@ static bool _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno,
1840 so->currPos.buf = InvalidBuffer;1792 so->currPos.buf = InvalidBuffer;
1841 1793 
1842 /* nope, keep going */1794 /* nope, keep going */
1843- if (scan->parallelScan != NULL) {1795+ blkno = opaque->btpo_next;
1844- status = _bt_parallel_seize(scan, &blkno);
1845- if (!status) {
1846- return false;
1847- }
1848- } else {
1849- blkno = opaque->btpo_next;
1850- }
1851 }1796 }
1852 } else {1797 } else {
1853 /*1798 /*
@@ -1915,25 +1860,6 @@ static bool _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno,
1915 if (_bt_readpage(scan, dir, PageGetMaxOffsetNumber(page))) {1860 if (_bt_readpage(scan, dir, PageGetMaxOffsetNumber(page))) {
1916 break;1861 break;
1917 }1862 }
1918- } else if (scan->parallelScan != NULL) {
1919- /* allow next page be processed by parallel worker */
1920- _bt_parallel_release(scan, BufferGetBlockNumber(so->currPos.buf));
1921- }
1922- 
1923- /*
1924- * For parallel scans, get the last page scanned as it is quite
1925- * possible that by the time we try to seize the scan, some other
1926- * worker has already advanced the scan to a different page. We
1927- * must continue based on the latest page scanned by any worker.
1928- */
1929- if (scan->parallelScan != NULL) {
1930- _bt_relbuf(rel, so->currPos.buf);
1931- status = _bt_parallel_seize(scan, &blkno);
1932- if (!status) {
1933- so->currPos.buf = InvalidBuffer;
1934- return false;
1935- }
1936- so->currPos.buf = _bt_getbuf(rel, blkno, BT_READ);
1937 }1863 }
1938 }1864 }
1939 }1865 }
@@ -1976,7 +1902,7 @@ static bool _bt_parallel_readpage(IndexScanDesc scan, BlockNumber blkno, ScanDir
1976 * to be half-dead; the caller should check that condition and step left1902 * to be half-dead; the caller should check that condition and step left
1977 * again if it's important.1903 * again if it's important.
1978 */1904 */
1979-Buffer _bt_walk_left(Relation rel, Buffer buf)1905+Buffer _bt_walk_left(Relation rel, Buffer buf, BlockNumber parallel_end)
1980{1906{
1981 Page page;1907 Page page;
1982 BTPageOpaqueInternal opaque;1908 BTPageOpaqueInternal opaque;
@@ -1997,6 +1923,14 @@ Buffer _bt_walk_left(Relation rel, Buffer buf)
1997 }1923 }
1998 /* remember original page we are stepping left from */1924 /* remember original page we are stepping left from */
1999 obknum = BufferGetBlockNumber(buf);1925 obknum = BufferGetBlockNumber(buf);
1926+ /*
1927+ * Before set to next block, we should check whether need to stop here,
1928+ * since the scan interval of all threads except the first thread is (start, end].
1929+ */
1930+ if (parallel_end != InvalidBlockNumber && obknum == parallel_end) {
1931+ _bt_relbuf(rel, buf);
1932+ return InvalidBuffer;
1933+ }
2000 /* step left */1934 /* step left */
2001 blkno = lblkno = opaque->btpo_prev;1935 blkno = lblkno = opaque->btpo_prev;
2002 _bt_relbuf(rel, buf);1936 _bt_relbuf(rel, buf);
@@ -2083,7 +2017,7 @@ Buffer _bt_walk_left(Relation rel, Buffer buf)
2083 *2017 *
2084 * The returned buffer is pinned and read-locked.2018 * The returned buffer is pinned and read-locked.
2085 */2019 */
2086-Buffer _bt_get_endpoint(Relation rel, uint32 level, bool rightmost)2020+Buffer _bt_get_endpoint(Relation rel, uint32 level, bool rightmost, BlockNumber parallel_end)
2087{2021{
2088 Buffer buf;2022 Buffer buf;
2089 Page page;2023 Page page;
@@ -2119,6 +2053,10 @@ Buffer _bt_get_endpoint(Relation rel, uint32 level, bool rightmost)
2119 */2053 */
2120 while (P_IGNORE(opaque) || (rightmost && !P_RIGHTMOST(opaque))) {2054 while (P_IGNORE(opaque) || (rightmost && !P_RIGHTMOST(opaque))) {
2121 blkno = opaque->btpo_next;2055 blkno = opaque->btpo_next;
2056+ if (parallel_end != InvalidBlockNumber && blkno == parallel_end) {
2057+ _bt_relbuf(rel, buf);
2058+ return InvalidBuffer;
2059+ }
2122 if (blkno == P_NONE)2060 if (blkno == P_NONE)
2123 ereport(ERROR, (errcode(ERRCODE_INDEX_CORRUPTED),2061 ereport(ERROR, (errcode(ERRCODE_INDEX_CORRUPTED),
2124 errmsg("fell off the end of index \"%s\"", RelationGetRelationName(rel))));2062 errmsg("fell off the end of index \"%s\"", RelationGetRelationName(rel))));
@@ -2148,6 +2086,10 @@ Buffer _bt_get_endpoint(Relation rel, uint32 level, bool rightmost)
2148 2086 
2149 itup = (IndexTuple)PageGetItem(page, PageGetItemId(page, offnum));2087 itup = (IndexTuple)PageGetItem(page, PageGetItemId(page, offnum));
2150 blkno = BTreeInnerTupleGetDownLink(itup);2088 blkno = BTreeInnerTupleGetDownLink(itup);
2089+ if (parallel_end != InvalidBlockNumber && blkno == parallel_end) {
2090+ _bt_relbuf(rel, buf);
2091+ return InvalidBuffer;
2092+ }
2151 if (borrowedRoot) {2093 if (borrowedRoot) {
2152 BtRootbufReleaseBorrowed(rel, buf);2094 BtRootbufReleaseBorrowed(rel, buf);
2153 buf = _bt_getbuf(rel, blkno, BT_READ);2095 buf = _bt_getbuf(rel, blkno, BT_READ);
@@ -2310,7 +2252,11 @@ bool _bt_gettuple_internal(IndexScanDesc scan, ScanDirection dir)
2310 /*2252 /*
2311 * Now continue the scan.2253 * Now continue the scan.
2312 */2254 */
2313- res = _bt_next(scan, dir);2255+ if (scan->dop > 1) {
2256+ res = _bt_parallel_next(scan, dir);
2257+ } else {
2258+ res = _bt_next(scan, dir);
2259+ }
2314 }2260 }
2315 2261 
2316 /* If we have a tuple, return it ... */2262 /* If we have a tuple, return it ... */
@@ -0,0 +1,770 @@
1+/*
2+ * Copyright (c) 2020 Huawei Technologies Co.,Ltd.
3+ * Portions Copyright (c) 2021, openGauss Contributors
4+ *
5+ * openGauss is licensed under Mulan PSL v2.
6+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
7+ * You may obtain a copy of Mulan PSL v2 at:
8+ *
9+ * http://license.coscl.org.cn/MulanPSL2
10+ *
11+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
12+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
13+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
14+ * See the Mulan PSL v2 for more details.
15+ * ---------------------------------------------------------------------------------------
16+ *
17+ * nbtsearch_parallel.cpp
18+ *
19+ *
20+ *
21+ * IDENTIFICATION
22+ * src\gausskernel\storage\access\nbtree\nbtsearch_parallel.cpp
23+ *
24+ * ---------------------------------------------------------------------------------------
25+ */
26+#include "postgres.h"
27+#include "access/nbtree.h"
28+#include "miscadmin.h"
29+#include "storage/predicate.h"
30+#include "access/tableam.h"
31+#include "utils/lsyscache.h"
32+#include "utils/rel.h"
33+#include "catalog/pg_opfamily.h"
34+#include "access/parallel_indexscan_core.h"
35+ 
36+/*
37+ * @brief find_next_block
38+ * Find the next block according to the siling pointer (btpo_next/btpo_prev).
39+ * @param bt_scan IndexScanDesc
40+ * @param dir Scanning direction
41+ * @param current_block Start block number of the current thread.
42+ * @param num_blocks Estimated number of lef nodes scanned by each thread
43+ * @return BlockNumber End block number of the current thread.
44+ */
45+BlockNumber find_next_block(IndexScanDesc bt_scan, ScanDirection dir, BlockNumber current_block, int num_blocks)
46+{
47+ if (current_block == InvalidBlockNumber || current_block == 0) {
48+ return 0;
49+ }
50+ int count = 1;
51+ BlockNumber bt_next = current_block;
52+ BlockNumber bt_now = current_block;
53+ Relation bt_rel = bt_scan->indexRelation;
54+ Buffer current_buf = _bt_getbuf(bt_rel, current_block, BT_READ);
55+ BTPageOpaqueInternal opaque = (BTPageOpaqueInternal)PageGetSpecialPointer(BufferGetPage(current_buf));
56+ bt_now = bt_next;
57+ if (ScanDirectionIsForward(dir)) {
58+ bt_next = opaque->btpo_next;
59+ } else {
60+ bt_next = opaque->btpo_prev;
61+ }
62+ _bt_relbuf(bt_rel, current_buf);
63+ Buffer buf_tmp = InvalidBuffer;
64+ int access = BT_READ;
65+ while (count <= num_blocks) {
66+ access = (count == num_blocks ? BT_WRITE : BT_READ);
67+ buf_tmp = _bt_getbuf(bt_rel, bt_next, access);
wofanzheng
wofanzhengwofanzheng6月21日

【问题】find_next_block() 在进入循环前先把 bt_next 设成 sibling 指针,但没有先判断它是否已经是 P_NONE/无效块;如果当前块本身就是最右/最左叶子页(例如只有 1 个叶子页但 dop > 1),这里会直接 _bt_getbuf() 一个非法块号,导致并行扫描初始化阶段崩溃。 【建议】在取下一页前先判断 bt_next 是否为 P_NONE/InvalidBlockNumber,命中时直接返回 0 或 InvalidBlockNumber,并把后续线程区间标记为空。建议补一个“单叶子页索引 + query_dop=2 + 正反向索引扫描”的回归用例。

likedislike
xiahanzhi
6月22日 评论:
68+ opaque = (BTPageOpaqueInternal)PageGetSpecialPointer(BufferGetPage(buf_tmp));
69+ bool empty_page = false;
70+ if (count == num_blocks && !P_RIGHTMOST(opaque) && !P_LEFTMOST(opaque)) {
71+ OffsetNumber max_off = PageGetMaxOffsetNumber(BufferGetPage(buf_tmp));
72+ empty_page = (P_FIRSTKEY > max_off);
73+ }
74+ if (ScanDirectionIsForward(dir)) {
75+ if (P_IGNORE(opaque) || empty_page) {
76+ _bt_relbuf(bt_rel, buf_tmp);
77+ bt_now = bt_next;
78+ bt_next = opaque->btpo_next;
79+ continue;
80+ }
81+ if (P_RIGHTMOST(opaque)) {
82+ bt_now = opaque->btpo_next;
83+ _bt_relbuf(bt_rel, buf_tmp);
84+ return bt_now;
85+ }
86+ bt_now = bt_next;
87+ bt_next = opaque->btpo_next;
88+ } else {
89+ if (P_IGNORE(opaque) || empty_page) {
90+ _bt_relbuf(bt_rel, buf_tmp);
91+ bt_now = bt_next;
92+ bt_next = opaque->btpo_prev;
93+ continue;
94+ }
95+ if (P_LEFTMOST(opaque)) {
96+ bt_now = opaque->btpo_prev;
97+ _bt_relbuf(bt_rel, buf_tmp);
98+ return bt_now;
99+ }
100+ bt_now = bt_next;
101+ bt_next = opaque->btpo_prev;
102+ }
103+ if (count == num_blocks) {
104+ opaque->btpo_flags |= BTP_PARALLEL_SCAN_END;
105+ if (((BTPageOpaque)opaque)->xact < bt_scan->xs_snapshot->xmin) {
106+ ((BTPageOpaque)opaque)->xact = bt_scan->xs_snapshot->xmin;
107+ }
108+ MarkBufferDirtyHint(buf_tmp, true);
109+ }
110+ _bt_relbuf(bt_rel, buf_tmp);
111+ count++;
112+ }
113+ return bt_now;
114+}
115+ 
116+/*
117+ * @brief _bt_get_parallel_scan_total_blocks
118+ * Calculate the number of block that meet the scankey reuqirement.
119+ * @param bt_scan IndexScanDesc
120+ * @param dir Scanning direction
121+ * @param bt_start_blk Start scan block number
122+ * @return int Number of all block that meet the conditions
123+ */
124+int _bt_get_parallel_scan_total_blocks(IndexScanDesc bt_scan, ScanDirection dir, BlockNumber bt_start_blk)
125+{
126+ if (bt_start_blk == InvalidBuffer) {
127+ return 0;
128+ }
129+ Relation bt_rel = bt_scan->indexRelation;
130+ BTScanOpaque bt_para_so = (BTScanOpaque)bt_scan->opaque;
131+ int total_blocks = 1;
132+ bool continuescan = true;
133+ BlockNumber bt_next = bt_start_blk;
134+ while (true) {
135+ CHECK_FOR_INTERRUPTS();
136+ Buffer tmp_buf = _bt_getbuf(bt_rel, bt_next, BT_READ);
137+ BTPageOpaqueInternal opaque = (BTPageOpaqueInternal)PageGetSpecialPointer(BufferGetPage(tmp_buf));
138+ if (ScanDirectionIsForward(dir)) {
139+ bt_next = opaque->btpo_next;
140+ if (P_RIGHTMOST(opaque)) {
141+ _bt_relbuf(bt_rel, tmp_buf);
142+ break;
143+ }
144+ } else {
145+ bt_next = opaque->btpo_prev;
146+ if (P_LEFTMOST(opaque)) {
147+ _bt_relbuf(bt_rel, tmp_buf);
148+ break;
149+ }
150+ }
151+ _bt_relbuf(bt_rel, tmp_buf);
152+ if (bt_para_so->numberOfKeys > 0) {
153+ tmp_buf = _bt_getbuf(bt_rel, bt_next, BT_READ);
154+ Page cur_page = BufferGetPage(tmp_buf);
155+ opaque = (BTPageOpaqueInternal)PageGetSpecialPointer(cur_page);
156+ if (ScanDirectionIsForward(dir)) {
157+ if (P_FIRSTDATAKEY(opaque) > PageGetMaxOffsetNumber(cur_page)) {
wofanzheng
wofanzhengwofanzheng6月21日

【问题】这里在 153 行重新获取了 bt_next 对应的新 buffer,但后面仍然用上一页的 opaque 去取 P_FIRSTDATAKEY();上一页在 151 行已经 _bt_relbuf(),这会变成对已释放页 special area 的误用,既可能算错总块数,也可能踩到无效内存。 【建议】重新打开 bt_next 页后,立即基于 cur_page/新页 special area 重新取 opaque,后续 P_FIRSTDATAKEY() 和 _bt_checkkeys() 都只使用当前页上下文

likedislike
xiahanzhi
6月22日 评论:
158+ _bt_relbuf(bt_rel, tmp_buf);
159+ total_blocks++;
160+ continue;
161+ }
162+ _bt_checkkeys(bt_scan, cur_page, P_FIRSTDATAKEY(opaque), dir, &continuescan, false, false);
163+ } else {
164+ _bt_checkkeys(bt_scan, cur_page, PageGetMaxOffsetNumber(cur_page), dir, &continuescan, false, false);
165+ }
166+ if (!continuescan) {
167+ _bt_relbuf(bt_rel, tmp_buf);
168+ break;
169+ }
170+ _bt_relbuf(bt_rel, tmp_buf);
171+ }
172+ total_blocks++;
173+ }
174+ return total_blocks;
175+}
176+ 
177+/*
178+ * @brief _bt_get_inskey_scankey_without_rowheader
179+ * Initialize inskey->scankey when cur_>sk_flags & SK_ROW_HEADER is 0.
180+ * @param cur No. i startKey
181+ * @param bt_rel relation of the current index
182+ * @param i Number of startkey iterations
183+ * @return void
184+ */
185+void _bt_get_inskey_scankey_without_rowheader(ScanKey cur, Relation bt_rel, int i, BTScanInsertData* inskey)
186+{
187+ if (cur->sk_subtype == bt_rel->rd_opcintype[i] || cur->sk_subtype == InvalidOid) {
188+ FmgrInfo* procinfo = index_getprocinfo(bt_rel, cur->sk_attno, BTORDER_PROC);
189+ ScanKeyEntryInitializeWithInfo(inskey->scankeys + i, cur->sk_flags, cur->sk_attno, InvalidStrategy,
190+ cur->sk_subtype, cur->sk_collation, procinfo, cur->sk_argument);
191+ } else {
192+ RegProcedure cmp_proc;
193+ if (bt_rel->rd_opfamily[i] == INTEGER_BTREE_FAM_OID && bt_rel->rd_opcintype[i] == INT8OID &&
194+ cur->sk_subtype == INT4OID) {
195+ cmp_proc = F_BTINT84CMP;
196+ } else {
197+ RegProcedure cmp_proc;
198+ cmp_proc =
199+ get_opfamily_proc(bt_rel->rd_opfamily[i], bt_rel->rd_opcintype[i], cur->sk_subtype, BTORDER_PROC);
200+ if (SECUREC_UNLIKELY(!RegProcedureIsValid(cmp_proc)))
201+ ereport(ERROR, (errcode(ERRCODE_INDEX_CORRUPTED),
202+ errmsg("missing support function %d(%u,%u) for attribute %d of index \"%s\"",
203+ BTORDER_PROC, bt_rel->rd_opcintype[i], cur->sk_subtype, cur->sk_attno,
204+ RelationGetRelationName(bt_rel))));
205+ ScanKeyEntryInitialize(inskey->scankeys + i, cur->sk_flags, cur->sk_attno, InvalidStrategy, cur->sk_subtype,
A
AAnderles10 天前

这里的 ScanKeyEntryInitialize 可能不应只放在else分支。 当索引键为 int8、查询键为 int4 时,if分支仅设置cmp_proc = F_BTINT84CMP,随后绕过本行,导致inskey->scankeys[i] 未初始化。后续 B-tree 比较使用sk_attno 调用 index_getattr(),会触发断言失败。附本地复现用例:

CREATE TABLE smp_int84_min (k int8 NOT NULL); INSERT INTO smp_int84_min VALUES (1); CREATE INDEX smp_int84_min_k_idx ON smp_int84_min(k); ANALYZE smp_int84_min;

SET query_dop = 3; SET enable_force_smp = on; SET enable_seqscan = off; SET enable_indexscan = on; SET enable_indexonlyscan = off; SET enable_bitmapscan = off;

EXPLAIN (COSTS OFF) SELECT count(*) FROM smp_int84_min WHERE k >= 1;

SELECT count(*) FROM smp_int84_min WHERE k >= 1;

likedislike
206+ cur->sk_collation, cmp_proc, cur->sk_argument);
207+ }
208+ }
209+ return;
210+}
211+ 
212+/*
213+ * @brief _bt_get_goback_need_to_next
214+ * If the number of start conditions is 0, the start buffer and offset are returned.
215+ * @param dir Scanning direction
216+ * @param *need_to_go_back need to take a step back
217+ * @param *need_to_next_key proceed to the next step
218+ * @param strat_total different scanning conditions
219+ * @return bool returns true if there is no error, false otherwise
220+ */
221+bool _bt_get_goback_need_to_next(ScanDirection dir, bool* need_to_go_back, bool* need_to_next_key,
222+ StrategyNumber strat_total)
223+{
224+ switch (strat_total) {
225+ case BTGreaterEqualStrategyNumber:
226+ break;
227+ case BTGreaterStrategyNumber:
228+ *need_to_next_key = true;
229+ break;
230+ case BTEqualStrategyNumber:
231+ *need_to_go_back = (ScanDirectionIsBackward(dir)) ? true : false;
232+ *need_to_next_key = (ScanDirectionIsBackward(dir)) ? true : false;
233+ break;
234+ case BTLessEqualStrategyNumber:
235+ *need_to_go_back = true;
236+ *need_to_next_key = true;
237+ break;
238+ case BTLessStrategyNumber:
239+ *need_to_go_back = true;
240+ break;
241+ default:
242+ ereport(ERROR, (errcode(ERRCODE_INDEX_CORRUPTED), errmsg("Unrecognized strategy number:%d.", strat_total)));
243+ return false;
244+ }
245+ return true;
246+}
247+ 
248+/*
249+ * @brief _bt_init_inskey
250+ * Initialize the inskey.
251+ * @param *inskey BTScanInsertData
252+ * @param bt_rel relation of the current index
253+ * @param nextkey proceed to the next step
254+ * @param keys_count number of startkeys
255+ * @return void
256+ */
257+void _bt_init_inskey(BTScanInsertData* inskey, Relation bt_rel, bool nextkey, int keys_count)
258+{
259+ btree_meta_version(bt_rel, &inskey->heapkeyspace, &inskey->allequalimage);
260+ inskey->anynullkeys = false;
261+ inskey->nextkey = nextkey;
262+ inskey->pivotsearch = false;
263+ inskey->scantid = NULL;
264+ inskey->keysz = keys_count;
265+ return;
266+}
267+ 
268+/*
269+ * @brief _bt_get_begin_parallel_scan_buf
270+ * Obtains the start scanning buffer.
271+ * @param bt_scan IndexScanDesc
272+ * @param dir Scanning direction
273+ * @param *offnum start offset
274+ * @param inskey BTsacnInsertData
275+ * @param has_init_inskey whether to initialize the key
276+ * @return Buffer return the start scanning buffer
277+ */
278+Buffer _bt_get_begin_parallel_scan_buf(IndexScanDesc bt_scan, ScanDirection dir, OffsetNumber* offnum,
279+ BTScanInsertData& inskey, bool* has_init_inskey)
280+{
281+ Relation bt_rel = bt_scan->indexRelation;
282+ Buffer buf;
283+ bool res = false;
284+ ScanKey start_keys[INDEX_MAX_KEYS] = {0};
285+ StrategyNumber strat_total = BTEqualStrategyNumber;
286+ int keys_count = _bt_get_start_keys(bt_scan, dir, start_keys, strat_total);
287+ if (keys_count == 0) {
288+ return _bt_get_first_buf_without_scankey(bt_scan, dir, offnum);
289+ }
290+ Assert(keys_count <= INDEX_MAX_KEYS);
291+ for (int i = 0; i < keys_count; i++) {
292+ ScanKey cur = start_keys[i];
293+ Assert(cur->sk_attno == i + 1);
294+ if (cur->sk_flags & SK_ROW_HEADER) {
295+ bool continue_loop = true;
296+ res = _bt_get_inskey_scankey_with_rowheader(cur, &inskey, strat_total, keys_count, i, continue_loop);
297+ if (!res) {
298+ return InvalidBuffer;
299+ }
300+ if (!continue_loop) {
301+ break;
302+ }
303+ } else {
304+ _bt_get_inskey_scankey_without_rowheader(cur, bt_rel, i, &inskey);
305+ }
306+ }
307+ bool nextkey = false;
308+ bool goback = false;
309+ res = _bt_get_goback_need_to_next(dir, &goback, &nextkey, strat_total);
310+ if (!res) {
311+ return InvalidBuffer;
312+ }
313+ _bt_init_inskey(&inskey, bt_rel, nextkey, keys_count);
314+ if (has_init_inskey != nullptr) {
315+ *has_init_inskey = true;
316+ }
317+ BlockNumber end_block = bt_scan->btps_end_block;
318+ (void)_bt_search(bt_rel, &inskey, &buf, BT_READ, false, end_block);
319+ if (!BufferIsValid(buf)) {
320+ PredicateLockRelation(bt_rel, bt_scan->xs_snapshot);
321+ return InvalidBuffer;
322+ } else {
323+ PredicateLockPage(bt_rel, BufferGetBlockNumber(buf), bt_scan->xs_snapshot);
324+ }
325+ int posting_off = 0;
326+ *offnum = _bt_binsrch(bt_rel, &inskey, buf, &posting_off);
327+ if (goback) {
328+ *offnum = OffsetNumberPrev(*offnum);
329+ }
330+ return buf;
331+}
332+ 
333+/*
334+ * @brief _bt_parallel_first_thread0_proc
335+ * In ther parallel_first func, thread 0 divides the scan blocks and records them to the shared memory,
336+ * and assigns the scan start and end blocks of thread 0.
337+ * @param bt_scan IndexScanDesc
338+ * @param dir Scanning direction
339+ * @param curr_off_start 2D Offset in Shared Memory
340+ * @param index One-dimensional index of the current index in the shared memory
341+ * @param bt_start_blk Start scan block number of thread 0
342+ * @param stream_nodegroup StreamNodeGroup
343+ * @param offnum Start offset
344+ * @return bool returns true if there is no error, false otherwise
345+ */
346+bool _bt_parallel_first_thread0_proc(IndexScanDesc bt_scan, ScanDirection dir, int curr_off_start, int index,
347+ BlockNumber& bt_start_blk, StreamNodeGroup* stream_nodegroup, OffsetNumber* offnum)
348+{
349+ Relation bt_rel = bt_scan->indexRelation;
350+ int curr_th0_start = curr_off_start + OFFSET_START_BASE;
351+ int curr_th0_end = curr_off_start + OFFSET_END_BASE;
352+ BTScanOpaque bt_para_so = (BTScanOpaque)bt_scan->opaque;
353+ BlockNumber blkno = InvalidBuffer;
354+ BTScanInsertData inskey = {0};
355+ Buffer begin_buf = _bt_get_begin_parallel_scan_buf(bt_scan, dir, offnum, inskey);
356+ if (BufferIsValid(begin_buf)) {
357+ blkno = BufferGetBlockNumber(begin_buf);
358+ _bt_relbuf(bt_rel, begin_buf);
359+ } else {
360+ bt_para_so->currPos.buf = InvalidBuffer;
361+ return false;
362+ }
363+ int real_scan_blocks = _bt_get_parallel_scan_total_blocks(bt_scan, dir, blkno);
364+ int num_blocks = (real_scan_blocks + (bt_scan->dop - 1)) / bt_scan->dop;
365+ 
366+ pthread_mutex_t* mutex = stream_nodegroup->GetIndexSmpMutex();
367+ pthread_cond_t* cond = stream_nodegroup->GetIndexSmpCond();
368+ ereport(LOG, (errmsg("btree parallel scan oid %u, dop %d, %d blocks per thread, total blocks %d, real scan blocks "
369+ "%u, plan nodeid %u.",
370+ bt_rel->rd_id, bt_scan->dop, num_blocks, RelationGetNumberOfBlocks(bt_rel), real_scan_blocks,
371+ bt_scan->plan_nodeid)));
372+ MemoryContext old_mem_context = MemoryContextSwitchTo(stream_nodegroup->m_streamRuntimeContext);
373+ PthreadMutexLock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
374+ stream_nodegroup->parallel_indexscan_map[index][curr_off_start + OFFSET_START_BASE] = blkno;
375+ PthreadMutexUnlock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
376+ for (int i = OFFSET_START_BASE; i < bt_scan->dop; i++) {
377+ PthreadMutexLock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
378+ uint32 current_block = stream_nodegroup->parallel_indexscan_map[index][curr_off_start + i];
379+ PthreadMutexUnlock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
380+ BlockNumber bt_next = find_next_block(bt_scan, dir, current_block, num_blocks);
381+ int next_thread_start_offset = curr_off_start + i + OFFSET_START_BASE;
382+ PthreadMutexLock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
383+ stream_nodegroup->parallel_indexscan_map[index][next_thread_start_offset] =
384+ bt_next == 0 ? InvalidBlockNumber : bt_next;
385+ PthreadMutexUnlock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
386+ }
387+ PthreadMutexLock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
388+ stream_nodegroup->parallel_indexscan_map[index][curr_off_start + bt_scan->dop + OFFSET_START_BASE] =
389+ InvalidBlockNumber;
390+ stream_nodegroup->parallel_indexscan_map[index][curr_off_start] = bt_scan->plan_nodeid;
391+ pg_memory_barrier();
392+ bt_start_blk = stream_nodegroup->parallel_indexscan_map[index][curr_th0_start];
393+ bt_scan->btps_end_block = stream_nodegroup->parallel_indexscan_map[index][curr_th0_end];
394+ pthread_cond_broadcast(cond);
395+ PthreadMutexUnlock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
396+ MemoryContextSwitchTo(old_mem_context);
397+ return true;
398+}
399+ 
400+/*
401+ * @brief _bt_parallel_first_get_first_buffer
402+ * Get start scan block of the current thread.
403+ * @param bt_scan IndexScanDesc
404+ * @param dir Scanning direction
405+ * @param bt_start_blk Start scan block number of current thread
406+ * @param offnum Start offset
407+ * @param inskey BTScanInsertData
408+ * @param has_init_inskey has init key
409+ * @return Buffer Start scan block of the current thread
410+ */
411+Buffer _bt_parallel_first_get_first_buffer(IndexScanDesc bt_scan, ScanDirection dir, BlockNumber bt_start_blk,
412+ OffsetNumber* offnum, BTScanInsertData& inskey, bool* has_init_inskey)
413+{
414+ Buffer buf;
415+ Relation bt_rel = bt_scan->indexRelation;
416+ BTScanOpaque bt_para_so = (BTScanOpaque)bt_scan->opaque;
417+ int thread_id = (int)(u_sess->stream_cxt.smp_id);
418+ if (ScanDirectionIsForward(dir)) {
419+ if (thread_id == 0) {
420+ buf = _bt_get_begin_parallel_scan_buf(bt_scan, dir, offnum, inskey, has_init_inskey);
421+ } else {
422+ buf = _bt_getbuf(bt_rel, bt_start_blk, BT_READ);
423+ }
424+ } else {
425+ if (thread_id == 0) {
426+ if (bt_para_so->numberOfKeys == 0) {
427+ BlockNumber end_block = bt_scan->btps_end_block;
428+ buf = _bt_get_endpoint(bt_rel, 0, true, end_block);
429+ } else {
430+ buf = _bt_get_begin_parallel_scan_buf(bt_scan, dir, offnum, inskey, has_init_inskey);
431+ }
432+ } else {
433+ buf = _bt_getbuf(bt_rel, bt_start_blk, BT_READ);
434+ buf = _bt_walk_left(bt_rel, buf, bt_scan->btps_end_block);
435+ }
436+ }
437+ ereport(LOG, (errmsg("btree parallel scan oid %u, thread id %u begin with block %u end with %u (4294967295 meas "
438+ "InvalidBlockNumer), plan nodeid %u.",
439+ bt_rel->rd_id, u_sess->stream_cxt.smp_id, bt_start_blk, bt_scan->btps_end_block,
440+ bt_scan->plan_nodeid)));
441+ if (thread_id > 0) {
442+ if (!BufferIsValid(buf)) {
443+ PredicateLockRelation(bt_rel, bt_scan->xs_snapshot);
444+ return InvalidBuffer;
445+ } else {
446+ PredicateLockPage(bt_rel, BufferGetBlockNumber(buf), bt_scan->xs_snapshot);
447+ }
448+ }
449+ ereport(DEBUG2, (errmodule(MOD_INDEX),
450+ errmsg("btree parallel scan oid %u, thread id %u start with block %u buf %d paln nodeid %u.",
451+ bt_rel->rd_id, u_sess->stream_cxt.smp_id,
452+ buf == InvalidBuffer ? 0 : BufferGetBlockNumber(buf), buf, bt_scan->plan_nodeid)));
453+ return buf;
454+}
455+ 
456+/*
457+ * @brief _bt_parallel_first_exec_scan
458+ * Perform a scan in the current scan interval
459+ * @param bt_scan IndexScanDesc
460+ * @param dir Scanning direction
461+ * @param bt_start_blk Start scan block number of current thread
462+ * @param offnum Start offset
463+ * @return bool returns true if there is no error, false otherwise
464+ */
465+bool _bt_parallel_first_exec_scan(IndexScanDesc bt_scan, ScanDirection dir, BlockNumber bt_start_blk,
466+ OffsetNumber offnum)
467+{
468+ BTScanOpaque bt_para_so = (BTScanOpaque)bt_scan->opaque;
469+ int thread_id = (int)(u_sess->stream_cxt.smp_id);
470+ BTScanInsertData inskey = {0};
471+ bool has_ini_inskey = false;
472+ bt_para_so->currPos.buf =
473+ _bt_parallel_first_get_first_buffer(bt_scan, dir, bt_start_blk, &offnum, inskey, &has_ini_inskey);
474+ if (bt_para_so->currPos.buf == InvalidBuffer) {
475+ return false;
476+ }
477+ if (ScanDirectionIsForward(dir) && bt_scan->btps_end_block == BufferGetBlockNumber(bt_para_so->currPos.buf)) {
478+ _bt_relbuf(bt_scan->indexRelation, bt_para_so->currPos.buf);
479+ bt_para_so->currPos.buf = InvalidBuffer;
480+ return false;
481+ }
482+ /* init moreRight/modeLeft for scan direction */
483+ bt_para_so->currPos.moreRight = (ScanDirectionIsForward(dir)) ? true : false;
484+ bt_para_so->currPos.moreLeft = (ScanDirectionIsForward(dir)) ? false : true;
485+ bt_para_so->markItemIndex = -1;
486+ bt_para_so->numKilled = 0;
487+ if (bt_para_so->numberOfKeys == 0 || thread_id != 0) {
488+ BTPageOpaqueInternal opaque =
489+ (BTPageOpaqueInternal)PageGetSpecialPointer(BufferGetPage(bt_para_so->currPos.buf));
490+ if (ScanDirectionIsBackward(dir)) {
491+ offnum = PageGetMaxOffsetNumber(BufferGetPage(bt_para_so->currPos.buf));
492+ } else if (ScanDirectionIsForward(dir)) {
493+ /* There could be dead pages to the left, so not this. */
494+ offnum = P_FIRSTDATAKEY(opaque);
495+ } else {
496+ ereport(ERROR, (errcode(ERRCODE_INDEX_CORRUPTED), errmsg("Invalid scan direction: %d", dir)));
497+ offnum = 0; /* init start anyway */
498+ }
499+ }
500+ if (!_bt_readpage(bt_scan, dir, offnum)) {
501+ if (!_bt_parallel_steppage(bt_scan, dir)) {
502+ return false;
503+ }
504+ }
505+ /* unlock the current page, but held the pin */
506+ LockBuffer(bt_para_so->currPos.buf, BUFFER_LOCK_UNLOCK);
507+ 
508+ BTScanPosItem* curr_item = &bt_para_so->currPos.items[bt_para_so->currPos.itemIndex];
509+ bt_scan->xs_ctup.t_self = curr_item->heapTid;
510+ if (bt_scan->xs_want_itup) {
511+ bt_scan->xs_itup = (IndexTuple)(bt_para_so->currTuples + curr_item->tupleOffset);
512+ }
513+ if (bt_scan->xs_want_ext_oid && GPIScanCheckPartOid(bt_scan->xs_gpi_scan, curr_item->partitionOid)) {
514+ GPISetCurrPartOid(bt_scan->xs_gpi_scan, curr_item->partitionOid);
515+ }
516+ if (bt_scan->xs_want_bucketid && cbi_scan_need_change_bucket(bt_scan->xs_cbi_scan, curr_item->bucketid)) {
517+ cbi_set_bucketid(bt_scan->xs_cbi_scan, curr_item->bucketid);
518+ }
519+ return true;
520+}
521+ 
522+/*
523+* @brief _bt_parallel_first
524+* Find the first item in a paralled index scan, and mark the start/end block of this thread
525+* during paralled index scanning.
526+* If DOP of current scan is not 1, the index scan should be parallelled.
527+* In the scanning direction, we need to pay attention to the start block and end block
528+* of current thread.
529+* If the end block was marked as InvalidBlockNumber, the thread would scan util
530+* the last block is met.
531+* It should be noticed that, not all threads will actually do the scanning, bt_para_some of the thread
532+* may not get ana blocks for inapproriate DOP value. For that case, the thread will just resturn.
533+* The shared momory is allocated as follows for the first time:
534+* | 32 | 32 | 32 | 32 | 32 | ... | 32 | 32 |
535+* | index_oid1 | nodeid_num | plan_node_id0 | th0_start | th1_start | ... | thn_start | thn_end |
536+* | .......... | .......... | .......... .. | ......... | ......... | ... | ......... | ....... |
537+* | index_oidn | nodeid_num | plan_node_id0 | th0_start | th1_start | ... | thn_start | thn_end |
538+*
539+* When a new plan node performs parallel scan, the realloc shared memory is as follows:
540+* | oidn | nodeid_num | plan_node_id0 | th0_start | ... | thn_end | plan_node_id1 | th0_start | ... | thn_end | ...
541+*
542+* In the hashbucket table, the first 16 bits of the paln_node_id record the pland nodeid value, and the last 16
543+* bits record the bucket id value. | 32 | 32 | 16 | 16 | 32 | 32 |
544+* ... | 32 | 32 | | index_oidn | nodeid_num | plan_node_id0 | bucket_id | th0_start | th1_start | ... |
545+* thn_start | thn_end |
546+*
547+* @param bt_scan IndexScanDesc
548+* @param dir Scanning direction
549+* @return bool returns true if there is no error, false otherwise.
550+*/
551+bool _bt_parallel_first(IndexScanDesc bt_scan, ScanDirection dir)
552+{
553+ Relation bt_rel = bt_scan->indexRelation;
554+ BlockNumber real_blocks = RelationGetNumberOfBlocksInFork(bt_rel, MAIN_FORKNUM);
555+ if (real_blocks <= 1) {
556+ return false;
557+ }
558+ OffsetNumber offnum = InvalidOffsetNumber;
559+ uint32 thread_id = u_sess->stream_cxt.smp_id;
560+ int index = 0;
561+ int curr_off_start = -1;
562+ int node_interval = _bt_get_node_interval(bt_scan->dop);
563+ BlockNumber bt_start_blk = InvalidBlockNumber;
564+ StreamNodeGroup* stream_nodegroup = u_sess->stream_cxt.global_obj;
565+ pthread_mutex_t* mutex = stream_nodegroup->GetIndexSmpMutex();
566+ if (thread_id == 0) {
567+ PthreadMutexLock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
568+ {
569+ index = _bt_find_parallel_divd(stream_nodegroup->parallel_indexscan_map, bt_rel->rd_id,
570+ stream_nodegroup->parallel_indexscan_size);
571+ MemoryContext old_mem_context = MemoryContextSwitchTo(stream_nodegroup->m_streamRuntimeContext);
572+ if (index != -1) {
573+ _bt_parallel_reallocat_shared_memory(stream_nodegroup, index, curr_off_start, node_interval);
574+ } else {
575+ _bt_parallel_allocat_shared_memory(bt_rel, stream_nodegroup, index, curr_off_start, node_interval);
576+ }
577+ MemoryContextSwitchTo(old_mem_context);
578+ }
579+ PthreadMutexUnlock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
580+ }
581+ bool res = false;
582+ if (thread_id == 0) {
583+ res = _bt_parallel_first_thread0_proc(bt_scan, dir, curr_off_start, index, bt_start_blk, stream_nodegroup,
584+ &offnum);
585+ } else {
586+ res = _bt_parallel_first_threadn_proc(bt_scan, dir, curr_off_start, bt_start_blk, stream_nodegroup);
587+ }
588+ if (res) {
589+ res = _bt_parallel_first_exec_scan(bt_scan, dir, bt_start_blk, offnum);
590+ }
591+ return res;
592+}
593+ 
594+/*
595+ * @brief _bt_parallel_next
596+ * Get the next item on parallel index scan
597+ * call parallel_step_page to get next page.
598+ * @param bt_scan IndexScanDesc
599+ * @param dir Scanning direction
600+ * @return bool returns true if there is no error, false otherwise.
601+ */
602+bool _bt_parallel_next(IndexScanDesc bt_scan, ScanDirection dir)
603+{
604+ BTScanOpaque bt_para_so = (BTScanOpaque)bt_scan->opaque;
605+ if (ScanDirectionIsForward(dir)) {
606+ if (++bt_para_so->currPos.itemIndex > bt_para_so->currPos.lastItem) {
607+ /* We must acquire lock before, applying _bt_steppage */
608+ Assert(BufferIsValid(bt_para_so->currPos.buf));
609+ LockBuffer(bt_para_so->currPos.buf, BT_READ);
610+ if (!_bt_parallel_steppage(bt_scan, dir)) {
611+ return false;
612+ }
613+ ereport(DEBUG2,
614+ (errmodule(MOD_INDEX),
615+ errmsg("btree forward index parallel scan oid %u, thread id %u deal with block %u paln nodeid %u.",
616+ bt_scan->indexRelation->rd_id, u_sess->stream_cxt.smp_id,
617+ BufferGetBlockNumber(bt_para_so->currPos.buf), bt_scan->plan_nodeid)));
618+ /* Drop the lock, but not pin, on the new page */
619+ LockBuffer(bt_para_so->currPos.buf, BUFFER_LOCK_UNLOCK);
620+ }
621+ } else {
622+ if (--bt_para_so->currPos.itemIndex < bt_para_so->currPos.firstItem) {
623+ /* We must acquire lock before, applying _bt_steppage */
624+ Assert(BufferIsValid(bt_para_so->currPos.buf));
625+ LockBuffer(bt_para_so->currPos.buf, BT_READ);
626+ if (!_bt_parallel_steppage(bt_scan, dir)) {
627+ return false;
628+ }
629+ ereport(
630+ DEBUG2,
631+ (errmodule(MOD_INDEX),
632+ errmsg("btree backforward index parallel scan oid %u, thread id %u deal with block %u paln nodeid %u.",
633+ bt_scan->indexRelation->rd_id, u_sess->stream_cxt.smp_id,
634+ BufferGetBlockNumber(bt_para_so->currPos.buf), bt_scan->plan_nodeid)));
635+ /* Drop the lock, but not pin, on the new page */
636+ LockBuffer(bt_para_so->currPos.buf, BUFFER_LOCK_UNLOCK);
637+ }
638+ }
639+ 
640+ /* OK, itemIndex says what to return */
641+ BTScanPosItem* para_curr_item = &bt_para_so->currPos.items[bt_para_so->currPos.itemIndex];
642+ bt_scan->xs_ctup.t_self = para_curr_item->heapTid;
643+ if (bt_scan->xs_want_itup) {
644+ bt_scan->xs_itup = (IndexTuple)(bt_para_so->currTuples + para_curr_item->tupleOffset);
645+ }
646+ if (bt_scan->xs_want_ext_oid && GPIScanCheckPartOid(bt_scan->xs_gpi_scan, para_curr_item->partitionOid)) {
647+ GPISetCurrPartOid(bt_scan->xs_gpi_scan, para_curr_item->partitionOid);
648+ }
649+ if (bt_scan->xs_want_bucketid && cbi_scan_need_change_bucket(bt_scan->xs_cbi_scan, para_curr_item->bucketid)) {
650+ cbi_set_bucketid(bt_scan->xs_cbi_scan, para_curr_item->bucketid);
651+ }
652+ return true;
653+}
654+ 
655+/*
656+ * @brief _bt_parallel_steppage
657+ * Go to the next page for parallel index scan
658+ * The whole process i just like ordiany step_page, except that we just stopo read for current thread if end block
659+ * is met.
660+ * @param bt_scan IndexScanDesc
661+ * @param dir Scanning direction
662+ * @return bool returns true if there is no error, false otherwise
663+ */
664+bool _bt_parallel_steppage(IndexScanDesc bt_scan, ScanDirection dir)
665+{
666+ BTScanOpaque bt_para_so = (BTScanOpaque)bt_scan->opaque;
667+ Relation bt_rel;
668+ Page page = NULL;
669+ BTPageOpaqueInternal opaque = NULL;
670+ /* we must have the buffer pinned and locked */
671+ Assert(BufferIsValid(bt_para_so->currPos.buf));
672+ 
673+ /* Before leaving current page, deal with any killed items */
674+ if (bt_para_so->numKilled > 0)
675+ _bt_killitems(bt_scan, true);
676+ 
677+ /*
678+ * Before we modify currPos, make a copy of the page data if there was a
679+ * mark position that needs it.
680+ */
681+ if (bt_para_so->markItemIndex >= 0) {
682+ /* bump pin on current buffer for assignment to mark buffer */
683+ IncrBufferRefCount(bt_para_so->currPos.buf);
684+ errno_t rc = memcpy_s(&bt_para_so->markPos,
685+ offsetof(BTScanPosData, items[1]) + bt_para_so->currPos.lastItem * sizeof(BTScanPosItem),
686+ &bt_para_so->currPos,
687+ offsetof(BTScanPosData, items[1]) + bt_para_so->currPos.lastItem * sizeof(BTScanPosItem));
688+ securec_check(rc, "", "");
689+ if (bt_para_so->markTuples) {
690+ rc = memcpy_s(bt_para_so->markTuples, (size_t)bt_para_so->currPos.nextTupleOffset, bt_para_so->currTuples,
691+ (size_t)bt_para_so->currPos.nextTupleOffset);
692+ securec_check(rc, "", "");
693+ }
694+ bt_para_so->markPos.itemIndex = bt_para_so->markItemIndex;
695+ bt_para_so->markItemIndex = -1;
696+ }
697+ bt_rel = bt_scan->indexRelation;
698+ if (ScanDirectionIsForward(dir)) {
699+ BlockNumber blkno = bt_para_so->currPos.nextPage;
700+ bt_para_so->currPos.moreLeft = true;
701+ for (;;) {
702+ Buffer cur_buf = bt_para_so->currPos.buf;
703+ /*
704+ * Before step to right sibling, keep the pin of origin page to prevent the origin
705+ * page from begin compressed and merged (such ILM) to its right sibling.
706+ * The compressed data will be moved to its right sibling, which will casuse repeatly reads.
707+ */
708+ LockBuffer(bt_para_so->currPos.buf, BUFFER_LOCK_UNLOCK);
709+ bt_para_so->currPos.buf = InvalidBuffer;
710+ /* if we're at end of scan, give up */
711+ if (bt_scan->btps_end_block != InvalidBlockNumber && blkno == bt_scan->btps_end_block) {
712+ ReleaseBuffer(cur_buf);
713+ return false;
714+ }
715+ if (blkno == P_NONE || !bt_para_so->currPos.moreRight) {
716+ ReleaseBuffer(cur_buf);
717+ ereport(DEBUG1, (errmodule(MOD_INDEX),
718+ errmsg("index parallel scan reach and thread id: %d.", u_sess->stream_cxt.smp_id)));
719+ return false;
720+ }
721+ ReleaseBuffer(cur_buf);
722+ CHECK_FOR_INTERRUPTS();
723+ bt_para_so->currPos.buf = _bt_getbuf(bt_rel, blkno, BT_READ);
724+ page = BufferGetPage(bt_para_so->currPos.buf);
725+ opaque = (BTPageOpaqueInternal)PageGetSpecialPointer(page);
726+ while (P_IGNORE(opaque)) {
727+ blkno = opaque->btpo_next;
728+ _bt_relbuf(bt_rel, bt_para_so->currPos.buf);
729+ bt_para_so->currPos.buf = InvalidBuffer;
730+ if ((bt_scan->btps_end_block != InvalidBlockNumber && blkno == bt_scan->btps_end_block) ||
731+ blkno == P_NONE) {
732+ return false;
733+ }
734+ /* setp right one page */
735+ bt_para_so->currPos.buf = _bt_getbuf(bt_rel, blkno, BT_READ);
736+ page = BufferGetPage(bt_para_so->currPos.buf);
737+ opaque = (BTPageOpaqueInternal)PageGetSpecialPointer(page);
738+ }
739+ PredicateLockPage(bt_rel, blkno, bt_scan->xs_snapshot);
740+ if (_bt_readpage(bt_scan, dir, P_FIRSTDATAKEY(opaque))) {
741+ break;
742+ }
743+ blkno = opaque->btpo_next;
744+ }
745+ } else {
746+ bt_para_so->currPos.moreRight = true;
747+ for (;;) {
748+ CHECK_FOR_INTERRUPTS();
749+ if (!bt_para_so->currPos.moreLeft) {
750+ _bt_relbuf(bt_rel, bt_para_so->currPos.buf);
751+ bt_para_so->currPos.buf = InvalidBuffer;
752+ return false;
753+ }
754+ Buffer temp = bt_para_so->currPos.buf;
755+ bt_para_so->currPos.buf = _bt_walk_left(bt_rel, temp, bt_scan->btps_end_block);
756+ if (bt_para_so->currPos.buf == InvalidBuffer) {
757+ return false;
758+ }
759+ page = BufferGetPage(bt_para_so->currPos.buf);
760+ opaque = (BTPageOpaqueInternal)PageGetSpecialPointer(page);
761+ if (!P_IGNORE(opaque)) {
762+ PredicateLockPage(bt_rel, BufferGetBlockNumber(bt_para_so->currPos.buf), bt_scan->xs_snapshot);
763+ if (_bt_readpage(bt_scan, dir, PageGetMaxOffsetNumber(page))) {
764+ break;
765+ }
766+ }
767+ }
768+ }
769+ return true;
770+}
@@ -0,0 +1,215 @@
1+/*
2+ * Copyright (c) 2020 Huawei Technologies Co.,Ltd.
3+ * Portions Copyright (c) 2021, openGauss Contributors
4+ *
5+ * openGauss is licensed under Mulan PSL v2.
6+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
7+ * You may obtain a copy of Mulan PSL v2 at:
8+ *
9+ * http://license.coscl.org.cn/MulanPSL2
10+ *
11+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
12+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
13+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
14+ * See the Mulan PSL v2 for more details.
15+ * ---------------------------------------------------------------------------------------
16+ *
17+ * parallel_indexscan_scankey_procs.cpp
18+ *
19+ *
20+ *
21+ * IDENTIFICATION
22+ * src\gausskernel\storage\access\nbtree\parallel_indexscan_scankey_procs.cpp
23+ *
24+ * ---------------------------------------------------------------------------------------
25+ */
26+#include "postgres.h"
27+#include "access/nbtree.h"
28+#include "access/parallel_indexscan_core.h"
29+ 
30+/*
31+ * @brief _bt_get_locating_start_scankey
32+ * Get start scan block of the current thread
33+ * @param cur current scankey
34+ * @param chosen temporary variable
35+ * @param implies_not_null temporary variable
36+ * @param dir scan direction
37+ * @return void
38+ */
39+void _bt_get_locating_start_scankey(ScanKey* cur, ScanKey* chosen, ScanKey* implies_not_null, ScanDirection dir)
40+{
41+ switch ((*cur)->sk_strategy) {
42+ case BTEqualStrategyNumber:
43+ *chosen = *cur;
44+ break;
45+ case BTLessStrategyNumber:
46+ case BTLessEqualStrategyNumber:
47+ if (*chosen == NULL) {
48+ if (ScanDirectionIsBackward(dir)) {
49+ *chosen = *cur;
50+ } else {
51+ *implies_not_null = *cur;
52+ }
53+ }
54+ break;
55+ case BTGreaterEqualStrategyNumber:
56+ case BTGreaterStrategyNumber:
57+ if (*chosen == NULL) {
58+ if (ScanDirectionIsForward(dir)) {
59+ *chosen = *cur;
60+ } else {
61+ *implies_not_null = *cur;
62+ }
63+ }
64+ break;
65+ default:
66+ break;
67+ }
68+}
69+ 
70+/*
71+ * @brief _bt_get_start_keys
72+ * Gets the condition used to locate the start scan position
73+ * @param scan IndexScanDesc
74+ * @param dir scan direction
75+ * @param start_keys different scanning conditions
76+ * @param para_strat_total startkeys number
77+ * @return int startkeys number
78+ */
79+int _bt_get_start_keys(IndexScanDesc scan, ScanDirection dir, ScanKey (&start_keys)[INDEX_MAX_KEYS],
80+ StrategyNumber& para_strat_total)
81+{
82+ ScanKeyData* notnullkeys = (ScanKeyData*)palloc0(INDEX_MAX_KEYS * sizeof(ScanKeyData));
wofanzheng
wofanzhengwofanzheng6月21日

【问题】_bt_get_start_keys() 里新分配的 notnullkeys 没有任何释放路径。这个函数会被 btree/ubtree/ubtreepcr 的并行首扫都走到,遇到 rescan 或一个查询里多次触发并行索引扫描时,会持续往当前 MemoryContext 累积泄漏。 【建议】不要在这里裸 palloc0 一个临时数组后直接返回;可以改成栈上对象,或者在函数尾统一 pfree_ext(notnullkeys)

likedislike
xiahanzhi
6月22日 评论:
83+ int keys_count = 0;
84+ StrategyNumber strat = InvalidStrategy;
85+ BTScanOpaque so = (BTScanOpaque)scan->opaque;
86+ int i = 0;
87+ if (so->numberOfKeys > 0) {
88+ AttrNumber para_curattr = 1;
89+ ScanKey para_chosen = NULL;
90+ ScanKey para_impies_not_null = NULL;
91+ ScanKey cur = NULL;
92+ for (cur = so->keyData, i = 0;; cur++, i++) {
93+ if (i >= so->numberOfKeys || cur->sk_attno != para_curattr) {
94+ if (para_chosen == NULL && para_impies_not_null != NULL &&
95+ ((para_impies_not_null->sk_flags & SK_BT_NULLS_FIRST) ? ScanDirectionIsForward(dir)
96+ : ScanDirectionIsBackward(dir))) {
97+ para_chosen = &notnullkeys[keys_count];
98+ ScanKeyEntryInitialize(
99+ para_chosen,
100+ (SK_SEARCHNOTNULL | SK_ISNULL |
101+ (para_impies_not_null->sk_flags & (SK_BT_DESC | SK_BT_INDOPTION_SHIFT))),
102+ para_curattr,
103+ ((para_impies_not_null->sk_flags & SK_BT_NULLS_FIRST) ? BTGreaterStrategyNumber
104+ : BTLessStrategyNumber),
105+ InvalidOid, InvalidOid, InvalidOid, (Datum)0);
106+ }
107+ if (para_chosen == NULL) {
108+ break;
109+ }
110+ start_keys[keys_count++] = para_chosen;
111+ strat = para_chosen->sk_strategy;
112+ if (strat != BTEqualStrategyNumber) {
113+ para_strat_total = strat;
114+ if (strat == BTGreaterStrategyNumber || strat == BTLessStrategyNumber) {
115+ break;
116+ }
117+ }
118+ if (i >= so->numberOfKeys || cur->sk_attno != para_curattr + 1) {
119+ break;
120+ }
121+ para_curattr = cur->sk_attno;
122+ para_chosen = NULL;
123+ para_impies_not_null = NULL;
124+ }
125+ _bt_get_locating_start_scankey(&cur, &para_chosen, &para_impies_not_null, dir);
126+ }
127+ }
128+ return keys_count;
129+}
130+ 
131+/*
132+ * @brief _bt_get_inskey_scankey_with_rowheader
133+ * Gets the condition used to locate the start scan position
134+ * @param cur current scankey
135+ * @param inskey BTScanInsertData
136+ * @param param_strat_total different scanning conditions
137+ * @param keys_count startkeys number
138+ * @param i index
139+ * @param continue_loop continue iteration
140+ * @return bool returns true if there is no error, false otherwise
141+ */
142+bool _bt_get_inskey_scankey_with_rowheader(ScanKey cur, BTScanInsertData* inskey, StrategyNumber& param_strat_total,
143+ int& keys_count, int i, bool& continue_loop)
144+{
145+ ScanKey para_subkey = (ScanKey)DatumGetPointer(cur->sk_argument);
146+ Assert(para_subkey->sk_flags & SK_ROW_HEADER);
147+ if (para_subkey->sk_flags & SK_ISNULL) {
148+ return false;
149+ }
150+ inskey->scankeys[i] = *para_subkey;
151+ if (i == keys_count - 1) {
152+ bool used_all_subkeys = false;
153+ Assert(!(para_subkey->sk_flags & SK_ROW_END));
154+ for (;;) {
155+ para_subkey++;
156+ Assert(para_subkey->sk_flags & SK_ROW_MEMBER);
157+ if (para_subkey->sk_attno != keys_count + 1) {
158+ break; /* out-of-sequence, can't use it */
159+ }
160+ if (para_subkey->sk_strategy != cur->sk_strategy) {
161+ break; /* wrong direction, cna't use it */
162+ }
163+ if (para_subkey->sk_flags & SK_ISNULL) {
164+ break; /* can't use null keys */
165+ }
166+ Assert(keys_count < INDEX_MAX_KEYS);
167+ inskey->scankeys[keys_count] = *para_subkey;
168+ keys_count++;
169+ if (para_subkey->sk_flags & SK_ROW_END) {
170+ used_all_subkeys = true;
171+ break;
172+ }
173+ }
174+ if (!used_all_subkeys) {
175+ switch (param_strat_total) {
176+ case BTLessStrategyNumber:
177+ param_strat_total = BTLessEqualStrategyNumber;
178+ break;
179+ case BTGreaterStrategyNumber:
180+ param_strat_total = BTGreaterEqualStrategyNumber;
181+ break;
182+ default:
183+ break;
184+ }
185+ }
186+ continue_loop = false;
187+ }
188+ return true;
189+}
190+ 
191+/*
192+ * @brief _bt_get_first_buf_without_scankey
193+ * If the number of start conditions is 0, the start buffer and offset are returned.
194+ * @param scan refer to IndexScanDesc(btree & ubtree)
195+ * @param dir Scanning direction
196+ * @param offnum Start offset
197+ * @return Buffer Start scanning buffer
198+ */
199+Buffer _bt_get_first_buf_without_scankey(IndexScanDesc scan, ScanDirection dir, OffsetNumber* offnum)
200+{
201+ Relation rel = scan->indexRelation;
202+ BlockNumber end_block = scan->btps_end_block;
203+ Buffer buf = _bt_get_endpoint(rel, 0, ScanDirectionIsBackward(dir), end_block);
204+ if (BufferIsInvalid(buf)) {
205+ return InvalidBuffer;
206+ }
207+ BTPageOpaqueInternal opaque = (BTPageOpaqueInternal)PageGetSpecialPointer(BufferGetPage(buf));
208+ if (ScanDirectionIsBackward(dir)) {
209+ *offnum = PageGetMaxOffsetNumber(BufferGetPage(buf));
210+ } else if (ScanDirectionIsForward(dir)) {
211+ /* There could be dead pages to the left, so not this. */
212+ *offnum = P_FIRSTDATAKEY(opaque);
213+ }
214+ return buf;
215+}
@@ -0,0 +1,326 @@
1+/*
2+ * Copyright (c) 2020 Huawei Technologies Co.,Ltd.
3+ * Portions Copyright (c) 2021, openGauss Contributors
4+ *
5+ * openGauss is licensed under Mulan PSL v2.
6+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
7+ * You may obtain a copy of Mulan PSL v2 at:
8+ *
9+ * http://license.coscl.org.cn/MulanPSL2
10+ *
11+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
12+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
13+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
14+ * See the Mulan PSL v2 for more details.
15+ * ---------------------------------------------------------------------------------------
16+ *
17+ * parallel_indexscan_thread_proc.cpp
18+ *
19+ *
20+ *
21+ * IDENTIFICATION
22+ * src\gausskernel\storage\access\nbtree\parallel_indexscan_thread_proc.cpp
23+ *
24+ * ---------------------------------------------------------------------------------------
25+ */
26+#include "postgres.h"
27+#include "access/nbtree.h"
28+#include "executor/executor.h"
29+#include "distributelayer/streamProducer.h"
30+#include "access/parallel_indexscan_core.h"
31+ 
32+#define DIV_TIME_OUT 1
33+ 
34+/*
35+ * @brief _bt_parallel_reallocat_shared_memory
36+ * When a new plan node performs a parallel index scan, realloc share memory.
37+ * @param stream_nodegroup StreamNodeGroup
38+ * @param index One_dimensional index of the current index in the shared memory
39+ * @param curr_off_start 2D offset in Shared Memory
40+ * @param node_interval Length of shared memory occupied by each plan code used for scanning
41+ * @return void
42+ */
43+void _bt_parallel_reallocat_shared_memory(StreamNodeGroup* stream_nodegroup, const int& index, int& curr_off_start,
44+ int node_interval)
45+{
46+ int nodeid_num = stream_nodegroup->parallel_indexscan_map[index][TOTAL_NODEID];
47+ nodeid_num++;
48+ stream_nodegroup->parallel_indexscan_map[index] =
49+ (volatile uint32*)repalloc((void*)stream_nodegroup->parallel_indexscan_map[index],
50+ (node_interval * nodeid_num + FIRST_NODE_OFFSET) * sizeof(uint32));
51+ stream_nodegroup->parallel_indexscan_map[index][TOTAL_NODEID]++;
52+ curr_off_start = node_interval * (nodeid_num - 1) + FIRST_NODE_OFFSET;
53+ stream_nodegroup->parallel_indexscan_map[index][curr_off_start] = InvalidNodeId;
54+ for (int i = curr_off_start + 1; i < curr_off_start + node_interval; i++) {
55+ stream_nodegroup->parallel_indexscan_map[index][i] = 0;
56+ }
57+}
58+ 
59+/*
60+ * @brief _bt_parallel_allocat_shared_memory
61+ * Initial allocation of shared memory for index parallel scans.
62+ * @param rel current index relation
63+ * @param stream_nodegroup StreamNodeGroup
64+ * @param index One_dimensional index of the current index in th e shared memory
65+ * @param curr_off_start 2D offset in Shared Memory
66+ * @param node_interval Length of shared memory occupied by each plan node used for scanning
67+ * @return void
68+ */
69+void _bt_parallel_allocat_shared_memory(Relation rel, StreamNodeGroup* stream_nodegroup, int& index,
70+ int& curr_off_start, int node_interval)
71+{
72+ int size = stream_nodegroup->parallel_indexscan_size;
73+ size++;
74+ if (stream_nodegroup->parallel_indexscan_map == NULL) {
75+ stream_nodegroup->parallel_indexscan_map = (volatile uint32**)palloc0(size * sizeof(uint32*));
76+ } else {
77+ stream_nodegroup->parallel_indexscan_map =
78+ (volatile uint32**)repalloc((void*)stream_nodegroup->parallel_indexscan_map, size * sizeof(uint32*));
79+ }
80+ stream_nodegroup->parallel_indexscan_map[size - 1] =
81+ (uint32*)palloc0((node_interval + FIRST_NODE_OFFSET) * sizeof(uint32));
82+ stream_nodegroup->parallel_indexscan_size++;
83+ stream_nodegroup->parallel_indexscan_map[size - 1][INDEX_OID] = rel->rd_id;
84+ stream_nodegroup->parallel_indexscan_map[size - 1][TOTAL_NODEID] = 1;
85+ stream_nodegroup->parallel_indexscan_map[size - 1][FIRST_NODE_OFFSET] = InvalidNodeId;
86+ index = size - 1;
87+ curr_off_start = FIRST_NODE_OFFSET;
88+}
89+ 
90+ /*
91+ * @brief _bt_parallel_get_threadn_scan_range
92+ * Initial allocation of shared memory for index parallel scans.
93+ * @param scan IndexScanDesc
94+ * @param lid Start block number of the current thread.
95+ * @param rid End block number of the current thread.
96+ * @param start_block Start scan block number
97+ * @return bool returns true if there is no error, false otherwise
98+ */
99+bool _bt_parallel_get_threadn_scan_range(IndexScanDesc scan, uint32 lid, uint32 rid, BlockNumber& start_block)
100+{
101+ if (lid == InvalidBlockNumber) {
102+ start_block = InvalidBlockNumber;
103+ return false;
104+ }
105+ start_block = lid;
106+ if (lid != InvalidBlockNumber && rid == InvalidBlockNumber) {
107+ scan->btps_end_block = InvalidBlockNumber;
108+ } else {
109+ scan->btps_end_block = rid;
110+ }
111+ return true;
112+}
113+ 
114+/*
115+ * @brief _bt_find_parallel_divd
116+ * Obtains the start offset of the current index in the shared memory.
117+ * @param divd_res shared memory
118+ * @param index_oid current index relation oid
119+ * @param size number of indexes
120+ * @return int Obtains the one-dimensional index of in the shared memory.
121+ */
122+int _bt_find_parallel_divd(volatile uint32** divd_res, Oid index_oid, int size)
123+{
124+ if (divd_res == NULL) {
125+ return -1;
126+ }
127+ for (int i = 0; i < size; i++) {
128+ if (divd_res[i] == NULL) {
129+ return -1;
130+ }
131+ pg_memory_barrier();
132+ if (divd_res[i][0] == index_oid) {
133+ return i;
134+ }
135+ }
136+ return -1;
137+}
138+ 
139+/*
140+ * @brief stream_find_obj_by_nodeid
141+ * Find stream object by nodeid
142+ * @param stream_list stream object list
143+ * @param nodeid plan node identifier
144+ * @return StreamProducer pointer to stream producer
145+ */
146+StreamProducer* stream_find_obj_by_nodeid(List* stream_list, uint32 nodeid)
147+{
148+ StreamProducer* obj = NULL;
149+ foreach_cell(cell, stream_list) {
150+ obj = (StreamProducer*)lfirst(cell);
151+ if (obj->getKey().smpIdentifier == 0 && obj->m_plan->planTree->plan_node_id == (int)nodeid) {
152+ break;
153+ } else {
154+ obj = NULL;
155+ }
156+ }
157+ return obj;
158+}
159+ 
160+/*
161+ * @brief is_thread0_quit
162+ * check if hte thread 0 for this paln node if is quit
163+ * @param stream_nodegroup stream node group
164+ * @param release_lock need release lock
165+ * @return bool returns true if there is no error, false otherwise.
166+ */
167+bool is_thread0_quit(StreamNodeGroup* stream_nodegroup, bool release_lock = false)
168+{
169+ pthread_mutex_t* mutex = stream_nodegroup->GetIndexSmpMutex();
170+ StreamProducer* producer_now = u_sess->stream_cxt.producer_obj;
171+ StreamProducer* producer_thread0 =
172+ stream_find_obj_by_nodeid(stream_nodegroup->m_streamProducerList, producer_now->m_plan->planTree->plan_node_id);
173+ if (producer_thread0 == NULL) {
174+ if (release_lock) {
175+ PthreadMutexUnlock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
176+ }
177+ ereport(ERROR, (errcode(ERRCODE_INDEX_CORRUPTED), errmsg("cannot find thread 0 for parallel index scan.")));
178+ }
179+ StreamNode* array = stream_nodegroup->GetSteamArray();
180+ if (array == NULL) {
181+ if (release_lock) {
182+ PthreadMutexUnlock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
183+ }
184+ ereport(ERROR, (errcode(ERRCODE_INDEX_CORRUPTED), errmsg("cannot find array for parallel index scan.")));
185+ }
186+ int ind = producer_thread0->getNodeGroupIdx();
187+ bool inited = producer_thread0->getThreadInit();
188+ bool* stop_flag = array[ind].stopFlag;
189+ if ((stop_flag == NULL && array[ind].status != STREAM_INPROGRESS && inited) || array[ind].status == STREAM_ERROR) {
190+ ereport(DEBUG1, (errmsg("index parallel scan thread 0 quit, thread 0 status: %d.", int(array[ind].status))));
191+ return true;
192+ }
193+ return false;
194+}
195+ 
196+/*
197+ * @brief _bt_parallel_first_threadn_proc
198+ * Obtains the start offset of current index in the shared memory.
199+ * @param scan IndexScanDesc
200+ * @param dir scanning direction
201+ * @param curr_off_start 2d Offset in Shared Memory
202+ * @param start_block Start scan block number
203+ * @param stream_nodegroup StreamGroup
204+ * @return bool returns true if there is no error, false otherwise.
205+ */
206+bool _bt_parallel_first_threadn_proc(IndexScanDesc scan, ScanDirection dir, int curr_off_start,
207+ BlockNumber& start_block, StreamNodeGroup* stream_nodegroup)
208+{
209+ int thread_id = (int)(u_sess->stream_cxt.smp_id);
210+ Relation rel = scan->indexRelation;
211+ pthread_mutex_t* mutex = stream_nodegroup->GetIndexSmpMutex();
212+ pthread_cond_t* cond = stream_nodegroup->GetIndexSmpCond();
213+ MemoryContext old_mem_context = MemoryContextSwitchTo(stream_nodegroup->m_streamRuntimeContext);
214+ CHECK_FOR_INTERRUPTS();
215+ PthreadMutexLock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
216+ int index = _bt_find_parallel_divd(stream_nodegroup->parallel_indexscan_map, rel->rd_id,
217+ stream_nodegroup->parallel_indexscan_size);
218+ struct timespec timer;
219+ while (!is_thread0_quit(stream_nodegroup, true) &&
220+ (index == -1 ||
221+ _bt_find_parallel_nodeid(scan, stream_nodegroup->parallel_indexscan_map[index], &curr_off_start))) {
222+ clock_gettime(CLOCK_MONOTONIC, &timer);
223+ timer.tv_sec += DIV_TIME_OUT;
224+ timer.tv_nsec = 0;
225+ pthread_cond_timedwait(cond, mutex, &timer);
226+ index = _bt_find_parallel_divd(stream_nodegroup->parallel_indexscan_map, rel->rd_id,
227+ stream_nodegroup->parallel_indexscan_size);
228+ }
229+ PthreadMutexUnlock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
230+ if (is_thread0_quit(stream_nodegroup)) {
231+ PthreadMutexLock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
232+ /* if the thread 0 is already quit, and we cannot find the divide res for cur relation, we need to quit */
233+ index = _bt_find_parallel_divd(stream_nodegroup->parallel_indexscan_map, rel->rd_id,
234+ stream_nodegroup->parallel_indexscan_size);
235+ if (index == -1) {
236+ PthreadMutexUnlock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
237+ ereport(
238+ DEBUG1,
239+ (errmodule(MOD_INDEX),
240+ errmsg("index parallel scan oid %u, thread id %u quit for thread 0 quit and cannot find array index.",
241+ rel->rd_id, u_sess->stream_cxt.smp_id)));
242+ MemoryContextSwitchTo(old_mem_context);
243+ return false;
wofanzheng
wofanzhengwofanzheng6月21日

【问题】_bt_parallel_first_threadn_proc() 在 213 行切到 stream_nodegroup->m_streamRuntimeContext 后,index == -1 这条早退路径直接 return false,没有切回 old_mem_context。这会把后续本线程的分配落到错误的 runtime context 里,既容易造成内存归属混乱,也会让后续对象的释放时机失真。 【建议】所有 return false 的早退分支都要先 MemoryContextSwitchTo(old_mem_context),最好改成统一 goto cleanup 的收口写法,避免再漏分支

likedislike
xiahanzhi
6月22日 评论:
244+ }
245+ bool continue_wait =
246+ _bt_find_parallel_nodeid(scan, stream_nodegroup->parallel_indexscan_map[index], &curr_off_start);
247+ if (continue_wait) {
248+ PthreadMutexUnlock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
249+ ereport(DEBUG1,
250+ (errmodule(MOD_INDEX),
251+ errmsg("index parallel scan oid %u, thread id %u quit for thread 0 quit and cannot find nodeid.",
252+ rel->rd_id, u_sess->stream_cxt.smp_id)));
253+ MemoryContextSwitchTo(old_mem_context);
254+ return false;
255+ }
256+ PthreadMutexUnlock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
257+ }
258+ uint32 lid = 0;
259+ uint32 rid = 0;
260+ do {
261+ CHECK_FOR_INTERRUPTS();
262+ if (is_thread0_quit(stream_nodegroup)) {
263+ PthreadMutexLock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
264+ /* if the thread 0 is already quit, and we cannot find the divide res for cur relation, we need to quit */
265+ pg_memory_barrier();
266+ lid = stream_nodegroup->parallel_indexscan_map[index][curr_off_start + thread_id + OFFSET_START_BASE];
267+ rid = stream_nodegroup->parallel_indexscan_map[index][curr_off_start + thread_id + OFFSET_END_BASE];
268+ if (lid == 0 || rid == 0) {
269+ PthreadMutexUnlock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
270+ ereport(DEBUG1,
271+ (errmodule(MOD_INDEX),
272+ errmsg("index parallel sacn oid %u, thread id %u quit for thread 0 quit and lid/rid is zero.",
273+ rel->rd_id, u_sess->stream_cxt.smp_id)));
274+ MemoryContextSwitchTo(old_mem_context);
275+ return false;
276+ }
277+ } else {
278+ PthreadMutexLock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
279+ /* thread 0 is working, we wait for devide res */
280+ lid = stream_nodegroup->parallel_indexscan_map[index][curr_off_start + thread_id + OFFSET_START_BASE];
281+ rid = stream_nodegroup->parallel_indexscan_map[index][curr_off_start + thread_id + OFFSET_END_BASE];
282+ }
283+ PthreadMutexUnlock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
284+ } while (lid == 0 || rid == 0);
285+ MemoryContextSwitchTo(old_mem_context);
286+ bool res = _bt_parallel_get_threadn_scan_range(scan, lid, rid, start_block);
287+ if (!res) {
288+ ereport(DEBUG1,
289+ (errmodule(MOD_INDEX), errmsg("index parallel sacn oid %u, thread id %u get no blocks and quit.",
290+ rel->rd_id, u_sess->stream_cxt.smp_id)));
291+ return false;
292+ }
293+ /* when the blocks cannot be devied intto threads, some of the thread should do nothing. */
294+ if (start_block == InvalidBlockNumber || (ScanDirectionIsForward(dir) && start_block == scan->btps_end_block)) {
295+ ereport(DEBUG1,
296+ (errmodule(MOD_INDEX), errmsg("index parallel sacn oid %u, thread id %u get no blocks and quit.",
297+ rel->rd_id, u_sess->stream_cxt.smp_id)));
298+ return false;
299+ }
300+ return true;
301+}
302+ 
303+/*
304+ * @brief _bt_find_parallel_nodeid
305+ * Check whether the current plan node has been allocated in the shared memory.
306+ * If the current paln node has been allocated, record the start array subscript of the corresponding paln node.
307+ * @param scan IndexScanDesc
308+ * @param divd_arr One-dimensional array of the shared memory corresponding to the current index
309+ * @param curr_off_start 2D offset in Shared Memory
310+ * @return bool returns true if there is no error, false otherwise.
311+ */
312+bool _bt_find_parallel_nodeid(IndexScanDesc scan, volatile uint32* divd_arr, int* curr_off_start)
313+{
314+ int interval = _bt_get_node_interval(scan->dop);
315+ uint32 node_id = scan->plan_nodeid;
316+ uint32 nodeid_num = divd_arr[TOTAL_NODEID];
317+ int total = interval * static_cast<int>(nodeid_num) + FIRST_NODE_OFFSET;
318+ for (int i = FIRST_NODE_OFFSET; i < total; i += interval) {
319+ pg_memory_barrier();
320+ if (divd_arr[i] == node_id) {
321+ *curr_off_start = i;
322+ return false;
323+ }
324+ }
325+ return true;
326+}
@@ -1316,9 +1316,9 @@ static void AtStart_ResourceOwner(void)
1316 1316 
1317 /* We shouldn't have a transaction resource owner already. */1317 /* We shouldn't have a transaction resource owner already. */
1318 Assert(t_thrd.utils_cxt.TopTransactionResourceOwner == NULL);1318 Assert(t_thrd.utils_cxt.TopTransactionResourceOwner == NULL);
1319- Assert(CurrentResourceOwnerIsEmpty(t_thrd.utils_cxt.CurrentResourceOwner));1319+ Assert(t_thrd.utils_cxt.CurTransactionResourceOwner == t_thrd.utils_cxt.ThreadRootResourceOwner ||
1320+ CurrentResourceOwnerIsEmpty(t_thrd.utils_cxt.CurrentResourceOwner));
1320 Assert(!EnableLocalSysCache() || CurrentResourceOwnerIsEmpty(t_thrd.lsc_cxt.local_sysdb_resowner));1321 Assert(!EnableLocalSysCache() || CurrentResourceOwnerIsEmpty(t_thrd.lsc_cxt.local_sysdb_resowner));
1321- 
1322 /* Create a toplevel resource owner for the transaction. */1322 /* Create a toplevel resource owner for the transaction. */
1323 s->curTransactionOwner = ResourceOwnerCreate(NULL, "TopTransaction",1323 s->curTransactionOwner = ResourceOwnerCreate(NULL, "TopTransaction",
1324 THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE));1324 THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE));
@@ -3141,6 +3141,7 @@ static void CommitTransaction(bool STP_commit)
3141 t_thrd.utils_cxt.CurTransactionResourceOwner = NULL;3141 t_thrd.utils_cxt.CurTransactionResourceOwner = NULL;
3142 t_thrd.utils_cxt.TopTransactionResourceOwner = NULL;3142 t_thrd.utils_cxt.TopTransactionResourceOwner = NULL;
3143 IsolatedResourceOwner = NULL;3143 IsolatedResourceOwner = NULL;
3144+ t_thrd.utils_cxt.CurrentResourceOwner = t_thrd.utils_cxt.OutOfTransResourceOwner;
3144 AtCommit_RelationSync();3145 AtCommit_RelationSync();
3145 3146 
3146 AtCommit_Memory();3147 AtCommit_Memory();
@@ -3613,7 +3614,7 @@ static void PrepareTransaction(bool STP_commit)
3613 AtEOXact_Snapshot(true);3614 AtEOXact_Snapshot(true);
3614 pgstat_report_xact_timestamp(0);3615 pgstat_report_xact_timestamp(0);
3615 3616 
3616- t_thrd.utils_cxt.CurrentResourceOwner = NULL;3617+ t_thrd.utils_cxt.CurrentResourceOwner = t_thrd.utils_cxt.OutOfTransResourceOwner;
3617 ResourceOwnerDelete(t_thrd.utils_cxt.TopTransactionResourceOwner);3618 ResourceOwnerDelete(t_thrd.utils_cxt.TopTransactionResourceOwner);
3618 s->curTransactionOwner = NULL;3619 s->curTransactionOwner = NULL;
3619 t_thrd.utils_cxt.CurTransactionResourceOwner = NULL;3620 t_thrd.utils_cxt.CurTransactionResourceOwner = NULL;
@@ -4118,7 +4119,7 @@ static void CleanupTransaction(void)
4118 u_sess->xact_cxt.sendSeqSchmaName = NULL;4119 u_sess->xact_cxt.sendSeqSchmaName = NULL;
4119 u_sess->xact_cxt.sendSeqName = NULL;4120 u_sess->xact_cxt.sendSeqName = NULL;
4120 u_sess->xact_cxt.send_result = NULL;4121 u_sess->xact_cxt.send_result = NULL;
4121- t_thrd.utils_cxt.CurrentResourceOwner = NULL; /* and resource owner */4122+ t_thrd.utils_cxt.CurrentResourceOwner = t_thrd.utils_cxt.OutOfTransResourceOwner; /* and resource owner */
4122 if (t_thrd.utils_cxt.TopTransactionResourceOwner)4123 if (t_thrd.utils_cxt.TopTransactionResourceOwner)
4123 ResourceOwnerDelete(t_thrd.utils_cxt.TopTransactionResourceOwner);4124 ResourceOwnerDelete(t_thrd.utils_cxt.TopTransactionResourceOwner);
4124 s->curTransactionOwner = NULL;4125 s->curTransactionOwner = NULL;
@@ -9,7 +9,8 @@ ifneq "$(MAKECMDGOALS)" "clean"
9 endif9 endif
10 endif10 endif
11endif11endif
12-OBJS = ubtree.o ubtinsert.o ubtpage.o ubtsort.o ubtutils.o ubtsearch.o \12+OBJS = ubtree.o ubtinsert.o ubtpage.o ubtsort.o ubtutils.o ubtsearch.o ubtsearchparallel.o \
13 ubtsplitloc.o ubtsplitloc_insertpt.o ubtxlog.o ubtdump.o ubtrecycle.o13 ubtsplitloc.o ubtsplitloc_insertpt.o ubtxlog.o ubtdump.o ubtrecycle.o
14 14 
15include $(top_srcdir)/src/gausskernel/common.mk15include $(top_srcdir)/src/gausskernel/common.mk
16+
@@ -792,7 +792,10 @@ bool UBTreeMarkPageHalfDead(Relation rel, Buffer leafbuf, BTStack stack)
792 792 
793 page = BufferGetPage(leafbuf);793 page = BufferGetPage(leafbuf);
794 opaque = (UBTPageOpaqueInternal)PageGetSpecialPointer(page);794 opaque = (UBTPageOpaqueInternal)PageGetSpecialPointer(page);
795- 795+ if (P_PARALLEL_SCAN_END(opaque) &&
796+ !(TransactionIdPrecedes(((UBTPageOpaque)opaque)->xact, u_sess->utils_cxt.RecentGlobalXmin))) {
797+ return false;
798+ }
796 Assert(!P_RIGHTMOST(opaque) && !P_ISROOT(opaque) && !P_ISDELETED(opaque) && !P_ISHALFDEAD(opaque) &&799 Assert(!P_RIGHTMOST(opaque) && !P_ISROOT(opaque) && !P_ISDELETED(opaque) && !P_ISHALFDEAD(opaque) &&
797 P_ISLEAF(opaque) && P_FIRSTDATAKEY(opaque) > PageGetMaxOffsetNumber(page));800 P_ISLEAF(opaque) && P_FIRSTDATAKEY(opaque) > PageGetMaxOffsetNumber(page));
798 801 
@@ -31,14 +31,10 @@
31#include "gstrace/access_gstrace.h"31#include "gstrace/access_gstrace.h"
32#include "catalog/pg_proc.h"32#include "catalog/pg_proc.h"
33 33 
34-static bool UBTreeReadPage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum);34+static bool UBTreeEndPoint(IndexScanDesc scan, ScanDirection dir);
35static void UBTreeSaveItem(BTScanOpaque so, int itemIndex, OffsetNumber offnum, const IndexTuple itup,35static void UBTreeSaveItem(BTScanOpaque so, int itemIndex, OffsetNumber offnum, const IndexTuple itup,
36 Oid partOid, bool wantItup, bool needRecheck);36 Oid partOid, bool wantItup, bool needRecheck);
37static bool UBTreeStepPage(IndexScanDesc scan, ScanDirection dir);37static bool UBTreeStepPage(IndexScanDesc scan, ScanDirection dir);
38-static bool UBTreeEndPoint(IndexScanDesc scan, ScanDirection dir);
39- 
40-const uint16 INVALID_TUPLE_OFFSET = (uint16)0xa5a5;
41- 
42/*38/*
43 * UBTreeSearch() -- Search the tree for a particular scankey,39 * UBTreeSearch() -- Search the tree for a particular scankey,
44 * or more precisely for the first leaf page it could be on.40 * or more precisely for the first leaf page it could be on.
@@ -60,7 +56,8 @@ const uint16 INVALID_TUPLE_OFFSET = (uint16)0xa5a5;
60 * InvalidBuffer. Also, in BT_WRITE mode, any incomplete splits encountered56 * InvalidBuffer. Also, in BT_WRITE mode, any incomplete splits encountered
61 * during the search will be finished57 * during the search will be finished
62 */58 */
63-BTStack UBTreeSearch(Relation rel, BTScanInsert key, Buffer *bufP, int access, bool needStack)59+BTStack UBTreeSearch(Relation rel, BTScanInsert key, Buffer *bufP, int access, bool needStack,
60+ BlockNumber parallel_end)
64{61{
65 BTStack stack_in = NULL;62 BTStack stack_in = NULL;
66 int pageAccess = BT_READ;63 int pageAccess = BT_READ;
@@ -94,7 +91,7 @@ BTStack UBTreeSearch(Relation rel, BTScanInsert key, Buffer *bufP, int access, b
94 * if the leaf page is split and we insert to the parent page). But91 * if the leaf page is split and we insert to the parent page). But
95 * this is a good opportunity to finish splits of internal pages too.92 * this is a good opportunity to finish splits of internal pages too.
96 */93 */
97- *bufP = UBTreeMoveRight(rel, key, *bufP, (access == BT_WRITE), stack_in, pageAccess);94+ *bufP = UBTreeMoveRight(rel, key, *bufP, (access == BT_WRITE), stack_in, pageAccess, parallel_end);
98 95 
99 /* if this is a leaf page, we're done */96 /* if this is a leaf page, we're done */
100 page = BufferGetPage(*bufP);97 page = BufferGetPage(*bufP);
@@ -202,7 +199,8 @@ BTStack UBTreeSearch(Relation rel, BTScanInsert key, Buffer *bufP, int access, b
202 * 'access'. If we move right, we release the buffer and lock and acquire199 * 'access'. If we move right, we release the buffer and lock and acquire
203 * the same on the right sibling. Return value is the buffer we stop at.200 * the same on the right sibling. Return value is the buffer we stop at.
204 */201 */
205-Buffer UBTreeMoveRight(Relation rel, BTScanInsert itup_key, Buffer buf, bool forupdate, BTStack stack, int access)202+Buffer UBTreeMoveRight(Relation rel, BTScanInsert itup_key, Buffer buf, bool forupdate, BTStack stack, int access,
203+ BlockNumber parallel_end)
206{204{
207 Page page;205 Page page;
208 UBTPageOpaqueInternal opaque;206 UBTPageOpaqueInternal opaque;
@@ -256,10 +254,12 @@ Buffer UBTreeMoveRight(Relation rel, BTScanInsert itup_key, Buffer buf, bool for
256 buf = _bt_getbuf(rel, blkno, access);254 buf = _bt_getbuf(rel, blkno, access);
257 continue;255 continue;
258 }256 }
259- 
260 if (P_IGNORE(opaque) || UBTreeCompare(rel, itup_key, page, P_HIKEY, InvalidBuffer) >= cmpval) {257 if (P_IGNORE(opaque) || UBTreeCompare(rel, itup_key, page, P_HIKEY, InvalidBuffer) >= cmpval) {
261 /* step right one page */258 /* step right one page */
262 buf = _bt_relandgetbuf(rel, buf, opaque->btpo_next, access);259 buf = _bt_relandgetbuf(rel, buf, opaque->btpo_next, access);
260+ if (parallel_end != InvalidBlockNumber && opaque->btpo_next == parallel_end) {
wofanzheng
wofanzhengwofanzheng6月21日

【问题】这里先 _bt_relandgetbuf() 跳到了右兄弟页,再去判断 parallel_end;这样一来当前 worker 已经迈进了下一个 worker 的边界页,前向扫描会出现区间重叠/重复返回,同时 opaque->btpo_next 也是在旧页释放后再访问,存在错误页上下文问题。 【建议】这里要和 btree 的 _bt_moveright() 保持一致:先保存 nextblk,先判断是否等于 parallel_end,确认还能继续后再真正跳右。

likedislike
xiahanzhi
6月22日 评论:
261+ return buf;
262+ }
263 continue;263 continue;
264 } else {264 } else {
265 break;265 break;
@@ -634,6 +634,9 @@ bool UBTreeFirst(IndexScanDesc scan, ScanDirection dir)
634 */634 */
635 if (!so->qual_ok)635 if (!so->qual_ok)
636 return false;636 return false;
637+ if (scan->dop > 1) {
638+ return UBTreeParallelFirst(scan, dir);
639+ }
637 640 
638 /* ----------641 /* ----------
639 * Examine the scan keys to discover where we need to start the scan.642 * Examine the scan keys to discover where we need to start the scan.
@@ -1289,7 +1292,7 @@ static void UBTreeTraceTupleInRange(IndexScanDesc scan, ScanDirection dir, Offse
1289 *1292 *
1290 * Returns true if any matching items found on the page, false if none.1293 * Returns true if any matching items found on the page, false if none.
1291 */1294 */
1292-static bool UBTreeReadPage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)1295+bool UBTreeReadPage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
1293{1296{
1294 BTScanOpaque so = (BTScanOpaque)scan->opaque;1297 BTScanOpaque so = (BTScanOpaque)scan->opaque;
1295 Page page;1298 Page page;
@@ -1828,7 +1831,11 @@ bool UBTreeGetTupleInternal(IndexScanDesc scan, ScanDirection dir)
1828 /*1831 /*
1829 * Now continue the scan.1832 * Now continue the scan.
1830 */1833 */
1831- res = UBTreeNext(scan, dir);1834+ if (scan->dop > 1) {
1835+ res = UBTreeParallelNext(scan, dir);
1836+ } else {
1837+ res = UBTreeNext(scan, dir);
1838+ }
1832 }1839 }
1833 1840 
1834 /* If we have a tuple, return it ... */1841 /* If we have a tuple, return it ... */
@@ -0,0 +1,875 @@
1+/*
2+ * Copyright (c) 2020 Huawei Technologies Co.,Ltd.
3+ * Portions Copyright (c) 2021, openGauss Contributors
4+ *
5+ * openGauss is licensed under Mulan PSL v2.
6+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
7+ * You may obtain a copy of Mulan PSL v2 at:
8+ *
9+ * http://license.coscl.org.cn/MulanPSL2
10+ *
11+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
12+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
13+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
14+ * See the Mulan PSL v2 for more details.
15+ * ---------------------------------------------------------------------------------------
16+ *
17+ * ubtsearchparallel.cpp
18+ *
19+ *
20+ *
21+ * IDENTIFICATION
22+ * src\gausskernel\storage\access\ubtree\ubtsearchparallel.cpp
23+ *
24+ * ---------------------------------------------------------------------------------------
25+ */
26+#include "postgres.h"
27+#include "access/nbtree.h"
28+#include "executor/executor.h"
29+#include "miscadmin.h"
30+#include "storage/predicate.h"
31+#include "distributelayer/streamCore.h"
32+#include "access/parallel_indexscan_core.h"
33+#include "access/ubtree.h"
34+ 
35+bool UBTreeParallelSteppage(IndexScanDesc ubt_scan, ScanDirection dir);
36+ 
37+/*
38+ * @brief UBTreeFindNextBlock
39+ * Find the next block according to the siling pointer (btpo_next/btpo_prev).
40+ * @param ubt_scan IndexScanDesc
41+ * @param dir Scanning direction
42+ * @param current_block Start block number of the current thread.
43+ * @param num_blocks Estimated number of lef nodes scanned by each thread
44+ * @return BlockNumber End block number of the current thread.
45+ */
46+BlockNumber UBTreeFindNextBlock(IndexScanDesc ubt_scan, ScanDirection dir, uint32 current_block, int num_blocks)
47+{
48+ if (current_block == InvalidBlockNumber || current_block == 0) {
49+ return 0;
50+ }
51+ int count = 1;
52+ BlockNumber ubt_next = current_block;
53+ BlockNumber ubt_now = current_block;
54+ Relation ubt_rel = ubt_scan->indexRelation;
55+ Buffer current_buf = _bt_getbuf(ubt_rel, static_cast<BlockNumber>(current_block), BT_READ);
56+ UBTPageOpaqueInternal opaque = (UBTPageOpaqueInternal)PageGetSpecialPointer(BufferGetPage(current_buf));
57+ ubt_now = ubt_next;
58+ if (ScanDirectionIsForward(dir)) {
59+ ubt_next = opaque->btpo_next;
60+ } else {
61+ ubt_next = opaque->btpo_prev;
62+ }
63+ _bt_relbuf(ubt_rel, current_buf);
64+ 
65+ Buffer buf_tmp = InvalidBuffer;
66+ while (count <= num_blocks) {
67+ buf_tmp = _bt_getbuf(ubt_rel, ubt_next, BT_READ);
68+ opaque = (UBTPageOpaqueInternal)PageGetSpecialPointer(BufferGetPage(buf_tmp));
69+ bool empty_page = false;
70+ if (count == num_blocks && !P_RIGHTMOST(opaque) && !P_LEFTMOST(opaque)) {
71+ OffsetNumber max_off = PageGetMaxOffsetNumber(BufferGetPage(buf_tmp));
72+ empty_page = (P_FIRSTKEY > max_off);
73+ }
74+ if (ScanDirectionIsForward(dir)) {
75+ if (P_IGNORE(opaque) || empty_page) {
76+ ubt_now = ubt_next;
77+ ubt_next = opaque->btpo_next;
78+ _bt_relbuf(ubt_rel, buf_tmp);
79+ continue;
80+ }
81+ if (P_RIGHTMOST(opaque)) {
82+ _bt_relbuf(ubt_rel, buf_tmp);
83+ return opaque->btpo_next;
84+ }
85+ ubt_now = ubt_next;
86+ ubt_next = opaque->btpo_next;
87+ } else {
88+ if (P_IGNORE(opaque) || empty_page) {
89+ _bt_relbuf(ubt_rel, buf_tmp);
90+ ubt_now = ubt_next;
91+ ubt_next = opaque->btpo_prev;
92+ continue;
93+ }
94+ if (P_LEFTMOST(opaque)) {
95+ _bt_relbuf(ubt_rel, buf_tmp);
96+ return opaque->btpo_prev;
97+ }
98+ ubt_now = ubt_next;
99+ ubt_next = opaque->btpo_prev;
100+ }
101+ if (count == num_blocks) {
102+ opaque->btpo_flags |= BTP_PARALLEL_SCAN_END;
103+ if (((UBTPageOpaque)opaque)->xact < ubt_scan->xs_snapshot->xmin) {
104+ ((UBTPageOpaque)opaque)->xact = ubt_scan->xs_snapshot->xmin;
105+ }
106+ MarkBufferDirtyHint(buf_tmp, true);
107+ }
108+ _bt_relbuf(ubt_rel, buf_tmp);
109+ count++;
110+ }
111+ return ubt_now;
112+}
113+ 
114+/*
115+ * @brief check_is_need_continue
116+ * In th0 of the parallel index scan, when obtaining the number of real scan pages,
117+ * the ubtree checks () funcation is used to determine whether to scan the next page.
118+ * During this period, debug-related processing such as trace_tuple dose not need to be performed,
119+ * and related logic is deleted to simplify the processs.
120+ * @param scan refer to IndexScanDesc
121+ * @param page curr page
122+ * @param offnum start offset
123+ * @param dir Scanning direction
124+ * @return bool returns true if there is continue scan, false otherwise
125+ */
126+bool check_is_need_continue(IndexScanDesc scan, Page page, OffsetNumber offnum, ScanDirection dir)
127+{
128+ bool continue_scan = true;
129+ bool tupleAlive = false;
130+ Datum res;
131+ ItemId iid = PageGetItemId(page, offnum);
132+ /*
133+ * If the scan specifies not to return killed tuples, then we treat a
134+ * killed tuple as not passing the qual. Most of the time, it's a win to
135+ * not bother examining the tuple's index keys, but just return
136+ * immediately with continuescan = true to proceed to the next tuple.
137+ * However, if this is the last tuple on the page, we should check the
138+ * index keys to prevent uselessly advancing to the next page.
139+ */
140+ if (scan->ignore_killed_tuples && ItemIdIsDead(iid) && (ItemIdHasStorage(iid))) {
141+ /* return immediately if there are more tuples on the page */
142+ if (ScanDirectionIsForward(dir)) {
143+ if (offnum < PageGetMaxOffsetNumber(page)) {
144+ return continue_scan;
145+ }
146+ } else {
147+ UBTPageOpaqueInternal opaque = (UBTPageOpaqueInternal)PageGetSpecialPointer(page);
148+ if (offnum > P_FIRSTDATAKEY(opaque)) {
149+ return continue_scan;
150+ }
151+ }
152+ 
153+ /*
154+ * OK, we want to check the keys so we can set continuescan correctly,
155+ * but we'll return NULL even if the tuple passes the key tests.
156+ */
157+ tupleAlive = false;
158+ } else {
159+ tupleAlive = true;
160+ }
161+ 
162+ IndexTuple tuple = (IndexTuple)PageGetItem(page, iid);
163+ TupleDesc tupdesc = RelationGetDescr(scan->indexRelation);
164+ BTScanOpaque ubt_so = (BTScanOpaque)scan->opaque;
165+ int keysz = ubt_so->numberOfKeys;
166+ int ikey = 0;
167+ ScanKey keydata = ubt_so->keyData;
168+ for (; ikey < keysz; keydata++, ikey++) {
169+ /* row-comparison keys need special processing */
170+ if (keydata->sk_flags & SK_ROW_HEADER) {
171+ if (_bt_check_rowcompare(keydata, tuple, tupdesc, dir, &continue_scan)) {
172+ continue;
173+ }
174+ return continue_scan;
175+ }
176+ bool isNull = false;
177+ Datum datum = index_getattr(tuple, keydata->sk_attno, tupdesc, &isNull);
178+ 
179+ if (keydata->sk_flags & SK_ISNULL) {
180+ /* Handle IS NULL/NOT NULL tests */
181+ if (keydata->sk_flags & SK_SEARCHNULL) {
182+ if (isNull) {
183+ continue; /* tuple satisfies this qual */
184+ }
185+ } else {
186+ Assert(keydata->sk_flags & SK_SEARCHNOTNULL);
187+ if (!isNull) {
188+ continue; /* tuple satisfies this qual */
189+ }
190+ }
191+ 
192+ /*
193+ * Tuple fails this qual. If it's a required qual for the current
194+ * scan direction, then we can conclude no further tuples will
195+ * pass, either.
196+ */
197+ if ((keydata->sk_flags & SK_BT_REQFWD) && ScanDirectionIsForward(dir)) {
198+ continue_scan = false;
199+ } else if ((keydata->sk_flags & SK_BT_REQBKWD) && ScanDirectionIsBackward(dir)) {
200+ continue_scan = false;
201+ }
202+ return continue_scan;
203+ }
204+ 
205+ if (isNull) {
206+ if (keydata->sk_flags & SK_BT_NULLS_FIRST) {
207+ /*
208+ * Since NULLs are sorted before non-NULLs, we know we have
209+ * reached the lower limit of the range of values for this
210+ * index attr. On a backward scan, we can stop if this qual
211+ * is one of the "must match" subset. We can stop regardless
212+ * of whether the qual is > or <, so long as it's required,
213+ * because it's not possible for any future tuples to pass. On
214+ * a forward scan, however, we must keep going, because we may
215+ * have initially positioned to the start of the index.
216+ */
217+ if ((keydata->sk_flags & (SK_BT_REQFWD | SK_BT_REQBKWD)) && ScanDirectionIsBackward(dir)) {
218+ continue_scan = false;
219+ }
220+ } else {
221+ /*
222+ * Since NULLs are sorted after non-NULLs, we know we have
223+ * reached the upper limit of the range of values for this
224+ * index attr. On a forward scan, we can stop if this qual is
225+ * one of the "must match" subset. We can stop regardless of
226+ * whether the qual is > or <, so long as it's required,
227+ * because it's not possible for any future tuples to pass. On
228+ * a backward scan, however, we must keep going, because we
229+ * may have initially positioned to the end of the index.
230+ */
231+ if ((keydata->sk_flags & (SK_BT_REQFWD | SK_BT_REQBKWD)) && ScanDirectionIsForward(dir)) {
232+ continue_scan = false;
233+ }
234+ }
235+ return continue_scan;
236+ }
237+ 
238+ res = FunctionCall2Coll(&keydata->sk_func, keydata->sk_collation, datum, keydata->sk_argument);
239+ if (!DatumGetBool(res)) {
240+ /*
241+ * Tuple fails this qual. If it's a required qual for the current
242+ * scan direction, then we can conclude no further tuples will
243+ * pass, either.
244+ *
245+ * Note: because we stop the scan as soon as any required equality
246+ * qual fails, it is critical that equality quals be used for the
247+ * initial positioning in _bt_first() when they are available. See
248+ * comments in _bt_first().
249+ */
250+ if ((keydata->sk_flags & SK_BT_REQFWD) && ScanDirectionIsForward(dir)) {
251+ continue_scan = false;
252+ } else if ((keydata->sk_flags & SK_BT_REQBKWD) && ScanDirectionIsBackward(dir)) {
253+ continue_scan = false;
254+ }
255+ return continue_scan;
256+ }
257+ }
258+ return continue_scan;
259+}
260+ 
261+/*
262+ * @brief UBTreeParallelGetScanTotalBlocks
263+ * Calculate the number of block that meet the scankey reuqirement.
264+ * @param ubt_scan IndexScanDesc
265+ * @param dir Scanning direction
266+ * @param ubt_start_blk Start scan block number
267+ * @return int Number of all block that meet the conditions
268+ */
269+int UBTreeParallelGetScanTotalBlocks(IndexScanDesc ubt_scan, ScanDirection dir, BlockNumber ubt_start_blk)
270+{
271+ if (ubt_start_blk == InvalidBuffer) {
272+ return 0;
273+ }
274+ Relation ubt_rel = ubt_scan->indexRelation;
275+ BTScanOpaque ubt_so = (BTScanOpaque)ubt_scan->opaque;
276+ int total_blocks = 1;
277+ bool continueloop = true;
278+ BlockNumber ubt_next = ubt_start_blk;
279+ while (true) {
280+ CHECK_FOR_INTERRUPTS();
281+ Buffer tmp_buf = _bt_getbuf(ubt_rel, ubt_next, BT_READ);
282+ UBTPageOpaqueInternal opaque = (UBTPageOpaqueInternal)PageGetSpecialPointer(BufferGetPage(tmp_buf));
283+ if (ScanDirectionIsForward(dir)) {
284+ ubt_next = opaque->btpo_next;
285+ if (P_RIGHTMOST(opaque)) {
286+ _bt_relbuf(ubt_rel, tmp_buf);
287+ break;
288+ }
289+ } else {
290+ ubt_next = opaque->btpo_prev;
291+ if (P_LEFTMOST(opaque)) {
292+ _bt_relbuf(ubt_rel, tmp_buf);
293+ break;
294+ }
295+ }
296+ _bt_relbuf(ubt_rel, tmp_buf);
297+ if (ubt_so->numberOfKeys > 0) {
298+ tmp_buf = _bt_getbuf(ubt_rel, ubt_next, BT_READ);
299+ Page cur_page = BufferGetPage(tmp_buf);
300+ opaque = (UBTPageOpaqueInternal)PageGetSpecialPointer(cur_page);
301+ if (ScanDirectionIsForward(dir)) {
302+ if (P_FIRSTDATAKEY(opaque) > PageGetMaxOffsetNumber(cur_page)) {
303+ _bt_relbuf(ubt_rel, tmp_buf);
304+ total_blocks++;
305+ continue;
306+ }
307+ continueloop = check_is_need_continue(ubt_scan, cur_page, P_FIRSTDATAKEY(opaque), dir);
308+ } else {
309+ continueloop = check_is_need_continue(ubt_scan, cur_page, PageGetMaxOffsetNumber(cur_page), dir);
310+ }
311+ if (!continueloop) {
312+ _bt_relbuf(ubt_rel, tmp_buf);
313+ break;
314+ }
315+ _bt_relbuf(ubt_rel, tmp_buf);
316+ }
317+ total_blocks++;
318+ }
319+ return total_blocks;
320+}
321+ 
322+/*
323+ * @brief UBTreeGetInskeyScankeyWithoutRowheader
324+ * Initialize inskey->scankey when cur_>sk_flags & SK_ROW_HEADER is 0.
325+ * @param cur No. i startKey
326+ * @param ubt_rel relation of the current index
327+ * @param i Number of startkey iterations
328+ * @return void
329+ */
330+void UBTreeGetInskeyScankeyWithoutRowheader(ScanKey cur, Relation ubt_rel, int i, BTScanInsertData* inskey)
331+{
332+ if (cur->sk_subtype == ubt_rel->rd_opcintype[i] || cur->sk_subtype == InvalidOid) {
333+ FmgrInfo* procinfo = index_getprocinfo(ubt_rel, cur->sk_attno, BTORDER_PROC);
334+ ScanKeyEntryInitializeWithInfo(inskey->scankeys + i, cur->sk_flags, cur->sk_attno, InvalidStrategy,
335+ cur->sk_subtype, cur->sk_collation, procinfo, cur->sk_argument);
336+ } else {
337+ RegProcedure cmp_proc =
338+ get_opfamily_proc(ubt_rel->rd_opfamily[i], ubt_rel->rd_opcintype[i], cur->sk_subtype, BTORDER_PROC);
339+ if (SECUREC_UNLIKELY(!RegProcedureIsValid(cmp_proc)))
340+ ereport(ERROR, (errcode(ERRCODE_INDEX_CORRUPTED),
341+ errmsg("missing support function %d(%u,%u) for attribute %d of index \"%s\"", BTORDER_PROC,
342+ ubt_rel->rd_opcintype[i], cur->sk_subtype, cur->sk_attno,
343+ RelationGetRelationName(ubt_rel))));
344+ ScanKeyEntryInitialize(inskey->scankeys + i, cur->sk_flags, cur->sk_attno, InvalidStrategy, cur->sk_subtype,
345+ cur->sk_collation, cmp_proc, cur->sk_argument);
346+ }
347+ return;
348+}
349+ 
350+/*
351+ * @brief UBTReeInitInskey
352+ * Initialize the inskey.
353+ * @param *inskey BTScanInsertData
354+ * @param nextkey proceed to the next step
355+ * @param keys_count number of startkeys
356+ * @return void
357+ */
358+void UBTReeInitInskey(BTScanInsertData* inskey, bool nextkey, int keys_count)
359+{
360+ inskey->heapkeyspace = true;
361+ inskey->anynullkeys = false;
362+ inskey->nextkey = nextkey;
363+ inskey->pivotsearch = false;
364+ inskey->scantid = NULL;
365+ inskey->keysz = keys_count;
366+ return;
367+}
368+ 
369+/*
370+ * @brief UBTreeGetGobackNeedToNext
371+ * If the number of start conditions is 0, the start buffer and offset are returned.
372+ * @param ubt_scan IndexScanDesc
373+ * @param *need_to_go_back need to take a step back
374+ * @param *need_to_next_key proceed to the next step
375+ * @param strat_total different scanning conditions
376+ * @return bool returns true if there is no error, false otherwise
377+ */
378+bool UBTreeGetGobackNeedToNext(IndexScanDesc ubt_scan, ScanDirection dir, bool* ubt_need_to_go_back,
379+ bool* ubt_need_to_next_key, StrategyNumber strat_total)
380+{
381+ switch (strat_total) {
382+ case BTGreaterEqualStrategyNumber:
383+ break;
384+ case BTGreaterStrategyNumber:
385+ *ubt_need_to_next_key = true;
386+ break;
387+ case BTEqualStrategyNumber:
388+ *ubt_need_to_go_back = (ScanDirectionIsBackward(dir)) ? true : false;
389+ *ubt_need_to_next_key = (ScanDirectionIsBackward(dir)) ? true : false;
390+ break;
391+ case BTLessEqualStrategyNumber:
392+ *ubt_need_to_go_back = true;
393+ *ubt_need_to_next_key = true;
394+ break;
395+ case BTLessStrategyNumber:
396+ *ubt_need_to_go_back = true;
397+ break;
398+ default:
399+ ereport(ERROR, (errcode(ERRCODE_INDEX_CORRUPTED),
400+ errmsg("Unrecognized strat_total:%d in index \"%s\".", strat_total,
401+ RelationGetRelationName(ubt_scan->indexRelation))));
402+ return false;
403+ }
404+ return true;
405+}
406+ 
407+/*
408+ * @brief UBTreeGetBeginParallelScanBuf
409+ * Obtains the start scanning buffer.
410+ * @param ubt_scan IndexScanDesc
411+ * @param dir Scanning direction
412+ * @param *offnum start offset
413+ * @return Buffer return the start scanning buffer
414+ */
415+Buffer UBTreeGetBeginParallelScanBuf(IndexScanDesc ubt_scan, ScanDirection dir, OffsetNumber* offnum)
416+{
417+ Relation ubt_rel = ubt_scan->indexRelation;
418+ Buffer buf;
419+ bool res = false;
420+ ScanKey start_keys[INDEX_MAX_KEYS] = {0};
421+ BTScanInsertData inskey;
422+ StrategyNumber strat_total = BTEqualStrategyNumber;
423+ int keys_count = _bt_get_start_keys(ubt_scan, dir, start_keys, strat_total);
424+ if (keys_count == 0) {
425+ return _bt_get_first_buf_without_scankey(ubt_scan, dir, offnum);
426+ }
427+ Assert(keys_count <= INDEX_MAX_KEYS);
428+ for (int i = 0; i < keys_count; i++) {
429+ ScanKey cur = start_keys[i];
430+ Assert(cur->sk_attno == i + 1);
431+ if (cur->sk_flags & SK_ROW_HEADER) {
432+ bool continue_loop = true;
433+ res = _bt_get_inskey_scankey_with_rowheader(cur, &inskey, strat_total, keys_count, i, continue_loop);
434+ if (!res) {
435+ return InvalidBuffer;
436+ }
437+ if (!continue_loop) {
438+ break;
439+ }
440+ } else {
441+ UBTreeGetInskeyScankeyWithoutRowheader(cur, ubt_rel, i, &inskey);
442+ }
443+ }
444+ bool nextkey = false;
445+ bool goback = false;
446+ res = UBTreeGetGobackNeedToNext(ubt_scan, dir, &goback, &nextkey, strat_total);
447+ if (!res) {
448+ return InvalidBuffer;
449+ }
450+ UBTReeInitInskey(&inskey, nextkey, keys_count);
451+ BlockNumber end_block = ubt_scan->btps_end_block;
452+ (void)UBTreeSearch(ubt_rel, &inskey, &buf, BT_READ, false, end_block);
453+ if (!BufferIsValid(buf)) {
454+ PredicateLockRelation(ubt_rel, ubt_scan->xs_snapshot);
455+ return InvalidBuffer;
456+ } else {
457+ PredicateLockPage(ubt_rel, BufferGetBlockNumber(buf), ubt_scan->xs_snapshot);
458+ }
459+ *offnum = UBTreeBinarySearch(ubt_rel, &inskey, buf, false);
460+ if (goback) {
461+ *offnum = OffsetNumberPrev(*offnum);
462+ }
463+ return buf;
464+}
465+ 
466+/*
467+ * @brief UBTreeParallelFirstThread0Proc
468+ * In ther parallel_first func, thread 0 divides the scan blocks and records them to the shared memory,
469+ * and assigns the scan start and end blocks of thread 0.
470+ * @param ubt_scan IndexScanDesc
471+ * @param dir Scanning direction
472+ * @param curr_off_start 2D Offset in Shared Memory
473+ * @param index One-dimensional index of the current index in the shared memory
474+ * @param bt_start_blk Start scan block number of thread 0
475+ * @param stream_nodegroup StreamNodeGroup
476+ * @param offnum Start offset
477+ * @return bool returns true if there is no error, false otherwise
478+ */
479+bool UBTreeParallelFirstThread0Proc(IndexScanDesc ubt_scan, ScanDirection dir, int curr_off_start, int index,
480+ BlockNumber& bt_start_blk, StreamNodeGroup* stream_nodegroup, OffsetNumber* offnum)
481+{
482+ Relation ubt_rel = ubt_scan->indexRelation;
483+ int curr_th0_start = curr_off_start + OFFSET_START_BASE;
484+ int curr_th0_end = curr_off_start + OFFSET_END_BASE;
485+ BTScanOpaque ubt_so = (BTScanOpaque)ubt_scan->opaque;
486+ BlockNumber blkno = InvalidBuffer;
487+ Buffer begin_buf = UBTreeGetBeginParallelScanBuf(ubt_scan, dir, offnum);
488+ if (BufferIsValid(begin_buf)) {
489+ blkno = BufferGetBlockNumber(begin_buf);
490+ _bt_relbuf(ubt_rel, begin_buf);
491+ } else {
492+ ubt_so->currPos.buf = InvalidBuffer;
493+ return false;
494+ }
495+ int real_scan_blocks = UBTreeParallelGetScanTotalBlocks(ubt_scan, dir, blkno);
496+ int num_blocks = (real_scan_blocks + (ubt_scan->dop - 1)) / ubt_scan->dop;
497+ 
498+ pthread_mutex_t* mutex = stream_nodegroup->GetIndexSmpMutex();
499+ pthread_cond_t* cond = stream_nodegroup->GetIndexSmpCond();
500+ ereport(LOG,
501+ (errmsg("ubtree parallel ubt_scan oid %u, dop %d, %d blocks per thread, total blocks %d, real scan blocks "
502+ "%u, plan nodeid %u.",
503+ ubt_rel->rd_id, ubt_scan->dop, num_blocks, RelationGetNumberOfBlocks(ubt_rel), real_scan_blocks,
504+ ubt_scan->plan_nodeid)));
505+ MemoryContext old_mem_context = MemoryContextSwitchTo(stream_nodegroup->m_streamRuntimeContext);
506+ PthreadMutexLock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
507+ stream_nodegroup->parallel_indexscan_map[index][curr_off_start + OFFSET_START_BASE] = blkno;
508+ PthreadMutexUnlock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
509+ for (int i = OFFSET_START_BASE; i < ubt_scan->dop; i++) {
510+ PthreadMutexLock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
511+ uint32 current_block = stream_nodegroup->parallel_indexscan_map[index][curr_off_start + i];
512+ PthreadMutexUnlock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
513+ BlockNumber bt_next = UBTreeFindNextBlock(ubt_scan, dir, current_block, num_blocks);
514+ int next_thread_start_offset = curr_off_start + i + OFFSET_START_BASE;
515+ PthreadMutexLock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
516+ stream_nodegroup->parallel_indexscan_map[index][next_thread_start_offset] =
517+ bt_next == 0 ? InvalidBlockNumber : bt_next;
518+ PthreadMutexUnlock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
519+ }
520+ PthreadMutexLock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
521+ stream_nodegroup->parallel_indexscan_map[index][curr_off_start + ubt_scan->dop + OFFSET_START_BASE] =
522+ InvalidBlockNumber;
523+ stream_nodegroup->parallel_indexscan_map[index][curr_off_start] = ubt_scan->plan_nodeid;
524+ pg_memory_barrier();
525+ bt_start_blk = stream_nodegroup->parallel_indexscan_map[index][curr_th0_start];
526+ ubt_scan->btps_end_block = stream_nodegroup->parallel_indexscan_map[index][curr_th0_end];
527+ pthread_cond_broadcast(cond);
528+ PthreadMutexUnlock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
529+ MemoryContextSwitchTo(old_mem_context);
530+ return true;
531+}
532+ 
533+/*
534+ * @brief UBTreeParallelFirstGetFirstBuffer
535+ * Get start scan block of the current thread.
536+ * @param ubt_scan IndexScanDesc
537+ * @param dir Scanning direction
538+ * @param ubt_start_blk Start scan block number of current thread
539+ * @param offnum Start offset
540+ * @return Buffer Start scan block of the current thread
541+ */
542+Buffer UBTreeParallelFirstGetFirstBuffer(IndexScanDesc ubt_scan, ScanDirection dir, BlockNumber ubt_start_blk,
543+ OffsetNumber* offnum)
544+{
545+ Buffer buf;
546+ Relation ubt_rel = ubt_scan->indexRelation;
547+ BTScanOpaque ubt_so = (BTScanOpaque)ubt_scan->opaque;
548+ int thread_id = (int)(u_sess->stream_cxt.smp_id);
549+ if (ScanDirectionIsForward(dir)) {
550+ if (thread_id == 0) {
551+ buf = UBTreeGetBeginParallelScanBuf(ubt_scan, dir, offnum);
552+ } else {
553+ buf = _bt_getbuf(ubt_rel, ubt_start_blk, BT_READ);
554+ }
555+ } else {
556+ if (thread_id == 0) {
557+ if (ubt_so->numberOfKeys == 0) {
558+ buf = UBTreeGetEndPoint(ubt_rel, 0, true);
559+ } else {
560+ buf = UBTreeGetBeginParallelScanBuf(ubt_scan, dir, offnum);
561+ }
562+ } else {
563+ buf = _bt_getbuf(ubt_rel, ubt_start_blk, BT_READ);
564+ buf = _bt_walk_left(ubt_rel, buf, ubt_scan->btps_end_block);
565+ }
566+ }
567+ ereport(LOG,
568+ (errmsg("ubtree parallel scan oid %u, thread id %u begin with block %u end with %u (4294967295 meas "
569+ "InvalidBlockNumer), plan nodeid %u.",
570+ ubt_rel->rd_id, u_sess->stream_cxt.smp_id, ubt_start_blk, ubt_scan->btps_end_block,
571+ ubt_scan->plan_nodeid)));
572+ if (thread_id > 0) {
573+ if (!BufferIsValid(buf)) {
574+ PredicateLockRelation(ubt_rel, ubt_scan->xs_snapshot);
575+ return InvalidBuffer;
576+ } else {
577+ PredicateLockPage(ubt_rel, BufferGetBlockNumber(buf), ubt_scan->xs_snapshot);
578+ }
579+ }
580+ ereport(DEBUG2, (errmodule(MOD_INDEX),
581+ errmsg("ubtree parallel scan oid %u, thread id %u start with block %u buf %d paln nodeid %u.",
582+ ubt_rel->rd_id, u_sess->stream_cxt.smp_id,
583+ buf == InvalidBuffer ? 0 : BufferGetBlockNumber(buf), buf, ubt_scan->plan_nodeid)));
584+ return buf;
585+}
586+ 
587+/*
588+ * @brief UBTreeScanSetTupleAndGPIOid
589+ * Set heap/index tuple and gpi scan partition oid of this scan
590+ * @param ubt_scan refer to IndexScanDesc
591+ * @return void
592+ */
593+void UBTreeScanSetTupleAndGPIOid(IndexScanDesc ubt_scan)
594+{
595+ BTScanOpaque ubt_so = (BTScanOpaque)ubt_scan->opaque;
596+ BTScanPosItem* curr_item = &ubt_so->currPos.items[ubt_so->currPos.itemIndex];
597+ ubt_scan->xs_ctup.t_self = curr_item->heapTid;
598+ ubt_scan->xs_recheck_itup = false;
599+ if (ubt_scan->xs_want_itup || curr_item->needRecheck) {
600+ /* in this case, curr_tuples and tupleOffset must be valid. */
601+ Assert(ubt_so->currTuples != NULL && curr_item->tupleOffset != INVALID_TUPLE_OFFSET);
602+ ubt_scan->xs_itup = (IndexTuple)(ubt_so->currTuples + curr_item->tupleOffset);
603+ /* if we can't tell whether this tuple is visible with out CID, we must fetch UHeapTuple to recheck. */
604+ ubt_scan->xs_recheck_itup = curr_item->needRecheck;
605+ }
606+ if (ubt_scan->xs_want_ext_oid && GPIScanCheckPartOid(ubt_scan->xs_gpi_scan, curr_item->partitionOid)) {
607+ GPISetCurrPartOid(ubt_scan->xs_gpi_scan, curr_item->partitionOid);
608+ }
609+ if (ubt_scan->xs_want_bucketid && cbi_scan_need_change_bucket(ubt_scan->xs_cbi_scan, curr_item->bucketid)) {
610+ cbi_set_bucketid(ubt_scan->xs_cbi_scan, curr_item->bucketid);
611+ }
612+}
613+ 
614+/*
615+ * @brief UBTreeParallelFirstExecScan
616+ * Perform a scan in the current scan interval
617+ * @param ubt_scan IndexScanDesc
618+ * @param dir Scanning direction
619+ * @param ubt_start_blk Start scan block number of current thread
620+ * @param offnum Start offset
621+ * @return bool returns true if there is no error, false otherwise
622+ */
623+bool UBTreeParallelFirstExecScan(IndexScanDesc ubt_scan, ScanDirection dir, BlockNumber ubt_start_blk,
624+ OffsetNumber offnum)
625+{
626+ BTScanOpaque ubt_so = (BTScanOpaque)ubt_scan->opaque;
627+ int thread_id = (int)(u_sess->stream_cxt.smp_id);
628+ ubt_so->currPos.buf = UBTreeParallelFirstGetFirstBuffer(ubt_scan, dir, ubt_start_blk, &offnum);
629+ if (ubt_so->currPos.buf == InvalidBuffer) {
630+ return false;
631+ }
632+ if (ScanDirectionIsForward(dir) && ubt_scan->btps_end_block == BufferGetBlockNumber(ubt_so->currPos.buf)) {
633+ _bt_relbuf(ubt_scan->indexRelation, ubt_so->currPos.buf);
634+ ubt_so->currPos.buf = InvalidBuffer;
635+ return false;
636+ }
637+ /* init moreRight/modeLeft for scan direction */
638+ ubt_so->currPos.moreRight = (ScanDirectionIsForward(dir)) ? true : false;
639+ ubt_so->currPos.moreLeft = (ScanDirectionIsForward(dir)) ? false : true;
640+ ubt_so->markItemIndex = -1;
641+ ubt_so->numKilled = 0;
642+ if (ubt_so->numberOfKeys == 0 || thread_id != 0) {
643+ UBTPageOpaqueInternal opaque =
644+ (UBTPageOpaqueInternal)PageGetSpecialPointer(BufferGetPage(ubt_so->currPos.buf));
645+ if (ScanDirectionIsBackward(dir)) {
646+ offnum = PageGetMaxOffsetNumber(BufferGetPage(ubt_so->currPos.buf));
647+ } else if (ScanDirectionIsForward(dir)) {
648+ /* There could be dead pages to the left, so not this. */
649+ offnum = P_FIRSTDATAKEY(opaque);
650+ } else {
651+ ereport(ERROR, (errcode(ERRCODE_INDEX_CORRUPTED), errmsg("Invalid scan direction: %d", dir)));
652+ offnum = 0; /* init start anyway */
653+ }
654+ }
655+ if (!UBTreeReadPage(ubt_scan, dir, offnum)) {
656+ if (!UBTreeParallelSteppage(ubt_scan, dir)) {
657+ return false;
658+ }
659+ }
660+ /* unlock the current page, but held the pin */
661+ LockBuffer(ubt_so->currPos.buf, BUFFER_LOCK_UNLOCK);
662+ 
663+ UBTreeScanSetTupleAndGPIOid(ubt_scan);
664+ return true;
665+}
666+ 
667+/*
668+ * @brief UBTreeParallelFirst
669+ * Find the first item in a parallel index scan, and mark the start/end block of this thread
670+ * during parallel index scanning.
671+ * If DOP of current scan is not 1, the index scan should be paralleled.
672+ * In the scanning direction, we need to pay attention to the start block and end block
673+ * of current thread.
674+ * If the end block was marked as InvalidBlockerNumber, the thread would scan until
675+ * the last block is met.
676+ * It should be noticed that, not all threads will actually do the scanning, some of the thread
677+ * may not get any blocks for inappropriate DOP value. For the case, the thread will ust resturn.
678+ * @param ubt_scan IndexScanDesc
679+ * @param dir Scanning direction
680+ * @return bool returns true if there is no error, false otherwise
681+ */
682+bool UBTreeParallelFirst(IndexScanDesc ubt_scan, ScanDirection dir)
683+{
684+ Relation ubt_rel = ubt_scan->indexRelation;
685+ BlockNumber real_blocks = RelationGetNumberOfBlocksInFork(ubt_rel, MAIN_FORKNUM, false);
686+ if (real_blocks <= 1) {
687+ return false;
688+ }
689+ bool res;
690+ OffsetNumber offnum = InvalidOffsetNumber;
691+ uint32 thread_id = u_sess->stream_cxt.smp_id;
692+ int idxval = 0;
693+ int curr_off_start = -1;
694+ int node_interval = _bt_get_node_interval(ubt_scan->dop);
695+ BlockNumber ubt_start_blk = InvalidBlockNumber;
696+ StreamNodeGroup* stream_nodegroup = u_sess->stream_cxt.global_obj;
697+ pthread_mutex_t* mutex = stream_nodegroup->GetIndexSmpMutex();
698+ if (thread_id == 0) {
699+ PthreadMutexLock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
700+ {
701+ idxval = _bt_find_parallel_divd(stream_nodegroup->parallel_indexscan_map, ubt_rel->rd_id,
702+ stream_nodegroup->parallel_indexscan_size);
703+ MemoryContext old_mem_context = MemoryContextSwitchTo(stream_nodegroup->m_streamRuntimeContext);
704+ if (idxval != -1) {
705+ _bt_parallel_reallocat_shared_memory(stream_nodegroup, idxval, curr_off_start, node_interval);
706+ } else {
707+ _bt_parallel_allocat_shared_memory(ubt_rel, stream_nodegroup, idxval, curr_off_start, node_interval);
708+ }
709+ MemoryContextSwitchTo(old_mem_context);
710+ }
711+ PthreadMutexUnlock(t_thrd.utils_cxt.ThreadRootResourceOwner, mutex);
712+ }
713+ if (thread_id == 0) {
714+ res = UBTreeParallelFirstThread0Proc(ubt_scan, dir, curr_off_start, idxval, ubt_start_blk, stream_nodegroup,
715+ &offnum);
716+ } else {
717+ res = _bt_parallel_first_threadn_proc(ubt_scan, dir, curr_off_start, ubt_start_blk, stream_nodegroup);
718+ }
719+ if (res) {
720+ res = UBTreeParallelFirstExecScan(ubt_scan, dir, ubt_start_blk, offnum);
721+ }
722+ return res;
723+}
724+ 
725+/*
726+ * @brief UBTreeParallelNext
727+ * Get the next item on parallel index scan
728+ * call parallel_step_page to get next page.
729+ * @param ubt_scan IndexScanDesc
730+ * @param dir Scanning direction
731+ * @return bool returns true if there is no error, false otherwise.
732+ */
733+bool UBTreeParallelNext(IndexScanDesc ubt_scan, ScanDirection dir)
734+{
735+ BTScanOpaque ubt_so = (BTScanOpaque)ubt_scan->opaque;
736+ if (ScanDirectionIsForward(dir)) {
737+ if (++ubt_so->currPos.itemIndex > ubt_so->currPos.lastItem) {
738+ /* We must acquire lock before, applying _bt_steppage */
739+ Assert(BufferIsValid(ubt_so->currPos.buf));
740+ LockBuffer(ubt_so->currPos.buf, BT_READ);
741+ if (!UBTreeParallelSteppage(ubt_scan, dir)) {
742+ return false;
743+ }
744+ ereport(
745+ DEBUG2,
746+ (errmodule(MOD_INDEX),
747+ errmsg("ubtree forward index parallel scan oid %u, thread id %u deal with block %u paln nodeid %u.",
748+ ubt_scan->indexRelation->rd_id, u_sess->stream_cxt.smp_id,
749+ BufferGetBlockNumber(ubt_so->currPos.buf), ubt_scan->plan_nodeid)));
750+ /* Drop the lock, but not pin, on the new page */
751+ LockBuffer(ubt_so->currPos.buf, BUFFER_LOCK_UNLOCK);
752+ }
753+ } else {
754+ if (--ubt_so->currPos.itemIndex < ubt_so->currPos.firstItem) {
755+ /* We must acquire lock before, applying _bt_steppage */
756+ Assert(BufferIsValid(ubt_so->currPos.buf));
757+ LockBuffer(ubt_so->currPos.buf, BT_READ);
758+ if (!UBTreeParallelSteppage(ubt_scan, dir)) {
759+ return false;
760+ }
761+ ereport(
762+ DEBUG2,
763+ (errmodule(MOD_INDEX),
764+ errmsg(
765+ "ubtree backforward index parallel scan oid %u, thread id %u deal with block %u paln nodeid %u.",
766+ ubt_scan->indexRelation->rd_id, u_sess->stream_cxt.smp_id,
767+ BufferGetBlockNumber(ubt_so->currPos.buf), ubt_scan->plan_nodeid)));
768+ /* Drop the lock, but not pin, on the new page */
769+ LockBuffer(ubt_so->currPos.buf, BUFFER_LOCK_UNLOCK);
770+ }
771+ }
772+ UBTreeScanSetTupleAndGPIOid(ubt_scan);
773+ return true;
774+}
775+ 
776+ 
777+/*
778+ * @brief UBTreeParallelSteppage
779+ * Go to the next page for parallel index scan
780+ * The whole process is just like ordinary step_page, except that we just stop read for current thread if end block
781+ * is met.
782+ * @param ubt_scan IndexScanDesc
783+ * @param dir Scanning direction
784+ * @return bool returns true if there is no error, false otherwise.
785+ */
786+bool UBTreeParallelSteppage(IndexScanDesc ubt_scan, ScanDirection dir)
787+{
788+ BTScanOpaque ubt_so = (BTScanOpaque)ubt_scan->opaque;
789+ UBTPageOpaqueInternal opaque = NULL;
790+ 
791+ /* we must have the buffer pinned and locked */
792+ Assert(BufferIsValid(ubt_so->currPos.buf));
793+ 
794+ /* Before leaving current page, deal with any killed items */
795+ if (ubt_so->numKilled > 0)
796+ _bt_killitems(ubt_scan, true);
797+ 
798+ /*
799+ * Before we modify currPos, make a copy of the page data if there was a
800+ * mark position that needs it.
801+ */
802+ if (ubt_so->markItemIndex >= 0) {
803+ /* bump pin on current buffer for assignment to mark buffer */
804+ IncrBufferRefCount(ubt_so->currPos.buf);
805+ errno_t rc = memcpy_s(&ubt_so->markPos,
806+ offsetof(BTScanPosData, items[1]) + ubt_so->currPos.lastItem * sizeof(BTScanPosItem),
807+ &ubt_so->currPos,
808+ offsetof(BTScanPosData, items[1]) + ubt_so->currPos.lastItem * sizeof(BTScanPosItem));
809+ securec_check(rc, "", "");
810+ if (ubt_so->markTuples) {
811+ rc = memcpy_s(ubt_so->markTuples, (size_t)ubt_so->currPos.nextTupleOffset, ubt_so->currTuples,
812+ (size_t)ubt_so->currPos.nextTupleOffset);
813+ securec_check(rc, "", "");
814+ }
815+ ubt_so->markPos.itemIndex = ubt_so->markItemIndex;
816+ ubt_so->markItemIndex = -1;
817+ }
818+ Relation index_rel = ubt_scan->indexRelation;
819+ if (ScanDirectionIsForward(dir)) {
820+ BlockNumber blkno = ubt_so->currPos.nextPage;
821+ ubt_so->currPos.moreLeft = true;
822+ for (;;) {
823+ /* if we're at end of scan, give up */
824+ if (ubt_scan->btps_end_block != InvalidBlockNumber && blkno == ubt_scan->btps_end_block) {
825+ _bt_relbuf(index_rel, ubt_so->currPos.buf);
826+ ubt_so->currPos.buf = InvalidBuffer;
827+ return false;
828+ }
829+ _bt_relbuf(index_rel, ubt_so->currPos.buf);
830+ ubt_so->currPos.buf = InvalidBuffer;
831+ if (blkno == P_NONE || !ubt_so->currPos.moreRight) {
832+ ereport(DEBUG1, (errmodule(MOD_INDEX),
833+ errmsg("index parallel scan reach and thread id: %d.", u_sess->stream_cxt.smp_id)));
834+ return false;
835+ }
836+ CHECK_FOR_INTERRUPTS();
837+ ubt_so->currPos.buf = _bt_getbuf(index_rel, blkno, BT_READ);
838+ Page page = BufferGetPage(ubt_so->currPos.buf);
839+ opaque = (UBTPageOpaqueInternal)PageGetSpecialPointer(page);
840+ if (!P_IGNORE(opaque)) {
841+ PredicateLockPage(index_rel, blkno, ubt_scan->xs_snapshot);
842+ bool ret = UBTreeReadPage(ubt_scan, dir, P_FIRSTDATAKEY(opaque));
843+ if (ret) {
844+ break;
845+ }
846+ }
847+ blkno = opaque->btpo_next;
848+ }
849+ } else {
850+ ubt_so->currPos.moreRight = true;
851+ for (;;) {
852+ CHECK_FOR_INTERRUPTS();
853+ if (!ubt_so->currPos.moreLeft) {
854+ _bt_relbuf(index_rel, ubt_so->currPos.buf);
855+ ubt_so->currPos.buf = InvalidBuffer;
856+ return false;
857+ }
858+ Buffer temp = ubt_so->currPos.buf;
859+ ubt_so->currPos.buf = _bt_walk_left(index_rel, temp, ubt_scan->btps_end_block);
860+ if (ubt_so->currPos.buf == InvalidBuffer) {
861+ return false;
862+ }
863+ Page page = BufferGetPage(ubt_so->currPos.buf);
864+ opaque = (UBTPageOpaqueInternal)PageGetSpecialPointer(page);
865+ if (!P_IGNORE(opaque)) {
866+ PredicateLockPage(index_rel, BufferGetBlockNumber(ubt_so->currPos.buf), ubt_scan->xs_snapshot);
867+ bool ret = UBTreeReadPage(ubt_scan, dir, PageGetMaxOffsetNumber(page));
868+ if (ret) {
869+ break;
870+ }
871+ }
872+ }
873+ }
874+ return true;
875+}
@@ -9,6 +9,7 @@ ifneq "$(MAKECMDGOALS)" "clean"
9 endif9 endif
10 endif10 endif
11endif11endif
12-OBJS = ubtpcrinsert.o ubtpcrpage.o ubtpcrrecycle.o ubtpcrsearch.o ubtpcrsort.o ubtpcrsplitloc.o ubtpcrundo.o ubtpcrtd.o ubtpcrrollback.o12+OBJS = ubtpcrinsert.o ubtpcrpage.o ubtpcrrecycle.o ubtpcrsearch.o ubtpcrsort.o ubtpcrsplitloc.o ubtpcrundo.o ubtpcrtd.o ubtpcrrollback.o \
13+ ubtpcrsearchparallel.o
13 14 
14include $(top_srcdir)/src/gausskernel/common.mk15include $(top_srcdir)/src/gausskernel/common.mk
@@ -1141,7 +1141,10 @@ bool UBTreePCRMarkPageHalfDead(Relation rel, Buffer leafbuf, BTStack stack)
1141 1141 
1142 page = BufferGetPage(leafbuf);1142 page = BufferGetPage(leafbuf);
1143 opaque = (UBTPCRPageOpaque)PageGetSpecialPointer(page);1143 opaque = (UBTPCRPageOpaque)PageGetSpecialPointer(page);
1144- 1144+ if (P_PARALLEL_SCAN_END(opaque) &&
1145+ !(TransactionIdPrecedes(((UBTPCRPageOpaque)opaque)->xact, u_sess->utils_cxt.RecentGlobalXmin))) {
1146+ return false;
1147+ }
1145 Assert(!P_RIGHTMOST(opaque) && !P_ISROOT(opaque) && !P_ISDELETED(opaque) && !P_ISHALFDEAD(opaque) &&1148 Assert(!P_RIGHTMOST(opaque) && !P_ISROOT(opaque) && !P_ISDELETED(opaque) && !P_ISHALFDEAD(opaque) &&
1146 P_ISLEAF(opaque) && P_FIRSTDATAKEY(opaque) > UBTPCRPageGetMaxOffsetNumber(page));1149 P_ISLEAF(opaque) && P_FIRSTDATAKEY(opaque) > UBTPCRPageGetMaxOffsetNumber(page));
1147 1150 
@@ -45,15 +45,12 @@
45#include "storage/buf/crbuf.h"45#include "storage/buf/crbuf.h"
46#include "storage/checksum_impl.h"46#include "storage/checksum_impl.h"
47 47 
48-static bool UBTreePCRReadPage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum);
49static void UBTreePCRSaveItem(IndexScanDesc scan, int itemIndex, Page page, OffsetNumber offnum,48static void UBTreePCRSaveItem(IndexScanDesc scan, int itemIndex, Page page, OffsetNumber offnum,
50 const IndexTuple itup, Oid partOid);49 const IndexTuple itup, Oid partOid);
51static bool UBTreePCRStepPage(IndexScanDesc scan, ScanDirection dir);50static bool UBTreePCRStepPage(IndexScanDesc scan, ScanDirection dir);
52static bool UBTreePCREndPoint(IndexScanDesc scan, ScanDirection dir);51static bool UBTreePCREndPoint(IndexScanDesc scan, ScanDirection dir);
53static void BuildCRPage(IndexScanDesc scan, Page crPage, Buffer baseBuffer, CommandId *page_cid);52static void BuildCRPage(IndexScanDesc scan, Page crPage, Buffer baseBuffer, CommandId *page_cid);
54 53 
55-const uint16 INVALID_TUPLE_OFFSET = (uint16)0xa5a5;
56- 
57/* thrshold switch scan mode from pbrcr to pcr */54/* thrshold switch scan mode from pbrcr to pcr */
58const int SCAN_MODE_SWITCH_THRESHOLD = 10;55const int SCAN_MODE_SWITCH_THRESHOLD = 10;
59 56 
@@ -583,7 +580,6 @@ bool UBTreePCRFirst(IndexScanDesc scan, ScanDirection dir)
583 Relation rel = scan->indexRelation;580 Relation rel = scan->indexRelation;
584 BTScanOpaque so = (BTScanOpaque)scan->opaque;581 BTScanOpaque so = (BTScanOpaque)scan->opaque;
585 Buffer buf;582 Buffer buf;
586- OffsetNumber offnum;
587 StrategyNumber strat;583 StrategyNumber strat;
588 bool nextkey = false;584 bool nextkey = false;
589 bool goback = false;585 bool goback = false;
@@ -609,7 +605,9 @@ bool UBTreePCRFirst(IndexScanDesc scan, ScanDirection dir)
609 */605 */
610 if (!so->qual_ok)606 if (!so->qual_ok)
611 return false;607 return false;
612- 608+ if (scan->dop > 1) {
609+ return UBTreePCRParallelFirst(scan, dir);
610+ }
613 /* ----------611 /* ----------
614 * Examine the scan keys to discover where we need to start the scan.612 * Examine the scan keys to discover where we need to start the scan.
615 *613 *
@@ -1023,34 +1021,10 @@ bool UBTreePCRFirst(IndexScanDesc scan, ScanDirection dir)
1023 so->numKilled = 0; /* just paranoia */1021 so->numKilled = 0; /* just paranoia */
1024 so->markItemIndex = -1; /* ditto */1022 so->markItemIndex = -1; /* ditto */
1025 1023 
1026- /* position to the precise item on the page */
1027- offnum = UBTreePCRBinarySearch(rel, &inskey, BufferGetPage(buf));
1028- 
1029- /*
1030- * If nextkey = false, we are positioned at the first item >= scan key, or
1031- * possibly at the end of a page on which all the existing items are less
1032- * than the scan key and we know that everything on later pages is greater
1033- * than or equal to scan key.
1034- *
1035- * If nextkey = true, we are positioned at the first item > scan key, or
1036- * possibly at the end of a page on which all the existing items are less
1037- * than or equal to the scan key and we know that everything on later
1038- * pages is greater than scan key.
1039- *
1040- * The actually desired starting point is either this item or the prior
1041- * one, or in the end-of-page case it's the first item on the next page or
1042- * the last item on this page. Adjust the starting offset if needed. (If
1043- * this results in an offset before the first item or after the last one,
1044- * _bt_readpage will report no items found, and then we'll step to the
1045- * next page as needed.)
1046- */
1047- if (goback)
1048- offnum = OffsetNumberPrev(offnum);
1049- 
1050 /*1024 /*
1051 * Now load data from the first page of the scan.1025 * Now load data from the first page of the scan.
1052 */1026 */
1053- if (!UBTreePCRReadPage(scan, dir, offnum)) {1027+ if (!UBTreePCRReadPage(scan, dir, &inskey, goback)) {
1054 /*1028 /*
1055 * There's no actually-matching data on this page. Try to advance to1029 * There's no actually-matching data on this page. Try to advance to
1056 * the next page. Return false if there's no matching data at all.1030 * the next page. Return false if there's no matching data at all.
@@ -1646,7 +1620,7 @@ static bool IsPCRScanModeSupported(Snapshot snapshot)
1646 *1620 *
1647 * Returns true if any matching items found on the page, false if none.1621 * Returns true if any matching items found on the page, false if none.
1648 */1622 */
1649-static bool UBTreePCRReadPage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)1623+bool UBTreePCRReadPage(IndexScanDesc scan, ScanDirection dir, BTScanInsert inskey, bool need_to_go_back)
1650{1624{
1651 BTScanOpaque so = (BTScanOpaque)scan->opaque;1625 BTScanOpaque so = (BTScanOpaque)scan->opaque;
1652 Page page;1626 Page page;
@@ -1701,6 +1675,37 @@ static bool UBTreePCRReadPage(IndexScanDesc scan, ScanDirection dir, OffsetNumbe
1701 1675 
1702 Page localPage = NULL;1676 Page localPage = NULL;
1703 uint32 checkNum = 0;1677 uint32 checkNum = 0;
1678+ OffsetNumber offnum = 0;
1679+ if (inskey == NULL) {
1680+ if (ScanDirectionIsForward(dir)) {
1681+ offnum = P_FIRSTDATAKEY(opaque);
1682+ } else {
1683+ offnum = UBTreePCRPageGetMaxOffsetNumber(page);
1684+ }
1685+ } else {
1686+ offnum = UBTreePCRBinarySearch(scan->indexRelation, inskey, page);
1687+ /*
1688+ * If nextkey = false, we are positioned at the first item >= scan key, or
1689+ * possibly at the end of a page on which all the existing items are less
1690+ * than the scan key and we know that everything on later pages is greater
1691+ * than or equal to scan key.
1692+ *
1693+ * If nextkey = true, we are positioned at the first item > scan key, or
1694+ * possibly at the end of a page on which all the existing items are less
1695+ * than or equal to the scan key and we know that everything on later
1696+ * pages is greater than scan key.
1697+ *
1698+ * The actually desired starting point is either this item or the prior
1699+ * one, or in the end-of-page case it's the first item on the next page or
1700+ * the last item on this page. Adjust the starting offset if needed. (If
1701+ * this results in an offset before the first item or after the last one,
1702+ * _bt_readpage will report no items found, and then we'll step to the
1703+ * next page as needed.)
1704+ */
1705+ if (need_to_go_back) {
1706+ offnum = OffsetNumberPrev(offnum);
1707+ }
1708+ }
1704 OffsetNumber checkVisibleOffs[MaxIndexTuplesPerPage] = {0};1709 OffsetNumber checkVisibleOffs[MaxIndexTuplesPerPage] = {0};
1705 OffsetNumber originOffnum = offnum;1710 OffsetNumber originOffnum = offnum;
1706 BlockNumber blockNum = BufferGetBlockNumber(so->currPos.buf);1711 BlockNumber blockNum = BufferGetBlockNumber(so->currPos.buf);
@@ -2200,8 +2205,9 @@ static bool UBTreePCRStepPage(IndexScanDesc scan, ScanDirection dir)
2200 PredicateLockPage(rel, blkno, scan->xs_snapshot);2205 PredicateLockPage(rel, blkno, scan->xs_snapshot);
2201 /* see if there are any matches on this page */2206 /* see if there are any matches on this page */
2202 /* note that this will clear moreRight if we can stop */2207 /* note that this will clear moreRight if we can stop */
2203- if (UBTreePCRReadPage(scan, dir, P_FIRSTDATAKEY(opaque)))2208+ if (UBTreePCRReadPage(scan, dir)) {
2204 break;2209 break;
2210+ }
2205 }2211 }
2206 /* nope, keep going */2212 /* nope, keep going */
2207 blkno = opaque->btpo_next;2213 blkno = opaque->btpo_next;
@@ -2244,8 +2250,9 @@ static bool UBTreePCRStepPage(IndexScanDesc scan, ScanDirection dir)
2244 PredicateLockPage(rel, BufferGetBlockNumber(so->currPos.buf), scan->xs_snapshot);2250 PredicateLockPage(rel, BufferGetBlockNumber(so->currPos.buf), scan->xs_snapshot);
2245 /* see if there are any matches on this page */2251 /* see if there are any matches on this page */
2246 /* note that this will clear moreLeft if we can stop */2252 /* note that this will clear moreLeft if we can stop */
2247- if (UBTreePCRReadPage(scan, dir, UBTreePCRPageGetMaxOffsetNumber(page)))2253+ if (UBTreePCRReadPage(scan, dir)) {
2248 break;2254 break;
2255+ }
2249 }2256 }
2250 }2257 }
2251 }2258 }
@@ -2344,7 +2351,6 @@ static bool UBTreePCREndPoint(IndexScanDesc scan, ScanDirection dir)
2344 Buffer buf;2351 Buffer buf;
2345 Page page;2352 Page page;
2346 UBTPCRPageOpaque opaque;2353 UBTPCRPageOpaque opaque;
2347- OffsetNumber start;
2348 BTScanPosItem *currItem = NULL;2354 BTScanPosItem *currItem = NULL;
2349 2355 
2350 /*2356 /*
@@ -2368,18 +2374,6 @@ static bool UBTreePCREndPoint(IndexScanDesc scan, ScanDirection dir)
2368 opaque = (UBTPCRPageOpaque)PageGetSpecialPointer(page);2374 opaque = (UBTPCRPageOpaque)PageGetSpecialPointer(page);
2369 Assert(P_ISLEAF(opaque));2375 Assert(P_ISLEAF(opaque));
2370 2376 
2371- if (ScanDirectionIsForward(dir)) {
2372- /* There could be dead pages to the left, so not this: */
2373- start = P_FIRSTDATAKEY(opaque);
2374- } else if (ScanDirectionIsBackward(dir)) {
2375- Assert(P_RIGHTMOST(opaque));
2376- 
2377- start = UBTreePCRPageGetMaxOffsetNumber(page);
2378- } else {
2379- ereport(ERROR, (errcode(ERRCODE_INDEX_CORRUPTED), errmsg("invalid scan direction: %d", (int)dir)));
2380- start = 0; /* keep compiler quiet */
2381- }
2382- 
2383 /* remember which buffer we have pinned */2377 /* remember which buffer we have pinned */
2384 so->currPos.buf = buf;2378 so->currPos.buf = buf;
2385 2379 
@@ -2397,7 +2391,7 @@ static bool UBTreePCREndPoint(IndexScanDesc scan, ScanDirection dir)
2397 /*2391 /*
2398 * Now load data from the first page of the scan.2392 * Now load data from the first page of the scan.
2399 */2393 */
2400- if (!UBTreePCRReadPage(scan, dir, start)) {2394+ if (!UBTreePCRReadPage(scan, dir)) {
2401 /*2395 /*
2402 * There's no actually-matching data on this page. Try to advance to2396 * There's no actually-matching data on this page. Try to advance to
2403 * the next page. Return false if there's no matching data at all.2397 * the next page. Return false if there's no matching data at all.
@@ -2483,7 +2477,11 @@ bool UBTreePCRGetTupleInternal(IndexScanDesc scan, ScanDirection dir)
2483 /*2477 /*
2484 * Now continue the scan.2478 * Now continue the scan.
2485 */2479 */
2486- res = UBTreePCRNext(scan, dir);2480+ if (scan->dop > 1) {
2481+ res = UBTreePCRParallelNext(scan, dir);
2482+ } else {
2483+ res = UBTreePCRNext(scan, dir);
2484+ }
2487 }2485 }
2488 2486 
2489 /* If we have a tuple, return it ... */2487 /* If we have a tuple, return it ... */