mirror of https://github.com/citusdata/citus.git
Hookup function/procedure deparsing to our utility hook (#3041)
DESCRIPTION: Propagate ALTER FUNCTION statements for distributed functions Using the implemented deparser for function statements to propagate changes to both functions and procedures that are previously distributed.pull/3046/head^2
parent
363409a0c2
commit
9c2c50d875
|
@ -26,22 +26,26 @@
|
||||||
#include "access/xact.h"
|
#include "access/xact.h"
|
||||||
#include "catalog/namespace.h"
|
#include "catalog/namespace.h"
|
||||||
#include "catalog/pg_proc.h"
|
#include "catalog/pg_proc.h"
|
||||||
#include "distributed/commands.h"
|
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
|
#include "commands/extension.h"
|
||||||
#include "distributed/colocation_utils.h"
|
#include "distributed/colocation_utils.h"
|
||||||
#include "distributed/master_protocol.h"
|
#include "distributed/commands.h"
|
||||||
|
#include "distributed/commands/utility_hook.h"
|
||||||
|
#include "distributed/deparser.h"
|
||||||
#include "distributed/maintenanced.h"
|
#include "distributed/maintenanced.h"
|
||||||
#include "distributed/metadata_sync.h"
|
#include "distributed/master_protocol.h"
|
||||||
#include "distributed/metadata/distobject.h"
|
#include "distributed/metadata/distobject.h"
|
||||||
#include "distributed/metadata/pg_dist_object.h"
|
#include "distributed/metadata/pg_dist_object.h"
|
||||||
|
#include "distributed/metadata_sync.h"
|
||||||
#include "distributed/multi_executor.h"
|
#include "distributed/multi_executor.h"
|
||||||
#include "distributed/relation_access_tracking.h"
|
#include "distributed/relation_access_tracking.h"
|
||||||
#include "distributed/worker_transaction.h"
|
#include "distributed/worker_transaction.h"
|
||||||
#include "parser/parse_coerce.h"
|
#include "parser/parse_coerce.h"
|
||||||
|
#include "parser/parse_type.h"
|
||||||
#include "storage/lmgr.h"
|
#include "storage/lmgr.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/fmgrprotos.h"
|
|
||||||
#include "utils/fmgroids.h"
|
#include "utils/fmgroids.h"
|
||||||
|
#include "utils/fmgrprotos.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/syscache.h"
|
#include "utils/syscache.h"
|
||||||
|
|
||||||
|
@ -62,10 +66,23 @@ static void UpdateFunctionDistributionInfo(const ObjectAddress *distAddress,
|
||||||
int *colocationId);
|
int *colocationId);
|
||||||
static void EnsureSequentialModeForFunctionDDL(void);
|
static void EnsureSequentialModeForFunctionDDL(void);
|
||||||
static void TriggerSyncMetadataToPrimaryNodes(void);
|
static void TriggerSyncMetadataToPrimaryNodes(void);
|
||||||
|
static bool ShouldPropagateAlterFunction(const ObjectAddress *address);
|
||||||
|
static ObjectAddress * FunctionToObjectAddress(ObjectType objectType,
|
||||||
|
ObjectWithArgs *objectWithArgs,
|
||||||
|
bool missing_ok);
|
||||||
|
static void ErrorIfUnsupportedAlterFunctionStmt(AlterFunctionStmt *stmt);
|
||||||
|
|
||||||
|
|
||||||
PG_FUNCTION_INFO_V1(create_distributed_function);
|
PG_FUNCTION_INFO_V1(create_distributed_function);
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM > 110000
|
||||||
|
#define AssertIsFunctionOrProcedure(objtype) \
|
||||||
|
Assert((objtype) == OBJECT_FUNCTION || (objtype) == OBJECT_PROCEDURE)
|
||||||
|
#else
|
||||||
|
#define AssertIsFunctionOrProcedure(objtype) \
|
||||||
|
Assert(objtype == OBJECT_FUNCTION)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* create_distributed_function gets a function or procedure name with their list of
|
* create_distributed_function gets a function or procedure name with their list of
|
||||||
|
@ -612,3 +629,472 @@ TriggerSyncMetadataToPrimaryNodes(void)
|
||||||
TriggerMetadataSync(MyDatabaseId);
|
TriggerMetadataSync(MyDatabaseId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ShouldPropagateAlterFunction returns, based on the address of a function, if alter
|
||||||
|
* statements targeting the function should be propagated.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
ShouldPropagateAlterFunction(const ObjectAddress *address)
|
||||||
|
{
|
||||||
|
if (creating_extension)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* extensions should be created separately on the workers, functions cascading
|
||||||
|
* from an extension should therefore not be propagated.
|
||||||
|
*/
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!EnableDependencyCreation)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* we are configured to disable object propagation, should not propagate anything
|
||||||
|
*/
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsObjectDistributed(address))
|
||||||
|
{
|
||||||
|
/* do not propagate alter function for non-distributed functions */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PlanAlterFunctionStmt is invoked for alter function statements with actions. Here we
|
||||||
|
* plan the jobs to be executed on the workers for functions that have been distributed in
|
||||||
|
* the cluster.
|
||||||
|
*/
|
||||||
|
List *
|
||||||
|
PlanAlterFunctionStmt(AlterFunctionStmt *stmt, const char *queryString)
|
||||||
|
{
|
||||||
|
const char *sql = NULL;
|
||||||
|
const ObjectAddress *address = NULL;
|
||||||
|
List *commands = NIL;
|
||||||
|
|
||||||
|
/* AlterFunctionStmt->objtype has only been added since pg11 */
|
||||||
|
#if PG_VERSION_NUM > 110000
|
||||||
|
AssertIsFunctionOrProcedure(stmt->objtype);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
address = GetObjectAddressFromParseTree((Node *) stmt, false);
|
||||||
|
if (!ShouldPropagateAlterFunction(address))
|
||||||
|
{
|
||||||
|
return NIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnsureCoordinator();
|
||||||
|
ErrorIfUnsupportedAlterFunctionStmt(stmt);
|
||||||
|
EnsureSequentialModeForFunctionDDL();
|
||||||
|
QualifyTreeNode((Node *) stmt);
|
||||||
|
sql = DeparseTreeNode((Node *) stmt);
|
||||||
|
|
||||||
|
commands = list_make3(DISABLE_DDL_PROPAGATION,
|
||||||
|
(void *) sql,
|
||||||
|
ENABLE_DDL_PROPAGATION);
|
||||||
|
|
||||||
|
return NodeDDLTaskList(ALL_WORKERS, commands);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PlanRenameFunctionStmt is called when the user is renaming a function. The invocation
|
||||||
|
* happens before the statement is applied locally.
|
||||||
|
*
|
||||||
|
* As the function already exists we have access to the ObjectAddress, this is used to
|
||||||
|
* check if it is distributed. If so the rename is executed on all the workers to keep the
|
||||||
|
* types in sync across the cluster.
|
||||||
|
*/
|
||||||
|
List *
|
||||||
|
PlanRenameFunctionStmt(RenameStmt *stmt, const char *queryString)
|
||||||
|
{
|
||||||
|
const char *sql = NULL;
|
||||||
|
const ObjectAddress *address = NULL;
|
||||||
|
List *commands = NIL;
|
||||||
|
|
||||||
|
AssertIsFunctionOrProcedure(stmt->renameType);
|
||||||
|
|
||||||
|
address = GetObjectAddressFromParseTree((Node *) stmt, false);
|
||||||
|
if (!ShouldPropagateAlterFunction(address))
|
||||||
|
{
|
||||||
|
return NIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnsureCoordinator();
|
||||||
|
EnsureSequentialModeForFunctionDDL();
|
||||||
|
QualifyTreeNode((Node *) stmt);
|
||||||
|
sql = DeparseTreeNode((Node *) stmt);
|
||||||
|
|
||||||
|
commands = list_make3(DISABLE_DDL_PROPAGATION,
|
||||||
|
(void *) sql,
|
||||||
|
ENABLE_DDL_PROPAGATION);
|
||||||
|
|
||||||
|
return NodeDDLTaskList(ALL_WORKERS, commands);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PlanAlterFunctionSchemaStmt is executed before the statement is applied to the local
|
||||||
|
* postgres instance.
|
||||||
|
*
|
||||||
|
* In this stage we can prepare the commands that need to be run on all workers.
|
||||||
|
*/
|
||||||
|
List *
|
||||||
|
PlanAlterFunctionSchemaStmt(AlterObjectSchemaStmt *stmt, const char *queryString)
|
||||||
|
{
|
||||||
|
const char *sql = NULL;
|
||||||
|
const ObjectAddress *address = NULL;
|
||||||
|
List *commands = NIL;
|
||||||
|
|
||||||
|
AssertIsFunctionOrProcedure(stmt->objectType);
|
||||||
|
|
||||||
|
address = GetObjectAddressFromParseTree((Node *) stmt, false);
|
||||||
|
if (!ShouldPropagateAlterFunction(address))
|
||||||
|
{
|
||||||
|
return NIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnsureCoordinator();
|
||||||
|
EnsureSequentialModeForFunctionDDL();
|
||||||
|
QualifyTreeNode((Node *) stmt);
|
||||||
|
sql = DeparseTreeNode((Node *) stmt);
|
||||||
|
|
||||||
|
commands = list_make3(DISABLE_DDL_PROPAGATION,
|
||||||
|
(void *) sql,
|
||||||
|
ENABLE_DDL_PROPAGATION);
|
||||||
|
|
||||||
|
return NodeDDLTaskList(ALL_WORKERS, commands);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PlanAlterTypeOwnerStmt is called for change of owner ship of functions before the owner
|
||||||
|
* ship is changed on the local instance.
|
||||||
|
*
|
||||||
|
* If the function for which the owner is changed is distributed we execute the change on
|
||||||
|
* all the workers to keep the type in sync across the cluster.
|
||||||
|
*/
|
||||||
|
List *
|
||||||
|
PlanAlterFunctionOwnerStmt(AlterOwnerStmt *stmt, const char *queryString)
|
||||||
|
{
|
||||||
|
const ObjectAddress *address = NULL;
|
||||||
|
const char *sql = NULL;
|
||||||
|
List *commands = NULL;
|
||||||
|
|
||||||
|
AssertIsFunctionOrProcedure(stmt->objectType);
|
||||||
|
|
||||||
|
address = GetObjectAddressFromParseTree((Node *) stmt, false);
|
||||||
|
if (!ShouldPropagateAlterFunction(address))
|
||||||
|
{
|
||||||
|
return NIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnsureCoordinator();
|
||||||
|
EnsureSequentialModeForFunctionDDL();
|
||||||
|
QualifyTreeNode((Node *) stmt);
|
||||||
|
sql = DeparseTreeNode((Node *) stmt);
|
||||||
|
|
||||||
|
commands = list_make3(DISABLE_DDL_PROPAGATION,
|
||||||
|
(void *) sql,
|
||||||
|
ENABLE_DDL_PROPAGATION);
|
||||||
|
|
||||||
|
return NodeDDLTaskList(ALL_WORKERS, commands);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PlanDropFunctionStmt gets called during the planning phase of a DROP FUNCTION statement
|
||||||
|
* and returns a list of DDLJob's that will drop any distributed functions from the
|
||||||
|
* workers.
|
||||||
|
*
|
||||||
|
* The DropStmt could have multiple objects to drop, the list of objects will be filtered
|
||||||
|
* to only keep the distributed functions for deletion on the workers. Non-distributed
|
||||||
|
* functions will still be dropped locally but not on the workers.
|
||||||
|
*/
|
||||||
|
List *
|
||||||
|
PlanDropFunctionStmt(DropStmt *stmt, const char *queryString)
|
||||||
|
{
|
||||||
|
List *deletingObjectWithArgsList = stmt->objects;
|
||||||
|
List *distributedObjectWithArgsList = NIL;
|
||||||
|
List *distributedFunctionAddresses = NIL;
|
||||||
|
ListCell *addressCell = NULL;
|
||||||
|
const char *dropStmtSql = NULL;
|
||||||
|
List *commands = NULL;
|
||||||
|
ListCell *objectWithArgsListCell = NULL;
|
||||||
|
DropStmt *stmtCopy = NULL;
|
||||||
|
|
||||||
|
AssertIsFunctionOrProcedure(stmt->removeType);
|
||||||
|
|
||||||
|
if (creating_extension)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* extensions should be created separately on the workers, types cascading from an
|
||||||
|
* extension should therefor not be propagated here.
|
||||||
|
*/
|
||||||
|
return NIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!EnableDependencyCreation)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* we are configured to disable object propagation, should not propagate anything
|
||||||
|
*/
|
||||||
|
return NIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Our statements need to be fully qualified so we can drop them from the right schema
|
||||||
|
* on the workers
|
||||||
|
*/
|
||||||
|
QualifyTreeNode((Node *) stmt);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* iterate over all functions to be dropped and filter to keep only distributed
|
||||||
|
* functions.
|
||||||
|
*/
|
||||||
|
foreach(objectWithArgsListCell, deletingObjectWithArgsList)
|
||||||
|
{
|
||||||
|
ObjectWithArgs *func = NULL;
|
||||||
|
ObjectAddress *address = NULL;
|
||||||
|
|
||||||
|
func = castNode(ObjectWithArgs, lfirst(objectWithArgsListCell));
|
||||||
|
address = FunctionToObjectAddress(stmt->removeType, func, stmt->missing_ok);
|
||||||
|
|
||||||
|
if (!IsObjectDistributed(address))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* collect information for all distributed functions */
|
||||||
|
distributedFunctionAddresses = lappend(distributedFunctionAddresses, address);
|
||||||
|
distributedObjectWithArgsList = lappend(distributedObjectWithArgsList, func);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list_length(distributedObjectWithArgsList) <= 0)
|
||||||
|
{
|
||||||
|
/* no distributed functions to drop */
|
||||||
|
return NIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* managing types can only be done on the coordinator if ddl propagation is on. when
|
||||||
|
* it is off we will never get here. MX workers don't have a notion of distributed
|
||||||
|
* types, so we block the call.
|
||||||
|
*/
|
||||||
|
EnsureCoordinator();
|
||||||
|
EnsureSequentialModeForFunctionDDL();
|
||||||
|
|
||||||
|
/* remove the entries for the distributed objects on dropping */
|
||||||
|
foreach(addressCell, distributedFunctionAddresses)
|
||||||
|
{
|
||||||
|
ObjectAddress *address = (ObjectAddress *) lfirst(addressCell);
|
||||||
|
UnmarkObjectDistributed(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Swap the list of objects before deparsing and restore the old list after. This
|
||||||
|
* ensures we only have distributed functions in the deparsed drop statement.
|
||||||
|
*/
|
||||||
|
stmtCopy = copyObject(stmt);
|
||||||
|
stmtCopy->objects = distributedObjectWithArgsList;
|
||||||
|
dropStmtSql = DeparseTreeNode((Node *) stmtCopy);
|
||||||
|
|
||||||
|
commands = list_make3(DISABLE_DDL_PROPAGATION,
|
||||||
|
(void *) dropStmtSql,
|
||||||
|
ENABLE_DDL_PROPAGATION);
|
||||||
|
|
||||||
|
return NodeDDLTaskList(ALL_WORKERS, commands);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ProcessAlterFunctionSchemaStmt is executed after the change has been applied locally,
|
||||||
|
* we can now use the new dependencies of the function to ensure all its dependencies
|
||||||
|
* exist on the workers before we apply the commands remotely.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ProcessAlterFunctionSchemaStmt(AlterObjectSchemaStmt *stmt, const char *queryString)
|
||||||
|
{
|
||||||
|
const ObjectAddress *address = NULL;
|
||||||
|
|
||||||
|
AssertIsFunctionOrProcedure(stmt->objectType);
|
||||||
|
|
||||||
|
address = GetObjectAddressFromParseTree((Node *) stmt, false);
|
||||||
|
if (!ShouldPropagateAlterFunction(address))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* dependencies have changed (schema) lets ensure they exist */
|
||||||
|
EnsureDependenciesExistsOnAllNodes(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AlterFunctionStmtObjectAddress returns the ObjectAddress of the subject in the
|
||||||
|
* AlterFunctionStmt. If missing_ok is set to false an error will be raised if postgres
|
||||||
|
* was unable to find the function/procedure that was the target of the statement.
|
||||||
|
*/
|
||||||
|
const ObjectAddress *
|
||||||
|
AlterFunctionStmtObjectAddress(AlterFunctionStmt *stmt, bool missing_ok)
|
||||||
|
{
|
||||||
|
ObjectType objectType = OBJECT_FUNCTION;
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM > 110000
|
||||||
|
objectType = stmt->objtype;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return FunctionToObjectAddress(objectType, stmt->func, missing_ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RenameFunctionStmtObjectAddress returns the ObjectAddress of the function that is the
|
||||||
|
* subject of the RenameStmt. Errors if missing_ok is false.
|
||||||
|
*/
|
||||||
|
const ObjectAddress *
|
||||||
|
RenameFunctionStmtObjectAddress(RenameStmt *stmt, bool missing_ok)
|
||||||
|
{
|
||||||
|
return FunctionToObjectAddress(stmt->renameType,
|
||||||
|
castNode(ObjectWithArgs, stmt->object), missing_ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AlterFunctionOwnerObjectAddress returns the ObjectAddress of the function that is the
|
||||||
|
* subject of the AlterOwnerStmt. Errors if missing_ok is false.
|
||||||
|
*/
|
||||||
|
const ObjectAddress *
|
||||||
|
AlterFunctionOwnerObjectAddress(AlterOwnerStmt *stmt, bool missing_ok)
|
||||||
|
{
|
||||||
|
return FunctionToObjectAddress(stmt->objectType,
|
||||||
|
castNode(ObjectWithArgs, stmt->object), missing_ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AlterFunctionSchemaStmtObjectAddress returns the ObjectAddress of the function that is
|
||||||
|
* the subject of the AlterObjectSchemaStmt. Errors if missing_ok is false.
|
||||||
|
*
|
||||||
|
* This could be called both before or after it has been applied locally. It will look in
|
||||||
|
* the old schema first, if the function cannot be found in that schema it will look in
|
||||||
|
* the new schema. Errors if missing_ok is false and the type cannot be found in either of
|
||||||
|
* the schemas.
|
||||||
|
*/
|
||||||
|
const ObjectAddress *
|
||||||
|
AlterFunctionSchemaStmtObjectAddress(AlterObjectSchemaStmt *stmt, bool missing_ok)
|
||||||
|
{
|
||||||
|
ObjectWithArgs *objectWithArgs = NULL;
|
||||||
|
Oid funcOid = InvalidOid;
|
||||||
|
List *names = NIL;
|
||||||
|
ObjectAddress *address = NULL;
|
||||||
|
|
||||||
|
AssertIsFunctionOrProcedure(stmt->objectType);
|
||||||
|
|
||||||
|
objectWithArgs = castNode(ObjectWithArgs, stmt->object);
|
||||||
|
funcOid = LookupFuncWithArgsCompat(stmt->objectType, objectWithArgs, true);
|
||||||
|
names = objectWithArgs->objname;
|
||||||
|
|
||||||
|
if (funcOid == InvalidOid)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* couldn't find the function, might have already been moved to the new schema, we
|
||||||
|
* construct a new objname that uses the new schema to search in.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* the name of the function is the last in the list of names */
|
||||||
|
Value *funcNameStr = lfirst(list_tail(names));
|
||||||
|
List *newNames = list_make2(makeString(stmt->newschema), funcNameStr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* we don't error here either, as the error would be not a good user facing
|
||||||
|
* error if the type didn't exist in the first place.
|
||||||
|
*/
|
||||||
|
objectWithArgs->objname = newNames;
|
||||||
|
funcOid = LookupFuncWithArgsCompat(stmt->objectType, objectWithArgs, true);
|
||||||
|
objectWithArgs->objname = names; /* restore the original names */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if the function is still invalid we couldn't find the function, cause postgres
|
||||||
|
* to error by preforming a lookup once more. Since we know the
|
||||||
|
*/
|
||||||
|
if (!missing_ok && funcOid == InvalidOid)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* this will most probably throw an error, unless for some reason the function
|
||||||
|
* has just been created (if possible at all). For safety we assign the
|
||||||
|
* funcOid.
|
||||||
|
*/
|
||||||
|
funcOid = LookupFuncWithArgsCompat(stmt->objectType, objectWithArgs,
|
||||||
|
missing_ok);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address = palloc0(sizeof(ObjectAddress));
|
||||||
|
ObjectAddressSet(*address, ProcedureRelationId, funcOid);
|
||||||
|
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FunctionToObjectAddress returns the ObjectAddress of a Function or Procedure based on
|
||||||
|
* its type and ObjectWithArgs describing the Function/Procedure. If missing_ok is set to
|
||||||
|
* false an error will be raised by postgres explaining the Function/Procedure could not
|
||||||
|
* be found.
|
||||||
|
*/
|
||||||
|
static ObjectAddress *
|
||||||
|
FunctionToObjectAddress(ObjectType objectType, ObjectWithArgs *objectWithArgs,
|
||||||
|
bool missing_ok)
|
||||||
|
{
|
||||||
|
Oid funcOid = InvalidOid;
|
||||||
|
ObjectAddress *address = NULL;
|
||||||
|
|
||||||
|
AssertIsFunctionOrProcedure(objectType);
|
||||||
|
|
||||||
|
funcOid = LookupFuncWithArgsCompat(objectType, objectWithArgs, missing_ok);
|
||||||
|
address = palloc0(sizeof(ObjectAddress));
|
||||||
|
ObjectAddressSet(*address, ProcedureRelationId, funcOid);
|
||||||
|
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ErrorIfUnsupportedAlterFunctionStmt raises an error if the AlterFunctionStmt contains a
|
||||||
|
* construct that is not supported to be altered on a distributed function. It is assumed
|
||||||
|
* the statement passed in is already tested to be targeting a distributed function, and
|
||||||
|
* will only execute the checks to error on unsupported constructs.
|
||||||
|
*
|
||||||
|
* Unsupported Constructs:
|
||||||
|
* - ALTER FUNCTION ... SET ... FROM CURRENT
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
ErrorIfUnsupportedAlterFunctionStmt(AlterFunctionStmt *stmt)
|
||||||
|
{
|
||||||
|
ListCell *actionCell = NULL;
|
||||||
|
|
||||||
|
foreach(actionCell, stmt->actions)
|
||||||
|
{
|
||||||
|
DefElem *action = castNode(DefElem, lfirst(actionCell));
|
||||||
|
if (strcmp(action->defname, "set") == 0)
|
||||||
|
{
|
||||||
|
VariableSetStmt *setStmt = castNode(VariableSetStmt, action->arg);
|
||||||
|
if (setStmt->kind == VAR_SET_CURRENT)
|
||||||
|
{
|
||||||
|
/* check if the set action is a SET ... FROM CURRENT */
|
||||||
|
ereport(ERROR, (errmsg("unsupported ALTER FUNCTION ... SET ... FROM "
|
||||||
|
"CURRENT for a distributed function"),
|
||||||
|
errhint("SET FROM CURRENT is not supported for "
|
||||||
|
"distributed functions, instead use the SET ... "
|
||||||
|
"TO ... syntax with a constant value.")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -121,6 +121,14 @@ PlanAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt, const char *queryString)
|
||||||
return PlanAlterTypeSchemaStmt(stmt, queryString);
|
return PlanAlterTypeSchemaStmt(stmt, queryString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM > 110000
|
||||||
|
case OBJECT_PROCEDURE:
|
||||||
|
#endif
|
||||||
|
case OBJECT_FUNCTION:
|
||||||
|
{
|
||||||
|
return PlanAlterFunctionSchemaStmt(stmt, queryString);
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
/* do nothing for unsupported objects */
|
/* do nothing for unsupported objects */
|
||||||
|
@ -190,6 +198,15 @@ ProcessAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt, const char *queryStrin
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM > 110000
|
||||||
|
case OBJECT_PROCEDURE:
|
||||||
|
#endif
|
||||||
|
case OBJECT_FUNCTION:
|
||||||
|
{
|
||||||
|
ProcessAlterFunctionSchemaStmt(stmt, queryString);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
/* do nothing for unsupported objects */
|
/* do nothing for unsupported objects */
|
||||||
|
|
|
@ -424,6 +424,14 @@ multi_ProcessUtility(PlannedStmt *pstmt,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM > 110000
|
||||||
|
case OBJECT_PROCEDURE:
|
||||||
|
#endif
|
||||||
|
case OBJECT_FUNCTION:
|
||||||
|
{
|
||||||
|
ddlJobs = PlanDropFunctionStmt(dropStatement, queryString);
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
/* unsupported type, skipping*/
|
/* unsupported type, skipping*/
|
||||||
|
@ -474,6 +482,15 @@ multi_ProcessUtility(PlannedStmt *pstmt,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM > 110000
|
||||||
|
case OBJECT_PROCEDURE:
|
||||||
|
#endif
|
||||||
|
case OBJECT_FUNCTION:
|
||||||
|
{
|
||||||
|
ddlJobs = PlanRenameFunctionStmt(renameStmt, queryString);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
ddlJobs = PlanRenameStmt(renameStmt, queryString);
|
ddlJobs = PlanRenameStmt(renameStmt, queryString);
|
||||||
|
@ -531,6 +548,12 @@ multi_ProcessUtility(PlannedStmt *pstmt,
|
||||||
queryString);
|
queryString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsA(parsetree, AlterFunctionStmt))
|
||||||
|
{
|
||||||
|
ddlJobs = PlanAlterFunctionStmt(castNode(AlterFunctionStmt, parsetree),
|
||||||
|
queryString);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ALTER TABLE ALL IN TABLESPACE statements have their node type as
|
* ALTER TABLE ALL IN TABLESPACE statements have their node type as
|
||||||
* AlterTableMoveAllStmt. At the moment we do not support this functionality in
|
* AlterTableMoveAllStmt. At the moment we do not support this functionality in
|
||||||
|
@ -799,6 +822,14 @@ PlanAlterOwnerStmt(AlterOwnerStmt *stmt, const char *queryString)
|
||||||
return PlanAlterTypeOwnerStmt(stmt, queryString);
|
return PlanAlterTypeOwnerStmt(stmt, queryString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM > 110000
|
||||||
|
case OBJECT_PROCEDURE:
|
||||||
|
#endif
|
||||||
|
case OBJECT_FUNCTION:
|
||||||
|
{
|
||||||
|
return PlanAlterFunctionOwnerStmt(stmt, queryString);
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
/* do nothing for unsupported alter owner statements */
|
/* do nothing for unsupported alter owner statements */
|
||||||
|
|
|
@ -76,6 +76,12 @@ GetObjectAddressFromParseTree(Node *parseTree, bool missing_ok)
|
||||||
missing_ok);
|
missing_ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case T_AlterFunctionStmt:
|
||||||
|
{
|
||||||
|
return AlterFunctionStmtObjectAddress(castNode(AlterFunctionStmt, parseTree),
|
||||||
|
missing_ok);
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
@ -123,6 +129,14 @@ RenameStmtObjectAddress(RenameStmt *stmt, bool missing_ok)
|
||||||
return RenameAttributeStmtObjectAddress(stmt, missing_ok);
|
return RenameAttributeStmtObjectAddress(stmt, missing_ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM > 110000
|
||||||
|
case OBJECT_PROCEDURE:
|
||||||
|
#endif
|
||||||
|
case OBJECT_FUNCTION:
|
||||||
|
{
|
||||||
|
return RenameFunctionStmtObjectAddress(stmt, missing_ok);
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
ereport(ERROR, (errmsg("unsupported rename statement to get object address "
|
ereport(ERROR, (errmsg("unsupported rename statement to get object address "
|
||||||
|
@ -142,6 +156,14 @@ AlterObjectSchemaStmtObjectAddress(AlterObjectSchemaStmt *stmt, bool missing_ok)
|
||||||
return AlterTypeSchemaStmtObjectAddress(stmt, missing_ok);
|
return AlterTypeSchemaStmtObjectAddress(stmt, missing_ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM > 110000
|
||||||
|
case OBJECT_PROCEDURE:
|
||||||
|
#endif
|
||||||
|
case OBJECT_FUNCTION:
|
||||||
|
{
|
||||||
|
return AlterFunctionSchemaStmtObjectAddress(stmt, missing_ok);
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
ereport(ERROR, (errmsg("unsupported alter schema statement to get object "
|
ereport(ERROR, (errmsg("unsupported alter schema statement to get object "
|
||||||
|
@ -182,6 +204,14 @@ AlterOwnerStmtObjectAddress(AlterOwnerStmt *stmt, bool missing_ok)
|
||||||
return AlterTypeOwnerObjectAddress(stmt, missing_ok);
|
return AlterTypeOwnerObjectAddress(stmt, missing_ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM > 110000
|
||||||
|
case OBJECT_PROCEDURE:
|
||||||
|
#endif
|
||||||
|
case OBJECT_FUNCTION:
|
||||||
|
{
|
||||||
|
return AlterFunctionOwnerObjectAddress(stmt, missing_ok);
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
ereport(ERROR, (errmsg("unsupported alter owner statement to get object "
|
ereport(ERROR, (errmsg("unsupported alter owner statement to get object "
|
||||||
|
|
|
@ -50,6 +50,25 @@ extern bool TableReferencing(Oid relationId);
|
||||||
extern bool ConstraintIsAForeignKey(char *constraintName, Oid relationId);
|
extern bool ConstraintIsAForeignKey(char *constraintName, Oid relationId);
|
||||||
|
|
||||||
|
|
||||||
|
/* function.c - forward declarations */
|
||||||
|
extern List * PlanAlterFunctionStmt(AlterFunctionStmt *stmt, const char *queryString);
|
||||||
|
extern const ObjectAddress * AlterFunctionStmtObjectAddress(AlterFunctionStmt *stmt,
|
||||||
|
bool missing_ok);
|
||||||
|
extern List * PlanRenameFunctionStmt(RenameStmt *stmt, const char *queryString);
|
||||||
|
extern const ObjectAddress * RenameFunctionStmtObjectAddress(RenameStmt *stmt,
|
||||||
|
bool missing_ok);
|
||||||
|
extern List * PlanAlterFunctionOwnerStmt(AlterOwnerStmt *stmt, const char *queryString);
|
||||||
|
extern const ObjectAddress * AlterFunctionOwnerObjectAddress(AlterOwnerStmt *stmt,
|
||||||
|
bool missing_ok);
|
||||||
|
extern List * PlanAlterFunctionSchemaStmt(AlterObjectSchemaStmt *stmt,
|
||||||
|
const char *queryString);
|
||||||
|
extern const ObjectAddress * AlterFunctionSchemaStmtObjectAddress(
|
||||||
|
AlterObjectSchemaStmt *stmt, bool missing_ok);
|
||||||
|
extern void ProcessAlterFunctionSchemaStmt(AlterObjectSchemaStmt *stmt,
|
||||||
|
const char *queryString);
|
||||||
|
extern List * PlanDropFunctionStmt(DropStmt *stmt, const char *queryString);
|
||||||
|
|
||||||
|
|
||||||
/* grant.c - forward declarations */
|
/* grant.c - forward declarations */
|
||||||
extern List * PlanGrantStmt(GrantStmt *grantStmt);
|
extern List * PlanGrantStmt(GrantStmt *grantStmt);
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ SELECT run_command_on_workers($$CREATE USER functionuser;$$);
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
CREATE SCHEMA function_tests AUTHORIZATION functionuser;
|
CREATE SCHEMA function_tests AUTHORIZATION functionuser;
|
||||||
|
CREATE SCHEMA function_tests2 AUTHORIZATION functionuser;
|
||||||
SET search_path TO function_tests;
|
SET search_path TO function_tests;
|
||||||
SET citus.shard_count TO 4;
|
SET citus.shard_count TO 4;
|
||||||
-- Create and distribute a simple function
|
-- Create and distribute a simple function
|
||||||
|
@ -56,6 +57,17 @@ CREATE FUNCTION add_mixed_param_names(integer, val1 integer) RETURNS integer
|
||||||
LANGUAGE SQL
|
LANGUAGE SQL
|
||||||
IMMUTABLE
|
IMMUTABLE
|
||||||
RETURNS NULL ON NULL INPUT;
|
RETURNS NULL ON NULL INPUT;
|
||||||
|
-- make sure to propagate ddl propagation after we have setup our functions, this will
|
||||||
|
-- allow alter statements to be propagated and keep the functions in sync across machines
|
||||||
|
SET citus.enable_ddl_propagation TO on;
|
||||||
|
-- functions are distributed by int arguments, when run in isolation it is not guaranteed a table actually exists.
|
||||||
|
CREATE TABLE colocation_table(id int);
|
||||||
|
SELECT create_distributed_table('colocation_table','id');
|
||||||
|
create_distributed_table
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
-- make sure that none of the active and primary nodes hasmetadata
|
-- make sure that none of the active and primary nodes hasmetadata
|
||||||
-- at the start of the test
|
-- at the start of the test
|
||||||
select bool_or(hasmetadata) from pg_dist_node WHERE isactive AND noderole = 'primary';
|
select bool_or(hasmetadata) from pg_dist_node WHERE isactive AND noderole = 'primary';
|
||||||
|
@ -121,6 +133,170 @@ SELECT * FROM run_command_on_workers('SELECT function_tests.add(2,3);') ORDER BY
|
||||||
localhost | 57638 | t | 5
|
localhost | 57638 | t | 5
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
|
||||||
|
verify_function_is_same_on_workers
|
||||||
|
------------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- testing alter statements for a distributed function
|
||||||
|
-- ROWS 5, untested because;
|
||||||
|
-- ERROR: ROWS is not applicable when function does not return a set
|
||||||
|
ALTER FUNCTION add(int,int) CALLED ON NULL INPUT IMMUTABLE SECURITY INVOKER PARALLEL UNSAFE LEAKPROOF COST 5;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
|
||||||
|
verify_function_is_same_on_workers
|
||||||
|
------------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
ALTER FUNCTION add(int,int) RETURNS NULL ON NULL INPUT STABLE SECURITY DEFINER PARALLEL RESTRICTED;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
|
||||||
|
verify_function_is_same_on_workers
|
||||||
|
------------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
ALTER FUNCTION add(int,int) STRICT VOLATILE PARALLEL SAFE;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
|
||||||
|
verify_function_is_same_on_workers
|
||||||
|
------------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- Test SET/RESET for alter function
|
||||||
|
ALTER FUNCTION add(int,int) SET client_min_messages TO warning;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
|
||||||
|
verify_function_is_same_on_workers
|
||||||
|
------------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
ALTER FUNCTION add(int,int) SET client_min_messages TO error;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
|
||||||
|
verify_function_is_same_on_workers
|
||||||
|
------------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
ALTER FUNCTION add(int,int) SET client_min_messages TO debug;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
|
||||||
|
verify_function_is_same_on_workers
|
||||||
|
------------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
ALTER FUNCTION add(int,int) RESET client_min_messages;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
|
||||||
|
verify_function_is_same_on_workers
|
||||||
|
------------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- SET ... FROM CURRENT is not supported, verify the query fails with a descriptive error irregardless of where in the action list the statement occurs
|
||||||
|
ALTER FUNCTION add(int,int) SET client_min_messages FROM CURRENT;
|
||||||
|
ERROR: unsupported ALTER FUNCTION ... SET ... FROM CURRENT for a distributed function
|
||||||
|
HINT: SET FROM CURRENT is not supported for distributed functions, instead use the SET ... TO ... syntax with a constant value.
|
||||||
|
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
|
||||||
|
verify_function_is_same_on_workers
|
||||||
|
------------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
ALTER FUNCTION add(int,int) RETURNS NULL ON NULL INPUT SET client_min_messages FROM CURRENT;
|
||||||
|
ERROR: unsupported ALTER FUNCTION ... SET ... FROM CURRENT for a distributed function
|
||||||
|
HINT: SET FROM CURRENT is not supported for distributed functions, instead use the SET ... TO ... syntax with a constant value.
|
||||||
|
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
|
||||||
|
verify_function_is_same_on_workers
|
||||||
|
------------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
ALTER FUNCTION add(int,int) SET client_min_messages FROM CURRENT SECURITY DEFINER;
|
||||||
|
ERROR: unsupported ALTER FUNCTION ... SET ... FROM CURRENT for a distributed function
|
||||||
|
HINT: SET FROM CURRENT is not supported for distributed functions, instead use the SET ... TO ... syntax with a constant value.
|
||||||
|
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
|
||||||
|
verify_function_is_same_on_workers
|
||||||
|
------------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- rename function and make sure the new name can be used on the workers while the old name can't
|
||||||
|
ALTER FUNCTION add(int,int) RENAME TO add2;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('function_tests.add2(int,int)');
|
||||||
|
verify_function_is_same_on_workers
|
||||||
|
------------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM run_command_on_workers('SELECT function_tests.add(2,3);') ORDER BY 1,2;
|
||||||
|
nodename | nodeport | success | result
|
||||||
|
-----------+----------+---------+----------------------------------------------------------------------
|
||||||
|
localhost | 57637 | f | ERROR: function function_tests.add(integer, integer) does not exist
|
||||||
|
localhost | 57638 | f | ERROR: function function_tests.add(integer, integer) does not exist
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT * FROM run_command_on_workers('SELECT function_tests.add2(2,3);') ORDER BY 1,2;
|
||||||
|
nodename | nodeport | success | result
|
||||||
|
-----------+----------+---------+--------
|
||||||
|
localhost | 57637 | t | 5
|
||||||
|
localhost | 57638 | t | 5
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
ALTER FUNCTION add2(int,int) RENAME TO add;
|
||||||
|
-- change the owner of the function and verify the owner has been changed on the workers
|
||||||
|
ALTER FUNCTION add(int,int) OWNER TO functionuser;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
|
||||||
|
verify_function_is_same_on_workers
|
||||||
|
------------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT run_command_on_workers($$
|
||||||
|
SELECT row(usename, nspname, proname)
|
||||||
|
FROM pg_proc
|
||||||
|
JOIN pg_user ON (usesysid = proowner)
|
||||||
|
JOIN pg_namespace ON (pg_namespace.oid = pronamespace)
|
||||||
|
WHERE proname = 'add';
|
||||||
|
$$);
|
||||||
|
run_command_on_workers
|
||||||
|
---------------------------------------------------------
|
||||||
|
(localhost,57637,t,"(functionuser,function_tests,add)")
|
||||||
|
(localhost,57638,t,"(functionuser,function_tests,add)")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- change the schema of the function and verify the old schema doesn't exist anymore while
|
||||||
|
-- the new schema has the function.
|
||||||
|
ALTER FUNCTION add(int,int) SET SCHEMA function_tests2;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('function_tests2.add(int,int)');
|
||||||
|
verify_function_is_same_on_workers
|
||||||
|
------------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM run_command_on_workers('SELECT function_tests.add(2,3);') ORDER BY 1,2;
|
||||||
|
nodename | nodeport | success | result
|
||||||
|
-----------+----------+---------+----------------------------------------------------------------------
|
||||||
|
localhost | 57637 | f | ERROR: function function_tests.add(integer, integer) does not exist
|
||||||
|
localhost | 57638 | f | ERROR: function function_tests.add(integer, integer) does not exist
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT * FROM run_command_on_workers('SELECT function_tests2.add(2,3);') ORDER BY 1,2;
|
||||||
|
nodename | nodeport | success | result
|
||||||
|
-----------+----------+---------+--------
|
||||||
|
localhost | 57637 | t | 5
|
||||||
|
localhost | 57638 | t | 5
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
ALTER FUNCTION function_tests2.add(int,int) SET SCHEMA function_tests;
|
||||||
|
DROP FUNCTION add(int,int);
|
||||||
|
-- call should fail as function should have been dropped
|
||||||
|
SELECT * FROM run_command_on_workers('SELECT function_tests.add(2,3);') ORDER BY 1,2;
|
||||||
|
nodename | nodeport | success | result
|
||||||
|
-----------+----------+---------+----------------------------------------------------------------------
|
||||||
|
localhost | 57637 | f | ERROR: function function_tests.add(integer, integer) does not exist
|
||||||
|
localhost | 57638 | f | ERROR: function function_tests.add(integer, integer) does not exist
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
-- postgres doesn't accept parameter names in the regprocedure input
|
-- postgres doesn't accept parameter names in the regprocedure input
|
||||||
SELECT create_distributed_function('add_with_param_names(val1 int, int)', 'val1');
|
SELECT create_distributed_function('add_with_param_names(val1 int, int)', 'val1');
|
||||||
ERROR: syntax error at or near "int"
|
ERROR: syntax error at or near "int"
|
||||||
|
@ -371,6 +547,7 @@ SELECT stop_metadata_sync_to_node(nodename,nodeport) FROM pg_dist_node WHERE isa
|
||||||
|
|
||||||
SET client_min_messages TO error; -- suppress cascading objects dropping
|
SET client_min_messages TO error; -- suppress cascading objects dropping
|
||||||
DROP SCHEMA function_tests CASCADE;
|
DROP SCHEMA function_tests CASCADE;
|
||||||
|
DROP SCHEMA function_tests2 CASCADE;
|
||||||
-- This is hacky, but we should clean-up the resources as below
|
-- This is hacky, but we should clean-up the resources as below
|
||||||
\c - - - :worker_1_port
|
\c - - - :worker_1_port
|
||||||
SET client_min_messages TO error; -- suppress cascading objects dropping
|
SET client_min_messages TO error; -- suppress cascading objects dropping
|
||||||
|
@ -385,6 +562,7 @@ SELECT worker_drop_distributed_table(logicalrelid::text) FROM pg_dist_partition
|
||||||
|
|
||||||
TRUNCATE pg_dist_node;
|
TRUNCATE pg_dist_node;
|
||||||
DROP SCHEMA function_tests CASCADE;
|
DROP SCHEMA function_tests CASCADE;
|
||||||
|
DROP SCHEMA function_tests2 CASCADE;
|
||||||
\c - - - :worker_2_port
|
\c - - - :worker_2_port
|
||||||
SET client_min_messages TO error; -- suppress cascading objects dropping
|
SET client_min_messages TO error; -- suppress cascading objects dropping
|
||||||
UPDATE pg_dist_local_group SET groupid = 0;
|
UPDATE pg_dist_local_group SET groupid = 0;
|
||||||
|
@ -398,6 +576,7 @@ SELECT worker_drop_distributed_table(logicalrelid::text) FROM pg_dist_partition
|
||||||
|
|
||||||
TRUNCATE pg_dist_node;
|
TRUNCATE pg_dist_node;
|
||||||
DROP SCHEMA function_tests CASCADE;
|
DROP SCHEMA function_tests CASCADE;
|
||||||
|
DROP SCHEMA function_tests2 CASCADE;
|
||||||
\c - - - :master_port
|
\c - - - :master_port
|
||||||
DROP USER functionuser;
|
DROP USER functionuser;
|
||||||
SELECT run_command_on_workers($$DROP USER functionuser;$$);
|
SELECT run_command_on_workers($$DROP USER functionuser;$$);
|
||||||
|
|
|
@ -0,0 +1,204 @@
|
||||||
|
SHOW server_version \gset
|
||||||
|
SELECT substring(:'server_version', '\d+')::int >= 11 AS server_verion_eleven_and_above
|
||||||
|
\gset
|
||||||
|
\if :server_verion_eleven_and_above
|
||||||
|
\else
|
||||||
|
\q
|
||||||
|
\endif
|
||||||
|
SET citus.next_shard_id TO 20030000;
|
||||||
|
CREATE USER procedureuser;
|
||||||
|
NOTICE: not propagating CREATE ROLE/USER commands to worker nodes
|
||||||
|
HINT: Connect to worker nodes directly to manually create all necessary users and roles.
|
||||||
|
SELECT run_command_on_workers($$CREATE USER procedureuser;$$);
|
||||||
|
run_command_on_workers
|
||||||
|
-----------------------------------
|
||||||
|
(localhost,57637,t,"CREATE ROLE")
|
||||||
|
(localhost,57638,t,"CREATE ROLE")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
CREATE SCHEMA procedure_tests AUTHORIZATION procedureuser;
|
||||||
|
CREATE SCHEMA procedure_tests2 AUTHORIZATION procedureuser;
|
||||||
|
SET search_path TO procedure_tests;
|
||||||
|
SET citus.shard_count TO 4;
|
||||||
|
-- Create and distribute a simple function
|
||||||
|
CREATE OR REPLACE PROCEDURE raise_info(text)
|
||||||
|
LANGUAGE PLPGSQL AS $proc$
|
||||||
|
BEGIN
|
||||||
|
RAISE INFO 'information message %', $1;
|
||||||
|
END;
|
||||||
|
$proc$;
|
||||||
|
-- procedures are distributed by text arguments, when run in isolation it is not guaranteed a table actually exists.
|
||||||
|
CREATE TABLE colocation_table(id text);
|
||||||
|
SELECT create_distributed_table('colocation_table','id');
|
||||||
|
create_distributed_table
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT create_distributed_function('raise_info(text)', '$1');
|
||||||
|
create_distributed_function
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM run_command_on_workers($$CALL procedure_tests.raise_info('hello');$$) ORDER BY 1,2;
|
||||||
|
nodename | nodeport | success | result
|
||||||
|
-----------+----------+---------+--------
|
||||||
|
localhost | 57637 | t | CALL
|
||||||
|
localhost | 57638 | t | CALL
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT public.verify_function_is_same_on_workers('procedure_tests.raise_info(text)');
|
||||||
|
verify_function_is_same_on_workers
|
||||||
|
------------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- testing alter statements for a distributed function
|
||||||
|
-- ROWS 5, untested because;
|
||||||
|
-- ERROR: ROWS is not applicable when function does not return a set
|
||||||
|
ALTER PROCEDURE raise_info(text) SECURITY INVOKER;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('procedure_tests.raise_info(text)');
|
||||||
|
verify_function_is_same_on_workers
|
||||||
|
------------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
ALTER PROCEDURE raise_info(text) SECURITY DEFINER;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('procedure_tests.raise_info(text)');
|
||||||
|
verify_function_is_same_on_workers
|
||||||
|
------------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- Test SET/RESET for alter procedure
|
||||||
|
ALTER PROCEDURE raise_info(text) SET client_min_messages TO warning;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('procedure_tests.raise_info(text)');
|
||||||
|
verify_function_is_same_on_workers
|
||||||
|
------------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
ALTER PROCEDURE raise_info(text) SET client_min_messages TO error;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('procedure_tests.raise_info(text)');
|
||||||
|
verify_function_is_same_on_workers
|
||||||
|
------------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
ALTER PROCEDURE raise_info(text) SET client_min_messages TO debug;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('procedure_tests.raise_info(text)');
|
||||||
|
verify_function_is_same_on_workers
|
||||||
|
------------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
ALTER PROCEDURE raise_info(text) RESET client_min_messages;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('procedure_tests.raise_info(text)');
|
||||||
|
verify_function_is_same_on_workers
|
||||||
|
------------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- rename function and make sure the new name can be used on the workers while the old name can't
|
||||||
|
ALTER PROCEDURE raise_info(text) RENAME TO raise_info2;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('procedure_tests.raise_info2(text)');
|
||||||
|
verify_function_is_same_on_workers
|
||||||
|
------------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM run_command_on_workers($$CALL procedure_tests.raise_info('hello');$$) ORDER BY 1,2;
|
||||||
|
nodename | nodeport | success | result
|
||||||
|
-----------+----------+---------+----------------------------------------------------------------------
|
||||||
|
localhost | 57637 | f | ERROR: procedure procedure_tests.raise_info(unknown) does not exist
|
||||||
|
localhost | 57638 | f | ERROR: procedure procedure_tests.raise_info(unknown) does not exist
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT * FROM run_command_on_workers($$CALL procedure_tests.raise_info2('hello');$$) ORDER BY 1,2;
|
||||||
|
nodename | nodeport | success | result
|
||||||
|
-----------+----------+---------+--------
|
||||||
|
localhost | 57637 | t | CALL
|
||||||
|
localhost | 57638 | t | CALL
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
ALTER PROCEDURE raise_info2(text) RENAME TO raise_info;
|
||||||
|
-- change the owner of the function and verify the owner has been changed on the workers
|
||||||
|
ALTER PROCEDURE raise_info(text) OWNER TO procedureuser;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('procedure_tests.raise_info(text)');
|
||||||
|
verify_function_is_same_on_workers
|
||||||
|
------------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT run_command_on_workers($$
|
||||||
|
SELECT row(usename, nspname, proname)
|
||||||
|
FROM pg_proc
|
||||||
|
JOIN pg_user ON (usesysid = proowner)
|
||||||
|
JOIN pg_namespace ON (pg_namespace.oid = pronamespace)
|
||||||
|
WHERE proname = 'raise_info';
|
||||||
|
$$);
|
||||||
|
run_command_on_workers
|
||||||
|
------------------------------------------------------------------
|
||||||
|
(localhost,57637,t,"(procedureuser,procedure_tests,raise_info)")
|
||||||
|
(localhost,57638,t,"(procedureuser,procedure_tests,raise_info)")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- change the schema of the procedure and verify the old schema doesn't exist anymore while
|
||||||
|
-- the new schema has the function.
|
||||||
|
ALTER PROCEDURE raise_info(text) SET SCHEMA procedure_tests2;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('procedure_tests2.raise_info(text)');
|
||||||
|
verify_function_is_same_on_workers
|
||||||
|
------------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM run_command_on_workers($$CALL procedure_tests.raise_info('hello');$$) ORDER BY 1,2;
|
||||||
|
nodename | nodeport | success | result
|
||||||
|
-----------+----------+---------+----------------------------------------------------------------------
|
||||||
|
localhost | 57637 | f | ERROR: procedure procedure_tests.raise_info(unknown) does not exist
|
||||||
|
localhost | 57638 | f | ERROR: procedure procedure_tests.raise_info(unknown) does not exist
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT * FROM run_command_on_workers($$CALL procedure_tests2.raise_info('hello');$$) ORDER BY 1,2;
|
||||||
|
nodename | nodeport | success | result
|
||||||
|
-----------+----------+---------+--------
|
||||||
|
localhost | 57637 | t | CALL
|
||||||
|
localhost | 57638 | t | CALL
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
ALTER PROCEDURE procedure_tests2.raise_info(text) SET SCHEMA procedure_tests;
|
||||||
|
DROP PROCEDURE raise_info(text);
|
||||||
|
-- call should fail as procedure should have been dropped
|
||||||
|
SELECT * FROM run_command_on_workers($$CALL procedure_tests.raise_info('hello');$$) ORDER BY 1,2;
|
||||||
|
nodename | nodeport | success | result
|
||||||
|
-----------+----------+---------+----------------------------------------------------------------------
|
||||||
|
localhost | 57637 | f | ERROR: procedure procedure_tests.raise_info(unknown) does not exist
|
||||||
|
localhost | 57638 | f | ERROR: procedure procedure_tests.raise_info(unknown) does not exist
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SET client_min_messages TO error; -- suppress cascading objects dropping
|
||||||
|
DROP SCHEMA procedure_tests CASCADE;
|
||||||
|
SELECT run_command_on_workers($$DROP SCHEMA procedure_tests CASCADE;$$);
|
||||||
|
run_command_on_workers
|
||||||
|
-----------------------------------
|
||||||
|
(localhost,57637,t,"DROP SCHEMA")
|
||||||
|
(localhost,57638,t,"DROP SCHEMA")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
DROP SCHEMA procedure_tests2 CASCADE;
|
||||||
|
SELECT run_command_on_workers($$DROP SCHEMA procedure_tests2 CASCADE;$$);
|
||||||
|
run_command_on_workers
|
||||||
|
-----------------------------------
|
||||||
|
(localhost,57637,t,"DROP SCHEMA")
|
||||||
|
(localhost,57638,t,"DROP SCHEMA")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
DROP USER procedureuser;
|
||||||
|
SELECT run_command_on_workers($$DROP USER procedureuser;$$);
|
||||||
|
run_command_on_workers
|
||||||
|
---------------------------------
|
||||||
|
(localhost,57637,t,"DROP ROLE")
|
||||||
|
(localhost,57638,t,"DROP ROLE")
|
||||||
|
(2 rows)
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
SHOW server_version \gset
|
||||||
|
SELECT substring(:'server_version', '\d+')::int >= 11 AS server_verion_eleven_and_above
|
||||||
|
\gset
|
||||||
|
\if :server_verion_eleven_and_above
|
||||||
|
\else
|
||||||
|
\q
|
|
@ -29,11 +29,11 @@ step detector-dump-wait-edges:
|
||||||
|
|
||||||
waiting_transaction_numblocking_transaction_numblocking_transaction_waiting
|
waiting_transaction_numblocking_transaction_numblocking_transaction_waiting
|
||||||
|
|
||||||
276 275 f
|
274 273 f
|
||||||
transactionnumberwaitingtransactionnumbers
|
transactionnumberwaitingtransactionnumbers
|
||||||
|
|
||||||
275
|
273
|
||||||
276 275
|
274 273
|
||||||
step s1-abort:
|
step s1-abort:
|
||||||
ABORT;
|
ABORT;
|
||||||
|
|
||||||
|
@ -77,14 +77,14 @@ step detector-dump-wait-edges:
|
||||||
|
|
||||||
waiting_transaction_numblocking_transaction_numblocking_transaction_waiting
|
waiting_transaction_numblocking_transaction_numblocking_transaction_waiting
|
||||||
|
|
||||||
280 279 f
|
278 277 f
|
||||||
281 279 f
|
279 277 f
|
||||||
281 280 t
|
279 278 t
|
||||||
transactionnumberwaitingtransactionnumbers
|
transactionnumberwaitingtransactionnumbers
|
||||||
|
|
||||||
279
|
277
|
||||||
280 279
|
278 277
|
||||||
281 279,280
|
279 277,278
|
||||||
step s1-abort:
|
step s1-abort:
|
||||||
ABORT;
|
ABORT;
|
||||||
|
|
||||||
|
|
|
@ -1571,7 +1571,7 @@ master_remove_node
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
starting permutation: s1-print-distributed-objects s1-begin s1-add-worker s2-public-schema s2-distribute-function s1-commit s2-begin s2-commit s3-wait-for-metadata-sync s1-print-distributed-objects
|
starting permutation: s1-print-distributed-objects s1-begin s1-add-worker s2-public-schema s2-distribute-function s1-commit s2-begin s2-commit s3-wait-for-metadata-sync s2-print-distributed-objects
|
||||||
?column?
|
?column?
|
||||||
|
|
||||||
1
|
1
|
||||||
|
@ -1659,9 +1659,7 @@ step s3-wait-for-metadata-sync:
|
||||||
wait_until_metadata_sync
|
wait_until_metadata_sync
|
||||||
|
|
||||||
|
|
||||||
step s1-print-distributed-objects:
|
step s2-print-distributed-objects:
|
||||||
SELECT 1 FROM master_add_node('localhost', 57638);
|
|
||||||
|
|
||||||
-- print an overview of all distributed objects
|
-- print an overview of all distributed objects
|
||||||
SELECT pg_identify_object_as_address(classid, objid, objsubid) FROM citus.pg_dist_object ORDER BY 1;
|
SELECT pg_identify_object_as_address(classid, objid, objsubid) FROM citus.pg_dist_object ORDER BY 1;
|
||||||
|
|
||||||
|
@ -1677,11 +1675,6 @@ step s1-print-distributed-objects:
|
||||||
SELECT count(*) FROM pg_proc WHERE proname='add';
|
SELECT count(*) FROM pg_proc WHERE proname='add';
|
||||||
SELECT run_command_on_workers($$SELECT count(*) FROM pg_proc WHERE proname='add';$$);
|
SELECT run_command_on_workers($$SELECT count(*) FROM pg_proc WHERE proname='add';$$);
|
||||||
|
|
||||||
SELECT master_remove_node('localhost', 57638);
|
|
||||||
|
|
||||||
?column?
|
|
||||||
|
|
||||||
1
|
|
||||||
pg_identify_object_as_address
|
pg_identify_object_as_address
|
||||||
|
|
||||||
(function,"{public,add}","{integer,integer}")
|
(function,"{public,add}","{integer,integer}")
|
||||||
|
@ -1709,9 +1702,6 @@ run_command_on_workers
|
||||||
master_remove_node
|
master_remove_node
|
||||||
|
|
||||||
|
|
||||||
master_remove_node
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
starting permutation: s1-print-distributed-objects s1-begin s2-public-schema s2-distribute-function s2-begin s2-commit s3-wait-for-metadata-sync s1-add-worker s1-commit s3-wait-for-metadata-sync s2-print-distributed-objects
|
starting permutation: s1-print-distributed-objects s1-begin s2-public-schema s2-distribute-function s2-begin s2-commit s3-wait-for-metadata-sync s1-add-worker s1-commit s3-wait-for-metadata-sync s2-print-distributed-objects
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
-- DROP FUNCTION [ IF EXISTS ] name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ] [, ...]
|
-- DROP FUNCTION [ IF EXISTS ] name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ] [, ...]
|
||||||
-- [ CASCADE | RESTRICT ]
|
-- [ CASCADE | RESTRICT ]
|
||||||
SET citus.next_shard_id TO 20020000;
|
SET citus.next_shard_id TO 20020000;
|
||||||
|
SET citus.enable_ddl_propagation TO off;
|
||||||
CREATE SCHEMA function_tests;
|
CREATE SCHEMA function_tests;
|
||||||
SET search_path TO function_tests;
|
SET search_path TO function_tests;
|
||||||
SET citus.shard_count TO 4;
|
SET citus.shard_count TO 4;
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
SHOW server_version \gset
|
||||||
|
SELECT substring(:'server_version', '\d+')::int >= 11 AS server_verion_eleven_and_above
|
||||||
|
\gset
|
||||||
|
\if :server_verion_eleven_and_above
|
||||||
|
\else
|
||||||
|
\q
|
||||||
|
\endif
|
||||||
--
|
--
|
||||||
-- Regression tests for deparsing ALTER/DROP PROCEDURE Queries
|
-- Regression tests for deparsing ALTER/DROP PROCEDURE Queries
|
||||||
--
|
--
|
||||||
|
@ -26,6 +33,7 @@
|
||||||
-- For example CALLED ON NULL INPUT action is valid only for FUNCTIONS, but we still
|
-- For example CALLED ON NULL INPUT action is valid only for FUNCTIONS, but we still
|
||||||
-- allow deparsing them here.
|
-- allow deparsing them here.
|
||||||
SET citus.next_shard_id TO 20030000;
|
SET citus.next_shard_id TO 20030000;
|
||||||
|
SET citus.enable_ddl_propagation TO off;
|
||||||
CREATE SCHEMA procedure_tests;
|
CREATE SCHEMA procedure_tests;
|
||||||
SET search_path TO procedure_tests;
|
SET search_path TO procedure_tests;
|
||||||
SET citus.shard_count TO 4;
|
SET citus.shard_count TO 4;
|
||||||
|
@ -288,8 +296,9 @@ $cmd$);
|
||||||
(localhost,57638,t,"ALTER PROCEDURE")
|
(localhost,57638,t,"ALTER PROCEDURE")
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
|
-- rename and rename back to keep the nodes in sync
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
ALTER PROCEDURE raise_info RENAME TO summation
|
ALTER PROCEDURE raise_info RENAME TO summation;
|
||||||
$cmd$);
|
$cmd$);
|
||||||
deparse_and_run_on_workers
|
deparse_and_run_on_workers
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
@ -297,16 +306,34 @@ $cmd$);
|
||||||
(localhost,57638,t,"ALTER PROCEDURE")
|
(localhost,57638,t,"ALTER PROCEDURE")
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
CREATE ROLE PROCEDURE_role;
|
ALTER PROCEDURE raise_info RENAME TO summation;
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE summation RENAME TO raise_info;
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
---------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER PROCEDURE")
|
||||||
|
(localhost,57638,t,"ALTER PROCEDURE")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
ALTER PROCEDURE summation RENAME TO raise_info;
|
||||||
|
CREATE ROLE procedure_role;
|
||||||
NOTICE: not propagating CREATE ROLE/USER commands to worker nodes
|
NOTICE: not propagating CREATE ROLE/USER commands to worker nodes
|
||||||
HINT: Connect to worker nodes directly to manually create all necessary users and roles.
|
HINT: Connect to worker nodes directly to manually create all necessary users and roles.
|
||||||
|
SELECT run_command_on_workers($$CREATE ROLE procedure_role;$$);
|
||||||
|
run_command_on_workers
|
||||||
|
-----------------------------------
|
||||||
|
(localhost,57637,t,"CREATE ROLE")
|
||||||
|
(localhost,57638,t,"CREATE ROLE")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
ALTER PROCEDURE raise_info OWNER TO PROCEDURE_role
|
ALTER PROCEDURE raise_info OWNER TO procedure_role
|
||||||
$cmd$);
|
$cmd$);
|
||||||
deparse_and_run_on_workers
|
deparse_and_run_on_workers
|
||||||
----------------------------------------------------------------------
|
---------------------------------------
|
||||||
(localhost,57637,f,"ERROR: role ""procedure_role"" does not exist")
|
(localhost,57637,t,"ALTER PROCEDURE")
|
||||||
(localhost,57638,f,"ERROR: role ""procedure_role"" does not exist")
|
(localhost,57638,t,"ALTER PROCEDURE")
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
@ -318,26 +345,38 @@ $cmd$);
|
||||||
(localhost,57638,f,"ERROR: role ""missing_role"" does not exist")
|
(localhost,57638,f,"ERROR: role ""missing_role"" does not exist")
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
|
-- move schema and back to keep the nodes in sync
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
ALTER PROCEDURE raise_info SET SCHEMA public
|
ALTER PROCEDURE raise_info SET SCHEMA public;
|
||||||
$cmd$);
|
$cmd$);
|
||||||
deparse_and_run_on_workers
|
deparse_and_run_on_workers
|
||||||
-----------------------------------------------------------------------------------------
|
---------------------------------------
|
||||||
(localhost,57637,f,"ERROR: procedure procedure_tests.raise_info(text) does not exist")
|
(localhost,57637,t,"ALTER PROCEDURE")
|
||||||
(localhost,57638,f,"ERROR: procedure procedure_tests.raise_info(text) does not exist")
|
(localhost,57638,t,"ALTER PROCEDURE")
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
|
ALTER PROCEDURE raise_info SET SCHEMA public;
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE public.raise_info SET SCHEMA procedure_tests;
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
---------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER PROCEDURE")
|
||||||
|
(localhost,57638,t,"ALTER PROCEDURE")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
ALTER PROCEDURE public.raise_info SET SCHEMA procedure_tests;
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
ALTER PROCEDURE raise_info DEPENDS ON EXTENSION citus
|
ALTER PROCEDURE raise_info DEPENDS ON EXTENSION citus
|
||||||
$cmd$);
|
$cmd$);
|
||||||
deparse_and_run_on_workers
|
deparse_and_run_on_workers
|
||||||
-----------------------------------------------------------------------------------------
|
---------------------------------------
|
||||||
(localhost,57637,f,"ERROR: procedure procedure_tests.raise_info(text) does not exist")
|
(localhost,57637,t,"ALTER PROCEDURE")
|
||||||
(localhost,57638,f,"ERROR: procedure procedure_tests.raise_info(text) does not exist")
|
(localhost,57638,t,"ALTER PROCEDURE")
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
DROP PROCEDURE IF EXISTS raise_info(int,int);
|
DROP PROCEDURE raise_info(text);
|
||||||
$cmd$);
|
$cmd$);
|
||||||
deparse_and_run_on_workers
|
deparse_and_run_on_workers
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
|
@ -376,4 +415,11 @@ $cmd$);
|
||||||
-- clear objects
|
-- clear objects
|
||||||
SET client_min_messages TO WARNING; -- suppress cascading objects dropping
|
SET client_min_messages TO WARNING; -- suppress cascading objects dropping
|
||||||
DROP SCHEMA procedure_tests CASCADE;
|
DROP SCHEMA procedure_tests CASCADE;
|
||||||
DROP ROLE PROCEDURE_role;
|
DROP ROLE procedure_role;
|
||||||
|
SELECT run_command_on_workers($$DROP ROLE procedure_role;$$);
|
||||||
|
run_command_on_workers
|
||||||
|
---------------------------------
|
||||||
|
(localhost,57637,t,"DROP ROLE")
|
||||||
|
(localhost,57638,t,"DROP ROLE")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
|
|
@ -1,448 +1,6 @@
|
||||||
--
|
|
||||||
-- Regression tests for deparsing ALTER/DROP PROCEDURE Queries
|
|
||||||
--
|
|
||||||
-- ALTER PROCEDURE name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ]
|
|
||||||
-- action [ ... ] [ RESTRICT ]
|
|
||||||
-- ALTER PROCEDURE name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ]
|
|
||||||
-- RENAME TO new_name
|
|
||||||
-- ALTER PROCEDURE name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ]
|
|
||||||
-- OWNER TO { new_owner | CURRENT_USER | SESSION_USER }
|
|
||||||
-- ALTER PROCEDURE name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ]
|
|
||||||
-- SET SCHEMA new_schema
|
|
||||||
-- ALTER PROCEDURE name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ]
|
|
||||||
-- DEPENDS ON EXTENSION extension_name
|
|
||||||
-- where action is one of:
|
|
||||||
-- [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER
|
|
||||||
-- SET configuration_parameter { TO | = } { value | DEFAULT }
|
|
||||||
-- SET configuration_parameter FROM CURRENT
|
|
||||||
-- RESET configuration_parameter
|
|
||||||
-- RESET ALL
|
|
||||||
--
|
|
||||||
-- DROP PROCEDURE [ IF EXISTS ] name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ] [, ...]
|
|
||||||
-- [ CASCADE | RESTRICT ]
|
|
||||||
--
|
|
||||||
-- Please note that current deparser does not return errors on some invalid queries.
|
|
||||||
--
|
|
||||||
-- For example CALLED ON NULL INPUT action is valid only for FUNCTIONS, but we still
|
|
||||||
-- allow deparsing them here.
|
|
||||||
SET citus.next_shard_id TO 20030000;
|
|
||||||
CREATE SCHEMA procedure_tests;
|
|
||||||
SET search_path TO procedure_tests;
|
|
||||||
SET citus.shard_count TO 4;
|
|
||||||
SET client_min_messages TO INFO;
|
|
||||||
-- print whether we're using version > 10 to make version-specific tests clear
|
|
||||||
SHOW server_version \gset
|
SHOW server_version \gset
|
||||||
SELECT substring(:'server_version', '\d+')::int > 10 AS version_above_ten;
|
SELECT substring(:'server_version', '\d+')::int >= 11 AS server_verion_eleven_and_above
|
||||||
version_above_ten
|
\gset
|
||||||
-------------------
|
\if :server_verion_eleven_and_above
|
||||||
f
|
\else
|
||||||
(1 row)
|
\q
|
||||||
|
|
||||||
CREATE FUNCTION deparse_test(text)
|
|
||||||
RETURNS text
|
|
||||||
AS 'citus'
|
|
||||||
LANGUAGE C STRICT;
|
|
||||||
CREATE FUNCTION deparse_and_run_on_workers(text)
|
|
||||||
RETURNS SETOF record
|
|
||||||
AS $fnc$
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
$fnc$
|
|
||||||
LANGUAGE SQL;
|
|
||||||
-- Create a simple PROCEDURE and distribute it
|
|
||||||
CREATE OR REPLACE PROCEDURE raise_info(text)
|
|
||||||
LANGUAGE PLPGSQL AS $proc$
|
|
||||||
BEGIN
|
|
||||||
RAISE INFO 'information message %', $1;
|
|
||||||
END;
|
|
||||||
$proc$;
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 1: CREATE OR REPLACE PROCEDURE raise_info(text)
|
|
||||||
^
|
|
||||||
SELECT create_distributed_function('raise_info(text)');
|
|
||||||
ERROR: function "raise_info(text)" does not exist
|
|
||||||
LINE 1: SELECT create_distributed_function('raise_info(text)');
|
|
||||||
^
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
ALTER PROCEDURE raise_info CALLED ON NULL INPUT
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
ALTER PROCEDURE raise_info RETURNS NULL ON NULL INPUT
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
ALTER PROCEDURE raise_info STRICT
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
ALTER PROCEDURE raise_info IMMUTABLE
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
ALTER PROCEDURE raise_info STABLE
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
ALTER PROCEDURE raise_info VOLATILE
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
ALTER PROCEDURE raise_info LEAKPROOF
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
ALTER PROCEDURE raise_info NOT LEAKPROOF
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
ALTER PROCEDURE raise_info EXTERNAL SECURITY INVOKER
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
ALTER PROCEDURE raise_info SECURITY INVOKER
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
ALTER PROCEDURE raise_info EXTERNAL SECURITY DEFINER
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
ALTER PROCEDURE raise_info SECURITY DEFINER
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
ALTER PROCEDURE raise_info PARALLEL UNSAFE
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
ALTER PROCEDURE raise_info PARALLEL RESTRICTED
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
ALTER PROCEDURE raise_info PARALLEL SAFE
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
-- The COST/ROWS arguments should always be numeric
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
ALTER PROCEDURE raise_info COST 1234
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
ALTER PROCEDURE raise_info COST 1234.5
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
ALTER PROCEDURE raise_info ROWS 10
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
ALTER PROCEDURE raise_info ROWS 10.8
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
ALTER PROCEDURE raise_info SECURITY INVOKER SET client_min_messages TO warning;
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
ALTER PROCEDURE raise_info SET log_min_messages = ERROR
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
ALTER PROCEDURE raise_info SET log_min_messages TO DEFAULT
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
ALTER PROCEDURE raise_info SET log_min_messages FROM CURRENT
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
ALTER PROCEDURE raise_info RESET log_min_messages
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
ALTER PROCEDURE raise_info RESET ALL
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
ALTER PROCEDURE raise_info RENAME TO summation
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
CREATE ROLE PROCEDURE_role;
|
|
||||||
NOTICE: not propagating CREATE ROLE/USER commands to worker nodes
|
|
||||||
HINT: Connect to worker nodes directly to manually create all necessary users and roles.
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
ALTER PROCEDURE raise_info OWNER TO PROCEDURE_role
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
ALTER PROCEDURE raise_info OWNER TO missing_role
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
ALTER PROCEDURE raise_info SET SCHEMA public
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
ALTER PROCEDURE raise_info DEPENDS ON EXTENSION citus
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
DROP PROCEDURE IF EXISTS raise_info(int,int);
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
-- Check that an invalid PROCEDURE name is still parsed correctly
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
DROP PROCEDURE IF EXISTS missing_PROCEDURE(int, text);
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
DROP PROCEDURE IF EXISTS missing_schema.missing_PROCEDURE(int,float);
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
|
||||||
DROP PROCEDURE IF EXISTS missing_schema.missing_PROCEDURE(int,float) CASCADE;
|
|
||||||
$cmd$);
|
|
||||||
ERROR: syntax error at or near "PROCEDURE"
|
|
||||||
LINE 2: WITH deparsed_query AS ( SELECT deparse_test($1) qualifi...
|
|
||||||
^
|
|
||||||
QUERY:
|
|
||||||
WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query )
|
|
||||||
SELECT run_command_on_workers(qualified_query) FROM deparsed_query d
|
|
||||||
|
|
||||||
CONTEXT: SQL function "deparse_and_run_on_workers" statement 1
|
|
||||||
-- clear objects
|
|
||||||
SET client_min_messages TO WARNING; -- suppress cascading objects dropping
|
|
||||||
DROP SCHEMA procedure_tests CASCADE;
|
|
||||||
DROP ROLE PROCEDURE_role;
|
|
||||||
|
|
|
@ -54,6 +54,8 @@ WHERE pgd.refclassid = 'pg_extension'::regclass AND
|
||||||
-- DROP EXTENSION pre-created by the regression suite
|
-- DROP EXTENSION pre-created by the regression suite
|
||||||
DROP EXTENSION citus;
|
DROP EXTENSION citus;
|
||||||
\c
|
\c
|
||||||
|
-- these tests switch between citus versions and call ddl's that require pg_dist_object to be created
|
||||||
|
SET citus.enable_object_propagation TO 'false';
|
||||||
SET citus.enable_version_checks TO 'false';
|
SET citus.enable_version_checks TO 'false';
|
||||||
CREATE EXTENSION citus VERSION '7.0-1';
|
CREATE EXTENSION citus VERSION '7.0-1';
|
||||||
ALTER EXTENSION citus UPDATE TO '7.0-2';
|
ALTER EXTENSION citus UPDATE TO '7.0-2';
|
||||||
|
|
|
@ -150,3 +150,23 @@ BEGIN
|
||||||
from pg_proc, pg_dist_partition
|
from pg_proc, pg_dist_partition
|
||||||
where proname = procname and oid = objid and pg_dist_partition.logicalrelid = tablerelid;
|
where proname = procname and oid = objid and pg_dist_partition.logicalrelid = tablerelid;
|
||||||
END;$$;
|
END;$$;
|
||||||
|
-- helper function to verify the function of a coordinator is the same on all workers
|
||||||
|
CREATE OR REPLACE FUNCTION verify_function_is_same_on_workers(funcname text)
|
||||||
|
RETURNS bool
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $func$
|
||||||
|
DECLARE
|
||||||
|
coordinatorSql text;
|
||||||
|
workerSql text;
|
||||||
|
BEGIN
|
||||||
|
SELECT pg_get_functiondef(funcname::regprocedure) INTO coordinatorSql;
|
||||||
|
FOR workerSql IN SELECT result FROM run_command_on_workers('SELECT pg_get_functiondef(' || quote_literal(funcname) || '::regprocedure)') LOOP
|
||||||
|
IF workerSql != coordinatorSql THEN
|
||||||
|
RAISE INFO 'functions are different, coordinator:% worker:%', coordinatorSql, workerSql;
|
||||||
|
RETURN false;
|
||||||
|
END IF;
|
||||||
|
END LOOP;
|
||||||
|
|
||||||
|
RETURN true;
|
||||||
|
END;
|
||||||
|
$func$;
|
||||||
|
|
|
@ -282,6 +282,7 @@ test: ssl_by_default
|
||||||
# ---------
|
# ---------
|
||||||
test: distributed_types distributed_types_conflict disable_object_propagation
|
test: distributed_types distributed_types_conflict disable_object_propagation
|
||||||
test: distributed_functions
|
test: distributed_functions
|
||||||
|
test: distributed_procedure
|
||||||
|
|
||||||
# ---------
|
# ---------
|
||||||
# deparsing logic tests
|
# deparsing logic tests
|
||||||
|
|
|
@ -30,20 +30,6 @@ teardown
|
||||||
DROP TYPE IF EXISTS tt1 CASCADE;
|
DROP TYPE IF EXISTS tt1 CASCADE;
|
||||||
DROP FUNCTION IF EXISTS add(INT,INT) CASCADE;
|
DROP FUNCTION IF EXISTS add(INT,INT) CASCADE;
|
||||||
|
|
||||||
-- Remove the pg_dist_object record manually as we do not yet hook into DROP FUNC
|
|
||||||
-- queries. If the function does not exist, the casting to regprocedure fails.
|
|
||||||
DO
|
|
||||||
$do$
|
|
||||||
BEGIN
|
|
||||||
DELETE FROM citus.pg_dist_object WHERE objid = 'add(int,int)'::regprocedure;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN undefined_function THEN RETURN;
|
|
||||||
END;
|
|
||||||
$do$;
|
|
||||||
|
|
||||||
-- similarly drop the function in the workers manually
|
|
||||||
SELECT run_command_on_workers($$DROP FUNCTION IF EXISTS add(INT,INT) CASCADE;$$);
|
|
||||||
|
|
||||||
SELECT master_remove_node(nodename, nodeport) FROM pg_dist_node;
|
SELECT master_remove_node(nodename, nodeport) FROM pg_dist_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,7 +216,7 @@ permutation "s1-print-distributed-objects" "s1-begin" "s2-begin" "s2-create-sche
|
||||||
# s3-wait-for-metadata-sync step, we do "s2-begin" followed directly by
|
# s3-wait-for-metadata-sync step, we do "s2-begin" followed directly by
|
||||||
# "s2-commit", because "COMMIT" syncs the messages
|
# "s2-commit", because "COMMIT" syncs the messages
|
||||||
|
|
||||||
permutation "s1-print-distributed-objects" "s1-begin" "s1-add-worker" "s2-public-schema" "s2-distribute-function" "s1-commit" "s2-begin" "s2-commit" "s3-wait-for-metadata-sync" "s1-print-distributed-objects"
|
permutation "s1-print-distributed-objects" "s1-begin" "s1-add-worker" "s2-public-schema" "s2-distribute-function" "s1-commit" "s2-begin" "s2-commit" "s3-wait-for-metadata-sync" "s2-print-distributed-objects"
|
||||||
permutation "s1-print-distributed-objects" "s1-begin" "s2-public-schema" "s2-distribute-function" "s2-begin" "s2-commit" "s3-wait-for-metadata-sync" "s1-add-worker" "s1-commit" "s3-wait-for-metadata-sync" "s2-print-distributed-objects"
|
permutation "s1-print-distributed-objects" "s1-begin" "s2-public-schema" "s2-distribute-function" "s2-begin" "s2-commit" "s3-wait-for-metadata-sync" "s1-add-worker" "s1-commit" "s3-wait-for-metadata-sync" "s2-print-distributed-objects"
|
||||||
|
|
||||||
# we cannot run the following operations concurrently
|
# we cannot run the following operations concurrently
|
||||||
|
|
|
@ -4,6 +4,7 @@ CREATE USER functionuser;
|
||||||
SELECT run_command_on_workers($$CREATE USER functionuser;$$);
|
SELECT run_command_on_workers($$CREATE USER functionuser;$$);
|
||||||
|
|
||||||
CREATE SCHEMA function_tests AUTHORIZATION functionuser;
|
CREATE SCHEMA function_tests AUTHORIZATION functionuser;
|
||||||
|
CREATE SCHEMA function_tests2 AUTHORIZATION functionuser;
|
||||||
|
|
||||||
SET search_path TO function_tests;
|
SET search_path TO function_tests;
|
||||||
SET citus.shard_count TO 4;
|
SET citus.shard_count TO 4;
|
||||||
|
@ -62,6 +63,14 @@ CREATE FUNCTION add_mixed_param_names(integer, val1 integer) RETURNS integer
|
||||||
IMMUTABLE
|
IMMUTABLE
|
||||||
RETURNS NULL ON NULL INPUT;
|
RETURNS NULL ON NULL INPUT;
|
||||||
|
|
||||||
|
-- make sure to propagate ddl propagation after we have setup our functions, this will
|
||||||
|
-- allow alter statements to be propagated and keep the functions in sync across machines
|
||||||
|
SET citus.enable_ddl_propagation TO on;
|
||||||
|
|
||||||
|
-- functions are distributed by int arguments, when run in isolation it is not guaranteed a table actually exists.
|
||||||
|
CREATE TABLE colocation_table(id int);
|
||||||
|
SELECT create_distributed_table('colocation_table','id');
|
||||||
|
|
||||||
-- make sure that none of the active and primary nodes hasmetadata
|
-- make sure that none of the active and primary nodes hasmetadata
|
||||||
-- at the start of the test
|
-- at the start of the test
|
||||||
select bool_or(hasmetadata) from pg_dist_node WHERE isactive AND noderole = 'primary';
|
select bool_or(hasmetadata) from pg_dist_node WHERE isactive AND noderole = 'primary';
|
||||||
|
@ -84,6 +93,65 @@ SELECT * FROM run_command_on_workers('SELECT function_tests.dup(42);') ORDER BY
|
||||||
|
|
||||||
SELECT create_distributed_function('add(int,int)', '$1');
|
SELECT create_distributed_function('add(int,int)', '$1');
|
||||||
SELECT * FROM run_command_on_workers('SELECT function_tests.add(2,3);') ORDER BY 1,2;
|
SELECT * FROM run_command_on_workers('SELECT function_tests.add(2,3);') ORDER BY 1,2;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
|
||||||
|
|
||||||
|
-- testing alter statements for a distributed function
|
||||||
|
-- ROWS 5, untested because;
|
||||||
|
-- ERROR: ROWS is not applicable when function does not return a set
|
||||||
|
ALTER FUNCTION add(int,int) CALLED ON NULL INPUT IMMUTABLE SECURITY INVOKER PARALLEL UNSAFE LEAKPROOF COST 5;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
|
||||||
|
ALTER FUNCTION add(int,int) RETURNS NULL ON NULL INPUT STABLE SECURITY DEFINER PARALLEL RESTRICTED;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
|
||||||
|
ALTER FUNCTION add(int,int) STRICT VOLATILE PARALLEL SAFE;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
|
||||||
|
|
||||||
|
-- Test SET/RESET for alter function
|
||||||
|
ALTER FUNCTION add(int,int) SET client_min_messages TO warning;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
|
||||||
|
ALTER FUNCTION add(int,int) SET client_min_messages TO error;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
|
||||||
|
ALTER FUNCTION add(int,int) SET client_min_messages TO debug;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
|
||||||
|
ALTER FUNCTION add(int,int) RESET client_min_messages;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
|
||||||
|
|
||||||
|
-- SET ... FROM CURRENT is not supported, verify the query fails with a descriptive error irregardless of where in the action list the statement occurs
|
||||||
|
ALTER FUNCTION add(int,int) SET client_min_messages FROM CURRENT;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
|
||||||
|
ALTER FUNCTION add(int,int) RETURNS NULL ON NULL INPUT SET client_min_messages FROM CURRENT;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
|
||||||
|
ALTER FUNCTION add(int,int) SET client_min_messages FROM CURRENT SECURITY DEFINER;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
|
||||||
|
|
||||||
|
-- rename function and make sure the new name can be used on the workers while the old name can't
|
||||||
|
ALTER FUNCTION add(int,int) RENAME TO add2;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('function_tests.add2(int,int)');
|
||||||
|
SELECT * FROM run_command_on_workers('SELECT function_tests.add(2,3);') ORDER BY 1,2;
|
||||||
|
SELECT * FROM run_command_on_workers('SELECT function_tests.add2(2,3);') ORDER BY 1,2;
|
||||||
|
ALTER FUNCTION add2(int,int) RENAME TO add;
|
||||||
|
|
||||||
|
-- change the owner of the function and verify the owner has been changed on the workers
|
||||||
|
ALTER FUNCTION add(int,int) OWNER TO functionuser;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
|
||||||
|
SELECT run_command_on_workers($$
|
||||||
|
SELECT row(usename, nspname, proname)
|
||||||
|
FROM pg_proc
|
||||||
|
JOIN pg_user ON (usesysid = proowner)
|
||||||
|
JOIN pg_namespace ON (pg_namespace.oid = pronamespace)
|
||||||
|
WHERE proname = 'add';
|
||||||
|
$$);
|
||||||
|
|
||||||
|
-- change the schema of the function and verify the old schema doesn't exist anymore while
|
||||||
|
-- the new schema has the function.
|
||||||
|
ALTER FUNCTION add(int,int) SET SCHEMA function_tests2;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('function_tests2.add(int,int)');
|
||||||
|
SELECT * FROM run_command_on_workers('SELECT function_tests.add(2,3);') ORDER BY 1,2;
|
||||||
|
SELECT * FROM run_command_on_workers('SELECT function_tests2.add(2,3);') ORDER BY 1,2;
|
||||||
|
ALTER FUNCTION function_tests2.add(int,int) SET SCHEMA function_tests;
|
||||||
|
|
||||||
|
DROP FUNCTION add(int,int);
|
||||||
|
-- call should fail as function should have been dropped
|
||||||
|
SELECT * FROM run_command_on_workers('SELECT function_tests.add(2,3);') ORDER BY 1,2;
|
||||||
|
|
||||||
-- postgres doesn't accept parameter names in the regprocedure input
|
-- postgres doesn't accept parameter names in the regprocedure input
|
||||||
SELECT create_distributed_function('add_with_param_names(val1 int, int)', 'val1');
|
SELECT create_distributed_function('add_with_param_names(val1 int, int)', 'val1');
|
||||||
|
@ -215,6 +283,7 @@ SELECT stop_metadata_sync_to_node(nodename,nodeport) FROM pg_dist_node WHERE isa
|
||||||
|
|
||||||
SET client_min_messages TO error; -- suppress cascading objects dropping
|
SET client_min_messages TO error; -- suppress cascading objects dropping
|
||||||
DROP SCHEMA function_tests CASCADE;
|
DROP SCHEMA function_tests CASCADE;
|
||||||
|
DROP SCHEMA function_tests2 CASCADE;
|
||||||
|
|
||||||
-- This is hacky, but we should clean-up the resources as below
|
-- This is hacky, but we should clean-up the resources as below
|
||||||
|
|
||||||
|
@ -224,6 +293,7 @@ UPDATE pg_dist_local_group SET groupid = 0;
|
||||||
SELECT worker_drop_distributed_table(logicalrelid::text) FROM pg_dist_partition WHERE logicalrelid::text ILIKE '%replicated_table_func_test%';
|
SELECT worker_drop_distributed_table(logicalrelid::text) FROM pg_dist_partition WHERE logicalrelid::text ILIKE '%replicated_table_func_test%';
|
||||||
TRUNCATE pg_dist_node;
|
TRUNCATE pg_dist_node;
|
||||||
DROP SCHEMA function_tests CASCADE;
|
DROP SCHEMA function_tests CASCADE;
|
||||||
|
DROP SCHEMA function_tests2 CASCADE;
|
||||||
|
|
||||||
\c - - - :worker_2_port
|
\c - - - :worker_2_port
|
||||||
SET client_min_messages TO error; -- suppress cascading objects dropping
|
SET client_min_messages TO error; -- suppress cascading objects dropping
|
||||||
|
@ -231,6 +301,7 @@ UPDATE pg_dist_local_group SET groupid = 0;
|
||||||
SELECT worker_drop_distributed_table(logicalrelid::text) FROM pg_dist_partition WHERE logicalrelid::text ILIKE '%replicated_table_func_test%';
|
SELECT worker_drop_distributed_table(logicalrelid::text) FROM pg_dist_partition WHERE logicalrelid::text ILIKE '%replicated_table_func_test%';
|
||||||
TRUNCATE pg_dist_node;
|
TRUNCATE pg_dist_node;
|
||||||
DROP SCHEMA function_tests CASCADE;
|
DROP SCHEMA function_tests CASCADE;
|
||||||
|
DROP SCHEMA function_tests2 CASCADE;
|
||||||
|
|
||||||
\c - - - :master_port
|
\c - - - :master_port
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
SHOW server_version \gset
|
||||||
|
SELECT substring(:'server_version', '\d+')::int >= 11 AS server_verion_eleven_and_above
|
||||||
|
\gset
|
||||||
|
\if :server_verion_eleven_and_above
|
||||||
|
\else
|
||||||
|
\q
|
||||||
|
\endif
|
||||||
|
|
||||||
|
SET citus.next_shard_id TO 20030000;
|
||||||
|
|
||||||
|
CREATE USER procedureuser;
|
||||||
|
SELECT run_command_on_workers($$CREATE USER procedureuser;$$);
|
||||||
|
|
||||||
|
CREATE SCHEMA procedure_tests AUTHORIZATION procedureuser;
|
||||||
|
CREATE SCHEMA procedure_tests2 AUTHORIZATION procedureuser;
|
||||||
|
|
||||||
|
SET search_path TO procedure_tests;
|
||||||
|
SET citus.shard_count TO 4;
|
||||||
|
|
||||||
|
-- Create and distribute a simple function
|
||||||
|
CREATE OR REPLACE PROCEDURE raise_info(text)
|
||||||
|
LANGUAGE PLPGSQL AS $proc$
|
||||||
|
BEGIN
|
||||||
|
RAISE INFO 'information message %', $1;
|
||||||
|
END;
|
||||||
|
$proc$;
|
||||||
|
|
||||||
|
-- procedures are distributed by text arguments, when run in isolation it is not guaranteed a table actually exists.
|
||||||
|
CREATE TABLE colocation_table(id text);
|
||||||
|
SELECT create_distributed_table('colocation_table','id');
|
||||||
|
|
||||||
|
SELECT create_distributed_function('raise_info(text)', '$1');
|
||||||
|
SELECT * FROM run_command_on_workers($$CALL procedure_tests.raise_info('hello');$$) ORDER BY 1,2;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('procedure_tests.raise_info(text)');
|
||||||
|
|
||||||
|
-- testing alter statements for a distributed function
|
||||||
|
-- ROWS 5, untested because;
|
||||||
|
-- ERROR: ROWS is not applicable when function does not return a set
|
||||||
|
ALTER PROCEDURE raise_info(text) SECURITY INVOKER;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('procedure_tests.raise_info(text)');
|
||||||
|
ALTER PROCEDURE raise_info(text) SECURITY DEFINER;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('procedure_tests.raise_info(text)');
|
||||||
|
|
||||||
|
-- Test SET/RESET for alter procedure
|
||||||
|
ALTER PROCEDURE raise_info(text) SET client_min_messages TO warning;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('procedure_tests.raise_info(text)');
|
||||||
|
ALTER PROCEDURE raise_info(text) SET client_min_messages TO error;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('procedure_tests.raise_info(text)');
|
||||||
|
ALTER PROCEDURE raise_info(text) SET client_min_messages TO debug;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('procedure_tests.raise_info(text)');
|
||||||
|
ALTER PROCEDURE raise_info(text) RESET client_min_messages;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('procedure_tests.raise_info(text)');
|
||||||
|
|
||||||
|
-- rename function and make sure the new name can be used on the workers while the old name can't
|
||||||
|
ALTER PROCEDURE raise_info(text) RENAME TO raise_info2;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('procedure_tests.raise_info2(text)');
|
||||||
|
SELECT * FROM run_command_on_workers($$CALL procedure_tests.raise_info('hello');$$) ORDER BY 1,2;
|
||||||
|
SELECT * FROM run_command_on_workers($$CALL procedure_tests.raise_info2('hello');$$) ORDER BY 1,2;
|
||||||
|
ALTER PROCEDURE raise_info2(text) RENAME TO raise_info;
|
||||||
|
|
||||||
|
-- change the owner of the function and verify the owner has been changed on the workers
|
||||||
|
ALTER PROCEDURE raise_info(text) OWNER TO procedureuser;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('procedure_tests.raise_info(text)');
|
||||||
|
SELECT run_command_on_workers($$
|
||||||
|
SELECT row(usename, nspname, proname)
|
||||||
|
FROM pg_proc
|
||||||
|
JOIN pg_user ON (usesysid = proowner)
|
||||||
|
JOIN pg_namespace ON (pg_namespace.oid = pronamespace)
|
||||||
|
WHERE proname = 'raise_info';
|
||||||
|
$$);
|
||||||
|
|
||||||
|
-- change the schema of the procedure and verify the old schema doesn't exist anymore while
|
||||||
|
-- the new schema has the function.
|
||||||
|
ALTER PROCEDURE raise_info(text) SET SCHEMA procedure_tests2;
|
||||||
|
SELECT public.verify_function_is_same_on_workers('procedure_tests2.raise_info(text)');
|
||||||
|
SELECT * FROM run_command_on_workers($$CALL procedure_tests.raise_info('hello');$$) ORDER BY 1,2;
|
||||||
|
SELECT * FROM run_command_on_workers($$CALL procedure_tests2.raise_info('hello');$$) ORDER BY 1,2;
|
||||||
|
ALTER PROCEDURE procedure_tests2.raise_info(text) SET SCHEMA procedure_tests;
|
||||||
|
|
||||||
|
DROP PROCEDURE raise_info(text);
|
||||||
|
-- call should fail as procedure should have been dropped
|
||||||
|
SELECT * FROM run_command_on_workers($$CALL procedure_tests.raise_info('hello');$$) ORDER BY 1,2;
|
||||||
|
|
||||||
|
SET client_min_messages TO error; -- suppress cascading objects dropping
|
||||||
|
DROP SCHEMA procedure_tests CASCADE;
|
||||||
|
SELECT run_command_on_workers($$DROP SCHEMA procedure_tests CASCADE;$$);
|
||||||
|
DROP SCHEMA procedure_tests2 CASCADE;
|
||||||
|
SELECT run_command_on_workers($$DROP SCHEMA procedure_tests2 CASCADE;$$);
|
||||||
|
DROP USER procedureuser;
|
||||||
|
SELECT run_command_on_workers($$DROP USER procedureuser;$$);
|
|
@ -32,6 +32,7 @@
|
||||||
-- [ CASCADE | RESTRICT ]
|
-- [ CASCADE | RESTRICT ]
|
||||||
|
|
||||||
SET citus.next_shard_id TO 20020000;
|
SET citus.next_shard_id TO 20020000;
|
||||||
|
SET citus.enable_ddl_propagation TO off;
|
||||||
|
|
||||||
CREATE SCHEMA function_tests;
|
CREATE SCHEMA function_tests;
|
||||||
SET search_path TO function_tests;
|
SET search_path TO function_tests;
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
SHOW server_version \gset
|
||||||
|
SELECT substring(:'server_version', '\d+')::int >= 11 AS server_verion_eleven_and_above
|
||||||
|
\gset
|
||||||
|
\if :server_verion_eleven_and_above
|
||||||
|
\else
|
||||||
|
\q
|
||||||
|
\endif
|
||||||
--
|
--
|
||||||
-- Regression tests for deparsing ALTER/DROP PROCEDURE Queries
|
-- Regression tests for deparsing ALTER/DROP PROCEDURE Queries
|
||||||
--
|
--
|
||||||
|
@ -29,6 +36,7 @@
|
||||||
-- allow deparsing them here.
|
-- allow deparsing them here.
|
||||||
|
|
||||||
SET citus.next_shard_id TO 20030000;
|
SET citus.next_shard_id TO 20030000;
|
||||||
|
SET citus.enable_ddl_propagation TO off;
|
||||||
|
|
||||||
CREATE SCHEMA procedure_tests;
|
CREATE SCHEMA procedure_tests;
|
||||||
SET search_path TO procedure_tests;
|
SET search_path TO procedure_tests;
|
||||||
|
@ -167,30 +175,43 @@ SELECT deparse_and_run_on_workers($cmd$
|
||||||
ALTER PROCEDURE raise_info RESET ALL
|
ALTER PROCEDURE raise_info RESET ALL
|
||||||
$cmd$);
|
$cmd$);
|
||||||
|
|
||||||
|
-- rename and rename back to keep the nodes in sync
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
ALTER PROCEDURE raise_info RENAME TO summation
|
ALTER PROCEDURE raise_info RENAME TO summation;
|
||||||
$cmd$);
|
$cmd$);
|
||||||
|
ALTER PROCEDURE raise_info RENAME TO summation;
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE summation RENAME TO raise_info;
|
||||||
|
$cmd$);
|
||||||
|
ALTER PROCEDURE summation RENAME TO raise_info;
|
||||||
|
|
||||||
CREATE ROLE PROCEDURE_role;
|
CREATE ROLE procedure_role;
|
||||||
|
SELECT run_command_on_workers($$CREATE ROLE procedure_role;$$);
|
||||||
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
ALTER PROCEDURE raise_info OWNER TO PROCEDURE_role
|
ALTER PROCEDURE raise_info OWNER TO procedure_role
|
||||||
$cmd$);
|
$cmd$);
|
||||||
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
ALTER PROCEDURE raise_info OWNER TO missing_role
|
ALTER PROCEDURE raise_info OWNER TO missing_role
|
||||||
$cmd$);
|
$cmd$);
|
||||||
|
|
||||||
|
-- move schema and back to keep the nodes in sync
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
ALTER PROCEDURE raise_info SET SCHEMA public
|
ALTER PROCEDURE raise_info SET SCHEMA public;
|
||||||
$cmd$);
|
$cmd$);
|
||||||
|
ALTER PROCEDURE raise_info SET SCHEMA public;
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE public.raise_info SET SCHEMA procedure_tests;
|
||||||
|
$cmd$);
|
||||||
|
ALTER PROCEDURE public.raise_info SET SCHEMA procedure_tests;
|
||||||
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
ALTER PROCEDURE raise_info DEPENDS ON EXTENSION citus
|
ALTER PROCEDURE raise_info DEPENDS ON EXTENSION citus
|
||||||
$cmd$);
|
$cmd$);
|
||||||
|
|
||||||
SELECT deparse_and_run_on_workers($cmd$
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
DROP PROCEDURE IF EXISTS raise_info(int,int);
|
DROP PROCEDURE raise_info(text);
|
||||||
$cmd$);
|
$cmd$);
|
||||||
|
|
||||||
-- Check that an invalid PROCEDURE name is still parsed correctly
|
-- Check that an invalid PROCEDURE name is still parsed correctly
|
||||||
|
@ -209,4 +230,5 @@ $cmd$);
|
||||||
-- clear objects
|
-- clear objects
|
||||||
SET client_min_messages TO WARNING; -- suppress cascading objects dropping
|
SET client_min_messages TO WARNING; -- suppress cascading objects dropping
|
||||||
DROP SCHEMA procedure_tests CASCADE;
|
DROP SCHEMA procedure_tests CASCADE;
|
||||||
DROP ROLE PROCEDURE_role;
|
DROP ROLE procedure_role;
|
||||||
|
SELECT run_command_on_workers($$DROP ROLE procedure_role;$$);
|
||||||
|
|
|
@ -53,6 +53,9 @@ WHERE pgd.refclassid = 'pg_extension'::regclass AND
|
||||||
DROP EXTENSION citus;
|
DROP EXTENSION citus;
|
||||||
\c
|
\c
|
||||||
|
|
||||||
|
-- these tests switch between citus versions and call ddl's that require pg_dist_object to be created
|
||||||
|
SET citus.enable_object_propagation TO 'false';
|
||||||
|
|
||||||
SET citus.enable_version_checks TO 'false';
|
SET citus.enable_version_checks TO 'false';
|
||||||
|
|
||||||
CREATE EXTENSION citus VERSION '7.0-1';
|
CREATE EXTENSION citus VERSION '7.0-1';
|
||||||
|
|
|
@ -152,4 +152,23 @@ BEGIN
|
||||||
where proname = procname and oid = objid and pg_dist_partition.logicalrelid = tablerelid;
|
where proname = procname and oid = objid and pg_dist_partition.logicalrelid = tablerelid;
|
||||||
END;$$;
|
END;$$;
|
||||||
|
|
||||||
|
-- helper function to verify the function of a coordinator is the same on all workers
|
||||||
|
CREATE OR REPLACE FUNCTION verify_function_is_same_on_workers(funcname text)
|
||||||
|
RETURNS bool
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $func$
|
||||||
|
DECLARE
|
||||||
|
coordinatorSql text;
|
||||||
|
workerSql text;
|
||||||
|
BEGIN
|
||||||
|
SELECT pg_get_functiondef(funcname::regprocedure) INTO coordinatorSql;
|
||||||
|
FOR workerSql IN SELECT result FROM run_command_on_workers('SELECT pg_get_functiondef(' || quote_literal(funcname) || '::regprocedure)') LOOP
|
||||||
|
IF workerSql != coordinatorSql THEN
|
||||||
|
RAISE INFO 'functions are different, coordinator:% worker:%', coordinatorSql, workerSql;
|
||||||
|
RETURN false;
|
||||||
|
END IF;
|
||||||
|
END LOOP;
|
||||||
|
|
||||||
|
RETURN true;
|
||||||
|
END;
|
||||||
|
$func$;
|
||||||
|
|
Loading…
Reference in New Issue