pg_explaintips.tgz

application/x-compressed-tar

Filename: pg_explaintips.tgz
Type: application/x-compressed-tar
Part: 0
Message: Re: making EXPLAIN extensible
pg_explaintips/0000755000175000017500000000000014761367265014657 5ustar  guillaumeguillaumepg_explaintips/meson.build0000644000175000017500000000074114761367255017022 0ustar  guillaumeguillaume# Copyright (c) 2022-2025, PostgreSQL Global Development Group

pg_overexplain_sources = files(
  'pg_overexplain.c',
)

if host_system == 'windows'
  pg_overexplain_sources += rc_lib_gen.process(win32ver_rc, extra_args: [
    '--NAME', 'pg_overexplain',
    '--FILEDESC', 'pg_overexplain - allow EXPLAIN to dump even more details',])
endif

pg_overexplain = shared_module('pg_overexplain',
  pg_overexplain_sources,
  kwargs: contrib_mod_args,
)
contrib_targets += pg_overexplain
pg_explaintips/.gitignore0000644000175000017500000000006714761367255016651 0ustar  guillaumeguillaume# Generated subdirectories
/log/
/results/
/tmp_check/
pg_explaintips/Makefile0000644000175000017500000000062514761367255016321 0ustar  guillaumeguillaume# contrib/pg_explaintips/Makefile

MODULE_big = pg_explaintips
OBJS = \
	$(WIN32RES) \
	pg_explaintips.o

PGFILEDESC = "pg_explaintips - add tips to EXPLAIN"

ifdef USE_PGXS
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
else
subdir = contrib/pg_explaintips
top_builddir = ../..
include $(top_builddir)/src/Makefile.global
include $(top_srcdir)/contrib/contrib-global.mk
endif
pg_explaintips/pg_explaintips.c0000644000175000017500000000665514761367255020064 0ustar  guillaumeguillaume/*-------------------------------------------------------------------------
 *
 * pg_explaintips.c
 *	  allow EXPLAIN to dump even more details
 *
 * Copyright (c) 2016-2025, PostgreSQL Global Development Group
 *
 *	  contrib/pg_explaintips/pg_explaintips.c
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "catalog/pg_class.h"
#include "commands/defrem.h"
#include "commands/explain.h"
#include "commands/explain_format.h"
#include "commands/explain_state.h"
#include "fmgr.h"
#include "parser/parsetree.h"
#include "storage/lock.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"

PG_MODULE_MAGIC;

typedef struct
{
	bool		tips;
} explaintips_options;

static explaintips_options *explaintips_ensure_options(ExplainState *es);
static void explaintips_handler(ExplainState *, DefElem *, ParseState *);
static void explaintips_per_node_hook(PlanState *planstate, List *ancestors,
									  const char *relationship,
									  const char *plan_name,
									  ExplainState *es);

static int	es_extension_id;
static explain_per_node_hook_type prev_explain_per_node_hook;

/*
 * Initialization we do when this module is loaded.
 */
void
_PG_init(void)
{
	/* Get an ID that we can use to cache data in an ExplainState. */
	es_extension_id = GetExplainExtensionId("pg_explaintips");

	/* Register the new EXPLAIN options implemented by this module. */
	RegisterExtensionExplainOption("tips", explaintips_handler);

	/* Use the per-node and per-plan hooks to make our options do something. */
	prev_explain_per_node_hook = explain_per_node_hook;
	explain_per_node_hook = explaintips_per_node_hook;
}

/*
 * Get the explaintips_options structure from an ExplainState; if there is
 * none, create one, attach it to the ExplainState, and return it.
 */
static explaintips_options *
explaintips_ensure_options(ExplainState *es)
{
	explaintips_options *options;

	options = GetExplainExtensionState(es, es_extension_id);

	if (options == NULL)
	{
		options = palloc0(sizeof(explaintips_options));
		SetExplainExtensionState(es, es_extension_id, options);
	}

	return options;
}

/*
 * Parse handler for EXPLAIN (DEBUG).
 */
static void
explaintips_handler(ExplainState *es, DefElem *opt, ParseState *pstate)
{
	explaintips_options *options = explaintips_ensure_options(es);

	options->tips = defGetBoolean(opt);
}

/*
 * Print out additional per-node information as appropriate. If the user didn't
 * specify any of the options we support, do nothing; else, print whatever is
 * relevant to the specified options.
 */
static void
explaintips_per_node_hook(PlanState *planstate, List *ancestors,
						  const char *relationship, const char *plan_name,
						  ExplainState *es)
{
	StringInfoData flags;
	explaintips_options *options;
	Plan	   *plan = planstate->plan;

	options = GetExplainExtensionState(es, es_extension_id);
	if (options == NULL)
		return;

	/*
	 * If the "debug" option was given, display miscellaneous fields from the
	 * "Plan" node that would not otherwise be displayed.
	 */
	if (options->tips)
	{
		/* if seqscan and filteredrows/totalsrows>70) --> tips */
		if (nodeTag(plan) == T_SeqScan)
		{
			double  rows = planstate->instrument->ntuples;
			double  nfiltered = planstate->instrument->nfiltered1;
			if (100*nfiltered/(nfiltered+rows) > 70)
			{
				initStringInfo(&flags);
				appendStringInfo(&flags, "You should probably add an index!");
				ExplainPropertyText("Tips", flags.data, es);
			}
		}
	}
}