Merge pull request #5889 from citusdata/velioglu/view_propagation

Introduce CREATE/DROP VIEW
pull/5930/head^2
Burak Velioglu 2022-05-10 14:25:06 +03:00 committed by GitHub
commit f11d851ef7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
64 changed files with 1637 additions and 79 deletions

View File

@ -216,6 +216,9 @@ PG_FUNCTION_INFO_V1(alter_distributed_table);
PG_FUNCTION_INFO_V1(alter_table_set_access_method);
PG_FUNCTION_INFO_V1(worker_change_sequence_dependency);
/* global variable keeping track of whether we are in a table type conversion function */
bool InTableTypeConversionFunctionCall = false;
/*
* undistribute_table gets a distributed table name and
@ -504,10 +507,16 @@ AlterTableSetAccessMethod(TableConversionParameters *params)
*
* The function returns a TableConversionReturn object that can stores variables that
* can be used at the caller operations.
*
* To be able to provide more meaningful messages while converting a table type,
* Citus keeps InTableTypeConversionFunctionCall flag. Don't forget to set it properly
* in case you add a new way to return from this function.
*/
TableConversionReturn *
ConvertTable(TableConversionState *con)
{
InTableTypeConversionFunctionCall = true;
/*
* We undistribute citus local tables that are not chained with any reference
* tables via foreign keys at the end of the utility hook.
@ -536,6 +545,7 @@ ConvertTable(TableConversionState *con)
* subgraph including itself, so return here.
*/
SetLocalEnableLocalReferenceForeignKeys(oldEnableLocalReferenceForeignKeys);
InTableTypeConversionFunctionCall = false;
return NULL;
}
char *newAccessMethod = con->accessMethod ? con->accessMethod :
@ -820,6 +830,7 @@ ConvertTable(TableConversionState *con)
SetLocalEnableLocalReferenceForeignKeys(oldEnableLocalReferenceForeignKeys);
InTableTypeConversionFunctionCall = false;
return ret;
}

View File

@ -349,6 +349,14 @@ GetDependencyCreateDDLCommands(const ObjectAddress *dependency)
return DDLCommandsForSequence(dependency->objectId, sequenceOwnerName);
}
if (relKind == RELKIND_VIEW)
{
char *createViewCommand = CreateViewDDLCommand(dependency->objectId);
char *alterViewOwnerCommand = AlterViewOwnerCommand(dependency->objectId);
return list_make2(createViewCommand, alterViewOwnerCommand);
}
/* if this relation is not supported, break to the error at the end */
break;
}

View File

@ -185,6 +185,14 @@ static DistributeObjectOps Any_CreateFunction = {
.address = CreateFunctionStmtObjectAddress,
.markDistributed = true,
};
static DistributeObjectOps Any_View = {
.deparse = NULL,
.qualify = NULL,
.preprocess = PreprocessViewStmt,
.postprocess = PostprocessViewStmt,
.address = ViewStmtObjectAddress,
.markDistributed = true,
};
static DistributeObjectOps Any_CreatePolicy = {
.deparse = NULL,
.qualify = NULL,
@ -442,6 +450,14 @@ static DistributeObjectOps Function_Drop = {
.address = NULL,
.markDistributed = false,
};
static DistributeObjectOps View_Drop = {
.deparse = DeparseDropViewStmt,
.qualify = QualifyDropViewStmt,
.preprocess = PreprocessDropViewStmt,
.postprocess = NULL,
.address = NULL,
.markDistributed = false,
};
static DistributeObjectOps Function_Rename = {
.deparse = DeparseRenameFunctionStmt,
.qualify = QualifyRenameFunctionStmt,
@ -1367,6 +1383,11 @@ GetDistributeObjectOps(Node *node)
return &Trigger_Drop;
}
case OBJECT_VIEW:
{
return &View_Drop;
}
default:
{
return &NoDistributeOps;
@ -1396,6 +1417,11 @@ GetDistributeObjectOps(Node *node)
return &Any_Index;
}
case T_ViewStmt:
{
return &Any_View;
}
case T_ReindexStmt:
{
return &Any_Reindex;

View File

@ -0,0 +1,462 @@
/*-------------------------------------------------------------------------
*
* view.c
* Commands for distributing CREATE OR REPLACE VIEW statements.
*
* Copyright (c) Citus Data, Inc.
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "fmgr.h"
#include "access/genam.h"
#include "catalog/objectaddress.h"
#include "commands/extension.h"
#include "distributed/commands.h"
#include "distributed/citus_ruleutils.h"
#include "distributed/commands/utility_hook.h"
#include "distributed/deparser.h"
#include "distributed/errormessage.h"
#include "distributed/listutils.h"
#include "distributed/metadata_sync.h"
#include "distributed/metadata/dependency.h"
#include "distributed/metadata/distobject.h"
#include "distributed/multi_executor.h"
#include "distributed/namespace_utils.h"
#include "distributed/worker_transaction.h"
#include "executor/spi.h"
#include "nodes/nodes.h"
#include "nodes/pg_list.h"
#include "tcop/utility.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
static List * FilterNameListForDistributedViews(List *viewNamesList, bool missing_ok);
static void AppendQualifiedViewNameToCreateViewCommand(StringInfo buf, Oid viewOid);
static void AppendAliasesToCreateViewCommand(StringInfo createViewCommand, Oid viewOid);
static void AppendOptionsToCreateViewCommand(StringInfo createViewCommand, Oid viewOid);
/*
* PreprocessViewStmt is called during the planning phase for CREATE OR REPLACE VIEW
* before it is created on the local node internally.
*/
List *
PreprocessViewStmt(Node *node, const char *queryString,
ProcessUtilityContext processUtilityContext)
{
if (!ShouldPropagate())
{
return NIL;
}
/* check creation against multi-statement transaction policy */
if (!ShouldPropagateCreateInCoordinatedTransction())
{
return NIL;
}
EnsureCoordinator();
return NIL;
}
/*
* PostprocessViewStmt actually creates the commmands we need to run on workers to
* propagate views.
*
* If view depends on any undistributable object, Citus can not distribute it. In order to
* not to prevent users from creating local views on the coordinator WARNING message will
* be sent to the customer about the case instead of erroring out. If no worker nodes exist
* at all, view will be created locally without any WARNING message.
*
* Besides creating the plan we also make sure all (new) dependencies of the view are
* created on all nodes.
*/
List *
PostprocessViewStmt(Node *node, const char *queryString)
{
ViewStmt *stmt = castNode(ViewStmt, node);
if (!ShouldPropagate())
{
return NIL;
}
/* check creation against multi-statement transaction policy */
if (!ShouldPropagateCreateInCoordinatedTransction())
{
return NIL;
}
ObjectAddress viewAddress = GetObjectAddressFromParseTree((Node *) stmt, false);
if (IsObjectAddressOwnedByExtension(&viewAddress, NULL))
{
return NIL;
}
/* If the view has any unsupported dependency, create it locally */
DeferredErrorMessage *errMsg = DeferErrorIfHasUnsupportedDependency(&viewAddress);
if (errMsg != NULL)
{
/*
* Don't need to give any warning/error messages if there is no worker nodes in
* the cluster as user's experience won't be affected on the single node even
* if the view won't be distributed.
*/
if (!HasAnyNodes())
{
return NIL;
}
/*
* Since Citus drops and recreates views while converting a table type, giving a
* NOTICE message is enough if the process in table type conversion function call
*/
if (InTableTypeConversionFunctionCall)
{
RaiseDeferredError(errMsg, DEBUG1);
return NIL;
}
/*
* If the view is already distributed, we should provide an error to not have
* different definition of view on coordinator and worker nodes. If the view
* is not distributed yet, we can create it locally to not affect user's local
* usage experience.
*/
if (IsObjectDistributed(&viewAddress))
{
RaiseDeferredError(errMsg, ERROR);
}
else
{
RaiseDeferredError(errMsg, WARNING);
return NIL;
}
}
EnsureDependenciesExistOnAllNodes(&viewAddress);
char *command = CreateViewDDLCommand(viewAddress.objectId);
/*
* We'd typically use NodeDDLTaskList() for generating node-level DDL commands,
* such as when creating a type. However, views are different in a sense that
* views do not depend on citus tables. Instead, they are `depending` on citus tables.
*
* When NodeDDLTaskList() used, it should be accompanied with sequential execution.
* Here, we do something equivalent to NodeDDLTaskList(), but using metadataSyncCommand
* field. This hack allows us to use the metadata connection
* (see `REQUIRE_METADATA_CONNECTION` flag). Meaning that, view creation is treated as
* a metadata operation.
*
* We do this mostly for performance reasons, because we cannot afford to switch to
* sequential execution, for instance when we are altering or creating distributed
* tables -- which may require significant resources.
*
* The downside of using this hack is that if a view is re-used in the same transaction
* that creates the view on the workers, we might get errors such as the below which
* we consider a decent trade-off currently:
*
* BEGIN;
* CREATE VIEW dist_view ..
* CRETAE TABLE t2(id int, val dist_view);
*
* -- shard creation fails on one of the connections
* SELECT create_distributed_table('t2', 'id');
* ERROR: type "public.dist_view" does not exist
*
*/
DDLJob *ddlJob = palloc0(sizeof(DDLJob));
ddlJob->targetObjectAddress = viewAddress;
ddlJob->metadataSyncCommand = command;
ddlJob->taskList = NIL;
return list_make1(ddlJob);
}
/*
* ViewStmtObjectAddress returns the ObjectAddress for the subject of the
* CREATE [OR REPLACE] VIEW statement.
*/
ObjectAddress
ViewStmtObjectAddress(Node *node, bool missing_ok)
{
ViewStmt *stmt = castNode(ViewStmt, node);
Oid viewOid = RangeVarGetRelid(stmt->view, NoLock, missing_ok);
ObjectAddress viewAddress = { 0 };
ObjectAddressSet(viewAddress, RelationRelationId, viewOid);
return viewAddress;
}
/*
* PreprocessDropViewStmt gets called during the planning phase of a DROP VIEW statement
* and returns a list of DDLJob's that will drop any distributed view from the
* workers.
*
* The DropStmt could have multiple objects to drop, the list of objects will be filtered
* to only keep the distributed views for deletion on the workers. Non-distributed
* views will still be dropped locally but not on the workers.
*/
List *
PreprocessDropViewStmt(Node *node, const char *queryString, ProcessUtilityContext
processUtilityContext)
{
DropStmt *stmt = castNode(DropStmt, node);
if (!ShouldPropagate())
{
return NIL;
}
List *distributedViewNames = FilterNameListForDistributedViews(stmt->objects,
stmt->missing_ok);
if (list_length(distributedViewNames) < 1)
{
/* no distributed view to drop */
return NIL;
}
EnsureCoordinator();
EnsureSequentialMode(OBJECT_VIEW);
/*
* Swap the list of objects before deparsing and restore the old list after. This
* ensures we only have distributed views in the deparsed drop statement.
*/
DropStmt *stmtCopy = copyObject(stmt);
stmtCopy->objects = distributedViewNames;
QualifyTreeNode((Node *) stmtCopy);
const char *dropStmtSql = DeparseTreeNode((Node *) stmtCopy);
List *commands = list_make3(DISABLE_DDL_PROPAGATION,
(void *) dropStmtSql,
ENABLE_DDL_PROPAGATION);
return NodeDDLTaskList(NON_COORDINATOR_NODES, commands);
}
/*
* FilterNameListForDistributedViews takes a list of view names and filters against the
* views that are distributed.
*
* The original list will not be touched, a new list will be created with only the objects
* in there.
*/
static List *
FilterNameListForDistributedViews(List *viewNamesList, bool missing_ok)
{
List *distributedViewNames = NIL;
List *possiblyQualifiedViewName = NULL;
foreach_ptr(possiblyQualifiedViewName, viewNamesList)
{
char *viewName = NULL;
char *schemaName = NULL;
DeconstructQualifiedName(possiblyQualifiedViewName, &schemaName, &viewName);
if (schemaName == NULL)
{
char *objName = NULL;
Oid schemaOid = QualifiedNameGetCreationNamespace(possiblyQualifiedViewName,
&objName);
schemaName = get_namespace_name(schemaOid);
}
Oid schemaId = get_namespace_oid(schemaName, missing_ok);
Oid viewOid = get_relname_relid(viewName, schemaId);
if (!OidIsValid(viewOid))
{
continue;
}
ObjectAddress viewAddress = { 0 };
ObjectAddressSet(viewAddress, RelationRelationId, viewOid);
if (IsObjectDistributed(&viewAddress))
{
distributedViewNames = lappend(distributedViewNames,
possiblyQualifiedViewName);
}
}
return distributedViewNames;
}
/*
* CreateViewDDLCommand returns the DDL command to create the view addressed by
* the viewAddress.
*/
char *
CreateViewDDLCommand(Oid viewOid)
{
StringInfo createViewCommand = makeStringInfo();
appendStringInfoString(createViewCommand, "CREATE OR REPLACE VIEW ");
AppendQualifiedViewNameToCreateViewCommand(createViewCommand, viewOid);
AppendAliasesToCreateViewCommand(createViewCommand, viewOid);
AppendOptionsToCreateViewCommand(createViewCommand, viewOid);
AppendViewDefinitionToCreateViewCommand(createViewCommand, viewOid);
return createViewCommand->data;
}
/*
* AppendQualifiedViewNameToCreateViewCommand adds the qualified view of the given view
* oid to the given create view command.
*/
static void
AppendQualifiedViewNameToCreateViewCommand(StringInfo buf, Oid viewOid)
{
char *viewName = get_rel_name(viewOid);
char *schemaName = get_namespace_name(get_rel_namespace(viewOid));
char *qualifiedViewName = quote_qualified_identifier(schemaName, viewName);
appendStringInfo(buf, "%s ", qualifiedViewName);
}
/*
* AppendAliasesToCreateViewCommand appends aliases to the create view
* command for the existing view.
*/
static void
AppendAliasesToCreateViewCommand(StringInfo createViewCommand, Oid viewOid)
{
/* Get column name aliases from pg_attribute */
ScanKeyData key[1];
ScanKeyInit(&key[0],
Anum_pg_attribute_attrelid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(viewOid));
Relation maprel = table_open(AttributeRelationId, AccessShareLock);
Relation mapidx = index_open(AttributeRelidNumIndexId, AccessShareLock);
SysScanDesc pgAttributeScan = systable_beginscan_ordered(maprel, mapidx, NULL, 1,
key);
bool isInitialAlias = true;
bool hasAlias = false;
HeapTuple attributeTuple;
while (HeapTupleIsValid(attributeTuple = systable_getnext_ordered(pgAttributeScan,
ForwardScanDirection)))
{
Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
const char *aliasName = quote_identifier(NameStr(att->attname));
if (isInitialAlias)
{
appendStringInfoString(createViewCommand, "(");
}
else
{
appendStringInfoString(createViewCommand, ",");
}
appendStringInfoString(createViewCommand, aliasName);
hasAlias = true;
isInitialAlias = false;
}
if (hasAlias)
{
appendStringInfoString(createViewCommand, ") ");
}
systable_endscan_ordered(pgAttributeScan);
index_close(mapidx, AccessShareLock);
table_close(maprel, AccessShareLock);
}
/*
* AppendOptionsToCreateViewCommand add relation options to create view command
* for an existing view
*/
static void
AppendOptionsToCreateViewCommand(StringInfo createViewCommand, Oid viewOid)
{
/* Add rel options to create view command */
char *relOptions = flatten_reloptions(viewOid);
if (relOptions != NULL)
{
appendStringInfo(createViewCommand, "WITH (%s) ", relOptions);
}
}
/*
* AppendViewDefinitionToCreateViewCommand adds the definition of the given view to the
* given create view command.
*/
void
AppendViewDefinitionToCreateViewCommand(StringInfo buf, Oid viewOid)
{
/*
* Set search_path to NIL so that all objects outside of pg_catalog will be
* schema-prefixed.
*/
OverrideSearchPath *overridePath = GetOverrideSearchPath(CurrentMemoryContext);
overridePath->schemas = NIL;
overridePath->addCatalog = true;
PushOverrideSearchPath(overridePath);
/*
* Push the transaction snapshot to be able to get vief definition with pg_get_viewdef
*/
PushActiveSnapshot(GetTransactionSnapshot());
Datum viewDefinitionDatum = DirectFunctionCall1(pg_get_viewdef,
ObjectIdGetDatum(viewOid));
char *viewDefinition = TextDatumGetCString(viewDefinitionDatum);
PopActiveSnapshot();
PopOverrideSearchPath();
appendStringInfo(buf, "AS %s ", viewDefinition);
}
/*
* AlterViewOwnerCommand returns the command to alter view owner command for the
* given view oid.
*/
char *
AlterViewOwnerCommand(Oid viewOid)
{
/* Add alter owner commmand */
StringInfo alterOwnerCommand = makeStringInfo();
char *viewName = get_rel_name(viewOid);
Oid schemaOid = get_rel_namespace(viewOid);
char *schemaName = get_namespace_name(schemaOid);
char *viewOwnerName = TableOwner(viewOid);
char *qualifiedViewName = NameListToQuotedString(list_make2(makeString(schemaName),
makeString(viewName)));
appendStringInfo(alterOwnerCommand,
"ALTER VIEW %s OWNER TO %s", qualifiedViewName,
quote_identifier(viewOwnerName));
return alterOwnerCommand->data;
}

View File

@ -80,7 +80,6 @@ static void deparse_index_columns(StringInfo buffer, List *indexParameterList,
static void AppendStorageParametersToString(StringInfo stringBuffer,
List *optionList);
static void simple_quote_literal(StringInfo buf, const char *val);
static char * flatten_reloptions(Oid relid);
static void AddVacuumParams(ReindexStmt *reindexStmt, StringInfo buffer);
@ -1231,7 +1230,7 @@ pg_get_replica_identity_command(Oid tableRelationId)
* This function comes from PostgreSQL source code in
* src/backend/utils/adt/ruleutils.c
*/
static char *
char *
flatten_reloptions(Oid relid)
{
char *result = NULL;

View File

@ -0,0 +1,94 @@
/*-------------------------------------------------------------------------
*
* deparse_view_stmts.c
*
* All routines to deparse view statements.
*
* Copyright (c), Citus Data, Inc.
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "catalog/namespace.h"
#include "commands/defrem.h"
#include "distributed/citus_ruleutils.h"
#include "distributed/commands.h"
#include "distributed/deparser.h"
#include "distributed/listutils.h"
#include "lib/stringinfo.h"
#include "nodes/parsenodes.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
static void AppendDropViewStmt(StringInfo buf, DropStmt *stmt);
static void AppendViewNameList(StringInfo buf, List *objects);
/*
* DeparseDropViewStmt deparses the given DROP VIEW statement.
*/
char *
DeparseDropViewStmt(Node *node)
{
DropStmt *stmt = castNode(DropStmt, node);
StringInfoData str = { 0 };
initStringInfo(&str);
Assert(stmt->removeType == OBJECT_VIEW);
AppendDropViewStmt(&str, stmt);
return str.data;
}
/*
* AppendDropViewStmt appends the deparsed representation of given drop stmt
* to the given string info buffer.
*/
static void
AppendDropViewStmt(StringInfo buf, DropStmt *stmt)
{
/*
* already tested at call site, but for future it might be collapsed in a
* DeparseDropStmt so be safe and check again
*/
Assert(stmt->removeType == OBJECT_VIEW);
appendStringInfo(buf, "DROP VIEW ");
if (stmt->missing_ok)
{
appendStringInfoString(buf, "IF EXISTS ");
}
AppendViewNameList(buf, stmt->objects);
if (stmt->behavior == DROP_CASCADE)
{
appendStringInfoString(buf, " CASCADE");
}
appendStringInfoString(buf, ";");
}
/*
* AppendViewNameList appends the qualified view names by constructing them from the given
* objects list to the given string info buffer. Note that, objects must hold schema
* qualified view names as its' members.
*/
static void
AppendViewNameList(StringInfo buf, List *viewNamesList)
{
bool isFirstView = true;
List *qualifiedViewName = NULL;
foreach_ptr(qualifiedViewName, viewNamesList)
{
char *quotedQualifiedVieName = NameListToQuotedString(qualifiedViewName);
if (!isFirstView)
{
appendStringInfo(buf, ", ");
}
appendStringInfoString(buf, quotedQualifiedVieName);
isFirstView = false;
}
}

View File

@ -0,0 +1,54 @@
/*-------------------------------------------------------------------------
*
* qualify_view_stmt.c
* Functions specialized in fully qualifying all view statements. These
* functions are dispatched from qualify.c
*
* Copyright (c), Citus Data, Inc.
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "catalog/namespace.h"
#include "distributed/deparser.h"
#include "distributed/listutils.h"
#include "nodes/nodes.h"
#include "utils/guc.h"
#include "utils/lsyscache.h"
/*
* QualifyDropViewStmt quailifies the view names of the DROP VIEW statement.
*/
void
QualifyDropViewStmt(Node *node)
{
DropStmt *stmt = castNode(DropStmt, node);
List *qualifiedViewNames = NIL;
List *possiblyQualifiedViewName = NULL;
foreach_ptr(possiblyQualifiedViewName, stmt->objects)
{
char *viewName = NULL;
char *schemaName = NULL;
DeconstructQualifiedName(possiblyQualifiedViewName, &schemaName, &viewName);
if (schemaName == NULL)
{
char *objname = NULL;
Oid schemaOid = QualifiedNameGetCreationNamespace(possiblyQualifiedViewName,
&objname);
schemaName = get_namespace_name(schemaOid);
List *qualifiedViewName = list_make2(makeString(schemaName),
makeString(viewName));
qualifiedViewNames = lappend(qualifiedViewNames, qualifiedViewName);
}
else
{
qualifiedViewNames = lappend(qualifiedViewNames, possiblyQualifiedViewName);
}
}
stmt->objects = qualifiedViewNames;
}

View File

@ -804,6 +804,11 @@ GetObjectTypeString(ObjectType objType)
return "type";
}
case OBJECT_VIEW:
{
return "view";
}
default:
{
ereport(DEBUG1, (errmsg("unsupported object type"),

View File

@ -165,6 +165,7 @@ static bool FollowAllDependencies(ObjectAddressCollector *collector,
DependencyDefinition *definition);
static void ApplyAddToDependencyList(ObjectAddressCollector *collector,
DependencyDefinition *definition);
static List * GetViewRuleReferenceDependencyList(Oid relationId);
static List * ExpandCitusSupportedTypes(ObjectAddressCollector *collector,
ObjectAddress target);
static ViewDependencyNode * BuildViewDependencyGraph(Oid relationId, HTAB *nodeMap);
@ -425,7 +426,7 @@ DependencyDefinitionFromPgDepend(ObjectAddress target)
/*
* DependencyDefinitionFromPgDepend loads all pg_shdepend records describing the
* DependencyDefinitionFromPgShDepend loads all pg_shdepend records describing the
* dependencies of target.
*/
static List *
@ -747,7 +748,8 @@ SupportedDependencyByCitus(const ObjectAddress *address)
relKind == RELKIND_FOREIGN_TABLE ||
relKind == RELKIND_SEQUENCE ||
relKind == RELKIND_INDEX ||
relKind == RELKIND_PARTITIONED_INDEX)
relKind == RELKIND_PARTITIONED_INDEX ||
relKind == RELKIND_VIEW)
{
return true;
}
@ -801,8 +803,11 @@ DeferErrorIfHasUnsupportedDependency(const ObjectAddress *objectAddress)
* Otherwise, callers are expected to throw the error returned from this
* function as a hard one by ignoring the detail part.
*/
appendStringInfo(detailInfo, "\"%s\" will be created only locally",
objectDescription);
if (!IsObjectDistributed(objectAddress))
{
appendStringInfo(detailInfo, "\"%s\" will be created only locally",
objectDescription);
}
if (SupportedDependencyByCitus(undistributableDependency))
{
@ -813,9 +818,19 @@ DeferErrorIfHasUnsupportedDependency(const ObjectAddress *objectAddress)
objectDescription,
dependencyDescription);
appendStringInfo(hintInfo, "Distribute \"%s\" first to distribute \"%s\"",
dependencyDescription,
objectDescription);
if (IsObjectDistributed(objectAddress))
{
appendStringInfo(hintInfo,
"Distribute \"%s\" first to modify \"%s\" on worker nodes",
dependencyDescription,
objectDescription);
}
else
{
appendStringInfo(hintInfo, "Distribute \"%s\" first to distribute \"%s\"",
dependencyDescription,
objectDescription);
}
return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,
errorInfo->data, detailInfo->data, hintInfo->data);
@ -893,7 +908,9 @@ GetUndistributableDependency(const ObjectAddress *objectAddress)
{
char relKind = get_rel_relkind(dependency->objectId);
if (relKind == RELKIND_SEQUENCE || relKind == RELKIND_COMPOSITE_TYPE)
if (relKind == RELKIND_SEQUENCE ||
relKind == RELKIND_COMPOSITE_TYPE ||
relKind == RELKIND_VIEW)
{
/* citus knows how to auto-distribute these dependencies */
continue;
@ -1307,9 +1324,26 @@ ExpandCitusSupportedTypes(ObjectAddressCollector *collector, ObjectAddress targe
* create all objects required by the indices before we create the table
* including indices.
*/
List *indexDependencyList = GetRelationIndicesDependencyList(relationId);
result = list_concat(result, indexDependencyList);
/*
* Get the dependencies of the rule for the given view. PG keeps internal
* dependency between view and rule. As it is stated on the PG doc, if
* there is an internal dependency, dependencies of the dependent object
* behave much like they were dependencies of the referenced object.
*
* We need to expand dependencies by including dependencies of the rule
* internally dependent to the view. PG doesn't keep any dependencies
* from view to any object, but it keeps an internal dependency to the
* rule and that rule has dependencies to other objects.
*/
char relKind = get_rel_relkind(relationId);
if (relKind == RELKIND_VIEW)
{
List *ruleRefDepList = GetViewRuleReferenceDependencyList(relationId);
result = list_concat(result, ruleRefDepList);
}
}
default:
@ -1322,6 +1356,64 @@ ExpandCitusSupportedTypes(ObjectAddressCollector *collector, ObjectAddress targe
}
/*
* GetViewRuleReferenceDependencyList returns the dependencies of the view's
* internal rule dependencies.
*/
static List *
GetViewRuleReferenceDependencyList(Oid viewId)
{
List *dependencyTupleList = GetPgDependTuplesForDependingObjects(RelationRelationId,
viewId);
List *nonInternalDependenciesOfDependingRules = NIL;
HeapTuple depTup = NULL;
foreach_ptr(depTup, dependencyTupleList)
{
Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
/*
* Dependencies of the internal rule dependency should be handled as the dependency
* of referenced view object.
*
* PG doesn't keep dependency relation between views and dependent objects directly
* but it keeps an internal dependency relation between the view and the rule, then
* keeps the dependent objects of the view as non-internal dependencies of the
* internally dependent rule object.
*/
if (pg_depend->deptype == DEPENDENCY_INTERNAL && pg_depend->classid ==
RewriteRelationId)
{
ObjectAddress ruleAddress = { 0 };
ObjectAddressSet(ruleAddress, RewriteRelationId, pg_depend->objid);
/* Expand results with the noninternal dependencies of it */
List *ruleDependencies = DependencyDefinitionFromPgDepend(ruleAddress);
DependencyDefinition *dependencyDef = NULL;
foreach_ptr(dependencyDef, ruleDependencies)
{
/*
* Follow all dependencies of the internally dependent rule dependencies
* except it is an internal dependency of view itself.
*/
if (dependencyDef->data.pg_depend.deptype == DEPENDENCY_INTERNAL ||
(dependencyDef->data.pg_depend.refclassid == RelationRelationId &&
dependencyDef->data.pg_depend.refobjid == viewId))
{
continue;
}
nonInternalDependenciesOfDependingRules =
lappend(nonInternalDependenciesOfDependingRules, dependencyDef);
}
}
}
return nonInternalDependenciesOfDependingRules;
}
/*
* GetRelationSequenceDependencyList returns the sequence dependency definition
* list for the given relation.

View File

@ -434,7 +434,9 @@ ShouldSyncUserCommandForObject(ObjectAddress objectAddress)
{
if (objectAddress.classId == RelationRelationId)
{
return ShouldSyncTableMetadata(objectAddress.objectId);
Oid relOid = objectAddress.objectId;
return ShouldSyncTableMetadata(relOid) ||
get_rel_relkind(relOid) == RELKIND_VIEW;
}
return false;

View File

@ -420,6 +420,7 @@ ErrorIfCurrentUserCanNotDistributeObject(ObjectType type, ObjectAddress *addr,
case OBJECT_TABLE:
case OBJECT_EXTENSION:
case OBJECT_COLLATION:
case OBJECT_VIEW:
{
check_object_ownership(userId, type, *addr, node, *relation);
break;

View File

@ -565,6 +565,7 @@ ResetGlobalVariables()
TransactionModifiedNodeMetadata = false;
NodeMetadataSyncOnCommit = false;
InTopLevelDelegatedFunctionCall = false;
InTableTypeConversionFunctionCall = false;
ResetWorkerErrorIndication();
memset(&AllowedDistributionColumnValue, 0,
sizeof(AllowedDistributionColumn));

View File

@ -46,6 +46,7 @@ extern char * pg_get_indexclusterdef_string(Oid indexRelationId);
extern bool contain_nextval_expression_walker(Node *node, void *context);
extern char * pg_get_replica_identity_command(Oid tableRelationId);
extern const char * RoleSpecString(RoleSpec *spec, bool withQuoteIdentifier);
extern char * flatten_reloptions(Oid relid);
/* Function declarations for version dependent PostgreSQL ruleutils functions */
extern void pg_get_query_def(Query *query, StringInfo buffer);

View File

@ -653,6 +653,18 @@ extern void UpdateFunctionDistributionInfo(const ObjectAddress *distAddress,
/* vacuum.c - forward declarations */
extern void PostprocessVacuumStmt(VacuumStmt *vacuumStmt, const char *vacuumCommand);
/* view.c - forward declarations */
extern List * PreprocessViewStmt(Node *node, const char *queryString,
ProcessUtilityContext processUtilityContext);
extern List * PostprocessViewStmt(Node *node, const char *queryString);
extern ObjectAddress ViewStmtObjectAddress(Node *node, bool missing_ok);
extern List * PreprocessDropViewStmt(Node *node, const char *queryString,
ProcessUtilityContext processUtilityContext);
extern char * CreateViewDDLCommand(Oid viewOid);
extern char * AlterViewOwnerCommand(Oid viewOid);
extern char * DeparseViewStmt(Node *node);
extern char * DeparseDropViewStmt(Node *node);
/* trigger.c - forward declarations */
extern List * GetExplicitTriggerCommandList(Oid relationId);
extern HeapTuple GetTriggerTupleById(Oid triggerId, bool missingOk);

View File

@ -144,6 +144,10 @@ extern Oid TypeOidGetNamespaceOid(Oid typeOid);
extern ObjectAddress GetObjectAddressFromParseTree(Node *parseTree, bool missing_ok);
extern ObjectAddress RenameAttributeStmtObjectAddress(Node *stmt, bool missing_ok);
/* forward declarations for deparse_view_stmts.c */
extern void QualifyDropViewStmt(Node *node);
extern void AppendViewDefinitionToCreateViewCommand(StringInfo buf, Oid viewOid);
/* forward declarations for deparse_function_stmts.c */
extern char * DeparseDropFunctionStmt(Node *stmt);
extern char * DeparseAlterFunctionStmt(Node *stmt);

View File

@ -41,6 +41,15 @@
#define SHARD_SIZES_COLUMN_COUNT (3)
/*
* Flag to keep track of whether the process is currently in a function converting the
* type of the table. Since it only affects the level of the log shown while dropping/
* recreating table within the table type conversion, rollbacking to the savepoint hasn't
* been implemented for the sake of simplicity. If you are planning to use that flag for
* any other purpose, please consider implementing that.
*/
extern bool InTableTypeConversionFunctionCall;
/* In-memory representation of a typed tuple in pg_dist_shard. */
typedef struct ShardInterval
{

View File

@ -575,6 +575,9 @@ CREATE TABLE local(a int, b bigserial, c int default nextval('c_seq'));
INSERT INTO local VALUES (3);
create materialized view m_local as select * from local;
create view v_local as select * from local;
WARNING: "view v_local" has dependency to "table local" that is not in Citus' metadata
DETAIL: "view v_local" will be created only locally
HINT: Distribute "table local" first to distribute "view v_local"
CREATE TABLE ref(a int);
SELECT create_Reference_table('ref');
create_reference_table

View File

@ -683,11 +683,13 @@ SELECT count(*) FROM distributed_table WHERE b in
0
(1 row)
SET citus.enable_ddl_propagation TO OFF;
CREATE VIEW view_2 AS
SELECT count(*)
FROM citus_local_table
JOIN citus_local_table_2 USING (a)
JOIN distributed_table USING (a);
RESET citus.enable_ddl_propagation;
-- should fail as view contains direct local dist join
SELECT count(*) FROM view_2;
count
@ -695,11 +697,13 @@ SELECT count(*) FROM view_2;
1
(1 row)
SET citus.enable_ddl_propagation TO OFF;
CREATE VIEW view_3
AS SELECT count(*)
FROM citus_local_table_2
JOIN reference_table
USING (a);
RESET citus.enable_ddl_propagation;
-- ok
SELECT count(*) FROM view_3;
count

View File

@ -158,8 +158,10 @@ BEGIN;
INSERT INTO test VALUES (1, 2);
COMMIT;
-- basic view queries
SET citus.enable_ddl_propagation TO OFF;
CREATE VIEW simple_view AS
SELECT count(*) as cnt FROM test t1 JOIN test t2 USING (x);
RESET citus.enable_ddl_propagation;
SELECT * FROM simple_view;
cnt
---------------------------------------------------------------------

View File

@ -46,21 +46,6 @@ FROM pg_catalog.pg_class c
WHERE n.nspname IN ('drop_partitioned_table', 'schema1')
AND c.relkind IN ('r','p')
ORDER BY 1, 2;
\c - - - :worker_1_port
SET search_path = drop_partitioned_table;
CREATE VIEW tables_info AS
SELECT n.nspname as "Schema",
c.relname as "Name",
CASE c.relkind WHEN 'r' THEN 'table' WHEN 'p' THEN 'partitioned table' END as "Type",
pg_catalog.pg_get_userbyid(c.relowner) as "Owner"
FROM pg_catalog.pg_class c
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
LEFT JOIN pg_user u ON u.usesysid = c.relowner
WHERE n.nspname IN ('drop_partitioned_table', 'schema1')
AND c.relkind IN ('r','p')
ORDER BY 1, 2;
\c - - - :master_port
SET search_path = drop_partitioned_table;
SET citus.next_shard_id TO 721000;
-- CASE 1
-- Dropping the parent table

View File

@ -177,7 +177,7 @@ SELECT * FROM run_command_on_workers($$SELECT pg_identify_object_as_address(clas
localhost | 57638 | t | (function,"{function_propagation_schema,func_6}",{function_propagation_schema.function_prop_table})
(2 rows)
-- Views are not supported
-- Views are supported
CREATE VIEW function_prop_view AS SELECT * FROM function_prop_table;
CREATE OR REPLACE FUNCTION func_7(param_1 function_prop_view)
RETURNS int
@ -187,8 +187,6 @@ BEGIN
return 1;
END;
$$;
WARNING: "function func_7(function_prop_view)" has dependency on unsupported object "view function_prop_view"
DETAIL: "function func_7(function_prop_view)" will be created only locally
CREATE OR REPLACE FUNCTION func_8(param_1 int)
RETURNS function_prop_view
LANGUAGE plpgsql AS
@ -197,8 +195,6 @@ BEGIN
return 1;
END;
$$;
WARNING: "function func_8(integer)" has dependency on unsupported object "view function_prop_view"
DETAIL: "function func_8(integer)" will be created only locally
-- Check within transaction
BEGIN;
CREATE TYPE type_in_transaction AS (a int, b int);
@ -504,7 +500,7 @@ BEGIN;
ALTER TABLE table_to_dist ADD COLUMN col_1 int default function_propagation_schema.non_dist_func(NULL::non_dist_table_for_function);
ERROR: "table table_to_dist" has dependency to "table non_dist_table_for_function" that is not in Citus' metadata
HINT: Distribute "table non_dist_table_for_function" first to distribute "table table_to_dist"
HINT: Distribute "table non_dist_table_for_function" first to modify "table table_to_dist" on worker nodes
ROLLBACK;
-- Adding multiple columns with default values should propagate the function
BEGIN;

View File

@ -358,6 +358,9 @@ DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS c
(1 row)
CREATE VIEW local_regular_view AS SELECT * FROM local;
WARNING: "view local_regular_view" has dependency to "table local" that is not in Citus' metadata
DETAIL: "view local_regular_view" will be created only locally
HINT: Distribute "table local" first to distribute "view local_regular_view"
CREATE VIEW dist_regular_view AS SELECT * FROM distributed;
SELECT count(*) FROM distributed JOIN local_regular_view USING (id);
DEBUG: generating subplan XXX_1 for subquery SELECT local.id, local.title FROM local_dist_join_mixed.local
@ -1601,14 +1604,5 @@ DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS c
101
(1 row)
SET client_min_messages TO ERROR;
DROP SCHEMA local_dist_join_mixed CASCADE;
DEBUG: switching to sequential query execution mode
DETAIL: A command for a distributed schema is run. To make sure subsequent commands see the schema correctly we need to make sure to use only one connection for all future commands
NOTICE: drop cascades to 7 other objects
DETAIL: drop cascades to table distributed
drop cascades to table reference
drop cascades to table local
drop cascades to table unlogged_local
drop cascades to materialized view mat_view
drop cascades to view local_regular_view
drop cascades to view dist_regular_view

View File

@ -398,7 +398,9 @@ INSERT INTO distributed_table VALUES (1, '22', 20);
NOTICE: executing the command locally: INSERT INTO local_shard_execution.distributed_table_1470001 (key, value, age) VALUES (1, '22'::text, 20)
INSERT INTO second_distributed_table VALUES (1, '1');
NOTICE: executing the command locally: INSERT INTO local_shard_execution.second_distributed_table_1470005 (key, value) VALUES (1, '1'::text)
SET citus.enable_ddl_propagation TO OFF;
CREATE VIEW abcd_view AS SELECT * FROM abcd;
RESET citus.enable_ddl_propagation;
SELECT * FROM abcd first join abcd second on first.b = second.b ORDER BY 1,2,3,4;
b | c | d | b | c | d
---------------------------------------------------------------------
@ -1656,8 +1658,10 @@ NOTICE: executing the command locally: DELETE FROM local_shard_execution.distri
ROLLBACK;
-- probably not a realistic case since views are not very
-- well supported with MX
SET citus.enable_ddl_propagation TO OFF;
CREATE VIEW v_local_query_execution AS
SELECT * FROM distributed_table WHERE key = 500;
RESET citus.enable_ddl_propagation;
SELECT * FROM v_local_query_execution;
NOTICE: executing the command locally: SELECT key, value, age FROM (SELECT distributed_table.key, distributed_table.value, distributed_table.age FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE (distributed_table.key OPERATOR(pg_catalog.=) 500)) v_local_query_execution
key | value | age
@ -1667,8 +1671,10 @@ NOTICE: executing the command locally: SELECT key, value, age FROM (SELECT dist
-- similar test, but this time the view itself is a non-local
-- query, but the query on the view is local
SET citus.enable_ddl_propagation TO OFF;
CREATE VIEW v_local_query_execution_2 AS
SELECT * FROM distributed_table;
RESET citus.enable_ddl_propagation;
SELECT * FROM v_local_query_execution_2 WHERE key = 500;
NOTICE: executing the command locally: SELECT key, value, age FROM (SELECT distributed_table.key, distributed_table.value, distributed_table.age FROM local_shard_execution.distributed_table_1470003 distributed_table) v_local_query_execution_2 WHERE (key OPERATOR(pg_catalog.=) 500)
key | value | age

View File

@ -334,7 +334,9 @@ NOTICE: executing the command locally: SELECT key, value FROM local_shard_execu
-- Put row back for other tests
INSERT INTO distributed_table VALUES (1, '22', 20);
NOTICE: executing the command locally: INSERT INTO local_shard_execution_replicated.distributed_table_1500001 (key, value, age) VALUES (1, '22'::text, 20)
SET citus.enable_ddl_propagation TO OFF;
CREATE VIEW abcd_view AS SELECT * FROM abcd;
RESET citus.enable_ddl_propagation;
SELECT * FROM abcd first join abcd second on first.b = second.b ORDER BY 1,2,3,4;
b | c | d | b | c | d
---------------------------------------------------------------------
@ -1650,8 +1652,10 @@ NOTICE: executing the command locally: DELETE FROM local_shard_execution_replic
ROLLBACK;
-- probably not a realistic case since views are not very
-- well supported with MX
SET citus.enable_ddl_propagation TO OFF;
CREATE VIEW v_local_query_execution AS
SELECT * FROM distributed_table WHERE key = 500;
RESET citus.enable_ddl_propagation;
SELECT * FROM v_local_query_execution;
NOTICE: executing the command locally: SELECT key, value, age FROM (SELECT distributed_table.key, distributed_table.value, distributed_table.age FROM local_shard_execution_replicated.distributed_table_1500003 distributed_table WHERE (distributed_table.key OPERATOR(pg_catalog.=) 500)) v_local_query_execution
key | value | age
@ -1661,8 +1665,10 @@ NOTICE: executing the command locally: SELECT key, value, age FROM (SELECT dist
-- similar test, but this time the view itself is a non-local
-- query, but the query on the view is local
SET citus.enable_ddl_propagation TO OFF;
CREATE VIEW v_local_query_execution_2 AS
SELECT * FROM distributed_table;
RESET citus.enable_ddl_propagation;
SELECT * FROM v_local_query_execution_2 WHERE key = 500;
NOTICE: executing the command locally: SELECT key, value, age FROM (SELECT distributed_table.key, distributed_table.value, distributed_table.age FROM local_shard_execution_replicated.distributed_table_1500003 distributed_table) v_local_query_execution_2 WHERE (key OPERATOR(pg_catalog.=) 500)
key | value | age

View File

@ -991,6 +991,9 @@ DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS c
(1 row)
CREATE view loc_view AS SELECT * FROM postgres_table WHERE key > 0;
WARNING: "view loc_view" has dependency to "table postgres_table" that is not in Citus' metadata
DETAIL: "view loc_view" will be created only locally
HINT: Distribute "table postgres_table" first to distribute "view loc_view"
UPDATE loc_view SET key = (SELECT COUNT(*) FROM distributed_table);
DEBUG: generating subplan XXX_1 for subquery SELECT count(*) AS count FROM local_table_join.distributed_table
DEBUG: Plan XXX query after replacing subqueries and CTEs: UPDATE local_table_join.postgres_table SET key = (SELECT intermediate_result.count FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(count bigint)) FROM local_table_join.postgres_table WHERE (postgres_table.key OPERATOR(pg_catalog.>) 0)

View File

@ -166,8 +166,11 @@ SELECT master_get_table_ddl_events('fiddly_table');
ALTER TABLE public.fiddly_table OWNER TO postgres
(3 rows)
-- propagating views is not supported
-- propagating views is not supported if local table dependency exists
CREATE VIEW local_view AS SELECT * FROM simple_table;
WARNING: "view local_view" has dependency to "table simple_table" that is not in Citus' metadata
DETAIL: "view local_view" will be created only locally
HINT: Distribute "table simple_table" first to distribute "view local_view"
SELECT master_get_table_ddl_events('local_view');
ERROR: local_view is not a regular, foreign or partitioned table
-- clean up

View File

@ -4301,12 +4301,14 @@ WHERE schemaname = 'partitioning_schema' AND tablename ilike '%part_table_with_%
(2 rows)
-- should work properly - no names clashes
SET client_min_messages TO WARNING;
SELECT 1 FROM citus_activate_node('localhost', :worker_1_port);
?column?
---------------------------------------------------------------------
1
1
(1 row)
RESET client_min_messages;
\c - - - :worker_1_port
-- check that indexes are named properly
SELECT tablename, indexname FROM pg_indexes

View File

@ -1076,7 +1076,6 @@ ERROR: cannot pushdown the subquery
-- make sure that non-colocated subquery joins work fine in
-- modifications
CREATE TABLE table1 (id int, tenant_id int);
CREATE VIEW table1_view AS SELECT * from table1 where id < 100;
CREATE TABLE table2 (id int, tenant_id int) partition by range(tenant_id);
CREATE TABLE table2_p1 PARTITION OF table2 FOR VALUES FROM (1) TO (10);
-- modifications on the partitons are only allowed with rep=1
@ -1093,6 +1092,7 @@ SELECT create_distributed_table('table1','tenant_id');
(1 row)
CREATE VIEW table1_view AS SELECT * from table1 where id < 100;
-- all of the above queries are non-colocated subquery joins
-- because the views are replaced with subqueries
UPDATE table2 SET id=20 FROM table1_view WHERE table1_view.id=table2.id;

View File

@ -50,8 +50,9 @@ ON TRUE
---------------------------------------------------------------------
("composite type","""object prop""",t1,"""object prop"".t1")
(schema,,"""object prop""","""object prop""")
(table,"""object prop""",test,"""object prop"".test")
(type,"""object prop""",t1,"""object prop"".t1")
(3 rows)
(4 rows)
-- find all the dependencies of type t1
SELECT

View File

@ -305,6 +305,9 @@ SELECT run_command_on_workers($$SELECT count(*) FROM pg_extension WHERE extname
-- propagated to the workers. the user should run it manually on the workers
CREATE TABLE t1 (A int);
CREATE VIEW v1 AS select * from t1;
WARNING: "view v1" has dependency to "table t1" that is not in Citus' metadata
DETAIL: "view v1" will be created only locally
HINT: Distribute "table t1" first to distribute "view v1"
ALTER EXTENSION seg ADD VIEW v1;
ALTER EXTENSION seg DROP VIEW v1;
DROP VIEW v1;

View File

@ -110,6 +110,7 @@ WHERE
SET search_path TO recursive_dml_queries_mx, public;
CREATE TABLE recursive_dml_queries_mx.local_table (id text, name text);
INSERT INTO local_table SELECT i::text, 'user_' || i FROM generate_series (0, 100) i;
SET citus.enable_ddl_propagation TO OFF;
CREATE VIEW tenant_ids AS
SELECT
tenant_id, name
@ -118,6 +119,7 @@ CREATE VIEW tenant_ids AS
WHERE
distributed_table.dept::text = reference_table.id
ORDER BY 2 DESC, 1 DESC;
RESET citus.enable_ddl_propagation;
-- we currently do not allow local tables in modification queries
UPDATE
distributed_table

View File

@ -12,6 +12,9 @@ CREATE RECURSIVE VIEW recursive_view(val_1, val_2) AS
WHERE val_2 < 50
);
CREATE RECURSIVE VIEW recursive_defined_non_recursive_view(c) AS (SELECT 1 FROM local_table);
WARNING: "view recursive_defined_non_recursive_view" has dependency to "table local_table" that is not in Citus' metadata
DETAIL: "view recursive_defined_non_recursive_view" will be created only locally
HINT: Distribute "table local_table" first to distribute "view recursive_defined_non_recursive_view"
CREATE TABLE ref_table(a int, b INT);
SELECT create_reference_table('ref_table');
create_reference_table

View File

@ -382,6 +382,9 @@ $Q$);
(2 rows)
CREATE VIEW local_table_v AS SELECT * FROM local_table WHERE a BETWEEN 1 AND 10;
WARNING: "view local_table_v" has dependency to "table local_table" that is not in Citus' metadata
DETAIL: "view local_table_v" will be created only locally
HINT: Distribute "table local_table" first to distribute "view local_table_v"
SELECT public.coordinator_plan($Q$
EXPLAIN (COSTS FALSE)
SELECT * FROM squares JOIN local_table_v ON squares.a = local_table_v.a;

View File

@ -140,6 +140,7 @@ INSERT INTO sensors_news VALUES (DEFAULT, DEFAULT, '2021-01-01') RETURNING *;
(1 row)
\c - - - :master_port
SET client_min_messages TO ERROR;
SELECT 1 FROM citus_activate_node('localhost', :worker_1_port);
?column?
---------------------------------------------------------------------

View File

@ -438,6 +438,9 @@ CREATE VIEW replication_test_table_placements_per_node AS
AND shardstate != 4
GROUP BY nodename, nodeport
ORDER BY nodename, nodeport;
WARNING: "view replication_test_table_placements_per_node" has dependency to "table replication_test_table" that is not in Citus' metadata
DETAIL: "view replication_test_table_placements_per_node" will be created only locally
HINT: Distribute "table replication_test_table" first to distribute "view replication_test_table_placements_per_node"
-- Create four shards with replication factor 2, and delete the placements
-- with smaller port number to simulate under-replicated shards.
SELECT count(master_create_empty_shard('replication_test_table'))

View File

@ -142,8 +142,9 @@ where (select pg_catalog.array_agg(id) from sqlsmith_failures.countries)
-- cleanup
DROP SCHEMA sqlsmith_failures CASCADE;
NOTICE: drop cascades to 6 other objects
NOTICE: drop cascades to 7 other objects
DETAIL: drop cascades to table countries
drop cascades to table countries_1280000
drop cascades to table orgs
drop cascades to table users
drop cascades to table orders

View File

@ -141,7 +141,6 @@ SELECT * FROM distributed_table_1;
---------------------------------------------------------------------
(0 rows)
CREATE VIEW test_view AS SELECT COUNT(*) FROM distributed_table_3;
CREATE MATERIALIZED VIEW test_matview AS SELECT COUNT(*) FROM distributed_table_3;
SELECT * FROM test_view;
count

View File

@ -278,6 +278,8 @@ ORDER BY 1 LIMIT 3;
ERROR: correlated subqueries are not supported when the FROM clause contains a subquery without FROM
-- sublink on view
CREATE TEMP VIEW view_1 AS (SELECT user_id, value_2 FROM users_table WHERE user_id = 1 AND value_1 = 1 ORDER BY 1,2);
WARNING: "view view_1" has dependency on unsupported object "schema pg_temp_xxx"
DETAIL: "view view_1" will be created only locally
-- with distribution column group by
SELECT (SELECT value_2 FROM view_1 WHERE user_id = e.user_id GROUP BY user_id, value_2)
FROM events_table e
@ -323,6 +325,8 @@ ORDER BY 1 LIMIT 3;
-- sublink on reference table view
CREATE TEMP VIEW view_2 AS (SELECT user_id, value_2 FROM users_reference_table WHERE user_id = 1 AND value_1 = 1);
WARNING: "view view_2" has dependency on unsupported object "schema pg_temp_xxx"
DETAIL: "view view_2" will be created only locally
SELECT (SELECT value_2 FROM view_2 WHERE user_id = e.user_id GROUP BY user_id, value_2)
FROM events_table e
GROUP BY 1

View File

@ -1096,6 +1096,8 @@ CREATE TEMPORARY VIEW correlated_subquery_view AS
FROM events_table e1
WHERE e1.user_id = u1.user_id
) > 0;
WARNING: "view correlated_subquery_view" has dependency on unsupported object "schema pg_temp_xxx"
DETAIL: "view correlated_subquery_view" will be created only locally
SELECT sum(user_id) FROM correlated_subquery_view;
sum
---------------------------------------------------------------------

View File

@ -209,6 +209,9 @@ FROM
) as foo
WHERE foo.user_id = cte.user_id
) as foo, users_table WHERE foo.cnt > users_table.value_2;
WARNING: "view subquery_and_ctes" has dependency to "table users_table_local" that is not in Citus' metadata
DETAIL: "view subquery_and_ctes" will be created only locally
HINT: Distribute "table users_table_local" first to distribute "view subquery_and_ctes"
SELECT * FROM subquery_and_ctes
ORDER BY 3 DESC, 1 DESC, 2 DESC, 4 DESC
LIMIT 5;

View File

@ -281,6 +281,9 @@ FROM
) as baz
WHERE baz.user_id = users_table.user_id
) as sub1;
WARNING: "view subquery_from_from_where_local_table" has dependency to "table events_table_local" that is not in Citus' metadata
DETAIL: "view subquery_from_from_where_local_table" will be created only locally
HINT: Distribute "table events_table_local" first to distribute "view subquery_from_from_where_local_table"
SELECT
*
FROM
@ -337,6 +340,9 @@ FROM
SELECT user_id FROM users_table_local WHERE user_id = 2
) baw
WHERE foo.value_2 = bar.user_id AND baz.value_2 = bar.user_id AND bar.user_id = baw.user_id;
WARNING: "view all_executors_view" has dependency to "table users_table_local" that is not in Citus' metadata
DETAIL: "view all_executors_view" will be created only locally
HINT: Distribute "table users_table_local" first to distribute "view all_executors_view"
SELECT
*
FROM
@ -390,6 +396,9 @@ FROM
) as foo
WHERE foo.user_id = cte.user_id
) as foo, users_table WHERE foo.cnt > users_table.value_2;
WARNING: "view subquery_and_ctes" has dependency to "table users_table_local" that is not in Citus' metadata
DETAIL: "view subquery_and_ctes" will be created only locally
HINT: Distribute "table users_table_local" first to distribute "view subquery_and_ctes"
SELECT * FROM subquery_and_ctes
ORDER BY 3 DESC, 1 DESC, 2 DESC, 4 DESC
LIMIT 5;
@ -437,6 +446,9 @@ SELECT time, event_type, value_2, value_3 FROM
events_table
WHERE
foo.user_id = events_table.value_2;
WARNING: "view subquery_and_ctes_second" has dependency to "table users_table_local" that is not in Citus' metadata
DETAIL: "view subquery_and_ctes_second" will be created only locally
HINT: Distribute "table users_table_local" first to distribute "view subquery_and_ctes_second"
SELECT * FROM subquery_and_ctes_second
ORDER BY 3 DESC, 2 DESC, 1 DESC
LIMIT 5;

View File

@ -899,6 +899,9 @@ INSERT INTO range_dist_table_2 VALUES ((10, 91));
INSERT INTO range_dist_table_2 VALUES ((20, 100));
-- the following can be pushed down
CREATE OR REPLACE VIEW v2 AS SELECT * from range_dist_table_2 UNION ALL SELECT * from range_dist_table_2;
WARNING: "view v2" has dependency to "table range_dist_table_2" that is not in Citus' metadata
DETAIL: "view v2" will be created only locally
HINT: Distribute "table range_dist_table_2" first to distribute "view v2"
SELECT public.explain_has_distributed_subplan($$
EXPLAIN
SELECT COUNT(dist_col) FROM v2;

View File

@ -0,0 +1,440 @@
-- Tests to check propagation of all view commands
CREATE SCHEMA view_prop_schema;
SET search_path to view_prop_schema;
-- Check creating views depending on different types of tables
-- and from multiple schemas
-- Check the most basic one
CREATE VIEW prop_view_basic AS SELECT 1;
-- Try to create view depending local table, then try to recreate it after distributing the table
CREATE TABLE view_table_1(id int, val_1 text);
CREATE VIEW prop_view_1 AS
SELECT * FROM view_table_1;
WARNING: "view prop_view_1" has dependency to "table view_table_1" that is not in Citus' metadata
DETAIL: "view prop_view_1" will be created only locally
HINT: Distribute "table view_table_1" first to distribute "view prop_view_1"
SELECT create_distributed_table('view_table_1', 'id');
create_distributed_table
---------------------------------------------------------------------
(1 row)
CREATE OR REPLACE VIEW prop_view_1 AS
SELECT * FROM view_table_1;
-- Try to create view depending local table, then try to recreate it after making the table reference table
CREATE TABLE view_table_2(id int PRIMARY KEY, val_1 text);
CREATE VIEW prop_view_2 AS
SELECT view_table_1.id, view_table_2.val_1 FROM view_table_1 INNER JOIN view_table_2
ON view_table_1.id = view_table_2.id;
WARNING: "view prop_view_2" has dependency to "table view_table_2" that is not in Citus' metadata
DETAIL: "view prop_view_2" will be created only locally
HINT: Distribute "table view_table_2" first to distribute "view prop_view_2"
SELECT create_reference_table('view_table_2');
create_reference_table
---------------------------------------------------------------------
(1 row)
CREATE OR REPLACE VIEW prop_view_2 AS
SELECT view_table_1.id, view_table_2.val_1 FROM view_table_1 INNER JOIN view_table_2
ON view_table_1.id = view_table_2.id;
-- Try to create view depending local table, then try to recreate it after making the table citus local table
CREATE TABLE view_table_3(id int, val_1 text);
CREATE VIEW prop_view_3 AS
SELECT * FROM view_table_1 WHERE id IN
(SELECT view_table_2.id FROM view_table_2 INNER JOIN view_table_3 ON view_table_2.id = view_table_3.id);
WARNING: "view prop_view_3" has dependency to "table view_table_3" that is not in Citus' metadata
DETAIL: "view prop_view_3" will be created only locally
HINT: Distribute "table view_table_3" first to distribute "view prop_view_3"
SET client_min_messages TO WARNING;
SELECT 1 FROM citus_add_node('localhost', :master_port, groupid=>0);
?column?
---------------------------------------------------------------------
1
(1 row)
RESET client_min_messages;
ALTER TABLE view_table_3
ADD CONSTRAINT f_key_for_local_table
FOREIGN KEY(id)
REFERENCES view_table_2(id);
CREATE OR REPLACE VIEW prop_view_3 AS
SELECT * FROM view_table_1 WHERE id IN
(SELECT view_table_2.id FROM view_table_2 INNER JOIN view_table_3 ON view_table_2.id = view_table_3.id);
-- Try to create view depending on PG metadata table
CREATE VIEW prop_view_4 AS
SELECT * FROM pg_stat_activity;
-- Try to create view depending on Citus metadata table
CREATE VIEW prop_view_5 AS
SELECT * FROM citus_dist_stat_activity;
-- Try to create table depending on a local table from another schema, then try to create it again after distributing the table
CREATE SCHEMA view_prop_schema_inner;
SET search_path TO view_prop_schema_inner;
-- Create local table for tests below
CREATE TABLE view_table_4(id int, val_1 text);
-- Create a distributed table and view to test drop view below
CREATE TABLE inner_view_table(id int);
SELECT create_distributed_table('inner_view_table','id');
create_distributed_table
---------------------------------------------------------------------
(1 row)
CREATE VIEW inner_view_prop AS SELECT * FROM inner_view_table;
SET search_path to view_prop_schema;
CREATE VIEW prop_view_6 AS
SELECT vt1.id, vt4.val_1 FROM view_table_1 AS vt1
INNER JOIN view_prop_schema_inner.view_table_4 AS vt4 ON vt1.id = vt4.id;
WARNING: "view prop_view_6" has dependency to "table view_prop_schema_inner.view_table_4" that is not in Citus' metadata
DETAIL: "view prop_view_6" will be created only locally
HINT: Distribute "table view_prop_schema_inner.view_table_4" first to distribute "view prop_view_6"
SELECT create_distributed_table('view_prop_schema_inner.view_table_4','id');
create_distributed_table
---------------------------------------------------------------------
(1 row)
CREATE OR REPLACE VIEW prop_view_6 AS
SELECT vt1.id, vt4.val_1 FROM view_table_1 AS vt1
INNER JOIN view_prop_schema_inner.view_table_4 AS vt4 ON vt1.id = vt4.id;
-- Show that all views are propagated as distributed object
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%prop_view_%' ORDER BY 1;
obj_identifier
---------------------------------------------------------------------
(view,"{view_prop_schema,prop_view_1}",{})
(view,"{view_prop_schema,prop_view_2}",{})
(view,"{view_prop_schema,prop_view_3}",{})
(view,"{view_prop_schema,prop_view_4}",{})
(view,"{view_prop_schema,prop_view_5}",{})
(view,"{view_prop_schema,prop_view_6}",{})
(view,"{view_prop_schema,prop_view_basic}",{})
(7 rows)
-- Check creating views depending various kind of objects
-- Tests will also check propagating dependent objects
-- Depending on function
SET citus.enable_ddl_propagation TO OFF;
CREATE OR REPLACE FUNCTION func_1_for_view(param_1 int)
RETURNS int
LANGUAGE plpgsql AS
$$
BEGIN
return param_1;
END;
$$;
RESET citus.enable_ddl_propagation;
-- Show that function will be propagated together with the view
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%func_1_for_view%';
obj_identifier
---------------------------------------------------------------------
(0 rows)
CREATE VIEW prop_view_7 AS SELECT func_1_for_view(id) FROM view_table_1;
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%func_1_for_view%';
obj_identifier
---------------------------------------------------------------------
(function,"{view_prop_schema,func_1_for_view}",{integer})
(1 row)
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%prop_view_7%';
obj_identifier
---------------------------------------------------------------------
(view,"{view_prop_schema,prop_view_7}",{})
(1 row)
-- Depending on type
SET citus.enable_ddl_propagation TO OFF;
CREATE TYPE type_for_view_prop AS ENUM ('a','b','c');
RESET citus.enable_ddl_propagation;
-- Show that type will be propagated together with the view
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%type_for_view_prop%';
obj_identifier
---------------------------------------------------------------------
(0 rows)
CREATE VIEW prop_view_8 AS SELECT val_1::type_for_view_prop FROM view_table_1;
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%type_for_view_prop%';
obj_identifier
---------------------------------------------------------------------
(type,{view_prop_schema.type_for_view_prop},{})
(1 row)
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%prop_view_8%';
obj_identifier
---------------------------------------------------------------------
(view,"{view_prop_schema,prop_view_8}",{})
(1 row)
-- Depending on another view
CREATE TABLE view_table_5(id int);
CREATE VIEW prop_view_9 AS SELECT * FROM view_table_5;
WARNING: "view prop_view_9" has dependency to "table view_table_5" that is not in Citus' metadata
DETAIL: "view prop_view_9" will be created only locally
HINT: Distribute "table view_table_5" first to distribute "view prop_view_9"
CREATE VIEW prop_view_10 AS SELECT * FROM prop_view_9;
WARNING: "view prop_view_10" has dependency to "table view_table_5" that is not in Citus' metadata
DETAIL: "view prop_view_10" will be created only locally
HINT: Distribute "table view_table_5" first to distribute "view prop_view_10"
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%prop_view_9%';
obj_identifier
---------------------------------------------------------------------
(0 rows)
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%prop_view_10%';
obj_identifier
---------------------------------------------------------------------
(0 rows)
SELECT create_distributed_table('view_table_5', 'id');
create_distributed_table
---------------------------------------------------------------------
(1 row)
CREATE OR REPLACE VIEW prop_view_10 AS SELECT * FROM prop_view_9;
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%prop_view_9%';
obj_identifier
---------------------------------------------------------------------
(view,"{view_prop_schema,prop_view_9}",{})
(1 row)
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%prop_view_10%';
obj_identifier
---------------------------------------------------------------------
(view,"{view_prop_schema,prop_view_10}",{})
(1 row)
-- Check views owned by non-superuser
SET client_min_messages TO ERROR;
CREATE USER view_creation_user;
SELECT 1 FROM run_command_on_workers($$CREATE USER view_creation_user;$$);
?column?
---------------------------------------------------------------------
1
1
(2 rows)
GRANT ALL PRIVILEGES ON SCHEMA view_prop_schema to view_creation_user;
SET ROLE view_creation_user;
CREATE TABLE user_owned_table_for_view(id int);
SELECT create_distributed_table('user_owned_table_for_view','id');
create_distributed_table
---------------------------------------------------------------------
(1 row)
CREATE VIEW view_owned_by_user AS SELECT * FROM user_owned_table_for_view;
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%view_owned_by_user%';
obj_identifier
---------------------------------------------------------------------
(view,"{view_prop_schema,view_owned_by_user}",{})
(1 row)
DROP VIEW view_owned_by_user;
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%view_owned_by_user%';
obj_identifier
---------------------------------------------------------------------
(0 rows)
DROP TABLE user_owned_table_for_view;
RESET ROLE;
RESET client_min_messages;
-- Create view with different options
CREATE TABLE view_table_6(id int, val_1 text);
SELECT create_distributed_table('view_table_6','id');
create_distributed_table
---------------------------------------------------------------------
(1 row)
-- TEMP VIEW is not supported. View will be created locally.
CREATE TEMP VIEW temp_prop_view AS SELECT * FROM view_table_6;
WARNING: "view temp_prop_view" has dependency on unsupported object "schema pg_temp_xxx"
DETAIL: "view temp_prop_view" will be created only locally
-- Recursive views are supported
CREATE RECURSIVE VIEW nums_1_100_prop_view (n) AS
VALUES (1)
UNION ALL
SELECT n+1 FROM nums_1_100_prop_view WHERE n < 100;
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%nums_1_100_prop_view%';
obj_identifier
---------------------------------------------------------------------
(view,"{view_prop_schema,nums_1_100_prop_view}",{})
(1 row)
-- Sequences are supported as dependency
CREATE SEQUENCE sequence_to_prop;
CREATE VIEW seq_view_prop AS SELECT sequence_to_prop.is_called FROM sequence_to_prop;
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%sequence_to_prop%';
obj_identifier
---------------------------------------------------------------------
(sequence,"{view_prop_schema,sequence_to_prop}",{})
(1 row)
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%seq_view_prop%';
obj_identifier
---------------------------------------------------------------------
(view,"{view_prop_schema,seq_view_prop}",{})
(1 row)
-- Views depend on temp sequences will be created locally
CREATE TEMPORARY SEQUENCE temp_sequence_to_drop;
CREATE VIEW temp_seq_view_prop AS SELECT temp_sequence_to_drop.is_called FROM temp_sequence_to_drop;
NOTICE: view "temp_seq_view_prop" will be a temporary view
WARNING: "view temp_seq_view_prop" has dependency on unsupported object "schema pg_temp_xxx"
DETAIL: "view temp_seq_view_prop" will be created only locally
-- Check circular dependencies are detected
CREATE VIEW circular_view_1 AS SELECT * FROM view_table_6;
CREATE VIEW circular_view_2 AS SELECT * FROM view_table_6;
CREATE OR REPLACE VIEW circular_view_1 AS SELECT view_table_6.* FROM view_table_6 JOIN circular_view_2 USING (id);
CREATE OR REPLACE VIEW circular_view_2 AS SELECT view_table_6.* FROM view_table_6 JOIN circular_view_1 USING (id);
ERROR: Citus can not handle circular dependencies between distributed objects
DETAIL: "view circular_view_2" circularly depends itself, resolve circular dependency first
-- Recursive views with distributed tables included
CREATE TABLE employees (employee_id int, manager_id int, full_name text);
SELECT create_distributed_table('employees', 'employee_id');
create_distributed_table
---------------------------------------------------------------------
(1 row)
CREATE OR REPLACE RECURSIVE VIEW reporting_line (employee_id, subordinates) AS
SELECT
employee_id,
full_name AS subordinates
FROM
employees
WHERE
manager_id IS NULL
UNION ALL
SELECT
e.employee_id,
(
rl.subordinates || ' > ' || e.full_name
) AS subordinates
FROM
employees e
INNER JOIN reporting_line rl ON e.manager_id = rl.employee_id;
-- Aliases are supported
CREATE VIEW aliased_opt_prop_view(alias_1, alias_2) AS SELECT * FROM view_table_6;
-- View options are supported
CREATE VIEW opt_prop_view
WITH(check_option=CASCADED, security_barrier=true)
AS SELECT * FROM view_table_6;
CREATE VIEW sep_opt_prop_view
AS SELECT * FROM view_table_6
WITH LOCAL CHECK OPTION;
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%opt_prop_view%' ORDER BY 1;
obj_identifier
---------------------------------------------------------------------
(view,"{view_prop_schema,aliased_opt_prop_view}",{})
(view,"{view_prop_schema,opt_prop_view}",{})
(view,"{view_prop_schema,sep_opt_prop_view}",{})
(3 rows)
-- Check definitions and reltoptions of views are correct on workers
\c - - - :worker_1_port
SELECT definition FROM pg_views WHERE viewname = 'aliased_opt_prop_view';
definition
---------------------------------------------------------------------
SELECT view_table_6.id AS alias_1, +
view_table_6.val_1 AS alias_2 +
FROM view_prop_schema.view_table_6;
(1 row)
SELECT definition FROM pg_views WHERE viewname = 'opt_prop_view';
definition
---------------------------------------------------------------------
SELECT view_table_6.id, +
view_table_6.val_1 +
FROM view_prop_schema.view_table_6;
(1 row)
SELECT definition FROM pg_views WHERE viewname = 'sep_opt_prop_view';
definition
---------------------------------------------------------------------
SELECT view_table_6.id, +
view_table_6.val_1 +
FROM view_prop_schema.view_table_6;
(1 row)
SELECT relname, reloptions
FROM pg_class
WHERE
oid = 'view_prop_schema.aliased_opt_prop_view'::regclass::oid OR
oid = 'view_prop_schema.opt_prop_view'::regclass::oid OR
oid = 'view_prop_schema.sep_opt_prop_view'::regclass::oid
ORDER BY 1;
relname | reloptions
---------------------------------------------------------------------
aliased_opt_prop_view |
opt_prop_view | {check_option=cascaded,security_barrier=true}
sep_opt_prop_view | {check_option=local}
(3 rows)
\c - - - :master_port
SET search_path to view_prop_schema;
-- Sync metadata to check it works properly after adding a view
SELECT start_metadata_sync_to_node('localhost', :worker_1_port);
start_metadata_sync_to_node
---------------------------------------------------------------------
(1 row)
-- Drop views and check metadata afterwards
DROP VIEW prop_view_9 CASCADE;
NOTICE: drop cascades to view prop_view_10
DROP VIEW opt_prop_view, aliased_opt_prop_view, view_prop_schema_inner.inner_view_prop, sep_opt_prop_view;
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%inner_view_prop%';
obj_identifier
---------------------------------------------------------------------
(0 rows)
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%opt_prop_view%';
obj_identifier
---------------------------------------------------------------------
(0 rows)
-- Drop a column that view depends on
ALTER TABLE view_table_1 DROP COLUMN val_1 CASCADE;
NOTICE: drop cascades to 3 other objects
DETAIL: drop cascades to view prop_view_1
drop cascades to view prop_view_3
drop cascades to view prop_view_8
-- Since prop_view_3 depends on the view_table_1's val_1 column, it should be dropped
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%prop_view_3%';
obj_identifier
---------------------------------------------------------------------
(0 rows)
-- Drop a table that view depends on
DROP TABLE view_table_2 CASCADE;
NOTICE: drop cascades to 2 other objects
DETAIL: drop cascades to view prop_view_2
drop cascades to constraint f_key_for_local_table on table view_table_3
NOTICE: drop cascades to constraint f_key_for_local_table_1410200 on table view_prop_schema.view_table_3_1410200
CONTEXT: SQL statement "SELECT citus_drop_all_shards(v_obj.objid, v_obj.schema_name, v_obj.object_name, drop_shards_metadata_only := false)"
PL/pgSQL function citus_drop_trigger() line XX at PERFORM
NOTICE: removing table view_prop_schema.view_table_3 from metadata as it is not connected to any reference tables via foreign keys
-- Since prop_view_2 depends on the view_table_2, it should be dropped
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%prop_view_2%';
obj_identifier
---------------------------------------------------------------------
(0 rows)
-- Show that unsupported CREATE OR REPLACE VIEW commands are catched by PG on the coordinator
CREATE TABLE table_to_test_unsup_view(id int, val1 text);
SELECT create_distributed_table('table_to_test_unsup_view', 'id');
create_distributed_table
---------------------------------------------------------------------
(1 row)
CREATE VIEW view_for_unsup_commands AS SELECT * FROM table_to_test_unsup_view;
CREATE OR REPLACE VIEW view_for_unsup_commands(a,b) AS SELECT * FROM table_to_test_unsup_view;
ERROR: cannot change name of view column "id" to "a"
HINT: Use ALTER VIEW ... RENAME COLUMN ... to change name of view column instead.
CREATE OR REPLACE VIEW view_for_unsup_commands AS SELECT id FROM table_to_test_unsup_view;
ERROR: cannot drop columns from view
SET client_min_messages TO ERROR;
DROP SCHEMA view_prop_schema_inner CASCADE;
DROP SCHEMA view_prop_schema CASCADE;

View File

@ -1,6 +1,15 @@
CREATE SCHEMA views_create;
SET search_path TO views_create;
CREATE TABLE view_test_table(a INT NOT NULL PRIMARY KEY, b BIGINT, c text);
SELECT create_distributed_table('view_test_table', 'a');
create_distributed_table
---------------------------------------------------------------------
(1 row)
-- Since creating view distributed or locally depends on the arbitrary config
-- set client_min_messages to ERROR to get consistent result.
SET client_min_messages TO ERROR;
CREATE OR REPLACE VIEW select_filtered_view AS
SELECT * FROM view_test_table WHERE c = 'testing'
WITH CASCADED CHECK OPTION;
@ -9,12 +18,7 @@ CREATE OR REPLACE VIEW select_all_view AS
WITH LOCAL CHECK OPTION;
CREATE OR REPLACE VIEW count_view AS
SELECT COUNT(*) FROM view_test_table;
SELECT create_distributed_table('view_test_table', 'a');
create_distributed_table
---------------------------------------------------------------------
(1 row)
RESET client_min_messages;
INSERT INTO view_test_table VALUES (1,1,'testing'), (2,1,'views');
SELECT * FROM count_view;
count
@ -42,6 +46,8 @@ SELECT * FROM select_filtered_view;
-- dummy temp recursive view
CREATE TEMP RECURSIVE VIEW recursive_defined_non_recursive_view(c) AS (SELECT 1);
WARNING: "view recursive_defined_non_recursive_view" has dependency on unsupported object "schema pg_temp_xxx"
DETAIL: "view recursive_defined_non_recursive_view" will be created only locally
CREATE MATERIALIZED VIEW select_all_matview AS
SELECT * FROM view_test_table
WITH DATA;

View File

@ -508,6 +508,7 @@ INSERT INTO pg_catalog.pg_dist_object(classid, objid, objsubid) values('pg_class
INSERT INTO pg_catalog.pg_dist_object(classid, objid, objsubid) values('pg_class'::regclass::oid, 'table_to_distribute'::regclass::oid, 0);
INSERT INTO pg_catalog.pg_dist_object(classid, objid, objsubid) values('pg_class'::regclass::oid, 'second_dustbunnies'::regclass::oid, 0);
SET client_min_messages TO ERROR;
SELECT 1 FROM master_activate_node('localhost', :worker_1_port);
RESET client_min_messages;
RESET citus.shard_replication_factor;

View File

@ -324,6 +324,7 @@ test: distributed_collations
test: distributed_procedure
test: distributed_collations_conflict
test: function_propagation
test: view_propagation
test: check_mx
# ---------

View File

@ -634,9 +634,8 @@ INSERT INTO pg_catalog.pg_dist_object(classid, objid, objsubid) values('pg_class
INSERT INTO pg_catalog.pg_dist_object(classid, objid, objsubid) values('pg_class'::regclass::oid, 'super_packed_numbers_hash'::regclass::oid, 0);
INSERT INTO pg_catalog.pg_dist_object(classid, objid, objsubid) values('pg_class'::regclass::oid, 'table_to_distribute'::regclass::oid, 0);
INSERT INTO pg_catalog.pg_dist_object(classid, objid, objsubid) values('pg_class'::regclass::oid, 'second_dustbunnies'::regclass::oid, 0);
SET client_min_messages TO ERROR;
SELECT 1 FROM master_activate_node('localhost', :worker_1_port);
NOTICE: Replicating postgres objects to node localhost:57637
DETAIL: There are 115 objects to replicate, depending on your environment this might take a while
?column?
---------------------------------------------------------------------
1

View File

@ -10,7 +10,9 @@ setup
SELECT create_distributed_table('test_table_1_rf1','id');
INSERT INTO test_table_1_rf1 values(1,2),(2,3),(3,4);
SET citus.enable_ddl_propagation TO OFF;
CREATE VIEW test_1 AS SELECT * FROM test_table_1_rf1 WHERE val_1 = 2;
RESET citus.enable_ddl_propagation;
CREATE TABLE test_table_2_rf1(id int, val_1 int);
SELECT create_distributed_table('test_table_2_rf1','id');

View File

@ -224,7 +224,6 @@ select alter_table_set_access_method('local','columnar');
select alter_table_set_access_method('ref','columnar');
select alter_table_set_access_method('dist','columnar');
SELECT alter_distributed_table('dist', shard_count:=1, cascade_to_colocated:=false);
select alter_table_set_access_method('local','heap');

View File

@ -445,20 +445,24 @@ SELECT count(*) FROM mat_view_4;
SELECT count(*) FROM distributed_table WHERE b in
(SELECT count FROM mat_view_4);
SET citus.enable_ddl_propagation TO OFF;
CREATE VIEW view_2 AS
SELECT count(*)
FROM citus_local_table
JOIN citus_local_table_2 USING (a)
JOIN distributed_table USING (a);
RESET citus.enable_ddl_propagation;
-- should fail as view contains direct local dist join
SELECT count(*) FROM view_2;
SET citus.enable_ddl_propagation TO OFF;
CREATE VIEW view_3
AS SELECT count(*)
FROM citus_local_table_2
JOIN reference_table
USING (a);
RESET citus.enable_ddl_propagation;
-- ok
SELECT count(*) FROM view_3;

View File

@ -73,8 +73,10 @@ COMMIT;
-- basic view queries
SET citus.enable_ddl_propagation TO OFF;
CREATE VIEW simple_view AS
SELECT count(*) as cnt FROM test t1 JOIN test t2 USING (x);
RESET citus.enable_ddl_propagation;
SELECT * FROM simple_view;
SELECT * FROM simple_view, test WHERE test.x = simple_view.cnt;

View File

@ -52,22 +52,6 @@ WHERE n.nspname IN ('drop_partitioned_table', 'schema1')
AND c.relkind IN ('r','p')
ORDER BY 1, 2;
\c - - - :worker_1_port
SET search_path = drop_partitioned_table;
CREATE VIEW tables_info AS
SELECT n.nspname as "Schema",
c.relname as "Name",
CASE c.relkind WHEN 'r' THEN 'table' WHEN 'p' THEN 'partitioned table' END as "Type",
pg_catalog.pg_get_userbyid(c.relowner) as "Owner"
FROM pg_catalog.pg_class c
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
LEFT JOIN pg_user u ON u.usesysid = c.relowner
WHERE n.nspname IN ('drop_partitioned_table', 'schema1')
AND c.relkind IN ('r','p')
ORDER BY 1, 2;
\c - - - :master_port
SET search_path = drop_partitioned_table;
SET citus.next_shard_id TO 721000;
-- CASE 1

View File

@ -101,7 +101,7 @@ $$;
SELECT pg_identify_object_as_address(classid, objid, objsubid) from pg_catalog.pg_dist_object where objid = 'function_propagation_schema.func_6'::regproc::oid;
SELECT * FROM run_command_on_workers($$SELECT pg_identify_object_as_address(classid, objid, objsubid) from pg_catalog.pg_dist_object where objid = 'function_propagation_schema.func_6'::regproc::oid;$$) ORDER BY 1,2;
-- Views are not supported
-- Views are supported
CREATE VIEW function_prop_view AS SELECT * FROM function_prop_table;
CREATE OR REPLACE FUNCTION func_7(param_1 function_prop_view)
RETURNS int

View File

@ -408,4 +408,5 @@ JOIN
USING
(id);
SET client_min_messages TO ERROR;
DROP SCHEMA local_dist_join_mixed CASCADE;

View File

@ -230,7 +230,9 @@ SELECT * FROM second_distributed_table WHERE key = 1 ORDER BY 1,2;
INSERT INTO distributed_table VALUES (1, '22', 20);
INSERT INTO second_distributed_table VALUES (1, '1');
SET citus.enable_ddl_propagation TO OFF;
CREATE VIEW abcd_view AS SELECT * FROM abcd;
RESET citus.enable_ddl_propagation;
SELECT * FROM abcd first join abcd second on first.b = second.b ORDER BY 1,2,3,4;
@ -765,15 +767,19 @@ ROLLBACK;
-- probably not a realistic case since views are not very
-- well supported with MX
SET citus.enable_ddl_propagation TO OFF;
CREATE VIEW v_local_query_execution AS
SELECT * FROM distributed_table WHERE key = 500;
RESET citus.enable_ddl_propagation;
SELECT * FROM v_local_query_execution;
-- similar test, but this time the view itself is a non-local
-- query, but the query on the view is local
SET citus.enable_ddl_propagation TO OFF;
CREATE VIEW v_local_query_execution_2 AS
SELECT * FROM distributed_table;
RESET citus.enable_ddl_propagation;
SELECT * FROM v_local_query_execution_2 WHERE key = 500;

View File

@ -194,7 +194,9 @@ SELECT * FROM second_distributed_table WHERE key = 1 ORDER BY 1,2;
-- Put row back for other tests
INSERT INTO distributed_table VALUES (1, '22', 20);
SET citus.enable_ddl_propagation TO OFF;
CREATE VIEW abcd_view AS SELECT * FROM abcd;
RESET citus.enable_ddl_propagation;
SELECT * FROM abcd first join abcd second on first.b = second.b ORDER BY 1,2,3,4;
@ -729,15 +731,19 @@ ROLLBACK;
-- probably not a realistic case since views are not very
-- well supported with MX
SET citus.enable_ddl_propagation TO OFF;
CREATE VIEW v_local_query_execution AS
SELECT * FROM distributed_table WHERE key = 500;
RESET citus.enable_ddl_propagation;
SELECT * FROM v_local_query_execution;
-- similar test, but this time the view itself is a non-local
-- query, but the query on the view is local
SET citus.enable_ddl_propagation TO OFF;
CREATE VIEW v_local_query_execution_2 AS
SELECT * FROM distributed_table;
RESET citus.enable_ddl_propagation;
SELECT * FROM v_local_query_execution_2 WHERE key = 500;

View File

@ -116,7 +116,7 @@ ALTER TABLE fiddly_table
SELECT master_get_table_ddl_events('fiddly_table');
-- propagating views is not supported
-- propagating views is not supported if local table dependency exists
CREATE VIEW local_view AS SELECT * FROM simple_table;
SELECT master_get_table_ddl_events('local_view');

View File

@ -1992,7 +1992,9 @@ SELECT tablename, indexname FROM pg_indexes
WHERE schemaname = 'partitioning_schema' AND tablename ilike '%part_table_with_%' ORDER BY 1, 2;
-- should work properly - no names clashes
SET client_min_messages TO WARNING;
SELECT 1 FROM citus_activate_node('localhost', :worker_1_port);
RESET client_min_messages;
\c - - - :worker_1_port
-- check that indexes are named properly

View File

@ -785,7 +785,6 @@ SELECT count(*) FROM events_table WHERE user_id NOT IN
-- make sure that non-colocated subquery joins work fine in
-- modifications
CREATE TABLE table1 (id int, tenant_id int);
CREATE VIEW table1_view AS SELECT * from table1 where id < 100;
CREATE TABLE table2 (id int, tenant_id int) partition by range(tenant_id);
CREATE TABLE table2_p1 PARTITION OF table2 FOR VALUES FROM (1) TO (10);
@ -795,6 +794,8 @@ SET citus.shard_replication_factor TO 1;
SELECT create_distributed_table('table2','tenant_id');
SELECT create_distributed_table('table1','tenant_id');
CREATE VIEW table1_view AS SELECT * from table1 where id < 100;
-- all of the above queries are non-colocated subquery joins
-- because the views are replaced with subqueries
UPDATE table2 SET id=20 FROM table1_view WHERE table1_view.id=table2.id;

View File

@ -104,6 +104,7 @@ SET search_path TO recursive_dml_queries_mx, public;
CREATE TABLE recursive_dml_queries_mx.local_table (id text, name text);
INSERT INTO local_table SELECT i::text, 'user_' || i FROM generate_series (0, 100) i;
SET citus.enable_ddl_propagation TO OFF;
CREATE VIEW tenant_ids AS
SELECT
tenant_id, name
@ -112,6 +113,7 @@ CREATE VIEW tenant_ids AS
WHERE
distributed_table.dept::text = reference_table.id
ORDER BY 2 DESC, 1 DESC;
RESET citus.enable_ddl_propagation;
-- we currently do not allow local tables in modification queries
UPDATE

View File

@ -55,6 +55,7 @@ INSERT INTO sensors VALUES (DEFAULT, DEFAULT, '2010-01-01') RETURNING *;
INSERT INTO sensors_news VALUES (DEFAULT, DEFAULT, '2021-01-01') RETURNING *;
\c - - - :master_port
SET client_min_messages TO ERROR;
SELECT 1 FROM citus_activate_node('localhost', :worker_1_port);

View File

@ -103,7 +103,6 @@ SELECT start_metadata_sync_to_node('localhost', :worker_1_port);
\c - - - :worker_1_port
SET search_path TO "start_stop_metadata_sync";
SELECT * FROM distributed_table_1;
CREATE VIEW test_view AS SELECT COUNT(*) FROM distributed_table_3;
CREATE MATERIALIZED VIEW test_matview AS SELECT COUNT(*) FROM distributed_table_3;
SELECT * FROM test_view;
SELECT * FROM test_matview;

View File

@ -0,0 +1,273 @@
-- Tests to check propagation of all view commands
CREATE SCHEMA view_prop_schema;
SET search_path to view_prop_schema;
-- Check creating views depending on different types of tables
-- and from multiple schemas
-- Check the most basic one
CREATE VIEW prop_view_basic AS SELECT 1;
-- Try to create view depending local table, then try to recreate it after distributing the table
CREATE TABLE view_table_1(id int, val_1 text);
CREATE VIEW prop_view_1 AS
SELECT * FROM view_table_1;
SELECT create_distributed_table('view_table_1', 'id');
CREATE OR REPLACE VIEW prop_view_1 AS
SELECT * FROM view_table_1;
-- Try to create view depending local table, then try to recreate it after making the table reference table
CREATE TABLE view_table_2(id int PRIMARY KEY, val_1 text);
CREATE VIEW prop_view_2 AS
SELECT view_table_1.id, view_table_2.val_1 FROM view_table_1 INNER JOIN view_table_2
ON view_table_1.id = view_table_2.id;
SELECT create_reference_table('view_table_2');
CREATE OR REPLACE VIEW prop_view_2 AS
SELECT view_table_1.id, view_table_2.val_1 FROM view_table_1 INNER JOIN view_table_2
ON view_table_1.id = view_table_2.id;
-- Try to create view depending local table, then try to recreate it after making the table citus local table
CREATE TABLE view_table_3(id int, val_1 text);
CREATE VIEW prop_view_3 AS
SELECT * FROM view_table_1 WHERE id IN
(SELECT view_table_2.id FROM view_table_2 INNER JOIN view_table_3 ON view_table_2.id = view_table_3.id);
SET client_min_messages TO WARNING;
SELECT 1 FROM citus_add_node('localhost', :master_port, groupid=>0);
RESET client_min_messages;
ALTER TABLE view_table_3
ADD CONSTRAINT f_key_for_local_table
FOREIGN KEY(id)
REFERENCES view_table_2(id);
CREATE OR REPLACE VIEW prop_view_3 AS
SELECT * FROM view_table_1 WHERE id IN
(SELECT view_table_2.id FROM view_table_2 INNER JOIN view_table_3 ON view_table_2.id = view_table_3.id);
-- Try to create view depending on PG metadata table
CREATE VIEW prop_view_4 AS
SELECT * FROM pg_stat_activity;
-- Try to create view depending on Citus metadata table
CREATE VIEW prop_view_5 AS
SELECT * FROM citus_dist_stat_activity;
-- Try to create table depending on a local table from another schema, then try to create it again after distributing the table
CREATE SCHEMA view_prop_schema_inner;
SET search_path TO view_prop_schema_inner;
-- Create local table for tests below
CREATE TABLE view_table_4(id int, val_1 text);
-- Create a distributed table and view to test drop view below
CREATE TABLE inner_view_table(id int);
SELECT create_distributed_table('inner_view_table','id');
CREATE VIEW inner_view_prop AS SELECT * FROM inner_view_table;
SET search_path to view_prop_schema;
CREATE VIEW prop_view_6 AS
SELECT vt1.id, vt4.val_1 FROM view_table_1 AS vt1
INNER JOIN view_prop_schema_inner.view_table_4 AS vt4 ON vt1.id = vt4.id;
SELECT create_distributed_table('view_prop_schema_inner.view_table_4','id');
CREATE OR REPLACE VIEW prop_view_6 AS
SELECT vt1.id, vt4.val_1 FROM view_table_1 AS vt1
INNER JOIN view_prop_schema_inner.view_table_4 AS vt4 ON vt1.id = vt4.id;
-- Show that all views are propagated as distributed object
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%prop_view_%' ORDER BY 1;
-- Check creating views depending various kind of objects
-- Tests will also check propagating dependent objects
-- Depending on function
SET citus.enable_ddl_propagation TO OFF;
CREATE OR REPLACE FUNCTION func_1_for_view(param_1 int)
RETURNS int
LANGUAGE plpgsql AS
$$
BEGIN
return param_1;
END;
$$;
RESET citus.enable_ddl_propagation;
-- Show that function will be propagated together with the view
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%func_1_for_view%';
CREATE VIEW prop_view_7 AS SELECT func_1_for_view(id) FROM view_table_1;
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%func_1_for_view%';
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%prop_view_7%';
-- Depending on type
SET citus.enable_ddl_propagation TO OFF;
CREATE TYPE type_for_view_prop AS ENUM ('a','b','c');
RESET citus.enable_ddl_propagation;
-- Show that type will be propagated together with the view
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%type_for_view_prop%';
CREATE VIEW prop_view_8 AS SELECT val_1::type_for_view_prop FROM view_table_1;
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%type_for_view_prop%';
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%prop_view_8%';
-- Depending on another view
CREATE TABLE view_table_5(id int);
CREATE VIEW prop_view_9 AS SELECT * FROM view_table_5;
CREATE VIEW prop_view_10 AS SELECT * FROM prop_view_9;
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%prop_view_9%';
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%prop_view_10%';
SELECT create_distributed_table('view_table_5', 'id');
CREATE OR REPLACE VIEW prop_view_10 AS SELECT * FROM prop_view_9;
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%prop_view_9%';
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%prop_view_10%';
-- Check views owned by non-superuser
SET client_min_messages TO ERROR;
CREATE USER view_creation_user;
SELECT 1 FROM run_command_on_workers($$CREATE USER view_creation_user;$$);
GRANT ALL PRIVILEGES ON SCHEMA view_prop_schema to view_creation_user;
SET ROLE view_creation_user;
CREATE TABLE user_owned_table_for_view(id int);
SELECT create_distributed_table('user_owned_table_for_view','id');
CREATE VIEW view_owned_by_user AS SELECT * FROM user_owned_table_for_view;
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%view_owned_by_user%';
DROP VIEW view_owned_by_user;
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%view_owned_by_user%';
DROP TABLE user_owned_table_for_view;
RESET ROLE;
RESET client_min_messages;
-- Create view with different options
CREATE TABLE view_table_6(id int, val_1 text);
SELECT create_distributed_table('view_table_6','id');
-- TEMP VIEW is not supported. View will be created locally.
CREATE TEMP VIEW temp_prop_view AS SELECT * FROM view_table_6;
-- Recursive views are supported
CREATE RECURSIVE VIEW nums_1_100_prop_view (n) AS
VALUES (1)
UNION ALL
SELECT n+1 FROM nums_1_100_prop_view WHERE n < 100;
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%nums_1_100_prop_view%';
-- Sequences are supported as dependency
CREATE SEQUENCE sequence_to_prop;
CREATE VIEW seq_view_prop AS SELECT sequence_to_prop.is_called FROM sequence_to_prop;
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%sequence_to_prop%';
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%seq_view_prop%';
-- Views depend on temp sequences will be created locally
CREATE TEMPORARY SEQUENCE temp_sequence_to_drop;
CREATE VIEW temp_seq_view_prop AS SELECT temp_sequence_to_drop.is_called FROM temp_sequence_to_drop;
-- Check circular dependencies are detected
CREATE VIEW circular_view_1 AS SELECT * FROM view_table_6;
CREATE VIEW circular_view_2 AS SELECT * FROM view_table_6;
CREATE OR REPLACE VIEW circular_view_1 AS SELECT view_table_6.* FROM view_table_6 JOIN circular_view_2 USING (id);
CREATE OR REPLACE VIEW circular_view_2 AS SELECT view_table_6.* FROM view_table_6 JOIN circular_view_1 USING (id);
-- Recursive views with distributed tables included
CREATE TABLE employees (employee_id int, manager_id int, full_name text);
SELECT create_distributed_table('employees', 'employee_id');
CREATE OR REPLACE RECURSIVE VIEW reporting_line (employee_id, subordinates) AS
SELECT
employee_id,
full_name AS subordinates
FROM
employees
WHERE
manager_id IS NULL
UNION ALL
SELECT
e.employee_id,
(
rl.subordinates || ' > ' || e.full_name
) AS subordinates
FROM
employees e
INNER JOIN reporting_line rl ON e.manager_id = rl.employee_id;
-- Aliases are supported
CREATE VIEW aliased_opt_prop_view(alias_1, alias_2) AS SELECT * FROM view_table_6;
-- View options are supported
CREATE VIEW opt_prop_view
WITH(check_option=CASCADED, security_barrier=true)
AS SELECT * FROM view_table_6;
CREATE VIEW sep_opt_prop_view
AS SELECT * FROM view_table_6
WITH LOCAL CHECK OPTION;
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%opt_prop_view%' ORDER BY 1;
-- Check definitions and reltoptions of views are correct on workers
\c - - - :worker_1_port
SELECT definition FROM pg_views WHERE viewname = 'aliased_opt_prop_view';
SELECT definition FROM pg_views WHERE viewname = 'opt_prop_view';
SELECT definition FROM pg_views WHERE viewname = 'sep_opt_prop_view';
SELECT relname, reloptions
FROM pg_class
WHERE
oid = 'view_prop_schema.aliased_opt_prop_view'::regclass::oid OR
oid = 'view_prop_schema.opt_prop_view'::regclass::oid OR
oid = 'view_prop_schema.sep_opt_prop_view'::regclass::oid
ORDER BY 1;
\c - - - :master_port
SET search_path to view_prop_schema;
-- Sync metadata to check it works properly after adding a view
SELECT start_metadata_sync_to_node('localhost', :worker_1_port);
-- Drop views and check metadata afterwards
DROP VIEW prop_view_9 CASCADE;
DROP VIEW opt_prop_view, aliased_opt_prop_view, view_prop_schema_inner.inner_view_prop, sep_opt_prop_view;
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%inner_view_prop%';
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%opt_prop_view%';
-- Drop a column that view depends on
ALTER TABLE view_table_1 DROP COLUMN val_1 CASCADE;
-- Since prop_view_3 depends on the view_table_1's val_1 column, it should be dropped
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%prop_view_3%';
-- Drop a table that view depends on
DROP TABLE view_table_2 CASCADE;
-- Since prop_view_2 depends on the view_table_2, it should be dropped
SELECT * FROM (SELECT pg_identify_object_as_address(classid, objid, objsubid) as obj_identifier from pg_catalog.pg_dist_object) as obj_identifiers where obj_identifier::text like '%prop_view_2%';
-- Show that unsupported CREATE OR REPLACE VIEW commands are catched by PG on the coordinator
CREATE TABLE table_to_test_unsup_view(id int, val1 text);
SELECT create_distributed_table('table_to_test_unsup_view', 'id');
CREATE VIEW view_for_unsup_commands AS SELECT * FROM table_to_test_unsup_view;
CREATE OR REPLACE VIEW view_for_unsup_commands(a,b) AS SELECT * FROM table_to_test_unsup_view;
CREATE OR REPLACE VIEW view_for_unsup_commands AS SELECT id FROM table_to_test_unsup_view;
SET client_min_messages TO ERROR;
DROP SCHEMA view_prop_schema_inner CASCADE;
DROP SCHEMA view_prop_schema CASCADE;

View File

@ -2,6 +2,10 @@ CREATE SCHEMA views_create;
SET search_path TO views_create;
CREATE TABLE view_test_table(a INT NOT NULL PRIMARY KEY, b BIGINT, c text);
SELECT create_distributed_table('view_test_table', 'a');
-- Since creating view distributed or locally depends on the arbitrary config
-- set client_min_messages to ERROR to get consistent result.
SET client_min_messages TO ERROR;
CREATE OR REPLACE VIEW select_filtered_view AS
SELECT * FROM view_test_table WHERE c = 'testing'
WITH CASCADED CHECK OPTION;
@ -10,7 +14,7 @@ CREATE OR REPLACE VIEW select_all_view AS
WITH LOCAL CHECK OPTION;
CREATE OR REPLACE VIEW count_view AS
SELECT COUNT(*) FROM view_test_table;
SELECT create_distributed_table('view_test_table', 'a');
RESET client_min_messages;
INSERT INTO view_test_table VALUES (1,1,'testing'), (2,1,'views');
SELECT * FROM count_view;