Commit 37c84570 authored by Robert Haas's avatar Robert Haas

postgres_fdw: Avoid possible misbehavior when RETURNING tableoid column only.

deparseReturningList ended up adding up RETURNING NULL to the code, but
code elsewhere saw an empty list of attributes and concluded that it
should not expect tuples from the remote side.

Etsuro Fujita and Robert Haas, reviewed by Thom Brown
parent 9418d79a
...@@ -112,6 +112,7 @@ static void deparseTargetList(StringInfo buf, ...@@ -112,6 +112,7 @@ static void deparseTargetList(StringInfo buf,
PlannerInfo *root, PlannerInfo *root,
Index rtindex, Index rtindex,
Relation rel, Relation rel,
bool is_returning,
Bitmapset *attrs_used, Bitmapset *attrs_used,
List **retrieved_attrs); List **retrieved_attrs);
static void deparseReturningList(StringInfo buf, PlannerInfo *root, static void deparseReturningList(StringInfo buf, PlannerInfo *root,
...@@ -776,7 +777,7 @@ deparseSelectSql(Bitmapset *attrs_used, List **retrieved_attrs, ...@@ -776,7 +777,7 @@ deparseSelectSql(Bitmapset *attrs_used, List **retrieved_attrs,
* Construct SELECT list * Construct SELECT list
*/ */
appendStringInfoString(buf, "SELECT "); appendStringInfoString(buf, "SELECT ");
deparseTargetList(buf, root, foreignrel->relid, rel, attrs_used, deparseTargetList(buf, root, foreignrel->relid, rel, false, attrs_used,
retrieved_attrs); retrieved_attrs);
/* /*
...@@ -790,7 +791,8 @@ deparseSelectSql(Bitmapset *attrs_used, List **retrieved_attrs, ...@@ -790,7 +791,8 @@ deparseSelectSql(Bitmapset *attrs_used, List **retrieved_attrs,
/* /*
* Emit a target list that retrieves the columns specified in attrs_used. * Emit a target list that retrieves the columns specified in attrs_used.
* This is used for both SELECT and RETURNING targetlists. * This is used for both SELECT and RETURNING targetlists; the is_returning
* parameter is true only for a RETURNING targetlist.
* *
* The tlist text is appended to buf, and we also create an integer List * The tlist text is appended to buf, and we also create an integer List
* of the columns being retrieved, which is returned to *retrieved_attrs. * of the columns being retrieved, which is returned to *retrieved_attrs.
...@@ -800,6 +802,7 @@ deparseTargetList(StringInfo buf, ...@@ -800,6 +802,7 @@ deparseTargetList(StringInfo buf,
PlannerInfo *root, PlannerInfo *root,
Index rtindex, Index rtindex,
Relation rel, Relation rel,
bool is_returning,
Bitmapset *attrs_used, Bitmapset *attrs_used,
List **retrieved_attrs) List **retrieved_attrs)
{ {
...@@ -829,6 +832,8 @@ deparseTargetList(StringInfo buf, ...@@ -829,6 +832,8 @@ deparseTargetList(StringInfo buf,
{ {
if (!first) if (!first)
appendStringInfoString(buf, ", "); appendStringInfoString(buf, ", ");
else if (is_returning)
appendStringInfoString(buf, " RETURNING ");
first = false; first = false;
deparseColumnRef(buf, rtindex, i, root); deparseColumnRef(buf, rtindex, i, root);
...@@ -846,6 +851,8 @@ deparseTargetList(StringInfo buf, ...@@ -846,6 +851,8 @@ deparseTargetList(StringInfo buf,
{ {
if (!first) if (!first)
appendStringInfoString(buf, ", "); appendStringInfoString(buf, ", ");
else if (is_returning)
appendStringInfoString(buf, " RETURNING ");
first = false; first = false;
appendStringInfoString(buf, "ctid"); appendStringInfoString(buf, "ctid");
...@@ -855,7 +862,7 @@ deparseTargetList(StringInfo buf, ...@@ -855,7 +862,7 @@ deparseTargetList(StringInfo buf,
} }
/* Don't generate bad syntax if no undropped columns */ /* Don't generate bad syntax if no undropped columns */
if (first) if (first && !is_returning)
appendStringInfoString(buf, "NULL"); appendStringInfoString(buf, "NULL");
} }
...@@ -1113,11 +1120,8 @@ deparseReturningList(StringInfo buf, PlannerInfo *root, ...@@ -1113,11 +1120,8 @@ deparseReturningList(StringInfo buf, PlannerInfo *root,
} }
if (attrs_used != NULL) if (attrs_used != NULL)
{ deparseTargetList(buf, root, rtindex, rel, true, attrs_used,
appendStringInfoString(buf, " RETURNING ");
deparseTargetList(buf, root, rtindex, rel, attrs_used,
retrieved_attrs); retrieved_attrs);
}
else else
*retrieved_attrs = NIL; *retrieved_attrs = NIL;
} }
......
...@@ -2408,6 +2408,59 @@ SELECT c1,c2,c3,c4 FROM ft2 ORDER BY c1; ...@@ -2408,6 +2408,59 @@ SELECT c1,c2,c3,c4 FROM ft2 ORDER BY c1;
1104 | 204 | ddd | 1104 | 204 | ddd |
(819 rows) (819 rows)
EXPLAIN (verbose, costs off)
INSERT INTO ft2 (c1,c2,c3) VALUES (9999,999,'foo') RETURNING tableoid::regclass;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Insert on public.ft2
Output: (tableoid)::regclass
Remote SQL: INSERT INTO "S 1"."T 1"("C 1", c2, c3, c4, c5, c6, c7, c8) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
-> Result
Output: 9999, 999, NULL::integer, 'foo'::text, NULL::timestamp with time zone, NULL::timestamp without time zone, NULL::character varying, 'ft2 '::character(10), NULL::user_enum
(5 rows)
INSERT INTO ft2 (c1,c2,c3) VALUES (9999,999,'foo') RETURNING tableoid::regclass;
tableoid
----------
ft2
(1 row)
EXPLAIN (verbose, costs off)
UPDATE ft2 SET c3 = 'bar' WHERE c1 = 9999 RETURNING tableoid::regclass;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------
Update on public.ft2
Output: (tableoid)::regclass
Remote SQL: UPDATE "S 1"."T 1" SET c3 = $2 WHERE ctid = $1
-> Foreign Scan on public.ft2
Output: c1, c2, NULL::integer, 'bar'::text, c4, c5, c6, c7, c8, ctid
Remote SQL: SELECT "C 1", c2, c4, c5, c6, c7, c8, ctid FROM "S 1"."T 1" WHERE (("C 1" = 9999)) FOR UPDATE
(6 rows)
UPDATE ft2 SET c3 = 'bar' WHERE c1 = 9999 RETURNING tableoid::regclass;
tableoid
----------
ft2
(1 row)
EXPLAIN (verbose, costs off)
DELETE FROM ft2 WHERE c1 = 9999 RETURNING tableoid::regclass;
QUERY PLAN
------------------------------------------------------------------------------------
Delete on public.ft2
Output: (tableoid)::regclass
Remote SQL: DELETE FROM "S 1"."T 1" WHERE ctid = $1
-> Foreign Scan on public.ft2
Output: ctid
Remote SQL: SELECT ctid FROM "S 1"."T 1" WHERE (("C 1" = 9999)) FOR UPDATE
(6 rows)
DELETE FROM ft2 WHERE c1 = 9999 RETURNING tableoid::regclass;
tableoid
----------
ft2
(1 row)
-- Test that trigger on remote table works as expected -- Test that trigger on remote table works as expected
CREATE OR REPLACE FUNCTION "S 1".F_BRTRIG() RETURNS trigger AS $$ CREATE OR REPLACE FUNCTION "S 1".F_BRTRIG() RETURNS trigger AS $$
BEGIN BEGIN
......
...@@ -413,6 +413,15 @@ EXPLAIN (verbose, costs off) ...@@ -413,6 +413,15 @@ EXPLAIN (verbose, costs off)
DELETE FROM ft2 USING ft1 WHERE ft1.c1 = ft2.c2 AND ft1.c1 % 10 = 2; DELETE FROM ft2 USING ft1 WHERE ft1.c1 = ft2.c2 AND ft1.c1 % 10 = 2;
DELETE FROM ft2 USING ft1 WHERE ft1.c1 = ft2.c2 AND ft1.c1 % 10 = 2; DELETE FROM ft2 USING ft1 WHERE ft1.c1 = ft2.c2 AND ft1.c1 % 10 = 2;
SELECT c1,c2,c3,c4 FROM ft2 ORDER BY c1; SELECT c1,c2,c3,c4 FROM ft2 ORDER BY c1;
EXPLAIN (verbose, costs off)
INSERT INTO ft2 (c1,c2,c3) VALUES (9999,999,'foo') RETURNING tableoid::regclass;
INSERT INTO ft2 (c1,c2,c3) VALUES (9999,999,'foo') RETURNING tableoid::regclass;
EXPLAIN (verbose, costs off)
UPDATE ft2 SET c3 = 'bar' WHERE c1 = 9999 RETURNING tableoid::regclass;
UPDATE ft2 SET c3 = 'bar' WHERE c1 = 9999 RETURNING tableoid::regclass;
EXPLAIN (verbose, costs off)
DELETE FROM ft2 WHERE c1 = 9999 RETURNING tableoid::regclass;
DELETE FROM ft2 WHERE c1 = 9999 RETURNING tableoid::regclass;
-- Test that trigger on remote table works as expected -- Test that trigger on remote table works as expected
CREATE OR REPLACE FUNCTION "S 1".F_BRTRIG() RETURNS trigger AS $$ CREATE OR REPLACE FUNCTION "S 1".F_BRTRIG() RETURNS trigger AS $$
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment