mirror of
https://github.com/timescale/timescaledb.git
synced 2025-05-26 08:41:09 +08:00
Add debug_optimizer_flags GUC option
Add a new `debug_optimizer_flags` option where you can request optimization debug notices. With this commit, the flags `show_upper` and `show_rel` are added. The flag `show_upper` will permit sending back a message with the resulting relation after creating upper paths. Since this is called at different stages, the flag support setting specific stages where printouts should be done using the format `show_upper=window,final`. The flag `show_rel` will permit sending back the resulting relation after executing `set_rel_pathlist` to add new paths for consideration. The actual implementation to sent the notices will be in a separate commit.
This commit is contained in:
parent
4e004c5564
commit
ad8b70ac12
@ -67,6 +67,8 @@ set(SOURCES
|
||||
if (CMAKE_BUILD_TYPE MATCHES Debug)
|
||||
set(TS_DEBUG 1)
|
||||
set(DEBUG 1)
|
||||
list(APPEND SOURCES
|
||||
debug_guc.c)
|
||||
endif (CMAKE_BUILD_TYPE MATCHES Debug)
|
||||
|
||||
include(build-defs.cmake)
|
||||
|
215
src/debug_guc.c
Normal file
215
src/debug_guc.c
Normal file
@ -0,0 +1,215 @@
|
||||
/*
|
||||
* This file and its contents are licensed under the Apache License 2.0.
|
||||
* Please see the included NOTICE for copyright information and
|
||||
* LICENSE-APACHE for a copy of the license.
|
||||
*/
|
||||
#include <postgres.h>
|
||||
#include <fmgr.h>
|
||||
#include "debug_guc.h"
|
||||
|
||||
#include <utils/builtins.h>
|
||||
#include <utils/guc.h>
|
||||
#if PG10_GE
|
||||
#include <utils/varlena.h>
|
||||
#endif
|
||||
|
||||
TSDLLEXPORT DebugOptimizerFlags ts_debug_optimizer_flags;
|
||||
|
||||
enum DebugFlag
|
||||
{
|
||||
DEBUG_FLAG_UPPER,
|
||||
DEBUG_FLAG_REL
|
||||
};
|
||||
|
||||
struct DebugFlagDef
|
||||
{
|
||||
const char *name;
|
||||
enum DebugFlag flag;
|
||||
};
|
||||
|
||||
static struct DebugFlagDef g_flag_names[] = {
|
||||
/* Show paths considered when planning a query. */
|
||||
{ "show_upper_paths", DEBUG_FLAG_UPPER },
|
||||
/* Show relations generated when planning a query. */
|
||||
{ "show_rel_pathlist", DEBUG_FLAG_REL },
|
||||
};
|
||||
|
||||
static unsigned long
|
||||
get_show_upper_mask(const char *paths, size_t paths_len)
|
||||
{
|
||||
unsigned long mask = 0UL;
|
||||
const char *beg = paths;
|
||||
|
||||
/* We can return early if there are no flags provided */
|
||||
if (paths_len == 0)
|
||||
return mask;
|
||||
|
||||
while (true)
|
||||
{
|
||||
const char *const maybe_end = strchr(beg, ',');
|
||||
const char *const end = maybe_end == NULL ? paths + paths_len : maybe_end;
|
||||
const size_t len = end - beg;
|
||||
if (len > 0)
|
||||
{
|
||||
/* For each of the checks below, we check the provided string and
|
||||
* allow a prefix to the full name, so "fin" will match
|
||||
* "final". We have special support for "*" to denote setting all
|
||||
* stages. */
|
||||
if (strncmp(beg, "*", len) == 0)
|
||||
mask |= ~0UL;
|
||||
else if (strncmp(beg, "setop", len) == 0)
|
||||
mask |= STAGE_SETOP;
|
||||
#if PG11_GE
|
||||
else if (strncmp(beg, "partial_group_agg", len) == 0)
|
||||
mask |= STAGE_PARTIAL_GROUP_AGG;
|
||||
#endif
|
||||
else if (strncmp(beg, "group_agg", len) == 0)
|
||||
mask |= STAGE_GROUP_AGG;
|
||||
else if (strncmp(beg, "window", len) == 0)
|
||||
mask |= STAGE_WINDOW;
|
||||
else if (strncmp(beg, "distinct", len) == 0)
|
||||
mask |= STAGE_DISTINCT;
|
||||
else if (strncmp(beg, "ordered", len) == 0)
|
||||
mask |= STAGE_ORDERED;
|
||||
else if (strncmp(beg, "final", len) == 0)
|
||||
mask |= STAGE_FINAL;
|
||||
else
|
||||
{
|
||||
char buf[20] = { 0 };
|
||||
char *ptr;
|
||||
strncpy(buf, beg, sizeof(buf));
|
||||
|
||||
/* If the path name was long, make it clear that it is
|
||||
* incomplete in the printout */
|
||||
if (buf[19] != '\0')
|
||||
{
|
||||
buf[19] = '\0';
|
||||
buf[18] = '.';
|
||||
buf[17] = '.';
|
||||
buf[16] = '.';
|
||||
}
|
||||
|
||||
/* Terminate the path if it is followed by a comma */
|
||||
ptr = strchr(buf, ',');
|
||||
if (ptr)
|
||||
*ptr = '\0';
|
||||
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg_internal("unrecognized flag option \"%s\"", buf)));
|
||||
}
|
||||
}
|
||||
if (maybe_end == NULL)
|
||||
break;
|
||||
beg = maybe_end + 1;
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
static bool
|
||||
set_debug_flag(const char *flag_string, size_t length, DebugOptimizerFlags *flags)
|
||||
{
|
||||
int i;
|
||||
char *end;
|
||||
size_t flag_length;
|
||||
|
||||
if ((end = strchr(flag_string, '=')) != NULL)
|
||||
{
|
||||
Assert(end - flag_string >= 0);
|
||||
flag_length = end - flag_string;
|
||||
}
|
||||
else
|
||||
{
|
||||
flag_length = length;
|
||||
}
|
||||
|
||||
for (i = 0; i < sizeof(g_flag_names) / sizeof(*g_flag_names); ++i)
|
||||
if (strncmp(g_flag_names[i].name, flag_string, flag_length) == 0)
|
||||
switch (g_flag_names[i].flag)
|
||||
{
|
||||
case DEBUG_FLAG_UPPER:
|
||||
flags->show_upper = get_show_upper_mask(end + 1, length - flag_length - 1);
|
||||
return true;
|
||||
case DEBUG_FLAG_REL:
|
||||
flags->show_rel = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
parse_optimizer_flags(const char *string, DebugOptimizerFlags *flags)
|
||||
{
|
||||
char *rawname;
|
||||
List *namelist;
|
||||
ListCell *cell;
|
||||
|
||||
Assert(string && flags);
|
||||
|
||||
if (strlen(string) == 0)
|
||||
return true;
|
||||
|
||||
rawname = pstrdup(string);
|
||||
if (!SplitIdentifierString(rawname, ':', &namelist))
|
||||
{
|
||||
GUC_check_errdetail("Invalid flag string syntax.");
|
||||
GUC_check_errhint("The flags string should be a list of colon-separated identifiers.");
|
||||
pfree(rawname);
|
||||
list_free(namelist);
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (cell, namelist)
|
||||
{
|
||||
char *flag_string = (char *) lfirst(cell);
|
||||
if (!set_debug_flag(flag_string, strlen(flag_string), flags))
|
||||
{
|
||||
GUC_check_errdetail("Unrecognized flag setting \"%s\".", flag_string);
|
||||
GUC_check_errhint("Allowed values are: show_upper_paths show_rel_pathlist");
|
||||
pfree(rawname);
|
||||
list_free(namelist);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
pfree(rawname);
|
||||
list_free(namelist);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
debug_optimizer_flags_check(char **newval, void **extra, GucSource source)
|
||||
{
|
||||
DebugOptimizerFlags flags;
|
||||
|
||||
Assert(newval);
|
||||
|
||||
if (*newval)
|
||||
return parse_optimizer_flags(*newval, &flags);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
debug_optimizer_flags_assign(const char *newval, void *extra)
|
||||
{
|
||||
if (newval && !parse_optimizer_flags(newval, &ts_debug_optimizer_flags))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INTERNAL_ERROR),
|
||||
errmsg("cannot parse \"%s\" as debug optimizer flags", newval)));
|
||||
}
|
||||
|
||||
void
|
||||
ts_debug_init(void)
|
||||
{
|
||||
static char *debug_optimizer_flags_string = NULL;
|
||||
DefineCustomStringVariable("timescaledb.debug_optimizer_flags",
|
||||
"List of optimizer debug flags",
|
||||
"A list of flags for configuring the optimizer debug output.",
|
||||
&debug_optimizer_flags_string,
|
||||
NULL,
|
||||
PGC_USERSET,
|
||||
GUC_LIST_INPUT,
|
||||
/* check_hook= */ debug_optimizer_flags_check,
|
||||
/* assign_hook= */ debug_optimizer_flags_assign,
|
||||
/* show_hook= */ NULL);
|
||||
}
|
50
src/debug_guc.h
Normal file
50
src/debug_guc.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This file and its contents are licensed under the Apache License 2.0.
|
||||
* Please see the included NOTICE for copyright information and
|
||||
* LICENSE-APACHE for a copy of the license.
|
||||
*/
|
||||
#ifndef TIMESCALEDB_DEBUG_GUC_H
|
||||
#define TIMESCALEDB_DEBUG_GUC_H
|
||||
|
||||
#include <postgres.h>
|
||||
#include <fmgr.h>
|
||||
#include <utils/guc.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include "export.h"
|
||||
|
||||
/*
|
||||
* Enable printout inside ts_create_upper based on the stage provided. It is
|
||||
* possible to enable printout for multiple stages, so we take the existing
|
||||
* stage list and create a mask from it.
|
||||
*/
|
||||
#define STAGE_SETOP (1UL << UPPERREL_SETOP) /* Enabled using "setop" */
|
||||
#if PG11_GE
|
||||
#define STAGE_PARTIAL_GROUP_AGG \
|
||||
(1UL << UPPERREL_PARTIAL_GROUP_AGG) /* Enabled using "partial_group_agg" */
|
||||
#endif
|
||||
#define STAGE_GROUP_AGG (1UL << UPPERREL_GROUP_AGG) /* Enabled using "group_agg" */
|
||||
#define STAGE_WINDOW (1UL << UPPERREL_WINDOW) /* Enabled using "window" */
|
||||
#define STAGE_DISTINCT (1UL << UPPERREL_DISTINCT) /* Enabled using "distinct" */
|
||||
#define STAGE_ORDERED (1UL << UPPERREL_ORDERED) /* Enabled using "ordered" */
|
||||
#define STAGE_FINAL (1UL << UPPERREL_FINAL) /* Enabled using "final" */
|
||||
|
||||
/*
|
||||
* Debug flags for the optimizer.
|
||||
*
|
||||
* Add new flags here as you see fit, but don't forget to update the flag list
|
||||
* `flag_names` in guc.c.
|
||||
*/
|
||||
typedef struct DebugOptimizerFlags
|
||||
{
|
||||
/* Bit mask to represent set of UpperRelationKind, which is used as the
|
||||
* stage inside create_upper. */
|
||||
unsigned long show_upper;
|
||||
bool show_rel;
|
||||
} DebugOptimizerFlags;
|
||||
|
||||
extern TSDLLEXPORT DebugOptimizerFlags ts_debug_optimizer_flags;
|
||||
|
||||
extern void ts_debug_init(void);
|
||||
|
||||
#endif /* TIMESCALEDB_DEBUG_GUC_H */
|
@ -14,6 +14,7 @@
|
||||
#include "extension.h"
|
||||
#include "bgw/launcher_interface.h"
|
||||
#include "guc.h"
|
||||
#include "debug_guc.h"
|
||||
#include "catalog.h"
|
||||
#include "version.h"
|
||||
#include "compat.h"
|
||||
@ -89,6 +90,7 @@ _PG_init(void)
|
||||
#endif
|
||||
#ifdef TS_DEBUG
|
||||
_conn_mock_init();
|
||||
ts_debug_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
111
test/expected/guc_options.out
Normal file
111
test/expected/guc_options.out
Normal file
@ -0,0 +1,111 @@
|
||||
-- This file and its contents are licensed under the Apache License 2.0.
|
||||
-- Please see the included NOTICE for copyright information and
|
||||
-- LICENSE-APACHE for a copy of the license.
|
||||
\c :TEST_DBNAME :ROLE_SUPERUSER
|
||||
SET timescaledb.debug_optimizer_flags = '';
|
||||
SHOW timescaledb.debug_optimizer_flags;
|
||||
timescaledb.debug_optimizer_flags
|
||||
-----------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=final';
|
||||
SHOW timescaledb.debug_optimizer_flags;
|
||||
timescaledb.debug_optimizer_flags
|
||||
-----------------------------------
|
||||
show_upper=final
|
||||
(1 row)
|
||||
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=fin';
|
||||
SHOW timescaledb.debug_optimizer_flags;
|
||||
timescaledb.debug_optimizer_flags
|
||||
-----------------------------------
|
||||
show_upper=fin
|
||||
(1 row)
|
||||
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=fin,win';
|
||||
SHOW timescaledb.debug_optimizer_flags;
|
||||
timescaledb.debug_optimizer_flags
|
||||
-----------------------------------
|
||||
show_upper=fin,win
|
||||
(1 row)
|
||||
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=*,fin,win';
|
||||
SHOW timescaledb.debug_optimizer_flags;
|
||||
timescaledb.debug_optimizer_flags
|
||||
-----------------------------------
|
||||
show_upper=*,fin,win
|
||||
(1 row)
|
||||
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=*';
|
||||
SHOW timescaledb.debug_optimizer_flags;
|
||||
timescaledb.debug_optimizer_flags
|
||||
-----------------------------------
|
||||
show_upper=*
|
||||
(1 row)
|
||||
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=win:show_rel';
|
||||
SHOW timescaledb.debug_optimizer_flags;
|
||||
timescaledb.debug_optimizer_flags
|
||||
-----------------------------------
|
||||
show_upper=win:show_rel
|
||||
(1 row)
|
||||
|
||||
SET timescaledb.debug_optimizer_flags = '"show_upper=win":show_rel';
|
||||
SHOW timescaledb.debug_optimizer_flags;
|
||||
timescaledb.debug_optimizer_flags
|
||||
-----------------------------------
|
||||
"show_upper=win":show_rel
|
||||
(1 row)
|
||||
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=win : show_rel';
|
||||
SHOW timescaledb.debug_optimizer_flags;
|
||||
timescaledb.debug_optimizer_flags
|
||||
-----------------------------------
|
||||
show_upper=win : show_rel
|
||||
(1 row)
|
||||
|
||||
SET timescaledb.debug_optimizer_flags = 'show_rel:show_upper=win';
|
||||
SHOW timescaledb.debug_optimizer_flags;
|
||||
timescaledb.debug_optimizer_flags
|
||||
-----------------------------------
|
||||
show_rel:show_upper=win
|
||||
(1 row)
|
||||
|
||||
-- These should all fail
|
||||
\set ON_ERROR_STOP 0
|
||||
SET timescaledb.debug_optimizer_flags = NULL;
|
||||
ERROR: syntax error at or near "NULL" at character 41
|
||||
SET timescaledb.debug_optimizer_flags = 'invalid';
|
||||
ERROR: invalid value for parameter "timescaledb.debug_optimizer_flags": "invalid"
|
||||
SET timescaledb.debug_optimizer_flags = '"unmatched quote:';
|
||||
ERROR: invalid value for parameter "timescaledb.debug_optimizer_flags": ""unmatched quote:"
|
||||
SET timescaledb.debug_optimizer_flags = 'space between';
|
||||
ERROR: invalid value for parameter "timescaledb.debug_optimizer_flags": "space between"
|
||||
SET timescaledb.debug_optimizer_flags = 'space between:';
|
||||
ERROR: invalid value for parameter "timescaledb.debug_optimizer_flags": "space between:"
|
||||
SET timescaledb.debug_optimizer_flags = 'show_rel:invalid';
|
||||
ERROR: invalid value for parameter "timescaledb.debug_optimizer_flags": "show_rel:invalid"
|
||||
SET timescaledb.debug_optimizer_flags = 'invalid:show_rel';
|
||||
ERROR: invalid value for parameter "timescaledb.debug_optimizer_flags": "invalid:show_rel"
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=xxx';
|
||||
ERROR: unrecognized flag option "xxx"
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=fin,xxx';
|
||||
ERROR: unrecognized flag option "xxx"
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=xxx,fin';
|
||||
ERROR: unrecognized flag option "xxx"
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=win,xxx,fin';
|
||||
ERROR: unrecognized flag option "xxx"
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=xxx';
|
||||
ERROR: unrecognized flag option "xxx"
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=*,xxx';
|
||||
ERROR: unrecognized flag option "xxx"
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=xxx,*';
|
||||
ERROR: unrecognized flag option "xxx"
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=xxx,*,yyy';
|
||||
ERROR: unrecognized flag option "xxx"
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=supercalifragilisticexpialidochious';
|
||||
ERROR: unrecognized flag option "supercalifragili..."
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=super,califragilisticexpialidochious';
|
||||
ERROR: unrecognized flag option "super"
|
||||
\set ON_ERROR_STOP 1
|
@ -79,6 +79,7 @@ if (CMAKE_BUILD_TYPE MATCHES Debug)
|
||||
bgw_launcher.sql
|
||||
bgw_db_scheduler.sql
|
||||
c_unit_tests.sql
|
||||
guc_options.sql
|
||||
loader.sql
|
||||
metadata.sql
|
||||
net.sql
|
||||
|
46
test/sql/guc_options.sql
Normal file
46
test/sql/guc_options.sql
Normal file
@ -0,0 +1,46 @@
|
||||
-- This file and its contents are licensed under the Apache License 2.0.
|
||||
-- Please see the included NOTICE for copyright information and
|
||||
-- LICENSE-APACHE for a copy of the license.
|
||||
|
||||
\c :TEST_DBNAME :ROLE_SUPERUSER
|
||||
SET timescaledb.debug_optimizer_flags = '';
|
||||
SHOW timescaledb.debug_optimizer_flags;
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=final';
|
||||
SHOW timescaledb.debug_optimizer_flags;
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=fin';
|
||||
SHOW timescaledb.debug_optimizer_flags;
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=fin,win';
|
||||
SHOW timescaledb.debug_optimizer_flags;
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=*,fin,win';
|
||||
SHOW timescaledb.debug_optimizer_flags;
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=*';
|
||||
SHOW timescaledb.debug_optimizer_flags;
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=win:show_rel';
|
||||
SHOW timescaledb.debug_optimizer_flags;
|
||||
SET timescaledb.debug_optimizer_flags = '"show_upper=win":show_rel';
|
||||
SHOW timescaledb.debug_optimizer_flags;
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=win : show_rel';
|
||||
SHOW timescaledb.debug_optimizer_flags;
|
||||
SET timescaledb.debug_optimizer_flags = 'show_rel:show_upper=win';
|
||||
SHOW timescaledb.debug_optimizer_flags;
|
||||
|
||||
-- These should all fail
|
||||
\set ON_ERROR_STOP 0
|
||||
SET timescaledb.debug_optimizer_flags = NULL;
|
||||
SET timescaledb.debug_optimizer_flags = 'invalid';
|
||||
SET timescaledb.debug_optimizer_flags = '"unmatched quote:';
|
||||
SET timescaledb.debug_optimizer_flags = 'space between';
|
||||
SET timescaledb.debug_optimizer_flags = 'space between:';
|
||||
SET timescaledb.debug_optimizer_flags = 'show_rel:invalid';
|
||||
SET timescaledb.debug_optimizer_flags = 'invalid:show_rel';
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=xxx';
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=fin,xxx';
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=xxx,fin';
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=win,xxx,fin';
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=xxx';
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=*,xxx';
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=xxx,*';
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=xxx,*,yyy';
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=supercalifragilisticexpialidochious';
|
||||
SET timescaledb.debug_optimizer_flags = 'show_upper=super,califragilisticexpialidochious';
|
||||
\set ON_ERROR_STOP 1
|
Loading…
x
Reference in New Issue
Block a user