summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/commands/matview.c44
-rw-r--r--src/backend/commands/tablecmds.c12
-rw-r--r--src/backend/nodes/copyfuncs.c5
-rw-r--r--src/backend/nodes/equalfuncs.c5
-rw-r--r--src/backend/optimizer/util/pathnode.c21
-rw-r--r--src/backend/parser/gram.y34
-rw-r--r--src/backend/postmaster/pgstat.c6
-rw-r--r--src/backend/rewrite/rewriteHandler.c5
-rw-r--r--src/backend/tcop/utility.c87
-rw-r--r--src/backend/utils/adt/ruleutils.c10
-rw-r--r--src/include/catalog/pg_class.h4
-rw-r--r--src/include/commands/tablecmds.h1
-rw-r--r--src/include/nodes/parsenodes.h5
-rw-r--r--src/include/utils/rel.h8
14 files changed, 207 insertions, 40 deletions
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index 5130d512a6..0b5053f5f2 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -25,9 +25,15 @@
#include "commands/matview.h"
#include "commands/tablecmds.h"
#include "commands/tablespace.h"
+#ifdef PGXC
+#include "commands/vacuum.h"
+#endif
#include "executor/executor.h"
#include "executor/spi.h"
#include "miscadmin.h"
+#ifdef PGXC
+#include "nodes/makefuncs.h"
+#endif
#include "parser/parse_relation.h"
#include "rewrite/rewriteHandler.h"
#include "storage/lmgr.h"
@@ -554,11 +560,29 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid)
if (SPI_connect() != SPI_OK_CONNECT)
elog(ERROR, "SPI_connect failed");
+#ifndef PGXC
/* Analyze the temp table with the new contents. */
appendStringInfo(&querybuf, "ANALYZE %s", tempname);
if (SPI_exec(querybuf.data, 0) != SPI_OK_UTILITY)
elog(ERROR, "SPI_exec failed: %s", querybuf.data);
-
+#else
+ {
+ /*
+ * Don't want to send down the ANALYZE on the remote nodes because the
+ * temporary table was not created there to start with. We could have
+ * invented a special option for ANALYZE to only run locally. But we
+ * could instead just cook up a VacuumStmt and call vacuum (with
+ * VACOPT_ANALYZE option of course) directly
+ */
+ VacuumStmt *stmt = makeNode(VacuumStmt);
+ RangeVar *rv = makeRangeVar(
+ get_namespace_name(RelationGetNamespace(tempRel)),
+ RelationGetRelationName(tempRel), -1);
+ stmt->relation = rv;
+ stmt->options = VACOPT_ANALYZE;
+ vacuum(stmt, InvalidOid, true, NULL, false, true);
+ }
+#endif
/*
* We need to ensure that there are not duplicate rows without NULLs in
* the new data set before we can count on the "diff" results. Check for
@@ -589,8 +613,22 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid)
/* Start building the query for creating the diff table. */
resetStringInfo(&querybuf);
+
+#ifdef PGXC
+ /*
+ * In Postgres-XL we use LOCAL TEMP table to define a table that gets
+ * created only on the coordinator. In this case, we need the local table
+ * only
+ *
+ * XXX Are we breaking SQL standard compatibility?
+ */
+#endif
appendStringInfo(&querybuf,
+#ifdef PGXC
+ "CREATE LOCAL TEMP TABLE %s AS "
+#else
"CREATE TEMP TABLE %s AS "
+#endif
"SELECT mv.ctid AS tid, newdata "
"FROM %s mv FULL JOIN %s newdata ON (",
diffname, matviewname, tempname);
@@ -678,7 +716,11 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid)
"ORDER BY tid");
/* Create the temporary "diff" table. */
+#ifdef PGXC
+ if (SPI_exec(querybuf.data, 0) != SPI_OK_INSERT)
+#else
if (SPI_exec(querybuf.data, 0) != SPI_OK_UTILITY)
+#endif
elog(ERROR, "SPI_exec failed: %s", querybuf.data);
/*
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index cf453cae03..0928269cfd 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -11854,6 +11854,18 @@ IsTempTable(Oid relid)
return res;
}
+bool
+IsLocalTempTable(Oid relid)
+{
+ Relation rel;
+ bool res;
+ rel = relation_open(relid, NoLock);
+ res = (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
+ rel->rd_locator_info == NULL);
+ relation_close(rel, NoLock);
+ return res;
+}
+
/*
* IsIndexUsingTemp
*
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 75dce487c8..9dfb1f2aa9 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2998,6 +2998,8 @@ CopyCreateStmtFields(const CreateStmt *from, CreateStmt *newnode)
COPY_STRING_FIELD(tablespacename);
COPY_SCALAR_FIELD(if_not_exists);
#ifdef PGXC
+ COPY_SCALAR_FIELD(islocal);
+ COPY_SCALAR_FIELD(relkind);
COPY_NODE_FIELD(distributeby);
COPY_NODE_FIELD(subcluster);
#endif
@@ -3506,6 +3508,9 @@ _copyCreateTableAsStmt(const CreateTableAsStmt *from)
COPY_NODE_FIELD(into);
COPY_SCALAR_FIELD(relkind);
COPY_SCALAR_FIELD(is_select_into);
+#ifdef PGXC
+ COPY_SCALAR_FIELD(islocal);
+#endif
return newnode;
}
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index fe5ec9aebc..ac82eacbe0 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1129,6 +1129,8 @@ _equalCreateStmt(const CreateStmt *a, const CreateStmt *b)
COMPARE_STRING_FIELD(tablespacename);
COMPARE_SCALAR_FIELD(if_not_exists);
#ifdef PGXC
+ COMPARE_SCALAR_FIELD(islocal);
+ COMPARE_SCALAR_FIELD(relkind);
COMPARE_NODE_FIELD(distributeby);
COMPARE_NODE_FIELD(subcluster);
#endif
@@ -1553,6 +1555,9 @@ _equalCreateTableAsStmt(const CreateTableAsStmt *a, const CreateTableAsStmt *b)
COMPARE_NODE_FIELD(into);
COMPARE_SCALAR_FIELD(relkind);
COMPARE_SCALAR_FIELD(is_select_into);
+#ifdef PGXC
+ COMPARE_SCALAR_FIELD(islocal);
+#endif
return true;
}
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index ec4bfdb49a..feaed857a9 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -1021,18 +1021,13 @@ set_joinpath_distribution(PlannerInfo *root, JoinPath *pathnode)
* Catalog tables are the same on all nodes, so treat them as replicated
* on all nodes.
*/
- if ((!innerd || IsLocatorReplicated(innerd->distributionType)) &&
- (!outerd || IsLocatorReplicated(outerd->distributionType)))
+ if ((innerd && IsLocatorReplicated(innerd->distributionType)) &&
+ (outerd && IsLocatorReplicated(outerd->distributionType)))
{
/* Determine common nodes */
Bitmapset *common;
- if (innerd == NULL)
- common = bms_copy(outerd->nodes);
- else if (outerd == NULL)
- common = bms_copy(innerd->nodes);
- else
- common = bms_intersect(innerd->nodes, outerd->nodes);
+ common = bms_intersect(innerd->nodes, outerd->nodes);
if (bms_is_empty(common))
goto not_allowed_join;
@@ -1056,14 +1051,14 @@ set_joinpath_distribution(PlannerInfo *root, JoinPath *pathnode)
* nullable data nodes will produce joined rows with NULLs for cases when
* matching row exists, but on other data node.
*/
- if ((!innerd || IsLocatorReplicated(innerd->distributionType)) &&
+ if ((innerd && IsLocatorReplicated(innerd->distributionType)) &&
(pathnode->jointype == JOIN_INNER ||
pathnode->jointype == JOIN_LEFT ||
pathnode->jointype == JOIN_SEMI ||
pathnode->jointype == JOIN_ANTI))
{
/* We need inner relation is defined on all nodes where outer is */
- if (innerd && !bms_is_subset(outerd->nodes, innerd->nodes))
+ if (!outerd || !bms_is_subset(outerd->nodes, innerd->nodes))
goto not_allowed_join;
targetd = makeNode(Distribution);
@@ -1084,12 +1079,12 @@ set_joinpath_distribution(PlannerInfo *root, JoinPath *pathnode)
* nullable data nodes will produce joined rows with NULLs for cases when
* matching row exists, but on other data node.
*/
- if ((!outerd || IsLocatorReplicated(outerd->distributionType)) &&
+ if ((outerd && IsLocatorReplicated(outerd->distributionType)) &&
(pathnode->jointype == JOIN_INNER ||
pathnode->jointype == JOIN_RIGHT))
{
/* We need outer relation is defined on all nodes where inner is */
- if (outerd && !bms_is_subset(innerd->nodes, outerd->nodes))
+ if (!innerd || !bms_is_subset(innerd->nodes, outerd->nodes))
goto not_allowed_join;
targetd = makeNode(Distribution);
@@ -1269,6 +1264,7 @@ not_allowed_join:
* by has as main variant.
*/
+#ifdef NOT_USED
/* These join types allow replicated inner */
if (outerd &&
(pathnode->jointype == JOIN_INNER ||
@@ -1322,6 +1318,7 @@ not_allowed_join:
altpath->path.distribution = targetd;
alternate = lappend(alternate, altpath);
}
+#endif
/*
* Redistribute subplans to make them compatible.
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 5931e2847e..bbc251fec6 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -2734,6 +2734,12 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
n->tablespacename = $11;
n->if_not_exists = false;
/* PGXC_BEGIN */
+ if ($2 == RELPERSISTENCE_LOCAL_TEMP)
+ {
+ $4->relpersistence = RELPERSISTENCE_TEMP;
+ n->islocal = true;
+ }
+ n->relkind = RELKIND_RELATION;
n->distributeby = $12;
n->subcluster = $13;
/* PGXC_END */
@@ -2757,6 +2763,12 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
n->tablespacename = $14;
n->if_not_exists = true;
/* PGXC_BEGIN */
+ if ($2 == RELPERSISTENCE_LOCAL_TEMP)
+ {
+ $7->relpersistence = RELPERSISTENCE_TEMP;
+ n->islocal = true;
+ }
+ n->relkind = RELKIND_RELATION;
n->distributeby = $15;
n->subcluster = $16;
if (n->inhRelations != NULL && n->distributeby != NULL)
@@ -2785,6 +2797,12 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
n->tablespacename = $10;
n->if_not_exists = false;
/* PGXC_BEGIN */
+ if ($2 == RELPERSISTENCE_LOCAL_TEMP)
+ {
+ $4->relpersistence = RELPERSISTENCE_TEMP;
+ n->islocal = true;
+ }
+ n->relkind = RELKIND_RELATION;
n->distributeby = $11;
n->subcluster = $12;
if (n->inhRelations != NULL && n->distributeby != NULL)
@@ -2813,6 +2831,12 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
n->tablespacename = $13;
n->if_not_exists = true;
/* PGXC_BEGIN */
+ if ($2 == RELPERSISTENCE_LOCAL_TEMP)
+ {
+ $7->relpersistence = RELPERSISTENCE_TEMP;
+ n->islocal = true;
+ }
+ n->relkind = RELKIND_RELATION;
n->distributeby = $14;
n->subcluster = $15;
if (n->inhRelations != NULL && n->distributeby != NULL)
@@ -2838,8 +2862,8 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
*/
OptTemp: TEMPORARY { $$ = RELPERSISTENCE_TEMP; }
| TEMP { $$ = RELPERSISTENCE_TEMP; }
- | LOCAL TEMPORARY { $$ = RELPERSISTENCE_TEMP; }
- | LOCAL TEMP { $$ = RELPERSISTENCE_TEMP; }
+ | LOCAL TEMPORARY { $$ = RELPERSISTENCE_LOCAL_TEMP; }
+ | LOCAL TEMP { $$ = RELPERSISTENCE_LOCAL_TEMP; }
| GLOBAL TEMPORARY
{
ereport(WARNING,
@@ -3473,6 +3497,12 @@ CreateAsStmt:
ctas->is_select_into = false;
/* cram additional flags into the IntoClause */
$4->rel->relpersistence = $2;
+ if ($2 == RELPERSISTENCE_LOCAL_TEMP)
+ {
+ $4->rel->relpersistence = RELPERSISTENCE_TEMP;
+ ctas->islocal = true;
+ }
+
$4->skipData = !($7);
$$ = (Node *) ctas;
}
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index a05730c769..04a17fda29 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -1892,9 +1892,6 @@ pgstat_update_heap_dead_tuples(Relation rel, int delta)
void
pgstat_count_remote_insert(Relation rel, int n)
{
- /* Should be only applied to distributed table */
- Assert(rel->rd_locator_info);
-
/* For now use the same counters as for heap insert */
pgstat_count_heap_insert(rel, n);
}
@@ -1908,9 +1905,6 @@ pgstat_count_remote_update(Relation rel, int n)
{
PgStat_TableStatus *pgstat_info = rel->pgstat_info;
- /* Should be only applied to distributed table */
- Assert(rel->rd_locator_info);
-
if (pgstat_info != NULL)
{
/* We have to log the effect at the proper transactional level */
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index c54db93676..be82dedd3e 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -3702,11 +3702,16 @@ QueryRewriteCTAS(Query *parsetree)
/* Get the target table */
stmt = (CreateTableAsStmt *) parsetree->utilityStmt;
+
+ if (stmt->relkind == OBJECT_MATVIEW)
+ return list_make1(parsetree);
+
relation = stmt->into->rel;
/* Start building a CreateStmt for creating the target table */
create_stmt = makeNode(CreateStmt);
create_stmt->relation = relation;
+ create_stmt->islocal = stmt->islocal;
into = stmt->into;
/* Obtain the target list of new table */
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index dff8567bac..77bd7a2a57 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1489,6 +1489,7 @@ ProcessUtilitySlow(Node *parsetree,
Oid relOid;
#ifdef PGXC
bool is_temp = false;
+ bool is_local = ((CreateStmt *) parsetree)->islocal;
#endif
/* Run parse analysis ... */
@@ -1501,7 +1502,7 @@ ProcessUtilitySlow(Node *parsetree,
* it should explicitly specify distribution.
*/
stmts = transformCreateStmt((CreateStmt *) parsetree,
- queryString, !sentToRemote);
+ queryString, !is_local && !sentToRemote);
#else
stmts = transformCreateStmt((CreateStmt *) parsetree,
queryString);
@@ -1566,7 +1567,9 @@ ProcessUtilitySlow(Node *parsetree,
*/
if (!sentToRemote)
#ifdef XCP
- stmts = AddRemoteQueryNode(stmts, queryString, is_temp ? EXEC_ON_DATANODES : EXEC_ON_ALL_NODES);
+ stmts = AddRemoteQueryNode(stmts, queryString, is_local
+ ? EXEC_ON_NONE
+ : (is_temp ? EXEC_ON_DATANODES : EXEC_ON_ALL_NODES));
#else
stmts = AddRemoteQueryNode(stmts, queryString, EXEC_ON_ALL_NODES, is_temp);
#endif
@@ -2133,11 +2136,35 @@ ProcessUtilitySlow(Node *parsetree,
case T_CreateTableAsStmt:
ExecCreateTableAs((CreateTableAsStmt *) parsetree,
queryString, params, completionTag);
+#ifdef PGXC
+ if ((IS_PGXC_COORDINATOR) && !IsConnFromCoord())
+ {
+ CreateTableAsStmt *stmt = (CreateTableAsStmt *) parsetree;
+
+ /*
+ * CTAS for normal tables should have been rewritten as a
+ * CREATE TABLE + SELECT INTO
+ */
+ Assert(stmt->relkind == OBJECT_MATVIEW);
+ if (stmt->into->rel->relpersistence != RELPERSISTENCE_TEMP)
+ ExecUtilityStmtOnNodes(queryString, NULL,
+ sentToRemote, false, EXEC_ON_COORDS, false);
+ }
+#endif
break;
case T_RefreshMatViewStmt:
ExecRefreshMatView((RefreshMatViewStmt *) parsetree,
queryString, params, completionTag);
+#ifdef PGXC
+ if ((IS_PGXC_COORDINATOR) && !IsConnFromCoord())
+ {
+ RefreshMatViewStmt *stmt = (RefreshMatViewStmt *) parsetree;
+ if (stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
+ ExecUtilityStmtOnNodes(queryString, NULL,
+ sentToRemote, false, EXEC_ON_COORDS, false);
+ }
+#endif
break;
case T_CreateTrigStmt:
@@ -4061,6 +4088,7 @@ ExecUtilityFindNodes(ObjectType object_type,
*/
case OBJECT_RULE:
case OBJECT_VIEW:
+ case OBJECT_MATVIEW:
/* Check if object is a temporary view */
if ((*is_temp = IsTempTable(object_id)))
exec_type = EXEC_ON_NONE;
@@ -4070,10 +4098,31 @@ ExecUtilityFindNodes(ObjectType object_type,
case OBJECT_INDEX:
/* Check if given index uses temporary tables */
- if ((*is_temp = IsTempTable(object_id)))
- exec_type = EXEC_ON_DATANODES;
- else
- exec_type = EXEC_ON_ALL_NODES;
+ {
+ Relation rel;
+ bool is_matview;
+
+ rel = relation_open(object_id, NoLock);
+
+ *is_temp = (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP);
+ is_matview = (rel->rd_rel->relkind == RELKIND_MATVIEW);
+
+ relation_close(rel, NoLock);
+
+ exec_type = EXEC_ON_NONE;
+ if (*is_temp)
+ {
+ if (!is_matview)
+ exec_type = EXEC_ON_DATANODES;
+ }
+ else
+ {
+ if (!is_matview)
+ exec_type = EXEC_ON_ALL_NODES;
+ else
+ exec_type = EXEC_ON_COORDS;
+ }
+ }
break;
default:
@@ -4107,13 +4156,18 @@ ExecUtilityFindNodesRelkind(Oid relid, bool *is_temp)
#endif
case RELKIND_RELATION:
#ifdef XCP
- if ((*is_temp = IsTempTable(relid)))
- exec_type = EXEC_ON_DATANODES;
- else
- exec_type = EXEC_ON_ALL_NODES;
+ if (*is_temp = IsTempTable(relid))
+ {
+ if (IsLocalTempTable(relid))
+ exec_type = EXEC_ON_NONE;
+ else
+ exec_type = EXEC_ON_DATANODES;
+ }
+ else
+ exec_type = EXEC_ON_ALL_NODES;
#else
- *is_temp = IsTempTable(relid);
- exec_type = EXEC_ON_ALL_NODES;
+ *is_temp = IsTempTable(relid);
+ exec_type = EXEC_ON_ALL_NODES;
#endif
break;
@@ -4124,6 +4178,14 @@ ExecUtilityFindNodesRelkind(Oid relid, bool *is_temp)
exec_type = EXEC_ON_COORDS;
break;
+ case RELKIND_MATVIEW:
+ /* Check if object is a temporary view */
+ if ((*is_temp = IsTempTable(relid)))
+ exec_type = EXEC_ON_NONE;
+ else
+ exec_type = EXEC_ON_COORDS;
+ break;
+
default:
*is_temp = false;
exec_type = EXEC_ON_ALL_NODES;
@@ -4343,6 +4405,7 @@ DropStmtPreTreatment(DropStmt *stmt, const char *queryString, bool sentToRemote,
case OBJECT_SEQUENCE:
case OBJECT_VIEW:
case OBJECT_INDEX:
+ case OBJECT_MATVIEW:
{
/*
* Check the list of objects going to be dropped.
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 20e6f80d82..0485ae2383 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -5796,16 +5796,12 @@ get_utility_query_def(Query *query, deparse_context *context)
bool istemp = (relation->relpersistence == RELPERSISTENCE_TEMP);
bool isunlogged = (relation->relpersistence == RELPERSISTENCE_UNLOGGED);
- if (istemp && relation->schemaname)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
- errmsg("temporary tables cannot specify a schema name")));
-
- appendStringInfo(buf, "CREATE %s %s TABLE ",
+ appendStringInfo(buf, "CREATE %s %s %s TABLE ",
+ stmt->islocal ? "LOCAL" : "",
istemp ? "TEMP" : "",
isunlogged ? "UNLOGGED" : "");
- if (relation->schemaname && relation->schemaname[0])
+ if (!istemp && relation->schemaname && relation->schemaname[0])
appendStringInfo(buf, "%s.", relation->schemaname);
appendStringInfo(buf, "%s", relation->relname);
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index f2fb317e5f..b343c4c928 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -161,6 +161,10 @@ DESCR("");
#define RELPERSISTENCE_UNLOGGED 'u' /* unlogged permanent table */
#define RELPERSISTENCE_TEMP 't' /* temporary table */
+#ifdef PGXC
+#define RELPERSISTENCE_LOCAL_TEMP 'l' /* local temp table */
+#endif
+
/* default selection for replica identity (primary key or nothing) */
#define REPLICA_IDENTITY_DEFAULT 'd'
/* no replica identity is logged for this relation */
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 15f487e757..bc97d6ccbe 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -76,6 +76,7 @@ extern void AtEOSubXact_on_commit_actions(bool isCommit,
SubTransactionId parentSubid);
#ifdef PGXC
extern bool IsTempTable(Oid relid);
+extern bool IsLocalTempTable(Oid relid);
extern bool IsIndexUsingTempTable(Oid relid);
extern bool IsOnCommitActions(void);
extern void DropTableThrowErrorExternal(RangeVar *relation,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 3aa13d8518..d5a3582399 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1592,6 +1592,8 @@ typedef struct CreateStmt
char *tablespacename; /* table space to use, or NULL */
bool if_not_exists; /* just do nothing if it already exists? */
#ifdef PGXC
+ ObjectType relkind; /* kind of relation to create */
+ bool islocal; /* create only on the current node */
DistributeBy *distributeby; /* distribution to use, or NULL */
PGXCSubCluster *subcluster; /* subcluster of table */
#endif
@@ -2701,6 +2703,9 @@ typedef struct CreateTableAsStmt
Node *query; /* the query (see comments above) */
IntoClause *into; /* destination table */
ObjectType relkind; /* OBJECT_TABLE or OBJECT_MATVIEW */
+#ifdef PGXC
+ bool islocal; /* local table */
+#endif
bool is_select_into; /* it was written as SELECT INTO */
} CreateTableAsStmt;
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 4fd2165db7..2a7c7c6bc4 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -504,6 +504,14 @@ typedef struct StdRdOptions
!(relation)->rd_islocaltemp)
#endif
+#ifdef XCP
+/*
+ * RELATION_IS_COORDINATOR_LOCAL
+ * Test for a coordinator only relation such as LOCAL TEMP table or a MATVIEW
+ */
+#define RELATION_IS_COORDINATOR_LOCAL(relation) \
+ ((RELATION_IS_LOCAL(relation) && !RelationGetLocInfo(relation)))
+#endif
/*
* RelationIsScannable