Add ruleutils support for decompiling MERGE commands.
This was overlooked when MERGE was added, but it's essential support for MERGE in new-style SQL functions. Alvaro Herrera Discussion: https://postgr.es/m/3579737.1683293801@sss.pgh.pa.us
This commit is contained in:
parent
58f5edf849
commit
41e2c52fd6
@ -411,6 +411,8 @@ static void get_update_query_targetlist_def(Query *query, List *targetList,
|
|||||||
RangeTblEntry *rte);
|
RangeTblEntry *rte);
|
||||||
static void get_delete_query_def(Query *query, deparse_context *context,
|
static void get_delete_query_def(Query *query, deparse_context *context,
|
||||||
bool colNamesVisible);
|
bool colNamesVisible);
|
||||||
|
static void get_merge_query_def(Query *query, deparse_context *context,
|
||||||
|
bool colNamesVisible);
|
||||||
static void get_utility_query_def(Query *query, deparse_context *context);
|
static void get_utility_query_def(Query *query, deparse_context *context);
|
||||||
static void get_basic_select_query(Query *query, deparse_context *context,
|
static void get_basic_select_query(Query *query, deparse_context *context,
|
||||||
TupleDesc resultDesc, bool colNamesVisible);
|
TupleDesc resultDesc, bool colNamesVisible);
|
||||||
@ -5448,6 +5450,10 @@ get_query_def(Query *query, StringInfo buf, List *parentnamespace,
|
|||||||
get_delete_query_def(query, &context, colNamesVisible);
|
get_delete_query_def(query, &context, colNamesVisible);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CMD_MERGE:
|
||||||
|
get_merge_query_def(query, &context, colNamesVisible);
|
||||||
|
break;
|
||||||
|
|
||||||
case CMD_NOTHING:
|
case CMD_NOTHING:
|
||||||
appendStringInfoString(buf, "NOTHING");
|
appendStringInfoString(buf, "NOTHING");
|
||||||
break;
|
break;
|
||||||
@ -7044,6 +7050,128 @@ get_delete_query_def(Query *query, deparse_context *context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----------
|
||||||
|
* get_merge_query_def - Parse back a MERGE parsetree
|
||||||
|
* ----------
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
get_merge_query_def(Query *query, deparse_context *context,
|
||||||
|
bool colNamesVisible)
|
||||||
|
{
|
||||||
|
StringInfo buf = context->buf;
|
||||||
|
RangeTblEntry *rte;
|
||||||
|
ListCell *lc;
|
||||||
|
|
||||||
|
/* Insert the WITH clause if given */
|
||||||
|
get_with_clause(query, context);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start the query with MERGE INTO relname
|
||||||
|
*/
|
||||||
|
rte = rt_fetch(query->resultRelation, query->rtable);
|
||||||
|
Assert(rte->rtekind == RTE_RELATION);
|
||||||
|
if (PRETTY_INDENT(context))
|
||||||
|
{
|
||||||
|
appendStringInfoChar(buf, ' ');
|
||||||
|
context->indentLevel += PRETTYINDENT_STD;
|
||||||
|
}
|
||||||
|
appendStringInfo(buf, "MERGE INTO %s%s",
|
||||||
|
only_marker(rte),
|
||||||
|
generate_relation_name(rte->relid, NIL));
|
||||||
|
|
||||||
|
/* Print the relation alias, if needed */
|
||||||
|
get_rte_alias(rte, query->resultRelation, false, context);
|
||||||
|
|
||||||
|
/* Print the source relation and join clause */
|
||||||
|
get_from_clause(query, " USING ", context);
|
||||||
|
appendContextKeyword(context, " ON ",
|
||||||
|
-PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
|
||||||
|
get_rule_expr(query->jointree->quals, context, false);
|
||||||
|
|
||||||
|
/* Print each merge action */
|
||||||
|
foreach(lc, query->mergeActionList)
|
||||||
|
{
|
||||||
|
MergeAction *action = lfirst_node(MergeAction, lc);
|
||||||
|
|
||||||
|
appendContextKeyword(context, " WHEN ",
|
||||||
|
-PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
|
||||||
|
appendStringInfo(buf, "%sMATCHED", action->matched ? "" : "NOT ");
|
||||||
|
|
||||||
|
if (action->qual)
|
||||||
|
{
|
||||||
|
appendContextKeyword(context, " AND ",
|
||||||
|
-PRETTYINDENT_STD, PRETTYINDENT_STD, 3);
|
||||||
|
get_rule_expr(action->qual, context, false);
|
||||||
|
}
|
||||||
|
appendContextKeyword(context, " THEN ",
|
||||||
|
-PRETTYINDENT_STD, PRETTYINDENT_STD, 3);
|
||||||
|
|
||||||
|
if (action->commandType == CMD_INSERT)
|
||||||
|
{
|
||||||
|
/* This generally matches get_insert_query_def() */
|
||||||
|
List *strippedexprs = NIL;
|
||||||
|
const char *sep = "";
|
||||||
|
ListCell *lc2;
|
||||||
|
|
||||||
|
appendStringInfoString(buf, "INSERT");
|
||||||
|
|
||||||
|
if (action->targetList)
|
||||||
|
appendStringInfoString(buf, " (");
|
||||||
|
foreach(lc2, action->targetList)
|
||||||
|
{
|
||||||
|
TargetEntry *tle = (TargetEntry *) lfirst(lc2);
|
||||||
|
|
||||||
|
Assert(!tle->resjunk);
|
||||||
|
|
||||||
|
appendStringInfoString(buf, sep);
|
||||||
|
sep = ", ";
|
||||||
|
|
||||||
|
appendStringInfoString(buf,
|
||||||
|
quote_identifier(get_attname(rte->relid,
|
||||||
|
tle->resno,
|
||||||
|
false)));
|
||||||
|
strippedexprs = lappend(strippedexprs,
|
||||||
|
processIndirection((Node *) tle->expr,
|
||||||
|
context));
|
||||||
|
}
|
||||||
|
if (action->targetList)
|
||||||
|
appendStringInfoChar(buf, ')');
|
||||||
|
|
||||||
|
if (action->override)
|
||||||
|
{
|
||||||
|
if (action->override == OVERRIDING_SYSTEM_VALUE)
|
||||||
|
appendStringInfoString(buf, " OVERRIDING SYSTEM VALUE");
|
||||||
|
else if (action->override == OVERRIDING_USER_VALUE)
|
||||||
|
appendStringInfoString(buf, " OVERRIDING USER VALUE");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strippedexprs)
|
||||||
|
{
|
||||||
|
appendContextKeyword(context, " VALUES (",
|
||||||
|
-PRETTYINDENT_STD, PRETTYINDENT_STD, 4);
|
||||||
|
get_rule_list_toplevel(strippedexprs, context, false);
|
||||||
|
appendStringInfoChar(buf, ')');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
appendStringInfoString(buf, " DEFAULT VALUES");
|
||||||
|
}
|
||||||
|
else if (action->commandType == CMD_UPDATE)
|
||||||
|
{
|
||||||
|
appendStringInfoString(buf, "UPDATE SET ");
|
||||||
|
get_update_query_targetlist_def(query, action->targetList,
|
||||||
|
context, rte);
|
||||||
|
}
|
||||||
|
else if (action->commandType == CMD_DELETE)
|
||||||
|
appendStringInfoString(buf, "DELETE");
|
||||||
|
else if (action->commandType == CMD_NOTHING)
|
||||||
|
appendStringInfoString(buf, "DO NOTHING");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No RETURNING support in MERGE yet */
|
||||||
|
Assert(query->returningList == NIL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ----------
|
/* ----------
|
||||||
* get_utility_query_def - Parse back a UTILITY parsetree
|
* get_utility_query_def - Parse back a UTILITY parsetree
|
||||||
* ----------
|
* ----------
|
||||||
|
@ -3585,6 +3585,91 @@ MERGE INTO rule_merge2 t USING (SELECT 1 AS a) s
|
|||||||
DELETE
|
DELETE
|
||||||
WHEN NOT MATCHED THEN
|
WHEN NOT MATCHED THEN
|
||||||
INSERT VALUES (s.a, '');
|
INSERT VALUES (s.a, '');
|
||||||
|
-- test deparsing
|
||||||
|
CREATE TABLE sf_target(id int, data text, filling int[]);
|
||||||
|
CREATE FUNCTION merge_sf_test()
|
||||||
|
RETURNS void
|
||||||
|
LANGUAGE sql
|
||||||
|
BEGIN ATOMIC
|
||||||
|
MERGE INTO sf_target t
|
||||||
|
USING rule_merge1 s
|
||||||
|
ON (s.a = t.id)
|
||||||
|
WHEN MATCHED
|
||||||
|
AND (s.a + t.id) = 42
|
||||||
|
THEN UPDATE SET data = repeat(t.data, s.a) || s.b, id = length(s.b)
|
||||||
|
WHEN NOT MATCHED
|
||||||
|
AND (s.b IS NOT NULL)
|
||||||
|
THEN INSERT (data, id)
|
||||||
|
VALUES (s.b, s.a)
|
||||||
|
WHEN MATCHED
|
||||||
|
AND length(s.b || t.data) > 10
|
||||||
|
THEN UPDATE SET data = s.b
|
||||||
|
WHEN MATCHED
|
||||||
|
AND s.a > 200
|
||||||
|
THEN UPDATE SET filling[s.a] = t.id
|
||||||
|
WHEN MATCHED
|
||||||
|
AND s.a > 100
|
||||||
|
THEN DELETE
|
||||||
|
WHEN MATCHED
|
||||||
|
THEN DO NOTHING
|
||||||
|
WHEN NOT MATCHED
|
||||||
|
AND s.a > 200
|
||||||
|
THEN INSERT DEFAULT VALUES
|
||||||
|
WHEN NOT MATCHED
|
||||||
|
AND s.a > 100
|
||||||
|
THEN INSERT (id, data) OVERRIDING USER VALUE
|
||||||
|
VALUES (s.a, DEFAULT)
|
||||||
|
WHEN NOT MATCHED
|
||||||
|
AND s.a > 0
|
||||||
|
THEN INSERT
|
||||||
|
VALUES (s.a, s.b, DEFAULT)
|
||||||
|
WHEN NOT MATCHED
|
||||||
|
THEN INSERT (filling[1], id)
|
||||||
|
VALUES (s.a, s.a);
|
||||||
|
END;
|
||||||
|
\sf merge_sf_test
|
||||||
|
CREATE OR REPLACE FUNCTION public.merge_sf_test()
|
||||||
|
RETURNS void
|
||||||
|
LANGUAGE sql
|
||||||
|
BEGIN ATOMIC
|
||||||
|
MERGE INTO sf_target t
|
||||||
|
USING rule_merge1 s
|
||||||
|
ON (s.a = t.id)
|
||||||
|
WHEN MATCHED
|
||||||
|
AND ((s.a + t.id) = 42)
|
||||||
|
THEN UPDATE SET data = (repeat(t.data, s.a) || s.b), id = length(s.b)
|
||||||
|
WHEN NOT MATCHED
|
||||||
|
AND (s.b IS NOT NULL)
|
||||||
|
THEN INSERT (data, id)
|
||||||
|
VALUES (s.b, s.a)
|
||||||
|
WHEN MATCHED
|
||||||
|
AND (length((s.b || t.data)) > 10)
|
||||||
|
THEN UPDATE SET data = s.b
|
||||||
|
WHEN MATCHED
|
||||||
|
AND (s.a > 200)
|
||||||
|
THEN UPDATE SET filling[s.a] = t.id
|
||||||
|
WHEN MATCHED
|
||||||
|
AND (s.a > 100)
|
||||||
|
THEN DELETE
|
||||||
|
WHEN MATCHED
|
||||||
|
THEN DO NOTHING
|
||||||
|
WHEN NOT MATCHED
|
||||||
|
AND (s.a > 200)
|
||||||
|
THEN INSERT DEFAULT VALUES
|
||||||
|
WHEN NOT MATCHED
|
||||||
|
AND (s.a > 100)
|
||||||
|
THEN INSERT (id, data) OVERRIDING USER VALUE
|
||||||
|
VALUES (s.a, DEFAULT)
|
||||||
|
WHEN NOT MATCHED
|
||||||
|
AND (s.a > 0)
|
||||||
|
THEN INSERT (id, data, filling)
|
||||||
|
VALUES (s.a, s.b, DEFAULT)
|
||||||
|
WHEN NOT MATCHED
|
||||||
|
THEN INSERT (filling[1], id)
|
||||||
|
VALUES (s.a, s.a);
|
||||||
|
END
|
||||||
|
DROP FUNCTION merge_sf_test;
|
||||||
|
DROP TABLE sf_target;
|
||||||
--
|
--
|
||||||
-- Test enabling/disabling
|
-- Test enabling/disabling
|
||||||
--
|
--
|
||||||
|
@ -1277,6 +1277,55 @@ MERGE INTO rule_merge2 t USING (SELECT 1 AS a) s
|
|||||||
WHEN NOT MATCHED THEN
|
WHEN NOT MATCHED THEN
|
||||||
INSERT VALUES (s.a, '');
|
INSERT VALUES (s.a, '');
|
||||||
|
|
||||||
|
-- test deparsing
|
||||||
|
CREATE TABLE sf_target(id int, data text, filling int[]);
|
||||||
|
|
||||||
|
CREATE FUNCTION merge_sf_test()
|
||||||
|
RETURNS void
|
||||||
|
LANGUAGE sql
|
||||||
|
BEGIN ATOMIC
|
||||||
|
MERGE INTO sf_target t
|
||||||
|
USING rule_merge1 s
|
||||||
|
ON (s.a = t.id)
|
||||||
|
WHEN MATCHED
|
||||||
|
AND (s.a + t.id) = 42
|
||||||
|
THEN UPDATE SET data = repeat(t.data, s.a) || s.b, id = length(s.b)
|
||||||
|
WHEN NOT MATCHED
|
||||||
|
AND (s.b IS NOT NULL)
|
||||||
|
THEN INSERT (data, id)
|
||||||
|
VALUES (s.b, s.a)
|
||||||
|
WHEN MATCHED
|
||||||
|
AND length(s.b || t.data) > 10
|
||||||
|
THEN UPDATE SET data = s.b
|
||||||
|
WHEN MATCHED
|
||||||
|
AND s.a > 200
|
||||||
|
THEN UPDATE SET filling[s.a] = t.id
|
||||||
|
WHEN MATCHED
|
||||||
|
AND s.a > 100
|
||||||
|
THEN DELETE
|
||||||
|
WHEN MATCHED
|
||||||
|
THEN DO NOTHING
|
||||||
|
WHEN NOT MATCHED
|
||||||
|
AND s.a > 200
|
||||||
|
THEN INSERT DEFAULT VALUES
|
||||||
|
WHEN NOT MATCHED
|
||||||
|
AND s.a > 100
|
||||||
|
THEN INSERT (id, data) OVERRIDING USER VALUE
|
||||||
|
VALUES (s.a, DEFAULT)
|
||||||
|
WHEN NOT MATCHED
|
||||||
|
AND s.a > 0
|
||||||
|
THEN INSERT
|
||||||
|
VALUES (s.a, s.b, DEFAULT)
|
||||||
|
WHEN NOT MATCHED
|
||||||
|
THEN INSERT (filling[1], id)
|
||||||
|
VALUES (s.a, s.a);
|
||||||
|
END;
|
||||||
|
|
||||||
|
\sf merge_sf_test
|
||||||
|
|
||||||
|
DROP FUNCTION merge_sf_test;
|
||||||
|
DROP TABLE sf_target;
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Test enabling/disabling
|
-- Test enabling/disabling
|
||||||
--
|
--
|
||||||
|
Loading…
x
Reference in New Issue
Block a user