mirror of https://github.com/citusdata/citus.git
Deparsing and qualifiying for FUNCTION/PROCEDURE statements (#3014)
This PR aims to add all the necessary logic to qualify and deparse all possible `{ALTER|DROP} .. {FUNCTION|PROCEDURE}` queries. As Procedures are introduced in PG11, the code contains many PG version checks. I tried my best to make it easy to clean up once we drop PG10 support. Here are some caveats: - I assumed that the parse tree is a valid one. There are some queries that are not allowed, but still are parsed successfully by postgres planner. Such queries will result in errors in execution time. (e.g. `ALTER PROCEDURE p STRICT` -> `STRICT` action is valid for functions but not procedures. Postgres decides to parse them nevertheless.)pull/2899/head
parent
28acab9d02
commit
66b9f2e887
|
@ -23,7 +23,7 @@ static const char * DeparseRenameStmt(RenameStmt *stmt);
|
||||||
static const char * DeparseRenameAttributeStmt(RenameStmt *stmt);
|
static const char * DeparseRenameAttributeStmt(RenameStmt *stmt);
|
||||||
static const char * DeparseAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt);
|
static const char * DeparseAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt);
|
||||||
static const char * DeparseAlterOwnerStmt(AlterOwnerStmt *stmt);
|
static const char * DeparseAlterOwnerStmt(AlterOwnerStmt *stmt);
|
||||||
|
static const char * DeparseAlterObjectDependsStmt(AlterObjectDependsStmt *stmt);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* DeparseTreeNode aims to be the inverse of postgres' ParseTreeNode. Currently with
|
* DeparseTreeNode aims to be the inverse of postgres' ParseTreeNode. Currently with
|
||||||
|
@ -34,6 +34,12 @@ static const char * DeparseAlterOwnerStmt(AlterOwnerStmt *stmt);
|
||||||
* - CREATE TYPE
|
* - CREATE TYPE
|
||||||
* - ALTER TYPE
|
* - ALTER TYPE
|
||||||
* - DROP TYPE
|
* - DROP TYPE
|
||||||
|
*
|
||||||
|
* - ALTER FUNCTION
|
||||||
|
* - DROP FUNCTION
|
||||||
|
*
|
||||||
|
* - ALTER PROCEDURE
|
||||||
|
* - DROP PROCEDURE
|
||||||
*/
|
*/
|
||||||
const char *
|
const char *
|
||||||
DeparseTreeNode(Node *stmt)
|
DeparseTreeNode(Node *stmt)
|
||||||
|
@ -65,6 +71,11 @@ DeparseTreeNode(Node *stmt)
|
||||||
return DeparseAlterEnumStmt(castNode(AlterEnumStmt, stmt));
|
return DeparseAlterEnumStmt(castNode(AlterEnumStmt, stmt));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case T_AlterFunctionStmt:
|
||||||
|
{
|
||||||
|
return DeparseAlterFunctionStmt(castNode(AlterFunctionStmt, stmt));
|
||||||
|
}
|
||||||
|
|
||||||
case T_RenameStmt:
|
case T_RenameStmt:
|
||||||
{
|
{
|
||||||
return DeparseRenameStmt(castNode(RenameStmt, stmt));
|
return DeparseRenameStmt(castNode(RenameStmt, stmt));
|
||||||
|
@ -80,6 +91,11 @@ DeparseTreeNode(Node *stmt)
|
||||||
return DeparseAlterOwnerStmt(castNode(AlterOwnerStmt, stmt));
|
return DeparseAlterOwnerStmt(castNode(AlterOwnerStmt, stmt));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case T_AlterObjectDependsStmt:
|
||||||
|
{
|
||||||
|
return DeparseAlterObjectDependsStmt(castNode(AlterObjectDependsStmt, stmt));
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
ereport(ERROR, (errmsg("unsupported statement for deparsing")));
|
ereport(ERROR, (errmsg("unsupported statement for deparsing")));
|
||||||
|
@ -88,6 +104,12 @@ DeparseTreeNode(Node *stmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DeparseDropStmt aims to deparse DROP statements.
|
||||||
|
*
|
||||||
|
* Currently with limited support. Check support before using, and add support for new
|
||||||
|
* statements as required.
|
||||||
|
*/
|
||||||
static const char *
|
static const char *
|
||||||
DeparseDropStmt(DropStmt *stmt)
|
DeparseDropStmt(DropStmt *stmt)
|
||||||
{
|
{
|
||||||
|
@ -98,6 +120,14 @@ DeparseDropStmt(DropStmt *stmt)
|
||||||
return DeparseDropTypeStmt(stmt);
|
return DeparseDropTypeStmt(stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM >= 110000
|
||||||
|
case OBJECT_PROCEDURE:
|
||||||
|
#endif
|
||||||
|
case OBJECT_FUNCTION:
|
||||||
|
{
|
||||||
|
return DeparseDropFunctionStmt(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
ereport(ERROR, (errmsg("unsupported drop statement for deparsing")));
|
ereport(ERROR, (errmsg("unsupported drop statement for deparsing")));
|
||||||
|
@ -106,6 +136,15 @@ DeparseDropStmt(DropStmt *stmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DeparseAlterTableStmt deparses an AlterTableStmt to its SQL command.
|
||||||
|
* Contrary to its name these statements are covering not only ALTER TABLE ...
|
||||||
|
* statements but are used for almost all relation-esque objects in postgres,
|
||||||
|
* including but not limited to, Tables, Types, ...
|
||||||
|
*
|
||||||
|
* Currently with limited support. Check support before using, and add support for new
|
||||||
|
* statements as required.
|
||||||
|
*/
|
||||||
static const char *
|
static const char *
|
||||||
DeparseAlterTableStmt(AlterTableStmt *stmt)
|
DeparseAlterTableStmt(AlterTableStmt *stmt)
|
||||||
{
|
{
|
||||||
|
@ -124,6 +163,16 @@ DeparseAlterTableStmt(AlterTableStmt *stmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DeparseRenameStmt deparses an RenameStmt to its SQL command.
|
||||||
|
* Contrary to its name these statements are not covering all ALTER .. RENAME
|
||||||
|
* statements.
|
||||||
|
*
|
||||||
|
* e.g. ALTER TYPE name RENAME VALUE old TO new is an AlterEnumStmt
|
||||||
|
*
|
||||||
|
* Currently with limited support. Check support before using, and add support for new
|
||||||
|
* statements as required.
|
||||||
|
*/
|
||||||
static const char *
|
static const char *
|
||||||
DeparseRenameStmt(RenameStmt *stmt)
|
DeparseRenameStmt(RenameStmt *stmt)
|
||||||
{
|
{
|
||||||
|
@ -139,6 +188,14 @@ DeparseRenameStmt(RenameStmt *stmt)
|
||||||
return DeparseRenameAttributeStmt(stmt);
|
return DeparseRenameAttributeStmt(stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM >= 110000
|
||||||
|
case OBJECT_PROCEDURE:
|
||||||
|
#endif
|
||||||
|
case OBJECT_FUNCTION:
|
||||||
|
{
|
||||||
|
return DeparseRenameFunctionStmt(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
ereport(ERROR, (errmsg("unsupported rename statement for deparsing")));
|
ereport(ERROR, (errmsg("unsupported rename statement for deparsing")));
|
||||||
|
@ -168,6 +225,14 @@ DeparseRenameAttributeStmt(RenameStmt *stmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DeparseAlterObjectSchemaStmt aims to deparse
|
||||||
|
* ALTER .. SET SCHEMA ..
|
||||||
|
* statements.
|
||||||
|
*
|
||||||
|
* Currently with limited support. Check support before using, and add support for new
|
||||||
|
* statements as required.
|
||||||
|
*/
|
||||||
static const char *
|
static const char *
|
||||||
DeparseAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
|
DeparseAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
|
||||||
{
|
{
|
||||||
|
@ -178,6 +243,14 @@ DeparseAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
|
||||||
return DeparseAlterTypeSchemaStmt(stmt);
|
return DeparseAlterTypeSchemaStmt(stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM >= 110000
|
||||||
|
case OBJECT_PROCEDURE:
|
||||||
|
#endif
|
||||||
|
case OBJECT_FUNCTION:
|
||||||
|
{
|
||||||
|
return DeparseAlterFunctionSchemaStmt(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
ereport(ERROR, (errmsg("unsupported rename statement for deparsing")));
|
ereport(ERROR, (errmsg("unsupported rename statement for deparsing")));
|
||||||
|
@ -186,6 +259,14 @@ DeparseAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DeparseAlterOwnerStmt aims to deparse
|
||||||
|
* ALTER .. OWNER TO ..
|
||||||
|
* statements.
|
||||||
|
*
|
||||||
|
* Currently with limited support. Check support before using, and add support for new
|
||||||
|
* statements as required.
|
||||||
|
*/
|
||||||
static const char *
|
static const char *
|
||||||
DeparseAlterOwnerStmt(AlterOwnerStmt *stmt)
|
DeparseAlterOwnerStmt(AlterOwnerStmt *stmt)
|
||||||
{
|
{
|
||||||
|
@ -196,9 +277,46 @@ DeparseAlterOwnerStmt(AlterOwnerStmt *stmt)
|
||||||
return DeparseAlterTypeOwnerStmt(stmt);
|
return DeparseAlterTypeOwnerStmt(stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM >= 110000
|
||||||
|
case OBJECT_PROCEDURE:
|
||||||
|
#endif
|
||||||
|
case OBJECT_FUNCTION:
|
||||||
|
{
|
||||||
|
return DeparseAlterFunctionOwnerStmt(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
ereport(ERROR, (errmsg("unsupported alter owner statement for deparsing")));
|
ereport(ERROR, (errmsg("unsupported alter owner statement for deparsing")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DeparseAlterObjectDependsStmt aims to deparse
|
||||||
|
* ALTER .. DEPENDS ON EXTENSION ..
|
||||||
|
* statements.
|
||||||
|
*
|
||||||
|
* Currently with limited support. Check support before using, and add support for new
|
||||||
|
* statements as required.
|
||||||
|
*/
|
||||||
|
static const char *
|
||||||
|
DeparseAlterObjectDependsStmt(AlterObjectDependsStmt *stmt)
|
||||||
|
{
|
||||||
|
switch (stmt->objectType)
|
||||||
|
{
|
||||||
|
#if PG_VERSION_NUM >= 110000
|
||||||
|
case OBJECT_PROCEDURE:
|
||||||
|
#endif
|
||||||
|
case OBJECT_FUNCTION:
|
||||||
|
{
|
||||||
|
return DeparseAlterFunctionDependsStmt(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
ereport(ERROR, (errmsg("unsupported alter depends statement for deparsing")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,674 @@
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* deparse_function_stmts.c
|
||||||
|
*
|
||||||
|
* All routines to deparse function and procedure statements.
|
||||||
|
* This file contains all entry points specific for function and procedure statement
|
||||||
|
* deparsing
|
||||||
|
*
|
||||||
|
* Functions that could move later are AppendDefElem, AppendDefElemStrict, etc. These
|
||||||
|
* should be reused across multiple statements and should live in their own deparse
|
||||||
|
* file.
|
||||||
|
*
|
||||||
|
* Copyright (c), Citus Data, Inc.
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "access/htup_details.h"
|
||||||
|
#include "catalog/namespace.h"
|
||||||
|
#include "catalog/pg_proc.h"
|
||||||
|
#include "commands/defrem.h"
|
||||||
|
#include "distributed/citus_ruleutils.h"
|
||||||
|
#include "distributed/commands.h"
|
||||||
|
#include "distributed/deparser.h"
|
||||||
|
#include "distributed/version_compat.h"
|
||||||
|
#include "lib/stringinfo.h"
|
||||||
|
#include "nodes/makefuncs.h"
|
||||||
|
#include "nodes/nodes.h"
|
||||||
|
#include "nodes/value.h"
|
||||||
|
#include "parser/parse_func.h"
|
||||||
|
#include "parser/parse_type.h"
|
||||||
|
#include "utils/builtins.h"
|
||||||
|
#include "utils/fmgroids.h"
|
||||||
|
#include "utils/fmgrprotos.h"
|
||||||
|
#include "utils/guc.h"
|
||||||
|
#include "utils/lsyscache.h"
|
||||||
|
#include "utils/memutils.h"
|
||||||
|
#include "utils/syscache.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* forward declaration for deparse functions */
|
||||||
|
static void AppendAlterFunctionStmt(StringInfo buf, AlterFunctionStmt *stmt);
|
||||||
|
static void AppendDropFunctionStmt(StringInfo buf, DropStmt *stmt);
|
||||||
|
static void AppendFunctionName(StringInfo buf, ObjectWithArgs *func, ObjectType objtype);
|
||||||
|
static void AppendFunctionNameList(StringInfo buf, List *objects, ObjectType objtype);
|
||||||
|
|
||||||
|
static void AppendDefElem(StringInfo buf, DefElem *def);
|
||||||
|
static void AppendDefElemStrict(StringInfo buf, DefElem *def);
|
||||||
|
static void AppendDefElemVolatility(StringInfo buf, DefElem *def);
|
||||||
|
static void AppendDefElemLeakproof(StringInfo buf, DefElem *def);
|
||||||
|
static void AppendDefElemSecurity(StringInfo buf, DefElem *def);
|
||||||
|
static void AppendDefElemParallel(StringInfo buf, DefElem *def);
|
||||||
|
static void AppendDefElemCost(StringInfo buf, DefElem *def);
|
||||||
|
static void AppendDefElemRows(StringInfo buf, DefElem *def);
|
||||||
|
static void AppendDefElemSet(StringInfo buf, DefElem *def);
|
||||||
|
|
||||||
|
static void AppendRenameFunctionStmt(StringInfo buf, RenameStmt *stmt);
|
||||||
|
static void AppendAlterFunctionSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt);
|
||||||
|
static void AppendAlterFunctionOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt);
|
||||||
|
static void AppendAlterFunctionDependsStmt(StringInfo buf, AlterObjectDependsStmt *stmt);
|
||||||
|
|
||||||
|
static char * CopyAndConvertToUpperCase(const char *str);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DeparseAlterFunctionStmt builds and returns a string representing the AlterFunctionStmt
|
||||||
|
*/
|
||||||
|
const char *
|
||||||
|
DeparseAlterFunctionStmt(AlterFunctionStmt *stmt)
|
||||||
|
{
|
||||||
|
StringInfoData str = { 0 };
|
||||||
|
initStringInfo(&str);
|
||||||
|
|
||||||
|
AppendAlterFunctionStmt(&str, stmt);
|
||||||
|
|
||||||
|
return str.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AppendAlterFunctionStmt appends a string representing the AlterFunctionStmt to a buffer
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AppendAlterFunctionStmt(StringInfo buf, AlterFunctionStmt *stmt)
|
||||||
|
{
|
||||||
|
ListCell *actionCell = NULL;
|
||||||
|
|
||||||
|
#if (PG_VERSION_NUM < 110000)
|
||||||
|
appendStringInfo(buf, "ALTER FUNCTION ");
|
||||||
|
|
||||||
|
AppendFunctionName(buf, stmt->func, OBJECT_FUNCTION);
|
||||||
|
#else
|
||||||
|
if (stmt->objtype == OBJECT_FUNCTION)
|
||||||
|
{
|
||||||
|
appendStringInfo(buf, "ALTER FUNCTION ");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
appendStringInfo(buf, "ALTER PROCEDURE ");
|
||||||
|
}
|
||||||
|
|
||||||
|
AppendFunctionName(buf, stmt->func, stmt->objtype);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
foreach(actionCell, stmt->actions)
|
||||||
|
{
|
||||||
|
DefElem *def = castNode(DefElem, lfirst(actionCell));
|
||||||
|
AppendDefElem(buf, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
appendStringInfoString(buf, ";");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AppendDefElem appends a string representing the DefElem to a buffer
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AppendDefElem(StringInfo buf, DefElem *def)
|
||||||
|
{
|
||||||
|
if (strcmp(def->defname, "strict") == 0)
|
||||||
|
{
|
||||||
|
AppendDefElemStrict(buf, def);
|
||||||
|
}
|
||||||
|
else if (strcmp(def->defname, "volatility") == 0)
|
||||||
|
{
|
||||||
|
AppendDefElemVolatility(buf, def);
|
||||||
|
}
|
||||||
|
else if (strcmp(def->defname, "leakproof") == 0)
|
||||||
|
{
|
||||||
|
AppendDefElemLeakproof(buf, def);
|
||||||
|
}
|
||||||
|
else if (strcmp(def->defname, "security") == 0)
|
||||||
|
{
|
||||||
|
AppendDefElemSecurity(buf, def);
|
||||||
|
}
|
||||||
|
else if (strcmp(def->defname, "parallel") == 0)
|
||||||
|
{
|
||||||
|
AppendDefElemParallel(buf, def);
|
||||||
|
}
|
||||||
|
else if (strcmp(def->defname, "cost") == 0)
|
||||||
|
{
|
||||||
|
AppendDefElemCost(buf, def);
|
||||||
|
}
|
||||||
|
else if (strcmp(def->defname, "rows") == 0)
|
||||||
|
{
|
||||||
|
AppendDefElemRows(buf, def);
|
||||||
|
}
|
||||||
|
else if (strcmp(def->defname, "set") == 0)
|
||||||
|
{
|
||||||
|
AppendDefElemSet(buf, def);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AppendDefElemStrict appends a string representing the DefElem to a buffer
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AppendDefElemStrict(StringInfo buf, DefElem *def)
|
||||||
|
{
|
||||||
|
if (intVal(def->arg) == 1)
|
||||||
|
{
|
||||||
|
appendStringInfo(buf, " STRICT");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
appendStringInfo(buf, " CALLED ON NULL INPUT");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AppendDefElemVolatility appends a string representing the DefElem to a buffer
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AppendDefElemVolatility(StringInfo buf, DefElem *def)
|
||||||
|
{
|
||||||
|
appendStringInfo(buf, " %s", CopyAndConvertToUpperCase(strVal(def->arg)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AppendDefElemLeakproof appends a string representing the DefElem to a buffer
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AppendDefElemLeakproof(StringInfo buf, DefElem *def)
|
||||||
|
{
|
||||||
|
if (intVal(def->arg) == 0)
|
||||||
|
{
|
||||||
|
appendStringInfo(buf, " NOT");
|
||||||
|
}
|
||||||
|
appendStringInfo(buf, " LEAKPROOF");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AppendDefElemSecurity appends a string representing the DefElem to a buffer
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AppendDefElemSecurity(StringInfo buf, DefElem *def)
|
||||||
|
{
|
||||||
|
if (intVal(def->arg) == 0)
|
||||||
|
{
|
||||||
|
appendStringInfo(buf, " SECURITY INVOKER");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
appendStringInfo(buf, " SECURITY DEFINER");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AppendDefElemParallel appends a string representing the DefElem to a buffer
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AppendDefElemParallel(StringInfo buf, DefElem *def)
|
||||||
|
{
|
||||||
|
appendStringInfo(buf, " PARALLEL %s", CopyAndConvertToUpperCase(strVal(def->arg)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AppendDefElemCost appends a string representing the DefElem to a buffer
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AppendDefElemCost(StringInfo buf, DefElem *def)
|
||||||
|
{
|
||||||
|
appendStringInfo(buf, " COST %lf", defGetNumeric(def));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AppendDefElemRows appends a string representing the DefElem to a buffer
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AppendDefElemRows(StringInfo buf, DefElem *def)
|
||||||
|
{
|
||||||
|
appendStringInfo(buf, " ROWS %lf", defGetNumeric(def));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AppendDefElemSet appends a string representing the DefElem to a buffer
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AppendDefElemSet(StringInfo buf, DefElem *def)
|
||||||
|
{
|
||||||
|
VariableSetStmt *setStmt = castNode(VariableSetStmt, def->arg);
|
||||||
|
char *setVariableArgs = ExtractSetVariableArgs(setStmt);
|
||||||
|
|
||||||
|
switch (setStmt->kind)
|
||||||
|
{
|
||||||
|
case VAR_SET_VALUE:
|
||||||
|
{
|
||||||
|
appendStringInfo(buf, " SET %s = %s", setStmt->name, setVariableArgs);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case VAR_SET_CURRENT:
|
||||||
|
{
|
||||||
|
appendStringInfo(buf, " SET %s FROM CURRENT", setStmt->name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case VAR_SET_DEFAULT:
|
||||||
|
{
|
||||||
|
appendStringInfo(buf, " SET %s TO DEFAULT", setStmt->name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case VAR_RESET:
|
||||||
|
{
|
||||||
|
appendStringInfo(buf, " RESET %s", setStmt->name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case VAR_RESET_ALL:
|
||||||
|
{
|
||||||
|
appendStringInfoString(buf, " RESET ALL");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* VAR_SET_MULTI is a special case for SET TRANSACTION that should not occur here */
|
||||||
|
case VAR_SET_MULTI:
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
ereport(ERROR, (errmsg("Unable to deparse SET statement")));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DeparseRenameFunctionStmt builds and returns a string representing the RenameStmt
|
||||||
|
*/
|
||||||
|
const char *
|
||||||
|
DeparseRenameFunctionStmt(RenameStmt *stmt)
|
||||||
|
{
|
||||||
|
StringInfoData str = { 0 };
|
||||||
|
initStringInfo(&str);
|
||||||
|
|
||||||
|
#if (PG_VERSION_NUM < 110000)
|
||||||
|
Assert(stmt->renameType == OBJECT_FUNCTION);
|
||||||
|
#else
|
||||||
|
Assert(stmt->renameType == OBJECT_FUNCTION || stmt->renameType == OBJECT_PROCEDURE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
AppendRenameFunctionStmt(&str, stmt);
|
||||||
|
|
||||||
|
return str.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AppendRenameFunctionStmt appends a string representing the RenameStmt to a buffer
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AppendRenameFunctionStmt(StringInfo buf, RenameStmt *stmt)
|
||||||
|
{
|
||||||
|
ObjectWithArgs *func = castNode(ObjectWithArgs, stmt->object);
|
||||||
|
|
||||||
|
#if (PG_VERSION_NUM < 110000)
|
||||||
|
appendStringInfo(buf, "ALTER FUNCTION ");
|
||||||
|
#else
|
||||||
|
if (stmt->renameType == OBJECT_FUNCTION)
|
||||||
|
{
|
||||||
|
appendStringInfoString(buf, "ALTER FUNCTION ");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
appendStringInfoString(buf, "ALTER PROCEDURE ");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
AppendFunctionName(buf, func, stmt->renameType);
|
||||||
|
|
||||||
|
appendStringInfo(buf, " RENAME TO %s;", quote_identifier(stmt->newname));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DeparseAlterFunctionSchemaStmt builds and returns a string representing the AlterObjectSchemaStmt
|
||||||
|
*/
|
||||||
|
const char *
|
||||||
|
DeparseAlterFunctionSchemaStmt(AlterObjectSchemaStmt *stmt)
|
||||||
|
{
|
||||||
|
StringInfoData str = { 0 };
|
||||||
|
initStringInfo(&str);
|
||||||
|
|
||||||
|
#if (PG_VERSION_NUM < 110000)
|
||||||
|
Assert(stmt->objectType == OBJECT_FUNCTION);
|
||||||
|
#else
|
||||||
|
Assert(stmt->objectType == OBJECT_FUNCTION || stmt->objectType == OBJECT_PROCEDURE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
AppendAlterFunctionSchemaStmt(&str, stmt);
|
||||||
|
|
||||||
|
return str.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AppendAlterFunctionSchemaStmt appends a string representing the AlterObjectSchemaStmt to a buffer
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AppendAlterFunctionSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt)
|
||||||
|
{
|
||||||
|
ObjectWithArgs *func = castNode(ObjectWithArgs, stmt->object);
|
||||||
|
|
||||||
|
#if (PG_VERSION_NUM < 110000)
|
||||||
|
appendStringInfo(buf, "ALTER FUNCTION ");
|
||||||
|
#else
|
||||||
|
if (stmt->objectType == OBJECT_FUNCTION)
|
||||||
|
{
|
||||||
|
appendStringInfoString(buf, "ALTER FUNCTION ");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
appendStringInfoString(buf, "ALTER PROCEDURE ");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
AppendFunctionName(buf, func, stmt->objectType);
|
||||||
|
appendStringInfo(buf, " SET SCHEMA %s;", quote_identifier(stmt->newschema));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DeparseAlterFunctionOwnerStmt builds and returns a string representing the AlterOwnerStmt
|
||||||
|
*/
|
||||||
|
const char *
|
||||||
|
DeparseAlterFunctionOwnerStmt(AlterOwnerStmt *stmt)
|
||||||
|
{
|
||||||
|
StringInfoData str = { 0 };
|
||||||
|
initStringInfo(&str);
|
||||||
|
|
||||||
|
#if (PG_VERSION_NUM < 110000)
|
||||||
|
Assert(stmt->objectType == OBJECT_FUNCTION);
|
||||||
|
#else
|
||||||
|
Assert(stmt->objectType == OBJECT_FUNCTION || stmt->objectType == OBJECT_PROCEDURE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
AppendAlterFunctionOwnerStmt(&str, stmt);
|
||||||
|
|
||||||
|
return str.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AppendAlterFunctionOwnerStmt appends a string representing the AlterOwnerStmt to a buffer
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AppendAlterFunctionOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt)
|
||||||
|
{
|
||||||
|
ObjectWithArgs *func = castNode(ObjectWithArgs, stmt->object);
|
||||||
|
|
||||||
|
#if (PG_VERSION_NUM < 110000)
|
||||||
|
appendStringInfo(buf, "ALTER FUNCTION ");
|
||||||
|
#else
|
||||||
|
if (stmt->objectType == OBJECT_FUNCTION)
|
||||||
|
{
|
||||||
|
appendStringInfoString(buf, "ALTER FUNCTION ");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
appendStringInfoString(buf, "ALTER PROCEDURE ");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
AppendFunctionName(buf, func, stmt->objectType);
|
||||||
|
appendStringInfo(buf, " OWNER TO %s;", RoleSpecString(stmt->newowner));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DeparseAlterFunctionDependsStmt builds and returns a string representing the AlterObjectDependsStmt
|
||||||
|
*/
|
||||||
|
const char *
|
||||||
|
DeparseAlterFunctionDependsStmt(AlterObjectDependsStmt *stmt)
|
||||||
|
{
|
||||||
|
StringInfoData str = { 0 };
|
||||||
|
initStringInfo(&str);
|
||||||
|
|
||||||
|
#if (PG_VERSION_NUM < 110000)
|
||||||
|
Assert(stmt->objectType == OBJECT_FUNCTION);
|
||||||
|
#else
|
||||||
|
Assert(stmt->objectType == OBJECT_FUNCTION || stmt->objectType == OBJECT_PROCEDURE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
AppendAlterFunctionDependsStmt(&str, stmt);
|
||||||
|
|
||||||
|
return str.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AppendAlterFunctionDependsStmt appends a string representing the AlterObjectDependsStmt to a buffer
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AppendAlterFunctionDependsStmt(StringInfo buf, AlterObjectDependsStmt *stmt)
|
||||||
|
{
|
||||||
|
ObjectWithArgs *func = castNode(ObjectWithArgs, stmt->object);
|
||||||
|
|
||||||
|
#if (PG_VERSION_NUM < 110000)
|
||||||
|
appendStringInfo(buf, "ALTER FUNCTION ");
|
||||||
|
#else
|
||||||
|
if (stmt->objectType == OBJECT_FUNCTION)
|
||||||
|
{
|
||||||
|
appendStringInfoString(buf, "ALTER FUNCTION ");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
appendStringInfoString(buf, "ALTER PROCEDURE ");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
AppendFunctionName(buf, func, stmt->objectType);
|
||||||
|
appendStringInfo(buf, " DEPENDS ON EXTENSION %s;", strVal(stmt->extname));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DeparseDropFunctionStmt builds and returns a string representing the DropStmt
|
||||||
|
*/
|
||||||
|
const char *
|
||||||
|
DeparseDropFunctionStmt(DropStmt *stmt)
|
||||||
|
{
|
||||||
|
StringInfoData str = { 0 };
|
||||||
|
initStringInfo(&str);
|
||||||
|
|
||||||
|
#if (PG_VERSION_NUM < 110000)
|
||||||
|
Assert(stmt->removeType == OBJECT_FUNCTION);
|
||||||
|
#else
|
||||||
|
Assert(stmt->removeType == OBJECT_FUNCTION || stmt->removeType == OBJECT_PROCEDURE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
AppendDropFunctionStmt(&str, stmt);
|
||||||
|
|
||||||
|
return str.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AppendDropFunctionStmt appends a string representing the DropStmt to a buffer
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AppendDropFunctionStmt(StringInfo buf, DropStmt *stmt)
|
||||||
|
{
|
||||||
|
#if (PG_VERSION_NUM < 110000)
|
||||||
|
appendStringInfo(buf, "DROP FUNCTION ");
|
||||||
|
#else
|
||||||
|
if (stmt->removeType == OBJECT_FUNCTION)
|
||||||
|
{
|
||||||
|
appendStringInfoString(buf, "DROP FUNCTION ");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
appendStringInfoString(buf, "DROP PROCEDURE ");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (stmt->missing_ok)
|
||||||
|
{
|
||||||
|
appendStringInfoString(buf, "IF EXISTS ");
|
||||||
|
}
|
||||||
|
|
||||||
|
AppendFunctionNameList(buf, stmt->objects, stmt->removeType);
|
||||||
|
|
||||||
|
if (stmt->behavior == DROP_CASCADE)
|
||||||
|
{
|
||||||
|
appendStringInfoString(buf, " CASCADE");
|
||||||
|
}
|
||||||
|
|
||||||
|
appendStringInfoString(buf, ";");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AppendFunctionNameList appends a string representing the list of function names to a buffer
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AppendFunctionNameList(StringInfo buf, List *objects, ObjectType objtype)
|
||||||
|
{
|
||||||
|
ListCell *objectCell = NULL;
|
||||||
|
foreach(objectCell, objects)
|
||||||
|
{
|
||||||
|
Node *object = lfirst(objectCell);
|
||||||
|
ObjectWithArgs *func = NULL;
|
||||||
|
|
||||||
|
if (objectCell != list_head(objects))
|
||||||
|
{
|
||||||
|
appendStringInfo(buf, ", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
func = castNode(ObjectWithArgs, object);
|
||||||
|
|
||||||
|
AppendFunctionName(buf, func, objtype);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AppendFunctionName appends a string representing a single function name to a buffer
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AppendFunctionName(StringInfo buf, ObjectWithArgs *func, ObjectType objtype)
|
||||||
|
{
|
||||||
|
Oid funcid = InvalidOid;
|
||||||
|
HeapTuple proctup;
|
||||||
|
char *functionName = NULL;
|
||||||
|
char *schemaName = NULL;
|
||||||
|
char *qualifiedFunctionName;
|
||||||
|
|
||||||
|
funcid = LookupFuncWithArgsCompat(objtype, func, true);
|
||||||
|
proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
|
||||||
|
|
||||||
|
if (!HeapTupleIsValid(proctup))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* DROP FUNCTION IF EXISTS absent_function arrives here
|
||||||
|
*
|
||||||
|
* There is no namespace associated with the nonexistent function,
|
||||||
|
* thus we return the function name as it is provided
|
||||||
|
*/
|
||||||
|
DeconstructQualifiedName(func->objname, &schemaName, &functionName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Form_pg_proc procform;
|
||||||
|
|
||||||
|
procform = (Form_pg_proc) GETSTRUCT(proctup);
|
||||||
|
functionName = NameStr(procform->proname);
|
||||||
|
functionName = pstrdup(functionName); /* we release the tuple before used */
|
||||||
|
schemaName = get_namespace_name(procform->pronamespace);
|
||||||
|
|
||||||
|
ReleaseSysCache(proctup);
|
||||||
|
}
|
||||||
|
|
||||||
|
qualifiedFunctionName = quote_qualified_identifier(schemaName, functionName);
|
||||||
|
appendStringInfoString(buf, qualifiedFunctionName);
|
||||||
|
|
||||||
|
if (OidIsValid(funcid))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* If the function exists we want to use pg_get_function_identity_arguments to
|
||||||
|
* serialize its canonical arguments
|
||||||
|
*/
|
||||||
|
OverrideSearchPath *overridePath = NULL;
|
||||||
|
Datum sqlTextDatum = 0;
|
||||||
|
const char *args = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set search_path to NIL so that all objects outside of pg_catalog will be
|
||||||
|
* schema-prefixed. pg_catalog will be added automatically when we call
|
||||||
|
* PushOverrideSearchPath(), since we set addCatalog to true;
|
||||||
|
*/
|
||||||
|
overridePath = GetOverrideSearchPath(CurrentMemoryContext);
|
||||||
|
overridePath->schemas = NIL;
|
||||||
|
overridePath->addCatalog = true;
|
||||||
|
|
||||||
|
PushOverrideSearchPath(overridePath);
|
||||||
|
|
||||||
|
sqlTextDatum = DirectFunctionCall1(pg_get_function_identity_arguments,
|
||||||
|
ObjectIdGetDatum(funcid));
|
||||||
|
|
||||||
|
/* revert back to original search_path */
|
||||||
|
PopOverrideSearchPath();
|
||||||
|
|
||||||
|
args = TextDatumGetCString(sqlTextDatum);
|
||||||
|
appendStringInfo(buf, "(%s)", args);
|
||||||
|
}
|
||||||
|
else if (!func->args_unspecified)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The function is not found, but there is an argument list specified, this has
|
||||||
|
* some known issues with the "any" type. However this is mostly a bug in
|
||||||
|
* postgres' TypeNameListToString. For now the best we can do until we understand
|
||||||
|
* the underlying cause better.
|
||||||
|
*/
|
||||||
|
const char *args = NULL;
|
||||||
|
|
||||||
|
args = TypeNameListToString(func->objargs);
|
||||||
|
appendStringInfo(buf, "(%s)", args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the type is not found, and no argument list given we don't append anything here.
|
||||||
|
* This will cause mostly the same sql as the original statement.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CopyAndConvertToUpperCase copies a string and converts all characters to uppercase
|
||||||
|
*/
|
||||||
|
static char *
|
||||||
|
CopyAndConvertToUpperCase(const char *str)
|
||||||
|
{
|
||||||
|
char *result, *p;
|
||||||
|
|
||||||
|
result = pstrdup(str);
|
||||||
|
|
||||||
|
for (p = result; *p; p++)
|
||||||
|
{
|
||||||
|
*p = pg_toupper((unsigned char) *p);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
|
@ -29,7 +29,7 @@ static void QualifyRenameAttributeStmt(RenameStmt *stmt);
|
||||||
static void QualifyAlterTableStmt(AlterTableStmt *stmt);
|
static void QualifyAlterTableStmt(AlterTableStmt *stmt);
|
||||||
static void QualifyAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt);
|
static void QualifyAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt);
|
||||||
static void QualifyAlterOwnerStmt(AlterOwnerStmt *stmt);
|
static void QualifyAlterOwnerStmt(AlterOwnerStmt *stmt);
|
||||||
|
static void QualifyAlterObjectDependsStmt(AlterObjectDependsStmt *stmt);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* QualifyTreeNode transforms the statement in place and makes all (supported) statements
|
* QualifyTreeNode transforms the statement in place and makes all (supported) statements
|
||||||
|
@ -83,6 +83,18 @@ QualifyTreeNode(Node *stmt)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case T_AlterFunctionStmt:
|
||||||
|
{
|
||||||
|
QualifyAlterFunctionStmt(castNode(AlterFunctionStmt, stmt));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case T_AlterObjectDependsStmt:
|
||||||
|
{
|
||||||
|
QualifyAlterObjectDependsStmt(castNode(AlterObjectDependsStmt, stmt));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
/* skip unsupported statements */
|
/* skip unsupported statements */
|
||||||
|
@ -92,6 +104,10 @@ QualifyTreeNode(Node *stmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* QualifyRenameStmt transforms a RENAME statement in place and makes all (supported)
|
||||||
|
* statements fully qualified.
|
||||||
|
*/
|
||||||
static void
|
static void
|
||||||
QualifyRenameStmt(RenameStmt *stmt)
|
QualifyRenameStmt(RenameStmt *stmt)
|
||||||
{
|
{
|
||||||
|
@ -109,6 +125,14 @@ QualifyRenameStmt(RenameStmt *stmt)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case OBJECT_FUNCTION:
|
||||||
|
#if PG_VERSION_NUM >= 110000
|
||||||
|
case OBJECT_PROCEDURE:
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
QualifyRenameFunctionStmt(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
/* skip unsupported statements */
|
/* skip unsupported statements */
|
||||||
|
@ -118,6 +142,10 @@ QualifyRenameStmt(RenameStmt *stmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* QualifyRenameAttributeStmt transforms a RENAME ATTRIBUTE statement in place and makes all (supported)
|
||||||
|
* statements fully qualified.
|
||||||
|
*/
|
||||||
static void
|
static void
|
||||||
QualifyRenameAttributeStmt(RenameStmt *stmt)
|
QualifyRenameAttributeStmt(RenameStmt *stmt)
|
||||||
{
|
{
|
||||||
|
@ -170,6 +198,14 @@ QualifyAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case OBJECT_FUNCTION:
|
||||||
|
#if PG_VERSION_NUM >= 110000
|
||||||
|
case OBJECT_PROCEDURE:
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
QualifyAlterFunctionSchemaStmt(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
/* skip unsupported statements */
|
/* skip unsupported statements */
|
||||||
|
@ -190,6 +226,35 @@ QualifyAlterOwnerStmt(AlterOwnerStmt *stmt)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case OBJECT_FUNCTION:
|
||||||
|
#if PG_VERSION_NUM >= 110000
|
||||||
|
case OBJECT_PROCEDURE:
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
QualifyAlterFunctionOwnerStmt(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
QualifyAlterObjectDependsStmt(AlterObjectDependsStmt *stmt)
|
||||||
|
{
|
||||||
|
switch (stmt->objectType)
|
||||||
|
{
|
||||||
|
case OBJECT_FUNCTION:
|
||||||
|
#if PG_VERSION_NUM >= 110000
|
||||||
|
case OBJECT_PROCEDURE:
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
QualifyAlterFunctionDependsStmt(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -0,0 +1,181 @@
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* qualify_function_stmt.c
|
||||||
|
* Functions specialized in fully qualifying all function statements. These
|
||||||
|
* functions are dispatched from qualify.c
|
||||||
|
*
|
||||||
|
* Fully qualifying function statements consists of adding the schema name
|
||||||
|
* to the subject of the function and types as well as any other branch of
|
||||||
|
* the parsetree.
|
||||||
|
*
|
||||||
|
* Goal would be that the deparser functions for these statements can
|
||||||
|
* serialize the statement without any external lookups.
|
||||||
|
*
|
||||||
|
* Copyright (c), Citus Data, Inc.
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "access/htup_details.h"
|
||||||
|
#include "catalog/namespace.h"
|
||||||
|
#include "catalog/pg_proc.h"
|
||||||
|
#include "distributed/deparser.h"
|
||||||
|
#include "distributed/version_compat.h"
|
||||||
|
#include "parser/parse_func.h"
|
||||||
|
#include "utils/lsyscache.h"
|
||||||
|
#include "utils/syscache.h"
|
||||||
|
|
||||||
|
/* forward declaration for qualify functions */
|
||||||
|
void QualifyFunction(ObjectWithArgs *func, ObjectType type);
|
||||||
|
void QualifyFunctionSchemaName(ObjectWithArgs *func, ObjectType type);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* QualifyAlterFunctionStmt transforms a
|
||||||
|
* ALTER {FUNCTION|PROCEDURE} ..
|
||||||
|
* statement in place and makes all (supported) statements fully qualified.
|
||||||
|
*
|
||||||
|
* Note that not all queries of this form are valid AlterFunctionStmt
|
||||||
|
* (e.g. ALTER FUNCTION .. RENAME .. queries are RenameStmt )
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
QualifyAlterFunctionStmt(AlterFunctionStmt *stmt)
|
||||||
|
{
|
||||||
|
ObjectType objtype = OBJECT_FUNCTION;
|
||||||
|
|
||||||
|
#if (PG_VERSION_NUM >= 110000)
|
||||||
|
objtype = stmt->objtype;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QualifyFunction(stmt->func, objtype);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* QualifyRenameFunctionStmt transforms a
|
||||||
|
* ALTER {FUNCTION|PROCEDURE} .. RENAME TO ..
|
||||||
|
* statement in place and makes the function name fully qualified.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
QualifyRenameFunctionStmt(RenameStmt *stmt)
|
||||||
|
{
|
||||||
|
#if (PG_VERSION_NUM < 110000)
|
||||||
|
Assert(stmt->renameType == OBJECT_FUNCTION);
|
||||||
|
#else
|
||||||
|
Assert(stmt->renameType == OBJECT_FUNCTION || stmt->renameType == OBJECT_PROCEDURE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QualifyFunction(castNode(ObjectWithArgs, stmt->object), stmt->renameType);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* QualifyAlterFunctionSchemaStmt transforms a
|
||||||
|
* ALTER {FUNCTION|PROCEDURE} .. SET SCHEMA ..
|
||||||
|
* statement in place and makes the function name fully qualified.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
QualifyAlterFunctionSchemaStmt(AlterObjectSchemaStmt *stmt)
|
||||||
|
{
|
||||||
|
#if (PG_VERSION_NUM < 110000)
|
||||||
|
Assert(stmt->objectType == OBJECT_FUNCTION);
|
||||||
|
#else
|
||||||
|
Assert(stmt->objectType == OBJECT_FUNCTION || stmt->objectType == OBJECT_PROCEDURE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QualifyFunction(castNode(ObjectWithArgs, stmt->object), stmt->objectType);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* QualifyAlterFunctionOwnerStmt transforms a
|
||||||
|
* ALTER {FUNCTION|PROCEDURE} .. OWNER TO ..
|
||||||
|
* statement in place and makes the function name fully qualified.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
QualifyAlterFunctionOwnerStmt(AlterOwnerStmt *stmt)
|
||||||
|
{
|
||||||
|
#if (PG_VERSION_NUM < 110000)
|
||||||
|
Assert(stmt->objectType == OBJECT_FUNCTION);
|
||||||
|
#else
|
||||||
|
Assert(stmt->objectType == OBJECT_FUNCTION || stmt->objectType == OBJECT_PROCEDURE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QualifyFunction(castNode(ObjectWithArgs, stmt->object), stmt->objectType);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* QualifyAlterFunctionDependsStmt transforms a
|
||||||
|
* ALTER {FUNCTION|PROCEDURE} .. DEPENDS ON EXTENSIOIN ..
|
||||||
|
* statement in place and makes the function name fully qualified.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
QualifyAlterFunctionDependsStmt(AlterObjectDependsStmt *stmt)
|
||||||
|
{
|
||||||
|
#if (PG_VERSION_NUM < 110000)
|
||||||
|
Assert(stmt->objectType == OBJECT_FUNCTION);
|
||||||
|
#else
|
||||||
|
Assert(stmt->objectType == OBJECT_FUNCTION || stmt->objectType == OBJECT_PROCEDURE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QualifyFunction(castNode(ObjectWithArgs, stmt->object), stmt->objectType);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* QualifyFunction transforms a function in place and makes it's name fully qualified.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
QualifyFunction(ObjectWithArgs *func, ObjectType type)
|
||||||
|
{
|
||||||
|
char *functionName = NULL;
|
||||||
|
char *schemaName = NULL;
|
||||||
|
|
||||||
|
/* check if the function name is already qualified */
|
||||||
|
DeconstructQualifiedName(func->objname, &schemaName, &functionName);
|
||||||
|
|
||||||
|
/* do a lookup for the schema name if the statement does not include one */
|
||||||
|
if (schemaName == NULL)
|
||||||
|
{
|
||||||
|
QualifyFunctionSchemaName(func, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* QualifyFunction transforms a function in place using a catalog lookup for its schema name to make it fully qualified.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
QualifyFunctionSchemaName(ObjectWithArgs *func, ObjectType type)
|
||||||
|
{
|
||||||
|
char *schemaName = NULL;
|
||||||
|
char *functionName = NULL;
|
||||||
|
Oid funcid = InvalidOid;
|
||||||
|
HeapTuple proctup;
|
||||||
|
|
||||||
|
funcid = LookupFuncWithArgsCompat(type, func, true);
|
||||||
|
proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We can not qualify the function if the catalogs do not have any records.
|
||||||
|
*
|
||||||
|
* e.g. DROP FUNC IF EXISTS non_existent_func() do not result in a valid heap tuple
|
||||||
|
*/
|
||||||
|
if (HeapTupleIsValid(proctup))
|
||||||
|
{
|
||||||
|
Form_pg_proc procform;
|
||||||
|
|
||||||
|
procform = (Form_pg_proc) GETSTRUCT(proctup);
|
||||||
|
schemaName = get_namespace_name(procform->pronamespace);
|
||||||
|
functionName = NameStr(procform->proname);
|
||||||
|
functionName = pstrdup(functionName); /* we release the tuple before used */
|
||||||
|
|
||||||
|
ReleaseSysCache(proctup);
|
||||||
|
|
||||||
|
/* update the function using the schema name */
|
||||||
|
func->objname = list_make2(makeString(schemaName), makeString(functionName));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* test/src/deparse_function_query.c
|
||||||
|
*
|
||||||
|
* This file contains functions to exercise deparsing of
|
||||||
|
* CREATE|ALTER|DROP [...] {FUNCTION|PROCEDURE} ...
|
||||||
|
* queries
|
||||||
|
*
|
||||||
|
* Copyright (c) Citus Data, Inc.
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "distributed/deparser.h"
|
||||||
|
#include "distributed/multi_executor.h"
|
||||||
|
#include "utils/builtins.h"
|
||||||
|
|
||||||
|
/* declarations for dynamic loading */
|
||||||
|
PG_FUNCTION_INFO_V1(deparse_test);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* deparse_test UDF is a UDF to test deparsing in Citus.
|
||||||
|
*
|
||||||
|
* This function accepts a query string; parses, qualifies and then deparses it to create
|
||||||
|
* a qualified query string.
|
||||||
|
*/
|
||||||
|
Datum
|
||||||
|
deparse_test(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
text *queryStringText = PG_GETARG_TEXT_P(0);
|
||||||
|
char *queryStringChar = NULL;
|
||||||
|
Query *query = NULL;
|
||||||
|
const char *deparsedQuery = NULL;
|
||||||
|
|
||||||
|
queryStringChar = text_to_cstring(queryStringText);
|
||||||
|
query = ParseQueryString(queryStringChar, NULL, 0);
|
||||||
|
|
||||||
|
QualifyTreeNode(query->utilityStmt);
|
||||||
|
deparsedQuery = DeparseTreeNode(query->utilityStmt);
|
||||||
|
|
||||||
|
PG_RETURN_TEXT_P(cstring_to_text(deparsedQuery));
|
||||||
|
}
|
|
@ -52,4 +52,19 @@ extern void QualifyAlterTypeOwnerStmt(AlterOwnerStmt *stmt);
|
||||||
extern const ObjectAddress * GetObjectAddressFromParseTree(Node *parseTree, bool
|
extern const ObjectAddress * GetObjectAddressFromParseTree(Node *parseTree, bool
|
||||||
missing_ok);
|
missing_ok);
|
||||||
|
|
||||||
|
/* forward declarations for deparse_function_stmts.c */
|
||||||
|
extern const char * DeparseDropFunctionStmt(DropStmt *stmt);
|
||||||
|
extern const char * DeparseAlterFunctionStmt(AlterFunctionStmt *stmt);
|
||||||
|
|
||||||
|
extern const char * DeparseRenameFunctionStmt(RenameStmt *stmt);
|
||||||
|
extern const char * DeparseAlterFunctionSchemaStmt(AlterObjectSchemaStmt *stmt);
|
||||||
|
extern const char * DeparseAlterFunctionOwnerStmt(AlterOwnerStmt *stmt);
|
||||||
|
extern const char * DeparseAlterFunctionDependsStmt(AlterObjectDependsStmt *stmt);
|
||||||
|
|
||||||
|
extern void QualifyAlterFunctionStmt(AlterFunctionStmt *stmt);
|
||||||
|
extern void QualifyRenameFunctionStmt(RenameStmt *stmt);
|
||||||
|
extern void QualifyAlterFunctionSchemaStmt(AlterObjectSchemaStmt *stmt);
|
||||||
|
extern void QualifyAlterFunctionOwnerStmt(AlterOwnerStmt *stmt);
|
||||||
|
extern void QualifyAlterFunctionDependsStmt(AlterObjectDependsStmt *stmt);
|
||||||
|
|
||||||
#endif /* CITUS_DEPARSER_H */
|
#endif /* CITUS_DEPARSER_H */
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "commands/explain.h"
|
#include "commands/explain.h"
|
||||||
#include "catalog/namespace.h"
|
#include "catalog/namespace.h"
|
||||||
#include "nodes/parsenodes.h"
|
#include "nodes/parsenodes.h"
|
||||||
|
#include "parser/parse_func.h"
|
||||||
|
|
||||||
#if (PG_VERSION_NUM >= 120000)
|
#if (PG_VERSION_NUM >= 120000)
|
||||||
#include "optimizer/optimizer.h"
|
#include "optimizer/optimizer.h"
|
||||||
|
@ -166,6 +167,23 @@ get_expr_result_tupdesc(Node *expr, bool noError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* following compat function and macro should be removed when we drop support for PG10 */
|
||||||
|
static inline Oid
|
||||||
|
LookupFuncWithArgsCompat(ObjectType objtype, ObjectWithArgs *func, bool noError)
|
||||||
|
{
|
||||||
|
if (objtype == OBJECT_FUNCTION)
|
||||||
|
{
|
||||||
|
return LookupFuncWithArgs(func, noError);
|
||||||
|
}
|
||||||
|
else if (objtype == OBJECT_AGGREGATE)
|
||||||
|
{
|
||||||
|
return LookupAggWithArgs(func, noError);
|
||||||
|
}
|
||||||
|
|
||||||
|
return InvalidOid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if (PG_VERSION_NUM >= 110000)
|
#if (PG_VERSION_NUM >= 110000)
|
||||||
|
@ -242,6 +260,14 @@ RangeVarGetRelidInternal(const RangeVar *relation, LOCKMODE lockmode, uint32 fla
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* following compat function and macro should be removed when we drop support for PG10 */
|
||||||
|
static inline Oid
|
||||||
|
LookupFuncWithArgsCompat(ObjectType objtype, ObjectWithArgs *func, bool noError)
|
||||||
|
{
|
||||||
|
return LookupFuncWithArgs(objtype, func, noError);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if PG_VERSION_NUM >= 120000
|
#if PG_VERSION_NUM >= 120000
|
||||||
|
|
|
@ -0,0 +1,707 @@
|
||||||
|
--
|
||||||
|
-- Regression tests for deparsing ALTER/DROP FUNCTION Queries
|
||||||
|
--
|
||||||
|
-- This test implements all the possible queries as of Postgres 11
|
||||||
|
-- in the order they are listed in the docs
|
||||||
|
--
|
||||||
|
-- ALTER FUNCTION name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ]
|
||||||
|
-- action [ ... ] [ RESTRICT ]
|
||||||
|
-- ALTER FUNCTION name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ]
|
||||||
|
-- RENAME TO new_name
|
||||||
|
-- ALTER FUNCTION name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ]
|
||||||
|
-- OWNER TO { new_owner | CURRENT_USER | SESSION_USER }
|
||||||
|
-- ALTER FUNCTION name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ]
|
||||||
|
-- SET SCHEMA new_schema
|
||||||
|
-- ALTER FUNCTION name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ]
|
||||||
|
-- DEPENDS ON EXTENSION extension_name
|
||||||
|
--
|
||||||
|
-- where action is one of:
|
||||||
|
--
|
||||||
|
-- CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT
|
||||||
|
-- IMMUTABLE | STABLE | VOLATILE | [ NOT ] LEAKPROOF
|
||||||
|
-- [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER
|
||||||
|
-- PARALLEL { UNSAFE | RESTRICTED | SAFE }
|
||||||
|
-- COST execution_cost
|
||||||
|
-- ROWS result_rows
|
||||||
|
-- SET configuration_parameter { TO | = } { value | DEFAULT }
|
||||||
|
-- SET configuration_parameter FROM CURRENT
|
||||||
|
-- RESET configuration_parameter
|
||||||
|
-- RESET ALL
|
||||||
|
--
|
||||||
|
-- DROP FUNCTION [ IF EXISTS ] name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ] [, ...]
|
||||||
|
-- [ CASCADE | RESTRICT ]
|
||||||
|
SET citus.next_shard_id TO 20020000;
|
||||||
|
CREATE SCHEMA function_tests;
|
||||||
|
SET search_path TO function_tests;
|
||||||
|
SET citus.shard_count TO 4;
|
||||||
|
SET client_min_messages TO INFO;
|
||||||
|
CREATE FUNCTION deparse_test(text)
|
||||||
|
RETURNS text
|
||||||
|
AS 'citus'
|
||||||
|
LANGUAGE C STRICT;
|
||||||
|
CREATE OR REPLACE FUNCTION deparse_and_run_on_workers(IN query text,
|
||||||
|
OUT nodename text,
|
||||||
|
OUT nodeport int,
|
||||||
|
OUT success bool,
|
||||||
|
OUT result text)
|
||||||
|
RETURNS SETOF record
|
||||||
|
LANGUAGE PLPGSQL AS $fnc$
|
||||||
|
DECLARE
|
||||||
|
deparsed_query character varying(255);
|
||||||
|
BEGIN
|
||||||
|
deparsed_query := ( SELECT deparse_test($1) );
|
||||||
|
RAISE INFO 'Propagating deparsed query: %', deparsed_query;
|
||||||
|
RETURN QUERY SELECT * FROM run_command_on_workers(deparsed_query);
|
||||||
|
END;
|
||||||
|
$fnc$;
|
||||||
|
-- Create a simple function and distribute it
|
||||||
|
CREATE FUNCTION add(integer, integer) RETURNS integer
|
||||||
|
AS 'select $1 + $2;'
|
||||||
|
LANGUAGE SQL
|
||||||
|
IMMUTABLE
|
||||||
|
RETURNS NULL ON NULL INPUT;
|
||||||
|
SELECT create_distributed_function('add(int,int)');
|
||||||
|
create_distributed_function
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add CALLED ON NULL INPUT
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) CALLED ON NULL INPUT;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- RETURNS NULL ON NULL INPUT and STRICT are synonyms and can be used interchangeably
|
||||||
|
-- RETURNS NULL ON NULL INPUT is actually stored as STRICT in the query parse tree
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add RETURNS NULL ON NULL INPUT
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) STRICT;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add STRICT
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) STRICT;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add IMMUTABLE
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) IMMUTABLE;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add STABLE
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) STABLE;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add VOLATILE
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) VOLATILE;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add LEAKPROOF
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) LEAKPROOF;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add NOT LEAKPROOF
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) NOT LEAKPROOF;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- EXTERNAL keyword is ignored by Postgres Parser. It is allowed only for SQL conformance
|
||||||
|
-- The following queries will not have the EXTERNAL keyword after deparsing
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add EXTERNAL SECURITY INVOKER
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) SECURITY INVOKER;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add SECURITY INVOKER
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) SECURITY INVOKER;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add EXTERNAL SECURITY DEFINER
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) SECURITY DEFINER;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add SECURITY DEFINER
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) SECURITY DEFINER;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add PARALLEL UNSAFE
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) PARALLEL UNSAFE;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add PARALLEL RESTRICTED
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) PARALLEL RESTRICTED;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add PARALLEL SAFE
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) PARALLEL SAFE;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- The COST arguments should always be numeric
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add COST 1234
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) COST 1234.000000;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add COST 1234.5
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) COST 1234.500000;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add SET log_min_messages = ERROR
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) SET log_min_messages = error;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add SET log_min_messages TO DEFAULT
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) SET log_min_messages TO DEFAULT;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add SET log_min_messages FROM CURRENT
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) SET log_min_messages FROM CURRENT;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add RESET log_min_messages
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) RESET log_min_messages;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add RESET ALL
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) RESET ALL;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- Rename the function in the workers
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add RENAME TO summation
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) RENAME TO summation;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- Rename the function inb the coordinator as well.
|
||||||
|
-- This is needed so the next query is parsed on the coordinator
|
||||||
|
ALTER FUNCTION add RENAME TO summation;
|
||||||
|
-- Rename it back to the original so that the next tests can pass
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION summation RENAME TO add
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.summation(integer, integer) RENAME TO add;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- Rename the function back to the original name in the coordinator
|
||||||
|
ALTER FUNCTION summation RENAME TO add;
|
||||||
|
CREATE ROLE function_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 run_command_on_workers('CREATE ROLE function_role');
|
||||||
|
run_command_on_workers
|
||||||
|
-----------------------------------
|
||||||
|
(localhost,57637,t,"CREATE ROLE")
|
||||||
|
(localhost,57638,t,"CREATE ROLE")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add OWNER TO function_role
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) OWNER TO function_role;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add OWNER TO missing_role
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) OWNER TO missing_role;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
(localhost,57637,f,"ERROR: role ""missing_role"" does not exist")
|
||||||
|
(localhost,57638,f,"ERROR: role ""missing_role"" does not exist")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- SET the schema in workers as well as the coordinator so that it remains in the same schema
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add SET SCHEMA public
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) SET SCHEMA public;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
ALTER FUNCTION add SET SCHEMA public;
|
||||||
|
-- Revert the schema back
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION public.add SET SCHEMA function_tests
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION public.add(integer, integer) SET SCHEMA function_tests;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
ALTER FUNCTION public.add SET SCHEMA function_tests;
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add DEPENDS ON EXTENSION citus
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) DEPENDS ON EXTENSION citus;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- make sure "any" type is correctly deparsed
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION pg_catalog.get_shard_id_for_distribution_column(table_name regclass, distribution_value "any") PARALLEL SAFE;
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION pg_catalog.get_shard_id_for_distribution_column(table_name regclass, distribution_value "any") PARALLEL SAFE;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- Do not run valid drop queries in the workers
|
||||||
|
SELECT deparse_test($cmd$
|
||||||
|
DROP FUNCTION add(int,int);
|
||||||
|
$cmd$);
|
||||||
|
deparse_test
|
||||||
|
-----------------------------------------------------
|
||||||
|
DROP FUNCTION function_tests.add(integer, integer);
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- have multiple actions in a single query
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add volatile leakproof SECURITY DEFINER PARALLEL unsafe;
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) VOLATILE LEAKPROOF SECURITY DEFINER PARALLEL UNSAFE;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- Check that an invalid function name is still parsed correctly
|
||||||
|
-- Test that it fails when run without IF EXISTS clause
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
DROP FUNCTION missing_function(int, text);
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: DROP FUNCTION missing_function(pg_catalog.int4,text);
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
---------------------------------------------------------------------------------------
|
||||||
|
(localhost,57637,f,"ERROR: function missing_function(integer, text) does not exist")
|
||||||
|
(localhost,57638,f,"ERROR: function missing_function(integer, text) does not exist")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- Check that an invalid function name is still parsed correctly
|
||||||
|
-- Test that it is successful when run with IF EXISTS clause
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
DROP FUNCTION IF EXISTS missing_function(int, text);
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: DROP FUNCTION IF EXISTS missing_function(pg_catalog.int4,text);
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
-------------------------------------
|
||||||
|
(localhost,57637,t,"DROP FUNCTION")
|
||||||
|
(localhost,57638,t,"DROP FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
DROP FUNCTION IF EXISTS missing_schema.missing_function(int,float);
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: DROP FUNCTION IF EXISTS missing_schema.missing_function(pg_catalog.int4,pg_catalog.float8);
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
-------------------------------------
|
||||||
|
(localhost,57637,t,"DROP FUNCTION")
|
||||||
|
(localhost,57638,t,"DROP FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
DROP FUNCTION IF EXISTS missing_func_without_args;
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: DROP FUNCTION IF EXISTS missing_func_without_args;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
-------------------------------------
|
||||||
|
(localhost,57637,t,"DROP FUNCTION")
|
||||||
|
(localhost,57638,t,"DROP FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- create schema with weird names
|
||||||
|
CREATE SCHEMA "CiTuS.TeeN";
|
||||||
|
CREATE SCHEMA "CiTUS.TEEN2";
|
||||||
|
SELECT run_command_on_workers($$
|
||||||
|
CREATE SCHEMA IF NOT EXISTS "CiTuS.TeeN";
|
||||||
|
CREATE SCHEMA IF NOT EXISTS "CiTUS.TEEN2";
|
||||||
|
$$);
|
||||||
|
run_command_on_workers
|
||||||
|
-------------------------------------
|
||||||
|
(localhost,57637,t,"CREATE SCHEMA")
|
||||||
|
(localhost,57638,t,"CREATE SCHEMA")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- create table with weird names
|
||||||
|
CREATE FUNCTION "CiTuS.TeeN"."TeeNFunCT10N.1!?!"() RETURNS TEXT
|
||||||
|
AS $$ SELECT 'test function without params' $$
|
||||||
|
LANGUAGE SQL;
|
||||||
|
CREATE FUNCTION "CiTuS.TeeN"."TeeNFunCT10N.1!?!"(text) RETURNS TEXT
|
||||||
|
AS $$ SELECT 'Overloaded function called with param: ' || $1 $$
|
||||||
|
LANGUAGE SQL;
|
||||||
|
SELECT create_distributed_function('"CiTuS.TeeN"."TeeNFunCT10N.1!?!"()');
|
||||||
|
create_distributed_function
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT create_distributed_function('"CiTuS.TeeN"."TeeNFunCT10N.1!?!"(text)');
|
||||||
|
create_distributed_function
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION "CiTuS.TeeN"."TeeNFunCT10N.1!?!"() SET SCHEMA "CiTUS.TEEN2"
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION "CiTuS.TeeN"."TeeNFunCT10N.1!?!"() SET SCHEMA "CiTUS.TEEN2";
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- drop 2 functions at the same time
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
DROP FUNCTION "CiTUS.TEEN2"."TeeNFunCT10N.1!?!"(),"CiTuS.TeeN"."TeeNFunCT10N.1!?!"(text);
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: DROP FUNCTION "CiTUS.TEEN2"."TeeNFunCT10N.1!?!"(), "CiTuS.TeeN"."TeeNFunCT10N.1!?!"(text);
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
-------------------------------------
|
||||||
|
(localhost,57637,t,"DROP FUNCTION")
|
||||||
|
(localhost,57638,t,"DROP FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- a function with a default parameter
|
||||||
|
CREATE FUNCTION func_default_param(param INT DEFAULT 0) RETURNS TEXT
|
||||||
|
AS $$ SELECT 'supplied param is : ' || param; $$
|
||||||
|
LANGUAGE SQL;
|
||||||
|
SELECT create_distributed_function('func_default_param(INT)');
|
||||||
|
create_distributed_function
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION func_default_param RENAME TO func_with_default_param;
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.func_default_param(param integer) RENAME TO func_with_default_param;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- a function with IN and OUT parameters
|
||||||
|
CREATE FUNCTION func_out_param(IN param INT, OUT result TEXT)
|
||||||
|
AS $$ SELECT 'supplied param is : ' || param; $$
|
||||||
|
LANGUAGE SQL;
|
||||||
|
SELECT create_distributed_function('func_out_param(INT)');
|
||||||
|
create_distributed_function
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION func_out_param RENAME TO func_in_and_out_param;
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.func_out_param(param integer, OUT result text) RENAME TO func_in_and_out_param;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- a function with INOUT parameter
|
||||||
|
CREATE FUNCTION square(INOUT a NUMERIC)
|
||||||
|
AS $$
|
||||||
|
BEGIN
|
||||||
|
a := a * a;
|
||||||
|
END; $$
|
||||||
|
LANGUAGE plpgsql;
|
||||||
|
SELECT create_distributed_function('square(NUMERIC)');
|
||||||
|
create_distributed_function
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION square SET search_path TO DEFAULT;
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.square(INOUT a numeric) SET search_path TO DEFAULT;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- a function with variadic input.
|
||||||
|
CREATE FUNCTION sum_avg(
|
||||||
|
VARIADIC list NUMERIC[],
|
||||||
|
OUT total NUMERIC,
|
||||||
|
OUT average NUMERIC)
|
||||||
|
AS $$
|
||||||
|
BEGIN
|
||||||
|
SELECT INTO total SUM(list[i])
|
||||||
|
FROM generate_subscripts(list, 1) g(i);
|
||||||
|
|
||||||
|
SELECT INTO average AVG(list[i])
|
||||||
|
FROM generate_subscripts(list, 1) g(i);
|
||||||
|
END; $$
|
||||||
|
LANGUAGE plpgsql;
|
||||||
|
SELECT create_distributed_function('sum_avg(NUMERIC[])');
|
||||||
|
create_distributed_function
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION sum_avg COST 10000;
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.sum_avg(VARIADIC list numeric[], OUT total numeric, OUT average numeric) COST 10000.000000;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- a function with a custom type IN parameter
|
||||||
|
CREATE TYPE intpair AS (x int, y int);
|
||||||
|
CREATE FUNCTION func_custom_param(IN param intpair, OUT total INT)
|
||||||
|
AS $$ SELECT param.x + param.y $$
|
||||||
|
LANGUAGE SQL;
|
||||||
|
SELECT create_distributed_function('func_custom_param(intpair)');
|
||||||
|
create_distributed_function
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION func_custom_param RENAME TO func_with_custom_param;
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.func_custom_param(param function_tests.intpair, OUT total integer) RENAME TO func_with_custom_param;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- a function that returns TABLE
|
||||||
|
CREATE FUNCTION func_returns_table(IN count INT)
|
||||||
|
RETURNS TABLE (x INT, y INT)
|
||||||
|
AS $$ SELECT i,i FROM generate_series(1,count) i $$
|
||||||
|
LANGUAGE SQL;
|
||||||
|
SELECT create_distributed_function('func_returns_table(INT)');
|
||||||
|
create_distributed_function
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION func_returns_table ROWS 100;
|
||||||
|
$cmd$);
|
||||||
|
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.func_returns_table(count integer) ROWS 100.000000;
|
||||||
|
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER FUNCTION")
|
||||||
|
(localhost,57638,t,"ALTER FUNCTION")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- clear objects
|
||||||
|
SET client_min_messages TO WARNING; -- suppress cascading objects dropping
|
||||||
|
DROP SCHEMA "CiTuS.TeeN" CASCADE;
|
||||||
|
DROP SCHEMA "CiTUS.TEEN2" CASCADE;
|
||||||
|
DROP SCHEMA function_tests CASCADE;
|
||||||
|
SELECT run_command_on_workers($$
|
||||||
|
DROP SCHEMA "CiTuS.TeeN" CASCADE;
|
||||||
|
DROP SCHEMA "CiTUS.TEEN2" CASCADE;
|
||||||
|
DROP SCHEMA function_tests CASCADE;
|
||||||
|
$$);
|
||||||
|
run_command_on_workers
|
||||||
|
-----------------------------------
|
||||||
|
(localhost,57637,t,"DROP SCHEMA")
|
||||||
|
(localhost,57638,t,"DROP SCHEMA")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
DROP ROLE function_role;
|
|
@ -0,0 +1,379 @@
|
||||||
|
--
|
||||||
|
-- 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
|
||||||
|
SELECT substring(:'server_version', '\d+')::int > 10 AS version_above_ten;
|
||||||
|
version_above_ten
|
||||||
|
-------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
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$;
|
||||||
|
SELECT create_distributed_function('raise_info(text)');
|
||||||
|
create_distributed_function
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info CALLED ON NULL INPUT
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
-------------------------------------------------------------------------
|
||||||
|
(localhost,57637,f,"ERROR: invalid attribute in procedure definition")
|
||||||
|
(localhost,57638,f,"ERROR: invalid attribute in procedure definition")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info RETURNS NULL ON NULL INPUT
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
-------------------------------------------------------------------------
|
||||||
|
(localhost,57637,f,"ERROR: invalid attribute in procedure definition")
|
||||||
|
(localhost,57638,f,"ERROR: invalid attribute in procedure definition")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info STRICT
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
-------------------------------------------------------------------------
|
||||||
|
(localhost,57637,f,"ERROR: invalid attribute in procedure definition")
|
||||||
|
(localhost,57638,f,"ERROR: invalid attribute in procedure definition")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info IMMUTABLE
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
-------------------------------------------------------------------------
|
||||||
|
(localhost,57637,f,"ERROR: invalid attribute in procedure definition")
|
||||||
|
(localhost,57638,f,"ERROR: invalid attribute in procedure definition")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info STABLE
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
-------------------------------------------------------------------------
|
||||||
|
(localhost,57637,f,"ERROR: invalid attribute in procedure definition")
|
||||||
|
(localhost,57638,f,"ERROR: invalid attribute in procedure definition")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info VOLATILE
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
-------------------------------------------------------------------------
|
||||||
|
(localhost,57637,f,"ERROR: invalid attribute in procedure definition")
|
||||||
|
(localhost,57638,f,"ERROR: invalid attribute in procedure definition")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info LEAKPROOF
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
-------------------------------------------------------------------------
|
||||||
|
(localhost,57637,f,"ERROR: invalid attribute in procedure definition")
|
||||||
|
(localhost,57638,f,"ERROR: invalid attribute in procedure definition")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info NOT LEAKPROOF
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
-------------------------------------------------------------------------
|
||||||
|
(localhost,57637,f,"ERROR: invalid attribute in procedure definition")
|
||||||
|
(localhost,57638,f,"ERROR: invalid attribute in procedure definition")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info EXTERNAL SECURITY INVOKER
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
---------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER PROCEDURE")
|
||||||
|
(localhost,57638,t,"ALTER PROCEDURE")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info SECURITY INVOKER
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
---------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER PROCEDURE")
|
||||||
|
(localhost,57638,t,"ALTER PROCEDURE")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info EXTERNAL SECURITY DEFINER
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
---------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER PROCEDURE")
|
||||||
|
(localhost,57638,t,"ALTER PROCEDURE")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info SECURITY DEFINER
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
---------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER PROCEDURE")
|
||||||
|
(localhost,57638,t,"ALTER PROCEDURE")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info PARALLEL UNSAFE
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
-------------------------------------------------------------------------
|
||||||
|
(localhost,57637,f,"ERROR: invalid attribute in procedure definition")
|
||||||
|
(localhost,57638,f,"ERROR: invalid attribute in procedure definition")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info PARALLEL RESTRICTED
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
-------------------------------------------------------------------------
|
||||||
|
(localhost,57637,f,"ERROR: invalid attribute in procedure definition")
|
||||||
|
(localhost,57638,f,"ERROR: invalid attribute in procedure definition")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info PARALLEL SAFE
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
-------------------------------------------------------------------------
|
||||||
|
(localhost,57637,f,"ERROR: invalid attribute in procedure definition")
|
||||||
|
(localhost,57638,f,"ERROR: invalid attribute in procedure definition")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- The COST/ROWS arguments should always be numeric
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info COST 1234
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
-------------------------------------------------------------------------
|
||||||
|
(localhost,57637,f,"ERROR: invalid attribute in procedure definition")
|
||||||
|
(localhost,57638,f,"ERROR: invalid attribute in procedure definition")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info COST 1234.5
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
-------------------------------------------------------------------------
|
||||||
|
(localhost,57637,f,"ERROR: invalid attribute in procedure definition")
|
||||||
|
(localhost,57638,f,"ERROR: invalid attribute in procedure definition")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info ROWS 10
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
-------------------------------------------------------------------------
|
||||||
|
(localhost,57637,f,"ERROR: invalid attribute in procedure definition")
|
||||||
|
(localhost,57638,f,"ERROR: invalid attribute in procedure definition")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info ROWS 10.8
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
-------------------------------------------------------------------------
|
||||||
|
(localhost,57637,f,"ERROR: invalid attribute in procedure definition")
|
||||||
|
(localhost,57638,f,"ERROR: invalid attribute in procedure definition")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info SECURITY INVOKER SET client_min_messages TO warning;
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
---------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER PROCEDURE")
|
||||||
|
(localhost,57638,t,"ALTER PROCEDURE")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info SET log_min_messages = ERROR
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
---------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER PROCEDURE")
|
||||||
|
(localhost,57638,t,"ALTER PROCEDURE")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info SET log_min_messages TO DEFAULT
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
---------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER PROCEDURE")
|
||||||
|
(localhost,57638,t,"ALTER PROCEDURE")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info SET log_min_messages FROM CURRENT
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
---------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER PROCEDURE")
|
||||||
|
(localhost,57638,t,"ALTER PROCEDURE")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info RESET log_min_messages
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
---------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER PROCEDURE")
|
||||||
|
(localhost,57638,t,"ALTER PROCEDURE")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info RESET ALL
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
---------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER PROCEDURE")
|
||||||
|
(localhost,57638,t,"ALTER PROCEDURE")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info RENAME TO summation
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
---------------------------------------
|
||||||
|
(localhost,57637,t,"ALTER PROCEDURE")
|
||||||
|
(localhost,57638,t,"ALTER PROCEDURE")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
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$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
(localhost,57637,f,"ERROR: role ""procedure_role"" does not exist")
|
||||||
|
(localhost,57638,f,"ERROR: role ""procedure_role"" does not exist")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info OWNER TO missing_role
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
(localhost,57637,f,"ERROR: role ""missing_role"" does not exist")
|
||||||
|
(localhost,57638,f,"ERROR: role ""missing_role"" does not exist")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info SET SCHEMA public
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
-----------------------------------------------------------------------------------------
|
||||||
|
(localhost,57637,f,"ERROR: procedure procedure_tests.raise_info(text) does not exist")
|
||||||
|
(localhost,57638,f,"ERROR: procedure procedure_tests.raise_info(text) does not exist")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info DEPENDS ON EXTENSION citus
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
-----------------------------------------------------------------------------------------
|
||||||
|
(localhost,57637,f,"ERROR: procedure procedure_tests.raise_info(text) does not exist")
|
||||||
|
(localhost,57638,f,"ERROR: procedure procedure_tests.raise_info(text) does not exist")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
DROP PROCEDURE IF EXISTS raise_info(int,int);
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"DROP PROCEDURE")
|
||||||
|
(localhost,57638,t,"DROP PROCEDURE")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- 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$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"DROP PROCEDURE")
|
||||||
|
(localhost,57638,t,"DROP PROCEDURE")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
DROP PROCEDURE IF EXISTS missing_schema.missing_PROCEDURE(int,float);
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"DROP PROCEDURE")
|
||||||
|
(localhost,57638,t,"DROP PROCEDURE")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
DROP PROCEDURE IF EXISTS missing_schema.missing_PROCEDURE(int,float) CASCADE;
|
||||||
|
$cmd$);
|
||||||
|
deparse_and_run_on_workers
|
||||||
|
--------------------------------------
|
||||||
|
(localhost,57637,t,"DROP PROCEDURE")
|
||||||
|
(localhost,57638,t,"DROP PROCEDURE")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- clear objects
|
||||||
|
SET client_min_messages TO WARNING; -- suppress cascading objects dropping
|
||||||
|
DROP SCHEMA procedure_tests CASCADE;
|
||||||
|
DROP ROLE PROCEDURE_role;
|
|
@ -0,0 +1,448 @@
|
||||||
|
--
|
||||||
|
-- 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
|
||||||
|
SELECT substring(:'server_version', '\d+')::int > 10 AS version_above_ten;
|
||||||
|
version_above_ten
|
||||||
|
-------------------
|
||||||
|
f
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
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;
|
|
@ -282,3 +282,8 @@ 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
|
||||||
|
|
||||||
|
# ---------
|
||||||
|
# deparsing logic tests
|
||||||
|
# ---------
|
||||||
|
test: multi_deparse_function multi_deparse_procedure
|
||||||
|
|
|
@ -0,0 +1,366 @@
|
||||||
|
--
|
||||||
|
-- Regression tests for deparsing ALTER/DROP FUNCTION Queries
|
||||||
|
--
|
||||||
|
-- This test implements all the possible queries as of Postgres 11
|
||||||
|
-- in the order they are listed in the docs
|
||||||
|
--
|
||||||
|
-- ALTER FUNCTION name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ]
|
||||||
|
-- action [ ... ] [ RESTRICT ]
|
||||||
|
-- ALTER FUNCTION name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ]
|
||||||
|
-- RENAME TO new_name
|
||||||
|
-- ALTER FUNCTION name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ]
|
||||||
|
-- OWNER TO { new_owner | CURRENT_USER | SESSION_USER }
|
||||||
|
-- ALTER FUNCTION name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ]
|
||||||
|
-- SET SCHEMA new_schema
|
||||||
|
-- ALTER FUNCTION name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ]
|
||||||
|
-- DEPENDS ON EXTENSION extension_name
|
||||||
|
--
|
||||||
|
-- where action is one of:
|
||||||
|
--
|
||||||
|
-- CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT
|
||||||
|
-- IMMUTABLE | STABLE | VOLATILE | [ NOT ] LEAKPROOF
|
||||||
|
-- [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER
|
||||||
|
-- PARALLEL { UNSAFE | RESTRICTED | SAFE }
|
||||||
|
-- COST execution_cost
|
||||||
|
-- ROWS result_rows
|
||||||
|
-- SET configuration_parameter { TO | = } { value | DEFAULT }
|
||||||
|
-- SET configuration_parameter FROM CURRENT
|
||||||
|
-- RESET configuration_parameter
|
||||||
|
-- RESET ALL
|
||||||
|
--
|
||||||
|
-- DROP FUNCTION [ IF EXISTS ] name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ] [, ...]
|
||||||
|
-- [ CASCADE | RESTRICT ]
|
||||||
|
|
||||||
|
SET citus.next_shard_id TO 20020000;
|
||||||
|
|
||||||
|
CREATE SCHEMA function_tests;
|
||||||
|
SET search_path TO function_tests;
|
||||||
|
SET citus.shard_count TO 4;
|
||||||
|
SET client_min_messages TO INFO;
|
||||||
|
|
||||||
|
CREATE FUNCTION deparse_test(text)
|
||||||
|
RETURNS text
|
||||||
|
AS 'citus'
|
||||||
|
LANGUAGE C STRICT;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION deparse_and_run_on_workers(IN query text,
|
||||||
|
OUT nodename text,
|
||||||
|
OUT nodeport int,
|
||||||
|
OUT success bool,
|
||||||
|
OUT result text)
|
||||||
|
RETURNS SETOF record
|
||||||
|
LANGUAGE PLPGSQL AS $fnc$
|
||||||
|
DECLARE
|
||||||
|
deparsed_query character varying(255);
|
||||||
|
BEGIN
|
||||||
|
deparsed_query := ( SELECT deparse_test($1) );
|
||||||
|
RAISE INFO 'Propagating deparsed query: %', deparsed_query;
|
||||||
|
RETURN QUERY SELECT * FROM run_command_on_workers(deparsed_query);
|
||||||
|
END;
|
||||||
|
$fnc$;
|
||||||
|
|
||||||
|
|
||||||
|
-- Create a simple function and distribute it
|
||||||
|
CREATE FUNCTION add(integer, integer) RETURNS integer
|
||||||
|
AS 'select $1 + $2;'
|
||||||
|
LANGUAGE SQL
|
||||||
|
IMMUTABLE
|
||||||
|
RETURNS NULL ON NULL INPUT;
|
||||||
|
SELECT create_distributed_function('add(int,int)');
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add CALLED ON NULL INPUT
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
-- RETURNS NULL ON NULL INPUT and STRICT are synonyms and can be used interchangeably
|
||||||
|
-- RETURNS NULL ON NULL INPUT is actually stored as STRICT in the query parse tree
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add RETURNS NULL ON NULL INPUT
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add STRICT
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add IMMUTABLE
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add STABLE
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add VOLATILE
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add LEAKPROOF
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add NOT LEAKPROOF
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
|
||||||
|
-- EXTERNAL keyword is ignored by Postgres Parser. It is allowed only for SQL conformance
|
||||||
|
-- The following queries will not have the EXTERNAL keyword after deparsing
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add EXTERNAL SECURITY INVOKER
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add SECURITY INVOKER
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add EXTERNAL SECURITY DEFINER
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add SECURITY DEFINER
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add PARALLEL UNSAFE
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add PARALLEL RESTRICTED
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add PARALLEL SAFE
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
|
||||||
|
-- The COST arguments should always be numeric
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add COST 1234
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add COST 1234.5
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add SET log_min_messages = ERROR
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add SET log_min_messages TO DEFAULT
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add SET log_min_messages FROM CURRENT
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add RESET log_min_messages
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add RESET ALL
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
-- Rename the function in the workers
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add RENAME TO summation
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
-- Rename the function inb the coordinator as well.
|
||||||
|
-- This is needed so the next query is parsed on the coordinator
|
||||||
|
ALTER FUNCTION add RENAME TO summation;
|
||||||
|
|
||||||
|
-- Rename it back to the original so that the next tests can pass
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION summation RENAME TO add
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
-- Rename the function back to the original name in the coordinator
|
||||||
|
ALTER FUNCTION summation RENAME TO add;
|
||||||
|
|
||||||
|
CREATE ROLE function_role;
|
||||||
|
SELECT run_command_on_workers('CREATE ROLE function_role');
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add OWNER TO function_role
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add OWNER TO missing_role
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
-- SET the schema in workers as well as the coordinator so that it remains in the same schema
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add SET SCHEMA public
|
||||||
|
$cmd$);
|
||||||
|
ALTER FUNCTION add SET SCHEMA public;
|
||||||
|
|
||||||
|
-- Revert the schema back
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION public.add SET SCHEMA function_tests
|
||||||
|
$cmd$);
|
||||||
|
ALTER FUNCTION public.add SET SCHEMA function_tests;
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add DEPENDS ON EXTENSION citus
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
-- make sure "any" type is correctly deparsed
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION pg_catalog.get_shard_id_for_distribution_column(table_name regclass, distribution_value "any") PARALLEL SAFE;
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
-- Do not run valid drop queries in the workers
|
||||||
|
SELECT deparse_test($cmd$
|
||||||
|
DROP FUNCTION add(int,int);
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
-- have multiple actions in a single query
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION add volatile leakproof SECURITY DEFINER PARALLEL unsafe;
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
-- Check that an invalid function name is still parsed correctly
|
||||||
|
-- Test that it fails when run without IF EXISTS clause
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
DROP FUNCTION missing_function(int, text);
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
-- Check that an invalid function name is still parsed correctly
|
||||||
|
-- Test that it is successful when run with IF EXISTS clause
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
DROP FUNCTION IF EXISTS missing_function(int, text);
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
DROP FUNCTION IF EXISTS missing_schema.missing_function(int,float);
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
DROP FUNCTION IF EXISTS missing_func_without_args;
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
-- create schema with weird names
|
||||||
|
CREATE SCHEMA "CiTuS.TeeN";
|
||||||
|
CREATE SCHEMA "CiTUS.TEEN2";
|
||||||
|
|
||||||
|
SELECT run_command_on_workers($$
|
||||||
|
CREATE SCHEMA IF NOT EXISTS "CiTuS.TeeN";
|
||||||
|
CREATE SCHEMA IF NOT EXISTS "CiTUS.TEEN2";
|
||||||
|
$$);
|
||||||
|
|
||||||
|
-- create table with weird names
|
||||||
|
CREATE FUNCTION "CiTuS.TeeN"."TeeNFunCT10N.1!?!"() RETURNS TEXT
|
||||||
|
AS $$ SELECT 'test function without params' $$
|
||||||
|
LANGUAGE SQL;
|
||||||
|
|
||||||
|
CREATE FUNCTION "CiTuS.TeeN"."TeeNFunCT10N.1!?!"(text) RETURNS TEXT
|
||||||
|
AS $$ SELECT 'Overloaded function called with param: ' || $1 $$
|
||||||
|
LANGUAGE SQL;
|
||||||
|
|
||||||
|
SELECT create_distributed_function('"CiTuS.TeeN"."TeeNFunCT10N.1!?!"()');
|
||||||
|
SELECT create_distributed_function('"CiTuS.TeeN"."TeeNFunCT10N.1!?!"(text)');
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION "CiTuS.TeeN"."TeeNFunCT10N.1!?!"() SET SCHEMA "CiTUS.TEEN2"
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
-- drop 2 functions at the same time
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
DROP FUNCTION "CiTUS.TEEN2"."TeeNFunCT10N.1!?!"(),"CiTuS.TeeN"."TeeNFunCT10N.1!?!"(text);
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
-- a function with a default parameter
|
||||||
|
CREATE FUNCTION func_default_param(param INT DEFAULT 0) RETURNS TEXT
|
||||||
|
AS $$ SELECT 'supplied param is : ' || param; $$
|
||||||
|
LANGUAGE SQL;
|
||||||
|
SELECT create_distributed_function('func_default_param(INT)');
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION func_default_param RENAME TO func_with_default_param;
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
-- a function with IN and OUT parameters
|
||||||
|
CREATE FUNCTION func_out_param(IN param INT, OUT result TEXT)
|
||||||
|
AS $$ SELECT 'supplied param is : ' || param; $$
|
||||||
|
LANGUAGE SQL;
|
||||||
|
SELECT create_distributed_function('func_out_param(INT)');
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION func_out_param RENAME TO func_in_and_out_param;
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
-- a function with INOUT parameter
|
||||||
|
CREATE FUNCTION square(INOUT a NUMERIC)
|
||||||
|
AS $$
|
||||||
|
BEGIN
|
||||||
|
a := a * a;
|
||||||
|
END; $$
|
||||||
|
LANGUAGE plpgsql;
|
||||||
|
SELECT create_distributed_function('square(NUMERIC)');
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION square SET search_path TO DEFAULT;
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
-- a function with variadic input.
|
||||||
|
CREATE FUNCTION sum_avg(
|
||||||
|
VARIADIC list NUMERIC[],
|
||||||
|
OUT total NUMERIC,
|
||||||
|
OUT average NUMERIC)
|
||||||
|
AS $$
|
||||||
|
BEGIN
|
||||||
|
SELECT INTO total SUM(list[i])
|
||||||
|
FROM generate_subscripts(list, 1) g(i);
|
||||||
|
|
||||||
|
SELECT INTO average AVG(list[i])
|
||||||
|
FROM generate_subscripts(list, 1) g(i);
|
||||||
|
END; $$
|
||||||
|
LANGUAGE plpgsql;
|
||||||
|
SELECT create_distributed_function('sum_avg(NUMERIC[])');
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION sum_avg COST 10000;
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
-- a function with a custom type IN parameter
|
||||||
|
CREATE TYPE intpair AS (x int, y int);
|
||||||
|
CREATE FUNCTION func_custom_param(IN param intpair, OUT total INT)
|
||||||
|
AS $$ SELECT param.x + param.y $$
|
||||||
|
LANGUAGE SQL;
|
||||||
|
SELECT create_distributed_function('func_custom_param(intpair)');
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION func_custom_param RENAME TO func_with_custom_param;
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
|
||||||
|
-- a function that returns TABLE
|
||||||
|
CREATE FUNCTION func_returns_table(IN count INT)
|
||||||
|
RETURNS TABLE (x INT, y INT)
|
||||||
|
AS $$ SELECT i,i FROM generate_series(1,count) i $$
|
||||||
|
LANGUAGE SQL;
|
||||||
|
SELECT create_distributed_function('func_returns_table(INT)');
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER FUNCTION func_returns_table ROWS 100;
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
-- clear objects
|
||||||
|
SET client_min_messages TO WARNING; -- suppress cascading objects dropping
|
||||||
|
|
||||||
|
DROP SCHEMA "CiTuS.TeeN" CASCADE;
|
||||||
|
DROP SCHEMA "CiTUS.TEEN2" CASCADE;
|
||||||
|
DROP SCHEMA function_tests CASCADE;
|
||||||
|
|
||||||
|
SELECT run_command_on_workers($$
|
||||||
|
DROP SCHEMA "CiTuS.TeeN" CASCADE;
|
||||||
|
DROP SCHEMA "CiTUS.TEEN2" CASCADE;
|
||||||
|
DROP SCHEMA function_tests CASCADE;
|
||||||
|
$$);
|
||||||
|
|
||||||
|
DROP ROLE function_role;
|
|
@ -0,0 +1,212 @@
|
||||||
|
--
|
||||||
|
-- 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
|
||||||
|
SELECT substring(:'server_version', '\d+')::int > 10 AS version_above_ten;
|
||||||
|
|
||||||
|
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$;
|
||||||
|
SELECT create_distributed_function('raise_info(text)');
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info CALLED ON NULL INPUT
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info RETURNS NULL ON NULL INPUT
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info STRICT
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info IMMUTABLE
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info STABLE
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info VOLATILE
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info LEAKPROOF
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info NOT LEAKPROOF
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info EXTERNAL SECURITY INVOKER
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info SECURITY INVOKER
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info EXTERNAL SECURITY DEFINER
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info SECURITY DEFINER
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info PARALLEL UNSAFE
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info PARALLEL RESTRICTED
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info PARALLEL SAFE
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
|
||||||
|
-- The COST/ROWS arguments should always be numeric
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info COST 1234
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info COST 1234.5
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info ROWS 10
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info ROWS 10.8
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info SECURITY INVOKER SET client_min_messages TO warning;
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info SET log_min_messages = ERROR
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info SET log_min_messages TO DEFAULT
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info SET log_min_messages FROM CURRENT
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info RESET log_min_messages
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info RESET ALL
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info RENAME TO summation
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
CREATE ROLE PROCEDURE_role;
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info OWNER TO PROCEDURE_role
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info OWNER TO missing_role
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info SET SCHEMA public
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
ALTER PROCEDURE raise_info DEPENDS ON EXTENSION citus
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
DROP PROCEDURE IF EXISTS raise_info(int,int);
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
-- 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$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
DROP PROCEDURE IF EXISTS missing_schema.missing_PROCEDURE(int,float);
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
SELECT deparse_and_run_on_workers($cmd$
|
||||||
|
DROP PROCEDURE IF EXISTS missing_schema.missing_PROCEDURE(int,float) CASCADE;
|
||||||
|
$cmd$);
|
||||||
|
|
||||||
|
-- clear objects
|
||||||
|
SET client_min_messages TO WARNING; -- suppress cascading objects dropping
|
||||||
|
DROP SCHEMA procedure_tests CASCADE;
|
||||||
|
DROP ROLE PROCEDURE_role;
|
Loading…
Reference in New Issue