mirror of
https://github.com/timescale/timescaledb.git
synced 2025-05-26 00:00:54 +08:00
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:
parent
b01cc6ef1b
commit
cfc72be01d
12
src/guc.c
12
src/guc.c
@ -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 "
|
||||
|
@ -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;
|
||||
|
@ -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 = {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user