Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 9bc77c4

Browse files
committed
Various fixes around ON CONFLICT for rule deparsing.
Neither the deparsing of the new alias for INSERT's target table, nor of the inference clause was supported. Also fixup a typo in an error message. Add regression tests to test those code paths. Author: Peter Geoghegan
1 parent 0740cbd commit 9bc77c4

File tree

5 files changed

+140
-23
lines changed

5 files changed

+140
-23
lines changed

src/backend/parser/parse_clause.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -2765,7 +2765,7 @@ transformOnConflictArbiter(ParseState *pstate,
27652765
ereport(ERROR,
27662766
(errcode(ERRCODE_SYNTAX_ERROR),
27672767
errmsg("ON CONFLICT DO UPDATE requires inference specification or constraint name"),
2768-
errhint("For example, ON CONFLICT ON CONFLICT (<column>)."),
2768+
errhint("For example, ON CONFLICT (<column>)."),
27692769
parser_errposition(pstate,
27702770
exprLocation((Node *) onConflictClause))));
27712771

src/backend/utils/adt/ruleutils.c

+77-2
Original file line numberDiff line numberDiff line change
@@ -5392,6 +5392,10 @@ get_insert_query_def(Query *query, deparse_context *context)
53925392
}
53935393
appendStringInfo(buf, "INSERT INTO %s ",
53945394
generate_relation_name(rte->relid, NIL));
5395+
/* INSERT requires AS keyword for target alias */
5396+
if (rte->alias != NULL)
5397+
appendStringInfo(buf, "AS %s ",
5398+
quote_identifier(rte->alias->aliasname));
53955399

53965400
/*
53975401
* Add the insert-column-names list. To handle indirection properly, we
@@ -5479,13 +5483,38 @@ get_insert_query_def(Query *query, deparse_context *context)
54795483
{
54805484
OnConflictExpr *confl = query->onConflict;
54815485

5486+
appendStringInfo(buf, " ON CONFLICT");
5487+
5488+
if (confl->arbiterElems)
5489+
{
5490+
/* Add the single-VALUES expression list */
5491+
appendStringInfoChar(buf, '(');
5492+
get_rule_expr((Node *) confl->arbiterElems, context, false);
5493+
appendStringInfoChar(buf, ')');
5494+
5495+
/* Add a WHERE clause (for partial indexes) if given */
5496+
if (confl->arbiterWhere != NULL)
5497+
{
5498+
appendContextKeyword(context, " WHERE ",
5499+
-PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
5500+
get_rule_expr(confl->arbiterWhere, context, false);
5501+
}
5502+
}
5503+
else
5504+
{
5505+
char *constraint = get_constraint_name(confl->constraint);
5506+
5507+
appendStringInfo(buf, " ON CONSTRAINT %s",
5508+
quote_qualified_identifier(NULL, constraint));
5509+
}
5510+
54825511
if (confl->action == ONCONFLICT_NOTHING)
54835512
{
5484-
appendStringInfoString(buf, " ON CONFLICT DO NOTHING");
5513+
appendStringInfoString(buf, " DO NOTHING");
54855514
}
54865515
else
54875516
{
5488-
appendStringInfoString(buf, " ON CONFLICT DO UPDATE SET ");
5517+
appendStringInfoString(buf, " DO UPDATE SET ");
54895518
/* Deparse targetlist */
54905519
get_update_query_targetlist_def(query, confl->onConflictSet,
54915520
context, rte);
@@ -7886,6 +7915,52 @@ get_rule_expr(Node *node, deparse_context *context,
78867915
}
78877916
break;
78887917

7918+
case T_InferenceElem:
7919+
{
7920+
InferenceElem *iexpr = (InferenceElem *) node;
7921+
bool varprefix = context->varprefix;
7922+
bool need_parens;
7923+
7924+
/*
7925+
* InferenceElem can only refer to target relation, so a
7926+
* prefix is never useful.
7927+
*/
7928+
context->varprefix = false;
7929+
7930+
/*
7931+
* Parenthesize the element unless it's a simple Var or a bare
7932+
* function call. Follows pg_get_indexdef_worker().
7933+
*/
7934+
need_parens = !IsA(iexpr->expr, Var);
7935+
if (IsA(iexpr->expr, FuncExpr) &&
7936+
((FuncExpr *) iexpr->expr)->funcformat ==
7937+
COERCE_EXPLICIT_CALL)
7938+
need_parens = false;
7939+
7940+
if (need_parens)
7941+
appendStringInfoChar(buf, '(');
7942+
get_rule_expr((Node *) iexpr->expr,
7943+
context, false);
7944+
if (need_parens)
7945+
appendStringInfoChar(buf, ')');
7946+
7947+
context->varprefix = varprefix;
7948+
7949+
if (iexpr->infercollid)
7950+
appendStringInfo(buf, " COLLATE %s",
7951+
generate_collation_name(iexpr->infercollid));
7952+
7953+
/* Add the operator class name, if not default */
7954+
if (iexpr->inferopclass)
7955+
{
7956+
Oid inferopclass = iexpr->inferopclass;
7957+
Oid inferopcinputtype = get_opclass_input_type(iexpr->inferopclass);
7958+
7959+
get_opclass_name(inferopclass, inferopcinputtype, buf);
7960+
}
7961+
}
7962+
break;
7963+
78897964
case T_List:
78907965
{
78917966
char *sep;

src/test/regress/expected/insert_conflict.out

+1-1
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ insert into insertconflicttest values (1, 'Apple') on conflict do update set fru
208208
ERROR: ON CONFLICT DO UPDATE requires inference specification or constraint name
209209
LINE 1: ...nsert into insertconflicttest values (1, 'Apple') on conflic...
210210
^
211-
HINT: For example, ON CONFLICT ON CONFLICT (<column>).
211+
HINT: For example, ON CONFLICT (<column>).
212212
-- inference succeeds:
213213
insert into insertconflicttest values (1, 'Apple') on conflict (key) do update set fruit = excluded.fruit;
214214
insert into insertconflicttest values (2, 'Orange') on conflict (key, key, key) do update set fruit = excluded.fruit;

src/test/regress/expected/rules.out

+49-17
Original file line numberDiff line numberDiff line change
@@ -2676,6 +2676,34 @@ Rules:
26762676
ON DELETE TO rules_src DO
26772677
NOTIFY rules_src_deletion
26782678

2679+
--
2680+
-- Ensure a aliased target relation for insert is correctly deparsed.
2681+
--
2682+
create rule r4 as on insert to rules_src do instead insert into rules_log AS trgt SELECT NEW.* RETURNING trgt.f1, trgt.f2;
2683+
create rule r5 as on update to rules_src do instead UPDATE rules_log AS trgt SET tag = 'updated' WHERE trgt.f1 = new.f1;
2684+
\d+ rules_src
2685+
Table "public.rules_src"
2686+
Column | Type | Modifiers | Storage | Stats target | Description
2687+
--------+---------+-----------+---------+--------------+-------------
2688+
f1 | integer | | plain | |
2689+
f2 | integer | | plain | |
2690+
Rules:
2691+
r1 AS
2692+
ON UPDATE TO rules_src DO INSERT INTO rules_log (f1, f2, tag) VALUES (old.f1,old.f2,'old'::text), (new.f1,new.f2,'new'::text)
2693+
r2 AS
2694+
ON UPDATE TO rules_src DO VALUES (old.f1,old.f2,'old'::text), (new.f1,new.f2,'new'::text)
2695+
r3 AS
2696+
ON DELETE TO rules_src DO
2697+
NOTIFY rules_src_deletion
2698+
r4 AS
2699+
ON INSERT TO rules_src DO INSTEAD INSERT INTO rules_log AS trgt (f1, f2) SELECT new.f1,
2700+
new.f2
2701+
RETURNING trgt.f1,
2702+
trgt.f2
2703+
r5 AS
2704+
ON UPDATE TO rules_src DO INSTEAD UPDATE rules_log trgt SET tag = 'updated'::text
2705+
WHERE trgt.f1 = new.f1
2706+
26792707
--
26802708
-- check alter rename rule
26812709
--
@@ -2778,16 +2806,19 @@ CREATE TABLE hats (
27782806
hat_color char(10) -- hat color
27792807
);
27802808
CREATE TABLE hat_data (
2781-
hat_name char(10) primary key,
2809+
hat_name char(10),
27822810
hat_color char(10) -- hat color
27832811
);
2812+
create unique index hat_data_unique_idx
2813+
on hat_data (hat_name COLLATE "C" bpchar_pattern_ops);
27842814
-- okay
27852815
CREATE RULE hat_nosert AS ON INSERT TO hats
27862816
DO INSTEAD
27872817
INSERT INTO hat_data VALUES (
27882818
NEW.hat_name,
27892819
NEW.hat_color)
2790-
ON CONFLICT (hat_name) DO NOTHING RETURNING *;
2820+
ON CONFLICT (hat_name COLLATE "C" bpchar_pattern_ops) WHERE hat_color = 'green'
2821+
DO NOTHING RETURNING *;
27912822
-- Works (projects row)
27922823
INSERT INTO hats VALUES ('h7', 'black') RETURNING *;
27932824
hat_name | hat_color
@@ -2803,12 +2834,13 @@ INSERT INTO hats VALUES ('h7', 'black') RETURNING *;
28032834

28042835
SELECT tablename, rulename, definition FROM pg_rules
28052836
WHERE tablename = 'hats';
2806-
tablename | rulename | definition
2807-
-----------+------------+------------------------------------------------------------------------------
2808-
hats | hat_nosert | CREATE RULE hat_nosert AS +
2809-
| | ON INSERT TO hats DO INSTEAD INSERT INTO hat_data (hat_name, hat_color)+
2810-
| | VALUES (new.hat_name, new.hat_color) ON CONFLICT DO NOTHING +
2811-
| | RETURNING hat_data.hat_name, +
2837+
tablename | rulename | definition
2838+
-----------+------------+---------------------------------------------------------------------------------------------
2839+
hats | hat_nosert | CREATE RULE hat_nosert AS +
2840+
| | ON INSERT TO hats DO INSTEAD INSERT INTO hat_data (hat_name, hat_color) +
2841+
| | VALUES (new.hat_name, new.hat_color) ON CONFLICT(hat_name COLLATE "C" bpchar_pattern_ops)+
2842+
| | WHERE (hat_data.hat_color = 'green'::bpchar) DO NOTHING +
2843+
| | RETURNING hat_data.hat_name, +
28122844
| | hat_data.hat_color;
28132845
(1 row)
28142846

@@ -2861,13 +2893,13 @@ SELECT * FROM hat_data WHERE hat_name = 'h8';
28612893

28622894
SELECT tablename, rulename, definition FROM pg_rules
28632895
WHERE tablename = 'hats';
2864-
tablename | rulename | definition
2865-
-----------+------------+-------------------------------------------------------------------------------------------------------------------------------
2866-
hats | hat_upsert | CREATE RULE hat_upsert AS +
2867-
| | ON INSERT TO hats DO INSTEAD INSERT INTO hat_data (hat_name, hat_color) +
2868-
| | VALUES (new.hat_name, new.hat_color) ON CONFLICT DO UPDATE SET hat_name = hat_data.hat_name, hat_color = excluded.hat_color+
2869-
| | WHERE (excluded.hat_color <> 'forbidden'::bpchar) +
2870-
| | RETURNING hat_data.hat_name, +
2896+
tablename | rulename | definition
2897+
-----------+------------+-----------------------------------------------------------------------------------------------------------------------------------------
2898+
hats | hat_upsert | CREATE RULE hat_upsert AS +
2899+
| | ON INSERT TO hats DO INSTEAD INSERT INTO hat_data (hat_name, hat_color) +
2900+
| | VALUES (new.hat_name, new.hat_color) ON CONFLICT(hat_name) DO UPDATE SET hat_name = hat_data.hat_name, hat_color = excluded.hat_color+
2901+
| | WHERE (excluded.hat_color <> 'forbidden'::bpchar) +
2902+
| | RETURNING hat_data.hat_name, +
28712903
| | hat_data.hat_color;
28722904
(1 row)
28732905

@@ -2877,7 +2909,7 @@ explain (costs off) INSERT INTO hats VALUES ('h8', 'forbidden') RETURNING *;
28772909
----------------------------------------------------------------
28782910
Insert on hat_data
28792911
Conflict Resolution: UPDATE
2880-
Conflict Arbiter Indexes: hat_data_pkey
2912+
Conflict Arbiter Indexes: hat_data_unique_idx
28812913
Conflict Filter: (excluded.hat_color <> 'forbidden'::bpchar)
28822914
-> Result
28832915
(5 rows)
@@ -2909,7 +2941,7 @@ RETURNING *;
29092941
----------------------------------------------------------------
29102942
Insert on hat_data
29112943
Conflict Resolution: UPDATE
2912-
Conflict Arbiter Indexes: hat_data_pkey
2944+
Conflict Arbiter Indexes: hat_data_unique_idx
29132945
Conflict Filter: (excluded.hat_color <> 'forbidden'::bpchar)
29142946
CTE data
29152947
-> Values Scan on "*VALUES*"

src/test/regress/sql/rules.sql

+12-2
Original file line numberDiff line numberDiff line change
@@ -996,6 +996,13 @@ select * from rules_log;
996996
create rule r3 as on delete to rules_src do notify rules_src_deletion;
997997
\d+ rules_src
998998

999+
--
1000+
-- Ensure a aliased target relation for insert is correctly deparsed.
1001+
--
1002+
create rule r4 as on insert to rules_src do instead insert into rules_log AS trgt SELECT NEW.* RETURNING trgt.f1, trgt.f2;
1003+
create rule r5 as on update to rules_src do instead UPDATE rules_log AS trgt SET tag = 'updated' WHERE trgt.f1 = new.f1;
1004+
\d+ rules_src
1005+
9991006
--
10001007
-- check alter rename rule
10011008
--
@@ -1049,17 +1056,20 @@ CREATE TABLE hats (
10491056
);
10501057

10511058
CREATE TABLE hat_data (
1052-
hat_name char(10) primary key,
1059+
hat_name char(10),
10531060
hat_color char(10) -- hat color
10541061
);
1062+
create unique index hat_data_unique_idx
1063+
on hat_data (hat_name COLLATE "C" bpchar_pattern_ops);
10551064

10561065
-- okay
10571066
CREATE RULE hat_nosert AS ON INSERT TO hats
10581067
DO INSTEAD
10591068
INSERT INTO hat_data VALUES (
10601069
NEW.hat_name,
10611070
NEW.hat_color)
1062-
ON CONFLICT (hat_name) DO NOTHING RETURNING *;
1071+
ON CONFLICT (hat_name COLLATE "C" bpchar_pattern_ops) WHERE hat_color = 'green'
1072+
DO NOTHING RETURNING *;
10631073

10641074
-- Works (projects row)
10651075
INSERT INTO hats VALUES ('h7', 'black') RETURNING *;

0 commit comments

Comments
 (0)