($INBOX_DIR/description missing)
help / color / mirror / Atom feedFrom: Alvaro Herrera <[email protected]>
Subject: [PATCH] add ruleutils.c support for MERGE
Date: Fri, 5 May 2023 20:40:38 +0200
---
src/backend/utils/adt/ruleutils.c | 119 ++++++++++++++++++++++++++++
src/test/regress/expected/merge.out | 68 ++++++++++++++++
src/test/regress/sql/merge.sql | 38 +++++++++
3 files changed, 225 insertions(+)
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 461735e84f0..851488dd6d3 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -411,6 +411,8 @@ static void get_update_query_targetlist_def(Query *query, List *targetList,
RangeTblEntry *rte);
static void get_delete_query_def(Query *query, deparse_context *context,
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_basic_select_query(Query *query, deparse_context *context,
TupleDesc resultDesc, bool colNamesVisible);
@@ -5448,6 +5450,10 @@ get_query_def(Query *query, StringInfo buf, List *parentnamespace,
get_delete_query_def(query, &context, colNamesVisible);
break;
+ case CMD_MERGE:
+ get_merge_query_def(query, &context, colNamesVisible);
+ break;
+
case CMD_NOTHING:
appendStringInfoString(buf, "NOTHING");
break;
@@ -7043,6 +7049,119 @@ 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);
+
+ get_from_clause(query, " USING ", context);
+ appendContextKeyword(context, " ON ",
+ -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
+ get_rule_expr(query->jointree->quals, context, false);
+
+ appendStringInfoChar(buf, '\n'); /* FIXME proper way to do this? */
+
+ /* FIXME missing: OVERRIDING clause */
+ foreach(lc, query->mergeActionList)
+ {
+ MergeAction *action = lfirst_node(MergeAction, lc);
+
+ appendStringInfo(buf, "WHEN %sMATCHED", action->matched ? "" : "NOT ");
+
+ if (action->qual)
+ {
+ appendContextKeyword(context, " AND ",
+ -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
+ get_rule_expr(action->qual, context, false);
+ }
+ appendContextKeyword(context, " THEN ", -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
+
+ if (action->commandType == CMD_INSERT)
+ {
+ ListCell *lc2;
+ char *sep = "";
+ List *strippedexprs = NIL;
+
+ appendStringInfoString(buf, "INSERT ");
+
+ /*
+ * This matches what get_insert_query_def does for the VALUES
+ * clause
+ */
+ if (action->targetList)
+ appendStringInfoChar(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)
+ appendStringInfoString(buf, ") ");
+ if (strippedexprs)
+ {
+ appendContextKeyword(context, "VALUES (",
+ -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
+ 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 ");
+
+ appendStringInfoChar(buf, '\n');
+ }
+
+ /* No RETURNING support yet */
+ Assert(query->returningList == NIL);
+}
/* ----------
* get_utility_query_def - Parse back a UTILITY parsetree
diff --git a/src/test/regress/expected/merge.out b/src/test/regress/expected/merge.out
index 133d42117c0..2e71dda4228 100644
--- a/src/test/regress/expected/merge.out
+++ b/src/test/regress/expected/merge.out
@@ -1319,6 +1319,74 @@ ERROR: syntax error at or near "RETURNING"
LINE 10: RETURNING *;
^
ROLLBACK;
+-- Verify that ruleutils can properly deparse
+CREATE TABLE sf_target(id int, data text, filling int[]);
+CREATE TABLE sf_source(id int, data text);
+CREATE OR REPLACE FUNCTION public.merge_sf_test()
+ RETURNS void
+ LANGUAGE sql
+BEGIN ATOMIC
+ MERGE INTO sf_target
+ USING sf_source s
+ ON (s.id = sf_target.id)
+WHEN MATCHED
+ AND ((s.id + sf_target.id) = 42)
+ THEN UPDATE SET data = (repeat(sf_target.data, s.id) || s.data), id = length(s.data)
+WHEN NOT MATCHED
+ AND (s.data IS NOT NULL)
+ THEN INSERT (data, id)
+ VALUES (s.data, s.id)
+WHEN MATCHED
+ AND (length((s.data || sf_target.data)) > 10)
+ THEN UPDATE SET data = s.data
+WHEN MATCHED
+ THEN UPDATE SET filling[s.id] = sf_target.id
+WHEN NOT MATCHED
+ AND (s.id > 200)
+ THEN INSERT DEFAULT VALUES
+WHEN NOT MATCHED
+ AND (s.id > 100)
+ THEN INSERT (id, data)
+ VALUES (s.id, DEFAULT)
+WHEN NOT MATCHED
+ THEN INSERT (filling[1], id)
+ VALUES (s.id, s.id)
+;
+END;
+\sf+ merge_sf_test
+ CREATE OR REPLACE FUNCTION public.merge_sf_test()
+ RETURNS void
+ LANGUAGE sql
+1 BEGIN ATOMIC
+2 MERGE INTO sf_target
+3 USING sf_source s
+4 ON (s.id = sf_target.id)
+5 WHEN MATCHED
+6 AND ((s.id + sf_target.id) = 42)
+7 THEN UPDATE SET data = (repeat(sf_target.data, s.id) || s.data), id = length(s.data)
+8 WHEN NOT MATCHED
+9 AND (s.data IS NOT NULL)
+10 THEN INSERT (data, id)
+11 VALUES (s.data, s.id)
+12 WHEN MATCHED
+13 AND (length((s.data || sf_target.data)) > 10)
+14 THEN UPDATE SET data = s.data
+15 WHEN MATCHED
+16 THEN UPDATE SET filling[s.id] = sf_target.id
+17 WHEN NOT MATCHED
+18 AND (s.id > 200)
+19 THEN INSERT DEFAULT VALUES
+20 WHEN NOT MATCHED
+21 AND (s.id > 100)
+22 THEN INSERT (id, data)
+23 VALUES (s.id, DEFAULT)
+24 WHEN NOT MATCHED
+25 THEN INSERT (filling[1], id)
+26 VALUES (s.id, s.id)
+27 ;
+28 END
+DROP FUNCTION merge_sf_test;
+DROP TABLE sf_target, sf_source;
-- EXPLAIN
CREATE TABLE ex_mtarget (a int, b int)
WITH (autovacuum_enabled=off);
diff --git a/src/test/regress/sql/merge.sql b/src/test/regress/sql/merge.sql
index 4cf6db908b5..41662089905 100644
--- a/src/test/regress/sql/merge.sql
+++ b/src/test/regress/sql/merge.sql
@@ -870,6 +870,44 @@ WHEN MATCHED AND tid < 2 THEN
RETURNING *;
ROLLBACK;
+-- Verify that ruleutils can properly deparse
+CREATE TABLE sf_target(id int, data text, filling int[]);
+CREATE TABLE sf_source(id int, data text);
+CREATE OR REPLACE FUNCTION public.merge_sf_test()
+ RETURNS void
+ LANGUAGE sql
+BEGIN ATOMIC
+ MERGE INTO sf_target
+ USING sf_source s
+ ON (s.id = sf_target.id)
+WHEN MATCHED
+ AND ((s.id + sf_target.id) = 42)
+ THEN UPDATE SET data = (repeat(sf_target.data, s.id) || s.data), id = length(s.data)
+WHEN NOT MATCHED
+ AND (s.data IS NOT NULL)
+ THEN INSERT (data, id)
+ VALUES (s.data, s.id)
+WHEN MATCHED
+ AND (length((s.data || sf_target.data)) > 10)
+ THEN UPDATE SET data = s.data
+WHEN MATCHED
+ THEN UPDATE SET filling[s.id] = sf_target.id
+WHEN NOT MATCHED
+ AND (s.id > 200)
+ THEN INSERT DEFAULT VALUES
+WHEN NOT MATCHED
+ AND (s.id > 100)
+ THEN INSERT (id, data)
+ VALUES (s.id, DEFAULT)
+WHEN NOT MATCHED
+ THEN INSERT (filling[1], id)
+ VALUES (s.id, s.id)
+;
+END;
+\sf+ merge_sf_test
+DROP FUNCTION merge_sf_test;
+DROP TABLE sf_target, sf_source;
+
-- EXPLAIN
CREATE TABLE ex_mtarget (a int, b int)
WITH (autovacuum_enabled=off);
--
2.39.2
--3dv3bnnc7qew3llg--
view thread (45+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected]
Subject: Re: [PATCH] add ruleutils.c support for MERGE
In-Reply-To: <no-message-id-1856613@localhost>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox