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:
Mats Kindahl 2019-09-20 09:45:56 +02:00 committed by Erik Nordström
parent 4e004c5564
commit ad8b70ac12
7 changed files with 427 additions and 0 deletions

View File

@ -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
View 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
View 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 */

View File

@ -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
}

View 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

View File

@ -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
View 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