Show explain from data nodes

We want to get more insights about what plans
are executed on data nodes. If a user runs
explain with verbose option we will connect to each data node,
run explain on data node and
print output together with existing explain output.
This commit is contained in:
niksa 2019-09-26 23:05:21 +02:00 committed by Erik Nordström
parent b01cc6ef1b
commit cfc72be01d
9 changed files with 469 additions and 32 deletions

View File

@ -61,6 +61,7 @@ TSDLLEXPORT bool ts_guc_enable_connection_binary_data;
TSDLLEXPORT bool ts_guc_enable_client_ddl_on_data_nodes = false;
TSDLLEXPORT char *ts_guc_ssl_dir = NULL;
TSDLLEXPORT char *ts_guc_passfile = NULL;
TSDLLEXPORT bool ts_guc_enable_remote_explain = false;
#ifdef TS_DEBUG
bool ts_shutdown_bgw = false;
@ -274,6 +275,17 @@ _guc_init(void)
NULL,
NULL);
DefineCustomBoolVariable("timescaledb.enable_remote_explain",
"Show explain from remote nodes when using VERBOSE flag",
"Enable getting and showing EXPLAIN output from remote nodes",
&ts_guc_enable_remote_explain,
false,
PGC_USERSET,
0,
NULL,
NULL,
NULL);
DefineCustomStringVariable("timescaledb.ssl_dir",
"TimescaleDB user certificate directory",
"Determines a path which is used to search user certificates and "

View File

@ -37,6 +37,7 @@ extern TSDLLEXPORT bool ts_guc_enable_connection_binary_data;
extern TSDLLEXPORT bool ts_guc_enable_client_ddl_on_data_nodes;
extern TSDLLEXPORT char *ts_guc_ssl_dir;
extern TSDLLEXPORT char *ts_guc_passfile;
extern TSDLLEXPORT bool ts_guc_enable_remote_explain;
#ifdef TS_DEBUG
extern bool ts_shutdown_bgw;

View File

@ -23,6 +23,7 @@
#include "data_node_scan_exec.h"
#include "async_append.h"
#include "remote/cursor.h"
#include "guc.h"
/*
* The execution stage of a DataNodeScan.
@ -50,7 +51,7 @@ data_node_scan_begin(CustomScanState *node, EState *estate, int eflags)
List *recheck_quals = lsecond(cscan->custom_exprs);
List *fdw_private = list_nth(cscan->custom_private, 0);
if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
if ((eflags & EXEC_FLAG_EXPLAIN_ONLY) && !ts_guc_enable_remote_explain)
return;
fdw_scan_init(&node->ss, &sss->fsstate, cscan->custom_relids, fdw_private, fdw_exprs, eflags);
@ -138,7 +139,7 @@ data_node_scan_explain(CustomScanState *node, List *ancestors, ExplainState *es)
CustomScan *scan = (CustomScan *) node->ss.ps.plan;
List *fdw_private = list_nth(scan->custom_private, 0);
fdw_scan_explain(&node->ss, fdw_private, es);
fdw_scan_explain(&node->ss, fdw_private, es, &((DataNodeScanState *) node)->fsstate);
}
static CustomExecMethods data_node_scan_state_methods = {

View File

@ -156,7 +156,7 @@ begin_foreign_scan(ForeignScanState *node, int eflags)
{
ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan;
if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
if ((eflags & EXEC_FLAG_EXPLAIN_ONLY) && !ts_guc_enable_remote_explain)
return;
node->fdw_state = (TsFdwScanState *) palloc0(sizeof(TsFdwScanState));
@ -275,7 +275,7 @@ explain_foreign_scan(ForeignScanState *node, struct ExplainState *es)
{
List *fdw_private = ((ForeignScan *) node->ss.ps.plan)->fdw_private;
fdw_scan_explain(&node->ss, fdw_private, es);
fdw_scan_explain(&node->ss, fdw_private, es, (TsFdwScanState *) node->fdw_state);
}
static void

View File

@ -20,6 +20,7 @@
#include "scan_exec.h"
#include "utils.h"
#include "remote/cursor.h"
#include "guc.h"
/*
* Indexes of FDW-private information stored in fdw_private lists.
@ -179,26 +180,14 @@ prepare_query_params(PlanState *node, List *fdw_exprs, int num_params, FmgrInfo
*param_values = (const char **) palloc0(num_params * sizeof(char *));
}
void
fdw_scan_init(ScanState *ss, TsFdwScanState *fsstate, Bitmapset *scanrelids, List *fdw_private,
List *fdw_exprs, int eflags)
static TSConnection *
get_connection(ScanState *ss, Oid const server_id, Bitmapset *scanrelids, List *exprs)
{
Scan *scan = (Scan *) ss->ps.plan;
EState *estate = ss->ps.state;
RangeTblEntry *rte;
TSConnectionId id;
int rtindex;
int num_params;
/*
* Do nothing in EXPLAIN (no ANALYZE) case. fdw_state stays NULL.
*/
if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
return;
/*
* We'll save private state in node->fdw_state.
*/
/*
* Identify which user to do the remote access as. This should match what
@ -213,19 +202,30 @@ fdw_scan_init(ScanState *ss, TsFdwScanState *fsstate, Bitmapset *scanrelids, Lis
rte = rt_fetch(rtindex, estate->es_range_table);
/* Get info about foreign server. */
remote_connection_id_set(&id,
intVal(list_nth(fdw_private, FdwScanPrivateServerId)),
rte->checkAsUser ? rte->checkAsUser : GetUserId());
remote_connection_id_set(&id, server_id, rte->checkAsUser ? rte->checkAsUser : GetUserId());
return remote_dist_txn_get_connection(id,
list_length(exprs) ? REMOTE_TXN_USE_PREP_STMT :
REMOTE_TXN_NO_PREP_STMT);
}
void
fdw_scan_init(ScanState *ss, TsFdwScanState *fsstate, Bitmapset *scanrelids, List *fdw_private,
List *fdw_exprs, int eflags)
{
int num_params;
if ((eflags & EXEC_FLAG_EXPLAIN_ONLY) && !ts_guc_enable_remote_explain)
return;
/*
* Get connection to the foreign server. Connection manager will
* establish new connection if necessary.
*/
fsstate->conn =
remote_dist_txn_get_connection(id,
list_length(fdw_exprs) > 0 ? REMOTE_TXN_USE_PREP_STMT :
REMOTE_TXN_NO_PREP_STMT);
fsstate->conn = get_connection(ss,
intVal(list_nth(fdw_private, FdwScanPrivateServerId)),
scanrelids,
fdw_exprs);
/* Get private info created by planner functions. */
fsstate->query = strVal(list_nth(fdw_private, FdwScanPrivateSelectSql));
@ -319,10 +319,68 @@ fdw_scan_end(TsFdwScanState *fsstate)
/* MemoryContexts will be deleted automatically. */
}
void
fdw_scan_explain(ScanState *ss, List *fdw_private, ExplainState *es)
static char *
get_data_node_explain(const char *sql, TSConnection *conn, ExplainState *es)
{
AsyncRequest *volatile req = NULL;
AsyncResponseResult *volatile res = NULL;
StringInfo explain_sql = makeStringInfo();
StringInfo buf = makeStringInfo();
appendStringInfo(explain_sql, "%s", "EXPLAIN (VERBOSE ");
if (es->analyze)
appendStringInfo(explain_sql, "%s", ", ANALYZE");
if (!es->costs)
appendStringInfo(explain_sql, "%s", ", COSTS OFF");
if (es->buffers)
appendStringInfo(explain_sql, "%s", ", BUFFERS ON");
if (!es->timing)
appendStringInfo(explain_sql, "%s", ", TIMING OFF");
if (es->summary)
appendStringInfo(explain_sql, "%s", ", SUMMARY ON");
else
appendStringInfo(explain_sql, "%s", ", SUMMARY OFF");
appendStringInfoChar(explain_sql, ')');
appendStringInfo(explain_sql, " %s", sql);
PG_TRY();
{
PGresult *pg_res;
int i;
req = async_request_send(conn, explain_sql->data);
res = async_request_wait_ok_result(req);
pg_res = async_response_result_get_pg_result(res);
appendStringInfoChar(buf, '\n');
for (i = 0; i < PQntuples(pg_res); i++)
{
appendStringInfoSpaces(buf, (es->indent + 1) * 2);
appendStringInfo(buf, "%s\n", PQgetvalue(pg_res, i, 0));
}
pfree(req);
async_response_result_close(res);
}
PG_CATCH();
{
if (req != NULL)
pfree(req);
if (res != NULL)
async_response_result_close(res);
PG_RE_THROW();
}
PG_END_TRY();
return buf->data;
}
void
fdw_scan_explain(ScanState *ss, List *fdw_private, ExplainState *es, TsFdwScanState *fsstate)
{
const char *sql;
const char *relations;
/*
@ -365,7 +423,15 @@ fdw_scan_explain(ScanState *ss, List *fdw_private, ExplainState *es)
ExplainPropertyText("Chunks", chunk_names.data, es);
}
sql = strVal(list_nth(fdw_private, FdwScanPrivateSelectSql));
ExplainPropertyText("Remote SQL", sql, es);
ExplainPropertyText("Remote SQL",
strVal(list_nth(fdw_private, FdwScanPrivateSelectSql)),
es);
if (ts_guc_enable_remote_explain)
{
const char *data_node_explain =
get_data_node_explain(fsstate->query, fsstate->conn, es);
ExplainPropertyText("Remote EXPLAIN", data_node_explain, es);
}
}
}

View File

@ -42,7 +42,8 @@ extern void fdw_scan_init(ScanState *ss, TsFdwScanState *fsstate, Bitmapset *sca
extern TupleTableSlot *fdw_scan_iterate(ScanState *ss, TsFdwScanState *fsstate);
extern void fdw_scan_rescan(ScanState *ss, TsFdwScanState *fsstate);
extern void fdw_scan_end(TsFdwScanState *fsstate);
extern void fdw_scan_explain(ScanState *ss, List *fdw_private, ExplainState *es);
extern void fdw_scan_explain(ScanState *ss, List *fdw_private, ExplainState *es,
TsFdwScanState *fsstate);
extern void create_cursor(ScanState *ss, TsFdwScanState *fsstate, bool block);

View File

@ -678,6 +678,175 @@ FROM disttable;
90 | 2.7 | 2.7
(9 rows)
-- Test remote explain
SET timescaledb.enable_remote_explain = ON;
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT max(temp)
FROM disttable;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Result
Output: $0
InitPlan 1 (returns $0)
-> Limit
Output: disttable.temp
-> Custom Scan (AsyncAppend)
Output: disttable.temp
-> Merge Append
Sort Key: disttable_1.temp DESC
-> Custom Scan (DataNodeScan) on public.disttable disttable_1
Output: disttable_1.temp
Data node: data_node_1
Chunks: _hyper_1_1_dist_chunk, _hyper_1_4_dist_chunk
Remote SQL: SELECT temp FROM public.disttable WHERE _timescaledb_internal.chunks_in(disttable, ARRAY[1, 2]) AND ((temp IS NOT NULL)) ORDER BY temp DESC NULLS FIRST
Remote EXPLAIN:
Sort
Output: _hyper_1_1_dist_chunk.temp
Sort Key: _hyper_1_1_dist_chunk.temp DESC
-> Append
-> Seq Scan on _timescaledb_internal._hyper_1_1_dist_chunk
Output: _hyper_1_1_dist_chunk.temp
Filter: (_hyper_1_1_dist_chunk.temp IS NOT NULL)
-> Seq Scan on _timescaledb_internal._hyper_1_4_dist_chunk
Output: _hyper_1_4_dist_chunk.temp
Filter: (_hyper_1_4_dist_chunk.temp IS NOT NULL)
-> Custom Scan (DataNodeScan) on public.disttable disttable_2
Output: disttable_2.temp
Data node: data_node_2
Chunks: _hyper_1_3_dist_chunk, _hyper_1_5_dist_chunk
Remote SQL: SELECT temp FROM public.disttable WHERE _timescaledb_internal.chunks_in(disttable, ARRAY[1, 2]) AND ((temp IS NOT NULL)) ORDER BY temp DESC NULLS FIRST
Remote EXPLAIN:
Sort
Output: _hyper_1_3_dist_chunk.temp
Sort Key: _hyper_1_3_dist_chunk.temp DESC
-> Append
-> Seq Scan on _timescaledb_internal._hyper_1_3_dist_chunk
Output: _hyper_1_3_dist_chunk.temp
Filter: (_hyper_1_3_dist_chunk.temp IS NOT NULL)
-> Seq Scan on _timescaledb_internal._hyper_1_5_dist_chunk
Output: _hyper_1_5_dist_chunk.temp
Filter: (_hyper_1_5_dist_chunk.temp IS NOT NULL)
-> Custom Scan (DataNodeScan) on public.disttable disttable_3
Output: disttable_3.temp
Data node: data_node_3
Chunks: _hyper_1_2_dist_chunk, _hyper_1_6_dist_chunk
Remote SQL: SELECT temp FROM public.disttable WHERE _timescaledb_internal.chunks_in(disttable, ARRAY[1, 2]) AND ((temp IS NOT NULL)) ORDER BY temp DESC NULLS FIRST
Remote EXPLAIN:
Sort
Output: _hyper_1_2_dist_chunk.temp
Sort Key: _hyper_1_2_dist_chunk.temp DESC
-> Append
-> Seq Scan on _timescaledb_internal._hyper_1_2_dist_chunk
Output: _hyper_1_2_dist_chunk.temp
Filter: (_hyper_1_2_dist_chunk.temp IS NOT NULL)
-> Seq Scan on _timescaledb_internal._hyper_1_6_dist_chunk
Output: _hyper_1_6_dist_chunk.temp
Filter: (_hyper_1_6_dist_chunk.temp IS NOT NULL)
(60 rows)
-- Don't remote explain if there is no VERBOSE flag
EXPLAIN (COSTS FALSE)
SELECT max(temp)
FROM disttable;
QUERY PLAN
-------------------------------------------------------------------------------
Result
InitPlan 1 (returns $0)
-> Limit
-> Custom Scan (AsyncAppend)
-> Merge Append
Sort Key: disttable_1.temp DESC
-> Custom Scan (DataNodeScan) on disttable disttable_1
-> Custom Scan (DataNodeScan) on disttable disttable_2
-> Custom Scan (DataNodeScan) on disttable disttable_3
(9 rows)
-- Test additional EXPLAIN flags
EXPLAIN (ANALYZE, VERBOSE, COSTS FALSE, BUFFERS ON, TIMING OFF, SUMMARY OFF)
SELECT max(temp)
FROM disttable;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Result (actual rows=1 loops=1)
Output: $0
InitPlan 1 (returns $0)
-> Limit (actual rows=1 loops=1)
Output: disttable.temp
-> Custom Scan (AsyncAppend) (actual rows=1 loops=1)
Output: disttable.temp
-> Merge Append (actual rows=1 loops=1)
Sort Key: disttable_1.temp DESC
-> Custom Scan (DataNodeScan) on public.disttable disttable_1 (actual rows=1 loops=1)
Output: disttable_1.temp
Data node: data_node_1
Chunks: _hyper_1_1_dist_chunk, _hyper_1_4_dist_chunk
Remote SQL: SELECT temp FROM public.disttable WHERE _timescaledb_internal.chunks_in(disttable, ARRAY[1, 2]) AND ((temp IS NOT NULL)) ORDER BY temp DESC NULLS FIRST
Remote EXPLAIN:
Sort (actual rows=4 loops=1)
Output: _hyper_1_1_dist_chunk.temp
Sort Key: _hyper_1_1_dist_chunk.temp DESC
Sort Method: quicksort
Buffers: shared hit=2
-> Append (actual rows=4 loops=1)
Buffers: shared hit=2
-> Seq Scan on _timescaledb_internal._hyper_1_1_dist_chunk (actual rows=3 loops=1)
Output: _hyper_1_1_dist_chunk.temp
Filter: (_hyper_1_1_dist_chunk.temp IS NOT NULL)
Buffers: shared hit=1
-> Seq Scan on _timescaledb_internal._hyper_1_4_dist_chunk (actual rows=1 loops=1)
Output: _hyper_1_4_dist_chunk.temp
Filter: (_hyper_1_4_dist_chunk.temp IS NOT NULL)
Buffers: shared hit=1
-> Custom Scan (DataNodeScan) on public.disttable disttable_2 (actual rows=1 loops=1)
Output: disttable_2.temp
Data node: data_node_2
Chunks: _hyper_1_3_dist_chunk, _hyper_1_5_dist_chunk
Remote SQL: SELECT temp FROM public.disttable WHERE _timescaledb_internal.chunks_in(disttable, ARRAY[1, 2]) AND ((temp IS NOT NULL)) ORDER BY temp DESC NULLS FIRST
Remote EXPLAIN:
Sort (actual rows=2 loops=1)
Output: _hyper_1_3_dist_chunk.temp
Sort Key: _hyper_1_3_dist_chunk.temp DESC
Sort Method: quicksort
Buffers: shared hit=2
-> Append (actual rows=2 loops=1)
Buffers: shared hit=2
-> Seq Scan on _timescaledb_internal._hyper_1_3_dist_chunk (actual rows=1 loops=1)
Output: _hyper_1_3_dist_chunk.temp
Filter: (_hyper_1_3_dist_chunk.temp IS NOT NULL)
Buffers: shared hit=1
-> Seq Scan on _timescaledb_internal._hyper_1_5_dist_chunk (actual rows=1 loops=1)
Output: _hyper_1_5_dist_chunk.temp
Filter: (_hyper_1_5_dist_chunk.temp IS NOT NULL)
Buffers: shared hit=1
-> Custom Scan (DataNodeScan) on public.disttable disttable_3 (actual rows=1 loops=1)
Output: disttable_3.temp
Data node: data_node_3
Chunks: _hyper_1_2_dist_chunk, _hyper_1_6_dist_chunk
Remote SQL: SELECT temp FROM public.disttable WHERE _timescaledb_internal.chunks_in(disttable, ARRAY[1, 2]) AND ((temp IS NOT NULL)) ORDER BY temp DESC NULLS FIRST
Remote EXPLAIN:
Sort (actual rows=3 loops=1)
Output: _hyper_1_2_dist_chunk.temp
Sort Key: _hyper_1_2_dist_chunk.temp DESC
Sort Method: quicksort
Buffers: shared hit=2
-> Append (actual rows=3 loops=1)
Buffers: shared hit=2
-> Seq Scan on _timescaledb_internal._hyper_1_2_dist_chunk (actual rows=1 loops=1)
Output: _hyper_1_2_dist_chunk.temp
Filter: (_hyper_1_2_dist_chunk.temp IS NOT NULL)
Buffers: shared hit=1
-> Seq Scan on _timescaledb_internal._hyper_1_6_dist_chunk (actual rows=2 loops=1)
Output: _hyper_1_6_dist_chunk.temp
Filter: (_hyper_1_6_dist_chunk.temp IS NOT NULL)
Buffers: shared hit=1
(75 rows)
-- The constraints, indexes, and triggers on foreign chunks. Only
-- check constraints should recurse to foreign chunks (although they
-- aren't enforced on a foreign table)

View File

@ -678,6 +678,175 @@ FROM disttable;
90 | 2.7 | 2.7
(9 rows)
-- Test remote explain
SET timescaledb.enable_remote_explain = ON;
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT max(temp)
FROM disttable;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Result
Output: $0
InitPlan 1 (returns $0)
-> Limit
Output: disttable.temp
-> Custom Scan (AsyncAppend)
Output: disttable.temp
-> Merge Append
Sort Key: disttable_1.temp DESC
-> Custom Scan (DataNodeScan) on public.disttable disttable_1
Output: disttable_1.temp
Data node: data_node_1
Chunks: _hyper_1_1_dist_chunk, _hyper_1_4_dist_chunk
Remote SQL: SELECT temp FROM public.disttable WHERE _timescaledb_internal.chunks_in(disttable, ARRAY[1, 2]) AND ((temp IS NOT NULL)) ORDER BY temp DESC NULLS FIRST
Remote EXPLAIN:
Sort
Output: _hyper_1_1_dist_chunk.temp
Sort Key: _hyper_1_1_dist_chunk.temp DESC
-> Append
-> Seq Scan on _timescaledb_internal._hyper_1_1_dist_chunk
Output: _hyper_1_1_dist_chunk.temp
Filter: (_hyper_1_1_dist_chunk.temp IS NOT NULL)
-> Seq Scan on _timescaledb_internal._hyper_1_4_dist_chunk
Output: _hyper_1_4_dist_chunk.temp
Filter: (_hyper_1_4_dist_chunk.temp IS NOT NULL)
-> Custom Scan (DataNodeScan) on public.disttable disttable_2
Output: disttable_2.temp
Data node: data_node_2
Chunks: _hyper_1_3_dist_chunk, _hyper_1_5_dist_chunk
Remote SQL: SELECT temp FROM public.disttable WHERE _timescaledb_internal.chunks_in(disttable, ARRAY[1, 2]) AND ((temp IS NOT NULL)) ORDER BY temp DESC NULLS FIRST
Remote EXPLAIN:
Sort
Output: _hyper_1_3_dist_chunk.temp
Sort Key: _hyper_1_3_dist_chunk.temp DESC
-> Append
-> Seq Scan on _timescaledb_internal._hyper_1_3_dist_chunk
Output: _hyper_1_3_dist_chunk.temp
Filter: (_hyper_1_3_dist_chunk.temp IS NOT NULL)
-> Seq Scan on _timescaledb_internal._hyper_1_5_dist_chunk
Output: _hyper_1_5_dist_chunk.temp
Filter: (_hyper_1_5_dist_chunk.temp IS NOT NULL)
-> Custom Scan (DataNodeScan) on public.disttable disttable_3
Output: disttable_3.temp
Data node: data_node_3
Chunks: _hyper_1_2_dist_chunk, _hyper_1_6_dist_chunk
Remote SQL: SELECT temp FROM public.disttable WHERE _timescaledb_internal.chunks_in(disttable, ARRAY[1, 2]) AND ((temp IS NOT NULL)) ORDER BY temp DESC NULLS FIRST
Remote EXPLAIN:
Sort
Output: _hyper_1_2_dist_chunk.temp
Sort Key: _hyper_1_2_dist_chunk.temp DESC
-> Append
-> Seq Scan on _timescaledb_internal._hyper_1_2_dist_chunk
Output: _hyper_1_2_dist_chunk.temp
Filter: (_hyper_1_2_dist_chunk.temp IS NOT NULL)
-> Seq Scan on _timescaledb_internal._hyper_1_6_dist_chunk
Output: _hyper_1_6_dist_chunk.temp
Filter: (_hyper_1_6_dist_chunk.temp IS NOT NULL)
(60 rows)
-- Don't remote explain if there is no VERBOSE flag
EXPLAIN (COSTS FALSE)
SELECT max(temp)
FROM disttable;
QUERY PLAN
-------------------------------------------------------------------------------
Result
InitPlan 1 (returns $0)
-> Limit
-> Custom Scan (AsyncAppend)
-> Merge Append
Sort Key: disttable_1.temp DESC
-> Custom Scan (DataNodeScan) on disttable disttable_1
-> Custom Scan (DataNodeScan) on disttable disttable_2
-> Custom Scan (DataNodeScan) on disttable disttable_3
(9 rows)
-- Test additional EXPLAIN flags
EXPLAIN (ANALYZE, VERBOSE, COSTS FALSE, BUFFERS ON, TIMING OFF, SUMMARY OFF)
SELECT max(temp)
FROM disttable;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Result (actual rows=1 loops=1)
Output: $0
InitPlan 1 (returns $0)
-> Limit (actual rows=1 loops=1)
Output: disttable.temp
-> Custom Scan (AsyncAppend) (actual rows=1 loops=1)
Output: disttable.temp
-> Merge Append (actual rows=1 loops=1)
Sort Key: disttable_1.temp DESC
-> Custom Scan (DataNodeScan) on public.disttable disttable_1 (actual rows=1 loops=1)
Output: disttable_1.temp
Data node: data_node_1
Chunks: _hyper_1_1_dist_chunk, _hyper_1_4_dist_chunk
Remote SQL: SELECT temp FROM public.disttable WHERE _timescaledb_internal.chunks_in(disttable, ARRAY[1, 2]) AND ((temp IS NOT NULL)) ORDER BY temp DESC NULLS FIRST
Remote EXPLAIN:
Sort (actual rows=4 loops=1)
Output: _hyper_1_1_dist_chunk.temp
Sort Key: _hyper_1_1_dist_chunk.temp DESC
Sort Method: quicksort
Buffers: shared hit=2
-> Append (actual rows=4 loops=1)
Buffers: shared hit=2
-> Seq Scan on _timescaledb_internal._hyper_1_1_dist_chunk (actual rows=3 loops=1)
Output: _hyper_1_1_dist_chunk.temp
Filter: (_hyper_1_1_dist_chunk.temp IS NOT NULL)
Buffers: shared hit=1
-> Seq Scan on _timescaledb_internal._hyper_1_4_dist_chunk (actual rows=1 loops=1)
Output: _hyper_1_4_dist_chunk.temp
Filter: (_hyper_1_4_dist_chunk.temp IS NOT NULL)
Buffers: shared hit=1
-> Custom Scan (DataNodeScan) on public.disttable disttable_2 (actual rows=1 loops=1)
Output: disttable_2.temp
Data node: data_node_2
Chunks: _hyper_1_3_dist_chunk, _hyper_1_5_dist_chunk
Remote SQL: SELECT temp FROM public.disttable WHERE _timescaledb_internal.chunks_in(disttable, ARRAY[1, 2]) AND ((temp IS NOT NULL)) ORDER BY temp DESC NULLS FIRST
Remote EXPLAIN:
Sort (actual rows=2 loops=1)
Output: _hyper_1_3_dist_chunk.temp
Sort Key: _hyper_1_3_dist_chunk.temp DESC
Sort Method: quicksort
Buffers: shared hit=2
-> Append (actual rows=2 loops=1)
Buffers: shared hit=2
-> Seq Scan on _timescaledb_internal._hyper_1_3_dist_chunk (actual rows=1 loops=1)
Output: _hyper_1_3_dist_chunk.temp
Filter: (_hyper_1_3_dist_chunk.temp IS NOT NULL)
Buffers: shared hit=1
-> Seq Scan on _timescaledb_internal._hyper_1_5_dist_chunk (actual rows=1 loops=1)
Output: _hyper_1_5_dist_chunk.temp
Filter: (_hyper_1_5_dist_chunk.temp IS NOT NULL)
Buffers: shared hit=1
-> Custom Scan (DataNodeScan) on public.disttable disttable_3 (actual rows=1 loops=1)
Output: disttable_3.temp
Data node: data_node_3
Chunks: _hyper_1_2_dist_chunk, _hyper_1_6_dist_chunk
Remote SQL: SELECT temp FROM public.disttable WHERE _timescaledb_internal.chunks_in(disttable, ARRAY[1, 2]) AND ((temp IS NOT NULL)) ORDER BY temp DESC NULLS FIRST
Remote EXPLAIN:
Sort (actual rows=3 loops=1)
Output: _hyper_1_2_dist_chunk.temp
Sort Key: _hyper_1_2_dist_chunk.temp DESC
Sort Method: quicksort
Buffers: shared hit=2
-> Append (actual rows=3 loops=1)
Buffers: shared hit=2
-> Seq Scan on _timescaledb_internal._hyper_1_2_dist_chunk (actual rows=1 loops=1)
Output: _hyper_1_2_dist_chunk.temp
Filter: (_hyper_1_2_dist_chunk.temp IS NOT NULL)
Buffers: shared hit=1
-> Seq Scan on _timescaledb_internal._hyper_1_6_dist_chunk (actual rows=2 loops=1)
Output: _hyper_1_6_dist_chunk.temp
Filter: (_hyper_1_6_dist_chunk.temp IS NOT NULL)
Buffers: shared hit=1
(75 rows)
-- The constraints, indexes, and triggers on foreign chunks. Only
-- check constraints should recurse to foreign chunks (although they
-- aren't enforced on a foreign table)

View File

@ -239,6 +239,24 @@ FROM disttable;
SELECT device, temp, avg(temp) OVER (PARTITION BY device)
FROM disttable;
-- Test remote explain
SET timescaledb.enable_remote_explain = ON;
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT max(temp)
FROM disttable;
-- Don't remote explain if there is no VERBOSE flag
EXPLAIN (COSTS FALSE)
SELECT max(temp)
FROM disttable;
-- Test additional EXPLAIN flags
EXPLAIN (ANALYZE, VERBOSE, COSTS FALSE, BUFFERS ON, TIMING OFF, SUMMARY OFF)
SELECT max(temp)
FROM disttable;
-- The constraints, indexes, and triggers on foreign chunks. Only
-- check constraints should recurse to foreign chunks (although they
-- aren't enforced on a foreign table)