Consider this test case
CREATE TABLE junk2 (a int, b int);
create or replace function blockme() returns int as $$
declare x int;
begin
select count(*) into x from junk2 a, junk2 b;
return x;
end$$ language plpgsql;
select blockme();
The query inside the function is planned like this
LOG: duration: 3.901 ms plan:
Query Text: select count(*) from junk2 a, junk2 b
Aggregate (cost=1.25..1.26 rows=1 width=0)
Output: count(*)
-> Nested Loop (cost=0.00..1.00 rows=100 width=0)
-> Data Node Scan on junk2 "_REMOTE_TABLE_QUERY_" (cost=0.00..0.00 rows=1000 width=0)
Node/s: data_node_1, data_node_2
Remote query: SELECT * FROM ONLY junk2 a WHERE true
-> Data Node Scan on junk2 "_REMOTE_TABLE_QUERY_" (cost=0.00..0.00 rows=1000 width=0)
Node/s: data_node_1, data_node_2
Remote query: SELECT * FROM ONLY junk2 b WHERE true
Note the two data node scans under the nested loop.
The problem is that these data node scans have a NULL step->scan.plan.targetlist.
The reason identified by Ashutosh is as follows:
For count(*) we don't need any column values, so
nothing is fetched, but just the existence of a row is sufficient.
Hence targetlist is NULL, since we don't project anything but the
status that row exists (in the form of tuple slot). We need to fix
this situation in XC, since we are unnecessarily converting a NULL
tlist into * and fetching all the columns of the table (which can be a
performance problem and a column permission problem.)
Why was this problem not uncovered when we worked on the JOIN planner?
1. The earlier code has patched this up in get_target_list() by
converting a NULL targetlist into * while deparsing the query. We
don't want to do that.
2. There is code in executor which allows if the number of columns we
got from datanode are more than the ones in the scan tuple descriptor.
Once this bug is fixed one needs to fix this condition in pgxc_start_command_on_connection function
if (step->base_tlist != NULL ||
step->exec_nodes->accesstype == RELATION_ACCESS_READ ||
step->has_row_marks)
send_desc = true;
The correct condition should be just
if (step->base_tlist != NULL)
send_desc = true;