Properly escape ALTER FUNCTION .. SET deparsing. Also test

pull/3227/head
Marco Slot 2019-11-23 13:01:38 +01:00 committed by Philip Dubé
parent 3c10c27b13
commit 4b0ac4b0dd
5 changed files with 242 additions and 12 deletions

View File

@ -58,6 +58,7 @@ static void AppendDefElemCost(StringInfo buf, DefElem *def);
static void AppendDefElemRows(StringInfo buf, DefElem *def); static void AppendDefElemRows(StringInfo buf, DefElem *def);
static void AppendDefElemSet(StringInfo buf, DefElem *def); static void AppendDefElemSet(StringInfo buf, DefElem *def);
static void AppendVarSetValue(StringInfo buf, VariableSetStmt *setStmt);
static void AppendRenameFunctionStmt(StringInfo buf, RenameStmt *stmt); static void AppendRenameFunctionStmt(StringInfo buf, RenameStmt *stmt);
static void AppendAlterFunctionSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt); static void AppendAlterFunctionSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt);
static void AppendAlterFunctionOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt); static void AppendAlterFunctionOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt);
@ -268,14 +269,12 @@ static void
AppendDefElemSet(StringInfo buf, DefElem *def) AppendDefElemSet(StringInfo buf, DefElem *def)
{ {
VariableSetStmt *setStmt = castNode(VariableSetStmt, def->arg); VariableSetStmt *setStmt = castNode(VariableSetStmt, def->arg);
char *setVariableArgs = ExtractSetVariableArgs(setStmt);
switch (setStmt->kind) switch (setStmt->kind)
{ {
case VAR_SET_VALUE: case VAR_SET_VALUE:
{ {
appendStringInfo(buf, " SET %s = %s", quote_identifier(setStmt->name), AppendVarSetValue(buf, setStmt);
setVariableArgs);
break; break;
} }
@ -315,6 +314,114 @@ AppendDefElemSet(StringInfo buf, DefElem *def)
} }
/*
* AppendVarSetValue deparses a VariableSetStmt with VAR_SET_VALUE_KIND
* It takes from flatten_set_variable_args in postgres's utils/misc/guc.c,
* however flatten_set_variable_args does not apply correct quoting.
*/
static void
AppendVarSetValue(StringInfo buf, VariableSetStmt *setStmt)
{
ListCell *varArgCell = NULL;
ListCell *firstCell = list_head(setStmt->args);
Assert(setStmt->kind == VAR_SET_VALUE);
foreach(varArgCell, setStmt->args)
{
Node *varArgNode = lfirst(varArgCell);
A_Const *varArgConst = NULL;
TypeName *typeName = NULL;
if (IsA(varArgNode, A_Const))
{
varArgConst = (A_Const *) varArgNode;
}
else if (IsA(varArgNode, TypeCast))
{
TypeCast *varArgTypeCast = (TypeCast *) varArgNode;
varArgConst = castNode(A_Const, varArgTypeCast->arg);
typeName = varArgTypeCast->typeName;
}
else
{
elog(ERROR, "unrecognized node type: %d", varArgNode->type);
}
/* don't know how to start SET until we inspect first arg */
if (varArgCell != firstCell)
{
appendStringInfoChar(buf, ',');
}
else if (typeName != NULL)
{
appendStringInfoString(buf, " SET TIME ZONE");
}
else
{
appendStringInfo(buf, " SET %s =", quote_identifier(setStmt->name));
}
Value value = varArgConst->val;
switch (value.type)
{
case T_Integer:
{
appendStringInfo(buf, " %d", intVal(&value));
break;
}
case T_Float:
{
appendStringInfo(buf, " %s", strVal(&value));
break;
}
case T_String:
{
if (typeName != NULL)
{
/*
* Must be a ConstInterval argument for TIME ZONE. Coerce
* to interval and back to normalize the value and account
* for any typmod.
*/
Oid typoid = InvalidOid;
int32 typmod = -1;
typenameTypeIdAndMod(NULL, typeName, &typoid, &typmod);
Assert(typoid == INTERVALOID);
Datum interval =
DirectFunctionCall3(interval_in,
CStringGetDatum(strVal(&value)),
ObjectIdGetDatum(InvalidOid),
Int32GetDatum(typmod));
char *intervalout =
DatumGetCString(DirectFunctionCall1(interval_out,
interval));
appendStringInfo(buf, " INTERVAL '%s'", intervalout);
}
else
{
appendStringInfo(buf, " %s", quote_literal_cstr(strVal(
&value)));
}
break;
}
default:
{
elog(ERROR, "Unexpected Value type in VAR_SET_VALUE arguments.");
break;
}
}
}
}
/* /*
* DeparseRenameFunctionStmt builds and returns a string representing the RenameStmt * DeparseRenameFunctionStmt builds and returns a string representing the RenameStmt
*/ */

View File

@ -318,6 +318,28 @@ SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
t t
(1 row) (1 row)
ALTER FUNCTION add(int,int) SET "citus.setting;'" TO 'hello '' world';
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
verify_function_is_same_on_workers
------------------------------------
t
(1 row)
ALTER FUNCTION add(int,int) RESET "citus.setting;'";
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
verify_function_is_same_on_workers
------------------------------------
t
(1 row)
ALTER FUNCTION add(int,int) SET search_path TO 'sch'';ma', public;
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
verify_function_is_same_on_workers
------------------------------------
t
(1 row)
ALTER FUNCTION add(int,int) RESET search_path;
-- SET ... FROM CURRENT is not supported, verify the query fails with a descriptive error irregardless of where in the action list the statement occurs -- SET ... FROM CURRENT is not supported, verify the query fails with a descriptive error irregardless of where in the action list the statement occurs
ALTER FUNCTION add(int,int) SET client_min_messages FROM CURRENT; ALTER FUNCTION add(int,int) SET client_min_messages FROM CURRENT;
ERROR: unsupported ALTER FUNCTION ... SET ... FROM CURRENT for a distributed function ERROR: unsupported ALTER FUNCTION ... SET ... FROM CURRENT for a distributed function

View File

@ -262,7 +262,7 @@ CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
SELECT deparse_and_run_on_workers($cmd$ SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add SET log_min_messages = ERROR ALTER FUNCTION add SET log_min_messages = ERROR
$cmd$); $cmd$);
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) SET log_min_messages = error; 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 CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
deparse_and_run_on_workers deparse_and_run_on_workers
-------------------------------------- --------------------------------------
@ -292,6 +292,74 @@ CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
(localhost,57638,t,"ALTER FUNCTION") (localhost,57638,t,"ALTER FUNCTION")
(2 rows) (2 rows)
SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add(int, int) SET TIME ZONE INTERVAL '-08:00' HOUR TO MINUTE;
$cmd$);
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) SET TIME ZONE INTERVAL '@ 8 hours ago';
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(int, int) SET TIME ZONE '-7';
$cmd$);
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) SET timezone = '-7';
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(int, int) SET "citus.setting;'" TO 'hello '' world';
$cmd$);
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) SET "citus.setting;'" = 'hello '' world';
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(int, int) SET "citus.setting;'" TO -3.2;
$cmd$);
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) SET "citus.setting;'" = -3.2;
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(int, int) SET "citus.setting;'" TO -32;
$cmd$);
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) SET "citus.setting;'" = -32;
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)
-- This raises an error about only accepting one item,
-- that's okay, we're just testing that we don't produce bad syntax.
SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add(int, int) SET "citus.setting;'" TO 'hello '' world', 'second '' item';
$cmd$);
INFO: Propagating deparsed query: ALTER FUNCTION function_tests.add(integer, integer) SET "citus.setting;'" = 'hello '' world', 'second '' item';
CONTEXT: PL/pgSQL function deparse_and_run_on_workers(text) line 6 at RAISE
deparse_and_run_on_workers
---------------------------------------------------------------------------
(localhost,57637,f,"ERROR: SET citus.setting;' takes only one argument")
(localhost,57638,f,"ERROR: SET citus.setting;' takes only one argument")
(2 rows)
SELECT deparse_and_run_on_workers($cmd$ SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add RESET log_min_messages ALTER FUNCTION add RESET log_min_messages
$cmd$); $cmd$);

View File

@ -223,6 +223,13 @@ ALTER FUNCTION add(int,int) SET client_min_messages TO debug;
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)'); SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
ALTER FUNCTION add(int,int) RESET client_min_messages; ALTER FUNCTION add(int,int) RESET client_min_messages;
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)'); SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
ALTER FUNCTION add(int,int) SET "citus.setting;'" TO 'hello '' world';
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
ALTER FUNCTION add(int,int) RESET "citus.setting;'";
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
ALTER FUNCTION add(int,int) SET search_path TO 'sch'';ma', public;
SELECT public.verify_function_is_same_on_workers('function_tests.add(int,int)');
ALTER FUNCTION add(int,int) RESET search_path;
-- SET ... FROM CURRENT is not supported, verify the query fails with a descriptive error irregardless of where in the action list the statement occurs -- SET ... FROM CURRENT is not supported, verify the query fails with a descriptive error irregardless of where in the action list the statement occurs
ALTER FUNCTION add(int,int) SET client_min_messages FROM CURRENT; ALTER FUNCTION add(int,int) SET client_min_messages FROM CURRENT;

View File

@ -158,6 +158,32 @@ SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add SET log_min_messages FROM CURRENT ALTER FUNCTION add SET log_min_messages FROM CURRENT
$cmd$); $cmd$);
SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add(int, int) SET TIME ZONE INTERVAL '-08:00' HOUR TO MINUTE;
$cmd$);
SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add(int, int) SET TIME ZONE '-7';
$cmd$);
SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add(int, int) SET "citus.setting;'" TO 'hello '' world';
$cmd$);
SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add(int, int) SET "citus.setting;'" TO -3.2;
$cmd$);
SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add(int, int) SET "citus.setting;'" TO -32;
$cmd$);
-- This raises an error about only accepting one item,
-- that's okay, we're just testing that we don't produce bad syntax.
SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add(int, int) SET "citus.setting;'" TO 'hello '' world', 'second '' item';
$cmd$);
SELECT deparse_and_run_on_workers($cmd$ SELECT deparse_and_run_on_workers($cmd$
ALTER FUNCTION add RESET log_min_messages ALTER FUNCTION add RESET log_min_messages
$cmd$); $cmd$);