2867 lines
90 KiB
C++
2867 lines
90 KiB
C++
/*
|
|
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
|
*
|
|
* openGauss is licensed under Mulan PSL v2.
|
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
* You may obtain a copy of Mulan PSL v2 at:
|
|
*
|
|
* http://license.coscl.org.cn/MulanPSL2
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
* See the Mulan PSL v2 for more details.
|
|
* -------------------------------------------------------------------------
|
|
*
|
|
* nodegroups.cpp
|
|
* Variables and functions used in multiple node group optimizer
|
|
*
|
|
* IDENTIFICATION
|
|
* src/gausskernel/optimizer/util/nodegroups.cpp
|
|
*
|
|
* -------------------------------------------------------------------------
|
|
*/
|
|
#include "optimizer/nodegroups.h"
|
|
|
|
#include "miscadmin.h"
|
|
#include "access/transam.h"
|
|
#include "catalog/index.h"
|
|
#include "catalog/pgxc_group.h"
|
|
#include "catalog/pgxc_node.h"
|
|
#include "nodes/nodeFuncs.h"
|
|
#include "nodes/print.h"
|
|
#include "optimizer/pathnode.h"
|
|
#include "optimizer/planmain.h"
|
|
#include "optimizer/planner.h"
|
|
#include "optimizer/restrictinfo.h"
|
|
#include "optimizer/streamplan.h"
|
|
#include "optimizer/tlist.h"
|
|
#include "optimizer/var.h"
|
|
#include "pgxc/groupmgr.h"
|
|
#include "pgxc/locator.h"
|
|
#include "pgxc/pgxc.h"
|
|
#include "pgxc/pgxcnode.h"
|
|
#include "utils/acl.h"
|
|
#include "utils/lsyscache.h"
|
|
#include "access/heapam.h"
|
|
#include "workload/memctl.h"
|
|
#include "access/hash.h"
|
|
#include "utils/dynahash.h"
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/* Data structure */
|
|
/* ------------------------------------------------------------------------- */
|
|
/* Context to find all base relation range table entry */
|
|
typedef struct FindBaserelRTEContext {
|
|
List* baserel_rte_list;
|
|
} FindBaserelRTEContext;
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/* Variables */
|
|
/* ------------------------------------------------------------------------- */
|
|
/* ------------------------------------------------------------------------- */
|
|
/* Static functions declaration */
|
|
/* ------------------------------------------------------------------------- */
|
|
/* Generic functions */
|
|
static bool get_baserel_rte_list(Node* node, FindBaserelRTEContext* context);
|
|
static List* get_baserel_rte_list_from_query(Query* query);
|
|
|
|
/* Private functions */
|
|
static Bitmapset* ng_node_oid_array_to_id_bms(Oid* members, int nmembers, char node_type);
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/* Generic functions */
|
|
/* ------------------------------------------------------------------------- */
|
|
/*
|
|
* get_baserel_rte_list
|
|
* get base relation range table entry from a node
|
|
*
|
|
* @param (in) node:
|
|
* the source node
|
|
* @param (in) context:
|
|
* the context to find all base relation range table entry
|
|
*
|
|
* @return:
|
|
* context marker of results, true means walker works well
|
|
*/
|
|
static bool get_baserel_rte_list(Node* node, FindBaserelRTEContext* context)
|
|
{
|
|
if (node == NULL) {
|
|
return false;
|
|
}
|
|
|
|
if (IsA(node, Query)) {
|
|
Query* query = (Query*)node;
|
|
List* rte_list = get_baserel_rte_list_from_query((Query*)query);
|
|
context->baserel_rte_list = list_concat_unique(context->baserel_rte_list, rte_list);
|
|
|
|
if (query_tree_walker(query, (bool (*)())get_baserel_rte_list, (void*)context, 0)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return expression_tree_walker(node, (bool (*)())get_baserel_rte_list, (void*)context);
|
|
}
|
|
|
|
/*
|
|
* get_baserel_rte_list_from_query
|
|
* get base relation range table entry from a Query node
|
|
*
|
|
* @param (in) query:
|
|
* the source Query node
|
|
*
|
|
* @return:
|
|
* the list of base relation range table entry
|
|
*/
|
|
static List* get_baserel_rte_list_from_query(Query* query)
|
|
{
|
|
List* baserel_rte_list = NIL;
|
|
|
|
ListCell* lc = NULL;
|
|
foreach (lc, query->rtable) {
|
|
RangeTblEntry* rte = (RangeTblEntry*)lfirst(lc);
|
|
|
|
/* Other parts will be searched by another get_baserel_rte_list */
|
|
if (RTE_RELATION == rte->rtekind) {
|
|
/* Append to result lists if current rte is base relation */
|
|
baserel_rte_list = lappend(baserel_rte_list, rte);
|
|
}
|
|
}
|
|
|
|
return baserel_rte_list;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/* Private functions */
|
|
/* ------------------------------------------------------------------------- */
|
|
/*
|
|
* ng_node_oid_array_to_id_bms
|
|
* convert node oid array to node index bitmap set
|
|
*
|
|
* @param (in) members:
|
|
* the node oid array
|
|
* @param (in) nmembers:
|
|
* the length of "members"
|
|
* @param (in) node_type:
|
|
* the node type, CN or DN
|
|
*
|
|
* @return:
|
|
* the node index bitmap set
|
|
*/
|
|
static Bitmapset* ng_node_oid_array_to_id_bms(Oid* members, int nmembers, char node_type)
|
|
{
|
|
Bitmapset* bms_nodeids = NULL;
|
|
|
|
for (int i = 0; i < nmembers; ++i) {
|
|
/* transform node oid to node id */
|
|
int nodeId = PGXCNodeGetNodeId(members[i], node_type);
|
|
bms_nodeids = bms_add_member(bms_nodeids, nodeId);
|
|
}
|
|
|
|
return bms_nodeids;
|
|
}
|
|
|
|
/*
|
|
* ng_node_oid_array_to_id_bms_skip_null
|
|
* convert node oid array to node index bitmap set
|
|
* node not found in handles means group add new node but handles not updated
|
|
* if installation group's node not found,we skip it
|
|
*
|
|
* @param (in) members:
|
|
* the node oid array
|
|
* @param (in) nmembers:
|
|
* the length of "members"
|
|
* @param (in) node_type:
|
|
* the node type, CN or DN
|
|
*
|
|
* @return:
|
|
* the node index bitmap set
|
|
*/
|
|
static Bitmapset* ng_node_oid_array_to_id_bms_skip_null(Oid* members, int nmembers, char node_type)
|
|
{
|
|
Bitmapset* bms_nodeids = NULL;
|
|
|
|
for (int i = 0; i < nmembers; ++i) {
|
|
/* transform node oid to node id */
|
|
int nodeId = PGXCNodeGetNodeId(members[i], node_type);
|
|
if (nodeId < 0) {
|
|
continue;
|
|
}
|
|
bms_nodeids = bms_add_member(bms_nodeids, nodeId);
|
|
}
|
|
|
|
return bms_nodeids;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/* Public functions */
|
|
/* ------------------------------------------------------------------------- */
|
|
/* ----------
|
|
* General functions
|
|
* ----------
|
|
*/
|
|
/*
|
|
* ng_init_nodegroup_optimizer
|
|
* initializer of node group optimizer to set initial values of:
|
|
* (1) u_sess->opt_cxt.in_redistribution_group_distribution
|
|
* (2) u_sess->opt_cxt.compute_permission_group_distribution
|
|
* (3) u_sess->opt_cxt.query_union_set_group_distribution
|
|
* (4) u_sess->opt_cxt.is_multiple_nodegroup_scenario
|
|
* (5) u_sess->opt_cxt.different_nodegroup_count
|
|
* if it's a multiple node group scenario, we should shut down SMP here
|
|
*
|
|
* @param (in) query:
|
|
* the Query tree
|
|
*
|
|
* @return: void
|
|
*/
|
|
void ng_init_nodegroup_optimizer(Query* query)
|
|
{
|
|
/*
|
|
* Get 'in redistribution' group distribution,
|
|
* it will be NULL if there is no 'in redistribution' group.
|
|
*/
|
|
u_sess->opt_cxt.in_redistribution_group_distribution = NULL;
|
|
u_sess->opt_cxt.in_redistribution_group_distribution = ng_get_in_redistribution_group_distribution();
|
|
|
|
/* Get installation group distribution */
|
|
Distribution* installation_group_distribution = ng_get_installation_group_distribution();
|
|
|
|
/*
|
|
* Init compute permission group distribution
|
|
* As we use u_sess->opt_cxt.compute_permission_group_distribution in CNG_MODE_COSTBASED_OPTIMAL mode only
|
|
* so, in this mode, we set u_sess->opt_cxt.compute_permission_group_distribution,
|
|
* and in other modes, we set u_sess->opt_cxt.compute_permission_group_distribution to NULL
|
|
*/
|
|
ComputingNodeGroupMode cng_mode = ng_get_computing_nodegroup_mode();
|
|
u_sess->opt_cxt.compute_permission_group_distribution = NULL;
|
|
if (CNG_MODE_COSTBASED_OPTIMAL == cng_mode) {
|
|
u_sess->opt_cxt.compute_permission_group_distribution = ng_get_compute_permission_group_distribution();
|
|
}
|
|
|
|
/*
|
|
* Get and set query union group distribution and check wheather base relations are in same group
|
|
* 1. Get base relations' range table entry list
|
|
* 2. Construct query union-set group
|
|
*/
|
|
FindBaserelRTEContext* findBaserelRTEContext = (FindBaserelRTEContext*)palloc0(sizeof(FindBaserelRTEContext));
|
|
(void)get_baserel_rte_list((Node*)query, findBaserelRTEContext);
|
|
bool baserels_in_same_group = true;
|
|
u_sess->opt_cxt.query_union_set_group_distribution =
|
|
ng_get_query_union_set_group_distribution(findBaserelRTEContext->baserel_rte_list, &baserels_in_same_group);
|
|
pfree_ext(findBaserelRTEContext);
|
|
|
|
/* Check wheather it is a multiple node group scenario */
|
|
u_sess->opt_cxt.is_multiple_nodegroup_scenario = true;
|
|
u_sess->opt_cxt.different_nodegroup_count = 2;
|
|
if (baserels_in_same_group) {
|
|
Distribution* default_computing_group_distribution = ng_get_default_computing_group_distribution();
|
|
if (ng_is_same_group(
|
|
u_sess->opt_cxt.query_union_set_group_distribution, default_computing_group_distribution)) {
|
|
u_sess->opt_cxt.is_multiple_nodegroup_scenario = false;
|
|
|
|
if (ng_is_same_group(u_sess->opt_cxt.query_union_set_group_distribution, installation_group_distribution)) {
|
|
u_sess->opt_cxt.different_nodegroup_count--;
|
|
}
|
|
}
|
|
}
|
|
|
|
u_sess->opt_cxt.single_node_distribution = NULL;
|
|
/* Init dn gather's single node distribution */
|
|
if (IS_PGXC_COORDINATOR && u_sess->attr.attr_sql.enable_dngather
|
|
&& bms_num_members(u_sess->opt_cxt.query_union_set_group_distribution->bms_data_nodeids) >= 1
|
|
&& u_sess->opt_cxt.query_dop <= 1) {
|
|
u_sess->opt_cxt.single_node_distribution = ng_get_random_single_dn_distribution(u_sess->opt_cxt.query_union_set_group_distribution);
|
|
if (!ng_is_same_group(u_sess->opt_cxt.query_union_set_group_distribution, u_sess->opt_cxt.single_node_distribution)) {
|
|
u_sess->opt_cxt.is_multiple_nodegroup_scenario = true;
|
|
u_sess->opt_cxt.different_nodegroup_count++;
|
|
}
|
|
}
|
|
|
|
u_sess->opt_cxt.enable_nodegroup_explain = u_sess->opt_cxt.different_nodegroup_count > 1;
|
|
}
|
|
|
|
/*
|
|
* ng_backup_nodegroup_options
|
|
* Backup options used by node group optimizer,
|
|
* as there may be some procedures may call standard_planner recursively.
|
|
* We also need to backup u_sess->opt_cxt.query_dop for SMP here.
|
|
*
|
|
* @params (out) all params:
|
|
* the params to backup options
|
|
*/
|
|
void ng_backup_nodegroup_options(bool* p_is_multiple_nodegroup_scenario,
|
|
int* p_different_nodegroup_count, Distribution** p_in_redistribution_group_distribution,
|
|
Distribution** p_compute_permission_group_distribution, Distribution** p_query_union_set_group_distribution,
|
|
Distribution** p_single_node_distribution)
|
|
{
|
|
*p_is_multiple_nodegroup_scenario = u_sess->opt_cxt.is_multiple_nodegroup_scenario;
|
|
*p_different_nodegroup_count = u_sess->opt_cxt.different_nodegroup_count;
|
|
*p_in_redistribution_group_distribution = u_sess->opt_cxt.in_redistribution_group_distribution;
|
|
*p_compute_permission_group_distribution = u_sess->opt_cxt.compute_permission_group_distribution;
|
|
*p_query_union_set_group_distribution = u_sess->opt_cxt.query_union_set_group_distribution;
|
|
*p_single_node_distribution = u_sess->opt_cxt.single_node_distribution;
|
|
}
|
|
|
|
/*
|
|
* ng_restore_nodegroup_options
|
|
* Restore options used by node group optimizer,
|
|
* as there may be some procedures may call standard_planner recursively.
|
|
* These options was backup by ng_backup_nodegroup_options.
|
|
* We also need to reset u_sess->opt_cxt.query_dop for SMP here.
|
|
*
|
|
* @params (in) all params:
|
|
* the params holds the original options
|
|
*/
|
|
void ng_restore_nodegroup_options(bool p_is_multiple_nodegroup_scenario,
|
|
int p_different_nodegroup_count, Distribution* p_in_redistribution_group_distribution,
|
|
Distribution* p_compute_permission_group_distribution, Distribution* p_query_union_set_group_distribution,
|
|
Distribution* p_single_node_distribution)
|
|
{
|
|
/* for further explain */
|
|
u_sess->opt_cxt.enable_nodegroup_explain = u_sess->opt_cxt.different_nodegroup_count > 1;
|
|
|
|
/* restore nodegroup optimizer options */
|
|
u_sess->opt_cxt.is_multiple_nodegroup_scenario = p_is_multiple_nodegroup_scenario;
|
|
u_sess->opt_cxt.different_nodegroup_count = p_different_nodegroup_count;
|
|
u_sess->opt_cxt.in_redistribution_group_distribution = p_in_redistribution_group_distribution;
|
|
u_sess->opt_cxt.compute_permission_group_distribution = p_compute_permission_group_distribution;
|
|
u_sess->opt_cxt.query_union_set_group_distribution = p_query_union_set_group_distribution;
|
|
u_sess->opt_cxt.single_node_distribution = p_single_node_distribution;
|
|
}
|
|
|
|
/*
|
|
* ng_get_computing_nodegroup_mode
|
|
* get computing node group mode from GUC variables
|
|
*
|
|
* @return:
|
|
* the mode of current computing node group mode
|
|
*/
|
|
ComputingNodeGroupMode ng_get_computing_nodegroup_mode()
|
|
{
|
|
if (OidIsValid(lc_replan_nodegroup)) {
|
|
return CNG_MODE_FORCE;
|
|
} else if (0 == strncasecmp(u_sess->attr.attr_sql.expected_computing_nodegroup,
|
|
CNG_OPTION_OPTIMAL, strlen(CNG_OPTION_OPTIMAL))) {
|
|
if (in_logic_cluster())
|
|
return CNG_MODE_COSTBASED_QUERY;
|
|
else
|
|
return CNG_MODE_COSTBASED_OPTIMAL;
|
|
} else if (0 == strncasecmp(u_sess->attr.attr_sql.expected_computing_nodegroup,
|
|
CNG_OPTION_QUERY, strlen(CNG_OPTION_QUERY))) {
|
|
return CNG_MODE_COSTBASED_QUERY;
|
|
} else {
|
|
if (u_sess->attr.attr_sql.enable_nodegroup_debug && !t_thrd.postmaster_cxt.forceNoSeparate) {
|
|
return CNG_MODE_FORCE;
|
|
} else {
|
|
return CNG_MODE_COSTBASED_EXPECT;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ----------
|
|
* Get general node groups
|
|
* ----------
|
|
*/
|
|
/*
|
|
* ng_get_installation_group_name
|
|
* get group name of installation group
|
|
*
|
|
* @return:
|
|
* the name of installation group
|
|
*/
|
|
char* ng_get_installation_group_name()
|
|
{
|
|
return PgxcGroupGetInstallationGroup();
|
|
}
|
|
|
|
/*
|
|
* ng_get_installation_group_oid
|
|
* get group oid of installation group
|
|
*
|
|
* @return:
|
|
* the oid of installation group
|
|
*/
|
|
Oid ng_get_installation_group_oid()
|
|
{
|
|
char* installation_group_name = ng_get_installation_group_name();
|
|
|
|
Oid oid = ng_get_group_groupoid(installation_group_name);
|
|
return oid;
|
|
}
|
|
|
|
/*
|
|
* ng_get_installation_group_nodeids
|
|
* get node index bitmap set of installation group
|
|
*
|
|
* @return:
|
|
* the node index bitmap set of installation group
|
|
*/
|
|
Bitmapset* ng_get_installation_group_nodeids()
|
|
{
|
|
Oid oid = ng_get_installation_group_oid();
|
|
|
|
Bitmapset* bms_nodeids = ng_get_group_nodeids(oid);
|
|
return bms_nodeids;
|
|
}
|
|
|
|
/*
|
|
* ng_get_installation_group_distribution
|
|
* get Distribution information of installation group
|
|
*
|
|
* @return:
|
|
* the Distribution information of installation group
|
|
*/
|
|
Distribution* ng_get_installation_group_distribution()
|
|
{
|
|
Oid oid = ng_get_installation_group_oid();
|
|
|
|
Distribution* distribution = ng_get_group_distribution(oid);
|
|
return distribution;
|
|
}
|
|
|
|
/*
|
|
* ng_get_installation_group_exec_node
|
|
* get exec nodes of installation group
|
|
*
|
|
* @return:
|
|
* the exec nodes of installation group
|
|
*/
|
|
ExecNodes* ng_get_installation_group_exec_node()
|
|
{
|
|
Distribution* distribution = ng_get_installation_group_distribution();
|
|
ExecNodes* exec_nodes = ng_convert_to_exec_nodes(distribution, LOCATOR_TYPE_REPLICATED, RELATION_ACCESS_READ);
|
|
return exec_nodes;
|
|
}
|
|
|
|
/*
|
|
* ng_get_u_sess->opt_cxt.compute_permission_group_distribution
|
|
* get Distribution information of compute permission group
|
|
*
|
|
* @return:
|
|
* the Distribution information of compute permission group
|
|
*/
|
|
Distribution* ng_get_compute_permission_group_distribution()
|
|
{
|
|
/* Just for initialize of computing permission group */
|
|
if (u_sess->opt_cxt.compute_permission_group_distribution == NULL) {
|
|
Oid user_oid = GetUserId();
|
|
List* group_oid_list = GetNodeGroupOidCompute(user_oid);
|
|
Distribution* distribution = NULL;
|
|
ListCell* lc = NULL;
|
|
foreach (lc, group_oid_list) {
|
|
Oid group_oid = (Oid)lfirst_oid(lc);
|
|
Distribution* d = ng_get_group_distribution(group_oid);
|
|
distribution = ng_get_union_distribution_recycle(distribution, d);
|
|
}
|
|
|
|
if (distribution == NULL || bms_is_empty(distribution->bms_data_nodeids)) {
|
|
if (IS_PGXC_COORDINATOR) {
|
|
/* We should report error here when there are no COMPUTE permission group */
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("No privilage assigned to user %u.", user_oid)));
|
|
} else {
|
|
u_sess->opt_cxt.compute_permission_group_distribution = ng_get_single_node_group_distribution();
|
|
}
|
|
} else {
|
|
u_sess->opt_cxt.compute_permission_group_distribution = distribution;
|
|
}
|
|
}
|
|
|
|
return u_sess->opt_cxt.compute_permission_group_distribution;
|
|
}
|
|
|
|
/*
|
|
* ng_get_u_sess->opt_cxt.query_union_set_group_distribution
|
|
* Get Distribution information of union set group of all base relations in a query.
|
|
* Another function is mark wheather these base relations in a same node group.
|
|
* If all base relations have no distribution information (such as all relations are
|
|
* foreign tables), we set query union set as same as installation group.
|
|
*
|
|
* @param (in) baserel_rte_list:
|
|
* the range table entry list of all base relations
|
|
* @param (out) baserels_in_same_group:
|
|
* mark wheather these base relations in same node group
|
|
*
|
|
* @return:
|
|
* the Distribution information of union set group of all base relations in a query
|
|
*/
|
|
Distribution* ng_get_query_union_set_group_distribution(List* baserel_rte_list, bool* baserels_in_same_group)
|
|
{
|
|
Distribution* union_distribution = NULL;
|
|
*baserels_in_same_group = true;
|
|
|
|
/* Step 1: check base tables' storage group */
|
|
ListCell* lc = NULL;
|
|
foreach (lc, baserel_rte_list) {
|
|
RangeTblEntry* rte = (RangeTblEntry*)lfirst(lc);
|
|
AssertEreport(rte->rtekind == RTE_RELATION, MOD_OPT, "");
|
|
|
|
/* Only check plain relaion */
|
|
if (RELKIND_VIEW == rte->relkind || RELKIND_CONTQUERY == rte->relkind) {
|
|
continue;
|
|
}
|
|
|
|
Distribution* rel_distribution = ng_get_baserel_data_distribution(rte->relid, rte->relkind);
|
|
|
|
if (union_distribution == NULL) {
|
|
union_distribution = rel_distribution;
|
|
} else {
|
|
if (!ng_is_same_group(union_distribution, rel_distribution)) {
|
|
*baserels_in_same_group = false;
|
|
union_distribution = ng_get_union_distribution_recycle(union_distribution, rel_distribution);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* No base table, or all base tables have no distribution information */
|
|
if (union_distribution == NULL || bms_is_empty(union_distribution->bms_data_nodeids)) {
|
|
*baserels_in_same_group = true;
|
|
union_distribution = ng_get_installation_group_distribution();
|
|
}
|
|
|
|
return union_distribution;
|
|
}
|
|
|
|
/*
|
|
* ng_get_u_sess->opt_cxt.query_union_set_group_distribution
|
|
* get Distribution information of query union set node group
|
|
*
|
|
* @return:
|
|
* the Distribution information of query union set node group
|
|
*/
|
|
Distribution* ng_get_query_union_set_group_distribution()
|
|
{
|
|
/* If query union-set group is not exists, shift it to installation group */
|
|
if (u_sess->opt_cxt.query_union_set_group_distribution == NULL) {
|
|
u_sess->opt_cxt.query_union_set_group_distribution = ng_get_installation_group_distribution();
|
|
}
|
|
|
|
return u_sess->opt_cxt.query_union_set_group_distribution;
|
|
}
|
|
|
|
/*
|
|
* ng_get_expected_computing_group_distribution
|
|
* Get Distribution information of expected computing node group.
|
|
* If the 'expected_computing_nodegroup' is not exists, we will use query union-set node group.
|
|
*
|
|
* @return:
|
|
* the Distribution information of expected computing node group
|
|
*/
|
|
Distribution* ng_get_expected_computing_group_distribution()
|
|
{
|
|
ComputingNodeGroupMode cng_mode = ng_get_computing_nodegroup_mode();
|
|
if (CNG_MODE_COSTBASED_OPTIMAL == cng_mode || CNG_MODE_COSTBASED_QUERY == cng_mode) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("No expected computing node group in 'optimal' or 'query' mode")));
|
|
}
|
|
|
|
Distribution* distribution = NULL;
|
|
if (OidIsValid(lc_replan_nodegroup))
|
|
distribution = ng_get_group_distribution(lc_replan_nodegroup);
|
|
else
|
|
distribution = ng_get_group_distribution(u_sess->attr.attr_sql.expected_computing_nodegroup);
|
|
|
|
/*
|
|
* (1) If expected_computing_nodegroup is not exists, shift it to query union-set group
|
|
* (2) If the user have no COMPUTE permission on expected_computing_nodegroup, report error
|
|
*/
|
|
if (InvalidOid == distribution->group_oid) {
|
|
ereport(DEBUG1, (errmodule(MOD_OPT), errmsg("Expected computing node group does not exist.")));
|
|
|
|
distribution = ng_copy_distribution(ng_get_query_union_set_group_distribution());
|
|
}
|
|
|
|
return distribution;
|
|
}
|
|
|
|
/*
|
|
* ng_get_default_computing_group_exec_node
|
|
* get exec nodes of default computing group
|
|
*
|
|
* @return:
|
|
* the exec nodes of default computing group
|
|
*/
|
|
ExecNodes* ng_get_default_computing_group_exec_node()
|
|
{
|
|
Distribution* distribution = ng_get_default_computing_group_distribution();
|
|
ExecNodes* exec_nodes = ng_convert_to_exec_nodes(distribution, LOCATOR_TYPE_REPLICATED, RELATION_ACCESS_READ);
|
|
return exec_nodes;
|
|
}
|
|
|
|
/*
|
|
* ng_get_in_redistribution_group_oid
|
|
* get group oid of in-redistribution group
|
|
*
|
|
* @return:
|
|
* the group oid of in-redistribution group
|
|
*/
|
|
Oid ng_get_in_redistribution_group_oid()
|
|
{
|
|
char* in_redistribution_group_name = PgxcGroupGetInRedistributionGroup();
|
|
if (NULL == in_redistribution_group_name) {
|
|
return InvalidOid;
|
|
} else {
|
|
return ng_get_group_groupoid(in_redistribution_group_name);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ng_get_u_sess->opt_cxt.in_redistribution_group_distribution
|
|
* get Distribution information of in-redistribution group
|
|
*
|
|
* @return:
|
|
* the Distribution information of in-redistribution group
|
|
*/
|
|
Distribution* ng_get_in_redistribution_group_distribution()
|
|
{
|
|
Oid group_oid = ng_get_in_redistribution_group_oid();
|
|
if (InvalidOid == group_oid) {
|
|
return NULL;
|
|
} else {
|
|
Distribution* distribution = NewDistribution();
|
|
distribution->group_oid = group_oid;
|
|
distribution->bms_data_nodeids = ng_get_group_nodeids(group_oid);
|
|
return distribution;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ng_get_redist_dest_group_oid
|
|
* get destination group oid in redistribution
|
|
*
|
|
* @return:
|
|
* the group oid of redistribution distination group
|
|
*/
|
|
Oid ng_get_redist_dest_group_oid()
|
|
{
|
|
if (in_logic_cluster()) {
|
|
return PgxcGroupGetRedistDestGroupOid();
|
|
} else {
|
|
return ng_get_installation_group_oid();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ng_get_default_computing_group_distribution
|
|
* get default node group's Distribution information of corresponding mode
|
|
*
|
|
* @return:
|
|
* the default node group's Distribution information of corresponding mode
|
|
*/
|
|
Distribution* ng_get_default_computing_group_distribution()
|
|
{
|
|
ComputingNodeGroupMode cng_mode = ng_get_computing_nodegroup_mode();
|
|
|
|
switch (cng_mode) {
|
|
case CNG_MODE_COSTBASED_OPTIMAL:
|
|
return ng_get_compute_permission_group_distribution();
|
|
case CNG_MODE_COSTBASED_QUERY:
|
|
return ng_get_query_union_set_group_distribution();
|
|
case CNG_MODE_COSTBASED_EXPECT:
|
|
case CNG_MODE_FORCE:
|
|
return ng_get_expected_computing_group_distribution();
|
|
default:
|
|
break;
|
|
}
|
|
|
|
AssertEreport(false, MOD_OPT, "get default computing group distribution failed");
|
|
return NULL;
|
|
}
|
|
|
|
Distribution* ng_get_single_node_distribution() {
|
|
return u_sess->opt_cxt.single_node_distribution;
|
|
}
|
|
|
|
/*
|
|
* ng_get_correlated_subplan_group_distribution
|
|
* Get Distribution information for a correlated sub-plan.
|
|
* In each computing mode, this group will be different, see code for detail.
|
|
*
|
|
* @return:
|
|
* the Distribution information for a correlated sub-plan
|
|
*/
|
|
Distribution* ng_get_correlated_subplan_group_distribution()
|
|
{
|
|
ComputingNodeGroupMode cng_mode = ng_get_computing_nodegroup_mode();
|
|
|
|
switch (cng_mode) {
|
|
case CNG_MODE_COSTBASED_OPTIMAL: {
|
|
Distribution* distribution_1 = ng_get_compute_permission_group_distribution();
|
|
Distribution* distribution_2 = ng_get_query_union_set_group_distribution();
|
|
return ng_get_union_distribution(distribution_1, distribution_2);
|
|
}
|
|
case CNG_MODE_COSTBASED_QUERY: {
|
|
return ng_get_query_union_set_group_distribution();
|
|
}
|
|
case CNG_MODE_COSTBASED_EXPECT:
|
|
case CNG_MODE_FORCE: {
|
|
Distribution* distribution_1 = ng_copy_distribution(ng_get_query_union_set_group_distribution());
|
|
Distribution* distribution_2 = ng_get_expected_computing_group_distribution();
|
|
return ng_get_union_distribution_recycle(distribution_1, distribution_2);
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
AssertEreport(false, MOD_OPT, "get correlated subplan group distribution failed");
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* ng_get_max_computable_group_distribution
|
|
* Get Distribution information for a "exec on everywhere" node.
|
|
*
|
|
* @return:
|
|
* the Distribution information for a "exec on everywhere" node
|
|
*/
|
|
Distribution* ng_get_max_computable_group_distribution()
|
|
{
|
|
return ng_get_correlated_subplan_group_distribution();
|
|
}
|
|
|
|
/* ----------
|
|
* Get distribution information of a node group
|
|
* ----------
|
|
*/
|
|
/*
|
|
* ng_get_group_groupoid
|
|
* get group oid from group name
|
|
*
|
|
* @param (in) group_name:
|
|
* the name of group
|
|
*
|
|
* @return:
|
|
* the oid of group
|
|
*/
|
|
Oid ng_get_group_groupoid(const char* group_name)
|
|
{
|
|
if (group_name == NULL || IS_PGXC_DATANODE) {
|
|
return InvalidOid;
|
|
} else {
|
|
return get_pgxc_groupoid(group_name);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ng_get_group_group_name
|
|
* get group name from group oid
|
|
*
|
|
* @param (in) group_oid:
|
|
* the oid of group
|
|
*
|
|
* @return:
|
|
* the name of group
|
|
*/
|
|
char* ng_get_group_group_name(Oid group_oid)
|
|
{
|
|
if (InvalidOid == group_oid) {
|
|
return "GenGroup";
|
|
} else {
|
|
return get_pgxc_groupname(group_oid);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ng_get_dist_group_name
|
|
* get group name from distribution
|
|
|
|
*/
|
|
char* ng_get_dist_group_name(Distribution *distribution)
|
|
{
|
|
if (IS_PGXC_COORDINATOR && u_sess->attr.attr_sql.enable_dngather
|
|
&& u_sess->opt_cxt.is_dngather_support && distribution->group_oid == InvalidOid) {
|
|
char *sep = "";
|
|
StringInfo node_names = makeStringInfo();
|
|
NameData nodename = {{0}};
|
|
int node_no = -1;
|
|
while ((node_no = bms_next_member(distribution->bms_data_nodeids, node_no)) >= 0) {
|
|
appendStringInfo(node_names,
|
|
"%s%s",
|
|
sep,
|
|
get_pgxc_nodename(PGXCNodeGetNodeOid(node_no, PGXC_NODE_DATANODE), &nodename));
|
|
sep = ", ";
|
|
}
|
|
|
|
return node_names->data;
|
|
}
|
|
|
|
return ng_get_group_group_name(distribution->group_oid);
|
|
}
|
|
|
|
|
|
/*
|
|
* ng_get_group_nodeids
|
|
* Get node index bitmap set from group oid
|
|
* If the group oid is invalid, return all nodes
|
|
*
|
|
* @param (in) groupoid:
|
|
* the oid of group
|
|
*
|
|
* @return:
|
|
* the node index bitmap set
|
|
*/
|
|
Bitmapset* ng_get_group_nodeids(const Oid groupoid)
|
|
{
|
|
Bitmapset* bms_nodeids = NULL;
|
|
|
|
if (InvalidOid == groupoid) {
|
|
List* nodeid_list = GetAllDataNodes();
|
|
return ng_convert_to_nodeids(nodeid_list);
|
|
}
|
|
|
|
bms_nodeids = ngroup_info_hash_search(groupoid);
|
|
if (!bms_nodeids) {
|
|
/* First check if we already have default nodegroup set */
|
|
Oid* members = NULL;
|
|
int nmembers = get_pgxc_groupmembers(groupoid, &members);
|
|
|
|
/* in logic cluster case, elastic_nodegroup can contain no members */
|
|
if (nmembers == 0 && in_logic_cluster() && groupoid == ng_get_group_groupoid(VNG_OPTION_ELASTIC_GROUP))
|
|
return NULL;
|
|
|
|
Assert(nmembers > 0);
|
|
|
|
/*
|
|
* Creating a bitmap from array.
|
|
* Notice : installation group's group_members in pgxc_group may be not match with u_sess->pgxc_cxt.dn_handles
|
|
* when node changed in online expansion, so we skip it here and this currently looks ok.
|
|
*
|
|
* Note: we only have to do the special processing for installation node group, because in
|
|
* cluster expansion stage(adding node), only installation group's node will change.
|
|
*/
|
|
Oid ng_installation_group_oid = ng_get_installation_group_oid();
|
|
if (groupoid == ng_installation_group_oid) {
|
|
bms_nodeids = ng_node_oid_array_to_id_bms_skip_null(members, nmembers, PGXC_NODE_DATANODE);
|
|
} else {
|
|
const char *group_parent = get_pgxc_groupparent(groupoid);
|
|
Oid group_parent_oid = InvalidOid;
|
|
if (group_parent != NULL) {
|
|
group_parent_oid = get_pgxc_groupoid(group_parent, false);
|
|
}
|
|
if (group_parent_oid != InvalidOid && group_parent_oid == ng_installation_group_oid) {
|
|
bms_nodeids = ng_node_oid_array_to_id_bms_skip_null(members, nmembers, PGXC_NODE_DATANODE);
|
|
} else {
|
|
bms_nodeids = ng_node_oid_array_to_id_bms(members, nmembers, PGXC_NODE_DATANODE);
|
|
}
|
|
}
|
|
pfree_ext(members);
|
|
|
|
ngroup_info_hash_insert(groupoid, bms_nodeids);
|
|
}
|
|
|
|
return bms_nodeids;
|
|
}
|
|
|
|
/*
|
|
* ng_get_group_distribution
|
|
* get Distribution information from group oid
|
|
*
|
|
* @param (in) groupoid:
|
|
* the oid of group
|
|
*
|
|
* @return:
|
|
* the Distribute information of a group
|
|
*/
|
|
Distribution* ng_get_group_distribution(const Oid groupoid)
|
|
{
|
|
Distribution* distribution = NewDistribution();
|
|
distribution->group_oid = groupoid;
|
|
distribution->bms_data_nodeids = ng_get_group_nodeids(groupoid);
|
|
|
|
/* special case for elastic_group */
|
|
if (distribution->bms_data_nodeids == NULL) {
|
|
distribution->group_oid = InvalidOid;
|
|
distribution->bms_data_nodeids = ng_get_group_nodeids(InvalidOid);
|
|
}
|
|
|
|
return distribution;
|
|
}
|
|
|
|
/*
|
|
* ng_get_group_distribution
|
|
* get Distribution information from group name
|
|
*
|
|
* @param (in) group_name:
|
|
* the name of group
|
|
*
|
|
* @return:
|
|
* the Distribute information of a group
|
|
*/
|
|
Distribution* ng_get_group_distribution(const char* group_name)
|
|
{
|
|
Oid group_oid = ng_get_group_groupoid(group_name);
|
|
Distribution* distribution = ng_get_group_distribution(group_oid);
|
|
return distribution;
|
|
}
|
|
|
|
/* ----------
|
|
* Whether the table is distributed foreign table in logic cluster.
|
|
* ----------
|
|
*/
|
|
/*
|
|
* need_get_installation_group
|
|
* Whether the table need to get installation group.
|
|
*
|
|
* @param (in) tableoid:
|
|
* the oid of the relation
|
|
* @param (in) relkind:
|
|
* the relation kind of the relation
|
|
*
|
|
* @return:
|
|
* Whether the table need to get installation group.
|
|
*/
|
|
static bool need_get_installation_group(Oid tableoid, char relkind)
|
|
{
|
|
/* System table will use installation group */
|
|
if (is_sys_table(tableoid))
|
|
return true;
|
|
|
|
/* Normal tables will not use installation group */
|
|
if (relkind == RELKIND_RELATION || relkind == RELKIND_INDEX || relkind == RELKIND_MATVIEW) {
|
|
return false;
|
|
}
|
|
|
|
if (in_logic_cluster()) {
|
|
/* Distributed foreign tables(in pgxc_class) will not use installation group in logic cluster.*/
|
|
if ((RELKIND_FOREIGN_TABLE == relkind || RELKIND_STREAM == relkind) && is_pgxc_class_table(tableoid))
|
|
return false;
|
|
}
|
|
|
|
/* sequence table or foreign table in non logic cluster will use installation group */
|
|
return true;
|
|
}
|
|
|
|
/* ----------
|
|
* Get distribution information of base relation
|
|
* ----------
|
|
*/
|
|
/*
|
|
* ng_get_baserel_groupoid
|
|
* get group oid of a base relation
|
|
*
|
|
* @param (in) tableoid:
|
|
* the oid of the relation
|
|
* @param (in) relkind:
|
|
* the relation kind of the relation
|
|
*
|
|
* @return:
|
|
* the group oid where the relation lacated
|
|
*/
|
|
Oid ng_get_baserel_groupoid(Oid tableoid, char relkind)
|
|
{
|
|
/* Fast query shipping to dn */
|
|
if (IS_PGXC_DATANODE) {
|
|
return InvalidOid;
|
|
}
|
|
|
|
if (need_get_installation_group(tableoid, relkind)) {
|
|
return ng_get_installation_group_oid();
|
|
}
|
|
|
|
/* Get the base relation oid of the index */
|
|
if (relkind == RELKIND_INDEX) {
|
|
tableoid = IndexGetRelation(tableoid, false);
|
|
}
|
|
|
|
Oid group_oid = get_pgxc_class_groupoid(tableoid);
|
|
|
|
return group_oid;
|
|
}
|
|
|
|
/*
|
|
* ng_get_baserel_data_nodeids
|
|
* get node index bitmap set of a base relation
|
|
*
|
|
* @param (in) tableoid:
|
|
* the oid of the relation
|
|
* @param (in) relkind:
|
|
* the relation kind of the relation
|
|
*
|
|
* @return:
|
|
* the node index bitmap set where the base relation located
|
|
*/
|
|
Bitmapset* ng_get_baserel_data_nodeids(Oid tableoid, char relkind)
|
|
{
|
|
Bitmapset* bms_nodeids = NULL;
|
|
|
|
/* Fast query shipping to dn */
|
|
if (IS_PGXC_DATANODE) {
|
|
int nodeid = u_sess->pgxc_cxt.PGXCNodeId;
|
|
if (nodeid >= 0) {
|
|
bms_nodeids = bms_add_member(bms_nodeids, nodeid);
|
|
} else {
|
|
bms_nodeids = NULL;
|
|
}
|
|
return bms_nodeids;
|
|
}
|
|
|
|
if (need_get_installation_group(tableoid, relkind)) {
|
|
return ng_get_installation_group_nodeids();
|
|
}
|
|
|
|
/* Get the base relation oid of the index */
|
|
if (relkind == RELKIND_INDEX) {
|
|
tableoid = IndexGetRelation(tableoid, false);
|
|
}
|
|
|
|
/* Get oid array of data nodes */
|
|
Oid* members = NULL;
|
|
int nmembers = get_pgxc_classnodes(tableoid, &members);
|
|
AssertEreport(nmembers > 0, MOD_OPT, "");
|
|
|
|
/* Creating a index bitmap from array */
|
|
bms_nodeids = ng_node_oid_array_to_id_bms(members, nmembers, PGXC_NODE_DATANODE);
|
|
|
|
return bms_nodeids;
|
|
}
|
|
|
|
/*
|
|
* ng_get_baserel_data_distribution
|
|
* get Distribution information of a base relation
|
|
*
|
|
* @param (in) tableoid:
|
|
* the oid of the relation
|
|
* @param (in) relkind:
|
|
* the relation kind of the relation
|
|
*
|
|
* @return:
|
|
* the Distribution information where the base relation located
|
|
*/
|
|
Distribution* ng_get_baserel_data_distribution(Oid tableoid, char relkind)
|
|
{
|
|
Distribution* distribution = NewDistribution();
|
|
distribution->group_oid = ng_get_baserel_groupoid(tableoid, relkind);
|
|
distribution->bms_data_nodeids = ng_get_baserel_data_nodeids(tableoid, relkind);
|
|
return distribution;
|
|
}
|
|
|
|
/*
|
|
* ng_get_baserel_num_data_nodes
|
|
* get number of data nodes of a base relation
|
|
*
|
|
* @param (in) tableoid:
|
|
* the oid of the relation
|
|
* @param (in) relkind:
|
|
* the relation kind of the relation
|
|
*
|
|
* @return:
|
|
* the number of data nodes where the base relation located
|
|
*/
|
|
unsigned int ng_get_baserel_num_data_nodes(Oid tableoid, char relkind)
|
|
{
|
|
Bitmapset* bms_nodeids = ng_get_baserel_data_nodeids(tableoid, relkind);
|
|
unsigned int num_datanodes = bms_num_members(bms_nodeids);
|
|
num_datanodes = 0 == num_datanodes ? 1 : num_datanodes;
|
|
return num_datanodes;
|
|
}
|
|
|
|
/* ----------
|
|
* Get distribution information of path and plan
|
|
* ----------
|
|
*/
|
|
/*
|
|
* ng_get_dest_nodeids
|
|
* get data node index bitmap set of a Path
|
|
*
|
|
* @param (in) path:
|
|
* the source path
|
|
*
|
|
* @return:
|
|
* the data node index bitmap set of the Path
|
|
*/
|
|
Bitmapset* ng_get_dest_nodeids(Path* path)
|
|
{
|
|
AssertEreport(path != NULL, MOD_OPT, "");
|
|
if (IsA(path, StreamPath)) {
|
|
StreamPath* stream_path = (StreamPath*)path;
|
|
return stream_path->consumer_distribution.bms_data_nodeids;
|
|
} else {
|
|
return path->distribution.bms_data_nodeids;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ng_get_dest_distribution
|
|
* get Distribution information of a Path node
|
|
*
|
|
* @param (in) path:
|
|
* the target Path node
|
|
*
|
|
* @return:
|
|
* the Distribution information of the Path node
|
|
*/
|
|
Distribution* ng_get_dest_distribution(Path* path)
|
|
{
|
|
AssertEreport(path != NULL, MOD_OPT, "");
|
|
|
|
if (IsA(path, StreamPath)) {
|
|
StreamPath* stream_path = (StreamPath*)path;
|
|
return &(stream_path->consumer_distribution);
|
|
} else {
|
|
return &(path->distribution);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ng_get_dest_nodeids
|
|
* get data node index bitmap set of a Plan node
|
|
*
|
|
* @param (in) plan:
|
|
* the target Plan node
|
|
*
|
|
* @return:
|
|
* the data node index bitmap set of a Plan node
|
|
*/
|
|
Bitmapset* ng_get_dest_nodeids(Plan* plan)
|
|
{
|
|
AssertEreport(plan != NULL, MOD_OPT, "");
|
|
|
|
if (plan->exec_type == EXEC_ON_DATANODES || plan->exec_type == EXEC_ON_ALL_NODES) {
|
|
ExecNodes* en = ng_get_dest_execnodes(plan);
|
|
return ng_convert_to_nodeids(en);
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ng_get_dest_distribution
|
|
* get Distribution information of a Plan node
|
|
*
|
|
* @param (in) plan:
|
|
* the target Plan node
|
|
*
|
|
* @return:
|
|
* the Distribution information of a Plan node
|
|
*/
|
|
Distribution* ng_get_dest_distribution(Plan* plan)
|
|
{
|
|
if (plan->exec_type == EXEC_ON_DATANODES || plan->exec_type == EXEC_ON_ALL_NODES) {
|
|
ExecNodes* en = ng_get_dest_execnodes(plan);
|
|
return &en->distribution;
|
|
} else {
|
|
Distribution* distribution = NewDistribution();
|
|
distribution->group_oid = InvalidOid;
|
|
distribution->bms_data_nodeids = NULL;
|
|
return distribution;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ng_get_dest_execnodes
|
|
* get the destination exec nodes of a Plan,
|
|
* mainly get the consumer side exec nodes for Stream or RemoteQuery
|
|
*
|
|
* @param (in) plan:
|
|
* the target Plan node
|
|
*
|
|
* @return:
|
|
* the destination exec nodes of a Plan
|
|
*/
|
|
ExecNodes* ng_get_dest_execnodes(Plan* plan)
|
|
{
|
|
if (IsA(plan, Stream) || IsA(plan, VecStream)) {
|
|
Stream* stream_plan = (Stream*)plan;
|
|
return stream_plan->consumer_nodes;
|
|
} else if (IsA(plan, RemoteQuery)) {
|
|
RemoteQuery* remote_query_plan = (RemoteQuery*)plan;
|
|
if (NULL != remote_query_plan->exec_nodes) {
|
|
return remote_query_plan->exec_nodes;
|
|
} else {
|
|
return plan->exec_nodes;
|
|
}
|
|
} else {
|
|
return plan->exec_nodes;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ng_get_dest_num_data_nodes
|
|
* get the number of data nodes of a Path node
|
|
*
|
|
* @param (in) path:
|
|
* the target Path node
|
|
*
|
|
* @return:
|
|
* the number of data nodes of a Path node
|
|
*/
|
|
unsigned int ng_get_dest_num_data_nodes(Path* path)
|
|
{
|
|
AssertEreport(path != NULL, MOD_OPT, "");
|
|
|
|
#ifndef ENABLE_MULTIPLE_NODES
|
|
return 1;
|
|
#endif
|
|
|
|
if (!IS_STREAM_PLAN) {
|
|
return 1;
|
|
}
|
|
|
|
if (ng_is_all_in_installation_nodegroup_scenario()) {
|
|
switch (path->pathtype) {
|
|
case T_CStoreScan:
|
|
#ifdef ENABLE_MULTIPLE_NODES
|
|
case T_TsStoreScan:
|
|
#endif /* ENABLE_MULTIPLE_NODES */
|
|
return u_sess->pgxc_cxt.NumDataNodes;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
Bitmapset* bms_nodeids = ng_get_dest_nodeids(path);
|
|
unsigned int num_data_nodes = (unsigned int)bms_num_members(bms_nodeids);
|
|
if (num_data_nodes == 0) {
|
|
elog(DEBUG1, "[ng_get_dest_num_data_nodes] num of data nodes is 0");
|
|
num_data_nodes = 1;
|
|
}
|
|
return num_data_nodes;
|
|
}
|
|
|
|
/*
|
|
* ng_get_dest_num_data_nodes
|
|
* get the number of data nodes of a Plan node
|
|
*
|
|
* @param (in) plan:
|
|
* the target Plan node
|
|
*
|
|
* @return:
|
|
* the number of data nodes of a Plan node
|
|
*/
|
|
unsigned int ng_get_dest_num_data_nodes(Plan* plan)
|
|
{
|
|
AssertEreport(plan != NULL, MOD_OPT, "");
|
|
|
|
#ifndef ENABLE_MULTIPLE_NODES
|
|
return 1;
|
|
#endif
|
|
|
|
if (!IS_STREAM_PLAN) {
|
|
return 1;
|
|
}
|
|
|
|
if (ng_is_all_in_installation_nodegroup_scenario() && !ng_enable_nodegroup_explain()) {
|
|
switch (plan->type) {
|
|
case T_SeqScan:
|
|
case T_CStoreScan:
|
|
#ifdef ENABLE_MULTIPLE_NODES
|
|
case T_TsStoreScan:
|
|
#endif /* ENABLE_MULTIPLE_NODES */
|
|
case T_IndexScan:
|
|
case T_IndexOnlyScan:
|
|
case T_CStoreIndexScan:
|
|
case T_BitmapIndexScan:
|
|
case T_BitmapHeapScan:
|
|
case T_CStoreIndexCtidScan:
|
|
case T_CStoreIndexHeapScan:
|
|
case T_TidScan:
|
|
case T_PartIterator:
|
|
case T_SubqueryScan:
|
|
return u_sess->pgxc_cxt.NumDataNodes;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (plan->exec_type == EXEC_ON_DATANODES) {
|
|
Bitmapset* bms_nodeids = ng_get_dest_nodeids(plan);
|
|
unsigned int num_data_nodes = (unsigned int)bms_num_members(bms_nodeids);
|
|
if (num_data_nodes == 0) {
|
|
elog(DEBUG1, "[ng_get_dest_num_data_nodes] num of data nodes is 0");
|
|
num_data_nodes = 1;
|
|
}
|
|
return num_data_nodes;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ng_get_dest_num_data_nodes
|
|
* get the number of data nodes from a RelOptInfo
|
|
*
|
|
* @param (in) rel:
|
|
* the target RelOptInfo node
|
|
*
|
|
* @return:
|
|
* the number of data nodes from a RelOptInfo
|
|
*/
|
|
unsigned int ng_get_dest_num_data_nodes(RelOptInfo* rel)
|
|
{
|
|
AssertEreport(rel != NULL, MOD_OPT, "");
|
|
|
|
#ifndef ENABLE_MULTIPLE_NODES
|
|
return 1;
|
|
#endif
|
|
|
|
if (!IS_STREAM_PLAN) {
|
|
return 1;
|
|
}
|
|
|
|
unsigned int num_data_nodes = u_sess->pgxc_cxt.NumDataNodes;
|
|
|
|
if (rel->cheapest_total_path != NULL) {
|
|
Path* path = (Path*)linitial(rel->cheapest_total_path);
|
|
num_data_nodes = ng_get_dest_num_data_nodes(path);
|
|
} else if (rel->subplan) {
|
|
num_data_nodes = ng_get_dest_num_data_nodes(rel->subplan);
|
|
}
|
|
|
|
if (num_data_nodes == 0) {
|
|
elog(DEBUG1, "[ng_get_dest_num_data_nodes] num of data nodes is 0");
|
|
num_data_nodes = 1;
|
|
}
|
|
return num_data_nodes;
|
|
}
|
|
|
|
/*
|
|
* ng_get_dest_num_data_nodes
|
|
* get the number of data nodes from a RelOptInfo
|
|
*
|
|
* @param (in) root:
|
|
* the PlannerInfo node of a query block
|
|
* @param (in) rel:
|
|
* the target RelOptInfo node
|
|
*
|
|
* @return:
|
|
* the number of data nodes from a RelOptInfo
|
|
*/
|
|
unsigned int ng_get_dest_num_data_nodes(PlannerInfo* root, RelOptInfo* rel)
|
|
{
|
|
AssertEreport(rel != NULL, MOD_OPT, "");
|
|
|
|
#ifndef ENABLE_MULTIPLE_NODES
|
|
return 1;
|
|
#endif
|
|
|
|
if (rel->num_data_nodes != 0) {
|
|
return rel->num_data_nodes;
|
|
}
|
|
|
|
if (!IS_STREAM_PLAN) {
|
|
rel->num_data_nodes = 1;
|
|
return 1;
|
|
}
|
|
|
|
unsigned int num_data_nodes = u_sess->pgxc_cxt.NumDataNodes;
|
|
|
|
switch (rel->rtekind) {
|
|
case RTE_RELATION: {
|
|
/* RELKIND_FOREIGN_TABLE or not */
|
|
RangeTblEntry* rte = root->simple_rte_array[rel->relid];
|
|
num_data_nodes = ng_get_baserel_num_data_nodes(rte->relid, rte->relkind);
|
|
break;
|
|
}
|
|
case RTE_SUBQUERY: {
|
|
Plan* subplan = rel->subplan;
|
|
if (subplan != NULL) {
|
|
num_data_nodes = ng_get_dest_num_data_nodes(subplan);
|
|
} else {
|
|
elog(DEBUG1, "[ng_get_dest_num_data_nodes] RTE_SUBQUERY has no subplan");
|
|
num_data_nodes = u_sess->pgxc_cxt.NumDataNodes;
|
|
}
|
|
break;
|
|
}
|
|
case RTE_JOIN: {
|
|
AssertEreport(rel->cheapest_total_path != NULL, MOD_OPT, "");
|
|
Path* path = (Path*)linitial(rel->cheapest_total_path);
|
|
AssertEreport(path != NULL, MOD_OPT, "");
|
|
num_data_nodes = ng_get_dest_num_data_nodes(path);
|
|
break;
|
|
}
|
|
case RTE_FUNCTION:
|
|
case RTE_VALUES:
|
|
case RTE_CTE:
|
|
num_data_nodes = u_sess->pgxc_cxt.NumDataNodes;
|
|
break;
|
|
case RTE_RESULT:
|
|
num_data_nodes = 1;
|
|
break;
|
|
default:
|
|
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Unexpected range table entry type.")));
|
|
break;
|
|
}
|
|
|
|
if (num_data_nodes == 0) {
|
|
elog(DEBUG1, "[ng_get_dest_num_data_nodes] num of data nodes is 0");
|
|
num_data_nodes = 1;
|
|
}
|
|
|
|
rel->num_data_nodes = num_data_nodes;
|
|
return num_data_nodes;
|
|
}
|
|
|
|
/*
|
|
* ng_get_dest_locator_type
|
|
* get the destination locator type of a Plan node
|
|
*
|
|
* @param (in) plan:
|
|
* the target Plan node
|
|
*
|
|
* @return:
|
|
* the destination locator type of a Plan node
|
|
*/
|
|
char ng_get_dest_locator_type(Plan* plan)
|
|
{
|
|
if (IsA(plan, Stream) || IsA(plan, VecStream)) {
|
|
Stream* stream_plan = (Stream*)plan;
|
|
return stream_plan->consumer_nodes->baselocatortype;
|
|
} else {
|
|
return plan->exec_nodes->baselocatortype;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ng_get_dest_distribute_keys
|
|
* get the destination distribute keys of a Plan node
|
|
*
|
|
* @param (in) plan:
|
|
* the target Plan node
|
|
*
|
|
* @return:
|
|
* the destination distribute keys of a Plan node
|
|
*/
|
|
List* ng_get_dest_distribute_keys(Plan* plan)
|
|
{
|
|
if (IsA(plan, Stream) || IsA(plan, VecStream)) {
|
|
Stream* stream_plan = (Stream*)plan;
|
|
return stream_plan->distribute_keys;
|
|
} else {
|
|
return plan->distributed_keys;
|
|
}
|
|
}
|
|
|
|
/* ----------
|
|
* Distribution data structure management
|
|
* ----------
|
|
*/
|
|
/*
|
|
* ng_copy_distribution
|
|
* copy a Distribution
|
|
*
|
|
* @param (in) src_distribution:
|
|
* the source Distribution
|
|
*
|
|
* @return:
|
|
* the destination Distribution
|
|
*/
|
|
Distribution* ng_copy_distribution(Distribution* src_distribution)
|
|
{
|
|
AssertEreport(src_distribution != NULL, MOD_OPT, "");
|
|
|
|
Distribution* dest_distribution = NewDistribution();
|
|
|
|
ng_copy_distribution(dest_distribution, src_distribution);
|
|
|
|
return dest_distribution;
|
|
}
|
|
|
|
/*
|
|
* ng_copy_distribution
|
|
* copy a Distribution
|
|
*
|
|
* @param (in) dest_distribution:
|
|
* the destination Distribution
|
|
* @param (in) src_distribution:
|
|
* the source Distribution
|
|
*
|
|
* @return: void
|
|
*/
|
|
void ng_copy_distribution(Distribution* dest_distribution, const Distribution* src_distribution)
|
|
{
|
|
dest_distribution->group_oid = src_distribution->group_oid;
|
|
/* bms_copy will palloc a new bms */
|
|
if (dest_distribution->bms_data_nodeids != NULL)
|
|
bms_free(dest_distribution->bms_data_nodeids);
|
|
dest_distribution->bms_data_nodeids = bms_copy(src_distribution->bms_data_nodeids);
|
|
}
|
|
|
|
/*
|
|
* ng_set_distribution
|
|
* set a Distribution by another Distribution
|
|
*
|
|
* @param (in) dest_distribution:
|
|
* the destination Distribution
|
|
* @param (in) src_distribution:
|
|
* the source Distribution
|
|
*
|
|
* @return: void
|
|
*/
|
|
void ng_set_distribution(Distribution* dest_distribution, Distribution* src_distribution)
|
|
{
|
|
dest_distribution->group_oid = src_distribution->group_oid;
|
|
dest_distribution->bms_data_nodeids = src_distribution->bms_data_nodeids;
|
|
}
|
|
|
|
/*
|
|
* ng_get_overlap_distribution
|
|
* get overlap Distribution of two Distribution node
|
|
*
|
|
* @param (in) distribution_1:
|
|
* first Distribution(s) to overlap, it could be NULL
|
|
* if this parameter is NULL, reture the second Distribution as overlap
|
|
* @param (in) distribution_2:
|
|
* second Distribution(s) to overlap
|
|
*
|
|
* @return:
|
|
* the overlap Distribution
|
|
*/
|
|
Distribution* ng_get_overlap_distribution(Distribution* distribution_1, Distribution* distribution_2)
|
|
{
|
|
AssertEreport(distribution_2 != NULL, MOD_OPT, "");
|
|
|
|
Distribution* distribution = NewDistribution();
|
|
|
|
if (distribution_1 == NULL) {
|
|
ng_copy_distribution(distribution, distribution_2);
|
|
return distribution;
|
|
}
|
|
|
|
if (ng_is_same_group(distribution_1, distribution_2)) {
|
|
Distribution* better_distribution = InvalidOid != distribution_1->group_oid ? distribution_1 : distribution_2;
|
|
ng_copy_distribution(distribution, better_distribution);
|
|
return distribution;
|
|
} else {
|
|
distribution->group_oid = InvalidOid;
|
|
distribution->bms_data_nodeids =
|
|
bms_intersect(distribution_1->bms_data_nodeids, distribution_2->bms_data_nodeids);
|
|
return distribution;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ng_get_union_distribution
|
|
* get union of two Distribution(s)
|
|
*
|
|
* @param (in) distribution_1:
|
|
* first Distribution(s) to union, it could be NULL
|
|
* if this parameter is NULL, reture the second Distribution as union
|
|
* @param (in) distribution_2:
|
|
* second Distribution(s) to union
|
|
*
|
|
* @return:
|
|
* the copied union Distribution and leaving their inputs untouched
|
|
*/
|
|
Distribution* ng_get_union_distribution(Distribution* distribution_1, Distribution* distribution_2)
|
|
{
|
|
AssertEreport(distribution_2 != NULL, MOD_OPT, "");
|
|
|
|
Distribution* distribution = NewDistribution();
|
|
|
|
if (distribution_1 == NULL) {
|
|
ng_copy_distribution(distribution, distribution_2);
|
|
return distribution;
|
|
}
|
|
|
|
if (ng_is_same_group(distribution_1, distribution_2)) {
|
|
Distribution* better_distribution = InvalidOid != distribution_1->group_oid ? distribution_1 : distribution_2;
|
|
ng_copy_distribution(distribution, better_distribution);
|
|
return distribution;
|
|
} else {
|
|
distribution->group_oid = InvalidOid;
|
|
distribution->bms_data_nodeids = bms_union(distribution_1->bms_data_nodeids, distribution_2->bms_data_nodeids);
|
|
return distribution;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ng_get_union_distribution_recycle
|
|
* get union of two Distribution(s)
|
|
*
|
|
* @param (in) distribution_1:
|
|
* first Distribution(s) to union, it could be NULL
|
|
* if this parameter is NULL, reture the second Distribution as union
|
|
* @param (in) distribution_2:
|
|
* second Distribution(s) to union
|
|
*
|
|
* @return:
|
|
* the copied union Distribution and recycle their inputs
|
|
*/
|
|
Distribution* ng_get_union_distribution_recycle(Distribution* distribution_1, Distribution* distribution_2)
|
|
{
|
|
Distribution* result = ng_get_union_distribution(distribution_1, distribution_2);
|
|
DestroyDistribution(distribution_1);
|
|
DestroyDistribution(distribution_2);
|
|
return result;
|
|
}
|
|
|
|
Distribution* ng_get_random_single_dn_distribution(Distribution* distribution)
|
|
{
|
|
Distribution* result_distribution = NewDistribution();
|
|
int random = pickup_random_datanode(bms_num_members(distribution->bms_data_nodeids));
|
|
int dn_oid = -1;
|
|
for (int i = 0; i <= random; ++i) {
|
|
dn_oid = bms_next_member(distribution->bms_data_nodeids, dn_oid);
|
|
}
|
|
AssertEreport(dn_oid >= 0, MOD_OPT, "");
|
|
result_distribution->bms_data_nodeids = bms_add_member(result_distribution->bms_data_nodeids, dn_oid);
|
|
result_distribution->group_oid = InvalidOid;
|
|
return result_distribution;
|
|
}
|
|
|
|
/* ----------
|
|
* convert functions between:
|
|
* ----------
|
|
*/
|
|
/*
|
|
* ng_convert_to_nodeid
|
|
* convert node oid to node index
|
|
*/
|
|
int ng_convert_to_nodeid(Oid nodeoid)
|
|
{
|
|
int nodeid = PGXCNodeGetNodeId(nodeoid, PGXC_NODE_DATANODE);
|
|
return nodeid;
|
|
}
|
|
|
|
/*
|
|
* ng_convert_to_nodeoid
|
|
* convert node index to node oid
|
|
*/
|
|
Oid ng_convert_to_nodeoid(int nodeid)
|
|
{
|
|
Oid nodeoid = PGXCNodeGetNodeOid(nodeid, PGXC_NODE_DATANODE);
|
|
return nodeoid;
|
|
}
|
|
|
|
/*
|
|
* ng_convert_to_nodeid_list
|
|
* convert node index bitmap set to node index list
|
|
*/
|
|
List* ng_convert_to_nodeid_list(Bitmapset* bms_nodeids)
|
|
{
|
|
List* nodeid_list = NIL;
|
|
|
|
for (int nodeid = -1; (nodeid = bms_next_member(bms_nodeids, nodeid)) >= 0;) {
|
|
nodeid_list = lappend_int(nodeid_list, nodeid);
|
|
}
|
|
|
|
return nodeid_list;
|
|
}
|
|
|
|
/*
|
|
* ng_convert_to_nodeids
|
|
* convert exec nodes to node index bitmap set
|
|
*/
|
|
Bitmapset* ng_convert_to_nodeids(ExecNodes* exec_nodes)
|
|
{
|
|
if (exec_nodes == NULL) {
|
|
ereport(ERROR,
|
|
(errmodule(MOD_OPT), errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("exec_nodes could not be NULL")));
|
|
}
|
|
return ng_convert_to_nodeids(exec_nodes->nodeList);
|
|
}
|
|
|
|
/*
|
|
* ng_convert_to_nodeids
|
|
* convert node index list to node index bitmap set
|
|
*/
|
|
Bitmapset* ng_convert_to_nodeids(List* nodeid_list)
|
|
{
|
|
Bitmapset* bms_nodeids = NULL;
|
|
|
|
ListCell* lc = NULL;
|
|
foreach (lc, nodeid_list) {
|
|
int nodeid = lfirst_int(lc);
|
|
bms_nodeids = bms_add_member(bms_nodeids, nodeid);
|
|
}
|
|
|
|
return bms_nodeids;
|
|
}
|
|
|
|
/*
|
|
* ng_convert_to_distribution
|
|
* convert node index list to Distribution
|
|
*/
|
|
Distribution* ng_convert_to_distribution(List* nodeid_list)
|
|
{
|
|
Distribution* distribution = NewDistribution();
|
|
distribution->group_oid = InvalidOid;
|
|
distribution->bms_data_nodeids = ng_convert_to_nodeids(nodeid_list);
|
|
return distribution;
|
|
}
|
|
|
|
/*
|
|
* ng_convert_to_distribution
|
|
* convert exec nodes to Distribution,
|
|
* this function concern on the exec nodes of an ExecNodes,
|
|
* not the data nodes in the ExecNodes
|
|
*/
|
|
Distribution* ng_convert_to_distribution(ExecNodes* exec_nodes)
|
|
{
|
|
/* Make a new instance and set data nodeids from ExecNodes::nodeList */
|
|
Distribution* distribution = ng_convert_to_distribution(exec_nodes->nodeList);
|
|
|
|
/* Set group oid */
|
|
if (bms_equal(distribution->bms_data_nodeids, exec_nodes->distribution.bms_data_nodeids)) {
|
|
distribution->group_oid = exec_nodes->distribution.group_oid;
|
|
} else {
|
|
distribution->group_oid = InvalidOid;
|
|
}
|
|
|
|
return distribution;
|
|
}
|
|
|
|
/*
|
|
* ng_convert_to_exec_nodes
|
|
* convert a Distribution to an ExecNodes
|
|
*
|
|
* @param (in) distribution:
|
|
* the Distribution information, provide exec nodes and data nodes for the ExecNodes
|
|
* @param (in) locator_type:
|
|
* the locator type of this ExecNodes
|
|
* @param (in) access_type:
|
|
* the access type of this ExecNodes
|
|
*
|
|
* @return:
|
|
* the new ExecNodes
|
|
*/
|
|
ExecNodes* ng_convert_to_exec_nodes(Distribution* distribution, char locator_type, RelationAccessType access_type)
|
|
{
|
|
ExecNodes* execnodes = makeNode(ExecNodes);
|
|
|
|
if (distribution == NULL) {
|
|
ereport(ERROR,
|
|
(errmodule(MOD_OPT), errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("distribution could not be NULL")));
|
|
}
|
|
|
|
execnodes->nodeList = ng_convert_to_nodeid_list(distribution->bms_data_nodeids);
|
|
ng_copy_distribution(&execnodes->distribution, distribution);
|
|
execnodes->baselocatortype = locator_type;
|
|
execnodes->accesstype = access_type;
|
|
execnodes->primarynodelist = NIL;
|
|
execnodes->en_expr = NULL;
|
|
|
|
return execnodes;
|
|
}
|
|
|
|
/*
|
|
* Check whether the distribution exists in the distributions.
|
|
*/
|
|
bool is_distribution_exists(List* distributions, Distribution* distribution)
|
|
{
|
|
bool find = false;
|
|
ListCell* lc = NULL;
|
|
foreach(lc, distributions) {
|
|
Distribution* tmp_distribution = (Distribution*)lfirst(lc);
|
|
if (tmp_distribution != NULL && ng_is_same_group(tmp_distribution, distribution)) {
|
|
find = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return find;
|
|
}
|
|
|
|
/*
|
|
* Heuristic methods
|
|
*/
|
|
/*
|
|
* ng_get_join_candidate_distribution_list
|
|
* Get candidate Distribution list of a Join
|
|
* Three candidates will be taken:
|
|
* (1) the node group of outer path
|
|
* (2) the node group of inner path
|
|
* (3) the default computing group of current computing mode
|
|
* Same group will be throw away
|
|
*
|
|
* @param (in) outer_path:
|
|
* the outer path
|
|
* @param (in) inner_path:
|
|
* the inner path
|
|
*
|
|
* @return:
|
|
* the candidates list
|
|
*/
|
|
List* ng_get_join_candidate_distribution_list(Path* outer_path, Path* inner_path, DistrbutionPreferenceType type)
|
|
{
|
|
List* candidate_list = NIL;
|
|
Distribution* distribution = NULL;
|
|
|
|
/* Default compute distribution. */
|
|
if (type == DPT_ALL || type == DPT_SHUFFLE) {
|
|
distribution = ng_get_default_computing_group_distribution();
|
|
candidate_list = lappend(candidate_list, distribution);
|
|
ComputingNodeGroupMode cng_mode = ng_get_computing_nodegroup_mode();
|
|
if (CNG_MODE_FORCE == cng_mode) {
|
|
return candidate_list;
|
|
}
|
|
}
|
|
|
|
/* Single node distribution. */
|
|
if (type == DPT_ALL || type == DPT_SINGLE) {
|
|
distribution = ng_get_single_node_distribution();
|
|
if (distribution != NULL && !is_distribution_exists(candidate_list, distribution)) {
|
|
candidate_list = lappend(candidate_list, distribution);
|
|
}
|
|
}
|
|
|
|
/* Join outer's distribution. */
|
|
distribution = ng_get_dest_distribution(outer_path);
|
|
if (!is_distribution_exists(candidate_list, distribution)) {
|
|
candidate_list = lappend(candidate_list, distribution);
|
|
}
|
|
|
|
/* Join inner's distribution. */
|
|
distribution = ng_get_dest_distribution(inner_path);
|
|
if (!is_distribution_exists(candidate_list, distribution)) {
|
|
candidate_list = lappend(candidate_list, distribution);
|
|
}
|
|
return candidate_list;
|
|
}
|
|
|
|
/*
|
|
* ng_get_join_candidate_distribution_list
|
|
* Get candidate Distribution list of a Join
|
|
* It will be different for correlated query block and un-correlated query block
|
|
* (1) for correlated query block, it's join should be in a special node group for correlated sub-plan
|
|
* (2) for un-correlated query block, it will use heuristic methods to get candidate group node list
|
|
*
|
|
* @param (in) outer_path:
|
|
* the outer path
|
|
* @param (in) inner_path:
|
|
* the inner path
|
|
*
|
|
* @return:
|
|
* the candidates list
|
|
*/
|
|
List* ng_get_join_candidate_distribution_list(Path* outer_path, Path* inner_path, bool is_correlated, DistrbutionPreferenceType type)
|
|
{
|
|
if (is_correlated) {
|
|
Distribution* installation_distribution = ng_get_correlated_subplan_group_distribution();
|
|
List* candidate_list = list_make1(installation_distribution);
|
|
return candidate_list;
|
|
} else {
|
|
return ng_get_join_candidate_distribution_list(outer_path, inner_path, type);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ng_get_agg_candidate_distribution_list
|
|
* Get candidate Distribution list of an Agg
|
|
* Two candidates will be taken:
|
|
* (1) the default computing group of current computing mode
|
|
* (2) the original distribution below the agg
|
|
*
|
|
* @param (in) plan:
|
|
* the Plan node to add Agg
|
|
*
|
|
* @return:
|
|
* the candidate Distribution list
|
|
*/
|
|
List* ng_get_agg_candidate_distribution_list(Plan* plan, bool is_correlated, DistrbutionPreferenceType type)
|
|
{
|
|
if (is_correlated) {
|
|
Distribution* installation_distribution = ng_get_correlated_subplan_group_distribution();
|
|
List* candidate_list = list_make1(installation_distribution);
|
|
return candidate_list;
|
|
}
|
|
|
|
List* candidate_list = NIL;
|
|
Distribution* distribution = NULL;
|
|
|
|
/* Default compute distribution. */
|
|
if (type == DPT_ALL || type == DPT_SHUFFLE) {
|
|
distribution = ng_get_default_computing_group_distribution();
|
|
candidate_list = lappend(candidate_list, distribution);
|
|
ComputingNodeGroupMode cng_mode = ng_get_computing_nodegroup_mode();
|
|
if (CNG_MODE_FORCE == cng_mode) {
|
|
return candidate_list;
|
|
}
|
|
}
|
|
|
|
/* Single node distribution. */
|
|
if (type == DPT_ALL || type == DPT_SINGLE) {
|
|
distribution = ng_get_single_node_distribution();
|
|
if (distribution != NULL && !is_distribution_exists(candidate_list, distribution)) {
|
|
candidate_list = lappend(candidate_list, distribution);
|
|
}
|
|
}
|
|
|
|
/* Agg's dest distribution. */
|
|
distribution = ng_get_dest_distribution(plan);
|
|
if (!is_distribution_exists(candidate_list, distribution)) {
|
|
candidate_list = lappend(candidate_list, distribution);
|
|
}
|
|
return candidate_list;
|
|
}
|
|
|
|
/*
|
|
* ng_get_setop_candidate_distribution_list
|
|
* Get candidate Distribution list of an setop
|
|
* Groups of all branchs will be taken.
|
|
*
|
|
* @param (in) subPlans:
|
|
* the branchs of a setop
|
|
*
|
|
* @return:
|
|
* the candidate Distribution list
|
|
*/
|
|
List* ng_get_setop_candidate_distribution_list(List* subPlans, bool is_correlated)
|
|
{
|
|
if (is_correlated) {
|
|
Distribution* installation_distribution = ng_get_correlated_subplan_group_distribution();
|
|
List* candidate_list = list_make1(installation_distribution);
|
|
return candidate_list;
|
|
}
|
|
|
|
List* candidate_distribution_list = NIL;
|
|
|
|
ListCell* lc = NULL;
|
|
foreach (lc, subPlans) {
|
|
Plan* subPlan = (Plan*)lfirst(lc);
|
|
Assert(subPlan->exec_nodes->nodeList != NIL);
|
|
Distribution* distribution = ng_get_dest_distribution(subPlan);
|
|
if (!is_distribution_exists(candidate_distribution_list, distribution)) {
|
|
candidate_distribution_list = lappend(candidate_distribution_list, distribution);
|
|
}
|
|
}
|
|
|
|
return candidate_distribution_list;
|
|
}
|
|
|
|
/*
|
|
* Cost based algorithms
|
|
*/
|
|
/*
|
|
* ng_get_nodegroup_stream_weight
|
|
* calculate cost weight for stream in multiple node group scenario
|
|
*
|
|
* @param (in) producer_num_dn, consumer_num_dn:
|
|
* the number of data nodes of producer and consumer
|
|
*
|
|
* @return:
|
|
* the weight
|
|
*/
|
|
double ng_get_nodegroup_stream_weight(unsigned int producer_num_dn, unsigned int consumer_num_dn)
|
|
{
|
|
double nodegroup_weight = 1.0;
|
|
|
|
if (!u_sess->opt_cxt.is_multiple_nodegroup_scenario || producer_num_dn == consumer_num_dn) {
|
|
return 1.0;
|
|
} else {
|
|
/*
|
|
* Constant for node group optimizer
|
|
* EXAMPLE: ngc {a, b, c, d, e}
|
|
* b = 1
|
|
* c + e = 1
|
|
* -1 < d < 0
|
|
*/
|
|
double ngc[5] = {0.4163, 1, 0.1124, -0.9, 0.8876};
|
|
|
|
/* cost factors for node group */
|
|
double num_dn_ratio = (double)consumer_num_dn / (double)producer_num_dn;
|
|
|
|
if (producer_num_dn < consumer_num_dn) {
|
|
/* small group to big group, num_dn_ratio > 1 */
|
|
nodegroup_weight = ngc[0] * log(num_dn_ratio) / log(exp(1)) + ngc[1];
|
|
} else {
|
|
/* big group to small group */
|
|
nodegroup_weight = ngc[2] * pow(num_dn_ratio, ngc[3]) + ngc[4];
|
|
}
|
|
}
|
|
|
|
return nodegroup_weight;
|
|
}
|
|
|
|
/*
|
|
* ng_calculate_setop_branch_stream_cost
|
|
* Calculate stream cost when redistribute branchs of setop into same node group and distribute key.
|
|
* In future version, take bigger cluster into consideration for upper agg.
|
|
*
|
|
* @param (in) subPlan:
|
|
* one branch of setop
|
|
*
|
|
* @return:
|
|
* the redistribute cost
|
|
*/
|
|
Cost ng_calculate_setop_branch_stream_cost(
|
|
Plan* subPlan, unsigned int producer_num_datanodes, unsigned int consumer_num_datanodes)
|
|
{
|
|
Cost cost = 0;
|
|
|
|
if (is_replicated_plan(subPlan)) {
|
|
cost = (PLAN_LOCAL_ROWS(subPlan) / ((double)(consumer_num_datanodes))) * Max(subPlan->plan_width, 8);
|
|
} else {
|
|
cost = PLAN_LOCAL_ROWS(subPlan) * Max(subPlan->plan_width, 8);
|
|
}
|
|
|
|
return cost;
|
|
}
|
|
|
|
/*
|
|
* Distribution management for each operators
|
|
*/
|
|
/*
|
|
* ng_get_join_distribution
|
|
* get Distribution information from two child for a Join node
|
|
*
|
|
* @param (in) outer_path, inner_path:
|
|
* the outer and inner path
|
|
*
|
|
* @return:
|
|
* the Distribution of Join node
|
|
*/
|
|
Distribution* ng_get_join_distribution(Path* outer_path, Path* inner_path)
|
|
{
|
|
Distribution* distribution_outer = ng_get_dest_distribution(outer_path);
|
|
Distribution* distribution_inner = ng_get_dest_distribution(inner_path);
|
|
|
|
AssertEreport(NULL != distribution_outer, MOD_OPT, "");
|
|
AssertEreport(NULL != distribution_inner, MOD_OPT, "");
|
|
|
|
if (is_subplan_exec_on_coordinator(inner_path)) {
|
|
return ng_copy_distribution(distribution_inner);
|
|
} else if (is_subplan_exec_on_coordinator(outer_path)) {
|
|
return ng_copy_distribution(distribution_outer);
|
|
} else if (ng_is_same_group(distribution_outer, distribution_inner)) {
|
|
return distribution_outer;
|
|
} else {
|
|
ereport(
|
|
ERROR, (errcode(ERRCODE_UNEXPECTED_NODE_STATE), errmsg("Node group of outer and inner should be same.")));
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ng_get_join_data_distribution
|
|
* get data Distribution for a Join node
|
|
*
|
|
* @param (in) lefttree, righttree:
|
|
* the lefttree and righttree plan
|
|
* @param (in) nodeid_list:
|
|
* the exec data node index list
|
|
*
|
|
* @return:
|
|
* the data Distribution for a Join node
|
|
*/
|
|
Distribution* ng_get_join_data_distribution(Plan* lefttree, Plan* righttree, List* nodeid_list)
|
|
{
|
|
Distribution* left_distribution = ng_get_dest_distribution(lefttree);
|
|
Distribution* right_distribution = ng_get_dest_distribution(righttree);
|
|
|
|
if (ng_is_same_group(left_distribution, right_distribution)) {
|
|
return left_distribution;
|
|
} else {
|
|
elog(DEBUG1, "Left and right side of join are not in same group");
|
|
return ng_convert_to_distribution(nodeid_list);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ng_get_union_distribution_for_union_all
|
|
* In union all cases, when all branchs are not replicate, and in same group,
|
|
* this group will be used as target Distribution of this setop
|
|
*
|
|
* @param (in) subPlans:
|
|
* the branchs sub plans
|
|
*
|
|
* @return:
|
|
* NULL : there are replicate branch, which we will determine target node group later
|
|
* Distribution : the selected node group
|
|
*/
|
|
Distribution* ng_get_union_distribution_for_union_all(List* subPlans)
|
|
{
|
|
bool all_hash_in_same_group = true;
|
|
Distribution* all_hash_distribution = NULL;
|
|
|
|
ListCell* lc = NULL;
|
|
foreach (lc, subPlans) {
|
|
Plan* subPlan = (Plan*)lfirst(lc);
|
|
ExecNodes* ori_exec_nodes = ng_get_dest_execnodes(subPlan);
|
|
if (NIL == ori_exec_nodes->nodeList) {
|
|
elog(DEBUG1, "Union all branch's exec node list is empty");
|
|
}
|
|
|
|
if (is_replicated_plan(subPlan)) {
|
|
all_hash_in_same_group = false;
|
|
}
|
|
|
|
if (all_hash_in_same_group) {
|
|
if (all_hash_distribution == NULL) {
|
|
all_hash_distribution = NewDistribution();
|
|
ng_copy_distribution(all_hash_distribution, &ori_exec_nodes->distribution);
|
|
} else {
|
|
if (!ng_is_same_group(all_hash_distribution, &ori_exec_nodes->distribution)) {
|
|
all_hash_in_same_group = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (all_hash_in_same_group) {
|
|
AssertEreport(
|
|
all_hash_distribution != NULL && !bms_is_empty(all_hash_distribution->bms_data_nodeids), MOD_OPT, "");
|
|
|
|
return all_hash_distribution;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ng_get_best_setop_distribution
|
|
* get target Distribution from branchs of setop
|
|
* (1) union all, all hash, all in same group => this group
|
|
* (2) others => need to shuffle to same group, choose cheapest one
|
|
*
|
|
* @param (in) subPlans:
|
|
* branchs
|
|
* @param (in) isUnionAll:
|
|
* mark wheather it's union all
|
|
*
|
|
* @return:
|
|
* the target Distribution
|
|
*/
|
|
Distribution* ng_get_best_setop_distribution(List* subPlans, bool isUnionAll, bool is_correlated)
|
|
{
|
|
Distribution* union_all_with_no_replicate = NULL;
|
|
if (isUnionAll) {
|
|
union_all_with_no_replicate = ng_get_union_distribution_for_union_all(subPlans);
|
|
}
|
|
|
|
if (union_all_with_no_replicate != NULL) {
|
|
return union_all_with_no_replicate;
|
|
}
|
|
|
|
List* candidate_distribution_list = ng_get_setop_candidate_distribution_list(subPlans, is_correlated);
|
|
|
|
Cost min_total_cost = -1.0;
|
|
Distribution* best_distribution = NULL;
|
|
|
|
ListCell* lc_candidate_distribution = NULL;
|
|
foreach (lc_candidate_distribution, candidate_distribution_list) {
|
|
Distribution* target_distribution = (Distribution*)lfirst(lc_candidate_distribution);
|
|
Cost total_cost = 0.0;
|
|
|
|
ListCell* lc_subPlan = NULL;
|
|
foreach (lc_subPlan, subPlans) {
|
|
Plan* subPlan = (Plan*)lfirst(lc_subPlan);
|
|
|
|
Distribution* src_distribution = ng_get_dest_distribution(subPlan);
|
|
if (ng_is_same_group(target_distribution, src_distribution)) {
|
|
continue;
|
|
}
|
|
|
|
unsigned int producer_num_datanodes = ng_get_dest_num_data_nodes(subPlan);
|
|
unsigned int consumer_num_datanodes = bms_num_members(target_distribution->bms_data_nodeids);
|
|
Cost cost = ng_calculate_setop_branch_stream_cost(subPlan, producer_num_datanodes, consumer_num_datanodes);
|
|
total_cost += cost;
|
|
}
|
|
|
|
if (min_total_cost < 0 || min_total_cost > total_cost) {
|
|
min_total_cost = total_cost;
|
|
best_distribution = target_distribution;
|
|
}
|
|
}
|
|
|
|
return best_distribution;
|
|
}
|
|
|
|
/*
|
|
* do shuffle between node groups if needed
|
|
*/
|
|
/*
|
|
* ng_stream_side_paths_for_replicate
|
|
* Check wheather shuffle is needed when there are replicate table in outer and inner path,
|
|
* and shuffle them if needed.
|
|
*
|
|
* @param (in) root:
|
|
* the PlannerInfo
|
|
* @param (in) outer_path, inner_path:
|
|
* the outer and inner path of a join
|
|
* @param (in) jointype:
|
|
* the join type
|
|
* @param (in) is_mergejoin:
|
|
* wheather it's a merge join
|
|
* @param (in) target_distribution:
|
|
* the target Distribution to calculate the join
|
|
*
|
|
* @return: void
|
|
*/
|
|
void ng_stream_side_paths_for_replicate(PlannerInfo* root, Path** outer_path, Path** inner_path, JoinType jointype,
|
|
bool is_mergejoin, Distribution* target_distribution)
|
|
{
|
|
const double SKEW = 0.5;
|
|
|
|
/* Return if one side is on CN */
|
|
if (is_subplan_exec_on_coordinator(*outer_path) || is_subplan_exec_on_coordinator(*inner_path))
|
|
return;
|
|
|
|
bool is_outer_replicated = is_replicated_path(*outer_path);
|
|
bool is_inner_replicated = is_replicated_path(*inner_path);
|
|
|
|
if (!is_outer_replicated && !is_inner_replicated) {
|
|
ereport(ERROR,
|
|
(errmodule(MOD_OPT),
|
|
errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
|
|
errmsg("is_outer_replicated and is_inner_replicated could not all be false")));
|
|
}
|
|
|
|
List* distribute_keys_outer = (*outer_path)->distribute_keys;
|
|
List* distribute_keys_inner = (*inner_path)->distribute_keys;
|
|
List* outer_pathkeys = is_mergejoin ? (*outer_path)->pathkeys : NIL;
|
|
List* inner_pathkeys = is_mergejoin ? (*inner_path)->pathkeys : NIL;
|
|
|
|
bool is_outer_need_shuffle = ng_is_shuffle_needed(root, *outer_path, target_distribution);
|
|
bool is_inner_need_shuffle = ng_is_shuffle_needed(root, *inner_path, target_distribution);
|
|
|
|
ParallelDesc* outer_smpDesc = NULL;
|
|
ParallelDesc* inner_smpDesc = NULL;
|
|
|
|
if (is_outer_need_shuffle) {
|
|
if (is_outer_replicated) {
|
|
/* stream for shuffle's producer and consumer dop are the same */
|
|
if ((*outer_path)->dop > 1)
|
|
outer_smpDesc = create_smpDesc((*outer_path)->dop, (*outer_path)->dop, REMOTE_SPLIT_BROADCAST);
|
|
|
|
*outer_path = stream_side_path(root,
|
|
*outer_path,
|
|
jointype,
|
|
is_outer_replicated,
|
|
STREAM_BROADCAST,
|
|
NIL,
|
|
outer_pathkeys,
|
|
false,
|
|
SKEW,
|
|
target_distribution,
|
|
outer_smpDesc);
|
|
} else {
|
|
if (!ng_is_distribute_key_valid(root, distribute_keys_outer, (*outer_path)->parent->reltargetlist)) {
|
|
*outer_path = NULL;
|
|
return;
|
|
}
|
|
|
|
/* stream for shuffle's producer and consumer dop are the same */
|
|
if ((*outer_path)->dop > 1)
|
|
outer_smpDesc = create_smpDesc((*outer_path)->dop, (*outer_path)->dop, REMOTE_SPLIT_DISTRIBUTE);
|
|
|
|
*outer_path = stream_side_path(root,
|
|
*outer_path,
|
|
jointype,
|
|
is_outer_replicated,
|
|
STREAM_REDISTRIBUTE,
|
|
distribute_keys_outer,
|
|
outer_pathkeys,
|
|
false,
|
|
SKEW,
|
|
target_distribution,
|
|
outer_smpDesc);
|
|
}
|
|
}
|
|
|
|
if (is_inner_need_shuffle) {
|
|
if (is_inner_replicated) {
|
|
/* stream for shuffle's producer and consumer dop are the same */
|
|
if ((*inner_path)->dop > 1)
|
|
inner_smpDesc = create_smpDesc((*inner_path)->dop, (*inner_path)->dop, REMOTE_SPLIT_BROADCAST);
|
|
|
|
*inner_path = stream_side_path(root,
|
|
*inner_path,
|
|
jointype,
|
|
is_inner_replicated,
|
|
STREAM_BROADCAST,
|
|
NIL,
|
|
inner_pathkeys,
|
|
true,
|
|
SKEW,
|
|
target_distribution);
|
|
} else {
|
|
if (!ng_is_distribute_key_valid(root, distribute_keys_inner, (*inner_path)->parent->reltargetlist)) {
|
|
*inner_path = NULL;
|
|
return;
|
|
}
|
|
|
|
/* stream for shuffle's producer and consumer dop are the same */
|
|
if ((*inner_path)->dop > 1)
|
|
inner_smpDesc = create_smpDesc((*inner_path)->dop, (*inner_path)->dop, REMOTE_SPLIT_DISTRIBUTE);
|
|
|
|
*inner_path = stream_side_path(root,
|
|
*inner_path,
|
|
jointype,
|
|
is_inner_replicated,
|
|
STREAM_REDISTRIBUTE,
|
|
distribute_keys_inner,
|
|
inner_pathkeys,
|
|
true,
|
|
SKEW,
|
|
target_distribution,
|
|
inner_smpDesc);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ng_stream_non_broadcast_side_for_join
|
|
* need add redistribution step if the non-broadcast side doesn't match the computing node group
|
|
*
|
|
* @param (in) non_stream_path:
|
|
* the non-broadcast side path
|
|
* @param (in) target_distribution:
|
|
* the target Distribution to calculate the join
|
|
*
|
|
* @return:
|
|
* the streamed Path
|
|
*/
|
|
Path* ng_stream_non_broadcast_side_for_join(PlannerInfo* root, Path* non_stream_path, JoinType save_jointype,
|
|
List* non_stream_pathkeys, bool is_replicate, bool stream_outer, Distribution* target_distribution)
|
|
{
|
|
if (ng_is_shuffle_needed(root, non_stream_path, target_distribution)) {
|
|
List* stream_distribute_key = non_stream_path->distribute_keys;
|
|
double skew_stream = non_stream_path->multiple;
|
|
|
|
if (stream_distribute_key == NIL) {
|
|
stream_distribute_key = get_distributekey_from_tlist(root,
|
|
non_stream_path->parent->reltargetlist,
|
|
non_stream_path->parent->reltargetlist,
|
|
non_stream_path->parent->rows,
|
|
&skew_stream);
|
|
}
|
|
|
|
if (ng_is_distribute_key_valid(root, stream_distribute_key, non_stream_path->parent->reltargetlist)) {
|
|
ParallelDesc* smpDesc = NULL;
|
|
|
|
/* stream for shuffle's producer and consumer dop are the same */
|
|
if (non_stream_path->dop > 1)
|
|
smpDesc = create_smpDesc(non_stream_path->dop, non_stream_path->dop, REMOTE_SPLIT_DISTRIBUTE);
|
|
|
|
non_stream_path = stream_side_path(root,
|
|
non_stream_path,
|
|
save_jointype,
|
|
is_replicate,
|
|
STREAM_REDISTRIBUTE,
|
|
stream_distribute_key,
|
|
non_stream_pathkeys,
|
|
stream_outer,
|
|
skew_stream,
|
|
target_distribution,
|
|
smpDesc);
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
return non_stream_path;
|
|
}
|
|
|
|
/*
|
|
* ng_agg_force_shuffle
|
|
* in FORCE mode (CNG_MODE_FORCE), add stream node before add agg nodes
|
|
*
|
|
* @param (in) root:
|
|
* the plan
|
|
* @param (in) groupcls:
|
|
* the group column list
|
|
* @param (in) subplan:
|
|
* the sub-plan tree
|
|
* @param (in) tlist:
|
|
* tlist where we search distribute keys
|
|
* @param (in) subpath:
|
|
* path used to reduce the targetlist if needed
|
|
*
|
|
* @return:
|
|
* the result plan
|
|
*/
|
|
Plan* ng_agg_force_shuffle(PlannerInfo* root, List* groupcls, Plan* subplan, List* tlist, Path* subpath)
|
|
{
|
|
Plan* result_plan = subplan;
|
|
|
|
ComputingNodeGroupMode cng_mode = ng_get_computing_nodegroup_mode();
|
|
Distribution* distribution = ng_get_dest_distribution(subplan);
|
|
Distribution* target_distribution = ng_get_default_computing_group_distribution();
|
|
if (is_execute_on_datanodes(subplan) && (!root->is_correlated) && CNG_MODE_FORCE == cng_mode &&
|
|
!ng_is_same_group(distribution, target_distribution)) {
|
|
if (subpath != NULL)
|
|
disuse_physical_tlist(subplan, subpath);
|
|
|
|
double multiple_force = 1.0;
|
|
List* distribute_keys =
|
|
get_distributekey_from_tlist(root, tlist, groupcls, subplan->plan_rows, &multiple_force);
|
|
if (distribute_keys == NULL) {
|
|
List* final_list_exprs = get_tlist_exprs(subplan->targetlist, false);
|
|
distribute_keys =
|
|
get_distributekey_from_tlist(root, NIL, final_list_exprs, subplan->plan_rows, &multiple_force);
|
|
}
|
|
if (ng_is_distribute_key_valid(root, distribute_keys, subplan->targetlist)) {
|
|
result_plan = make_stream_plan(root, subplan, distribute_keys, multiple_force, target_distribution);
|
|
}
|
|
}
|
|
|
|
return result_plan;
|
|
}
|
|
|
|
/*
|
|
* compare functions, judge functions
|
|
*/
|
|
/*
|
|
* ng_u_sess->opt_cxt.is_multiple_nodegroup_scenario
|
|
* get wheather it's a multiple node group scenario
|
|
*/
|
|
bool ng_is_multiple_nodegroup_scenario()
|
|
{
|
|
return u_sess->opt_cxt.is_multiple_nodegroup_scenario;
|
|
}
|
|
|
|
/*
|
|
* ng_u_sess->opt_cxt.different_nodegroup_count
|
|
* get wheather it's a everything in installation node group scenario
|
|
*/
|
|
bool ng_is_all_in_installation_nodegroup_scenario()
|
|
{
|
|
return u_sess->opt_cxt.different_nodegroup_count == 1;
|
|
}
|
|
|
|
/*
|
|
* ng_u_sess->opt_cxt.enable_nodegroup_explain
|
|
* get wheather show node group information in explain (node group DFX)
|
|
*/
|
|
bool ng_enable_nodegroup_explain()
|
|
{
|
|
return u_sess->opt_cxt.enable_nodegroup_explain;
|
|
}
|
|
|
|
/*
|
|
* ng_is_valid_group_name
|
|
* check wheather the group name is valid
|
|
*/
|
|
bool ng_is_valid_group_name(const char* group_name)
|
|
{
|
|
if (group_name == NULL) {
|
|
return false;
|
|
}
|
|
|
|
if (0 == strncasecmp(CNG_OPTION_OPTIMAL, group_name, strlen(group_name)) ||
|
|
0 == strncasecmp(CNG_OPTION_QUERY, group_name, strlen(group_name)) ||
|
|
0 == strncasecmp(CNG_OPTION_INSTALLATION, group_name, strlen(group_name))) {
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ng_is_special_group
|
|
* check wheather a group is a spacial group whose bucket map will be disordered
|
|
*/
|
|
bool ng_is_special_group(Distribution* distribution)
|
|
{
|
|
/* Group built by optimizer is not special group */
|
|
if (InvalidOid == distribution->group_oid) {
|
|
return false;
|
|
}
|
|
|
|
/* Check 'in redistribution' group */
|
|
if (u_sess->opt_cxt.in_redistribution_group_distribution != NULL &&
|
|
u_sess->opt_cxt.in_redistribution_group_distribution->group_oid == distribution->group_oid) {
|
|
return true;
|
|
}
|
|
|
|
/* Check installation group */
|
|
Distribution* installation_distribution = ng_get_installation_group_distribution();
|
|
AssertEreport(NULL != installation_distribution, MOD_OPT, "");
|
|
if (installation_distribution->group_oid == distribution->group_oid) {
|
|
return true;
|
|
}
|
|
const char *group_parent = get_pgxc_groupparent(distribution->group_oid);
|
|
if (group_parent != NULL) {
|
|
Oid group_parent_oid = get_pgxc_groupoid(group_parent, false);
|
|
if ((u_sess->opt_cxt.in_redistribution_group_distribution != NULL &&
|
|
u_sess->opt_cxt.in_redistribution_group_distribution->group_oid == group_parent_oid) ||
|
|
installation_distribution->group_oid == group_parent_oid) {
|
|
return true;
|
|
}
|
|
}
|
|
/* Not a special group */
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* ng_is_same_group
|
|
* check wheather two node index bitmap set is same group
|
|
*/
|
|
bool ng_is_same_group(Bitmapset* bms_nodeids_1, Bitmapset* bms_nodeids_2)
|
|
{
|
|
return bms_equal(bms_nodeids_1, bms_nodeids_2);
|
|
}
|
|
|
|
/*
|
|
* ng_is_same_group
|
|
* check wheather two node index list is same group
|
|
*/
|
|
bool ng_is_same_group(List* nodeid_list_1, List* nodeid_list_2)
|
|
{
|
|
Bitmapset* bms_nodeids_1 = ng_convert_to_nodeids(nodeid_list_1);
|
|
Bitmapset* bms_nodeids_2 = ng_convert_to_nodeids(nodeid_list_2);
|
|
|
|
return ng_is_same_group(bms_nodeids_1, bms_nodeids_2);
|
|
}
|
|
|
|
/*
|
|
* ng_is_same_group
|
|
* check wheather ExecNodes (data nodes) and node index bitmap set are same group
|
|
*/
|
|
bool ng_is_same_group(ExecNodes* exec_nodes, Bitmapset* bms_nodeids)
|
|
{
|
|
return ng_is_same_group(exec_nodes->distribution.bms_data_nodeids, bms_nodeids);
|
|
}
|
|
|
|
/*
|
|
* ng_is_same_group
|
|
* check wheather two Distribution is same group
|
|
*/
|
|
bool ng_is_same_group(Distribution* distribution_1, Distribution* distribution_2)
|
|
{
|
|
int bucketlen1 = -1;
|
|
int bucketlen2 = -1;
|
|
/* Compare using group oid */
|
|
if (InvalidOid != distribution_1->group_oid && InvalidOid != distribution_2->group_oid &&
|
|
distribution_1->group_oid == distribution_2->group_oid) {
|
|
return true;
|
|
}
|
|
|
|
/* Check spacial node group (bucket map could be disordered) */
|
|
bool is_special_group_1 = ng_is_special_group(distribution_1);
|
|
bool is_special_group_2 = ng_is_special_group(distribution_2);
|
|
if (is_special_group_1 || is_special_group_2) {
|
|
return false;
|
|
}
|
|
if (InvalidOid != distribution_1->group_oid) {
|
|
(void)BucketMapCacheGetBucketmap(distribution_1->group_oid, &bucketlen1);
|
|
}
|
|
if (InvalidOid != distribution_2->group_oid) {
|
|
(void)BucketMapCacheGetBucketmap(distribution_2->group_oid, &bucketlen2);
|
|
}
|
|
/* Bucket map are all ordered? just compare data node index bitmap set */
|
|
return (bucketlen1 == bucketlen2) &&
|
|
ng_is_same_group(distribution_1->bms_data_nodeids, distribution_2->bms_data_nodeids);
|
|
}
|
|
|
|
/*
|
|
* ng_is_exec_on_subset_nodes
|
|
* check wheather one ExecNodes (exec nodes) is subset of another ExecNodes (exec nodes)
|
|
*/
|
|
bool ng_is_exec_on_subset_nodes(ExecNodes* en1, ExecNodes* en2)
|
|
{
|
|
Bitmapset* bms_en1 = ng_convert_to_nodeids(en1->nodeList);
|
|
Bitmapset* bms_en2 = ng_convert_to_nodeids(en2->nodeList);
|
|
|
|
return bms_is_subset(bms_en1, bms_en2);
|
|
}
|
|
|
|
/*
|
|
* ng_is_shuffle_needed
|
|
* check wheather need to shuffle between two node groups
|
|
*/
|
|
bool ng_is_shuffle_needed(Distribution* current_distribution, Distribution* target_distribution)
|
|
{
|
|
return !ng_is_same_group(current_distribution, target_distribution);
|
|
}
|
|
|
|
/*
|
|
* ng_is_shuffle_needed
|
|
* check wheather need to shuffle a path to a target node groups
|
|
*
|
|
* @param (in) root:
|
|
* the PlannerInfo
|
|
* @param (in) path:
|
|
* the path to be checked
|
|
* @param (in) target_distribution:
|
|
* the target node group
|
|
*
|
|
* @return:
|
|
* wheather shuffle is needed
|
|
*/
|
|
bool ng_is_shuffle_needed(PlannerInfo* root, Path* path, Distribution* target_distribution)
|
|
{
|
|
bool need_shuffle = false;
|
|
|
|
Distribution* current_distribution = ng_get_dest_distribution(path);
|
|
|
|
/*
|
|
* Check if exec nodes is not matching.
|
|
* for correlated subplan, destination node depends on the plan using the subplan,
|
|
* not default node group, we should have shuffled to a same group,
|
|
* even we will add broadcast node later
|
|
*/
|
|
if (ng_is_shuffle_needed(current_distribution, target_distribution)) {
|
|
/* Mark join rel needs shuffle */
|
|
need_shuffle = true;
|
|
}
|
|
|
|
return need_shuffle;
|
|
}
|
|
|
|
bool ng_is_distribute_key_valid(PlannerInfo* root, List* distribute_key, List* target_list)
|
|
{
|
|
if (distribute_key == NIL) {
|
|
return false;
|
|
}
|
|
|
|
ListCell* lc = NULL;
|
|
foreach (lc, distribute_key) {
|
|
Node* dkey = (Node*)lfirst(lc);
|
|
|
|
/* If we can not find distribute key in targetlist, further process it. */
|
|
if (find_node_in_targetlist(dkey, target_list) < 0) {
|
|
/* Try to find a qualify equal class. */
|
|
Node* equal_expr = find_qualify_equal_class(root, dkey, target_list);
|
|
if (NULL == equal_expr) {
|
|
if (IsA(dkey, Var)) {
|
|
return false;
|
|
}
|
|
|
|
/* search into expression to find each var(s) */
|
|
List* var_list =
|
|
pull_var_clause(dkey, PVC_RECURSE_AGGREGATES, PVC_INCLUDE_PLACEHOLDERS, PVC_RECURSE_SPECIAL_EXPR);
|
|
ListCell* lc_var = NULL;
|
|
foreach (lc_var, var_list) {
|
|
Node* dkey_var = (Node*)lfirst(lc_var);
|
|
|
|
if (!find_node_in_targetlist(dkey_var, target_list)) {
|
|
Node* equal_expr_var = find_qualify_equal_class(root, dkey_var, target_list);
|
|
if (NULL == equal_expr_var) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Other functions
|
|
*/
|
|
/*
|
|
* ng_get_single_node_group_nodeids
|
|
* get node index bitmap set of a single node group
|
|
*/
|
|
Bitmapset* ng_get_single_node_group_nodeids()
|
|
{
|
|
int nodeid = 0;
|
|
if (IS_PGXC_DATANODE) {
|
|
if (u_sess->pgxc_cxt.PGXCNodeId < 0)
|
|
return NULL;
|
|
else
|
|
nodeid = u_sess->pgxc_cxt.PGXCNodeId;
|
|
}
|
|
return bms_make_singleton(nodeid);
|
|
}
|
|
|
|
/*
|
|
* ng_get_single_node_group_distribution
|
|
* get Distribution of a single node group
|
|
*/
|
|
Distribution* ng_get_single_node_group_distribution()
|
|
{
|
|
Distribution* distribution = NewDistribution();
|
|
distribution->group_oid = InvalidOid;
|
|
distribution->bms_data_nodeids = ng_get_single_node_group_nodeids();
|
|
return distribution;
|
|
}
|
|
|
|
/*
|
|
* ng_get_single_node_group_exec_node
|
|
* get ExecNodes of a single node group
|
|
*/
|
|
ExecNodes* ng_get_single_node_group_exec_node()
|
|
{
|
|
Distribution* distribution = NewDistribution();
|
|
|
|
if (IS_PGXC_DATANODE) {
|
|
int nodeid = u_sess->pgxc_cxt.PGXCNodeId;
|
|
if (nodeid >= 0) {
|
|
distribution->bms_data_nodeids = bms_add_member(distribution->bms_data_nodeids, nodeid);
|
|
} else {
|
|
distribution->bms_data_nodeids = NULL;
|
|
}
|
|
} else if (IS_PGXC_COORDINATOR) {
|
|
distribution->bms_data_nodeids = ng_get_single_node_group_nodeids();
|
|
} else {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SQL_ROUTINE_EXCEPTION),
|
|
errmsg("[ng_get_single_dn_group_exec_node] unknown exec location.")));
|
|
}
|
|
|
|
distribution->group_oid = InvalidOid;
|
|
|
|
ExecNodes* exec_nodes = ng_convert_to_exec_nodes(distribution, LOCATOR_TYPE_REPLICATED, RELATION_ACCESS_READ);
|
|
|
|
return exec_nodes;
|
|
}
|
|
|
|
char* dist_to_str(Distribution* distribution)
|
|
{
|
|
StringInfo str = makeStringInfo();
|
|
|
|
appendStringInfo(str, "groupid(%u) nodeids(", distribution->group_oid);
|
|
|
|
_outBitmapset(str, distribution->bms_data_nodeids);
|
|
|
|
appendStringInfo(str, ")");
|
|
|
|
return str->data;
|
|
}
|
|
|
|
bool ng_is_single_node_group_distribution(Distribution* distribution)
|
|
{
|
|
Distribution* single_node_distribution = u_sess->opt_cxt.single_node_distribution;
|
|
if (single_node_distribution == NULL || distribution == NULL) {
|
|
return false;
|
|
}
|
|
|
|
if (single_node_distribution == distribution) {
|
|
return true;
|
|
}
|
|
|
|
return ng_is_same_group(single_node_distribution, distribution);
|
|
}
|
|
|
|
int ng_get_different_nodegroup_count()
|
|
{
|
|
return u_sess->opt_cxt.different_nodegroup_count;
|
|
}
|
|
|
|
static int ngroup_hash_partition_id(int id)
|
|
{
|
|
return id % NUM_NGROUP_INFO_PARTITIONS;
|
|
}
|
|
|
|
static LWLock *ngroup_mapping_partitionlock(int hashcode)
|
|
{
|
|
int id = FirstNGroupMappingLock + ngroup_hash_partition_id(hashcode);
|
|
return &t_thrd.shemem_ptr_cxt.mainLWLockArray[id].lock;
|
|
}
|
|
|
|
void ngroup_info_hash_create()
|
|
{
|
|
const int num_hash_elems = 1024;
|
|
HASHCTL hash_ctl;
|
|
|
|
errno_t rc = memset_s(&hash_ctl, sizeof(hash_ctl), 0, sizeof(hash_ctl));
|
|
securec_check(rc, "", "");
|
|
hash_ctl.keysize = sizeof(Oid);
|
|
hash_ctl.entrysize = sizeof(NGroupInfo);
|
|
hash_ctl.hash = oid_hash;
|
|
hash_ctl.hcxt = g_instance.cache_cxt.global_cache_mem;
|
|
hash_ctl.num_partitions = NUM_NGROUP_INFO_PARTITIONS;
|
|
|
|
int flags = HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT | HASH_EXTERN_CONTEXT | HASH_NOEXCEPT | HASH_PARTITION;
|
|
g_instance.ngroup_hash_table = hash_create("ngroup cache hash", num_hash_elems, &hash_ctl, flags);
|
|
|
|
if (g_instance.ngroup_hash_table == NULL) {
|
|
ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY),
|
|
errmsg("nodegroup hash create failed")));
|
|
}
|
|
}
|
|
|
|
Bitmapset *ngroup_info_hash_search(Oid ngroup_oid)
|
|
{
|
|
Bitmapset *bms_nodeids = NULL;
|
|
bool found = false;
|
|
|
|
if (InvalidOid == ngroup_oid) {
|
|
List *nodeid_list = GetAllDataNodes();
|
|
bms_nodeids = ng_convert_to_nodeids(nodeid_list);
|
|
list_free(nodeid_list);
|
|
return bms_nodeids;
|
|
}
|
|
|
|
uint32 hashcode = oid_hash((const void *)&ngroup_oid, sizeof(ngroup_oid));
|
|
LWLock *new_partition_lock = ngroup_mapping_partitionlock(hashcode);
|
|
(void)LWLockAcquire(NgroupDestoryLock, LW_SHARED);
|
|
(void)LWLockAcquire(new_partition_lock, LW_SHARED);
|
|
NGroupInfo *ngroup_info = (NGroupInfo *)hash_search(g_instance.ngroup_hash_table, &ngroup_oid, HASH_FIND, &found);
|
|
|
|
if (found) {
|
|
/* the memory is release by caller */
|
|
bms_nodeids = bms_copy(ngroup_info->bms_nodeids);
|
|
}
|
|
LWLockRelease(new_partition_lock);
|
|
LWLockRelease(NgroupDestoryLock);
|
|
|
|
return bms_nodeids;
|
|
}
|
|
|
|
void ngroup_info_hash_insert(Oid ngroup_oid, Bitmapset *bms_node_ids)
|
|
{
|
|
bool found = false;
|
|
uint32 hashcode = oid_hash((const void *)&ngroup_oid, sizeof(ngroup_oid));
|
|
LWLock *new_partition_lock = ngroup_mapping_partitionlock(hashcode);
|
|
Bitmapset *bms_node_ids_copy = NULL;
|
|
|
|
MemoryContext old_mem_context = MemoryContextSwitchTo(g_instance.ngroup_hash_table->hcxt);
|
|
bms_node_ids_copy = bms_copy(bms_node_ids);
|
|
MemoryContextSwitchTo(old_mem_context);
|
|
(void)LWLockAcquire(NgroupDestoryLock, LW_SHARED);
|
|
(void)LWLockAcquire(new_partition_lock, LW_EXCLUSIVE);
|
|
NGroupInfo *ngroup_info = (NGroupInfo *)hash_search(g_instance.ngroup_hash_table, &ngroup_oid, HASH_ENTER, &found);
|
|
|
|
if (ngroup_info) {
|
|
ngroup_info->oid = ngroup_oid;
|
|
ngroup_info->bms_nodeids = bms_node_ids_copy;
|
|
} else {
|
|
LWLockRelease(new_partition_lock);
|
|
LWLockRelease(NgroupDestoryLock);
|
|
pfree(bms_node_ids_copy);
|
|
ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY),
|
|
errmsg("failed to insert node group hash table")));
|
|
}
|
|
LWLockRelease(new_partition_lock);
|
|
LWLockRelease(NgroupDestoryLock);
|
|
}
|
|
|
|
void ngroup_info_hash_delete(Oid ngroup_oid, bool is_destory)
|
|
{
|
|
if (InvalidOid == ngroup_oid) {
|
|
ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NODE_STATE),
|
|
errmsg("NodeGroup Oid is invalid, invalid Oid is %u.", ngroup_oid)));
|
|
}
|
|
bool found = false;
|
|
Bitmapset *bms_ptr = NULL;
|
|
uint32 hashcode = oid_hash((const void *)&ngroup_oid, sizeof(ngroup_oid));
|
|
LWLock *new_partition_lock = ngroup_mapping_partitionlock(hashcode);
|
|
if (!is_destory) {
|
|
(void)LWLockAcquire(NgroupDestoryLock, LW_SHARED);
|
|
}
|
|
(void)LWLockAcquire(new_partition_lock, LW_EXCLUSIVE);
|
|
NGroupInfo *ngroup_info = (NGroupInfo *)hash_search(g_instance.ngroup_hash_table, &ngroup_oid, HASH_FIND, &found);
|
|
|
|
if (ngroup_info)
|
|
bms_ptr = ngroup_info->bms_nodeids;
|
|
|
|
hash_search(g_instance.ngroup_hash_table, &ngroup_oid, HASH_REMOVE, &found);
|
|
LWLockRelease(new_partition_lock);
|
|
if (!is_destory) {
|
|
LWLockRelease(NgroupDestoryLock);
|
|
}
|
|
if (!found && ngroup_info) {
|
|
if (is_destory) {
|
|
LWLockRelease(NgroupDestoryLock);
|
|
}
|
|
ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NODE_STATE),
|
|
errmsg("delete failed from nodegroup hash table, Oid is %u.", ngroup_oid)));
|
|
}
|
|
if (bms_ptr)
|
|
pfree(bms_ptr);
|
|
}
|
|
|
|
void ngroup_info_hash_destory(void)
|
|
{
|
|
HASH_SEQ_STATUS hash_seq;
|
|
NGroupInfo* entry = NULL;
|
|
|
|
(void)LWLockAcquire(NgroupDestoryLock, LW_EXCLUSIVE);
|
|
|
|
hash_seq_init(&hash_seq, g_instance.ngroup_hash_table);
|
|
while ((entry = (NGroupInfo*)hash_seq_search(&hash_seq)) != NULL) {
|
|
ereport(LOG, (errmsg(" ngroup_info_hash_print ngroup_info_hash__delete_all entry->oid: %d ", entry->oid)));
|
|
ngroup_info_hash_delete(entry->oid, true);
|
|
}
|
|
LWLockRelease(NgroupDestoryLock);
|
|
}
|
|
|
|
|