forked from openGauss-Ecosystem/openGauss-server
pgfdw support join\agg\sort\limit\lockrows
This commit is contained in:
parent
0d771b4357
commit
f995bd2209
|
@ -106,8 +106,8 @@ PG_FUNCTION_INFO_V1(file_fdw_validator);
|
|||
*/
|
||||
static void fileGetForeignRelSize(PlannerInfo* root, RelOptInfo* baserel, Oid foreigntableid);
|
||||
static void fileGetForeignPaths(PlannerInfo* root, RelOptInfo* baserel, Oid foreigntableid);
|
||||
static ForeignScan* fileGetForeignPlan(PlannerInfo* root, RelOptInfo* baserel, Oid foreigntableid,
|
||||
ForeignPath* best_path, List* tlist, List* scan_clauses);
|
||||
static ForeignScan *fileGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid,
|
||||
ForeignPath *best_path, List *tlist, List *scan_clauses, Plan *outer_plan);
|
||||
static void fileExplainForeignScan(ForeignScanState* node, ExplainState* es);
|
||||
static void fileBeginForeignScan(ForeignScanState* node, int eflags);
|
||||
static TupleTableSlot* fileIterateForeignScan(ForeignScanState* node);
|
||||
|
@ -452,8 +452,9 @@ static void fileGetForeignPaths(PlannerInfo* root, RelOptInfo* baserel, Oid fore
|
|||
startup_cost,
|
||||
total_cost,
|
||||
NIL, /* no pathkeys */
|
||||
NULL, /* no outer rel either */
|
||||
NIL)); /* no fdw_private data */
|
||||
NULL, /* no outer rel either */
|
||||
NULL, /* no outer path either */
|
||||
NIL)); /* no fdw_private data */
|
||||
|
||||
/*
|
||||
* If data file was sorted, and we knew it somehow, we could insert
|
||||
|
@ -464,10 +465,10 @@ static void fileGetForeignPaths(PlannerInfo* root, RelOptInfo* baserel, Oid fore
|
|||
|
||||
/*
|
||||
* fileGetForeignPlan
|
||||
* Create a ForeignScan plan node for scanning the foreign table
|
||||
* Create a ForeignScan plan node for scanning the foreign table
|
||||
*/
|
||||
static ForeignScan* fileGetForeignPlan(
|
||||
PlannerInfo* root, RelOptInfo* baserel, Oid foreigntableid, ForeignPath* best_path, List* tlist, List* scan_clauses)
|
||||
static ForeignScan *fileGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid,
|
||||
ForeignPath *best_path, List *tlist, List *scan_clauses, Plan *outer_plan)
|
||||
{
|
||||
Index scan_relid = baserel->relid;
|
||||
|
||||
|
@ -485,8 +486,11 @@ static ForeignScan* fileGetForeignPlan(
|
|||
scan_clauses,
|
||||
scan_relid,
|
||||
NIL, /* no expressions to evaluate */
|
||||
NIL, /* no private state either */
|
||||
NIL,
|
||||
EXEC_ON_DATANODES); /* no private state either */
|
||||
NIL,
|
||||
NULL,
|
||||
EXEC_ON_DATANODES);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -448,8 +448,9 @@ static void gcGetForeignPaths(PlannerInfo* root, RelOptInfo* baserel, Oid foreig
|
|||
fpinfo->startup_cost,
|
||||
fpinfo->total_cost,
|
||||
NIL, /* no pathkeys */
|
||||
NULL, /* no outer rel either */
|
||||
NIL, /* no fdw_private list */
|
||||
NULL, /* no outer rel either */
|
||||
NULL, /* no outer path either */
|
||||
NIL, /* no fdw_private list */
|
||||
u_sess->opt_cxt.query_dop);
|
||||
add_path(root, baserel, (Path*)path);
|
||||
}
|
||||
|
@ -459,7 +460,7 @@ static void gcGetForeignPaths(PlannerInfo* root, RelOptInfo* baserel, Oid foreig
|
|||
* Create ForeignScan plan node which implements selected best path
|
||||
*/
|
||||
static ForeignScan* gcGetForeignPlan(PlannerInfo* root, RelOptInfo* foreignrel, Oid foreigntableid,
|
||||
ForeignPath* best_path, List* tlist, List* scan_clauses)
|
||||
ForeignPath* best_path, List* tlist, List* scan_clauses, Plan* outer_plan)
|
||||
{
|
||||
GcFdwRelationInfo* fpinfo = (GcFdwRelationInfo*)foreignrel->fdw_private;
|
||||
Index scan_relid;
|
||||
|
@ -617,7 +618,8 @@ static ForeignScan* gcGetForeignPlan(PlannerInfo* root, RelOptInfo* foreignrel,
|
|||
* field of the finished plan node; we can't keep them in private state
|
||||
* because then they wouldn't be subject to later planner processing.
|
||||
*/
|
||||
return make_foreignscan(tlist, local_exprs, scan_relid, params_list, fdw_private, EXEC_ON_DATANODES);
|
||||
return make_foreignscan(tlist, local_exprs, scan_relid, params_list, fdw_private, NIL, NIL, NULL,
|
||||
EXEC_ON_DATANODES);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -72,12 +72,8 @@ static void gcGetForeignPaths(PlannerInfo* root, RelOptInfo* baserel, Oid foreig
|
|||
return;
|
||||
}
|
||||
|
||||
static ForeignScan *gcGetForeignPlan(PlannerInfo *root,
|
||||
RelOptInfo *baserel,
|
||||
Oid foreigntableid,
|
||||
ForeignPath *best_path,
|
||||
List *tlist,
|
||||
List *scan_clauses)
|
||||
static ForeignScan *gcGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid,
|
||||
ForeignPath *best_path, List *tlist, List *scan_clauses, Plan *outer_plan)
|
||||
{
|
||||
DISTRIBUTED_FEATURE_NOT_SUPPORTED();
|
||||
return NULL;
|
||||
|
|
|
@ -2722,8 +2722,8 @@ static void log_rescan_foreign_scan(ForeignScanState* node)
|
|||
(void)MemoryContextSwitchTo(old_memcnxt);
|
||||
}
|
||||
|
||||
static ForeignScan* log_get_foreign_plan(
|
||||
PlannerInfo* root, RelOptInfo* baserel, Oid foreigntableid, ForeignPath* best_path, List* tlist, List* scan_clauses)
|
||||
static ForeignScan* log_get_foreign_plan(PlannerInfo* root, RelOptInfo* baserel, Oid foreigntableid,
|
||||
ForeignPath* best_path, List* tlist, List* scan_clauses, Plan* outer_plan)
|
||||
{
|
||||
Index scan_relid = baserel->relid;
|
||||
List* fdw_data = best_path->fdw_private;
|
||||
|
@ -2773,6 +2773,9 @@ static ForeignScan* log_get_foreign_plan(
|
|||
scan_relid,
|
||||
NIL, /* no expressions to evaluate */
|
||||
fdw_data,
|
||||
NIL,
|
||||
NIL,
|
||||
NULL,
|
||||
EXEC_ON_DATANODES); /* no private state either */
|
||||
|
||||
return fscan;
|
||||
|
@ -2796,6 +2799,7 @@ static void log_get_foreign_paths(PlannerInfo* root, RelOptInfo* baserel, Oid fo
|
|||
total_cost,
|
||||
NIL, /* no pathkeys */
|
||||
NULL, /* no outer rel either */
|
||||
NULL, /* no outer path either */
|
||||
NIL,
|
||||
1)); /* no fdw_private data */
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# contrib/postgres_fdw/Makefile
|
||||
|
||||
MODULE_big = postgres_fdw
|
||||
OBJS = postgres_fdw.o option.o deparse.o connection.o
|
||||
OBJS = postgres_fdw.o option.o deparse.o connection.o internal_interface.o
|
||||
|
||||
PG_CPPFLAGS = -I$(libpq_srcdir)
|
||||
SHLIB_LINK_INTERNAL = $(libpq)
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#include "utils/syscache.h"
|
||||
#include "utils/timestamp.h"
|
||||
#include "storage/ipc.h"
|
||||
|
||||
#include "executor/executor.h"
|
||||
|
||||
/*
|
||||
* Connection cache hash table entry
|
||||
|
@ -243,6 +243,39 @@ PGconn *GetConnection(ForeignServer *server, UserMapping *user, bool will_prep_s
|
|||
return entry->conn;
|
||||
}
|
||||
|
||||
PGconn* GetConnectionByFScanState(ForeignScanState* node, bool will_prep_stmt)
|
||||
{
|
||||
ForeignScan* fsplan = (ForeignScan *)node->ss.ps.plan;
|
||||
EState* estate = node->ss.ps.state;
|
||||
RangeTblEntry* rte = NULL;
|
||||
Oid userid;
|
||||
ForeignTable* table = NULL;
|
||||
ForeignServer* server = NULL;
|
||||
UserMapping* user = NULL;
|
||||
int rtindex;
|
||||
|
||||
/*
|
||||
* Identify which user to do the remote access as. This should match what
|
||||
* ExecCheckRTEPerms() does. In case of a join or aggregate, use the
|
||||
* lowest-numbered member RTE as a representative; we would get the same
|
||||
* result from any.
|
||||
*/
|
||||
if (fsplan->scan.scanrelid > 0) {
|
||||
rtindex = fsplan->scan.scanrelid;
|
||||
} else {
|
||||
rtindex = bms_next_member(fsplan->fs_relids, -1);
|
||||
}
|
||||
rte = exec_rt_fetch(rtindex, estate);
|
||||
userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
|
||||
|
||||
/* Get info about foreign table. */
|
||||
table = GetForeignTable(rte->relid);
|
||||
server = GetForeignServer(table->serverid);
|
||||
user = GetUserMapping(userid, table->serverid);
|
||||
|
||||
return GetConnection(server, user, will_prep_stmt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Connect to remote server using specified server and user mapping properties.
|
||||
*/
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,663 @@
|
|||
/* -------------------------------------------------------------------------
|
||||
*
|
||||
* postgres_fdw.c
|
||||
* Foreign-data wrapper for remote openGauss servers
|
||||
*
|
||||
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
||||
* Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* contrib/postgres_fdw/postgres_fdw.c
|
||||
*
|
||||
* -------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef FRONTEND
|
||||
|
||||
#include "postgres.h"
|
||||
#include "utils/memutils.h"
|
||||
#else
|
||||
#include "postgres_fe.h"
|
||||
/* It's possible we could use a different value for this in frontend code */
|
||||
#define MaxAllocSize ((Size)0x3fffffff) /* 1 gigabyte - 1 */
|
||||
#endif
|
||||
|
||||
#include "internal_interface.h"
|
||||
#include "optimizer/pathnode.h"
|
||||
#include "optimizer/cost.h"
|
||||
#include "optimizer/planmain.h"
|
||||
|
||||
/*
|
||||
* Get a copy of an existing local path for a given join relation.
|
||||
*
|
||||
* This function is usually helpful to obtain an alternate local path for EPQ
|
||||
* checks.
|
||||
*
|
||||
* Right now, this function only supports unparameterized foreign joins, so we
|
||||
* only search for unparameterized path in the given list of paths. Since we
|
||||
* are searching for a path which can be used to construct an alternative local
|
||||
* plan for a foreign join, we look for only MergeJoin, HashJoin or NestLoop
|
||||
* paths.
|
||||
*
|
||||
* If the inner or outer subpath of the chosen path is a ForeignScan, we
|
||||
* replace it with its outer subpath. For this reason, and also because the
|
||||
* planner might free the original path later, the path returned by this
|
||||
* function is a shallow copy of the original. There's no need to copy
|
||||
* the substructure, so we don't.
|
||||
*
|
||||
* Since the plan created using this path will presumably only be used to
|
||||
* execute EPQ checks, efficiency of the path is not a concern. But since the
|
||||
* path list in RelOptInfo is anyway sorted by total cost we are likely to
|
||||
* choose the most efficient path, which is all for the best.
|
||||
*/
|
||||
Path *GetExistingLocalJoinPath(RelOptInfo *joinrel)
|
||||
{
|
||||
ListCell *lc = NULL;
|
||||
errno_t rc;
|
||||
|
||||
Assert(IS_JOIN_REL(joinrel));
|
||||
|
||||
foreach (lc, joinrel->pathlist) {
|
||||
Path *path = (Path *)lfirst(lc);
|
||||
JoinPath *joinpath = NULL;
|
||||
|
||||
/* Skip parameterized paths. */
|
||||
if (path->param_info != NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (path->pathtype) {
|
||||
case T_HashJoin: {
|
||||
HashPath *hash_path = makeNode(HashPath);
|
||||
rc = memcpy_s(hash_path, sizeof(HashPath), path, sizeof(HashPath));
|
||||
securec_check(rc, "\0", "\0");
|
||||
joinpath = (JoinPath *)hash_path;
|
||||
} break;
|
||||
|
||||
case T_NestLoop: {
|
||||
NestPath *nest_path = makeNode(NestPath);
|
||||
rc = memcpy_s(nest_path, sizeof(NestPath), path, sizeof(NestPath));
|
||||
securec_check(rc, "\0", "\0");
|
||||
joinpath = (JoinPath *)nest_path;
|
||||
} break;
|
||||
|
||||
case T_MergeJoin: {
|
||||
MergePath *merge_path = makeNode(MergePath);
|
||||
rc = memcpy_s(merge_path, sizeof(MergePath), path, sizeof(MergePath));
|
||||
securec_check(rc, "\0", "\0");
|
||||
joinpath = (JoinPath *)merge_path;
|
||||
} break;
|
||||
|
||||
default:
|
||||
|
||||
/*
|
||||
* Just skip anything else. We don't know if corresponding
|
||||
* plan would build the output row from whole-row references
|
||||
* of base relations and execute the EPQ checks.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
|
||||
/* This path isn't good for us, check next. */
|
||||
if (!joinpath) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* If either inner or outer path is a ForeignPath corresponding to a
|
||||
* pushed down join, replace it with the fdw_outerpath, so that we
|
||||
* maintain path for EPQ checks built entirely of local join
|
||||
* strategies.
|
||||
*/
|
||||
if (IsA(joinpath->outerjoinpath, ForeignPath)) {
|
||||
ForeignPath *foreign_path;
|
||||
|
||||
foreign_path = (ForeignPath *)joinpath->outerjoinpath;
|
||||
if (IS_JOIN_REL(foreign_path->path.parent)) {
|
||||
joinpath->outerjoinpath = foreign_path->fdw_outerpath;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsA(joinpath->innerjoinpath, ForeignPath)) {
|
||||
ForeignPath *foreign_path;
|
||||
|
||||
foreign_path = (ForeignPath *)joinpath->innerjoinpath;
|
||||
if (IS_JOIN_REL(foreign_path->path.parent)) {
|
||||
joinpath->innerjoinpath = foreign_path->fdw_outerpath;
|
||||
}
|
||||
}
|
||||
|
||||
return (Path *)joinpath;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* psprintf
|
||||
*
|
||||
* Format text data under the control of fmt (an sprintf-style format string)
|
||||
* and return it in an allocated-on-demand buffer. The buffer is allocated
|
||||
* with palloc in the backend, or malloc in frontend builds. Caller is
|
||||
* responsible to free the buffer when no longer needed, if appropriate.
|
||||
*
|
||||
* Errors are not returned to the caller, but are reported via elog(ERROR)
|
||||
* in the backend, or printf-to-stderr-and-exit() in frontend builds.
|
||||
* One should therefore think twice about using this in libpq.
|
||||
*/
|
||||
char *psprintf(const char *fmt, ...)
|
||||
{
|
||||
int save_errno = errno;
|
||||
size_t len = 128; /* initial assumption about buffer size */
|
||||
|
||||
for (;;) {
|
||||
char *result = NULL;
|
||||
va_list args;
|
||||
size_t newlen;
|
||||
|
||||
/*
|
||||
* Allocate result buffer. Note that in frontend this maps to malloc
|
||||
* with exit-on-error.
|
||||
*/
|
||||
result = (char *)palloc(len);
|
||||
|
||||
/* Try to format the data. */
|
||||
errno = save_errno;
|
||||
va_start(args, fmt);
|
||||
newlen = pvsnprintf(result, len, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (newlen < len) {
|
||||
return result; /* success */
|
||||
}
|
||||
|
||||
/* Release buffer and loop around to try again with larger len. */
|
||||
pfree(result);
|
||||
len = newlen;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* pvsnprintf
|
||||
*
|
||||
* Attempt to format text data under the control of fmt (an sprintf-style
|
||||
* format string) and insert it into buf (which has length len).
|
||||
*
|
||||
* If successful, return the number of bytes emitted, not counting the
|
||||
* trailing zero byte. This will always be strictly less than len.
|
||||
*
|
||||
* If there's not enough space in buf, return an estimate of the buffer size
|
||||
* needed to succeed (this *must* be more than the given len, else callers
|
||||
* might loop infinitely).
|
||||
*
|
||||
* Other error cases do not return, but exit via elog(ERROR) or exit().
|
||||
* Hence, this shouldn't be used inside libpq.
|
||||
*
|
||||
* Caution: callers must be sure to preserve their entry-time errno
|
||||
* when looping, in case the fmt contains "%m".
|
||||
*
|
||||
* Note that the semantics of the return value are not exactly C99's.
|
||||
* First, we don't promise that the estimated buffer size is exactly right;
|
||||
* callers must be prepared to loop multiple times to get the right size.
|
||||
* (Given a C99-compliant vsnprintf, that won't happen, but it is rumored
|
||||
* that some implementations don't always return the same value ...)
|
||||
* Second, we return the recommended buffer size, not one less than that;
|
||||
* this lets overflow concerns be handled here rather than in the callers.
|
||||
*/
|
||||
size_t pvsnprintf(char *buf, size_t len, const char *fmt, va_list args)
|
||||
{
|
||||
int nprinted = 0;
|
||||
|
||||
nprinted = vsnprintf_s(buf, len, len, fmt, args);
|
||||
securec_check_ss(nprinted, "\0", "\0");
|
||||
|
||||
/* We assume failure means the fmt is bogus, hence hard failure is OK */
|
||||
if (unlikely(nprinted < 0)) {
|
||||
#ifndef FRONTEND
|
||||
elog(ERROR, "vsnprintf failed: %m with format string \"%s\"", fmt);
|
||||
#else
|
||||
fprintf(stderr, "vsnprintf failed: %s with format string \"%s\"\n", strerror(errno), fmt);
|
||||
exit(EXIT_FAILURE);
|
||||
#endif
|
||||
}
|
||||
|
||||
if ((size_t)nprinted < len) {
|
||||
/* Success. Note nprinted does not include trailing null. */
|
||||
return (size_t)nprinted;
|
||||
}
|
||||
|
||||
/*
|
||||
* We assume a C99-compliant vsnprintf, so believe its estimate of the
|
||||
* required space, and add one for the trailing null. (If it's wrong, the
|
||||
* logic will still work, but we may loop multiple times.)
|
||||
*
|
||||
* Choke if the required space would exceed MaxAllocSize. Note we use
|
||||
* this palloc-oriented overflow limit even when in frontend.
|
||||
*/
|
||||
if (unlikely((size_t)nprinted > MaxAllocSize - 1)) {
|
||||
#ifndef FRONTEND
|
||||
ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("out of memory")));
|
||||
#else
|
||||
fprintf(stderr, _("out of memory\n"));
|
||||
exit(EXIT_FAILURE);
|
||||
#endif
|
||||
}
|
||||
|
||||
return nprinted + 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* create_foreign_join_path
|
||||
* Creates a path corresponding to a scan of a foreign join,
|
||||
* returning the pathnode.
|
||||
*
|
||||
* This function is never called from core Postgres; rather, it's expected
|
||||
* to be called by the GetForeignJoinPaths function of a foreign data wrapper.
|
||||
* We make the FDW supply all fields of the path, since we do not have any way
|
||||
* to calculate them in core. However, there is a usually-sane default for
|
||||
* the pathtarget (rel->reltarget), so we let a NULL for "target" select that.
|
||||
*/
|
||||
ForeignPath *create_foreign_join_path(PlannerInfo *root, RelOptInfo *rel, List *target, double rows,
|
||||
Cost startup_cost, Cost total_cost, List *pathkeys, Relids required_outer, Path *fdw_outerpath, List *fdw_private)
|
||||
{
|
||||
ForeignPath *pathnode = makeNode(ForeignPath);
|
||||
|
||||
/*
|
||||
* We should use get_joinrel_parampathinfo to handle parameterized paths,
|
||||
* but the API of this function doesn't support it, and existing
|
||||
* extensions aren't yet trying to build such paths anyway. For the
|
||||
* moment just throw an error if someone tries it; eventually we should
|
||||
* revisit this.
|
||||
*/
|
||||
if (!bms_is_empty(required_outer) || !bms_is_empty(rel->lateral_relids)) {
|
||||
elog(ERROR, "parameterized foreign joins are not supported yet");
|
||||
}
|
||||
|
||||
pathnode->path.pathtype = T_ForeignScan;
|
||||
pathnode->path.parent = rel;
|
||||
pathnode->path.param_info = NULL; /* XXX see above */
|
||||
pathnode->path.rows = rows;
|
||||
pathnode->path.startup_cost = startup_cost;
|
||||
pathnode->path.total_cost = total_cost;
|
||||
pathnode->path.pathkeys = pathkeys;
|
||||
pathnode->path.dop = 1;
|
||||
|
||||
pathnode->fdw_outerpath = fdw_outerpath;
|
||||
pathnode->fdw_private = fdw_private;
|
||||
|
||||
return pathnode;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call ExecInitExpr() on a list of expressions, return a list of ExprStates.
|
||||
*/
|
||||
List *ExecInitExprList(List *nodes, PlanState *parent)
|
||||
{
|
||||
List *result = NIL;
|
||||
ListCell *lc = NULL;
|
||||
|
||||
foreach (lc, nodes) {
|
||||
Expr *e = (Expr *)lfirst(lc);
|
||||
result = lappend(result, ExecInitExpr(e, parent));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* get_namespace_name_or_temp
|
||||
* As above, but if it is this backend's temporary namespace, return
|
||||
* "pg_temp" instead.
|
||||
*/
|
||||
char *get_namespace_name_or_temp(Oid nspid)
|
||||
{
|
||||
if (isTempNamespace(nspid)) {
|
||||
return pstrdup("pg_temp");
|
||||
} else {
|
||||
return get_namespace_name(nspid);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* fetch_upper_rel
|
||||
* Build a RelOptInfo describing some post-scan/join query processing,
|
||||
* or return a pre-existing one if somebody already built it.
|
||||
*
|
||||
* An "upper" relation is identified by an UpperRelationKind and a Relids set.
|
||||
* The meaning of the Relids set is not specified here, and very likely will
|
||||
* vary for different relation kinds.
|
||||
*
|
||||
* Most of the fields in an upper-level RelOptInfo are not used and are not
|
||||
* set here (though makeNode should ensure they're zeroes). We basically only
|
||||
* care about fields that are of interest to add_path() and set_cheapest().
|
||||
*/
|
||||
RelOptInfo *fetch_upper_rel(FDWUpperRelCxt *ufdwCxt, UpperRelationKind kind)
|
||||
{
|
||||
/*
|
||||
* For the moment, our indexing data structure is just a List for each
|
||||
* relation kind. If we ever get so many of one kind that this stops
|
||||
* working well, we can improve it. No code outside this function should
|
||||
* assume anything about how to find a particular upperrel.
|
||||
*/
|
||||
if (ufdwCxt->upperRels[kind] != NULL) {
|
||||
return ufdwCxt->upperRels[kind];
|
||||
}
|
||||
|
||||
RelOptInfo *upperrel = makeNode(RelOptInfo);
|
||||
upperrel->reloptkind = RELOPT_UPPER_REL;
|
||||
upperrel->relids = NULL;
|
||||
|
||||
upperrel->reltargetlist = NIL;
|
||||
upperrel->pathlist = NIL;
|
||||
upperrel->cheapest_startup_path = NULL;
|
||||
upperrel->cheapest_total_path = NULL;
|
||||
upperrel->cheapest_unique_path = NULL;
|
||||
upperrel->cheapest_parameterized_paths = NIL;
|
||||
|
||||
ufdwCxt->upperRels[kind] = upperrel;
|
||||
|
||||
return upperrel;
|
||||
}
|
||||
|
||||
List* extract_target_from_tel(FDWUpperRelCxt *ufdw_cxt, PgFdwRelationInfo *fpinfo)
|
||||
{
|
||||
List* tel = NIL;
|
||||
switch (fpinfo->stage) {
|
||||
case UPPERREL_INIT:
|
||||
tel = ufdw_cxt->spjExtra->targetList;
|
||||
break;
|
||||
case UPPERREL_GROUP_AGG:
|
||||
tel = ufdw_cxt->groupExtra->targetList;
|
||||
break;
|
||||
case UPPERREL_ORDERED:
|
||||
tel = ufdw_cxt->orderExtra->targetList;
|
||||
break;
|
||||
case UPPERREL_FINAL:
|
||||
tel = ufdw_cxt->finalExtra->targetList;
|
||||
break;
|
||||
default:
|
||||
Assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
fpinfo->complete_tlist = tel;
|
||||
|
||||
List* tl = NULL;
|
||||
ListCell* lc = NULL;
|
||||
foreach(lc, tel) {
|
||||
Assert(IsA(lfirst(lc), TargetEntry));
|
||||
TargetEntry* te = (TargetEntry*)lfirst(lc);
|
||||
|
||||
tl = lappend(tl, copyObject(te->expr));
|
||||
}
|
||||
|
||||
return tl;
|
||||
}
|
||||
|
||||
/*
|
||||
* make_upper_rel
|
||||
*
|
||||
* Create a new grouping rel and set basic properties.
|
||||
*
|
||||
* input_rel represents the underlying scan/join relation.
|
||||
* target is the output expected from the grouping relation.
|
||||
*/
|
||||
RelOptInfo *make_upper_rel(FDWUpperRelCxt *ufdwCxt, PgFdwRelationInfo *fpinfo)
|
||||
{
|
||||
RelOptInfo *upper_rel = fetch_upper_rel(ufdwCxt, fpinfo->stage);
|
||||
|
||||
/* Set target. */
|
||||
upper_rel->reltargetlist = extract_target_from_tel(ufdwCxt, fpinfo);
|
||||
|
||||
/*
|
||||
* If the input rel belongs to a single FDW, so does the grouped rel.
|
||||
*/
|
||||
upper_rel->serverid = ufdwCxt->currentRel->serverid;
|
||||
upper_rel->userid = ufdwCxt->currentRel->userid;
|
||||
upper_rel->useridiscurrent = ufdwCxt->currentRel->useridiscurrent;
|
||||
upper_rel->fdwroutine = ufdwCxt->currentRel->fdwroutine;
|
||||
upper_rel->fdw_private = fpinfo;
|
||||
|
||||
return upper_rel;
|
||||
}
|
||||
|
||||
/*
|
||||
* get_sortgroupref_clause_noerr
|
||||
* As above, but return NULL rather than throwing an error if not found.
|
||||
*/
|
||||
SortGroupClause *get_sortgroupref_clause_noerr(Index sortref, List *clauses)
|
||||
{
|
||||
ListCell *l = NULL;
|
||||
|
||||
foreach (l, clauses) {
|
||||
SortGroupClause *cl = (SortGroupClause *)lfirst(l);
|
||||
|
||||
if (cl->tleSortGroupRef == sortref) {
|
||||
return cl;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* create_foreign_upper_path
|
||||
* Creates a path corresponding to an upper relation that's computed
|
||||
* directly by an FDW, returning the pathnode.
|
||||
*
|
||||
* This function is never called from core Postgres; rather, it's expected to
|
||||
* be called by the GetForeignUpperPaths function of a foreign data wrapper.
|
||||
* We make the FDW supply all fields of the path, since we do not have any way
|
||||
* to calculate them in core. However, there is a usually-sane default for
|
||||
* the pathtarget (rel->reltarget), so we let a NULL for "target" select that.
|
||||
*/
|
||||
ForeignPath *create_foreign_upper_path(PlannerInfo *root, RelOptInfo *rel, List *target, double rows,
|
||||
Cost startup_cost, Cost total_cost, List *pathkeys, Path *fdw_outerpath, List *fdw_private)
|
||||
{
|
||||
ForeignPath *pathnode = makeNode(ForeignPath);
|
||||
|
||||
/*
|
||||
* Upper relations should never have any lateral references, since joining
|
||||
* is complete.
|
||||
*/
|
||||
Assert(bms_is_empty(rel->lateral_relids));
|
||||
|
||||
pathnode->path.pathtype = T_ForeignScan;
|
||||
pathnode->path.parent = rel;
|
||||
pathnode->path.param_info = NULL;
|
||||
pathnode->path.rows = rows;
|
||||
pathnode->path.startup_cost = startup_cost;
|
||||
pathnode->path.total_cost = total_cost;
|
||||
pathnode->path.pathkeys = pathkeys;
|
||||
pathnode->path.dop = 1;
|
||||
|
||||
pathnode->fdw_outerpath = fdw_outerpath;
|
||||
pathnode->fdw_private = fdw_private;
|
||||
|
||||
return pathnode;
|
||||
}
|
||||
|
||||
/*
|
||||
* adjust_limit_rows_costs
|
||||
* Adjust the size and cost estimates for a LimitPath node according to the
|
||||
* offset/limit.
|
||||
*
|
||||
* This is only a cosmetic issue if we are at top level, but if we are
|
||||
* building a subquery then it's important to report correct info to the outer
|
||||
* planner.
|
||||
*
|
||||
* When the offset or count couldn't be estimated, use 10% of the estimated
|
||||
* number of rows emitted from the subpath.
|
||||
*
|
||||
* XXX we don't bother to add eval costs of the offset/limit expressions
|
||||
* themselves to the path costs. In theory we should, but in most cases those
|
||||
* expressions are trivial and it's just not worth the trouble.
|
||||
*/
|
||||
void adjust_limit_rows_costs(double *rows, /* in/out parameter */
|
||||
Cost *startup_cost, /* in/out parameter */
|
||||
Cost *total_cost, /* in/out parameter */
|
||||
int64 offset_est, int64 count_est)
|
||||
{
|
||||
double input_rows = *rows;
|
||||
Cost input_startup_cost = *startup_cost;
|
||||
Cost input_total_cost = *total_cost;
|
||||
|
||||
if (offset_est != 0) {
|
||||
double offset_rows;
|
||||
|
||||
if (offset_est > 0) {
|
||||
offset_rows = (double)offset_est;
|
||||
} else {
|
||||
offset_rows = clamp_row_est(input_rows * 0.10);
|
||||
}
|
||||
if (offset_rows > *rows) {
|
||||
offset_rows = *rows;
|
||||
}
|
||||
if (input_rows > 0) {
|
||||
*startup_cost += (input_total_cost - input_startup_cost) * offset_rows / input_rows;
|
||||
}
|
||||
*rows -= offset_rows;
|
||||
if (*rows < 1) {
|
||||
*rows = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (count_est != 0) {
|
||||
double count_rows;
|
||||
|
||||
if (count_est > 0) {
|
||||
count_rows = (double)count_est;
|
||||
} else {
|
||||
count_rows = clamp_row_est(input_rows * 0.10);
|
||||
}
|
||||
if (count_rows > *rows) {
|
||||
count_rows = *rows;
|
||||
}
|
||||
if (input_rows > 0) {
|
||||
*total_cost = *startup_cost + (input_total_cost - input_startup_cost) * count_rows / input_rows;
|
||||
}
|
||||
*rows = count_rows;
|
||||
if (*rows < 1) {
|
||||
*rows = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* tlist_same_exprs
|
||||
* Check whether two target lists contain the same expressions
|
||||
*
|
||||
* Note: this function is used to decide whether it's safe to jam a new tlist
|
||||
* into a non-projection-capable plan node. Obviously we can't do that unless
|
||||
* the node's tlist shows it already returns the column values we want.
|
||||
* However, we can ignore the TargetEntry attributes resname, ressortgroupref,
|
||||
* resorigtbl, resorigcol, and resjunk, because those are only labelings that
|
||||
* don't affect the row values computed by the node. (Moreover, if we didn't
|
||||
* ignore them, we'd frequently fail to make the desired optimization, since
|
||||
* the planner tends to not bother to make resname etc. valid in intermediate
|
||||
* plan nodes.) Note that on success, the caller must still jam the desired
|
||||
* tlist into the plan node, else it won't have the desired labeling fields.
|
||||
*/
|
||||
static bool tlist_same_exprs(List *tlist1, List *tlist2)
|
||||
{
|
||||
ListCell *lc1 = NULL;
|
||||
ListCell *lc2 = NULL;
|
||||
|
||||
if (list_length(tlist1) != list_length(tlist2)) {
|
||||
return false; /* not same length, so can't match */
|
||||
}
|
||||
|
||||
forboth(lc1, tlist1, lc2, tlist2) {
|
||||
TargetEntry *tle1 = (TargetEntry *)lfirst(lc1);
|
||||
TargetEntry *tle2 = (TargetEntry *)lfirst(lc2);
|
||||
|
||||
if (!equal(tle1->expr, tle2->expr)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* inject_projection_plan
|
||||
* Insert a Result node to do a projection step.
|
||||
*
|
||||
* This is used in a few places where we decide on-the-fly that we need a
|
||||
* projection step as part of the tree generated for some Path node.
|
||||
* We should try to get rid of this in favor of doing it more honestly.
|
||||
*
|
||||
* One reason it's ugly is we have to be told the right parallel_safe marking
|
||||
* to apply (since the tlist might be unsafe even if the child plan is safe).
|
||||
*/
|
||||
static Plan *inject_projection_plan(PlannerInfo *root, Plan *subplan, List *tlist)
|
||||
{
|
||||
Plan *plan = NULL;
|
||||
|
||||
plan = (Plan *)make_result(root, tlist, NULL, subplan);
|
||||
|
||||
/*
|
||||
* In principle, we should charge tlist eval cost plus cpu_per_tuple per
|
||||
* row for the Result node. But the former has probably been factored in
|
||||
* already and the latter was not accounted for during Path construction,
|
||||
* so being formally correct might just make the EXPLAIN output look less
|
||||
* consistent not more so. Hence, just copy the subplan's cost.
|
||||
*/
|
||||
copy_plan_costsize(plan, subplan);
|
||||
|
||||
return plan;
|
||||
}
|
||||
|
||||
/*
|
||||
* change_plan_targetlist
|
||||
* Externally available wrapper for inject_projection_plan.
|
||||
*
|
||||
* This is meant for use by FDW plan-generation functions, which might
|
||||
* want to adjust the tlist computed by some subplan tree. In general,
|
||||
* a Result node is needed to compute the new tlist, but we can optimize
|
||||
* some cases.
|
||||
*
|
||||
* In most cases, tlist_parallel_safe can just be passed as the parallel_safe
|
||||
* flag of the FDW's own Path node.
|
||||
*/
|
||||
Plan *change_plan_targetlist(PlannerInfo *root, Plan *subplan, List *tlist)
|
||||
{
|
||||
/*
|
||||
* If the top plan node can't do projections and its existing target list
|
||||
* isn't already what we need, we need to add a Result node to help it
|
||||
* along.
|
||||
*/
|
||||
if (!is_projection_capable_plan(subplan) && !tlist_same_exprs(tlist, subplan->targetlist)) {
|
||||
subplan = inject_projection_plan(root, subplan, tlist);
|
||||
} else {
|
||||
/* Else we can just replace the plan node's tlist */
|
||||
subplan->targetlist = tlist;
|
||||
}
|
||||
return subplan;
|
||||
}
|
||||
|
||||
/*
|
||||
* apply_tlist_labeling
|
||||
* Apply the TargetEntry labeling attributes of src_tlist to dest_tlist
|
||||
*
|
||||
* This is useful for reattaching column names etc to a plan's final output
|
||||
* targetlist.
|
||||
*/
|
||||
void apply_tlist_labeling(List *dest_tlist, List *src_tlist)
|
||||
{
|
||||
ListCell *ld = NULL;
|
||||
ListCell *ls = NULL;
|
||||
|
||||
Assert(list_length(dest_tlist) == list_length(src_tlist));
|
||||
forboth(ld, dest_tlist, ls, src_tlist)
|
||||
{
|
||||
TargetEntry *dest_tle = (TargetEntry *)lfirst(ld);
|
||||
TargetEntry *src_tle = (TargetEntry *)lfirst(ls);
|
||||
|
||||
Assert(dest_tle->resno == src_tle->resno);
|
||||
dest_tle->resname = src_tle->resname;
|
||||
dest_tle->ressortgroupref = src_tle->ressortgroupref;
|
||||
dest_tle->resorigtbl = src_tle->resorigtbl;
|
||||
dest_tle->resorigcol = src_tle->resorigcol;
|
||||
dest_tle->resjunk = src_tle->resjunk;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/* -------------------------------------------------------------------------
|
||||
*
|
||||
* postgres_fdw.c
|
||||
* Foreign-data wrapper for remote openGauss servers
|
||||
*
|
||||
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
||||
* Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* contrib/postgres_fdw/postgres_fdw.c
|
||||
*
|
||||
* -------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef _PGFDW_INTERNAL_INTERFACE_H_
|
||||
#define _PGFDW_INTERNAL_INTERFACE_H_
|
||||
|
||||
#include "c.h"
|
||||
#include "datatypes.h"
|
||||
#include "nodes/bitmapset.h"
|
||||
#include "nodes/relation.h"
|
||||
#include "nodes/nodes.h"
|
||||
#include "nodes/execnodes.h"
|
||||
#include "executor/executor.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "postgres_fdw.h"
|
||||
|
||||
/*
|
||||
* This macro embodies the correct way to test whether a RestrictInfo is
|
||||
* "pushed down" to a given outer join, that is, should be treated as a filter
|
||||
* clause rather than a join clause at that outer join. This is certainly so
|
||||
* if is_pushed_down is true; but examining that is not sufficient anymore,
|
||||
* because outer-join clauses will get pushed down to lower outer joins when
|
||||
* we generate a path for the lower outer join that is parameterized by the
|
||||
* LHS of the upper one. We can detect such a clause by noting that its
|
||||
* required_relids exceed the scope of the join.
|
||||
*/
|
||||
#define RINFO_IS_PUSHED_DOWN(rinfo, joinrelids) \
|
||||
((rinfo)->is_pushed_down || !bms_is_subset((rinfo)->required_relids, joinrelids))
|
||||
|
||||
extern char *psprintf(const char *fmt, ...);
|
||||
extern size_t pvsnprintf(char *buf, size_t len, const char *fmt, va_list args);
|
||||
|
||||
extern Path *GetExistingLocalJoinPath(RelOptInfo *joinrel);
|
||||
extern ForeignPath *create_foreign_join_path(PlannerInfo *root, RelOptInfo *rel, List *target, double rows,
|
||||
Cost startup_cost, Cost total_cost, List *pathkeys, Relids required_outer, Path *fdw_outerpath, List *fdw_private);
|
||||
ForeignPath *create_foreign_upper_path(PlannerInfo *root, RelOptInfo *rel, List *target, double rows,
|
||||
Cost startup_cost, Cost total_cost, List *pathkeys, Path *fdw_outerpath, List *fdw_private);
|
||||
extern List *ExecInitExprList(List *nodes, PlanState *parent);
|
||||
extern char *get_namespace_name_or_temp(Oid nspid);
|
||||
extern RelOptInfo *fetch_upper_rel(FDWUpperRelCxt *ufdwCxt, UpperRelationKind kind);
|
||||
extern RelOptInfo *make_upper_rel(FDWUpperRelCxt *ufdwCxt, PgFdwRelationInfo *fpinfo);
|
||||
extern void adjust_limit_rows_costs(double *rows, Cost *startup_cost, Cost *total_cost, int64 offset_est, int64 count_est);
|
||||
extern Plan *change_plan_targetlist(PlannerInfo *root, Plan *subplan, List *tlist);
|
||||
extern void apply_tlist_labeling(List *dest_tlist, List *src_tlist);
|
||||
extern List* extract_target_from_tel(FDWUpperRelCxt *ufdw_cxt, PgFdwRelationInfo *fpinfo);
|
||||
|
||||
#define boolVal(v) ((bool)(((Value*)(v))->val.ival))
|
||||
|
||||
extern SortGroupClause *get_sortgroupref_clause_noerr(Index sortref, List *clauses);
|
||||
|
||||
/* Control flags for format_type_extended */
|
||||
#define FORMAT_TYPE_TYPEMOD_GIVEN 0x01 /* typemod defined by caller */
|
||||
#define FORMAT_TYPE_ALLOW_INVALID 0x02 /* allow invalid types */
|
||||
#define FORMAT_TYPE_FORCE_QUALIFY 0x04 /* force qualification of type */
|
||||
#define FORMAT_TYPE_INVALID_AS_NULL 0x08 /* NULL if undefined */
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -18,15 +18,123 @@
|
|||
#include "lib/stringinfo.h"
|
||||
#include "nodes/relation.h"
|
||||
#include "utils/rel.h"
|
||||
|
||||
#include "libpq/libpq-fe.h"
|
||||
|
||||
#include "nodes/execnodes.h"
|
||||
#include "utils/relcache.h"
|
||||
|
||||
/*
|
||||
* FDW-specific planner information kept in RelOptInfo.fdw_private for a
|
||||
* postgres_fdw foreign table. For a baserel, this struct is created by
|
||||
* postgresGetForeignRelSize, although some fields are not filled till later.
|
||||
* postgresGetForeignJoinPaths creates it for a joinrel, and
|
||||
* postgresGetForeignUpperPaths creates it for an upperrel.
|
||||
*/
|
||||
typedef struct PgFdwRelationInfo {
|
||||
/*
|
||||
* True means that the relation can be pushed down. Always true for simple
|
||||
* foreign scan.
|
||||
*/
|
||||
bool pushdown_safe;
|
||||
|
||||
/*
|
||||
* Restriction clauses, divided into safe and unsafe to pushdown subsets.
|
||||
* All entries in these lists should have RestrictInfo wrappers; that
|
||||
* improves efficiency of selectivity and cost estimation.
|
||||
*/
|
||||
List *remote_conds;
|
||||
List *local_conds;
|
||||
|
||||
/* Actual remote restriction clauses for scan (sans RestrictInfos) */
|
||||
List *final_remote_exprs;
|
||||
|
||||
/* Bitmap of attr numbers we need to fetch from the remote server. */
|
||||
Bitmapset *attrs_used;
|
||||
|
||||
/* True means that the query_pathkeys is safe to push down */
|
||||
bool qp_is_pushdown_safe;
|
||||
|
||||
/* Cost and selectivity of local_conds. */
|
||||
QualCost local_conds_cost;
|
||||
Selectivity local_conds_sel;
|
||||
|
||||
/* Selectivity of join conditions */
|
||||
Selectivity joinclause_sel;
|
||||
|
||||
/* Estimated size and cost for a scan with baserestrictinfo quals. */
|
||||
double rows;
|
||||
int width;
|
||||
Cost startup_cost;
|
||||
Cost total_cost;
|
||||
|
||||
/*
|
||||
* Estimated number of rows fetched from the foreign server, and costs
|
||||
* excluding costs for transferring those rows from the foreign server.
|
||||
* These are only used by estimate_path_cost_size().
|
||||
*/
|
||||
double retrieved_rows;
|
||||
Cost rel_startup_cost;
|
||||
Cost rel_total_cost;
|
||||
|
||||
/* Options extracted from catalogs. */
|
||||
bool use_remote_estimate;
|
||||
Cost fdw_startup_cost;
|
||||
Cost fdw_tuple_cost;
|
||||
List *shippable_extensions; /* OIDs of shippable extensions */
|
||||
bool async_capable;
|
||||
|
||||
/* Cached catalog information. */
|
||||
ForeignTable *table;
|
||||
ForeignServer *server;
|
||||
UserMapping *user; /* only set in use_remote_estimate mode */
|
||||
|
||||
int fetch_size; /* fetch size for this remote table */
|
||||
|
||||
/*
|
||||
* Name of the relation, for use while EXPLAINing ForeignScan. It is used
|
||||
* for join and upper relations but is set for all relations. For a base
|
||||
* relation, this is really just the RT index as a string; we convert that
|
||||
* while producing EXPLAIN output. For join and upper relations, the name
|
||||
* indicates which base foreign tables are included and the join type or
|
||||
* aggregation type used.
|
||||
*/
|
||||
char *relation_name;
|
||||
|
||||
/* Join information */
|
||||
RelOptInfo *outerrel;
|
||||
RelOptInfo *innerrel;
|
||||
JoinType jointype;
|
||||
/* joinclauses contains only JOIN/ON conditions for an outer join */
|
||||
List *joinclauses; /* List of RestrictInfo */
|
||||
|
||||
/* Upper relation information */
|
||||
UpperRelationKind stage;
|
||||
|
||||
/* Grouping information */
|
||||
List *grouped_tlist;
|
||||
|
||||
/* Subquery information */
|
||||
bool make_outerrel_subquery; /* do we deparse outerrel as a subquery? */
|
||||
bool make_innerrel_subquery; /* do we deparse innerrel as a subquery? */
|
||||
Relids lower_subquery_rels; /* all relids appearing in lower subqueries */
|
||||
|
||||
/*
|
||||
* Index of the relation. It is used to create an alias to a subquery
|
||||
* representing the relation.
|
||||
*/
|
||||
int relation_index;
|
||||
|
||||
/* target entry list from */
|
||||
List* complete_tlist;
|
||||
} PgFdwRelationInfo;
|
||||
|
||||
/* in postgres_fdw.c */
|
||||
extern int set_transmission_modes(void);
|
||||
extern void reset_transmission_modes(int nestlevel);
|
||||
|
||||
/* in connection.c */
|
||||
extern PGconn *GetConnection(ForeignServer *server, UserMapping *user, bool will_prep_stmt);
|
||||
PGconn* GetConnectionByFScanState(ForeignScanState *node, bool will_prep_stmt);
|
||||
extern void ReleaseConnection(PGconn *conn);
|
||||
extern unsigned int GetCursorNumber(PGconn *conn);
|
||||
extern unsigned int GetPrepStmtNumber(PGconn *conn);
|
||||
|
@ -41,10 +149,7 @@ extern int ExtractConnectionOptions(List *defelems, const char **keywords, const
|
|||
extern void classifyConditions(PlannerInfo *root, RelOptInfo *baserel, List *input_conds, List **remote_conds,
|
||||
List **local_conds);
|
||||
extern bool is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expr);
|
||||
extern void deparseSelectSql(StringInfo buf, PlannerInfo *root, RelOptInfo *baserel, Bitmapset *attrs_used,
|
||||
List **retrieved_attrs);
|
||||
extern void appendWhereClause(StringInfo buf, PlannerInfo *root, RelOptInfo *baserel, List *exprs, bool is_first,
|
||||
List **params);
|
||||
extern bool is_foreign_pathkey(PlannerInfo *root, RelOptInfo *baserel, PathKey *pathkey);
|
||||
extern void deparseInsertSql(StringInfo buf, RangeTblEntry *rte, Index rtindex, Relation rel, List *targetAttrs,
|
||||
List *returningList, List **retrieved_attrs);
|
||||
extern void deparseUpdateSql(StringInfo buf, RangeTblEntry *rte, Index rtindex, Relation rel, List *targetAttrs,
|
||||
|
@ -53,6 +158,17 @@ extern void deparseDeleteSql(StringInfo buf, RangeTblEntry *rte, Index rtindex,
|
|||
List **retrieved_attrs);
|
||||
extern void deparseAnalyzeSizeSql(StringInfo buf, Relation rel);
|
||||
extern void deparseAnalyzeSql(StringInfo buf, Relation rel, List **retrieved_attrs);
|
||||
extern void deparseSelectStmtForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel, List *tlist,
|
||||
List *remote_conds, List *pathkeys, bool has_final_sort, bool has_limit, bool is_subquery, List **retrieved_attrs,
|
||||
List **params_list);
|
||||
|
||||
extern const char *get_jointype_name(JoinType jointype);
|
||||
extern List *build_tlist_to_deparse(RelOptInfo *foreignrel);
|
||||
extern bool is_foreign_param(PlannerInfo *root, RelOptInfo *baserel, Expr *expr);
|
||||
extern EquivalenceMember *find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel);
|
||||
extern EquivalenceMember *find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel);
|
||||
|
||||
extern bool is_builtin(Oid oid);
|
||||
|
||||
#endif /* POSTGRES_FDW_H */
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -448,6 +448,7 @@ shared_buffers|int|16,1073741823|kB|NULL|
|
|||
pca_shared_buffers|int|8,1073741823|kB|NULL|
|
||||
shared_preload_libraries|string|0,0|NULL|NULL|
|
||||
show_acce_estimate_detail|bool|0,0|NULL|NULL|
|
||||
show_fdw_remote_plan|bool|0,0|NULL|NULL|
|
||||
skew_option|enum|normal,lazy,off|NULL|NULL|
|
||||
sql_inheritance|bool|0,0|NULL|NULL|
|
||||
ssl|bool|0,0|NULL|NULL|
|
||||
|
|
|
@ -959,6 +959,13 @@ static ForeignScan* _copyForeignScan(const ForeignScan* from)
|
|||
}
|
||||
}
|
||||
|
||||
COPY_SCALAR_FIELD(operation);
|
||||
COPY_SCALAR_FIELD(resultRelation);
|
||||
COPY_SCALAR_FIELD(fs_server);
|
||||
COPY_BITMAPSET_FIELD(fs_relids);
|
||||
COPY_NODE_FIELD(fdw_scan_tlist);
|
||||
COPY_NODE_FIELD(fdw_recheck_quals);
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
|
@ -1831,6 +1838,13 @@ static VecForeignScan* _copyVecForeignScan(const VecForeignScan* from)
|
|||
}
|
||||
}
|
||||
|
||||
COPY_SCALAR_FIELD(operation);
|
||||
COPY_SCALAR_FIELD(resultRelation);
|
||||
COPY_SCALAR_FIELD(fs_server);
|
||||
COPY_BITMAPSET_FIELD(fs_relids);
|
||||
COPY_NODE_FIELD(fdw_scan_tlist);
|
||||
COPY_NODE_FIELD(fdw_recheck_quals);
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
|
|
|
@ -541,6 +541,31 @@ List* list_concat(List* list1, List* list2)
|
|||
return list1;
|
||||
}
|
||||
|
||||
/* make new listcell to concat data of list2, not distory the struction of list2. */
|
||||
List* list_concat2(List* list1, List* list2)
|
||||
{
|
||||
if (list2 == NIL) {
|
||||
return list1;
|
||||
}
|
||||
if (list1 == list2) {
|
||||
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("cannot list_concat2() a list to itself")));
|
||||
}
|
||||
|
||||
Assert(list1 == NIL || list1->type == list2->type);
|
||||
|
||||
ListCell* lc = NULL;
|
||||
foreach(lc, list2) {
|
||||
/* no matter the type, use "lfirst" */
|
||||
list1 = lappend(list1, lfirst(lc));
|
||||
}
|
||||
|
||||
list1->type = list2->type;
|
||||
|
||||
check_list_invariants(list1);
|
||||
check_list_invariants(list2);
|
||||
return list1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Truncate 'list' to contain no more than 'new_size' elements. This
|
||||
* modifies the list in-place! Despite this, callers should use the
|
||||
|
|
|
@ -1482,6 +1482,15 @@ static void _outCommonForeignScanPart(StringInfo str, T* node)
|
|||
WRITE_NODE_FIELD(bloomFilterSet[i]);
|
||||
}
|
||||
WRITE_BOOL_FIELD(in_compute_pool);
|
||||
|
||||
if (t_thrd.proc->workingVersionNum >= FDW_SUPPORT_JOIN_AGG_VERSION_NUM) {
|
||||
WRITE_ENUM_FIELD(operation, CmdType);
|
||||
WRITE_UINT_FIELD(resultRelation);
|
||||
WRITE_OID_FIELD(fs_server);
|
||||
WRITE_BITMAPSET_FIELD(fs_relids);
|
||||
WRITE_NODE_FIELD(fdw_scan_tlist);
|
||||
WRITE_NODE_FIELD(fdw_recheck_quals);
|
||||
}
|
||||
}
|
||||
static void _outForeignScan(StringInfo str, ForeignScan* node)
|
||||
{
|
||||
|
@ -3289,6 +3298,9 @@ static void _outRelOptInfo(StringInfo str, RelOptInfo* node)
|
|||
WRITE_INT_FIELD(partItrs);
|
||||
WRITE_NODE_FIELD(subplan);
|
||||
WRITE_NODE_FIELD(subroot);
|
||||
WRITE_OID_FIELD(serverid);
|
||||
WRITE_OID_FIELD(userid);
|
||||
WRITE_BOOL_FIELD(useridiscurrent);
|
||||
/* we don't try to print fdwroutine or fdw_private */
|
||||
WRITE_NODE_FIELD(baserestrictinfo);
|
||||
WRITE_UINT_FIELD(baserestrict_min_security);
|
||||
|
|
|
@ -4512,6 +4512,16 @@ static ForeignScan* _readForeignScan(ForeignScan* local_node)
|
|||
}
|
||||
|
||||
READ_BOOL_FIELD(in_compute_pool);
|
||||
|
||||
IF_EXIST(operation) {
|
||||
READ_ENUM_FIELD(operation, CmdType);
|
||||
READ_UINT_FIELD(resultRelation);
|
||||
READ_OID_FIELD(fs_server);
|
||||
READ_BITMAPSET_FIELD(fs_relids);
|
||||
READ_NODE_FIELD(fdw_scan_tlist);
|
||||
READ_NODE_FIELD(fdw_recheck_quals);
|
||||
}
|
||||
|
||||
READ_DONE();
|
||||
}
|
||||
|
||||
|
@ -5046,6 +5056,14 @@ static VecForeignScan* _readVecForeignScan(VecForeignScan* local_node)
|
|||
}
|
||||
|
||||
READ_BOOL_FIELD(in_compute_pool);
|
||||
IF_EXIST(operation) {
|
||||
READ_ENUM_FIELD(operation, CmdType);
|
||||
READ_UINT_FIELD(resultRelation);
|
||||
READ_OID_FIELD(fs_server);
|
||||
READ_BITMAPSET_FIELD(fs_relids);
|
||||
READ_NODE_FIELD(fdw_scan_tlist);
|
||||
READ_NODE_FIELD(fdw_recheck_quals);
|
||||
}
|
||||
READ_DONE();
|
||||
}
|
||||
|
||||
|
|
|
@ -5142,6 +5142,8 @@ static void set_deparse_planstate(deparse_namespace* dpns, PlanState* ps)
|
|||
/* index_tlist is set only if it's an IndexOnlyScan */
|
||||
if (IsA(ps->plan, IndexOnlyScan))
|
||||
dpns->index_tlist = ((IndexOnlyScan*)ps->plan)->indextlist;
|
||||
else if (IsA(ps->plan, ForeignScan))
|
||||
dpns->index_tlist = ((ForeignScan *)ps->plan)->fdw_scan_tlist;
|
||||
else if (IsA(ps->plan, ExtensiblePlan))
|
||||
dpns->index_tlist = ((ExtensiblePlan*)ps->plan)->extensible_plan_tlist;
|
||||
else
|
||||
|
|
|
@ -59,7 +59,7 @@ bool open_join_children = true;
|
|||
bool will_shutdown = false;
|
||||
|
||||
/* hard-wired binary version number */
|
||||
const uint32 GRAND_VERSION_NUM = 92838;
|
||||
const uint32 GRAND_VERSION_NUM = 92839;
|
||||
|
||||
const uint32 SELECT_INTO_VAR_VERSION_NUM = 92834;
|
||||
const uint32 DOLPHIN_ENABLE_DROP_NUM = 92830;
|
||||
|
@ -164,6 +164,8 @@ const uint32 MAT_VIEW_RECURSIVE_VERSION_NUM = 92833;
|
|||
|
||||
const uint32 NEGETIVE_BOOL_VERSION_NUM = 92835;
|
||||
|
||||
const uint32 FDW_SUPPORT_JOIN_AGG_VERSION_NUM = 92839;
|
||||
|
||||
#ifdef PGXC
|
||||
bool useLocalXid = false;
|
||||
#endif
|
||||
|
|
|
@ -1921,6 +1921,18 @@ static void InitConfigureNamesBool()
|
|||
NULL,
|
||||
NULL
|
||||
},
|
||||
{{"show_fdw_remote_plan",
|
||||
PGC_USERSET,
|
||||
NODE_SINGLENODE,
|
||||
UNGROUPED,
|
||||
gettext_noop("show remote plan of a foreign scan."),
|
||||
NULL},
|
||||
&u_sess->attr.attr_common.show_fdw_remote_plan,
|
||||
false,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
},
|
||||
/* End-of-list marker */
|
||||
{{NULL,
|
||||
(GucContext)0,
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
#include "utils/syscache.h"
|
||||
#include "cipher.h"
|
||||
#include "utils/knl_relcache.h"
|
||||
#include "optimizer/planmain.h"
|
||||
#include "optimizer/pathnode.h"
|
||||
|
||||
extern Datum pg_options_to_table(PG_FUNCTION_ARGS);
|
||||
extern Datum postgresql_fdw_validator(PG_FUNCTION_ARGS);
|
||||
|
@ -215,6 +217,11 @@ bool IsSpecifiedFDW(const char* ServerName, const char* SepcifiedType)
|
|||
*/
|
||||
bool IsSpecifiedFDWFromRelid(Oid relId, const char* SepcifiedType)
|
||||
{
|
||||
if (!OidIsValid(relId)) {
|
||||
ereport(ERROR, (errmsg("Invalid relid is used to verify fdw type."),
|
||||
errhint("This may be a join relationship, or has aggregation, etc.")));
|
||||
}
|
||||
|
||||
ForeignTable* ftbl = NULL;
|
||||
ForeignServer* fsvr = NULL;
|
||||
bool IsSpecifiedTable = false;
|
||||
|
@ -248,10 +255,10 @@ bool IsSpecifiedFDWFromRelid(Oid relId, const char* SepcifiedType)
|
|||
|
||||
/**
|
||||
* @Description: Jude whether type of the foreign table support SELECT/INSERT/UPDATE/DELETE/COPY
|
||||
* @in relId: The foreign table Oid.
|
||||
* @in oid: The foreign table Oid or The foreign server Oid.
|
||||
* @return Rreturn true if the foreign table support those DML.
|
||||
*/
|
||||
bool CheckSupportedFDWType(Oid relId)
|
||||
bool CheckSupportedFDWType(Oid oid, bool byServerId)
|
||||
{
|
||||
static const char* supportFDWType[] = {MOT_FDW, MYSQL_FDW, ORACLE_FDW, POSTGRES_FDW};
|
||||
int size = sizeof(supportFDWType) / sizeof(supportFDWType[0]);
|
||||
|
@ -268,8 +275,8 @@ bool CheckSupportedFDWType(Oid relId)
|
|||
}
|
||||
MemoryContext oldContext = MemoryContextSwitchTo(u_sess->opt_cxt.ft_context);
|
||||
|
||||
ForeignTable* ftbl = GetForeignTable(relId);
|
||||
ForeignServer* fsvr = GetForeignServer(ftbl->serverid);
|
||||
Oid serverid = byServerId ? oid : GetForeignTable(oid)->serverid;
|
||||
ForeignServer* fsvr = GetForeignServer(serverid);
|
||||
ForeignDataWrapper* fdw = GetForeignDataWrapper(fsvr->fdwid);
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
|
@ -286,6 +293,11 @@ bool CheckSupportedFDWType(Oid relId)
|
|||
|
||||
bool isSpecifiedSrvTypeFromRelId(Oid relId, const char* SepcifiedType)
|
||||
{
|
||||
if (!OidIsValid(relId)) {
|
||||
ereport(ERROR, (errmsg("Invalid relid is used to verify foreign server type."),
|
||||
errhint("This may be a join relationship, or has aggregation, etc.")));
|
||||
}
|
||||
|
||||
ForeignTable* ftbl = NULL;
|
||||
ForeignServer* fsrv = NULL;
|
||||
bool ret = false;
|
||||
|
@ -494,6 +506,26 @@ FdwRoutine* GetFdwRoutine(Oid fdwhandler)
|
|||
return routine;
|
||||
}
|
||||
|
||||
/*
|
||||
* GetForeignServerIdByRelId - look up the foreign server
|
||||
* for the given foreign table, and return its OID.
|
||||
*/
|
||||
Oid GetForeignServerIdByRelId(Oid relid)
|
||||
{
|
||||
HeapTuple tp;
|
||||
Form_pg_foreign_table tableform;
|
||||
Oid serverid;
|
||||
|
||||
tp = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(relid));
|
||||
if (!HeapTupleIsValid(tp))
|
||||
elog(ERROR, "cache lookup failed for foreign table %u", relid);
|
||||
tableform = (Form_pg_foreign_table)GETSTRUCT(tp);
|
||||
serverid = tableform->ftserver;
|
||||
ReleaseSysCache(tp);
|
||||
|
||||
return serverid;
|
||||
}
|
||||
|
||||
/*
|
||||
* GetFdwRoutineByRelId - look up the handler of the foreign-data wrapper
|
||||
* for the given foreign table, and retrieve its FdwRoutine struct.
|
||||
|
@ -996,6 +1028,10 @@ ObsOptions* getObsOptions(Oid foreignTableId)
|
|||
*/
|
||||
ServerTypeOption getServerType(Oid foreignTableId)
|
||||
{
|
||||
if (!OidIsValid(foreignTableId)) {
|
||||
ereport(ERROR, (errmsg("Invalid foreignTableId is used to get server type."),
|
||||
errhint("This may be a join relationship, or has aggregation, etc.")));
|
||||
}
|
||||
char* optionValue = HdfsGetOptionValue(foreignTableId, "type");
|
||||
ServerTypeOption srvType = T_INVALID;
|
||||
|
||||
|
@ -1698,3 +1734,38 @@ void CheckFoldernameOrFilenamesOrCfgPtah(const char *OptStr, char *OptType)
|
|||
}
|
||||
}
|
||||
|
||||
FDWUpperRelCxt* InitFDWUpperPlan(PlannerInfo* root, RelOptInfo* baseRel, Plan* localPlan)
|
||||
{
|
||||
if (baseRel == NULL || baseRel->fdwroutine == NULL ||
|
||||
baseRel->fdwroutine->GetForeignUpperPaths == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (IS_STREAM_PLAN) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FDWUpperRelCxt* ufdwCxt = (FDWUpperRelCxt*)palloc0(sizeof(FDWUpperRelCxt));
|
||||
ufdwCxt->root = root;
|
||||
ufdwCxt->currentRel = baseRel;
|
||||
|
||||
/* these will definitely be used, create it early. */
|
||||
ufdwCxt->spjExtra = (SPJPathExtraData*)palloc0(sizeof(SPJPathExtraData));
|
||||
ufdwCxt->finalExtra = (FinalPathExtraData*)palloc0(sizeof(FinalPathExtraData));
|
||||
|
||||
ufdwCxt->spjExtra->targetList = localPlan->targetlist;
|
||||
AdvanceFDWUpperPlan(ufdwCxt, UPPERREL_INIT, localPlan);
|
||||
|
||||
return ufdwCxt;
|
||||
}
|
||||
|
||||
void AdvanceFDWUpperPlan(FDWUpperRelCxt* ufdwCxt, UpperRelationKind stage, Plan* localPlan)
|
||||
{
|
||||
if (ufdwCxt->currentRel == NULL || ufdwCxt->currentRel->fdwroutine == NULL ||
|
||||
ufdwCxt->currentRel->fdwroutine->GetForeignUpperPaths == NULL) {
|
||||
ufdwCxt->state = FDW_UPPER_REL_END;
|
||||
return;
|
||||
}
|
||||
|
||||
ufdwCxt->currentRel->fdwroutine->GetForeignUpperPaths(ufdwCxt, stage, localPlan);
|
||||
}
|
||||
|
|
|
@ -567,6 +567,7 @@ void ExplainInitState(ExplainState* es)
|
|||
es->indent = 0;
|
||||
es->pindent = 0;
|
||||
es->wlm_statistics_plan_max_digit = NULL;
|
||||
es->es_frs.parent = es;
|
||||
/* Reset flag for plan_table. */
|
||||
IsExplainPlanStmt = false;
|
||||
IsExplainPlanSelectForUpdateStmt = false;
|
||||
|
@ -1211,6 +1212,13 @@ void ExplainOnePlan(
|
|||
}
|
||||
u_sess->exec_cxt.remotequery_list = NIL;
|
||||
}
|
||||
|
||||
/* we have get all plan of foreignscan remote sql, append it */
|
||||
if (es->es_frs.node_num > 0) {
|
||||
appendStringInfo(es->str, "%s\n", es->es_frs.str->data);
|
||||
pfree_ext(es->es_frs.str->data);
|
||||
}
|
||||
|
||||
/*
|
||||
* Close down the query and free resources. Include time for this in the
|
||||
* total runtime (although it should be pretty minimal).
|
||||
|
@ -2102,7 +2110,9 @@ static void ExplainNode(
|
|||
case T_WorkTableScan:
|
||||
case T_ForeignScan:
|
||||
case T_VecForeignScan:
|
||||
ExplainScanTarget((Scan*)plan, es);
|
||||
if (((Scan *) plan)->scanrelid > 0) {
|
||||
ExplainScanTarget((Scan*)plan, es);
|
||||
}
|
||||
break;
|
||||
case T_TrainModel:
|
||||
appendStringInfo(es->str, " - %s", sname);
|
||||
|
@ -3330,7 +3340,7 @@ static void show_plan_tlist(PlanState* planstate, List* ancestors, ExplainState*
|
|||
|
||||
if (IsA(plan, ForeignScan) || IsA(plan, VecForeignScan)) {
|
||||
ForeignScan* fscan = (ForeignScan*)plan;
|
||||
if (IsSpecifiedFDWFromRelid(fscan->scan_relid, GC_FDW)) {
|
||||
if (OidIsValid(fscan->scan_relid) && IsSpecifiedFDWFromRelid(fscan->scan_relid, GC_FDW)) {
|
||||
List* str_targetlist = get_str_targetlist(fscan->fdw_private);
|
||||
if (str_targetlist != NULL)
|
||||
result = str_targetlist;
|
||||
|
@ -7449,9 +7459,33 @@ static void show_foreignscan_info(ForeignScanState* fsstate, ExplainState* es)
|
|||
{
|
||||
FdwRoutine* fdwroutine = fsstate->fdwroutine;
|
||||
|
||||
/* if we want to know what the remote plan like, numbering this node to find it quickly later. */
|
||||
if (u_sess->attr.attr_common.show_fdw_remote_plan) {
|
||||
es->es_frs.node_num++;
|
||||
ExplainPropertyInteger("Node ID", es->es_frs.node_num , es);
|
||||
}
|
||||
|
||||
/* Let the FDW emit whatever fields it wants */
|
||||
if (NULL != fdwroutine && NULL != fdwroutine->ExplainForeignScan)
|
||||
fdwroutine->ExplainForeignScan(fsstate, es);
|
||||
|
||||
/* Let the FDW get the remote plan */
|
||||
if (u_sess->attr.attr_common.show_fdw_remote_plan) {
|
||||
/* Set a title */
|
||||
if (es->es_frs.node_num == 1) {
|
||||
es->es_frs.str=makeStringInfo();
|
||||
appendStringInfo(es->es_frs.str, "\nFDW remote plans:\n");
|
||||
}
|
||||
|
||||
appendStringInfo(es->es_frs.str, "Node %d: ", es->es_frs.node_num);
|
||||
|
||||
/* Let the FDW emit whatever fields it wants */
|
||||
if (NULL != fdwroutine && NULL != fdwroutine->ExplainForeignScanRemote) {
|
||||
fdwroutine->ExplainForeignScanRemote(fsstate, es);
|
||||
} else {
|
||||
appendStringInfo(es->es_frs.str, "No remote plan information.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -17810,13 +17810,11 @@ static void ATExecGenericOptions(Relation rel, List* options)
|
|||
simple_heap_update(ftrel, &tuple->t_self, tuple);
|
||||
CatalogUpdateIndexes(ftrel, tuple);
|
||||
|
||||
#ifdef ENABLE_MOT
|
||||
/*
|
||||
* Invalidate relcache so that all sessions will refresh any cached plans
|
||||
* that might depend on the old options.
|
||||
*/
|
||||
CacheInvalidateRelcache(rel);
|
||||
#endif
|
||||
|
||||
heap_close(ftrel, RowExclusiveLock);
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
* -------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
#include "foreign/fdwapi.h"
|
||||
#include "knl/knl_variable.h"
|
||||
|
||||
#include <math.h>
|
||||
|
@ -338,6 +339,15 @@ void add_paths_to_joinrel(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* ou
|
|||
list_free_ext(mergejoin_hint);
|
||||
list_free_ext(hashjoin_hint);
|
||||
|
||||
/*
|
||||
* 5. If inner and outer relations are foreign tables (or joins) belonging
|
||||
* to the same server and assigned to the same user to check access
|
||||
* permissions as, give the FDW a chance to push down joins.
|
||||
*/
|
||||
if (joinrel->fdwroutine && joinrel->fdwroutine->GetForeignJoinPaths) {
|
||||
joinrel->fdwroutine->GetForeignJoinPaths(root, joinrel, outerrel, innerrel, jointype, sjinfo, restrictlist);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finally, give extensions a change to manipulate the path list.
|
||||
*/
|
||||
|
|
|
@ -32,8 +32,6 @@
|
|||
#include "utils/lsyscache.h"
|
||||
|
||||
static PathKey* makePathKey(EquivalenceClass* eclass, Oid opfamily, int strategy, bool nulls_first);
|
||||
static PathKey* make_canonical_pathkey(
|
||||
PlannerInfo* root, EquivalenceClass* eclass, Oid opfamily, int strategy, bool nulls_first);
|
||||
static bool pathkey_is_redundant(PathKey* new_pathkey, List* pathkeys, bool predpush = false);
|
||||
static bool right_merge_direction(PlannerInfo* root, PathKey* pathkey);
|
||||
|
||||
|
@ -68,7 +66,7 @@ static PathKey* makePathKey(EquivalenceClass* eclass, Oid opfamily, int strategy
|
|||
* Note that this function must not be used until after we have completed
|
||||
* merging EquivalenceClasses.
|
||||
*/
|
||||
static PathKey* make_canonical_pathkey(
|
||||
PathKey* make_canonical_pathkey(
|
||||
PlannerInfo* root, EquivalenceClass* eclass, Oid opfamily, int strategy, bool nulls_first)
|
||||
{
|
||||
PathKey* pk = NULL;
|
||||
|
|
|
@ -3632,13 +3632,23 @@ static ForeignScan* create_foreignscan_plan(PlannerInfo* root, ForeignPath* best
|
|||
Index scan_relid = rel->relid;
|
||||
RangeTblEntry* rte = NULL;
|
||||
RangeTblEntry* target_rte = NULL;
|
||||
Plan* outer_plan = NULL;
|
||||
int i;
|
||||
|
||||
/* it should be a base rel... */
|
||||
Assert(scan_relid > 0);
|
||||
Assert(rel->rtekind == RTE_RELATION);
|
||||
rte = planner_rt_fetch(scan_relid, root);
|
||||
Assert(rte->rtekind == RTE_RELATION);
|
||||
/* transform the child path if any */
|
||||
if (best_path->fdw_outerpath) {
|
||||
outer_plan = create_plan_recurse(root, best_path->fdw_outerpath);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we're scanning a base relation, fetch its OID. (Irrelevant if
|
||||
* scanning a join relation.)
|
||||
*/
|
||||
if (scan_relid > 0) {
|
||||
Assert(rel->rtekind == RTE_RELATION);
|
||||
rte = planner_rt_fetch(scan_relid, root);
|
||||
Assert(rte->rtekind == RTE_RELATION);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sort clauses into best execution order. We do this first since the FDW
|
||||
|
@ -3655,7 +3665,7 @@ static ForeignScan* create_foreignscan_plan(PlannerInfo* root, ForeignPath* best
|
|||
* For now, error table only support insert statement.
|
||||
*/
|
||||
target_rte = rte;
|
||||
if (root->parse->commandType == CMD_INSERT) {
|
||||
if (scan_relid > 0 && root->parse->commandType == CMD_INSERT) {
|
||||
// Confirm whether exists error table.
|
||||
DefElem* def = GetForeignTableOptionByName(rte->relid, optErrorRel);
|
||||
if (def != NULL) {
|
||||
|
@ -3686,19 +3696,36 @@ static ForeignScan* create_foreignscan_plan(PlannerInfo* root, ForeignPath* best
|
|||
* Assign task to the datanodes where target table exists so that
|
||||
* the error information will be saved only in these nodes.
|
||||
*/
|
||||
scan_plan = rel->fdwroutine->GetForeignPlan(root, rel, target_rte->relid, best_path, tlist, scan_clauses);
|
||||
scan_plan->scan_relid = rte->relid;
|
||||
scan_plan = rel->fdwroutine->GetForeignPlan(root, rel,
|
||||
target_rte == NULL ? scan_relid : target_rte->relid,
|
||||
best_path, tlist, scan_clauses, outer_plan);
|
||||
|
||||
char locator_type = GetLocatorType(rte->relid);
|
||||
scan_plan->scan_relid = (rte == NULL ? scan_relid : rte->relid);
|
||||
/* Copy foreign server OID; likewise, no need to make FDW do this */
|
||||
scan_plan->fs_server = rel->serverid;
|
||||
|
||||
/*
|
||||
* Likewise, copy the relids that are represented by this foreign scan. An
|
||||
* upper rel doesn't have relids set, but it covers all the base relations
|
||||
* participating in the underlying scan, so use root's all_baserels.
|
||||
*/
|
||||
if (rel->reloptkind == RELOPT_UPPER_REL) {
|
||||
scan_plan->fs_relids = root->all_baserels;
|
||||
} else {
|
||||
scan_plan->fs_relids = best_path->path.parent->relids;
|
||||
}
|
||||
|
||||
char locator_type = (rte == NULL ? LOCATOR_TYPE_NONE : GetLocatorType(rte->relid));
|
||||
|
||||
/*
|
||||
* In order to support error table in multi-nodegroup situation,
|
||||
* execute foreign scan only on DNs where the insert-targeted table exists.
|
||||
* Secondly, find the DNs where the target table lies and set them as the exec_nodes.
|
||||
*/
|
||||
exec_nodes = GetRelationNodesByQuals(
|
||||
(void*)root->parse, target_rte->relid, scan_relid, (Node*)scan_clauses, RELATION_ACCESS_READ, NULL);
|
||||
|
||||
if (scan_relid > 0) {
|
||||
exec_nodes = GetRelationNodesByQuals((void *)root->parse, target_rte->relid,
|
||||
scan_relid, (Node *)scan_clauses, RELATION_ACCESS_READ, NULL);
|
||||
}
|
||||
if (exec_nodes == NULL) {
|
||||
/* foreign scan is executed on installation group */
|
||||
exec_nodes = ng_get_installation_group_exec_node();
|
||||
|
@ -3723,6 +3750,15 @@ static ForeignScan* create_foreignscan_plan(PlannerInfo* root, ForeignPath* best
|
|||
/* Copy cost data from Path to Plan; no need to make FDW do this */
|
||||
copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
|
||||
|
||||
/*
|
||||
* If this is a foreign join, and to make it valid to push down we had to
|
||||
* assume that the current user is the same as some user explicitly named
|
||||
* in the query, mark the finished plan as depending on the current user.
|
||||
*/
|
||||
if (rel->useridiscurrent) {
|
||||
root->glob->dependsOnRole = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Replace any outer-relation variables with nestloop params in the qual
|
||||
* and fdw_exprs expressions. We do this last so that the FDW doesn't
|
||||
|
@ -3731,8 +3767,9 @@ static ForeignScan* create_foreignscan_plan(PlannerInfo* root, ForeignPath* best
|
|||
* wouldn't work.)
|
||||
*/
|
||||
if (best_path->path.param_info) {
|
||||
scan_plan->scan.plan.qual = (List*)replace_nestloop_params(root, (Node*)scan_plan->scan.plan.qual);
|
||||
scan_plan->fdw_exprs = (List*)replace_nestloop_params(root, (Node*)scan_plan->fdw_exprs);
|
||||
scan_plan->scan.plan.qual = (List *)replace_nestloop_params(root, (Node *)scan_plan->scan.plan.qual);
|
||||
scan_plan->fdw_exprs = (List *)replace_nestloop_params(root, (Node *)scan_plan->fdw_exprs);
|
||||
scan_plan->fdw_recheck_quals = (List *)replace_nestloop_params(root, (Node *)scan_plan->fdw_recheck_quals);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -3741,6 +3778,10 @@ static ForeignScan* create_foreignscan_plan(PlannerInfo* root, ForeignPath* best
|
|||
* out of the API presented to FDWs.
|
||||
*/
|
||||
scan_plan->fsSystemCol = false;
|
||||
if (scan_relid == 0) {
|
||||
return scan_plan;
|
||||
}
|
||||
|
||||
for (i = rel->min_attr; i < 0; i++) {
|
||||
if (!bms_is_empty(rel->attr_needed[i - rel->min_attr])) {
|
||||
scan_plan->fsSystemCol = true;
|
||||
|
@ -5921,8 +5962,8 @@ static WorkTableScan* make_worktablescan(List* qptlist, List* qpqual, Index scan
|
|||
return node;
|
||||
}
|
||||
|
||||
ForeignScan* make_foreignscan(
|
||||
List* qptlist, List* qpqual, Index scanrelid, List* fdw_exprs, List* fdw_private, RemoteQueryExecType type)
|
||||
ForeignScan *make_foreignscan(List *qptlist, List *qpqual, Index scanrelid, List *fdw_exprs, List *fdw_private,
|
||||
List *fdw_scan_tlist, List *fdw_recheck_quals, Plan *outer_plan, RemoteQueryExecType type)
|
||||
{
|
||||
ForeignScan* node = makeNode(ForeignScan);
|
||||
|
||||
|
@ -5931,17 +5972,29 @@ ForeignScan* make_foreignscan(
|
|||
/* cost will be filled in by create_foreignscan_plan */
|
||||
plan->targetlist = qptlist;
|
||||
plan->qual = qpqual;
|
||||
plan->lefttree = NULL;
|
||||
plan->lefttree = outer_plan;
|
||||
plan->righttree = NULL;
|
||||
plan->exec_type = type;
|
||||
plan->distributed_keys = NIL;
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
plan->distributed_keys =
|
||||
lappend(plan->distributed_keys, makeVar(0, InvalidAttrNumber, InvalidOid, -1, InvalidOid, 0));
|
||||
#endif
|
||||
node->scan.scanrelid = scanrelid;
|
||||
|
||||
/* these may be overridden by the FDW's PlanDirectModify callback. */
|
||||
node->operation = CMD_SELECT;
|
||||
node->resultRelation = 0;
|
||||
|
||||
/* fs_server will be filled in by create_foreignscan_plan */
|
||||
node->fs_server = InvalidOid;
|
||||
node->fdw_exprs = fdw_exprs;
|
||||
node->fdw_private = fdw_private;
|
||||
node->fdw_scan_tlist = fdw_scan_tlist;
|
||||
node->fdw_recheck_quals = fdw_recheck_quals;
|
||||
|
||||
/* fs_relids will be filled in by create_foreignscan_plan */
|
||||
node->fs_relids = NULL;
|
||||
|
||||
/* fsSystemCol will be filled in by create_foreignscan_plan */
|
||||
node->fsSystemCol = false;
|
||||
|
||||
|
@ -8298,8 +8351,10 @@ static Plan* FindForeignScan(Plan* plan)
|
|||
switch (nodeTag(plan)) {
|
||||
case T_ForeignScan: {
|
||||
ForeignScan* fscan = (ForeignScan*)plan;
|
||||
if (isObsOrHdfsTableFormTblOid(fscan->scan_relid) || IS_LOGFDW_FOREIGN_TABLE(fscan->scan_relid) ||
|
||||
IS_POSTGRESFDW_FOREIGN_TABLE(fscan->scan_relid)) {
|
||||
if (fscan->scan_relid > 0 &&
|
||||
(isObsOrHdfsTableFormTblOid(fscan->scan_relid) ||
|
||||
IS_LOGFDW_FOREIGN_TABLE(fscan->scan_relid) ||
|
||||
IS_POSTGRESFDW_FOREIGN_TABLE(fscan->scan_relid))) {
|
||||
return NULL;
|
||||
}
|
||||
return plan;
|
||||
|
@ -8709,7 +8764,7 @@ ModifyTable* make_modifytable(CmdType operation, bool canSetTag, List* resultRel
|
|||
Plan* subplan = (Plan*)(linitial(subplans));
|
||||
ForeignScan* fscan = NULL;
|
||||
if ((fscan = (ForeignScan*)FindForeignScan(subplan)) != NULL) {
|
||||
if (!CheckSupportedFDWType(fscan->scan_relid))
|
||||
if (!CheckSupportedFDWType(fscan->fs_server, true))
|
||||
ereport(ERROR,
|
||||
(errmodule(MOD_OPT),
|
||||
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
|
|
|
@ -1191,6 +1191,22 @@ void recover_set_hint(int savedNestLevel)
|
|||
u_sess->attr.attr_common.node_name = "";
|
||||
}
|
||||
|
||||
static bool has_foreign_table_in_rtable(Query* query)
|
||||
{
|
||||
ListCell* lc = NULL;
|
||||
RangeTblEntry* rte = NULL;
|
||||
foreach(lc, query->rtable) {
|
||||
rte = (RangeTblEntry*)lfirst(lc);
|
||||
if (rte->rtekind != RTE_RELATION) {
|
||||
continue;
|
||||
}
|
||||
if (rte->relkind == RELKIND_FOREIGN_TABLE) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* --------------------
|
||||
* subquery_planner
|
||||
* Invokes the planner on a subquery. We recurse to here for each
|
||||
|
@ -1757,6 +1773,9 @@ Plan* subquery_planner(PlannerGlobal* glob, Query* parse, PlannerInfo* parent_ro
|
|||
context.func_exprs = NIL;
|
||||
support_rewrite = false;
|
||||
}
|
||||
if (has_foreign_table_in_rtable(root->parse)) {
|
||||
support_rewrite = false;
|
||||
}
|
||||
if (support_rewrite) {
|
||||
reduce_inequality_fulljoins(root);
|
||||
DEBUG_QRW("After full join conversion");
|
||||
|
@ -2628,6 +2647,7 @@ static Plan* grouping_planner(PlannerInfo* root, double tuple_fraction)
|
|||
char PlanContextName[NAMEDATALEN] = {0};
|
||||
MemoryContext PlanGenerateContext = NULL;
|
||||
MemoryContext oldcontext = NULL;
|
||||
FDWUpperRelCxt* ufdwCxt = NULL;
|
||||
errno_t rc = EOK;
|
||||
|
||||
/*
|
||||
|
@ -2723,6 +2743,7 @@ static Plan* grouping_planner(PlannerInfo* root, double tuple_fraction)
|
|||
List* sub_tlist = NIL;
|
||||
double sub_limit_tuples;
|
||||
AttrNumber* groupColIdx = NULL;
|
||||
bool need_try_fdw_plan = false;
|
||||
bool need_tlist_eval = true;
|
||||
Path* cheapest_path = NULL;
|
||||
Path* sorted_path = NULL;
|
||||
|
@ -3095,6 +3116,11 @@ static Plan* grouping_planner(PlannerInfo* root, double tuple_fraction)
|
|||
|
||||
rel_info = best_path->parent;
|
||||
|
||||
/* if it is an foreign rel, try to create foreign plan continue */
|
||||
if (rel_info != NULL && rel_info->fdwroutine != NULL) {
|
||||
need_try_fdw_plan = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* For dummy plan, we should return it quickly. Meanwhile, we should
|
||||
* eliminate agg node, or an error will thrown out later
|
||||
|
@ -3229,6 +3255,10 @@ static Plan* grouping_planner(PlannerInfo* root, double tuple_fraction)
|
|||
|
||||
locate_grouping_columns(root, tlist, result_plan->targetlist, groupColIdx);
|
||||
}
|
||||
|
||||
if (need_try_fdw_plan) {
|
||||
ufdwCxt = InitFDWUpperPlan(root, rel_info, result_plan);
|
||||
}
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
/* shuffle to another node group in FORCE mode (CNG_MODE_FORCE) */
|
||||
if (IS_STREAM_PLAN && !parse->hasForUpdate &&
|
||||
|
@ -3838,6 +3868,18 @@ static Plan* grouping_planner(PlannerInfo* root, double tuple_fraction)
|
|||
result_plan = (Plan*)make_append(plans, tlist);
|
||||
}
|
||||
}
|
||||
|
||||
if (parse->hasAggs || parse->groupClause != NIL || parse->groupingSets || parse->havingQual != NULL) {
|
||||
if (ufdwCxt != NULL && ufdwCxt->state != FDW_UPPER_REL_END) {
|
||||
GroupPathExtraData *extra = (GroupPathExtraData*)palloc0(sizeof(GroupPathExtraData));
|
||||
extra->havingQual = parse->havingQual;
|
||||
extra->targetList = parse->targetList;
|
||||
extra->partial_costs_set = false;
|
||||
ufdwCxt->groupExtra = extra;
|
||||
AdvanceFDWUpperPlan(ufdwCxt, UPPERREL_GROUP_AGG, result_plan);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef PGXC
|
||||
/*
|
||||
* Grouping will certainly not increase the number of rows
|
||||
|
@ -4000,6 +4042,10 @@ static Plan* grouping_planner(PlannerInfo* root, double tuple_fraction)
|
|||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (ufdwCxt != NULL && ufdwCxt->state != FDW_UPPER_REL_END) {
|
||||
AdvanceFDWUpperPlan(ufdwCxt, UPPERREL_WINDOW, result_plan);
|
||||
}
|
||||
}
|
||||
(void)MemoryContextSwitchTo(oldcontext);
|
||||
} /* end of if (setOperations) */
|
||||
|
@ -4194,6 +4240,10 @@ static Plan* grouping_planner(PlannerInfo* root, double tuple_fraction)
|
|||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (ufdwCxt != NULL && ufdwCxt->state != FDW_UPPER_REL_END) {
|
||||
AdvanceFDWUpperPlan(ufdwCxt, UPPERREL_WINDOW, result_plan);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -4218,6 +4268,10 @@ static Plan* grouping_planner(PlannerInfo* root, double tuple_fraction)
|
|||
#ifndef PGXC
|
||||
current_pathkeys = NIL;
|
||||
#endif
|
||||
|
||||
if (ufdwCxt != NULL && ufdwCxt->state != FDW_UPPER_REL_END) {
|
||||
AdvanceFDWUpperPlan(ufdwCxt, UPPERREL_ROWMARKS, result_plan);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -4246,6 +4300,12 @@ static Plan* grouping_planner(PlannerInfo* root, double tuple_fraction)
|
|||
#endif /* PGXC */
|
||||
current_pathkeys = root->sort_pathkeys;
|
||||
}
|
||||
|
||||
if (ufdwCxt != NULL && ufdwCxt->state != FDW_UPPER_REL_END) {
|
||||
ufdwCxt->orderExtra = (OrderPathExtraData*)palloc(sizeof(OrderPathExtraData));
|
||||
ufdwCxt->orderExtra->targetList = result_plan->targetlist;
|
||||
AdvanceFDWUpperPlan(ufdwCxt, UPPERREL_ORDERED, result_plan);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -4275,6 +4335,27 @@ static Plan* grouping_planner(PlannerInfo* root, double tuple_fraction)
|
|||
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && !IS_STREAM)
|
||||
result_plan = (Plan*)create_remotelimit_plan(root, result_plan);
|
||||
#endif /* PGXC */
|
||||
|
||||
if (ufdwCxt != NULL && ufdwCxt->state != FDW_UPPER_REL_END) {
|
||||
ufdwCxt->finalExtra->limit_needed = true;
|
||||
ufdwCxt->finalExtra->limit_tuples = limit_tuples;
|
||||
ufdwCxt->finalExtra->count_est = count_est;
|
||||
ufdwCxt->finalExtra->offset_est = offset_est;
|
||||
AdvanceFDWUpperPlan(ufdwCxt, UPPERREL_LIMIT, result_plan);
|
||||
}
|
||||
}
|
||||
|
||||
if (ufdwCxt != NULL && ufdwCxt->state != FDW_UPPER_REL_END) {
|
||||
Assert(!IS_STREAM_PLAN);
|
||||
ufdwCxt->finalExtra->targetList = result_plan->targetlist;
|
||||
|
||||
AdvanceFDWUpperPlan(ufdwCxt, UPPERREL_FINAL, result_plan);
|
||||
|
||||
/* apply plan. */
|
||||
if (ufdwCxt->resultPlan != NULL) {
|
||||
result_plan = ufdwCxt->resultPlan;
|
||||
current_pathkeys = ufdwCxt->resultPathKeys;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef STREAMPLAN
|
||||
|
@ -9033,6 +9114,12 @@ static bool CheckWindowsAggExpr(Plan* resultPlan, bool check_rescan, VectorPlanC
|
|||
static bool CheckForeignScanExpr(Plan* resultPlan, VectorPlanContext* planContext)
|
||||
{
|
||||
ForeignScan* fscan = (ForeignScan*)resultPlan;
|
||||
|
||||
/* only support foreign scan for base rel, not support for join\agg\etc. */
|
||||
if (!OidIsValid(fscan->scan_relid)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsSpecifiedFDWFromRelid(fscan->scan_relid, GC_FDW) ||
|
||||
IsSpecifiedFDWFromRelid(fscan->scan_relid, LOG_FDW)) {
|
||||
resultPlan->vec_output = false;
|
||||
|
|
|
@ -133,6 +133,9 @@ static bool extract_query_dependencies_walker(Node* node, PlannerInfo* context);
|
|||
static void fix_skew_quals(PlannerInfo* root, Plan* plan, indexed_tlist* subplan_itlist, int rtoffset);
|
||||
static void fix_assign_targetlists(ModifyTable* plan, Plan* subplan, List* resultRelation);
|
||||
|
||||
static Relids offset_relid_set(Relids relids, int rtoffset);
|
||||
static void set_foreignscan_references(PlannerInfo *root, ForeignScan *fscan, int rtoffset);
|
||||
|
||||
#ifdef PGXC
|
||||
/* References for remote plans */
|
||||
static List* fix_remote_expr(PlannerInfo* root, List* clauses, indexed_tlist* base_itlist, Index newrelid, int rtoffset,
|
||||
|
@ -496,22 +499,7 @@ static Plan* set_plan_refs(PlannerInfo* root, Plan* plan, int rtoffset)
|
|||
break;
|
||||
case T_ForeignScan:
|
||||
case T_VecForeignScan: {
|
||||
ForeignScan* splan = (ForeignScan*)plan;
|
||||
|
||||
splan->scan.scanrelid += rtoffset;
|
||||
splan->scan.plan.targetlist = fix_scan_list(root, splan->scan.plan.targetlist, rtoffset);
|
||||
if (splan->scan.plan.distributed_keys != NIL) {
|
||||
splan->scan.plan.distributed_keys = fix_scan_list(root, splan->scan.plan.distributed_keys, rtoffset);
|
||||
}
|
||||
splan->scan.plan.var_list = fix_scan_list(root, splan->scan.plan.var_list, rtoffset);
|
||||
splan->scan.plan.qual = fix_scan_list(root, splan->scan.plan.qual, rtoffset);
|
||||
splan->fdw_exprs = fix_scan_list(root, splan->fdw_exprs, rtoffset);
|
||||
|
||||
if (isObsOrHdfsTableFormTblOid(splan->scan_relid)) {
|
||||
DefElem* private_data = (DefElem*)linitial(splan->fdw_private);
|
||||
DfsPrivateItem* item = (DfsPrivateItem*)private_data->arg;
|
||||
fix_dfs_private_item(root, rtoffset, item);
|
||||
}
|
||||
set_foreignscan_references(root, (ForeignScan*) plan, rtoffset);
|
||||
} break;
|
||||
case T_NestLoop:
|
||||
case T_VecNestLoop:
|
||||
|
@ -2727,3 +2715,75 @@ void pgxc_set_agg_references(PlannerInfo* root, Agg* aggplan)
|
|||
return;
|
||||
}
|
||||
#endif /* PGXC */
|
||||
|
||||
/*
|
||||
* offset_relid_set
|
||||
* Apply rtoffset to the members of a Relids set.
|
||||
*/
|
||||
static Relids offset_relid_set(Relids relids, int rtoffset)
|
||||
{
|
||||
Relids result = NULL;
|
||||
int rtindex;
|
||||
|
||||
/* If there's no offset to apply, we needn't recompute the value */
|
||||
if (rtoffset == 0) {
|
||||
return relids;
|
||||
}
|
||||
rtindex = -1;
|
||||
while ((rtindex = bms_next_member(relids, rtindex)) >= 0) {
|
||||
result = bms_add_member(result, rtindex + rtoffset);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* set_foreignscan_references
|
||||
* Do set_plan_references processing on a ForeignScan
|
||||
*/
|
||||
static void set_foreignscan_references(PlannerInfo *root, ForeignScan *fscan, int rtoffset)
|
||||
{
|
||||
/* Adjust scanrelid if it's valid */
|
||||
if (fscan->scan.scanrelid > 0) {
|
||||
fscan->scan.scanrelid += rtoffset;
|
||||
}
|
||||
|
||||
if (fscan->fdw_scan_tlist != NIL || fscan->scan.scanrelid == 0) {
|
||||
/*
|
||||
* Adjust tlist, qual, fdw_exprs, fdw_recheck_quals to reference
|
||||
* foreign scan tuple
|
||||
*/
|
||||
indexed_tlist *itlist = build_tlist_index(fscan->fdw_scan_tlist);
|
||||
|
||||
fscan->scan.plan.targetlist =
|
||||
(List *)fix_upper_expr(root, (Node *)fscan->scan.plan.targetlist, itlist, INDEX_VAR, rtoffset);
|
||||
fscan->scan.plan.qual =
|
||||
(List *)fix_upper_expr(root, (Node *)fscan->scan.plan.qual, itlist, INDEX_VAR, rtoffset);
|
||||
fscan->fdw_exprs = (List *)fix_upper_expr(root, (Node *)fscan->fdw_exprs, itlist, INDEX_VAR, rtoffset);
|
||||
fscan->fdw_recheck_quals =
|
||||
(List *)fix_upper_expr(root, (Node *)fscan->fdw_recheck_quals, itlist, INDEX_VAR, rtoffset);
|
||||
pfree(itlist);
|
||||
/* fdw_scan_tlist itself just needs fix_scan_list() adjustments */
|
||||
fscan->fdw_scan_tlist = fix_scan_list(root, fscan->fdw_scan_tlist, rtoffset);
|
||||
} else {
|
||||
/*
|
||||
* Adjust tlist, qual, fdw_exprs, fdw_recheck_quals in the standard
|
||||
* way
|
||||
*/
|
||||
fscan->scan.plan.targetlist = fix_scan_list(root, fscan->scan.plan.targetlist, rtoffset);
|
||||
fscan->scan.plan.qual = fix_scan_list(root, fscan->scan.plan.qual, rtoffset);
|
||||
fscan->fdw_exprs = fix_scan_list(root, fscan->fdw_exprs, rtoffset);
|
||||
fscan->fdw_recheck_quals = fix_scan_list(root, fscan->fdw_recheck_quals, rtoffset);
|
||||
}
|
||||
|
||||
if (fscan->scan.plan.distributed_keys != NIL) {
|
||||
fscan->scan.plan.distributed_keys = fix_scan_list(root, fscan->scan.plan.distributed_keys, rtoffset);
|
||||
}
|
||||
|
||||
fscan->fs_relids = offset_relid_set(fscan->fs_relids, rtoffset);
|
||||
|
||||
if (fscan->scan_relid > 0 && isObsOrHdfsTableFormTblOid(fscan->scan_relid)) {
|
||||
DefElem* private_data = (DefElem*)linitial(fscan->fdw_private);
|
||||
DfsPrivateItem* item = (DfsPrivateItem*)private_data->arg;
|
||||
fix_dfs_private_item(root, rtoffset, item);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,9 +56,12 @@ List* check_op_list_template(Plan* result_plan, List* (*check_eval)(Node*))
|
|||
case T_ForeignScan:
|
||||
case T_VecForeignScan: {
|
||||
ForeignScan* foreignScan = (ForeignScan*)result_plan;
|
||||
if (!OidIsValid(foreignScan->scan_relid)) {
|
||||
break;
|
||||
}
|
||||
|
||||
ForeignTable* ftbl = NULL;
|
||||
ForeignServer* fsvr = NULL;
|
||||
|
||||
ftbl = GetForeignTable(foreignScan->scan_relid);
|
||||
AssertEreport(NULL != ftbl, MOD_OPT, "The foreign table is NULL");
|
||||
fsvr = GetForeignServer(ftbl->serverid);
|
||||
|
|
|
@ -3779,7 +3779,7 @@ Path* create_worktablescan_path(PlannerInfo* root, RelOptInfo* rel)
|
|||
* way to calculate them in core.
|
||||
*/
|
||||
ForeignPath* create_foreignscan_path(PlannerInfo* root, RelOptInfo* rel, Cost startup_cost, Cost total_cost,
|
||||
List* pathkeys, Relids required_outer, List* fdw_private, int dop)
|
||||
List* pathkeys, Relids required_outer, Path* fdw_outerpath, List* fdw_private, int dop)
|
||||
{
|
||||
ForeignPath* pathnode = makeNode(ForeignPath);
|
||||
|
||||
|
@ -3793,14 +3793,14 @@ ForeignPath* create_foreignscan_path(PlannerInfo* root, RelOptInfo* rel, Cost st
|
|||
pathnode->path.locator_type = rel->locator_type;
|
||||
pathnode->path.exec_type = SetBasePathExectype(root, rel);
|
||||
pathnode->path.stream_cost = 0;
|
||||
|
||||
pathnode->fdw_outerpath = fdw_outerpath;
|
||||
pathnode->fdw_private = fdw_private;
|
||||
|
||||
pathnode->path.dop = 1;
|
||||
dop = SET_DOP(dop);
|
||||
|
||||
/* Create a parallel foreignscan path. */
|
||||
if (root->parse && dop > 1) {
|
||||
if (root->parse && dop > 1 && IS_SIMPLE_REL(rel)) {
|
||||
RangeTblEntry* source = rt_fetch(rel->relid, root->parse->rtable);
|
||||
|
||||
AssertEreport(NULL != source, MOD_OPT_JOIN, "There should be rtable in table list");
|
||||
|
@ -3824,6 +3824,8 @@ ForeignPath* create_foreignscan_path(PlannerInfo* root, RelOptInfo* rel, Cost st
|
|||
* It is comfortable to add smp foreign scan for this scenario.
|
||||
* HDFS Server: we don't add smp feature for this kind of server. No reason.
|
||||
* Others: Keep constant with the original logic.
|
||||
*
|
||||
* Notice: Only Base scan is supported, join\agg is not supported.
|
||||
*/
|
||||
if (T_OBS_SERVER == serverType) {
|
||||
if ((CMD_SELECT == root->parse->commandType || CMD_INSERT == root->parse->commandType) &&
|
||||
|
|
|
@ -716,10 +716,13 @@ void get_relation_info(PlannerInfo* root, Oid relationObjectId, bool inhparent,
|
|||
setRelStoreInfo(rel, relation);
|
||||
|
||||
/* Grab the fdwroutine info using the relcache, while we have it */
|
||||
if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE || relation->rd_rel->relkind == RELKIND_STREAM)
|
||||
if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE || relation->rd_rel->relkind == RELKIND_STREAM) {
|
||||
rel->serverid = GetForeignServerIdByRelId(RelationGetRelid(relation));
|
||||
rel->fdwroutine = GetFdwRoutineForRelation(relation, true);
|
||||
else
|
||||
} else {
|
||||
rel->serverid = InvalidOid;
|
||||
rel->fdwroutine = NULL;
|
||||
}
|
||||
|
||||
heap_close(relation, NoLock);
|
||||
|
||||
|
|
|
@ -201,6 +201,9 @@ RelOptInfo* build_simple_rel(PlannerInfo* root, int relid, RelOptKind reloptkind
|
|||
rel->subplan = NULL;
|
||||
rel->subroot = NULL;
|
||||
rel->subplan_params = NIL;
|
||||
rel->serverid = InvalidOid;
|
||||
rel->userid = rte->checkAsUser;
|
||||
rel->useridiscurrent = false;
|
||||
rel->fdwroutine = NULL;
|
||||
rel->fdw_private = NULL;
|
||||
rel->baserestrictinfo = NIL;
|
||||
|
@ -455,6 +458,45 @@ RelOptInfo* find_join_rel(PlannerInfo* root, Relids relids)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* set_foreign_rel_properties
|
||||
* Set up foreign-join fields if outer and inner relation are foreign
|
||||
* tables (or joins) belonging to the same server and assigned to the same
|
||||
* user to check access permissions as.
|
||||
*
|
||||
* In addition to an exact match of userid, we allow the case where one side
|
||||
* has zero userid (implying current user) and the other side has explicit
|
||||
* userid that happens to equal the current user; but in that case, pushdown of
|
||||
* the join is only valid for the current user. The useridiscurrent field
|
||||
* records whether we had to make such an assumption for this join or any
|
||||
* sub-join.
|
||||
*
|
||||
* Otherwise these fields are left invalid, so GetForeignJoinPaths will not be
|
||||
* called for the join relation.
|
||||
*
|
||||
*/
|
||||
static void set_foreign_rel_properties(RelOptInfo *joinrel, RelOptInfo *outer_rel, RelOptInfo *inner_rel)
|
||||
{
|
||||
if (OidIsValid(outer_rel->serverid) && inner_rel->serverid == outer_rel->serverid) {
|
||||
if (inner_rel->userid == outer_rel->userid) {
|
||||
joinrel->serverid = outer_rel->serverid;
|
||||
joinrel->userid = outer_rel->userid;
|
||||
joinrel->useridiscurrent = outer_rel->useridiscurrent || inner_rel->useridiscurrent;
|
||||
joinrel->fdwroutine = outer_rel->fdwroutine;
|
||||
} else if (!OidIsValid(inner_rel->userid) && outer_rel->userid == GetUserId()) {
|
||||
joinrel->serverid = outer_rel->serverid;
|
||||
joinrel->userid = outer_rel->userid;
|
||||
joinrel->useridiscurrent = true;
|
||||
joinrel->fdwroutine = outer_rel->fdwroutine;
|
||||
} else if (!OidIsValid(outer_rel->userid) && inner_rel->userid == GetUserId()) {
|
||||
joinrel->serverid = outer_rel->serverid;
|
||||
joinrel->userid = inner_rel->userid;
|
||||
joinrel->useridiscurrent = true;
|
||||
joinrel->fdwroutine = outer_rel->fdwroutine;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void remove_join_rel(PlannerInfo *root, RelOptInfo *rel)
|
||||
{
|
||||
root->join_rel_level[root->join_cur_level] =
|
||||
|
@ -631,10 +673,14 @@ RelOptInfo* build_join_rel(PlannerInfo* root, Relids joinrelids, RelOptInfo* out
|
|||
joinrel->joininfo = NIL;
|
||||
joinrel->has_eclass_joins = false;
|
||||
joinrel->varratio = NIL;
|
||||
if (IsLocatorReplicated(inner_rel->locator_type) && IsLocatorReplicated(outer_rel->locator_type))
|
||||
if (IsLocatorReplicated(inner_rel->locator_type) && IsLocatorReplicated(outer_rel->locator_type)) {
|
||||
joinrel->locator_type = LOCATOR_TYPE_REPLICATED;
|
||||
else
|
||||
} else {
|
||||
joinrel->locator_type = LOCATOR_TYPE_NONE;
|
||||
}
|
||||
|
||||
/* Compute information relevant to the foreign relations. */
|
||||
set_foreign_rel_properties(joinrel, outer_rel, inner_rel);
|
||||
|
||||
/*
|
||||
* Create a new tlist containing just the vars that need to be output from
|
||||
|
|
|
@ -43,8 +43,17 @@ static TupleTableSlot* ExecScanFetch(ScanState* node, ExecScanAccessMtd access_m
|
|||
*/
|
||||
Index scan_rel_id = ((Scan*)node->ps.plan)->scanrelid;
|
||||
|
||||
Assert(scan_rel_id > 0);
|
||||
if (estate->es_epqTupleSet[scan_rel_id - 1]) {
|
||||
if (scan_rel_id == 0) {
|
||||
/*
|
||||
* This is a ForeignScan which has pushed down a
|
||||
* join to the remote side. The recheck method is responsible not
|
||||
* only for rechecking the scan/join quals but also for storing
|
||||
* the correct tuple in the slot.
|
||||
*
|
||||
* currently not support.
|
||||
*/
|
||||
Assert(false);
|
||||
} else if (estate->es_epqTupleSet[scan_rel_id - 1]) {
|
||||
TupleTableSlot* slot = node->ss_ScanTupleSlot;
|
||||
|
||||
/* Return empty slot if we already returned a tuple */
|
||||
|
|
|
@ -71,7 +71,8 @@ static TupleTableSlot* ForeignNext(ForeignScanState* node)
|
|||
if (plan->fsSystemCol && !TupIsNull(slot)) {
|
||||
HeapTuple tup = ExecMaterializeSlot(slot);
|
||||
|
||||
tup->t_tableOid = RelationGetRelid(node->ss.ss_currentRelation);
|
||||
if (!OidIsValid(tup->t_tableOid))
|
||||
tup->t_tableOid = RelationGetRelid(node->ss.ss_currentRelation);
|
||||
tup->t_bucketId = RelationGetBktid(node->ss.ss_currentRelation);
|
||||
#ifdef PGXC
|
||||
tup->t_xc_node_id = u_sess->pgxc_cxt.PGXCNodeIdentifier;
|
||||
|
@ -86,7 +87,6 @@ static TupleTableSlot* ForeignNext(ForeignScanState* node)
|
|||
*/
|
||||
static bool ForeignRecheck(ForeignScanState* node, TupleTableSlot* slot)
|
||||
{
|
||||
/* There are no access-method-specific conditions to recheck. */
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -111,8 +111,10 @@ TupleTableSlot* ExecForeignScan(ForeignScanState* node)
|
|||
ForeignScanState* ExecInitForeignScan(ForeignScan* node, EState* estate, int eflags)
|
||||
{
|
||||
ForeignScanState* scanstate = NULL;
|
||||
Relation currentRelation;
|
||||
Relation currentRelation = NULL;
|
||||
FdwRoutine* fdwroutine = NULL;
|
||||
Index scanrelid = node->scan.scanrelid;
|
||||
int tlistvarno;
|
||||
errno_t rc;
|
||||
|
||||
/* check for unsupported flags */
|
||||
|
@ -159,25 +161,65 @@ ForeignScanState* ExecInitForeignScan(ForeignScan* node, EState* estate, int efl
|
|||
|
||||
/*
|
||||
* open the base relation and acquire appropriate lock on it.
|
||||
* or it is a foreign join, we should recreate resultdesc and project info
|
||||
*/
|
||||
if (node->rel == NULL) {
|
||||
currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid);
|
||||
} else {
|
||||
if (node->in_compute_pool == false) {
|
||||
if (scanrelid > 0) {
|
||||
if (node->rel == NULL) {
|
||||
currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid);
|
||||
} else {
|
||||
currentRelation = get_rel_from_meta(node->rel);
|
||||
scanstate->options = node->options;
|
||||
if (node->in_compute_pool == false) {
|
||||
currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid);
|
||||
} else {
|
||||
currentRelation = get_rel_from_meta(node->rel);
|
||||
scanstate->options = node->options;
|
||||
}
|
||||
}
|
||||
scanstate->ss.ss_currentRelation = currentRelation;
|
||||
|
||||
/*
|
||||
* Acquire function pointers from the FDW's handler, and init fdw_state.
|
||||
*/
|
||||
if (!node->rel) {
|
||||
fdwroutine = GetFdwRoutineForRelation(currentRelation, true);
|
||||
} else {
|
||||
ForeignDataWrapper* fdw = NULL;
|
||||
if (node->options->stype == T_OBS_SERVER || node->options->stype == T_HDFS_SERVER) {
|
||||
fdw = GetForeignDataWrapperByName(HDFS_FDW, false);
|
||||
} else {
|
||||
fdw = GetForeignDataWrapperByName(DIST_FDW, false);
|
||||
}
|
||||
fdwroutine = GetFdwRoutine(fdw->fdwhandler);
|
||||
|
||||
/* Save the data for later reuse in LocalMyDBCacheMemCxt */
|
||||
FdwRoutine* cfdwroutine = (FdwRoutine*)MemoryContextAlloc(LocalMyDBCacheMemCxt(), sizeof(FdwRoutine));
|
||||
rc = memcpy_s(cfdwroutine, sizeof(FdwRoutine), fdwroutine, sizeof(FdwRoutine));
|
||||
securec_check(rc, "\0", "\0");
|
||||
currentRelation->rd_fdwroutine = cfdwroutine;
|
||||
}
|
||||
} else {
|
||||
/* We can't use the relcache, so get fdwroutine the hard way */
|
||||
fdwroutine = GetFdwRoutineByServerId(node->fs_server);
|
||||
}
|
||||
|
||||
scanstate->ss.ss_currentRelation = currentRelation;
|
||||
|
||||
/*
|
||||
* get the scan type from the relation descriptor. (XXX at some point we
|
||||
* might want to let the FDW editorialize on the scan tupdesc.)
|
||||
* Determine the scan tuple type. If the FDW provided a targetlist
|
||||
* describing the scan tuples, use that; else use base relation's rowtype.
|
||||
*/
|
||||
ExecAssignScanType(&scanstate->ss, RelationGetDescr(currentRelation));
|
||||
if (node->fdw_scan_tlist != NIL || currentRelation == NULL) {
|
||||
TupleDesc scan_tupdesc;
|
||||
|
||||
scan_tupdesc = ExecTypeFromTL(node->fdw_scan_tlist, false);
|
||||
ExecAssignScanType(&scanstate->ss, scan_tupdesc);
|
||||
tlistvarno = INDEX_VAR;
|
||||
} else {
|
||||
TupleDesc scan_tupdesc;
|
||||
|
||||
/* don't trust FDWs to return tuples fulfilling NOT NULL constraints */
|
||||
scan_tupdesc = CreateTupleDescCopy(RelationGetDescr(currentRelation));
|
||||
ExecAssignScanType(&scanstate->ss, scan_tupdesc);
|
||||
/* Node's targetlist will contain Vars with varno = scanrelid */
|
||||
tlistvarno = scanrelid;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize result tuple type and projection info.
|
||||
|
@ -185,34 +227,16 @@ ForeignScanState* ExecInitForeignScan(ForeignScan* node, EState* estate, int efl
|
|||
ExecAssignResultTypeFromTL(
|
||||
&scanstate->ss.ps,
|
||||
scanstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor->tdTableAmType);
|
||||
ExecAssignScanProjectionInfo(&scanstate->ss);
|
||||
Assert(scanstate->ss.ps.ps_ResultTupleSlot->tts_tupleDescriptor->tdTableAmType != TAM_INVALID);
|
||||
|
||||
/*
|
||||
* Acquire function pointers from the FDW's handler, and init fdw_state.
|
||||
*/
|
||||
if (!node->rel) {
|
||||
fdwroutine = GetFdwRoutineForRelation(currentRelation, true);
|
||||
} else {
|
||||
ForeignDataWrapper* fdw = NULL;
|
||||
if (node->options->stype == T_OBS_SERVER || node->options->stype == T_HDFS_SERVER) {
|
||||
fdw = GetForeignDataWrapperByName(HDFS_FDW, false);
|
||||
} else {
|
||||
fdw = GetForeignDataWrapperByName(DIST_FDW, false);
|
||||
}
|
||||
|
||||
fdwroutine = GetFdwRoutine(fdw->fdwhandler);
|
||||
|
||||
/* Save the data for later reuse in LocalMyDBCacheMemCxt */
|
||||
FdwRoutine* cfdwroutine = (FdwRoutine*)MemoryContextAlloc(LocalMyDBCacheMemCxt(), sizeof(FdwRoutine));
|
||||
rc = memcpy_s(cfdwroutine, sizeof(FdwRoutine), fdwroutine, sizeof(FdwRoutine));
|
||||
securec_check(rc, "\0", "\0");
|
||||
currentRelation->rd_fdwroutine = cfdwroutine;
|
||||
}
|
||||
ExecAssignScanProjectionInfoWithVarno(&scanstate->ss, tlistvarno);
|
||||
|
||||
scanstate->fdwroutine = fdwroutine;
|
||||
scanstate->fdw_state = NULL;
|
||||
|
||||
/* Initialize any outer plan. */
|
||||
if (outerPlan(node)) {
|
||||
outerPlanState(scanstate) = ExecInitNode(outerPlan(node), estate, eflags);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_MOT
|
||||
if ((estate->mot_jit_context == NULL) || IS_PGXC_COORDINATOR || !JitExec::IsMotCodegenEnabled()) {
|
||||
#endif
|
||||
|
@ -250,6 +274,10 @@ void ExecEndForeignScan(ForeignScanState* node)
|
|||
}
|
||||
#endif
|
||||
|
||||
/* Shut down any outer plan. */
|
||||
if (outerPlanState(node))
|
||||
ExecEndNode(outerPlanState(node));
|
||||
|
||||
/* Free the exprcontext */
|
||||
ExecFreeExprContext(&node->ss.ps);
|
||||
|
||||
|
@ -259,11 +287,13 @@ void ExecEndForeignScan(ForeignScanState* node)
|
|||
|
||||
/* close the relation. */
|
||||
ForeignScan* scan = (ForeignScan*)node->ss.ps.plan;
|
||||
if (NULL == scan->rel) {
|
||||
ExecCloseScanRelation(node->ss.ss_currentRelation);
|
||||
} else {
|
||||
if (false == scan->in_compute_pool) {
|
||||
if (node->ss.ss_currentRelation != NULL) {
|
||||
if (NULL == scan->rel) {
|
||||
ExecCloseScanRelation(node->ss.ss_currentRelation);
|
||||
} else {
|
||||
if (false == scan->in_compute_pool) {
|
||||
ExecCloseScanRelation(node->ss.ss_currentRelation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1198,7 +1198,7 @@ void distImportGetPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntable
|
|||
*/
|
||||
add_path(root, baserel,
|
||||
(Path *)create_foreignscan_path(root, baserel, startup_cost, total_cost, NIL, /* no pathkeys */
|
||||
NULL, /* no outer rel either */
|
||||
NULL, NULL, /* no outer rel and path either */
|
||||
coptions, u_sess->opt_cxt.query_dop));
|
||||
}
|
||||
|
||||
|
@ -1207,7 +1207,7 @@ void distImportGetPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntable
|
|||
* Create a ForeignScan plan node for scanning the foreign table
|
||||
*/
|
||||
ForeignScan *distImportGetPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid, ForeignPath *best_path,
|
||||
List *tlist, List *scan_clauses)
|
||||
List *tlist, List *scan_clauses, Plan *outer_plan)
|
||||
{
|
||||
Index scan_relid = baserel->relid;
|
||||
DistImportPlanState *planstate = (DistImportPlanState *)baserel->fdw_private;
|
||||
|
@ -1252,7 +1252,7 @@ ForeignScan *distImportGetPlan(PlannerInfo *root, RelOptInfo *baserel, Oid forei
|
|||
|
||||
/* Create the ForeignScan node */
|
||||
ForeignScan *fScan = make_foreignscan(tlist, scan_clauses, scan_relid, NIL, /* no expressions to evaluate */
|
||||
fdw_data, EXEC_ON_DATANODES); /* no private state either */
|
||||
fdw_data, NIL, NIL, NULL, EXEC_ON_DATANODES); /* no private state either */
|
||||
|
||||
fScan->objectNum = fileNum;
|
||||
if (root->parse->commandType != CMD_INSERT && is_obs_protocol(HdfsGetOptionValue(foreigntableid, optLocation))) {
|
||||
|
|
|
@ -127,7 +127,7 @@ PG_FUNCTION_INFO_V1(mot_fdw_validator);
|
|||
static void MOTGetForeignRelSize(PlannerInfo* root, RelOptInfo* baserel, Oid foreigntableid);
|
||||
static void MOTGetForeignPaths(PlannerInfo* root, RelOptInfo* baserel, Oid foreigntableid);
|
||||
static ForeignScan* MOTGetForeignPlan(PlannerInfo* root, RelOptInfo* baserel, Oid foreigntableid,
|
||||
ForeignPath* best_path, List* tlist, List* scan_clauses);
|
||||
ForeignPath* best_path, List* tlist, List* scan_clauses, Plan *outer_plan);
|
||||
static void MOTExplainForeignScan(ForeignScanState* node, ExplainState* es);
|
||||
static void MOTBeginForeignScan(ForeignScanState* node, int eflags);
|
||||
static TupleTableSlot* MOTIterateForeignScan(ForeignScanState* node);
|
||||
|
@ -621,7 +621,8 @@ static void MOTGetForeignPaths(PlannerInfo* root, RelOptInfo* baserel, Oid forei
|
|||
planstate->m_startupCost,
|
||||
planstate->m_totalCost,
|
||||
usablePathkeys,
|
||||
nullptr, /* no outer rel either */
|
||||
nullptr, /* no outer rel either */
|
||||
nullptr, /* no outer path either */
|
||||
nullptr, // private data will be assigned later
|
||||
0);
|
||||
|
||||
|
@ -691,6 +692,7 @@ static void MOTGetForeignPaths(PlannerInfo* root, RelOptInfo* baserel, Oid forei
|
|||
planstate->m_totalCost,
|
||||
usablePathkeys,
|
||||
nullptr, /* no outer rel either */
|
||||
nullptr, /* no outer path either */
|
||||
nullptr, // private data will be assigned later
|
||||
0);
|
||||
|
||||
|
@ -721,8 +723,8 @@ static void MOTGetForeignPaths(PlannerInfo* root, RelOptInfo* baserel, Oid forei
|
|||
/*
|
||||
*
|
||||
*/
|
||||
static ForeignScan* MOTGetForeignPlan(
|
||||
PlannerInfo* root, RelOptInfo* baserel, Oid foreigntableid, ForeignPath* best_path, List* tlist, List* scan_clauses)
|
||||
static ForeignScan *MOTGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid,
|
||||
ForeignPath *best_path, List *tlist, List *scan_clauses, Plan *outer_plan)
|
||||
{
|
||||
ListCell* lc = nullptr;
|
||||
::Index scanRelid = baserel->relid;
|
||||
|
@ -789,7 +791,10 @@ static ForeignScan* MOTGetForeignPlan(
|
|||
quals,
|
||||
scanRelid,
|
||||
remote, /* no expressions to evaluate */
|
||||
(List*)SerializeFdwState(planstate)
|
||||
(List*)SerializeFdwState(planstate),
|
||||
NIL,
|
||||
NIL,
|
||||
NULL
|
||||
#if PG_VERSION_NUM >= 90500
|
||||
,
|
||||
nullptr,
|
||||
|
|
|
@ -83,7 +83,7 @@ extern void ProcessDistImportOptions(DistImportPlanState *planstate, List *optio
|
|||
extern void distImportGetRelSize(PlannerInfo* root, RelOptInfo* baserel, Oid foreigntableid);
|
||||
extern void distImportGetPaths(PlannerInfo* root, RelOptInfo* baserel, Oid foreigntableid);
|
||||
extern ForeignScan* distImportGetPlan(PlannerInfo* root, RelOptInfo* baserel, Oid foreigntableid,
|
||||
ForeignPath* best_path, List* tlist, List* scan_clauses);
|
||||
ForeignPath* best_path, List* tlist, List* scan_clauses, Plan *outer_plan);
|
||||
|
||||
extern void distImportExplain(ForeignScanState* node, ExplainState* es);
|
||||
|
||||
|
|
|
@ -472,6 +472,7 @@ typedef struct ExplainState {
|
|||
char* statement_id; /* statement_id for EXPLAIN PLAN */
|
||||
bool is_explain_gplan;
|
||||
char* opt_model_name;
|
||||
ExplainFRSqlState es_frs; /* explain state for remote sql of foreign scan. */
|
||||
} ExplainState;
|
||||
|
||||
/* Hook for plugins to get control in explain_get_index_name() */
|
||||
|
|
|
@ -30,7 +30,7 @@ typedef void (*GetForeignRelSize_function)(PlannerInfo* root, RelOptInfo* basere
|
|||
typedef void (*GetForeignPaths_function)(PlannerInfo* root, RelOptInfo* baserel, Oid foreigntableid);
|
||||
|
||||
typedef ForeignScan* (*GetForeignPlan_function)(PlannerInfo* root, RelOptInfo* baserel, Oid foreigntableid,
|
||||
ForeignPath* best_path, List* tlist, List* scan_clauses);
|
||||
ForeignPath* best_path, List* tlist, List* scan_clauses, Plan *outer_plan);
|
||||
|
||||
typedef void (*BeginForeignScan_function)(ForeignScanState* node, int eflags);
|
||||
|
||||
|
@ -40,6 +40,11 @@ typedef void (*ReScanForeignScan_function)(ForeignScanState* node);
|
|||
|
||||
typedef void (*EndForeignScan_function)(ForeignScanState* node);
|
||||
|
||||
typedef void (*GetForeignJoinPaths_function)(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel,
|
||||
RelOptInfo *innerrel, JoinType jointype, SpecialJoinInfo* sjinfo, List* restrictlist);
|
||||
|
||||
typedef void (*GetForeignUpperPaths_function)(FDWUpperRelCxt* ufdwCxt, UpperRelationKind stage, Plan* mainPlan);
|
||||
|
||||
typedef void (*AddForeignUpdateTargets_function)(Query* parsetree, RangeTblEntry* target_rte, Relation target_relation);
|
||||
|
||||
typedef List* (*PlanForeignModify_function)(
|
||||
|
@ -63,6 +68,8 @@ typedef int (*IsForeignRelUpdatable_function)(Relation rel);
|
|||
|
||||
typedef void (*ExplainForeignScan_function)(ForeignScanState* node, struct ExplainState* es);
|
||||
|
||||
typedef void (*ExplainForeignScanRemote_function)(ForeignScanState* node, struct ExplainState* es);
|
||||
|
||||
typedef void (*ExplainForeignModify_function)(
|
||||
ModifyTableState* mtstate, ResultRelInfo* rinfo, List* fdw_private, int subplan_index, struct ExplainState* es);
|
||||
|
||||
|
@ -140,6 +147,12 @@ typedef struct FdwRoutine {
|
|||
* These functions are optional. Set the pointer to NULL for any that are
|
||||
* not provided.
|
||||
*/
|
||||
|
||||
/* Functions for remote-join planning */
|
||||
GetForeignJoinPaths_function GetForeignJoinPaths;
|
||||
|
||||
/* Functions for remote upper-relation (post scan/join) planning */
|
||||
GetForeignUpperPaths_function GetForeignUpperPaths;
|
||||
|
||||
/* Functions for updating foreign tables */
|
||||
AddForeignUpdateTargets_function AddForeignUpdateTargets;
|
||||
|
@ -154,6 +167,7 @@ typedef struct FdwRoutine {
|
|||
/* Support functions for EXPLAIN */
|
||||
ExplainForeignScan_function ExplainForeignScan;
|
||||
ExplainForeignModify_function ExplainForeignModify;
|
||||
ExplainForeignScanRemote_function ExplainForeignScanRemote;
|
||||
|
||||
/* @hdfs Support functions for ANALYZE */
|
||||
AnalyzeForeignTable_function AnalyzeForeignTable;
|
||||
|
@ -202,5 +216,6 @@ extern FdwRoutine* GetFdwRoutine(Oid fdwhandler);
|
|||
extern FdwRoutine* GetFdwRoutineByRelId(Oid relid, bool missHandlerOk = false);
|
||||
extern FdwRoutine* GetFdwRoutineByServerId(Oid serverid);
|
||||
extern FdwRoutine* GetFdwRoutineForRelation(Relation relation, bool makecopy);
|
||||
extern Oid GetForeignServerIdByRelId(Oid relid);
|
||||
|
||||
#endif /* FDWAPI_H */
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "access/obs/obs_am.h"
|
||||
#include "commands/defrem.h"
|
||||
#include "nodes/parsenodes.h"
|
||||
#include "nodes/relation.h"
|
||||
|
||||
#ifndef OBS_SERVER
|
||||
#define OBS_SERVER "obs"
|
||||
|
@ -223,7 +224,7 @@ extern bool IsSpecifiedFDWFromRelid(Oid relId, const char* SepcifiedType);
|
|||
* @in relId: The foreign table Oid.
|
||||
* @return Rreturn true if the foreign table support those DML.
|
||||
*/
|
||||
extern bool CheckSupportedFDWType(Oid relId);
|
||||
extern bool CheckSupportedFDWType(Oid oid, bool byServerId = false);
|
||||
|
||||
/**
|
||||
* @Description: Get the all options for the OBS foreign table.
|
||||
|
@ -304,11 +305,101 @@ void CheckGetServerIpAndPort(const char *Address, List **AddrList, bool IsCheck,
|
|||
|
||||
bool isWriteOnlyFt(Oid relid);
|
||||
|
||||
/*
|
||||
* state for explain foreign remote sql
|
||||
*/
|
||||
typedef struct ExplainFRSqlState {
|
||||
struct ExplainState* parent;
|
||||
int node_num;
|
||||
StringInfo str; /* output buffer */
|
||||
} ExplainFRSqlState;
|
||||
|
||||
typedef struct SPJPathExtraData {
|
||||
List *targetList;
|
||||
} SPJPathExtraData;
|
||||
|
||||
/*
|
||||
* Struct for extra information passed to subroutines of create_grouping_paths
|
||||
*
|
||||
* flags indicating what kinds of grouping are possible.
|
||||
* partial_costs_set is true if the agg_partial_costs and agg_final_costs
|
||||
* have been initialized.
|
||||
* agg_partial_costs gives partial aggregation costs.
|
||||
* agg_final_costs gives finalization costs.
|
||||
* target_parallel_safe is true if target is parallel safe.
|
||||
* havingQual gives list of quals to be applied after aggregation.
|
||||
* targetList gives list of columns to be projected.
|
||||
* patype is the type of partitionwise aggregation that is being performed.
|
||||
*/
|
||||
typedef struct GroupPathExtraData {
|
||||
/* Data which remains constant once set. */
|
||||
int flags;
|
||||
bool partial_costs_set;
|
||||
AggClauseCosts agg_partial_costs;
|
||||
AggClauseCosts agg_final_costs;
|
||||
|
||||
/* Data which may differ across partitions. */
|
||||
Node *havingQual;
|
||||
List *targetList;
|
||||
} GroupPathExtraData;
|
||||
|
||||
typedef double Cardinality; /* (estimated) number of rows or other integer count */
|
||||
|
||||
|
||||
typedef struct OrderPathExtraData {
|
||||
List *targetList;
|
||||
List *irel_tel; // target entry list of lefttree
|
||||
} OrderPathExtraData;
|
||||
|
||||
/*
|
||||
* Struct for extra information passed to subroutines of grouping_planner
|
||||
*
|
||||
* limit_needed is true if we actually need a Limit plan node.
|
||||
* limit_tuples is an estimated bound on the number of output tuples,
|
||||
* or -1 if no LIMIT or couldn't estimate.
|
||||
* count_est and offset_est are the estimated values of the LIMIT and OFFSET
|
||||
* expressions computed by preprocess_limit() (see comments for
|
||||
* preprocess_limit() for more information).
|
||||
*/
|
||||
typedef struct FinalPathExtraData {
|
||||
bool limit_needed;
|
||||
Cardinality limit_tuples;
|
||||
int64 count_est;
|
||||
int64 offset_est;
|
||||
|
||||
List* targetList;
|
||||
} FinalPathExtraData;
|
||||
|
||||
typedef enum FDWUpperRelState {
|
||||
FDW_UPPER_REL_INIT,
|
||||
FDW_UPPER_REL_TRY,
|
||||
FDW_UPPER_REL_END
|
||||
} FDWUpperRelState;
|
||||
|
||||
typedef struct FDWUpperRelCxt {
|
||||
FDWUpperRelState state;
|
||||
PlannerInfo* root;
|
||||
RelOptInfo* currentRel; // current rel to create path
|
||||
RelOptInfo* upperRels[UPPERREL_FINAL + 1]; // the rel that we using to create foreign path
|
||||
SPJPathExtraData* spjExtra;
|
||||
GroupPathExtraData* groupExtra;
|
||||
OrderPathExtraData* orderExtra;
|
||||
FinalPathExtraData* finalExtra;
|
||||
|
||||
/* result */
|
||||
Plan* resultPlan;
|
||||
List* resultPathKeys;
|
||||
} FDWUpperRelCxt;
|
||||
|
||||
#define FDWUpperPlanContinue(cxt) ((cxt) != NULL && (cxt)->stage != FDW_UPPER_REL_END)
|
||||
extern FDWUpperRelCxt* InitFDWUpperPlan(PlannerInfo* root, RelOptInfo* baseRel, Plan* localPlan);
|
||||
extern void AdvanceFDWUpperPlan(FDWUpperRelCxt* ufdwCxt, UpperRelationKind stage, Plan* localPlan);
|
||||
|
||||
#define isObsOrHdfsTableFormTblOid(relId) \
|
||||
(isSpecifiedSrvTypeFromRelId(relId, HDFS) || isSpecifiedSrvTypeFromRelId(relId, OBS))
|
||||
(OidIsValid(relId) && (isSpecifiedSrvTypeFromRelId(relId, HDFS) || isSpecifiedSrvTypeFromRelId(relId, OBS)))
|
||||
|
||||
#define isMOTFromTblOid(relId) \
|
||||
(IsSpecifiedFDWFromRelid(relId, MOT_FDW))
|
||||
(OidIsValid(relId) && IsSpecifiedFDWFromRelid(relId, MOT_FDW))
|
||||
|
||||
#define isObsOrHdfsTableFormSrvName(srvName) \
|
||||
(isSpecifiedSrvTypeFromSrvName(srvName, HDFS) || isSpecifiedSrvTypeFromSrvName(srvName, OBS))
|
||||
|
@ -320,16 +411,18 @@ bool isWriteOnlyFt(Oid relid);
|
|||
(IsSpecifiedFDW(srvName, POSTGRES_FDW))
|
||||
|
||||
#define isMysqlFDWFromTblOid(relId) \
|
||||
(IsSpecifiedFDWFromRelid(relId, MYSQL_FDW))
|
||||
(OidIsValid(relId) && IsSpecifiedFDWFromRelid(relId, MYSQL_FDW))
|
||||
|
||||
#define isOracleFDWFromTblOid(relId) \
|
||||
(IsSpecifiedFDWFromRelid(relId, ORACLE_FDW))
|
||||
(OidIsValid(relId) && IsSpecifiedFDWFromRelid(relId, ORACLE_FDW))
|
||||
|
||||
#define isPostgresFDWFromTblOid(relId) \
|
||||
(IsSpecifiedFDWFromRelid(relId, POSTGRES_FDW))
|
||||
(OidIsValid(relId) && IsSpecifiedFDWFromRelid(relId, POSTGRES_FDW))
|
||||
|
||||
#define IS_OBS_CSV_TXT_FOREIGN_TABLE(relId) \
|
||||
(IsSpecifiedFDWFromRelid(relId, DIST_FDW) && (is_obs_protocol(HdfsGetOptionValue(relId, optLocation))))
|
||||
(OidIsValid(relId) && \
|
||||
IsSpecifiedFDWFromRelid(relId, DIST_FDW) && \
|
||||
(is_obs_protocol(HdfsGetOptionValue(relId, optLocation))))
|
||||
|
||||
#define CAN_BUILD_INFORMATIONAL_CONSTRAINT_BY_RELID(relId) \
|
||||
(isObsOrHdfsTableFormTblOid(relId) || IS_OBS_CSV_TXT_FOREIGN_TABLE(relId))
|
||||
|
@ -340,9 +433,9 @@ bool isWriteOnlyFt(Oid relid);
|
|||
? false \
|
||||
: is_obs_protocol(getFTOptionValue(stmt->options, optLocation))))
|
||||
|
||||
#define IS_LOGFDW_FOREIGN_TABLE(relId) (IsSpecifiedFDWFromRelid(relId, LOG_FDW))
|
||||
#define IS_LOGFDW_FOREIGN_TABLE(relId) (OidIsValid(relId) && IsSpecifiedFDWFromRelid(relId, LOG_FDW))
|
||||
|
||||
#define IS_POSTGRESFDW_FOREIGN_TABLE(relId) (IsSpecifiedFDWFromRelid(relId, GC_FDW))
|
||||
#define IS_POSTGRESFDW_FOREIGN_TABLE(relId) (OidIsValid(relId) && IsSpecifiedFDWFromRelid(relId, GC_FDW))
|
||||
|
||||
#define ENCRYPT_STR_PREFIX "encryptstr"
|
||||
#define MIN_ENCRYPTED_PASSWORD_LENGTH 54
|
||||
|
|
|
@ -209,6 +209,7 @@ typedef struct knl_session_attr_common {
|
|||
bool enable_wdr_snapshot;
|
||||
bool enable_set_variable_b_format;
|
||||
bool enable_asp;
|
||||
bool show_fdw_remote_plan;
|
||||
int wdr_snapshot_interval;
|
||||
int wdr_snapshot_retention_days;
|
||||
int asp_sample_interval;
|
||||
|
|
|
@ -117,6 +117,7 @@ extern const uint32 STANDBY_STMTHIST_VERSION_NUM;
|
|||
extern const uint32 PG_AUTHID_PASSWORDEXT_VERSION_NUM;
|
||||
extern const uint32 MAT_VIEW_RECURSIVE_VERSION_NUM;
|
||||
extern const uint32 SUPPORT_VIEW_AUTO_UPDATABLE;
|
||||
extern const uint32 FDW_SUPPORT_JOIN_AGG_VERSION_NUM;
|
||||
|
||||
extern void register_backend_version(uint32 backend_version);
|
||||
extern bool contain_backend_version(uint32 version_number);
|
||||
|
|
|
@ -2007,6 +2007,7 @@ typedef struct WorkTableScanState {
|
|||
*/
|
||||
typedef struct ForeignScanState {
|
||||
ScanState ss; /* its first field is NodeTag */
|
||||
ExprState* fdw_recheck_quals; /* original quals not in ss.ps.qual */
|
||||
/* use struct pointer to avoid including fdwapi.h here */
|
||||
struct FdwRoutine* fdwroutine;
|
||||
void* fdw_state; /* foreign-data wrapper can keep state here */
|
||||
|
|
|
@ -251,6 +251,7 @@ extern List* lcons_int(int datum, List* list);
|
|||
extern List* lcons_oid(Oid datum, List* list);
|
||||
|
||||
extern List* list_concat(List* list1, List* list2);
|
||||
extern List* list_concat2(List* list1, List* list2);
|
||||
extern List* list_truncate(List* list, int new_size);
|
||||
|
||||
extern void* list_nth(const List* list, int n);
|
||||
|
|
|
@ -921,11 +921,10 @@ typedef struct WorkTableScan {
|
|||
typedef struct ForeignScan {
|
||||
Scan scan;
|
||||
|
||||
Oid scan_relid; /* Oid of the scan relation */
|
||||
Oid scan_relid; /* Oid of the scan relation, InValidOid if this is a join\agg foreign scan. */
|
||||
List* fdw_exprs; /* expressions that FDW may evaluate */
|
||||
List* fdw_private; /* private data for FDW */
|
||||
bool fsSystemCol; /* true if any "system column" is needed */
|
||||
|
||||
bool needSaveError;
|
||||
ErrorCacheEntry* errCache; /* Error record cache */
|
||||
|
||||
|
@ -945,6 +944,14 @@ typedef struct ForeignScan {
|
|||
*/
|
||||
bool in_compute_pool;
|
||||
bool not_use_bloomfilter; /* set true in ExecInitXXXX() of planrouter node */
|
||||
|
||||
// using for pg_fdw
|
||||
CmdType operation; /* SELECT/INSERT/UPDATE/DELETE */
|
||||
Index resultRelation; /* direct modification target's RT index */
|
||||
Oid fs_server; /* OID of foreign server */
|
||||
Bitmapset *fs_relids; /* RTIs generated by this scan */
|
||||
List *fdw_scan_tlist; /* optional tlist describing scan tuple */
|
||||
List *fdw_recheck_quals; /* original quals not in scan.plan.qual */
|
||||
} ForeignScan;
|
||||
|
||||
/* ----------------
|
||||
|
|
|
@ -85,6 +85,23 @@ typedef struct AggClauseCosts {
|
|||
int aggWidth; /* total width of agg function */
|
||||
} AggClauseCosts;
|
||||
|
||||
/*
|
||||
* This enum identifies the different types of "upper" (post-scan/join)
|
||||
* relations that we might deal with during planning.
|
||||
*/
|
||||
typedef enum UpperRelationKind {
|
||||
UPPERREL_INIT, /* is a base rel */
|
||||
UPPERREL_SETOP, /* result of UNION/INTERSECT/EXCEPT, if any */
|
||||
UPPERREL_GROUP_AGG, /* result of grouping/aggregation, if any */
|
||||
UPPERREL_WINDOW, /* result of window functions, if any */
|
||||
UPPERREL_DISTINCT, /* result of "SELECT DISTINCT", if any */
|
||||
UPPERREL_ORDERED, /* result of ORDER BY, if any */
|
||||
UPPERREL_ROWMARKS, /* result of ROMARKS, if any */
|
||||
UPPERREL_LIMIT, /* result of limit offset, if any */
|
||||
UPPERREL_FINAL /* result of any remaining top-level actions */
|
||||
/* NB: UPPERREL_FINAL must be last enum entry; it's used to size arrays */
|
||||
} UpperRelationKind;
|
||||
|
||||
/*
|
||||
* For global path optimization, we should keep all paths with interesting distribute
|
||||
* keys. There are two kinds of such keys: super set (taking effect for intermediate
|
||||
|
@ -465,9 +482,14 @@ typedef struct PlannerInfo {
|
|||
*
|
||||
* We also have "other rels", which are like base rels in that they refer to
|
||||
* single RT indexes; but they are not part of the join tree, and are given
|
||||
* a different RelOptKind to identify them. Lastly, there is a RelOptKind
|
||||
* for "dead" relations, which are base rels that we have proven we don't
|
||||
* need to join after all.
|
||||
* a different RelOptKind to identify them.
|
||||
* There is also a RelOptKind for "upper" relations, which are RelOptInfos
|
||||
* that describe post-scan/join processing steps, such as aggregation.
|
||||
* Many of the fields in these RelOptInfos are meaningless, but their Path
|
||||
* fields always hold Paths showing ways to do that processing step, currently
|
||||
* this kind is only used for fdw to search path.
|
||||
* Lastly, there is a RelOptKind for "dead" relations, which are base rels
|
||||
* that we have proven we don't need to join after all.
|
||||
*
|
||||
* Currently the only kind of otherrels are those made for member relations
|
||||
* of an "append relation", that is an inheritance set or UNION ALL subquery.
|
||||
|
@ -531,8 +553,6 @@ typedef struct PlannerInfo {
|
|||
* allvisfrac - fraction of disk pages that are marked all-visible
|
||||
* subplan - plan for subquery (NULL if it's not a subquery)
|
||||
* subroot - PlannerInfo for subquery (NULL if it's not a subquery)
|
||||
* fdwroutine - function hooks for FDW, if foreign table (else NULL)
|
||||
* fdw_private - private state for FDW, if foreign table (else NULL)
|
||||
*
|
||||
* Note: for a subquery, tuples, subplan, subroot are not set immediately
|
||||
* upon creation of the RelOptInfo object; they are filled in when
|
||||
|
@ -541,7 +561,16 @@ typedef struct PlannerInfo {
|
|||
*
|
||||
* For otherrels that are appendrel members, these fields are filled
|
||||
* in just as for a baserel.
|
||||
* If the relation is either a foreign table or a join of foreign tables that
|
||||
* all belong to the same foreign server and are assigned to the same user to
|
||||
* check access permissions as (cf checkAsUser), these fields will be set:
|
||||
*
|
||||
* serverid - OID of foreign server, if foreign table (else InvalidOid)
|
||||
* userid - OID of user to check access as (InvalidOid means current user)
|
||||
* useridiscurrent - we've assumed that userid equals current user
|
||||
* fdwroutine - function hooks for FDW, if foreign table (else NULL)
|
||||
* fdw_private - private state for FDW, if foreign table (else NULL)
|
||||
*
|
||||
* The presence of the remaining fields depends on the restrictions
|
||||
* and joins that the relation participates in:
|
||||
*
|
||||
|
@ -575,7 +604,7 @@ typedef struct PlannerInfo {
|
|||
* and may need it multiple times to price index scans.
|
||||
* ----------
|
||||
*/
|
||||
typedef enum RelOptKind { RELOPT_BASEREL, RELOPT_JOINREL, RELOPT_OTHER_MEMBER_REL, RELOPT_DEADREL } RelOptKind;
|
||||
typedef enum RelOptKind { RELOPT_BASEREL, RELOPT_JOINREL, RELOPT_OTHER_MEMBER_REL, RELOPT_UPPER_REL, RELOPT_DEADREL } RelOptKind;
|
||||
|
||||
typedef enum PartitionFlag { PARTITION_NONE, PARTITION_REQURIED, PARTITION_ANCESOR } PartitionFlag;
|
||||
|
||||
|
@ -588,6 +617,9 @@ typedef enum PartitionFlag { PARTITION_NONE, PARTITION_REQURIED, PARTITION_ANCES
|
|||
/* Is the given relation a join relation? */
|
||||
#define IS_JOIN_REL(rel) ((rel)->reloptkind == RELOPT_JOINREL)
|
||||
|
||||
/* Is the given relation an upper relation? */
|
||||
#define IS_UPPER_REL(rel) ((rel)->reloptkind == RELOPT_UPPER_REL)
|
||||
|
||||
typedef struct RelOptInfo {
|
||||
NodeTag type;
|
||||
|
||||
|
@ -653,6 +685,11 @@ typedef struct RelOptInfo {
|
|||
struct Plan* subplan; /* if subquery */
|
||||
PlannerInfo* subroot; /* if subquery */
|
||||
List *subplan_params; /* if subquery */
|
||||
|
||||
/* Information about foreign tables and foreign joins */
|
||||
Oid serverid; /* identifies server for the table or join */
|
||||
Oid userid; /* identifies user to check access as */
|
||||
bool useridiscurrent; /* join is only valid for current user */
|
||||
/* use "struct FdwRoutine" to avoid including fdwapi.h here */
|
||||
struct FdwRoutine* fdwroutine; /* if foreign table */
|
||||
void* fdw_private; /* if foreign table */
|
||||
|
|
|
@ -84,7 +84,7 @@ extern Path* create_valuesscan_path(PlannerInfo* root, RelOptInfo* rel, Relids r
|
|||
extern Path* create_ctescan_path(PlannerInfo* root, RelOptInfo* rel);
|
||||
extern Path* create_worktablescan_path(PlannerInfo* root, RelOptInfo* rel);
|
||||
extern ForeignPath* create_foreignscan_path(PlannerInfo* root, RelOptInfo* rel, Cost startup_cost, Cost total_cost,
|
||||
List* pathkeys, Relids required_outer, List* fdw_private, int dop = 1);
|
||||
List* pathkeys, Relids required_outer, Path* fdw_outerpath, List* fdw_private, int dop = 1);
|
||||
extern Relids calc_nestloop_required_outer(Path* outer_path, Path* inner_path);
|
||||
extern Relids calc_non_nestloop_required_outer(Path* outer_path, Path* inner_path);
|
||||
|
||||
|
|
|
@ -142,7 +142,7 @@ extern bool have_relevant_eclass_joinclause(PlannerInfo* root, RelOptInfo* rel1,
|
|||
extern bool has_relevant_eclass_joinclause(PlannerInfo* root, RelOptInfo* rel1);
|
||||
extern bool eclass_useful_for_merging(EquivalenceClass* eclass, RelOptInfo* rel);
|
||||
extern bool is_redundant_derived_clause(RestrictInfo* rinfo, List* clauselist);
|
||||
|
||||
extern PathKey* make_canonical_pathkey(PlannerInfo* root, EquivalenceClass* eclass, Oid opfamily, int strategy, bool nulls_first);
|
||||
/*
|
||||
* pathkeys.c
|
||||
* utilities for matching and building path keys
|
||||
|
|
|
@ -71,7 +71,7 @@ extern void disuse_physical_tlist(Plan* plan, Path* path);
|
|||
extern void copy_plan_costsize(Plan* dest, Plan* src);
|
||||
extern SubqueryScan* make_subqueryscan(List* qptlist, List* qpqual, Index scanrelid, Plan* subplan);
|
||||
extern ForeignScan* make_foreignscan(List* qptlist, List* qpqual, Index scanrelid, List* fdw_exprs, List* fdw_private,
|
||||
RemoteQueryExecType type = EXEC_ON_ALL_NODES);
|
||||
List *fdw_scan_tlist, List *fdw_recheck_quals, Plan *outer_plan, RemoteQueryExecType type = EXEC_ON_ALL_NODES);
|
||||
extern Append* make_append(List* appendplans, List* tlist);
|
||||
extern RecursiveUnion* make_recursive_union(
|
||||
List* tlist, Plan* lefttree, Plan* righttree, int wtParam, List* distinctList, long numGroups);
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
-- 1. create usermapping.cihper
|
||||
\! echo $OLDGAUSSHOME | xargs -I{} rm -f {}/bin/usermapping.key.cipher
|
||||
\! echo $OLDGAUSSHOME | xargs -I{} rm -f {}/bin/usermapping.key.rand
|
||||
\! echo $OLDGAUSSHOME | xargs -I{} @abs_bindir@/gs_guc generate -S 123456@pwd -D {}/bin -o usermapping > /dev/null 2>&1 ; echo $?
|
||||
|
||||
|
||||
-- 2. copy the test case script from contrib/, touch it first in case copy failed, otherwise fastcheck will stop becase cannot find test case script;
|
||||
\! sh -c "touch @abs_srcdir@/sql/postgres_fdw.sql"
|
||||
\! sh -c "echo 'failed, please check the test case: fdw_prepare' > @abs_srcdir@/sql/postgres_fdw.sql"
|
||||
\! sh -c "touch @abs_srcdir@/expected/postgres_fdw.out"
|
||||
\! sh -c "cp @abs_srcdir@/../../../contrib/postgres_fdw/sql/postgres_fdw.sql @abs_srcdir@/sql/postgres_fdw.sql"
|
||||
\! sh -c "cp @abs_srcdir@/../../../contrib/postgres_fdw/expected/postgres_fdw.out @abs_srcdir@/expected/postgres_fdw.out"
|
||||
|
||||
\! sh -c "touch @abs_srcdir@/sql/postgres_fdw_cstore.sql"
|
||||
\! sh -c "echo 'failed, please check the test case: fdw_prepare' > @abs_srcdir@/sql/postgres_fdw_cstore.sql"
|
||||
\! sh -c "touch @abs_srcdir@/expected/postgres_fdw_cstore.out"
|
||||
\! sh -c "cp @abs_srcdir@/../../../contrib/postgres_fdw/sql/postgres_fdw_cstore.sql @abs_srcdir@/sql/postgres_fdw_cstore.sql"
|
||||
\! sh -c "cp @abs_srcdir@/../../../contrib/postgres_fdw/expected/postgres_fdw_cstore.out @abs_srcdir@/expected/postgres_fdw_cstore.out"
|
||||
|
||||
\! sh -c "touch @abs_srcdir@/sql/postgres_fdw_partition.sql"
|
||||
\! sh -c "echo 'failed, please check the test case: fdw_prepare' > @abs_srcdir@/sql/postgres_fdw_partition.sql"
|
||||
\! sh -c "touch @abs_srcdir@/expected/postgres_fdw_partition.out"
|
||||
\! sh -c "cp @abs_srcdir@/../../../contrib/postgres_fdw/sql/postgres_fdw_partition.sql @abs_srcdir@/sql/postgres_fdw_partition.sql"
|
||||
\! sh -c "cp @abs_srcdir@/../../../contrib/postgres_fdw/expected/postgres_fdw_partition.out @abs_srcdir@/expected/postgres_fdw_partition.out"
|
|
@ -0,0 +1,21 @@
|
|||
-- 1. create usermapping.cihper
|
||||
\! echo $OLDGAUSSHOME | xargs -I{} rm -f {}/bin/usermapping.key.cipher
|
||||
\! echo $OLDGAUSSHOME | xargs -I{} rm -f {}/bin/usermapping.key.rand
|
||||
\! echo $OLDGAUSSHOME | xargs -I{} @abs_bindir@/gs_guc generate -S 123456@pwd -D {}/bin -o usermapping > /dev/null 2>&1 ; echo $?
|
||||
0
|
||||
-- 2. copy the test case script from contrib/, touch it first in case copy failed, otherwise fastcheck will stop becase cannot find test case script;
|
||||
\! sh -c "touch @abs_srcdir@/sql/postgres_fdw.sql"
|
||||
\! sh -c "echo 'failed, please check the test case: fdw_prepare' > @abs_srcdir@/sql/postgres_fdw.sql"
|
||||
\! sh -c "touch @abs_srcdir@/expected/postgres_fdw.out"
|
||||
\! sh -c "cp @abs_srcdir@/../../../contrib/postgres_fdw/sql/postgres_fdw.sql @abs_srcdir@/sql/postgres_fdw.sql"
|
||||
\! sh -c "cp @abs_srcdir@/../../../contrib/postgres_fdw/expected/postgres_fdw.out @abs_srcdir@/expected/postgres_fdw.out"
|
||||
\! sh -c "touch @abs_srcdir@/sql/postgres_fdw_cstore.sql"
|
||||
\! sh -c "echo 'failed, please check the test case: fdw_prepare' > @abs_srcdir@/sql/postgres_fdw_cstore.sql"
|
||||
\! sh -c "touch @abs_srcdir@/expected/postgres_fdw_cstore.out"
|
||||
\! sh -c "cp @abs_srcdir@/../../../contrib/postgres_fdw/sql/postgres_fdw_cstore.sql @abs_srcdir@/sql/postgres_fdw_cstore.sql"
|
||||
\! sh -c "cp @abs_srcdir@/../../../contrib/postgres_fdw/expected/postgres_fdw_cstore.out @abs_srcdir@/expected/postgres_fdw_cstore.out"
|
||||
\! sh -c "touch @abs_srcdir@/sql/postgres_fdw_partition.sql"
|
||||
\! sh -c "echo 'failed, please check the test case: fdw_prepare' > @abs_srcdir@/sql/postgres_fdw_partition.sql"
|
||||
\! sh -c "touch @abs_srcdir@/expected/postgres_fdw_partition.out"
|
||||
\! sh -c "cp @abs_srcdir@/../../../contrib/postgres_fdw/sql/postgres_fdw_partition.sql @abs_srcdir@/sql/postgres_fdw_partition.sql"
|
||||
\! sh -c "cp @abs_srcdir@/../../../contrib/postgres_fdw/expected/postgres_fdw_partition.out @abs_srcdir@/expected/postgres_fdw_partition.out"
|
|
@ -584,6 +584,7 @@ select name,vartype,unit,min_val,max_val from pg_settings where name <> 'qunit_c
|
|||
shared_buffers | integer | 8kB | 16 | 1073741823
|
||||
shared_preload_libraries | string | | |
|
||||
show_acce_estimate_detail | bool | | |
|
||||
show_fdw_remote_plan | bool | | |
|
||||
skew_option | enum | | |
|
||||
sql_beta_feature | enum | | |
|
||||
sql_compatibility | enum | | |
|
||||
|
|
|
@ -27,6 +27,12 @@ test: replace_func_with_two_args trunc_func_for_date nlssort_pinyin updatable_vi
|
|||
# test multiple statistics
|
||||
test: functional_dependency
|
||||
test: pg_proc_test
|
||||
|
||||
# test fdw
|
||||
# NOTICE: In the "fdw_prepare", we copy the fdw test to be used from contrib into regress sql set.
|
||||
test: fdw_prepare
|
||||
test: postgres_fdw postgres_fdw_cstore postgres_fdw_partition
|
||||
|
||||
# parse xlog and page
|
||||
#test: parse_page
|
||||
#test: parse_xlog
|
||||
|
@ -880,7 +886,7 @@ test: llvm_vecsort llvm_vecsort2
|
|||
test: udf_crem create_c_function
|
||||
|
||||
# procedure, Function Test
|
||||
#test: create_procedure postgres_fdw
|
||||
#test: create_procedure
|
||||
test: create_function
|
||||
test: pg_compatibility
|
||||
|
||||
|
|
Loading…
Reference in New Issue