mirror of https://github.com/citusdata/citus.git
Merge pull request #5914 from citusdata/velioglu/alter_view_propagation
Introduce alter view propagationpull/5881/head
commit
544e6c7428
|
@ -828,6 +828,22 @@ static DistributeObjectOps Type_AlterObjectSchema = {
|
|||
.address = AlterTypeSchemaStmtObjectAddress,
|
||||
.markDistributed = false,
|
||||
};
|
||||
|
||||
/*
|
||||
* PreprocessAlterViewSchemaStmt and PostprocessAlterViewSchemaStmt functions can be called
|
||||
* internally by ALTER TABLE view_name SET SCHEMA ... if the ALTER TABLE command targets a
|
||||
* view. In other words ALTER VIEW view_name SET SCHEMA will use the View_AlterObjectSchema
|
||||
* but ALTER TABLE view_name SET SCHEMA will use Table_AlterObjectSchema but call process
|
||||
* functions of View_AlterObjectSchema internally.
|
||||
*/
|
||||
static DistributeObjectOps View_AlterObjectSchema = {
|
||||
.deparse = DeparseAlterViewSchemaStmt,
|
||||
.qualify = QualifyAlterViewSchemaStmt,
|
||||
.preprocess = PreprocessAlterViewSchemaStmt,
|
||||
.postprocess = PostprocessAlterViewSchemaStmt,
|
||||
.address = AlterViewSchemaStmtObjectAddress,
|
||||
.markDistributed = false,
|
||||
};
|
||||
static DistributeObjectOps Type_AlterOwner = {
|
||||
.deparse = DeparseAlterTypeOwnerStmt,
|
||||
.qualify = QualifyAlterTypeOwnerStmt,
|
||||
|
@ -844,6 +860,22 @@ static DistributeObjectOps Type_AlterTable = {
|
|||
.address = AlterTypeStmtObjectAddress,
|
||||
.markDistributed = false,
|
||||
};
|
||||
|
||||
/*
|
||||
* PreprocessAlterViewStmt and PostprocessAlterViewStmt functions can be called internally
|
||||
* by ALTER TABLE view_name SET/RESET ... if the ALTER TABLE command targets a view. In
|
||||
* other words ALTER VIEW view_name SET/RESET will use the View_AlterView
|
||||
* but ALTER TABLE view_name SET/RESET will use Table_AlterTable but call process
|
||||
* functions of View_AlterView internally.
|
||||
*/
|
||||
static DistributeObjectOps View_AlterView = {
|
||||
.deparse = DeparseAlterViewStmt,
|
||||
.qualify = QualifyAlterViewStmt,
|
||||
.preprocess = PreprocessAlterViewStmt,
|
||||
.postprocess = PostprocessAlterViewStmt,
|
||||
.address = AlterViewStmtObjectAddress,
|
||||
.markDistributed = false,
|
||||
};
|
||||
static DistributeObjectOps Type_Drop = {
|
||||
.deparse = DeparseDropTypeStmt,
|
||||
.qualify = NULL,
|
||||
|
@ -868,6 +900,21 @@ static DistributeObjectOps Type_Rename = {
|
|||
.address = RenameTypeStmtObjectAddress,
|
||||
.markDistributed = false,
|
||||
};
|
||||
|
||||
/*
|
||||
* PreprocessRenameViewStmt function can be called internally by ALTER TABLE view_name
|
||||
* RENAME ... if the ALTER TABLE command targets a view or a view's column. In other words
|
||||
* ALTER VIEW view_name RENAME will use the View_Rename but ALTER TABLE view_name RENAME
|
||||
* will use Any_Rename but call process functions of View_Rename internally.
|
||||
*/
|
||||
static DistributeObjectOps View_Rename = {
|
||||
.deparse = DeparseRenameViewStmt,
|
||||
.qualify = QualifyRenameViewStmt,
|
||||
.preprocess = PreprocessRenameViewStmt,
|
||||
.postprocess = NULL,
|
||||
.address = RenameViewStmtObjectAddress,
|
||||
.markDistributed = false,
|
||||
};
|
||||
static DistributeObjectOps Trigger_Rename = {
|
||||
.deparse = NULL,
|
||||
.qualify = NULL,
|
||||
|
@ -1021,6 +1068,11 @@ GetDistributeObjectOps(Node *node)
|
|||
return &Type_AlterObjectSchema;
|
||||
}
|
||||
|
||||
case OBJECT_VIEW:
|
||||
{
|
||||
return &View_AlterObjectSchema;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
return &NoDistributeOps;
|
||||
|
@ -1157,6 +1209,11 @@ GetDistributeObjectOps(Node *node)
|
|||
return &Sequence_AlterOwner;
|
||||
}
|
||||
|
||||
case OBJECT_VIEW:
|
||||
{
|
||||
return &View_AlterView;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
return &NoDistributeOps;
|
||||
|
@ -1512,6 +1569,27 @@ GetDistributeObjectOps(Node *node)
|
|||
return &Trigger_Rename;
|
||||
}
|
||||
|
||||
case OBJECT_VIEW:
|
||||
{
|
||||
return &View_Rename;
|
||||
}
|
||||
|
||||
case OBJECT_COLUMN:
|
||||
{
|
||||
switch (stmt->relationType)
|
||||
{
|
||||
case OBJECT_VIEW:
|
||||
{
|
||||
return &View_Rename;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
return &Any_Rename;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
return &Any_Rename;
|
||||
|
|
|
@ -36,11 +36,12 @@ PreprocessRenameStmt(Node *node, const char *renameCommand,
|
|||
|
||||
/*
|
||||
* We only support some of the PostgreSQL supported RENAME statements, and
|
||||
* our list include only renaming table and index (related) objects.
|
||||
* our list include only renaming table, index, policy and view (related) objects.
|
||||
*/
|
||||
if (!IsAlterTableRenameStmt(renameStmt) &&
|
||||
!IsIndexRenameStmt(renameStmt) &&
|
||||
!IsPolicyRenameStmt(renameStmt))
|
||||
!IsPolicyRenameStmt(renameStmt) &&
|
||||
!IsViewRenameStmt(renameStmt))
|
||||
{
|
||||
return NIL;
|
||||
}
|
||||
|
@ -48,7 +49,7 @@ PreprocessRenameStmt(Node *node, const char *renameCommand,
|
|||
/*
|
||||
* The lock levels here should be same as the ones taken in
|
||||
* RenameRelation(), renameatt() and RenameConstraint(). However, since all
|
||||
* three statements have identical lock levels, we just use a single statement.
|
||||
* four statements have identical lock levels, we just use a single statement.
|
||||
*/
|
||||
objectRelationId = RangeVarGetRelid(renameStmt->relation,
|
||||
AccessExclusiveLock,
|
||||
|
@ -63,14 +64,31 @@ PreprocessRenameStmt(Node *node, const char *renameCommand,
|
|||
return NIL;
|
||||
}
|
||||
|
||||
/* check whether we are dealing with a sequence here */
|
||||
if (get_rel_relkind(objectRelationId) == RELKIND_SEQUENCE)
|
||||
/*
|
||||
* Check whether we are dealing with a sequence or view here and route queries
|
||||
* accordingly to the right processor function. We need to check both objects here
|
||||
* since PG supports targeting sequences and views with ALTER TABLE commands.
|
||||
*/
|
||||
char relKind = get_rel_relkind(objectRelationId);
|
||||
if (relKind == RELKIND_SEQUENCE)
|
||||
{
|
||||
RenameStmt *stmtCopy = copyObject(renameStmt);
|
||||
stmtCopy->renameType = OBJECT_SEQUENCE;
|
||||
return PreprocessRenameSequenceStmt((Node *) stmtCopy, renameCommand,
|
||||
processUtilityContext);
|
||||
}
|
||||
else if (relKind == RELKIND_VIEW)
|
||||
{
|
||||
RenameStmt *stmtCopy = copyObject(renameStmt);
|
||||
stmtCopy->relationType = OBJECT_VIEW;
|
||||
if (stmtCopy->renameType == OBJECT_TABLE)
|
||||
{
|
||||
stmtCopy->renameType = OBJECT_VIEW;
|
||||
}
|
||||
|
||||
return PreprocessRenameViewStmt((Node *) stmtCopy, renameCommand,
|
||||
processUtilityContext);
|
||||
}
|
||||
|
||||
/* we have no planning to do unless the table is distributed */
|
||||
switch (renameStmt->renameType)
|
||||
|
|
|
@ -651,12 +651,21 @@ PostprocessAlterTableSchemaStmt(Node *node, const char *queryString)
|
|||
*/
|
||||
ObjectAddress tableAddress = GetObjectAddressFromParseTree((Node *) stmt, true);
|
||||
|
||||
/* check whether we are dealing with a sequence here */
|
||||
if (get_rel_relkind(tableAddress.objectId) == RELKIND_SEQUENCE)
|
||||
/*
|
||||
* Check whether we are dealing with a sequence or view here and route queries
|
||||
* accordingly to the right processor function.
|
||||
*/
|
||||
char relKind = get_rel_relkind(tableAddress.objectId);
|
||||
if (relKind == RELKIND_SEQUENCE)
|
||||
{
|
||||
stmt->objectType = OBJECT_SEQUENCE;
|
||||
return PostprocessAlterSequenceSchemaStmt((Node *) stmt, queryString);
|
||||
}
|
||||
else if (relKind == RELKIND_VIEW)
|
||||
{
|
||||
stmt->objectType = OBJECT_VIEW;
|
||||
return PostprocessAlterViewSchemaStmt((Node *) stmt, queryString);
|
||||
}
|
||||
|
||||
if (!ShouldPropagate() || !IsCitusTable(tableAddress.objectId))
|
||||
{
|
||||
|
@ -699,18 +708,26 @@ PreprocessAlterTableStmt(Node *node, const char *alterTableCommand,
|
|||
}
|
||||
|
||||
/*
|
||||
* check whether we are dealing with a sequence here
|
||||
* check whether we are dealing with a sequence or view here
|
||||
* if yes, it must be ALTER TABLE .. OWNER TO .. command
|
||||
* since this is the only ALTER command of a sequence that
|
||||
* since this is the only ALTER command of a sequence or view that
|
||||
* passes through an AlterTableStmt
|
||||
*/
|
||||
if (get_rel_relkind(leftRelationId) == RELKIND_SEQUENCE)
|
||||
char relKind = get_rel_relkind(leftRelationId);
|
||||
if (relKind == RELKIND_SEQUENCE)
|
||||
{
|
||||
AlterTableStmt *stmtCopy = copyObject(alterTableStatement);
|
||||
AlterTableStmtObjType_compat(stmtCopy) = OBJECT_SEQUENCE;
|
||||
return PreprocessAlterSequenceOwnerStmt((Node *) stmtCopy, alterTableCommand,
|
||||
processUtilityContext);
|
||||
}
|
||||
else if (relKind == RELKIND_VIEW)
|
||||
{
|
||||
AlterTableStmt *stmtCopy = copyObject(alterTableStatement);
|
||||
AlterTableStmtObjType_compat(stmtCopy) = OBJECT_VIEW;
|
||||
return PreprocessAlterViewStmt((Node *) stmtCopy, alterTableCommand,
|
||||
processUtilityContext);
|
||||
}
|
||||
|
||||
/*
|
||||
* AlterTableStmt applies also to INDEX relations, and we have support for
|
||||
|
@ -1758,18 +1775,31 @@ PreprocessAlterTableSchemaStmt(Node *node, const char *queryString,
|
|||
{
|
||||
return NIL;
|
||||
}
|
||||
|
||||
ObjectAddress address = GetObjectAddressFromParseTree((Node *) stmt,
|
||||
stmt->missing_ok);
|
||||
Oid relationId = address.objectId;
|
||||
|
||||
/* check whether we are dealing with a sequence here */
|
||||
if (get_rel_relkind(relationId) == RELKIND_SEQUENCE)
|
||||
/*
|
||||
* Check whether we are dealing with a sequence or view here and route queries
|
||||
* accordingly to the right processor function. We need to check both objects here
|
||||
* since PG supports targeting sequences and views with ALTER TABLE commands.
|
||||
*/
|
||||
char relKind = get_rel_relkind(relationId);
|
||||
if (relKind == RELKIND_SEQUENCE)
|
||||
{
|
||||
AlterObjectSchemaStmt *stmtCopy = copyObject(stmt);
|
||||
stmtCopy->objectType = OBJECT_SEQUENCE;
|
||||
return PreprocessAlterSequenceSchemaStmt((Node *) stmtCopy, queryString,
|
||||
processUtilityContext);
|
||||
}
|
||||
else if (relKind == RELKIND_VIEW)
|
||||
{
|
||||
AlterObjectSchemaStmt *stmtCopy = copyObject(stmt);
|
||||
stmtCopy->objectType = OBJECT_VIEW;
|
||||
return PreprocessAlterViewSchemaStmt((Node *) stmtCopy, queryString,
|
||||
processUtilityContext);
|
||||
}
|
||||
|
||||
/* first check whether a distributed relation is affected */
|
||||
if (!OidIsValid(relationId) || !IsCitusTable(relationId))
|
||||
|
@ -1939,12 +1969,19 @@ PostprocessAlterTableStmt(AlterTableStmt *alterTableStatement)
|
|||
* since this is the only ALTER command of a sequence that
|
||||
* passes through an AlterTableStmt
|
||||
*/
|
||||
if (get_rel_relkind(relationId) == RELKIND_SEQUENCE)
|
||||
char relKind = get_rel_relkind(relationId);
|
||||
if (relKind == RELKIND_SEQUENCE)
|
||||
{
|
||||
AlterTableStmtObjType_compat(alterTableStatement) = OBJECT_SEQUENCE;
|
||||
PostprocessAlterSequenceOwnerStmt((Node *) alterTableStatement, NULL);
|
||||
return;
|
||||
}
|
||||
else if (relKind == RELKIND_VIEW)
|
||||
{
|
||||
AlterTableStmtObjType_compat(alterTableStatement) = OBJECT_VIEW;
|
||||
PostprocessAlterViewStmt((Node *) alterTableStatement, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Before ensuring each dependency exist, update dependent sequences
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
|
||||
static List * FilterNameListForDistributedViews(List *viewNamesList, bool missing_ok);
|
||||
static void AppendQualifiedViewNameToCreateViewCommand(StringInfo buf, Oid viewOid);
|
||||
static void AppendViewDefinitionToCreateViewCommand(StringInfo buf, Oid viewOid);
|
||||
static void AppendAliasesToCreateViewCommand(StringInfo createViewCommand, Oid viewOid);
|
||||
static void AppendOptionsToCreateViewCommand(StringInfo createViewCommand, Oid viewOid);
|
||||
|
||||
|
@ -101,45 +102,9 @@ PostprocessViewStmt(Node *node, const char *queryString)
|
|||
}
|
||||
|
||||
/* If the view has any unsupported dependency, create it locally */
|
||||
DeferredErrorMessage *errMsg = DeferErrorIfHasUnsupportedDependency(&viewAddress);
|
||||
|
||||
if (errMsg != NULL)
|
||||
if (ErrorOrWarnIfObjectHasUnsupportedDependency(&viewAddress))
|
||||
{
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
return NIL;
|
||||
}
|
||||
|
||||
EnsureDependenciesExistOnAllNodes(&viewAddress);
|
||||
|
@ -409,7 +374,7 @@ AppendOptionsToCreateViewCommand(StringInfo createViewCommand, Oid viewOid)
|
|||
* AppendViewDefinitionToCreateViewCommand adds the definition of the given view to the
|
||||
* given create view command.
|
||||
*/
|
||||
void
|
||||
static void
|
||||
AppendViewDefinitionToCreateViewCommand(StringInfo buf, Oid viewOid)
|
||||
{
|
||||
/*
|
||||
|
@ -460,3 +425,250 @@ AlterViewOwnerCommand(Oid viewOid)
|
|||
|
||||
return alterOwnerCommand->data;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* PreprocessAlterViewStmt is invoked for alter view statements.
|
||||
*/
|
||||
List *
|
||||
PreprocessAlterViewStmt(Node *node, const char *queryString, ProcessUtilityContext
|
||||
processUtilityContext)
|
||||
{
|
||||
AlterTableStmt *stmt = castNode(AlterTableStmt, node);
|
||||
|
||||
ObjectAddress viewAddress = GetObjectAddressFromParseTree((Node *) stmt, true);
|
||||
if (!ShouldPropagateObject(&viewAddress))
|
||||
{
|
||||
return NIL;
|
||||
}
|
||||
|
||||
QualifyTreeNode((Node *) stmt);
|
||||
|
||||
EnsureCoordinator();
|
||||
EnsureSequentialMode(OBJECT_VIEW);
|
||||
|
||||
/* reconstruct alter statement in a portable fashion */
|
||||
const char *alterViewStmtSql = DeparseTreeNode((Node *) stmt);
|
||||
|
||||
List *commands = list_make3(DISABLE_DDL_PROPAGATION,
|
||||
(void *) alterViewStmtSql,
|
||||
ENABLE_DDL_PROPAGATION);
|
||||
|
||||
return NodeDDLTaskList(NON_COORDINATOR_NODES, commands);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* PostprocessAlterViewStmt is invoked for alter view statements.
|
||||
*/
|
||||
List *
|
||||
PostprocessAlterViewStmt(Node *node, const char *queryString)
|
||||
{
|
||||
AlterTableStmt *stmt = castNode(AlterTableStmt, node);
|
||||
Assert(AlterTableStmtObjType_compat(stmt) == OBJECT_VIEW);
|
||||
|
||||
ObjectAddress viewAddress = GetObjectAddressFromParseTree((Node *) stmt, true);
|
||||
if (!ShouldPropagateObject(&viewAddress))
|
||||
{
|
||||
return NIL;
|
||||
}
|
||||
|
||||
if (IsObjectAddressOwnedByExtension(&viewAddress, NULL))
|
||||
{
|
||||
return NIL;
|
||||
}
|
||||
|
||||
/* If the view has any unsupported dependency, create it locally */
|
||||
if (ErrorOrWarnIfObjectHasUnsupportedDependency(&viewAddress))
|
||||
{
|
||||
return NIL;
|
||||
}
|
||||
|
||||
EnsureDependenciesExistOnAllNodes(&viewAddress);
|
||||
|
||||
return NIL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* AlterViewStmtObjectAddress returns the ObjectAddress for the subject of the
|
||||
* ALTER VIEW statement.
|
||||
*/
|
||||
ObjectAddress
|
||||
AlterViewStmtObjectAddress(Node *node, bool missing_ok)
|
||||
{
|
||||
AlterTableStmt *stmt = castNode(AlterTableStmt, node);
|
||||
Oid viewOid = RangeVarGetRelid(stmt->relation, NoLock, missing_ok);
|
||||
|
||||
ObjectAddress viewAddress = { 0 };
|
||||
ObjectAddressSet(viewAddress, RelationRelationId, viewOid);
|
||||
|
||||
return viewAddress;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* PreprocessRenameViewStmt is called when the user is renaming the view or the column of
|
||||
* the view.
|
||||
*/
|
||||
List *
|
||||
PreprocessRenameViewStmt(Node *node, const char *queryString,
|
||||
ProcessUtilityContext processUtilityContext)
|
||||
{
|
||||
ObjectAddress typeAddress = GetObjectAddressFromParseTree(node, true);
|
||||
if (!ShouldPropagateObject(&typeAddress))
|
||||
{
|
||||
return NIL;
|
||||
}
|
||||
|
||||
EnsureCoordinator();
|
||||
|
||||
/* fully qualify */
|
||||
QualifyTreeNode(node);
|
||||
|
||||
/* deparse sql*/
|
||||
const char *renameStmtSql = DeparseTreeNode(node);
|
||||
|
||||
EnsureSequentialMode(OBJECT_VIEW);
|
||||
|
||||
/* to prevent recursion with mx we disable ddl propagation */
|
||||
List *commands = list_make3(DISABLE_DDL_PROPAGATION,
|
||||
(void *) renameStmtSql,
|
||||
ENABLE_DDL_PROPAGATION);
|
||||
|
||||
return NodeDDLTaskList(NON_COORDINATOR_NODES, commands);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* RenameViewStmtObjectAddress returns the ObjectAddress of the view that is the object
|
||||
* of the RenameStmt. Errors if missing_ok is false.
|
||||
*/
|
||||
ObjectAddress
|
||||
RenameViewStmtObjectAddress(Node *node, bool missing_ok)
|
||||
{
|
||||
RenameStmt *stmt = castNode(RenameStmt, node);
|
||||
|
||||
Oid viewOid = RangeVarGetRelid(stmt->relation, NoLock, missing_ok);
|
||||
|
||||
ObjectAddress viewAddress = { 0 };
|
||||
ObjectAddressSet(viewAddress, RelationRelationId, viewOid);
|
||||
|
||||
return viewAddress;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* PreprocessAlterViewSchemaStmt is executed before the statement is applied to the local
|
||||
* postgres instance.
|
||||
*/
|
||||
List *
|
||||
PreprocessAlterViewSchemaStmt(Node *node, const char *queryString,
|
||||
ProcessUtilityContext processUtilityContext)
|
||||
{
|
||||
AlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);
|
||||
|
||||
ObjectAddress typeAddress = GetObjectAddressFromParseTree((Node *) stmt, true);
|
||||
if (!ShouldPropagateObject(&typeAddress))
|
||||
{
|
||||
return NIL;
|
||||
}
|
||||
|
||||
EnsureCoordinator();
|
||||
EnsureSequentialMode(OBJECT_VIEW);
|
||||
|
||||
QualifyTreeNode((Node *) stmt);
|
||||
|
||||
const char *sql = DeparseTreeNode((Node *) stmt);
|
||||
|
||||
List *commands = list_make3(DISABLE_DDL_PROPAGATION,
|
||||
(void *) sql,
|
||||
ENABLE_DDL_PROPAGATION);
|
||||
|
||||
return NodeDDLTaskList(NON_COORDINATOR_NODES, commands);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* PostprocessAlterViewSchemaStmt is executed after the change has been applied locally, we
|
||||
* can now use the new dependencies of the view to ensure all its dependencies exist on
|
||||
* the workers before we apply the commands remotely.
|
||||
*/
|
||||
List *
|
||||
PostprocessAlterViewSchemaStmt(Node *node, const char *queryString)
|
||||
{
|
||||
AlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);
|
||||
|
||||
ObjectAddress viewAddress = GetObjectAddressFromParseTree((Node *) stmt, true);
|
||||
if (!ShouldPropagateObject(&viewAddress))
|
||||
{
|
||||
return NIL;
|
||||
}
|
||||
|
||||
/* dependencies have changed (schema) let's ensure they exist */
|
||||
EnsureDependenciesExistOnAllNodes(&viewAddress);
|
||||
|
||||
return NIL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* AlterViewSchemaStmtObjectAddress returns the ObjectAddress of the view that is the object
|
||||
* of the alter schema statement.
|
||||
*/
|
||||
ObjectAddress
|
||||
AlterViewSchemaStmtObjectAddress(Node *node, bool missing_ok)
|
||||
{
|
||||
AlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);
|
||||
|
||||
Oid viewOid = RangeVarGetRelid(stmt->relation, NoLock, true);
|
||||
|
||||
/*
|
||||
* Since it can be called both before and after executing the standardProcess utility,
|
||||
* we need to check both old and new schemas
|
||||
*/
|
||||
if (viewOid == InvalidOid)
|
||||
{
|
||||
Oid schemaId = get_namespace_oid(stmt->newschema, missing_ok);
|
||||
viewOid = get_relname_relid(stmt->relation->relname, schemaId);
|
||||
|
||||
/*
|
||||
* if the view is still invalid we couldn't find the view, error with the same
|
||||
* message postgres would error with it missing_ok is false (not ok to miss)
|
||||
*/
|
||||
if (!missing_ok && viewOid == InvalidOid)
|
||||
{
|
||||
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("view \"%s\" does not exist",
|
||||
stmt->relation->relname)));
|
||||
}
|
||||
}
|
||||
|
||||
ObjectAddress viewAddress = { 0 };
|
||||
ObjectAddressSet(viewAddress, RelationRelationId, viewOid);
|
||||
|
||||
return viewAddress;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* IsViewRenameStmt returns whether the passed-in RenameStmt is the following
|
||||
* form:
|
||||
*
|
||||
* - ALTER VIEW RENAME
|
||||
* - ALTER VIEW RENAME COLUMN
|
||||
*/
|
||||
bool
|
||||
IsViewRenameStmt(RenameStmt *renameStmt)
|
||||
{
|
||||
bool isViewRenameStmt = false;
|
||||
|
||||
if (renameStmt->renameType == OBJECT_VIEW ||
|
||||
(renameStmt->renameType == OBJECT_COLUMN &&
|
||||
renameStmt->relationType == OBJECT_VIEW))
|
||||
{
|
||||
isViewRenameStmt = true;
|
||||
}
|
||||
|
||||
return isViewRenameStmt;
|
||||
}
|
||||
|
|
|
@ -24,6 +24,13 @@
|
|||
|
||||
static void AppendDropViewStmt(StringInfo buf, DropStmt *stmt);
|
||||
static void AppendViewNameList(StringInfo buf, List *objects);
|
||||
static void AppendAlterViewStmt(StringInfo buf, AlterTableStmt *stmt);
|
||||
static void AppendAlterViewCmd(StringInfo buf, AlterTableCmd *alterTableCmd);
|
||||
static void AppendAlterViewOwnerStmt(StringInfo buf, AlterTableCmd *alterTableCmd);
|
||||
static void AppendAlterViewSetOptionsStmt(StringInfo buf, AlterTableCmd *alterTableCmd);
|
||||
static void AppendAlterViewResetOptionsStmt(StringInfo buf, AlterTableCmd *alterTableCmd);
|
||||
static void AppendRenameViewStmt(StringInfo buf, RenameStmt *stmt);
|
||||
static void AppendAlterViewSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt);
|
||||
|
||||
/*
|
||||
* DeparseDropViewStmt deparses the given DROP VIEW statement.
|
||||
|
@ -92,3 +99,212 @@ AppendViewNameList(StringInfo buf, List *viewNamesList)
|
|||
isFirstView = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* DeparseAlterViewStmt deparses the given ALTER VIEW statement.
|
||||
*/
|
||||
char *
|
||||
DeparseAlterViewStmt(Node *node)
|
||||
{
|
||||
AlterTableStmt *stmt = castNode(AlterTableStmt, node);
|
||||
StringInfoData str = { 0 };
|
||||
initStringInfo(&str);
|
||||
|
||||
AppendAlterViewStmt(&str, stmt);
|
||||
|
||||
return str.data;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
AppendAlterViewStmt(StringInfo buf, AlterTableStmt *stmt)
|
||||
{
|
||||
const char *identifier = quote_qualified_identifier(stmt->relation->schemaname,
|
||||
stmt->relation->relname);
|
||||
|
||||
appendStringInfo(buf, "ALTER VIEW %s ", identifier);
|
||||
|
||||
AlterTableCmd *alterTableCmd = castNode(AlterTableCmd, lfirst(list_head(stmt->cmds)));
|
||||
AppendAlterViewCmd(buf, alterTableCmd);
|
||||
|
||||
appendStringInfoString(buf, ";");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
AppendAlterViewCmd(StringInfo buf, AlterTableCmd *alterTableCmd)
|
||||
{
|
||||
switch (alterTableCmd->subtype)
|
||||
{
|
||||
case AT_ChangeOwner:
|
||||
{
|
||||
AppendAlterViewOwnerStmt(buf, alterTableCmd);
|
||||
break;
|
||||
}
|
||||
|
||||
case AT_SetRelOptions:
|
||||
{
|
||||
AppendAlterViewSetOptionsStmt(buf, alterTableCmd);
|
||||
break;
|
||||
}
|
||||
|
||||
case AT_ResetRelOptions:
|
||||
{
|
||||
AppendAlterViewResetOptionsStmt(buf, alterTableCmd);
|
||||
break;
|
||||
}
|
||||
|
||||
case AT_ColumnDefault:
|
||||
{
|
||||
elog(ERROR, "Citus doesn't support setting or resetting default values for a "
|
||||
"column of view");
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
/*
|
||||
* ALTER VIEW command only supports for the cases checked above but an
|
||||
* ALTER TABLE commands targeting views may have different cases. To let
|
||||
* PG throw the right error locally, we don't throw any error here
|
||||
*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
AppendAlterViewOwnerStmt(StringInfo buf, AlterTableCmd *alterTableCmd)
|
||||
{
|
||||
appendStringInfo(buf, "OWNER TO %s", RoleSpecString(alterTableCmd->newowner, true));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
AppendAlterViewSetOptionsStmt(StringInfo buf, AlterTableCmd *alterTableCmd)
|
||||
{
|
||||
ListCell *lc = NULL;
|
||||
bool initialOption = true;
|
||||
foreach(lc, (List *) alterTableCmd->def)
|
||||
{
|
||||
DefElem *def = (DefElem *) lfirst(lc);
|
||||
|
||||
if (initialOption)
|
||||
{
|
||||
appendStringInfo(buf, "SET (");
|
||||
initialOption = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
appendStringInfo(buf, ",");
|
||||
}
|
||||
|
||||
appendStringInfo(buf, "%s", def->defname);
|
||||
if (def->arg != NULL)
|
||||
{
|
||||
appendStringInfo(buf, "=");
|
||||
appendStringInfo(buf, "%s", defGetString(def));
|
||||
}
|
||||
}
|
||||
|
||||
appendStringInfo(buf, ")");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
AppendAlterViewResetOptionsStmt(StringInfo buf, AlterTableCmd *alterTableCmd)
|
||||
{
|
||||
ListCell *lc = NULL;
|
||||
bool initialOption = true;
|
||||
foreach(lc, (List *) alterTableCmd->def)
|
||||
{
|
||||
DefElem *def = (DefElem *) lfirst(lc);
|
||||
|
||||
if (initialOption)
|
||||
{
|
||||
appendStringInfo(buf, "RESET (");
|
||||
initialOption = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
appendStringInfo(buf, ",");
|
||||
}
|
||||
|
||||
appendStringInfo(buf, "%s", def->defname);
|
||||
}
|
||||
|
||||
appendStringInfo(buf, ")");
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
DeparseRenameViewStmt(Node *node)
|
||||
{
|
||||
RenameStmt *stmt = castNode(RenameStmt, node);
|
||||
StringInfoData str = { 0 };
|
||||
initStringInfo(&str);
|
||||
|
||||
AppendRenameViewStmt(&str, stmt);
|
||||
|
||||
return str.data;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
AppendRenameViewStmt(StringInfo buf, RenameStmt *stmt)
|
||||
{
|
||||
switch (stmt->renameType)
|
||||
{
|
||||
case OBJECT_COLUMN:
|
||||
{
|
||||
const char *identifier =
|
||||
quote_qualified_identifier(stmt->relation->schemaname,
|
||||
stmt->relation->relname);
|
||||
appendStringInfo(buf, "ALTER VIEW %s RENAME COLUMN %s TO %s;", identifier,
|
||||
quote_identifier(stmt->subname), quote_identifier(
|
||||
stmt->newname));
|
||||
break;
|
||||
}
|
||||
|
||||
case OBJECT_VIEW:
|
||||
{
|
||||
const char *identifier =
|
||||
quote_qualified_identifier(stmt->relation->schemaname,
|
||||
stmt->relation->relname);
|
||||
appendStringInfo(buf, "ALTER VIEW %s RENAME TO %s;", identifier,
|
||||
quote_identifier(stmt->newname));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
ereport(ERROR, (errmsg("unsupported subtype for alter view rename command"),
|
||||
errdetail("sub command type: %d", stmt->renameType)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
DeparseAlterViewSchemaStmt(Node *node)
|
||||
{
|
||||
AlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);
|
||||
StringInfoData str = { 0 };
|
||||
initStringInfo(&str);
|
||||
|
||||
AppendAlterViewSchemaStmt(&str, stmt);
|
||||
|
||||
return str.data;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
AppendAlterViewSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt)
|
||||
{
|
||||
const char *identifier = quote_qualified_identifier(stmt->relation->schemaname,
|
||||
stmt->relation->relname);
|
||||
appendStringInfo(buf, "ALTER VIEW %s SET SCHEMA %s;", identifier, quote_identifier(
|
||||
stmt->newschema));
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include "utils/guc.h"
|
||||
#include "utils/lsyscache.h"
|
||||
|
||||
static void QualifyViewRangeVar(RangeVar *view);
|
||||
|
||||
/*
|
||||
* QualifyDropViewStmt quailifies the view names of the DROP VIEW statement.
|
||||
*/
|
||||
|
@ -52,3 +54,54 @@ QualifyDropViewStmt(Node *node)
|
|||
|
||||
stmt->objects = qualifiedViewNames;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* QualifyAlterViewStmt quailifies the view name of the ALTER VIEW statement.
|
||||
*/
|
||||
void
|
||||
QualifyAlterViewStmt(Node *node)
|
||||
{
|
||||
AlterTableStmt *stmt = castNode(AlterTableStmt, node);
|
||||
RangeVar *view = stmt->relation;
|
||||
QualifyViewRangeVar(view);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* QualifyRenameViewStmt quailifies the view name of the ALTER VIEW ... RENAME statement.
|
||||
*/
|
||||
void
|
||||
QualifyRenameViewStmt(Node *node)
|
||||
{
|
||||
RenameStmt *stmt = castNode(RenameStmt, node);
|
||||
RangeVar *view = stmt->relation;
|
||||
QualifyViewRangeVar(view);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* QualifyAlterViewSchemaStmt quailifies the view name of the ALTER VIEW ... SET SCHEMA statement.
|
||||
*/
|
||||
void
|
||||
QualifyAlterViewSchemaStmt(Node *node)
|
||||
{
|
||||
AlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);
|
||||
RangeVar *view = stmt->relation;
|
||||
QualifyViewRangeVar(view);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* QualifyViewRangeVar qualifies the given view RangeVar if it is not qualified.
|
||||
*/
|
||||
static void
|
||||
QualifyViewRangeVar(RangeVar *view)
|
||||
{
|
||||
if (view->schemaname == NULL)
|
||||
{
|
||||
Oid viewOid = RelnameGetRelid(view->relname);
|
||||
Oid schemaOid = get_rel_namespace(viewOid);
|
||||
view->schemaname = get_namespace_name(schemaOid);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -766,6 +766,58 @@ SupportedDependencyByCitus(const ObjectAddress *address)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* ErrorOrWarnIfObjectHasUnsupportedDependency returns false without throwing any message if
|
||||
* object doesn't have any unsupported dependency, else throws a message with proper level
|
||||
* (except the cluster doesn't have any node) and return true.
|
||||
*/
|
||||
bool
|
||||
ErrorOrWarnIfObjectHasUnsupportedDependency(ObjectAddress *objectAddress)
|
||||
{
|
||||
DeferredErrorMessage *errMsg = DeferErrorIfHasUnsupportedDependency(objectAddress);
|
||||
if (errMsg != NULL)
|
||||
{
|
||||
/*
|
||||
* Don't need to give any 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 object won't be distributed.
|
||||
*/
|
||||
if (!HasAnyNodes())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Since Citus drops and recreates some object while converting a table type
|
||||
* giving a DEBUG1 message is enough if the process in table type conversion
|
||||
* function call
|
||||
*/
|
||||
if (InTableTypeConversionFunctionCall)
|
||||
{
|
||||
RaiseDeferredError(errMsg, DEBUG1);
|
||||
}
|
||||
/*
|
||||
* If the view is object distributed, we should provide an error to not have
|
||||
* different definition of object on coordinator and worker nodes. If the object
|
||||
* is not distributed yet, we can create it locally to not affect user's local
|
||||
* usage experience.
|
||||
*/
|
||||
else if (IsObjectDistributed(objectAddress))
|
||||
{
|
||||
RaiseDeferredError(errMsg, ERROR);
|
||||
}
|
||||
else
|
||||
{
|
||||
RaiseDeferredError(errMsg, WARNING);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* DeferErrorIfHasUnsupportedDependency returns deferred error message if the given
|
||||
* object has any undistributable dependency.
|
||||
|
|
|
@ -658,12 +658,25 @@ 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 ObjectAddress AlterViewStmtObjectAddress(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);
|
||||
extern List * CreateViewDDLCommandsIdempotent(Oid viewOid);
|
||||
extern List * PreprocessAlterViewStmt(Node *node, const char *queryString,
|
||||
ProcessUtilityContext processUtilityContext);
|
||||
extern List * PostprocessAlterViewStmt(Node *node, const char *queryString);
|
||||
extern List * PreprocessRenameViewStmt(Node *node, const char *queryString,
|
||||
ProcessUtilityContext processUtilityContext);
|
||||
extern ObjectAddress RenameViewStmtObjectAddress(Node *node, bool missing_ok);
|
||||
extern List * PreprocessAlterViewSchemaStmt(Node *node, const char *queryString,
|
||||
ProcessUtilityContext processUtilityContext);
|
||||
extern List * PostprocessAlterViewSchemaStmt(Node *node, const char *queryString);
|
||||
extern ObjectAddress AlterViewSchemaStmtObjectAddress(Node *node, bool missing_ok);
|
||||
extern bool IsViewRenameStmt(RenameStmt *renameStmt);
|
||||
|
||||
/* trigger.c - forward declarations */
|
||||
extern List * GetExplicitTriggerCommandList(Oid relationId);
|
||||
|
|
|
@ -146,7 +146,14 @@ extern ObjectAddress RenameAttributeStmtObjectAddress(Node *stmt, bool missing_o
|
|||
|
||||
/* forward declarations for deparse_view_stmts.c */
|
||||
extern void QualifyDropViewStmt(Node *node);
|
||||
extern void AppendViewDefinitionToCreateViewCommand(StringInfo buf, Oid viewOid);
|
||||
extern void QualifyAlterViewStmt(Node *node);
|
||||
extern void QualifyRenameViewStmt(Node *node);
|
||||
extern void QualifyAlterViewSchemaStmt(Node *node);
|
||||
extern char * DeparseRenameViewStmt(Node *stmt);
|
||||
extern char * DeparseAlterViewStmt(Node *node);
|
||||
extern char * DeparseDropViewStmt(Node *node);
|
||||
extern char * DeparseAlterViewSchemaStmt(Node *node);
|
||||
|
||||
|
||||
/* forward declarations for deparse_function_stmts.c */
|
||||
extern char * DeparseDropFunctionStmt(Node *stmt);
|
||||
|
|
|
@ -22,6 +22,7 @@ extern List * GetUniqueDependenciesList(List *objectAddressesList);
|
|||
extern List * GetDependenciesForObject(const ObjectAddress *target);
|
||||
extern List * GetAllSupportedDependenciesForObject(const ObjectAddress *target);
|
||||
extern List * GetAllDependenciesForObject(const ObjectAddress *target);
|
||||
extern bool ErrorOrWarnIfObjectHasUnsupportedDependency(ObjectAddress *objectAddress);
|
||||
extern DeferredErrorMessage * DeferErrorIfHasUnsupportedDependency(const
|
||||
ObjectAddress *
|
||||
objectAddress);
|
||||
|
|
|
@ -435,6 +435,135 @@ 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
|
||||
-- ALTER VIEW PROPAGATION
|
||||
CREATE TABLE alter_view_table(id int, val1 text);
|
||||
SELECT create_distributed_table('alter_view_table','id');
|
||||
create_distributed_table
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
CREATE VIEW alter_view_1 AS SELECT * FROM alter_view_table;
|
||||
-- Set/drop default value is not supported by Citus
|
||||
ALTER VIEW alter_view_1 ALTER COLUMN val1 SET DEFAULT random()::text;
|
||||
ERROR: Citus doesn't support setting or resetting default values for a column of view
|
||||
ALTER TABLE alter_view_1 ALTER COLUMN val1 SET DEFAULT random()::text;
|
||||
ERROR: Citus doesn't support setting or resetting default values for a column of view
|
||||
ALTER VIEW alter_view_1 ALTER COLUMN val1 DROP DEFAULT;
|
||||
ERROR: Citus doesn't support setting or resetting default values for a column of view
|
||||
ALTER TABLE alter_view_1 ALTER COLUMN val1 DROP DEFAULT;
|
||||
ERROR: Citus doesn't support setting or resetting default values for a column of view
|
||||
-- Set/reset options view alter view/alter table commands
|
||||
ALTER VIEW alter_view_1 SET (check_option=cascaded);
|
||||
ALTER VIEW alter_view_1 SET (security_barrier);
|
||||
ALTER VIEW alter_view_1 SET (check_option=cascaded, security_barrier);
|
||||
ALTER VIEW alter_view_1 SET (check_option=cascaded, security_barrier = true);
|
||||
ALTER TABLE alter_view_1 SET (check_option=cascaded);
|
||||
ALTER TABLE alter_view_1 SET (security_barrier);
|
||||
ALTER TABLE alter_view_1 SET (check_option=cascaded, security_barrier);
|
||||
ALTER TABLE alter_view_1 SET (check_option=cascaded, security_barrier = true);
|
||||
-- Check the definition on both coordinator and worker node
|
||||
SELECT definition FROM pg_views WHERE viewname = 'alter_view_1';
|
||||
definition
|
||||
---------------------------------------------------------------------
|
||||
SELECT alter_view_table.id,+
|
||||
alter_view_table.val1 +
|
||||
FROM alter_view_table;
|
||||
(1 row)
|
||||
|
||||
SELECT relname, reloptions
|
||||
FROM pg_class
|
||||
WHERE oid = 'view_prop_schema.alter_view_1'::regclass::oid;
|
||||
relname | reloptions
|
||||
---------------------------------------------------------------------
|
||||
alter_view_1 | {check_option=cascaded,security_barrier=true}
|
||||
(1 row)
|
||||
|
||||
\c - - - :worker_1_port
|
||||
SELECT definition FROM pg_views WHERE viewname = 'alter_view_1';
|
||||
definition
|
||||
---------------------------------------------------------------------
|
||||
SELECT alter_view_table.id,+
|
||||
alter_view_table.val1 +
|
||||
FROM view_prop_schema.alter_view_table;
|
||||
(1 row)
|
||||
|
||||
SELECT relname, reloptions
|
||||
FROM pg_class
|
||||
WHERE oid = 'view_prop_schema.alter_view_1'::regclass::oid;
|
||||
relname | reloptions
|
||||
---------------------------------------------------------------------
|
||||
alter_view_1 | {check_option=cascaded,security_barrier=true}
|
||||
(1 row)
|
||||
|
||||
\c - - - :master_port
|
||||
SET search_path to view_prop_schema;
|
||||
ALTER TABLE alter_view_1 RESET (check_option, security_barrier);
|
||||
ALTER VIEW alter_view_1 RESET (check_option, security_barrier);
|
||||
-- Change the schema of the view
|
||||
ALTER TABLE alter_view_1 SET SCHEMA view_prop_schema_inner;
|
||||
ALTER VIEW view_prop_schema_inner.alter_view_1 SET SCHEMA view_prop_schema;
|
||||
-- Rename view and view's column name
|
||||
ALTER VIEW alter_view_1 RENAME COLUMN val1 TO val2;
|
||||
ALTER VIEW alter_view_1 RENAME val2 TO val1;
|
||||
ALTER VIEW alter_view_1 RENAME TO alter_view_2;
|
||||
ALTER TABLE alter_view_2 RENAME COLUMN val1 TO val2;
|
||||
ALTER TABLE alter_view_2 RENAME val2 TO val1;
|
||||
ALTER TABLE alter_view_2 RENAME TO alter_view_1;
|
||||
-- Alter owner vith alter view/alter table
|
||||
SET client_min_messages TO ERROR;
|
||||
CREATE USER alter_view_user;
|
||||
SELECT 1 FROM run_command_on_workers($$CREATE USER alter_view_user;$$);
|
||||
?column?
|
||||
---------------------------------------------------------------------
|
||||
1
|
||||
1
|
||||
(2 rows)
|
||||
|
||||
RESET client_min_messages;
|
||||
ALTER VIEW alter_view_1 OWNER TO alter_view_user;
|
||||
ALTER TABLE alter_view_1 OWNER TO alter_view_user;
|
||||
-- Alter view owned by extension
|
||||
CREATE TABLE table_for_ext_owned_view(id int);
|
||||
CREATE VIEW extension_owned_view AS SELECT * FROM table_for_ext_owned_view;
|
||||
WARNING: "view extension_owned_view" has dependency to "table table_for_ext_owned_view" that is not in Citus' metadata
|
||||
DETAIL: "view extension_owned_view" will be created only locally
|
||||
HINT: Distribute "table table_for_ext_owned_view" first to distribute "view extension_owned_view"
|
||||
CREATE EXTENSION seg;
|
||||
ALTER EXTENSION seg ADD VIEW extension_owned_view;
|
||||
NOTICE: Citus does not propagate adding/dropping member objects
|
||||
HINT: You can add/drop the member objects on the workers as well.
|
||||
SELECT create_distributed_table('table_for_ext_owned_view','id');
|
||||
create_distributed_table
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
CREATE OR REPLACE VIEW extension_owned_view AS SELECT * FROM table_for_ext_owned_view;
|
||||
-- Since the view is owned by extension Citus shouldn't propagate it
|
||||
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 '%extension_owned_view%';
|
||||
obj_identifier
|
||||
---------------------------------------------------------------------
|
||||
(0 rows)
|
||||
|
||||
-- Try syncing metadata after running ALTER VIEW commands
|
||||
SELECT start_metadata_sync_to_node('localhost', :worker_1_port);
|
||||
start_metadata_sync_to_node
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- Alter non-existing view
|
||||
ALTER VIEW IF EXISTS non_existing_view ALTER COLUMN val1 SET DEFAULT random()::text;
|
||||
NOTICE: relation "non_existing_view" does not exist, skipping
|
||||
ALTER VIEW IF EXISTS non_existing_view SET (check_option=cascaded);
|
||||
NOTICE: relation "non_existing_view" does not exist, skipping
|
||||
ALTER VIEW IF EXISTS non_existing_view RENAME COLUMN val1 TO val2;
|
||||
NOTICE: relation "non_existing_view" does not exist, skipping
|
||||
ALTER VIEW IF EXISTS non_existing_view RENAME val2 TO val1;
|
||||
NOTICE: relation "non_existing_view" does not exist, skipping
|
||||
ALTER VIEW IF EXISTS non_existing_view SET SCHEMA view_prop_schema;
|
||||
NOTICE: relation "non_existing_view" does not exist, skipping
|
||||
SET client_min_messages TO ERROR;
|
||||
DROP SCHEMA view_prop_schema_inner CASCADE;
|
||||
DROP SCHEMA view_prop_schema CASCADE;
|
||||
|
|
|
@ -268,6 +268,94 @@ 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;
|
||||
|
||||
-- ALTER VIEW PROPAGATION
|
||||
CREATE TABLE alter_view_table(id int, val1 text);
|
||||
SELECT create_distributed_table('alter_view_table','id');
|
||||
|
||||
CREATE VIEW alter_view_1 AS SELECT * FROM alter_view_table;
|
||||
|
||||
-- Set/drop default value is not supported by Citus
|
||||
ALTER VIEW alter_view_1 ALTER COLUMN val1 SET DEFAULT random()::text;
|
||||
ALTER TABLE alter_view_1 ALTER COLUMN val1 SET DEFAULT random()::text;
|
||||
|
||||
ALTER VIEW alter_view_1 ALTER COLUMN val1 DROP DEFAULT;
|
||||
ALTER TABLE alter_view_1 ALTER COLUMN val1 DROP DEFAULT;
|
||||
|
||||
-- Set/reset options view alter view/alter table commands
|
||||
ALTER VIEW alter_view_1 SET (check_option=cascaded);
|
||||
ALTER VIEW alter_view_1 SET (security_barrier);
|
||||
ALTER VIEW alter_view_1 SET (check_option=cascaded, security_barrier);
|
||||
ALTER VIEW alter_view_1 SET (check_option=cascaded, security_barrier = true);
|
||||
|
||||
ALTER TABLE alter_view_1 SET (check_option=cascaded);
|
||||
ALTER TABLE alter_view_1 SET (security_barrier);
|
||||
ALTER TABLE alter_view_1 SET (check_option=cascaded, security_barrier);
|
||||
ALTER TABLE alter_view_1 SET (check_option=cascaded, security_barrier = true);
|
||||
|
||||
-- Check the definition on both coordinator and worker node
|
||||
SELECT definition FROM pg_views WHERE viewname = 'alter_view_1';
|
||||
|
||||
SELECT relname, reloptions
|
||||
FROM pg_class
|
||||
WHERE oid = 'view_prop_schema.alter_view_1'::regclass::oid;
|
||||
|
||||
\c - - - :worker_1_port
|
||||
SELECT definition FROM pg_views WHERE viewname = 'alter_view_1';
|
||||
|
||||
SELECT relname, reloptions
|
||||
FROM pg_class
|
||||
WHERE oid = 'view_prop_schema.alter_view_1'::regclass::oid;
|
||||
|
||||
\c - - - :master_port
|
||||
SET search_path to view_prop_schema;
|
||||
|
||||
ALTER TABLE alter_view_1 RESET (check_option, security_barrier);
|
||||
ALTER VIEW alter_view_1 RESET (check_option, security_barrier);
|
||||
|
||||
-- Change the schema of the view
|
||||
ALTER TABLE alter_view_1 SET SCHEMA view_prop_schema_inner;
|
||||
ALTER VIEW view_prop_schema_inner.alter_view_1 SET SCHEMA view_prop_schema;
|
||||
|
||||
-- Rename view and view's column name
|
||||
ALTER VIEW alter_view_1 RENAME COLUMN val1 TO val2;
|
||||
ALTER VIEW alter_view_1 RENAME val2 TO val1;
|
||||
ALTER VIEW alter_view_1 RENAME TO alter_view_2;
|
||||
|
||||
ALTER TABLE alter_view_2 RENAME COLUMN val1 TO val2;
|
||||
ALTER TABLE alter_view_2 RENAME val2 TO val1;
|
||||
ALTER TABLE alter_view_2 RENAME TO alter_view_1;
|
||||
|
||||
-- Alter owner vith alter view/alter table
|
||||
SET client_min_messages TO ERROR;
|
||||
CREATE USER alter_view_user;
|
||||
SELECT 1 FROM run_command_on_workers($$CREATE USER alter_view_user;$$);
|
||||
RESET client_min_messages;
|
||||
ALTER VIEW alter_view_1 OWNER TO alter_view_user;
|
||||
ALTER TABLE alter_view_1 OWNER TO alter_view_user;
|
||||
|
||||
-- Alter view owned by extension
|
||||
CREATE TABLE table_for_ext_owned_view(id int);
|
||||
CREATE VIEW extension_owned_view AS SELECT * FROM table_for_ext_owned_view;
|
||||
|
||||
CREATE EXTENSION seg;
|
||||
ALTER EXTENSION seg ADD VIEW extension_owned_view;
|
||||
|
||||
SELECT create_distributed_table('table_for_ext_owned_view','id');
|
||||
CREATE OR REPLACE VIEW extension_owned_view AS SELECT * FROM table_for_ext_owned_view;
|
||||
|
||||
-- Since the view is owned by extension Citus shouldn't propagate it
|
||||
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 '%extension_owned_view%';
|
||||
|
||||
-- Try syncing metadata after running ALTER VIEW commands
|
||||
SELECT start_metadata_sync_to_node('localhost', :worker_1_port);
|
||||
|
||||
-- Alter non-existing view
|
||||
ALTER VIEW IF EXISTS non_existing_view ALTER COLUMN val1 SET DEFAULT random()::text;
|
||||
ALTER VIEW IF EXISTS non_existing_view SET (check_option=cascaded);
|
||||
ALTER VIEW IF EXISTS non_existing_view RENAME COLUMN val1 TO val2;
|
||||
ALTER VIEW IF EXISTS non_existing_view RENAME val2 TO val1;
|
||||
ALTER VIEW IF EXISTS non_existing_view SET SCHEMA view_prop_schema;
|
||||
|
||||
SET client_min_messages TO ERROR;
|
||||
DROP SCHEMA view_prop_schema_inner CASCADE;
|
||||
DROP SCHEMA view_prop_schema CASCADE;
|
||||
|
|
Loading…
Reference in New Issue