| @@ -25,9 +25,9 @@ | |||
| 25 | 25 | ||
| 26 | 26 | ||
| 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 | 31 | ||
| 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 | */ |
| 214 | ResourceOwner ResourceOwnerCreate(ResourceOwner parent, const char* name, MemoryContext memCxt) | 214 | ResourceOwner 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 | + | ||
| 2718 | void ReleaseResownerOutOfTransaction() | 2731 | void 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 | ||
| 2744 | FORCE_INLINE | 2790 | FORCE_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 | 84 | ||
| 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); |
| 87 | static List* build_path_tlist(PlannerInfo* root, Path* path); | 87 | static List* build_path_tlist(PlannerInfo* root, Path* path); |
| 88 | static Plan* create_scan_plan(PlannerInfo* root, Path* best_path); | 88 | static Plan* create_scan_plan(PlannerInfo* root, Path* best_path); |
| 89 | static List* build_relation_tlist(RelOptInfo* rel); | 89 | static List* build_relation_tlist(RelOptInfo* rel); |
| @@ -160,7 +160,7 @@ static Plan* setPartitionParam(PlannerInfo* root, Plan* plan, RelOptInfo* rel); | |||
| 160 | 160 | ||
| 161 | static Plan* setBucketInfoParam(PlannerInfo* root, Plan* plan, RelOptInfo* rel); | 161 | static Plan* setBucketInfoParam(PlannerInfo* root, Plan* plan, RelOptInfo* rel); |
| 162 | 162 | ||
| 163 | -Plan* create_globalpartInterator_plan(PlannerInfo* root, PartIteratorPath* pIterpath); | 163 | +Plan* create_globalpartInterator_plan(PlannerInfo* root, PartIteratorPath* pIterpath, bool* may_change = NULL); |
| 164 | static bool is_ann_partiterator_local_limit_safe(Node* limitCount); | 164 | static bool is_ann_partiterator_local_limit_safe(Node* limitCount); |
| 165 | static bool need_ann_partiterator_local_limit(PlannerInfo* root, PartIteratorPath* pIterpath); | 165 | static 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_recurse | 601 | * 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 | 685 | ||
| 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 | 6903 | ||
| 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 | 6911 | ||
| 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 | 9487 | ||
| 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 | 369 | ||
| 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 | ||
| 368 | StreamNodeGroup::~StreamNodeGroup() | 375 | StreamNodeGroup::~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 to | 1166 | * 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 | 1182 | ||
| 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 node | 577 | * 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 the | 1182 | * 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 checks | 262 | // 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 columns | 614 | * @IN param indexorderby: the ordered qual on the index columns |
| 625 | */ | 615 | */ |
| 626 | void BuildCBtreeIndexScan(CBTreeScanState* btreeIndexScan, ScanState* scanstate, Scan* node, EState* estate, | 616 | void 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 columns | 761 | * @IN param indexorderby: the ordered qual on the index columns |
| 776 | */ | 762 | */ |
| 777 | void BuildCBtreeIndexOnlyScan(CBTreeOnlyScanState* btreeIndexOnlyScan, ScanState* scanstate, Scan* node, EState* estate, | 763 | void 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 | ||
| 548 | IndexScanDesc scan_handler_idx_beginscan(Relation heap_relation, Relation index_relation, Snapshot snapshot, | 548 | IndexScanDesc 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 | */ |
| 324 | IndexScanDesc index_beginscan( | 324 | IndexScanDesc 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 | endif | 10 | endif |
| 11 | endif | 11 | endif |
| 12 | OBJS = nbtcompare.o nbtdedup.o nbtinsert.o nbtpage.o nbtree.o nbtsearch.o \ | 12 | OBJS = nbtcompare.o nbtdedup.o nbtinsert.o nbtpage.o nbtree.o nbtsearch.o \ |
| 13 | - nbtutils.o nbtsort.o nbtxlog.o spq_btbuild.o | 13 | + 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 | ||
| 15 | include $(top_srcdir)/src/gausskernel/common.mk | 16 | include $(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 in | 1337 | * 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 the | 1338 | * 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 | 32 | ||
| 33 | 33 | ||
| 34 | static int32 btree_compare_heap_tid(Relation rel, BTScanInsert itup_key, IndexTuple itup, int num_tuple_attrs); | 34 | static 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); | ||
| 36 | static void _bt_saveitem(BTScanOpaque so, int itemIndex, OffsetNumber offnum, IndexTuple itup, Oid partOid, | 35 | static void _bt_saveitem(BTScanOpaque so, int itemIndex, OffsetNumber offnum, IndexTuple itup, Oid partOid, |
| 37 | int2 bucketid); | 36 | int2 bucketid); |
| 38 | static bool _bt_steppage(IndexScanDesc scan, ScanDirection dir); | 37 | static 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 index | 285 | * 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). But | 320 | * 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 to | 394 | * 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 | */ |
| 434 | Buffer _bt_moveright(Relation rel, BTScanInsert key, Buffer buf, bool forupdate, BTStack stack, | 433 | Buffer _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_NEON | 886 | + if (scan->dop > 1) { |
| 883 | - _bt_init_prefetch_state(so, scan); | 887 | + return _bt_parallel_first(scan, dir); |
| 884 | - | ||
| 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 left | 1902 | * 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 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + | ||
| 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); | ||
| 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)) { | ||
【问题】这里在 153 行重新获取了 bt_next 对应的新 buffer,但后面仍然用上一页的 opaque 去取 P_FIRSTDATAKEY();上一页在 151 行已经 _bt_relbuf(),这会变成对已释放页 special area 的误用,既可能算错总块数,也可能踩到无效内存。 【建议】重新打开 bt_next 页后,立即基于 cur_page/新页 special area 重新取 opaque,后续 P_FIRSTDATAKEY() 和 _bt_checkkeys() 都只使用当前页上下文 ![]() ![]() | |||
| 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 这里的 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; ![]() ![]() | |||
| 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 | + | ||
| 27 | + | ||
| 28 | + | ||
| 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)); | ||
【问题】_bt_get_start_keys() 里新分配的 notnullkeys 没有任何释放路径。这个函数会被 btree/ubtree/ubtreepcr 的并行首扫都走到,遇到 rescan 或一个查询里多次触发并行索引扫描时,会持续往当前 MemoryContext 累积泄漏。 【建议】不要在这里裸 palloc0 一个临时数组后直接返回;可以改成栈上对象,或者在函数尾统一 pfree_ext(notnullkeys) ![]() ![]() | |||
| 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 = ¬nullkeys[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, ¶_chosen, ¶_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 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + | ||
| 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; | ||
【问题】_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 的收口写法,避免再漏分支 ![]() ![]() | |||
| 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 | endif | 9 | endif |
| 10 | endif | 10 | endif |
| 11 | endif | 11 | endif |
| 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.o | 13 | ubtsplitloc.o ubtsplitloc_insertpt.o ubtxlog.o ubtdump.o ubtrecycle.o |
| 14 | 14 | ||
| 15 | include $(top_srcdir)/src/gausskernel/common.mk | 15 | include $(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 | 31 | ||
| 32 | 32 | ||
| 33 | 33 | ||
| 34 | -static bool UBTreeReadPage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum); | 34 | +static bool UBTreeEndPoint(IndexScanDesc scan, ScanDirection dir); |
| 35 | static void UBTreeSaveItem(BTScanOpaque so, int itemIndex, OffsetNumber offnum, const IndexTuple itup, | 35 | static 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); |
| 37 | static bool UBTreeStepPage(IndexScanDesc scan, ScanDirection dir); | 37 | static 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 encountered | 56 | * InvalidBuffer. Also, in BT_WRITE mode, any incomplete splits encountered |
| 61 | * during the search will be finished | 57 | * 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). But | 91 | * 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 acquire | 199 | * '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) { | ||
【问题】这里先 _bt_relandgetbuf() 跳到了右兄弟页,再去判断 parallel_end;这样一来当前 worker 已经迈进了下一个 worker 的边界页,前向扫描会出现区间重叠/重复返回,同时 opaque->btpo_next 也是在旧页释放后再访问,存在错误页上下文问题。 【建议】这里要和 btree 的 _bt_moveright() 保持一致:先保存 nextblk,先判断是否等于 parallel_end,确认还能继续后再真正跳右。 ![]() ![]() | |||
| 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 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + | ||
| 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 | endif | 9 | endif |
| 10 | endif | 10 | endif |
| 11 | endif | 11 | endif |
| 12 | -OBJS = ubtpcrinsert.o ubtpcrpage.o ubtpcrrecycle.o ubtpcrsearch.o ubtpcrsort.o ubtpcrsplitloc.o ubtpcrundo.o ubtpcrtd.o ubtpcrrollback.o | 12 | +OBJS = ubtpcrinsert.o ubtpcrpage.o ubtpcrrecycle.o ubtpcrsearch.o ubtpcrsort.o ubtpcrsplitloc.o ubtpcrundo.o ubtpcrtd.o ubtpcrrollback.o \ |
| 13 | + ubtpcrsearchparallel.o | ||
| 13 | 14 | ||
| 14 | include $(top_srcdir)/src/gausskernel/common.mk | 15 | include $(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 | 45 | ||
| 46 | 46 | ||
| 47 | 47 | ||
| 48 | -static bool UBTreePCRReadPage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum); | ||
| 49 | static void UBTreePCRSaveItem(IndexScanDesc scan, int itemIndex, Page page, OffsetNumber offnum, | 48 | static void UBTreePCRSaveItem(IndexScanDesc scan, int itemIndex, Page page, OffsetNumber offnum, |
| 50 | const IndexTuple itup, Oid partOid); | 49 | const IndexTuple itup, Oid partOid); |
| 51 | static bool UBTreePCRStepPage(IndexScanDesc scan, ScanDirection dir); | 50 | static bool UBTreePCRStepPage(IndexScanDesc scan, ScanDirection dir); |
| 52 | static bool UBTreePCREndPoint(IndexScanDesc scan, ScanDirection dir); | 51 | static bool UBTreePCREndPoint(IndexScanDesc scan, ScanDirection dir); |
| 53 | static void BuildCRPage(IndexScanDesc scan, Page crPage, Buffer baseBuffer, CommandId *page_cid); | 52 | static 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 */ |
| 58 | const int SCAN_MODE_SWITCH_THRESHOLD = 10; | 55 | const 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 to | 1029 | * 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 to | 2396 | * 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 ... */ |


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