v2.source_files.tar

application/octet-stream

Filename: v2.source_files.tar
Type: application/octet-stream
Part: 6
Message: Re: support create index on virtual generated column.
./analyze.c0000664000175000017500000027252015221675371011535 0ustar  xmanxman/*-------------------------------------------------------------------------
 *
 * analyze.c
 *	  the Postgres statistics generator
 *
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/commands/analyze.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include <math.h>

#include "access/detoast.h"
#include "access/genam.h"
#include "access/multixact.h"
#include "access/relation.h"
#include "access/table.h"
#include "access/tableam.h"
#include "access/transam.h"
#include "access/tupconvert.h"
#include "access/visibilitymap.h"
#include "access/xact.h"
#include "catalog/index.h"
#include "catalog/indexing.h"
#include "catalog/pg_inherits.h"
#include "commands/progress.h"
#include "commands/tablecmds.h"
#include "commands/vacuum.h"
#include "common/pg_prng.h"
#include "executor/executor.h"
#include "executor/instrument.h"
#include "foreign/fdwapi.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "parser/parse_oper.h"
#include "parser/parse_relation.h"
#include "pgstat.h"
#include "statistics/extended_stats_internal.h"
#include "statistics/statistics.h"
#include "storage/bufmgr.h"
#include "storage/procarray.h"
#include "utils/attoptcache.h"
#include "utils/datum.h"
#include "utils/guc.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/pg_rusage.h"
#include "utils/sampling.h"
#include "utils/sortsupport.h"
#include "utils/syscache.h"
#include "utils/timestamp.h"


/* Per-index data for ANALYZE */
typedef struct AnlIndexData
{
	IndexInfo  *indexInfo;		/* BuildIndexInfo result */
	double		tupleFract;		/* fraction of rows for partial index */
	VacAttrStats **vacattrstats;	/* index attrs to analyze */
	int			attr_cnt;
} AnlIndexData;


/* Default statistics target (GUC parameter) */
int			default_statistics_target = 100;

/* A few variables that don't seem worth passing around as parameters */
static MemoryContext anl_context = NULL;
static BufferAccessStrategy vac_strategy;


static void do_analyze_rel(Relation onerel,
						   const VacuumParams *params, List *va_cols,
						   AcquireSampleRowsFunc acquirefunc, BlockNumber relpages,
						   bool inh, bool in_outer_xact, int elevel);
static void compute_index_stats(Relation onerel, double totalrows,
								AnlIndexData *indexdata, int nindexes,
								HeapTuple *rows, int numrows,
								MemoryContext col_context);
static void validate_va_cols_list(Relation onerel, List *va_cols);
static VacAttrStats *examine_attribute(Relation onerel, int attnum,
									   Node *index_expr);
static int	acquire_sample_rows(Relation onerel, int elevel,
								HeapTuple *rows, int targrows,
								double *totalrows, double *totaldeadrows);
static int	compare_rows(const void *a, const void *b, void *arg);
static int	acquire_inherited_sample_rows(Relation onerel, int elevel,
										  HeapTuple *rows, int targrows,
										  double *totalrows, double *totaldeadrows);
static void update_attstats(Oid relid, bool inh,
							int natts, VacAttrStats **vacattrstats);
static Datum std_fetch_func(VacAttrStatsP stats, int rownum, bool *isNull);
static Datum ind_fetch_func(VacAttrStatsP stats, int rownum, bool *isNull);


/*
 *	analyze_rel() -- analyze one relation
 *
 * relid identifies the relation to analyze.  If relation is supplied, use
 * the name therein for reporting any failure to open/lock the rel; do not
 * use it once we've successfully opened the rel, since it might be stale.
 */
void
analyze_rel(Oid relid, RangeVar *relation,
			const VacuumParams *params, List *va_cols, bool in_outer_xact,
			BufferAccessStrategy bstrategy)
{
	Relation	onerel;
	int			elevel;
	AcquireSampleRowsFunc acquirefunc = NULL;
	BlockNumber relpages = 0;
	bool		stats_imported = false;

	/* Select logging level */
	if (params->options & VACOPT_VERBOSE)
		elevel = INFO;
	else
		elevel = DEBUG2;

	/* Set up static variables */
	vac_strategy = bstrategy;

	/*
	 * Check for user-requested abort.
	 */
	CHECK_FOR_INTERRUPTS();

	/*
	 * Open the relation, getting ShareUpdateExclusiveLock to ensure that two
	 * ANALYZEs don't run on it concurrently.  (This also locks out a
	 * concurrent VACUUM, which doesn't matter much at the moment but might
	 * matter if we ever try to accumulate stats on dead tuples.) If the rel
	 * has been dropped since we last saw it, we don't need to process it.
	 *
	 * Make sure to generate only logs for ANALYZE in this case.
	 */
	onerel = vacuum_open_relation(relid, relation, params->options & ~(VACOPT_VACUUM),
								  params->log_analyze_min_duration >= 0,
								  ShareUpdateExclusiveLock);

	/* leave if relation could not be opened or locked */
	if (!onerel)
		return;

	/*
	 * Check if relation needs to be skipped based on privileges.  This check
	 * happens also when building the relation list to analyze for a manual
	 * operation, and needs to be done additionally here as ANALYZE could
	 * happen across multiple transactions where privileges could have changed
	 * in-between.  Make sure to generate only logs for ANALYZE in this case.
	 */
	if (!vacuum_is_permitted_for_relation(RelationGetRelid(onerel),
										  onerel->rd_rel,
										  params->options & ~VACOPT_VACUUM))
	{
		relation_close(onerel, ShareUpdateExclusiveLock);
		return;
	}

	/*
	 * Silently ignore tables that are temp tables of other backends ---
	 * trying to analyze these is rather pointless, since their contents are
	 * probably not up-to-date on disk.  (We don't throw a warning here; it
	 * would just lead to chatter during a database-wide ANALYZE.)
	 */
	if (RELATION_IS_OTHER_TEMP(onerel))
	{
		relation_close(onerel, ShareUpdateExclusiveLock);
		return;
	}

	/*
	 * We can ANALYZE any table except pg_statistic. See update_attstats
	 */
	if (RelationGetRelid(onerel) == StatisticRelationId)
	{
		relation_close(onerel, ShareUpdateExclusiveLock);
		return;
	}

	/*
	 * Check the given list of columns
	 */
	if (va_cols != NIL)
		validate_va_cols_list(onerel, va_cols);

	/*
	 * Initialize progress reporting before setup for regular/foreign tables.
	 * (For the former, the time spent on it would be negligible, but for the
	 * latter, if FDWs support statistics import or analysis, they'd do some
	 * work that needs the remote access, so the time might be
	 * non-negligible.)
	 */
	pgstat_progress_start_command(PROGRESS_COMMAND_ANALYZE,
								  RelationGetRelid(onerel));
	if (AmAutoVacuumWorkerProcess())
		pgstat_progress_update_param(PROGRESS_ANALYZE_STARTED_BY,
									 PROGRESS_ANALYZE_STARTED_BY_AUTOVACUUM);
	else
		pgstat_progress_update_param(PROGRESS_ANALYZE_STARTED_BY,
									 PROGRESS_ANALYZE_STARTED_BY_MANUAL);

	/*
	 * Check that it's of an analyzable relkind, and set up appropriately.
	 */
	if (onerel->rd_rel->relkind == RELKIND_RELATION ||
		onerel->rd_rel->relkind == RELKIND_MATVIEW)
	{
		/* Regular table, so we'll use the regular row acquisition function */
		acquirefunc = acquire_sample_rows;
		/* Also get regular table's size */
		relpages = RelationGetNumberOfBlocks(onerel);
	}
	else if (onerel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
	{
		/*
		 * For a foreign table, call the FDW's hook functions to see whether
		 * it supports statistics import or analysis.
		 */
		FdwRoutine *fdwroutine;

		fdwroutine = GetFdwRoutineForRelation(onerel, false);

		if (fdwroutine->ImportForeignStatistics != NULL &&
			fdwroutine->ImportForeignStatistics(onerel, va_cols, elevel))
			stats_imported = true;
		else
		{
			bool		ok = false;

			if (fdwroutine->AnalyzeForeignTable != NULL)
				ok = fdwroutine->AnalyzeForeignTable(onerel,
													 &acquirefunc,
													 &relpages);

			if (!ok)
			{
				ereport(WARNING,
						errmsg("skipping \"%s\" -- cannot analyze this foreign table.",
							   RelationGetRelationName(onerel)));
				relation_close(onerel, ShareUpdateExclusiveLock);
				goto out;
			}
		}
	}
	else if (onerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
	{
		/*
		 * For partitioned tables, we want to do the recursive ANALYZE below.
		 */
	}
	else
	{
		/* No need for a WARNING if we already complained during VACUUM */
		if (!(params->options & VACOPT_VACUUM))
			ereport(WARNING,
					(errmsg("skipping \"%s\" --- cannot analyze non-tables or special system tables",
							RelationGetRelationName(onerel))));
		relation_close(onerel, ShareUpdateExclusiveLock);
		goto out;
	}

	/*
	 * Do the normal non-recursive ANALYZE.  We can skip this for partitioned
	 * tables, which don't contain any rows, and foreign tables that
	 * successfully imported statistics.
	 */
	if ((onerel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
		&& !stats_imported)
		do_analyze_rel(onerel, params, va_cols, acquirefunc,
					   relpages, false, in_outer_xact, elevel);

	/*
	 * If there are child tables, do recursive ANALYZE.
	 */
	if (onerel->rd_rel->relhassubclass)
		do_analyze_rel(onerel, params, va_cols, acquirefunc, relpages,
					   true, in_outer_xact, elevel);

	/*
	 * Close source relation now, but keep lock so that no one deletes it
	 * before we commit.  (If someone did, they'd fail to clean up the entries
	 * we made in pg_statistic.  Also, releasing the lock before commit would
	 * expose us to concurrent-update failures in update_attstats.)
	 */
	relation_close(onerel, NoLock);

out:
	pgstat_progress_end_command();
}

/*
 *	do_analyze_rel() -- analyze one relation, recursively or not
 *
 * Note that "acquirefunc" is only relevant for the non-inherited case.
 * For the inherited case, acquire_inherited_sample_rows() determines the
 * appropriate acquirefunc for each child table.
 */
static void
do_analyze_rel(Relation onerel, const VacuumParams *params,
			   List *va_cols, AcquireSampleRowsFunc acquirefunc,
			   BlockNumber relpages, bool inh, bool in_outer_xact,
			   int elevel)
{
	int			attr_cnt,
				tcnt,
				i,
				ind;
	Relation   *Irel;
	int			nindexes;
	bool		verbose,
				instrument,
				hasindex;
	VacAttrStats **vacattrstats;
	AnlIndexData *indexdata;
	int			targrows,
				numrows,
				minrows;
	double		totalrows,
				totaldeadrows;
	HeapTuple  *rows;
	PGRUsage	ru0;
	TimestampTz starttime = 0;
	MemoryContext caller_context;
	Oid			save_userid;
	int			save_sec_context;
	int			save_nestlevel;
	WalUsage	startwalusage = pgWalUsage;
	BufferUsage startbufferusage = pgBufferUsage;
	BufferUsage bufferusage;
	PgStat_Counter startreadtime = 0;
	PgStat_Counter startwritetime = 0;

	verbose = (params->options & VACOPT_VERBOSE) != 0;
	instrument = (verbose || (AmAutoVacuumWorkerProcess() &&
							  params->log_analyze_min_duration >= 0));
	if (inh)
		ereport(elevel,
				(errmsg("analyzing \"%s.%s\" inheritance tree",
						get_namespace_name(RelationGetNamespace(onerel)),
						RelationGetRelationName(onerel))));
	else
		ereport(elevel,
				(errmsg("analyzing \"%s.%s\"",
						get_namespace_name(RelationGetNamespace(onerel)),
						RelationGetRelationName(onerel))));

	/*
	 * Set up a working context so that we can easily free whatever junk gets
	 * created.
	 */
	anl_context = AllocSetContextCreate(CurrentMemoryContext,
										"Analyze",
										ALLOCSET_DEFAULT_SIZES);
	caller_context = MemoryContextSwitchTo(anl_context);

	/*
	 * Switch to the table owner's userid, so that any index functions are run
	 * as that user.  Also lock down security-restricted operations and
	 * arrange to make GUC variable changes local to this command.
	 */
	GetUserIdAndSecContext(&save_userid, &save_sec_context);
	SetUserIdAndSecContext(onerel->rd_rel->relowner,
						   save_sec_context | SECURITY_RESTRICTED_OPERATION);
	save_nestlevel = NewGUCNestLevel();
	RestrictSearchPath();

	/*
	 * When verbose or autovacuum logging is used, initialize a resource usage
	 * snapshot and optionally track I/O timing.
	 */
	if (instrument)
	{
		if (track_io_timing)
		{
			startreadtime = pgStatBlockReadTime;
			startwritetime = pgStatBlockWriteTime;
		}

		pg_rusage_init(&ru0);
	}

	/* Used for instrumentation and stats report */
	starttime = GetCurrentTimestamp();

	/*
	 * Determine which columns to analyze.
	 */
	if (va_cols != NIL)
	{
		ListCell   *le;

		vacattrstats = (VacAttrStats **) palloc(list_length(va_cols) *
												sizeof(VacAttrStats *));
		tcnt = 0;
		foreach(le, va_cols)
		{
			char	   *col = strVal(lfirst(le));

			i = attnameAttNum(onerel, col, false);
			Assert(i != InvalidAttrNumber);
			vacattrstats[tcnt] = examine_attribute(onerel, i, NULL);
			if (vacattrstats[tcnt] != NULL)
				tcnt++;
		}
		attr_cnt = tcnt;
	}
	else
	{
		attr_cnt = onerel->rd_att->natts;
		vacattrstats = (VacAttrStats **)
			palloc(attr_cnt * sizeof(VacAttrStats *));
		tcnt = 0;
		for (i = 1; i <= attr_cnt; i++)
		{
			vacattrstats[tcnt] = examine_attribute(onerel, i, NULL);
			if (vacattrstats[tcnt] != NULL)
				tcnt++;
		}
		attr_cnt = tcnt;
	}

	/*
	 * Open all indexes of the relation, and see if there are any analyzable
	 * columns in the indexes.  We do not analyze index columns if there was
	 * an explicit column list in the ANALYZE command, however.
	 *
	 * If we are doing a recursive scan, we don't want to touch the parent's
	 * indexes at all.  If we're processing a partitioned table, we need to
	 * know if there are any indexes, but we don't want to process them.
	 */
	if (onerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
	{
		List	   *idxs = RelationGetIndexList(onerel);

		Irel = NULL;
		nindexes = 0;
		hasindex = idxs != NIL;
		list_free(idxs);
	}
	else if (!inh)
	{
		vac_open_indexes(onerel, AccessShareLock, &nindexes, &Irel);
		hasindex = nindexes > 0;
	}
	else
	{
		Irel = NULL;
		nindexes = 0;
		hasindex = false;
	}
	indexdata = NULL;
	if (nindexes > 0)
	{
		indexdata = (AnlIndexData *) palloc0(nindexes * sizeof(AnlIndexData));
		for (ind = 0; ind < nindexes; ind++)
		{
			AnlIndexData *thisdata = &indexdata[ind];
			IndexInfo  *indexInfo;

			thisdata->indexInfo = indexInfo = BuildIndexInfo(Irel[ind]);
			thisdata->tupleFract = 1.0; /* fix later if partial */
			if (indexInfo->ii_Expressions != NIL && va_cols == NIL)
			{
				ListCell   *indexpr_item = list_head(indexInfo->ii_Expressions);

				thisdata->vacattrstats = (VacAttrStats **)
					palloc(indexInfo->ii_NumIndexAttrs * sizeof(VacAttrStats *));
				tcnt = 0;
				for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
				{
					int			keycol = indexInfo->ii_IndexAttrNumbers[i];

					if (keycol == 0)
					{
						/* Found an index expression */
						Node	   *indexkey;

						if (indexpr_item == NULL)	/* shouldn't happen */
							elog(ERROR, "too few entries in indexprs list");
						indexkey = (Node *) lfirst(indexpr_item);
						indexpr_item = lnext(indexInfo->ii_Expressions,
											 indexpr_item);
						thisdata->vacattrstats[tcnt] =
							examine_attribute(Irel[ind], i + 1, indexkey);
						if (thisdata->vacattrstats[tcnt] != NULL)
							tcnt++;
					}
				}
				thisdata->attr_cnt = tcnt;
			}
		}
	}

	/*
	 * Determine how many rows we need to sample, using the worst case from
	 * all analyzable columns.  We use a lower bound of 100 rows to avoid
	 * possible overflow in Vitter's algorithm.  (Note: that will also be the
	 * target in the corner case where there are no analyzable columns.)
	 */
	targrows = 100;
	for (i = 0; i < attr_cnt; i++)
	{
		if (targrows < vacattrstats[i]->minrows)
			targrows = vacattrstats[i]->minrows;
	}
	for (ind = 0; ind < nindexes; ind++)
	{
		AnlIndexData *thisdata = &indexdata[ind];

		for (i = 0; i < thisdata->attr_cnt; i++)
		{
			if (targrows < thisdata->vacattrstats[i]->minrows)
				targrows = thisdata->vacattrstats[i]->minrows;
		}
	}

	/*
	 * Look at extended statistics objects too, as those may define custom
	 * statistics target. So we may need to sample more rows and then build
	 * the statistics with enough detail.
	 */
	minrows = ComputeExtStatisticsRows(onerel, attr_cnt, vacattrstats);

	if (targrows < minrows)
		targrows = minrows;

	/*
	 * Acquire the sample rows
	 */
	rows = (HeapTuple *) palloc(targrows * sizeof(HeapTuple));
	pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
								 inh ? PROGRESS_ANALYZE_PHASE_ACQUIRE_SAMPLE_ROWS_INH :
								 PROGRESS_ANALYZE_PHASE_ACQUIRE_SAMPLE_ROWS);
	if (inh)
		numrows = acquire_inherited_sample_rows(onerel, elevel,
												rows, targrows,
												&totalrows, &totaldeadrows);
	else
		numrows = (*acquirefunc) (onerel, elevel,
								  rows, targrows,
								  &totalrows, &totaldeadrows);

	/*
	 * Compute the statistics.  Temporary results during the calculations for
	 * each column are stored in a child context.  The calc routines are
	 * responsible to make sure that whatever they store into the VacAttrStats
	 * structure is allocated in anl_context.
	 */
	if (numrows > 0)
	{
		MemoryContext col_context,
					old_context;

		pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
									 PROGRESS_ANALYZE_PHASE_COMPUTE_STATS);

		col_context = AllocSetContextCreate(anl_context,
											"Analyze Column",
											ALLOCSET_DEFAULT_SIZES);
		old_context = MemoryContextSwitchTo(col_context);

		for (i = 0; i < attr_cnt; i++)
		{
			VacAttrStats *stats = vacattrstats[i];
			AttributeOpts *aopt;

			stats->rows = rows;
			stats->tupDesc = onerel->rd_att;
			stats->compute_stats(stats,
								 std_fetch_func,
								 numrows,
								 totalrows);

			/*
			 * If the appropriate flavor of the n_distinct option is
			 * specified, override with the corresponding value.
			 */
			aopt = get_attribute_options(onerel->rd_id, stats->tupattnum);
			if (aopt != NULL)
			{
				float8		n_distinct;

				n_distinct = inh ? aopt->n_distinct_inherited : aopt->n_distinct;
				if (n_distinct != 0.0)
					stats->stadistinct = n_distinct;
			}

			MemoryContextReset(col_context);
		}

		if (nindexes > 0)
			compute_index_stats(onerel, totalrows,
								indexdata, nindexes,
								rows, numrows,
								col_context);

		MemoryContextSwitchTo(old_context);
		MemoryContextDelete(col_context);

		/*
		 * Emit the completed stats rows into pg_statistic, replacing any
		 * previous statistics for the target columns.  (If there are stats in
		 * pg_statistic for columns we didn't process, we leave them alone.)
		 */
		update_attstats(RelationGetRelid(onerel), inh,
						attr_cnt, vacattrstats);

		for (ind = 0; ind < nindexes; ind++)
		{
			AnlIndexData *thisdata = &indexdata[ind];

			update_attstats(RelationGetRelid(Irel[ind]), false,
							thisdata->attr_cnt, thisdata->vacattrstats);
		}

		/* Build extended statistics (if there are any). */
		BuildRelationExtStatistics(onerel, inh, totalrows, numrows, rows,
								   attr_cnt, vacattrstats);
	}

	pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
								 PROGRESS_ANALYZE_PHASE_FINALIZE_ANALYZE);

	/*
	 * Update pages/tuples stats in pg_class ... but not if we're doing
	 * inherited stats.
	 *
	 * We assume that VACUUM hasn't set pg_class.reltuples already, even
	 * during a VACUUM ANALYZE.  Although VACUUM often updates pg_class,
	 * exceptions exist.  A "VACUUM (ANALYZE, INDEX_CLEANUP OFF)" command will
	 * never update pg_class entries for index relations.  It's also possible
	 * that an individual index's pg_class entry won't be updated during
	 * VACUUM if the index AM returns NULL from its amvacuumcleanup() routine.
	 */
	if (!inh)
	{
		BlockNumber relallvisible = 0;
		BlockNumber relallfrozen = 0;

		if (RELKIND_HAS_STORAGE(onerel->rd_rel->relkind))
			visibilitymap_count(onerel, &relallvisible, &relallfrozen);

		/*
		 * Update pg_class for table relation.  CCI first, in case acquirefunc
		 * updated pg_class.
		 */
		CommandCounterIncrement();
		vac_update_relstats(onerel,
							relpages,
							totalrows,
							relallvisible,
							relallfrozen,
							hasindex,
							InvalidTransactionId,
							InvalidMultiXactId,
							NULL, NULL,
							in_outer_xact);

		/* Same for indexes */
		for (ind = 0; ind < nindexes; ind++)
		{
			AnlIndexData *thisdata = &indexdata[ind];
			double		totalindexrows;

			totalindexrows = ceil(thisdata->tupleFract * totalrows);
			vac_update_relstats(Irel[ind],
								RelationGetNumberOfBlocks(Irel[ind]),
								totalindexrows,
								0, 0,
								false,
								InvalidTransactionId,
								InvalidMultiXactId,
								NULL, NULL,
								in_outer_xact);
		}
	}
	else if (onerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
	{
		/*
		 * Partitioned tables don't have storage, so we don't set any fields
		 * in their pg_class entries except for reltuples and relhasindex.
		 */
		CommandCounterIncrement();
		vac_update_relstats(onerel, -1, totalrows,
							0, 0, hasindex, InvalidTransactionId,
							InvalidMultiXactId,
							NULL, NULL,
							in_outer_xact);
	}

	/*
	 * Now report ANALYZE to the cumulative stats system.  For regular tables,
	 * we do it only if not doing inherited stats.  For partitioned tables, we
	 * only do it for inherited stats. (We're never called for not-inherited
	 * stats on partitioned tables anyway.)
	 *
	 * Reset the mod_since_analyze counter only if we analyzed all columns;
	 * otherwise, there is still work for auto-analyze to do.
	 */
	if (!inh)
		pgstat_report_analyze(onerel, totalrows, totaldeadrows,
							  (va_cols == NIL), starttime);
	else if (onerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
		pgstat_report_analyze(onerel, 0, 0, (va_cols == NIL), starttime);

	/*
	 * If this isn't part of VACUUM ANALYZE, let index AMs do cleanup.
	 *
	 * Note that most index AMs perform a no-op as a matter of policy for
	 * amvacuumcleanup() when called in ANALYZE-only mode.  The only exception
	 * among core index AMs is GIN/ginvacuumcleanup().
	 */
	if (!(params->options & VACOPT_VACUUM))
	{
		for (ind = 0; ind < nindexes; ind++)
		{
			IndexBulkDeleteResult *stats;
			IndexVacuumInfo ivinfo;

			ivinfo.index = Irel[ind];
			ivinfo.heaprel = onerel;
			ivinfo.analyze_only = true;
			ivinfo.estimated_count = true;
			ivinfo.message_level = elevel;
			ivinfo.num_heap_tuples = onerel->rd_rel->reltuples;
			ivinfo.strategy = vac_strategy;

			stats = index_vacuum_cleanup(&ivinfo, NULL);

			if (stats)
				pfree(stats);
		}
	}

	/* Done with indexes */
	vac_close_indexes(nindexes, Irel, NoLock);

	/* Log the action if appropriate */
	if (instrument)
	{
		TimestampTz endtime = GetCurrentTimestamp();

		if (verbose || params->log_analyze_min_duration == 0 ||
			TimestampDifferenceExceeds(starttime, endtime,
									   params->log_analyze_min_duration))
		{
			long		delay_in_ms;
			WalUsage	walusage;
			double		read_rate = 0;
			double		write_rate = 0;
			char	   *msgfmt;
			StringInfoData buf;
			int64		total_blks_hit;
			int64		total_blks_read;
			int64		total_blks_dirtied;

			memset(&bufferusage, 0, sizeof(BufferUsage));
			BufferUsageAccumDiff(&bufferusage, &pgBufferUsage, &startbufferusage);
			memset(&walusage, 0, sizeof(WalUsage));
			WalUsageAccumDiff(&walusage, &pgWalUsage, &startwalusage);

			total_blks_hit = bufferusage.shared_blks_hit +
				bufferusage.local_blks_hit;
			total_blks_read = bufferusage.shared_blks_read +
				bufferusage.local_blks_read;
			total_blks_dirtied = bufferusage.shared_blks_dirtied +
				bufferusage.local_blks_dirtied;

			/*
			 * We do not expect an analyze to take > 25 days and it simplifies
			 * things a bit to use TimestampDifferenceMilliseconds.
			 */
			delay_in_ms = TimestampDifferenceMilliseconds(starttime, endtime);

			/*
			 * Note that we are reporting these read/write rates in the same
			 * manner as VACUUM does, which means that while the 'average read
			 * rate' here actually corresponds to page misses and resulting
			 * reads which are also picked up by track_io_timing, if enabled,
			 * the 'average write rate' is actually talking about the rate of
			 * pages being dirtied, not being written out, so it's typical to
			 * have a non-zero 'avg write rate' while I/O timings only reports
			 * reads.
			 *
			 * It's not clear that an ANALYZE will ever result in
			 * FlushBuffer() being called, but we track and support reporting
			 * on I/O write time in case that changes as it's practically free
			 * to do so anyway.
			 */

			if (delay_in_ms > 0)
			{
				read_rate = (double) BLCKSZ * total_blks_read /
					(1024 * 1024) / (delay_in_ms / 1000.0);
				write_rate = (double) BLCKSZ * total_blks_dirtied /
					(1024 * 1024) / (delay_in_ms / 1000.0);
			}

			/*
			 * We split this up so we don't emit empty I/O timing values when
			 * track_io_timing isn't enabled.
			 */

			initStringInfo(&buf);

			if (AmAutoVacuumWorkerProcess())
				msgfmt = _("automatic analyze of table \"%s.%s.%s\"\n");
			else
				msgfmt = _("finished analyzing table \"%s.%s.%s\"\n");

			appendStringInfo(&buf, msgfmt,
							 get_database_name(MyDatabaseId),
							 get_namespace_name(RelationGetNamespace(onerel)),
							 RelationGetRelationName(onerel));
			if (track_cost_delay_timing)
			{
				/*
				 * We bypass the changecount mechanism because this value is
				 * only updated by the calling process.
				 */
				appendStringInfo(&buf, _("delay time: %.3f ms\n"),
								 (double) MyBEEntry->st_progress_param[PROGRESS_ANALYZE_DELAY_TIME] / 1000000.0);
			}
			if (track_io_timing)
			{
				double		read_ms = (double) (pgStatBlockReadTime - startreadtime) / 1000;
				double		write_ms = (double) (pgStatBlockWriteTime - startwritetime) / 1000;

				appendStringInfo(&buf, _("I/O timings: read: %.3f ms, write: %.3f ms\n"),
								 read_ms, write_ms);
			}
			appendStringInfo(&buf, _("avg read rate: %.3f MB/s, avg write rate: %.3f MB/s\n"),
							 read_rate, write_rate);
			appendStringInfo(&buf, _("buffer usage: %" PRId64 " hits, %" PRId64 " reads, %" PRId64 " dirtied\n"),
							 total_blks_hit,
							 total_blks_read,
							 total_blks_dirtied);
			appendStringInfo(&buf,
							 _("WAL usage: %" PRId64 " records, %" PRId64 " full page images, %" PRIu64 " bytes, %" PRIu64 " full page image bytes, %" PRId64 " buffers full\n"),
							 walusage.wal_records,
							 walusage.wal_fpi,
							 walusage.wal_bytes,
							 walusage.wal_fpi_bytes,
							 walusage.wal_buffers_full);
			appendStringInfo(&buf, _("system usage: %s"), pg_rusage_show(&ru0));

			ereport(verbose ? INFO : LOG,
					(errmsg_internal("%s", buf.data)));

			pfree(buf.data);
		}
	}

	/* Roll back any GUC changes executed by index functions */
	AtEOXact_GUC(false, save_nestlevel);

	/* Restore userid and security context */
	SetUserIdAndSecContext(save_userid, save_sec_context);

	/* Restore current context and release memory */
	MemoryContextSwitchTo(caller_context);
	MemoryContextDelete(anl_context);
	anl_context = NULL;
}

/*
 * Compute statistics about indexes of a relation
 */
static void
compute_index_stats(Relation onerel, double totalrows,
					AnlIndexData *indexdata, int nindexes,
					HeapTuple *rows, int numrows,
					MemoryContext col_context)
{
	MemoryContext ind_context,
				old_context;
	Datum		values[INDEX_MAX_KEYS];
	bool		isnull[INDEX_MAX_KEYS];
	int			ind,
				i;

	ind_context = AllocSetContextCreate(anl_context,
										"Analyze Index",
										ALLOCSET_DEFAULT_SIZES);
	old_context = MemoryContextSwitchTo(ind_context);

	for (ind = 0; ind < nindexes; ind++)
	{
		AnlIndexData *thisdata = &indexdata[ind];
		IndexInfo  *indexInfo = thisdata->indexInfo;
		int			attr_cnt = thisdata->attr_cnt;
		TupleTableSlot *slot;
		EState	   *estate;
		ExprContext *econtext;
		ExprState  *predicateExpand;
		Datum	   *exprvals;
		bool	   *exprnulls;
		int			numindexrows,
					tcnt,
					rowno;
		double		totalindexrows;

		/* Ignore index if no columns to analyze and not partial */
		if (attr_cnt == 0 && indexInfo->ii_PredicateExpand == NIL)
			continue;

		/*
		 * Need an EState for evaluation of index expressions and
		 * partial-index predicates.  Create it in the per-index context to be
		 * sure it gets cleaned up at the bottom of the loop.
		 */
		estate = CreateExecutorState();
		econtext = GetPerTupleExprContext(estate);
		/* Need a slot to hold the current heap tuple, too */
		slot = MakeSingleTupleTableSlot(RelationGetDescr(onerel),
										&TTSOpsHeapTuple);

		/* Arrange for econtext's scan tuple to be the tuple under test */
		econtext->ecxt_scantuple = slot;

		/* Set up execution state for predicate. */
		predicateExpand = ExecPrepareQual(indexInfo->ii_PredicateExpand, estate);

		/* Compute and save index expression values */
		exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum));
		exprnulls = (bool *) palloc(numrows * attr_cnt * sizeof(bool));
		numindexrows = 0;
		tcnt = 0;
		for (rowno = 0; rowno < numrows; rowno++)
		{
			HeapTuple	heapTuple = rows[rowno];

			vacuum_delay_point(true);

			/*
			 * Reset the per-tuple context each time, to reclaim any cruft
			 * left behind by evaluating the predicate or index expressions.
			 */
			ResetExprContext(econtext);

			/* Set up for predicate or expression evaluation */
			ExecStoreHeapTuple(heapTuple, slot, false);

			/* If index is partial, check predicate */
			if (predicateExpand != NULL)
			{
				if (!ExecQual(predicateExpand, econtext))
					continue;
			}
			numindexrows++;

			if (attr_cnt > 0)
			{
				/*
				 * Evaluate the index row to compute expression values. We
				 * could do this by hand, but FormIndexDatum is convenient.
				 */
				FormIndexDatum(indexInfo,
							   slot,
							   estate,
							   values,
							   isnull);

				/*
				 * Save just the columns we care about.  We copy the values
				 * into ind_context from the estate's per-tuple context.
				 */
				for (i = 0; i < attr_cnt; i++)
				{
					VacAttrStats *stats = thisdata->vacattrstats[i];
					int			attnum = stats->tupattnum;

					if (isnull[attnum - 1])
					{
						exprvals[tcnt] = (Datum) 0;
						exprnulls[tcnt] = true;
					}
					else
					{
						exprvals[tcnt] = datumCopy(values[attnum - 1],
												   stats->attrtype->typbyval,
												   stats->attrtype->typlen);
						exprnulls[tcnt] = false;
					}
					tcnt++;
				}
			}
		}

		/*
		 * Having counted the number of rows that pass the predicate in the
		 * sample, we can estimate the total number of rows in the index.
		 */
		thisdata->tupleFract = (double) numindexrows / (double) numrows;
		totalindexrows = ceil(thisdata->tupleFract * totalrows);

		/*
		 * Now we can compute the statistics for the expression columns.
		 */
		if (numindexrows > 0)
		{
			MemoryContextSwitchTo(col_context);
			for (i = 0; i < attr_cnt; i++)
			{
				VacAttrStats *stats = thisdata->vacattrstats[i];

				stats->exprvals = exprvals + i;
				stats->exprnulls = exprnulls + i;
				stats->rowstride = attr_cnt;
				stats->compute_stats(stats,
									 ind_fetch_func,
									 numindexrows,
									 totalindexrows);

				MemoryContextReset(col_context);
			}
		}

		/* And clean up */
		MemoryContextSwitchTo(ind_context);

		ExecDropSingleTupleTableSlot(slot);
		FreeExecutorState(estate);
		MemoryContextReset(ind_context);
	}

	MemoryContextSwitchTo(old_context);
	MemoryContextDelete(ind_context);
}

/*
 * validate_va_cols_list -- validate the columns list given to analyze_rel
 *
 * Note that system attributes are never analyzed, so we just reject them at
 * the lookup stage.  We also reject duplicate column mentions.  (We could
 * alternatively ignore duplicates, but analyzing a column twice won't work;
 * we'd end up making a conflicting update in pg_statistic.)
 */
static void
validate_va_cols_list(Relation onerel, List *va_cols)
{
	Bitmapset  *unique_cols = NULL;
	ListCell   *le;

	Assert(va_cols != NIL);
	foreach(le, va_cols)
	{
		char	   *col = strVal(lfirst(le));
		int			i = attnameAttNum(onerel, col, false);

		if (i == InvalidAttrNumber)
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_COLUMN),
					 errmsg("column \"%s\" of relation \"%s\" does not exist",
							col, RelationGetRelationName(onerel))));
		if (bms_is_member(i, unique_cols))
			ereport(ERROR,
					(errcode(ERRCODE_DUPLICATE_COLUMN),
					 errmsg("column \"%s\" of relation \"%s\" appears more than once",
							col, RelationGetRelationName(onerel))));
		unique_cols = bms_add_member(unique_cols, i);
	}
}

/*
 * examine_attribute -- pre-analysis of a single column
 *
 * Determine whether the column is analyzable; if so, create and initialize
 * a VacAttrStats struct for it.  If not, return NULL.
 *
 * If index_expr isn't NULL, then we're trying to analyze an expression index,
 * and index_expr is the expression tree representing the column's data.
 */
static VacAttrStats *
examine_attribute(Relation onerel, int attnum, Node *index_expr)
{
	Form_pg_attribute attr = TupleDescAttr(onerel->rd_att, attnum - 1);
	int			attstattarget;
	HeapTuple	typtuple;
	VacAttrStats *stats;
	int			i;
	bool		ok;

	/*
	 * Check if the column is analyzable.
	 */
	if (!attribute_is_analyzable(onerel, attnum, attr, &attstattarget))
		return NULL;

	/*
	 * Create the VacAttrStats struct.
	 */
	stats = palloc0_object(VacAttrStats);
	stats->attstattarget = attstattarget;

	/*
	 * When analyzing an expression index, believe the expression tree's type
	 * not the column datatype --- the latter might be the opckeytype storage
	 * type of the opclass, which is not interesting for our purposes.  (Note:
	 * if we did anything with non-expression index columns, we'd need to
	 * figure out where to get the correct type info from, but for now that's
	 * not a problem.)	It's not clear whether anyone will care about the
	 * typmod, but we store that too just in case.
	 */
	if (index_expr)
	{
		stats->attrtypid = exprType(index_expr);
		stats->attrtypmod = exprTypmod(index_expr);

		/*
		 * If a collation has been specified for the index column, use that in
		 * preference to anything else; but if not, fall back to whatever we
		 * can get from the expression.
		 */
		if (OidIsValid(onerel->rd_indcollation[attnum - 1]))
			stats->attrcollid = onerel->rd_indcollation[attnum - 1];
		else
			stats->attrcollid = exprCollation(index_expr);
	}
	else
	{
		stats->attrtypid = attr->atttypid;
		stats->attrtypmod = attr->atttypmod;
		stats->attrcollid = attr->attcollation;
	}

	typtuple = SearchSysCacheCopy1(TYPEOID,
								   ObjectIdGetDatum(stats->attrtypid));
	if (!HeapTupleIsValid(typtuple))
		elog(ERROR, "cache lookup failed for type %u", stats->attrtypid);
	stats->attrtype = (Form_pg_type) GETSTRUCT(typtuple);
	stats->anl_context = anl_context;
	stats->tupattnum = attnum;

	/*
	 * The fields describing the stats->stavalues[n] element types default to
	 * the type of the data being analyzed, but the type-specific typanalyze
	 * function can change them if it wants to store something else.
	 */
	for (i = 0; i < STATISTIC_NUM_SLOTS; i++)
	{
		stats->statypid[i] = stats->attrtypid;
		stats->statyplen[i] = stats->attrtype->typlen;
		stats->statypbyval[i] = stats->attrtype->typbyval;
		stats->statypalign[i] = stats->attrtype->typalign;
	}

	/*
	 * Call the type-specific typanalyze function.  If none is specified, use
	 * std_typanalyze().
	 */
	if (OidIsValid(stats->attrtype->typanalyze))
		ok = DatumGetBool(OidFunctionCall1(stats->attrtype->typanalyze,
										   PointerGetDatum(stats)));
	else
		ok = std_typanalyze(stats);

	if (!ok || stats->compute_stats == NULL || stats->minrows <= 0)
	{
		heap_freetuple(typtuple);
		pfree(stats);
		return NULL;
	}

	return stats;
}

bool
attribute_is_analyzable(Relation onerel, int attnum, Form_pg_attribute attr,
						int *p_attstattarget)
{
	int			attstattarget;
	HeapTuple	atttuple;
	Datum		dat;
	bool		isnull;

	/* Never analyze dropped columns */
	if (attr->attisdropped)
		return false;

	/* Don't analyze virtual generated columns */
	if (attr->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
		return false;

	/*
	 * Get attstattarget value.  Set to -1 if null.  (Analyze functions expect
	 * -1 to mean use default_statistics_target; see for example
	 * std_typanalyze.)
	 */
	atttuple = SearchSysCache2(ATTNUM, ObjectIdGetDatum(RelationGetRelid(onerel)), Int16GetDatum(attnum));
	if (!HeapTupleIsValid(atttuple))
		elog(ERROR, "cache lookup failed for attribute %d of relation %u",
			 attnum, RelationGetRelid(onerel));
	dat = SysCacheGetAttr(ATTNUM, atttuple, Anum_pg_attribute_attstattarget, &isnull);
	attstattarget = isnull ? -1 : DatumGetInt16(dat);
	ReleaseSysCache(atttuple);

	/* Don't analyze column if user has specified not to */
	if (attstattarget == 0)
		return false;

	if (p_attstattarget)
		*p_attstattarget = attstattarget;
	return true;
}

/*
 * Read stream callback returning the next BlockNumber as chosen by the
 * BlockSampling algorithm.
 */
static BlockNumber
block_sampling_read_stream_next(ReadStream *stream,
								void *callback_private_data,
								void *per_buffer_data)
{
	BlockSamplerData *bs = callback_private_data;

	return BlockSampler_HasMore(bs) ? BlockSampler_Next(bs) : InvalidBlockNumber;
}

/*
 * acquire_sample_rows -- acquire a random sample of rows from the table
 *
 * Selected rows are returned in the caller-allocated array rows[], which
 * must have at least targrows entries.
 * The actual number of rows selected is returned as the function result.
 * We also estimate the total numbers of live and dead rows in the table,
 * and return them into *totalrows and *totaldeadrows, respectively.
 *
 * The returned list of tuples is in order by physical position in the table.
 * (We will rely on this later to derive correlation estimates.)
 *
 * As of May 2004 we use a new two-stage method:  Stage one selects up
 * to targrows random blocks (or all blocks, if there aren't so many).
 * Stage two scans these blocks and uses the Vitter algorithm to create
 * a random sample of targrows rows (or less, if there are less in the
 * sample of blocks).  The two stages are executed simultaneously: each
 * block is processed as soon as stage one returns its number and while
 * the rows are read stage two controls which ones are to be inserted
 * into the sample.
 *
 * Although every row has an equal chance of ending up in the final
 * sample, this sampling method is not perfect: not every possible
 * sample has an equal chance of being selected.  For large relations
 * the number of different blocks represented by the sample tends to be
 * too small.  We can live with that for now.  Improvements are welcome.
 *
 * An important property of this sampling method is that because we do
 * look at a statistically unbiased set of blocks, we should get
 * unbiased estimates of the average numbers of live and dead rows per
 * block.  The previous sampling method put too much credence in the row
 * density near the start of the table.
 */
static int
acquire_sample_rows(Relation onerel, int elevel,
					HeapTuple *rows, int targrows,
					double *totalrows, double *totaldeadrows)
{
	int			numrows = 0;	/* # rows now in reservoir */
	double		samplerows = 0; /* total # rows collected */
	double		liverows = 0;	/* # live rows seen */
	double		deadrows = 0;	/* # dead rows seen */
	double		rowstoskip = -1;	/* -1 means not set yet */
	uint32		randseed;		/* Seed for block sampler(s) */
	BlockNumber totalblocks;
	BlockSamplerData bs;
	ReservoirStateData rstate;
	TupleTableSlot *slot;
	TableScanDesc scan;
	BlockNumber nblocks;
	BlockNumber blksdone = 0;
	ReadStream *stream;

	Assert(targrows > 0);

	totalblocks = RelationGetNumberOfBlocks(onerel);

	/* Prepare for sampling block numbers */
	randseed = pg_prng_uint32(&pg_global_prng_state);
	nblocks = BlockSampler_Init(&bs, totalblocks, targrows, randseed);

	/* Report sampling block numbers */
	pgstat_progress_update_param(PROGRESS_ANALYZE_BLOCKS_TOTAL,
								 nblocks);

	/* Prepare for sampling rows */
	reservoir_init_selection_state(&rstate, targrows);

	scan = table_beginscan_analyze(onerel);
	slot = table_slot_create(onerel, NULL);

	/*
	 * It is safe to use batching, as block_sampling_read_stream_next never
	 * blocks.
	 */
	stream = read_stream_begin_relation(READ_STREAM_MAINTENANCE |
										READ_STREAM_USE_BATCHING,
										vac_strategy,
										scan->rs_rd,
										MAIN_FORKNUM,
										block_sampling_read_stream_next,
										&bs,
										0);

	/* Outer loop over blocks to sample */
	while (table_scan_analyze_next_block(scan, stream))
	{
		vacuum_delay_point(true);

		while (table_scan_analyze_next_tuple(scan, &liverows, &deadrows, slot))
		{
			/*
			 * The first targrows sample rows are simply copied into the
			 * reservoir. Then we start replacing tuples in the sample until
			 * we reach the end of the relation.  This algorithm is from Jeff
			 * Vitter's paper (see full citation in utils/misc/sampling.c). It
			 * works by repeatedly computing the number of tuples to skip
			 * before selecting a tuple, which replaces a randomly chosen
			 * element of the reservoir (current set of tuples).  At all times
			 * the reservoir is a true random sample of the tuples we've
			 * passed over so far, so when we fall off the end of the relation
			 * we're done.
			 */
			if (numrows < targrows)
				rows[numrows++] = ExecCopySlotHeapTuple(slot);
			else
			{
				/*
				 * t in Vitter's paper is the number of records already
				 * processed.  If we need to compute a new S value, we must
				 * use the not-yet-incremented value of samplerows as t.
				 */
				if (rowstoskip < 0)
					rowstoskip = reservoir_get_next_S(&rstate, samplerows, targrows);

				if (rowstoskip <= 0)
				{
					/*
					 * Found a suitable tuple, so save it, replacing one old
					 * tuple at random
					 */
					int			k = (int) (targrows * sampler_random_fract(&rstate.randstate));

					Assert(k >= 0 && k < targrows);
					heap_freetuple(rows[k]);
					rows[k] = ExecCopySlotHeapTuple(slot);
				}

				rowstoskip -= 1;
			}

			samplerows += 1;
		}

		pgstat_progress_update_param(PROGRESS_ANALYZE_BLOCKS_DONE,
									 ++blksdone);
	}

	read_stream_end(stream);

	ExecDropSingleTupleTableSlot(slot);
	table_endscan(scan);

	/*
	 * If we didn't find as many tuples as we wanted then we're done. No sort
	 * is needed, since they're already in order.
	 *
	 * Otherwise we need to sort the collected tuples by position
	 * (itempointer). It's not worth worrying about corner cases where the
	 * tuples are already sorted.
	 */
	if (numrows == targrows)
		qsort_interruptible(rows, numrows, sizeof(HeapTuple),
							compare_rows, NULL);

	/*
	 * Estimate total numbers of live and dead rows in relation, extrapolating
	 * on the assumption that the average tuple density in pages we didn't
	 * scan is the same as in the pages we did scan.  Since what we scanned is
	 * a random sample of the pages in the relation, this should be a good
	 * assumption.
	 */
	if (bs.m > 0)
	{
		*totalrows = floor((liverows / bs.m) * totalblocks + 0.5);
		*totaldeadrows = floor((deadrows / bs.m) * totalblocks + 0.5);
	}
	else
	{
		*totalrows = 0.0;
		*totaldeadrows = 0.0;
	}

	/*
	 * Emit some interesting relation info
	 */
	ereport(elevel,
			(errmsg("\"%s\": scanned %d of %u pages, "
					"containing %.0f live rows and %.0f dead rows; "
					"%d rows in sample, %.0f estimated total rows",
					RelationGetRelationName(onerel),
					bs.m, totalblocks,
					liverows, deadrows,
					numrows, *totalrows)));

	return numrows;
}

/*
 * Comparator for sorting rows[] array
 */
static int
compare_rows(const void *a, const void *b, void *arg)
{
	HeapTuple	ha = *(const HeapTuple *) a;
	HeapTuple	hb = *(const HeapTuple *) b;
	BlockNumber ba = ItemPointerGetBlockNumber(&ha->t_self);
	OffsetNumber oa = ItemPointerGetOffsetNumber(&ha->t_self);
	BlockNumber bb = ItemPointerGetBlockNumber(&hb->t_self);
	OffsetNumber ob = ItemPointerGetOffsetNumber(&hb->t_self);

	if (ba < bb)
		return -1;
	if (ba > bb)
		return 1;
	if (oa < ob)
		return -1;
	if (oa > ob)
		return 1;
	return 0;
}


/*
 * acquire_inherited_sample_rows -- acquire sample rows from inheritance tree
 *
 * This has the same API as acquire_sample_rows, except that rows are
 * collected from all inheritance children as well as the specified table.
 * We fail and return zero if there are no inheritance children, or if all
 * children are foreign tables that don't support ANALYZE.
 */
static int
acquire_inherited_sample_rows(Relation onerel, int elevel,
							  HeapTuple *rows, int targrows,
							  double *totalrows, double *totaldeadrows)
{
	List	   *tableOIDs;
	Relation   *rels;
	AcquireSampleRowsFunc *acquirefuncs;
	double	   *relblocks;
	double		totalblocks;
	int			numrows,
				nrels,
				i;
	ListCell   *lc;
	bool		has_child;

	/* Initialize output parameters to zero now, in case we exit early */
	*totalrows = 0;
	*totaldeadrows = 0;

	/*
	 * Find all members of inheritance set.  We only need AccessShareLock on
	 * the children.
	 */
	tableOIDs =
		find_all_inheritors(RelationGetRelid(onerel), AccessShareLock, NULL);

	/*
	 * Check that there's at least one descendant, else fail.  This could
	 * happen despite analyze_rel's relhassubclass check, if table once had a
	 * child but no longer does.  In that case, we can clear the
	 * relhassubclass field so as not to make the same mistake again later.
	 * (This is safe because we hold ShareUpdateExclusiveLock.)
	 */
	if (list_length(tableOIDs) < 2)
	{
		/* CCI because we already updated the pg_class row in this command */
		CommandCounterIncrement();
		SetRelationHasSubclass(RelationGetRelid(onerel), false);
		ereport(elevel,
				(errmsg("skipping analyze of \"%s.%s\" inheritance tree --- this inheritance tree contains no child tables",
						get_namespace_name(RelationGetNamespace(onerel)),
						RelationGetRelationName(onerel))));
		return 0;
	}

	/*
	 * Identify acquirefuncs to use, and count blocks in all the relations.
	 * The result could overflow BlockNumber, so we use double arithmetic.
	 */
	rels = (Relation *) palloc(list_length(tableOIDs) * sizeof(Relation));
	acquirefuncs = (AcquireSampleRowsFunc *)
		palloc(list_length(tableOIDs) * sizeof(AcquireSampleRowsFunc));
	relblocks = (double *) palloc(list_length(tableOIDs) * sizeof(double));
	totalblocks = 0;
	nrels = 0;
	has_child = false;
	foreach(lc, tableOIDs)
	{
		Oid			childOID = lfirst_oid(lc);
		Relation	childrel;
		AcquireSampleRowsFunc acquirefunc = NULL;
		BlockNumber relpages = 0;

		/* We already got the needed lock */
		childrel = table_open(childOID, NoLock);

		/* Ignore if temp table of another backend */
		if (RELATION_IS_OTHER_TEMP(childrel))
		{
			/* ... but release the lock on it */
			Assert(childrel != onerel);
			table_close(childrel, AccessShareLock);
			continue;
		}

		/* Check table type (MATVIEW can't happen, but might as well allow) */
		if (childrel->rd_rel->relkind == RELKIND_RELATION ||
			childrel->rd_rel->relkind == RELKIND_MATVIEW)
		{
			/* Regular table, so use the regular row acquisition function */
			acquirefunc = acquire_sample_rows;
			relpages = RelationGetNumberOfBlocks(childrel);
		}
		else if (childrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
		{
			/*
			 * For a foreign table, call the FDW's hook function to see
			 * whether it supports analysis.
			 */
			FdwRoutine *fdwroutine;
			bool		ok = false;

			fdwroutine = GetFdwRoutineForRelation(childrel, false);

			if (fdwroutine->AnalyzeForeignTable != NULL)
				ok = fdwroutine->AnalyzeForeignTable(childrel,
													 &acquirefunc,
													 &relpages);

			if (!ok)
			{
				/* ignore, but release the lock on it */
				Assert(childrel != onerel);
				table_close(childrel, AccessShareLock);
				continue;
			}
		}
		else
		{
			/*
			 * ignore, but release the lock on it.  don't try to unlock the
			 * passed-in relation
			 */
			Assert(childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
			if (childrel != onerel)
				table_close(childrel, AccessShareLock);
			else
				table_close(childrel, NoLock);
			continue;
		}

		/* OK, we'll process this child */
		has_child = true;
		rels[nrels] = childrel;
		acquirefuncs[nrels] = acquirefunc;
		relblocks[nrels] = (double) relpages;
		totalblocks += (double) relpages;
		nrels++;
	}

	/*
	 * If we don't have at least one child table to consider, fail.  If the
	 * relation is a partitioned table, it's not counted as a child table.
	 */
	if (!has_child)
	{
		ereport(elevel,
				(errmsg("skipping analyze of \"%s.%s\" inheritance tree --- this inheritance tree contains no analyzable child tables",
						get_namespace_name(RelationGetNamespace(onerel)),
						RelationGetRelationName(onerel))));
		return 0;
	}

	/*
	 * Now sample rows from each relation, proportionally to its fraction of
	 * the total block count.  (This might be less than desirable if the child
	 * rels have radically different free-space percentages, but it's not
	 * clear that it's worth working harder.)
	 */
	pgstat_progress_update_param(PROGRESS_ANALYZE_CHILD_TABLES_TOTAL,
								 nrels);
	numrows = 0;
	for (i = 0; i < nrels; i++)
	{
		Relation	childrel = rels[i];
		AcquireSampleRowsFunc acquirefunc = acquirefuncs[i];
		double		childblocks = relblocks[i];

		/*
		 * Report progress.  The sampling function will normally report blocks
		 * done/total, but we need to reset them to 0 here, so that they don't
		 * show an old value until that.
		 */
		{
			const int	progress_index[] = {
				PROGRESS_ANALYZE_CURRENT_CHILD_TABLE_RELID,
				PROGRESS_ANALYZE_BLOCKS_DONE,
				PROGRESS_ANALYZE_BLOCKS_TOTAL
			};
			const int64 progress_vals[] = {
				RelationGetRelid(childrel),
				0,
				0,
			};

			pgstat_progress_update_multi_param(3, progress_index, progress_vals);
		}

		if (childblocks > 0)
		{
			int			childtargrows;

			childtargrows = (int) rint(targrows * childblocks / totalblocks);
			/* Make sure we don't overrun due to roundoff error */
			childtargrows = Min(childtargrows, targrows - numrows);
			if (childtargrows > 0)
			{
				int			childrows;
				double		trows,
							tdrows;

				/* Fetch a random sample of the child's rows */
				childrows = (*acquirefunc) (childrel, elevel,
											rows + numrows, childtargrows,
											&trows, &tdrows);

				/* We may need to convert from child's rowtype to parent's */
				if (childrows > 0 &&
					!equalRowTypes(RelationGetDescr(childrel),
								   RelationGetDescr(onerel)))
				{
					TupleConversionMap *map;

					map = convert_tuples_by_name(RelationGetDescr(childrel),
												 RelationGetDescr(onerel));
					if (map != NULL)
					{
						int			j;

						for (j = 0; j < childrows; j++)
						{
							HeapTuple	newtup;

							newtup = execute_attr_map_tuple(rows[numrows + j], map);
							heap_freetuple(rows[numrows + j]);
							rows[numrows + j] = newtup;
						}
						free_conversion_map(map);
					}
				}

				/* And add to counts */
				numrows += childrows;
				*totalrows += trows;
				*totaldeadrows += tdrows;
			}
		}

		/*
		 * Note: we cannot release the child-table locks, since we may have
		 * pointers to their TOAST tables in the sampled rows.
		 */
		table_close(childrel, NoLock);
		pgstat_progress_update_param(PROGRESS_ANALYZE_CHILD_TABLES_DONE,
									 i + 1);
	}

	return numrows;
}


/*
 *	update_attstats() -- update attribute statistics for one relation
 *
 *		Statistics are stored in several places: the pg_class row for the
 *		relation has stats about the whole relation, and there is a
 *		pg_statistic row for each (non-system) attribute that has ever
 *		been analyzed.  The pg_class values are updated by VACUUM, not here.
 *
 *		pg_statistic rows are just added or updated normally.  This means
 *		that pg_statistic will probably contain some deleted rows at the
 *		completion of a vacuum cycle, unless it happens to get vacuumed last.
 *
 *		To keep things simple, we punt for pg_statistic, and don't try
 *		to compute or store rows for pg_statistic itself in pg_statistic.
 *		This could possibly be made to work, but it's not worth the trouble.
 *		Note analyze_rel() has seen to it that we won't come here when
 *		vacuuming pg_statistic itself.
 *
 *		Note: there would be a race condition here if two backends could
 *		ANALYZE the same table concurrently.  Presently, we lock that out
 *		by taking a self-exclusive lock on the relation in analyze_rel().
 */
static void
update_attstats(Oid relid, bool inh, int natts, VacAttrStats **vacattrstats)
{
	Relation	sd;
	int			attno;
	CatalogIndexState indstate = NULL;

	if (natts <= 0)
		return;					/* nothing to do */

	sd = table_open(StatisticRelationId, RowExclusiveLock);

	for (attno = 0; attno < natts; attno++)
	{
		VacAttrStats *stats = vacattrstats[attno];
		HeapTuple	stup,
					oldtup;
		int			i,
					k,
					n;
		Datum		values[Natts_pg_statistic];
		bool		nulls[Natts_pg_statistic];
		bool		replaces[Natts_pg_statistic];

		/* Ignore attr if we weren't able to collect stats */
		if (!stats->stats_valid)
			continue;

		/*
		 * Construct a new pg_statistic tuple
		 */
		for (i = 0; i < Natts_pg_statistic; ++i)
		{
			nulls[i] = false;
			replaces[i] = true;
		}

		values[Anum_pg_statistic_starelid - 1] = ObjectIdGetDatum(relid);
		values[Anum_pg_statistic_staattnum - 1] = Int16GetDatum(stats->tupattnum);
		values[Anum_pg_statistic_stainherit - 1] = BoolGetDatum(inh);
		values[Anum_pg_statistic_stanullfrac - 1] = Float4GetDatum(stats->stanullfrac);
		values[Anum_pg_statistic_stawidth - 1] = Int32GetDatum(stats->stawidth);
		values[Anum_pg_statistic_stadistinct - 1] = Float4GetDatum(stats->stadistinct);
		i = Anum_pg_statistic_stakind1 - 1;
		for (k = 0; k < STATISTIC_NUM_SLOTS; k++)
		{
			values[i++] = Int16GetDatum(stats->stakind[k]); /* stakindN */
		}
		i = Anum_pg_statistic_staop1 - 1;
		for (k = 0; k < STATISTIC_NUM_SLOTS; k++)
		{
			values[i++] = ObjectIdGetDatum(stats->staop[k]);	/* staopN */
		}
		i = Anum_pg_statistic_stacoll1 - 1;
		for (k = 0; k < STATISTIC_NUM_SLOTS; k++)
		{
			values[i++] = ObjectIdGetDatum(stats->stacoll[k]);	/* stacollN */
		}
		i = Anum_pg_statistic_stanumbers1 - 1;
		for (k = 0; k < STATISTIC_NUM_SLOTS; k++)
		{
			if (stats->stanumbers[k] != NULL)
			{
				int			nnum = stats->numnumbers[k];
				Datum	   *numdatums = (Datum *) palloc(nnum * sizeof(Datum));
				ArrayType  *arry;

				for (n = 0; n < nnum; n++)
					numdatums[n] = Float4GetDatum(stats->stanumbers[k][n]);
				arry = construct_array_builtin(numdatums, nnum, FLOAT4OID);
				values[i++] = PointerGetDatum(arry);	/* stanumbersN */
			}
			else
			{
				nulls[i] = true;
				values[i++] = (Datum) 0;
			}
		}
		i = Anum_pg_statistic_stavalues1 - 1;
		for (k = 0; k < STATISTIC_NUM_SLOTS; k++)
		{
			if (stats->stavalues[k] != NULL)
			{
				ArrayType  *arry;

				arry = construct_array(stats->stavalues[k],
									   stats->numvalues[k],
									   stats->statypid[k],
									   stats->statyplen[k],
									   stats->statypbyval[k],
									   stats->statypalign[k]);
				values[i++] = PointerGetDatum(arry);	/* stavaluesN */
			}
			else
			{
				nulls[i] = true;
				values[i++] = (Datum) 0;
			}
		}

		/* Is there already a pg_statistic tuple for this attribute? */
		oldtup = SearchSysCache3(STATRELATTINH,
								 ObjectIdGetDatum(relid),
								 Int16GetDatum(stats->tupattnum),
								 BoolGetDatum(inh));

		/* Open index information when we know we need it */
		if (indstate == NULL)
			indstate = CatalogOpenIndexes(sd);

		if (HeapTupleIsValid(oldtup))
		{
			/* Yes, replace it */
			stup = heap_modify_tuple(oldtup,
									 RelationGetDescr(sd),
									 values,
									 nulls,
									 replaces);
			ReleaseSysCache(oldtup);
			CatalogTupleUpdateWithInfo(sd, &stup->t_self, stup, indstate);
		}
		else
		{
			/* No, insert new tuple */
			stup = heap_form_tuple(RelationGetDescr(sd), values, nulls);
			CatalogTupleInsertWithInfo(sd, stup, indstate);
		}

		heap_freetuple(stup);
	}

	if (indstate != NULL)
		CatalogCloseIndexes(indstate);
	table_close(sd, RowExclusiveLock);
}

/*
 * Standard fetch function for use by compute_stats subroutines.
 *
 * This exists to provide some insulation between compute_stats routines
 * and the actual storage of the sample data.
 */
static Datum
std_fetch_func(VacAttrStatsP stats, int rownum, bool *isNull)
{
	int			attnum = stats->tupattnum;
	HeapTuple	tuple = stats->rows[rownum];
	TupleDesc	tupDesc = stats->tupDesc;

	return heap_getattr(tuple, attnum, tupDesc, isNull);
}

/*
 * Fetch function for analyzing index expressions.
 *
 * We have not bothered to construct index tuples, instead the data is
 * just in Datum arrays.
 */
static Datum
ind_fetch_func(VacAttrStatsP stats, int rownum, bool *isNull)
{
	int			i;

	/* exprvals and exprnulls are already offset for proper column */
	i = rownum * stats->rowstride;
	*isNull = stats->exprnulls[i];
	return stats->exprvals[i];
}


/*
 * ==========================================================================
 *
 * Code below this point represents the "standard" type-specific statistics
 * analysis algorithms.  This code can be replaced on a per-data-type basis
 * by setting a nonzero value in pg_type.typanalyze.
 *
 *==========================================================================
 */


/*
 * To avoid consuming too much memory during analysis and/or too much space
 * in the resulting pg_statistic rows, we ignore varlena datums that are wider
 * than WIDTH_THRESHOLD (after detoasting!).  This is legitimate for MCV
 * and distinct-value calculations since a wide value is unlikely to be
 * duplicated at all, much less be a most-common value.  For the same reason,
 * ignoring wide values will not affect our estimates of histogram bin
 * boundaries very much.
 */
#define WIDTH_THRESHOLD  1024

#define swapInt(a,b)	do {int _tmp; _tmp=a; a=b; b=_tmp;} while(0)
#define swapDatum(a,b)	do {Datum _tmp; _tmp=a; a=b; b=_tmp;} while(0)

/*
 * Extra information used by the default analysis routines
 */
typedef struct
{
	int			count;			/* # of duplicates */
	int			first;			/* values[] index of first occurrence */
} ScalarMCVItem;

typedef struct
{
	SortSupport ssup;
	int		   *tupnoLink;
} CompareScalarsContext;


static void compute_trivial_stats(VacAttrStatsP stats,
								  AnalyzeAttrFetchFunc fetchfunc,
								  int samplerows,
								  double totalrows);
static void compute_distinct_stats(VacAttrStatsP stats,
								   AnalyzeAttrFetchFunc fetchfunc,
								   int samplerows,
								   double totalrows);
static void compute_scalar_stats(VacAttrStatsP stats,
								 AnalyzeAttrFetchFunc fetchfunc,
								 int samplerows,
								 double totalrows);
static int	compare_scalars(const void *a, const void *b, void *arg);
static int	compare_mcvs(const void *a, const void *b, void *arg);
static int	analyze_mcv_list(int *mcv_counts,
							 int num_mcv,
							 double stadistinct,
							 double stanullfrac,
							 int samplerows,
							 double totalrows);


/*
 * std_typanalyze -- the default type-specific typanalyze function
 */
bool
std_typanalyze(VacAttrStats *stats)
{
	Oid			ltopr;
	Oid			eqopr;
	StdAnalyzeData *mystats;

	/* If the attstattarget column is negative, use the default value */
	if (stats->attstattarget < 0)
		stats->attstattarget = default_statistics_target;

	/* Look for default "<" and "=" operators for column's type */
	get_sort_group_operators(stats->attrtypid,
							 false, false, false,
							 &ltopr, &eqopr, NULL,
							 NULL);

	/* Save the operator info for compute_stats routines */
	mystats = palloc_object(StdAnalyzeData);
	mystats->eqopr = eqopr;
	mystats->eqfunc = OidIsValid(eqopr) ? get_opcode(eqopr) : InvalidOid;
	mystats->ltopr = ltopr;
	stats->extra_data = mystats;

	/*
	 * Determine which standard statistics algorithm to use
	 */
	if (OidIsValid(eqopr) && OidIsValid(ltopr))
	{
		/* Seems to be a scalar datatype */
		stats->compute_stats = compute_scalar_stats;
		/*--------------------
		 * The following choice of minrows is based on the paper
		 * "Random sampling for histogram construction: how much is enough?"
		 * by Surajit Chaudhuri, Rajeev Motwani and Vivek Narasayya, in
		 * Proceedings of ACM SIGMOD International Conference on Management
		 * of Data, 1998, Pages 436-447.  Their Corollary 1 to Theorem 5
		 * says that for table size n, histogram size k, maximum relative
		 * error in bin size f, and error probability gamma, the minimum
		 * random sample size is
		 *		r = 4 * k * ln(2*n/gamma) / f^2
		 * Taking f = 0.5, gamma = 0.01, n = 10^6 rows, we obtain
		 *		r = 305.82 * k
		 * Note that because of the log function, the dependence on n is
		 * quite weak; even at n = 10^12, a 300*k sample gives <= 0.66
		 * bin size error with probability 0.99.  So there's no real need to
		 * scale for n, which is a good thing because we don't necessarily
		 * know it at this point.
		 *--------------------
		 */
		stats->minrows = 300 * stats->attstattarget;
	}
	else if (OidIsValid(eqopr))
	{
		/* We can still recognize distinct values */
		stats->compute_stats = compute_distinct_stats;
		/* Might as well use the same minrows as above */
		stats->minrows = 300 * stats->attstattarget;
	}
	else
	{
		/* Can't do much but the trivial stuff */
		stats->compute_stats = compute_trivial_stats;
		/* Might as well use the same minrows as above */
		stats->minrows = 300 * stats->attstattarget;
	}

	return true;
}


/*
 *	compute_trivial_stats() -- compute very basic column statistics
 *
 *	We use this when we cannot find a hash "=" operator for the datatype.
 *
 *	We determine the fraction of non-null rows and the average datum width.
 */
static void
compute_trivial_stats(VacAttrStatsP stats,
					  AnalyzeAttrFetchFunc fetchfunc,
					  int samplerows,
					  double totalrows)
{
	int			i;
	int			null_cnt = 0;
	int			nonnull_cnt = 0;
	double		total_width = 0;
	bool		is_varlena = (!stats->attrtype->typbyval &&
							  stats->attrtype->typlen == -1);
	bool		is_varwidth = (!stats->attrtype->typbyval &&
							   stats->attrtype->typlen < 0);

	for (i = 0; i < samplerows; i++)
	{
		Datum		value;
		bool		isnull;

		vacuum_delay_point(true);

		value = fetchfunc(stats, i, &isnull);

		/* Check for null/nonnull */
		if (isnull)
		{
			null_cnt++;
			continue;
		}
		nonnull_cnt++;

		/*
		 * If it's a variable-width field, add up widths for average width
		 * calculation.  Note that if the value is toasted, we use the toasted
		 * width.  We don't bother with this calculation if it's a fixed-width
		 * type.
		 */
		if (is_varlena)
		{
			total_width += VARSIZE_ANY(DatumGetPointer(value));
		}
		else if (is_varwidth)
		{
			/* must be cstring */
			total_width += strlen(DatumGetCString(value)) + 1;
		}
	}

	/* We can only compute average width if we found some non-null values. */
	if (nonnull_cnt > 0)
	{
		stats->stats_valid = true;
		/* Do the simple null-frac and width stats */
		stats->stanullfrac = (double) null_cnt / (double) samplerows;
		if (is_varwidth)
			stats->stawidth = total_width / (double) nonnull_cnt;
		else
			stats->stawidth = stats->attrtype->typlen;
		stats->stadistinct = 0.0;	/* "unknown" */
	}
	else if (null_cnt > 0)
	{
		/* We found only nulls; assume the column is entirely null */
		stats->stats_valid = true;
		stats->stanullfrac = 1.0;
		if (is_varwidth)
			stats->stawidth = 0;	/* "unknown" */
		else
			stats->stawidth = stats->attrtype->typlen;
		stats->stadistinct = 0.0;	/* "unknown" */
	}
}


/*
 *	compute_distinct_stats() -- compute column statistics including ndistinct
 *
 *	We use this when we can find only an "=" operator for the datatype.
 *
 *	We determine the fraction of non-null rows, the average width, the
 *	most common values, and the (estimated) number of distinct values.
 *
 *	The most common values are determined by brute force: we keep a list
 *	of previously seen values, ordered by number of times seen, as we scan
 *	the samples.  A newly seen value is inserted just after the last
 *	multiply-seen value, causing the bottommost (oldest) singly-seen value
 *	to drop off the list.  The accuracy of this method, and also its cost,
 *	depend mainly on the length of the list we are willing to keep.
 */
static void
compute_distinct_stats(VacAttrStatsP stats,
					   AnalyzeAttrFetchFunc fetchfunc,
					   int samplerows,
					   double totalrows)
{
	int			i;
	int			null_cnt = 0;
	int			nonnull_cnt = 0;
	int			toowide_cnt = 0;
	double		total_width = 0;
	bool		is_varlena = (!stats->attrtype->typbyval &&
							  stats->attrtype->typlen == -1);
	bool		is_varwidth = (!stats->attrtype->typbyval &&
							   stats->attrtype->typlen < 0);
	FmgrInfo	f_cmpeq;
	typedef struct
	{
		Datum		value;
		int			count;
	} TrackItem;
	TrackItem  *track;
	int			track_cnt,
				track_max;
	int			num_mcv = stats->attstattarget;
	StdAnalyzeData *mystats = (StdAnalyzeData *) stats->extra_data;

	/*
	 * We track up to 2*n values for an n-element MCV list; but at least 10
	 */
	track_max = 2 * num_mcv;
	if (track_max < 10)
		track_max = 10;
	track = (TrackItem *) palloc(track_max * sizeof(TrackItem));
	track_cnt = 0;

	fmgr_info(mystats->eqfunc, &f_cmpeq);

	for (i = 0; i < samplerows; i++)
	{
		Datum		value;
		bool		isnull;
		bool		match;
		int			firstcount1,
					j;

		vacuum_delay_point(true);

		value = fetchfunc(stats, i, &isnull);

		/* Check for null/nonnull */
		if (isnull)
		{
			null_cnt++;
			continue;
		}
		nonnull_cnt++;

		/*
		 * If it's a variable-width field, add up widths for average width
		 * calculation.  Note that if the value is toasted, we use the toasted
		 * width.  We don't bother with this calculation if it's a fixed-width
		 * type.
		 */
		if (is_varlena)
		{
			total_width += VARSIZE_ANY(DatumGetPointer(value));

			/*
			 * If the value is toasted, we want to detoast it just once to
			 * avoid repeated detoastings and resultant excess memory usage
			 * during the comparisons.  Also, check to see if the value is
			 * excessively wide, and if so don't detoast at all --- just
			 * ignore the value.
			 */
			if (toast_raw_datum_size(value) > WIDTH_THRESHOLD)
			{
				toowide_cnt++;
				continue;
			}
			value = PointerGetDatum(PG_DETOAST_DATUM(value));
		}
		else if (is_varwidth)
		{
			/* must be cstring */
			total_width += strlen(DatumGetCString(value)) + 1;
		}

		/*
		 * See if the value matches anything we're already tracking.
		 */
		match = false;
		firstcount1 = track_cnt;
		for (j = 0; j < track_cnt; j++)
		{
			if (DatumGetBool(FunctionCall2Coll(&f_cmpeq,
											   stats->attrcollid,
											   value, track[j].value)))
			{
				match = true;
				break;
			}
			if (j < firstcount1 && track[j].count == 1)
				firstcount1 = j;
		}

		if (match)
		{
			/* Found a match */
			track[j].count++;
			/* This value may now need to "bubble up" in the track list */
			while (j > 0 && track[j].count > track[j - 1].count)
			{
				swapDatum(track[j].value, track[j - 1].value);
				swapInt(track[j].count, track[j - 1].count);
				j--;
			}
		}
		else
		{
			/* No match.  Insert at head of count-1 list */
			if (track_cnt < track_max)
				track_cnt++;
			for (j = track_cnt - 1; j > firstcount1; j--)
			{
				track[j].value = track[j - 1].value;
				track[j].count = track[j - 1].count;
			}
			if (firstcount1 < track_cnt)
			{
				track[firstcount1].value = value;
				track[firstcount1].count = 1;
			}
		}
	}

	/* We can only compute real stats if we found some non-null values. */
	if (nonnull_cnt > 0)
	{
		int			nmultiple,
					summultiple;

		stats->stats_valid = true;
		/* Do the simple null-frac and width stats */
		stats->stanullfrac = (double) null_cnt / (double) samplerows;
		if (is_varwidth)
			stats->stawidth = total_width / (double) nonnull_cnt;
		else
			stats->stawidth = stats->attrtype->typlen;

		/* Count the number of values we found multiple times */
		summultiple = 0;
		for (nmultiple = 0; nmultiple < track_cnt; nmultiple++)
		{
			if (track[nmultiple].count == 1)
				break;
			summultiple += track[nmultiple].count;
		}

		if (nmultiple == 0)
		{
			/*
			 * If we found no repeated non-null values, assume it's a unique
			 * column; but be sure to discount for any nulls we found.
			 */
			stats->stadistinct = -1.0 * (1.0 - stats->stanullfrac);
		}
		else if (track_cnt < track_max && toowide_cnt == 0 &&
				 nmultiple == track_cnt)
		{
			/*
			 * Our track list includes every value in the sample, and every
			 * value appeared more than once.  Assume the column has just
			 * these values.  (This case is meant to address columns with
			 * small, fixed sets of possible values, such as boolean or enum
			 * columns.  If there are any values that appear just once in the
			 * sample, including too-wide values, we should assume that that's
			 * not what we're dealing with.)
			 */
			stats->stadistinct = track_cnt;
		}
		else
		{
			/*----------
			 * Estimate the number of distinct values using the estimator
			 * proposed by Haas and Stokes in IBM Research Report RJ 10025:
			 *		n*d / (n - f1 + f1*n/N)
			 * where f1 is the number of distinct values that occurred
			 * exactly once in our sample of n rows (from a total of N),
			 * and d is the total number of distinct values in the sample.
			 * This is their Duj1 estimator; the other estimators they
			 * recommend are considerably more complex, and are numerically
			 * very unstable when n is much smaller than N.
			 *
			 * In this calculation, we consider only non-nulls.  We used to
			 * include rows with null values in the n and N counts, but that
			 * leads to inaccurate answers in columns with many nulls, and
			 * it's intuitively bogus anyway considering the desired result is
			 * the number of distinct non-null values.
			 *
			 * We assume (not very reliably!) that all the multiply-occurring
			 * values are reflected in the final track[] list, and the other
			 * nonnull values all appeared but once.  (XXX this usually
			 * results in a drastic overestimate of ndistinct.  Can we do
			 * any better?)
			 *----------
			 */
			int			f1 = nonnull_cnt - summultiple;
			int			d = f1 + nmultiple;
			double		n = samplerows - null_cnt;
			double		N = totalrows * (1.0 - stats->stanullfrac);
			double		stadistinct;

			/* N == 0 shouldn't happen, but just in case ... */
			if (N > 0)
				stadistinct = (n * d) / ((n - f1) + f1 * n / N);
			else
				stadistinct = 0;

			/* Clamp to sane range in case of roundoff error */
			if (stadistinct < d)
				stadistinct = d;
			if (stadistinct > N)
				stadistinct = N;
			/* And round to integer */
			stats->stadistinct = floor(stadistinct + 0.5);
		}

		/*
		 * If we estimated the number of distinct values at more than 10% of
		 * the total row count (a very arbitrary limit), then assume that
		 * stadistinct should scale with the row count rather than be a fixed
		 * value.
		 */
		if (stats->stadistinct > 0.1 * totalrows)
			stats->stadistinct = -(stats->stadistinct / totalrows);

		/*
		 * Decide how many values are worth storing as most-common values. If
		 * we are able to generate a complete MCV list (all the values in the
		 * sample will fit, and we think these are all the ones in the table),
		 * then do so.  Otherwise, store only those values that are
		 * significantly more common than the values not in the list.
		 *
		 * Note: the first of these cases is meant to address columns with
		 * small, fixed sets of possible values, such as boolean or enum
		 * columns.  If we can *completely* represent the column population by
		 * an MCV list that will fit into the stats target, then we should do
		 * so and thus provide the planner with complete information.  But if
		 * the MCV list is not complete, it's generally worth being more
		 * selective, and not just filling it all the way up to the stats
		 * target.
		 */
		if (track_cnt < track_max && toowide_cnt == 0 &&
			stats->stadistinct > 0 &&
			track_cnt <= num_mcv)
		{
			/* Track list includes all values seen, and all will fit */
			num_mcv = track_cnt;
		}
		else
		{
			int		   *mcv_counts;

			/* Incomplete list; decide how many values are worth keeping */
			if (num_mcv > track_cnt)
				num_mcv = track_cnt;

			if (num_mcv > 0)
			{
				mcv_counts = (int *) palloc(num_mcv * sizeof(int));
				for (i = 0; i < num_mcv; i++)
					mcv_counts[i] = track[i].count;

				num_mcv = analyze_mcv_list(mcv_counts, num_mcv,
										   stats->stadistinct,
										   stats->stanullfrac,
										   samplerows, totalrows);
			}
		}

		/* Generate MCV slot entry */
		if (num_mcv > 0)
		{
			MemoryContext old_context;
			Datum	   *mcv_values;
			float4	   *mcv_freqs;

			/* Must copy the target values into anl_context */
			old_context = MemoryContextSwitchTo(stats->anl_context);
			mcv_values = (Datum *) palloc(num_mcv * sizeof(Datum));
			mcv_freqs = (float4 *) palloc(num_mcv * sizeof(float4));
			for (i = 0; i < num_mcv; i++)
			{
				mcv_values[i] = datumCopy(track[i].value,
										  stats->attrtype->typbyval,
										  stats->attrtype->typlen);
				mcv_freqs[i] = (double) track[i].count / (double) samplerows;
			}
			MemoryContextSwitchTo(old_context);

			stats->stakind[0] = STATISTIC_KIND_MCV;
			stats->staop[0] = mystats->eqopr;
			stats->stacoll[0] = stats->attrcollid;
			stats->stanumbers[0] = mcv_freqs;
			stats->numnumbers[0] = num_mcv;
			stats->stavalues[0] = mcv_values;
			stats->numvalues[0] = num_mcv;

			/*
			 * Accept the defaults for stats->statypid and others. They have
			 * been set before we were called (see vacuum.h)
			 */
		}
	}
	else if (null_cnt > 0)
	{
		/* We found only nulls; assume the column is entirely null */
		stats->stats_valid = true;
		stats->stanullfrac = 1.0;
		if (is_varwidth)
			stats->stawidth = 0;	/* "unknown" */
		else
			stats->stawidth = stats->attrtype->typlen;
		stats->stadistinct = 0.0;	/* "unknown" */
	}

	/* We don't need to bother cleaning up any of our temporary palloc's */
}


/*
 *	compute_scalar_stats() -- compute column statistics
 *
 *	We use this when we can find "=" and "<" operators for the datatype.
 *
 *	We determine the fraction of non-null rows, the average width, the
 *	most common values, the (estimated) number of distinct values, the
 *	distribution histogram, and the correlation of physical to logical order.
 *
 *	The desired stats can be determined fairly easily after sorting the
 *	data values into order.
 */
static void
compute_scalar_stats(VacAttrStatsP stats,
					 AnalyzeAttrFetchFunc fetchfunc,
					 int samplerows,
					 double totalrows)
{
	int			i;
	int			null_cnt = 0;
	int			nonnull_cnt = 0;
	int			toowide_cnt = 0;
	double		total_width = 0;
	bool		is_varlena = (!stats->attrtype->typbyval &&
							  stats->attrtype->typlen == -1);
	bool		is_varwidth = (!stats->attrtype->typbyval &&
							   stats->attrtype->typlen < 0);
	double		corr_xysum;
	SortSupportData ssup;
	ScalarItem *values;
	int			values_cnt = 0;
	int		   *tupnoLink;
	ScalarMCVItem *track;
	int			track_cnt = 0;
	int			num_mcv = stats->attstattarget;
	int			num_bins = stats->attstattarget;
	StdAnalyzeData *mystats = (StdAnalyzeData *) stats->extra_data;

	values = (ScalarItem *) palloc(samplerows * sizeof(ScalarItem));
	tupnoLink = (int *) palloc(samplerows * sizeof(int));
	track = (ScalarMCVItem *) palloc(num_mcv * sizeof(ScalarMCVItem));

	memset(&ssup, 0, sizeof(ssup));
	ssup.ssup_cxt = CurrentMemoryContext;
	ssup.ssup_collation = stats->attrcollid;
	ssup.ssup_nulls_first = false;

	/*
	 * For now, don't perform abbreviated key conversion, because full values
	 * are required for MCV slot generation.  Supporting that optimization
	 * would necessitate teaching compare_scalars() to call a tie-breaker.
	 */
	ssup.abbreviate = false;

	PrepareSortSupportFromOrderingOp(mystats->ltopr, &ssup);

	/* Initial scan to find sortable values */
	for (i = 0; i < samplerows; i++)
	{
		Datum		value;
		bool		isnull;

		vacuum_delay_point(true);

		value = fetchfunc(stats, i, &isnull);

		/* Check for null/nonnull */
		if (isnull)
		{
			null_cnt++;
			continue;
		}
		nonnull_cnt++;

		/*
		 * If it's a variable-width field, add up widths for average width
		 * calculation.  Note that if the value is toasted, we use the toasted
		 * width.  We don't bother with this calculation if it's a fixed-width
		 * type.
		 */
		if (is_varlena)
		{
			total_width += VARSIZE_ANY(DatumGetPointer(value));

			/*
			 * If the value is toasted, we want to detoast it just once to
			 * avoid repeated detoastings and resultant excess memory usage
			 * during the comparisons.  Also, check to see if the value is
			 * excessively wide, and if so don't detoast at all --- just
			 * ignore the value.
			 */
			if (toast_raw_datum_size(value) > WIDTH_THRESHOLD)
			{
				toowide_cnt++;
				continue;
			}
			value = PointerGetDatum(PG_DETOAST_DATUM(value));
		}
		else if (is_varwidth)
		{
			/* must be cstring */
			total_width += strlen(DatumGetCString(value)) + 1;
		}

		/* Add it to the list to be sorted */
		values[values_cnt].value = value;
		values[values_cnt].tupno = values_cnt;
		tupnoLink[values_cnt] = values_cnt;
		values_cnt++;
	}

	/* We can only compute real stats if we found some sortable values. */
	if (values_cnt > 0)
	{
		int			ndistinct,	/* # distinct values in sample */
					nmultiple,	/* # that appear multiple times */
					num_hist,
					dups_cnt;
		int			slot_idx = 0;
		CompareScalarsContext cxt;

		/* Sort the collected values */
		cxt.ssup = &ssup;
		cxt.tupnoLink = tupnoLink;
		qsort_interruptible(values, values_cnt, sizeof(ScalarItem),
							compare_scalars, &cxt);

		/*
		 * Now scan the values in order, find the most common ones, and also
		 * accumulate ordering-correlation statistics.
		 *
		 * To determine which are most common, we first have to count the
		 * number of duplicates of each value.  The duplicates are adjacent in
		 * the sorted list, so a brute-force approach is to compare successive
		 * datum values until we find two that are not equal. However, that
		 * requires N-1 invocations of the datum comparison routine, which are
		 * completely redundant with work that was done during the sort.  (The
		 * sort algorithm must at some point have compared each pair of items
		 * that are adjacent in the sorted order; otherwise it could not know
		 * that it's ordered the pair correctly.) We exploit this by having
		 * compare_scalars remember the highest tupno index that each
		 * ScalarItem has been found equal to.  At the end of the sort, a
		 * ScalarItem's tupnoLink will still point to itself if and only if it
		 * is the last item of its group of duplicates (since the group will
		 * be ordered by tupno).
		 */
		corr_xysum = 0;
		ndistinct = 0;
		nmultiple = 0;
		dups_cnt = 0;
		for (i = 0; i < values_cnt; i++)
		{
			int			tupno = values[i].tupno;

			corr_xysum += ((double) i) * ((double) tupno);
			dups_cnt++;
			if (tupnoLink[tupno] == tupno)
			{
				/* Reached end of duplicates of this value */
				ndistinct++;
				if (dups_cnt > 1)
				{
					nmultiple++;
					if (track_cnt < num_mcv ||
						dups_cnt > track[track_cnt - 1].count)
					{
						/*
						 * Found a new item for the mcv list; find its
						 * position, bubbling down old items if needed. Loop
						 * invariant is that j points at an empty/ replaceable
						 * slot.
						 */
						int			j;

						if (track_cnt < num_mcv)
							track_cnt++;
						for (j = track_cnt - 1; j > 0; j--)
						{
							if (dups_cnt <= track[j - 1].count)
								break;
							track[j].count = track[j - 1].count;
							track[j].first = track[j - 1].first;
						}
						track[j].count = dups_cnt;
						track[j].first = i + 1 - dups_cnt;
					}
				}
				dups_cnt = 0;
			}
		}

		stats->stats_valid = true;
		/* Do the simple null-frac and width stats */
		stats->stanullfrac = (double) null_cnt / (double) samplerows;
		if (is_varwidth)
			stats->stawidth = total_width / (double) nonnull_cnt;
		else
			stats->stawidth = stats->attrtype->typlen;

		if (nmultiple == 0)
		{
			/*
			 * If we found no repeated non-null values, assume it's a unique
			 * column; but be sure to discount for any nulls we found.
			 */
			stats->stadistinct = -1.0 * (1.0 - stats->stanullfrac);
		}
		else if (toowide_cnt == 0 && nmultiple == ndistinct)
		{
			/*
			 * Every value in the sample appeared more than once.  Assume the
			 * column has just these values.  (This case is meant to address
			 * columns with small, fixed sets of possible values, such as
			 * boolean or enum columns.  If there are any values that appear
			 * just once in the sample, including too-wide values, we should
			 * assume that that's not what we're dealing with.)
			 */
			stats->stadistinct = ndistinct;
		}
		else
		{
			/*----------
			 * Estimate the number of distinct values using the estimator
			 * proposed by Haas and Stokes in IBM Research Report RJ 10025:
			 *		n*d / (n - f1 + f1*n/N)
			 * where f1 is the number of distinct values that occurred
			 * exactly once in our sample of n rows (from a total of N),
			 * and d is the total number of distinct values in the sample.
			 * This is their Duj1 estimator; the other estimators they
			 * recommend are considerably more complex, and are numerically
			 * very unstable when n is much smaller than N.
			 *
			 * In this calculation, we consider only non-nulls.  We used to
			 * include rows with null values in the n and N counts, but that
			 * leads to inaccurate answers in columns with many nulls, and
			 * it's intuitively bogus anyway considering the desired result is
			 * the number of distinct non-null values.
			 *
			 * Overwidth values are assumed to have been distinct.
			 *----------
			 */
			int			f1 = ndistinct - nmultiple + toowide_cnt;
			int			d = f1 + nmultiple;
			double		n = samplerows - null_cnt;
			double		N = totalrows * (1.0 - stats->stanullfrac);
			double		stadistinct;

			/* N == 0 shouldn't happen, but just in case ... */
			if (N > 0)
				stadistinct = (n * d) / ((n - f1) + f1 * n / N);
			else
				stadistinct = 0;

			/* Clamp to sane range in case of roundoff error */
			if (stadistinct < d)
				stadistinct = d;
			if (stadistinct > N)
				stadistinct = N;
			/* And round to integer */
			stats->stadistinct = floor(stadistinct + 0.5);
		}

		/*
		 * If we estimated the number of distinct values at more than 10% of
		 * the total row count (a very arbitrary limit), then assume that
		 * stadistinct should scale with the row count rather than be a fixed
		 * value.
		 */
		if (stats->stadistinct > 0.1 * totalrows)
			stats->stadistinct = -(stats->stadistinct / totalrows);

		/*
		 * Decide how many values are worth storing as most-common values. If
		 * we are able to generate a complete MCV list (all the values in the
		 * sample will fit, and we think these are all the ones in the table),
		 * then do so.  Otherwise, store only those values that are
		 * significantly more common than the values not in the list.
		 *
		 * Note: the first of these cases is meant to address columns with
		 * small, fixed sets of possible values, such as boolean or enum
		 * columns.  If we can *completely* represent the column population by
		 * an MCV list that will fit into the stats target, then we should do
		 * so and thus provide the planner with complete information.  But if
		 * the MCV list is not complete, it's generally worth being more
		 * selective, and not just filling it all the way up to the stats
		 * target.
		 */
		if (track_cnt == ndistinct && toowide_cnt == 0 &&
			stats->stadistinct > 0 &&
			track_cnt <= num_mcv)
		{
			/* Track list includes all values seen, and all will fit */
			num_mcv = track_cnt;
		}
		else
		{
			int		   *mcv_counts;

			/* Incomplete list; decide how many values are worth keeping */
			if (num_mcv > track_cnt)
				num_mcv = track_cnt;

			if (num_mcv > 0)
			{
				mcv_counts = (int *) palloc(num_mcv * sizeof(int));
				for (i = 0; i < num_mcv; i++)
					mcv_counts[i] = track[i].count;

				num_mcv = analyze_mcv_list(mcv_counts, num_mcv,
										   stats->stadistinct,
										   stats->stanullfrac,
										   samplerows, totalrows);
			}
		}

		/* Generate MCV slot entry */
		if (num_mcv > 0)
		{
			MemoryContext old_context;
			Datum	   *mcv_values;
			float4	   *mcv_freqs;

			/* Must copy the target values into anl_context */
			old_context = MemoryContextSwitchTo(stats->anl_context);
			mcv_values = (Datum *) palloc(num_mcv * sizeof(Datum));
			mcv_freqs = (float4 *) palloc(num_mcv * sizeof(float4));
			for (i = 0; i < num_mcv; i++)
			{
				mcv_values[i] = datumCopy(values[track[i].first].value,
										  stats->attrtype->typbyval,
										  stats->attrtype->typlen);
				mcv_freqs[i] = (double) track[i].count / (double) samplerows;
			}
			MemoryContextSwitchTo(old_context);

			stats->stakind[slot_idx] = STATISTIC_KIND_MCV;
			stats->staop[slot_idx] = mystats->eqopr;
			stats->stacoll[slot_idx] = stats->attrcollid;
			stats->stanumbers[slot_idx] = mcv_freqs;
			stats->numnumbers[slot_idx] = num_mcv;
			stats->stavalues[slot_idx] = mcv_values;
			stats->numvalues[slot_idx] = num_mcv;

			/*
			 * Accept the defaults for stats->statypid and others. They have
			 * been set before we were called (see vacuum.h)
			 */
			slot_idx++;
		}

		/*
		 * Generate a histogram slot entry if there are at least two distinct
		 * values not accounted for in the MCV list.  (This ensures the
		 * histogram won't collapse to empty or a singleton.)
		 */
		num_hist = ndistinct - num_mcv;
		if (num_hist > num_bins)
			num_hist = num_bins + 1;
		if (num_hist >= 2)
		{
			MemoryContext old_context;
			Datum	   *hist_values;
			int			nvals;
			int			pos,
						posfrac,
						delta,
						deltafrac;

			/* Sort the MCV items into position order to speed next loop */
			qsort_interruptible(track, num_mcv, sizeof(ScalarMCVItem),
								compare_mcvs, NULL);

			/*
			 * Collapse out the MCV items from the values[] array.
			 *
			 * Note we destroy the values[] array here... but we don't need it
			 * for anything more.  We do, however, still need values_cnt.
			 * nvals will be the number of remaining entries in values[].
			 */
			if (num_mcv > 0)
			{
				int			src,
							dest;
				int			j;

				src = dest = 0;
				j = 0;			/* index of next interesting MCV item */
				while (src < values_cnt)
				{
					int			ncopy;

					if (j < num_mcv)
					{
						int			first = track[j].first;

						if (src >= first)
						{
							/* advance past this MCV item */
							src = first + track[j].count;
							j++;
							continue;
						}
						ncopy = first - src;
					}
					else
						ncopy = values_cnt - src;
					memmove(&values[dest], &values[src],
							ncopy * sizeof(ScalarItem));
					src += ncopy;
					dest += ncopy;
				}
				nvals = dest;
			}
			else
				nvals = values_cnt;
			Assert(nvals >= num_hist);

			/* Must copy the target values into anl_context */
			old_context = MemoryContextSwitchTo(stats->anl_context);
			hist_values = (Datum *) palloc(num_hist * sizeof(Datum));

			/*
			 * The object of this loop is to copy the first and last values[]
			 * entries along with evenly-spaced values in between.  So the
			 * i'th value is values[(i * (nvals - 1)) / (num_hist - 1)].  But
			 * computing that subscript directly risks integer overflow when
			 * the stats target is more than a couple thousand.  Instead we
			 * add (nvals - 1) / (num_hist - 1) to pos at each step, tracking
			 * the integral and fractional parts of the sum separately.
			 */
			delta = (nvals - 1) / (num_hist - 1);
			deltafrac = (nvals - 1) % (num_hist - 1);
			pos = posfrac = 0;

			for (i = 0; i < num_hist; i++)
			{
				hist_values[i] = datumCopy(values[pos].value,
										   stats->attrtype->typbyval,
										   stats->attrtype->typlen);
				pos += delta;
				posfrac += deltafrac;
				if (posfrac >= (num_hist - 1))
				{
					/* fractional part exceeds 1, carry to integer part */
					pos++;
					posfrac -= (num_hist - 1);
				}
			}

			MemoryContextSwitchTo(old_context);

			stats->stakind[slot_idx] = STATISTIC_KIND_HISTOGRAM;
			stats->staop[slot_idx] = mystats->ltopr;
			stats->stacoll[slot_idx] = stats->attrcollid;
			stats->stavalues[slot_idx] = hist_values;
			stats->numvalues[slot_idx] = num_hist;

			/*
			 * Accept the defaults for stats->statypid and others. They have
			 * been set before we were called (see vacuum.h)
			 */
			slot_idx++;
		}

		/* Generate a correlation entry if there are multiple values */
		if (values_cnt > 1)
		{
			MemoryContext old_context;
			float4	   *corrs;
			double		corr_xsum,
						corr_x2sum;

			/* Must copy the target values into anl_context */
			old_context = MemoryContextSwitchTo(stats->anl_context);
			corrs = palloc_object(float4);
			MemoryContextSwitchTo(old_context);

			/*----------
			 * Since we know the x and y value sets are both
			 *		0, 1, ..., values_cnt-1
			 * we have sum(x) = sum(y) =
			 *		(values_cnt-1)*values_cnt / 2
			 * and sum(x^2) = sum(y^2) =
			 *		(values_cnt-1)*values_cnt*(2*values_cnt-1) / 6.
			 *----------
			 */
			corr_xsum = ((double) (values_cnt - 1)) *
				((double) values_cnt) / 2.0;
			corr_x2sum = ((double) (values_cnt - 1)) *
				((double) values_cnt) * (double) (2 * values_cnt - 1) / 6.0;

			/* And the correlation coefficient reduces to */
			corrs[0] = (values_cnt * corr_xysum - corr_xsum * corr_xsum) /
				(values_cnt * corr_x2sum - corr_xsum * corr_xsum);

			stats->stakind[slot_idx] = STATISTIC_KIND_CORRELATION;
			stats->staop[slot_idx] = mystats->ltopr;
			stats->stacoll[slot_idx] = stats->attrcollid;
			stats->stanumbers[slot_idx] = corrs;
			stats->numnumbers[slot_idx] = 1;
			slot_idx++;
		}
	}
	else if (nonnull_cnt > 0)
	{
		/* We found some non-null values, but they were all too wide */
		Assert(nonnull_cnt == toowide_cnt);
		stats->stats_valid = true;
		/* Do the simple null-frac and width stats */
		stats->stanullfrac = (double) null_cnt / (double) samplerows;
		if (is_varwidth)
			stats->stawidth = total_width / (double) nonnull_cnt;
		else
			stats->stawidth = stats->attrtype->typlen;
		/* Assume all too-wide values are distinct, so it's a unique column */
		stats->stadistinct = -1.0 * (1.0 - stats->stanullfrac);
	}
	else if (null_cnt > 0)
	{
		/* We found only nulls; assume the column is entirely null */
		stats->stats_valid = true;
		stats->stanullfrac = 1.0;
		if (is_varwidth)
			stats->stawidth = 0;	/* "unknown" */
		else
			stats->stawidth = stats->attrtype->typlen;
		stats->stadistinct = 0.0;	/* "unknown" */
	}

	/* We don't need to bother cleaning up any of our temporary palloc's */
}

/*
 * Comparator for sorting ScalarItems
 *
 * Aside from sorting the items, we update the tupnoLink[] array
 * whenever two ScalarItems are found to contain equal datums.  The array
 * is indexed by tupno; for each ScalarItem, it contains the highest
 * tupno that that item's datum has been found to be equal to.  This allows
 * us to avoid additional comparisons in compute_scalar_stats().
 */
static int
compare_scalars(const void *a, const void *b, void *arg)
{
	Datum		da = ((const ScalarItem *) a)->value;
	int			ta = ((const ScalarItem *) a)->tupno;
	Datum		db = ((const ScalarItem *) b)->value;
	int			tb = ((const ScalarItem *) b)->tupno;
	CompareScalarsContext *cxt = (CompareScalarsContext *) arg;
	int			compare;

	compare = ApplySortComparator(da, false, db, false, cxt->ssup);
	if (compare != 0)
		return compare;

	/*
	 * The two datums are equal, so update cxt->tupnoLink[].
	 */
	if (cxt->tupnoLink[ta] < tb)
		cxt->tupnoLink[ta] = tb;
	if (cxt->tupnoLink[tb] < ta)
		cxt->tupnoLink[tb] = ta;

	/*
	 * For equal datums, sort by tupno
	 */
	return ta - tb;
}

/*
 * Comparator for sorting ScalarMCVItems by position
 */
static int
compare_mcvs(const void *a, const void *b, void *arg)
{
	int			da = ((const ScalarMCVItem *) a)->first;
	int			db = ((const ScalarMCVItem *) b)->first;

	return da - db;
}

/*
 * Analyze the list of common values in the sample and decide how many are
 * worth storing in the table's MCV list.
 *
 * mcv_counts is assumed to be a list of the counts of the most common values
 * seen in the sample, starting with the most common.  The return value is the
 * number that are significantly more common than the values not in the list,
 * and which are therefore deemed worth storing in the table's MCV list.
 */
static int
analyze_mcv_list(int *mcv_counts,
				 int num_mcv,
				 double stadistinct,
				 double stanullfrac,
				 int samplerows,
				 double totalrows)
{
	double		ndistinct_table;
	double		sumcount;
	int			i;

	/*
	 * If the entire table was sampled, keep the whole list.  This also
	 * protects us against division by zero in the code below.
	 */
	if (samplerows == totalrows || totalrows <= 1.0)
		return num_mcv;

	/* Re-extract the estimated number of distinct nonnull values in table */
	ndistinct_table = stadistinct;
	if (ndistinct_table < 0)
		ndistinct_table = -ndistinct_table * totalrows;

	/*
	 * Exclude the least common values from the MCV list, if they are not
	 * significantly more common than the estimated selectivity they would
	 * have if they weren't in the list.  All non-MCV values are assumed to be
	 * equally common, after taking into account the frequencies of all the
	 * values in the MCV list and the number of nulls (c.f. eqsel()).
	 *
	 * Here sumcount tracks the total count of all but the last (least common)
	 * value in the MCV list, allowing us to determine the effect of excluding
	 * that value from the list.
	 *
	 * Note that we deliberately do this by removing values from the full
	 * list, rather than starting with an empty list and adding values,
	 * because the latter approach can fail to add any values if all the most
	 * common values have around the same frequency and make up the majority
	 * of the table, so that the overall average frequency of all values is
	 * roughly the same as that of the common values.  This would lead to any
	 * uncommon values being significantly overestimated.
	 */
	sumcount = 0.0;
	for (i = 0; i < num_mcv - 1; i++)
		sumcount += mcv_counts[i];

	while (num_mcv > 0)
	{
		double		selec,
					otherdistinct,
					N,
					n,
					K,
					variance,
					stddev;

		/*
		 * Estimated selectivity the least common value would have if it
		 * wasn't in the MCV list (c.f. eqsel()).
		 */
		selec = 1.0 - sumcount / samplerows - stanullfrac;
		if (selec < 0.0)
			selec = 0.0;
		if (selec > 1.0)
			selec = 1.0;
		otherdistinct = ndistinct_table - (num_mcv - 1);
		if (otherdistinct > 1)
			selec /= otherdistinct;

		/*
		 * If the value is kept in the MCV list, its population frequency is
		 * assumed to equal its sample frequency.  We use the lower end of a
		 * textbook continuity-corrected Wald-type confidence interval to
		 * determine if that is significantly more common than the non-MCV
		 * frequency --- specifically we assume the population frequency is
		 * highly likely to be within around 2 standard errors of the sample
		 * frequency, which equates to an interval of 2 standard deviations
		 * either side of the sample count, plus an additional 0.5 for the
		 * continuity correction.  Since we are sampling without replacement,
		 * this is a hypergeometric distribution.
		 *
		 * XXX: Empirically, this approach seems to work quite well, but it
		 * may be worth considering more advanced techniques for estimating
		 * the confidence interval of the hypergeometric distribution.
		 */
		N = totalrows;
		n = samplerows;
		K = N * mcv_counts[num_mcv - 1] / n;
		variance = n * K * (N - K) * (N - n) / (N * N * (N - 1));
		stddev = sqrt(variance);

		if (mcv_counts[num_mcv - 1] > selec * samplerows + 2 * stddev + 0.5)
		{
			/*
			 * The value is significantly more common than the non-MCV
			 * selectivity would suggest.  Keep it, and all the other more
			 * common values in the list.
			 */
			break;
		}
		else
		{
			/* Discard this value and consider the next least common value */
			num_mcv--;
			if (num_mcv == 0)
				break;
			sumcount -= mcv_counts[num_mcv - 1];
		}
	}
	return num_mcv;
}
./bootstrap.c0000664000175000017500000010076715221603750012102 0ustar  xmanxman/*-------------------------------------------------------------------------
 *
 * bootstrap.c
 *	  routines to support running postgres in 'bootstrap' mode
 *	bootstrap mode is used to create the initial template database
 *
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *	  src/backend/bootstrap/bootstrap.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include <unistd.h>
#include <signal.h>

#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/tableam.h"
#include "access/toast_compression.h"
#include "access/xact.h"
#include "bootstrap/bootstrap.h"
#include "catalog/index.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "common/link-canary.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "port/pg_getopt_ctx.h"
#include "postmaster/postmaster.h"
#include "storage/bufpage.h"
#include "storage/checksum.h"
#include "storage/fd.h"
#include "storage/ipc.h"
#include "storage/proc.h"
#include "storage/shmem_internal.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/relmapper.h"


static void CheckerModeMain(void);
static void bootstrap_signals(void);
static Form_pg_attribute AllocateAttribute(void);
static void InsertOneProargdefaultsValue(char *value);
static void populate_typ_list(void);
static Oid	gettype(char *type);
static void cleanup(void);

/* ----------------
 *		global variables
 * ----------------
 */

Relation	boot_reldesc;		/* current relation descriptor */

Form_pg_attribute attrtypes[MAXATTR];	/* points to attribute info */
int			numattr;			/* number of attributes for cur. rel */


/*
 * Basic information associated with each type.  This is used before
 * pg_type is filled, so it has to cover the datatypes used as column types
 * in the core "bootstrapped" catalogs.
 *
 *		XXX several of these input/output functions do catalog scans
 *			(e.g., F_REGPROCIN scans pg_proc).  this obviously creates some
 *			order dependencies in the catalog creation process.
 */
struct typinfo
{
	char		name[NAMEDATALEN];
	Oid			oid;
	Oid			elem;
	int16		len;
	bool		byval;
	char		align;
	char		storage;
	Oid			collation;
	Oid			inproc;
	Oid			outproc;
};

static const struct typinfo TypInfo[] = {
	{"bool", BOOLOID, 0, 1, true, TYPALIGN_CHAR, TYPSTORAGE_PLAIN, InvalidOid,
	F_BOOLIN, F_BOOLOUT},
	{"bytea", BYTEAOID, 0, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, InvalidOid,
	F_BYTEAIN, F_BYTEAOUT},
	{"char", CHAROID, 0, 1, true, TYPALIGN_CHAR, TYPSTORAGE_PLAIN, InvalidOid,
	F_CHARIN, F_CHAROUT},
	{"cstring", CSTRINGOID, 0, -2, false, TYPALIGN_CHAR, TYPSTORAGE_PLAIN, InvalidOid,
	F_CSTRING_IN, F_CSTRING_OUT},
	{"int2", INT2OID, 0, 2, true, TYPALIGN_SHORT, TYPSTORAGE_PLAIN, InvalidOid,
	F_INT2IN, F_INT2OUT},
	{"int4", INT4OID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid,
	F_INT4IN, F_INT4OUT},
	{"int8", INT8OID, 0, 8, true, TYPALIGN_DOUBLE, TYPSTORAGE_PLAIN, InvalidOid,
	F_INT8IN, F_INT8OUT},
	{"float4", FLOAT4OID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid,
	F_FLOAT4IN, F_FLOAT4OUT},
	{"float8", FLOAT8OID, 0, 8, true, TYPALIGN_DOUBLE, TYPSTORAGE_PLAIN, InvalidOid,
	F_FLOAT8IN, F_FLOAT8OUT},
	{"name", NAMEOID, CHAROID, NAMEDATALEN, false, TYPALIGN_CHAR, TYPSTORAGE_PLAIN, C_COLLATION_OID,
	F_NAMEIN, F_NAMEOUT},
	{"regproc", REGPROCOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid,
	F_REGPROCIN, F_REGPROCOUT},
	{"text", TEXTOID, 0, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, DEFAULT_COLLATION_OID,
	F_TEXTIN, F_TEXTOUT},
	{"jsonb", JSONBOID, 0, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, InvalidOid,
	F_JSONB_IN, F_JSONB_OUT},
	{"oid", OIDOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid,
	F_OIDIN, F_OIDOUT},
	{"aclitem", ACLITEMOID, 0, 16, false, TYPALIGN_DOUBLE, TYPSTORAGE_PLAIN, InvalidOid,
	F_ACLITEMIN, F_ACLITEMOUT},
	{"pg_node_tree", PG_NODE_TREEOID, 0, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, DEFAULT_COLLATION_OID,
	F_PG_NODE_TREE_IN, F_PG_NODE_TREE_OUT},
	{"int2vector", INT2VECTOROID, INT2OID, -1, false, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid,
	F_INT2VECTORIN, F_INT2VECTOROUT},
	{"oidvector", OIDVECTOROID, OIDOID, -1, false, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid,
	F_OIDVECTORIN, F_OIDVECTOROUT},
	{"_int4", INT4ARRAYOID, INT4OID, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, InvalidOid,
	F_ARRAY_IN, F_ARRAY_OUT},
	{"_text", TEXTARRAYOID, TEXTOID, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, DEFAULT_COLLATION_OID,
	F_ARRAY_IN, F_ARRAY_OUT},
	{"_oid", OIDARRAYOID, OIDOID, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, InvalidOid,
	F_ARRAY_IN, F_ARRAY_OUT},
	{"_char", CHARARRAYOID, CHAROID, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, InvalidOid,
	F_ARRAY_IN, F_ARRAY_OUT},
	{"_aclitem", ACLITEMARRAYOID, ACLITEMOID, -1, false, TYPALIGN_DOUBLE, TYPSTORAGE_EXTENDED, InvalidOid,
	F_ARRAY_IN, F_ARRAY_OUT}
};

static const int n_types = sizeof(TypInfo) / sizeof(struct typinfo);

struct typmap
{								/* a hack */
	Oid			am_oid;
	FormData_pg_type am_typ;
};

static List *Typ = NIL;			/* List of struct typmap* */
static struct typmap *Ap = NULL;

/*
 * Basic information about built-in roles.
 *
 * Presently, this need only list roles that are mentioned in aclitem arrays
 * in the catalog .dat files.  We might as well list everything that is in
 * pg_authid.dat, since there aren't that many.  Like pg_authid.dat, we
 * represent the bootstrap superuser's name as "POSTGRES", even though it
 * (probably) won't be that in the finished installation; this means aclitem
 * entries in .dat files must spell it like that.
 */
struct rolinfo
{
	const char *rolname;
	Oid			oid;
};

static const struct rolinfo RolInfo[] = {
	{"POSTGRES", BOOTSTRAP_SUPERUSERID},
	{"pg_database_owner", ROLE_PG_DATABASE_OWNER},
	{"pg_read_all_data", ROLE_PG_READ_ALL_DATA},
	{"pg_write_all_data", ROLE_PG_WRITE_ALL_DATA},
	{"pg_monitor", ROLE_PG_MONITOR},
	{"pg_read_all_settings", ROLE_PG_READ_ALL_SETTINGS},
	{"pg_read_all_stats", ROLE_PG_READ_ALL_STATS},
	{"pg_stat_scan_tables", ROLE_PG_STAT_SCAN_TABLES},
	{"pg_read_server_files", ROLE_PG_READ_SERVER_FILES},
	{"pg_write_server_files", ROLE_PG_WRITE_SERVER_FILES},
	{"pg_execute_server_program", ROLE_PG_EXECUTE_SERVER_PROGRAM},
	{"pg_signal_backend", ROLE_PG_SIGNAL_BACKEND},
	{"pg_checkpoint", ROLE_PG_CHECKPOINT},
	{"pg_maintain", ROLE_PG_MAINTAIN},
	{"pg_use_reserved_connections", ROLE_PG_USE_RESERVED_CONNECTIONS},
	{"pg_create_subscription", ROLE_PG_CREATE_SUBSCRIPTION},
	{"pg_signal_autovacuum_worker", ROLE_PG_SIGNAL_AUTOVACUUM_WORKER}
};


static Datum values[MAXATTR];	/* current row's attribute values */
static bool Nulls[MAXATTR];

static MemoryContext nogc = NULL;	/* special no-gc mem context */

/*
 *	At bootstrap time, we first declare all the indices to be built, and
 *	then build them.  The IndexList structure stores enough information
 *	to allow us to build the indices after they've been declared.
 */

typedef struct _IndexList
{
	Oid			il_heap;
	Oid			il_ind;
	IndexInfo  *il_info;
	struct _IndexList *il_next;
} IndexList;

static IndexList *ILHead = NULL;


/*
 * In shared memory checker mode, all we really want to do is create shared
 * memory and semaphores (just to prove we can do it with the current GUC
 * settings).  Since, in fact, that was already done by
 * CreateSharedMemoryAndSemaphores(), we have nothing more to do here.
 */
static void
CheckerModeMain(void)
{
	proc_exit(0);
}

/*
 *	 The main entry point for running the backend in bootstrap mode
 *
 *	 The bootstrap mode is used to initialize the template database.
 *	 The bootstrap backend doesn't speak SQL, but instead expects
 *	 commands in a special bootstrap language.
 *
 *	 When check_only is true, startup is done only far enough to verify that
 *	 the current configuration, particularly the passed in options pertaining
 *	 to shared memory sizing, options work (or at least do not cause an error
 *	 up to shared memory creation).
 */
void
BootstrapModeMain(int argc, char *argv[], bool check_only)
{
	int			i;
	char	   *progname = argv[0];
	pg_getopt_ctx optctx;
	int			flag;
	char	   *userDoption = NULL;
	uint32		bootstrap_data_checksum_version = PG_DATA_CHECKSUM_OFF;
	yyscan_t	scanner;

	Assert(!IsUnderPostmaster);

	InitStandaloneProcess(argv[0]);

	/* Set defaults, to be overridden by explicit options below */
	InitializeGUCOptions();

	/* an initial --boot or --check should be present */
	Assert(argc > 1
		   && (strcmp(argv[1], "--boot") == 0
			   || strcmp(argv[1], "--check") == 0));
	argv++;
	argc--;

	pg_getopt_start(&optctx, argc, argv, "B:c:d:D:Fkr:X:-:");
	while ((flag = pg_getopt_next(&optctx)) != -1)
	{
		switch (flag)
		{
			case 'B':
				SetConfigOption("shared_buffers", optctx.optarg, PGC_POSTMASTER, PGC_S_ARGV);
				break;
			case '-':

				/*
				 * Error if the user misplaced a special must-be-first option
				 * for dispatching to a subprogram.  parse_dispatch_option()
				 * returns DISPATCH_POSTMASTER if it doesn't find a match, so
				 * error for anything else.
				 */
				if (parse_dispatch_option(optctx.optarg) != DISPATCH_POSTMASTER)
					ereport(ERROR,
							(errcode(ERRCODE_SYNTAX_ERROR),
							 errmsg("--%s must be first argument", optctx.optarg)));

				pg_fallthrough;
			case 'c':
				{
					char	   *name,
							   *value;

					ParseLongOption(optctx.optarg, &name, &value);
					if (!value)
					{
						if (flag == '-')
							ereport(ERROR,
									(errcode(ERRCODE_SYNTAX_ERROR),
									 errmsg("--%s requires a value",
											optctx.optarg)));
						else
							ereport(ERROR,
									(errcode(ERRCODE_SYNTAX_ERROR),
									 errmsg("-c %s requires a value",
											optctx.optarg)));
					}

					SetConfigOption(name, value, PGC_POSTMASTER, PGC_S_ARGV);
					pfree(name);
					pfree(value);
					break;
				}
			case 'D':
				userDoption = pstrdup(optctx.optarg);
				break;
			case 'd':
				{
					/* Turn on debugging for the bootstrap process. */
					char	   *debugstr;

					debugstr = psprintf("debug%s", optctx.optarg);
					SetConfigOption("log_min_messages", debugstr,
									PGC_POSTMASTER, PGC_S_ARGV);
					SetConfigOption("client_min_messages", debugstr,
									PGC_POSTMASTER, PGC_S_ARGV);
					pfree(debugstr);
				}
				break;
			case 'F':
				SetConfigOption("fsync", "false", PGC_POSTMASTER, PGC_S_ARGV);
				break;
			case 'k':
				bootstrap_data_checksum_version = PG_DATA_CHECKSUM_VERSION;
				break;
			case 'r':
				strlcpy(OutputFileName, optctx.optarg, MAXPGPATH);
				break;
			case 'X':
				SetConfigOption("wal_segment_size", optctx.optarg, PGC_INTERNAL, PGC_S_DYNAMIC_DEFAULT);
				break;
			default:
				write_stderr("Try \"%s --help\" for more information.\n",
							 progname);
				proc_exit(1);
				break;
		}
	}

	if (argc != optctx.optind)
	{
		write_stderr("%s: invalid command-line arguments\n", progname);
		proc_exit(1);
	}

	/* Acquire configuration parameters */
	if (!SelectConfigFiles(userDoption, progname))
		proc_exit(1);

	/*
	 * Validate we have been given a reasonable-looking DataDir and change
	 * into it
	 */
	checkDataDir();
	ChangeToDataDir();

	CreateDataDirLockFile(false);

	SetProcessingMode(BootstrapProcessing);
	IgnoreSystemIndexes = true;

	RegisterBuiltinShmemCallbacks();

	InitializeMaxBackends();

	/*
	 * Even though bootstrapping runs in single-process mode, initialize
	 * postmaster child slots array so that --check can detect running out of
	 * shared memory or other resources if max_connections is set too high.
	 */
	InitPostmasterChildSlots();

	InitializeFastPathLocks();

	ShmemCallRequestCallbacks();
	CreateSharedMemoryAndSemaphores();

	/*
	 * Estimate number of openable files.  This is essential too in --check
	 * mode, because on some platforms semaphores count as open files.
	 */
	set_max_safe_fds();

	/*
	 * XXX: It might make sense to move this into its own function at some
	 * point. Right now it seems like it'd cause more code duplication than
	 * it's worth.
	 */
	if (check_only)
	{
		SetProcessingMode(NormalProcessing);
		CheckerModeMain();
		abort();
	}

	/*
	 * Do backend-like initialization for bootstrap mode
	 */
	InitProcess();

	BaseInit();

	bootstrap_signals();
	BootStrapXLOG(bootstrap_data_checksum_version);

	/*
	 * To ensure that src/common/link-canary.c is linked into the backend, we
	 * must call it from somewhere.  Here is as good as anywhere.
	 */
	if (pg_link_canary_is_frontend())
		elog(ERROR, "backend is incorrectly linked to frontend functions");

	InitPostgres(NULL, InvalidOid, NULL, InvalidOid, 0, NULL);

	/* Initialize stuff for bootstrap-file processing */
	for (i = 0; i < MAXATTR; i++)
	{
		attrtypes[i] = NULL;
		Nulls[i] = false;
	}

	if (boot_yylex_init(&scanner) != 0)
		elog(ERROR, "yylex_init() failed: %m");

	/*
	 * Process bootstrap input.
	 */
	StartTransactionCommand();
	boot_yyparse(scanner);
	CommitTransactionCommand();

	/*
	 * We should now know about all mapped relations, so it's okay to write
	 * out the initial relation mapping files.
	 */
	RelationMapFinishBootstrap();

	/* Clean up and exit */
	cleanup();
	proc_exit(0);
}


/* ----------------------------------------------------------------
 *						misc functions
 * ----------------------------------------------------------------
 */

/*
 * Set up signal handling for a bootstrap process
 */
static void
bootstrap_signals(void)
{
	Assert(!IsUnderPostmaster);

	/*
	 * We don't actually need any non-default signal handling in bootstrap
	 * mode; "curl up and die" is a sufficient response for all these cases.
	 * Let's set that handling explicitly, as documentation if nothing else.
	 */
	pqsignal(SIGHUP, PG_SIG_DFL);
	pqsignal(SIGINT, PG_SIG_DFL);
	pqsignal(SIGTERM, PG_SIG_DFL);
	pqsignal(SIGQUIT, PG_SIG_DFL);
}

/* ----------------------------------------------------------------
 *				MANUAL BACKEND INTERACTIVE INTERFACE COMMANDS
 * ----------------------------------------------------------------
 */

/* ----------------
 *		boot_openrel
 *
 * Execute BKI OPEN command.
 * ----------------
 */
void
boot_openrel(char *relname)
{
	int			i;

	if (strlen(relname) >= NAMEDATALEN)
		relname[NAMEDATALEN - 1] = '\0';

	/*
	 * pg_type must be filled before any OPEN command is executed, hence we
	 * can now populate Typ if we haven't yet.
	 */
	if (Typ == NIL)
		populate_typ_list();

	if (boot_reldesc != NULL)
		closerel(NULL);

	elog(DEBUG4, "open relation %s, attrsize %d",
		 relname, (int) ATTRIBUTE_FIXED_PART_SIZE);

	boot_reldesc = table_openrv(makeRangeVar(NULL, relname, -1), NoLock);
	numattr = RelationGetNumberOfAttributes(boot_reldesc);
	for (i = 0; i < numattr; i++)
	{
		if (attrtypes[i] == NULL)
			attrtypes[i] = AllocateAttribute();
		memmove(attrtypes[i],
				TupleDescAttr(boot_reldesc->rd_att, i),
				ATTRIBUTE_FIXED_PART_SIZE);

		{
			Form_pg_attribute at = attrtypes[i];

			elog(DEBUG4, "create attribute %d name %s len %d num %d type %u",
				 i, NameStr(at->attname), at->attlen, at->attnum,
				 at->atttypid);
		}
	}
}

/* ----------------
 *		closerel
 * ----------------
 */
void
closerel(char *relname)
{
	if (relname)
	{
		if (boot_reldesc)
		{
			if (strcmp(RelationGetRelationName(boot_reldesc), relname) != 0)
				elog(ERROR, "close of %s when %s was expected",
					 relname, RelationGetRelationName(boot_reldesc));
		}
		else
			elog(ERROR, "close of %s before any relation was opened",
				 relname);
	}

	if (boot_reldesc == NULL)
		elog(ERROR, "no open relation to close");
	else
	{
		elog(DEBUG4, "close relation %s",
			 RelationGetRelationName(boot_reldesc));
		table_close(boot_reldesc, NoLock);
		boot_reldesc = NULL;
	}
}



/* ----------------
 * DEFINEATTR()
 *
 * define a <field,type> pair
 * if there are n fields in a relation to be created, this routine
 * will be called n times
 * ----------------
 */
void
DefineAttr(char *name, char *type, int attnum, int nullness)
{
	Oid			typeoid;

	if (boot_reldesc != NULL)
	{
		elog(WARNING, "no open relations allowed with CREATE command");
		closerel(NULL);
	}

	if (attrtypes[attnum] == NULL)
		attrtypes[attnum] = AllocateAttribute();
	MemSet(attrtypes[attnum], 0, ATTRIBUTE_FIXED_PART_SIZE);

	namestrcpy(&attrtypes[attnum]->attname, name);
	elog(DEBUG4, "column %s %s", NameStr(attrtypes[attnum]->attname), type);
	attrtypes[attnum]->attnum = attnum + 1;

	typeoid = gettype(type);

	if (Typ != NIL)
	{
		attrtypes[attnum]->atttypid = Ap->am_oid;
		attrtypes[attnum]->attlen = Ap->am_typ.typlen;
		attrtypes[attnum]->attbyval = Ap->am_typ.typbyval;
		attrtypes[attnum]->attalign = Ap->am_typ.typalign;
		attrtypes[attnum]->attstorage = Ap->am_typ.typstorage;
		attrtypes[attnum]->attcompression = InvalidCompressionMethod;
		attrtypes[attnum]->attcollation = Ap->am_typ.typcollation;
		/* if an array type, assume 1-dimensional attribute */
		if (Ap->am_typ.typelem != InvalidOid && Ap->am_typ.typlen < 0)
			attrtypes[attnum]->attndims = 1;
		else
			attrtypes[attnum]->attndims = 0;
	}
	else
	{
		attrtypes[attnum]->atttypid = TypInfo[typeoid].oid;
		attrtypes[attnum]->attlen = TypInfo[typeoid].len;
		attrtypes[attnum]->attbyval = TypInfo[typeoid].byval;
		attrtypes[attnum]->attalign = TypInfo[typeoid].align;
		attrtypes[attnum]->attstorage = TypInfo[typeoid].storage;
		attrtypes[attnum]->attcompression = InvalidCompressionMethod;
		attrtypes[attnum]->attcollation = TypInfo[typeoid].collation;
		/* if an array type, assume 1-dimensional attribute */
		if (TypInfo[typeoid].elem != InvalidOid &&
			attrtypes[attnum]->attlen < 0)
			attrtypes[attnum]->attndims = 1;
		else
			attrtypes[attnum]->attndims = 0;
	}

	/*
	 * If a system catalog column is collation-aware, force it to use C
	 * collation, so that its behavior is independent of the database's
	 * collation.  This is essential to allow template0 to be cloned with a
	 * different database collation.
	 */
	if (OidIsValid(attrtypes[attnum]->attcollation))
		attrtypes[attnum]->attcollation = C_COLLATION_OID;

	attrtypes[attnum]->atttypmod = -1;
	attrtypes[attnum]->attislocal = true;

	if (nullness == BOOTCOL_NULL_FORCE_NOT_NULL)
	{
		attrtypes[attnum]->attnotnull = true;
	}
	else if (nullness == BOOTCOL_NULL_FORCE_NULL)
	{
		attrtypes[attnum]->attnotnull = false;
	}
	else
	{
		Assert(nullness == BOOTCOL_NULL_AUTO);

		/*
		 * Mark as "not null" if type is fixed-width and prior columns are
		 * likewise fixed-width and not-null.  This corresponds to case where
		 * column can be accessed directly via C struct declaration.
		 */
		if (attrtypes[attnum]->attlen > 0)
		{
			int			i;

			/* check earlier attributes */
			for (i = 0; i < attnum; i++)
			{
				if (attrtypes[i]->attlen <= 0 ||
					!attrtypes[i]->attnotnull)
					break;
			}
			if (i == attnum)
				attrtypes[attnum]->attnotnull = true;
		}
	}
}


/* ----------------
 *		InsertOneTuple
 *
 * If objectid is not zero, it is a specific OID to assign to the tuple.
 * Otherwise, an OID will be assigned (if necessary) by heap_insert.
 * ----------------
 */
void
InsertOneTuple(void)
{
	HeapTuple	tuple;
	TupleDesc	tupDesc;
	int			i;

	elog(DEBUG4, "inserting row with %d columns", numattr);

	tupDesc = CreateTupleDesc(numattr, attrtypes);
	tuple = heap_form_tuple(tupDesc, values, Nulls);
	pfree(tupDesc);				/* just free's tupDesc, not the attrtypes */

	simple_heap_insert(boot_reldesc, tuple);
	heap_freetuple(tuple);
	elog(DEBUG4, "row inserted");

	/*
	 * Reset null markers for next tuple
	 */
	for (i = 0; i < numattr; i++)
		Nulls[i] = false;
}

/* ----------------
 *		InsertOneValue
 * ----------------
 */
void
InsertOneValue(char *value, int i)
{
	Form_pg_attribute attr;
	Oid			typoid;
	int16		typlen;
	bool		typbyval;
	char		typalign;
	char		typdelim;
	Oid			typioparam;
	Oid			typinput;
	Oid			typoutput;
	Oid			typcollation;

	Assert(i >= 0 && i < MAXATTR);

	elog(DEBUG4, "inserting column %d value \"%s\"", i, value);

	attr = TupleDescAttr(RelationGetDescr(boot_reldesc), i);
	typoid = attr->atttypid;

	boot_get_type_io_data(typoid,
						  &typlen, &typbyval, &typalign,
						  &typdelim, &typioparam,
						  &typinput, &typoutput,
						  &typcollation);

	/*
	 * pg_node_tree values can't be inserted normally (pg_node_tree_in would
	 * just error out), so provide special cases for such columns that we
	 * would like to fill during bootstrap.
	 */
	if (typoid == PG_NODE_TREEOID)
	{
		/* pg_proc.proargdefaults */
		if (RelationGetRelid(boot_reldesc) == ProcedureRelationId &&
			i == Anum_pg_proc_proargdefaults - 1)
			InsertOneProargdefaultsValue(value);
		else					/* maybe other cases later */
			elog(ERROR, "can't handle pg_node_tree input for %s.%s",
				 RelationGetRelationName(boot_reldesc),
				 NameStr(attr->attname));
	}
	else
	{
		/* Normal case */
		values[i] = OidInputFunctionCall(typinput, value, typioparam, -1);
	}

	/*
	 * We use ereport not elog here so that parameters aren't evaluated unless
	 * the message is going to be printed, which generally it isn't
	 */
	ereport(DEBUG4,
			(errmsg_internal("inserted -> %s",
							 OidOutputFunctionCall(typoutput, values[i]))));
}

/* ----------------
 *		InsertOneProargdefaultsValue
 *
 * In general, proargdefaults can be a list of any expressions, but
 * for bootstrap we only support a list of Const nodes.  The input
 * has the form of a text array, and we feed non-null elements to the
 * typinput functions for the appropriate parameters.
 * ----------------
 */
static void
InsertOneProargdefaultsValue(char *value)
{
	int			pronargs;
	oidvector  *proargtypes;
	Datum		arrayval;
	Datum	   *array_datums;
	bool	   *array_nulls;
	int			array_count;
	List	   *proargdefaults;
	char	   *nodestring;

	/* The pg_proc columns we need to use must have been filled already */
	StaticAssertDecl(Anum_pg_proc_pronargs < Anum_pg_proc_proargdefaults,
					 "pronargs must come before proargdefaults");
	StaticAssertDecl(Anum_pg_proc_pronargdefaults < Anum_pg_proc_proargdefaults,
					 "pronargdefaults must come before proargdefaults");
	StaticAssertDecl(Anum_pg_proc_proargtypes < Anum_pg_proc_proargdefaults,
					 "proargtypes must come before proargdefaults");
	if (Nulls[Anum_pg_proc_pronargs - 1])
		elog(ERROR, "pronargs must not be null");
	if (Nulls[Anum_pg_proc_proargtypes - 1])
		elog(ERROR, "proargtypes must not be null");
	pronargs = DatumGetInt16(values[Anum_pg_proc_pronargs - 1]);
	proargtypes = DatumGetPointer(values[Anum_pg_proc_proargtypes - 1]);
	Assert(pronargs == proargtypes->dim1);

	/* Parse the input string as an array value, then deconstruct to Datums */
	arrayval = OidFunctionCall3(F_ARRAY_IN,
								CStringGetDatum(value),
								ObjectIdGetDatum(CSTRINGOID),
								Int32GetDatum(-1));
	deconstruct_array_builtin(DatumGetArrayTypeP(arrayval), CSTRINGOID,
							  &array_datums, &array_nulls, &array_count);

	/* The values should correspond to the last N argtypes */
	if (array_count > pronargs)
		elog(ERROR, "too many proargdefaults entries");

	/* Build the List of Const nodes */
	proargdefaults = NIL;
	for (int i = 0; i < array_count; i++)
	{
		Oid			argtype = proargtypes->values[pronargs - array_count + i];
		int16		typlen;
		bool		typbyval;
		char		typalign;
		char		typdelim;
		Oid			typioparam;
		Oid			typinput;
		Oid			typoutput;
		Oid			typcollation;
		Datum		defval;
		bool		defnull;
		Const	   *defConst;

		boot_get_type_io_data(argtype,
							  &typlen, &typbyval, &typalign,
							  &typdelim, &typioparam,
							  &typinput, &typoutput,
							  &typcollation);

		defnull = array_nulls[i];
		if (defnull)
			defval = (Datum) 0;
		else
			defval = OidInputFunctionCall(typinput,
										  DatumGetCString(array_datums[i]),
										  typioparam, -1);

		defConst = makeConst(argtype,
							 -1,	/* never any typmod */
							 typcollation,
							 typlen,
							 defval,
							 defnull,
							 typbyval);
		proargdefaults = lappend(proargdefaults, defConst);
	}

	/*
	 * Flatten the List to a node-tree string, then convert to a text datum,
	 * which is the storage representation of pg_node_tree.
	 */
	nodestring = nodeToString(proargdefaults);
	values[Anum_pg_proc_proargdefaults - 1] = CStringGetTextDatum(nodestring);
	Nulls[Anum_pg_proc_proargdefaults - 1] = false;

	/*
	 * Hack: fill in pronargdefaults with the right value.  This is surely
	 * ugly, but it beats making the programmer do it.
	 */
	values[Anum_pg_proc_pronargdefaults - 1] = Int16GetDatum(array_count);
	Nulls[Anum_pg_proc_pronargdefaults - 1] = false;
}

/* ----------------
 *		InsertOneNull
 * ----------------
 */
void
InsertOneNull(int i)
{
	elog(DEBUG4, "inserting column %d NULL", i);
	Assert(i >= 0 && i < MAXATTR);
	if (TupleDescAttr(boot_reldesc->rd_att, i)->attnotnull)
		elog(ERROR,
			 "NULL value specified for not-null column \"%s\" of relation \"%s\"",
			 NameStr(TupleDescAttr(boot_reldesc->rd_att, i)->attname),
			 RelationGetRelationName(boot_reldesc));
	values[i] = PointerGetDatum(NULL);
	Nulls[i] = true;
}

/* ----------------
 *		cleanup
 * ----------------
 */
static void
cleanup(void)
{
	if (boot_reldesc != NULL)
		closerel(NULL);
}

/* ----------------
 *		populate_typ_list
 *
 * Load the Typ list by reading pg_type.
 * ----------------
 */
static void
populate_typ_list(void)
{
	Relation	rel;
	TableScanDesc scan;
	HeapTuple	tup;
	MemoryContext old;

	Assert(Typ == NIL);

	rel = table_open(TypeRelationId, NoLock);
	scan = table_beginscan_catalog(rel, 0, NULL);
	old = MemoryContextSwitchTo(TopMemoryContext);
	while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
	{
		Form_pg_type typForm = (Form_pg_type) GETSTRUCT(tup);
		struct typmap *newtyp;

		newtyp = palloc_object(struct typmap);
		Typ = lappend(Typ, newtyp);

		newtyp->am_oid = typForm->oid;
		memcpy(&newtyp->am_typ, typForm, sizeof(newtyp->am_typ));
	}
	MemoryContextSwitchTo(old);
	table_endscan(scan);
	table_close(rel, NoLock);
}

/* ----------------
 *		gettype
 *
 * NB: this is really ugly; it will return an integer index into TypInfo[],
 * and not an OID at all, until the first reference to a type not known in
 * TypInfo[].  At that point it will read and cache pg_type in Typ,
 * and subsequently return a real OID (and set the global pointer Ap to
 * point at the found row in Typ).  So caller must check whether Typ is
 * still NIL to determine what the return value is!
 * ----------------
 */
static Oid
gettype(char *type)
{
	if (Typ != NIL)
	{
		ListCell   *lc;

		foreach(lc, Typ)
		{
			struct typmap *app = lfirst(lc);

			if (strncmp(NameStr(app->am_typ.typname), type, NAMEDATALEN) == 0)
			{
				Ap = app;
				return app->am_oid;
			}
		}

		/*
		 * The type wasn't known; reload the pg_type contents and check again
		 * to handle composite types, added since last populating the list.
		 */

		list_free_deep(Typ);
		Typ = NIL;
		populate_typ_list();

		/*
		 * Calling gettype would result in infinite recursion for types
		 * missing in pg_type, so just repeat the lookup.
		 */
		foreach(lc, Typ)
		{
			struct typmap *app = lfirst(lc);

			if (strncmp(NameStr(app->am_typ.typname), type, NAMEDATALEN) == 0)
			{
				Ap = app;
				return app->am_oid;
			}
		}
	}
	else
	{
		int			i;

		for (i = 0; i < n_types; i++)
		{
			if (strncmp(type, TypInfo[i].name, NAMEDATALEN) == 0)
				return i;
		}
		/* Not in TypInfo, so we'd better be able to read pg_type now */
		elog(DEBUG4, "external type: %s", type);
		populate_typ_list();
		return gettype(type);
	}
	elog(ERROR, "unrecognized type \"%s\"", type);
	/* not reached, here to make compiler happy */
	return 0;
}

/* ----------------
 *		boot_get_type_io_data
 *
 * Obtain type I/O information at bootstrap time.  This intentionally has
 * an API very close to that of lsyscache.c's get_type_io_data, except that
 * we only support obtaining the typinput and typoutput routines, not
 * the binary I/O routines, and we also return the type's collation.
 * This is exported so that array_in and array_out can be made to work
 * during early bootstrap.
 * ----------------
 */
void
boot_get_type_io_data(Oid typid,
					  int16 *typlen,
					  bool *typbyval,
					  char *typalign,
					  char *typdelim,
					  Oid *typioparam,
					  Oid *typinput,
					  Oid *typoutput,
					  Oid *typcollation)
{
	if (Typ != NIL)
	{
		/* We have the boot-time contents of pg_type, so use it */
		struct typmap *ap = NULL;
		ListCell   *lc;

		foreach(lc, Typ)
		{
			ap = lfirst(lc);
			if (ap->am_oid == typid)
				break;
		}

		if (!ap || ap->am_oid != typid)
			elog(ERROR, "type OID %u not found in Typ list", typid);

		*typlen = ap->am_typ.typlen;
		*typbyval = ap->am_typ.typbyval;
		*typalign = ap->am_typ.typalign;
		*typdelim = ap->am_typ.typdelim;

		/* XXX this logic must match getTypeIOParam() */
		if (OidIsValid(ap->am_typ.typelem))
			*typioparam = ap->am_typ.typelem;
		else
			*typioparam = typid;

		*typinput = ap->am_typ.typinput;
		*typoutput = ap->am_typ.typoutput;

		*typcollation = ap->am_typ.typcollation;
	}
	else
	{
		/* We don't have pg_type yet, so use the hard-wired TypInfo array */
		int			typeindex;

		for (typeindex = 0; typeindex < n_types; typeindex++)
		{
			if (TypInfo[typeindex].oid == typid)
				break;
		}
		if (typeindex >= n_types)
			elog(ERROR, "type OID %u not found in TypInfo", typid);

		*typlen = TypInfo[typeindex].len;
		*typbyval = TypInfo[typeindex].byval;
		*typalign = TypInfo[typeindex].align;
		/* We assume typdelim is ',' for all boot-time types */
		*typdelim = ',';

		/* XXX this logic must match getTypeIOParam() */
		if (OidIsValid(TypInfo[typeindex].elem))
			*typioparam = TypInfo[typeindex].elem;
		else
			*typioparam = typid;

		*typinput = TypInfo[typeindex].inproc;
		*typoutput = TypInfo[typeindex].outproc;

		*typcollation = TypInfo[typeindex].collation;
	}
}

/* ----------------
 *		boot_get_role_oid
 *
 * Look up a role name at bootstrap time.  This is equivalent to
 * get_role_oid(rolname, true): return the role OID or InvalidOid if
 * not found.  We only need to cope with built-in role names.
 * ----------------
 */
Oid
boot_get_role_oid(const char *rolname)
{
	for (int i = 0; i < lengthof(RolInfo); i++)
	{
		if (strcmp(RolInfo[i].rolname, rolname) == 0)
			return RolInfo[i].oid;
	}
	return InvalidOid;
}

/* ----------------
 *		AllocateAttribute
 *
 * Note: bootstrap never sets any per-column ACLs, so we only need
 * ATTRIBUTE_FIXED_PART_SIZE space per attribute.
 * ----------------
 */
static Form_pg_attribute
AllocateAttribute(void)
{
	return (Form_pg_attribute)
		MemoryContextAllocZero(TopMemoryContext, ATTRIBUTE_FIXED_PART_SIZE);
}

/*
 *	index_register() -- record an index that has been set up for building
 *						later.
 *
 *		At bootstrap time, we define a bunch of indexes on system catalogs.
 *		We postpone actually building the indexes until just before we're
 *		finished with initialization, however.  This is because the indexes
 *		themselves have catalog entries, and those have to be included in the
 *		indexes on those catalogs.  Doing it in two phases is the simplest
 *		way of making sure the indexes have the right contents at the end.
 */
void
index_register(Oid heap,
			   Oid ind,
			   const IndexInfo *indexInfo)
{
	IndexList  *newind;
	MemoryContext oldcxt;

	/*
	 * XXX mao 10/31/92 -- don't gc index reldescs, associated info at
	 * bootstrap time.  we'll declare the indexes now, but want to create them
	 * later.
	 */

	if (nogc == NULL)
		nogc = AllocSetContextCreate(NULL,
									 "BootstrapNoGC",
									 ALLOCSET_DEFAULT_SIZES);

	oldcxt = MemoryContextSwitchTo(nogc);

	newind = palloc_object(IndexList);
	newind->il_heap = heap;
	newind->il_ind = ind;
	newind->il_info = palloc_object(IndexInfo);

	memcpy(newind->il_info, indexInfo, sizeof(IndexInfo));
	/* expressions will likely be null, but may as well copy it */
	newind->il_info->ii_Expressions =
		copyObject(indexInfo->ii_Expressions);
	newind->il_info->ii_ExpressionsExpand =
		copyObject(indexInfo->ii_ExpressionsExpand);
	newind->il_info->ii_ExpressionsState = NIL;
	newind->il_info->ii_ExpressionsExpandState = NIL;
	/* predicate will likely be null, but may as well copy it */
	newind->il_info->ii_Predicate =
		copyObject(indexInfo->ii_Predicate);
	newind->il_info->ii_PredicateExpand =
		copyObject(indexInfo->ii_PredicateExpand);
	newind->il_info->ii_PredicateState = NULL;
	newind->il_info->ii_PredicateExpandState = NULL;
	/* no exclusion constraints at bootstrap time, so no need to copy */
	Assert(indexInfo->ii_ExclusionOps == NULL);
	Assert(indexInfo->ii_ExclusionProcs == NULL);
	Assert(indexInfo->ii_ExclusionStrats == NULL);

	newind->il_next = ILHead;
	ILHead = newind;

	MemoryContextSwitchTo(oldcxt);
}


/*
 * build_indices -- fill in all the indexes registered earlier
 */
void
build_indices(void)
{
	for (; ILHead != NULL; ILHead = ILHead->il_next)
	{
		Relation	heap;
		Relation	ind;

		/* need not bother with locks during bootstrap */
		heap = table_open(ILHead->il_heap, NoLock);
		ind = index_open(ILHead->il_ind, NoLock);

		index_build(heap, ind, ILHead->il_info, false, false, false);

		index_close(ind, NoLock);
		table_close(heap, NoLock);
	}
}
./createplan.c0000664000175000017500000066653115221730313012205 0ustar  xmanxman/*-------------------------------------------------------------------------
 *
 * createplan.c
 *	  Routines to create the desired plan for processing a query.
 *	  Planning is complete, we just need to convert the selected
 *	  Path into a Plan.
 *
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/optimizer/plan/createplan.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/sysattr.h"
#include "access/transam.h"
#include "catalog/pg_class.h"
#include "foreign/fdwapi.h"
#include "miscadmin.h"
#include "nodes/extensible.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/optimizer.h"
#include "optimizer/paramassign.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/placeholder.h"
#include "optimizer/plancat.h"
#include "optimizer/planmain.h"
#include "optimizer/prep.h"
#include "optimizer/restrictinfo.h"
#include "optimizer/subselect.h"
#include "optimizer/tlist.h"
#include "parser/parse_clause.h"
#include "parser/parsetree.h"
#include "partitioning/partprune.h"
#include "tcop/tcopprot.h"
#include "utils/lsyscache.h"


/*
 * Flag bits that can appear in the flags argument of create_plan_recurse().
 * These can be OR-ed together.
 *
 * CP_EXACT_TLIST specifies that the generated plan node must return exactly
 * the tlist specified by the path's pathtarget (this overrides both
 * CP_SMALL_TLIST and CP_LABEL_TLIST, if those are set).  Otherwise, the
 * plan node is allowed to return just the Vars and PlaceHolderVars needed
 * to evaluate the pathtarget.
 *
 * CP_SMALL_TLIST specifies that a narrower tlist is preferred.  This is
 * passed down by parent nodes such as Sort and Hash, which will have to
 * store the returned tuples.
 *
 * CP_LABEL_TLIST specifies that the plan node must return columns matching
 * any sortgrouprefs specified in its pathtarget, with appropriate
 * ressortgroupref labels.  This is passed down by parent nodes such as Sort
 * and Group, which need these values to be available in their inputs.
 *
 * CP_IGNORE_TLIST specifies that the caller plans to replace the targetlist,
 * and therefore it doesn't matter a bit what target list gets generated.
 */
#define CP_EXACT_TLIST		0x0001	/* Plan must return specified tlist */
#define CP_SMALL_TLIST		0x0002	/* Prefer narrower tlists */
#define CP_LABEL_TLIST		0x0004	/* tlist must contain sortgrouprefs */
#define CP_IGNORE_TLIST		0x0008	/* caller will replace tlist */


static Plan *create_plan_recurse(PlannerInfo *root, Path *best_path,
								 int flags);
static Plan *create_scan_plan(PlannerInfo *root, Path *best_path,
							  int flags);
static List *build_path_tlist(PlannerInfo *root, Path *path);
static bool use_physical_tlist(PlannerInfo *root, Path *path, int flags);
static List *get_gating_quals(PlannerInfo *root, List *quals);
static Plan *create_gating_plan(PlannerInfo *root, Path *path, Plan *plan,
								List *gating_quals);
static Plan *create_join_plan(PlannerInfo *root, JoinPath *best_path);
static bool mark_async_capable_plan(Plan *plan, Path *path);
static Plan *create_append_plan(PlannerInfo *root, AppendPath *best_path,
								int flags);
static Plan *create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
									  int flags);
static Result *create_group_result_plan(PlannerInfo *root,
										GroupResultPath *best_path);
static ProjectSet *create_project_set_plan(PlannerInfo *root, ProjectSetPath *best_path);
static Material *create_material_plan(PlannerInfo *root, MaterialPath *best_path,
									  int flags);
static Memoize *create_memoize_plan(PlannerInfo *root, MemoizePath *best_path,
									int flags);
static Gather *create_gather_plan(PlannerInfo *root, GatherPath *best_path);
static Plan *create_projection_plan(PlannerInfo *root,
									ProjectionPath *best_path,
									int flags);
static Plan *inject_projection_plan(Plan *subplan, List *tlist,
									bool parallel_safe);
static Sort *create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags);
static IncrementalSort *create_incrementalsort_plan(PlannerInfo *root,
													IncrementalSortPath *best_path, int flags);
static Group *create_group_plan(PlannerInfo *root, GroupPath *best_path);
static Unique *create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags);
static Agg *create_agg_plan(PlannerInfo *root, AggPath *best_path);
static Plan *create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path);
static Result *create_minmaxagg_plan(PlannerInfo *root, MinMaxAggPath *best_path);
static WindowAgg *create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path);
static SetOp *create_setop_plan(PlannerInfo *root, SetOpPath *best_path,
								int flags);
static RecursiveUnion *create_recursiveunion_plan(PlannerInfo *root, RecursiveUnionPath *best_path);
static LockRows *create_lockrows_plan(PlannerInfo *root, LockRowsPath *best_path,
									  int flags);
static ModifyTable *create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path);
static Limit *create_limit_plan(PlannerInfo *root, LimitPath *best_path,
								int flags);
static SeqScan *create_seqscan_plan(PlannerInfo *root, Path *best_path,
									List *tlist, List *scan_clauses);
static SampleScan *create_samplescan_plan(PlannerInfo *root, Path *best_path,
										  List *tlist, List *scan_clauses);
static Scan *create_indexscan_plan(PlannerInfo *root, IndexPath *best_path,
								   List *tlist, List *scan_clauses, bool indexonly);
static BitmapHeapScan *create_bitmap_scan_plan(PlannerInfo *root,
											   BitmapHeapPath *best_path,
											   List *tlist, List *scan_clauses);
static Plan *create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
								   List **qual, List **indexqual, List **indexECs);
static void bitmap_subplan_mark_shared(Plan *plan);
static TidScan *create_tidscan_plan(PlannerInfo *root, TidPath *best_path,
									List *tlist, List *scan_clauses);
static TidRangeScan *create_tidrangescan_plan(PlannerInfo *root,
											  TidRangePath *best_path,
											  List *tlist,
											  List *scan_clauses);
static SubqueryScan *create_subqueryscan_plan(PlannerInfo *root,
											  SubqueryScanPath *best_path,
											  List *tlist, List *scan_clauses);
static FunctionScan *create_functionscan_plan(PlannerInfo *root, Path *best_path,
											  List *tlist, List *scan_clauses);
static ValuesScan *create_valuesscan_plan(PlannerInfo *root, Path *best_path,
										  List *tlist, List *scan_clauses);
static TableFuncScan *create_tablefuncscan_plan(PlannerInfo *root, Path *best_path,
												List *tlist, List *scan_clauses);
static CteScan *create_ctescan_plan(PlannerInfo *root, Path *best_path,
									List *tlist, List *scan_clauses);
static NamedTuplestoreScan *create_namedtuplestorescan_plan(PlannerInfo *root,
															Path *best_path, List *tlist, List *scan_clauses);
static Result *create_resultscan_plan(PlannerInfo *root, Path *best_path,
									  List *tlist, List *scan_clauses);
static WorkTableScan *create_worktablescan_plan(PlannerInfo *root, Path *best_path,
												List *tlist, List *scan_clauses);
static ForeignScan *create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
											List *tlist, List *scan_clauses);
static CustomScan *create_customscan_plan(PlannerInfo *root,
										  CustomPath *best_path,
										  List *tlist, List *scan_clauses);
static NestLoop *create_nestloop_plan(PlannerInfo *root, NestPath *best_path);
static MergeJoin *create_mergejoin_plan(PlannerInfo *root, MergePath *best_path);
static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path);
static Node *replace_nestloop_params(PlannerInfo *root, Node *expr);
static Node *replace_nestloop_params_mutator(Node *node, PlannerInfo *root);
static void fix_indexqual_references(PlannerInfo *root, IndexPath *index_path,
									 List **stripped_indexquals_p,
									 List **fixed_indexquals_p);
static List *fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path);
static Node *fix_indexqual_clause(PlannerInfo *root,
								  IndexOptInfo *index, int indexcol,
								  Node *clause, List *indexcolnos);
static Node *fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol);
static List *get_switched_clauses(List *clauses, Relids outerrelids);
static List *order_qual_clauses(PlannerInfo *root, List *clauses);
static void copy_generic_path_info(Plan *dest, Path *src);
static void copy_plan_costsize(Plan *dest, Plan *src);
static void label_sort_with_costsize(PlannerInfo *root, Sort *plan,
									 double limit_tuples);
static void label_incrementalsort_with_costsize(PlannerInfo *root, IncrementalSort *plan,
												List *pathkeys, double limit_tuples);
static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);
static SampleScan *make_samplescan(List *qptlist, List *qpqual, Index scanrelid,
								   TableSampleClause *tsc);
static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
								 Oid indexid, List *indexqual, List *indexqualorig,
								 List *indexorderby, List *indexorderbyorig,
								 List *indexorderbyops,
								 ScanDirection indexscandir);
static IndexOnlyScan *make_indexonlyscan(List *qptlist, List *qpqual,
										 Index scanrelid, Oid indexid,
										 List *indexqual, List *recheckqual,
										 List *indexorderby,
										 List *indextlist,
										 ScanDirection indexscandir);
static BitmapIndexScan *make_bitmap_indexscan(Index scanrelid, Oid indexid,
											  List *indexqual,
											  List *indexqualorig);
static BitmapHeapScan *make_bitmap_heapscan(List *qptlist,
											List *qpqual,
											Plan *lefttree,
											List *bitmapqualorig,
											Index scanrelid);
static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid,
							 List *tidquals);
static TidRangeScan *make_tidrangescan(List *qptlist, List *qpqual,
									   Index scanrelid, List *tidrangequals);
static SubqueryScan *make_subqueryscan(List *qptlist,
									   List *qpqual,
									   Index scanrelid,
									   Plan *subplan);
static FunctionScan *make_functionscan(List *qptlist, List *qpqual,
									   Index scanrelid, List *functions, bool funcordinality);
static ValuesScan *make_valuesscan(List *qptlist, List *qpqual,
								   Index scanrelid, List *values_lists);
static TableFuncScan *make_tablefuncscan(List *qptlist, List *qpqual,
										 Index scanrelid, TableFunc *tablefunc);
static CteScan *make_ctescan(List *qptlist, List *qpqual,
							 Index scanrelid, int ctePlanId, int cteParam);
static NamedTuplestoreScan *make_namedtuplestorescan(List *qptlist, List *qpqual,
													 Index scanrelid, char *enrname);
static WorkTableScan *make_worktablescan(List *qptlist, List *qpqual,
										 Index scanrelid, int wtParam);
static RecursiveUnion *make_recursive_union(List *tlist,
											Plan *lefttree,
											Plan *righttree,
											int wtParam,
											List *distinctList,
											Cardinality numGroups);
static BitmapAnd *make_bitmap_and(List *bitmapplans);
static BitmapOr *make_bitmap_or(List *bitmapplans);
static NestLoop *make_nestloop(List *tlist,
							   List *joinclauses, List *otherclauses, List *nestParams,
							   Plan *lefttree, Plan *righttree,
							   JoinType jointype, bool inner_unique);
static HashJoin *make_hashjoin(List *tlist,
							   List *joinclauses, List *otherclauses,
							   List *hashclauses,
							   List *hashoperators, List *hashcollations,
							   List *hashkeys,
							   Plan *lefttree, Plan *righttree,
							   JoinType jointype, bool inner_unique);
static Hash *make_hash(Plan *lefttree,
					   List *hashkeys,
					   Oid skewTable,
					   AttrNumber skewColumn,
					   bool skewInherit);
static MergeJoin *make_mergejoin(List *tlist,
								 List *joinclauses, List *otherclauses,
								 List *mergeclauses,
								 Oid *mergefamilies,
								 Oid *mergecollations,
								 bool *mergereversals,
								 bool *mergenullsfirst,
								 Plan *lefttree, Plan *righttree,
								 JoinType jointype, bool inner_unique,
								 bool skip_mark_restore);
static Sort *make_sort(Plan *lefttree, int numCols,
					   AttrNumber *sortColIdx, Oid *sortOperators,
					   Oid *collations, bool *nullsFirst);
static IncrementalSort *make_incrementalsort(Plan *lefttree,
											 int numCols, int nPresortedCols,
											 AttrNumber *sortColIdx, Oid *sortOperators,
											 Oid *collations, bool *nullsFirst);
static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
										Relids relids,
										const AttrNumber *reqColIdx,
										bool adjust_tlist_in_place,
										int *p_numsortkeys,
										AttrNumber **p_sortColIdx,
										Oid **p_sortOperators,
										Oid **p_collations,
										bool **p_nullsFirst);
static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
									 Relids relids);
static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
														   List *pathkeys, Relids relids, int nPresortedCols);
static Sort *make_sort_from_groupcols(List *groupcls,
									  AttrNumber *grpColIdx,
									  Plan *lefttree);
static Material *make_material(Plan *lefttree);
static Memoize *make_memoize(Plan *lefttree, Oid *hashoperators,
							 Oid *collations, List *param_exprs,
							 bool singlerow, bool binary_mode,
							 uint32 est_entries, Bitmapset *keyparamids,
							 Cardinality est_calls,
							 Cardinality est_unique_keys,
							 double est_hit_ratio);
static WindowAgg *make_windowagg(List *tlist, WindowClause *wc,
								 int partNumCols, AttrNumber *partColIdx, Oid *partOperators, Oid *partCollations,
								 int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators, Oid *ordCollations,
								 List *runCondition, List *qual, bool topWindow,
								 Plan *lefttree);
static Group *make_group(List *tlist, List *qual, int numGroupCols,
						 AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
						 Plan *lefttree);
static Unique *make_unique_from_pathkeys(Plan *lefttree,
										 List *pathkeys, int numCols,
										 Relids relids);
static Gather *make_gather(List *qptlist, List *qpqual,
						   int nworkers, int rescan_param, bool single_copy, Plan *subplan);
static SetOp *make_setop(SetOpCmd cmd, SetOpStrategy strategy,
						 List *tlist, Plan *lefttree, Plan *righttree,
						 List *groupList, Cardinality numGroups);
static LockRows *make_lockrows(Plan *lefttree, List *rowMarks, int epqParam);
static Result *make_gating_result(List *tlist, Node *resconstantqual,
								  Plan *subplan);
static Result *make_one_row_result(List *tlist, Node *resconstantqual,
								   RelOptInfo *rel);
static ProjectSet *make_project_set(List *tlist, Plan *subplan);
static ModifyTable *make_modifytable(PlannerInfo *root, Plan *subplan,
									 CmdType operation, bool canSetTag,
									 Index nominalRelation, Index rootRelation,
									 List *resultRelations,
									 List *updateColnosLists,
									 List *withCheckOptionLists, List *returningLists,
									 List *rowMarks, OnConflictExpr *onconflict,
									 List *mergeActionLists, List *mergeJoinConditions,
									 ForPortionOfExpr *forPortionOf, int epqParam);
static GatherMerge *create_gather_merge_plan(PlannerInfo *root,
											 GatherMergePath *best_path);


/*
 * create_plan
 *	  Creates the access plan for a query by recursively processing the
 *	  desired tree of pathnodes, starting at the node 'best_path'.  For
 *	  every pathnode found, we create a corresponding plan node containing
 *	  appropriate id, target list, and qualification information.
 *
 *	  The tlists and quals in the plan tree are still in planner format,
 *	  ie, Vars still correspond to the parser's numbering.  This will be
 *	  fixed later by setrefs.c.
 *
 *	  best_path is the best access path
 *
 *	  Returns a Plan tree.
 */
Plan *
create_plan(PlannerInfo *root, Path *best_path)
{
	Plan	   *plan;

	/* plan_params should not be in use in current query level */
	Assert(root->plan_params == NIL);

	/* Initialize this module's workspace in PlannerInfo */
	root->curOuterRels = NULL;
	root->curOuterParams = NIL;

	/* Recursively process the path tree, demanding the correct tlist result */
	plan = create_plan_recurse(root, best_path, CP_EXACT_TLIST);

	/*
	 * Make sure the topmost plan node's targetlist exposes the original
	 * column names and other decorative info.  Targetlists generated within
	 * the planner don't bother with that stuff, but we must have it on the
	 * top-level tlist seen at execution time.  However, ModifyTable plan
	 * nodes don't have a tlist matching the querytree targetlist.
	 */
	if (!IsA(plan, ModifyTable))
		apply_tlist_labeling(plan->targetlist, root->processed_tlist);

	/*
	 * Attach any initPlans created in this query level to the topmost plan
	 * node.  (In principle the initplans could go in any plan node at or
	 * above where they're referenced, but there seems no reason to put them
	 * any lower than the topmost node for the query level.  Also, see
	 * comments for SS_finalize_plan before you try to change this.)
	 */
	SS_attach_initplans(root, plan);

	/* Check we successfully assigned all NestLoopParams to plan nodes */
	if (root->curOuterParams != NIL)
		elog(ERROR, "failed to assign all NestLoopParams to plan nodes");

	/*
	 * Reset plan_params to ensure param IDs used for nestloop params are not
	 * re-used later
	 */
	root->plan_params = NIL;

	return plan;
}

/*
 * create_plan_recurse
 *	  Recursive guts of create_plan().
 */
static Plan *
create_plan_recurse(PlannerInfo *root, Path *best_path, int flags)
{
	Plan	   *plan;

	/* Guard against stack overflow due to overly complex plans */
	check_stack_depth();

	switch (best_path->pathtype)
	{
		case T_SeqScan:
		case T_SampleScan:
		case T_IndexScan:
		case T_IndexOnlyScan:
		case T_BitmapHeapScan:
		case T_TidScan:
		case T_TidRangeScan:
		case T_SubqueryScan:
		case T_FunctionScan:
		case T_TableFuncScan:
		case T_ValuesScan:
		case T_CteScan:
		case T_WorkTableScan:
		case T_NamedTuplestoreScan:
		case T_ForeignScan:
		case T_CustomScan:
			plan = create_scan_plan(root, best_path, flags);
			break;
		case T_HashJoin:
		case T_MergeJoin:
		case T_NestLoop:
			plan = create_join_plan(root,
									(JoinPath *) best_path);
			break;
		case T_Append:
			plan = create_append_plan(root,
									  (AppendPath *) best_path,
									  flags);
			break;
		case T_MergeAppend:
			plan = create_merge_append_plan(root,
											(MergeAppendPath *) best_path,
											flags);
			break;
		case T_Result:
			if (IsA(best_path, ProjectionPath))
			{
				plan = create_projection_plan(root,
											  (ProjectionPath *) best_path,
											  flags);
			}
			else if (IsA(best_path, MinMaxAggPath))
			{
				plan = (Plan *) create_minmaxagg_plan(root,
													  (MinMaxAggPath *) best_path);
			}
			else if (IsA(best_path, GroupResultPath))
			{
				plan = (Plan *) create_group_result_plan(root,
														 (GroupResultPath *) best_path);
			}
			else
			{
				/* Simple RTE_RESULT base relation */
				Assert(IsA(best_path, Path));
				plan = create_scan_plan(root, best_path, flags);
			}
			break;
		case T_ProjectSet:
			plan = (Plan *) create_project_set_plan(root,
													(ProjectSetPath *) best_path);
			break;
		case T_Material:
			plan = (Plan *) create_material_plan(root,
												 (MaterialPath *) best_path,
												 flags);
			break;
		case T_Memoize:
			plan = (Plan *) create_memoize_plan(root,
												(MemoizePath *) best_path,
												flags);
			break;
		case T_Unique:
			plan = (Plan *) create_unique_plan(root,
											   (UniquePath *) best_path,
											   flags);
			break;
		case T_Gather:
			plan = (Plan *) create_gather_plan(root,
											   (GatherPath *) best_path);
			break;
		case T_Sort:
			plan = (Plan *) create_sort_plan(root,
											 (SortPath *) best_path,
											 flags);
			break;
		case T_IncrementalSort:
			plan = (Plan *) create_incrementalsort_plan(root,
														(IncrementalSortPath *) best_path,
														flags);
			break;
		case T_Group:
			plan = (Plan *) create_group_plan(root,
											  (GroupPath *) best_path);
			break;
		case T_Agg:
			if (IsA(best_path, GroupingSetsPath))
				plan = create_groupingsets_plan(root,
												(GroupingSetsPath *) best_path);
			else
			{
				Assert(IsA(best_path, AggPath));
				plan = (Plan *) create_agg_plan(root,
												(AggPath *) best_path);
			}
			break;
		case T_WindowAgg:
			plan = (Plan *) create_windowagg_plan(root,
												  (WindowAggPath *) best_path);
			break;
		case T_SetOp:
			plan = (Plan *) create_setop_plan(root,
											  (SetOpPath *) best_path,
											  flags);
			break;
		case T_RecursiveUnion:
			plan = (Plan *) create_recursiveunion_plan(root,
													   (RecursiveUnionPath *) best_path);
			break;
		case T_LockRows:
			plan = (Plan *) create_lockrows_plan(root,
												 (LockRowsPath *) best_path,
												 flags);
			break;
		case T_ModifyTable:
			plan = (Plan *) create_modifytable_plan(root,
													(ModifyTablePath *) best_path);
			break;
		case T_Limit:
			plan = (Plan *) create_limit_plan(root,
											  (LimitPath *) best_path,
											  flags);
			break;
		case T_GatherMerge:
			plan = (Plan *) create_gather_merge_plan(root,
													 (GatherMergePath *) best_path);
			break;
		default:
			elog(ERROR, "unrecognized node type: %d",
				 (int) best_path->pathtype);
			plan = NULL;		/* keep compiler quiet */
			break;
	}

	return plan;
}

/*
 * create_scan_plan
 *	 Create a scan plan for the parent relation of 'best_path'.
 */
static Plan *
create_scan_plan(PlannerInfo *root, Path *best_path, int flags)
{
	RelOptInfo *rel = best_path->parent;
	List	   *scan_clauses;
	List	   *gating_clauses;
	List	   *tlist;
	Plan	   *plan;

	/*
	 * Extract the relevant restriction clauses from the parent relation. The
	 * executor must apply all these restrictions during the scan, except for
	 * pseudoconstants which we'll take care of below.
	 *
	 * If this is a plain indexscan or index-only scan, we need not consider
	 * restriction clauses that are implied by the index's predicate, so use
	 * indrestrictinfo not baserestrictinfo.  Note that we can't do that for
	 * bitmap indexscans, since there's not necessarily a single index
	 * involved; but it doesn't matter since create_bitmap_scan_plan() will be
	 * able to get rid of such clauses anyway via predicate proof.
	 */
	switch (best_path->pathtype)
	{
		case T_IndexScan:
		case T_IndexOnlyScan:
			scan_clauses = castNode(IndexPath, best_path)->indexinfo->indrestrictinfo;
			break;
		default:
			scan_clauses = rel->baserestrictinfo;
			break;
	}

	/*
	 * If this is a parameterized scan, we also need to enforce all the join
	 * clauses available from the outer relation(s).
	 *
	 * For paranoia's sake, don't modify the stored baserestrictinfo list.
	 */
	if (best_path->param_info)
		scan_clauses = list_concat_copy(scan_clauses,
										best_path->param_info->ppi_clauses);

	/*
	 * Detect whether we have any pseudoconstant quals to deal with.  Then, if
	 * we'll need a gating Result node, it will be able to project, so there
	 * are no requirements on the child's tlist.
	 *
	 * If this replaces a join, it must be a foreign scan or a custom scan,
	 * and the FDW or the custom scan provider would have stored in the best
	 * path the list of RestrictInfo nodes to apply to the join; check against
	 * that list in that case.
	 */
	if (IS_JOIN_REL(rel))
	{
		List	   *join_clauses;

		Assert(best_path->pathtype == T_ForeignScan ||
			   best_path->pathtype == T_CustomScan);
		if (best_path->pathtype == T_ForeignScan)
			join_clauses = ((ForeignPath *) best_path)->fdw_restrictinfo;
		else
			join_clauses = ((CustomPath *) best_path)->custom_restrictinfo;

		gating_clauses = get_gating_quals(root, join_clauses);
	}
	else
		gating_clauses = get_gating_quals(root, scan_clauses);
	if (gating_clauses)
		flags = 0;

	/*
	 * For table scans, rather than using the relation targetlist (which is
	 * only those Vars actually needed by the query), we prefer to generate a
	 * tlist containing all Vars in order.  This will allow the executor to
	 * optimize away projection of the table tuples, if possible.
	 *
	 * But if the caller is going to ignore our tlist anyway, then don't
	 * bother generating one at all.  We use an exact equality test here, so
	 * that this only applies when CP_IGNORE_TLIST is the only flag set.
	 */
	if (flags == CP_IGNORE_TLIST)
	{
		tlist = NULL;
	}
	else if (use_physical_tlist(root, best_path, flags))
	{
		if (best_path->pathtype == T_IndexOnlyScan)
		{
			/* For index-only scan, the preferred tlist is the index's */
			tlist = copyObject(((IndexPath *) best_path)->indexinfo->indextlist);

			/*
			 * Transfer sortgroupref data to the replacement tlist, if
			 * requested (use_physical_tlist checked that this will work).
			 */
			if (flags & CP_LABEL_TLIST)
				apply_pathtarget_labeling_to_tlist(tlist, best_path->pathtarget);
		}
		else
		{
			tlist = build_physical_tlist(root, rel);
			if (tlist == NIL)
			{
				/* Failed because of dropped cols, so use regular method */
				tlist = build_path_tlist(root, best_path);
			}
			else
			{
				/* As above, transfer sortgroupref data to replacement tlist */
				if (flags & CP_LABEL_TLIST)
					apply_pathtarget_labeling_to_tlist(tlist, best_path->pathtarget);
			}
		}
	}
	else
	{
		tlist = build_path_tlist(root, best_path);
	}

	switch (best_path->pathtype)
	{
		case T_SeqScan:
			plan = (Plan *) create_seqscan_plan(root,
												best_path,
												tlist,
												scan_clauses);
			break;

		case T_SampleScan:
			plan = (Plan *) create_samplescan_plan(root,
												   best_path,
												   tlist,
												   scan_clauses);
			break;

		case T_IndexScan:
			plan = (Plan *) create_indexscan_plan(root,
												  (IndexPath *) best_path,
												  tlist,
												  scan_clauses,
												  false);
			break;

		case T_IndexOnlyScan:
			plan = (Plan *) create_indexscan_plan(root,
												  (IndexPath *) best_path,
												  tlist,
												  scan_clauses,
												  true);
			break;

		case T_BitmapHeapScan:
			plan = (Plan *) create_bitmap_scan_plan(root,
													(BitmapHeapPath *) best_path,
													tlist,
													scan_clauses);
			break;

		case T_TidScan:
			plan = (Plan *) create_tidscan_plan(root,
												(TidPath *) best_path,
												tlist,
												scan_clauses);
			break;

		case T_TidRangeScan:
			plan = (Plan *) create_tidrangescan_plan(root,
													 (TidRangePath *) best_path,
													 tlist,
													 scan_clauses);
			break;

		case T_SubqueryScan:
			plan = (Plan *) create_subqueryscan_plan(root,
													 (SubqueryScanPath *) best_path,
													 tlist,
													 scan_clauses);
			break;

		case T_FunctionScan:
			plan = (Plan *) create_functionscan_plan(root,
													 best_path,
													 tlist,
													 scan_clauses);
			break;

		case T_TableFuncScan:
			plan = (Plan *) create_tablefuncscan_plan(root,
													  best_path,
													  tlist,
													  scan_clauses);
			break;

		case T_ValuesScan:
			plan = (Plan *) create_valuesscan_plan(root,
												   best_path,
												   tlist,
												   scan_clauses);
			break;

		case T_CteScan:
			plan = (Plan *) create_ctescan_plan(root,
												best_path,
												tlist,
												scan_clauses);
			break;

		case T_NamedTuplestoreScan:
			plan = (Plan *) create_namedtuplestorescan_plan(root,
															best_path,
															tlist,
															scan_clauses);
			break;

		case T_Result:
			plan = (Plan *) create_resultscan_plan(root,
												   best_path,
												   tlist,
												   scan_clauses);
			break;

		case T_WorkTableScan:
			plan = (Plan *) create_worktablescan_plan(root,
													  best_path,
													  tlist,
													  scan_clauses);
			break;

		case T_ForeignScan:
			plan = (Plan *) create_foreignscan_plan(root,
													(ForeignPath *) best_path,
													tlist,
													scan_clauses);
			break;

		case T_CustomScan:
			plan = (Plan *) create_customscan_plan(root,
												   (CustomPath *) best_path,
												   tlist,
												   scan_clauses);
			break;

		default:
			elog(ERROR, "unrecognized node type: %d",
				 (int) best_path->pathtype);
			plan = NULL;		/* keep compiler quiet */
			break;
	}

	/*
	 * If there are any pseudoconstant clauses attached to this node, insert a
	 * gating Result node that evaluates the pseudoconstants as one-time
	 * quals.
	 */
	if (gating_clauses)
		plan = create_gating_plan(root, best_path, plan, gating_clauses);

	return plan;
}

/*
 * Build a target list (ie, a list of TargetEntry) for the Path's output.
 *
 * This is almost just make_tlist_from_pathtarget(), but we also have to
 * deal with replacing nestloop params.
 */
static List *
build_path_tlist(PlannerInfo *root, Path *path)
{
	List	   *tlist = NIL;
	Index	   *sortgrouprefs = path->pathtarget->sortgrouprefs;
	int			resno = 1;
	ListCell   *v;

	foreach(v, path->pathtarget->exprs)
	{
		Node	   *node = (Node *) lfirst(v);
		TargetEntry *tle;

		/*
		 * If it's a parameterized path, there might be lateral references in
		 * the tlist, which need to be replaced with Params.  There's no need
		 * to remake the TargetEntry nodes, so apply this to each list item
		 * separately.
		 */
		if (path->param_info)
			node = replace_nestloop_params(root, node);

		tle = makeTargetEntry((Expr *) node,
							  resno,
							  NULL,
							  false);
		if (sortgrouprefs)
			tle->ressortgroupref = sortgrouprefs[resno - 1];

		tlist = lappend(tlist, tle);
		resno++;
	}
	return tlist;
}

/*
 * use_physical_tlist
 *		Decide whether to use a tlist matching relation structure,
 *		rather than only those Vars actually referenced.
 */
static bool
use_physical_tlist(PlannerInfo *root, Path *path, int flags)
{
	RelOptInfo *rel = path->parent;
	int			i;
	ListCell   *lc;

	/*
	 * Forget it if either exact tlist or small tlist is demanded.
	 */
	if (flags & (CP_EXACT_TLIST | CP_SMALL_TLIST))
		return false;

	/*
	 * We can do this for real relation scans, subquery scans, function scans,
	 * tablefunc scans, values scans, and CTE scans (but not for, eg, joins).
	 */
	if (rel->rtekind != RTE_RELATION &&
		rel->rtekind != RTE_SUBQUERY &&
		rel->rtekind != RTE_FUNCTION &&
		rel->rtekind != RTE_TABLEFUNC &&
		rel->rtekind != RTE_VALUES &&
		rel->rtekind != RTE_CTE)
		return false;

	/*
	 * Can't do it with inheritance cases either (mainly because Append
	 * doesn't project; this test may be unnecessary now that
	 * create_append_plan instructs its children to return an exact tlist).
	 */
	if (rel->reloptkind != RELOPT_BASEREL)
		return false;

	/*
	 * Also, don't do it to a CustomPath; the premise that we're extracting
	 * columns from a simple physical tuple is unlikely to hold for those.
	 * (When it does make sense, the custom path creator can set up the path's
	 * pathtarget that way.)
	 */
	if (IsA(path, CustomPath))
		return false;

	/*
	 * If a bitmap scan's tlist is empty, keep it as-is.  This may allow the
	 * executor to skip heap page fetches, and in any case, the benefit of
	 * using a physical tlist instead would be minimal.
	 */
	if (IsA(path, BitmapHeapPath) &&
		path->pathtarget->exprs == NIL)
		return false;

	/*
	 * Can't do it if any system columns or whole-row Vars are requested.
	 * (This could possibly be fixed but would take some fragile assumptions
	 * in setrefs.c, I think.)
	 */
	for (i = rel->min_attr; i <= 0; i++)
	{
		if (!bms_is_empty(rel->attr_needed[i - rel->min_attr]))
			return false;
	}

	/*
	 * Can't do it if the rel is required to emit any placeholder expressions,
	 * either.
	 */
	foreach(lc, root->placeholder_list)
	{
		PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);

		if (bms_nonempty_difference(phinfo->ph_needed, rel->relids) &&
			bms_is_subset(phinfo->ph_eval_at, rel->relids))
			return false;
	}

	/*
	 * For an index-only scan, the "physical tlist" is the index's indextlist.
	 * We can only return that without a projection if all the index's columns
	 * are returnable.
	 */
	if (path->pathtype == T_IndexOnlyScan)
	{
		IndexOptInfo *indexinfo = ((IndexPath *) path)->indexinfo;

		for (i = 0; i < indexinfo->ncolumns; i++)
		{
			if (!indexinfo->canreturn[i])
				return false;
		}
	}

	/*
	 * Also, can't do it if CP_LABEL_TLIST is specified and path is requested
	 * to emit any sort/group columns that are not simple Vars.  (If they are
	 * simple Vars, they should appear in the physical tlist, and
	 * apply_pathtarget_labeling_to_tlist will take care of getting them
	 * labeled again.)	We also have to check that no two sort/group columns
	 * are the same Var, else that element of the physical tlist would need
	 * conflicting ressortgroupref labels.
	 */
	if ((flags & CP_LABEL_TLIST) && path->pathtarget->sortgrouprefs)
	{
		Bitmapset  *sortgroupatts = NULL;

		i = 0;
		foreach(lc, path->pathtarget->exprs)
		{
			Expr	   *expr = (Expr *) lfirst(lc);

			if (path->pathtarget->sortgrouprefs[i])
			{
				if (expr && IsA(expr, Var))
				{
					int			attno = ((Var *) expr)->varattno;

					attno -= FirstLowInvalidHeapAttributeNumber;
					if (bms_is_member(attno, sortgroupatts))
						return false;
					sortgroupatts = bms_add_member(sortgroupatts, attno);
				}
				else
					return false;
			}
			i++;
		}
	}

	return true;
}

/*
 * get_gating_quals
 *	  See if there are pseudoconstant quals in a node's quals list
 *
 * If the node's quals list includes any pseudoconstant quals,
 * return just those quals.
 */
static List *
get_gating_quals(PlannerInfo *root, List *quals)
{
	/* No need to look if we know there are no pseudoconstants */
	if (!root->hasPseudoConstantQuals)
		return NIL;

	/* Sort into desirable execution order while still in RestrictInfo form */
	quals = order_qual_clauses(root, quals);

	/* Pull out any pseudoconstant quals from the RestrictInfo list */
	return extract_actual_clauses(quals, true);
}

/*
 * create_gating_plan
 *	  Deal with pseudoconstant qual clauses
 *
 * Add a gating Result node atop the already-built plan.
 */
static Plan *
create_gating_plan(PlannerInfo *root, Path *path, Plan *plan,
				   List *gating_quals)
{
	Result	   *gplan;

	Assert(gating_quals);

	/*
	 * Since we need a Result node anyway, always return the path's requested
	 * tlist; that's never a wrong choice, even if the parent node didn't ask
	 * for CP_EXACT_TLIST.
	 */
	gplan = make_gating_result(build_path_tlist(root, path),
							   (Node *) gating_quals, plan);

	/*
	 * We might have had a trivial Result plan already.  Stacking one Result
	 * atop another is silly, so if that applies, just discard the input plan.
	 * (We're assuming its targetlist is uninteresting; it should be either
	 * the same as the result of build_path_tlist, or a simplified version.
	 * However, we preserve the set of relids that it purports to scan and
	 * attribute that to our replacement Result instead, and likewise for the
	 * result_type.)
	 */
	if (IsA(plan, Result))
	{
		Result	   *rplan = (Result *) plan;

		gplan->plan.lefttree = NULL;
		gplan->relids = rplan->relids;
		gplan->result_type = rplan->result_type;
	}

	/*
	 * Notice that we don't change cost or size estimates when doing gating.
	 * The costs of qual eval were already included in the subplan's cost.
	 * Leaving the size alone amounts to assuming that the gating qual will
	 * succeed, which is the conservative estimate for planning upper queries.
	 * We certainly don't want to assume the output size is zero (unless the
	 * gating qual is actually constant FALSE, and that case is dealt with in
	 * clausesel.c).  Interpolating between the two cases is silly, because it
	 * doesn't reflect what will really happen at runtime, and besides which
	 * in most cases we have only a very bad idea of the probability of the
	 * gating qual being true.
	 */
	copy_plan_costsize(&gplan->plan, plan);

	/* Gating quals could be unsafe, so better use the Path's safety flag */
	gplan->plan.parallel_safe = path->parallel_safe;

	return &gplan->plan;
}

/*
 * create_join_plan
 *	  Create a join plan for 'best_path' and (recursively) plans for its
 *	  inner and outer paths.
 */
static Plan *
create_join_plan(PlannerInfo *root, JoinPath *best_path)
{
	Plan	   *plan;
	List	   *gating_clauses;

	switch (best_path->path.pathtype)
	{
		case T_MergeJoin:
			plan = (Plan *) create_mergejoin_plan(root,
												  (MergePath *) best_path);
			break;
		case T_HashJoin:
			plan = (Plan *) create_hashjoin_plan(root,
												 (HashPath *) best_path);
			break;
		case T_NestLoop:
			plan = (Plan *) create_nestloop_plan(root,
												 (NestPath *) best_path);
			break;
		default:
			elog(ERROR, "unrecognized node type: %d",
				 (int) best_path->path.pathtype);
			plan = NULL;		/* keep compiler quiet */
			break;
	}

	/*
	 * If there are any pseudoconstant clauses attached to this node, insert a
	 * gating Result node that evaluates the pseudoconstants as one-time
	 * quals.
	 */
	gating_clauses = get_gating_quals(root, best_path->joinrestrictinfo);
	if (gating_clauses)
		plan = create_gating_plan(root, (Path *) best_path, plan,
								  gating_clauses);

#ifdef NOT_USED

	/*
	 * * Expensive function pullups may have pulled local predicates * into
	 * this path node.  Put them in the qpqual of the plan node. * JMH,
	 * 6/15/92
	 */
	if (get_loc_restrictinfo(best_path) != NIL)
		set_qpqual((Plan) plan,
				   list_concat(get_qpqual((Plan) plan),
							   get_actual_clauses(get_loc_restrictinfo(best_path))));
#endif

	return plan;
}

/*
 * mark_async_capable_plan
 *		Check whether the Plan node created from a Path node is async-capable,
 *		and if so, mark the Plan node as such and return true, otherwise
 *		return false.
 */
static bool
mark_async_capable_plan(Plan *plan, Path *path)
{
	switch (nodeTag(path))
	{
		case T_SubqueryScanPath:
			{
				SubqueryScan *scan_plan = (SubqueryScan *) plan;

				/*
				 * If the generated plan node includes a gating Result node,
				 * we can't execute it asynchronously.
				 */
				if (IsA(plan, Result))
					return false;

				/*
				 * If a SubqueryScan node atop of an async-capable plan node
				 * is deletable, consider it as async-capable.
				 */
				if (trivial_subqueryscan(scan_plan) &&
					mark_async_capable_plan(scan_plan->subplan,
											((SubqueryScanPath *) path)->subpath))
					break;
				return false;
			}
		case T_ForeignPath:
			{
				FdwRoutine *fdwroutine = path->parent->fdwroutine;

				/*
				 * If the generated plan node includes a gating Result node,
				 * we can't execute it asynchronously.
				 */
				if (IsA(plan, Result))
					return false;

				Assert(fdwroutine != NULL);
				if (fdwroutine->IsForeignPathAsyncCapable != NULL &&
					fdwroutine->IsForeignPathAsyncCapable((ForeignPath *) path))
					break;
				return false;
			}
		case T_ProjectionPath:

			/*
			 * If the generated plan node includes a Result node for the
			 * projection, we can't execute it asynchronously.
			 */
			if (IsA(plan, Result))
				return false;

			/*
			 * create_projection_plan() would have pulled up the subplan, so
			 * check the capability using the subpath.
			 */
			if (mark_async_capable_plan(plan,
										((ProjectionPath *) path)->subpath))
				return true;
			return false;
		default:
			return false;
	}

	plan->async_capable = true;

	return true;
}

/*
 * create_append_plan
 *	  Create an Append plan for 'best_path' and (recursively) plans
 *	  for its subpaths.
 *
 *	  Returns a Plan node.
 */
static Plan *
create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
{
	Append	   *plan;
	List	   *tlist = build_path_tlist(root, &best_path->path);
	int			orig_tlist_length = list_length(tlist);
	bool		tlist_was_changed = false;
	List	   *pathkeys = best_path->path.pathkeys;
	List	   *subplans = NIL;
	ListCell   *subpaths;
	int			nasyncplans = 0;
	RelOptInfo *rel = best_path->path.parent;
	int			nodenumsortkeys = 0;
	AttrNumber *nodeSortColIdx = NULL;
	Oid		   *nodeSortOperators = NULL;
	Oid		   *nodeCollations = NULL;
	bool	   *nodeNullsFirst = NULL;
	bool		consider_async = false;

	/*
	 * The subpaths list could be empty, if every child was proven empty by
	 * constraint exclusion.  In that case generate a dummy plan that returns
	 * no rows.
	 *
	 * Note that an AppendPath with no members is also generated in certain
	 * cases where there was no appending construct at all, but we know the
	 * relation is empty (see set_dummy_rel_pathlist and mark_dummy_rel).
	 */
	if (best_path->subpaths == NIL)
	{
		/* Generate a Result plan with constant-FALSE gating qual */
		Plan	   *plan;

		plan = (Plan *) make_one_row_result(tlist,
											(Node *) list_make1(makeBoolConst(false,
																			  false)),
											best_path->path.parent);

		copy_generic_path_info(plan, (Path *) best_path);

		return plan;
	}

	/*
	 * Otherwise build an Append plan.  Note that if there's just one child,
	 * the Append is pretty useless; but we wait till setrefs.c to get rid of
	 * it.  Doing so here doesn't work because the varno of the child scan
	 * plan won't match the parent-rel Vars it'll be asked to emit.
	 *
	 * We don't have the actual creation of the Append node split out into a
	 * separate make_xxx function.  This is because we want to run
	 * prepare_sort_from_pathkeys on it before we do so on the individual
	 * child plans, to make cross-checking the sort info easier.
	 */
	plan = makeNode(Append);
	plan->plan.targetlist = tlist;
	plan->plan.qual = NIL;
	plan->plan.lefttree = NULL;
	plan->plan.righttree = NULL;
	plan->apprelids = rel->relids;
	plan->child_append_relid_sets = best_path->child_append_relid_sets;

	if (pathkeys != NIL)
	{
		/*
		 * Compute sort column info, and adjust the Append's tlist as needed.
		 * Because we pass adjust_tlist_in_place = true, we may ignore the
		 * function result; it must be the same plan node.  However, we then
		 * need to detect whether any tlist entries were added.
		 */
		(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
										  best_path->path.parent->relids,
										  NULL,
										  true,
										  &nodenumsortkeys,
										  &nodeSortColIdx,
										  &nodeSortOperators,
										  &nodeCollations,
										  &nodeNullsFirst);
		tlist_was_changed = (orig_tlist_length != list_length(plan->plan.targetlist));
	}

	/* If appropriate, consider async append */
	consider_async = (enable_async_append && pathkeys == NIL &&
					  !best_path->path.parallel_safe &&
					  list_length(best_path->subpaths) > 1);

	/* Build the plan for each child */
	foreach(subpaths, best_path->subpaths)
	{
		Path	   *subpath = (Path *) lfirst(subpaths);
		Plan	   *subplan;

		/* Must insist that all children return the same tlist */
		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);

		/*
		 * For ordered Appends, we must insert a Sort node if subplan isn't
		 * sufficiently ordered.
		 */
		if (pathkeys != NIL)
		{
			int			numsortkeys;
			AttrNumber *sortColIdx;
			Oid		   *sortOperators;
			Oid		   *collations;
			bool	   *nullsFirst;
			int			presorted_keys;

			/*
			 * Compute sort column info, and adjust subplan's tlist as needed.
			 * We must apply prepare_sort_from_pathkeys even to subplans that
			 * don't need an explicit sort, to make sure they are returning
			 * the same sort key columns the Append expects.
			 */
			subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
												 subpath->parent->relids,
												 nodeSortColIdx,
												 false,
												 &numsortkeys,
												 &sortColIdx,
												 &sortOperators,
												 &collations,
												 &nullsFirst);

			/*
			 * Check that we got the same sort key information.  We just
			 * Assert that the sortops match, since those depend only on the
			 * pathkeys; but it seems like a good idea to check the sort
			 * column numbers explicitly, to ensure the tlists match up.
			 */
			Assert(numsortkeys == nodenumsortkeys);
			if (memcmp(sortColIdx, nodeSortColIdx,
					   numsortkeys * sizeof(AttrNumber)) != 0)
				elog(ERROR, "Append child's targetlist doesn't match Append");
			Assert(memcmp(sortOperators, nodeSortOperators,
						  numsortkeys * sizeof(Oid)) == 0);
			Assert(memcmp(collations, nodeCollations,
						  numsortkeys * sizeof(Oid)) == 0);
			Assert(memcmp(nullsFirst, nodeNullsFirst,
						  numsortkeys * sizeof(bool)) == 0);

			/* Now, insert a Sort node if subplan isn't sufficiently ordered */
			if (!pathkeys_count_contained_in(pathkeys, subpath->pathkeys,
											 &presorted_keys))
			{
				Plan	   *sort_plan;

				/*
				 * We choose to use incremental sort if it is enabled and
				 * there are presorted keys; otherwise we use full sort.
				 */
				if (enable_incremental_sort && presorted_keys > 0)
				{
					sort_plan = (Plan *)
						make_incrementalsort(subplan, numsortkeys, presorted_keys,
											 sortColIdx, sortOperators,
											 collations, nullsFirst);

					label_incrementalsort_with_costsize(root,
														(IncrementalSort *) sort_plan,
														pathkeys,
														best_path->limit_tuples);
				}
				else
				{
					sort_plan = (Plan *) make_sort(subplan, numsortkeys,
												   sortColIdx, sortOperators,
												   collations, nullsFirst);

					label_sort_with_costsize(root, (Sort *) sort_plan,
											 best_path->limit_tuples);
				}

				subplan = sort_plan;
			}
		}

		/* If needed, check to see if subplan can be executed asynchronously */
		if (consider_async && mark_async_capable_plan(subplan, subpath))
		{
			Assert(subplan->async_capable);
			++nasyncplans;
		}

		subplans = lappend(subplans, subplan);
	}

	/* Set below if we find quals that we can use to run-time prune */
	plan->part_prune_index = -1;

	/*
	 * If any quals exist, they may be useful to perform further partition
	 * pruning during execution.  Gather information needed by the executor to
	 * do partition pruning.
	 */
	if (enable_partition_pruning)
	{
		List	   *prunequal;

		prunequal = extract_actual_clauses(rel->baserestrictinfo, false);

		if (best_path->path.param_info)
		{
			List	   *prmquals = best_path->path.param_info->ppi_clauses;

			prmquals = extract_actual_clauses(prmquals, false);
			prmquals = (List *) replace_nestloop_params(root,
														(Node *) prmquals);

			prunequal = list_concat(prunequal, prmquals);
		}

		if (prunequal != NIL)
			plan->part_prune_index = make_partition_pruneinfo(root, rel,
															  best_path->subpaths,
															  prunequal);
	}

	plan->appendplans = subplans;
	plan->nasyncplans = nasyncplans;
	plan->first_partial_plan = best_path->first_partial_path;

	copy_generic_path_info(&plan->plan, (Path *) best_path);

	/*
	 * If prepare_sort_from_pathkeys added sort columns, but we were told to
	 * produce either the exact tlist or a narrow tlist, we should get rid of
	 * the sort columns again.  We must inject a projection node to do so.
	 */
	if (tlist_was_changed && (flags & (CP_EXACT_TLIST | CP_SMALL_TLIST)))
	{
		tlist = list_copy_head(plan->plan.targetlist, orig_tlist_length);
		return inject_projection_plan((Plan *) plan, tlist,
									  plan->plan.parallel_safe);
	}
	else
		return (Plan *) plan;
}

/*
 * create_merge_append_plan
 *	  Create a MergeAppend plan for 'best_path' and (recursively) plans
 *	  for its subpaths.
 *
 *	  Returns a Plan node.
 */
static Plan *
create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
						 int flags)
{
	MergeAppend *node = makeNode(MergeAppend);
	Plan	   *plan = &node->plan;
	List	   *tlist = build_path_tlist(root, &best_path->path);
	int			orig_tlist_length = list_length(tlist);
	bool		tlist_was_changed;
	List	   *pathkeys = best_path->path.pathkeys;
	List	   *subplans = NIL;
	ListCell   *subpaths;
	RelOptInfo *rel = best_path->path.parent;

	/*
	 * We don't have the actual creation of the MergeAppend node split out
	 * into a separate make_xxx function.  This is because we want to run
	 * prepare_sort_from_pathkeys on it before we do so on the individual
	 * child plans, to make cross-checking the sort info easier.
	 */
	copy_generic_path_info(plan, (Path *) best_path);
	plan->targetlist = tlist;
	plan->qual = NIL;
	plan->lefttree = NULL;
	plan->righttree = NULL;
	node->apprelids = rel->relids;
	node->child_append_relid_sets = best_path->child_append_relid_sets;

	/*
	 * Compute sort column info, and adjust MergeAppend's tlist as needed.
	 * Because we pass adjust_tlist_in_place = true, we may ignore the
	 * function result; it must be the same plan node.  However, we then need
	 * to detect whether any tlist entries were added.
	 */
	(void) prepare_sort_from_pathkeys(plan, pathkeys,
									  best_path->path.parent->relids,
									  NULL,
									  true,
									  &node->numCols,
									  &node->sortColIdx,
									  &node->sortOperators,
									  &node->collations,
									  &node->nullsFirst);
	tlist_was_changed = (orig_tlist_length != list_length(plan->targetlist));

	/*
	 * Now prepare the child plans.  We must apply prepare_sort_from_pathkeys
	 * even to subplans that don't need an explicit sort, to make sure they
	 * are returning the same sort key columns the MergeAppend expects.
	 */
	foreach(subpaths, best_path->subpaths)
	{
		Path	   *subpath = (Path *) lfirst(subpaths);
		Plan	   *subplan;
		int			numsortkeys;
		AttrNumber *sortColIdx;
		Oid		   *sortOperators;
		Oid		   *collations;
		bool	   *nullsFirst;
		int			presorted_keys;

		/* Build the child plan */
		/* Must insist that all children return the same tlist */
		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);

		/* Compute sort column info, and adjust subplan's tlist as needed */
		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
											 subpath->parent->relids,
											 node->sortColIdx,
											 false,
											 &numsortkeys,
											 &sortColIdx,
											 &sortOperators,
											 &collations,
											 &nullsFirst);

		/*
		 * Check that we got the same sort key information.  We just Assert
		 * that the sortops match, since those depend only on the pathkeys;
		 * but it seems like a good idea to check the sort column numbers
		 * explicitly, to ensure the tlists really do match up.
		 */
		Assert(numsortkeys == node->numCols);
		if (memcmp(sortColIdx, node->sortColIdx,
				   numsortkeys * sizeof(AttrNumber)) != 0)
			elog(ERROR, "MergeAppend child's targetlist doesn't match MergeAppend");
		Assert(memcmp(sortOperators, node->sortOperators,
					  numsortkeys * sizeof(Oid)) == 0);
		Assert(memcmp(collations, node->collations,
					  numsortkeys * sizeof(Oid)) == 0);
		Assert(memcmp(nullsFirst, node->nullsFirst,
					  numsortkeys * sizeof(bool)) == 0);

		/* Now, insert a Sort node if subplan isn't sufficiently ordered */
		if (!pathkeys_count_contained_in(pathkeys, subpath->pathkeys,
										 &presorted_keys))
		{
			Plan	   *sort_plan;

			/*
			 * We choose to use incremental sort if it is enabled and there
			 * are presorted keys; otherwise we use full sort.
			 */
			if (enable_incremental_sort && presorted_keys > 0)
			{
				sort_plan = (Plan *)
					make_incrementalsort(subplan, numsortkeys, presorted_keys,
										 sortColIdx, sortOperators,
										 collations, nullsFirst);

				label_incrementalsort_with_costsize(root,
													(IncrementalSort *) sort_plan,
													pathkeys,
													best_path->limit_tuples);
			}
			else
			{
				sort_plan = (Plan *) make_sort(subplan, numsortkeys,
											   sortColIdx, sortOperators,
											   collations, nullsFirst);

				label_sort_with_costsize(root, (Sort *) sort_plan,
										 best_path->limit_tuples);
			}

			subplan = sort_plan;
		}

		subplans = lappend(subplans, subplan);
	}

	/* Set below if we find quals that we can use to run-time prune */
	node->part_prune_index = -1;

	/*
	 * If any quals exist, they may be useful to perform further partition
	 * pruning during execution.  Gather information needed by the executor to
	 * do partition pruning.
	 */
	if (enable_partition_pruning)
	{
		List	   *prunequal;

		prunequal = extract_actual_clauses(rel->baserestrictinfo, false);

		/* We don't currently generate any parameterized MergeAppend paths */
		Assert(best_path->path.param_info == NULL);

		if (prunequal != NIL)
			node->part_prune_index = make_partition_pruneinfo(root, rel,
															  best_path->subpaths,
															  prunequal);
	}

	node->mergeplans = subplans;

	/*
	 * If prepare_sort_from_pathkeys added sort columns, but we were told to
	 * produce either the exact tlist or a narrow tlist, we should get rid of
	 * the sort columns again.  We must inject a projection node to do so.
	 */
	if (tlist_was_changed && (flags & (CP_EXACT_TLIST | CP_SMALL_TLIST)))
	{
		tlist = list_copy_head(plan->targetlist, orig_tlist_length);
		return inject_projection_plan(plan, tlist, plan->parallel_safe);
	}
	else
		return plan;
}

/*
 * create_group_result_plan
 *	  Create a Result plan for 'best_path'.
 *	  This is only used for degenerate grouping cases.
 *
 *	  Returns a Plan node.
 */
static Result *
create_group_result_plan(PlannerInfo *root, GroupResultPath *best_path)
{
	Result	   *plan;
	List	   *tlist;
	List	   *quals;

	tlist = build_path_tlist(root, &best_path->path);

	/* best_path->quals is just bare clauses */
	quals = order_qual_clauses(root, best_path->quals);

	plan = make_one_row_result(tlist, (Node *) quals, best_path->path.parent);

	copy_generic_path_info(&plan->plan, (Path *) best_path);

	return plan;
}

/*
 * create_project_set_plan
 *	  Create a ProjectSet plan for 'best_path'.
 *
 *	  Returns a Plan node.
 */
static ProjectSet *
create_project_set_plan(PlannerInfo *root, ProjectSetPath *best_path)
{
	ProjectSet *plan;
	Plan	   *subplan;
	List	   *tlist;

	/* Since we intend to project, we don't need to constrain child tlist */
	subplan = create_plan_recurse(root, best_path->subpath, 0);

	tlist = build_path_tlist(root, &best_path->path);

	plan = make_project_set(tlist, subplan);

	copy_generic_path_info(&plan->plan, (Path *) best_path);

	return plan;
}

/*
 * create_material_plan
 *	  Create a Material plan for 'best_path' and (recursively) plans
 *	  for its subpaths.
 *
 *	  Returns a Plan node.
 */
static Material *
create_material_plan(PlannerInfo *root, MaterialPath *best_path, int flags)
{
	Material   *plan;
	Plan	   *subplan;

	/*
	 * We don't want any excess columns in the materialized tuples, so request
	 * a smaller tlist.  Otherwise, since Material doesn't project, tlist
	 * requirements pass through.
	 */
	subplan = create_plan_recurse(root, best_path->subpath,
								  flags | CP_SMALL_TLIST);

	plan = make_material(subplan);

	copy_generic_path_info(&plan->plan, (Path *) best_path);

	return plan;
}

/*
 * create_memoize_plan
 *	  Create a Memoize plan for 'best_path' and (recursively) plans for its
 *	  subpaths.
 *
 *	  Returns a Plan node.
 */
static Memoize *
create_memoize_plan(PlannerInfo *root, MemoizePath *best_path, int flags)
{
	Memoize    *plan;
	Bitmapset  *keyparamids;
	Plan	   *subplan;
	Oid		   *operators;
	Oid		   *collations;
	List	   *param_exprs = NIL;
	ListCell   *lc;
	ListCell   *lc2;
	int			nkeys;
	int			i;

	subplan = create_plan_recurse(root, best_path->subpath,
								  flags | CP_SMALL_TLIST);

	param_exprs = (List *) replace_nestloop_params(root, (Node *)
												   best_path->param_exprs);

	nkeys = list_length(param_exprs);
	Assert(nkeys > 0);
	operators = palloc(nkeys * sizeof(Oid));
	collations = palloc(nkeys * sizeof(Oid));

	i = 0;
	forboth(lc, param_exprs, lc2, best_path->hash_operators)
	{
		Expr	   *param_expr = (Expr *) lfirst(lc);
		Oid			opno = lfirst_oid(lc2);

		operators[i] = opno;
		collations[i] = exprCollation((Node *) param_expr);
		i++;
	}

	keyparamids = pull_paramids((Expr *) param_exprs);

	plan = make_memoize(subplan, operators, collations, param_exprs,
						best_path->singlerow, best_path->binary_mode,
						best_path->est_entries, keyparamids, best_path->est_calls,
						best_path->est_unique_keys, best_path->est_hit_ratio);

	copy_generic_path_info(&plan->plan, (Path *) best_path);

	return plan;
}

/*
 * create_gather_plan
 *
 *	  Create a Gather plan for 'best_path' and (recursively) plans
 *	  for its subpaths.
 */
static Gather *
create_gather_plan(PlannerInfo *root, GatherPath *best_path)
{
	Gather	   *gather_plan;
	Plan	   *subplan;
	List	   *tlist;

	/*
	 * Push projection down to the child node.  That way, the projection work
	 * is parallelized, and there can be no system columns in the result (they
	 * can't travel through a tuple queue because it uses MinimalTuple
	 * representation).
	 */
	subplan = create_plan_recurse(root, best_path->subpath, CP_EXACT_TLIST);

	tlist = build_path_tlist(root, &best_path->path);

	gather_plan = make_gather(tlist,
							  NIL,
							  best_path->num_workers,
							  assign_special_exec_param(root),
							  best_path->single_copy,
							  subplan);

	copy_generic_path_info(&gather_plan->plan, &best_path->path);

	/* use parallel mode for parallel plans. */
	root->glob->parallelModeNeeded = true;

	return gather_plan;
}

/*
 * create_gather_merge_plan
 *
 *	  Create a Gather Merge plan for 'best_path' and (recursively)
 *	  plans for its subpaths.
 */
static GatherMerge *
create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
{
	GatherMerge *gm_plan;
	Plan	   *subplan;
	List	   *pathkeys = best_path->path.pathkeys;
	List	   *tlist = build_path_tlist(root, &best_path->path);

	/* As with Gather, project away columns in the workers. */
	subplan = create_plan_recurse(root, best_path->subpath, CP_EXACT_TLIST);

	/* Create a shell for a GatherMerge plan. */
	gm_plan = makeNode(GatherMerge);
	gm_plan->plan.targetlist = tlist;
	gm_plan->num_workers = best_path->num_workers;
	copy_generic_path_info(&gm_plan->plan, &best_path->path);

	/* Assign the rescan Param. */
	gm_plan->rescan_param = assign_special_exec_param(root);

	/* Gather Merge is pointless with no pathkeys; use Gather instead. */
	Assert(pathkeys != NIL);

	/* Compute sort column info, and adjust subplan's tlist as needed */
	subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
										 best_path->subpath->parent->relids,
										 gm_plan->sortColIdx,
										 false,
										 &gm_plan->numCols,
										 &gm_plan->sortColIdx,
										 &gm_plan->sortOperators,
										 &gm_plan->collations,
										 &gm_plan->nullsFirst);

	/*
	 * All gather merge paths should have already guaranteed the necessary
	 * sort order.  See create_gather_merge_path.
	 */
	Assert(pathkeys_contained_in(pathkeys, best_path->subpath->pathkeys));

	/* Now insert the subplan under GatherMerge. */
	gm_plan->plan.lefttree = subplan;

	/* use parallel mode for parallel plans. */
	root->glob->parallelModeNeeded = true;

	return gm_plan;
}

/*
 * create_projection_plan
 *
 *	  Create a plan tree to do a projection step and (recursively) plans
 *	  for its subpaths.  We may need a Result node for the projection,
 *	  but sometimes we can just let the subplan do the work.
 */
static Plan *
create_projection_plan(PlannerInfo *root, ProjectionPath *best_path, int flags)
{
	Plan	   *plan;
	Plan	   *subplan;
	List	   *tlist;
	bool		needs_result_node = false;

	/*
	 * Convert our subpath to a Plan and determine whether we need a Result
	 * node.
	 *
	 * In most cases where we don't need to project, create_projection_path
	 * will have set dummypp, but not always.  First, some createplan.c
	 * routines change the tlists of their nodes.  (An example is that
	 * create_merge_append_plan might add resjunk sort columns to a
	 * MergeAppend.)  Second, create_projection_path has no way of knowing
	 * what path node will be placed on top of the projection path and
	 * therefore can't predict whether it will require an exact tlist. For
	 * both of these reasons, we have to recheck here.
	 */
	if (use_physical_tlist(root, &best_path->path, flags))
	{
		/*
		 * Our caller doesn't really care what tlist we return, so we don't
		 * actually need to project.  However, we may still need to ensure
		 * proper sortgroupref labels, if the caller cares about those.
		 */
		subplan = create_plan_recurse(root, best_path->subpath, 0);
		tlist = subplan->targetlist;
		if (flags & CP_LABEL_TLIST)
			apply_pathtarget_labeling_to_tlist(tlist,
											   best_path->path.pathtarget);
	}
	else if (is_projection_capable_path(best_path->subpath))
	{
		/*
		 * Our caller requires that we return the exact tlist, but no separate
		 * result node is needed because the subpath is projection-capable.
		 * Tell create_plan_recurse that we're going to ignore the tlist it
		 * produces.
		 */
		subplan = create_plan_recurse(root, best_path->subpath,
									  CP_IGNORE_TLIST);
		Assert(is_projection_capable_plan(subplan));
		tlist = build_path_tlist(root, &best_path->path);
	}
	else
	{
		/*
		 * It looks like we need a result node, unless by good fortune the
		 * requested tlist is exactly the one the child wants to produce.
		 */
		subplan = create_plan_recurse(root, best_path->subpath, 0);
		tlist = build_path_tlist(root, &best_path->path);
		needs_result_node = !tlist_same_exprs(tlist, subplan->targetlist);
	}

	/*
	 * If we make a different decision about whether to include a Result node
	 * than create_projection_path did, we'll have made slightly wrong cost
	 * estimates; but label the plan with the cost estimates we actually used,
	 * not "corrected" ones.  (XXX this could be cleaned up if we moved more
	 * of the sortcolumn setup logic into Path creation, but that would add
	 * expense to creating Paths we might end up not using.)
	 */
	if (!needs_result_node)
	{
		/* Don't need a separate Result, just assign tlist to subplan */
		plan = subplan;
		plan->targetlist = tlist;

		/* Label plan with the estimated costs we actually used */
		plan->startup_cost = best_path->path.startup_cost;
		plan->total_cost = best_path->path.total_cost;
		plan->plan_rows = best_path->path.rows;
		plan->plan_width = best_path->path.pathtarget->width;
		plan->parallel_safe = best_path->path.parallel_safe;
		/* ... but don't change subplan's parallel_aware flag */
	}
	else
	{
		plan = (Plan *) make_gating_result(tlist, NULL, subplan);

		copy_generic_path_info(plan, (Path *) best_path);
	}

	return plan;
}

/*
 * inject_projection_plan
 *	  Insert a Result node to do a projection step.
 *
 * This is used in a few places where we decide on-the-fly that we need a
 * projection step as part of the tree generated for some Path node.
 * We should try to get rid of this in favor of doing it more honestly.
 *
 * One reason it's ugly is we have to be told the right parallel_safe marking
 * to apply (since the tlist might be unsafe even if the child plan is safe).
 */
static Plan *
inject_projection_plan(Plan *subplan, List *tlist, bool parallel_safe)
{
	Plan	   *plan;

	plan = (Plan *) make_gating_result(tlist, NULL, subplan);

	/*
	 * In principle, we should charge tlist eval cost plus cpu_per_tuple per
	 * row for the Result node.  But the former has probably been factored in
	 * already and the latter was not accounted for during Path construction,
	 * so being formally correct might just make the EXPLAIN output look less
	 * consistent not more so.  Hence, just copy the subplan's cost.
	 */
	copy_plan_costsize(plan, subplan);
	plan->parallel_safe = parallel_safe;

	return plan;
}

/*
 * change_plan_targetlist
 *	  Externally available wrapper for inject_projection_plan.
 *
 * This is meant for use by FDW plan-generation functions, which might
 * want to adjust the tlist computed by some subplan tree.  In general,
 * a Result node is needed to compute the new tlist, but we can optimize
 * some cases.
 *
 * In most cases, tlist_parallel_safe can just be passed as the parallel_safe
 * flag of the FDW's own Path node.
 */
Plan *
change_plan_targetlist(Plan *subplan, List *tlist, bool tlist_parallel_safe)
{
	/*
	 * If the top plan node can't do projections and its existing target list
	 * isn't already what we need, we need to add a Result node to help it
	 * along.
	 */
	if (!is_projection_capable_plan(subplan) &&
		!tlist_same_exprs(tlist, subplan->targetlist))
		subplan = inject_projection_plan(subplan, tlist,
										 subplan->parallel_safe &&
										 tlist_parallel_safe);
	else
	{
		/* Else we can just replace the plan node's tlist */
		subplan->targetlist = tlist;
		subplan->parallel_safe &= tlist_parallel_safe;
	}
	return subplan;
}

/*
 * create_sort_plan
 *
 *	  Create a Sort plan for 'best_path' and (recursively) plans
 *	  for its subpaths.
 */
static Sort *
create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
{
	Sort	   *plan;
	Plan	   *subplan;

	/*
	 * We don't want any excess columns in the sorted tuples, so request a
	 * smaller tlist.  Otherwise, since Sort doesn't project, tlist
	 * requirements pass through.
	 */
	subplan = create_plan_recurse(root, best_path->subpath,
								  flags | CP_SMALL_TLIST);

	/*
	 * make_sort_from_pathkeys indirectly calls find_ec_member_matching_expr,
	 * which will ignore any child EC members that don't belong to the given
	 * relids. Thus, if this sort path is based on a child relation, we must
	 * pass its relids.
	 */
	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
								   IS_OTHER_REL(best_path->subpath->parent) ?
								   best_path->path.parent->relids : NULL);

	copy_generic_path_info(&plan->plan, (Path *) best_path);

	return plan;
}

/*
 * create_incrementalsort_plan
 *
 *	  Do the same as create_sort_plan, but create IncrementalSort plan.
 */
static IncrementalSort *
create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
							int flags)
{
	IncrementalSort *plan;
	Plan	   *subplan;

	/* See comments in create_sort_plan() above */
	subplan = create_plan_recurse(root, best_path->spath.subpath,
								  flags | CP_SMALL_TLIST);
	plan = make_incrementalsort_from_pathkeys(subplan,
											  best_path->spath.path.pathkeys,
											  IS_OTHER_REL(best_path->spath.subpath->parent) ?
											  best_path->spath.path.parent->relids : NULL,
											  best_path->nPresortedCols);

	copy_generic_path_info(&plan->sort.plan, (Path *) best_path);

	return plan;
}

/*
 * create_group_plan
 *
 *	  Create a Group plan for 'best_path' and (recursively) plans
 *	  for its subpaths.
 */
static Group *
create_group_plan(PlannerInfo *root, GroupPath *best_path)
{
	Group	   *plan;
	Plan	   *subplan;
	List	   *tlist;
	List	   *quals;

	/*
	 * Group can project, so no need to be terribly picky about child tlist,
	 * but we do need grouping columns to be available
	 */
	subplan = create_plan_recurse(root, best_path->subpath, CP_LABEL_TLIST);

	tlist = build_path_tlist(root, &best_path->path);

	quals = order_qual_clauses(root, best_path->qual);

	plan = make_group(tlist,
					  quals,
					  list_length(best_path->groupClause),
					  extract_grouping_cols(best_path->groupClause,
											subplan->targetlist),
					  extract_grouping_ops(best_path->groupClause),
					  extract_grouping_collations(best_path->groupClause,
												  subplan->targetlist),
					  subplan);

	copy_generic_path_info(&plan->plan, (Path *) best_path);

	return plan;
}

/*
 * create_unique_plan
 *
 *	  Create a Unique plan for 'best_path' and (recursively) plans
 *	  for its subpaths.
 */
static Unique *
create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags)
{
	Unique	   *plan;
	Plan	   *subplan;

	/*
	 * Unique doesn't project, so tlist requirements pass through; moreover we
	 * need grouping columns to be labeled.
	 */
	subplan = create_plan_recurse(root, best_path->subpath,
								  flags | CP_LABEL_TLIST);

	/*
	 * make_unique_from_pathkeys calls find_ec_member_matching_expr, which
	 * will ignore any child EC members that don't belong to the given relids.
	 * Thus, if this unique path is based on a child relation, we must pass
	 * its relids.
	 */
	plan = make_unique_from_pathkeys(subplan,
									 best_path->path.pathkeys,
									 best_path->numkeys,
									 IS_OTHER_REL(best_path->path.parent) ?
									 best_path->path.parent->relids : NULL);

	copy_generic_path_info(&plan->plan, (Path *) best_path);

	return plan;
}

/*
 * create_agg_plan
 *
 *	  Create an Agg plan for 'best_path' and (recursively) plans
 *	  for its subpaths.
 */
static Agg *
create_agg_plan(PlannerInfo *root, AggPath *best_path)
{
	Agg		   *plan;
	Plan	   *subplan;
	List	   *tlist;
	List	   *quals;

	/*
	 * Agg can project, so no need to be terribly picky about child tlist, but
	 * we do need grouping columns to be available
	 */
	subplan = create_plan_recurse(root, best_path->subpath, CP_LABEL_TLIST);

	tlist = build_path_tlist(root, &best_path->path);

	quals = order_qual_clauses(root, best_path->qual);

	plan = make_agg(tlist, quals,
					best_path->aggstrategy,
					best_path->aggsplit,
					list_length(best_path->groupClause),
					extract_grouping_cols(best_path->groupClause,
										  subplan->targetlist),
					extract_grouping_ops(best_path->groupClause),
					extract_grouping_collations(best_path->groupClause,
												subplan->targetlist),
					NIL,
					NIL,
					best_path->numGroups,
					best_path->transitionSpace,
					subplan);

	copy_generic_path_info(&plan->plan, (Path *) best_path);

	return plan;
}

/*
 * Given a groupclause for a collection of grouping sets, produce the
 * corresponding groupColIdx.
 *
 * root->grouping_map maps the tleSortGroupRef to the actual column position in
 * the input tuple. So we get the ref from the entries in the groupclause and
 * look them up there.
 */
static AttrNumber *
remap_groupColIdx(PlannerInfo *root, List *groupClause)
{
	AttrNumber *grouping_map = root->grouping_map;
	AttrNumber *new_grpColIdx;
	ListCell   *lc;
	int			i;

	Assert(grouping_map);

	new_grpColIdx = palloc0_array(AttrNumber, list_length(groupClause));

	i = 0;
	foreach(lc, groupClause)
	{
		SortGroupClause *clause = lfirst(lc);

		new_grpColIdx[i++] = grouping_map[clause->tleSortGroupRef];
	}

	return new_grpColIdx;
}

/*
 * create_groupingsets_plan
 *	  Create a plan for 'best_path' and (recursively) plans
 *	  for its subpaths.
 *
 *	  What we emit is an Agg plan with some vestigial Agg and Sort nodes
 *	  hanging off the side.  The top Agg implements the last grouping set
 *	  specified in the GroupingSetsPath, and any additional grouping sets
 *	  each give rise to a subsidiary Agg and Sort node in the top Agg's
 *	  "chain" list.  These nodes don't participate in the plan directly,
 *	  but they are a convenient way to represent the required data for
 *	  the extra steps.
 *
 *	  Returns a Plan node.
 */
static Plan *
create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path)
{
	Agg		   *plan;
	Plan	   *subplan;
	List	   *rollups = best_path->rollups;
	AttrNumber *grouping_map;
	int			maxref;
	List	   *chain;
	ListCell   *lc;

	/* Shouldn't get here without grouping sets */
	Assert(root->parse->groupingSets);
	Assert(rollups != NIL);

	/*
	 * Agg can project, so no need to be terribly picky about child tlist, but
	 * we do need grouping columns to be available
	 */
	subplan = create_plan_recurse(root, best_path->subpath, CP_LABEL_TLIST);

	/*
	 * Compute the mapping from tleSortGroupRef to column index in the child's
	 * tlist.  First, identify max SortGroupRef in groupClause, for array
	 * sizing.
	 */
	maxref = 0;
	foreach(lc, root->processed_groupClause)
	{
		SortGroupClause *gc = (SortGroupClause *) lfirst(lc);

		if (gc->tleSortGroupRef > maxref)
			maxref = gc->tleSortGroupRef;
	}

	grouping_map = (AttrNumber *) palloc0((maxref + 1) * sizeof(AttrNumber));

	/* Now look up the column numbers in the child's tlist */
	foreach(lc, root->processed_groupClause)
	{
		SortGroupClause *gc = (SortGroupClause *) lfirst(lc);
		TargetEntry *tle = get_sortgroupclause_tle(gc, subplan->targetlist);

		grouping_map[gc->tleSortGroupRef] = tle->resno;
	}

	/*
	 * During setrefs.c, we'll need the grouping_map to fix up the cols lists
	 * in GroupingFunc nodes.  Save it for setrefs.c to use.
	 */
	Assert(root->grouping_map == NULL);
	root->grouping_map = grouping_map;

	/*
	 * Generate the side nodes that describe the other sort and group
	 * operations besides the top one.  Note that we don't worry about putting
	 * accurate cost estimates in the side nodes; only the topmost Agg node's
	 * costs will be shown by EXPLAIN.
	 */
	chain = NIL;
	if (list_length(rollups) > 1)
	{
		bool		is_first_sort = ((RollupData *) linitial(rollups))->is_hashed;

		for_each_from(lc, rollups, 1)
		{
			RollupData *rollup = lfirst(lc);
			AttrNumber *new_grpColIdx;
			Plan	   *sort_plan = NULL;
			Plan	   *agg_plan;
			AggStrategy strat;

			new_grpColIdx = remap_groupColIdx(root, rollup->groupClause);

			if (!rollup->is_hashed && !is_first_sort)
			{
				sort_plan = (Plan *)
					make_sort_from_groupcols(rollup->groupClause,
											 new_grpColIdx,
											 subplan);
			}

			if (!rollup->is_hashed)
				is_first_sort = false;

			if (rollup->is_hashed)
				strat = AGG_HASHED;
			else if (linitial(rollup->gsets) == NIL)
				strat = AGG_PLAIN;
			else
				strat = AGG_SORTED;

			agg_plan = (Plan *) make_agg(NIL,
										 NIL,
										 strat,
										 AGGSPLIT_SIMPLE,
										 list_length((List *) linitial(rollup->gsets)),
										 new_grpColIdx,
										 extract_grouping_ops(rollup->groupClause),
										 extract_grouping_collations(rollup->groupClause, subplan->targetlist),
										 rollup->gsets,
										 NIL,
										 rollup->numGroups,
										 best_path->transitionSpace,
										 sort_plan);

			/*
			 * Remove stuff we don't need to avoid bloating debug output.
			 */
			if (sort_plan)
			{
				sort_plan->targetlist = NIL;
				sort_plan->lefttree = NULL;
			}

			chain = lappend(chain, agg_plan);
		}
	}

	/*
	 * Now make the real Agg node
	 */
	{
		RollupData *rollup = linitial(rollups);
		AttrNumber *top_grpColIdx;
		int			numGroupCols;

		top_grpColIdx = remap_groupColIdx(root, rollup->groupClause);

		numGroupCols = list_length((List *) linitial(rollup->gsets));

		plan = make_agg(build_path_tlist(root, &best_path->path),
						best_path->qual,
						best_path->aggstrategy,
						AGGSPLIT_SIMPLE,
						numGroupCols,
						top_grpColIdx,
						extract_grouping_ops(rollup->groupClause),
						extract_grouping_collations(rollup->groupClause, subplan->targetlist),
						rollup->gsets,
						chain,
						rollup->numGroups,
						best_path->transitionSpace,
						subplan);

		/* Copy cost data from Path to Plan */
		copy_generic_path_info(&plan->plan, &best_path->path);
	}

	return (Plan *) plan;
}

/*
 * create_minmaxagg_plan
 *
 *	  Create a Result plan for 'best_path' and (recursively) plans
 *	  for its subpaths.
 */
static Result *
create_minmaxagg_plan(PlannerInfo *root, MinMaxAggPath *best_path)
{
	Result	   *plan;
	List	   *tlist;
	ListCell   *lc;

	/* Prepare an InitPlan for each aggregate's subquery. */
	foreach(lc, best_path->mmaggregates)
	{
		MinMaxAggInfo *mminfo = (MinMaxAggInfo *) lfirst(lc);
		PlannerInfo *subroot = mminfo->subroot;
		Query	   *subparse = subroot->parse;
		Plan	   *plan;

		/*
		 * Generate the plan for the subquery. We already have a Path, but we
		 * have to convert it to a Plan and attach a LIMIT node above it.
		 * Since we are entering a different planner context (subroot),
		 * recurse to create_plan not create_plan_recurse.
		 */
		plan = create_plan(subroot, mminfo->path);

		plan = (Plan *) make_limit(plan,
								   subparse->limitOffset,
								   subparse->limitCount,
								   subparse->limitOption,
								   0, NULL, NULL, NULL);

		/* Must apply correct cost/width data to Limit node */
		plan->disabled_nodes = mminfo->path->disabled_nodes;
		plan->startup_cost = mminfo->path->startup_cost;
		plan->total_cost = mminfo->pathcost;
		plan->plan_rows = 1;
		plan->plan_width = mminfo->path->pathtarget->width;
		plan->parallel_aware = false;
		plan->parallel_safe = mminfo->path->parallel_safe;

		/* Convert the plan into an InitPlan in the outer query. */
		SS_make_initplan_from_plan(root, subroot, plan, mminfo->param);
	}

	/* Generate the output plan --- basically just a Result */
	tlist = build_path_tlist(root, &best_path->path);

	plan = make_one_row_result(tlist, (Node *) best_path->quals,
							   best_path->path.parent);
	plan->result_type = RESULT_TYPE_MINMAX;

	copy_generic_path_info(&plan->plan, (Path *) best_path);

	/*
	 * During setrefs.c, we'll need to replace references to the Agg nodes
	 * with InitPlan output params.  (We can't just do that locally in the
	 * MinMaxAgg node, because path nodes above here may have Agg references
	 * as well.)  Save the mmaggregates list to tell setrefs.c to do that.
	 */
	Assert(root->minmax_aggs == NIL);
	root->minmax_aggs = best_path->mmaggregates;

	return plan;
}

/*
 * create_windowagg_plan
 *
 *	  Create a WindowAgg plan for 'best_path' and (recursively) plans
 *	  for its subpaths.
 */
static WindowAgg *
create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path)
{
	WindowAgg  *plan;
	WindowClause *wc = best_path->winclause;
	int			numPart = list_length(wc->partitionClause);
	int			numOrder = list_length(wc->orderClause);
	Plan	   *subplan;
	List	   *tlist;
	int			partNumCols;
	AttrNumber *partColIdx;
	Oid		   *partOperators;
	Oid		   *partCollations;
	int			ordNumCols;
	AttrNumber *ordColIdx;
	Oid		   *ordOperators;
	Oid		   *ordCollations;
	ListCell   *lc;

	/*
	 * Choice of tlist here is motivated by the fact that WindowAgg will be
	 * storing the input rows of window frames in a tuplestore; it therefore
	 * behooves us to request a small tlist to avoid wasting space. We do of
	 * course need grouping columns to be available.
	 */
	subplan = create_plan_recurse(root, best_path->subpath,
								  CP_LABEL_TLIST | CP_SMALL_TLIST);

	tlist = build_path_tlist(root, &best_path->path);

	/*
	 * Convert SortGroupClause lists into arrays of attr indexes and equality
	 * operators, as wanted by executor.
	 */
	partColIdx = palloc_array(AttrNumber, numPart);
	partOperators = palloc_array(Oid, numPart);
	partCollations = palloc_array(Oid, numPart);

	partNumCols = 0;
	foreach(lc, wc->partitionClause)
	{
		SortGroupClause *sgc = (SortGroupClause *) lfirst(lc);
		TargetEntry *tle = get_sortgroupclause_tle(sgc, subplan->targetlist);

		Assert(OidIsValid(sgc->eqop));
		partColIdx[partNumCols] = tle->resno;
		partOperators[partNumCols] = sgc->eqop;
		partCollations[partNumCols] = exprCollation((Node *) tle->expr);
		partNumCols++;
	}

	ordColIdx = palloc_array(AttrNumber, numOrder);
	ordOperators = palloc_array(Oid, numOrder);
	ordCollations = palloc_array(Oid, numOrder);

	ordNumCols = 0;
	foreach(lc, wc->orderClause)
	{
		SortGroupClause *sgc = (SortGroupClause *) lfirst(lc);
		TargetEntry *tle = get_sortgroupclause_tle(sgc, subplan->targetlist);

		Assert(OidIsValid(sgc->eqop));
		ordColIdx[ordNumCols] = tle->resno;
		ordOperators[ordNumCols] = sgc->eqop;
		ordCollations[ordNumCols] = exprCollation((Node *) tle->expr);
		ordNumCols++;
	}

	/* And finally we can make the WindowAgg node */
	plan = make_windowagg(tlist,
						  wc,
						  partNumCols,
						  partColIdx,
						  partOperators,
						  partCollations,
						  ordNumCols,
						  ordColIdx,
						  ordOperators,
						  ordCollations,
						  best_path->runCondition,
						  best_path->qual,
						  best_path->topwindow,
						  subplan);

	copy_generic_path_info(&plan->plan, (Path *) best_path);

	return plan;
}

/*
 * create_setop_plan
 *
 *	  Create a SetOp plan for 'best_path' and (recursively) plans
 *	  for its subpaths.
 */
static SetOp *
create_setop_plan(PlannerInfo *root, SetOpPath *best_path, int flags)
{
	SetOp	   *plan;
	List	   *tlist = build_path_tlist(root, &best_path->path);
	Plan	   *leftplan;
	Plan	   *rightplan;

	/*
	 * SetOp doesn't project, so tlist requirements pass through; moreover we
	 * need grouping columns to be labeled.
	 */
	leftplan = create_plan_recurse(root, best_path->leftpath,
								   flags | CP_LABEL_TLIST);
	rightplan = create_plan_recurse(root, best_path->rightpath,
									flags | CP_LABEL_TLIST);

	plan = make_setop(best_path->cmd,
					  best_path->strategy,
					  tlist,
					  leftplan,
					  rightplan,
					  best_path->groupList,
					  best_path->numGroups);

	copy_generic_path_info(&plan->plan, (Path *) best_path);

	return plan;
}

/*
 * create_recursiveunion_plan
 *
 *	  Create a RecursiveUnion plan for 'best_path' and (recursively) plans
 *	  for its subpaths.
 */
static RecursiveUnion *
create_recursiveunion_plan(PlannerInfo *root, RecursiveUnionPath *best_path)
{
	RecursiveUnion *plan;
	Plan	   *leftplan;
	Plan	   *rightplan;
	List	   *tlist;

	/* Need both children to produce same tlist, so force it */
	leftplan = create_plan_recurse(root, best_path->leftpath, CP_EXACT_TLIST);
	rightplan = create_plan_recurse(root, best_path->rightpath, CP_EXACT_TLIST);

	tlist = build_path_tlist(root, &best_path->path);

	plan = make_recursive_union(tlist,
								leftplan,
								rightplan,
								best_path->wtParam,
								best_path->distinctList,
								best_path->numGroups);

	copy_generic_path_info(&plan->plan, (Path *) best_path);

	return plan;
}

/*
 * create_lockrows_plan
 *
 *	  Create a LockRows plan for 'best_path' and (recursively) plans
 *	  for its subpaths.
 */
static LockRows *
create_lockrows_plan(PlannerInfo *root, LockRowsPath *best_path,
					 int flags)
{
	LockRows   *plan;
	Plan	   *subplan;

	/* LockRows doesn't project, so tlist requirements pass through */
	subplan = create_plan_recurse(root, best_path->subpath, flags);

	plan = make_lockrows(subplan, best_path->rowMarks, best_path->epqParam);

	copy_generic_path_info(&plan->plan, (Path *) best_path);

	return plan;
}

/*
 * create_modifytable_plan
 *	  Create a ModifyTable plan for 'best_path'.
 *
 *	  Returns a Plan node.
 */
static ModifyTable *
create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path)
{
	ModifyTable *plan;
	Path	   *subpath = best_path->subpath;
	Plan	   *subplan;

	/* Subplan must produce exactly the specified tlist */
	subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);

	/* Transfer resname/resjunk labeling, too, to keep executor happy */
	apply_tlist_labeling(subplan->targetlist, root->processed_tlist);

	plan = make_modifytable(root,
							subplan,
							best_path->operation,
							best_path->canSetTag,
							best_path->nominalRelation,
							best_path->rootRelation,
							best_path->resultRelations,
							best_path->updateColnosLists,
							best_path->withCheckOptionLists,
							best_path->returningLists,
							best_path->rowMarks,
							best_path->onconflict,
							best_path->mergeActionLists,
							best_path->mergeJoinConditions,
							best_path->forPortionOf,
							best_path->epqParam);

	copy_generic_path_info(&plan->plan, &best_path->path);

	return plan;
}

/*
 * create_limit_plan
 *
 *	  Create a Limit plan for 'best_path' and (recursively) plans
 *	  for its subpaths.
 */
static Limit *
create_limit_plan(PlannerInfo *root, LimitPath *best_path, int flags)
{
	Limit	   *plan;
	Plan	   *subplan;
	int			numUniqkeys = 0;
	AttrNumber *uniqColIdx = NULL;
	Oid		   *uniqOperators = NULL;
	Oid		   *uniqCollations = NULL;

	/* Limit doesn't project, so tlist requirements pass through */
	subplan = create_plan_recurse(root, best_path->subpath, flags);

	/* Extract information necessary for comparing rows for WITH TIES. */
	if (best_path->limitOption == LIMIT_OPTION_WITH_TIES)
	{
		Query	   *parse = root->parse;
		ListCell   *l;

		numUniqkeys = list_length(parse->sortClause);
		uniqColIdx = (AttrNumber *) palloc(numUniqkeys * sizeof(AttrNumber));
		uniqOperators = (Oid *) palloc(numUniqkeys * sizeof(Oid));
		uniqCollations = (Oid *) palloc(numUniqkeys * sizeof(Oid));

		numUniqkeys = 0;
		foreach(l, parse->sortClause)
		{
			SortGroupClause *sortcl = (SortGroupClause *) lfirst(l);
			TargetEntry *tle = get_sortgroupclause_tle(sortcl, parse->targetList);

			uniqColIdx[numUniqkeys] = tle->resno;
			uniqOperators[numUniqkeys] = sortcl->eqop;
			uniqCollations[numUniqkeys] = exprCollation((Node *) tle->expr);
			numUniqkeys++;
		}
	}

	plan = make_limit(subplan,
					  best_path->limitOffset,
					  best_path->limitCount,
					  best_path->limitOption,
					  numUniqkeys, uniqColIdx, uniqOperators, uniqCollations);

	copy_generic_path_info(&plan->plan, (Path *) best_path);

	return plan;
}


/*****************************************************************************
 *
 *	BASE-RELATION SCAN METHODS
 *
 *****************************************************************************/


/*
 * create_seqscan_plan
 *	 Returns a seqscan plan for the base relation scanned by 'best_path'
 *	 with restriction clauses 'scan_clauses' and targetlist 'tlist'.
 */
static SeqScan *
create_seqscan_plan(PlannerInfo *root, Path *best_path,
					List *tlist, List *scan_clauses)
{
	SeqScan    *scan_plan;
	Index		scan_relid = best_path->parent->relid;

	/* it should be a base rel... */
	Assert(scan_relid > 0);
	Assert(best_path->parent->rtekind == RTE_RELATION);

	/* Sort clauses into best execution order */
	scan_clauses = order_qual_clauses(root, scan_clauses);

	/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
	scan_clauses = extract_actual_clauses(scan_clauses, false);

	/* Replace any outer-relation variables with nestloop params */
	if (best_path->param_info)
	{
		scan_clauses = (List *)
			replace_nestloop_params(root, (Node *) scan_clauses);
	}

	scan_plan = make_seqscan(tlist,
							 scan_clauses,
							 scan_relid);

	copy_generic_path_info(&scan_plan->scan.plan, best_path);

	return scan_plan;
}

/*
 * create_samplescan_plan
 *	 Returns a samplescan plan for the base relation scanned by 'best_path'
 *	 with restriction clauses 'scan_clauses' and targetlist 'tlist'.
 */
static SampleScan *
create_samplescan_plan(PlannerInfo *root, Path *best_path,
					   List *tlist, List *scan_clauses)
{
	SampleScan *scan_plan;
	Index		scan_relid = best_path->parent->relid;
	RangeTblEntry *rte;
	TableSampleClause *tsc;

	/* it should be a base rel with a tablesample clause... */
	Assert(scan_relid > 0);
	rte = planner_rt_fetch(scan_relid, root);
	Assert(rte->rtekind == RTE_RELATION);
	tsc = rte->tablesample;
	Assert(tsc != NULL);

	/* Sort clauses into best execution order */
	scan_clauses = order_qual_clauses(root, scan_clauses);

	/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
	scan_clauses = extract_actual_clauses(scan_clauses, false);

	/* Replace any outer-relation variables with nestloop params */
	if (best_path->param_info)
	{
		scan_clauses = (List *)
			replace_nestloop_params(root, (Node *) scan_clauses);
		tsc = (TableSampleClause *)
			replace_nestloop_params(root, (Node *) tsc);
	}

	scan_plan = make_samplescan(tlist,
								scan_clauses,
								scan_relid,
								tsc);

	copy_generic_path_info(&scan_plan->scan.plan, best_path);

	return scan_plan;
}

/*
 * create_indexscan_plan
 *	  Returns an indexscan plan for the base relation scanned by 'best_path'
 *	  with restriction clauses 'scan_clauses' and targetlist 'tlist'.
 *
 * We use this for both plain IndexScans and IndexOnlyScans, because the
 * qual preprocessing work is the same for both.  Note that the caller tells
 * us which to build --- we don't look at best_path->path.pathtype, because
 * create_bitmap_subplan needs to be able to override the prior decision.
 */
static Scan *
create_indexscan_plan(PlannerInfo *root,
					  IndexPath *best_path,
					  List *tlist,
					  List *scan_clauses,
					  bool indexonly)
{
	Scan	   *scan_plan;
	List	   *indexclauses = best_path->indexclauses;
	List	   *indexorderbys = best_path->indexorderbys;
	Index		baserelid = best_path->path.parent->relid;
	IndexOptInfo *indexinfo = best_path->indexinfo;
	Oid			indexoid = indexinfo->indexoid;
	List	   *qpqual;
	List	   *stripped_indexquals;
	List	   *fixed_indexquals;
	List	   *fixed_indexorderbys;
	List	   *indexorderbyops = NIL;
	ListCell   *l;

	/* it should be a base rel... */
	Assert(baserelid > 0);
	Assert(best_path->path.parent->rtekind == RTE_RELATION);
	/* check the scan direction is valid */
	Assert(best_path->indexscandir == ForwardScanDirection ||
		   best_path->indexscandir == BackwardScanDirection);

	/*
	 * Extract the index qual expressions (stripped of RestrictInfos) from the
	 * IndexClauses list, and prepare a copy with index Vars substituted for
	 * table Vars.  (This step also does replace_nestloop_params on the
	 * fixed_indexquals.)
	 */
	fix_indexqual_references(root, best_path,
							 &stripped_indexquals,
							 &fixed_indexquals);

	/*
	 * Likewise fix up index attr references in the ORDER BY expressions.
	 */
	fixed_indexorderbys = fix_indexorderby_references(root, best_path);

	/*
	 * The qpqual list must contain all restrictions not automatically handled
	 * by the index, other than pseudoconstant clauses which will be handled
	 * by a separate gating plan node.  All the predicates in the indexquals
	 * will be checked (either by the index itself, or by nodeIndexscan.c),
	 * but if there are any "special" operators involved then they must be
	 * included in qpqual.  The upshot is that qpqual must contain
	 * scan_clauses minus whatever appears in indexquals.
	 *
	 * is_redundant_with_indexclauses() detects cases where a scan clause is
	 * present in the indexclauses list or is generated from the same
	 * EquivalenceClass as some indexclause, and is therefore redundant with
	 * it, though not equal.  (The latter happens when indxpath.c prefers a
	 * different derived equality than what generate_join_implied_equalities
	 * picked for a parameterized scan's ppi_clauses.)  Note that it will not
	 * match to lossy index clauses, which is critical because we have to
	 * include the original clause in qpqual in that case.
	 *
	 * In some situations (particularly with OR'd index conditions) we may
	 * have scan_clauses that are not equal to, but are logically implied by,
	 * the index quals; so we also try a predicate_implied_by() check to see
	 * if we can discard quals that way.  (predicate_implied_by assumes its
	 * first input contains only immutable functions, so we have to check
	 * that.)
	 *
	 * Note: if you change this bit of code you should also look at
	 * extract_nonindex_conditions() in costsize.c.
	 */
	qpqual = NIL;
	foreach(l, scan_clauses)
	{
		RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);

		if (rinfo->pseudoconstant)
			continue;			/* we may drop pseudoconstants here */
		if (is_redundant_with_indexclauses(rinfo, indexclauses))
			continue;			/* dup or derived from same EquivalenceClass */
		if (!contain_mutable_functions((Node *) rinfo->clause) &&
			predicate_implied_by(list_make1(rinfo->clause), stripped_indexquals,
								 false))
			continue;			/* provably implied by indexquals */
		qpqual = lappend(qpqual, rinfo);
	}

	/* Sort clauses into best execution order */
	qpqual = order_qual_clauses(root, qpqual);

	/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
	qpqual = extract_actual_clauses(qpqual, false);

	/*
	 * We have to replace any outer-relation variables with nestloop params in
	 * the indexqualorig, qpqual, and indexorderbyorig expressions.  A bit
	 * annoying to have to do this separately from the processing in
	 * fix_indexqual_references --- rethink this when generalizing the inner
	 * indexscan support.  But note we can't really do this earlier because
	 * it'd break the comparisons to predicates above ... (or would it?  Those
	 * wouldn't have outer refs)
	 */
	if (best_path->path.param_info)
	{
		stripped_indexquals = (List *)
			replace_nestloop_params(root, (Node *) stripped_indexquals);
		qpqual = (List *)
			replace_nestloop_params(root, (Node *) qpqual);
		indexorderbys = (List *)
			replace_nestloop_params(root, (Node *) indexorderbys);
	}

	/*
	 * If there are ORDER BY expressions, look up the sort operators for their
	 * result datatypes.
	 */
	if (indexorderbys)
	{
		ListCell   *pathkeyCell,
				   *exprCell;

		/*
		 * PathKey contains OID of the btree opfamily we're sorting by, but
		 * that's not quite enough because we need the expression's datatype
		 * to look up the sort operator in the operator family.
		 */
		Assert(list_length(best_path->path.pathkeys) == list_length(indexorderbys));
		forboth(pathkeyCell, best_path->path.pathkeys, exprCell, indexorderbys)
		{
			PathKey    *pathkey = (PathKey *) lfirst(pathkeyCell);
			Node	   *expr = (Node *) lfirst(exprCell);
			Oid			exprtype = exprType(expr);
			Oid			sortop;

			/* Get sort operator from opfamily */
			sortop = get_opfamily_member_for_cmptype(pathkey->pk_opfamily,
													 exprtype,
													 exprtype,
													 pathkey->pk_cmptype);
			if (!OidIsValid(sortop))
				elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
					 pathkey->pk_cmptype, exprtype, exprtype, pathkey->pk_opfamily);
			indexorderbyops = lappend_oid(indexorderbyops, sortop);
		}
	}

	/*
	 * For an index-only scan, we must mark indextlist entries as resjunk if
	 * they are columns that the index AM can't return; this cues setrefs.c to
	 * not generate references to those columns.
	 */
	if (indexonly)
	{
		int			i = 0;

		foreach(l, indexinfo->indextlist)
		{
			TargetEntry *indextle = (TargetEntry *) lfirst(l);

			indextle->resjunk = !indexinfo->canreturn[i];
			i++;
		}
	}

	/* Finally ready to build the plan node */
	if (indexonly)
		scan_plan = (Scan *) make_indexonlyscan(tlist,
												qpqual,
												baserelid,
												indexoid,
												fixed_indexquals,
												stripped_indexquals,
												fixed_indexorderbys,
												indexinfo->indextlist,
												best_path->indexscandir);
	else
		scan_plan = (Scan *) make_indexscan(tlist,
											qpqual,
											baserelid,
											indexoid,
											fixed_indexquals,
											stripped_indexquals,
											fixed_indexorderbys,
											indexorderbys,
											indexorderbyops,
											best_path->indexscandir);

	copy_generic_path_info(&scan_plan->plan, &best_path->path);

	return scan_plan;
}

/*
 * create_bitmap_scan_plan
 *	  Returns a bitmap scan plan for the base relation scanned by 'best_path'
 *	  with restriction clauses 'scan_clauses' and targetlist 'tlist'.
 */
static BitmapHeapScan *
create_bitmap_scan_plan(PlannerInfo *root,
						BitmapHeapPath *best_path,
						List *tlist,
						List *scan_clauses)
{
	Index		baserelid = best_path->path.parent->relid;
	Plan	   *bitmapqualplan;
	List	   *bitmapqualorig;
	List	   *indexquals;
	List	   *indexECs;
	List	   *qpqual;
	ListCell   *l;
	BitmapHeapScan *scan_plan;

	/* it should be a base rel... */
	Assert(baserelid > 0);
	Assert(best_path->path.parent->rtekind == RTE_RELATION);

	/* Process the bitmapqual tree into a Plan tree and qual lists */
	bitmapqualplan = create_bitmap_subplan(root, best_path->bitmapqual,
										   &bitmapqualorig, &indexquals,
										   &indexECs);

	if (best_path->path.parallel_aware)
		bitmap_subplan_mark_shared(bitmapqualplan);

	/*
	 * The qpqual list must contain all restrictions not automatically handled
	 * by the index, other than pseudoconstant clauses which will be handled
	 * by a separate gating plan node.  All the predicates in the indexquals
	 * will be checked (either by the index itself, or by
	 * nodeBitmapHeapscan.c), but if there are any "special" operators
	 * involved then they must be added to qpqual.  The upshot is that qpqual
	 * must contain scan_clauses minus whatever appears in indexquals.
	 *
	 * This loop is similar to the comparable code in create_indexscan_plan(),
	 * but with some differences because it has to compare the scan clauses to
	 * stripped (no RestrictInfos) indexquals.  See comments there for more
	 * info.
	 *
	 * In normal cases simple equal() checks will be enough to spot duplicate
	 * clauses, so we try that first.  We next see if the scan clause is
	 * redundant with any top-level indexqual by virtue of being generated
	 * from the same EC.  After that, try predicate_implied_by().
	 *
	 * Unlike create_indexscan_plan(), the predicate_implied_by() test here is
	 * useful for getting rid of qpquals that are implied by index predicates,
	 * because the predicate conditions are included in the "indexquals"
	 * returned by create_bitmap_subplan().  Bitmap scans have to do it that
	 * way because predicate conditions need to be rechecked if the scan
	 * becomes lossy, so they have to be included in bitmapqualorig.
	 */
	qpqual = NIL;
	foreach(l, scan_clauses)
	{
		RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
		Node	   *clause = (Node *) rinfo->clause;

		if (rinfo->pseudoconstant)
			continue;			/* we may drop pseudoconstants here */
		if (list_member(indexquals, clause))
			continue;			/* simple duplicate */
		if (rinfo->parent_ec && list_member_ptr(indexECs, rinfo->parent_ec))
			continue;			/* derived from same EquivalenceClass */
		if (!contain_mutable_functions(clause) &&
			predicate_implied_by(list_make1(clause), indexquals, false))
			continue;			/* provably implied by indexquals */
		qpqual = lappend(qpqual, rinfo);
	}

	/* Sort clauses into best execution order */
	qpqual = order_qual_clauses(root, qpqual);

	/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
	qpqual = extract_actual_clauses(qpqual, false);

	/*
	 * When dealing with special operators, we will at this point have
	 * duplicate clauses in qpqual and bitmapqualorig.  We may as well drop
	 * 'em from bitmapqualorig, since there's no point in making the tests
	 * twice.
	 */
	bitmapqualorig = list_difference_ptr(bitmapqualorig, qpqual);

	/*
	 * We have to replace any outer-relation variables with nestloop params in
	 * the qpqual and bitmapqualorig expressions.  (This was already done for
	 * expressions attached to plan nodes in the bitmapqualplan tree.)
	 */
	if (best_path->path.param_info)
	{
		qpqual = (List *)
			replace_nestloop_params(root, (Node *) qpqual);
		bitmapqualorig = (List *)
			replace_nestloop_params(root, (Node *) bitmapqualorig);
	}

	/* Finally ready to build the plan node */
	scan_plan = make_bitmap_heapscan(tlist,
									 qpqual,
									 bitmapqualplan,
									 bitmapqualorig,
									 baserelid);

	copy_generic_path_info(&scan_plan->scan.plan, &best_path->path);

	return scan_plan;
}

/*
 * Given a bitmapqual tree, generate the Plan tree that implements it
 *
 * As byproducts, we also return in *qual and *indexqual the qual lists
 * (in implicit-AND form, without RestrictInfos) describing the original index
 * conditions and the generated indexqual conditions.  (These are the same in
 * simple cases, but when special index operators are involved, the former
 * list includes the special conditions while the latter includes the actual
 * indexable conditions derived from them.)  Both lists include partial-index
 * predicates, because we have to recheck predicates as well as index
 * conditions if the bitmap scan becomes lossy.
 *
 * In addition, we return a list of EquivalenceClass pointers for all the
 * top-level indexquals that were possibly-redundantly derived from ECs.
 * This allows removal of scan_clauses that are redundant with such quals.
 * (We do not attempt to detect such redundancies for quals that are within
 * OR subtrees.  This could be done in a less hacky way if we returned the
 * indexquals in RestrictInfo form, but that would be slower and still pretty
 * messy, since we'd have to build new RestrictInfos in many cases.)
 */
static Plan *
create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
					  List **qual, List **indexqual, List **indexECs)
{
	Plan	   *plan;

	if (IsA(bitmapqual, BitmapAndPath))
	{
		BitmapAndPath *apath = (BitmapAndPath *) bitmapqual;
		List	   *subplans = NIL;
		List	   *subquals = NIL;
		List	   *subindexquals = NIL;
		List	   *subindexECs = NIL;
		ListCell   *l;

		/*
		 * There may well be redundant quals among the subplans, since a
		 * top-level WHERE qual might have gotten used to form several
		 * different index quals.  We don't try exceedingly hard to eliminate
		 * redundancies, but we do eliminate obvious duplicates by using
		 * list_concat_unique.
		 */
		foreach(l, apath->bitmapquals)
		{
			Plan	   *subplan;
			List	   *subqual;
			List	   *subindexqual;
			List	   *subindexEC;

			subplan = create_bitmap_subplan(root, (Path *) lfirst(l),
											&subqual, &subindexqual,
											&subindexEC);
			subplans = lappend(subplans, subplan);
			subquals = list_concat_unique(subquals, subqual);
			subindexquals = list_concat_unique(subindexquals, subindexqual);
			/* Duplicates in indexECs aren't worth getting rid of */
			subindexECs = list_concat(subindexECs, subindexEC);
		}
		plan = (Plan *) make_bitmap_and(subplans);
		plan->startup_cost = apath->path.startup_cost;
		plan->total_cost = apath->path.total_cost;
		plan->plan_rows =
			clamp_row_est(apath->bitmapselectivity * apath->path.parent->tuples);
		plan->plan_width = 0;	/* meaningless */
		plan->parallel_aware = false;
		plan->parallel_safe = apath->path.parallel_safe;
		*qual = subquals;
		*indexqual = subindexquals;
		*indexECs = subindexECs;
	}
	else if (IsA(bitmapqual, BitmapOrPath))
	{
		BitmapOrPath *opath = (BitmapOrPath *) bitmapqual;
		List	   *subplans = NIL;
		List	   *subquals = NIL;
		List	   *subindexquals = NIL;
		bool		const_true_subqual = false;
		bool		const_true_subindexqual = false;
		ListCell   *l;

		/*
		 * Here, we only detect qual-free subplans.  A qual-free subplan would
		 * cause us to generate "... OR true ..."  which we may as well reduce
		 * to just "true".  We do not try to eliminate redundant subclauses
		 * because (a) it's not as likely as in the AND case, and (b) we might
		 * well be working with hundreds or even thousands of OR conditions,
		 * perhaps from a long IN list.  The performance of list_append_unique
		 * would be unacceptable.
		 */
		foreach(l, opath->bitmapquals)
		{
			Plan	   *subplan;
			List	   *subqual;
			List	   *subindexqual;
			List	   *subindexEC;

			subplan = create_bitmap_subplan(root, (Path *) lfirst(l),
											&subqual, &subindexqual,
											&subindexEC);
			subplans = lappend(subplans, subplan);
			if (subqual == NIL)
				const_true_subqual = true;
			else if (!const_true_subqual)
				subquals = lappend(subquals,
								   make_ands_explicit(subqual));
			if (subindexqual == NIL)
				const_true_subindexqual = true;
			else if (!const_true_subindexqual)
				subindexquals = lappend(subindexquals,
										make_ands_explicit(subindexqual));
		}

		/*
		 * In the presence of ScalarArrayOpExpr quals, we might have built
		 * BitmapOrPaths with just one subpath; don't add an OR step.
		 */
		if (list_length(subplans) == 1)
		{
			plan = (Plan *) linitial(subplans);
		}
		else
		{
			plan = (Plan *) make_bitmap_or(subplans);
			plan->startup_cost = opath->path.startup_cost;
			plan->total_cost = opath->path.total_cost;
			plan->plan_rows =
				clamp_row_est(opath->bitmapselectivity * opath->path.parent->tuples);
			plan->plan_width = 0;	/* meaningless */
			plan->parallel_aware = false;
			plan->parallel_safe = opath->path.parallel_safe;
		}

		/*
		 * If there were constant-TRUE subquals, the OR reduces to constant
		 * TRUE.  Also, avoid generating one-element ORs, which could happen
		 * due to redundancy elimination or ScalarArrayOpExpr quals.
		 */
		if (const_true_subqual)
			*qual = NIL;
		else if (list_length(subquals) <= 1)
			*qual = subquals;
		else
			*qual = list_make1(make_orclause(subquals));
		if (const_true_subindexqual)
			*indexqual = NIL;
		else if (list_length(subindexquals) <= 1)
			*indexqual = subindexquals;
		else
			*indexqual = list_make1(make_orclause(subindexquals));
		*indexECs = NIL;
	}
	else if (IsA(bitmapqual, IndexPath))
	{
		IndexPath  *ipath = (IndexPath *) bitmapqual;
		IndexScan  *iscan;
		List	   *subquals;
		List	   *subindexquals;
		List	   *subindexECs;
		ListCell   *l;

		/* Use the regular indexscan plan build machinery... */
		iscan = castNode(IndexScan,
						 create_indexscan_plan(root, ipath,
											   NIL, NIL, false));
		/* then convert to a bitmap indexscan */
		plan = (Plan *) make_bitmap_indexscan(iscan->scan.scanrelid,
											  iscan->indexid,
											  iscan->indexqual,
											  iscan->indexqualorig);
		/* and set its cost/width fields appropriately */
		plan->startup_cost = 0.0;
		plan->total_cost = ipath->indextotalcost;
		plan->plan_rows =
			clamp_row_est(ipath->indexselectivity * ipath->path.parent->tuples);
		plan->plan_width = 0;	/* meaningless */
		plan->parallel_aware = false;
		plan->parallel_safe = ipath->path.parallel_safe;
		/* Extract original index clauses, actual index quals, relevant ECs */
		subquals = NIL;
		subindexquals = NIL;
		subindexECs = NIL;
		foreach(l, ipath->indexclauses)
		{
			IndexClause *iclause = (IndexClause *) lfirst(l);
			RestrictInfo *rinfo = iclause->rinfo;

			Assert(!rinfo->pseudoconstant);
			subquals = lappend(subquals, rinfo->clause);
			subindexquals = list_concat(subindexquals,
										get_actual_clauses(iclause->indexquals));
			if (rinfo->parent_ec)
				subindexECs = lappend(subindexECs, rinfo->parent_ec);
		}
		/* We can add any index predicate conditions, too */
		foreach(l, ipath->indexinfo->indpredExpand)
		{
			Expr	   *pred = (Expr *) lfirst(l);

			/*
			 * We know that the index predicate must have been implied by the
			 * query condition as a whole, but it may or may not be implied by
			 * the conditions that got pushed into the bitmapqual.  Avoid
			 * generating redundant conditions.
			 */
			if (!predicate_implied_by(list_make1(pred), subquals, false))
			{
				subquals = lappend(subquals, pred);
				subindexquals = lappend(subindexquals, pred);
			}
		}
		*qual = subquals;
		*indexqual = subindexquals;
		*indexECs = subindexECs;
	}
	else
	{
		elog(ERROR, "unrecognized node type: %d", nodeTag(bitmapqual));
		plan = NULL;			/* keep compiler quiet */
	}

	return plan;
}

/*
 * create_tidscan_plan
 *	 Returns a tidscan plan for the base relation scanned by 'best_path'
 *	 with restriction clauses 'scan_clauses' and targetlist 'tlist'.
 */
static TidScan *
create_tidscan_plan(PlannerInfo *root, TidPath *best_path,
					List *tlist, List *scan_clauses)
{
	TidScan    *scan_plan;
	Index		scan_relid = best_path->path.parent->relid;
	List	   *tidquals = best_path->tidquals;

	/* it should be a base rel... */
	Assert(scan_relid > 0);
	Assert(best_path->path.parent->rtekind == RTE_RELATION);

	/*
	 * The qpqual list must contain all restrictions not enforced by the
	 * tidquals list.  Since tidquals has OR semantics, we have to be careful
	 * about matching it up to scan_clauses.  It's convenient to handle the
	 * single-tidqual case separately from the multiple-tidqual case.  In the
	 * single-tidqual case, we look through the scan_clauses while they are
	 * still in RestrictInfo form, and drop any that are redundant with the
	 * tidqual.
	 *
	 * In normal cases simple pointer equality checks will be enough to spot
	 * duplicate RestrictInfos, so we try that first.
	 *
	 * Another common case is that a scan_clauses entry is generated from the
	 * same EquivalenceClass as some tidqual, and is therefore redundant with
	 * it, though not equal.
	 *
	 * Unlike indexpaths, we don't bother with predicate_implied_by(); the
	 * number of cases where it could win are pretty small.
	 */
	if (list_length(tidquals) == 1)
	{
		List	   *qpqual = NIL;
		ListCell   *l;

		foreach(l, scan_clauses)
		{
			RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);

			if (rinfo->pseudoconstant)
				continue;		/* we may drop pseudoconstants here */
			if (list_member_ptr(tidquals, rinfo))
				continue;		/* simple duplicate */
			if (is_redundant_derived_clause(rinfo, tidquals))
				continue;		/* derived from same EquivalenceClass */
			qpqual = lappend(qpqual, rinfo);
		}
		scan_clauses = qpqual;
	}

	/* Sort clauses into best execution order */
	scan_clauses = order_qual_clauses(root, scan_clauses);

	/* Reduce RestrictInfo lists to bare expressions; ignore pseudoconstants */
	tidquals = extract_actual_clauses(tidquals, false);
	scan_clauses = extract_actual_clauses(scan_clauses, false);

	/*
	 * If we have multiple tidquals, it's more convenient to remove duplicate
	 * scan_clauses after stripping the RestrictInfos.  In this situation,
	 * because the tidquals represent OR sub-clauses, they could not have come
	 * from EquivalenceClasses so we don't have to worry about matching up
	 * non-identical clauses.  On the other hand, because tidpath.c will have
	 * extracted those sub-clauses from some OR clause and built its own list,
	 * we will certainly not have pointer equality to any scan clause.  So
	 * convert the tidquals list to an explicit OR clause and see if we can
	 * match it via equal() to any scan clause.
	 */
	if (list_length(tidquals) > 1)
		scan_clauses = list_difference(scan_clauses,
									   list_make1(make_orclause(tidquals)));

	/* Replace any outer-relation variables with nestloop params */
	if (best_path->path.param_info)
	{
		tidquals = (List *)
			replace_nestloop_params(root, (Node *) tidquals);
		scan_clauses = (List *)
			replace_nestloop_params(root, (Node *) scan_clauses);
	}

	scan_plan = make_tidscan(tlist,
							 scan_clauses,
							 scan_relid,
							 tidquals);

	copy_generic_path_info(&scan_plan->scan.plan, &best_path->path);

	return scan_plan;
}

/*
 * create_tidrangescan_plan
 *	 Returns a tidrangescan plan for the base relation scanned by 'best_path'
 *	 with restriction clauses 'scan_clauses' and targetlist 'tlist'.
 */
static TidRangeScan *
create_tidrangescan_plan(PlannerInfo *root, TidRangePath *best_path,
						 List *tlist, List *scan_clauses)
{
	TidRangeScan *scan_plan;
	Index		scan_relid = best_path->path.parent->relid;
	List	   *tidrangequals = best_path->tidrangequals;

	/* it should be a base rel... */
	Assert(scan_relid > 0);
	Assert(best_path->path.parent->rtekind == RTE_RELATION);

	/*
	 * The qpqual list must contain all restrictions not enforced by the
	 * tidrangequals list.  tidrangequals has AND semantics, so we can simply
	 * remove any qual that appears in it.
	 */
	{
		List	   *qpqual = NIL;
		ListCell   *l;

		foreach(l, scan_clauses)
		{
			RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);

			if (rinfo->pseudoconstant)
				continue;		/* we may drop pseudoconstants here */
			if (list_member_ptr(tidrangequals, rinfo))
				continue;		/* simple duplicate */
			qpqual = lappend(qpqual, rinfo);
		}
		scan_clauses = qpqual;
	}

	/* Sort clauses into best execution order */
	scan_clauses = order_qual_clauses(root, scan_clauses);

	/* Reduce RestrictInfo lists to bare expressions; ignore pseudoconstants */
	tidrangequals = extract_actual_clauses(tidrangequals, false);
	scan_clauses = extract_actual_clauses(scan_clauses, false);

	/* Replace any outer-relation variables with nestloop params */
	if (best_path->path.param_info)
	{
		tidrangequals = (List *)
			replace_nestloop_params(root, (Node *) tidrangequals);
		scan_clauses = (List *)
			replace_nestloop_params(root, (Node *) scan_clauses);
	}

	scan_plan = make_tidrangescan(tlist,
								  scan_clauses,
								  scan_relid,
								  tidrangequals);

	copy_generic_path_info(&scan_plan->scan.plan, &best_path->path);

	return scan_plan;
}

/*
 * create_subqueryscan_plan
 *	 Returns a subqueryscan plan for the base relation scanned by 'best_path'
 *	 with restriction clauses 'scan_clauses' and targetlist 'tlist'.
 */
static SubqueryScan *
create_subqueryscan_plan(PlannerInfo *root, SubqueryScanPath *best_path,
						 List *tlist, List *scan_clauses)
{
	SubqueryScan *scan_plan;
	RelOptInfo *rel = best_path->path.parent;
	Index		scan_relid = rel->relid;
	Plan	   *subplan;

	/* it should be a subquery base rel... */
	Assert(scan_relid > 0);
	Assert(rel->rtekind == RTE_SUBQUERY);

	/*
	 * Recursively create Plan from Path for subquery.  Since we are entering
	 * a different planner context (subroot), recurse to create_plan not
	 * create_plan_recurse.
	 */
	subplan = create_plan(rel->subroot, best_path->subpath);

	/* Sort clauses into best execution order */
	scan_clauses = order_qual_clauses(root, scan_clauses);

	/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
	scan_clauses = extract_actual_clauses(scan_clauses, false);

	/*
	 * Replace any outer-relation variables with nestloop params.
	 *
	 * We must provide nestloop params for both lateral references of the
	 * subquery and outer vars in the scan_clauses.  It's better to assign the
	 * former first, because that code path requires specific param IDs, while
	 * replace_nestloop_params can adapt to the IDs assigned by
	 * process_subquery_nestloop_params.  This avoids possibly duplicating
	 * nestloop params when the same Var is needed for both reasons.
	 */
	if (best_path->path.param_info)
	{
		process_subquery_nestloop_params(root,
										 rel->subplan_params);
		scan_clauses = (List *)
			replace_nestloop_params(root, (Node *) scan_clauses);
	}

	scan_plan = make_subqueryscan(tlist,
								  scan_clauses,
								  scan_relid,
								  subplan);

	copy_generic_path_info(&scan_plan->scan.plan, &best_path->path);

	return scan_plan;
}

/*
 * create_functionscan_plan
 *	 Returns a functionscan plan for the base relation scanned by 'best_path'
 *	 with restriction clauses 'scan_clauses' and targetlist 'tlist'.
 */
static FunctionScan *
create_functionscan_plan(PlannerInfo *root, Path *best_path,
						 List *tlist, List *scan_clauses)
{
	FunctionScan *scan_plan;
	Index		scan_relid = best_path->parent->relid;
	RangeTblEntry *rte;
	List	   *functions;

	/* it should be a function base rel... */
	Assert(scan_relid > 0);
	rte = planner_rt_fetch(scan_relid, root);
	Assert(rte->rtekind == RTE_FUNCTION);
	functions = rte->functions;

	/* Sort clauses into best execution order */
	scan_clauses = order_qual_clauses(root, scan_clauses);

	/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
	scan_clauses = extract_actual_clauses(scan_clauses, false);

	/* Replace any outer-relation variables with nestloop params */
	if (best_path->param_info)
	{
		scan_clauses = (List *)
			replace_nestloop_params(root, (Node *) scan_clauses);
		/* The function expressions could contain nestloop params, too */
		functions = (List *) replace_nestloop_params(root, (Node *) functions);
	}

	scan_plan = make_functionscan(tlist, scan_clauses, scan_relid,
								  functions, rte->funcordinality);

	copy_generic_path_info(&scan_plan->scan.plan, best_path);

	return scan_plan;
}

/*
 * create_tablefuncscan_plan
 *	 Returns a tablefuncscan plan for the base relation scanned by 'best_path'
 *	 with restriction clauses 'scan_clauses' and targetlist 'tlist'.
 */
static TableFuncScan *
create_tablefuncscan_plan(PlannerInfo *root, Path *best_path,
						  List *tlist, List *scan_clauses)
{
	TableFuncScan *scan_plan;
	Index		scan_relid = best_path->parent->relid;
	RangeTblEntry *rte;
	TableFunc  *tablefunc;

	/* it should be a function base rel... */
	Assert(scan_relid > 0);
	rte = planner_rt_fetch(scan_relid, root);
	Assert(rte->rtekind == RTE_TABLEFUNC);
	tablefunc = rte->tablefunc;

	/* Sort clauses into best execution order */
	scan_clauses = order_qual_clauses(root, scan_clauses);

	/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
	scan_clauses = extract_actual_clauses(scan_clauses, false);

	/* Replace any outer-relation variables with nestloop params */
	if (best_path->param_info)
	{
		scan_clauses = (List *)
			replace_nestloop_params(root, (Node *) scan_clauses);
		/* The function expressions could contain nestloop params, too */
		tablefunc = (TableFunc *) replace_nestloop_params(root, (Node *) tablefunc);
	}

	scan_plan = make_tablefuncscan(tlist, scan_clauses, scan_relid,
								   tablefunc);

	copy_generic_path_info(&scan_plan->scan.plan, best_path);

	return scan_plan;
}

/*
 * create_valuesscan_plan
 *	 Returns a valuesscan plan for the base relation scanned by 'best_path'
 *	 with restriction clauses 'scan_clauses' and targetlist 'tlist'.
 */
static ValuesScan *
create_valuesscan_plan(PlannerInfo *root, Path *best_path,
					   List *tlist, List *scan_clauses)
{
	ValuesScan *scan_plan;
	Index		scan_relid = best_path->parent->relid;
	RangeTblEntry *rte;
	List	   *values_lists;

	/* it should be a values base rel... */
	Assert(scan_relid > 0);
	rte = planner_rt_fetch(scan_relid, root);
	Assert(rte->rtekind == RTE_VALUES);
	values_lists = rte->values_lists;

	/* Sort clauses into best execution order */
	scan_clauses = order_qual_clauses(root, scan_clauses);

	/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
	scan_clauses = extract_actual_clauses(scan_clauses, false);

	/* Replace any outer-relation variables with nestloop params */
	if (best_path->param_info)
	{
		scan_clauses = (List *)
			replace_nestloop_params(root, (Node *) scan_clauses);
		/* The values lists could contain nestloop params, too */
		values_lists = (List *)
			replace_nestloop_params(root, (Node *) values_lists);
	}

	scan_plan = make_valuesscan(tlist, scan_clauses, scan_relid,
								values_lists);

	copy_generic_path_info(&scan_plan->scan.plan, best_path);

	return scan_plan;
}

/*
 * create_ctescan_plan
 *	 Returns a ctescan plan for the base relation scanned by 'best_path'
 *	 with restriction clauses 'scan_clauses' and targetlist 'tlist'.
 */
static CteScan *
create_ctescan_plan(PlannerInfo *root, Path *best_path,
					List *tlist, List *scan_clauses)
{
	CteScan    *scan_plan;
	Index		scan_relid = best_path->parent->relid;
	RangeTblEntry *rte;
	SubPlan    *ctesplan = NULL;
	int			plan_id;
	int			cte_param_id;
	PlannerInfo *cteroot;
	Index		levelsup;
	int			ndx;
	ListCell   *lc;

	Assert(scan_relid > 0);
	rte = planner_rt_fetch(scan_relid, root);
	Assert(rte->rtekind == RTE_CTE);
	Assert(!rte->self_reference);

	/*
	 * Find the referenced CTE, and locate the SubPlan previously made for it.
	 */
	levelsup = rte->ctelevelsup;
	cteroot = root;
	while (levelsup-- > 0)
	{
		cteroot = cteroot->parent_root;
		if (!cteroot)			/* shouldn't happen */
			elog(ERROR, "bad levelsup for CTE \"%s\"", rte->ctename);
	}

	/*
	 * Note: cte_plan_ids can be shorter than cteList, if we are still working
	 * on planning the CTEs (ie, this is a side-reference from another CTE).
	 * So we mustn't use forboth here.
	 */
	ndx = 0;
	foreach(lc, cteroot->parse->cteList)
	{
		CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);

		if (strcmp(cte->ctename, rte->ctename) == 0)
			break;
		ndx++;
	}
	if (lc == NULL)				/* shouldn't happen */
		elog(ERROR, "could not find CTE \"%s\"", rte->ctename);
	if (ndx >= list_length(cteroot->cte_plan_ids))
		elog(ERROR, "could not find plan for CTE \"%s\"", rte->ctename);
	plan_id = list_nth_int(cteroot->cte_plan_ids, ndx);
	if (plan_id <= 0)
		elog(ERROR, "no plan was made for CTE \"%s\"", rte->ctename);
	foreach(lc, cteroot->init_plans)
	{
		ctesplan = (SubPlan *) lfirst(lc);
		if (ctesplan->plan_id == plan_id)
			break;
	}
	if (lc == NULL)				/* shouldn't happen */
		elog(ERROR, "could not find plan for CTE \"%s\"", rte->ctename);

	/*
	 * We need the CTE param ID, which is the sole member of the SubPlan's
	 * setParam list.
	 */
	cte_param_id = linitial_int(ctesplan->setParam);

	/* Sort clauses into best execution order */
	scan_clauses = order_qual_clauses(root, scan_clauses);

	/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
	scan_clauses = extract_actual_clauses(scan_clauses, false);

	/* Replace any outer-relation variables with nestloop params */
	if (best_path->param_info)
	{
		scan_clauses = (List *)
			replace_nestloop_params(root, (Node *) scan_clauses);
	}

	scan_plan = make_ctescan(tlist, scan_clauses, scan_relid,
							 plan_id, cte_param_id);

	copy_generic_path_info(&scan_plan->scan.plan, best_path);

	return scan_plan;
}

/*
 * create_namedtuplestorescan_plan
 *	 Returns a tuplestorescan plan for the base relation scanned by
 *	'best_path' with restriction clauses 'scan_clauses' and targetlist
 *	'tlist'.
 */
static NamedTuplestoreScan *
create_namedtuplestorescan_plan(PlannerInfo *root, Path *best_path,
								List *tlist, List *scan_clauses)
{
	NamedTuplestoreScan *scan_plan;
	Index		scan_relid = best_path->parent->relid;
	RangeTblEntry *rte;

	Assert(scan_relid > 0);
	rte = planner_rt_fetch(scan_relid, root);
	Assert(rte->rtekind == RTE_NAMEDTUPLESTORE);

	/* Sort clauses into best execution order */
	scan_clauses = order_qual_clauses(root, scan_clauses);

	/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
	scan_clauses = extract_actual_clauses(scan_clauses, false);

	/* Replace any outer-relation variables with nestloop params */
	if (best_path->param_info)
	{
		scan_clauses = (List *)
			replace_nestloop_params(root, (Node *) scan_clauses);
	}

	scan_plan = make_namedtuplestorescan(tlist, scan_clauses, scan_relid,
										 rte->enrname);

	copy_generic_path_info(&scan_plan->scan.plan, best_path);

	return scan_plan;
}

/*
 * create_resultscan_plan
 *	 Returns a Result plan for the RTE_RESULT base relation scanned by
 *	'best_path' with restriction clauses 'scan_clauses' and targetlist
 *	'tlist'.
 */
static Result *
create_resultscan_plan(PlannerInfo *root, Path *best_path,
					   List *tlist, List *scan_clauses)
{
	Result	   *scan_plan;
	Index		scan_relid = best_path->parent->relid;
	RangeTblEntry *rte PG_USED_FOR_ASSERTS_ONLY;

	Assert(scan_relid > 0);
	rte = planner_rt_fetch(scan_relid, root);
	Assert(rte->rtekind == RTE_RESULT);

	/* Sort clauses into best execution order */
	scan_clauses = order_qual_clauses(root, scan_clauses);

	/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
	scan_clauses = extract_actual_clauses(scan_clauses, false);

	/* Replace any outer-relation variables with nestloop params */
	if (best_path->param_info)
	{
		scan_clauses = (List *)
			replace_nestloop_params(root, (Node *) scan_clauses);
	}

	scan_plan = make_one_row_result(tlist, (Node *) scan_clauses,
									best_path->parent);

	copy_generic_path_info(&scan_plan->plan, best_path);

	return scan_plan;
}

/*
 * create_worktablescan_plan
 *	 Returns a worktablescan plan for the base relation scanned by 'best_path'
 *	 with restriction clauses 'scan_clauses' and targetlist 'tlist'.
 */
static WorkTableScan *
create_worktablescan_plan(PlannerInfo *root, Path *best_path,
						  List *tlist, List *scan_clauses)
{
	WorkTableScan *scan_plan;
	Index		scan_relid = best_path->parent->relid;
	RangeTblEntry *rte;
	Index		levelsup;
	PlannerInfo *cteroot;

	Assert(scan_relid > 0);
	rte = planner_rt_fetch(scan_relid, root);
	Assert(rte->rtekind == RTE_CTE);
	Assert(rte->self_reference);

	/*
	 * We need to find the worktable param ID, which is in the plan level
	 * that's processing the recursive UNION, which is one level *below* where
	 * the CTE comes from.
	 */
	levelsup = rte->ctelevelsup;
	if (levelsup == 0)			/* shouldn't happen */
		elog(ERROR, "bad levelsup for CTE \"%s\"", rte->ctename);
	levelsup--;
	cteroot = root;
	while (levelsup-- > 0)
	{
		cteroot = cteroot->parent_root;
		if (!cteroot)			/* shouldn't happen */
			elog(ERROR, "bad levelsup for CTE \"%s\"", rte->ctename);
	}
	if (cteroot->wt_param_id < 0)	/* shouldn't happen */
		elog(ERROR, "could not find param ID for CTE \"%s\"", rte->ctename);

	/* Sort clauses into best execution order */
	scan_clauses = order_qual_clauses(root, scan_clauses);

	/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
	scan_clauses = extract_actual_clauses(scan_clauses, false);

	/* Replace any outer-relation variables with nestloop params */
	if (best_path->param_info)
	{
		scan_clauses = (List *)
			replace_nestloop_params(root, (Node *) scan_clauses);
	}

	scan_plan = make_worktablescan(tlist, scan_clauses, scan_relid,
								   cteroot->wt_param_id);

	copy_generic_path_info(&scan_plan->scan.plan, best_path);

	return scan_plan;
}

/*
 * create_foreignscan_plan
 *	 Returns a foreignscan plan for the relation scanned by 'best_path'
 *	 with restriction clauses 'scan_clauses' and targetlist 'tlist'.
 */
static ForeignScan *
create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
						List *tlist, List *scan_clauses)
{
	ForeignScan *scan_plan;
	RelOptInfo *rel = best_path->path.parent;
	Index		scan_relid = rel->relid;
	Oid			rel_oid = InvalidOid;
	Plan	   *outer_plan = NULL;

	Assert(rel->fdwroutine != NULL);

	/* transform the child path if any */
	if (best_path->fdw_outerpath)
		outer_plan = create_plan_recurse(root, best_path->fdw_outerpath,
										 CP_EXACT_TLIST);

	/*
	 * If we're scanning a base relation, fetch its OID.  (Irrelevant if
	 * scanning a join relation.)
	 */
	if (scan_relid > 0)
	{
		RangeTblEntry *rte;

		Assert(rel->rtekind == RTE_RELATION);
		rte = planner_rt_fetch(scan_relid, root);
		Assert(rte->rtekind == RTE_RELATION);
		rel_oid = rte->relid;
	}

	/*
	 * Sort clauses into best execution order.  We do this first since the FDW
	 * might have more info than we do and wish to adjust the ordering.
	 */
	scan_clauses = order_qual_clauses(root, scan_clauses);

	/*
	 * Let the FDW perform its processing on the restriction clauses and
	 * generate the plan node.  Note that the FDW might remove restriction
	 * clauses that it intends to execute remotely, or even add more (if it
	 * has selected some join clauses for remote use but also wants them
	 * rechecked locally).
	 */
	scan_plan = rel->fdwroutine->GetForeignPlan(root, rel, rel_oid,
												best_path,
												tlist, scan_clauses,
												outer_plan);

	/* Copy cost data from Path to Plan; no need to make FDW do this */
	copy_generic_path_info(&scan_plan->scan.plan, &best_path->path);

	/* Copy user OID to access as; likewise no need to make FDW do this */
	scan_plan->checkAsUser = rel->userid;

	/* Copy foreign server OID; likewise, no need to make FDW do this */
	scan_plan->fs_server = rel->serverid;

	/*
	 * Likewise, copy the relids that are represented by this foreign scan. An
	 * upper rel doesn't have relids set, but it covers all the relations
	 * participating in the underlying scan/join, so use root->all_query_rels.
	 */
	if (rel->reloptkind == RELOPT_UPPER_REL)
		scan_plan->fs_relids = root->all_query_rels;
	else
		scan_plan->fs_relids = best_path->path.parent->relids;

	/*
	 * Join relid sets include relevant outer joins, but FDWs may need to know
	 * which are the included base rels.  That's a bit tedious to get without
	 * access to the plan-time data structures, so compute it here.
	 */
	scan_plan->fs_base_relids = bms_difference(scan_plan->fs_relids,
											   root->outer_join_rels);

	/*
	 * If this is a foreign join, and to make it valid to push down we had to
	 * assume that the current user is the same as some user explicitly named
	 * in the query, mark the finished plan as depending on the current user.
	 */
	if (rel->useridiscurrent)
		root->glob->dependsOnRole = true;

	/*
	 * Replace any outer-relation variables with nestloop params in the qual,
	 * fdw_exprs and fdw_recheck_quals expressions.  We do this last so that
	 * the FDW doesn't have to be involved.  (Note that parts of fdw_exprs or
	 * fdw_recheck_quals could have come from join clauses, so doing this
	 * beforehand on the scan_clauses wouldn't work.)  We assume
	 * fdw_scan_tlist contains no such variables.
	 */
	if (best_path->path.param_info)
	{
		scan_plan->scan.plan.qual = (List *)
			replace_nestloop_params(root, (Node *) scan_plan->scan.plan.qual);
		scan_plan->fdw_exprs = (List *)
			replace_nestloop_params(root, (Node *) scan_plan->fdw_exprs);
		scan_plan->fdw_recheck_quals = (List *)
			replace_nestloop_params(root,
									(Node *) scan_plan->fdw_recheck_quals);
	}

	/*
	 * If rel is a base relation, detect whether any system columns are
	 * requested from the rel.  (If rel is a join relation, rel->relid will be
	 * 0, but there can be no Var with relid 0 in the rel's targetlist or the
	 * restriction clauses, so we skip this in that case.  Note that any such
	 * columns in base relations that were joined are assumed to be contained
	 * in fdw_scan_tlist.)	This is a bit of a kluge and might go away
	 * someday, so we intentionally leave it out of the API presented to FDWs.
	 */
	scan_plan->fsSystemCol = false;
	if (scan_relid > 0)
	{
		Bitmapset  *attrs_used = NULL;
		ListCell   *lc;
		int			i;

		/*
		 * First, examine all the attributes needed for joins or final output.
		 * Note: we must look at rel's targetlist, not the attr_needed data,
		 * because attr_needed isn't computed for inheritance child rels.
		 */
		pull_varattnos((Node *) rel->reltarget->exprs, scan_relid, &attrs_used);

		/* Add all the attributes used by restriction clauses. */
		foreach(lc, rel->baserestrictinfo)
		{
			RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);

			pull_varattnos((Node *) rinfo->clause, scan_relid, &attrs_used);
		}

		/* Now, are any system columns requested from rel? */
		for (i = FirstLowInvalidHeapAttributeNumber + 1; i < 0; i++)
		{
			if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber, attrs_used))
			{
				scan_plan->fsSystemCol = true;
				break;
			}
		}

		bms_free(attrs_used);
	}

	return scan_plan;
}

/*
 * create_customscan_plan
 *
 * Transform a CustomPath into a Plan.
 */
static CustomScan *
create_customscan_plan(PlannerInfo *root, CustomPath *best_path,
					   List *tlist, List *scan_clauses)
{
	CustomScan *cplan;
	RelOptInfo *rel = best_path->path.parent;
	List	   *custom_plans = NIL;
	ListCell   *lc;

	/* Recursively transform child paths. */
	foreach(lc, best_path->custom_paths)
	{
		Plan	   *plan = create_plan_recurse(root, (Path *) lfirst(lc),
											   CP_EXACT_TLIST);

		custom_plans = lappend(custom_plans, plan);
	}

	/*
	 * Sort clauses into the best execution order, although custom-scan
	 * provider can reorder them again.
	 */
	scan_clauses = order_qual_clauses(root, scan_clauses);

	/*
	 * Invoke custom plan provider to create the Plan node represented by the
	 * CustomPath.
	 */
	cplan = castNode(CustomScan,
					 best_path->methods->PlanCustomPath(root,
														rel,
														best_path,
														tlist,
														scan_clauses,
														custom_plans));

	/*
	 * Copy cost data from Path to Plan; no need to make custom-plan providers
	 * do this
	 */
	copy_generic_path_info(&cplan->scan.plan, &best_path->path);

	/* Likewise, copy the relids that are represented by this custom scan */
	cplan->custom_relids = best_path->path.parent->relids;

	/*
	 * Replace any outer-relation variables with nestloop params in the qual
	 * and custom_exprs expressions.  We do this last so that the custom-plan
	 * provider doesn't have to be involved.  (Note that parts of custom_exprs
	 * could have come from join clauses, so doing this beforehand on the
	 * scan_clauses wouldn't work.)  We assume custom_scan_tlist contains no
	 * such variables.
	 */
	if (best_path->path.param_info)
	{
		cplan->scan.plan.qual = (List *)
			replace_nestloop_params(root, (Node *) cplan->scan.plan.qual);
		cplan->custom_exprs = (List *)
			replace_nestloop_params(root, (Node *) cplan->custom_exprs);
	}

	return cplan;
}


/*****************************************************************************
 *
 *	JOIN METHODS
 *
 *****************************************************************************/

static NestLoop *
create_nestloop_plan(PlannerInfo *root,
					 NestPath *best_path)
{
	NestLoop   *join_plan;
	Plan	   *outer_plan;
	Plan	   *inner_plan;
	Relids		outerrelids;
	List	   *tlist = build_path_tlist(root, &best_path->jpath.path);
	List	   *joinrestrictclauses = best_path->jpath.joinrestrictinfo;
	List	   *joinclauses;
	List	   *otherclauses;
	List	   *nestParams;
	List	   *outer_tlist;
	bool		outer_parallel_safe;
	Relids		saveOuterRels = root->curOuterRels;
	ListCell   *lc;

	/*
	 * If the inner path is parameterized by the topmost parent of the outer
	 * rel rather than the outer rel itself, fix that.  (Nothing happens here
	 * if it is not so parameterized.)
	 */
	best_path->jpath.innerjoinpath =
		reparameterize_path_by_child(root,
									 best_path->jpath.innerjoinpath,
									 best_path->jpath.outerjoinpath->parent);

	/*
	 * Failure here probably means that reparameterize_path_by_child() is not
	 * in sync with path_is_reparameterizable_by_child().
	 */
	Assert(best_path->jpath.innerjoinpath != NULL);

	/* NestLoop can project, so no need to be picky about child tlists */
	outer_plan = create_plan_recurse(root, best_path->jpath.outerjoinpath, 0);

	/* For a nestloop, include outer relids in curOuterRels for inner side */
	outerrelids = best_path->jpath.outerjoinpath->parent->relids;
	root->curOuterRels = bms_union(root->curOuterRels, outerrelids);

	inner_plan = create_plan_recurse(root, best_path->jpath.innerjoinpath, 0);

	/* Restore curOuterRels */
	bms_free(root->curOuterRels);
	root->curOuterRels = saveOuterRels;

	/* Sort join qual clauses into best execution order */
	joinrestrictclauses = order_qual_clauses(root, joinrestrictclauses);

	/* Get the join qual clauses (in plain expression form) */
	/* Any pseudoconstant clauses are ignored here */
	if (IS_OUTER_JOIN(best_path->jpath.jointype))
	{
		extract_actual_join_clauses(joinrestrictclauses,
									best_path->jpath.path.parent->relids,
									&joinclauses, &otherclauses);
	}
	else
	{
		/* We can treat all clauses alike for an inner join */
		joinclauses = extract_actual_clauses(joinrestrictclauses, false);
		otherclauses = NIL;
	}

	/* Replace any outer-relation variables with nestloop params */
	if (best_path->jpath.path.param_info)
	{
		joinclauses = (List *)
			replace_nestloop_params(root, (Node *) joinclauses);
		otherclauses = (List *)
			replace_nestloop_params(root, (Node *) otherclauses);
	}

	/*
	 * Identify any nestloop parameters that should be supplied by this join
	 * node, and remove them from root->curOuterParams.
	 */
	nestParams = identify_current_nestloop_params(root,
												  outerrelids,
												  PATH_REQ_OUTER((Path *) best_path));

	/*
	 * While nestloop parameters that are Vars had better be available from
	 * the outer_plan already, there are edge cases where nestloop parameters
	 * that are PHVs won't be.  In such cases we must add them to the
	 * outer_plan's tlist, since the executor's NestLoopParam machinery
	 * requires the params to be simple outer-Var references to that tlist.
	 * (This is cheating a little bit, because the outer path's required-outer
	 * relids might not be enough to allow evaluating such a PHV.  But in
	 * practice, if we could have evaluated the PHV at the nestloop node, we
	 * can do so in the outer plan too.)
	 */
	outer_tlist = outer_plan->targetlist;
	outer_parallel_safe = outer_plan->parallel_safe;
	foreach(lc, nestParams)
	{
		NestLoopParam *nlp = (NestLoopParam *) lfirst(lc);
		PlaceHolderVar *phv;
		TargetEntry *tle;

		if (IsA(nlp->paramval, Var))
			continue;			/* nothing to do for simple Vars */
		/* Otherwise it must be a PHV */
		phv = castNode(PlaceHolderVar, nlp->paramval);

		if (tlist_member((Expr *) phv, outer_tlist))
			continue;			/* already available */

		/*
		 * It's possible that nestloop parameter PHVs selected to evaluate
		 * here contain references to surviving root->curOuterParams items
		 * (that is, they reference values that will be supplied by some
		 * higher-level nestloop).  Those need to be converted to Params now.
		 * Note: it's safe to do this after the tlist_member() check, because
		 * equal() won't pay attention to phv->phexpr.
		 */
		phv->phexpr = (Expr *) replace_nestloop_params(root,
													   (Node *) phv->phexpr);

		/* Make a shallow copy of outer_tlist, if we didn't already */
		if (outer_tlist == outer_plan->targetlist)
			outer_tlist = list_copy(outer_tlist);
		/* ... and add the needed expression */
		tle = makeTargetEntry((Expr *) copyObject(phv),
							  list_length(outer_tlist) + 1,
							  NULL,
							  true);
		outer_tlist = lappend(outer_tlist, tle);
		/* ... and track whether tlist is (still) parallel-safe */
		if (outer_parallel_safe)
			outer_parallel_safe = is_parallel_safe(root, (Node *) phv);
	}
	if (outer_tlist != outer_plan->targetlist)
		outer_plan = change_plan_targetlist(outer_plan, outer_tlist,
											outer_parallel_safe);

	/* And finally, we can build the join plan node */
	join_plan = make_nestloop(tlist,
							  joinclauses,
							  otherclauses,
							  nestParams,
							  outer_plan,
							  inner_plan,
							  best_path->jpath.jointype,
							  best_path->jpath.inner_unique);

	copy_generic_path_info(&join_plan->join.plan, &best_path->jpath.path);

	return join_plan;
}

static MergeJoin *
create_mergejoin_plan(PlannerInfo *root,
					  MergePath *best_path)
{
	MergeJoin  *join_plan;
	Plan	   *outer_plan;
	Plan	   *inner_plan;
	List	   *tlist = build_path_tlist(root, &best_path->jpath.path);
	List	   *joinclauses;
	List	   *otherclauses;
	List	   *mergeclauses;
	List	   *outerpathkeys;
	List	   *innerpathkeys;
	int			nClauses;
	Oid		   *mergefamilies;
	Oid		   *mergecollations;
	bool	   *mergereversals;
	bool	   *mergenullsfirst;
	PathKey    *opathkey;
	EquivalenceClass *opeclass;
	int			i;
	ListCell   *lc;
	ListCell   *lop;
	ListCell   *lip;
	Path	   *outer_path = best_path->jpath.outerjoinpath;
	Path	   *inner_path = best_path->jpath.innerjoinpath;

	/*
	 * MergeJoin can project, so we don't have to demand exact tlists from the
	 * inputs.  However, if we're intending to sort an input's result, it's
	 * best to request a small tlist so we aren't sorting more data than
	 * necessary.
	 */
	outer_plan = create_plan_recurse(root, best_path->jpath.outerjoinpath,
									 (best_path->outersortkeys != NIL) ? CP_SMALL_TLIST : 0);

	inner_plan = create_plan_recurse(root, best_path->jpath.innerjoinpath,
									 (best_path->innersortkeys != NIL) ? CP_SMALL_TLIST : 0);

	/* Sort join qual clauses into best execution order */
	/* NB: do NOT reorder the mergeclauses */
	joinclauses = order_qual_clauses(root, best_path->jpath.joinrestrictinfo);

	/* Get the join qual clauses (in plain expression form) */
	/* Any pseudoconstant clauses are ignored here */
	if (IS_OUTER_JOIN(best_path->jpath.jointype))
	{
		extract_actual_join_clauses(joinclauses,
									best_path->jpath.path.parent->relids,
									&joinclauses, &otherclauses);
	}
	else
	{
		/* We can treat all clauses alike for an inner join */
		joinclauses = extract_actual_clauses(joinclauses, false);
		otherclauses = NIL;
	}

	/*
	 * Remove the mergeclauses from the list of join qual clauses, leaving the
	 * list of quals that must be checked as qpquals.
	 */
	mergeclauses = get_actual_clauses(best_path->path_mergeclauses);
	joinclauses = list_difference(joinclauses, mergeclauses);

	/*
	 * Replace any outer-relation variables with nestloop params.  There
	 * should not be any in the mergeclauses.
	 */
	if (best_path->jpath.path.param_info)
	{
		joinclauses = (List *)
			replace_nestloop_params(root, (Node *) joinclauses);
		otherclauses = (List *)
			replace_nestloop_params(root, (Node *) otherclauses);
	}

	/*
	 * Rearrange mergeclauses, if needed, so that the outer variable is always
	 * on the left; mark the mergeclause restrictinfos with correct
	 * outer_is_left status.
	 */
	mergeclauses = get_switched_clauses(best_path->path_mergeclauses,
										best_path->jpath.outerjoinpath->parent->relids);

	/*
	 * Create explicit sort nodes for the outer and inner paths if necessary.
	 */
	if (best_path->outersortkeys)
	{
		Relids		outer_relids = outer_path->parent->relids;
		Plan	   *sort_plan;

		/*
		 * We can assert that the outer path is not already ordered
		 * appropriately for the mergejoin; otherwise, outersortkeys would
		 * have been set to NIL.
		 */
		Assert(!pathkeys_contained_in(best_path->outersortkeys,
									  outer_path->pathkeys));

		/*
		 * We choose to use incremental sort if it is enabled and there are
		 * presorted keys; otherwise we use full sort.
		 */
		if (enable_incremental_sort && best_path->outer_presorted_keys > 0)
		{
			sort_plan = (Plan *)
				make_incrementalsort_from_pathkeys(outer_plan,
												   best_path->outersortkeys,
												   outer_relids,
												   best_path->outer_presorted_keys);

			label_incrementalsort_with_costsize(root,
												(IncrementalSort *) sort_plan,
												best_path->outersortkeys,
												-1.0);
		}
		else
		{
			sort_plan = (Plan *)
				make_sort_from_pathkeys(outer_plan,
										best_path->outersortkeys,
										outer_relids);

			label_sort_with_costsize(root, (Sort *) sort_plan, -1.0);
		}

		outer_plan = sort_plan;
		outerpathkeys = best_path->outersortkeys;
	}
	else
		outerpathkeys = best_path->jpath.outerjoinpath->pathkeys;

	if (best_path->innersortkeys)
	{
		/*
		 * We do not consider incremental sort for inner path, because
		 * incremental sort does not support mark/restore.
		 */

		Relids		inner_relids = inner_path->parent->relids;
		Sort	   *sort;

		/*
		 * We can assert that the inner path is not already ordered
		 * appropriately for the mergejoin; otherwise, innersortkeys would
		 * have been set to NIL.
		 */
		Assert(!pathkeys_contained_in(best_path->innersortkeys,
									  inner_path->pathkeys));

		sort = make_sort_from_pathkeys(inner_plan,
									   best_path->innersortkeys,
									   inner_relids);

		label_sort_with_costsize(root, sort, -1.0);
		inner_plan = (Plan *) sort;
		innerpathkeys = best_path->innersortkeys;
	}
	else
		innerpathkeys = best_path->jpath.innerjoinpath->pathkeys;

	/*
	 * If specified, add a materialize node to shield the inner plan from the
	 * need to handle mark/restore.
	 */
	if (best_path->materialize_inner)
	{
		Plan	   *matplan = (Plan *) make_material(inner_plan);

		/*
		 * We assume the materialize will not spill to disk, and therefore
		 * charge just cpu_operator_cost per tuple.  (Keep this estimate in
		 * sync with final_cost_mergejoin.)
		 */
		copy_plan_costsize(matplan, inner_plan);
		matplan->total_cost += cpu_operator_cost * matplan->plan_rows;

		inner_plan = matplan;
	}

	/*
	 * Compute the opfamily/collation/strategy/nullsfirst arrays needed by the
	 * executor.  The information is in the pathkeys for the two inputs, but
	 * we need to be careful about the possibility of mergeclauses sharing a
	 * pathkey, as well as the possibility that the inner pathkeys are not in
	 * an order matching the mergeclauses.
	 */
	nClauses = list_length(mergeclauses);
	Assert(nClauses == list_length(best_path->path_mergeclauses));
	mergefamilies = (Oid *) palloc(nClauses * sizeof(Oid));
	mergecollations = (Oid *) palloc(nClauses * sizeof(Oid));
	mergereversals = (bool *) palloc(nClauses * sizeof(bool));
	mergenullsfirst = (bool *) palloc(nClauses * sizeof(bool));

	opathkey = NULL;
	opeclass = NULL;
	lop = list_head(outerpathkeys);
	lip = list_head(innerpathkeys);
	i = 0;
	foreach(lc, best_path->path_mergeclauses)
	{
		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
		EquivalenceClass *oeclass;
		EquivalenceClass *ieclass;
		PathKey    *ipathkey = NULL;
		EquivalenceClass *ipeclass = NULL;
		bool		first_inner_match = false;

		/* fetch outer/inner eclass from mergeclause */
		if (rinfo->outer_is_left)
		{
			oeclass = rinfo->left_ec;
			ieclass = rinfo->right_ec;
		}
		else
		{
			oeclass = rinfo->right_ec;
			ieclass = rinfo->left_ec;
		}
		Assert(oeclass != NULL);
		Assert(ieclass != NULL);

		/*
		 * We must identify the pathkey elements associated with this clause
		 * by matching the eclasses (which should give a unique match, since
		 * the pathkey lists should be canonical).  In typical cases the merge
		 * clauses are one-to-one with the pathkeys, but when dealing with
		 * partially redundant query conditions, things are more complicated.
		 *
		 * lop and lip reference the first as-yet-unmatched pathkey elements.
		 * If they're NULL then all pathkey elements have been matched.
		 *
		 * The ordering of the outer pathkeys should match the mergeclauses,
		 * by construction (see find_mergeclauses_for_outer_pathkeys()). There
		 * could be more than one mergeclause for the same outer pathkey, but
		 * no pathkey may be entirely skipped over.
		 */
		if (oeclass != opeclass)	/* multiple matches are not interesting */
		{
			/* doesn't match the current opathkey, so must match the next */
			if (lop == NULL)
				elog(ERROR, "outer pathkeys do not match mergeclauses");
			opathkey = (PathKey *) lfirst(lop);
			opeclass = opathkey->pk_eclass;
			lop = lnext(outerpathkeys, lop);
			if (oeclass != opeclass)
				elog(ERROR, "outer pathkeys do not match mergeclauses");
		}

		/*
		 * The inner pathkeys likewise should not have skipped-over keys, but
		 * it's possible for a mergeclause to reference some earlier inner
		 * pathkey if we had redundant pathkeys.  For example we might have
		 * mergeclauses like "o.a = i.x AND o.b = i.y AND o.c = i.x".  The
		 * implied inner ordering is then "ORDER BY x, y, x", but the pathkey
		 * mechanism drops the second sort by x as redundant, and this code
		 * must cope.
		 *
		 * It's also possible for the implied inner-rel ordering to be like
		 * "ORDER BY x, y, x DESC".  We still drop the second instance of x as
		 * redundant; but this means that the sort ordering of a redundant
		 * inner pathkey should not be considered significant.  So we must
		 * detect whether this is the first clause matching an inner pathkey.
		 */
		if (lip)
		{
			ipathkey = (PathKey *) lfirst(lip);
			ipeclass = ipathkey->pk_eclass;
			if (ieclass == ipeclass)
			{
				/* successful first match to this inner pathkey */
				lip = lnext(innerpathkeys, lip);
				first_inner_match = true;
			}
		}
		if (!first_inner_match)
		{
			/* redundant clause ... must match something before lip */
			ListCell   *l2;

			foreach(l2, innerpathkeys)
			{
				if (l2 == lip)
					break;
				ipathkey = (PathKey *) lfirst(l2);
				ipeclass = ipathkey->pk_eclass;
				if (ieclass == ipeclass)
					break;
			}
			if (ieclass != ipeclass)
				elog(ERROR, "inner pathkeys do not match mergeclauses");
		}

		/*
		 * The pathkeys should always match each other as to opfamily and
		 * collation (which affect equality), but if we're considering a
		 * redundant inner pathkey, its sort ordering might not match.  In
		 * such cases we may ignore the inner pathkey's sort ordering and use
		 * the outer's.  (In effect, we're lying to the executor about the
		 * sort direction of this inner column, but it does not matter since
		 * the run-time row comparisons would only reach this column when
		 * there's equality for the earlier column containing the same eclass.
		 * There could be only one value in this column for the range of inner
		 * rows having a given value in the earlier column, so it does not
		 * matter which way we imagine this column to be ordered.)  But a
		 * non-redundant inner pathkey had better match outer's ordering too.
		 */
		if (opathkey->pk_opfamily != ipathkey->pk_opfamily ||
			opathkey->pk_eclass->ec_collation != ipathkey->pk_eclass->ec_collation)
			elog(ERROR, "left and right pathkeys do not match in mergejoin");
		if (first_inner_match &&
			(opathkey->pk_cmptype != ipathkey->pk_cmptype ||
			 opathkey->pk_nulls_first != ipathkey->pk_nulls_first))
			elog(ERROR, "left and right pathkeys do not match in mergejoin");

		/* OK, save info for executor */
		mergefamilies[i] = opathkey->pk_opfamily;
		mergecollations[i] = opathkey->pk_eclass->ec_collation;
		mergereversals[i] = (opathkey->pk_cmptype == COMPARE_GT ? true : false);
		mergenullsfirst[i] = opathkey->pk_nulls_first;
		i++;
	}

	/*
	 * Note: it is not an error if we have additional pathkey elements (i.e.,
	 * lop or lip isn't NULL here).  The input paths might be better-sorted
	 * than we need for the current mergejoin.
	 */

	/*
	 * Now we can build the mergejoin node.
	 */
	join_plan = make_mergejoin(tlist,
							   joinclauses,
							   otherclauses,
							   mergeclauses,
							   mergefamilies,
							   mergecollations,
							   mergereversals,
							   mergenullsfirst,
							   outer_plan,
							   inner_plan,
							   best_path->jpath.jointype,
							   best_path->jpath.inner_unique,
							   best_path->skip_mark_restore);

	/* Costs of sort and material steps are included in path cost already */
	copy_generic_path_info(&join_plan->join.plan, &best_path->jpath.path);

	return join_plan;
}

static HashJoin *
create_hashjoin_plan(PlannerInfo *root,
					 HashPath *best_path)
{
	HashJoin   *join_plan;
	Hash	   *hash_plan;
	Plan	   *outer_plan;
	Plan	   *inner_plan;
	List	   *tlist = build_path_tlist(root, &best_path->jpath.path);
	List	   *joinclauses;
	List	   *otherclauses;
	List	   *hashclauses;
	List	   *hashoperators = NIL;
	List	   *hashcollations = NIL;
	List	   *inner_hashkeys = NIL;
	List	   *outer_hashkeys = NIL;
	Oid			skewTable = InvalidOid;
	AttrNumber	skewColumn = InvalidAttrNumber;
	bool		skewInherit = false;
	ListCell   *lc;

	/*
	 * HashJoin can project, so we don't have to demand exact tlists from the
	 * inputs.  However, it's best to request a small tlist from the inner
	 * side, so that we aren't storing more data than necessary.  Likewise, if
	 * we anticipate batching, request a small tlist from the outer side so
	 * that we don't put extra data in the outer batch files.
	 */
	outer_plan = create_plan_recurse(root, best_path->jpath.outerjoinpath,
									 (best_path->num_batches > 1) ? CP_SMALL_TLIST : 0);

	inner_plan = create_plan_recurse(root, best_path->jpath.innerjoinpath,
									 CP_SMALL_TLIST);

	/* Sort join qual clauses into best execution order */
	joinclauses = order_qual_clauses(root, best_path->jpath.joinrestrictinfo);
	/* There's no point in sorting the hash clauses ... */

	/* Get the join qual clauses (in plain expression form) */
	/* Any pseudoconstant clauses are ignored here */
	if (IS_OUTER_JOIN(best_path->jpath.jointype))
	{
		extract_actual_join_clauses(joinclauses,
									best_path->jpath.path.parent->relids,
									&joinclauses, &otherclauses);
	}
	else
	{
		/* We can treat all clauses alike for an inner join */
		joinclauses = extract_actual_clauses(joinclauses, false);
		otherclauses = NIL;
	}

	/*
	 * Remove the hashclauses from the list of join qual clauses, leaving the
	 * list of quals that must be checked as qpquals.
	 */
	hashclauses = get_actual_clauses(best_path->path_hashclauses);
	joinclauses = list_difference(joinclauses, hashclauses);

	/*
	 * Replace any outer-relation variables with nestloop params.  There
	 * should not be any in the hashclauses.
	 */
	if (best_path->jpath.path.param_info)
	{
		joinclauses = (List *)
			replace_nestloop_params(root, (Node *) joinclauses);
		otherclauses = (List *)
			replace_nestloop_params(root, (Node *) otherclauses);
	}

	/*
	 * Rearrange hashclauses, if needed, so that the outer variable is always
	 * on the left.
	 */
	hashclauses = get_switched_clauses(best_path->path_hashclauses,
									   best_path->jpath.outerjoinpath->parent->relids);

	/*
	 * If there is a single join clause and we can identify the outer variable
	 * as a simple column reference, supply its identity for possible use in
	 * skew optimization.  (Note: in principle we could do skew optimization
	 * with multiple join clauses, but we'd have to be able to determine the
	 * most common combinations of outer values, which we don't currently have
	 * enough stats for.)
	 */
	if (list_length(hashclauses) == 1)
	{
		OpExpr	   *clause = (OpExpr *) linitial(hashclauses);
		Node	   *node;

		Assert(is_opclause(clause));
		node = (Node *) linitial(clause->args);
		if (IsA(node, RelabelType))
			node = (Node *) ((RelabelType *) node)->arg;
		if (IsA(node, Var))
		{
			Var		   *var = (Var *) node;
			RangeTblEntry *rte;

			rte = root->simple_rte_array[var->varno];
			if (rte->rtekind == RTE_RELATION)
			{
				skewTable = rte->relid;
				skewColumn = var->varattno;
				skewInherit = rte->inh;
			}
		}
	}

	/*
	 * Collect hash related information. The hashed expressions are
	 * deconstructed into outer/inner expressions, so they can be computed
	 * separately (inner expressions are used to build the hashtable via Hash,
	 * outer expressions to perform lookups of tuples from HashJoin's outer
	 * plan in the hashtable). Also collect operator information necessary to
	 * build the hashtable.
	 */
	foreach(lc, hashclauses)
	{
		OpExpr	   *hclause = lfirst_node(OpExpr, lc);

		hashoperators = lappend_oid(hashoperators, hclause->opno);
		hashcollations = lappend_oid(hashcollations, hclause->inputcollid);
		outer_hashkeys = lappend(outer_hashkeys, linitial(hclause->args));
		inner_hashkeys = lappend(inner_hashkeys, lsecond(hclause->args));
	}

	/*
	 * Build the hash node and hash join node.
	 */
	hash_plan = make_hash(inner_plan,
						  inner_hashkeys,
						  skewTable,
						  skewColumn,
						  skewInherit);

	/*
	 * Set Hash node's startup & total costs equal to total cost of input
	 * plan; this only affects EXPLAIN display not decisions.
	 */
	copy_plan_costsize(&hash_plan->plan, inner_plan);
	hash_plan->plan.startup_cost = hash_plan->plan.total_cost;

	/*
	 * If parallel-aware, the executor will also need an estimate of the total
	 * number of rows expected from all participants so that it can size the
	 * shared hash table.
	 */
	if (best_path->jpath.path.parallel_aware)
	{
		hash_plan->plan.parallel_aware = true;
		hash_plan->rows_total = best_path->inner_rows_total;
	}

	join_plan = make_hashjoin(tlist,
							  joinclauses,
							  otherclauses,
							  hashclauses,
							  hashoperators,
							  hashcollations,
							  outer_hashkeys,
							  outer_plan,
							  (Plan *) hash_plan,
							  best_path->jpath.jointype,
							  best_path->jpath.inner_unique);

	copy_generic_path_info(&join_plan->join.plan, &best_path->jpath.path);

	return join_plan;
}


/*****************************************************************************
 *
 *	SUPPORTING ROUTINES
 *
 *****************************************************************************/

/*
 * replace_nestloop_params
 *	  Replace outer-relation Vars and PlaceHolderVars in the given expression
 *	  with nestloop Params
 *
 * All Vars and PlaceHolderVars belonging to the relation(s) identified by
 * root->curOuterRels are replaced by Params, and entries are added to
 * root->curOuterParams if not already present.
 */
static Node *
replace_nestloop_params(PlannerInfo *root, Node *expr)
{
	/* No setup needed for tree walk, so away we go */
	return replace_nestloop_params_mutator(expr, root);
}

static Node *
replace_nestloop_params_mutator(Node *node, PlannerInfo *root)
{
	if (node == NULL)
		return NULL;
	if (IsA(node, Var))
	{
		Var		   *var = (Var *) node;

		/* Upper-level Vars should be long gone at this point */
		Assert(var->varlevelsup == 0);
		/* If not to be replaced, we can just return the Var unmodified */
		if (IS_SPECIAL_VARNO(var->varno) ||
			!bms_is_member(var->varno, root->curOuterRels))
			return node;
		/* Replace the Var with a nestloop Param */
		return (Node *) replace_nestloop_param_var(root, var);
	}
	if (IsA(node, PlaceHolderVar))
	{
		PlaceHolderVar *phv = (PlaceHolderVar *) node;

		/* Upper-level PlaceHolderVars should be long gone at this point */
		Assert(phv->phlevelsup == 0);

		/* Check whether we need to replace the PHV */
		if (!bms_is_subset(find_placeholder_info(root, phv)->ph_eval_at,
						   root->curOuterRels))
		{
			/*
			 * We can't replace the whole PHV, but we might still need to
			 * replace Vars or PHVs within its expression, in case it ends up
			 * actually getting evaluated here.  (It might get evaluated in
			 * this plan node, or some child node; in the latter case we don't
			 * really need to process the expression here, but we haven't got
			 * enough info to tell if that's the case.)  Flat-copy the PHV
			 * node and then recurse on its expression.
			 *
			 * Note that after doing this, we might have different
			 * representations of the contents of the same PHV in different
			 * parts of the plan tree.  This is OK because equal() will just
			 * match on phid/phlevelsup, so setrefs.c will still recognize an
			 * upper-level reference to a lower-level copy of the same PHV.
			 */
			PlaceHolderVar *newphv = makeNode(PlaceHolderVar);

			memcpy(newphv, phv, sizeof(PlaceHolderVar));
			newphv->phexpr = (Expr *)
				replace_nestloop_params_mutator((Node *) phv->phexpr,
												root);
			return (Node *) newphv;
		}
		/* Replace the PlaceHolderVar with a nestloop Param */
		return (Node *) replace_nestloop_param_placeholdervar(root, phv);
	}
	return expression_tree_mutator(node, replace_nestloop_params_mutator, root);
}

/*
 * fix_indexqual_references
 *	  Adjust indexqual clauses to the form the executor's indexqual
 *	  machinery needs.
 *
 * We have three tasks here:
 *	* Select the actual qual clauses out of the input IndexClause list,
 *	  and remove RestrictInfo nodes from the qual clauses.
 *	* Replace any outer-relation Var or PHV nodes with nestloop Params.
 *	  (XXX eventually, that responsibility should go elsewhere?)
 *	* Index keys must be represented by Var nodes with varattno set to the
 *	  index's attribute number, not the attribute number in the original rel.
 *
 * *stripped_indexquals_p receives a list of the actual qual clauses.
 *
 * *fixed_indexquals_p receives a list of the adjusted quals.  This is a copy
 * that shares no substructure with the original; this is needed in case there
 * are subplans in it (we need two separate copies of the subplan tree, or
 * things will go awry).
 */
static void
fix_indexqual_references(PlannerInfo *root, IndexPath *index_path,
						 List **stripped_indexquals_p, List **fixed_indexquals_p)
{
	IndexOptInfo *index = index_path->indexinfo;
	List	   *stripped_indexquals;
	List	   *fixed_indexquals;
	ListCell   *lc;

	stripped_indexquals = fixed_indexquals = NIL;

	foreach(lc, index_path->indexclauses)
	{
		IndexClause *iclause = lfirst_node(IndexClause, lc);
		int			indexcol = iclause->indexcol;
		ListCell   *lc2;

		foreach(lc2, iclause->indexquals)
		{
			RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc2);
			Node	   *clause = (Node *) rinfo->clause;

			stripped_indexquals = lappend(stripped_indexquals, clause);
			clause = fix_indexqual_clause(root, index, indexcol,
										  clause, iclause->indexcols);
			fixed_indexquals = lappend(fixed_indexquals, clause);
		}
	}

	*stripped_indexquals_p = stripped_indexquals;
	*fixed_indexquals_p = fixed_indexquals;
}

/*
 * fix_indexorderby_references
 *	  Adjust indexorderby clauses to the form the executor's index
 *	  machinery needs.
 *
 * This is a simplified version of fix_indexqual_references.  The input is
 * bare clauses and a separate indexcol list, instead of IndexClauses.
 */
static List *
fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path)
{
	IndexOptInfo *index = index_path->indexinfo;
	List	   *fixed_indexorderbys;
	ListCell   *lcc,
			   *lci;

	fixed_indexorderbys = NIL;

	forboth(lcc, index_path->indexorderbys, lci, index_path->indexorderbycols)
	{
		Node	   *clause = (Node *) lfirst(lcc);
		int			indexcol = lfirst_int(lci);

		clause = fix_indexqual_clause(root, index, indexcol, clause, NIL);
		fixed_indexorderbys = lappend(fixed_indexorderbys, clause);
	}

	return fixed_indexorderbys;
}

/*
 * fix_indexqual_clause
 *	  Convert a single indexqual clause to the form needed by the executor.
 *
 * We replace nestloop params here, and replace the index key variables
 * or expressions by index Var nodes.
 */
static Node *
fix_indexqual_clause(PlannerInfo *root, IndexOptInfo *index, int indexcol,
					 Node *clause, List *indexcolnos)
{
	/*
	 * Replace any outer-relation variables with nestloop params.
	 *
	 * This also makes a copy of the clause, so it's safe to modify it
	 * in-place below.
	 */
	clause = replace_nestloop_params(root, clause);

	if (IsA(clause, OpExpr))
	{
		OpExpr	   *op = (OpExpr *) clause;

		/* Replace the indexkey expression with an index Var. */
		linitial(op->args) = fix_indexqual_operand(linitial(op->args),
												   index,
												   indexcol);
	}
	else if (IsA(clause, RowCompareExpr))
	{
		RowCompareExpr *rc = (RowCompareExpr *) clause;
		ListCell   *lca,
				   *lcai;

		/* Replace the indexkey expressions with index Vars. */
		Assert(list_length(rc->largs) == list_length(indexcolnos));
		forboth(lca, rc->largs, lcai, indexcolnos)
		{
			lfirst(lca) = fix_indexqual_operand(lfirst(lca),
												index,
												lfirst_int(lcai));
		}
	}
	else if (IsA(clause, ScalarArrayOpExpr))
	{
		ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;

		/* Replace the indexkey expression with an index Var. */
		linitial(saop->args) = fix_indexqual_operand(linitial(saop->args),
													 index,
													 indexcol);
	}
	else if (IsA(clause, NullTest))
	{
		NullTest   *nt = (NullTest *) clause;

		/* Replace the indexkey expression with an index Var. */
		nt->arg = (Expr *) fix_indexqual_operand((Node *) nt->arg,
												 index,
												 indexcol);
	}
	else
		elog(ERROR, "unsupported indexqual type: %d",
			 (int) nodeTag(clause));

	return clause;
}

/*
 * fix_indexqual_operand
 *	  Convert an indexqual expression to a Var referencing the index column.
 *
 * We represent index keys by Var nodes having varno == INDEX_VAR and varattno
 * equal to the index's attribute number (index column position).
 *
 * Most of the code here is just for sanity cross-checking that the given
 * expression actually matches the index column it's claimed to.  It should
 * match the logic in match_index_to_operand().
 */
static Node *
fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol)
{
	Var		   *result;
	int			pos;
	ListCell   *indexpr_item;

	Assert(indexcol >= 0 && indexcol < index->ncolumns);

	/*
	 * Remove any PlaceHolderVar wrapping of the indexkey
	 */
	node = strip_noop_phvs(node);

	/*
	 * Remove any binary-compatible relabeling of the indexkey
	 */
	while (IsA(node, RelabelType))
		node = (Node *) ((RelabelType *) node)->arg;

	if (index->indexkeys[indexcol] != 0)
	{
		/* It's a simple index column */
		if (IsA(node, Var) &&
			((Var *) node)->varno == index->rel->relid &&
			((Var *) node)->varattno == index->indexkeys[indexcol])
		{
			result = (Var *) copyObject(node);
			result->varno = INDEX_VAR;
			result->varattno = indexcol + 1;
			return (Node *) result;
		}
		else
			elog(ERROR, "index key does not match expected index column");
	}

	/* It's an index expression, so find and cross-check the expression */
	indexpr_item = list_head(index->indexprsExpand);
	for (pos = 0; pos < index->ncolumns; pos++)
	{
		if (index->indexkeys[pos] == 0)
		{
			if (indexpr_item == NULL)
				elog(ERROR, "too few entries in indexprs list");
			if (pos == indexcol)
			{
				Node	   *indexkey;

				indexkey = (Node *) lfirst(indexpr_item);
				if (indexkey && IsA(indexkey, RelabelType))
					indexkey = (Node *) ((RelabelType *) indexkey)->arg;
				if (equal(node, indexkey))
				{
					result = makeVar(INDEX_VAR, indexcol + 1,
									 exprType(lfirst(indexpr_item)), -1,
									 exprCollation(lfirst(indexpr_item)),
									 0);
					return (Node *) result;
				}
				else
					elog(ERROR, "index key does not match expected index column");
			}
			indexpr_item = lnext(index->indexprsExpand, indexpr_item);
		}
	}

	/* Oops... */
	elog(ERROR, "index key does not match expected index column");
	return NULL;				/* keep compiler quiet */
}

/*
 * get_switched_clauses
 *	  Given a list of merge or hash joinclauses (as RestrictInfo nodes),
 *	  extract the bare clauses, and rearrange the elements within the
 *	  clauses, if needed, so the outer join variable is on the left and
 *	  the inner is on the right.  The original clause data structure is not
 *	  touched; a modified list is returned.  We do, however, set the transient
 *	  outer_is_left field in each RestrictInfo to show which side was which.
 */
static List *
get_switched_clauses(List *clauses, Relids outerrelids)
{
	List	   *t_list = NIL;
	ListCell   *l;

	foreach(l, clauses)
	{
		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(l);
		OpExpr	   *clause = (OpExpr *) restrictinfo->clause;

		Assert(is_opclause(clause));
		if (bms_is_subset(restrictinfo->right_relids, outerrelids))
		{
			/*
			 * Duplicate just enough of the structure to allow commuting the
			 * clause without changing the original list.  Could use
			 * copyObject, but a complete deep copy is overkill.
			 */
			OpExpr	   *temp = makeNode(OpExpr);

			temp->opno = clause->opno;
			temp->opfuncid = InvalidOid;
			temp->opresulttype = clause->opresulttype;
			temp->opretset = clause->opretset;
			temp->opcollid = clause->opcollid;
			temp->inputcollid = clause->inputcollid;
			temp->args = list_copy(clause->args);
			temp->location = clause->location;
			/* Commute it --- note this modifies the temp node in-place. */
			CommuteOpExpr(temp);
			t_list = lappend(t_list, temp);
			restrictinfo->outer_is_left = false;
		}
		else
		{
			Assert(bms_is_subset(restrictinfo->left_relids, outerrelids));
			t_list = lappend(t_list, clause);
			restrictinfo->outer_is_left = true;
		}
	}
	return t_list;
}

/*
 * order_qual_clauses
 *		Given a list of qual clauses that will all be evaluated at the same
 *		plan node, sort the list into the order we want to check the quals
 *		in at runtime.
 *
 * When security barrier quals are used in the query, we may have quals with
 * different security levels in the list.  Quals of lower security_level
 * must go before quals of higher security_level, except that we can grant
 * exceptions to move up quals that are leakproof.  When security level
 * doesn't force the decision, we prefer to order clauses by estimated
 * execution cost, cheapest first.
 *
 * Ideally the order should be driven by a combination of execution cost and
 * selectivity, but it's not immediately clear how to account for both,
 * and given the uncertainty of the estimates the reliability of the decisions
 * would be doubtful anyway.  So we just order by security level then
 * estimated per-tuple cost, being careful not to change the order when
 * (as is often the case) the estimates are identical.
 *
 * Although this will work on either bare clauses or RestrictInfos, it's
 * much faster to apply it to RestrictInfos, since it can re-use cost
 * information that is cached in RestrictInfos.  XXX in the bare-clause
 * case, we are also not able to apply security considerations.  That is
 * all right for the moment, because the bare-clause case doesn't occur
 * anywhere that barrier quals could be present, but it would be better to
 * get rid of it.
 *
 * Note: some callers pass lists that contain entries that will later be
 * removed; this is the easiest way to let this routine see RestrictInfos
 * instead of bare clauses.  This is another reason why trying to consider
 * selectivity in the ordering would likely do the wrong thing.
 */
static List *
order_qual_clauses(PlannerInfo *root, List *clauses)
{
	typedef struct
	{
		Node	   *clause;
		Cost		cost;
		Index		security_level;
	} QualItem;
	int			nitems = list_length(clauses);
	QualItem   *items;
	ListCell   *lc;
	int			i;
	List	   *result;

	/* No need to work hard for 0 or 1 clause */
	if (nitems <= 1)
		return clauses;

	/*
	 * Collect the items and costs into an array.  This is to avoid repeated
	 * cost_qual_eval work if the inputs aren't RestrictInfos.
	 */
	items = (QualItem *) palloc(nitems * sizeof(QualItem));
	i = 0;
	foreach(lc, clauses)
	{
		Node	   *clause = (Node *) lfirst(lc);
		QualCost	qcost;

		cost_qual_eval_node(&qcost, clause, root);
		items[i].clause = clause;
		items[i].cost = qcost.per_tuple;
		if (IsA(clause, RestrictInfo))
		{
			RestrictInfo *rinfo = (RestrictInfo *) clause;

			/*
			 * If a clause is leakproof, it doesn't have to be constrained by
			 * its nominal security level.  If it's also reasonably cheap
			 * (here defined as 10X cpu_operator_cost), pretend it has
			 * security_level 0, which will allow it to go in front of
			 * more-expensive quals of lower security levels.  Of course, that
			 * will also force it to go in front of cheaper quals of its own
			 * security level, which is not so great, but we can alleviate
			 * that risk by applying the cost limit cutoff.
			 */
			if (rinfo->leakproof && items[i].cost < 10 * cpu_operator_cost)
				items[i].security_level = 0;
			else
				items[i].security_level = rinfo->security_level;
		}
		else
			items[i].security_level = 0;
		i++;
	}

	/*
	 * Sort.  We don't use qsort() because it's not guaranteed stable for
	 * equal keys.  The expected number of entries is small enough that a
	 * simple insertion sort should be good enough.
	 */
	for (i = 1; i < nitems; i++)
	{
		QualItem	newitem = items[i];
		int			j;

		/* insert newitem into the already-sorted subarray */
		for (j = i; j > 0; j--)
		{
			QualItem   *olditem = &items[j - 1];

			if (newitem.security_level > olditem->security_level ||
				(newitem.security_level == olditem->security_level &&
				 newitem.cost >= olditem->cost))
				break;
			items[j] = *olditem;
		}
		items[j] = newitem;
	}

	/* Convert back to a list */
	result = NIL;
	for (i = 0; i < nitems; i++)
		result = lappend(result, items[i].clause);

	return result;
}

/*
 * Copy cost and size info from a Path node to the Plan node created from it.
 * The executor usually won't use this info, but it's needed by EXPLAIN.
 * Also copy the parallel-related flags, which the executor *will* use.
 */
static void
copy_generic_path_info(Plan *dest, Path *src)
{
	dest->disabled_nodes = src->disabled_nodes;
	dest->startup_cost = src->startup_cost;
	dest->total_cost = src->total_cost;
	dest->plan_rows = src->rows;
	dest->plan_width = src->pathtarget->width;
	dest->parallel_aware = src->parallel_aware;
	dest->parallel_safe = src->parallel_safe;
}

/*
 * Copy cost and size info from a lower plan node to an inserted node.
 * (Most callers alter the info after copying it.)
 */
static void
copy_plan_costsize(Plan *dest, Plan *src)
{
	dest->disabled_nodes = src->disabled_nodes;
	dest->startup_cost = src->startup_cost;
	dest->total_cost = src->total_cost;
	dest->plan_rows = src->plan_rows;
	dest->plan_width = src->plan_width;
	/* Assume the inserted node is not parallel-aware. */
	dest->parallel_aware = false;
	/* Assume the inserted node is parallel-safe, if child plan is. */
	dest->parallel_safe = src->parallel_safe;
}

/*
 * Some places in this file build Sort nodes that don't have a directly
 * corresponding Path node.  The cost of the sort is, or should have been,
 * included in the cost of the Path node we're working from, but since it's
 * not split out, we have to re-figure it using cost_sort().  This is just
 * to label the Sort node nicely for EXPLAIN.
 *
 * limit_tuples is as for cost_sort (in particular, pass -1 if no limit)
 */
static void
label_sort_with_costsize(PlannerInfo *root, Sort *plan, double limit_tuples)
{
	Plan	   *lefttree = plan->plan.lefttree;
	Path		sort_path;		/* dummy for result of cost_sort */

	Assert(IsA(plan, Sort));

	cost_sort(&sort_path, root, NIL,
			  plan->plan.disabled_nodes,
			  lefttree->total_cost,
			  lefttree->plan_rows,
			  lefttree->plan_width,
			  0.0,
			  work_mem,
			  limit_tuples);
	plan->plan.startup_cost = sort_path.startup_cost;
	plan->plan.total_cost = sort_path.total_cost;
	plan->plan.plan_rows = lefttree->plan_rows;
	plan->plan.plan_width = lefttree->plan_width;
	plan->plan.parallel_aware = false;
	plan->plan.parallel_safe = lefttree->parallel_safe;
}

/*
 * Same as label_sort_with_costsize, but labels the IncrementalSort node
 * instead.
 */
static void
label_incrementalsort_with_costsize(PlannerInfo *root, IncrementalSort *plan,
									List *pathkeys, double limit_tuples)
{
	Plan	   *lefttree = plan->sort.plan.lefttree;
	Path		sort_path;		/* dummy for result of cost_incremental_sort */

	Assert(IsA(plan, IncrementalSort));

	cost_incremental_sort(&sort_path, root, pathkeys,
						  plan->nPresortedCols,
						  plan->sort.plan.disabled_nodes,
						  lefttree->startup_cost,
						  lefttree->total_cost,
						  lefttree->plan_rows,
						  lefttree->plan_width,
						  0.0,
						  work_mem,
						  limit_tuples);
	plan->sort.plan.startup_cost = sort_path.startup_cost;
	plan->sort.plan.total_cost = sort_path.total_cost;
	plan->sort.plan.plan_rows = lefttree->plan_rows;
	plan->sort.plan.plan_width = lefttree->plan_width;
	plan->sort.plan.parallel_aware = false;
	plan->sort.plan.parallel_safe = lefttree->parallel_safe;
}

/*
 * bitmap_subplan_mark_shared
 *	 Set isshared flag in bitmap subplan so that it will be created in
 *	 shared memory.
 */
static void
bitmap_subplan_mark_shared(Plan *plan)
{
	if (IsA(plan, BitmapAnd))
		bitmap_subplan_mark_shared(linitial(((BitmapAnd *) plan)->bitmapplans));
	else if (IsA(plan, BitmapOr))
	{
		((BitmapOr *) plan)->isshared = true;
		bitmap_subplan_mark_shared(linitial(((BitmapOr *) plan)->bitmapplans));
	}
	else if (IsA(plan, BitmapIndexScan))
		((BitmapIndexScan *) plan)->isshared = true;
	else
		elog(ERROR, "unrecognized node type: %d", nodeTag(plan));
}

/*****************************************************************************
 *
 *	PLAN NODE BUILDING ROUTINES
 *
 * In general, these functions are not passed the original Path and therefore
 * leave it to the caller to fill in the cost/width fields from the Path,
 * typically by calling copy_generic_path_info().  This convention is
 * somewhat historical, but it does support a few places above where we build
 * a plan node without having an exactly corresponding Path node.  Under no
 * circumstances should one of these functions do its own cost calculations,
 * as that would be redundant with calculations done while building Paths.
 *
 *****************************************************************************/

static SeqScan *
make_seqscan(List *qptlist,
			 List *qpqual,
			 Index scanrelid)
{
	SeqScan    *node = makeNode(SeqScan);
	Plan	   *plan = &node->scan.plan;

	plan->targetlist = qptlist;
	plan->qual = qpqual;
	plan->lefttree = NULL;
	plan->righttree = NULL;
	node->scan.scanrelid = scanrelid;

	return node;
}

static SampleScan *
make_samplescan(List *qptlist,
				List *qpqual,
				Index scanrelid,
				TableSampleClause *tsc)
{
	SampleScan *node = makeNode(SampleScan);
	Plan	   *plan = &node->scan.plan;

	plan->targetlist = qptlist;
	plan->qual = qpqual;
	plan->lefttree = NULL;
	plan->righttree = NULL;
	node->scan.scanrelid = scanrelid;
	node->tablesample = tsc;

	return node;
}

static IndexScan *
make_indexscan(List *qptlist,
			   List *qpqual,
			   Index scanrelid,
			   Oid indexid,
			   List *indexqual,
			   List *indexqualorig,
			   List *indexorderby,
			   List *indexorderbyorig,
			   List *indexorderbyops,
			   ScanDirection indexscandir)
{
	IndexScan  *node = makeNode(IndexScan);
	Plan	   *plan = &node->scan.plan;

	plan->targetlist = qptlist;
	plan->qual = qpqual;
	plan->lefttree = NULL;
	plan->righttree = NULL;
	node->scan.scanrelid = scanrelid;
	node->indexid = indexid;
	node->indexqual = indexqual;
	node->indexqualorig = indexqualorig;
	node->indexorderby = indexorderby;
	node->indexorderbyorig = indexorderbyorig;
	node->indexorderbyops = indexorderbyops;
	node->indexorderdir = indexscandir;

	return node;
}

static IndexOnlyScan *
make_indexonlyscan(List *qptlist,
				   List *qpqual,
				   Index scanrelid,
				   Oid indexid,
				   List *indexqual,
				   List *recheckqual,
				   List *indexorderby,
				   List *indextlist,
				   ScanDirection indexscandir)
{
	IndexOnlyScan *node = makeNode(IndexOnlyScan);
	Plan	   *plan = &node->scan.plan;

	plan->targetlist = qptlist;
	plan->qual = qpqual;
	plan->lefttree = NULL;
	plan->righttree = NULL;
	node->scan.scanrelid = scanrelid;
	node->indexid = indexid;
	node->indexqual = indexqual;
	node->recheckqual = recheckqual;
	node->indexorderby = indexorderby;
	node->indextlist = indextlist;
	node->indexorderdir = indexscandir;

	return node;
}

static BitmapIndexScan *
make_bitmap_indexscan(Index scanrelid,
					  Oid indexid,
					  List *indexqual,
					  List *indexqualorig)
{
	BitmapIndexScan *node = makeNode(BitmapIndexScan);
	Plan	   *plan = &node->scan.plan;

	plan->targetlist = NIL;		/* not used */
	plan->qual = NIL;			/* not used */
	plan->lefttree = NULL;
	plan->righttree = NULL;
	node->scan.scanrelid = scanrelid;
	node->indexid = indexid;
	node->indexqual = indexqual;
	node->indexqualorig = indexqualorig;

	return node;
}

static BitmapHeapScan *
make_bitmap_heapscan(List *qptlist,
					 List *qpqual,
					 Plan *lefttree,
					 List *bitmapqualorig,
					 Index scanrelid)
{
	BitmapHeapScan *node = makeNode(BitmapHeapScan);
	Plan	   *plan = &node->scan.plan;

	plan->targetlist = qptlist;
	plan->qual = qpqual;
	plan->lefttree = lefttree;
	plan->righttree = NULL;
	node->scan.scanrelid = scanrelid;
	node->bitmapqualorig = bitmapqualorig;

	return node;
}

static TidScan *
make_tidscan(List *qptlist,
			 List *qpqual,
			 Index scanrelid,
			 List *tidquals)
{
	TidScan    *node = makeNode(TidScan);
	Plan	   *plan = &node->scan.plan;

	plan->targetlist = qptlist;
	plan->qual = qpqual;
	plan->lefttree = NULL;
	plan->righttree = NULL;
	node->scan.scanrelid = scanrelid;
	node->tidquals = tidquals;

	return node;
}

static TidRangeScan *
make_tidrangescan(List *qptlist,
				  List *qpqual,
				  Index scanrelid,
				  List *tidrangequals)
{
	TidRangeScan *node = makeNode(TidRangeScan);
	Plan	   *plan = &node->scan.plan;

	plan->targetlist = qptlist;
	plan->qual = qpqual;
	plan->lefttree = NULL;
	plan->righttree = NULL;
	node->scan.scanrelid = scanrelid;
	node->tidrangequals = tidrangequals;

	return node;
}

static SubqueryScan *
make_subqueryscan(List *qptlist,
				  List *qpqual,
				  Index scanrelid,
				  Plan *subplan)
{
	SubqueryScan *node = makeNode(SubqueryScan);
	Plan	   *plan = &node->scan.plan;

	plan->targetlist = qptlist;
	plan->qual = qpqual;
	plan->lefttree = NULL;
	plan->righttree = NULL;
	node->scan.scanrelid = scanrelid;
	node->subplan = subplan;
	node->scanstatus = SUBQUERY_SCAN_UNKNOWN;

	return node;
}

static FunctionScan *
make_functionscan(List *qptlist,
				  List *qpqual,
				  Index scanrelid,
				  List *functions,
				  bool funcordinality)
{
	FunctionScan *node = makeNode(FunctionScan);
	Plan	   *plan = &node->scan.plan;

	plan->targetlist = qptlist;
	plan->qual = qpqual;
	plan->lefttree = NULL;
	plan->righttree = NULL;
	node->scan.scanrelid = scanrelid;
	node->functions = functions;
	node->funcordinality = funcordinality;

	return node;
}

static TableFuncScan *
make_tablefuncscan(List *qptlist,
				   List *qpqual,
				   Index scanrelid,
				   TableFunc *tablefunc)
{
	TableFuncScan *node = makeNode(TableFuncScan);
	Plan	   *plan = &node->scan.plan;

	plan->targetlist = qptlist;
	plan->qual = qpqual;
	plan->lefttree = NULL;
	plan->righttree = NULL;
	node->scan.scanrelid = scanrelid;
	node->tablefunc = tablefunc;

	return node;
}

static ValuesScan *
make_valuesscan(List *qptlist,
				List *qpqual,
				Index scanrelid,
				List *values_lists)
{
	ValuesScan *node = makeNode(ValuesScan);
	Plan	   *plan = &node->scan.plan;

	plan->targetlist = qptlist;
	plan->qual = qpqual;
	plan->lefttree = NULL;
	plan->righttree = NULL;
	node->scan.scanrelid = scanrelid;
	node->values_lists = values_lists;

	return node;
}

static CteScan *
make_ctescan(List *qptlist,
			 List *qpqual,
			 Index scanrelid,
			 int ctePlanId,
			 int cteParam)
{
	CteScan    *node = makeNode(CteScan);
	Plan	   *plan = &node->scan.plan;

	plan->targetlist = qptlist;
	plan->qual = qpqual;
	plan->lefttree = NULL;
	plan->righttree = NULL;
	node->scan.scanrelid = scanrelid;
	node->ctePlanId = ctePlanId;
	node->cteParam = cteParam;

	return node;
}

static NamedTuplestoreScan *
make_namedtuplestorescan(List *qptlist,
						 List *qpqual,
						 Index scanrelid,
						 char *enrname)
{
	NamedTuplestoreScan *node = makeNode(NamedTuplestoreScan);
	Plan	   *plan = &node->scan.plan;

	/* cost should be inserted by caller */
	plan->targetlist = qptlist;
	plan->qual = qpqual;
	plan->lefttree = NULL;
	plan->righttree = NULL;
	node->scan.scanrelid = scanrelid;
	node->enrname = enrname;

	return node;
}

static WorkTableScan *
make_worktablescan(List *qptlist,
				   List *qpqual,
				   Index scanrelid,
				   int wtParam)
{
	WorkTableScan *node = makeNode(WorkTableScan);
	Plan	   *plan = &node->scan.plan;

	plan->targetlist = qptlist;
	plan->qual = qpqual;
	plan->lefttree = NULL;
	plan->righttree = NULL;
	node->scan.scanrelid = scanrelid;
	node->wtParam = wtParam;

	return node;
}

ForeignScan *
make_foreignscan(List *qptlist,
				 List *qpqual,
				 Index scanrelid,
				 List *fdw_exprs,
				 List *fdw_private,
				 List *fdw_scan_tlist,
				 List *fdw_recheck_quals,
				 Plan *outer_plan)
{
	ForeignScan *node = makeNode(ForeignScan);
	Plan	   *plan = &node->scan.plan;

	/* cost will be filled in by create_foreignscan_plan */
	plan->targetlist = qptlist;
	plan->qual = qpqual;
	plan->lefttree = outer_plan;
	plan->righttree = NULL;
	node->scan.scanrelid = scanrelid;

	/* these may be overridden by the FDW's PlanDirectModify callback. */
	node->operation = CMD_SELECT;
	node->resultRelation = 0;

	/* checkAsUser, fs_server will be filled in by create_foreignscan_plan */
	node->checkAsUser = InvalidOid;
	node->fs_server = InvalidOid;
	node->fdw_exprs = fdw_exprs;
	node->fdw_private = fdw_private;
	node->fdw_scan_tlist = fdw_scan_tlist;
	node->fdw_recheck_quals = fdw_recheck_quals;
	/* fs_relids, fs_base_relids will be filled by create_foreignscan_plan */
	node->fs_relids = NULL;
	node->fs_base_relids = NULL;
	/* fsSystemCol will be filled in by create_foreignscan_plan */
	node->fsSystemCol = false;

	return node;
}

static RecursiveUnion *
make_recursive_union(List *tlist,
					 Plan *lefttree,
					 Plan *righttree,
					 int wtParam,
					 List *distinctList,
					 Cardinality numGroups)
{
	RecursiveUnion *node = makeNode(RecursiveUnion);
	Plan	   *plan = &node->plan;
	int			numCols = list_length(distinctList);

	plan->targetlist = tlist;
	plan->qual = NIL;
	plan->lefttree = lefttree;
	plan->righttree = righttree;
	node->wtParam = wtParam;

	/*
	 * convert SortGroupClause list into arrays of attr indexes and equality
	 * operators, as wanted by executor
	 */
	node->numCols = numCols;
	if (numCols > 0)
	{
		int			keyno = 0;
		AttrNumber *dupColIdx;
		Oid		   *dupOperators;
		Oid		   *dupCollations;
		ListCell   *slitem;

		dupColIdx = palloc_array(AttrNumber, numCols);
		dupOperators = palloc_array(Oid, numCols);
		dupCollations = palloc_array(Oid, numCols);

		foreach(slitem, distinctList)
		{
			SortGroupClause *sortcl = (SortGroupClause *) lfirst(slitem);
			TargetEntry *tle = get_sortgroupclause_tle(sortcl,
													   plan->targetlist);

			dupColIdx[keyno] = tle->resno;
			dupOperators[keyno] = sortcl->eqop;
			dupCollations[keyno] = exprCollation((Node *) tle->expr);
			Assert(OidIsValid(dupOperators[keyno]));
			keyno++;
		}
		node->dupColIdx = dupColIdx;
		node->dupOperators = dupOperators;
		node->dupCollations = dupCollations;
	}
	node->numGroups = numGroups;

	return node;
}

static BitmapAnd *
make_bitmap_and(List *bitmapplans)
{
	BitmapAnd  *node = makeNode(BitmapAnd);
	Plan	   *plan = &node->plan;

	plan->targetlist = NIL;
	plan->qual = NIL;
	plan->lefttree = NULL;
	plan->righttree = NULL;
	node->bitmapplans = bitmapplans;

	return node;
}

static BitmapOr *
make_bitmap_or(List *bitmapplans)
{
	BitmapOr   *node = makeNode(BitmapOr);
	Plan	   *plan = &node->plan;

	plan->targetlist = NIL;
	plan->qual = NIL;
	plan->lefttree = NULL;
	plan->righttree = NULL;
	node->bitmapplans = bitmapplans;

	return node;
}

static NestLoop *
make_nestloop(List *tlist,
			  List *joinclauses,
			  List *otherclauses,
			  List *nestParams,
			  Plan *lefttree,
			  Plan *righttree,
			  JoinType jointype,
			  bool inner_unique)
{
	NestLoop   *node = makeNode(NestLoop);
	Plan	   *plan = &node->join.plan;

	plan->targetlist = tlist;
	plan->qual = otherclauses;
	plan->lefttree = lefttree;
	plan->righttree = righttree;
	node->join.jointype = jointype;
	node->join.inner_unique = inner_unique;
	node->join.joinqual = joinclauses;
	node->nestParams = nestParams;

	return node;
}

static HashJoin *
make_hashjoin(List *tlist,
			  List *joinclauses,
			  List *otherclauses,
			  List *hashclauses,
			  List *hashoperators,
			  List *hashcollations,
			  List *hashkeys,
			  Plan *lefttree,
			  Plan *righttree,
			  JoinType jointype,
			  bool inner_unique)
{
	HashJoin   *node = makeNode(HashJoin);
	Plan	   *plan = &node->join.plan;

	plan->targetlist = tlist;
	plan->qual = otherclauses;
	plan->lefttree = lefttree;
	plan->righttree = righttree;
	node->hashclauses = hashclauses;
	node->hashoperators = hashoperators;
	node->hashcollations = hashcollations;
	node->hashkeys = hashkeys;
	node->join.jointype = jointype;
	node->join.inner_unique = inner_unique;
	node->join.joinqual = joinclauses;

	return node;
}

static Hash *
make_hash(Plan *lefttree,
		  List *hashkeys,
		  Oid skewTable,
		  AttrNumber skewColumn,
		  bool skewInherit)
{
	Hash	   *node = makeNode(Hash);
	Plan	   *plan = &node->plan;

	plan->targetlist = lefttree->targetlist;
	plan->qual = NIL;
	plan->lefttree = lefttree;
	plan->righttree = NULL;

	node->hashkeys = hashkeys;
	node->skewTable = skewTable;
	node->skewColumn = skewColumn;
	node->skewInherit = skewInherit;

	return node;
}

static MergeJoin *
make_mergejoin(List *tlist,
			   List *joinclauses,
			   List *otherclauses,
			   List *mergeclauses,
			   Oid *mergefamilies,
			   Oid *mergecollations,
			   bool *mergereversals,
			   bool *mergenullsfirst,
			   Plan *lefttree,
			   Plan *righttree,
			   JoinType jointype,
			   bool inner_unique,
			   bool skip_mark_restore)
{
	MergeJoin  *node = makeNode(MergeJoin);
	Plan	   *plan = &node->join.plan;

	plan->targetlist = tlist;
	plan->qual = otherclauses;
	plan->lefttree = lefttree;
	plan->righttree = righttree;
	node->skip_mark_restore = skip_mark_restore;
	node->mergeclauses = mergeclauses;
	node->mergeFamilies = mergefamilies;
	node->mergeCollations = mergecollations;
	node->mergeReversals = mergereversals;
	node->mergeNullsFirst = mergenullsfirst;
	node->join.jointype = jointype;
	node->join.inner_unique = inner_unique;
	node->join.joinqual = joinclauses;

	return node;
}

/*
 * make_sort --- basic routine to build a Sort plan node
 *
 * Caller must have built the sortColIdx, sortOperators, collations, and
 * nullsFirst arrays already.
 */
static Sort *
make_sort(Plan *lefttree, int numCols,
		  AttrNumber *sortColIdx, Oid *sortOperators,
		  Oid *collations, bool *nullsFirst)
{
	Sort	   *node;
	Plan	   *plan;

	node = makeNode(Sort);

	plan = &node->plan;
	plan->targetlist = lefttree->targetlist;
	plan->disabled_nodes = lefttree->disabled_nodes + (enable_sort == false);
	plan->qual = NIL;
	plan->lefttree = lefttree;
	plan->righttree = NULL;
	node->numCols = numCols;
	node->sortColIdx = sortColIdx;
	node->sortOperators = sortOperators;
	node->collations = collations;
	node->nullsFirst = nullsFirst;

	return node;
}

/*
 * make_incrementalsort --- basic routine to build an IncrementalSort plan node
 *
 * Caller must have built the sortColIdx, sortOperators, collations, and
 * nullsFirst arrays already.
 */
static IncrementalSort *
make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
					 AttrNumber *sortColIdx, Oid *sortOperators,
					 Oid *collations, bool *nullsFirst)
{
	IncrementalSort *node;
	Plan	   *plan;

	node = makeNode(IncrementalSort);

	plan = &node->sort.plan;
	plan->targetlist = lefttree->targetlist;
	plan->qual = NIL;
	plan->lefttree = lefttree;
	plan->righttree = NULL;
	node->nPresortedCols = nPresortedCols;
	node->sort.numCols = numCols;
	node->sort.sortColIdx = sortColIdx;
	node->sort.sortOperators = sortOperators;
	node->sort.collations = collations;
	node->sort.nullsFirst = nullsFirst;

	return node;
}

/*
 * prepare_sort_from_pathkeys
 *	  Prepare to sort according to given pathkeys
 *
 * This is used to set up for Sort, MergeAppend, and Gather Merge nodes.  It
 * calculates the executor's representation of the sort key information, and
 * adjusts the plan targetlist if needed to add resjunk sort columns.
 *
 * Input parameters:
 *	  'lefttree' is the plan node which yields input tuples
 *	  'pathkeys' is the list of pathkeys by which the result is to be sorted
 *	  'relids' identifies the child relation being sorted, if any
 *	  'reqColIdx' is NULL or an array of required sort key column numbers
 *	  'adjust_tlist_in_place' is true if lefttree must be modified in-place
 *
 * We must convert the pathkey information into arrays of sort key column
 * numbers, sort operator OIDs, collation OIDs, and nulls-first flags,
 * which is the representation the executor wants.  These are returned into
 * the output parameters *p_numsortkeys etc.
 *
 * When looking for matches to an EquivalenceClass's members, we will only
 * consider child EC members if they belong to given 'relids'.  This protects
 * against possible incorrect matches to child expressions that contain no
 * Vars.
 *
 * If reqColIdx isn't NULL then it contains sort key column numbers that
 * we should match.  This is used when making child plans for a MergeAppend;
 * it's an error if we can't match the columns.
 *
 * If the pathkeys include expressions that aren't simple Vars, we will
 * usually need to add resjunk items to the input plan's targetlist to
 * compute these expressions, since a Sort or MergeAppend node itself won't
 * do any such calculations.  If the input plan type isn't one that can do
 * projections, this means adding a Result node just to do the projection.
 * However, the caller can pass adjust_tlist_in_place = true to force the
 * lefttree tlist to be modified in-place regardless of whether the node type
 * can project --- we use this for fixing the tlist of MergeAppend itself.
 *
 * Returns the node which is to be the input to the Sort (either lefttree,
 * or a Result stacked atop lefttree).
 */
static Plan *
prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
						   Relids relids,
						   const AttrNumber *reqColIdx,
						   bool adjust_tlist_in_place,
						   int *p_numsortkeys,
						   AttrNumber **p_sortColIdx,
						   Oid **p_sortOperators,
						   Oid **p_collations,
						   bool **p_nullsFirst)
{
	List	   *tlist = lefttree->targetlist;
	ListCell   *i;
	int			numsortkeys;
	AttrNumber *sortColIdx;
	Oid		   *sortOperators;
	Oid		   *collations;
	bool	   *nullsFirst;

	/*
	 * We will need at most list_length(pathkeys) sort columns; possibly less
	 */
	numsortkeys = list_length(pathkeys);
	sortColIdx = (AttrNumber *) palloc(numsortkeys * sizeof(AttrNumber));
	sortOperators = (Oid *) palloc(numsortkeys * sizeof(Oid));
	collations = (Oid *) palloc(numsortkeys * sizeof(Oid));
	nullsFirst = (bool *) palloc(numsortkeys * sizeof(bool));

	numsortkeys = 0;

	foreach(i, pathkeys)
	{
		PathKey    *pathkey = (PathKey *) lfirst(i);
		EquivalenceClass *ec = pathkey->pk_eclass;
		EquivalenceMember *em;
		TargetEntry *tle = NULL;
		Oid			pk_datatype = InvalidOid;
		Oid			sortop;
		ListCell   *j;

		if (ec->ec_has_volatile)
		{
			/*
			 * If the pathkey's EquivalenceClass is volatile, then it must
			 * have come from an ORDER BY clause, and we have to match it to
			 * that same targetlist entry.
			 */
			if (ec->ec_sortref == 0)	/* can't happen */
				elog(ERROR, "volatile EquivalenceClass has no sortref");
			tle = get_sortgroupref_tle(ec->ec_sortref, tlist);
			Assert(tle);
			Assert(list_length(ec->ec_members) == 1);
			pk_datatype = ((EquivalenceMember *) linitial(ec->ec_members))->em_datatype;
		}
		else if (reqColIdx != NULL)
		{
			/*
			 * If we are given a sort column number to match, only consider
			 * the single TLE at that position.  It's possible that there is
			 * no such TLE, in which case fall through and generate a resjunk
			 * targetentry (we assume this must have happened in the parent
			 * plan as well).  If there is a TLE but it doesn't match the
			 * pathkey's EC, we do the same, which is probably the wrong thing
			 * but we'll leave it to caller to complain about the mismatch.
			 */
			tle = get_tle_by_resno(tlist, reqColIdx[numsortkeys]);
			if (tle)
			{
				em = find_ec_member_matching_expr(ec, tle->expr, relids);
				if (em)
				{
					/* found expr at right place in tlist */
					pk_datatype = em->em_datatype;
				}
				else
					tle = NULL;
			}
		}
		else
		{
			/*
			 * Otherwise, we can sort by any non-constant expression listed in
			 * the pathkey's EquivalenceClass.  For now, we take the first
			 * tlist item found in the EC. If there's no match, we'll generate
			 * a resjunk entry using the first EC member that is an expression
			 * in the input's vars.
			 *
			 * XXX if we have a choice, is there any way of figuring out which
			 * might be cheapest to execute?  (For example, int4lt is likely
			 * much cheaper to execute than numericlt, but both might appear
			 * in the same equivalence class...)  Not clear that we ever will
			 * have an interesting choice in practice, so it may not matter.
			 */
			foreach(j, tlist)
			{
				tle = (TargetEntry *) lfirst(j);
				em = find_ec_member_matching_expr(ec, tle->expr, relids);
				if (em)
				{
					/* found expr already in tlist */
					pk_datatype = em->em_datatype;
					break;
				}
				tle = NULL;
			}
		}

		if (!tle)
		{
			/*
			 * No matching tlist item; look for a computable expression.
			 */
			em = find_computable_ec_member(NULL, ec, tlist, relids, false);
			if (!em)
				elog(ERROR, "could not find pathkey item to sort");
			pk_datatype = em->em_datatype;

			/*
			 * Do we need to insert a Result node?
			 */
			if (!adjust_tlist_in_place &&
				!is_projection_capable_plan(lefttree))
			{
				/* copy needed so we don't modify input's tlist below */
				tlist = copyObject(tlist);
				lefttree = inject_projection_plan(lefttree, tlist,
												  lefttree->parallel_safe);
			}

			/* Don't bother testing is_projection_capable_plan again */
			adjust_tlist_in_place = true;

			/*
			 * Add resjunk entry to input's tlist
			 */
			tle = makeTargetEntry(copyObject(em->em_expr),
								  list_length(tlist) + 1,
								  NULL,
								  true);
			tlist = lappend(tlist, tle);
			lefttree->targetlist = tlist;	/* just in case NIL before */
		}

		/*
		 * Look up the correct sort operator from the PathKey's slightly
		 * abstracted representation.
		 */
		sortop = get_opfamily_member_for_cmptype(pathkey->pk_opfamily,
												 pk_datatype,
												 pk_datatype,
												 pathkey->pk_cmptype);
		if (!OidIsValid(sortop))	/* should not happen */
			elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
				 pathkey->pk_cmptype, pk_datatype, pk_datatype,
				 pathkey->pk_opfamily);

		/* Add the column to the sort arrays */
		sortColIdx[numsortkeys] = tle->resno;
		sortOperators[numsortkeys] = sortop;
		collations[numsortkeys] = ec->ec_collation;
		nullsFirst[numsortkeys] = pathkey->pk_nulls_first;
		numsortkeys++;
	}

	/* Return results */
	*p_numsortkeys = numsortkeys;
	*p_sortColIdx = sortColIdx;
	*p_sortOperators = sortOperators;
	*p_collations = collations;
	*p_nullsFirst = nullsFirst;

	return lefttree;
}

/*
 * make_sort_from_pathkeys
 *	  Create sort plan to sort according to given pathkeys
 *
 *	  'lefttree' is the node which yields input tuples
 *	  'pathkeys' is the list of pathkeys by which the result is to be sorted
 *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
 */
static Sort *
make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
{
	int			numsortkeys;
	AttrNumber *sortColIdx;
	Oid		   *sortOperators;
	Oid		   *collations;
	bool	   *nullsFirst;

	/* Compute sort column info, and adjust lefttree as needed */
	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
										  relids,
										  NULL,
										  false,
										  &numsortkeys,
										  &sortColIdx,
										  &sortOperators,
										  &collations,
										  &nullsFirst);

	/* Now build the Sort node */
	return make_sort(lefttree, numsortkeys,
					 sortColIdx, sortOperators,
					 collations, nullsFirst);
}

/*
 * make_incrementalsort_from_pathkeys
 *	  Create sort plan to sort according to given pathkeys
 *
 *	  'lefttree' is the node which yields input tuples
 *	  'pathkeys' is the list of pathkeys by which the result is to be sorted
 *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
 *	  'nPresortedCols' is the number of presorted columns in input tuples
 */
static IncrementalSort *
make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
								   Relids relids, int nPresortedCols)
{
	int			numsortkeys;
	AttrNumber *sortColIdx;
	Oid		   *sortOperators;
	Oid		   *collations;
	bool	   *nullsFirst;

	/* Compute sort column info, and adjust lefttree as needed */
	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
										  relids,
										  NULL,
										  false,
										  &numsortkeys,
										  &sortColIdx,
										  &sortOperators,
										  &collations,
										  &nullsFirst);

	/* Now build the Sort node */
	return make_incrementalsort(lefttree, numsortkeys, nPresortedCols,
								sortColIdx, sortOperators,
								collations, nullsFirst);
}

/*
 * make_sort_from_sortclauses
 *	  Create sort plan to sort according to given sortclauses
 *
 *	  'sortcls' is a list of SortGroupClauses
 *	  'lefttree' is the node which yields input tuples
 */
Sort *
make_sort_from_sortclauses(List *sortcls, Plan *lefttree)
{
	List	   *sub_tlist = lefttree->targetlist;
	ListCell   *l;
	int			numsortkeys;
	AttrNumber *sortColIdx;
	Oid		   *sortOperators;
	Oid		   *collations;
	bool	   *nullsFirst;

	/* Convert list-ish representation to arrays wanted by executor */
	numsortkeys = list_length(sortcls);
	sortColIdx = (AttrNumber *) palloc(numsortkeys * sizeof(AttrNumber));
	sortOperators = (Oid *) palloc(numsortkeys * sizeof(Oid));
	collations = (Oid *) palloc(numsortkeys * sizeof(Oid));
	nullsFirst = (bool *) palloc(numsortkeys * sizeof(bool));

	numsortkeys = 0;
	foreach(l, sortcls)
	{
		SortGroupClause *sortcl = (SortGroupClause *) lfirst(l);
		TargetEntry *tle = get_sortgroupclause_tle(sortcl, sub_tlist);

		sortColIdx[numsortkeys] = tle->resno;
		sortOperators[numsortkeys] = sortcl->sortop;
		collations[numsortkeys] = exprCollation((Node *) tle->expr);
		nullsFirst[numsortkeys] = sortcl->nulls_first;
		numsortkeys++;
	}

	return make_sort(lefttree, numsortkeys,
					 sortColIdx, sortOperators,
					 collations, nullsFirst);
}

/*
 * make_sort_from_groupcols
 *	  Create sort plan to sort based on grouping columns
 *
 * 'groupcls' is the list of SortGroupClauses
 * 'grpColIdx' gives the column numbers to use
 *
 * This might look like it could be merged with make_sort_from_sortclauses,
 * but presently we *must* use the grpColIdx[] array to locate sort columns,
 * because the child plan's tlist is not marked with ressortgroupref info
 * appropriate to the grouping node.  So, only the sort ordering info
 * is used from the SortGroupClause entries.
 */
static Sort *
make_sort_from_groupcols(List *groupcls,
						 AttrNumber *grpColIdx,
						 Plan *lefttree)
{
	List	   *sub_tlist = lefttree->targetlist;
	ListCell   *l;
	int			numsortkeys;
	AttrNumber *sortColIdx;
	Oid		   *sortOperators;
	Oid		   *collations;
	bool	   *nullsFirst;

	/* Convert list-ish representation to arrays wanted by executor */
	numsortkeys = list_length(groupcls);
	sortColIdx = (AttrNumber *) palloc(numsortkeys * sizeof(AttrNumber));
	sortOperators = (Oid *) palloc(numsortkeys * sizeof(Oid));
	collations = (Oid *) palloc(numsortkeys * sizeof(Oid));
	nullsFirst = (bool *) palloc(numsortkeys * sizeof(bool));

	numsortkeys = 0;
	foreach(l, groupcls)
	{
		SortGroupClause *grpcl = (SortGroupClause *) lfirst(l);
		TargetEntry *tle = get_tle_by_resno(sub_tlist, grpColIdx[numsortkeys]);

		if (!tle)
			elog(ERROR, "could not retrieve tle for sort-from-groupcols");

		sortColIdx[numsortkeys] = tle->resno;
		sortOperators[numsortkeys] = grpcl->sortop;
		collations[numsortkeys] = exprCollation((Node *) tle->expr);
		nullsFirst[numsortkeys] = grpcl->nulls_first;
		numsortkeys++;
	}

	return make_sort(lefttree, numsortkeys,
					 sortColIdx, sortOperators,
					 collations, nullsFirst);
}

static Material *
make_material(Plan *lefttree)
{
	Material   *node = makeNode(Material);
	Plan	   *plan = &node->plan;

	plan->targetlist = lefttree->targetlist;
	plan->qual = NIL;
	plan->lefttree = lefttree;
	plan->righttree = NULL;

	return node;
}

/*
 * materialize_finished_plan: stick a Material node atop a completed plan
 *
 * There are a couple of places where we want to attach a Material node
 * after completion of create_plan(), without any MaterialPath path.
 * Those places should probably be refactored someday to do this on the
 * Path representation, but it's not worth the trouble yet.
 */
Plan *
materialize_finished_plan(Plan *subplan)
{
	Plan	   *matplan;
	Path		matpath;		/* dummy for cost_material */
	Cost		initplan_cost;
	bool		unsafe_initplans;

	matplan = (Plan *) make_material(subplan);

	/*
	 * XXX horrid kluge: if there are any initPlans attached to the subplan,
	 * move them up to the Material node, which is now effectively the top
	 * plan node in its query level.  This prevents failure in
	 * SS_finalize_plan(), which see for comments.
	 */
	matplan->initPlan = subplan->initPlan;
	subplan->initPlan = NIL;

	/* Move the initplans' cost delta, as well */
	SS_compute_initplan_cost(matplan->initPlan,
							 &initplan_cost, &unsafe_initplans);
	subplan->startup_cost -= initplan_cost;
	subplan->total_cost -= initplan_cost;

	/* Set cost data */
	cost_material(&matpath,
				  enable_material,
				  subplan->disabled_nodes,
				  subplan->startup_cost,
				  subplan->total_cost,
				  subplan->plan_rows,
				  subplan->plan_width);
	matplan->disabled_nodes = subplan->disabled_nodes;
	matplan->startup_cost = matpath.startup_cost + initplan_cost;
	matplan->total_cost = matpath.total_cost + initplan_cost;
	matplan->plan_rows = subplan->plan_rows;
	matplan->plan_width = subplan->plan_width;
	matplan->parallel_aware = false;
	matplan->parallel_safe = subplan->parallel_safe;

	return matplan;
}

static Memoize *
make_memoize(Plan *lefttree, Oid *hashoperators, Oid *collations,
			 List *param_exprs, bool singlerow, bool binary_mode,
			 uint32 est_entries, Bitmapset *keyparamids,
			 Cardinality est_calls, Cardinality est_unique_keys,
			 double est_hit_ratio)
{
	Memoize    *node = makeNode(Memoize);
	Plan	   *plan = &node->plan;

	plan->targetlist = lefttree->targetlist;
	plan->qual = NIL;
	plan->lefttree = lefttree;
	plan->righttree = NULL;

	node->numKeys = list_length(param_exprs);
	node->hashOperators = hashoperators;
	node->collations = collations;
	node->param_exprs = param_exprs;
	node->singlerow = singlerow;
	node->binary_mode = binary_mode;
	node->est_entries = est_entries;
	node->keyparamids = keyparamids;
	node->est_calls = est_calls;
	node->est_unique_keys = est_unique_keys;
	node->est_hit_ratio = est_hit_ratio;

	return node;
}

Agg *
make_agg(List *tlist, List *qual,
		 AggStrategy aggstrategy, AggSplit aggsplit,
		 int numGroupCols, AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
		 List *groupingSets, List *chain, Cardinality numGroups,
		 Size transitionSpace, Plan *lefttree)
{
	Agg		   *node = makeNode(Agg);
	Plan	   *plan = &node->plan;

	node->aggstrategy = aggstrategy;
	node->aggsplit = aggsplit;
	node->numCols = numGroupCols;
	node->grpColIdx = grpColIdx;
	node->grpOperators = grpOperators;
	node->grpCollations = grpCollations;
	node->numGroups = numGroups;
	node->transitionSpace = transitionSpace;
	node->aggParams = NULL;		/* SS_finalize_plan() will fill this */
	node->groupingSets = groupingSets;
	node->chain = chain;

	plan->qual = qual;
	plan->targetlist = tlist;
	plan->lefttree = lefttree;
	plan->righttree = NULL;

	return node;
}

static WindowAgg *
make_windowagg(List *tlist, WindowClause *wc,
			   int partNumCols, AttrNumber *partColIdx, Oid *partOperators, Oid *partCollations,
			   int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators, Oid *ordCollations,
			   List *runCondition, List *qual, bool topWindow, Plan *lefttree)
{
	WindowAgg  *node = makeNode(WindowAgg);
	Plan	   *plan = &node->plan;

	node->winname = wc->name;
	node->winref = wc->winref;
	node->partNumCols = partNumCols;
	node->partColIdx = partColIdx;
	node->partOperators = partOperators;
	node->partCollations = partCollations;
	node->ordNumCols = ordNumCols;
	node->ordColIdx = ordColIdx;
	node->ordOperators = ordOperators;
	node->ordCollations = ordCollations;
	node->frameOptions = wc->frameOptions;
	node->startOffset = wc->startOffset;
	node->endOffset = wc->endOffset;
	node->runCondition = runCondition;
	/* a duplicate of the above for EXPLAIN */
	node->runConditionOrig = runCondition;
	node->startInRangeFunc = wc->startInRangeFunc;
	node->endInRangeFunc = wc->endInRangeFunc;
	node->inRangeColl = wc->inRangeColl;
	node->inRangeAsc = wc->inRangeAsc;
	node->inRangeNullsFirst = wc->inRangeNullsFirst;
	node->topWindow = topWindow;

	plan->targetlist = tlist;
	plan->lefttree = lefttree;
	plan->righttree = NULL;
	plan->qual = qual;

	return node;
}

static Group *
make_group(List *tlist,
		   List *qual,
		   int numGroupCols,
		   AttrNumber *grpColIdx,
		   Oid *grpOperators,
		   Oid *grpCollations,
		   Plan *lefttree)
{
	Group	   *node = makeNode(Group);
	Plan	   *plan = &node->plan;

	node->numCols = numGroupCols;
	node->grpColIdx = grpColIdx;
	node->grpOperators = grpOperators;
	node->grpCollations = grpCollations;

	plan->qual = qual;
	plan->targetlist = tlist;
	plan->lefttree = lefttree;
	plan->righttree = NULL;

	return node;
}

/*
 * pathkeys is a list of PathKeys, identifying the sort columns and semantics.
 * The input plan must already be sorted accordingly.
 *
 * relids identifies the child relation being unique-ified, if any.
 */
static Unique *
make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols,
						  Relids relids)
{
	Unique	   *node = makeNode(Unique);
	Plan	   *plan = &node->plan;
	int			keyno = 0;
	AttrNumber *uniqColIdx;
	Oid		   *uniqOperators;
	Oid		   *uniqCollations;
	ListCell   *lc;

	plan->targetlist = lefttree->targetlist;
	plan->qual = NIL;
	plan->lefttree = lefttree;
	plan->righttree = NULL;

	/*
	 * Convert pathkeys list into arrays of attr indexes and equality
	 * operators, as wanted by executor.  This has a lot in common with
	 * prepare_sort_from_pathkeys ... maybe unify sometime?
	 */
	Assert(numCols >= 0 && numCols <= list_length(pathkeys));
	uniqColIdx = palloc_array(AttrNumber, numCols);
	uniqOperators = palloc_array(Oid, numCols);
	uniqCollations = palloc_array(Oid, numCols);

	foreach(lc, pathkeys)
	{
		PathKey    *pathkey = (PathKey *) lfirst(lc);
		EquivalenceClass *ec = pathkey->pk_eclass;
		EquivalenceMember *em;
		TargetEntry *tle = NULL;
		Oid			pk_datatype = InvalidOid;
		Oid			eqop;
		ListCell   *j;

		/* Ignore pathkeys beyond the specified number of columns */
		if (keyno >= numCols)
			break;

		if (ec->ec_has_volatile)
		{
			/*
			 * If the pathkey's EquivalenceClass is volatile, then it must
			 * have come from an ORDER BY clause, and we have to match it to
			 * that same targetlist entry.
			 */
			if (ec->ec_sortref == 0)	/* can't happen */
				elog(ERROR, "volatile EquivalenceClass has no sortref");
			tle = get_sortgroupref_tle(ec->ec_sortref, plan->targetlist);
			Assert(tle);
			Assert(list_length(ec->ec_members) == 1);
			pk_datatype = ((EquivalenceMember *) linitial(ec->ec_members))->em_datatype;
		}
		else
		{
			/*
			 * Otherwise, we can use any non-constant expression listed in the
			 * pathkey's EquivalenceClass.  For now, we take the first tlist
			 * item found in the EC.
			 */
			foreach(j, plan->targetlist)
			{
				tle = (TargetEntry *) lfirst(j);
				em = find_ec_member_matching_expr(ec, tle->expr, relids);
				if (em)
				{
					/* found expr already in tlist */
					pk_datatype = em->em_datatype;
					break;
				}
				tle = NULL;
			}
		}

		if (!tle)
			elog(ERROR, "could not find pathkey item to sort");

		/*
		 * Look up the correct equality operator from the PathKey's slightly
		 * abstracted representation.
		 */
		eqop = get_opfamily_member_for_cmptype(pathkey->pk_opfamily,
											   pk_datatype,
											   pk_datatype,
											   COMPARE_EQ);
		if (!OidIsValid(eqop))	/* should not happen */
			elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
				 COMPARE_EQ, pk_datatype, pk_datatype,
				 pathkey->pk_opfamily);

		uniqColIdx[keyno] = tle->resno;
		uniqOperators[keyno] = eqop;
		uniqCollations[keyno] = ec->ec_collation;

		keyno++;
	}

	node->numCols = numCols;
	node->uniqColIdx = uniqColIdx;
	node->uniqOperators = uniqOperators;
	node->uniqCollations = uniqCollations;

	return node;
}

static Gather *
make_gather(List *qptlist,
			List *qpqual,
			int nworkers,
			int rescan_param,
			bool single_copy,
			Plan *subplan)
{
	Gather	   *node = makeNode(Gather);
	Plan	   *plan = &node->plan;

	plan->targetlist = qptlist;
	plan->qual = qpqual;
	plan->lefttree = subplan;
	plan->righttree = NULL;
	node->num_workers = nworkers;
	node->rescan_param = rescan_param;
	node->single_copy = single_copy;
	node->invisible = false;
	node->initParam = NULL;

	return node;
}

/*
 * groupList is a list of SortGroupClauses, identifying the targetlist
 * items that should be considered by the SetOp filter.  The input plans must
 * already be sorted accordingly, if we're doing SETOP_SORTED mode.
 */
static SetOp *
make_setop(SetOpCmd cmd, SetOpStrategy strategy,
		   List *tlist, Plan *lefttree, Plan *righttree,
		   List *groupList, Cardinality numGroups)
{
	SetOp	   *node = makeNode(SetOp);
	Plan	   *plan = &node->plan;
	int			numCols = list_length(groupList);
	int			keyno = 0;
	AttrNumber *cmpColIdx;
	Oid		   *cmpOperators;
	Oid		   *cmpCollations;
	bool	   *cmpNullsFirst;
	ListCell   *slitem;

	plan->targetlist = tlist;
	plan->qual = NIL;
	plan->lefttree = lefttree;
	plan->righttree = righttree;

	/*
	 * convert SortGroupClause list into arrays of attr indexes and comparison
	 * operators, as wanted by executor
	 */
	cmpColIdx = palloc_array(AttrNumber, numCols);
	cmpOperators = palloc_array(Oid, numCols);
	cmpCollations = palloc_array(Oid, numCols);
	cmpNullsFirst = palloc_array(bool, numCols);

	foreach(slitem, groupList)
	{
		SortGroupClause *sortcl = (SortGroupClause *) lfirst(slitem);
		TargetEntry *tle = get_sortgroupclause_tle(sortcl, plan->targetlist);

		cmpColIdx[keyno] = tle->resno;
		if (strategy == SETOP_HASHED)
			cmpOperators[keyno] = sortcl->eqop;
		else
			cmpOperators[keyno] = sortcl->sortop;
		Assert(OidIsValid(cmpOperators[keyno]));
		cmpCollations[keyno] = exprCollation((Node *) tle->expr);
		cmpNullsFirst[keyno] = sortcl->nulls_first;
		keyno++;
	}

	node->cmd = cmd;
	node->strategy = strategy;
	node->numCols = numCols;
	node->cmpColIdx = cmpColIdx;
	node->cmpOperators = cmpOperators;
	node->cmpCollations = cmpCollations;
	node->cmpNullsFirst = cmpNullsFirst;
	node->numGroups = numGroups;

	return node;
}

/*
 * make_lockrows
 *	  Build a LockRows plan node
 */
static LockRows *
make_lockrows(Plan *lefttree, List *rowMarks, int epqParam)
{
	LockRows   *node = makeNode(LockRows);
	Plan	   *plan = &node->plan;

	plan->targetlist = lefttree->targetlist;
	plan->qual = NIL;
	plan->lefttree = lefttree;
	plan->righttree = NULL;

	node->rowMarks = rowMarks;
	node->epqParam = epqParam;

	return node;
}

/*
 * make_limit
 *	  Build a Limit plan node
 */
Limit *
make_limit(Plan *lefttree, Node *limitOffset, Node *limitCount,
		   LimitOption limitOption, int uniqNumCols, AttrNumber *uniqColIdx,
		   Oid *uniqOperators, Oid *uniqCollations)
{
	Limit	   *node = makeNode(Limit);
	Plan	   *plan = &node->plan;

	plan->targetlist = lefttree->targetlist;
	plan->qual = NIL;
	plan->lefttree = lefttree;
	plan->righttree = NULL;

	node->limitOffset = limitOffset;
	node->limitCount = limitCount;
	node->limitOption = limitOption;
	node->uniqNumCols = uniqNumCols;
	node->uniqColIdx = uniqColIdx;
	node->uniqOperators = uniqOperators;
	node->uniqCollations = uniqCollations;

	return node;
}

/*
 * make_gating_result
 *	  Build a Result plan node that performs projection of a subplan, and/or
 *	  applies a one time filter (resconstantqual)
 */
static Result *
make_gating_result(List *tlist,
				   Node *resconstantqual,
				   Plan *subplan)
{
	Result	   *node = makeNode(Result);
	Plan	   *plan = &node->plan;

	Assert(subplan != NULL);

	plan->targetlist = tlist;
	plan->qual = NIL;
	plan->lefttree = subplan;
	plan->righttree = NULL;
	node->result_type = RESULT_TYPE_GATING;
	node->resconstantqual = resconstantqual;
	node->relids = NULL;

	return node;
}

/*
 * make_one_row_result
 *	  Build a Result plan node that returns a single row (or possibly no rows,
 *	  if the one-time filtered defined by resconstantqual returns false)
 *
 * 'rel' should be this path's RelOptInfo. In essence, we're saying that this
 * Result node generates all the tuples for that RelOptInfo. Note that the same
 * consideration can never arise in make_gating_result(), because in that case
 * the tuples are always coming from some subordinate node.
 */
static Result *
make_one_row_result(List *tlist,
					Node *resconstantqual,
					RelOptInfo *rel)
{
	Result	   *node = makeNode(Result);
	Plan	   *plan = &node->plan;

	plan->targetlist = tlist;
	plan->qual = NIL;
	plan->lefttree = NULL;
	plan->righttree = NULL;
	node->result_type = IS_UPPER_REL(rel) ? RESULT_TYPE_UPPER :
		IS_JOIN_REL(rel) ? RESULT_TYPE_JOIN : RESULT_TYPE_SCAN;
	node->resconstantqual = resconstantqual;
	node->relids = rel->relids;

	return node;
}

/*
 * make_project_set
 *	  Build a ProjectSet plan node
 */
static ProjectSet *
make_project_set(List *tlist,
				 Plan *subplan)
{
	ProjectSet *node = makeNode(ProjectSet);
	Plan	   *plan = &node->plan;

	plan->targetlist = tlist;
	plan->qual = NIL;
	plan->lefttree = subplan;
	plan->righttree = NULL;

	return node;
}

/*
 * make_modifytable
 *	  Build a ModifyTable plan node
 */
static ModifyTable *
make_modifytable(PlannerInfo *root, Plan *subplan,
				 CmdType operation, bool canSetTag,
				 Index nominalRelation, Index rootRelation,
				 List *resultRelations,
				 List *updateColnosLists,
				 List *withCheckOptionLists, List *returningLists,
				 List *rowMarks, OnConflictExpr *onconflict,
				 List *mergeActionLists, List *mergeJoinConditions,
				 ForPortionOfExpr *forPortionOf, int epqParam)
{
	ModifyTable *node = makeNode(ModifyTable);
	bool		returning_old_or_new = false;
	bool		returning_old_or_new_valid = false;
	bool		transition_tables = false;
	bool		transition_tables_valid = false;
	List	   *fdw_private_list;
	Bitmapset  *direct_modify_plans;
	ListCell   *lc;
	int			i;

	Assert(operation == CMD_MERGE ||
		   (operation == CMD_UPDATE ?
			list_length(resultRelations) == list_length(updateColnosLists) :
			updateColnosLists == NIL));
	Assert(withCheckOptionLists == NIL ||
		   list_length(resultRelations) == list_length(withCheckOptionLists));
	Assert(returningLists == NIL ||
		   list_length(resultRelations) == list_length(returningLists));

	node->plan.lefttree = subplan;
	node->plan.righttree = NULL;
	node->plan.qual = NIL;
	/* setrefs.c will fill in the targetlist, if needed */
	node->plan.targetlist = NIL;

	node->operation = operation;
	node->canSetTag = canSetTag;
	node->nominalRelation = nominalRelation;
	node->rootRelation = rootRelation;
	node->resultRelations = resultRelations;
	if (!onconflict)
	{
		node->onConflictAction = ONCONFLICT_NONE;
		node->onConflictLockStrength = LCS_NONE;
		node->onConflictSet = NIL;
		node->onConflictCols = NIL;
		node->onConflictWhere = NULL;
		node->arbiterIndexes = NIL;
		node->exclRelRTI = 0;
		node->exclRelTlist = NIL;
	}
	else
	{
		node->onConflictAction = onconflict->action;

		/* Lock strength for ON CONFLICT DO SELECT [FOR UPDATE/SHARE] */
		node->onConflictLockStrength = onconflict->lockStrength;

		/*
		 * Here we convert the ON CONFLICT UPDATE tlist, if any, to the
		 * executor's convention of having consecutive resno's.  The actual
		 * target column numbers are saved in node->onConflictCols.  (This
		 * could be done earlier, but there seems no need to.)
		 */
		node->onConflictSet = onconflict->onConflictSet;
		node->onConflictCols =
			extract_update_targetlist_colnos(node->onConflictSet);
		node->onConflictWhere = onconflict->onConflictWhere;

		/*
		 * If a set of unique index inference elements was provided (an
		 * INSERT...ON CONFLICT "inference specification"), then infer
		 * appropriate unique indexes (or throw an error if none are
		 * available).
		 */
		node->arbiterIndexes = infer_arbiter_indexes(root);

		node->exclRelRTI = onconflict->exclRelIndex;
		node->exclRelTlist = onconflict->exclRelTlist;
	}
	node->updateColnosLists = updateColnosLists;
	node->forPortionOf = (Node *) forPortionOf;
	node->withCheckOptionLists = withCheckOptionLists;
	node->returningOldAlias = root->parse->returningOldAlias;
	node->returningNewAlias = root->parse->returningNewAlias;
	node->returningLists = returningLists;
	node->rowMarks = rowMarks;
	node->mergeActionLists = mergeActionLists;
	node->mergeJoinConditions = mergeJoinConditions;
	node->epqParam = epqParam;

	/*
	 * For each result relation that is a foreign table, allow the FDW to
	 * construct private plan data, and accumulate it all into a list.
	 */
	fdw_private_list = NIL;
	direct_modify_plans = NULL;
	i = 0;
	foreach(lc, resultRelations)
	{
		Index		rti = lfirst_int(lc);
		FdwRoutine *fdwroutine;
		List	   *fdw_private;
		bool		direct_modify;

		/*
		 * If possible, we want to get the FdwRoutine from our RelOptInfo for
		 * the table.  But sometimes we don't have a RelOptInfo and must get
		 * it the hard way.  (In INSERT, the target relation is not scanned,
		 * so it's not a baserel; and there are also corner cases for
		 * updatable views where the target rel isn't a baserel.)
		 */
		if (rti < root->simple_rel_array_size &&
			root->simple_rel_array[rti] != NULL)
		{
			RelOptInfo *resultRel = root->simple_rel_array[rti];

			fdwroutine = resultRel->fdwroutine;
		}
		else
		{
			RangeTblEntry *rte = planner_rt_fetch(rti, root);

			if (rte->rtekind == RTE_RELATION &&
				rte->relkind == RELKIND_FOREIGN_TABLE)
			{
				/* Check if the access to foreign tables is restricted */
				if (unlikely((restrict_nonsystem_relation_kind & RESTRICT_RELKIND_FOREIGN_TABLE) != 0))
				{
					/* there must not be built-in foreign tables */
					Assert(rte->relid >= FirstNormalObjectId);
					ereport(ERROR,
							(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
							 errmsg("access to non-system foreign table is restricted")));
				}

				fdwroutine = GetFdwRoutineByRelId(rte->relid);
			}
			else
				fdwroutine = NULL;
		}

		/*
		 * MERGE is not currently supported for foreign tables.  We already
		 * checked that when the table mentioned in the query is foreign; but
		 * we can still get here if a partitioned table has a foreign table as
		 * partition.  Disallow that now, to avoid an uglier error message
		 * later.
		 */
		if (operation == CMD_MERGE && fdwroutine != NULL)
		{
			RangeTblEntry *rte = planner_rt_fetch(rti, root);

			ereport(ERROR,
					errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					errmsg("cannot execute MERGE on relation \"%s\"",
						   get_rel_name(rte->relid)),
					errdetail_relkind_not_supported(rte->relkind));
		}

		/*
		 * Try to modify the foreign table directly if (1) the FDW provides
		 * callback functions needed for that and (2) there are no local
		 * structures that need to be run for each modified row: row-level
		 * triggers on the foreign table, stored generated columns, WITH CHECK
		 * OPTIONs from parent views, Vars returning OLD/NEW in the RETURNING
		 * list, or transition tables on the named relation.
		 */
		direct_modify = false;
		if (fdwroutine != NULL &&
			fdwroutine->PlanDirectModify != NULL &&
			fdwroutine->BeginDirectModify != NULL &&
			fdwroutine->IterateDirectModify != NULL &&
			fdwroutine->EndDirectModify != NULL &&
			withCheckOptionLists == NIL &&
			!has_row_triggers(root, rti, operation) &&
			!has_stored_generated_columns(root, rti))
		{
			/*
			 * returning_old_or_new and transition_tables are the same for all
			 * result relations, respectively
			 */
			if (!returning_old_or_new_valid)
			{
				returning_old_or_new =
					contain_vars_returning_old_or_new((Node *)
													  root->parse->returningList);
				returning_old_or_new_valid = true;
			}
			if (!returning_old_or_new)
			{
				if (!transition_tables_valid)
				{
					transition_tables = has_transition_tables(root,
															  nominalRelation,
															  operation);
					transition_tables_valid = true;
				}
				if (!transition_tables)
					direct_modify = fdwroutine->PlanDirectModify(root, node,
																 rti, i);
			}
		}
		if (direct_modify)
			direct_modify_plans = bms_add_member(direct_modify_plans, i);

		if (!direct_modify &&
			fdwroutine != NULL &&
			fdwroutine->PlanForeignModify != NULL)
			fdw_private = fdwroutine->PlanForeignModify(root, node, rti, i);
		else
			fdw_private = NIL;
		fdw_private_list = lappend(fdw_private_list, fdw_private);
		i++;
	}
	node->fdwPrivLists = fdw_private_list;
	node->fdwDirectModifyPlans = direct_modify_plans;

	return node;
}

/*
 * is_projection_capable_path
 *		Check whether a given Path node is able to do projection.
 */
bool
is_projection_capable_path(Path *path)
{
	/* Most plan types can project, so just list the ones that can't */
	switch (path->pathtype)
	{
		case T_Hash:
		case T_Material:
		case T_Memoize:
		case T_Sort:
		case T_IncrementalSort:
		case T_Unique:
		case T_SetOp:
		case T_LockRows:
		case T_Limit:
		case T_ModifyTable:
		case T_MergeAppend:
		case T_RecursiveUnion:
			return false;
		case T_CustomScan:
			if (castNode(CustomPath, path)->flags & CUSTOMPATH_SUPPORT_PROJECTION)
				return true;
			return false;
		case T_Append:

			/*
			 * Append can't project, but if an AppendPath is being used to
			 * represent a dummy path, what will actually be generated is a
			 * Result which can project.
			 */
			return IS_DUMMY_APPEND(path);
		case T_ProjectSet:

			/*
			 * Although ProjectSet certainly projects, say "no" because we
			 * don't want the planner to randomly replace its tlist with
			 * something else; the SRFs have to stay at top level.  This might
			 * get relaxed later.
			 */
			return false;
		default:
			break;
	}
	return true;
}

/*
 * is_projection_capable_plan
 *		Check whether a given Plan node is able to do projection.
 */
bool
is_projection_capable_plan(Plan *plan)
{
	/* Most plan types can project, so just list the ones that can't */
	switch (nodeTag(plan))
	{
		case T_Hash:
		case T_Material:
		case T_Memoize:
		case T_Sort:
		case T_Unique:
		case T_SetOp:
		case T_LockRows:
		case T_Limit:
		case T_ModifyTable:
		case T_Append:
		case T_MergeAppend:
		case T_RecursiveUnion:
			return false;
		case T_CustomScan:
			if (((CustomScan *) plan)->flags & CUSTOMPATH_SUPPORT_PROJECTION)
				return true;
			return false;
		case T_ProjectSet:

			/*
			 * Although ProjectSet certainly projects, say "no" because we
			 * don't want the planner to randomly replace its tlist with
			 * something else; the SRFs have to stay at top level.  This might
			 * get relaxed later.
			 */
			return false;
		default:
			break;
	}
	return true;
}
./execIndexing.c0000664000175000017500000011454415221603750012475 0ustar  xmanxman/*-------------------------------------------------------------------------
 *
 * execIndexing.c
 *	  routines for inserting index tuples and enforcing unique and
 *	  exclusion constraints.
 *
 * ExecInsertIndexTuples() is the main entry point.  It's called after
 * inserting a tuple to the heap, and it inserts corresponding index tuples
 * into all indexes.  At the same time, it enforces any unique and
 * exclusion constraints:
 *
 * Unique Indexes
 * --------------
 *
 * Enforcing a unique constraint is straightforward.  When the index AM
 * inserts the tuple to the index, it also checks that there are no
 * conflicting tuples in the index already.  It does so atomically, so that
 * even if two backends try to insert the same key concurrently, only one
 * of them will succeed.  All the logic to ensure atomicity, and to wait
 * for in-progress transactions to finish, is handled by the index AM.
 *
 * If a unique constraint is deferred, we request the index AM to not
 * throw an error if a conflict is found.  Instead, we make note that there
 * was a conflict and return the list of indexes with conflicts to the
 * caller.  The caller must re-check them later, by calling index_insert()
 * with the UNIQUE_CHECK_EXISTING option.
 *
 * Exclusion Constraints
 * ---------------------
 *
 * Exclusion constraints are different from unique indexes in that when the
 * tuple is inserted to the index, the index AM does not check for
 * duplicate keys at the same time.  After the insertion, we perform a
 * separate scan on the index to check for conflicting tuples, and if one
 * is found, we throw an error and the transaction is aborted.  If the
 * conflicting tuple's inserter or deleter is in-progress, we wait for it
 * to finish first.
 *
 * There is a chance of deadlock, if two backends insert a tuple at the
 * same time, and then perform the scan to check for conflicts.  They will
 * find each other's tuple, and both try to wait for each other.  The
 * deadlock detector will detect that, and abort one of the transactions.
 * That's fairly harmless, as one of them was bound to abort with a
 * "duplicate key error" anyway, although you get a different error
 * message.
 *
 * If an exclusion constraint is deferred, we still perform the conflict
 * checking scan immediately after inserting the index tuple.  But instead
 * of throwing an error if a conflict is found, we return that information
 * to the caller.  The caller must re-check them later by calling
 * check_exclusion_constraint().
 *
 * Speculative insertion
 * ---------------------
 *
 * Speculative insertion is a two-phase mechanism used to implement
 * INSERT ... ON CONFLICT.  The tuple is first inserted into the heap
 * and the indexes are updated as usual, but if a constraint is violated,
 * we can still back out of the insertion without aborting the whole
 * transaction.  In an INSERT ... ON CONFLICT statement, if a conflict is
 * detected, the inserted tuple is backed out and the ON CONFLICT action is
 * executed instead.
 *
 * Insertion to a unique index works as usual: the index AM checks for
 * duplicate keys atomically with the insertion.  But instead of throwing
 * an error on a conflict, the speculatively inserted heap tuple is backed
 * out.
 *
 * Exclusion constraints are slightly more complicated.  As mentioned
 * earlier, there is a risk of deadlock when two backends insert the same
 * key concurrently.  That was not a problem for regular insertions, when
 * one of the transactions has to be aborted anyway, but with a speculative
 * insertion we cannot let a deadlock happen, because we only want to back
 * out the speculatively inserted tuple on conflict, not abort the whole
 * transaction.
 *
 * When a backend detects that the speculative insertion conflicts with
 * another in-progress tuple, it has two options:
 *
 * 1. back out the speculatively inserted tuple, then wait for the other
 *	  transaction, and retry. Or,
 * 2. wait for the other transaction, with the speculatively inserted tuple
 *	  still in place.
 *
 * If two backends insert at the same time, and both try to wait for each
 * other, they will deadlock.  So option 2 is not acceptable.  Option 1
 * avoids the deadlock, but it is prone to a livelock instead.  Both
 * transactions will wake up immediately as the other transaction backs
 * out.  Then they both retry, and conflict with each other again, lather,
 * rinse, repeat.
 *
 * To avoid the livelock, one of the backends must back out first, and then
 * wait, while the other one waits without backing out.  It doesn't matter
 * which one backs out, so we employ an arbitrary rule that the transaction
 * with the higher XID backs out.
 *
 *
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/executor/execIndexing.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/genam.h"
#include "access/relscan.h"
#include "access/tableam.h"
#include "access/xact.h"
#include "catalog/index.h"
#include "executor/executor.h"
#include "nodes/nodeFuncs.h"
#include "storage/lmgr.h"
#include "utils/injection_point.h"
#include "utils/lsyscache.h"
#include "utils/multirangetypes.h"
#include "utils/rangetypes.h"
#include "utils/snapmgr.h"

/* waitMode argument to check_exclusion_or_unique_constraint() */
typedef enum
{
	CEOUC_WAIT,
	CEOUC_NOWAIT,
	CEOUC_LIVELOCK_PREVENTING_WAIT,
} CEOUC_WAIT_MODE;

static bool check_exclusion_or_unique_constraint(Relation heap, Relation index,
												 IndexInfo *indexInfo,
												 const ItemPointerData *tupleid,
												 const Datum *values, const bool *isnull,
												 EState *estate, bool newIndex,
												 CEOUC_WAIT_MODE waitMode,
												 bool violationOK,
												 ItemPointer conflictTid);

static bool index_recheck_constraint(Relation index, const Oid *constr_procs,
									 const Datum *existing_values, const bool *existing_isnull,
									 const Datum *new_values);
static bool index_unchanged_by_update(ResultRelInfo *resultRelInfo,
									  EState *estate, IndexInfo *indexInfo,
									  Relation indexRelation);
static bool index_expression_changed_walker(Node *node,
											Bitmapset *allUpdatedCols);
static void ExecWithoutOverlapsNotEmpty(Relation rel, NameData attname, Datum attval,
										char typtype, Oid atttypid);

/* ----------------------------------------------------------------
 *		ExecOpenIndices
 *
 *		Find the indices associated with a result relation, open them,
 *		and save information about them in the result ResultRelInfo.
 *
 *		At entry, caller has already opened and locked
 *		resultRelInfo->ri_RelationDesc.
 * ----------------------------------------------------------------
 */
void
ExecOpenIndices(ResultRelInfo *resultRelInfo, bool speculative)
{
	Relation	resultRelation = resultRelInfo->ri_RelationDesc;
	List	   *indexoidlist;
	ListCell   *l;
	int			len,
				i;
	RelationPtr relationDescs;
	IndexInfo **indexInfoArray;

	resultRelInfo->ri_NumIndices = 0;

	/* fast path if no indexes */
	if (!RelationGetForm(resultRelation)->relhasindex)
		return;

	/*
	 * Get cached list of index OIDs
	 */
	indexoidlist = RelationGetIndexList(resultRelation);
	len = list_length(indexoidlist);
	if (len == 0)
		return;

	/* This Assert will fail if ExecOpenIndices is called twice */
	Assert(resultRelInfo->ri_IndexRelationDescs == NULL);

	/*
	 * allocate space for result arrays
	 */
	relationDescs = palloc_array(Relation, len);
	indexInfoArray = palloc_array(IndexInfo *, len);

	resultRelInfo->ri_NumIndices = len;
	resultRelInfo->ri_IndexRelationDescs = relationDescs;
	resultRelInfo->ri_IndexRelationInfo = indexInfoArray;

	/*
	 * For each index, open the index relation and save pg_index info. We
	 * acquire RowExclusiveLock, signifying we will update the index.
	 *
	 * Note: we do this even if the index is not indisready; it's not worth
	 * the trouble to optimize for the case where it isn't.
	 */
	i = 0;
	foreach(l, indexoidlist)
	{
		Oid			indexOid = lfirst_oid(l);
		Relation	indexDesc;
		IndexInfo  *ii;

		indexDesc = index_open(indexOid, RowExclusiveLock);

		/* extract index key information from the index's pg_index info */
		ii = BuildIndexInfo(indexDesc);

		/*
		 * If the indexes are to be used for speculative insertion, add extra
		 * information required by unique index entries.
		 */
		if (speculative && ii->ii_Unique && !indexDesc->rd_index->indisexclusion)
			BuildSpeculativeIndexInfo(indexDesc, ii);

		relationDescs[i] = indexDesc;
		indexInfoArray[i] = ii;
		i++;
	}

	list_free(indexoidlist);
}

/* ----------------------------------------------------------------
 *		ExecCloseIndices
 *
 *		Close the index relations stored in resultRelInfo
 * ----------------------------------------------------------------
 */
void
ExecCloseIndices(ResultRelInfo *resultRelInfo)
{
	int			i;
	int			numIndices;
	RelationPtr indexDescs;
	IndexInfo **indexInfos;

	numIndices = resultRelInfo->ri_NumIndices;
	indexDescs = resultRelInfo->ri_IndexRelationDescs;
	indexInfos = resultRelInfo->ri_IndexRelationInfo;

	for (i = 0; i < numIndices; i++)
	{
		/* This Assert will fail if ExecCloseIndices is called twice */
		Assert(indexDescs[i] != NULL);

		/* Give the index a chance to do some post-insert cleanup */
		index_insert_cleanup(indexDescs[i], indexInfos[i]);

		/* Drop lock acquired by ExecOpenIndices */
		index_close(indexDescs[i], RowExclusiveLock);

		/* Mark the index as closed */
		indexDescs[i] = NULL;
	}

	/*
	 * We don't attempt to free the IndexInfo data structures or the arrays,
	 * instead assuming that such stuff will be cleaned up automatically in
	 * FreeExecutorState.
	 */
}

/* ----------------------------------------------------------------
 *		ExecInsertIndexTuples
 *
 *		This routine takes care of inserting index tuples
 *		into all the relations indexing the result relation
 *		when a heap tuple is inserted into the result relation.
 *
 *		When EIIT_IS_UPDATE is set and EIIT_ONLY_SUMMARIZING isn't,
 *		executor is performing an UPDATE that could not use an
 *		optimization like heapam's HOT (in more general terms a
 *		call to table_tuple_update() took place and set
 *		'update_indexes' to TU_All).  Receiving this hint makes
 *		us consider if we should pass down the 'indexUnchanged'
 *		hint in turn.  That's something that we figure out for
 *		each index_insert() call iff EIIT_IS_UPDATE is set.
 *		(When that flag is not set we already know not to pass the
 *		hint to any index.)
 *
 *		If EIIT_ONLY_SUMMARIZING is set, an equivalent optimization to
 *		HOT has been applied and any updated columns are indexed
 *		only by summarizing indexes (or in more general terms a
 *		call to table_tuple_update() took place and set
 *		'update_indexes' to TU_Summarizing). We can (and must)
 *		therefore only update the indexes that have
 *		'amsummarizing' = true.
 *
 *		Unique and exclusion constraints are enforced at the same
 *		time.  This returns a list of index OIDs for any unique or
 *		exclusion constraints that are deferred and that had
 *		potential (unconfirmed) conflicts.  (if EIIT_NO_DUPE_ERROR,
 *		the same is done for non-deferred constraints, but report
 *		if conflict was speculative or deferred conflict to caller)
 *
 *		If 'arbiterIndexes' is nonempty, EIIT_NO_DUPE_ERROR applies only to
 *		those indexes.  NIL means EIIT_NO_DUPE_ERROR applies to all indexes.
 * ----------------------------------------------------------------
 */
List *
ExecInsertIndexTuples(ResultRelInfo *resultRelInfo,
					  EState *estate,
					  uint32 flags,
					  TupleTableSlot *slot,
					  List *arbiterIndexes,
					  bool *specConflict)
{
	ItemPointer tupleid = &slot->tts_tid;
	List	   *result = NIL;
	int			i;
	int			numIndices;
	RelationPtr relationDescs;
	Relation	heapRelation;
	IndexInfo **indexInfoArray;
	ExprContext *econtext;
	Datum		values[INDEX_MAX_KEYS];
	bool		isnull[INDEX_MAX_KEYS];

	Assert(ItemPointerIsValid(tupleid));

	/*
	 * Get information from the result relation info structure.
	 */
	numIndices = resultRelInfo->ri_NumIndices;
	relationDescs = resultRelInfo->ri_IndexRelationDescs;
	indexInfoArray = resultRelInfo->ri_IndexRelationInfo;
	heapRelation = resultRelInfo->ri_RelationDesc;

	/* Sanity check: slot must belong to the same rel as the resultRelInfo. */
	Assert(slot->tts_tableOid == RelationGetRelid(heapRelation));

	/*
	 * We will use the EState's per-tuple context for evaluating predicates
	 * and index expressions (creating it if it's not already there).
	 */
	econtext = GetPerTupleExprContext(estate);

	/* Arrange for econtext's scan tuple to be the tuple under test */
	econtext->ecxt_scantuple = slot;

	/*
	 * for each index, form and insert the index tuple
	 */
	for (i = 0; i < numIndices; i++)
	{
		Relation	indexRelation = relationDescs[i];
		IndexInfo  *indexInfo;
		bool		applyNoDupErr;
		IndexUniqueCheck checkUnique;
		bool		indexUnchanged;
		bool		satisfiesConstraint;

		if (indexRelation == NULL)
			continue;

		indexInfo = indexInfoArray[i];

		/* If the index is marked as read-only, ignore it */
		if (!indexInfo->ii_ReadyForInserts)
			continue;

		/*
		 * Skip processing of non-summarizing indexes if we only update
		 * summarizing indexes
		 */
		if ((flags & EIIT_ONLY_SUMMARIZING) && !indexInfo->ii_Summarizing)
			continue;

		/* Check for partial index */
		if (indexInfo->ii_Predicate != NIL)
		{
			ExprState  *predicate;
			ExprState  *predicateExpand;

			/*
			 * If predicate state not set up yet, create it (in the estate's
			 * per-query context)
			 */
			predicate = indexInfo->ii_PredicateState;
			predicateExpand = indexInfo->ii_PredicateExpandState;
			if (predicate == NULL)
			{
				predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
				predicateExpand = ExecPrepareQual(indexInfo->ii_PredicateExpand, estate);
				indexInfo->ii_PredicateState = predicate;
				indexInfo->ii_PredicateExpandState = predicateExpand;
			}

			/* Skip this index-update if the predicate isn't satisfied */
			if (!ExecQual(predicateExpand, econtext))
				continue;
		}

		/*
		 * FormIndexDatum fills in its values and isnull parameters with the
		 * appropriate values for the column(s) of the index.
		 */
		FormIndexDatum(indexInfo,
					   slot,
					   estate,
					   values,
					   isnull);

		/* Check whether to apply noDupErr to this index */
		applyNoDupErr = (flags & EIIT_NO_DUPE_ERROR) &&
			(arbiterIndexes == NIL ||
			 list_member_oid(arbiterIndexes,
							 indexRelation->rd_index->indexrelid));

		/*
		 * The index AM does the actual insertion, plus uniqueness checking.
		 *
		 * For an immediate-mode unique index, we just tell the index AM to
		 * throw error if not unique.
		 *
		 * For a deferrable unique index, we tell the index AM to just detect
		 * possible non-uniqueness, and we add the index OID to the result
		 * list if further checking is needed.
		 *
		 * For a speculative insertion (used by INSERT ... ON CONFLICT), do
		 * the same as for a deferrable unique index.
		 */
		if (!indexRelation->rd_index->indisunique)
			checkUnique = UNIQUE_CHECK_NO;
		else if (applyNoDupErr)
			checkUnique = UNIQUE_CHECK_PARTIAL;
		else if (indexRelation->rd_index->indimmediate)
			checkUnique = UNIQUE_CHECK_YES;
		else
			checkUnique = UNIQUE_CHECK_PARTIAL;

		/*
		 * There's definitely going to be an index_insert() call for this
		 * index.  If we're being called as part of an UPDATE statement,
		 * consider if the 'indexUnchanged' = true hint should be passed.
		 */
		indexUnchanged = ((flags & EIIT_IS_UPDATE) &&
						  index_unchanged_by_update(resultRelInfo,
													estate,
													indexInfo,
													indexRelation));

		satisfiesConstraint =
			index_insert(indexRelation, /* index relation */
						 values,	/* array of index Datums */
						 isnull,	/* null flags */
						 tupleid,	/* tid of heap tuple */
						 heapRelation,	/* heap relation */
						 checkUnique,	/* type of uniqueness check to do */
						 indexUnchanged,	/* UPDATE without logical change? */
						 indexInfo);	/* index AM may need this */

		/*
		 * If the index has an associated exclusion constraint, check that.
		 * This is simpler than the process for uniqueness checks since we
		 * always insert first and then check.  If the constraint is deferred,
		 * we check now anyway, but don't throw error on violation or wait for
		 * a conclusive outcome from a concurrent insertion; instead we'll
		 * queue a recheck event.  Similarly, noDupErr callers (speculative
		 * inserters) will recheck later, and wait for a conclusive outcome
		 * then.
		 *
		 * An index for an exclusion constraint can't also be UNIQUE (not an
		 * essential property, we just don't allow it in the grammar), so no
		 * need to preserve the prior state of satisfiesConstraint.
		 */
		if (indexInfo->ii_ExclusionOps != NULL)
		{
			bool		violationOK;
			CEOUC_WAIT_MODE waitMode;

			if (applyNoDupErr)
			{
				violationOK = true;
				waitMode = CEOUC_LIVELOCK_PREVENTING_WAIT;
			}
			else if (!indexRelation->rd_index->indimmediate)
			{
				violationOK = true;
				waitMode = CEOUC_NOWAIT;
			}
			else
			{
				violationOK = false;
				waitMode = CEOUC_WAIT;
			}

			satisfiesConstraint =
				check_exclusion_or_unique_constraint(heapRelation,
													 indexRelation, indexInfo,
													 tupleid, values, isnull,
													 estate, false,
													 waitMode, violationOK, NULL);
		}

		if ((checkUnique == UNIQUE_CHECK_PARTIAL ||
			 indexInfo->ii_ExclusionOps != NULL) &&
			!satisfiesConstraint)
		{
			/*
			 * The tuple potentially violates the uniqueness or exclusion
			 * constraint, so make a note of the index so that we can re-check
			 * it later.  Speculative inserters are told if there was a
			 * speculative conflict, since that always requires a restart.
			 */
			result = lappend_oid(result, RelationGetRelid(indexRelation));
			if (indexRelation->rd_index->indimmediate && specConflict)
				*specConflict = true;
		}
	}

	return result;
}

/* ----------------------------------------------------------------
 *		ExecCheckIndexConstraints
 *
 *		This routine checks if a tuple violates any unique or
 *		exclusion constraints.  Returns true if there is no conflict.
 *		Otherwise returns false, and the TID of the conflicting
 *		tuple is returned in *conflictTid.
 *
 *		If 'arbiterIndexes' is given, only those indexes are checked.
 *		NIL means all indexes.
 *
 *		Note that this doesn't lock the values in any way, so it's
 *		possible that a conflicting tuple is inserted immediately
 *		after this returns.  This can be used for either a pre-check
 *		before insertion or a re-check after finding a conflict.
 *
 *		'tupleid' should be the TID of the tuple that has been recently
 *		inserted (or can be invalid if we haven't inserted a new tuple yet).
 *		This tuple will be excluded from conflict checking.
 * ----------------------------------------------------------------
 */
bool
ExecCheckIndexConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
						  EState *estate, ItemPointer conflictTid,
						  const ItemPointerData *tupleid, List *arbiterIndexes)
{
	int			i;
	int			numIndices;
	RelationPtr relationDescs;
	Relation	heapRelation;
	IndexInfo **indexInfoArray;
	ExprContext *econtext;
	Datum		values[INDEX_MAX_KEYS];
	bool		isnull[INDEX_MAX_KEYS];
	ItemPointerData invalidItemPtr;
	bool		checkedIndex = false;

	ItemPointerSetInvalid(conflictTid);
	ItemPointerSetInvalid(&invalidItemPtr);

	/*
	 * Get information from the result relation info structure.
	 */
	numIndices = resultRelInfo->ri_NumIndices;
	relationDescs = resultRelInfo->ri_IndexRelationDescs;
	indexInfoArray = resultRelInfo->ri_IndexRelationInfo;
	heapRelation = resultRelInfo->ri_RelationDesc;

	/*
	 * We will use the EState's per-tuple context for evaluating predicates
	 * and index expressions (creating it if it's not already there).
	 */
	econtext = GetPerTupleExprContext(estate);

	/* Arrange for econtext's scan tuple to be the tuple under test */
	econtext->ecxt_scantuple = slot;

	/*
	 * For each index, form index tuple and check if it satisfies the
	 * constraint.
	 */
	for (i = 0; i < numIndices; i++)
	{
		Relation	indexRelation = relationDescs[i];
		IndexInfo  *indexInfo;
		bool		satisfiesConstraint;

		if (indexRelation == NULL)
			continue;

		indexInfo = indexInfoArray[i];

		if (!indexInfo->ii_Unique && !indexInfo->ii_ExclusionOps)
			continue;

		/* If the index is marked as read-only, ignore it */
		if (!indexInfo->ii_ReadyForInserts)
			continue;

		/* When specific arbiter indexes requested, only examine them */
		if (arbiterIndexes != NIL &&
			!list_member_oid(arbiterIndexes,
							 indexRelation->rd_index->indexrelid))
			continue;

		if (!indexRelation->rd_index->indimmediate)
			ereport(ERROR,
					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
					 errmsg("ON CONFLICT does not support deferrable unique constraints/exclusion constraints as arbiters"),
					 errtableconstraint(heapRelation,
										RelationGetRelationName(indexRelation))));

		checkedIndex = true;

		/* Check for partial index */
		if (indexInfo->ii_Predicate != NIL)
		{
			ExprState  *predicate;
			ExprState  *predicateExpand;

			/*
			 * If predicate state not set up yet, create it (in the estate's
			 * per-query context)
			 */
			predicate = indexInfo->ii_PredicateState;
			predicateExpand = indexInfo->ii_PredicateExpandState;
			if (predicate == NULL)
			{
				predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
				predicateExpand = ExecPrepareQual(indexInfo->ii_PredicateExpand, estate);
				indexInfo->ii_PredicateState = predicate;
				indexInfo->ii_PredicateExpandState = predicateExpand;
			}

			/* Skip this index-update if the predicate isn't satisfied */
			if (!ExecQual(predicateExpand, econtext))
				continue;
		}

		/*
		 * FormIndexDatum fills in its values and isnull parameters with the
		 * appropriate values for the column(s) of the index.
		 */
		FormIndexDatum(indexInfo,
					   slot,
					   estate,
					   values,
					   isnull);

		satisfiesConstraint =
			check_exclusion_or_unique_constraint(heapRelation, indexRelation,
												 indexInfo, tupleid,
												 values, isnull, estate, false,
												 CEOUC_WAIT, true,
												 conflictTid);
		if (!satisfiesConstraint)
			return false;
	}

	if (arbiterIndexes != NIL && !checkedIndex)
		elog(ERROR, "unexpected failure to find arbiter index");

	return true;
}

/*
 * Check for violation of an exclusion or unique constraint
 *
 * heap: the table containing the new tuple
 * index: the index supporting the constraint
 * indexInfo: info about the index, including the exclusion properties
 * tupleid: heap TID of the new tuple we have just inserted (invalid if we
 *		haven't inserted a new tuple yet)
 * values, isnull: the *index* column values computed for the new tuple
 * estate: an EState we can do evaluation in
 * newIndex: if true, we are trying to build a new index (this affects
 *		only the wording of error messages)
 * waitMode: whether to wait for concurrent inserters/deleters
 * violationOK: if true, don't throw error for violation
 * conflictTid: if not-NULL, the TID of the conflicting tuple is returned here
 *
 * Returns true if OK, false if actual or potential violation
 *
 * 'waitMode' determines what happens if a conflict is detected with a tuple
 * that was inserted or deleted by a transaction that's still running.
 * CEOUC_WAIT means that we wait for the transaction to commit, before
 * throwing an error or returning.  CEOUC_NOWAIT means that we report the
 * violation immediately; so the violation is only potential, and the caller
 * must recheck sometime later.  This behavior is convenient for deferred
 * exclusion checks; we need not bother queuing a deferred event if there is
 * definitely no conflict at insertion time.
 *
 * CEOUC_LIVELOCK_PREVENTING_WAIT is like CEOUC_NOWAIT, but we will sometimes
 * wait anyway, to prevent livelocking if two transactions try inserting at
 * the same time.  This is used with speculative insertions, for INSERT ON
 * CONFLICT statements. (See notes in file header)
 *
 * If violationOK is true, we just report the potential or actual violation to
 * the caller by returning 'false'.  Otherwise we throw a descriptive error
 * message here.  When violationOK is false, a false result is impossible.
 *
 * Note: The indexam is normally responsible for checking unique constraints,
 * so this normally only needs to be used for exclusion constraints.  But this
 * function is also called when doing a "pre-check" for conflicts on a unique
 * constraint, when doing speculative insertion.  Caller may use the returned
 * conflict TID to take further steps.
 */
static bool
check_exclusion_or_unique_constraint(Relation heap, Relation index,
									 IndexInfo *indexInfo,
									 const ItemPointerData *tupleid,
									 const Datum *values, const bool *isnull,
									 EState *estate, bool newIndex,
									 CEOUC_WAIT_MODE waitMode,
									 bool violationOK,
									 ItemPointer conflictTid)
{
	Oid		   *constr_procs;
	uint16	   *constr_strats;
	Oid		   *index_collations = index->rd_indcollation;
	int			indnkeyatts = IndexRelationGetNumberOfKeyAttributes(index);
	IndexScanDesc index_scan;
	ScanKeyData scankeys[INDEX_MAX_KEYS];
	SnapshotData DirtySnapshot;
	int			i;
	bool		conflict;
	bool		found_self;
	ExprContext *econtext;
	TupleTableSlot *existing_slot;
	TupleTableSlot *save_scantuple;

	if (indexInfo->ii_ExclusionOps)
	{
		constr_procs = indexInfo->ii_ExclusionProcs;
		constr_strats = indexInfo->ii_ExclusionStrats;
	}
	else
	{
		constr_procs = indexInfo->ii_UniqueProcs;
		constr_strats = indexInfo->ii_UniqueStrats;
	}

	/*
	 * If this is a WITHOUT OVERLAPS constraint, we must also forbid empty
	 * ranges/multiranges. This must happen before we look for NULLs below, or
	 * a UNIQUE constraint could insert an empty range along with a NULL
	 * scalar part.
	 */
	if (indexInfo->ii_WithoutOverlaps)
	{
		/*
		 * Look up the type from the heap tuple, but check the Datum from the
		 * index tuple.
		 */
		AttrNumber	attno = indexInfo->ii_IndexAttrNumbers[indnkeyatts - 1];

		if (!isnull[indnkeyatts - 1])
		{
			TupleDesc	tupdesc = RelationGetDescr(heap);
			Form_pg_attribute att = TupleDescAttr(tupdesc, attno - 1);
			TypeCacheEntry *typcache = lookup_type_cache(att->atttypid,
														 TYPECACHE_DOMAIN_BASE_INFO);
			char		typtype;

			if (OidIsValid(typcache->domainBaseType))
				typtype = get_typtype(typcache->domainBaseType);
			else
				typtype = typcache->typtype;

			ExecWithoutOverlapsNotEmpty(heap, att->attname,
										values[indnkeyatts - 1],
										typtype, att->atttypid);
		}
	}

	/*
	 * If any of the input values are NULL, and the index uses the default
	 * nulls-are-distinct mode, the constraint check is assumed to pass (i.e.,
	 * we assume the operators are strict).  Otherwise, we interpret the
	 * constraint as specifying IS NULL for each column whose input value is
	 * NULL.
	 */
	if (!indexInfo->ii_NullsNotDistinct)
	{
		for (i = 0; i < indnkeyatts; i++)
		{
			if (isnull[i])
				return true;
		}
	}

	/*
	 * Search the tuples that are in the index for any violations, including
	 * tuples that aren't visible yet.
	 */
	InitDirtySnapshot(DirtySnapshot);

	for (i = 0; i < indnkeyatts; i++)
	{
		ScanKeyEntryInitialize(&scankeys[i],
							   isnull[i] ? SK_ISNULL | SK_SEARCHNULL : 0,
							   i + 1,
							   constr_strats[i],
							   InvalidOid,
							   index_collations[i],
							   constr_procs[i],
							   values[i]);
	}

	/*
	 * Need a TupleTableSlot to put existing tuples in.
	 *
	 * To use FormIndexDatum, we have to make the econtext's scantuple point
	 * to this slot.  Be sure to save and restore caller's value for
	 * scantuple.
	 */
	existing_slot = table_slot_create(heap, NULL);

	econtext = GetPerTupleExprContext(estate);
	save_scantuple = econtext->ecxt_scantuple;
	econtext->ecxt_scantuple = existing_slot;

	/*
	 * May have to restart scan from this point if a potential conflict is
	 * found.
	 */
retry:
	conflict = false;
	found_self = false;
	index_scan = index_beginscan(heap, index,
								 &DirtySnapshot, NULL, indnkeyatts, 0,
								 SO_NONE);
	index_rescan(index_scan, scankeys, indnkeyatts, NULL, 0);

	while (index_getnext_slot(index_scan, ForwardScanDirection, existing_slot))
	{
		TransactionId xwait;
		XLTW_Oper	reason_wait;
		Datum		existing_values[INDEX_MAX_KEYS];
		bool		existing_isnull[INDEX_MAX_KEYS];
		char	   *error_new;
		char	   *error_existing;

		/*
		 * Ignore the entry for the tuple we're trying to check.
		 */
		if (ItemPointerIsValid(tupleid) &&
			ItemPointerEquals(tupleid, &existing_slot->tts_tid))
		{
			if (found_self)		/* should not happen */
				elog(ERROR, "found self tuple multiple times in index \"%s\"",
					 RelationGetRelationName(index));
			found_self = true;
			continue;
		}

		/*
		 * Extract the index column values and isnull flags from the existing
		 * tuple.
		 */
		FormIndexDatum(indexInfo, existing_slot, estate,
					   existing_values, existing_isnull);

		/* If lossy indexscan, must recheck the condition */
		if (index_scan->xs_recheck)
		{
			if (!index_recheck_constraint(index,
										  constr_procs,
										  existing_values,
										  existing_isnull,
										  values))
				continue;		/* tuple doesn't actually match, so no
								 * conflict */
		}

		/*
		 * At this point we have either a conflict or a potential conflict.
		 *
		 * If an in-progress transaction is affecting the visibility of this
		 * tuple, we need to wait for it to complete and then recheck (unless
		 * the caller requested not to).  For simplicity we do rechecking by
		 * just restarting the whole scan --- this case probably doesn't
		 * happen often enough to be worth trying harder, and anyway we don't
		 * want to hold any index internal locks while waiting.
		 */
		xwait = TransactionIdIsValid(DirtySnapshot.xmin) ?
			DirtySnapshot.xmin : DirtySnapshot.xmax;

		if (TransactionIdIsValid(xwait) &&
			(waitMode == CEOUC_WAIT ||
			 (waitMode == CEOUC_LIVELOCK_PREVENTING_WAIT &&
			  DirtySnapshot.speculativeToken &&
			  TransactionIdPrecedes(GetCurrentTransactionId(), xwait))))
		{
			reason_wait = indexInfo->ii_ExclusionOps ?
				XLTW_RecheckExclusionConstr : XLTW_InsertIndex;
			index_endscan(index_scan);
			if (DirtySnapshot.speculativeToken)
				SpeculativeInsertionWait(DirtySnapshot.xmin,
										 DirtySnapshot.speculativeToken);
			else
				XactLockTableWait(xwait, heap,
								  &existing_slot->tts_tid, reason_wait);
			goto retry;
		}

		/*
		 * We have a definite conflict (or a potential one, but the caller
		 * didn't want to wait).  Return it to caller, or report it.
		 */
		if (violationOK)
		{
			conflict = true;
			if (conflictTid)
				*conflictTid = existing_slot->tts_tid;
			break;
		}

		error_new = BuildIndexValueDescription(index, values, isnull);
		error_existing = BuildIndexValueDescription(index, existing_values,
													existing_isnull);
		if (newIndex)
			ereport(ERROR,
					(errcode(ERRCODE_EXCLUSION_VIOLATION),
					 errmsg("could not create exclusion constraint \"%s\"",
							RelationGetRelationName(index)),
					 error_new && error_existing ?
					 errdetail("Key %s conflicts with key %s.",
							   error_new, error_existing) :
					 errdetail("Key conflicts exist."),
					 errtableconstraint(heap,
										RelationGetRelationName(index))));
		else
			ereport(ERROR,
					(errcode(ERRCODE_EXCLUSION_VIOLATION),
					 errmsg("conflicting key value violates exclusion constraint \"%s\"",
							RelationGetRelationName(index)),
					 error_new && error_existing ?
					 errdetail("Key %s conflicts with existing key %s.",
							   error_new, error_existing) :
					 errdetail("Key conflicts with existing key."),
					 errtableconstraint(heap,
										RelationGetRelationName(index))));
	}

	index_endscan(index_scan);

	/*
	 * Ordinarily, at this point the search should have found the originally
	 * inserted tuple (if any), unless we exited the loop early because of
	 * conflict.  However, it is possible to define exclusion constraints for
	 * which that wouldn't be true --- for instance, if the operator is <>. So
	 * we no longer complain if found_self is still false.
	 */

	econtext->ecxt_scantuple = save_scantuple;

	ExecDropSingleTupleTableSlot(existing_slot);

#ifdef USE_INJECTION_POINTS
	if (!conflict)
		INJECTION_POINT("check-exclusion-or-unique-constraint-no-conflict", NULL);
#endif

	return !conflict;
}

/*
 * Check for violation of an exclusion constraint
 *
 * This is a dumbed down version of check_exclusion_or_unique_constraint
 * for external callers. They don't need all the special modes.
 */
void
check_exclusion_constraint(Relation heap, Relation index,
						   IndexInfo *indexInfo,
						   const ItemPointerData *tupleid,
						   const Datum *values, const bool *isnull,
						   EState *estate, bool newIndex)
{
	(void) check_exclusion_or_unique_constraint(heap, index, indexInfo, tupleid,
												values, isnull,
												estate, newIndex,
												CEOUC_WAIT, false, NULL);
}

/*
 * Check existing tuple's index values to see if it really matches the
 * exclusion condition against the new_values.  Returns true if conflict.
 */
static bool
index_recheck_constraint(Relation index, const Oid *constr_procs,
						 const Datum *existing_values, const bool *existing_isnull,
						 const Datum *new_values)
{
	int			indnkeyatts = IndexRelationGetNumberOfKeyAttributes(index);
	int			i;

	for (i = 0; i < indnkeyatts; i++)
	{
		/* Assume the exclusion operators are strict */
		if (existing_isnull[i])
			return false;

		if (!DatumGetBool(OidFunctionCall2Coll(constr_procs[i],
											   index->rd_indcollation[i],
											   existing_values[i],
											   new_values[i])))
			return false;
	}

	return true;
}

/*
 * Check if ExecInsertIndexTuples() should pass indexUnchanged hint.
 *
 * When the executor performs an UPDATE that requires a new round of index
 * tuples, determine if we should pass 'indexUnchanged' = true hint for one
 * single index.
 */
static bool
index_unchanged_by_update(ResultRelInfo *resultRelInfo, EState *estate,
						  IndexInfo *indexInfo, Relation indexRelation)
{
	Bitmapset  *updatedCols;
	Bitmapset  *extraUpdatedCols;
	Bitmapset  *allUpdatedCols;
	bool		hasexpression = false;
	List	   *idxExprs;

	/*
	 * Check cache first
	 */
	if (indexInfo->ii_CheckedUnchanged)
		return indexInfo->ii_IndexUnchanged;
	indexInfo->ii_CheckedUnchanged = true;

	/*
	 * Check for indexed attribute overlap with updated columns.
	 *
	 * Only do this for key columns.  A change to a non-key column within an
	 * INCLUDE index should not be counted here.  Non-key column values are
	 * opaque payload state to the index AM, a little like an extra table TID.
	 *
	 * Note that row-level BEFORE triggers won't affect our behavior, since
	 * they don't affect the updatedCols bitmaps generally.  It doesn't seem
	 * worth the trouble of checking which attributes were changed directly.
	 */
	updatedCols = ExecGetUpdatedCols(resultRelInfo, estate);
	extraUpdatedCols = ExecGetExtraUpdatedCols(resultRelInfo, estate);
	for (int attr = 0; attr < indexInfo->ii_NumIndexKeyAttrs; attr++)
	{
		int			keycol = indexInfo->ii_IndexAttrNumbers[attr];

		if (keycol <= 0)
		{
			/*
			 * Skip expressions for now, but remember to deal with them later
			 * on
			 */
			hasexpression = true;
			continue;
		}

		if (bms_is_member(keycol - FirstLowInvalidHeapAttributeNumber,
						  updatedCols) ||
			bms_is_member(keycol - FirstLowInvalidHeapAttributeNumber,
						  extraUpdatedCols))
		{
			/* Changed key column -- don't hint for this index */
			indexInfo->ii_IndexUnchanged = false;
			return false;
		}
	}

	/*
	 * When we get this far and index has no expressions, return true so that
	 * index_insert() call will go on to pass 'indexUnchanged' = true hint.
	 *
	 * The _absence_ of an indexed key attribute that overlaps with updated
	 * attributes (in addition to the total absence of indexed expressions)
	 * shows that the index as a whole is logically unchanged by UPDATE.
	 */
	if (!hasexpression)
	{
		indexInfo->ii_IndexUnchanged = true;
		return true;
	}

	/*
	 * Need to pass only one bms to expression_tree_walker helper function.
	 * Avoid allocating memory in common case where there are no extra cols.
	 */
	if (!extraUpdatedCols)
		allUpdatedCols = updatedCols;
	else
		allUpdatedCols = bms_union(updatedCols, extraUpdatedCols);

	/*
	 * We have to work slightly harder in the event of indexed expressions,
	 * but the principle is the same as before: try to find columns (Vars,
	 * actually) that overlap with known-updated columns.
	 *
	 * If we find any matching Vars, don't pass hint for index.  Otherwise
	 * pass hint.
	 */
	idxExprs = RelationGetIndexExpressions(indexRelation);
	idxExprs = list_concat(idxExprs, RelationGetIndexExpressionsExpand(indexRelation));
	hasexpression = index_expression_changed_walker((Node *) idxExprs,
													allUpdatedCols);
	list_free(idxExprs);
	if (extraUpdatedCols)
		bms_free(allUpdatedCols);

	if (hasexpression)
	{
		indexInfo->ii_IndexUnchanged = false;
		return false;
	}

	/*
	 * Deliberately don't consider index predicates.  We should even give the
	 * hint when result rel's "updated tuple" has no corresponding index
	 * tuple, which is possible with a partial index (provided the usual
	 * conditions are met).
	 */
	indexInfo->ii_IndexUnchanged = true;
	return true;
}

/*
 * Indexed expression helper for index_unchanged_by_update().
 *
 * Returns true when Var that appears within allUpdatedCols located.
 */
static bool
index_expression_changed_walker(Node *node, Bitmapset *allUpdatedCols)
{
	if (node == NULL)
		return false;

	if (IsA(node, Var))
	{
		Var		   *var = (Var *) node;

		if (bms_is_member(var->varattno - FirstLowInvalidHeapAttributeNumber,
						  allUpdatedCols))
		{
			/* Var was updated -- indicates that we should not hint */
			return true;
		}

		/* Still haven't found a reason to not pass the hint */
		return false;
	}

	return expression_tree_walker(node, index_expression_changed_walker,
								  allUpdatedCols);
}

/*
 * ExecWithoutOverlapsNotEmpty - raise an error if the tuple has an empty
 * range or multirange in the given attribute.
 */
static void
ExecWithoutOverlapsNotEmpty(Relation rel, NameData attname, Datum attval, char typtype, Oid atttypid)
{
	bool		isempty;
	RangeType  *r;
	MultirangeType *mr;

	switch (typtype)
	{
		case TYPTYPE_RANGE:
			r = DatumGetRangeTypeP(attval);
			isempty = RangeIsEmpty(r);
			break;
		case TYPTYPE_MULTIRANGE:
			mr = DatumGetMultirangeTypeP(attval);
			isempty = MultirangeIsEmpty(mr);
			break;
		default:
			elog(ERROR, "WITHOUT OVERLAPS column \"%s\" is not a range or multirange",
				 NameStr(attname));
	}

	/* Report a CHECK_VIOLATION */
	if (isempty)
		ereport(ERROR,
				(errcode(ERRCODE_CHECK_VIOLATION),
				 errmsg("empty WITHOUT OVERLAPS value found in column \"%s\" in relation \"%s\"",
						NameStr(attname), RelationGetRelationName(rel))));
}
./execnodes.h0000664000175000017500000031456715221603750012054 0ustar  xmanxman/*-------------------------------------------------------------------------
 *
 * execnodes.h
 *	  definitions for executor state nodes
 *
 * Most plan node types declared in plannodes.h have a corresponding
 * execution-state node type declared here.  An exception is that
 * expression nodes (subtypes of Expr) are usually represented by steps
 * of an ExprState, and fully handled within execExpr* - but sometimes
 * their state needs to be shared with other parts of the executor, as
 * for example with SubPlanState, which nodeSubplan.c has to modify.
 *
 * Node types declared in this file do not have any copy/equal/out/read
 * support.  (That is currently hard-wired in gen_node_support.pl, rather
 * than being explicitly represented by pg_node_attr decorations here.)
 * There is no need for copy, equal, or read support for executor trees.
 * Output support could be useful for debugging; but there are a lot of
 * specialized fields that would require custom code, so for now it's
 * not provided.
 *
 *
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/nodes/execnodes.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef EXECNODES_H
#define EXECNODES_H

#include "access/htup.h"
#include "executor/instrument_node.h"
#include "fmgr.h"
#include "lib/ilist.h"
#include "nodes/miscnodes.h"
#include "nodes/params.h"
#include "nodes/plannodes.h"
#include "partitioning/partdefs.h"
#include "storage/buf.h"
#include "utils/reltrigger.h"
#include "utils/typcache.h"


/*
 * forward references in this file
 */
typedef struct BufferUsage BufferUsage;
typedef struct ExecRowMark ExecRowMark;
typedef struct ExprState ExprState;
typedef struct ExprContext ExprContext;
typedef struct HTAB HTAB;
typedef struct Instrumentation Instrumentation;
typedef struct pairingheap pairingheap;
typedef struct PlanState PlanState;
typedef struct QueryEnvironment QueryEnvironment;
typedef struct RelationData *Relation;
typedef Relation *RelationPtr;
typedef struct ScanKeyData ScanKeyData;
typedef struct SnapshotData *Snapshot;
typedef struct SortSupportData *SortSupport;
typedef struct TIDBitmap TIDBitmap;
typedef struct NodeInstrumentation NodeInstrumentation;
typedef struct TriggerInstrumentation TriggerInstrumentation;
typedef struct TupleConversionMap TupleConversionMap;
typedef struct TupleDescData *TupleDesc;
typedef struct Tuplesortstate Tuplesortstate;
typedef struct Tuplestorestate Tuplestorestate;
typedef struct TupleTableSlot TupleTableSlot;
typedef struct TupleTableSlotOps TupleTableSlotOps;
typedef struct WalUsage WalUsage;
typedef struct WorkerNodeInstrumentation WorkerNodeInstrumentation;


/* ----------------
 *		ExprState node
 *
 * ExprState represents the evaluation state for a whole expression tree.
 * It contains instructions (in ->steps) to evaluate the expression.
 * ----------------
 */
typedef Datum (*ExprStateEvalFunc) (ExprState *expression,
									ExprContext *econtext,
									bool *isNull);

/* Bits in ExprState->flags (see also execExpr.h for private flag bits): */
/* expression is for use with ExecQual() */
#define EEO_FLAG_IS_QUAL					(1 << 0)
/* expression refers to OLD table columns */
#define EEO_FLAG_HAS_OLD					(1 << 1)
/* expression refers to NEW table columns */
#define EEO_FLAG_HAS_NEW					(1 << 2)
/* OLD table row is NULL in RETURNING list */
#define EEO_FLAG_OLD_IS_NULL				(1 << 3)
/* NEW table row is NULL in RETURNING list */
#define EEO_FLAG_NEW_IS_NULL				(1 << 4)

typedef struct ExprState
{
	NodeTag		type;

#define FIELDNO_EXPRSTATE_FLAGS 1
	uint8		flags;			/* bitmask of EEO_FLAG_* bits, see above */

	/*
	 * Storage for result value of a scalar expression, or for individual
	 * column results within expressions built by ExecBuildProjectionInfo().
	 */
#define FIELDNO_EXPRSTATE_RESNULL 2
	bool		resnull;
#define FIELDNO_EXPRSTATE_RESVALUE 3
	Datum		resvalue;

	/*
	 * If projecting a tuple result, this slot holds the result; else NULL.
	 */
#define FIELDNO_EXPRSTATE_RESULTSLOT 4
	TupleTableSlot *resultslot;

	/*
	 * Instructions to compute expression's return value.
	 */
	struct ExprEvalStep *steps;

	/*
	 * Function that actually evaluates the expression.  This can be set to
	 * different values depending on the complexity of the expression.
	 */
	ExprStateEvalFunc evalfunc;

	/* original expression tree, for debugging only */
	Expr	   *expr;

	/* private state for an evalfunc */
	void	   *evalfunc_private;

	/*
	 * XXX: following fields only needed during "compilation" (ExecInitExpr);
	 * could be thrown away afterwards.
	 */

	int			steps_len;		/* number of steps currently */
	int			steps_alloc;	/* allocated length of steps array */

#define FIELDNO_EXPRSTATE_PARENT 11
	PlanState  *parent;			/* parent PlanState node, if any */
	ParamListInfo ext_params;	/* for compiling PARAM_EXTERN nodes */

	Datum	   *innermost_caseval;
	bool	   *innermost_casenull;

	Datum	   *innermost_domainval;
	bool	   *innermost_domainnull;

	/*
	 * For expression nodes that support soft errors. Should be set to NULL if
	 * the caller wants errors to be thrown. Callers that do not want errors
	 * thrown should set it to a valid ErrorSaveContext before calling
	 * ExecInitExprRec().
	 */
	ErrorSaveContext *escontext;
} ExprState;


/* ----------------
 *	  IndexInfo information
 *
 *		this struct holds the information needed to construct new index
 *		entries for a particular index.  Used for both index_build and
 *		retail creation of index entries.
 *
 * ii_Concurrent, ii_BrokenHotChain, and ii_ParallelWorkers are used only
 * during index build; they're conventionally zeroed otherwise.
 * ----------------
 */
typedef struct IndexInfo
{
	NodeTag		type;

	/* total number of columns in index */
	int			ii_NumIndexAttrs;
	/* number of key columns in index */
	int			ii_NumIndexKeyAttrs;

	/*
	 * Underlying-rel attribute numbers used as keys (zeroes indicate
	 * expressions). It also contains info about included columns.
	 */
	AttrNumber	ii_IndexAttrNumbers[INDEX_MAX_KEYS];

	/* expr trees for expression entries, or NIL if none */
	List	   *ii_Expressions; /* list of Expr */
	List	   *ii_ExpressionsExpand; 	/* list of Expr */
	/* exec state for expressions, or NIL if none */
	List	   *ii_ExpressionsState;	/* list of ExprState */
	List	   *ii_ExpressionsExpandState;	/* list of ExprState */

	/* partial-index predicate, or NIL if none */
	List	   *ii_Predicate;	/* list of Expr */
	List	   *ii_PredicateExpand;		/* list of Expr */
	/* exec state for expressions, or NIL if none */
	ExprState  *ii_PredicateState;
	ExprState  *ii_PredicateExpandState;

	/* Per-column exclusion operators, or NULL if none */
	Oid		   *ii_ExclusionOps;	/* array with one entry per column */
	/* Underlying function OIDs for ExclusionOps */
	Oid		   *ii_ExclusionProcs;	/* array with one entry per column */
	/* Opclass strategy numbers for ExclusionOps */
	uint16	   *ii_ExclusionStrats; /* array with one entry per column */

	/* These are like Exclusion*, but for unique indexes */
	Oid		   *ii_UniqueOps;	/* array with one entry per column */
	Oid		   *ii_UniqueProcs; /* array with one entry per column */
	uint16	   *ii_UniqueStrats;	/* array with one entry per column */

	/* is it a unique index? */
	bool		ii_Unique;
	/* is NULLS NOT DISTINCT? */
	bool		ii_NullsNotDistinct;
	/* is it valid for inserts? */
	bool		ii_ReadyForInserts;
	/* IndexUnchanged status determined yet? */
	bool		ii_CheckedUnchanged;
	/* aminsert hint, cached for retail inserts */
	bool		ii_IndexUnchanged;
	/* are we doing a concurrent index build? */
	bool		ii_Concurrent;
	/* did we detect any broken HOT chains? */
	bool		ii_BrokenHotChain;
	/* is it a summarizing index? */
	bool		ii_Summarizing;
	/* is it a WITHOUT OVERLAPS index? */
	bool		ii_WithoutOverlaps;
	/* # of workers requested (excludes leader) */
	int			ii_ParallelWorkers;

	/* Oid of index AM */
	Oid			ii_Am;
	/* private cache area for index AM */
	void	   *ii_AmCache;

	/* memory context holding this IndexInfo */
	MemoryContext ii_Context;
} IndexInfo;

/* ----------------
 *	  ExprContext_CB
 *
 *		List of callbacks to be called at ExprContext shutdown.
 * ----------------
 */
typedef void (*ExprContextCallbackFunction) (Datum arg);

typedef struct ExprContext_CB
{
	struct ExprContext_CB *next;
	ExprContextCallbackFunction function;
	Datum		arg;
} ExprContext_CB;

/* ----------------
 *	  ExprContext
 *
 *		This class holds the "current context" information
 *		needed to evaluate expressions for doing tuple qualifications
 *		and tuple projections.  For example, if an expression refers
 *		to an attribute in the current inner tuple then we need to know
 *		what the current inner tuple is and so we look at the expression
 *		context.
 *
 *	There are two memory contexts associated with an ExprContext:
 *	* ecxt_per_query_memory is a query-lifespan context, typically the same
 *	  context the ExprContext node itself is allocated in.  This context
 *	  can be used for purposes such as storing function call cache info.
 *	* ecxt_per_tuple_memory is a short-term context for expression results.
 *	  As the name suggests, it will typically be reset once per tuple,
 *	  before we begin to evaluate expressions for that tuple.  Each
 *	  ExprContext normally has its very own per-tuple memory context.
 *
 *	CurrentMemoryContext should be set to ecxt_per_tuple_memory before
 *	calling ExecEvalExpr() --- see ExecEvalExprSwitchContext().
 * ----------------
 */
typedef struct ExprContext
{
	NodeTag		type;

	/* Tuples that Var nodes in expression may refer to */
#define FIELDNO_EXPRCONTEXT_SCANTUPLE 1
	TupleTableSlot *ecxt_scantuple;
#define FIELDNO_EXPRCONTEXT_INNERTUPLE 2
	TupleTableSlot *ecxt_innertuple;
#define FIELDNO_EXPRCONTEXT_OUTERTUPLE 3
	TupleTableSlot *ecxt_outertuple;

	/* Memory contexts for expression evaluation --- see notes above */
	MemoryContext ecxt_per_query_memory;
	MemoryContext ecxt_per_tuple_memory;

	/* Values to substitute for Param nodes in expression */
	ParamExecData *ecxt_param_exec_vals;	/* for PARAM_EXEC params */
	ParamListInfo ecxt_param_list_info; /* for other param types */

	/*
	 * Values to substitute for Aggref nodes in the expressions of an Agg
	 * node, or for WindowFunc nodes within a WindowAgg node.
	 */
#define FIELDNO_EXPRCONTEXT_AGGVALUES 8
	Datum	   *ecxt_aggvalues; /* precomputed values for aggs/windowfuncs */
#define FIELDNO_EXPRCONTEXT_AGGNULLS 9
	bool	   *ecxt_aggnulls;	/* null flags for aggs/windowfuncs */

	/* Value to substitute for CaseTestExpr nodes in expression */
#define FIELDNO_EXPRCONTEXT_CASEDATUM 10
	Datum		caseValue_datum;
#define FIELDNO_EXPRCONTEXT_CASENULL 11
	bool		caseValue_isNull;

	/* Value to substitute for CoerceToDomainValue nodes in expression */
#define FIELDNO_EXPRCONTEXT_DOMAINDATUM 12
	Datum		domainValue_datum;
#define FIELDNO_EXPRCONTEXT_DOMAINNULL 13
	bool		domainValue_isNull;

	/* Tuples that OLD/NEW Var nodes in RETURNING may refer to */
#define FIELDNO_EXPRCONTEXT_OLDTUPLE 14
	TupleTableSlot *ecxt_oldtuple;
#define FIELDNO_EXPRCONTEXT_NEWTUPLE 15
	TupleTableSlot *ecxt_newtuple;

	/* Link to containing EState (NULL if a standalone ExprContext) */
	struct EState *ecxt_estate;

	/* Functions to call back when ExprContext is shut down or rescanned */
	ExprContext_CB *ecxt_callbacks;
} ExprContext;

/*
 * Set-result status used when evaluating functions potentially returning a
 * set.
 */
typedef enum
{
	ExprSingleResult,			/* expression does not return a set */
	ExprMultipleResult,			/* this result is an element of a set */
	ExprEndResult,				/* there are no more elements in the set */
} ExprDoneCond;

/*
 * Return modes for functions returning sets.  Note values must be chosen
 * as separate bits so that a bitmask can be formed to indicate supported
 * modes.  SFRM_Materialize_Random and SFRM_Materialize_Preferred are
 * auxiliary flags about SFRM_Materialize mode, rather than separate modes.
 */
typedef enum
{
	SFRM_ValuePerCall = 0x01,	/* one value returned per call */
	SFRM_Materialize = 0x02,	/* result set instantiated in Tuplestore */
	SFRM_Materialize_Random = 0x04, /* Tuplestore needs randomAccess */
	SFRM_Materialize_Preferred = 0x08,	/* caller prefers Tuplestore */
} SetFunctionReturnMode;

/*
 * When calling a function that might return a set (multiple rows),
 * a node of this type is passed as fcinfo->resultinfo to allow
 * return status to be passed back.  A function returning set should
 * raise an error if no such resultinfo is provided.
 */
typedef struct ReturnSetInfo
{
	NodeTag		type;
	/* values set by caller: */
	ExprContext *econtext;		/* context function is being called in */
	TupleDesc	expectedDesc;	/* tuple descriptor expected by caller */
	int			allowedModes;	/* bitmask: return modes caller can handle */
	/* result status from function (but pre-initialized by caller): */
	SetFunctionReturnMode returnMode;	/* actual return mode */
	ExprDoneCond isDone;		/* status for ValuePerCall mode */
	/* fields filled by function in Materialize return mode: */
	Tuplestorestate *setResult; /* holds the complete returned tuple set */
	TupleDesc	setDesc;		/* actual descriptor for returned tuples */
} ReturnSetInfo;

/* ----------------
 *		ProjectionInfo node information
 *
 *		This is all the information needed to perform projections ---
 *		that is, form new tuples by evaluation of targetlist expressions.
 *		Nodes which need to do projections create one of these.
 *
 *		The target tuple slot is kept in ProjectionInfo->pi_state.resultslot.
 *		ExecProject() evaluates the tlist, forms a tuple, and stores it
 *		in the given slot.  Note that the result will be a "virtual" tuple
 *		unless ExecMaterializeSlot() is then called to force it to be
 *		converted to a physical tuple.  The slot must have a tupledesc
 *		that matches the output of the tlist!
 * ----------------
 */
typedef struct ProjectionInfo
{
	NodeTag		type;
	/* instructions to evaluate projection */
	ExprState	pi_state;
	/* expression context in which to evaluate expression */
	ExprContext *pi_exprContext;
} ProjectionInfo;

/* ----------------
 *	  JunkFilter
 *
 *	  This class is used to store information regarding junk attributes.
 *	  A junk attribute is an attribute in a tuple that is needed only for
 *	  storing intermediate information in the executor, and does not belong
 *	  in emitted tuples.  For example, when we do an UPDATE query,
 *	  the planner adds a "junk" entry to the targetlist so that the tuples
 *	  returned to ExecutePlan() contain an extra attribute: the ctid of
 *	  the tuple to be updated.  This is needed to do the update, but we
 *	  don't want the ctid to be part of the stored new tuple!  So, we
 *	  apply a "junk filter" to remove the junk attributes and form the
 *	  real output tuple.  The junkfilter code also provides routines to
 *	  extract the values of the junk attribute(s) from the input tuple.
 *
 *	  targetList:		the original target list (including junk attributes).
 *	  cleanTupType:		the tuple descriptor for the "clean" tuple (with
 *						junk attributes removed).
 *	  cleanMap:			A map with the correspondence between the non-junk
 *						attribute numbers of the "original" tuple and the
 *						attribute numbers of the "clean" tuple.
 *	  resultSlot:		tuple slot used to hold cleaned tuple.
 * ----------------
 */
typedef struct JunkFilter
{
	NodeTag		type;
	List	   *jf_targetList;
	TupleDesc	jf_cleanTupType;
	AttrNumber *jf_cleanMap;
	TupleTableSlot *jf_resultSlot;
} JunkFilter;

/*
 * OnConflictActionState
 *
 * Executor state of an ON CONFLICT DO SELECT/UPDATE operation.
 */
typedef struct OnConflictActionState
{
	NodeTag		type;

	TupleTableSlot *oc_Existing;	/* slot to store existing target tuple in */
	TupleTableSlot *oc_ProjSlot;	/* CONFLICT ... SET ... projection target */
	ProjectionInfo *oc_ProjInfo;	/* for ON CONFLICT DO UPDATE SET */
	LockClauseStrength oc_LockStrength; /* lock strength for DO SELECT */
	ExprState  *oc_WhereClause; /* state for the WHERE clause */
} OnConflictActionState;

/* ----------------
 *	 MergeActionState information
 *
 *	Executor state for a MERGE action.
 * ----------------
 */
typedef struct MergeActionState
{
	NodeTag		type;

	MergeAction *mas_action;	/* associated MergeAction node */
	ProjectionInfo *mas_proj;	/* projection of the action's targetlist for
								 * this rel */
	ExprState  *mas_whenqual;	/* WHEN [NOT] MATCHED AND conditions */
} MergeActionState;

/*
 * ForPortionOfState
 *
 * Executor state of a FOR PORTION OF operation.
 */
typedef struct ForPortionOfState
{
	NodeTag		type;

	char	   *fp_rangeName;	/* the column named in FOR PORTION OF */
	Oid			fp_rangeType;	/* the base type (not domain) of the FOR
								 * PORTION OF expression */
	int			fp_rangeAttno;	/* the attno of the range column */
	Datum		fp_targetRange; /* the range/multirange from FOR PORTION OF */
	TypeCacheEntry *fp_leftoverstypcache;	/* type cache entry of the range */
	TupleTableSlot *fp_Existing;	/* slot to store old tuple */
	TupleTableSlot *fp_Leftover;	/* slot to store leftover */
} ForPortionOfState;

/*
 * ResultRelInfo
 *
 * Whenever we update an existing relation, we have to update indexes on the
 * relation, and perhaps also fire triggers.  ResultRelInfo holds all the
 * information needed about a result relation, including indexes.
 *
 * Normally, a ResultRelInfo refers to a table that is in the query's range
 * table; then ri_RangeTableIndex is the RT index and ri_RelationDesc is
 * just a copy of the relevant es_relations[] entry.  However, in some
 * situations we create ResultRelInfos for relations that are not in the
 * range table, namely for targets of tuple routing in a partitioned table,
 * and when firing triggers in tables other than the target tables (See
 * ExecGetTriggerResultRel).  In these situations, ri_RangeTableIndex is 0
 * and ri_RelationDesc is a separately-opened relcache pointer that needs to
 * be separately closed.
 */
typedef struct ResultRelInfo
{
	NodeTag		type;

	/* result relation's range table index, or 0 if not in range table */
	Index		ri_RangeTableIndex;

	/* relation descriptor for result relation */
	Relation	ri_RelationDesc;

	/* # of indices existing on result relation */
	int			ri_NumIndices;

	/* array of relation descriptors for indices */
	RelationPtr ri_IndexRelationDescs;

	/* array of key/attr info for indices */
	IndexInfo **ri_IndexRelationInfo;

	/*
	 * For UPDATE/DELETE/MERGE result relations, the attribute number of the
	 * row identity junk attribute in the source plan's output tuples
	 */
	AttrNumber	ri_RowIdAttNo;

	/* For UPDATE, attnums of generated columns to be computed */
	Bitmapset  *ri_extraUpdatedCols;
	/* true if the above has been computed */
	bool		ri_extraUpdatedCols_valid;

	/* Projection to generate new tuple in an INSERT/UPDATE */
	ProjectionInfo *ri_projectNew;
	/* Slot to hold that tuple */
	TupleTableSlot *ri_newTupleSlot;
	/* Slot to hold the old tuple being updated */
	TupleTableSlot *ri_oldTupleSlot;
	/* Have the projection and the slots above been initialized? */
	bool		ri_projectNewInfoValid;

	/* updates do LockTuple() before oldtup read; see README.tuplock */
	bool		ri_needLockTagTuple;

	/* triggers to be fired, if any */
	TriggerDesc *ri_TrigDesc;

	/* cached lookup info for trigger functions */
	FmgrInfo   *ri_TrigFunctions;

	/* array of trigger WHEN expr states */
	ExprState **ri_TrigWhenExprs;

	/* optional runtime measurements for triggers */
	TriggerInstrumentation *ri_TrigInstrument;

	/* On-demand created slots for triggers / returning processing */
	TupleTableSlot *ri_ReturningSlot;	/* for trigger output tuples */
	TupleTableSlot *ri_TrigOldSlot; /* for a trigger's old tuple */
	TupleTableSlot *ri_TrigNewSlot; /* for a trigger's new tuple */
	TupleTableSlot *ri_AllNullSlot; /* for RETURNING OLD/NEW */

	/* FDW callback functions, if foreign table */
	struct FdwRoutine *ri_FdwRoutine;

	/* available to save private state of FDW */
	void	   *ri_FdwState;

	/* true when modifying foreign table directly */
	bool		ri_usesFdwDirectModify;

	/* batch insert stuff */
	int			ri_NumSlots;	/* number of slots in the array */
	int			ri_NumSlotsInitialized; /* number of initialized slots */
	int			ri_BatchSize;	/* max slots inserted in a single batch */
	TupleTableSlot **ri_Slots;	/* input tuples for batch insert */
	TupleTableSlot **ri_PlanSlots;

	/* list of WithCheckOption's to be checked */
	List	   *ri_WithCheckOptions;

	/* list of WithCheckOption expr states */
	List	   *ri_WithCheckOptionExprs;

	/* array of expr states for checking check constraints */
	ExprState **ri_CheckConstraintExprs;

	/*
	 * array of expr states for checking not-null constraints on virtual
	 * generated columns
	 */
	ExprState **ri_GenVirtualNotNullConstraintExprs;

	/*
	 * Arrays of stored generated columns ExprStates for INSERT/UPDATE/MERGE.
	 */
	ExprState **ri_GeneratedExprsI;
	ExprState **ri_GeneratedExprsU;

	/* number of stored generated columns we need to compute */
	int			ri_NumGeneratedNeededI;
	int			ri_NumGeneratedNeededU;

	/* list of RETURNING expressions */
	List	   *ri_returningList;

	/* for computing a RETURNING list */
	ProjectionInfo *ri_projectReturning;

	/* list of arbiter indexes to use to check conflicts */
	List	   *ri_onConflictArbiterIndexes;

	/* ON CONFLICT evaluation state for DO SELECT/UPDATE */
	OnConflictActionState *ri_onConflict;

	/* for MERGE, lists of MergeActionState (one per MergeMatchKind) */
	List	   *ri_MergeActions[NUM_MERGE_MATCH_KINDS];

	/* for MERGE, expr state for checking the join condition */
	ExprState  *ri_MergeJoinCondition;

	/* FOR PORTION OF evaluation state */
	ForPortionOfState *ri_forPortionOf;

	/* partition check expression state (NULL if not set up yet) */
	ExprState  *ri_PartitionCheckExpr;

	/*
	 * Map to convert child result relation tuples to the format of the table
	 * actually mentioned in the query (called "root").  Computed only if
	 * needed.  A NULL map value indicates that no conversion is needed, so we
	 * must have a separate flag to show if the map has been computed.
	 */
	TupleConversionMap *ri_ChildToRootMap;
	bool		ri_ChildToRootMapValid;

	/*
	 * As above, but in the other direction.
	 */
	TupleConversionMap *ri_RootToChildMap;
	bool		ri_RootToChildMapValid;

	/*
	 * Other information needed by child result relations
	 *
	 * ri_RootResultRelInfo gives the target relation mentioned in the query.
	 * Used as the root for tuple routing and/or transition capture.
	 *
	 * ri_PartitionTupleSlot is non-NULL if the relation is a partition to
	 * route tuples into and ri_RootToChildMap conversion is needed.
	 */
	struct ResultRelInfo *ri_RootResultRelInfo;
	TupleTableSlot *ri_PartitionTupleSlot;

	/* for use by copyfrom.c when performing multi-inserts */
	struct CopyMultiInsertBuffer *ri_CopyMultiInsertBuffer;

	/*
	 * Used when a leaf partition is involved in a cross-partition update of
	 * one of its ancestors; see ExecCrossPartitionUpdateForeignKey().
	 */
	List	   *ri_ancestorResultRels;
} ResultRelInfo;

/* ----------------
 *	  AsyncRequest
 *
 * State for an asynchronous tuple request.
 * ----------------
 */
typedef struct AsyncRequest
{
	PlanState  *requestor;		/* Node that wants a tuple */
	PlanState  *requestee;		/* Node from which a tuple is wanted */
	int			request_index;	/* Scratch space for requestor */
	bool		callback_pending;	/* Callback is needed */
	bool		request_complete;	/* Request complete, result valid */
	TupleTableSlot *result;		/* Result (NULL or an empty slot if no more
								 * tuples) */
} AsyncRequest;

/* ----------------
 *	  EState information
 *
 * Working state for an Executor invocation
 * ----------------
 */
typedef struct EState
{
	NodeTag		type;

	/* Basic state for all query types: */
	ScanDirection es_direction; /* current scan direction */
	Snapshot	es_snapshot;	/* time qual to use */
	Snapshot	es_crosscheck_snapshot; /* crosscheck time qual for RI */
	List	   *es_range_table; /* List of RangeTblEntry */
	Index		es_range_table_size;	/* size of the range table arrays */
	Relation   *es_relations;	/* Array of per-range-table-entry Relation
								 * pointers, or NULL if not yet opened */
	ExecRowMark **es_rowmarks;	/* Array of per-range-table-entry
								 * ExecRowMarks, or NULL if none */
	List	   *es_rteperminfos;	/* List of RTEPermissionInfo */
	PlannedStmt *es_plannedstmt;	/* link to top of plan tree */
	List	   *es_part_prune_infos;	/* List of PartitionPruneInfo */
	List	   *es_part_prune_states;	/* List of PartitionPruneState */
	List	   *es_part_prune_results;	/* List of Bitmapset */
	Bitmapset  *es_unpruned_relids; /* PlannedStmt.unprunableRelids + RT
									 * indexes of leaf partitions that survive
									 * initial pruning; see
									 * ExecDoInitialPruning() */
	const char *es_sourceText;	/* Source text from QueryDesc */

	JunkFilter *es_junkFilter;	/* top-level junk filter, if any */

	/* If query can insert/delete tuples, the command ID to mark them with */
	CommandId	es_output_cid;

	/* Info about target table(s) for insert/update/delete queries: */
	ResultRelInfo **es_result_relations;	/* Array of per-range-table-entry
											 * ResultRelInfo pointers, or NULL
											 * if not a target table */
	List	   *es_opened_result_relations; /* List of non-NULL entries in
											 * es_result_relations in no
											 * specific order */

	PartitionDirectory es_partition_directory;	/* for PartitionDesc lookup */

	/*
	 * The following list contains ResultRelInfos created by the tuple routing
	 * code for partitions that aren't found in the es_result_relations array.
	 */
	List	   *es_tuple_routing_result_relations;

	/* Stuff used for firing triggers: */
	List	   *es_trig_target_relations;	/* trigger-only ResultRelInfos */

	/* Parameter info: */
	ParamListInfo es_param_list_info;	/* values of external params */
	ParamExecData *es_param_exec_vals;	/* values of internal params */

	QueryEnvironment *es_queryEnv;	/* query environment */

	/* Other working state: */
	MemoryContext es_query_cxt; /* per-query context in which EState lives */

	List	   *es_tupleTable;	/* List of TupleTableSlots */

	uint64		es_processed;	/* # of tuples processed during one
								 * ExecutorRun() call. */
	uint64		es_total_processed; /* total # of tuples aggregated across all
									 * ExecutorRun() calls. */

	int			es_top_eflags;	/* eflags passed to ExecutorStart */
	int			es_instrument;	/* OR of InstrumentOption flags */
	bool		es_finished;	/* true when ExecutorFinish is done */

	List	   *es_exprcontexts;	/* List of ExprContexts within EState */

	List	   *es_subplanstates;	/* List of PlanState for SubPlans */

	List	   *es_auxmodifytables; /* List of secondary ModifyTableStates */

	/*
	 * this ExprContext is for per-output-tuple operations, such as constraint
	 * checks and index-value computations.  It will be reset for each output
	 * tuple.  Note that it will be created only if needed.
	 */
	ExprContext *es_per_tuple_exprcontext;

	/*
	 * If not NULL, this is an EPQState's EState. This is a field in EState
	 * both to allow EvalPlanQual aware executor nodes to detect that they
	 * need to perform EPQ related work, and to provide necessary information
	 * to do so.
	 */
	struct EPQState *es_epq_active;

	bool		es_use_parallel_mode;	/* can we use parallel workers? */

	int			es_parallel_workers_to_launch;	/* number of workers to
												 * launch. */
	int			es_parallel_workers_launched;	/* number of workers actually
												 * launched. */

	/* The per-query shared memory area to use for parallel execution. */
	struct dsa_area *es_query_dsa;

	/*
	 * JIT information. es_jit_flags indicates whether JIT should be performed
	 * and with which options.  es_jit is created on-demand when JITing is
	 * performed.
	 *
	 * es_jit_worker_instr is the combined, on demand allocated,
	 * instrumentation from all workers. The leader's instrumentation is kept
	 * separate, and is combined on demand by ExplainPrintJITSummary().
	 */
	int			es_jit_flags;
	struct JitContext *es_jit;
	struct JitInstrumentation *es_jit_worker_instr;

	/*
	 * Lists of ResultRelInfos for foreign tables on which batch-inserts are
	 * to be executed and owning ModifyTableStates, stored in the same order.
	 */
	List	   *es_insert_pending_result_relations;
	List	   *es_insert_pending_modifytables;
} EState;


/*
 * ExecRowMark -
 *	   runtime representation of FOR [KEY] UPDATE/SHARE clauses
 *
 * When doing UPDATE/DELETE/MERGE/SELECT FOR [KEY] UPDATE/SHARE, we will have
 * an ExecRowMark for each non-target relation in the query (except inheritance
 * parent RTEs, which can be ignored at runtime).  Virtual relations such as
 * subqueries-in-FROM will have an ExecRowMark with relation == NULL.  See
 * PlanRowMark for details about most of the fields.  In addition to fields
 * directly derived from PlanRowMark, we store an activity flag (to denote
 * inactive children of inheritance trees), curCtid, which is used by the
 * WHERE CURRENT OF code, and ermExtra, which is available for use by the plan
 * node that sources the relation (e.g., for a foreign table the FDW can use
 * ermExtra to hold information).
 *
 * EState->es_rowmarks is an array of these structs, indexed by RT index,
 * with NULLs for irrelevant RT indexes.  es_rowmarks itself is NULL if
 * there are no rowmarks.
 */
typedef struct ExecRowMark
{
	Relation	relation;		/* opened and suitably locked relation */
	Oid			relid;			/* its OID (or InvalidOid, if subquery) */
	Index		rti;			/* its range table index */
	Index		prti;			/* parent range table index, if child */
	Index		rowmarkId;		/* unique identifier for resjunk columns */
	RowMarkType markType;		/* see enum in nodes/plannodes.h */
	LockClauseStrength strength;	/* LockingClause's strength, or LCS_NONE */
	LockWaitPolicy waitPolicy;	/* NOWAIT and SKIP LOCKED */
	bool		ermActive;		/* is this mark relevant for current tuple? */
	ItemPointerData curCtid;	/* ctid of currently locked tuple, if any */
	void	   *ermExtra;		/* available for use by relation source node */
} ExecRowMark;

/*
 * ExecAuxRowMark -
 *	   additional runtime representation of FOR [KEY] UPDATE/SHARE clauses
 *
 * Each LockRows and ModifyTable node keeps a list of the rowmarks it needs to
 * deal with.  In addition to a pointer to the related entry in es_rowmarks,
 * this struct carries the column number(s) of the resjunk columns associated
 * with the rowmark (see comments for PlanRowMark for more detail).
 */
typedef struct ExecAuxRowMark
{
	ExecRowMark *rowmark;		/* related entry in es_rowmarks */
	AttrNumber	ctidAttNo;		/* resno of ctid junk attribute, if any */
	AttrNumber	toidAttNo;		/* resno of tableoid junk attribute, if any */
	AttrNumber	wholeAttNo;		/* resno of whole-row junk attribute, if any */
} ExecAuxRowMark;


/* ----------------------------------------------------------------
 *				 Tuple Hash Tables
 *
 * All-in-memory tuple hash tables are used for a number of purposes.
 *
 * Note: tab_hash_expr is for hashing the key datatype(s) stored in the table,
 * and tab_eq_func is a non-cross-type ExprState for equality checks on those
 * types.  Normally these are the only ExprStates used, but
 * FindTupleHashEntry() supports searching a hashtable using cross-data-type
 * hashing.  For that, the caller must supply an ExprState to hash the LHS
 * datatype as well as the cross-type equality ExprState to use.  in_hash_expr
 * and cur_eq_func are set to point to the caller's hash and equality
 * ExprStates while doing such a search.  During LookupTupleHashEntry(), they
 * point to tab_hash_expr and tab_eq_func respectively.
 * ----------------------------------------------------------------
 */
typedef struct TupleHashEntryData *TupleHashEntry;
typedef struct TupleHashTableData *TupleHashTable;

/*
 * TupleHashEntryData is a slot in the tuplehash_hash table.  If it's
 * populated, it contains a pointer to a MinimalTuple that can also have
 * associated "additional data".  That's stored in the TupleHashTable's
 * tuplescxt.
 */
typedef struct TupleHashEntryData
{
	MinimalTuple firstTuple;	/* -> copy of first tuple in this group */
	uint32		status;			/* hash status */
	uint32		hash;			/* hash value (cached) */
} TupleHashEntryData;

/* define parameters necessary to generate the tuple hash table interface */
#define SH_PREFIX tuplehash
#define SH_ELEMENT_TYPE TupleHashEntryData
#define SH_KEY_TYPE MinimalTuple
#define SH_SCOPE extern
#define SH_DECLARE
#include "lib/simplehash.h"

typedef struct TupleHashTableData
{
	tuplehash_hash *hashtab;	/* underlying simplehash hash table */
	int			numCols;		/* number of columns in lookup key */
	AttrNumber *keyColIdx;		/* attr numbers of key columns */
	ExprState  *tab_hash_expr;	/* ExprState for hashing table datatype(s) */
	ExprState  *tab_eq_func;	/* comparator for table datatype(s) */
	Oid		   *tab_collations; /* collations for hash and comparison */
	MemoryContext tuplescxt;	/* memory context storing hashed tuples */
	MemoryContext tempcxt;		/* context for function evaluations */
	Size		additionalsize; /* size of additional data */
	TupleTableSlot *tableslot;	/* slot for referencing table entries */
	/* The following fields are set transiently for each table search: */
	TupleTableSlot *inputslot;	/* current input tuple's slot */
	ExprState  *in_hash_expr;	/* ExprState for hashing input datatype(s) */
	ExprState  *cur_eq_func;	/* comparator for input vs. table */
	ExprContext *exprcontext;	/* expression context */
} TupleHashTableData;

typedef tuplehash_iterator TupleHashIterator;

/*
 * Use InitTupleHashIterator/TermTupleHashIterator for a read/write scan.
 * Use ResetTupleHashIterator if the table can be frozen (in this case no
 * explicit scan termination is needed).
 */
#define InitTupleHashIterator(htable, iter) \
	tuplehash_start_iterate(htable->hashtab, iter)
#define TermTupleHashIterator(iter) \
	((void) 0)
#define ResetTupleHashIterator(htable, iter) \
	InitTupleHashIterator(htable, iter)
#define ScanTupleHashTable(htable, iter) \
	tuplehash_iterate(htable->hashtab, iter)


/* ----------------------------------------------------------------
 *				 Expression State Nodes
 *
 * Formerly, there was a separate executor expression state node corresponding
 * to each node in a planned expression tree.  That's no longer the case; for
 * common expression node types, all the execution info is embedded into
 * step(s) in a single ExprState node.  But we still have a few executor state
 * node types for selected expression node types, mostly those in which info
 * has to be shared with other parts of the execution state tree.
 * ----------------------------------------------------------------
 */

/* ----------------
 *		WindowFuncExprState node
 * ----------------
 */
typedef struct WindowFuncExprState
{
	NodeTag		type;
	WindowFunc *wfunc;			/* expression plan node */
	List	   *args;			/* ExprStates for argument expressions */
	ExprState  *aggfilter;		/* FILTER expression */
	int			wfuncno;		/* ID number for wfunc within its plan node */
} WindowFuncExprState;


/* ----------------
 *		SetExprState node
 *
 * State for evaluating a potentially set-returning expression (like FuncExpr
 * or OpExpr).  In some cases, like some of the expressions in ROWS FROM(...)
 * the expression might not be a SRF, but nonetheless it uses the same
 * machinery as SRFs; it will be treated as a SRF returning a single row.
 * ----------------
 */
typedef struct SetExprState
{
	NodeTag		type;
	Expr	   *expr;			/* expression plan node */
	List	   *args;			/* ExprStates for argument expressions */

	/*
	 * In ROWS FROM, functions can be inlined, removing the FuncExpr normally
	 * inside.  In such a case this is the compiled expression (which cannot
	 * return a set), which'll be evaluated using regular ExecEvalExpr().
	 */
	ExprState  *elidedFuncState;

	/*
	 * Function manager's lookup info for the target function.  If func.fn_oid
	 * is InvalidOid, we haven't initialized it yet (nor any of the following
	 * fields, except funcReturnsSet).
	 */
	FmgrInfo	func;

	/*
	 * For a set-returning function (SRF) that returns a tuplestore, we keep
	 * the tuplestore here and dole out the result rows one at a time. The
	 * slot holds the row currently being returned.
	 */
	Tuplestorestate *funcResultStore;
	TupleTableSlot *funcResultSlot;

	/*
	 * In some cases we need to compute a tuple descriptor for the function's
	 * output.  If so, it's stored here.
	 */
	TupleDesc	funcResultDesc;
	bool		funcReturnsTuple;	/* valid when funcResultDesc isn't NULL */

	/*
	 * Remember whether the function is declared to return a set.  This is set
	 * by ExecInitExpr, and is valid even before the FmgrInfo is set up.
	 */
	bool		funcReturnsSet;

	/*
	 * setArgsValid is true when we are evaluating a set-returning function
	 * that uses value-per-call mode and we are in the middle of a call
	 * series; we want to pass the same argument values to the function again
	 * (and again, until it returns ExprEndResult).  This indicates that
	 * fcinfo_data already contains valid argument data.
	 */
	bool		setArgsValid;

	/*
	 * Flag to remember whether we have registered a shutdown callback for
	 * this SetExprState.  We do so only if funcResultStore or setArgsValid
	 * has been set at least once (since all the callback is for is to release
	 * the tuplestore or clear setArgsValid).
	 */
	bool		shutdown_reg;	/* a shutdown callback is registered */

	/*
	 * Call parameter structure for the function.  This has been initialized
	 * (by InitFunctionCallInfoData) if func.fn_oid is valid.  It also saves
	 * argument values between calls, when setArgsValid is true.
	 */
	FunctionCallInfo fcinfo;
} SetExprState;

/* ----------------
 *		SubPlanState node
 * ----------------
 */
typedef struct SubPlanState
{
	NodeTag		type;
	SubPlan    *subplan;		/* expression plan node */
	PlanState  *planstate;		/* subselect plan's state tree */
	PlanState  *parent;			/* parent plan node's state tree */
	ExprState  *testexpr;		/* state of combining expression */
	HeapTuple	curTuple;		/* copy of most recent tuple from subplan */
	Datum		curArray;		/* most recent array from ARRAY() subplan */
	/* these are used when hashing the subselect's output: */
	TupleDesc	descRight;		/* subselect desc after projection */
	ProjectionInfo *projLeft;	/* for projecting lefthand exprs */
	ProjectionInfo *projRight;	/* for projecting subselect output */
	TupleHashTable hashtable;	/* hash table for no-nulls subselect rows */
	TupleHashTable hashnulls;	/* hash table for rows with null(s) */
	bool		havehashrows;	/* true if hashtable is not empty */
	bool		havenullrows;	/* true if hashnulls is not empty */
	MemoryContext tuplesContext;	/* context containing hash tables' tuples */
	ExprContext *innerecontext; /* econtext for computing inner tuples */
	int			numCols;		/* number of columns being hashed */
	/* each of the remaining fields is an array of length numCols: */
	AttrNumber *keyColIdx;		/* control data for hash tables */
	Oid		   *tab_eq_funcoids;	/* equality func oids for table
									 * datatype(s) */
	Oid		   *tab_collations; /* collations for hash and comparison */
	FmgrInfo   *tab_hash_funcs; /* hash functions for table datatype(s) */
	ExprState  *lhs_hash_expr;	/* hash expr for lefthand datatype(s) */
	FmgrInfo   *cur_eq_funcs;	/* equality functions for LHS vs. table */
	ExprState  *cur_eq_comp;	/* equality comparator for LHS vs. table */
} SubPlanState;

/*
 * DomainConstraintState - one item to check during CoerceToDomain
 *
 * Note: we consider this to be part of an ExprState tree, so we give it
 * a name following the xxxState convention.  But there's no directly
 * associated plan-tree node.
 */
typedef enum DomainConstraintType
{
	DOM_CONSTRAINT_NOTNULL,
	DOM_CONSTRAINT_CHECK,
} DomainConstraintType;

typedef struct DomainConstraintState
{
	NodeTag		type;
	DomainConstraintType constrainttype;	/* constraint type */
	char	   *name;			/* name of constraint (for error msgs) */
	Expr	   *check_expr;		/* for CHECK, a boolean expression */
	ExprState  *check_exprstate;	/* check_expr's eval state, or NULL */
} DomainConstraintState;

/*
 * State for JsonExpr evaluation, too big to inline.
 *
 * This contains the information going into and coming out of the
 * EEOP_JSONEXPR_PATH eval step.
 */
typedef struct JsonExprState
{
	/* original expression node */
	JsonExpr   *jsexpr;

	/* value/isnull for formatted_expr */
	NullableDatum formatted_expr;

	/* value/isnull for pathspec */
	NullableDatum pathspec;

	/* JsonPathVariable entries for passing_values */
	List	   *args;

	/*
	 * Output variables that drive the EEOP_JUMP_IF_NOT_TRUE steps that are
	 * added for ON ERROR and ON EMPTY expressions, if any.
	 *
	 * Reset for each evaluation of EEOP_JSONEXPR_PATH.
	 */

	/* Set to true if jsonpath evaluation cause an error.  */
	NullableDatum error;

	/* Set to true if the jsonpath evaluation returned 0 items. */
	NullableDatum empty;

	/*
	 * Addresses of steps that implement the non-ERROR variant of ON EMPTY and
	 * ON ERROR behaviors, respectively.
	 */
	int			jump_empty;
	int			jump_error;

	/*
	 * Address of the step to coerce the result value of jsonpath evaluation
	 * to the RETURNING type.  -1 if no coercion if JsonExpr.use_io_coercion
	 * is true.
	 */
	int			jump_eval_coercion;

	/*
	 * Address to jump to when skipping all the steps after performing
	 * ExecEvalJsonExprPath() so as to return whatever the JsonPath* function
	 * returned as is, that is, in the cases where there's no error and no
	 * coercion is necessary.
	 */
	int			jump_end;

	/*
	 * RETURNING type input function invocation info when
	 * JsonExpr.use_io_coercion is true.
	 */
	FunctionCallInfo input_fcinfo;

	/*
	 * For error-safe evaluation of coercions.  When the ON ERROR behavior is
	 * not ERROR, a pointer to this is passed to ExecInitExprRec() when
	 * initializing the coercion expressions or to ExecInitJsonCoercion().
	 *
	 * Reset for each evaluation of EEOP_JSONEXPR_PATH.
	 */
	ErrorSaveContext escontext;
} JsonExprState;


/* ----------------------------------------------------------------
 *				 Executor State Trees
 *
 * An executing query has a PlanState tree paralleling the Plan tree
 * that describes the plan.
 * ----------------------------------------------------------------
 */

/* ----------------
 *	 ExecProcNodeMtd
 *
 * This is the method called by ExecProcNode to return the next tuple
 * from an executor node.  It returns NULL, or an empty TupleTableSlot,
 * if no more tuples are available.
 * ----------------
 */
typedef TupleTableSlot *(*ExecProcNodeMtd) (PlanState *pstate);

/* ----------------
 *		PlanState node
 *
 * We never actually instantiate any PlanState nodes; this is just the common
 * abstract superclass for all PlanState-type nodes.
 * ----------------
 */
typedef struct PlanState
{
	pg_node_attr(abstract)

	NodeTag		type;

	Plan	   *plan;			/* associated Plan node */

	EState	   *state;			/* at execution time, states of individual
								 * nodes point to one EState for the whole
								 * top-level plan */

	ExecProcNodeMtd ExecProcNode;	/* function to return next tuple */
	ExecProcNodeMtd ExecProcNodeReal;	/* actual function, if above is a
										 * wrapper */

	NodeInstrumentation *instrument;	/* Optional runtime stats for this
										 * node */
	WorkerNodeInstrumentation *worker_instrument;	/* per-worker
													 * instrumentation */

	/* Per-worker JIT instrumentation */
	struct SharedJitInstrumentation *worker_jit_instrument;

	/*
	 * Common structural data for all Plan types.  These links to subsidiary
	 * state trees parallel links in the associated plan tree (except for the
	 * subPlan list, which does not exist in the plan tree).
	 */
	ExprState  *qual;			/* boolean qual condition */
	PlanState  *lefttree;		/* input plan tree(s) */
	PlanState  *righttree;

	List	   *initPlan;		/* Init SubPlanState nodes (un-correlated expr
								 * subselects) */
	List	   *subPlan;		/* SubPlanState nodes in my expressions */

	/*
	 * State for management of parameter-change-driven rescanning
	 */
	Bitmapset  *chgParam;		/* set of IDs of changed Params */

	/*
	 * Other run-time state needed by most if not all node types.
	 */
	TupleDesc	ps_ResultTupleDesc; /* node's return type */
	TupleTableSlot *ps_ResultTupleSlot; /* slot for my result tuples */
	ExprContext *ps_ExprContext;	/* node's expression-evaluation context */
	ProjectionInfo *ps_ProjInfo;	/* info for doing tuple projection */

	bool		async_capable;	/* true if node is async-capable */

	/*
	 * Scanslot's descriptor if known. This is a bit of a hack, but otherwise
	 * it's hard for expression compilation to optimize based on the
	 * descriptor, without encoding knowledge about all executor nodes.
	 */
	TupleDesc	scandesc;

	/*
	 * Define the slot types for inner, outer and scanslots for expression
	 * contexts with this state as a parent.  If *opsset is set, then
	 * *opsfixed indicates whether *ops is guaranteed to be the type of slot
	 * used. That means that every slot in the corresponding
	 * ExprContext.ecxt_*tuple will point to a slot of that type, while
	 * evaluating the expression.  If *opsfixed is false, but *ops is set,
	 * that indicates the most likely type of slot.
	 *
	 * The scan* fields are set by ExecInitScanTupleSlot(). If that's not
	 * called, nodes can initialize the fields themselves.
	 *
	 * If outer/inneropsset is false, the information is inferred on-demand
	 * using ExecGetResultSlotOps() on ->righttree/lefttree, using the
	 * corresponding node's resultops* fields.
	 *
	 * The result* fields are automatically set when ExecInitResultSlot is
	 * used (be it directly or when the slot is created by
	 * ExecAssignScanProjectionInfo() /
	 * ExecConditionalAssignProjectionInfo()).  If no projection is necessary
	 * ExecConditionalAssignProjectionInfo() defaults those fields to the scan
	 * operations.
	 */
	const TupleTableSlotOps *scanops;
	const TupleTableSlotOps *outerops;
	const TupleTableSlotOps *innerops;
	const TupleTableSlotOps *resultops;
	bool		scanopsfixed;
	bool		outeropsfixed;
	bool		inneropsfixed;
	bool		resultopsfixed;
	bool		scanopsset;
	bool		outeropsset;
	bool		inneropsset;
	bool		resultopsset;
} PlanState;

/* ----------------
 *	these are defined to avoid confusion problems with "left"
 *	and "right" and "inner" and "outer".  The convention is that
 *	the "left" plan is the "outer" plan and the "right" plan is
 *	the inner plan, but these make the code more readable.
 * ----------------
 */
#define innerPlanState(node)		(((PlanState *)(node))->righttree)
#define outerPlanState(node)		(((PlanState *)(node))->lefttree)

/* Macros for inline access to certain instrumentation counters */
#define InstrCountTuples2(node, delta) \
	do { \
		if (((PlanState *)(node))->instrument) \
			((PlanState *)(node))->instrument->ntuples2 += (delta); \
	} while (0)
#define InstrCountFiltered1(node, delta) \
	do { \
		if (((PlanState *)(node))->instrument) \
			((PlanState *)(node))->instrument->nfiltered1 += (delta); \
	} while(0)
#define InstrCountFiltered2(node, delta) \
	do { \
		if (((PlanState *)(node))->instrument) \
			((PlanState *)(node))->instrument->nfiltered2 += (delta); \
	} while(0)

/*
 * EPQState is state for executing an EvalPlanQual recheck on a candidate
 * tuples e.g. in ModifyTable or LockRows.
 *
 * To execute EPQ a separate EState is created (stored in ->recheckestate),
 * which shares some resources, like the rangetable, with the main query's
 * EState (stored in ->parentestate). The (sub-)tree of the plan that needs to
 * be rechecked (in ->plan), is separately initialized (into
 * ->recheckplanstate), but shares plan nodes with the corresponding nodes in
 * the main query. The scan nodes in that separate executor tree are changed
 * to return only the current tuple of interest for the respective
 * table. Those tuples are either provided by the caller (using
 * EvalPlanQualSlot), and/or found using the rowmark mechanism (non-locking
 * rowmarks by the EPQ machinery itself, locking ones by the caller).
 *
 * While the plan to be checked may be changed using EvalPlanQualSetPlan(),
 * all such plans need to share the same EState.
 */
typedef struct EPQState
{
	/* These are initialized by EvalPlanQualInit() and do not change later: */
	EState	   *parentestate;	/* main query's EState */
	int			epqParam;		/* ID of Param to force scan node re-eval */
	List	   *resultRelations;	/* integer list of RT indexes, or NIL */

	/*
	 * relsubs_slot[scanrelid - 1] holds the EPQ test tuple to be returned by
	 * the scan node for the scanrelid'th RT index, in place of performing an
	 * actual table scan.  Callers should use EvalPlanQualSlot() to fetch
	 * these slots.
	 */
	List	   *tuple_table;	/* tuple table for relsubs_slot */
	TupleTableSlot **relsubs_slot;

	/*
	 * Initialized by EvalPlanQualInit(), may be changed later with
	 * EvalPlanQualSetPlan():
	 */

	Plan	   *plan;			/* plan tree to be executed */
	List	   *arowMarks;		/* ExecAuxRowMarks (non-locking only) */


	/*
	 * The original output tuple to be rechecked.  Set by
	 * EvalPlanQualSetSlot(), before EvalPlanQualNext() or EvalPlanQual() may
	 * be called.
	 */
	TupleTableSlot *origslot;


	/* Initialized or reset by EvalPlanQualBegin(): */

	EState	   *recheckestate;	/* EState for EPQ execution, see above */

	/*
	 * Rowmarks that can be fetched on-demand using
	 * EvalPlanQualFetchRowMark(), indexed by scanrelid - 1. Only non-locking
	 * rowmarks.
	 */
	ExecAuxRowMark **relsubs_rowmark;

	/*
	 * relsubs_done[scanrelid - 1] is true if there is no EPQ tuple for this
	 * target relation or it has already been fetched in the current scan of
	 * this target relation within the current EvalPlanQual test.
	 */
	bool	   *relsubs_done;

	/*
	 * relsubs_blocked[scanrelid - 1] is true if there is no EPQ tuple for
	 * this target relation during the current EvalPlanQual test.  We keep
	 * these flags set for all relids listed in resultRelations, but
	 * transiently clear the one for the relation whose tuple is actually
	 * passed to EvalPlanQual().
	 */
	bool	   *relsubs_blocked;

	PlanState  *recheckplanstate;	/* EPQ specific exec nodes, for ->plan */
} EPQState;


/* ----------------
 *	 ResultState information
 * ----------------
 */
typedef struct ResultState
{
	PlanState	ps;				/* its first field is NodeTag */
	ExprState  *resconstantqual;
	bool		rs_done;		/* are we done? */
	bool		rs_checkqual;	/* do we need to check the qual? */
} ResultState;

/* ----------------
 *	 ProjectSetState information
 *
 * Note: at least one of the "elems" will be a SetExprState; the rest are
 * regular ExprStates.
 * ----------------
 */
typedef struct ProjectSetState
{
	PlanState	ps;				/* its first field is NodeTag */
	Node	  **elems;			/* array of expression states */
	ExprDoneCond *elemdone;		/* array of per-SRF is-done states */
	int			nelems;			/* length of elemdone[] array */
	bool		pending_srf_tuples; /* still evaluating srfs in tlist? */
	MemoryContext argcontext;	/* context for SRF arguments */
} ProjectSetState;


/* flags for mt_merge_subcommands */
#define MERGE_INSERT	0x01
#define MERGE_UPDATE	0x02
#define MERGE_DELETE	0x04

/* ----------------
 *	 ModifyTableState information
 * ----------------
 */
typedef struct ModifyTableState
{
	PlanState	ps;				/* its first field is NodeTag */
	CmdType		operation;		/* INSERT, UPDATE, DELETE, or MERGE */
	bool		canSetTag;		/* do we set the command tag/es_processed? */
	bool		mt_done;		/* are we done? */
	int			mt_nrels;		/* number of entries in resultRelInfo[] */
	ResultRelInfo *resultRelInfo;	/* info about target relation(s) */

	/*
	 * Target relation mentioned in the original statement, used to fire
	 * statement-level triggers and as the root for tuple routing.  (This
	 * might point to one of the resultRelInfo[] entries, but it can also be a
	 * distinct struct.)
	 */
	ResultRelInfo *rootResultRelInfo;

	EPQState	mt_epqstate;	/* for evaluating EvalPlanQual rechecks */
	bool		fireBSTriggers; /* do we need to fire stmt triggers? */

	/*
	 * These fields are used for inherited UPDATE and DELETE, to track which
	 * target relation a given tuple is from.  If there are a lot of target
	 * relations, we use a hash table to translate table OIDs to
	 * resultRelInfo[] indexes; otherwise mt_resultOidHash is NULL.
	 */
	int			mt_resultOidAttno;	/* resno of "tableoid" junk attr */
	Oid			mt_lastResultOid;	/* last-seen value of tableoid */
	int			mt_lastResultIndex; /* corresponding index in resultRelInfo[] */
	HTAB	   *mt_resultOidHash;	/* optional hash table to speed lookups */

	/*
	 * Slot for storing tuples in the root partitioned table's rowtype during
	 * an UPDATE of a partitioned table.
	 */
	TupleTableSlot *mt_root_tuple_slot;

	/* Tuple-routing support info */
	struct PartitionTupleRouting *mt_partition_tuple_routing;

	/* controls transition table population for specified operation */
	struct TransitionCaptureState *mt_transition_capture;

	/* controls transition table population for INSERT...ON CONFLICT UPDATE */
	struct TransitionCaptureState *mt_oc_transition_capture;

	/* Flags showing which subcommands are present INS/UPD/DEL/DO NOTHING */
	int			mt_merge_subcommands;

	/* For MERGE, the action currently being executed */
	MergeActionState *mt_merge_action;

	/*
	 * For MERGE, if there is a pending NOT MATCHED [BY TARGET] action to be
	 * performed, this will be the last tuple read from the subplan; otherwise
	 * it will be NULL --- see the comments in ExecMerge().
	 */
	TupleTableSlot *mt_merge_pending_not_matched;

	/* tuple counters for MERGE */
	double		mt_merge_inserted;
	double		mt_merge_updated;
	double		mt_merge_deleted;

	/*
	 * Lists of valid updateColnosLists, mergeActionLists, and
	 * mergeJoinConditions.  These contain only entries for unpruned
	 * relations, filtered from the corresponding lists in ModifyTable.
	 */
	List	   *mt_updateColnosLists;
	List	   *mt_mergeActionLists;
	List	   *mt_mergeJoinConditions;
} ModifyTableState;

/* ----------------
 *	 AppendState information
 *
 *		nplans				how many plans are in the array
 *		whichplan			which synchronous plan is being executed (0 .. n-1)
 *							or a special negative value. See nodeAppend.c.
 *		prune_state			details required to allow partitions to be
 *							eliminated from the scan, or NULL if not possible.
 *		valid_subplans		for runtime pruning, valid synchronous appendplans
 *							indexes to scan.
 * ----------------
 */

struct AppendState;
typedef struct AppendState AppendState;
struct ParallelAppendState;
typedef struct ParallelAppendState ParallelAppendState;
struct PartitionPruneState;

struct AppendState
{
	PlanState	ps;				/* its first field is NodeTag */
	PlanState **appendplans;	/* array of PlanStates for my inputs */
	int			as_nplans;
	int			as_whichplan;
	bool		as_begun;		/* false means need to initialize */
	Bitmapset  *as_asyncplans;	/* asynchronous plans indexes */
	int			as_nasyncplans; /* # of asynchronous plans */
	AsyncRequest **as_asyncrequests;	/* array of AsyncRequests */
	TupleTableSlot **as_asyncresults;	/* unreturned results of async plans */
	int			as_nasyncresults;	/* # of valid entries in as_asyncresults */
	bool		as_syncdone;	/* true if all synchronous plans done in
								 * asynchronous mode, else false */
	int			as_nasyncremain;	/* # of remaining asynchronous plans */
	Bitmapset  *as_needrequest; /* asynchronous plans needing a new request */
	struct WaitEventSet *as_eventset;	/* WaitEventSet used to configure file
										 * descriptor wait events */
	int			as_first_partial_plan;	/* Index of 'appendplans' containing
										 * the first partial plan */
	ParallelAppendState *as_pstate; /* parallel coordination info */
	Size		pstate_len;		/* size of parallel coordination info */
	struct PartitionPruneState *as_prune_state;
	bool		as_valid_subplans_identified;	/* is as_valid_subplans valid? */
	Bitmapset  *as_valid_subplans;
	Bitmapset  *as_valid_asyncplans;	/* valid asynchronous plans indexes */
	bool		(*choose_next_subplan) (AppendState *);
};

/* ----------------
 *	 MergeAppendState information
 *
 *		nplans			how many plans are in the array
 *		nkeys			number of sort key columns
 *		sortkeys		sort keys in SortSupport representation
 *		slots			current output tuple of each subplan
 *		heap			heap of active tuples
 *		initialized		true if we have fetched first tuple from each subplan
 *		prune_state		details required to allow partitions to be
 *						eliminated from the scan, or NULL if not possible.
 *		valid_subplans	for runtime pruning, valid mergeplans indexes to
 *						scan.
 * ----------------
 */
typedef struct MergeAppendState
{
	PlanState	ps;				/* its first field is NodeTag */
	PlanState **mergeplans;		/* array of PlanStates for my inputs */
	int			ms_nplans;
	int			ms_nkeys;
	SortSupport ms_sortkeys;	/* array of length ms_nkeys */
	TupleTableSlot **ms_slots;	/* array of length ms_nplans */
	struct binaryheap *ms_heap; /* binary heap of slot indices */
	bool		ms_initialized; /* are subplans started? */
	struct PartitionPruneState *ms_prune_state;
	Bitmapset  *ms_valid_subplans;
} MergeAppendState;

/* ----------------
 *	 RecursiveUnionState information
 *
 *		RecursiveUnionState is used for performing a recursive union.
 *
 *		recursing			T when we're done scanning the non-recursive term
 *		intermediate_empty	T if intermediate_table is currently empty
 *		working_table		working table (to be scanned by recursive term)
 *		intermediate_table	current recursive output (next generation of WT)
 * ----------------
 */
typedef struct RecursiveUnionState
{
	PlanState	ps;				/* its first field is NodeTag */
	bool		recursing;
	bool		intermediate_empty;
	Tuplestorestate *working_table;
	Tuplestorestate *intermediate_table;
	/* Remaining fields are unused in UNION ALL case */
	Oid		   *eqfuncoids;		/* per-grouping-field equality fns */
	FmgrInfo   *hashfunctions;	/* per-grouping-field hash fns */
	MemoryContext tempContext;	/* short-term context for comparisons */
	TupleHashTable hashtable;	/* hash table for tuples already seen */
	MemoryContext tuplesContext;	/* context containing hash table's tuples */
} RecursiveUnionState;

/* ----------------
 *	 BitmapAndState information
 * ----------------
 */
typedef struct BitmapAndState
{
	PlanState	ps;				/* its first field is NodeTag */
	PlanState **bitmapplans;	/* array of PlanStates for my inputs */
	int			nplans;			/* number of input plans */
} BitmapAndState;

/* ----------------
 *	 BitmapOrState information
 * ----------------
 */
typedef struct BitmapOrState
{
	PlanState	ps;				/* its first field is NodeTag */
	PlanState **bitmapplans;	/* array of PlanStates for my inputs */
	int			nplans;			/* number of input plans */
} BitmapOrState;

/* ----------------------------------------------------------------
 *				 Scan State Information
 * ----------------------------------------------------------------
 */

/* ----------------
 *	 ScanState information
 *
 *		ScanState extends PlanState for node types that represent
 *		scans of an underlying relation.  It can also be used for nodes
 *		that scan the output of an underlying plan node --- in that case,
 *		only ScanTupleSlot is actually useful, and it refers to the tuple
 *		retrieved from the subplan.
 *
 *		currentRelation    relation being scanned (NULL if none)
 *		currentScanDesc    current scan descriptor for scan (NULL if none)
 *		ScanTupleSlot	   pointer to slot in tuple table holding scan tuple
 * ----------------
 */
typedef struct ScanState
{
	PlanState	ps;				/* its first field is NodeTag */
	Relation	ss_currentRelation;
	struct TableScanDescData *ss_currentScanDesc;
	TupleTableSlot *ss_ScanTupleSlot;
} ScanState;

/* ----------------
 *	 SeqScanState information
 * ----------------
 */
typedef struct SeqScanState
{
	ScanState	ss;				/* its first field is NodeTag */
	Size		pscan_len;		/* size of parallel heap scan descriptor */
	struct SharedSeqScanInstrumentation *sinstrument;
} SeqScanState;

/* ----------------
 *	 SampleScanState information
 * ----------------
 */
typedef struct SampleScanState
{
	ScanState	ss;
	List	   *args;			/* expr states for TABLESAMPLE params */
	ExprState  *repeatable;		/* expr state for REPEATABLE expr */
	/* use struct pointer to avoid including tsmapi.h here */
	struct TsmRoutine *tsmroutine;	/* descriptor for tablesample method */
	void	   *tsm_state;		/* tablesample method can keep state here */
	bool		use_bulkread;	/* use bulkread buffer access strategy? */
	bool		use_pagemode;	/* use page-at-a-time visibility checking? */
	bool		begun;			/* false means need to call BeginSampleScan */
	uint32		seed;			/* random seed */
	int64		donetuples;		/* number of tuples already returned */
	bool		haveblock;		/* has a block for sampling been determined */
	bool		done;			/* exhausted all tuples? */
} SampleScanState;

/*
 * These structs store information about index quals that don't have simple
 * constant right-hand sides.  See comments for ExecIndexBuildScanKeys()
 * for discussion.
 */
typedef struct
{
	ScanKeyData *scan_key;		/* scankey to put value into */
	ExprState  *key_expr;		/* expr to evaluate to get value */
	bool		key_toastable;	/* is expr's result a toastable datatype? */
} IndexRuntimeKeyInfo;

typedef struct
{
	ScanKeyData *scan_key;		/* scankey to put value into */
	ExprState  *array_expr;		/* expr to evaluate to get array value */
	int			next_elem;		/* next array element to use */
	int			num_elems;		/* number of elems in current array value */
	Datum	   *elem_values;	/* array of num_elems Datums */
	bool	   *elem_nulls;		/* array of num_elems is-null flags */
} IndexArrayKeyInfo;

/* ----------------
 *	 IndexScanState information
 *
 *		indexqualorig	   execution state for indexqualorig expressions
 *		indexorderbyorig   execution state for indexorderbyorig expressions
 *		ScanKeys		   Skey structures for index quals
 *		NumScanKeys		   number of ScanKeys
 *		OrderByKeys		   Skey structures for index ordering operators
 *		NumOrderByKeys	   number of OrderByKeys
 *		RuntimeKeys		   info about Skeys that must be evaluated at runtime
 *		NumRuntimeKeys	   number of RuntimeKeys
 *		RuntimeKeysReady   true if runtime Skeys have been computed
 *		RuntimeContext	   expr context for evaling runtime Skeys
 *		RelationDesc	   index relation descriptor
 *		ScanDesc		   index scan descriptor
 *		Instrument		   local index scan instrumentation
 *		SharedInfo		   parallel worker instrumentation (no leader entry)
 *
 *		ReorderQueue	   tuples that need reordering due to re-check
 *		ReachedEnd		   have we fetched all tuples from index already?
 *		OrderByValues	   values of ORDER BY exprs of last fetched tuple
 *		OrderByNulls	   null flags for OrderByValues
 *		SortSupport		   for reordering ORDER BY exprs
 *		OrderByTypByVals   is the datatype of order by expression pass-by-value?
 *		OrderByTypLens	   typlens of the datatypes of order by expressions
 *		PscanLen		   size of parallel index scan descriptor
 * ----------------
 */
typedef struct IndexScanState
{
	ScanState	ss;				/* its first field is NodeTag */
	ExprState  *indexqualorig;
	List	   *indexorderbyorig;
	ScanKeyData *iss_ScanKeys;
	int			iss_NumScanKeys;
	ScanKeyData *iss_OrderByKeys;
	int			iss_NumOrderByKeys;
	IndexRuntimeKeyInfo *iss_RuntimeKeys;
	int			iss_NumRuntimeKeys;
	bool		iss_RuntimeKeysReady;
	ExprContext *iss_RuntimeContext;
	Relation	iss_RelationDesc;
	struct IndexScanDescData *iss_ScanDesc;
	IndexScanInstrumentation *iss_Instrument;
	SharedIndexScanInstrumentation *iss_SharedInfo;

	/* These are needed for re-checking ORDER BY expr ordering */
	pairingheap *iss_ReorderQueue;
	bool		iss_ReachedEnd;
	Datum	   *iss_OrderByValues;
	bool	   *iss_OrderByNulls;
	SortSupport iss_SortSupport;
	bool	   *iss_OrderByTypByVals;
	int16	   *iss_OrderByTypLens;
	Size		iss_PscanLen;
} IndexScanState;

/* ----------------
 *	 IndexOnlyScanState information
 *
 *		recheckqual		   execution state for recheckqual expressions
 *		ScanKeys		   Skey structures for index quals
 *		NumScanKeys		   number of ScanKeys
 *		OrderByKeys		   Skey structures for index ordering operators
 *		NumOrderByKeys	   number of OrderByKeys
 *		RuntimeKeys		   info about Skeys that must be evaluated at runtime
 *		NumRuntimeKeys	   number of RuntimeKeys
 *		RuntimeKeysReady   true if runtime Skeys have been computed
 *		RuntimeContext	   expr context for evaling runtime Skeys
 *		RelationDesc	   index relation descriptor
 *		ScanDesc		   index scan descriptor
 *		Instrument		   local index scan instrumentation
 *		SharedInfo		   parallel worker instrumentation (no leader entry)
 *		TableSlot		   slot for holding tuples fetched from the table
 *		VMBuffer		   buffer in use for visibility map testing, if any
 *		PscanLen		   size of parallel index-only scan descriptor
 *		NameCStringAttNums attnums of name typed columns to pad to NAMEDATALEN
 *		NameCStringCount   number of elements in the NameCStringAttNums array
 * ----------------
 */
typedef struct IndexOnlyScanState
{
	ScanState	ss;				/* its first field is NodeTag */
	ExprState  *recheckqual;
	ScanKeyData *ioss_ScanKeys;
	int			ioss_NumScanKeys;
	ScanKeyData *ioss_OrderByKeys;
	int			ioss_NumOrderByKeys;
	IndexRuntimeKeyInfo *ioss_RuntimeKeys;
	int			ioss_NumRuntimeKeys;
	bool		ioss_RuntimeKeysReady;
	ExprContext *ioss_RuntimeContext;
	Relation	ioss_RelationDesc;
	struct IndexScanDescData *ioss_ScanDesc;
	IndexScanInstrumentation *ioss_Instrument;
	SharedIndexScanInstrumentation *ioss_SharedInfo;
	TupleTableSlot *ioss_TableSlot;
	Buffer		ioss_VMBuffer;
	Size		ioss_PscanLen;
	AttrNumber *ioss_NameCStringAttNums;
	int			ioss_NameCStringCount;
} IndexOnlyScanState;

/* ----------------
 *	 BitmapIndexScanState information
 *
 *		result			   bitmap to return output into, or NULL
 *		ScanKeys		   Skey structures for index quals
 *		NumScanKeys		   number of ScanKeys
 *		RuntimeKeys		   info about Skeys that must be evaluated at runtime
 *		NumRuntimeKeys	   number of RuntimeKeys
 *		ArrayKeys		   info about Skeys that come from ScalarArrayOpExprs
 *		NumArrayKeys	   number of ArrayKeys
 *		RuntimeKeysReady   true if runtime Skeys have been computed
 *		RuntimeContext	   expr context for evaling runtime Skeys
 *		RelationDesc	   index relation descriptor
 *		ScanDesc		   index scan descriptor
 *		Instrument		   local index scan instrumentation
 *		SharedInfo		   parallel worker instrumentation (no leader entry)
 * ----------------
 */
typedef struct BitmapIndexScanState
{
	ScanState	ss;				/* its first field is NodeTag */
	TIDBitmap  *biss_result;
	ScanKeyData *biss_ScanKeys;
	int			biss_NumScanKeys;
	IndexRuntimeKeyInfo *biss_RuntimeKeys;
	int			biss_NumRuntimeKeys;
	IndexArrayKeyInfo *biss_ArrayKeys;
	int			biss_NumArrayKeys;
	bool		biss_RuntimeKeysReady;
	ExprContext *biss_RuntimeContext;
	Relation	biss_RelationDesc;
	struct IndexScanDescData *biss_ScanDesc;
	IndexScanInstrumentation *biss_Instrument;
	SharedIndexScanInstrumentation *biss_SharedInfo;
} BitmapIndexScanState;


/* ----------------
 *	 BitmapHeapScanState information
 *
 *		bitmapqualorig	   execution state for bitmapqualorig expressions
 *		tbm				   bitmap obtained from child index scan(s)
 *		stats			   execution statistics
 *		initialized		   is node is ready to iterate
 *		pstate			   shared state for parallel bitmap scan
 *		sinstrument		   statistics for parallel workers
 *		recheck			   do current page's tuples need recheck
 * ----------------
 */

/* this struct is defined in nodeBitmapHeapscan.c */
typedef struct ParallelBitmapHeapState ParallelBitmapHeapState;

typedef struct BitmapHeapScanState
{
	ScanState	ss;				/* its first field is NodeTag */
	ExprState  *bitmapqualorig;
	TIDBitmap  *tbm;
	BitmapHeapScanInstrumentation stats;
	bool		initialized;
	ParallelBitmapHeapState *pstate;
	SharedBitmapHeapInstrumentation *sinstrument;
	bool		recheck;
} BitmapHeapScanState;

/* ----------------
 *	 TidScanState information
 *
 *		tidexprs	   list of TidExpr structs (see nodeTidscan.c)
 *		isCurrentOf    scan has a CurrentOfExpr qual
 *		NumTids		   number of tids in this scan
 *		TidPtr		   index of currently fetched tid
 *		TidList		   evaluated item pointers (array of size NumTids)
 * ----------------
 */
typedef struct TidScanState
{
	ScanState	ss;				/* its first field is NodeTag */
	List	   *tss_tidexprs;
	bool		tss_isCurrentOf;
	int			tss_NumTids;
	int			tss_TidPtr;
	ItemPointerData *tss_TidList;
} TidScanState;

/* ----------------
 *	 TidRangeScanState information
 *
 *		trss_tidexprs		list of TidOpExpr structs (see nodeTidrangescan.c)
 *		trss_mintid			the lowest TID in the scan range
 *		trss_maxtid			the highest TID in the scan range
 *		trss_inScan			is a scan currently in progress?
 *		trss_pscanlen		size of parallel heap scan descriptor
 * ----------------
 */
typedef struct TidRangeScanState
{
	ScanState	ss;				/* its first field is NodeTag */
	List	   *trss_tidexprs;
	ItemPointerData trss_mintid;
	ItemPointerData trss_maxtid;
	bool		trss_inScan;
	Size		trss_pscanlen;
	struct SharedTidRangeScanInstrumentation *trss_sinstrument;
} TidRangeScanState;

/* ----------------
 *	 SubqueryScanState information
 *
 *		SubqueryScanState is used for scanning a sub-query in the range table.
 *		ScanTupleSlot references the current output tuple of the sub-query.
 * ----------------
 */
typedef struct SubqueryScanState
{
	ScanState	ss;				/* its first field is NodeTag */
	PlanState  *subplan;
} SubqueryScanState;

/* ----------------
 *	 FunctionScanState information
 *
 *		Function nodes are used to scan the results of a
 *		function appearing in FROM (typically a function returning set).
 *
 *		eflags				node's capability flags
 *		ordinality			is this scan WITH ORDINALITY?
 *		simple				true if we have 1 function and no ordinality
 *		ordinal				current ordinal column value
 *		nfuncs				number of functions being executed
 *		funcstates			per-function execution states (private in
 *							nodeFunctionscan.c)
 *		argcontext			memory context to evaluate function arguments in
 * ----------------
 */
struct FunctionScanPerFuncState;

typedef struct FunctionScanState
{
	ScanState	ss;				/* its first field is NodeTag */
	int			eflags;
	bool		ordinality;
	bool		simple;
	int64		ordinal;
	int			nfuncs;
	struct FunctionScanPerFuncState *funcstates;	/* array of length nfuncs */
	MemoryContext argcontext;
} FunctionScanState;

/* ----------------
 *	 ValuesScanState information
 *
 *		ValuesScan nodes are used to scan the results of a VALUES list
 *
 *		rowcontext			per-expression-list context
 *		exprlists			array of expression lists being evaluated
 *		exprstatelists		array of expression state lists, for SubPlans only
 *		array_len			size of above arrays
 *		curr_idx			current array index (0-based)
 *
 *	Note: ss.ps.ps_ExprContext is used to evaluate any qual or projection
 *	expressions attached to the node.  We create a second ExprContext,
 *	rowcontext, in which to build the executor expression state for each
 *	Values sublist.  Resetting this context lets us get rid of expression
 *	state for each row, avoiding major memory leakage over a long values list.
 *	However, that doesn't work for sublists containing SubPlans, because a
 *	SubPlan has to be connected up to the outer plan tree to work properly.
 *	Therefore, for only those sublists containing SubPlans, we do expression
 *	state construction at executor start, and store those pointers in
 *	exprstatelists[].  NULL entries in that array correspond to simple
 *	subexpressions that are handled as described above.
 * ----------------
 */
typedef struct ValuesScanState
{
	ScanState	ss;				/* its first field is NodeTag */
	ExprContext *rowcontext;
	List	  **exprlists;
	List	  **exprstatelists;
	int			array_len;
	int			curr_idx;
} ValuesScanState;

/* ----------------
 *		TableFuncScanState node
 *
 * Used in table-expression functions like XMLTABLE.
 * ----------------
 */
typedef struct TableFuncScanState
{
	ScanState	ss;				/* its first field is NodeTag */
	ExprState  *docexpr;		/* state for document expression */
	ExprState  *rowexpr;		/* state for row-generating expression */
	List	   *colexprs;		/* state for column-generating expression */
	List	   *coldefexprs;	/* state for column default expressions */
	List	   *colvalexprs;	/* state for column value expressions */
	List	   *passingvalexprs;	/* state for PASSING argument expressions */
	List	   *ns_names;		/* same as TableFunc.ns_names */
	List	   *ns_uris;		/* list of states of namespace URI exprs */
	Bitmapset  *notnulls;		/* nullability flag for each output column */
	void	   *opaque;			/* table builder private space */
	const struct TableFuncRoutine *routine; /* table builder methods */
	FmgrInfo   *in_functions;	/* input function for each column */
	Oid		   *typioparams;	/* typioparam for each column */
	int64		ordinal;		/* row number to be output next */
	MemoryContext perTableCxt;	/* per-table context */
	Tuplestorestate *tupstore;	/* output tuple store */
} TableFuncScanState;

/* ----------------
 *	 CteScanState information
 *
 *		CteScan nodes are used to scan a CommonTableExpr query.
 *
 * Multiple CteScan nodes can read out from the same CTE query.  We use
 * a tuplestore to hold rows that have been read from the CTE query but
 * not yet consumed by all readers.
 * ----------------
 */
typedef struct CteScanState
{
	ScanState	ss;				/* its first field is NodeTag */
	int			eflags;			/* capability flags to pass to tuplestore */
	int			readptr;		/* index of my tuplestore read pointer */
	PlanState  *cteplanstate;	/* PlanState for the CTE query itself */
	/* Link to the "leader" CteScanState (possibly this same node) */
	struct CteScanState *leader;
	/* The remaining fields are only valid in the "leader" CteScanState */
	Tuplestorestate *cte_table; /* rows already read from the CTE query */
	bool		eof_cte;		/* reached end of CTE query? */
} CteScanState;

/* ----------------
 *	 NamedTuplestoreScanState information
 *
 *		NamedTuplestoreScan nodes are used to scan a Tuplestore created and
 *		named prior to execution of the query.  An example is a transition
 *		table for an AFTER trigger.
 *
 * Multiple NamedTuplestoreScan nodes can read out from the same Tuplestore.
 * ----------------
 */
typedef struct NamedTuplestoreScanState
{
	ScanState	ss;				/* its first field is NodeTag */
	int			readptr;		/* index of my tuplestore read pointer */
	TupleDesc	tupdesc;		/* format of the tuples in the tuplestore */
	Tuplestorestate *relation;	/* the rows */
} NamedTuplestoreScanState;

/* ----------------
 *	 WorkTableScanState information
 *
 *		WorkTableScan nodes are used to scan the work table created by
 *		a RecursiveUnion node.  We locate the RecursiveUnion node
 *		during executor startup.
 * ----------------
 */
typedef struct WorkTableScanState
{
	ScanState	ss;				/* its first field is NodeTag */
	RecursiveUnionState *rustate;
} WorkTableScanState;

/* ----------------
 *	 ForeignScanState information
 *
 *		ForeignScan nodes are used to scan foreign-data tables.
 * ----------------
 */
typedef struct ForeignScanState
{
	ScanState	ss;				/* its first field is NodeTag */
	ExprState  *fdw_recheck_quals;	/* original quals not in ss.ps.qual */
	Size		pscan_len;		/* size of parallel coordination information */
	ResultRelInfo *resultRelInfo;	/* result rel info, if UPDATE or DELETE */
	/* use struct pointer to avoid including fdwapi.h here */
	struct FdwRoutine *fdwroutine;
	void	   *fdw_state;		/* foreign-data wrapper can keep state here */
} ForeignScanState;

/* ----------------
 *	 CustomScanState information
 *
 *		CustomScan nodes are used to execute custom code within executor.
 *
 * Core code must avoid assuming that the CustomScanState is only as large as
 * the structure declared here; providers are allowed to make it the first
 * element in a larger structure, and typically would need to do so.  The
 * struct is actually allocated by the CreateCustomScanState method associated
 * with the plan node.  Any additional fields can be initialized there, or in
 * the BeginCustomScan method.
 * ----------------
 */
struct CustomExecMethods;

typedef struct CustomScanState
{
	ScanState	ss;
	uint32		flags;			/* mask of CUSTOMPATH_* flags, see
								 * nodes/extensible.h */
	List	   *custom_ps;		/* list of child PlanState nodes, if any */
	Size		pscan_len;		/* size of parallel coordination information */
	const struct CustomExecMethods *methods;
	const struct TupleTableSlotOps *slotOps;
} CustomScanState;

/* ----------------------------------------------------------------
 *				 Join State Information
 * ----------------------------------------------------------------
 */

/* ----------------
 *	 JoinState information
 *
 *		Superclass for state nodes of join plans.
 * ----------------
 */
typedef struct JoinState
{
	PlanState	ps;
	JoinType	jointype;
	bool		single_match;	/* True if we should skip to next outer tuple
								 * after finding one inner match */
	ExprState  *joinqual;		/* JOIN quals (in addition to ps.qual) */
} JoinState;

/* ----------------
 *	 NestLoopState information
 *
 *		NeedNewOuter	   true if need new outer tuple on next call
 *		MatchedOuter	   true if found a join match for current outer tuple
 *		NullInnerTupleSlot prepared null tuple for left outer joins
 * ----------------
 */
typedef struct NestLoopState
{
	JoinState	js;				/* its first field is NodeTag */
	bool		nl_NeedNewOuter;
	bool		nl_MatchedOuter;
	TupleTableSlot *nl_NullInnerTupleSlot;
} NestLoopState;

/* ----------------
 *	 MergeJoinState information
 *
 *		NumClauses		   number of mergejoinable join clauses
 *		Clauses			   info for each mergejoinable clause
 *		JoinState		   current state of ExecMergeJoin state machine
 *		SkipMarkRestore    true if we may skip Mark and Restore operations
 *		ExtraMarks		   true to issue extra Mark operations on inner scan
 *		ConstFalseJoin	   true if we have a constant-false joinqual
 *		FillOuter		   true if should emit unjoined outer tuples anyway
 *		FillInner		   true if should emit unjoined inner tuples anyway
 *		MatchedOuter	   true if found a join match for current outer tuple
 *		MatchedInner	   true if found a join match for current inner tuple
 *		OuterTupleSlot	   slot in tuple table for cur outer tuple
 *		InnerTupleSlot	   slot in tuple table for cur inner tuple
 *		MarkedTupleSlot    slot in tuple table for marked tuple
 *		NullOuterTupleSlot prepared null tuple for right outer joins
 *		NullInnerTupleSlot prepared null tuple for left outer joins
 *		OuterEContext	   workspace for computing outer tuple's join values
 *		InnerEContext	   workspace for computing inner tuple's join values
 * ----------------
 */
/* private in nodeMergejoin.c: */
typedef struct MergeJoinClauseData *MergeJoinClause;

typedef struct MergeJoinState
{
	JoinState	js;				/* its first field is NodeTag */
	int			mj_NumClauses;
	MergeJoinClause mj_Clauses; /* array of length mj_NumClauses */
	int			mj_JoinState;
	bool		mj_SkipMarkRestore;
	bool		mj_ExtraMarks;
	bool		mj_ConstFalseJoin;
	bool		mj_FillOuter;
	bool		mj_FillInner;
	bool		mj_MatchedOuter;
	bool		mj_MatchedInner;
	TupleTableSlot *mj_OuterTupleSlot;
	TupleTableSlot *mj_InnerTupleSlot;
	TupleTableSlot *mj_MarkedTupleSlot;
	TupleTableSlot *mj_NullOuterTupleSlot;
	TupleTableSlot *mj_NullInnerTupleSlot;
	ExprContext *mj_OuterEContext;
	ExprContext *mj_InnerEContext;
} MergeJoinState;

/* ----------------
 *	 HashJoinState information
 *
 *		hashclauses				original form of the hashjoin condition
 *		hj_OuterHash			ExprState for hashing outer keys
 *		hj_HashTable			hash table for the hashjoin
 *								(NULL if table not built yet)
 *		hj_CurHashValue			hash value for current outer tuple
 *		hj_CurBucketNo			regular bucket# for current outer tuple
 *		hj_CurSkewBucketNo		skew bucket# for current outer tuple
 *		hj_CurTuple				last inner tuple matched to current outer
 *								tuple, or NULL if starting search
 *								(hj_CurXXX variables are undefined if
 *								OuterTupleSlot is empty!)
 *		hj_OuterTupleSlot		tuple slot for outer tuples
 *		hj_HashTupleSlot		tuple slot for inner (hashed) tuples
 *		hj_NullOuterTupleSlot	prepared null tuple for right/right-anti/full
 *								outer joins
 *		hj_NullInnerTupleSlot	prepared null tuple for left/full outer joins
 *		hj_NullOuterTupleStore	tuplestore holding outer tuples that have
 *								null join keys (but must be emitted anyway)
 *		hj_FirstOuterTupleSlot	first tuple retrieved from outer plan
 *		hj_JoinState			current state of ExecHashJoin state machine
 *		hj_KeepNullTuples		true to keep outer tuples with null join keys
 *		hj_MatchedOuter			true if found a join match for current outer
 *		hj_OuterNotEmpty		true if outer relation known not empty
 * ----------------
 */

/* these structs are defined in executor/hashjoin.h: */
typedef struct HashJoinTupleData *HashJoinTuple;
typedef struct HashJoinTableData *HashJoinTable;

typedef struct HashJoinState
{
	JoinState	js;				/* its first field is NodeTag */
	ExprState  *hashclauses;
	ExprState  *hj_OuterHash;
	HashJoinTable hj_HashTable;
	uint32		hj_CurHashValue;
	int			hj_CurBucketNo;
	int			hj_CurSkewBucketNo;
	HashJoinTuple hj_CurTuple;
	TupleTableSlot *hj_OuterTupleSlot;
	TupleTableSlot *hj_HashTupleSlot;
	TupleTableSlot *hj_NullOuterTupleSlot;
	TupleTableSlot *hj_NullInnerTupleSlot;
	Tuplestorestate *hj_NullOuterTupleStore;
	TupleTableSlot *hj_FirstOuterTupleSlot;
	int			hj_JoinState;
	bool		hj_KeepNullTuples;
	bool		hj_MatchedOuter;
	bool		hj_OuterNotEmpty;
} HashJoinState;


/* ----------------------------------------------------------------
 *				 Materialization State Information
 * ----------------------------------------------------------------
 */

/* ----------------
 *	 MaterialState information
 *
 *		materialize nodes are used to materialize the results
 *		of a subplan into a temporary file.
 *
 *		ss.ss_ScanTupleSlot refers to output of underlying plan.
 * ----------------
 */
typedef struct MaterialState
{
	ScanState	ss;				/* its first field is NodeTag */
	int			eflags;			/* capability flags to pass to tuplestore */
	bool		eof_underlying; /* reached end of underlying plan? */
	Tuplestorestate *tuplestorestate;
} MaterialState;

struct MemoizeEntry;
struct MemoizeTuple;
struct MemoizeKey;

/* ----------------
 *	 MemoizeState information
 *
 *		memoize nodes are used to cache recent and commonly seen results from
 *		a parameterized scan.
 * ----------------
 */
typedef struct MemoizeState
{
	ScanState	ss;				/* its first field is NodeTag */
	int			mstatus;		/* value of ExecMemoize state machine */
	int			nkeys;			/* number of cache keys */
	struct memoize_hash *hashtable; /* hash table for cache entries */
	TupleDesc	hashkeydesc;	/* tuple descriptor for cache keys */
	TupleTableSlot *tableslot;	/* min tuple slot for existing cache entries */
	TupleTableSlot *probeslot;	/* virtual slot used for hash lookups */
	ExprState  *cache_eq_expr;	/* Compare exec params to hash key */
	ExprState **param_exprs;	/* exprs containing the parameters to this
								 * node */
	FmgrInfo   *hashfunctions;	/* lookup data for hash funcs nkeys in size */
	Oid		   *collations;		/* collation for comparisons nkeys in size */
	uint64		mem_used;		/* bytes of memory used by cache */
	uint64		mem_limit;		/* memory limit in bytes for the cache */
	MemoryContext tableContext; /* memory context to store cache data */
	dlist_head	lru_list;		/* least recently used entry list */
	struct MemoizeTuple *last_tuple;	/* Used to point to the last tuple
										 * returned during a cache hit and the
										 * tuple we last stored when
										 * populating the cache. */
	struct MemoizeEntry *entry; /* the entry that 'last_tuple' belongs to or
								 * NULL if 'last_tuple' is NULL. */
	bool		singlerow;		/* true if the cache entry is to be marked as
								 * complete after caching the first tuple. */
	bool		binary_mode;	/* true when cache key should be compared bit
								 * by bit, false when using hash equality ops */
	MemoizeInstrumentation stats;	/* execution statistics */
	SharedMemoizeInfo *shared_info; /* statistics for parallel workers */
	Bitmapset  *keyparamids;	/* Param->paramids of expressions belonging to
								 * param_exprs */
} MemoizeState;

/* ----------------
 *	 When performing sorting by multiple keys, it's possible that the input
 *	 dataset is already sorted on a prefix of those keys. We call these
 *	 "presorted keys".
 *	 PresortedKeyData represents information about one such key.
 * ----------------
 */
typedef struct PresortedKeyData
{
	FmgrInfo	flinfo;			/* comparison function info */
	FunctionCallInfo fcinfo;	/* comparison function call info */
	OffsetNumber attno;			/* attribute number in tuple */
} PresortedKeyData;

/* ----------------
 *	 SortState information
 * ----------------
 */
typedef struct SortState
{
	ScanState	ss;				/* its first field is NodeTag */
	bool		randomAccess;	/* need random access to sort output? */
	bool		bounded;		/* is the result set bounded? */
	int64		bound;			/* if bounded, how many tuples are needed */
	bool		sort_Done;		/* sort completed yet? */
	bool		bounded_Done;	/* value of bounded we did the sort with */
	int64		bound_Done;		/* value of bound we did the sort with */
	void	   *tuplesortstate; /* private state of tuplesort.c */
	bool		am_worker;		/* are we a worker? */
	bool		datumSort;		/* Datum sort instead of tuple sort? */
	SharedSortInfo *shared_info;	/* one entry per worker */
} SortState;

typedef enum
{
	INCSORT_LOADFULLSORT,
	INCSORT_LOADPREFIXSORT,
	INCSORT_READFULLSORT,
	INCSORT_READPREFIXSORT,
} IncrementalSortExecutionStatus;

typedef struct IncrementalSortState
{
	ScanState	ss;				/* its first field is NodeTag */
	bool		bounded;		/* is the result set bounded? */
	int64		bound;			/* if bounded, how many tuples are needed */
	bool		outerNodeDone;	/* finished fetching tuples from outer node */
	int64		bound_Done;		/* value of bound we did the sort with */
	IncrementalSortExecutionStatus execution_status;
	int64		n_fullsort_remaining;
	Tuplesortstate *fullsort_state; /* private state of tuplesort.c */
	Tuplesortstate *prefixsort_state;	/* private state of tuplesort.c */
	/* the keys by which the input path is already sorted */
	PresortedKeyData *presorted_keys;

	IncrementalSortInfo incsort_info;

	/* slot for pivot tuple defining values of presorted keys within group */
	TupleTableSlot *group_pivot;
	TupleTableSlot *transfer_tuple;
	bool		am_worker;		/* are we a worker? */
	SharedIncrementalSortInfo *shared_info; /* one entry per worker */
} IncrementalSortState;

/* ---------------------
 *	GroupState information
 * ---------------------
 */
typedef struct GroupState
{
	ScanState	ss;				/* its first field is NodeTag */
	ExprState  *eqfunction;		/* equality function */
	bool		grp_done;		/* indicates completion of Group scan */
} GroupState;

/* ---------------------
 *	AggState information
 *
 *	ss.ss_ScanTupleSlot refers to output of underlying plan.
 *
 *	Note: ss.ps.ps_ExprContext contains ecxt_aggvalues and
 *	ecxt_aggnulls arrays, which hold the computed agg values for the current
 *	input group during evaluation of an Agg node's output tuple(s).  We
 *	create a second ExprContext, tmpcontext, in which to evaluate input
 *	expressions and run the aggregate transition functions.
 * ---------------------
 */
/* these structs are private in nodeAgg.c: */
typedef struct AggStatePerAggData *AggStatePerAgg;
typedef struct AggStatePerTransData *AggStatePerTrans;
typedef struct AggStatePerGroupData *AggStatePerGroup;
typedef struct AggStatePerPhaseData *AggStatePerPhase;
typedef struct AggStatePerHashData *AggStatePerHash;

typedef struct AggState
{
	ScanState	ss;				/* its first field is NodeTag */
	List	   *aggs;			/* all Aggref nodes in targetlist & quals */
	int			numaggs;		/* length of list (could be zero!) */
	int			numtrans;		/* number of pertrans items */
	AggStrategy aggstrategy;	/* strategy mode */
	AggSplit	aggsplit;		/* agg-splitting mode, see nodes.h */
	AggStatePerPhase phase;		/* pointer to current phase data */
	int			numphases;		/* number of phases (including phase 0) */
	int			current_phase;	/* current phase number */
	AggStatePerAgg peragg;		/* per-Aggref information */
	AggStatePerTrans pertrans;	/* per-Trans state information */
	ExprContext *hashcontext;	/* econtexts for long-lived data (hashtable) */
	ExprContext **aggcontexts;	/* econtexts for long-lived data (per GS) */
	ExprContext *tmpcontext;	/* econtext for input expressions */
#define FIELDNO_AGGSTATE_CURAGGCONTEXT 14
	ExprContext *curaggcontext; /* currently active aggcontext */
	AggStatePerAgg curperagg;	/* currently active aggregate, if any */
#define FIELDNO_AGGSTATE_CURPERTRANS 16
	AggStatePerTrans curpertrans;	/* currently active trans state, if any */
	bool		input_done;		/* indicates end of input */
	bool		agg_done;		/* indicates completion of Agg scan */
	int			projected_set;	/* The last projected grouping set */
#define FIELDNO_AGGSTATE_CURRENT_SET 20
	int			current_set;	/* The current grouping set being evaluated */
	Bitmapset  *grouped_cols;	/* grouped cols in current projection */
	List	   *all_grouped_cols;	/* list of all grouped cols in DESC order */
	Bitmapset  *colnos_needed;	/* all columns needed from the outer plan */
	int			max_colno_needed;	/* highest colno needed from outer plan */
	bool		all_cols_needed;	/* are all cols from outer plan needed? */
	/* These fields are for grouping set phase data */
	int			maxsets;		/* The max number of sets in any phase */
	AggStatePerPhase phases;	/* array of all phases */
	Tuplesortstate *sort_in;	/* sorted input to phases > 1 */
	Tuplesortstate *sort_out;	/* input is copied here for next phase */
	TupleTableSlot *sort_slot;	/* slot for sort results */
	/* these fields are used in AGG_PLAIN and AGG_SORTED modes: */
	AggStatePerGroup *pergroups;	/* grouping set indexed array of per-group
									 * pointers */
	HeapTuple	grp_firstTuple; /* copy of first tuple of current group */
	/* these fields are used in AGG_HASHED and AGG_MIXED modes: */
	bool		table_filled;	/* hash table filled yet? */
	int			num_hashes;
	MemoryContext hash_metacxt; /* memory for hash table bucket array */
	MemoryContext hash_tuplescxt;	/* memory for hash table tuples */
	struct LogicalTapeSet *hash_tapeset;	/* tape set for hash spill tapes */
	struct HashAggSpill *hash_spills;	/* HashAggSpill for each grouping set,
										 * exists only during first pass */
	TupleTableSlot *hash_spill_rslot;	/* for reading spill files */
	TupleTableSlot *hash_spill_wslot;	/* for writing spill files */
	List	   *hash_batches;	/* hash batches remaining to be processed */
	bool		hash_ever_spilled;	/* ever spilled during this execution? */
	bool		hash_spill_mode;	/* we hit a limit during the current batch
									 * and we must not create new groups */
	Size		hash_mem_limit; /* limit before spilling hash table */
	uint64		hash_ngroups_limit; /* limit before spilling hash table */
	int			hash_planned_partitions;	/* number of partitions planned
											 * for first pass */
	double		hashentrysize;	/* estimate revised during execution */
	Size		hash_mem_peak;	/* peak hash table memory usage */
	uint64		hash_ngroups_current;	/* number of groups currently in
										 * memory in all hash tables */
	uint64		hash_disk_used; /* kB of disk space used */
	int			hash_batches_used;	/* batches used during entire execution */

	AggStatePerHash perhash;	/* array of per-hashtable data */
	AggStatePerGroup *hash_pergroup;	/* grouping set indexed array of
										 * per-group pointers */

	/* support for evaluation of agg input expressions: */
#define FIELDNO_AGGSTATE_ALL_PERGROUPS 54
	AggStatePerGroup *all_pergroups;	/* array of first ->pergroups, than
										 * ->hash_pergroup */
	SharedAggInfo *shared_info; /* one entry per worker */
} AggState;

/* ----------------
 *	WindowAggState information
 * ----------------
 */
/* these structs are private in nodeWindowAgg.c: */
typedef struct WindowStatePerFuncData *WindowStatePerFunc;
typedef struct WindowStatePerAggData *WindowStatePerAgg;

/*
 * WindowAggStatus -- Used to track the status of WindowAggState
 */
typedef enum WindowAggStatus
{
	WINDOWAGG_DONE,				/* No more processing to do */
	WINDOWAGG_RUN,				/* Normal processing of window funcs */
	WINDOWAGG_PASSTHROUGH,		/* Don't eval window funcs */
	WINDOWAGG_PASSTHROUGH_STRICT,	/* Pass-through plus don't store new
									 * tuples during spool */
} WindowAggStatus;

typedef struct WindowAggState
{
	ScanState	ss;				/* its first field is NodeTag */

	/* these fields are filled in by ExecInitExpr: */
	List	   *funcs;			/* all WindowFunc nodes in targetlist */
	int			numfuncs;		/* total number of window functions */
	int			numaggs;		/* number that are plain aggregates */

	WindowStatePerFunc perfunc; /* per-window-function information */
	WindowStatePerAgg peragg;	/* per-plain-aggregate information */
	ExprState  *partEqfunction; /* equality funcs for partition columns */
	ExprState  *ordEqfunction;	/* equality funcs for ordering columns */
	Tuplestorestate *buffer;	/* stores rows of current partition */
	int			current_ptr;	/* read pointer # for current row */
	int			framehead_ptr;	/* read pointer # for frame head, if used */
	int			frametail_ptr;	/* read pointer # for frame tail, if used */
	int			grouptail_ptr;	/* read pointer # for group tail, if used */
	int64		spooled_rows;	/* total # of rows in buffer */
	int64		currentpos;		/* position of current row in partition */
	int64		frameheadpos;	/* current frame head position */
	int64		frametailpos;	/* current frame tail position (frame end+1) */
	/* use struct pointer to avoid including windowapi.h here */
	struct WindowObjectData *agg_winobj;	/* winobj for aggregate fetches */
	int64		aggregatedbase; /* start row for current aggregates */
	int64		aggregatedupto; /* rows before this one are aggregated */
	WindowAggStatus status;		/* run status of WindowAggState */

	int			frameOptions;	/* frame_clause options, see WindowDef */
	ExprState  *startOffset;	/* expression for starting bound offset */
	ExprState  *endOffset;		/* expression for ending bound offset */
	Datum		startOffsetValue;	/* result of startOffset evaluation */
	Datum		endOffsetValue; /* result of endOffset evaluation */

	/* these fields are used with RANGE offset PRECEDING/FOLLOWING: */
	FmgrInfo	startInRangeFunc;	/* in_range function for startOffset */
	FmgrInfo	endInRangeFunc; /* in_range function for endOffset */
	Oid			inRangeColl;	/* collation for in_range tests */
	bool		inRangeAsc;		/* use ASC sort order for in_range tests? */
	bool		inRangeNullsFirst;	/* nulls sort first for in_range tests? */

	/* fields relating to runconditions */
	bool		use_pass_through;	/* When false, stop execution when
									 * runcondition is no longer true.  Else
									 * just stop evaluating window funcs. */
	bool		top_window;		/* true if this is the top-most WindowAgg or
								 * the only WindowAgg in this query level */
	ExprState  *runcondition;	/* Condition which must remain true otherwise
								 * execution of the WindowAgg will finish or
								 * go into pass-through mode.  NULL when there
								 * is no such condition. */

	/* these fields are used in GROUPS mode: */
	int64		currentgroup;	/* peer group # of current row in partition */
	int64		frameheadgroup; /* peer group # of frame head row */
	int64		frametailgroup; /* peer group # of frame tail row */
	int64		groupheadpos;	/* current row's peer group head position */
	int64		grouptailpos;	/* " " " " tail position (group end+1) */

	MemoryContext partcontext;	/* context for partition-lifespan data */
	MemoryContext aggcontext;	/* shared context for aggregate working data */
	MemoryContext curaggcontext;	/* current aggregate's working data */
	ExprContext *tmpcontext;	/* short-term evaluation context */

	bool		all_first;		/* true if the scan is starting */
	bool		partition_spooled;	/* true if all tuples in current partition
									 * have been spooled into tuplestore */
	bool		next_partition; /* true if begin_partition needs to be called */
	bool		more_partitions;	/* true if there's more partitions after
									 * this one */
	bool		framehead_valid;	/* true if frameheadpos is known up to
									 * date for current row */
	bool		frametail_valid;	/* true if frametailpos is known up to
									 * date for current row */
	bool		grouptail_valid;	/* true if grouptailpos is known up to
									 * date for current row */

	TupleTableSlot *first_part_slot;	/* first tuple of current or next
										 * partition */
	TupleTableSlot *framehead_slot; /* first tuple of current frame */
	TupleTableSlot *frametail_slot; /* first tuple after current frame */

	/* temporary slots for tuples fetched back from tuplestore */
	TupleTableSlot *agg_row_slot;
	TupleTableSlot *temp_slot_1;
	TupleTableSlot *temp_slot_2;
} WindowAggState;

/* ----------------
 *	 UniqueState information
 *
 *		Unique nodes are used "on top of" sort nodes to discard
 *		duplicate tuples returned from the sort phase.  Basically
 *		all it does is compare the current tuple from the subplan
 *		with the previously fetched tuple (stored in its result slot).
 *		If the two are identical in all interesting fields, then
 *		we just fetch another tuple from the sort and try again.
 * ----------------
 */
typedef struct UniqueState
{
	PlanState	ps;				/* its first field is NodeTag */
	ExprState  *eqfunction;		/* tuple equality qual */
} UniqueState;

/* ----------------
 * GatherState information
 *
 *		Gather nodes launch 1 or more parallel workers, run a subplan
 *		in those workers, and collect the results.
 * ----------------
 */
typedef struct GatherState
{
	PlanState	ps;				/* its first field is NodeTag */
	bool		initialized;	/* workers launched? */
	bool		need_to_scan_locally;	/* need to read from local plan? */
	int64		tuples_needed;	/* tuple bound, see ExecSetTupleBound */
	/* these fields are set up once: */
	TupleTableSlot *funnel_slot;
	struct ParallelExecutorInfo *pei;
	/* all remaining fields are reinitialized during a rescan: */
	int			nworkers_launched;	/* original number of workers */
	int			nreaders;		/* number of still-active workers */
	int			nextreader;		/* next one to try to read from */
	struct TupleQueueReader **reader;	/* array with nreaders active entries */
} GatherState;

/* ----------------
 * GatherMergeState information
 *
 *		Gather merge nodes launch 1 or more parallel workers, run a
 *		subplan which produces sorted output in each worker, and then
 *		merge the results into a single sorted stream.
 * ----------------
 */
struct GMReaderTupleBuffer;		/* private in nodeGatherMerge.c */

typedef struct GatherMergeState
{
	PlanState	ps;				/* its first field is NodeTag */
	bool		initialized;	/* workers launched? */
	bool		gm_initialized; /* gather_merge_init() done? */
	bool		need_to_scan_locally;	/* need to read from local plan? */
	int64		tuples_needed;	/* tuple bound, see ExecSetTupleBound */
	/* these fields are set up once: */
	TupleDesc	tupDesc;		/* descriptor for subplan result tuples */
	int			gm_nkeys;		/* number of sort columns */
	SortSupport gm_sortkeys;	/* array of length gm_nkeys */
	struct ParallelExecutorInfo *pei;
	/* all remaining fields are reinitialized during a rescan */
	/* (but the arrays are not reallocated, just cleared) */
	int			nworkers_launched;	/* original number of workers */
	int			nreaders;		/* number of active workers */
	TupleTableSlot **gm_slots;	/* array with nreaders+1 entries */
	struct TupleQueueReader **reader;	/* array with nreaders active entries */
	struct GMReaderTupleBuffer *gm_tuple_buffers;	/* nreaders tuple buffers */
	struct binaryheap *gm_heap; /* binary heap of slot indices */
} GatherMergeState;

/* ----------------
 *	 HashState information
 * ----------------
 */
typedef struct HashState
{
	PlanState	ps;				/* its first field is NodeTag */
	HashJoinTable hashtable;	/* hash table for the hashjoin */
	ExprState  *hash_expr;		/* ExprState to get hash value */

	FmgrInfo   *skew_hashfunction;	/* lookup data for skew hash function */
	Oid			skew_collation; /* collation to call skew_hashfunction with */

	Tuplestorestate *null_tuple_store;	/* where to put null-keyed tuples */
	bool		keep_null_tuples;	/* do we need to save such tuples? */

	/*
	 * In a parallelized hash join, the leader retains a pointer to the
	 * shared-memory stats area in its shared_info field, and then copies the
	 * shared-memory info back to local storage before DSM shutdown.  The
	 * shared_info field remains NULL in workers, or in non-parallel joins.
	 */
	SharedHashInfo *shared_info;

	/*
	 * If we are collecting hash stats, this points to an initially-zeroed
	 * collection area, which could be either local storage or in shared
	 * memory; either way it's for just one process.
	 */
	HashInstrumentation *hinstrument;

	/* Parallel hash state. */
	struct ParallelHashJoinState *parallel_state;
} HashState;

/* ----------------
 *	 SetOpState information
 *
 *		SetOp nodes support either sorted or hashed de-duplication.
 *		The sorted mode is a bit like MergeJoin, the hashed mode like Agg.
 * ----------------
 */
typedef struct SetOpStatePerInput
{
	TupleTableSlot *firstTupleSlot; /* first tuple of current group */
	int64		numTuples;		/* number of tuples in current group */
	TupleTableSlot *nextTupleSlot;	/* next input tuple, if already read */
	bool		needGroup;		/* do we need to load a new group? */
} SetOpStatePerInput;

typedef struct SetOpState
{
	PlanState	ps;				/* its first field is NodeTag */
	bool		setop_done;		/* indicates completion of output scan */
	int64		numOutput;		/* number of dups left to output */
	int			numCols;		/* number of grouping columns */

	/* these fields are used in SETOP_SORTED mode: */
	SortSupport sortKeys;		/* per-grouping-field sort data */
	SetOpStatePerInput leftInput;	/* current outer-relation input state */
	SetOpStatePerInput rightInput;	/* current inner-relation input state */
	bool		need_init;		/* have we read the first tuples yet? */

	/* these fields are used in SETOP_HASHED mode: */
	Oid		   *eqfuncoids;		/* per-grouping-field equality fns */
	FmgrInfo   *hashfunctions;	/* per-grouping-field hash fns */
	TupleHashTable hashtable;	/* hash table with one entry per group */
	MemoryContext tuplesContext;	/* context containing hash table's tuples */
	bool		table_filled;	/* hash table filled yet? */
	TupleHashIterator hashiter; /* for iterating through hash table */
} SetOpState;

/* ----------------
 *	 LockRowsState information
 *
 *		LockRows nodes are used to enforce FOR [KEY] UPDATE/SHARE locking.
 * ----------------
 */
typedef struct LockRowsState
{
	PlanState	ps;				/* its first field is NodeTag */
	List	   *lr_arowMarks;	/* List of ExecAuxRowMarks */
	EPQState	lr_epqstate;	/* for evaluating EvalPlanQual rechecks */
} LockRowsState;

/* ----------------
 *	 LimitState information
 *
 *		Limit nodes are used to enforce LIMIT/OFFSET clauses.
 *		They just select the desired subrange of their subplan's output.
 *
 * offset is the number of initial tuples to skip (0 does nothing).
 * count is the number of tuples to return after skipping the offset tuples.
 * If no limit count was specified, count is undefined and noCount is true.
 * When lstate == LIMIT_INITIAL, offset/count/noCount haven't been set yet.
 * ----------------
 */
typedef enum
{
	LIMIT_INITIAL,				/* initial state for LIMIT node */
	LIMIT_RESCAN,				/* rescan after recomputing parameters */
	LIMIT_EMPTY,				/* there are no returnable rows */
	LIMIT_INWINDOW,				/* have returned a row in the window */
	LIMIT_WINDOWEND_TIES,		/* have returned a tied row */
	LIMIT_SUBPLANEOF,			/* at EOF of subplan (within window) */
	LIMIT_WINDOWEND,			/* stepped off end of window */
	LIMIT_WINDOWSTART,			/* stepped off beginning of window */
} LimitStateCond;

typedef struct LimitState
{
	PlanState	ps;				/* its first field is NodeTag */
	ExprState  *limitOffset;	/* OFFSET parameter, or NULL if none */
	ExprState  *limitCount;		/* COUNT parameter, or NULL if none */
	LimitOption limitOption;	/* limit specification type */
	int64		offset;			/* current OFFSET value */
	int64		count;			/* current COUNT, if any */
	bool		noCount;		/* if true, ignore count */
	LimitStateCond lstate;		/* state machine status, as above */
	int64		position;		/* 1-based index of last tuple returned */
	TupleTableSlot *subSlot;	/* tuple last obtained from subplan */
	ExprState  *eqfunction;		/* tuple equality qual in case of WITH TIES
								 * option */
	TupleTableSlot *last_slot;	/* slot for evaluation of ties */
} LimitState;

#endif							/* EXECNODES_H */
./files.txt0000664000175000017500000000111715222336323011551 0ustar  xmanxmansrc/backend/access/heap/heapam_handler.c
src/backend/bootstrap/bootstrap.c
src/backend/catalog/index.c
src/backend/catalog/indexing.c
src/backend/catalog/toasting.c
src/backend/commands/analyze.c
src/backend/commands/indexcmds.c
src/backend/executor/execIndexing.c
src/backend/nodes/makefuncs.c
src/backend/optimizer/path/indxpath.c
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/util/plancat.c
src/backend/utils/adt/selfuncs.c
src/backend/utils/cache/relcache.c
src/include/nodes/execnodes.h
src/include/nodes/pathnodes.h
src/include/utils/rel.h
src/include/utils/relcache.h
./heapam_handler.c0000664000175000017500000023606515221604040013007 0ustar  xmanxman/*-------------------------------------------------------------------------
 *
 * heapam_handler.c
 *	  heap table access method code
 *
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/access/heap/heapam_handler.c
 *
 *
 * NOTES
 *	  This files wires up the lower level heapam.c et al routines with the
 *	  tableam abstraction.
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/genam.h"
#include "access/heapam.h"
#include "access/heaptoast.h"
#include "access/multixact.h"
#include "access/rewriteheap.h"
#include "access/syncscan.h"
#include "access/tableam.h"
#include "access/tsmapi.h"
#include "access/visibilitymap.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/index.h"
#include "catalog/storage.h"
#include "catalog/storage_xlog.h"
#include "commands/progress.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "storage/bufmgr.h"
#include "storage/bufpage.h"
#include "storage/lmgr.h"
#include "storage/lock.h"
#include "storage/predicate.h"
#include "storage/procarray.h"
#include "storage/smgr.h"
#include "utils/builtins.h"
#include "utils/rel.h"
#include "utils/tuplesort.h"

static void reform_and_rewrite_tuple(HeapTuple tuple,
									 Relation OldHeap, Relation NewHeap,
									 Datum *values, bool *isnull, RewriteState rwstate);
static void heap_insert_for_repack(HeapTuple tuple, Relation OldHeap,
								   Relation NewHeap, Datum *values, bool *isnull,
								   BulkInsertState bistate);
static HeapTuple reform_tuple(HeapTuple tuple, Relation OldHeap,
							  Relation NewHeap, Datum *values, bool *isnull);

static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer,
								   HeapTuple tuple,
								   OffsetNumber tupoffset);

static BlockNumber heapam_scan_get_blocks_done(HeapScanDesc hscan);

static bool BitmapHeapScanNextBlock(TableScanDesc scan,
									bool *recheck,
									uint64 *lossy_pages, uint64 *exact_pages);


/* ------------------------------------------------------------------------
 * Slot related callbacks for heap AM
 * ------------------------------------------------------------------------
 */

static const TupleTableSlotOps *
heapam_slot_callbacks(Relation relation)
{
	return &TTSOpsBufferHeapTuple;
}


/* ------------------------------------------------------------------------
 * Callbacks for non-modifying operations on individual tuples for heap AM
 * ------------------------------------------------------------------------
 */

static bool
heapam_fetch_row_version(Relation relation,
						 ItemPointer tid,
						 Snapshot snapshot,
						 TupleTableSlot *slot)
{
	BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
	Buffer		buffer;

	Assert(TTS_IS_BUFFERTUPLE(slot));

	bslot->base.tupdata.t_self = *tid;
	if (heap_fetch(relation, snapshot, &bslot->base.tupdata, &buffer, false))
	{
		/* store in slot, transferring existing pin */
		ExecStorePinnedBufferHeapTuple(&bslot->base.tupdata, slot, buffer);
		slot->tts_tableOid = RelationGetRelid(relation);

		return true;
	}

	return false;
}

static bool
heapam_tuple_tid_valid(TableScanDesc scan, ItemPointer tid)
{
	HeapScanDesc hscan = (HeapScanDesc) scan;

	return ItemPointerIsValid(tid) &&
		ItemPointerGetBlockNumber(tid) < hscan->rs_nblocks;
}

static bool
heapam_tuple_satisfies_snapshot(Relation rel, TupleTableSlot *slot,
								Snapshot snapshot)
{
	BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
	bool		res;

	Assert(TTS_IS_BUFFERTUPLE(slot));
	Assert(BufferIsValid(bslot->buffer));

	/*
	 * We need buffer pin and lock to call HeapTupleSatisfiesVisibility.
	 * Caller should be holding pin, but not lock.
	 */
	LockBuffer(bslot->buffer, BUFFER_LOCK_SHARE);
	res = HeapTupleSatisfiesVisibility(bslot->base.tuple, snapshot,
									   bslot->buffer);
	LockBuffer(bslot->buffer, BUFFER_LOCK_UNLOCK);

	return res;
}


/* ----------------------------------------------------------------------------
 *  Functions for manipulations of physical tuples for heap AM.
 * ----------------------------------------------------------------------------
 */

static void
heapam_tuple_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
					uint32 options, BulkInsertState bistate)
{
	bool		shouldFree = true;
	HeapTuple	tuple = ExecFetchSlotHeapTuple(slot, true, &shouldFree);

	/* Update the tuple with table oid */
	slot->tts_tableOid = RelationGetRelid(relation);
	tuple->t_tableOid = slot->tts_tableOid;

	/* Perform the insertion, and copy the resulting ItemPointer */
	heap_insert(relation, tuple, cid, options, bistate);
	ItemPointerCopy(&tuple->t_self, &slot->tts_tid);

	if (shouldFree)
		pfree(tuple);
}

static void
heapam_tuple_insert_speculative(Relation relation, TupleTableSlot *slot,
								CommandId cid, uint32 options,
								BulkInsertState bistate, uint32 specToken)
{
	bool		shouldFree = true;
	HeapTuple	tuple = ExecFetchSlotHeapTuple(slot, true, &shouldFree);

	/* Update the tuple with table oid */
	slot->tts_tableOid = RelationGetRelid(relation);
	tuple->t_tableOid = slot->tts_tableOid;

	HeapTupleHeaderSetSpeculativeToken(tuple->t_data, specToken);
	options |= HEAP_INSERT_SPECULATIVE;

	/* Perform the insertion, and copy the resulting ItemPointer */
	heap_insert(relation, tuple, cid, options, bistate);
	ItemPointerCopy(&tuple->t_self, &slot->tts_tid);

	if (shouldFree)
		pfree(tuple);
}

static void
heapam_tuple_complete_speculative(Relation relation, TupleTableSlot *slot,
								  uint32 specToken, bool succeeded)
{
	bool		shouldFree = true;
	HeapTuple	tuple = ExecFetchSlotHeapTuple(slot, true, &shouldFree);

	/* adjust the tuple's state accordingly */
	if (succeeded)
		heap_finish_speculative(relation, &slot->tts_tid);
	else
		heap_abort_speculative(relation, &slot->tts_tid);

	if (shouldFree)
		pfree(tuple);
}

static TM_Result
heapam_tuple_delete(Relation relation, ItemPointer tid, CommandId cid,
					uint32 options, Snapshot snapshot, Snapshot crosscheck,
					bool wait, TM_FailureData *tmfd)
{
	/*
	 * Currently Deleting of index tuples are handled at vacuum, in case if
	 * the storage itself is cleaning the dead tuples by itself, it is the
	 * time to call the index tuple deletion also.
	 */
	return heap_delete(relation, tid, cid, options, crosscheck, wait,
					   tmfd);
}


static TM_Result
heapam_tuple_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
					CommandId cid, uint32 options,
					Snapshot snapshot, Snapshot crosscheck,
					bool wait, TM_FailureData *tmfd,
					LockTupleMode *lockmode, TU_UpdateIndexes *update_indexes)
{
	bool		shouldFree = true;
	HeapTuple	tuple = ExecFetchSlotHeapTuple(slot, true, &shouldFree);
	TM_Result	result;

	/* Update the tuple with table oid */
	slot->tts_tableOid = RelationGetRelid(relation);
	tuple->t_tableOid = slot->tts_tableOid;

	result = heap_update(relation, otid, tuple, cid, options,
						 crosscheck, wait,
						 tmfd, lockmode, update_indexes);
	ItemPointerCopy(&tuple->t_self, &slot->tts_tid);

	/*
	 * Decide whether new index entries are needed for the tuple
	 *
	 * Note: heap_update returns the tid (location) of the new tuple in the
	 * t_self field.
	 *
	 * If the update is not HOT, we must update all indexes. If the update is
	 * HOT, it could be that we updated summarized columns, so we either
	 * update only summarized indexes, or none at all.
	 */
	if (result != TM_Ok)
	{
		Assert(*update_indexes == TU_None);
		*update_indexes = TU_None;
	}
	else if (!HeapTupleIsHeapOnly(tuple))
		Assert(*update_indexes == TU_All);
	else
		Assert((*update_indexes == TU_Summarizing) ||
			   (*update_indexes == TU_None));

	if (shouldFree)
		pfree(tuple);

	return result;
}

static TM_Result
heapam_tuple_lock(Relation relation, ItemPointer tid, Snapshot snapshot,
				  TupleTableSlot *slot, CommandId cid, LockTupleMode mode,
				  LockWaitPolicy wait_policy, uint8 flags,
				  TM_FailureData *tmfd)
{
	BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
	TM_Result	result;
	Buffer		buffer;
	HeapTuple	tuple = &bslot->base.tupdata;
	bool		follow_updates;

	follow_updates = (flags & TUPLE_LOCK_FLAG_LOCK_UPDATE_IN_PROGRESS) != 0;
	tmfd->traversed = false;

	Assert(TTS_IS_BUFFERTUPLE(slot));

tuple_lock_retry:
	tuple->t_self = *tid;
	result = heap_lock_tuple(relation, tuple, cid, mode, wait_policy,
							 follow_updates, &buffer, tmfd);

	if (result == TM_Updated &&
		(flags & TUPLE_LOCK_FLAG_FIND_LAST_VERSION))
	{
		/* Should not encounter speculative tuple on recheck */
		Assert(!HeapTupleHeaderIsSpeculative(tuple->t_data));

		ReleaseBuffer(buffer);

		if (!ItemPointerEquals(&tmfd->ctid, &tuple->t_self))
		{
			SnapshotData SnapshotDirty;
			TransactionId priorXmax;

			/* it was updated, so look at the updated version */
			*tid = tmfd->ctid;
			/* updated row should have xmin matching this xmax */
			priorXmax = tmfd->xmax;

			/* signal that a tuple later in the chain is getting locked */
			tmfd->traversed = true;

			/*
			 * fetch target tuple
			 *
			 * Loop here to deal with updated or busy tuples
			 */
			InitDirtySnapshot(SnapshotDirty);
			for (;;)
			{
				if (ItemPointerIndicatesMovedPartitions(tid))
					ereport(ERROR,
							(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
							 errmsg("tuple to be locked was already moved to another partition due to concurrent update")));

				tuple->t_self = *tid;
				if (heap_fetch(relation, &SnapshotDirty, tuple, &buffer, true))
				{
					/*
					 * If xmin isn't what we're expecting, the slot must have
					 * been recycled and reused for an unrelated tuple.  This
					 * implies that the latest version of the row was deleted,
					 * so we need do nothing.  (Should be safe to examine xmin
					 * without getting buffer's content lock.  We assume
					 * reading a TransactionId to be atomic, and Xmin never
					 * changes in an existing tuple, except to invalid or
					 * frozen, and neither of those can match priorXmax.)
					 */
					if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple->t_data),
											 priorXmax))
					{
						ReleaseBuffer(buffer);
						return TM_Deleted;
					}

					/* otherwise xmin should not be dirty... */
					if (TransactionIdIsValid(SnapshotDirty.xmin))
						ereport(ERROR,
								(errcode(ERRCODE_DATA_CORRUPTED),
								 errmsg_internal("t_xmin %u is uncommitted in tuple (%u,%u) to be updated in table \"%s\"",
												 SnapshotDirty.xmin,
												 ItemPointerGetBlockNumber(&tuple->t_self),
												 ItemPointerGetOffsetNumber(&tuple->t_self),
												 RelationGetRelationName(relation))));

					/*
					 * If tuple is being updated by other transaction then we
					 * have to wait for its commit/abort, or die trying.
					 */
					if (TransactionIdIsValid(SnapshotDirty.xmax))
					{
						ReleaseBuffer(buffer);
						switch (wait_policy)
						{
							case LockWaitBlock:
								XactLockTableWait(SnapshotDirty.xmax,
												  relation, &tuple->t_self,
												  XLTW_FetchUpdated);
								break;
							case LockWaitSkip:
								if (!ConditionalXactLockTableWait(SnapshotDirty.xmax, false))
									/* skip instead of waiting */
									return TM_WouldBlock;
								break;
							case LockWaitError:
								if (!ConditionalXactLockTableWait(SnapshotDirty.xmax, log_lock_failures))
									ereport(ERROR,
											(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
											 errmsg("could not obtain lock on row in relation \"%s\"",
													RelationGetRelationName(relation))));
								break;
						}
						continue;	/* loop back to repeat heap_fetch */
					}

					/*
					 * If tuple was inserted by our own transaction, we have
					 * to check cmin against cid: cmin >= current CID means
					 * our command cannot see the tuple, so we should ignore
					 * it. Otherwise heap_lock_tuple() will throw an error,
					 * and so would any later attempt to update or delete the
					 * tuple.  (We need not check cmax because
					 * HeapTupleSatisfiesDirty will consider a tuple deleted
					 * by our transaction dead, regardless of cmax.)  We just
					 * checked that priorXmax == xmin, so we can test that
					 * variable instead of doing HeapTupleHeaderGetXmin again.
					 */
					if (TransactionIdIsCurrentTransactionId(priorXmax) &&
						HeapTupleHeaderGetCmin(tuple->t_data) >= cid)
					{
						tmfd->xmax = priorXmax;

						/*
						 * Cmin is the problematic value, so store that. See
						 * above.
						 */
						tmfd->cmax = HeapTupleHeaderGetCmin(tuple->t_data);
						ReleaseBuffer(buffer);
						return TM_SelfModified;
					}

					/*
					 * This is a live tuple, so try to lock it again.
					 */
					ReleaseBuffer(buffer);
					goto tuple_lock_retry;
				}

				/*
				 * If the referenced slot was actually empty, the latest
				 * version of the row must have been deleted, so we need do
				 * nothing.
				 */
				if (tuple->t_data == NULL)
				{
					Assert(!BufferIsValid(buffer));
					return TM_Deleted;
				}

				/*
				 * As above, if xmin isn't what we're expecting, do nothing.
				 */
				if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple->t_data),
										 priorXmax))
				{
					ReleaseBuffer(buffer);
					return TM_Deleted;
				}

				/*
				 * If we get here, the tuple was found but failed
				 * SnapshotDirty. Assuming the xmin is either a committed xact
				 * or our own xact (as it certainly should be if we're trying
				 * to modify the tuple), this must mean that the row was
				 * updated or deleted by either a committed xact or our own
				 * xact.  If it was deleted, we can ignore it; if it was
				 * updated then chain up to the next version and repeat the
				 * whole process.
				 *
				 * As above, it should be safe to examine xmax and t_ctid
				 * without the buffer content lock, because they can't be
				 * changing.  We'd better hold a buffer pin though.
				 */
				if (ItemPointerEquals(&tuple->t_self, &tuple->t_data->t_ctid))
				{
					/* deleted, so forget about it */
					ReleaseBuffer(buffer);
					return TM_Deleted;
				}

				/* updated, so look at the updated row */
				*tid = tuple->t_data->t_ctid;
				/* updated row should have xmin matching this xmax */
				priorXmax = HeapTupleHeaderGetUpdateXid(tuple->t_data);
				ReleaseBuffer(buffer);
				/* loop back to fetch next in chain */
			}
		}
		else
		{
			/* tuple was deleted, so give up */
			return TM_Deleted;
		}
	}

	slot->tts_tableOid = RelationGetRelid(relation);
	tuple->t_tableOid = slot->tts_tableOid;

	/* store in slot, transferring existing pin */
	ExecStorePinnedBufferHeapTuple(tuple, slot, buffer);

	return result;
}


/* ------------------------------------------------------------------------
 * DDL related callbacks for heap AM.
 * ------------------------------------------------------------------------
 */

static void
heapam_relation_set_new_filelocator(Relation rel,
									const RelFileLocator *newrlocator,
									char persistence,
									TransactionId *freezeXid,
									MultiXactId *minmulti)
{
	SMgrRelation srel;

	/*
	 * Initialize to the minimum XID that could put tuples in the table. We
	 * know that no xacts older than RecentXmin are still running, so that
	 * will do.
	 */
	*freezeXid = RecentXmin;

	/*
	 * Similarly, initialize the minimum Multixact to the first value that
	 * could possibly be stored in tuples in the table.  Running transactions
	 * could reuse values from their local cache, so we are careful to
	 * consider all currently running multis.
	 *
	 * XXX this could be refined further, but is it worth the hassle?
	 */
	*minmulti = GetOldestMultiXactId();

	srel = RelationCreateStorage(*newrlocator, persistence, true);

	/*
	 * If required, set up an init fork for an unlogged table so that it can
	 * be correctly reinitialized on restart.
	 */
	if (persistence == RELPERSISTENCE_UNLOGGED)
	{
		Assert(rel->rd_rel->relkind == RELKIND_RELATION ||
			   rel->rd_rel->relkind == RELKIND_TOASTVALUE);
		smgrcreate(srel, INIT_FORKNUM, false);
		log_smgrcreate(newrlocator, INIT_FORKNUM);
	}

	smgrclose(srel);
}

static void
heapam_relation_nontransactional_truncate(Relation rel)
{
	RelationTruncate(rel, 0);
}

static void
heapam_relation_copy_data(Relation rel, const RelFileLocator *newrlocator)
{
	SMgrRelation dstrel;

	/*
	 * Since we copy the file directly without looking at the shared buffers,
	 * we'd better first flush out any pages of the source relation that are
	 * in shared buffers.  We assume no new changes will be made while we are
	 * holding exclusive lock on the rel.
	 */
	FlushRelationBuffers(rel);

	/*
	 * Create and copy all forks of the relation, and schedule unlinking of
	 * old physical files.
	 *
	 * NOTE: any conflict in relfilenumber value will be caught in
	 * RelationCreateStorage().
	 */
	dstrel = RelationCreateStorage(*newrlocator, rel->rd_rel->relpersistence, true);

	/* copy main fork */
	RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
						rel->rd_rel->relpersistence);

	/* copy those extra forks that exist */
	for (ForkNumber forkNum = MAIN_FORKNUM + 1;
		 forkNum <= MAX_FORKNUM; forkNum++)
	{
		if (smgrexists(RelationGetSmgr(rel), forkNum))
		{
			smgrcreate(dstrel, forkNum, false);

			/*
			 * WAL log creation if the relation is persistent, or this is the
			 * init fork of an unlogged relation.
			 */
			if (RelationIsPermanent(rel) ||
				(rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
				 forkNum == INIT_FORKNUM))
				log_smgrcreate(newrlocator, forkNum);
			RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
								rel->rd_rel->relpersistence);
		}
	}


	/* drop old relation, and close new one */
	RelationDropStorage(rel);
	smgrclose(dstrel);
}

static void
heapam_relation_copy_for_cluster(Relation OldHeap, Relation NewHeap,
								 Relation OldIndex, bool use_sort,
								 TransactionId OldestXmin,
								 Snapshot snapshot,
								 TransactionId *xid_cutoff,
								 MultiXactId *multi_cutoff,
								 double *num_tuples,
								 double *tups_vacuumed,
								 double *tups_recently_dead)
{
	RewriteState rwstate;
	BulkInsertState bistate;
	IndexScanDesc indexScan;
	TableScanDesc tableScan;
	HeapScanDesc heapScan;
	bool		is_system_catalog;
	Tuplesortstate *tuplesort;
	TupleDesc	oldTupDesc = RelationGetDescr(OldHeap);
	TupleDesc	newTupDesc = RelationGetDescr(NewHeap);
	TupleTableSlot *slot;
	int			natts;
	Datum	   *values;
	bool	   *isnull;
	BufferHeapTupleTableSlot *hslot;
	BlockNumber prev_cblock = InvalidBlockNumber;
	bool		concurrent = snapshot != NULL;

	/* Remember if it's a system catalog */
	is_system_catalog = IsSystemRelation(OldHeap);

	/*
	 * Valid smgr_targblock implies something already wrote to the relation.
	 * This may be harmless, but this function hasn't planned for it.
	 */
	Assert(RelationGetTargetBlock(NewHeap) == InvalidBlockNumber);

	/* Preallocate values/isnull arrays */
	natts = newTupDesc->natts;
	values = palloc_array(Datum, natts);
	isnull = palloc_array(bool, natts);

	/*
	 * In non-concurrent mode, initialize the rewrite operation.  This is not
	 * needed in concurrent mode.
	 */
	if (!concurrent)
		rwstate = begin_heap_rewrite(OldHeap, NewHeap, OldestXmin,
									 *xid_cutoff, *multi_cutoff);
	else
		rwstate = NULL;

	/* In concurrent mode, prepare for bulk-insert operation. */
	if (concurrent)
		bistate = GetBulkInsertState();
	else
		bistate = NULL;

	/* Set up sorting if wanted */
	if (use_sort)
		tuplesort = tuplesort_begin_cluster(oldTupDesc, OldIndex,
											maintenance_work_mem,
											NULL, TUPLESORT_NONE);
	else
		tuplesort = NULL;

	/*
	 * Prepare to scan the OldHeap.  To ensure we see recently-dead tuples
	 * that still need to be copied, we scan with SnapshotAny and use
	 * HeapTupleSatisfiesVacuum for the visibility test.
	 *
	 * In the CONCURRENTLY case, we do regular MVCC visibility tests, using
	 * the snapshot passed by the caller.
	 */
	if (OldIndex != NULL && !use_sort)
	{
		const int	ci_index[] = {
			PROGRESS_REPACK_PHASE,
			PROGRESS_REPACK_INDEX_RELID
		};
		int64		ci_val[2];

		/* Set phase and OIDOldIndex to columns */
		ci_val[0] = PROGRESS_REPACK_PHASE_INDEX_SCAN_HEAP;
		ci_val[1] = RelationGetRelid(OldIndex);
		pgstat_progress_update_multi_param(2, ci_index, ci_val);

		tableScan = NULL;
		heapScan = NULL;
		indexScan = index_beginscan(OldHeap, OldIndex,
									snapshot ? snapshot : SnapshotAny,
									NULL, 0, 0,
									SO_NONE);
		index_rescan(indexScan, NULL, 0, NULL, 0);
	}
	else
	{
		/* In scan-and-sort mode and also VACUUM FULL, set phase */
		pgstat_progress_update_param(PROGRESS_REPACK_PHASE,
									 PROGRESS_REPACK_PHASE_SEQ_SCAN_HEAP);

		tableScan = table_beginscan(OldHeap,
									snapshot ? snapshot : SnapshotAny,
									0, (ScanKey) NULL,
									SO_NONE);
		heapScan = (HeapScanDesc) tableScan;
		indexScan = NULL;

		/* Set total heap blocks */
		pgstat_progress_update_param(PROGRESS_REPACK_TOTAL_HEAP_BLKS,
									 heapScan->rs_nblocks);
	}

	slot = table_slot_create(OldHeap, NULL);
	hslot = (BufferHeapTupleTableSlot *) slot;

	/*
	 * Scan through the OldHeap, either in OldIndex order or sequentially;
	 * copy each tuple into the NewHeap, or transiently to the tuplesort
	 * module.  Note that we don't bother sorting dead tuples (they won't get
	 * to the new table anyway).
	 */
	for (;;)
	{
		HeapTuple	tuple;
		Buffer		buf;
		bool		isdead;

		CHECK_FOR_INTERRUPTS();

		if (indexScan != NULL)
		{
			if (!index_getnext_slot(indexScan, ForwardScanDirection, slot))
				break;

			/* Since we used no scan keys, should never need to recheck */
			if (indexScan->xs_recheck)
				elog(ERROR, "CLUSTER does not support lossy index conditions");
		}
		else
		{
			if (!table_scan_getnextslot(tableScan, ForwardScanDirection, slot))
			{
				/*
				 * If the last pages of the scan were empty, we would go to
				 * the next phase while heap_blks_scanned != heap_blks_total.
				 * Instead, to ensure that heap_blks_scanned is equivalent to
				 * heap_blks_total after the table scan phase, this parameter
				 * is manually updated to the correct value when the table
				 * scan finishes.
				 */
				pgstat_progress_update_param(PROGRESS_REPACK_HEAP_BLKS_SCANNED,
											 heapScan->rs_nblocks);
				break;
			}

			/*
			 * In scan-and-sort mode and also VACUUM FULL, set heap blocks
			 * scanned
			 *
			 * Note that heapScan may start at an offset and wrap around, i.e.
			 * rs_startblock may be >0, and rs_cblock may end with a number
			 * below rs_startblock. To prevent showing this wraparound to the
			 * user, we offset rs_cblock by rs_startblock (modulo rs_nblocks).
			 */
			if (prev_cblock != heapScan->rs_cblock)
			{
				pgstat_progress_update_param(PROGRESS_REPACK_HEAP_BLKS_SCANNED,
											 (heapScan->rs_cblock +
											  heapScan->rs_nblocks -
											  heapScan->rs_startblock
											  ) % heapScan->rs_nblocks + 1);
				prev_cblock = heapScan->rs_cblock;
			}
		}

		tuple = ExecFetchSlotHeapTuple(slot, false, NULL);
		buf = hslot->buffer;

		/*
		 * In concurrent mode, our table or index scan has used regular MVCC
		 * visibility test against a snapshot passed by caller; therefore we
		 * don't need another visibility test.  In non-concurrent mode
		 * however, we must test the visibility of each tuple we read.
		 */
		if (!concurrent)
		{
			/*
			 * To be able to guarantee that we can set the hint bit, acquire
			 * an exclusive lock on the old buffer. We need the hint bits, set
			 * in heapam_relation_copy_for_cluster() ->
			 * HeapTupleSatisfiesVacuum(), to be set, as otherwise
			 * reform_and_rewrite_tuple() -> rewrite_heap_tuple() will get
			 * confused. Specifically, rewrite_heap_tuple() checks for
			 * HEAP_XMAX_INVALID in the old tuple to determine whether to
			 * check the old-to-new mapping hash table.
			 *
			 * It'd be better if we somehow could avoid setting hint bits on
			 * the old page. One reason to use VACUUM FULL are very bloated
			 * tables - rewriting most of the old table during VACUUM FULL
			 * doesn't exactly help...
			 */
			LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);

			switch (HeapTupleSatisfiesVacuum(tuple, OldestXmin, buf))
			{
				case HEAPTUPLE_DEAD:
					/* Definitely dead */
					isdead = true;
					break;
				case HEAPTUPLE_RECENTLY_DEAD:
					*tups_recently_dead += 1;
					pg_fallthrough;
				case HEAPTUPLE_LIVE:
					/* Live or recently dead, must copy it */
					isdead = false;
					break;
				case HEAPTUPLE_INSERT_IN_PROGRESS:

					/*
					 * As long as we hold exclusive lock on the relation,
					 * normally the only way to see this is if it was inserted
					 * earlier in our own transaction.  However, it can happen
					 * in system catalogs, since we tend to release write lock
					 * before commit there. Give a warning if neither case
					 * applies; but in any case we had better copy it.
					 */
					if (!is_system_catalog &&
						!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple->t_data)))
						elog(WARNING, "concurrent insert in progress within table \"%s\"",
							 RelationGetRelationName(OldHeap));
					/* treat as live */
					isdead = false;
					break;
				case HEAPTUPLE_DELETE_IN_PROGRESS:

					/*
					 * Similar situation to INSERT_IN_PROGRESS case.
					 */
					if (!is_system_catalog &&
						!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(tuple->t_data)))
						elog(WARNING, "concurrent delete in progress within table \"%s\"",
							 RelationGetRelationName(OldHeap));
					/* treat as recently dead */
					*tups_recently_dead += 1;
					isdead = false;
					break;
				default:
					elog(ERROR, "unexpected HeapTupleSatisfiesVacuum result");
					isdead = false; /* keep compiler quiet */
					break;
			}

			LockBuffer(buf, BUFFER_LOCK_UNLOCK);

			if (isdead)
			{
				*tups_vacuumed += 1;
				/* heap rewrite module still needs to see it... */
				if (rewrite_heap_dead_tuple(rwstate, tuple))
				{
					/* A previous recently-dead tuple is now known dead */
					*tups_vacuumed += 1;
					*tups_recently_dead -= 1;
				}

				continue;
			}
		}

		*num_tuples += 1;
		if (tuplesort != NULL)
		{
			tuplesort_putheaptuple(tuplesort, tuple);

			/*
			 * In scan-and-sort mode, report increase in number of tuples
			 * scanned
			 */
			pgstat_progress_update_param(PROGRESS_REPACK_HEAP_TUPLES_SCANNED,
										 *num_tuples);
		}
		else
		{
			const int	ct_index[] = {
				PROGRESS_REPACK_HEAP_TUPLES_SCANNED,
				PROGRESS_REPACK_HEAP_TUPLES_INSERTED
			};
			int64		ct_val[2];

			if (!concurrent)
				reform_and_rewrite_tuple(tuple, OldHeap, NewHeap,
										 values, isnull, rwstate);
			else
				heap_insert_for_repack(tuple, OldHeap, NewHeap,
									   values, isnull, bistate);

			/*
			 * In indexscan mode and also VACUUM FULL, report increase in
			 * number of tuples scanned and written
			 */
			ct_val[0] = *num_tuples;
			ct_val[1] = *num_tuples;
			pgstat_progress_update_multi_param(2, ct_index, ct_val);
		}
	}

	if (indexScan != NULL)
		index_endscan(indexScan);
	if (tableScan != NULL)
		table_endscan(tableScan);
	if (slot)
		ExecDropSingleTupleTableSlot(slot);

	/*
	 * In scan-and-sort mode, complete the sort, then read out all live tuples
	 * from the tuplestore and write them to the new relation.
	 */
	if (tuplesort != NULL)
	{
		double		n_tuples = 0;

		/* Report that we are now sorting tuples */
		pgstat_progress_update_param(PROGRESS_REPACK_PHASE,
									 PROGRESS_REPACK_PHASE_SORT_TUPLES);

		tuplesort_performsort(tuplesort);

		/* Report that we are now writing new heap */
		pgstat_progress_update_param(PROGRESS_REPACK_PHASE,
									 PROGRESS_REPACK_PHASE_WRITE_NEW_HEAP);

		for (;;)
		{
			HeapTuple	tuple;

			CHECK_FOR_INTERRUPTS();

			tuple = tuplesort_getheaptuple(tuplesort, true);
			if (tuple == NULL)
				break;

			n_tuples += 1;
			if (!concurrent)
				reform_and_rewrite_tuple(tuple,
										 OldHeap, NewHeap,
										 values, isnull,
										 rwstate);
			else
				heap_insert_for_repack(tuple, OldHeap, NewHeap,
									   values, isnull, bistate);

			/* Report n_tuples */
			pgstat_progress_update_param(PROGRESS_REPACK_HEAP_TUPLES_INSERTED,
										 n_tuples);
		}

		tuplesort_end(tuplesort);
	}

	/* Write out any remaining tuples, and fsync if needed */
	if (rwstate)
		end_heap_rewrite(rwstate);
	if (bistate)
		FreeBulkInsertState(bistate);

	/* Clean up */
	pfree(values);
	pfree(isnull);
}

/*
 * Prepare to analyze the next block in the read stream.  Returns false if
 * the stream is exhausted and true otherwise. The scan must have been started
 * with SO_TYPE_ANALYZE option.
 *
 * This routine holds a buffer pin and lock on the heap page.  They are held
 * until heapam_scan_analyze_next_tuple() returns false.  That is until all the
 * items of the heap page are analyzed.
 */
static bool
heapam_scan_analyze_next_block(TableScanDesc scan, ReadStream *stream)
{
	HeapScanDesc hscan = (HeapScanDesc) scan;

	/*
	 * We must maintain a pin on the target page's buffer to ensure that
	 * concurrent activity - e.g. HOT pruning - doesn't delete tuples out from
	 * under us.  It comes from the stream already pinned.   We also choose to
	 * hold sharelock on the buffer throughout --- we could release and
	 * re-acquire sharelock for each tuple, but since we aren't doing much
	 * work per tuple, the extra lock traffic is probably better avoided.
	 */
	hscan->rs_cbuf = read_stream_next_buffer(stream, NULL);
	if (!BufferIsValid(hscan->rs_cbuf))
		return false;

	LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_SHARE);

	hscan->rs_cblock = BufferGetBlockNumber(hscan->rs_cbuf);
	hscan->rs_cindex = FirstOffsetNumber;
	return true;
}

static bool
heapam_scan_analyze_next_tuple(TableScanDesc scan,
							   double *liverows, double *deadrows,
							   TupleTableSlot *slot)
{
	HeapScanDesc hscan = (HeapScanDesc) scan;
	Page		targpage;
	OffsetNumber maxoffset;
	BufferHeapTupleTableSlot *hslot;

	Assert(TTS_IS_BUFFERTUPLE(slot));

	hslot = (BufferHeapTupleTableSlot *) slot;
	targpage = BufferGetPage(hscan->rs_cbuf);
	maxoffset = PageGetMaxOffsetNumber(targpage);

	/* Inner loop over all tuples on the selected page */
	for (; hscan->rs_cindex <= maxoffset; hscan->rs_cindex++)
	{
		ItemId		itemid;
		HeapTuple	targtuple = &hslot->base.tupdata;
		bool		sample_it = false;
		TransactionId dead_after;

		itemid = PageGetItemId(targpage, hscan->rs_cindex);

		/*
		 * We ignore unused and redirect line pointers.  DEAD line pointers
		 * should be counted as dead, because we need vacuum to run to get rid
		 * of them.  Note that this rule agrees with the way that
		 * heap_page_prune_and_freeze() counts things.
		 */
		if (!ItemIdIsNormal(itemid))
		{
			if (ItemIdIsDead(itemid))
				*deadrows += 1;
			continue;
		}

		ItemPointerSet(&targtuple->t_self, hscan->rs_cblock, hscan->rs_cindex);

		targtuple->t_tableOid = RelationGetRelid(scan->rs_rd);
		targtuple->t_data = (HeapTupleHeader) PageGetItem(targpage, itemid);
		targtuple->t_len = ItemIdGetLength(itemid);

		switch (HeapTupleSatisfiesVacuumHorizon(targtuple,
												hscan->rs_cbuf,
												&dead_after))
		{
			case HEAPTUPLE_LIVE:
				sample_it = true;
				*liverows += 1;
				break;

			case HEAPTUPLE_DEAD:
			case HEAPTUPLE_RECENTLY_DEAD:
				/* Count dead and recently-dead rows */
				*deadrows += 1;
				break;

			case HEAPTUPLE_INSERT_IN_PROGRESS:

				/*
				 * Insert-in-progress rows are not counted.  We assume that
				 * when the inserting transaction commits or aborts, it will
				 * send a stats message to increment the proper count.  This
				 * works right only if that transaction ends after we finish
				 * analyzing the table; if things happen in the other order,
				 * its stats update will be overwritten by ours.  However, the
				 * error will be large only if the other transaction runs long
				 * enough to insert many tuples, so assuming it will finish
				 * after us is the safer option.
				 *
				 * A special case is that the inserting transaction might be
				 * our own.  In this case we should count and sample the row,
				 * to accommodate users who load a table and analyze it in one
				 * transaction.  (pgstat_report_analyze has to adjust the
				 * numbers we report to the cumulative stats system to make
				 * this come out right.)
				 */
				if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(targtuple->t_data)))
				{
					sample_it = true;
					*liverows += 1;
				}
				break;

			case HEAPTUPLE_DELETE_IN_PROGRESS:

				/*
				 * We count and sample delete-in-progress rows the same as
				 * live ones, so that the stats counters come out right if the
				 * deleting transaction commits after us, per the same
				 * reasoning given above.
				 *
				 * If the delete was done by our own transaction, however, we
				 * must count the row as dead to make pgstat_report_analyze's
				 * stats adjustments come out right.  (Note: this works out
				 * properly when the row was both inserted and deleted in our
				 * xact.)
				 *
				 * The net effect of these choices is that we act as though an
				 * IN_PROGRESS transaction hasn't happened yet, except if it
				 * is our own transaction, which we assume has happened.
				 *
				 * This approach ensures that we behave sanely if we see both
				 * the pre-image and post-image rows for a row being updated
				 * by a concurrent transaction: we will sample the pre-image
				 * but not the post-image.  We also get sane results if the
				 * concurrent transaction never commits.
				 */
				if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(targtuple->t_data)))
					*deadrows += 1;
				else
				{
					sample_it = true;
					*liverows += 1;
				}
				break;

			default:
				elog(ERROR, "unexpected HeapTupleSatisfiesVacuum result");
				break;
		}

		if (sample_it)
		{
			ExecStoreBufferHeapTuple(targtuple, slot, hscan->rs_cbuf);
			hscan->rs_cindex++;

			/* note that we leave the buffer locked here! */
			return true;
		}
	}

	/* Now release the lock and pin on the page */
	UnlockReleaseBuffer(hscan->rs_cbuf);
	hscan->rs_cbuf = InvalidBuffer;

	/* also prevent old slot contents from having pin on page */
	ExecClearTuple(slot);

	return false;
}

static double
heapam_index_build_range_scan(Relation heapRelation,
							  Relation indexRelation,
							  IndexInfo *indexInfo,
							  bool allow_sync,
							  bool anyvisible,
							  bool progress,
							  BlockNumber start_blockno,
							  BlockNumber numblocks,
							  IndexBuildCallback callback,
							  void *callback_state,
							  TableScanDesc scan)
{
	HeapScanDesc hscan;
	bool		is_system_catalog;
	bool		checking_uniqueness;
	HeapTuple	heapTuple;
	Datum		values[INDEX_MAX_KEYS];
	bool		isnull[INDEX_MAX_KEYS];
	double		reltuples;
	ExprState  *predicateExpand;
	TupleTableSlot *slot;
	EState	   *estate;
	ExprContext *econtext;
	Snapshot	snapshot;
	bool		need_unregister_snapshot = false;
	TransactionId OldestXmin;
	BlockNumber previous_blkno = InvalidBlockNumber;
	BlockNumber root_blkno = InvalidBlockNumber;
	OffsetNumber root_offsets[MaxHeapTuplesPerPage];

	/*
	 * sanity checks
	 */
	Assert(OidIsValid(indexRelation->rd_rel->relam));

	/* Remember if it's a system catalog */
	is_system_catalog = IsSystemRelation(heapRelation);

	/* See whether we're verifying uniqueness/exclusion properties */
	checking_uniqueness = (indexInfo->ii_Unique ||
						   indexInfo->ii_ExclusionOps != NULL);

	/*
	 * "Any visible" mode is not compatible with uniqueness checks; make sure
	 * only one of those is requested.
	 */
	Assert(!(anyvisible && checking_uniqueness));

	/*
	 * Need an EState for evaluation of index expressions and partial-index
	 * predicates.  Also a slot to hold the current tuple.
	 */
	estate = CreateExecutorState();
	econtext = GetPerTupleExprContext(estate);
	slot = table_slot_create(heapRelation, NULL);

	/* Arrange for econtext's scan tuple to be the tuple under test */
	econtext->ecxt_scantuple = slot;

	/* Set up execution state for predicate, if any. */
	predicateExpand = ExecPrepareQual(indexInfo->ii_PredicateExpand, estate);

	/*
	 * Prepare for scan of the base relation.  In a normal index build, we use
	 * SnapshotAny because we must retrieve all tuples and do our own time
	 * qual checks (because we have to index RECENTLY_DEAD tuples). In a
	 * concurrent build, or during bootstrap, we take a regular MVCC snapshot
	 * and index whatever's live according to that.
	 */
	OldestXmin = InvalidTransactionId;

	/* okay to ignore lazy VACUUMs here */
	if (!IsBootstrapProcessingMode() && !indexInfo->ii_Concurrent)
		OldestXmin = GetOldestNonRemovableTransactionId(heapRelation);

	if (!scan)
	{
		/*
		 * Serial index build.
		 *
		 * Must begin our own heap scan in this case.  We may also need to
		 * register a snapshot whose lifetime is under our direct control.
		 */
		if (!TransactionIdIsValid(OldestXmin))
		{
			snapshot = RegisterSnapshot(GetTransactionSnapshot());
			need_unregister_snapshot = true;
		}
		else
			snapshot = SnapshotAny;

		scan = table_beginscan_strat(heapRelation,	/* relation */
									 snapshot,	/* snapshot */
									 0, /* number of keys */
									 NULL,	/* scan key */
									 true,	/* buffer access strategy OK */
									 allow_sync);	/* syncscan OK? */
	}
	else
	{
		/*
		 * Parallel index build.
		 *
		 * Parallel case never registers/unregisters own snapshot.  Snapshot
		 * is taken from parallel heap scan, and is SnapshotAny or an MVCC
		 * snapshot, based on same criteria as serial case.
		 */
		Assert(!IsBootstrapProcessingMode());
		Assert(allow_sync);
		snapshot = scan->rs_snapshot;
	}

	hscan = (HeapScanDesc) scan;

	/*
	 * Must have called GetOldestNonRemovableTransactionId() if using
	 * SnapshotAny.  Shouldn't have for an MVCC snapshot. (It's especially
	 * worth checking this for parallel builds, since ambuild routines that
	 * support parallel builds must work these details out for themselves.)
	 */
	Assert(snapshot == SnapshotAny || IsMVCCSnapshot(snapshot));
	Assert(snapshot == SnapshotAny ? TransactionIdIsValid(OldestXmin) :
		   !TransactionIdIsValid(OldestXmin));
	Assert(snapshot == SnapshotAny || !anyvisible);

	/* Publish number of blocks to scan */
	if (progress)
	{
		BlockNumber nblocks;

		if (hscan->rs_base.rs_parallel != NULL)
		{
			ParallelBlockTableScanDesc pbscan;

			pbscan = (ParallelBlockTableScanDesc) hscan->rs_base.rs_parallel;
			nblocks = pbscan->phs_nblocks;
		}
		else
			nblocks = hscan->rs_nblocks;

		pgstat_progress_update_param(PROGRESS_SCAN_BLOCKS_TOTAL,
									 nblocks);
	}

	/* set our scan endpoints */
	if (!allow_sync)
		heap_setscanlimits(scan, start_blockno, numblocks);
	else
	{
		/* syncscan can only be requested on whole relation */
		Assert(start_blockno == 0);
		Assert(numblocks == InvalidBlockNumber);
	}

	reltuples = 0;

	/*
	 * Scan all tuples in the base relation.
	 */
	while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
	{
		bool		tupleIsAlive;

		CHECK_FOR_INTERRUPTS();

		/* Report scan progress, if asked to. */
		if (progress)
		{
			BlockNumber blocks_done = heapam_scan_get_blocks_done(hscan);

			if (blocks_done != previous_blkno)
			{
				pgstat_progress_update_param(PROGRESS_SCAN_BLOCKS_DONE,
											 blocks_done);
				previous_blkno = blocks_done;
			}
		}

		/*
		 * When dealing with a HOT-chain of updated tuples, we want to index
		 * the values of the live tuple (if any), but index it under the TID
		 * of the chain's root tuple.  This approach is necessary to preserve
		 * the HOT-chain structure in the heap. So we need to be able to find
		 * the root item offset for every tuple that's in a HOT-chain.  When
		 * first reaching a new page of the relation, call
		 * heap_get_root_tuples() to build a map of root item offsets on the
		 * page.
		 *
		 * It might look unsafe to use this information across buffer
		 * lock/unlock.  However, we hold ShareLock on the table so no
		 * ordinary insert/update/delete should occur; and we hold pin on the
		 * buffer continuously while visiting the page, so no pruning
		 * operation can occur either.
		 *
		 * In cases with only ShareUpdateExclusiveLock on the table, it's
		 * possible for some HOT tuples to appear that we didn't know about
		 * when we first read the page.  To handle that case, we re-obtain the
		 * list of root offsets when a HOT tuple points to a root item that we
		 * don't know about.
		 *
		 * Also, although our opinions about tuple liveness could change while
		 * we scan the page (due to concurrent transaction commits/aborts),
		 * the chain root locations won't, so this info doesn't need to be
		 * rebuilt after waiting for another transaction.
		 *
		 * Note the implied assumption that there is no more than one live
		 * tuple per HOT-chain --- else we could create more than one index
		 * entry pointing to the same root tuple.
		 */
		if (hscan->rs_cblock != root_blkno)
		{
			Page		page = BufferGetPage(hscan->rs_cbuf);

			LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_SHARE);
			heap_get_root_tuples(page, root_offsets);
			LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);

			root_blkno = hscan->rs_cblock;
		}

		if (snapshot == SnapshotAny)
		{
			/* do our own time qual check */
			bool		indexIt;
			TransactionId xwait;

	recheck:

			/*
			 * We could possibly get away with not locking the buffer here,
			 * since caller should hold ShareLock on the relation, but let's
			 * be conservative about it.  (This remark is still correct even
			 * with HOT-pruning: our pin on the buffer prevents pruning.)
			 */
			LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_SHARE);

			/*
			 * The criteria for counting a tuple as live in this block need to
			 * match what analyze.c's heapam_scan_analyze_next_tuple() does,
			 * otherwise CREATE INDEX and ANALYZE may produce wildly different
			 * reltuples values, e.g. when there are many recently-dead
			 * tuples.
			 */
			switch (HeapTupleSatisfiesVacuum(heapTuple, OldestXmin,
											 hscan->rs_cbuf))
			{
				case HEAPTUPLE_DEAD:
					/* Definitely dead, we can ignore it */
					indexIt = false;
					tupleIsAlive = false;
					break;
				case HEAPTUPLE_LIVE:
					/* Normal case, index and unique-check it */
					indexIt = true;
					tupleIsAlive = true;
					/* Count it as live, too */
					reltuples += 1;
					break;
				case HEAPTUPLE_RECENTLY_DEAD:

					/*
					 * If tuple is recently deleted then we must index it
					 * anyway to preserve MVCC semantics.  (Pre-existing
					 * transactions could try to use the index after we finish
					 * building it, and may need to see such tuples.)
					 *
					 * However, if it was HOT-updated then we must only index
					 * the live tuple at the end of the HOT-chain.  Since this
					 * breaks semantics for pre-existing snapshots, mark the
					 * index as unusable for them.
					 *
					 * We don't count recently-dead tuples in reltuples, even
					 * if we index them; see heapam_scan_analyze_next_tuple().
					 */
					if (HeapTupleIsHotUpdated(heapTuple))
					{
						indexIt = false;
						/* mark the index as unsafe for old snapshots */
						indexInfo->ii_BrokenHotChain = true;
					}
					else
						indexIt = true;
					/* In any case, exclude the tuple from unique-checking */
					tupleIsAlive = false;
					break;
				case HEAPTUPLE_INSERT_IN_PROGRESS:

					/*
					 * In "anyvisible" mode, this tuple is visible and we
					 * don't need any further checks.
					 */
					if (anyvisible)
					{
						indexIt = true;
						tupleIsAlive = true;
						reltuples += 1;
						break;
					}

					/*
					 * Since caller should hold ShareLock or better, normally
					 * the only way to see this is if it was inserted earlier
					 * in our own transaction.  However, it can happen in
					 * system catalogs, since we tend to release write lock
					 * before commit there.  Give a warning if neither case
					 * applies.
					 */
					xwait = HeapTupleHeaderGetXmin(heapTuple->t_data);
					if (!TransactionIdIsCurrentTransactionId(xwait))
					{
						if (!is_system_catalog)
							elog(WARNING, "concurrent insert in progress within table \"%s\"",
								 RelationGetRelationName(heapRelation));

						/*
						 * If we are performing uniqueness checks, indexing
						 * such a tuple could lead to a bogus uniqueness
						 * failure.  In that case we wait for the inserting
						 * transaction to finish and check again.
						 */
						if (checking_uniqueness)
						{
							/*
							 * Must drop the lock on the buffer before we wait
							 */
							LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
							XactLockTableWait(xwait, heapRelation,
											  &heapTuple->t_self,
											  XLTW_InsertIndexUnique);
							CHECK_FOR_INTERRUPTS();
							goto recheck;
						}
					}
					else
					{
						/*
						 * For consistency with
						 * heapam_scan_analyze_next_tuple(), count
						 * HEAPTUPLE_INSERT_IN_PROGRESS tuples as live only
						 * when inserted by our own transaction.
						 */
						reltuples += 1;
					}

					/*
					 * We must index such tuples, since if the index build
					 * commits then they're good.
					 */
					indexIt = true;
					tupleIsAlive = true;
					break;
				case HEAPTUPLE_DELETE_IN_PROGRESS:

					/*
					 * As with INSERT_IN_PROGRESS case, this is unexpected
					 * unless it's our own deletion or a system catalog; but
					 * in anyvisible mode, this tuple is visible.
					 */
					if (anyvisible)
					{
						indexIt = true;
						tupleIsAlive = false;
						reltuples += 1;
						break;
					}

					xwait = HeapTupleHeaderGetUpdateXid(heapTuple->t_data);
					if (!TransactionIdIsCurrentTransactionId(xwait))
					{
						if (!is_system_catalog)
							elog(WARNING, "concurrent delete in progress within table \"%s\"",
								 RelationGetRelationName(heapRelation));

						/*
						 * If we are performing uniqueness checks, assuming
						 * the tuple is dead could lead to missing a
						 * uniqueness violation.  In that case we wait for the
						 * deleting transaction to finish and check again.
						 *
						 * Also, if it's a HOT-updated tuple, we should not
						 * index it but rather the live tuple at the end of
						 * the HOT-chain.  However, the deleting transaction
						 * could abort, possibly leaving this tuple as live
						 * after all, in which case it has to be indexed. The
						 * only way to know what to do is to wait for the
						 * deleting transaction to finish and check again.
						 */
						if (checking_uniqueness ||
							HeapTupleIsHotUpdated(heapTuple))
						{
							/*
							 * Must drop the lock on the buffer before we wait
							 */
							LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
							XactLockTableWait(xwait, heapRelation,
											  &heapTuple->t_self,
											  XLTW_InsertIndexUnique);
							CHECK_FOR_INTERRUPTS();
							goto recheck;
						}

						/*
						 * Otherwise index it but don't check for uniqueness,
						 * the same as a RECENTLY_DEAD tuple.
						 */
						indexIt = true;

						/*
						 * Count HEAPTUPLE_DELETE_IN_PROGRESS tuples as live,
						 * if they were not deleted by the current
						 * transaction.  That's what
						 * heapam_scan_analyze_next_tuple() does, and we want
						 * the behavior to be consistent.
						 */
						reltuples += 1;
					}
					else if (HeapTupleIsHotUpdated(heapTuple))
					{
						/*
						 * It's a HOT-updated tuple deleted by our own xact.
						 * We can assume the deletion will commit (else the
						 * index contents don't matter), so treat the same as
						 * RECENTLY_DEAD HOT-updated tuples.
						 */
						indexIt = false;
						/* mark the index as unsafe for old snapshots */
						indexInfo->ii_BrokenHotChain = true;
					}
					else
					{
						/*
						 * It's a regular tuple deleted by our own xact. Index
						 * it, but don't check for uniqueness nor count in
						 * reltuples, the same as a RECENTLY_DEAD tuple.
						 */
						indexIt = true;
					}
					/* In any case, exclude the tuple from unique-checking */
					tupleIsAlive = false;
					break;
				default:
					elog(ERROR, "unexpected HeapTupleSatisfiesVacuum result");
					indexIt = tupleIsAlive = false; /* keep compiler quiet */
					break;
			}

			LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);

			if (!indexIt)
				continue;
		}
		else
		{
			/* heap_getnext did the time qual check */
			tupleIsAlive = true;
			reltuples += 1;
		}

		MemoryContextReset(econtext->ecxt_per_tuple_memory);

		/* Set up for predicate or expression evaluation */
		ExecStoreBufferHeapTuple(heapTuple, slot, hscan->rs_cbuf);

		/*
		 * In a partial index, discard tuples that don't satisfy the
		 * predicate.
		 */
		if (predicateExpand != NULL)
		{
			if (!ExecQual(predicateExpand, econtext))
				continue;
		}

		/*
		 * For the current heap tuple, extract all the attributes we use in
		 * this index, and note which are null.  This also performs evaluation
		 * of any expressions needed.
		 */
		FormIndexDatum(indexInfo,
					   slot,
					   estate,
					   values,
					   isnull);

		/*
		 * You'd think we should go ahead and build the index tuple here, but
		 * some index AMs want to do further processing on the data first.  So
		 * pass the values[] and isnull[] arrays, instead.
		 */

		if (HeapTupleIsHeapOnly(heapTuple))
		{
			/*
			 * For a heap-only tuple, pretend its TID is that of the root. See
			 * src/backend/access/heap/README.HOT for discussion.
			 */
			ItemPointerData tid;
			OffsetNumber offnum;

			offnum = ItemPointerGetOffsetNumber(&heapTuple->t_self);

			/*
			 * If a HOT tuple points to a root that we don't know about,
			 * obtain root items afresh.  If that still fails, report it as
			 * corruption.
			 */
			if (root_offsets[offnum - 1] == InvalidOffsetNumber)
			{
				Page		page = BufferGetPage(hscan->rs_cbuf);

				LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_SHARE);
				heap_get_root_tuples(page, root_offsets);
				LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
			}

			if (!OffsetNumberIsValid(root_offsets[offnum - 1]))
				ereport(ERROR,
						(errcode(ERRCODE_DATA_CORRUPTED),
						 errmsg_internal("failed to find parent tuple for heap-only tuple at (%u,%u) in table \"%s\"",
										 ItemPointerGetBlockNumber(&heapTuple->t_self),
										 offnum,
										 RelationGetRelationName(heapRelation))));

			ItemPointerSet(&tid, ItemPointerGetBlockNumber(&heapTuple->t_self),
						   root_offsets[offnum - 1]);

			/* Call the AM's callback routine to process the tuple */
			callback(indexRelation, &tid, values, isnull, tupleIsAlive,
					 callback_state);
		}
		else
		{
			/* Call the AM's callback routine to process the tuple */
			callback(indexRelation, &heapTuple->t_self, values, isnull,
					 tupleIsAlive, callback_state);
		}
	}

	/* Report scan progress one last time. */
	if (progress)
	{
		BlockNumber blks_done;

		if (hscan->rs_base.rs_parallel != NULL)
		{
			ParallelBlockTableScanDesc pbscan;

			pbscan = (ParallelBlockTableScanDesc) hscan->rs_base.rs_parallel;
			blks_done = pbscan->phs_nblocks;
		}
		else
			blks_done = hscan->rs_nblocks;

		pgstat_progress_update_param(PROGRESS_SCAN_BLOCKS_DONE,
									 blks_done);
	}

	table_endscan(scan);

	/* we can now forget our snapshot, if set and registered by us */
	if (need_unregister_snapshot)
		UnregisterSnapshot(snapshot);

	ExecDropSingleTupleTableSlot(slot);

	FreeExecutorState(estate);

	/* These may have been pointing to the now-gone estate */
	indexInfo->ii_ExpressionsState = NIL;
	indexInfo->ii_ExpressionsExpandState = NIL;
	indexInfo->ii_PredicateState = NULL;
	indexInfo->ii_PredicateExpandState = NULL;

	return reltuples;
}

static void
heapam_index_validate_scan(Relation heapRelation,
						   Relation indexRelation,
						   IndexInfo *indexInfo,
						   Snapshot snapshot,
						   ValidateIndexState *state)
{
	TableScanDesc scan;
	HeapScanDesc hscan;
	HeapTuple	heapTuple;
	Datum		values[INDEX_MAX_KEYS];
	bool		isnull[INDEX_MAX_KEYS];
	ExprState  *predicateExpand;
	TupleTableSlot *slot;
	EState	   *estate;
	ExprContext *econtext;
	BlockNumber root_blkno = InvalidBlockNumber;
	OffsetNumber root_offsets[MaxHeapTuplesPerPage];
	bool		in_index[MaxHeapTuplesPerPage];
	BlockNumber previous_blkno = InvalidBlockNumber;

	/* state variables for the merge */
	ItemPointer indexcursor = NULL;
	ItemPointerData decoded;
	bool		tuplesort_empty = false;

	/*
	 * sanity checks
	 */
	Assert(OidIsValid(indexRelation->rd_rel->relam));

	/*
	 * Need an EState for evaluation of index expressions and partial-index
	 * predicates.  Also a slot to hold the current tuple.
	 */
	estate = CreateExecutorState();
	econtext = GetPerTupleExprContext(estate);
	slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation),
									&TTSOpsHeapTuple);

	/* Arrange for econtext's scan tuple to be the tuple under test */
	econtext->ecxt_scantuple = slot;

	/* Set up execution state for predicate, if any. */
	predicateExpand = ExecPrepareQual(indexInfo->ii_PredicateExpand, estate);

	/*
	 * Prepare for scan of the base relation.  We need just those tuples
	 * satisfying the passed-in reference snapshot.  We must disable syncscan
	 * here, because it's critical that we read from block zero forward to
	 * match the sorted TIDs.
	 */
	scan = table_beginscan_strat(heapRelation,	/* relation */
								 snapshot,	/* snapshot */
								 0, /* number of keys */
								 NULL,	/* scan key */
								 true,	/* buffer access strategy OK */
								 false);	/* syncscan not OK */
	hscan = (HeapScanDesc) scan;

	pgstat_progress_update_param(PROGRESS_SCAN_BLOCKS_TOTAL,
								 hscan->rs_nblocks);

	/*
	 * Scan all tuples matching the snapshot.
	 */
	while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
	{
		ItemPointer heapcursor = &heapTuple->t_self;
		ItemPointerData rootTuple;
		OffsetNumber root_offnum;

		CHECK_FOR_INTERRUPTS();

		state->htups += 1;

		if ((previous_blkno == InvalidBlockNumber) ||
			(hscan->rs_cblock != previous_blkno))
		{
			pgstat_progress_update_param(PROGRESS_SCAN_BLOCKS_DONE,
										 hscan->rs_cblock);
			previous_blkno = hscan->rs_cblock;
		}

		/*
		 * As commented in table_index_build_scan, we should index heap-only
		 * tuples under the TIDs of their root tuples; so when we advance onto
		 * a new heap page, build a map of root item offsets on the page.
		 *
		 * This complicates merging against the tuplesort output: we will
		 * visit the live tuples in order by their offsets, but the root
		 * offsets that we need to compare against the index contents might be
		 * ordered differently.  So we might have to "look back" within the
		 * tuplesort output, but only within the current page.  We handle that
		 * by keeping a bool array in_index[] showing all the
		 * already-passed-over tuplesort output TIDs of the current page. We
		 * clear that array here, when advancing onto a new heap page.
		 */
		if (hscan->rs_cblock != root_blkno)
		{
			Page		page = BufferGetPage(hscan->rs_cbuf);

			LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_SHARE);
			heap_get_root_tuples(page, root_offsets);
			LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);

			memset(in_index, 0, sizeof(in_index));

			root_blkno = hscan->rs_cblock;
		}

		/* Convert actual tuple TID to root TID */
		rootTuple = *heapcursor;
		root_offnum = ItemPointerGetOffsetNumber(heapcursor);

		if (HeapTupleIsHeapOnly(heapTuple))
		{
			root_offnum = root_offsets[root_offnum - 1];
			if (!OffsetNumberIsValid(root_offnum))
				ereport(ERROR,
						(errcode(ERRCODE_DATA_CORRUPTED),
						 errmsg_internal("failed to find parent tuple for heap-only tuple at (%u,%u) in table \"%s\"",
										 ItemPointerGetBlockNumber(heapcursor),
										 ItemPointerGetOffsetNumber(heapcursor),
										 RelationGetRelationName(heapRelation))));
			ItemPointerSetOffsetNumber(&rootTuple, root_offnum);
		}

		/*
		 * "merge" by skipping through the index tuples until we find or pass
		 * the current root tuple.
		 */
		while (!tuplesort_empty &&
			   (!indexcursor ||
				ItemPointerCompare(indexcursor, &rootTuple) < 0))
		{
			Datum		ts_val;
			bool		ts_isnull;

			if (indexcursor)
			{
				/*
				 * Remember index items seen earlier on the current heap page
				 */
				if (ItemPointerGetBlockNumber(indexcursor) == root_blkno)
					in_index[ItemPointerGetOffsetNumber(indexcursor) - 1] = true;
			}

			tuplesort_empty = !tuplesort_getdatum(state->tuplesort, true,
												  false, &ts_val, &ts_isnull,
												  NULL);
			Assert(tuplesort_empty || !ts_isnull);
			if (!tuplesort_empty)
			{
				itemptr_decode(&decoded, DatumGetInt64(ts_val));
				indexcursor = &decoded;
			}
			else
			{
				/* Be tidy */
				indexcursor = NULL;
			}
		}

		/*
		 * If the tuplesort has overshot *and* we didn't see a match earlier,
		 * then this tuple is missing from the index, so insert it.
		 */
		if ((tuplesort_empty ||
			 ItemPointerCompare(indexcursor, &rootTuple) > 0) &&
			!in_index[root_offnum - 1])
		{
			MemoryContextReset(econtext->ecxt_per_tuple_memory);

			/* Set up for predicate or expression evaluation */
			ExecStoreHeapTuple(heapTuple, slot, false);

			/*
			 * In a partial index, discard tuples that don't satisfy the
			 * predicate.
			 */
			if (predicateExpand != NULL)
			{
				if (!ExecQual(predicateExpand, econtext))
					continue;
			}

			/*
			 * For the current heap tuple, extract all the attributes we use
			 * in this index, and note which are null.  This also performs
			 * evaluation of any expressions needed.
			 */
			FormIndexDatum(indexInfo,
						   slot,
						   estate,
						   values,
						   isnull);

			/*
			 * You'd think we should go ahead and build the index tuple here,
			 * but some index AMs want to do further processing on the data
			 * first. So pass the values[] and isnull[] arrays, instead.
			 */

			/*
			 * If the tuple is already committed dead, you might think we
			 * could suppress uniqueness checking, but this is no longer true
			 * in the presence of HOT, because the insert is actually a proxy
			 * for a uniqueness check on the whole HOT-chain.  That is, the
			 * tuple we have here could be dead because it was already
			 * HOT-updated, and if so the updating transaction will not have
			 * thought it should insert index entries.  The index AM will
			 * check the whole HOT-chain and correctly detect a conflict if
			 * there is one.
			 */

			index_insert(indexRelation,
						 values,
						 isnull,
						 &rootTuple,
						 heapRelation,
						 indexInfo->ii_Unique ?
						 UNIQUE_CHECK_YES : UNIQUE_CHECK_NO,
						 false,
						 indexInfo);

			state->tups_inserted += 1;
		}
	}

	table_endscan(scan);

	ExecDropSingleTupleTableSlot(slot);

	FreeExecutorState(estate);

	/* These may have been pointing to the now-gone estate */
	indexInfo->ii_ExpressionsState = NIL;
	indexInfo->ii_ExpressionsExpandState = NIL;
	indexInfo->ii_PredicateState = NULL;
	indexInfo->ii_PredicateExpandState = NULL;
}

/*
 * Return the number of blocks that have been read by this scan since
 * starting.  This is meant for progress reporting rather than be fully
 * accurate: in a parallel scan, workers can be concurrently reading blocks
 * further ahead than what we report.
 */
static BlockNumber
heapam_scan_get_blocks_done(HeapScanDesc hscan)
{
	ParallelBlockTableScanDesc bpscan = NULL;
	BlockNumber startblock;
	BlockNumber blocks_done;

	if (hscan->rs_base.rs_parallel != NULL)
	{
		bpscan = (ParallelBlockTableScanDesc) hscan->rs_base.rs_parallel;
		startblock = bpscan->phs_startblock;
	}
	else
		startblock = hscan->rs_startblock;

	/*
	 * Might have wrapped around the end of the relation, if startblock was
	 * not zero.
	 */
	if (hscan->rs_cblock > startblock)
		blocks_done = hscan->rs_cblock - startblock;
	else
	{
		BlockNumber nblocks;

		nblocks = bpscan != NULL ? bpscan->phs_nblocks : hscan->rs_nblocks;
		blocks_done = nblocks - startblock +
			hscan->rs_cblock;
	}

	return blocks_done;
}


/* ------------------------------------------------------------------------
 * Miscellaneous callbacks for the heap AM
 * ------------------------------------------------------------------------
 */

/*
 * Check to see whether the table needs a TOAST table.  It does only if
 * (1) there are any toastable attributes, and (2) the maximum length
 * of a tuple could exceed TOAST_TUPLE_THRESHOLD.  (We don't want to
 * create a toast table for something like "f1 varchar(20)".)
 */
static bool
heapam_relation_needs_toast_table(Relation rel)
{
	int32		data_length = 0;
	bool		maxlength_unknown = false;
	bool		has_toastable_attrs = false;
	TupleDesc	tupdesc = rel->rd_att;
	int32		tuple_length;
	int			i;

	for (i = 0; i < tupdesc->natts; i++)
	{
		Form_pg_attribute att = TupleDescAttr(tupdesc, i);

		if (att->attisdropped)
			continue;
		if (att->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
			continue;
		data_length = att_align_nominal(data_length, att->attalign);
		if (att->attlen > 0)
		{
			/* Fixed-length types are never toastable */
			data_length += att->attlen;
		}
		else
		{
			int32		maxlen = type_maximum_size(att->atttypid,
												   att->atttypmod);

			if (maxlen < 0)
				maxlength_unknown = true;
			else
				data_length += maxlen;
			if (att->attstorage != TYPSTORAGE_PLAIN)
				has_toastable_attrs = true;
		}
	}
	if (!has_toastable_attrs)
		return false;			/* nothing to toast? */
	if (maxlength_unknown)
		return true;			/* any unlimited-length attrs? */
	tuple_length = MAXALIGN(SizeofHeapTupleHeader +
							BITMAPLEN(tupdesc->natts)) +
		MAXALIGN(data_length);
	return (tuple_length > TOAST_TUPLE_THRESHOLD);
}

/*
 * TOAST tables for heap relations are just heap relations.
 */
static Oid
heapam_relation_toast_am(Relation rel)
{
	return rel->rd_rel->relam;
}


/* ------------------------------------------------------------------------
 * Planner related callbacks for the heap AM
 * ------------------------------------------------------------------------
 */

#define HEAP_OVERHEAD_BYTES_PER_TUPLE \
	(MAXALIGN(SizeofHeapTupleHeader) + sizeof(ItemIdData))
#define HEAP_USABLE_BYTES_PER_PAGE \
	(BLCKSZ - SizeOfPageHeaderData)

static void
heapam_estimate_rel_size(Relation rel, int32 *attr_widths,
						 BlockNumber *pages, double *tuples,
						 double *allvisfrac)
{
	table_block_relation_estimate_size(rel, attr_widths, pages,
									   tuples, allvisfrac,
									   HEAP_OVERHEAD_BYTES_PER_TUPLE,
									   HEAP_USABLE_BYTES_PER_PAGE);
}


/* ------------------------------------------------------------------------
 * Executor related callbacks for the heap AM
 * ------------------------------------------------------------------------
 */

static bool
heapam_scan_bitmap_next_tuple(TableScanDesc scan,
							  TupleTableSlot *slot,
							  bool *recheck,
							  uint64 *lossy_pages,
							  uint64 *exact_pages)
{
	BitmapHeapScanDesc bscan = (BitmapHeapScanDesc) scan;
	HeapScanDesc hscan = (HeapScanDesc) bscan;
	OffsetNumber targoffset;
	Page		page;
	ItemId		lp;

	/*
	 * Out of range?  If so, nothing more to look at on this page
	 */
	while (hscan->rs_cindex >= hscan->rs_ntuples)
	{
		/*
		 * Returns false if the bitmap is exhausted and there are no further
		 * blocks we need to scan.
		 */
		if (!BitmapHeapScanNextBlock(scan, recheck, lossy_pages, exact_pages))
			return false;
	}

	targoffset = hscan->rs_vistuples[hscan->rs_cindex];
	page = BufferGetPage(hscan->rs_cbuf);
	lp = PageGetItemId(page, targoffset);
	Assert(ItemIdIsNormal(lp));

	hscan->rs_ctup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
	hscan->rs_ctup.t_len = ItemIdGetLength(lp);
	hscan->rs_ctup.t_tableOid = scan->rs_rd->rd_id;
	ItemPointerSet(&hscan->rs_ctup.t_self, hscan->rs_cblock, targoffset);

	pgstat_count_heap_fetch(scan->rs_rd);

	/*
	 * Set up the result slot to point to this tuple.  Note that the slot
	 * acquires a pin on the buffer.
	 */
	ExecStoreBufferHeapTuple(&hscan->rs_ctup,
							 slot,
							 hscan->rs_cbuf);

	hscan->rs_cindex++;

	return true;
}

static bool
heapam_scan_sample_next_block(TableScanDesc scan, SampleScanState *scanstate)
{
	HeapScanDesc hscan = (HeapScanDesc) scan;
	TsmRoutine *tsm = scanstate->tsmroutine;
	BlockNumber blockno;

	/* return false immediately if relation is empty */
	if (hscan->rs_nblocks == 0)
		return false;

	/* release previous scan buffer, if any */
	if (BufferIsValid(hscan->rs_cbuf))
	{
		ReleaseBuffer(hscan->rs_cbuf);
		hscan->rs_cbuf = InvalidBuffer;
	}

	if (tsm->NextSampleBlock)
		blockno = tsm->NextSampleBlock(scanstate, hscan->rs_nblocks);
	else
	{
		/* scanning table sequentially */

		if (hscan->rs_cblock == InvalidBlockNumber)
		{
			Assert(!hscan->rs_inited);
			blockno = hscan->rs_startblock;
		}
		else
		{
			Assert(hscan->rs_inited);

			blockno = hscan->rs_cblock + 1;

			if (blockno >= hscan->rs_nblocks)
			{
				/* wrap to beginning of rel, might not have started at 0 */
				blockno = 0;
			}

			/*
			 * Report our new scan position for synchronization purposes.
			 *
			 * Note: we do this before checking for end of scan so that the
			 * final state of the position hint is back at the start of the
			 * rel.  That's not strictly necessary, but otherwise when you run
			 * the same query multiple times the starting position would shift
			 * a little bit backwards on every invocation, which is confusing.
			 * We don't guarantee any specific ordering in general, though.
			 */
			if (scan->rs_flags & SO_ALLOW_SYNC)
				ss_report_location(scan->rs_rd, blockno);

			if (blockno == hscan->rs_startblock)
			{
				blockno = InvalidBlockNumber;
			}
		}
	}

	hscan->rs_cblock = blockno;

	if (!BlockNumberIsValid(blockno))
	{
		hscan->rs_inited = false;
		return false;
	}

	Assert(hscan->rs_cblock < hscan->rs_nblocks);

	/*
	 * Be sure to check for interrupts at least once per page.  Checks at
	 * higher code levels won't be able to stop a sample scan that encounters
	 * many pages' worth of consecutive dead tuples.
	 */
	CHECK_FOR_INTERRUPTS();

	/* Read page using selected strategy */
	hscan->rs_cbuf = ReadBufferExtended(hscan->rs_base.rs_rd, MAIN_FORKNUM,
										blockno, RBM_NORMAL, hscan->rs_strategy);

	/* in pagemode, prune the page and determine visible tuple offsets */
	if (hscan->rs_base.rs_flags & SO_ALLOW_PAGEMODE)
		heap_prepare_pagescan(scan);

	hscan->rs_inited = true;
	return true;
}

static bool
heapam_scan_sample_next_tuple(TableScanDesc scan, SampleScanState *scanstate,
							  TupleTableSlot *slot)
{
	HeapScanDesc hscan = (HeapScanDesc) scan;
	TsmRoutine *tsm = scanstate->tsmroutine;
	BlockNumber blockno = hscan->rs_cblock;
	bool		pagemode = (scan->rs_flags & SO_ALLOW_PAGEMODE) != 0;

	Page		page;
	bool		all_visible;
	OffsetNumber maxoffset;

	/*
	 * When not using pagemode, we must lock the buffer during tuple
	 * visibility checks.
	 */
	if (!pagemode)
		LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_SHARE);

	page = BufferGetPage(hscan->rs_cbuf);
	all_visible = PageIsAllVisible(page) &&
		!scan->rs_snapshot->takenDuringRecovery;
	maxoffset = PageGetMaxOffsetNumber(page);

	for (;;)
	{
		OffsetNumber tupoffset;

		CHECK_FOR_INTERRUPTS();

		/* Ask the tablesample method which tuples to check on this page. */
		tupoffset = tsm->NextSampleTuple(scanstate,
										 blockno,
										 maxoffset);

		if (OffsetNumberIsValid(tupoffset))
		{
			ItemId		itemid;
			bool		visible;
			HeapTuple	tuple = &(hscan->rs_ctup);

			/* Skip invalid tuple pointers. */
			itemid = PageGetItemId(page, tupoffset);
			if (!ItemIdIsNormal(itemid))
				continue;

			tuple->t_data = (HeapTupleHeader) PageGetItem(page, itemid);
			tuple->t_len = ItemIdGetLength(itemid);
			ItemPointerSet(&(tuple->t_self), blockno, tupoffset);


			if (all_visible)
				visible = true;
			else
				visible = SampleHeapTupleVisible(scan, hscan->rs_cbuf,
												 tuple, tupoffset);

			/* in pagemode, heap_prepare_pagescan did this for us */
			if (!pagemode)
				HeapCheckForSerializableConflictOut(visible, scan->rs_rd, tuple,
													hscan->rs_cbuf, scan->rs_snapshot);

			/* Try next tuple from same page. */
			if (!visible)
				continue;

			/* Found visible tuple, return it. */
			if (!pagemode)
				LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);

			ExecStoreBufferHeapTuple(tuple, slot, hscan->rs_cbuf);

			/* Count successfully-fetched tuples as heap fetches */
			pgstat_count_heap_getnext(scan->rs_rd);

			return true;
		}
		else
		{
			/*
			 * If we get here, it means we've exhausted the items on this page
			 * and it's time to move to the next.
			 */
			if (!pagemode)
				LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);

			ExecClearTuple(slot);
			return false;
		}
	}

	Assert(0);
}


/* ----------------------------------------------------------------------------
 *  Helper functions for the above.
 * ----------------------------------------------------------------------------
 */

/*
 * Reconstruct and rewrite the given tuple
 *
 * We cannot simply copy the tuple as-is, for several reasons:
 *
 * 1. We'd like to squeeze out the values of any dropped columns, both
 * to save space and to ensure we have no corner-case failures. (It's
 * possible for example that the new table hasn't got a TOAST table
 * and so is unable to store any large values of dropped cols.)
 *
 * 2. The tuple might not even be legal for the new table; this is
 * currently only known to happen as an after-effect of ALTER TABLE
 * SET WITHOUT OIDS.
 *
 * So, we must reconstruct the tuple from component Datums.
 */
static void
reform_and_rewrite_tuple(HeapTuple tuple,
						 Relation OldHeap, Relation NewHeap,
						 Datum *values, bool *isnull, RewriteState rwstate)
{
	HeapTuple	newtuple;

	newtuple = reform_tuple(tuple, OldHeap, NewHeap, values, isnull);

	/* The heap rewrite module does the rest */
	rewrite_heap_tuple(rwstate, tuple, newtuple);

	heap_freetuple(newtuple);
}

/*
 * Insert tuple when processing REPACK CONCURRENTLY.
 *
 * rewriteheap.c is not used in the CONCURRENTLY case because it'd be
 * difficult to do the same in the catch-up phase (as the logical
 * decoding does not provide us with sufficient visibility
 * information). Thus we must use heap_insert() both during the
 * catch-up and here.
 *
 * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical
 * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops
 * this relation, so no logical replication subscription should need the data.
 *
 * BulkInsertState is used because many tuples are inserted in the typical
 * case.
 */
static void
heap_insert_for_repack(HeapTuple tuple, Relation OldHeap, Relation NewHeap,
					   Datum *values, bool *isnull, BulkInsertState bistate)
{
	HeapTuple	newtuple;

	newtuple = reform_tuple(tuple, OldHeap, NewHeap, values, isnull);

	heap_insert(NewHeap, newtuple, GetCurrentCommandId(true),
				HEAP_INSERT_NO_LOGICAL, bistate);

	heap_freetuple(newtuple);
}

/*
 * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack.
 *
 * Deform the given tuple, set values of dropped columns to NULL, and fill in
 * any values from attmissingval; then form a new tuple and return it.  If no
 * attributes need to be changed, a copy of the original tuple is returned.
 * Caller is responsible for freeing the returned tuple.
 *
 * XXX this coding assumes that both relations have the same tupledesc.
 */
static HeapTuple
reform_tuple(HeapTuple tuple, Relation OldHeap, Relation NewHeap,
			 Datum *values, bool *isnull)
{
	TupleDesc	oldTupDesc = RelationGetDescr(OldHeap);
	TupleDesc	newTupDesc = RelationGetDescr(NewHeap);
	bool		needs_reform = false;

	/*
	 * A short tuple might require values from attmissing val, so activate the
	 * coding unconditionally in that case.  The value might legitimally be
	 * NULL otherwise, so this is slightly wasteful, but it probably beats
	 * having to test each attribute for presence of attmissingval each time.
	 */
	if (HeapTupleHeaderGetNatts(tuple->t_data) < newTupDesc->natts)
		needs_reform = true;

	/*
	 * If the column has been dropped but a value is still present, we can
	 * optimize storage now by getting rid of it.
	 */
	if (!needs_reform)
	{
		for (int i = 0; i < newTupDesc->natts; i++)
		{
			if (TupleDescCompactAttr(newTupDesc, i)->attisdropped &&
				!heap_attisnull(tuple, i + 1, newTupDesc))
			{
				needs_reform = true;
				break;
			}
		}
	}

	/* Skip work if no changes are needed */
	if (!needs_reform)
		return heap_copytuple(tuple);

	heap_deform_tuple(tuple, oldTupDesc, values, isnull);

	for (int i = 0; i < newTupDesc->natts; i++)
	{
		if (TupleDescCompactAttr(newTupDesc, i)->attisdropped)
			isnull[i] = true;
	}

	return heap_form_tuple(newTupDesc, values, isnull);
}

/*
 * Check visibility of the tuple.
 */
static bool
SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer,
					   HeapTuple tuple,
					   OffsetNumber tupoffset)
{
	HeapScanDesc hscan = (HeapScanDesc) scan;

	if (scan->rs_flags & SO_ALLOW_PAGEMODE)
	{
		uint32		start = 0,
					end = hscan->rs_ntuples;

		/*
		 * In pageatatime mode, heap_prepare_pagescan() already did visibility
		 * checks, so just look at the info it left in rs_vistuples[].
		 *
		 * We use a binary search over the known-sorted array.  Note: we could
		 * save some effort if we insisted that NextSampleTuple select tuples
		 * in increasing order, but it's not clear that there would be enough
		 * gain to justify the restriction.
		 */
		while (start < end)
		{
			uint32		mid = start + (end - start) / 2;
			OffsetNumber curoffset = hscan->rs_vistuples[mid];

			if (tupoffset == curoffset)
				return true;
			else if (tupoffset < curoffset)
				end = mid;
			else
				start = mid + 1;
		}

		return false;
	}
	else
	{
		/* Otherwise, we have to check the tuple individually. */
		return HeapTupleSatisfiesVisibility(tuple, scan->rs_snapshot,
											buffer);
	}
}

/*
 * Helper function get the next block of a bitmap heap scan. Returns true when
 * it got the next block and saved it in the scan descriptor and false when
 * the bitmap and or relation are exhausted.
 */
static bool
BitmapHeapScanNextBlock(TableScanDesc scan,
						bool *recheck,
						uint64 *lossy_pages, uint64 *exact_pages)
{
	BitmapHeapScanDesc bscan = (BitmapHeapScanDesc) scan;
	HeapScanDesc hscan = (HeapScanDesc) bscan;
	BlockNumber block;
	void	   *per_buffer_data;
	Buffer		buffer;
	Snapshot	snapshot;
	int			ntup;
	TBMIterateResult *tbmres;
	OffsetNumber offsets[TBM_MAX_TUPLES_PER_PAGE];
	int			noffsets = -1;

	Assert(scan->rs_flags & SO_TYPE_BITMAPSCAN);
	Assert(hscan->rs_read_stream);

	hscan->rs_cindex = 0;
	hscan->rs_ntuples = 0;

	/* Release buffer containing previous block. */
	if (BufferIsValid(hscan->rs_cbuf))
	{
		ReleaseBuffer(hscan->rs_cbuf);
		hscan->rs_cbuf = InvalidBuffer;
	}

	hscan->rs_cbuf = read_stream_next_buffer(hscan->rs_read_stream,
											 &per_buffer_data);

	if (BufferIsInvalid(hscan->rs_cbuf))
	{
		/* the bitmap is exhausted */
		return false;
	}

	Assert(per_buffer_data);

	tbmres = per_buffer_data;

	Assert(BlockNumberIsValid(tbmres->blockno));
	Assert(BufferGetBlockNumber(hscan->rs_cbuf) == tbmres->blockno);

	/* Exact pages need their tuple offsets extracted. */
	if (!tbmres->lossy)
		noffsets = tbm_extract_page_tuple(tbmres, offsets,
										  TBM_MAX_TUPLES_PER_PAGE);

	*recheck = tbmres->recheck;

	block = hscan->rs_cblock = tbmres->blockno;
	buffer = hscan->rs_cbuf;
	snapshot = scan->rs_snapshot;

	ntup = 0;

	/*
	 * Prune and repair fragmentation for the whole page, if possible.
	 */
	heap_page_prune_opt(scan->rs_rd, buffer, &hscan->rs_vmbuffer,
						scan->rs_flags & SO_HINT_REL_READ_ONLY);

	/*
	 * We must hold share lock on the buffer content while examining tuple
	 * visibility.  Afterwards, however, the tuples we have found to be
	 * visible are guaranteed good as long as we hold the buffer pin.
	 */
	LockBuffer(buffer, BUFFER_LOCK_SHARE);

	/*
	 * We need two separate strategies for lossy and non-lossy cases.
	 */
	if (!tbmres->lossy)
	{
		/*
		 * Bitmap is non-lossy, so we just look through the offsets listed in
		 * tbmres; but we have to follow any HOT chain starting at each such
		 * offset.
		 */
		int			curslot;

		/* We must have extracted the tuple offsets by now */
		Assert(noffsets > -1);

		for (curslot = 0; curslot < noffsets; curslot++)
		{
			OffsetNumber offnum = offsets[curslot];
			ItemPointerData tid;
			HeapTupleData heapTuple;

			ItemPointerSet(&tid, block, offnum);
			if (heap_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
									   &heapTuple, NULL, true))
				hscan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
		}
	}
	else
	{
		/*
		 * Bitmap is lossy, so we must examine each line pointer on the page.
		 * But we can ignore HOT chains, since we'll check each tuple anyway.
		 */
		Page		page = BufferGetPage(buffer);
		OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
		OffsetNumber offnum;

		for (offnum = FirstOffsetNumber; offnum <= maxoff; offnum = OffsetNumberNext(offnum))
		{
			ItemId		lp;
			HeapTupleData loctup;
			bool		valid;

			lp = PageGetItemId(page, offnum);
			if (!ItemIdIsNormal(lp))
				continue;
			loctup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
			loctup.t_len = ItemIdGetLength(lp);
			loctup.t_tableOid = scan->rs_rd->rd_id;
			ItemPointerSet(&loctup.t_self, block, offnum);
			valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer);
			if (valid)
			{
				hscan->rs_vistuples[ntup++] = offnum;
				PredicateLockTID(scan->rs_rd, &loctup.t_self, snapshot,
								 HeapTupleHeaderGetXmin(loctup.t_data));
			}
			HeapCheckForSerializableConflictOut(valid, scan->rs_rd, &loctup,
												buffer, snapshot);
		}
	}

	LockBuffer(buffer, BUFFER_LOCK_UNLOCK);

	Assert(ntup <= MaxHeapTuplesPerPage);
	hscan->rs_ntuples = ntup;

	if (tbmres->lossy)
		(*lossy_pages)++;
	else
		(*exact_pages)++;

	/*
	 * Return true to indicate that a valid block was found and the bitmap is
	 * not exhausted. If there are no visible tuples on this page,
	 * hscan->rs_ntuples will be 0 and heapam_scan_bitmap_next_tuple() will
	 * return false returning control to this function to advance to the next
	 * block in the bitmap.
	 */
	return true;
}

/* ------------------------------------------------------------------------
 * Definition of the heap table access method.
 * ------------------------------------------------------------------------
 */

static const TableAmRoutine heapam_methods = {
	.type = T_TableAmRoutine,

	.slot_callbacks = heapam_slot_callbacks,

	.scan_begin = heap_beginscan,
	.scan_end = heap_endscan,
	.scan_rescan = heap_rescan,
	.scan_getnextslot = heap_getnextslot,

	.scan_set_tidrange = heap_set_tidrange,
	.scan_getnextslot_tidrange = heap_getnextslot_tidrange,

	.parallelscan_estimate = table_block_parallelscan_estimate,
	.parallelscan_initialize = table_block_parallelscan_initialize,
	.parallelscan_reinitialize = table_block_parallelscan_reinitialize,

	.index_fetch_begin = heapam_index_fetch_begin,
	.index_fetch_reset = heapam_index_fetch_reset,
	.index_fetch_end = heapam_index_fetch_end,
	.index_fetch_tuple = heapam_index_fetch_tuple,

	.tuple_insert = heapam_tuple_insert,
	.tuple_insert_speculative = heapam_tuple_insert_speculative,
	.tuple_complete_speculative = heapam_tuple_complete_speculative,
	.multi_insert = heap_multi_insert,
	.tuple_delete = heapam_tuple_delete,
	.tuple_update = heapam_tuple_update,
	.tuple_lock = heapam_tuple_lock,

	.tuple_fetch_row_version = heapam_fetch_row_version,
	.tuple_get_latest_tid = heap_get_latest_tid,
	.tuple_tid_valid = heapam_tuple_tid_valid,
	.tuple_satisfies_snapshot = heapam_tuple_satisfies_snapshot,
	.index_delete_tuples = heap_index_delete_tuples,

	.relation_set_new_filelocator = heapam_relation_set_new_filelocator,
	.relation_nontransactional_truncate = heapam_relation_nontransactional_truncate,
	.relation_copy_data = heapam_relation_copy_data,
	.relation_copy_for_cluster = heapam_relation_copy_for_cluster,
	.relation_vacuum = heap_vacuum_rel,
	.scan_analyze_next_block = heapam_scan_analyze_next_block,
	.scan_analyze_next_tuple = heapam_scan_analyze_next_tuple,
	.index_build_range_scan = heapam_index_build_range_scan,
	.index_validate_scan = heapam_index_validate_scan,

	.relation_size = table_block_relation_size,
	.relation_needs_toast_table = heapam_relation_needs_toast_table,
	.relation_toast_am = heapam_relation_toast_am,
	.relation_fetch_toast_slice = heap_fetch_toast_slice,

	.relation_estimate_size = heapam_estimate_rel_size,

	.scan_bitmap_next_tuple = heapam_scan_bitmap_next_tuple,
	.scan_sample_next_block = heapam_scan_sample_next_block,
	.scan_sample_next_tuple = heapam_scan_sample_next_tuple
};


const TableAmRoutine *
GetHeapamTableAmRoutine(void)
{
	return &heapam_methods;
}

Datum
heap_tableam_handler(PG_FUNCTION_ARGS)
{
	PG_RETURN_POINTER(&heapam_methods);
}
./index.c0000664000175000017500000042761115222105363011172 0ustar  xmanxman/*-------------------------------------------------------------------------
 *
 * index.c
 *	  code to create and destroy POSTGRES index relations
 *
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/catalog/index.c
 *
 *
 * INTERFACE ROUTINES
 *		index_create()			- Create a cataloged index relation
 *		index_drop()			- Removes index relation from catalogs
 *		BuildIndexInfo()		- Prepare to insert index tuples
 *		FormIndexDatum()		- Construct datum vector for one index tuple
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include <unistd.h>

#include "access/amapi.h"
#include "access/attmap.h"
#include "access/heapam.h"
#include "access/multixact.h"
#include "access/relscan.h"
#include "access/tableam.h"
#include "access/toast_compression.h"
#include "access/transam.h"
#include "access/visibilitymap.h"
#include "access/xact.h"
#include "bootstrap/bootstrap.h"
#include "catalog/binary_upgrade.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/objectaccess.h"
#include "catalog/partition.h"
#include "catalog/pg_am.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_description.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
#include "catalog/storage.h"
#include "catalog/storage_xlog.h"
#include "commands/event_trigger.h"
#include "commands/progress.h"
#include "commands/tablecmds.h"
#include "commands/trigger.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/optimizer.h"
#include "parser/parser.h"
#include "pgstat.h"
#include "postmaster/autovacuum.h"
#include "rewrite/rewriteManip.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/predicate.h"
#include "storage/smgr.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/guc.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/pg_rusage.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/tuplesort.h"

/* Potentially set by pg_upgrade_support functions */
Oid			binary_upgrade_next_index_pg_class_oid = InvalidOid;
RelFileNumber binary_upgrade_next_index_pg_class_relfilenumber =
InvalidRelFileNumber;

/*
 * Pointer-free representation of variables used when reindexing system
 * catalogs; we use this to propagate those values to parallel workers.
 */
typedef struct
{
	Oid			currentlyReindexedHeap;
	Oid			currentlyReindexedIndex;
	int			numPendingReindexedIndexes;
	Oid			pendingReindexedIndexes[FLEXIBLE_ARRAY_MEMBER];
} SerializedReindexState;

/* non-export function prototypes */
static bool relationHasPrimaryKey(Relation rel);
static TupleDesc ConstructTupleDescriptor(Relation heapRelation,
										  const IndexInfo *indexInfo,
										  const List *indexColNames,
										  Oid accessMethodId,
										  const Oid *collationIds,
										  const Oid *opclassIds);
static void InitializeAttributeOids(Relation indexRelation,
									int numatts, Oid indexoid);
static void AppendAttributeTuples(Relation indexRelation, const Datum *attopts, const NullableDatum *stattargets);
static void UpdateIndexRelation(Oid indexoid, Oid heapoid,
								Oid parentIndexId,
								const IndexInfo *indexInfo,
								const Oid *collationOids,
								const Oid *opclassOids,
								const int16 *coloptions,
								bool primary,
								bool isexclusion,
								bool immediate,
								bool isvalid,
								bool isready);
static void index_update_stats(Relation rel,
							   bool hasindex,
							   double reltuples);
static void IndexCheckExclusion(Relation heapRelation,
								Relation indexRelation,
								IndexInfo *indexInfo);
static bool validate_index_callback(ItemPointer itemptr, void *opaque);
static bool ReindexIsCurrentlyProcessingIndex(Oid indexOid);
static void SetReindexProcessing(Oid heapOid, Oid indexOid);
static void ResetReindexProcessing(void);
static void SetReindexPending(List *indexes);
static void RemoveReindexPending(Oid indexOid);


/*
 * relationHasPrimaryKey
 *		See whether an existing relation has a primary key.
 *
 * Caller must have suitable lock on the relation.
 *
 * Note: we intentionally do not check indisvalid here; that's because this
 * is used to enforce the rule that there can be only one indisprimary index,
 * and we want that to be true even if said index is invalid.
 */
static bool
relationHasPrimaryKey(Relation rel)
{
	bool		result = false;
	List	   *indexoidlist;
	ListCell   *indexoidscan;

	/*
	 * Get the list of index OIDs for the table from the relcache, and look up
	 * each one in the pg_index syscache until we find one marked primary key
	 * (hopefully there isn't more than one such).
	 */
	indexoidlist = RelationGetIndexList(rel);

	foreach(indexoidscan, indexoidlist)
	{
		Oid			indexoid = lfirst_oid(indexoidscan);
		HeapTuple	indexTuple;

		indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
		if (!HeapTupleIsValid(indexTuple))	/* should not happen */
			elog(ERROR, "cache lookup failed for index %u", indexoid);
		result = ((Form_pg_index) GETSTRUCT(indexTuple))->indisprimary;
		ReleaseSysCache(indexTuple);
		if (result)
			break;
	}

	list_free(indexoidlist);

	return result;
}

/*
 * index_check_primary_key
 *		Apply special checks needed before creating a PRIMARY KEY index
 *
 * This processing used to be in DefineIndex(), but has been split out
 * so that it can be applied during ALTER TABLE ADD PRIMARY KEY USING INDEX.
 *
 * We check for a pre-existing primary key, and that all columns of the index
 * are simple column references (not expressions), and that all those
 * columns are marked NOT NULL.  If not, fail.
 *
 * We used to automatically change unmarked columns to NOT NULL here by doing
 * our own local ALTER TABLE command.  But that doesn't work well if we're
 * executing one subcommand of an ALTER TABLE: the operations may not get
 * performed in the right order overall.  Now we expect that the parser
 * inserted any required ALTER TABLE SET NOT NULL operations before trying
 * to create a primary-key index.
 *
 * Caller had better have at least ShareLock on the table, else the not-null
 * checking isn't trustworthy.
 */
void
index_check_primary_key(Relation heapRel,
						const IndexInfo *indexInfo,
						bool is_alter_table,
						const IndexStmt *stmt)
{
	int			i;

	/*
	 * If ALTER TABLE or CREATE TABLE .. PARTITION OF, check that there isn't
	 * already a PRIMARY KEY.  In CREATE TABLE for an ordinary relation, we
	 * have faith that the parser rejected multiple pkey clauses; and CREATE
	 * INDEX doesn't have a way to say PRIMARY KEY, so it's no problem either.
	 */
	if ((is_alter_table || heapRel->rd_rel->relispartition) &&
		relationHasPrimaryKey(heapRel))
	{
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
				 errmsg("multiple primary keys for table \"%s\" are not allowed",
						RelationGetRelationName(heapRel))));
	}

	/*
	 * Indexes created with NULLS NOT DISTINCT cannot be used for primary key
	 * constraints. While there is no direct syntax to reach here, it can be
	 * done by creating a separate index and attaching it via ALTER TABLE ..
	 * USING INDEX.
	 */
	if (indexInfo->ii_NullsNotDistinct)
	{
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
				 errmsg("primary keys cannot use NULLS NOT DISTINCT indexes")));
	}

	/*
	 * Check that all of the attributes in a primary key are marked as not
	 * null.  (We don't really expect to see that; it'd mean the parser messed
	 * up.  But it seems wise to check anyway.)
	 */
	for (i = 0; i < indexInfo->ii_NumIndexKeyAttrs; i++)
	{
		AttrNumber	attnum = indexInfo->ii_IndexAttrNumbers[i];
		HeapTuple	atttuple;
		Form_pg_attribute attform;

		if (attnum == 0)
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("primary keys cannot be expressions")));

		/* System attributes are never null, so no need to check */
		if (attnum < 0)
			continue;

		atttuple = SearchSysCache2(ATTNUM,
								   ObjectIdGetDatum(RelationGetRelid(heapRel)),
								   Int16GetDatum(attnum));
		if (!HeapTupleIsValid(atttuple))
			elog(ERROR, "cache lookup failed for attribute %d of relation %u",
				 attnum, RelationGetRelid(heapRel));
		attform = (Form_pg_attribute) GETSTRUCT(atttuple);

		if (!attform->attnotnull)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
					 errmsg("primary key column \"%s\" is not marked NOT NULL",
							NameStr(attform->attname))));

		ReleaseSysCache(atttuple);
	}
}

/*
 *		ConstructTupleDescriptor
 *
 * Build an index tuple descriptor for a new index
 */
static TupleDesc
ConstructTupleDescriptor(Relation heapRelation,
						 const IndexInfo *indexInfo,
						 const List *indexColNames,
						 Oid accessMethodId,
						 const Oid *collationIds,
						 const Oid *opclassIds)
{
	int			numatts = indexInfo->ii_NumIndexAttrs;
	int			numkeyatts = indexInfo->ii_NumIndexKeyAttrs;
	ListCell   *colnames_item = list_head(indexColNames);
	ListCell   *indexpr_item = list_head(indexInfo->ii_Expressions);
	const IndexAmRoutine *amroutine;
	TupleDesc	heapTupDesc;
	TupleDesc	indexTupDesc;
	int			natts;			/* #atts in heap rel --- for error checks */
	int			i;

	/* We need access to the index AM's API struct */
	amroutine = GetIndexAmRoutineByAmId(accessMethodId, false);

	/* ... and to the table's tuple descriptor */
	heapTupDesc = RelationGetDescr(heapRelation);
	natts = RelationGetForm(heapRelation)->relnatts;

	/*
	 * allocate the new tuple descriptor
	 */
	indexTupDesc = CreateTemplateTupleDesc(numatts);

	/*
	 * Fill in the pg_attribute row.
	 */
	for (i = 0; i < numatts; i++)
	{
		AttrNumber	atnum = indexInfo->ii_IndexAttrNumbers[i];
		Form_pg_attribute to = TupleDescAttr(indexTupDesc, i);
		HeapTuple	tuple;
		Form_pg_type typeTup;
		Form_pg_opclass opclassTup;
		Oid			keyType;

		MemSet(to, 0, ATTRIBUTE_FIXED_PART_SIZE);
		to->attnum = i + 1;
		to->attislocal = true;
		to->attcollation = (i < numkeyatts) ? collationIds[i] : InvalidOid;

		/*
		 * Set the attribute name as specified by caller.
		 */
		if (colnames_item == NULL)	/* shouldn't happen */
			elog(ERROR, "too few entries in colnames list");
		namestrcpy(&to->attname, (const char *) lfirst(colnames_item));
		colnames_item = lnext(indexColNames, colnames_item);

		/*
		 * For simple index columns, we copy some pg_attribute fields from the
		 * parent relation.  For expressions we have to look at the expression
		 * result.
		 */
		if (atnum != 0)
		{
			/* Simple index column */
			const FormData_pg_attribute *from;

			Assert(atnum > 0);	/* should've been caught above */

			if (atnum > natts)	/* safety check */
				elog(ERROR, "invalid column number %d", atnum);
			from = TupleDescAttr(heapTupDesc,
								 AttrNumberGetAttrOffset(atnum));

			to->atttypid = from->atttypid;
			to->attlen = from->attlen;
			to->attndims = from->attndims;
			to->atttypmod = from->atttypmod;
			to->attbyval = from->attbyval;
			to->attalign = from->attalign;
			to->attstorage = from->attstorage;
			to->attcompression = from->attcompression;
		}
		else
		{
			/* Expressional index */
			Node	   *indexkey;

			if (indexpr_item == NULL)	/* shouldn't happen */
				elog(ERROR, "too few entries in indexprs list");
			indexkey = (Node *) lfirst(indexpr_item);
			indexpr_item = lnext(indexInfo->ii_Expressions, indexpr_item);

			/*
			 * Lookup the expression type in pg_type for the type length etc.
			 */
			keyType = exprType(indexkey);
			tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(keyType));
			if (!HeapTupleIsValid(tuple))
				elog(ERROR, "cache lookup failed for type %u", keyType);
			typeTup = (Form_pg_type) GETSTRUCT(tuple);

			/*
			 * Assign some of the attributes values. Leave the rest.
			 */
			to->atttypid = keyType;
			to->attlen = typeTup->typlen;
			to->atttypmod = exprTypmod(indexkey);
			to->attbyval = typeTup->typbyval;
			to->attalign = typeTup->typalign;
			to->attstorage = typeTup->typstorage;

			/*
			 * For expression columns, set attcompression invalid, since
			 * there's no table column from which to copy the value. Whenever
			 * we actually need to compress a value, we'll use whatever the
			 * current value of default_toast_compression is at that point in
			 * time.
			 */
			to->attcompression = InvalidCompressionMethod;

			ReleaseSysCache(tuple);

			/*
			 * Make sure the expression yields a type that's safe to store in
			 * an index.  We need this defense because we have index opclasses
			 * for pseudo-types such as "record", and the actually stored type
			 * had better be safe; eg, a named composite type is okay, an
			 * anonymous record type is not.  The test is the same as for
			 * whether a table column is of a safe type (which is why we
			 * needn't check for the non-expression case).
			 */
			CheckAttributeType(NameStr(to->attname),
							   to->atttypid, to->attcollation,
							   NIL, 0);
		}

		/*
		 * We do not yet have the correct relation OID for the index, so just
		 * set it invalid for now.  InitializeAttributeOids() will fix it
		 * later.
		 */
		to->attrelid = InvalidOid;

		/*
		 * Check the opclass and index AM to see if either provides a keytype
		 * (overriding the attribute type).  Opclass (if exists) takes
		 * precedence.
		 */
		keyType = amroutine->amkeytype;

		if (i < indexInfo->ii_NumIndexKeyAttrs)
		{
			tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassIds[i]));
			if (!HeapTupleIsValid(tuple))
				elog(ERROR, "cache lookup failed for opclass %u", opclassIds[i]);
			opclassTup = (Form_pg_opclass) GETSTRUCT(tuple);
			if (OidIsValid(opclassTup->opckeytype))
				keyType = opclassTup->opckeytype;

			/*
			 * If keytype is specified as ANYELEMENT, and opcintype is
			 * ANYARRAY, then the attribute type must be an array (else it'd
			 * not have matched this opclass); use its element type.
			 *
			 * We could also allow ANYCOMPATIBLE/ANYCOMPATIBLEARRAY here, but
			 * there seems no need to do so; there's no reason to declare an
			 * opclass as taking ANYCOMPATIBLEARRAY rather than ANYARRAY.
			 */
			if (keyType == ANYELEMENTOID && opclassTup->opcintype == ANYARRAYOID)
			{
				keyType = get_base_element_type(to->atttypid);
				if (!OidIsValid(keyType))
					elog(ERROR, "could not get element type of array type %u",
						 to->atttypid);
			}

			ReleaseSysCache(tuple);
		}

		/*
		 * If a key type different from the heap value is specified, update
		 * the type-related fields in the index tupdesc.
		 */
		if (OidIsValid(keyType) && keyType != to->atttypid)
		{
			tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(keyType));
			if (!HeapTupleIsValid(tuple))
				elog(ERROR, "cache lookup failed for type %u", keyType);
			typeTup = (Form_pg_type) GETSTRUCT(tuple);

			to->atttypid = keyType;
			to->atttypmod = -1;
			to->attlen = typeTup->typlen;
			to->attbyval = typeTup->typbyval;
			to->attalign = typeTup->typalign;
			to->attstorage = typeTup->typstorage;
			/* As above, use the default compression method in this case */
			to->attcompression = InvalidCompressionMethod;

			ReleaseSysCache(tuple);
		}

		populate_compact_attribute(indexTupDesc, i);
	}

	TupleDescFinalize(indexTupDesc);

	return indexTupDesc;
}

/* ----------------------------------------------------------------
 *		InitializeAttributeOids
 * ----------------------------------------------------------------
 */
static void
InitializeAttributeOids(Relation indexRelation,
						int numatts,
						Oid indexoid)
{
	TupleDesc	tupleDescriptor;
	int			i;

	tupleDescriptor = RelationGetDescr(indexRelation);

	for (i = 0; i < numatts; i += 1)
		TupleDescAttr(tupleDescriptor, i)->attrelid = indexoid;
}

/* ----------------------------------------------------------------
 *		AppendAttributeTuples
 * ----------------------------------------------------------------
 */
static void
AppendAttributeTuples(Relation indexRelation, const Datum *attopts, const NullableDatum *stattargets)
{
	Relation	pg_attribute;
	CatalogIndexState indstate;
	TupleDesc	indexTupDesc;
	FormExtraData_pg_attribute *attrs_extra = NULL;

	if (attopts)
	{
		attrs_extra = palloc0_array(FormExtraData_pg_attribute, indexRelation->rd_att->natts);

		for (int i = 0; i < indexRelation->rd_att->natts; i++)
		{
			if (attopts[i])
				attrs_extra[i].attoptions.value = attopts[i];
			else
				attrs_extra[i].attoptions.isnull = true;

			if (stattargets)
				attrs_extra[i].attstattarget = stattargets[i];
			else
				attrs_extra[i].attstattarget.isnull = true;
		}
	}

	/*
	 * open the attribute relation and its indexes
	 */
	pg_attribute = table_open(AttributeRelationId, RowExclusiveLock);

	indstate = CatalogOpenIndexes(pg_attribute);

	/*
	 * insert data from new index's tupdesc into pg_attribute
	 */
	indexTupDesc = RelationGetDescr(indexRelation);

	InsertPgAttributeTuples(pg_attribute, indexTupDesc, InvalidOid, attrs_extra, indstate);

	CatalogCloseIndexes(indstate);

	table_close(pg_attribute, RowExclusiveLock);
}

/* ----------------------------------------------------------------
 *		UpdateIndexRelation
 *
 * Construct and insert a new entry in the pg_index catalog
 * ----------------------------------------------------------------
 */
static void
UpdateIndexRelation(Oid indexoid,
					Oid heapoid,
					Oid parentIndexId,
					const IndexInfo *indexInfo,
					const Oid *collationOids,
					const Oid *opclassOids,
					const int16 *coloptions,
					bool primary,
					bool isexclusion,
					bool immediate,
					bool isvalid,
					bool isready)
{
	int2vector *indkey;
	oidvector  *indcollation;
	oidvector  *indclass;
	int2vector *indoption;
	Datum		exprsDatum;
	Datum		predDatum;
	Datum		values[Natts_pg_index];
	bool		nulls[Natts_pg_index] = {0};
	Relation	pg_index;
	HeapTuple	tuple;
	int			i;

	/*
	 * Copy the index key, opclass, and indoption info into arrays (should we
	 * make the caller pass them like this to start with?)
	 */
	indkey = buildint2vector(NULL, indexInfo->ii_NumIndexAttrs);
	for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
		indkey->values[i] = indexInfo->ii_IndexAttrNumbers[i];
	indcollation = buildoidvector(collationOids, indexInfo->ii_NumIndexKeyAttrs);
	indclass = buildoidvector(opclassOids, indexInfo->ii_NumIndexKeyAttrs);
	indoption = buildint2vector(coloptions, indexInfo->ii_NumIndexKeyAttrs);

	/*
	 * Convert the index expressions (if any) to a text datum
	 */
	if (indexInfo->ii_Expressions != NIL)
	{
		char	   *exprsString;

		exprsString = nodeToString(indexInfo->ii_Expressions);
		exprsDatum = CStringGetTextDatum(exprsString);
		pfree(exprsString);
	}
	else
		exprsDatum = (Datum) 0;

	/*
	 * Convert the index predicate (if any) to a text datum.  Note we convert
	 * implicit-AND format to normal explicit-AND for storage.
	 */
	if (indexInfo->ii_Predicate != NIL)
	{
		char	   *predString;

		predString = nodeToString(make_ands_explicit(indexInfo->ii_Predicate));
		predDatum = CStringGetTextDatum(predString);
		pfree(predString);
	}
	else
		predDatum = (Datum) 0;


	/*
	 * open the system catalog index relation
	 */
	pg_index = table_open(IndexRelationId, RowExclusiveLock);

	/*
	 * Build a pg_index tuple
	 */
	values[Anum_pg_index_indexrelid - 1] = ObjectIdGetDatum(indexoid);
	values[Anum_pg_index_indrelid - 1] = ObjectIdGetDatum(heapoid);
	values[Anum_pg_index_indnatts - 1] = Int16GetDatum(indexInfo->ii_NumIndexAttrs);
	values[Anum_pg_index_indnkeyatts - 1] = Int16GetDatum(indexInfo->ii_NumIndexKeyAttrs);
	values[Anum_pg_index_indisunique - 1] = BoolGetDatum(indexInfo->ii_Unique);
	values[Anum_pg_index_indnullsnotdistinct - 1] = BoolGetDatum(indexInfo->ii_NullsNotDistinct);
	values[Anum_pg_index_indisprimary - 1] = BoolGetDatum(primary);
	values[Anum_pg_index_indisexclusion - 1] = BoolGetDatum(isexclusion);
	values[Anum_pg_index_indimmediate - 1] = BoolGetDatum(immediate);
	values[Anum_pg_index_indisclustered - 1] = BoolGetDatum(false);
	values[Anum_pg_index_indisvalid - 1] = BoolGetDatum(isvalid);
	values[Anum_pg_index_indcheckxmin - 1] = BoolGetDatum(false);
	values[Anum_pg_index_indisready - 1] = BoolGetDatum(isready);
	values[Anum_pg_index_indislive - 1] = BoolGetDatum(true);
	values[Anum_pg_index_indisreplident - 1] = BoolGetDatum(false);
	values[Anum_pg_index_indkey - 1] = PointerGetDatum(indkey);
	values[Anum_pg_index_indcollation - 1] = PointerGetDatum(indcollation);
	values[Anum_pg_index_indclass - 1] = PointerGetDatum(indclass);
	values[Anum_pg_index_indoption - 1] = PointerGetDatum(indoption);
	values[Anum_pg_index_indexprs - 1] = exprsDatum;
	if (exprsDatum == (Datum) 0)
		nulls[Anum_pg_index_indexprs - 1] = true;
	values[Anum_pg_index_indpred - 1] = predDatum;
	if (predDatum == (Datum) 0)
		nulls[Anum_pg_index_indpred - 1] = true;

	tuple = heap_form_tuple(RelationGetDescr(pg_index), values, nulls);

	/*
	 * insert the tuple into the pg_index catalog
	 */
	CatalogTupleInsert(pg_index, tuple);

	/*
	 * close the relation and free the tuple
	 */
	table_close(pg_index, RowExclusiveLock);
	heap_freetuple(tuple);
}


/*
 * index_create
 *
 * heapRelation: table to build index on (suitably locked by caller)
 * indexRelationName: what it say
 * indexRelationId: normally, pass InvalidOid to let this routine
 *		generate an OID for the index.  During bootstrap this may be
 *		nonzero to specify a preselected OID.
 * parentIndexRelid: if creating an index partition, the OID of the
 *		parent index; otherwise InvalidOid.
 * parentConstraintId: if creating a constraint on a partition, the OID
 *		of the constraint in the parent; otherwise InvalidOid.
 * relFileNumber: normally, pass InvalidRelFileNumber to get new storage.
 *		May be nonzero to attach an existing valid build.
 * indexInfo: same info executor uses to insert into the index
 * indexColNames: column names to use for index (List of char *)
 * accessMethodId: OID of index AM to use
 * tableSpaceId: OID of tablespace to use
 * collationIds: array of collation OIDs, one per index column
 * opclassIds: array of index opclass OIDs, one per index column
 * coloptions: array of per-index-column indoption settings
 * reloptions: AM-specific options
 * flags: bitmask that can include any combination of these bits:
 *		INDEX_CREATE_IS_PRIMARY
 *			the index is a primary key
 *		INDEX_CREATE_ADD_CONSTRAINT:
 *			invoke index_constraint_create also
 *		INDEX_CREATE_SKIP_BUILD:
 *			skip the index_build() step for the moment; caller must do it
 *			later (typically via reindex_index())
 *		INDEX_CREATE_CONCURRENT:
 *			do not lock the table against writers.  The index will be
 *			marked "invalid" and the caller must take additional steps
 *			to fix it up.
 *		INDEX_CREATE_IF_NOT_EXISTS:
 *			do not throw an error if a relation with the same name
 *			already exists.
 *		INDEX_CREATE_PARTITIONED:
 *			create a partitioned index (table must be partitioned)
 *		INDEX_CREATE_SUPPRESS_PROGRESS:
 *			don't report progress during the index build.
 *
 * constr_flags: flags passed to index_constraint_create
 *		(only if INDEX_CREATE_ADD_CONSTRAINT is set)
 * allow_system_table_mods: allow table to be a system catalog
 * is_internal: if true, post creation hook for new index
 * constraintId: if not NULL, receives OID of created constraint
 *
 * Returns the OID of the created index.
 */
Oid
index_create(Relation heapRelation,
			 const char *indexRelationName,
			 Oid indexRelationId,
			 Oid parentIndexRelid,
			 Oid parentConstraintId,
			 RelFileNumber relFileNumber,
			 IndexInfo *indexInfo,
			 const List *indexColNames,
			 Oid accessMethodId,
			 Oid tableSpaceId,
			 const Oid *collationIds,
			 const Oid *opclassIds,
			 const Datum *opclassOptions,
			 const int16 *coloptions,
			 const NullableDatum *stattargets,
			 Datum reloptions,
			 uint16 flags,
			 uint16 constr_flags,
			 bool allow_system_table_mods,
			 bool is_internal,
			 Oid *constraintId)
{
	Oid			heapRelationId = RelationGetRelid(heapRelation);
	Relation	pg_class;
	Relation	indexRelation;
	TupleDesc	indexTupDesc;
	bool		shared_relation;
	bool		mapped_relation;
	bool		is_exclusion;
	Oid			namespaceId;
	int			i;
	char		relpersistence;
	bool		isprimary = (flags & INDEX_CREATE_IS_PRIMARY) != 0;
	bool		invalid = (flags & INDEX_CREATE_INVALID) != 0;
	bool		concurrent = (flags & INDEX_CREATE_CONCURRENT) != 0;
	bool		partitioned = (flags & INDEX_CREATE_PARTITIONED) != 0;
	bool		progress = (flags & INDEX_CREATE_SUPPRESS_PROGRESS) == 0;
	char		relkind;
	TransactionId relfrozenxid;
	MultiXactId relminmxid;
	bool		create_storage = !RelFileNumberIsValid(relFileNumber);

	/* constraint flags can only be set when a constraint is requested */
	Assert((constr_flags == 0) ||
		   ((flags & INDEX_CREATE_ADD_CONSTRAINT) != 0));
	/* partitioned indexes must never be "built" by themselves */
	Assert(!partitioned || (flags & INDEX_CREATE_SKIP_BUILD));

	relkind = partitioned ? RELKIND_PARTITIONED_INDEX : RELKIND_INDEX;
	is_exclusion = (indexInfo->ii_ExclusionOps != NULL);

	pg_class = table_open(RelationRelationId, RowExclusiveLock);

	/*
	 * The index will be in the same namespace as its parent table, and is
	 * shared across databases if and only if the parent is.  Likewise, it
	 * will use the relfilenumber map if and only if the parent does; and it
	 * inherits the parent's relpersistence.
	 */
	namespaceId = RelationGetNamespace(heapRelation);
	shared_relation = heapRelation->rd_rel->relisshared;
	mapped_relation = RelationIsMapped(heapRelation);
	relpersistence = heapRelation->rd_rel->relpersistence;

	/*
	 * check parameters
	 */
	if (indexInfo->ii_NumIndexAttrs < 1)
		elog(ERROR, "must index at least one column");

	if (!allow_system_table_mods &&
		IsSystemRelation(heapRelation) &&
		IsNormalProcessingMode())
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("user-defined indexes on system catalog tables are not supported")));

	/*
	 * Btree text_pattern_ops uses texteq as the equality operator, which is
	 * fine as long as the collation is deterministic; texteq then reduces to
	 * bitwise equality and so it is semantically compatible with the other
	 * operators and functions in that opclass.  But with a nondeterministic
	 * collation, texteq could yield results that are incompatible with the
	 * actual behavior of the index (which is determined by the opclass's
	 * comparison function).  We prevent such problems by refusing creation of
	 * an index with that opclass and a nondeterministic collation.
	 *
	 * The same applies to varchar_pattern_ops and bpchar_pattern_ops.  If we
	 * find more cases, we might decide to create a real mechanism for marking
	 * opclasses as incompatible with nondeterminism; but for now, this small
	 * hack suffices.
	 *
	 * Another solution is to use a special operator, not texteq, as the
	 * equality opclass member; but that is undesirable because it would
	 * prevent index usage in many queries that work fine today.
	 */
	for (i = 0; i < indexInfo->ii_NumIndexKeyAttrs; i++)
	{
		Oid			collation = collationIds[i];
		Oid			opclass = opclassIds[i];

		if (collation)
		{
			if ((opclass == TEXT_BTREE_PATTERN_OPS_OID ||
				 opclass == VARCHAR_BTREE_PATTERN_OPS_OID ||
				 opclass == BPCHAR_BTREE_PATTERN_OPS_OID) &&
				!get_collation_isdeterministic(collation))
			{
				HeapTuple	classtup;

				classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
				if (!HeapTupleIsValid(classtup))
					elog(ERROR, "cache lookup failed for operator class %u", opclass);
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("nondeterministic collations are not supported for operator class \"%s\"",
								NameStr(((Form_pg_opclass) GETSTRUCT(classtup))->opcname))));
				ReleaseSysCache(classtup);
			}
		}
	}

	/*
	 * Concurrent index build on a system catalog is unsafe because we tend to
	 * release locks before committing in catalogs.
	 */
	if (concurrent &&
		IsCatalogRelation(heapRelation))
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("concurrent index creation on system catalog tables is not supported")));

	/*
	 * This case is currently not supported.  There's no way to ask for it in
	 * the grammar with CREATE INDEX, but it can happen with REINDEX.
	 */
	if (concurrent && is_exclusion)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("concurrent index creation for exclusion constraints is not supported")));

	/*
	 * We cannot allow indexing a shared relation after initdb (because
	 * there's no way to make the entry in other databases' pg_class).
	 */
	if (shared_relation && !IsBootstrapProcessingMode())
		ereport(ERROR,
				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
				 errmsg("shared indexes cannot be created after initdb")));

	/*
	 * Shared relations must be in pg_global, too (last-ditch check)
	 */
	if (shared_relation && tableSpaceId != GLOBALTABLESPACE_OID)
		elog(ERROR, "shared relations must be placed in pg_global tablespace");

	/*
	 * Check for duplicate name (both as to the index, and as to the
	 * associated constraint if any).  Such cases would fail on the relevant
	 * catalogs' unique indexes anyway, but we prefer to give a friendlier
	 * error message.
	 */
	if (get_relname_relid(indexRelationName, namespaceId))
	{
		if ((flags & INDEX_CREATE_IF_NOT_EXISTS) != 0)
		{
			ereport(NOTICE,
					(errcode(ERRCODE_DUPLICATE_TABLE),
					 errmsg("relation \"%s\" already exists, skipping",
							indexRelationName)));
			table_close(pg_class, RowExclusiveLock);
			return InvalidOid;
		}

		ereport(ERROR,
				(errcode(ERRCODE_DUPLICATE_TABLE),
				 errmsg("relation \"%s\" already exists",
						indexRelationName)));
	}

	if ((flags & INDEX_CREATE_ADD_CONSTRAINT) != 0 &&
		ConstraintNameIsUsed(CONSTRAINT_RELATION, heapRelationId,
							 indexRelationName))
	{
		/*
		 * INDEX_CREATE_IF_NOT_EXISTS does not apply here, since the
		 * conflicting constraint is not an index.
		 */
		ereport(ERROR,
				(errcode(ERRCODE_DUPLICATE_OBJECT),
				 errmsg("constraint \"%s\" for relation \"%s\" already exists",
						indexRelationName, RelationGetRelationName(heapRelation))));
	}

	/*
	 * construct tuple descriptor for index tuples
	 */
	indexTupDesc = ConstructTupleDescriptor(heapRelation,
											indexInfo,
											indexColNames,
											accessMethodId,
											collationIds,
											opclassIds);

	/*
	 * Allocate an OID for the index, unless we were told what to use.
	 *
	 * The OID will be the relfilenumber as well, so make sure it doesn't
	 * collide with either pg_class OIDs or existing physical files.
	 */
	if (!OidIsValid(indexRelationId))
	{
		/* Use binary-upgrade override for pg_class.oid and relfilenumber */
		if (IsBinaryUpgrade)
		{
			if (!OidIsValid(binary_upgrade_next_index_pg_class_oid))
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
						 errmsg("pg_class index OID value not set when in binary upgrade mode")));

			indexRelationId = binary_upgrade_next_index_pg_class_oid;
			binary_upgrade_next_index_pg_class_oid = InvalidOid;

			/* Override the index relfilenumber */
			if ((relkind == RELKIND_INDEX) &&
				(!RelFileNumberIsValid(binary_upgrade_next_index_pg_class_relfilenumber)))
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
						 errmsg("index relfilenumber value not set when in binary upgrade mode")));
			relFileNumber = binary_upgrade_next_index_pg_class_relfilenumber;
			binary_upgrade_next_index_pg_class_relfilenumber = InvalidRelFileNumber;

			/*
			 * Note that we want create_storage = true for binary upgrade. The
			 * storage we create here will be replaced later, but we need to
			 * have something on disk in the meanwhile.
			 */
			Assert(create_storage);
		}
		else
		{
			indexRelationId =
				GetNewRelFileNumber(tableSpaceId, pg_class, relpersistence);
		}
	}

	/*
	 * create the index relation's relcache entry and, if necessary, the
	 * physical disk file. (If we fail further down, it's the smgr's
	 * responsibility to remove the disk file again, if any.)
	 */
	indexRelation = heap_create(indexRelationName,
								namespaceId,
								tableSpaceId,
								indexRelationId,
								relFileNumber,
								accessMethodId,
								indexTupDesc,
								relkind,
								relpersistence,
								shared_relation,
								mapped_relation,
								allow_system_table_mods,
								&relfrozenxid,
								&relminmxid,
								create_storage);

	Assert(relfrozenxid == InvalidTransactionId);
	Assert(relminmxid == InvalidMultiXactId);
	Assert(indexRelationId == RelationGetRelid(indexRelation));

	/*
	 * Obtain exclusive lock on it.  Although no other transactions can see it
	 * until we commit, this prevents deadlock-risk complaints from lock
	 * manager in cases such as CLUSTER.
	 */
	LockRelation(indexRelation, AccessExclusiveLock);

	/*
	 * Fill in fields of the index's pg_class entry that are not set correctly
	 * by heap_create.
	 *
	 * XXX should have a cleaner way to create cataloged indexes
	 */
	indexRelation->rd_rel->relowner = heapRelation->rd_rel->relowner;
	indexRelation->rd_rel->relam = accessMethodId;
	indexRelation->rd_rel->relispartition = OidIsValid(parentIndexRelid);

	/*
	 * store index's pg_class entry
	 */
	InsertPgClassTuple(pg_class, indexRelation,
					   RelationGetRelid(indexRelation),
					   (Datum) 0,
					   reloptions);

	/* done with pg_class */
	table_close(pg_class, RowExclusiveLock);

	/*
	 * now update the object id's of all the attribute tuple forms in the
	 * index relation's tuple descriptor
	 */
	InitializeAttributeOids(indexRelation,
							indexInfo->ii_NumIndexAttrs,
							indexRelationId);

	/*
	 * append ATTRIBUTE tuples for the index
	 */
	AppendAttributeTuples(indexRelation, opclassOptions, stattargets);

	/* ----------------
	 *	  update pg_index
	 *	  (append INDEX tuple)
	 *
	 *	  Note that this stows away a representation of "predicate".
	 *	  (Or, could define a rule to maintain the predicate) --Nels, Feb '92
	 * ----------------
	 */
	UpdateIndexRelation(indexRelationId, heapRelationId, parentIndexRelid,
						indexInfo,
						collationIds, opclassIds, coloptions,
						isprimary, is_exclusion,
						(constr_flags & INDEX_CONSTR_CREATE_DEFERRABLE) == 0,
						!concurrent && !invalid,
						!concurrent);

	/*
	 * Register relcache invalidation on the indexes' heap relation, to
	 * maintain consistency of its index list
	 */
	CacheInvalidateRelcache(heapRelation);

	/* update pg_inherits and the parent's relhassubclass, if needed */
	if (OidIsValid(parentIndexRelid))
	{
		StoreSingleInheritance(indexRelationId, parentIndexRelid, 1);
		LockRelationOid(parentIndexRelid, ShareUpdateExclusiveLock);
		SetRelationHasSubclass(parentIndexRelid, true);
	}

	/*
	 * Register constraint and dependencies for the index.
	 *
	 * If the index is from a CONSTRAINT clause, construct a pg_constraint
	 * entry.  The index will be linked to the constraint, which in turn is
	 * linked to the table.  If it's not a CONSTRAINT, we need to make a
	 * dependency directly on the table.
	 *
	 * We don't need a dependency on the namespace, because there'll be an
	 * indirect dependency via our parent table.
	 *
	 * During bootstrap we can't register any dependencies, and we don't try
	 * to make a constraint either.
	 */
	if (!IsBootstrapProcessingMode())
	{
		ObjectAddress myself,
					referenced;
		ObjectAddresses *addrs;

		ObjectAddressSet(myself, RelationRelationId, indexRelationId);

		if ((flags & INDEX_CREATE_ADD_CONSTRAINT) != 0)
		{
			char		constraintType;
			ObjectAddress localaddr;

			if (isprimary)
				constraintType = CONSTRAINT_PRIMARY;
			else if (indexInfo->ii_Unique)
				constraintType = CONSTRAINT_UNIQUE;
			else if (is_exclusion)
				constraintType = CONSTRAINT_EXCLUSION;
			else
			{
				elog(ERROR, "constraint must be PRIMARY, UNIQUE or EXCLUDE");
				constraintType = 0; /* keep compiler quiet */
			}

			localaddr = index_constraint_create(heapRelation,
												indexRelationId,
												parentConstraintId,
												indexInfo,
												indexRelationName,
												constraintType,
												constr_flags,
												allow_system_table_mods,
												is_internal);
			if (constraintId)
				*constraintId = localaddr.objectId;
		}
		else
		{
			bool		have_simple_col = false;

			addrs = new_object_addresses();

			/* Create auto dependencies on simply-referenced columns */
			for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
			{
				if (indexInfo->ii_IndexAttrNumbers[i] != 0)
				{
					ObjectAddressSubSet(referenced, RelationRelationId,
										heapRelationId,
										indexInfo->ii_IndexAttrNumbers[i]);
					add_exact_object_address(&referenced, addrs);
					have_simple_col = true;
				}
			}

			/*
			 * If there are no simply-referenced columns, give the index an
			 * auto dependency on the whole table.  In most cases, this will
			 * be redundant, but it might not be if the index expressions and
			 * predicate contain no Vars or only whole-row Vars.
			 */
			if (!have_simple_col)
			{
				ObjectAddressSet(referenced, RelationRelationId,
								 heapRelationId);
				add_exact_object_address(&referenced, addrs);
			}

			record_object_address_dependencies(&myself, addrs, DEPENDENCY_AUTO);
			free_object_addresses(addrs);
		}

		/*
		 * If this is an index partition, create partition dependencies on
		 * both the parent index and the table.  (Note: these must be *in
		 * addition to*, not instead of, all other dependencies.  Otherwise
		 * we'll be short some dependencies after DETACH PARTITION.)
		 */
		if (OidIsValid(parentIndexRelid))
		{
			ObjectAddressSet(referenced, RelationRelationId, parentIndexRelid);
			recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI);

			ObjectAddressSet(referenced, RelationRelationId, heapRelationId);
			recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC);
		}

		/* placeholder for normal dependencies */
		addrs = new_object_addresses();

		/* Store dependency on collations */

		/* The default collation is pinned, so don't bother recording it */
		for (i = 0; i < indexInfo->ii_NumIndexKeyAttrs; i++)
		{
			if (OidIsValid(collationIds[i]) && collationIds[i] != DEFAULT_COLLATION_OID)
			{
				ObjectAddressSet(referenced, CollationRelationId, collationIds[i]);
				add_exact_object_address(&referenced, addrs);
			}
		}

		/* Store dependency on operator classes */
		for (i = 0; i < indexInfo->ii_NumIndexKeyAttrs; i++)
		{
			ObjectAddressSet(referenced, OperatorClassRelationId, opclassIds[i]);
			add_exact_object_address(&referenced, addrs);
		}

		record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
		free_object_addresses(addrs);

		/* Store dependencies on anything mentioned in index expressions */
		if (indexInfo->ii_Expressions)
		{
			recordDependencyOnSingleRelExpr(&myself,
											(Node *) indexInfo->ii_Expressions,
											heapRelationId,
											DEPENDENCY_NORMAL,
											DEPENDENCY_AUTO, false);
		}

		/* Store dependencies on anything mentioned in predicate */
		if (indexInfo->ii_Predicate)
		{
			recordDependencyOnSingleRelExpr(&myself,
											(Node *) indexInfo->ii_Predicate,
											heapRelationId,
											DEPENDENCY_NORMAL,
											DEPENDENCY_AUTO, false);
		}
	}
	else
	{
		/* Bootstrap mode - assert we weren't asked for constraint support */
		Assert((flags & INDEX_CREATE_ADD_CONSTRAINT) == 0);
	}

	/* Post creation hook for new index */
	InvokeObjectPostCreateHookArg(RelationRelationId,
								  indexRelationId, 0, is_internal);

	/*
	 * Advance the command counter so that we can see the newly-entered
	 * catalog tuples for the index.
	 */
	CommandCounterIncrement();

	/*
	 * In bootstrap mode, we have to fill in the index strategy structure with
	 * information from the catalogs.  If we aren't bootstrapping, then the
	 * relcache entry has already been rebuilt thanks to sinval update during
	 * CommandCounterIncrement.
	 */
	if (IsBootstrapProcessingMode())
		RelationInitIndexAccessInfo(indexRelation);
	else
		Assert(indexRelation->rd_indexcxt != NULL);

	indexRelation->rd_index->indnkeyatts = indexInfo->ii_NumIndexKeyAttrs;

	/* Validate opclass-specific options */
	if (opclassOptions)
		for (i = 0; i < indexInfo->ii_NumIndexKeyAttrs; i++)
			(void) index_opclass_options(indexRelation, i + 1,
										 opclassOptions[i],
										 true);

	/*
	 * If this is bootstrap (initdb) time, then we don't actually fill in the
	 * index yet.  We'll be creating more indexes and classes later, so we
	 * delay filling them in until just before we're done with bootstrapping.
	 * Similarly, if the caller specified to skip the build then filling the
	 * index is delayed till later (ALTER TABLE can save work in some cases
	 * with this).  Otherwise, we call the AM routine that constructs the
	 * index.
	 */
	if (IsBootstrapProcessingMode())
	{
		index_register(heapRelationId, indexRelationId, indexInfo);
	}
	else if ((flags & INDEX_CREATE_SKIP_BUILD) != 0)
	{
		/*
		 * Caller is responsible for filling the index later on.  However,
		 * we'd better make sure that the heap relation is correctly marked as
		 * having an index.
		 */
		index_update_stats(heapRelation,
						   true,
						   -1.0);
		/* Make the above update visible */
		CommandCounterIncrement();
	}
	else
	{
		index_build(heapRelation, indexRelation, indexInfo, false, true,
					progress);
	}

	/*
	 * Close the index; but we keep the lock that we acquired above until end
	 * of transaction.  Closing the heap is caller's responsibility.
	 */
	index_close(indexRelation, NoLock);

	return indexRelationId;
}

/*
 * index_create_copy
 *
 * Create an index based on the definition of the one provided by caller.  The
 * index is inserted into catalogs.  'flags' are passed directly to
 * index_create.
 *
 * "tablespaceOid" is the tablespace to use for this index.
 */
Oid
index_create_copy(Relation heapRelation, uint16 flags,
				  Oid oldIndexId, Oid tablespaceOid, const char *newName)
{
	Relation	indexRelation;
	IndexInfo  *oldInfo,
			   *newInfo;
	Oid			newIndexId = InvalidOid;
	bool		concurrently = (flags & INDEX_CREATE_CONCURRENT) != 0;
	HeapTuple	indexTuple,
				classTuple;
	Datum		indclassDatum,
				colOptionDatum,
				reloptionsDatum;
	Datum	   *opclassOptions;
	oidvector  *indclass;
	int2vector *indcoloptions;
	NullableDatum *stattargets;
	bool		isnull;
	List	   *indexColNames = NIL;
	List	   *indexExprs = NIL;
	List	   *indexPreds = NIL;

	indexRelation = index_open(oldIndexId, RowExclusiveLock);

	/* The new index needs some information from the old index */
	oldInfo = BuildIndexInfo(indexRelation);

	/*
	 * Concurrent build of an index with exclusion constraints is not
	 * supported.
	 */
	if (oldInfo->ii_ExclusionOps != NULL && concurrently)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("concurrent index creation for exclusion constraints is not supported")));

	/* Get the array of class and column options IDs from index info */
	indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(oldIndexId));
	if (!HeapTupleIsValid(indexTuple))
		elog(ERROR, "cache lookup failed for index %u", oldIndexId);
	indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
										   Anum_pg_index_indclass);
	indclass = (oidvector *) DatumGetPointer(indclassDatum);

	colOptionDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
											Anum_pg_index_indoption);
	indcoloptions = (int2vector *) DatumGetPointer(colOptionDatum);

	/* Fetch reloptions of index if any */
	classTuple = SearchSysCache1(RELOID, ObjectIdGetDatum(oldIndexId));
	if (!HeapTupleIsValid(classTuple))
		elog(ERROR, "cache lookup failed for relation %u", oldIndexId);
	reloptionsDatum = SysCacheGetAttr(RELOID, classTuple,
									  Anum_pg_class_reloptions, &isnull);

	/*
	 * Fetch the list of expressions and predicates directly from the
	 * catalogs.  This cannot rely on the information from IndexInfo of the
	 * old index as these have been flattened for the planner.
	 */
	if (oldInfo->ii_Expressions != NIL)
	{
		Datum		exprDatum;
		char	   *exprString;

		exprDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
										   Anum_pg_index_indexprs);
		exprString = TextDatumGetCString(exprDatum);
		indexExprs = (List *) stringToNode(exprString);
		pfree(exprString);
	}
	if (oldInfo->ii_Predicate != NIL)
	{
		Datum		predDatum;
		char	   *predString;

		predDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
										   Anum_pg_index_indpred);
		predString = TextDatumGetCString(predDatum);
		indexPreds = (List *) stringToNode(predString);

		/* Also convert to implicit-AND format */
		indexPreds = make_ands_implicit((Expr *) indexPreds);
		pfree(predString);
	}

	/*
	 * Build the index information for the new index.
	 */
	newInfo = makeIndexInfo(oldInfo->ii_NumIndexAttrs,
							oldInfo->ii_NumIndexKeyAttrs,
							oldInfo->ii_Am,
							indexExprs,
							indexPreds,
							oldInfo->ii_Unique,
							oldInfo->ii_NullsNotDistinct,
							!concurrently,	/* isready */
							concurrently,	/* concurrent */
							indexRelation->rd_indam->amsummarizing,
							oldInfo->ii_WithoutOverlaps);
	newInfo->ii_ExpressionsExpand =
		ExpandVirtualGeneratedColumns(newInfo->ii_ExpressionsExpand, heapRelation, InvalidOid);
	newInfo->ii_PredicateExpand =
		ExpandVirtualGeneratedColumns(newInfo->ii_PredicateExpand, heapRelation, InvalidOid);

	/* fetch exclusion constraint info if any */
	if (indexRelation->rd_index->indisexclusion)
	{
		/*
		 * XXX Beware: we're making newInfo point to oldInfo-owned memory.  It
		 * would be more orthodox to palloc+memcpy, but we don't need that
		 * here at present.
		 */
		newInfo->ii_ExclusionOps = oldInfo->ii_ExclusionOps;
		newInfo->ii_ExclusionProcs = oldInfo->ii_ExclusionProcs;
		newInfo->ii_ExclusionStrats = oldInfo->ii_ExclusionStrats;
	}

	/*
	 * Extract the list of column names and the column numbers for the new
	 * index information.  All this information will be used for the index
	 * creation.
	 */
	for (int i = 0; i < oldInfo->ii_NumIndexAttrs; i++)
	{
		TupleDesc	indexTupDesc = RelationGetDescr(indexRelation);
		Form_pg_attribute att = TupleDescAttr(indexTupDesc, i);

		indexColNames = lappend(indexColNames, NameStr(att->attname));
		newInfo->ii_IndexAttrNumbers[i] = oldInfo->ii_IndexAttrNumbers[i];
	}

	/* Extract opclass options for each attribute */
	opclassOptions = palloc0_array(Datum, newInfo->ii_NumIndexAttrs);
	for (int i = 0; i < newInfo->ii_NumIndexAttrs; i++)
		opclassOptions[i] = get_attoptions(oldIndexId, i + 1);

	/* Extract statistic targets for each attribute */
	stattargets = palloc0_array(NullableDatum, newInfo->ii_NumIndexAttrs);
	for (int i = 0; i < newInfo->ii_NumIndexAttrs; i++)
	{
		HeapTuple	tp;
		Datum		dat;

		tp = SearchSysCache2(ATTNUM, ObjectIdGetDatum(oldIndexId), Int16GetDatum(i + 1));
		if (!HeapTupleIsValid(tp))
			elog(ERROR, "cache lookup failed for attribute %d of relation %u",
				 i + 1, oldIndexId);
		dat = SysCacheGetAttr(ATTNUM, tp, Anum_pg_attribute_attstattarget, &isnull);
		ReleaseSysCache(tp);
		stattargets[i].value = dat;
		stattargets[i].isnull = isnull;
	}

	/*
	 * Now create the new index.
	 *
	 * For a partition index, we adjust the partition dependency later, to
	 * ensure a consistent state at all times.  That is why parentIndexRelid
	 * is not set here.
	 */
	newIndexId = index_create(heapRelation,
							  newName,
							  InvalidOid,	/* indexRelationId */
							  InvalidOid,	/* parentIndexRelid */
							  InvalidOid,	/* parentConstraintId */
							  InvalidRelFileNumber, /* relFileNumber */
							  newInfo,
							  indexColNames,
							  indexRelation->rd_rel->relam,
							  tablespaceOid,
							  indexRelation->rd_indcollation,
							  indclass->values,
							  opclassOptions,
							  indcoloptions->values,
							  stattargets,
							  reloptionsDatum,
							  flags,
							  0,
							  true, /* allow table to be a system catalog? */
							  false,	/* is_internal? */
							  NULL);

	/* Close the relations used and clean up */
	index_close(indexRelation, NoLock);
	ReleaseSysCache(indexTuple);
	ReleaseSysCache(classTuple);

	return newIndexId;
}

/*
 * index_concurrently_build
 *
 * Build index for a concurrent operation.  Low-level locks are taken when
 * this operation is performed to prevent only schema changes, but they need
 * to be kept until the end of the transaction performing this operation.
 * 'indexOid' refers to an index relation OID already created as part of
 * previous processing, and 'heapOid' refers to its parent heap relation.
 */
void
index_concurrently_build(Oid heapRelationId,
						 Oid indexRelationId)
{
	Relation	heapRel;
	Oid			save_userid;
	int			save_sec_context;
	int			save_nestlevel;
	Relation	indexRelation;
	IndexInfo  *indexInfo;

	/* This had better make sure that a snapshot is active */
	Assert(ActiveSnapshotSet());

	/* Open and lock the parent heap relation */
	heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock);

	/*
	 * Switch to the table owner's userid, so that any index functions are run
	 * as that user.  Also lock down security-restricted operations and
	 * arrange to make GUC variable changes local to this command.
	 */
	GetUserIdAndSecContext(&save_userid, &save_sec_context);
	SetUserIdAndSecContext(heapRel->rd_rel->relowner,
						   save_sec_context | SECURITY_RESTRICTED_OPERATION);
	save_nestlevel = NewGUCNestLevel();
	RestrictSearchPath();

	indexRelation = index_open(indexRelationId, RowExclusiveLock);

	/*
	 * We have to re-build the IndexInfo struct, since it was lost in the
	 * commit of the transaction where this concurrent index was created at
	 * the catalog level.
	 */
	indexInfo = BuildIndexInfo(indexRelation);
	Assert(!indexInfo->ii_ReadyForInserts);
	indexInfo->ii_Concurrent = true;
	indexInfo->ii_BrokenHotChain = false;

	/* Now build the index */
	index_build(heapRel, indexRelation, indexInfo, false, true, true);

	/* Roll back any GUC changes executed by index functions */
	AtEOXact_GUC(false, save_nestlevel);

	/* Restore userid and security context */
	SetUserIdAndSecContext(save_userid, save_sec_context);

	/* Close both the relations, but keep the locks */
	table_close(heapRel, NoLock);
	index_close(indexRelation, NoLock);

	/*
	 * Update the pg_index row to mark the index as ready for inserts. Once we
	 * commit this transaction, any new transactions that open the table must
	 * insert new entries into the index for insertions and non-HOT updates.
	 */
	index_set_state_flags(indexRelationId, INDEX_CREATE_SET_READY);
}

/*
 * index_concurrently_swap
 *
 * Swap name, dependencies, and constraints of the old index over to the new
 * index, while marking the old index as invalid and the new as valid.
 */
void
index_concurrently_swap(Oid newIndexId, Oid oldIndexId, const char *oldName)
{
	Relation	pg_class,
				pg_index,
				pg_constraint,
				pg_trigger;
	Relation	oldClassRel,
				newClassRel;
	HeapTuple	oldClassTuple,
				newClassTuple;
	Form_pg_class oldClassForm,
				newClassForm;
	HeapTuple	oldIndexTuple,
				newIndexTuple;
	Form_pg_index oldIndexForm,
				newIndexForm;
	bool		isPartition;
	Oid			indexConstraintOid;
	List	   *constraintOids = NIL;
	ListCell   *lc;

	/*
	 * Take a necessary lock on the old and new index before swapping them.
	 */
	oldClassRel = relation_open(oldIndexId, ShareUpdateExclusiveLock);
	newClassRel = relation_open(newIndexId, ShareUpdateExclusiveLock);

	/* Now swap names and dependencies of those indexes */
	pg_class = table_open(RelationRelationId, RowExclusiveLock);

	oldClassTuple = SearchSysCacheCopy1(RELOID,
										ObjectIdGetDatum(oldIndexId));
	if (!HeapTupleIsValid(oldClassTuple))
		elog(ERROR, "could not find tuple for relation %u", oldIndexId);
	newClassTuple = SearchSysCacheCopy1(RELOID,
										ObjectIdGetDatum(newIndexId));
	if (!HeapTupleIsValid(newClassTuple))
		elog(ERROR, "could not find tuple for relation %u", newIndexId);

	oldClassForm = (Form_pg_class) GETSTRUCT(oldClassTuple);
	newClassForm = (Form_pg_class) GETSTRUCT(newClassTuple);

	/* Swap the names */
	namestrcpy(&newClassForm->relname, NameStr(oldClassForm->relname));
	namestrcpy(&oldClassForm->relname, oldName);

	/* Swap the partition flags to track inheritance properly */
	isPartition = newClassForm->relispartition;
	newClassForm->relispartition = oldClassForm->relispartition;
	oldClassForm->relispartition = isPartition;

	CatalogTupleUpdate(pg_class, &oldClassTuple->t_self, oldClassTuple);
	CatalogTupleUpdate(pg_class, &newClassTuple->t_self, newClassTuple);

	heap_freetuple(oldClassTuple);
	heap_freetuple(newClassTuple);

	/* Now swap index info */
	pg_index = table_open(IndexRelationId, RowExclusiveLock);

	oldIndexTuple = SearchSysCacheCopy1(INDEXRELID,
										ObjectIdGetDatum(oldIndexId));
	if (!HeapTupleIsValid(oldIndexTuple))
		elog(ERROR, "could not find tuple for relation %u", oldIndexId);
	newIndexTuple = SearchSysCacheCopy1(INDEXRELID,
										ObjectIdGetDatum(newIndexId));
	if (!HeapTupleIsValid(newIndexTuple))
		elog(ERROR, "could not find tuple for relation %u", newIndexId);

	oldIndexForm = (Form_pg_index) GETSTRUCT(oldIndexTuple);
	newIndexForm = (Form_pg_index) GETSTRUCT(newIndexTuple);

	/*
	 * Copy constraint flags from the old index. This is safe because the old
	 * index guaranteed uniqueness.
	 */
	newIndexForm->indisprimary = oldIndexForm->indisprimary;
	oldIndexForm->indisprimary = false;
	newIndexForm->indisexclusion = oldIndexForm->indisexclusion;
	oldIndexForm->indisexclusion = false;
	newIndexForm->indimmediate = oldIndexForm->indimmediate;
	oldIndexForm->indimmediate = true;

	/* Preserve indisreplident in the new index */
	newIndexForm->indisreplident = oldIndexForm->indisreplident;

	/* Preserve indisclustered in the new index */
	newIndexForm->indisclustered = oldIndexForm->indisclustered;

	/*
	 * Mark the new index as valid, and the old index as invalid similarly to
	 * what index_set_state_flags() does.
	 */
	newIndexForm->indisvalid = true;
	oldIndexForm->indisvalid = false;
	oldIndexForm->indisclustered = false;
	oldIndexForm->indisreplident = false;

	CatalogTupleUpdate(pg_index, &oldIndexTuple->t_self, oldIndexTuple);
	CatalogTupleUpdate(pg_index, &newIndexTuple->t_self, newIndexTuple);

	heap_freetuple(oldIndexTuple);
	heap_freetuple(newIndexTuple);

	/*
	 * Move constraints and triggers over to the new index
	 */

	constraintOids = get_index_ref_constraints(oldIndexId);

	indexConstraintOid = get_index_constraint(oldIndexId);

	if (OidIsValid(indexConstraintOid))
		constraintOids = lappend_oid(constraintOids, indexConstraintOid);

	pg_constraint = table_open(ConstraintRelationId, RowExclusiveLock);
	pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);

	foreach(lc, constraintOids)
	{
		HeapTuple	constraintTuple,
					triggerTuple;
		Form_pg_constraint conForm;
		ScanKeyData key[1];
		SysScanDesc scan;
		Oid			constraintOid = lfirst_oid(lc);

		/* Move the constraint from the old to the new index */
		constraintTuple = SearchSysCacheCopy1(CONSTROID,
											  ObjectIdGetDatum(constraintOid));
		if (!HeapTupleIsValid(constraintTuple))
			elog(ERROR, "could not find tuple for constraint %u", constraintOid);

		conForm = ((Form_pg_constraint) GETSTRUCT(constraintTuple));

		if (conForm->conindid == oldIndexId)
		{
			conForm->conindid = newIndexId;

			CatalogTupleUpdate(pg_constraint, &constraintTuple->t_self, constraintTuple);
		}

		heap_freetuple(constraintTuple);

		/* Search for trigger records */
		ScanKeyInit(&key[0],
					Anum_pg_trigger_tgconstraint,
					BTEqualStrategyNumber, F_OIDEQ,
					ObjectIdGetDatum(constraintOid));

		scan = systable_beginscan(pg_trigger, TriggerConstraintIndexId, true,
								  NULL, 1, key);

		while (HeapTupleIsValid((triggerTuple = systable_getnext(scan))))
		{
			Form_pg_trigger tgForm = (Form_pg_trigger) GETSTRUCT(triggerTuple);

			if (tgForm->tgconstrindid != oldIndexId)
				continue;

			/* Make a modifiable copy */
			triggerTuple = heap_copytuple(triggerTuple);
			tgForm = (Form_pg_trigger) GETSTRUCT(triggerTuple);

			tgForm->tgconstrindid = newIndexId;

			CatalogTupleUpdate(pg_trigger, &triggerTuple->t_self, triggerTuple);

			heap_freetuple(triggerTuple);
		}

		systable_endscan(scan);
	}

	/*
	 * Move comment if any
	 */
	{
		Relation	description;
		ScanKeyData skey[3];
		SysScanDesc sd;
		HeapTuple	tuple;
		Datum		values[Natts_pg_description] = {0};
		bool		nulls[Natts_pg_description] = {0};
		bool		replaces[Natts_pg_description] = {0};

		values[Anum_pg_description_objoid - 1] = ObjectIdGetDatum(newIndexId);
		replaces[Anum_pg_description_objoid - 1] = true;

		ScanKeyInit(&skey[0],
					Anum_pg_description_objoid,
					BTEqualStrategyNumber, F_OIDEQ,
					ObjectIdGetDatum(oldIndexId));
		ScanKeyInit(&skey[1],
					Anum_pg_description_classoid,
					BTEqualStrategyNumber, F_OIDEQ,
					ObjectIdGetDatum(RelationRelationId));
		ScanKeyInit(&skey[2],
					Anum_pg_description_objsubid,
					BTEqualStrategyNumber, F_INT4EQ,
					Int32GetDatum(0));

		description = table_open(DescriptionRelationId, RowExclusiveLock);

		sd = systable_beginscan(description, DescriptionObjIndexId, true,
								NULL, 3, skey);

		while ((tuple = systable_getnext(sd)) != NULL)
		{
			tuple = heap_modify_tuple(tuple, RelationGetDescr(description),
									  values, nulls, replaces);
			CatalogTupleUpdate(description, &tuple->t_self, tuple);

			break;				/* Assume there can be only one match */
		}

		systable_endscan(sd);
		table_close(description, NoLock);
	}

	/*
	 * Swap inheritance relationship with parent index
	 */
	if (get_rel_relispartition(oldIndexId))
	{
		List	   *ancestors = get_partition_ancestors(oldIndexId);
		Oid			parentIndexRelid = linitial_oid(ancestors);

		DeleteInheritsTuple(oldIndexId, parentIndexRelid, false, NULL);
		StoreSingleInheritance(newIndexId, parentIndexRelid, 1);

		list_free(ancestors);
	}

	/*
	 * Swap all dependencies of and on the old index to the new one, and
	 * vice-versa.  Note that a call to CommandCounterIncrement() would cause
	 * duplicate entries in pg_depend, so this should not be done.
	 */
	changeDependenciesOf(RelationRelationId, newIndexId, oldIndexId);
	changeDependenciesOn(RelationRelationId, newIndexId, oldIndexId);

	changeDependenciesOf(RelationRelationId, oldIndexId, newIndexId);
	changeDependenciesOn(RelationRelationId, oldIndexId, newIndexId);

	/* copy over statistics from old to new index */
	pgstat_copy_relation_stats(newClassRel, oldClassRel);

	/* Copy data of pg_statistic from the old index to the new one */
	CopyStatistics(oldIndexId, newIndexId);

	/* Close relations */
	table_close(pg_class, RowExclusiveLock);
	table_close(pg_index, RowExclusiveLock);
	table_close(pg_constraint, RowExclusiveLock);
	table_close(pg_trigger, RowExclusiveLock);

	/* The lock taken previously is not released until the end of transaction */
	relation_close(oldClassRel, NoLock);
	relation_close(newClassRel, NoLock);
}

/*
 * index_concurrently_set_dead
 *
 * Perform the last invalidation stage of DROP INDEX CONCURRENTLY or REINDEX
 * CONCURRENTLY before actually dropping the index.  After calling this
 * function, the index is seen by all the backends as dead.  Low-level locks
 * taken here are kept until the end of the transaction calling this function.
 */
void
index_concurrently_set_dead(Oid heapId, Oid indexId)
{
	Relation	userHeapRelation;
	Relation	userIndexRelation;

	/*
	 * No more predicate locks will be acquired on this index, and we're about
	 * to stop doing inserts into the index which could show conflicts with
	 * existing predicate locks, so now is the time to move them to the heap
	 * relation.
	 */
	userHeapRelation = table_open(heapId, ShareUpdateExclusiveLock);
	userIndexRelation = index_open(indexId, ShareUpdateExclusiveLock);
	TransferPredicateLocksToHeapRelation(userIndexRelation);

	/*
	 * Now we are sure that nobody uses the index for queries; they just might
	 * have it open for updating it.  So now we can unset indisready and
	 * indislive, then wait till nobody could be using it at all anymore.
	 */
	index_set_state_flags(indexId, INDEX_DROP_SET_DEAD);

	/*
	 * Invalidate the relcache for the table, so that after this commit all
	 * sessions will refresh the table's index list.  Forgetting just the
	 * index's relcache entry is not enough.
	 */
	CacheInvalidateRelcache(userHeapRelation);

	/*
	 * Close the relations again, though still holding session lock.
	 */
	table_close(userHeapRelation, NoLock);
	index_close(userIndexRelation, NoLock);
}

/*
 * index_constraint_create
 *
 * Set up a constraint associated with an index.  Return the new constraint's
 * address.
 *
 * heapRelation: table owning the index (must be suitably locked by caller)
 * indexRelationId: OID of the index
 * parentConstraintId: if constraint is on a partition, the OID of the
 *		constraint in the parent.
 * indexInfo: same info executor uses to insert into the index
 * constraintName: what it say (generally, should match name of index)
 * constraintType: one of CONSTRAINT_PRIMARY, CONSTRAINT_UNIQUE, or
 *		CONSTRAINT_EXCLUSION
 * flags: bitmask that can include any combination of these bits:
 *		INDEX_CONSTR_CREATE_MARK_AS_PRIMARY: index is a PRIMARY KEY
 *		INDEX_CONSTR_CREATE_DEFERRABLE: constraint is DEFERRABLE
 *		INDEX_CONSTR_CREATE_INIT_DEFERRED: constraint is INITIALLY DEFERRED
 *		INDEX_CONSTR_CREATE_UPDATE_INDEX: update the pg_index row
 *		INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS: remove existing dependencies
 *			of index on table's columns
 *		INDEX_CONSTR_CREATE_WITHOUT_OVERLAPS: constraint uses WITHOUT OVERLAPS
 * allow_system_table_mods: allow table to be a system catalog
 * is_internal: index is constructed due to internal process
 */
ObjectAddress
index_constraint_create(Relation heapRelation,
						Oid indexRelationId,
						Oid parentConstraintId,
						const IndexInfo *indexInfo,
						const char *constraintName,
						char constraintType,
						uint16 constr_flags,
						bool allow_system_table_mods,
						bool is_internal)
{
	Oid			namespaceId = RelationGetNamespace(heapRelation);
	ObjectAddress myself,
				idxaddr;
	Oid			conOid;
	bool		deferrable;
	bool		initdeferred;
	bool		mark_as_primary;
	bool		islocal;
	bool		noinherit;
	bool		is_without_overlaps;
	int16		inhcount;

	deferrable = (constr_flags & INDEX_CONSTR_CREATE_DEFERRABLE) != 0;
	initdeferred = (constr_flags & INDEX_CONSTR_CREATE_INIT_DEFERRED) != 0;
	mark_as_primary = (constr_flags & INDEX_CONSTR_CREATE_MARK_AS_PRIMARY) != 0;
	is_without_overlaps = (constr_flags & INDEX_CONSTR_CREATE_WITHOUT_OVERLAPS) != 0;

	/* constraint creation support doesn't work while bootstrapping */
	Assert(!IsBootstrapProcessingMode());

	/* enforce system-table restriction */
	if (!allow_system_table_mods &&
		IsSystemRelation(heapRelation) &&
		IsNormalProcessingMode())
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("user-defined indexes on system catalog tables are not supported")));

	/* primary/unique constraints shouldn't have any expressions */
	if (indexInfo->ii_Expressions &&
		constraintType != CONSTRAINT_EXCLUSION)
		elog(ERROR, "constraints cannot have index expressions");

	/*
	 * If we're manufacturing a constraint for a pre-existing index, we need
	 * to get rid of the existing auto dependencies for the index (the ones
	 * that index_create() would have made instead of calling this function).
	 *
	 * Note: this code would not necessarily do the right thing if the index
	 * has any expressions or predicate, but we'd never be turning such an
	 * index into a UNIQUE or PRIMARY KEY constraint.
	 */
	if (constr_flags & INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS)
		deleteDependencyRecordsForClass(RelationRelationId, indexRelationId,
										RelationRelationId, DEPENDENCY_AUTO);

	if (OidIsValid(parentConstraintId))
	{
		islocal = false;
		inhcount = 1;
		noinherit = false;
	}
	else
	{
		islocal = true;
		inhcount = 0;
		noinherit = true;
	}

	/*
	 * Construct a pg_constraint entry.
	 */
	conOid = CreateConstraintEntry(constraintName,
								   namespaceId,
								   constraintType,
								   deferrable,
								   initdeferred,
								   true,	/* Is Enforced */
								   true,
								   parentConstraintId,
								   RelationGetRelid(heapRelation),
								   indexInfo->ii_IndexAttrNumbers,
								   indexInfo->ii_NumIndexKeyAttrs,
								   indexInfo->ii_NumIndexAttrs,
								   InvalidOid,	/* no domain */
								   indexRelationId, /* index OID */
								   InvalidOid,	/* no foreign key */
								   NULL,
								   NULL,
								   NULL,
								   NULL,
								   0,
								   ' ',
								   ' ',
								   NULL,
								   0,
								   ' ',
								   indexInfo->ii_ExclusionOps,
								   NULL,	/* no check constraint */
								   NULL,
								   islocal,
								   inhcount,
								   noinherit,
								   is_without_overlaps,
								   is_internal);

	/*
	 * Register the index as internally dependent on the constraint.
	 *
	 * Note that the constraint has a dependency on the table, so we don't
	 * need (or want) any direct dependency from the index to the table.
	 */
	ObjectAddressSet(myself, ConstraintRelationId, conOid);
	ObjectAddressSet(idxaddr, RelationRelationId, indexRelationId);
	recordDependencyOn(&idxaddr, &myself, DEPENDENCY_INTERNAL);

	/*
	 * Also, if this is a constraint on a partition, give it partition-type
	 * dependencies on the parent constraint as well as the table.
	 */
	if (OidIsValid(parentConstraintId))
	{
		ObjectAddress referenced;

		ObjectAddressSet(referenced, ConstraintRelationId, parentConstraintId);
		recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI);
		ObjectAddressSet(referenced, RelationRelationId,
						 RelationGetRelid(heapRelation));
		recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC);
	}

	/*
	 * If the constraint is deferrable, create the deferred uniqueness
	 * checking trigger.  (The trigger will be given an internal dependency on
	 * the constraint by CreateTrigger.)
	 */
	if (deferrable)
	{
		CreateTrigStmt *trigger = makeNode(CreateTrigStmt);

		trigger->replace = false;
		trigger->isconstraint = true;
		trigger->trigname = (constraintType == CONSTRAINT_PRIMARY) ?
			"PK_ConstraintTrigger" :
			"Unique_ConstraintTrigger";
		trigger->relation = NULL;
		trigger->funcname = SystemFuncName("unique_key_recheck");
		trigger->args = NIL;
		trigger->row = true;
		trigger->timing = TRIGGER_TYPE_AFTER;
		trigger->events = TRIGGER_TYPE_INSERT | TRIGGER_TYPE_UPDATE;
		trigger->columns = NIL;
		trigger->whenClause = NULL;
		trigger->transitionRels = NIL;
		trigger->deferrable = true;
		trigger->initdeferred = initdeferred;
		trigger->constrrel = NULL;

		(void) CreateTrigger(trigger, NULL, RelationGetRelid(heapRelation),
							 InvalidOid, conOid, indexRelationId, InvalidOid,
							 InvalidOid, NULL, true, false);
	}

	/*
	 * If needed, mark the index as primary and/or deferred in pg_index.
	 *
	 * Note: When making an existing index into a constraint, caller must have
	 * a table lock that prevents concurrent table updates; otherwise, there
	 * is a risk that concurrent readers of the table will miss seeing this
	 * index at all.
	 */
	if ((constr_flags & INDEX_CONSTR_CREATE_UPDATE_INDEX) &&
		(mark_as_primary || deferrable))
	{
		Relation	pg_index;
		HeapTuple	indexTuple;
		Form_pg_index indexForm;
		bool		dirty = false;
		bool		marked_as_primary = false;

		pg_index = table_open(IndexRelationId, RowExclusiveLock);

		indexTuple = SearchSysCacheCopy1(INDEXRELID,
										 ObjectIdGetDatum(indexRelationId));
		if (!HeapTupleIsValid(indexTuple))
			elog(ERROR, "cache lookup failed for index %u", indexRelationId);
		indexForm = (Form_pg_index) GETSTRUCT(indexTuple);

		if (mark_as_primary && !indexForm->indisprimary)
		{
			indexForm->indisprimary = true;
			dirty = true;
			marked_as_primary = true;
		}

		if (deferrable && indexForm->indimmediate)
		{
			indexForm->indimmediate = false;
			dirty = true;
		}

		if (dirty)
		{
			CatalogTupleUpdate(pg_index, &indexTuple->t_self, indexTuple);

			/*
			 * When we mark an existing index as primary, force a relcache
			 * flush on its parent table, so that all sessions will become
			 * aware that the table now has a primary key.  This is important
			 * because it affects some replication behaviors.
			 */
			if (marked_as_primary)
				CacheInvalidateRelcache(heapRelation);

			InvokeObjectPostAlterHookArg(IndexRelationId, indexRelationId, 0,
										 InvalidOid, is_internal);
		}

		heap_freetuple(indexTuple);
		table_close(pg_index, RowExclusiveLock);
	}

	return myself;
}

/*
 *		index_drop
 *
 * NOTE: this routine should now only be called through performDeletion(),
 * else associated dependencies won't be cleaned up.
 *
 * If concurrent is true, do a DROP INDEX CONCURRENTLY.  If concurrent is
 * false but concurrent_lock_mode is true, then do a normal DROP INDEX but
 * take a lock for CONCURRENTLY processing.  That is used as part of REINDEX
 * CONCURRENTLY.
 */
void
index_drop(Oid indexId, bool concurrent, bool concurrent_lock_mode)
{
	Oid			heapId;
	Relation	userHeapRelation;
	Relation	userIndexRelation;
	Relation	indexRelation;
	HeapTuple	tuple;
	bool		hasexprs;
	LockRelId	heaprelid,
				indexrelid;
	LOCKTAG		heaplocktag;
	LOCKMODE	lockmode;

	/*
	 * A temporary relation uses a non-concurrent DROP.  Other backends can't
	 * access a temporary relation, so there's no harm in grabbing a stronger
	 * lock (see comments in RemoveRelations), and a non-concurrent DROP is
	 * more efficient.
	 */
	Assert(get_rel_persistence(indexId) != RELPERSISTENCE_TEMP ||
		   (!concurrent && !concurrent_lock_mode));

	/*
	 * To drop an index safely, we must grab exclusive lock on its parent
	 * table.  Exclusive lock on the index alone is insufficient because
	 * another backend might be about to execute a query on the parent table.
	 * If it relies on a previously cached list of index OIDs, then it could
	 * attempt to access the just-dropped index.  We must therefore take a
	 * table lock strong enough to prevent all queries on the table from
	 * proceeding until we commit and send out a shared-cache-inval notice
	 * that will make them update their index lists.
	 *
	 * In the concurrent case we avoid this requirement by disabling index use
	 * in multiple steps and waiting out any transactions that might be using
	 * the index, so we don't need exclusive lock on the parent table. Instead
	 * we take ShareUpdateExclusiveLock, to ensure that two sessions aren't
	 * doing CREATE/DROP INDEX CONCURRENTLY on the same index.  (We will get
	 * AccessExclusiveLock on the index below, once we're sure nobody else is
	 * using it.)
	 */
	heapId = IndexGetRelation(indexId, false);
	lockmode = (concurrent || concurrent_lock_mode) ? ShareUpdateExclusiveLock : AccessExclusiveLock;
	userHeapRelation = table_open(heapId, lockmode);
	userIndexRelation = index_open(indexId, lockmode);

	/*
	 * We might still have open queries using it in our own session, which the
	 * above locking won't prevent, so test explicitly.
	 */
	CheckTableNotInUse(userIndexRelation, "DROP INDEX");

	/*
	 * Drop Index Concurrently is more or less the reverse process of Create
	 * Index Concurrently.
	 *
	 * First we unset indisvalid so queries starting afterwards don't use the
	 * index to answer queries anymore.  We have to keep indisready = true so
	 * transactions that are still scanning the index can continue to see
	 * valid index contents.  For instance, if they are using READ COMMITTED
	 * mode, and another transaction makes changes and commits, they need to
	 * see those new tuples in the index.
	 *
	 * After all transactions that could possibly have used the index for
	 * queries end, we can unset indisready and indislive, then wait till
	 * nobody could be touching it anymore.  (Note: we need indislive because
	 * this state must be distinct from the initial state during CREATE INDEX
	 * CONCURRENTLY, which has indislive true while indisready and indisvalid
	 * are false.  That's because in that state, transactions must examine the
	 * index for HOT-safety decisions, while in this state we don't want them
	 * to open it at all.)
	 *
	 * Since all predicate locks on the index are about to be made invalid, we
	 * must promote them to predicate locks on the heap.  In the
	 * non-concurrent case we can just do that now.  In the concurrent case
	 * it's a bit trickier.  The predicate locks must be moved when there are
	 * no index scans in progress on the index and no more can subsequently
	 * start, so that no new predicate locks can be made on the index.  Also,
	 * they must be moved before heap inserts stop maintaining the index, else
	 * the conflict with the predicate lock on the index gap could be missed
	 * before the lock on the heap relation is in place to detect a conflict
	 * based on the heap tuple insert.
	 */
	if (concurrent)
	{
		/*
		 * We must commit our transaction in order to make the first pg_index
		 * state update visible to other sessions.  If the DROP machinery has
		 * already performed any other actions (removal of other objects,
		 * pg_depend entries, etc), the commit would make those actions
		 * permanent, which would leave us with inconsistent catalog state if
		 * we fail partway through the following sequence.  Since DROP INDEX
		 * CONCURRENTLY is restricted to dropping just one index that has no
		 * dependencies, we should get here before anything's been done ---
		 * but let's check that to be sure.  We can verify that the current
		 * transaction has not executed any transactional updates by checking
		 * that no XID has been assigned.
		 */
		if (GetTopTransactionIdIfAny() != InvalidTransactionId)
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("DROP INDEX CONCURRENTLY must be first action in transaction")));

		/*
		 * Mark index invalid by updating its pg_index entry
		 */
		index_set_state_flags(indexId, INDEX_DROP_CLEAR_VALID);

		/*
		 * Invalidate the relcache for the table, so that after this commit
		 * all sessions will refresh any cached plans that might reference the
		 * index.
		 */
		CacheInvalidateRelcache(userHeapRelation);

		/* save lockrelid and locktag for below, then close but keep locks */
		heaprelid = userHeapRelation->rd_lockInfo.lockRelId;
		SET_LOCKTAG_RELATION(heaplocktag, heaprelid.dbId, heaprelid.relId);
		indexrelid = userIndexRelation->rd_lockInfo.lockRelId;

		table_close(userHeapRelation, NoLock);
		index_close(userIndexRelation, NoLock);

		/*
		 * We must commit our current transaction so that the indisvalid
		 * update becomes visible to other transactions; then start another.
		 * Note that any previously-built data structures are lost in the
		 * commit.  The only data we keep past here are the relation IDs.
		 *
		 * Before committing, get a session-level lock on the table, to ensure
		 * that neither it nor the index can be dropped before we finish. This
		 * cannot block, even if someone else is waiting for access, because
		 * we already have the same lock within our transaction.
		 */
		LockRelationIdForSession(&heaprelid, ShareUpdateExclusiveLock);
		LockRelationIdForSession(&indexrelid, ShareUpdateExclusiveLock);

		PopActiveSnapshot();
		CommitTransactionCommand();
		StartTransactionCommand();

		/*
		 * Now we must wait until no running transaction could be using the
		 * index for a query.  Use AccessExclusiveLock here to check for
		 * running transactions that hold locks of any kind on the table. Note
		 * we do not need to worry about xacts that open the table for reading
		 * after this point; they will see the index as invalid when they open
		 * the relation.
		 *
		 * Note: the reason we use actual lock acquisition here, rather than
		 * just checking the ProcArray and sleeping, is that deadlock is
		 * possible if one of the transactions in question is blocked trying
		 * to acquire an exclusive lock on our table.  The lock code will
		 * detect deadlock and error out properly.
		 *
		 * Note: we report progress through WaitForLockers() unconditionally
		 * here, even though it will only be used when we're called by REINDEX
		 * CONCURRENTLY and not when called by DROP INDEX CONCURRENTLY.
		 */
		WaitForLockers(heaplocktag, AccessExclusiveLock, true);

		/*
		 * Updating pg_index might involve TOAST table access, so ensure we
		 * have a valid snapshot.
		 */
		PushActiveSnapshot(GetTransactionSnapshot());

		/* Finish invalidation of index and mark it as dead */
		index_concurrently_set_dead(heapId, indexId);

		PopActiveSnapshot();

		/*
		 * Again, commit the transaction to make the pg_index update visible
		 * to other sessions.
		 */
		CommitTransactionCommand();
		StartTransactionCommand();

		/*
		 * Wait till every transaction that saw the old index state has
		 * finished.  See above about progress reporting.
		 */
		WaitForLockers(heaplocktag, AccessExclusiveLock, true);

		/*
		 * Re-open relations to allow us to complete our actions.
		 *
		 * At this point, nothing should be accessing the index, but lets
		 * leave nothing to chance and grab AccessExclusiveLock on the index
		 * before the physical deletion.
		 */
		userHeapRelation = table_open(heapId, ShareUpdateExclusiveLock);
		userIndexRelation = index_open(indexId, AccessExclusiveLock);
	}
	else
	{
		/* Not concurrent, so just transfer predicate locks and we're good */
		TransferPredicateLocksToHeapRelation(userIndexRelation);
	}

	/*
	 * Schedule physical removal of the files (if any)
	 */
	if (RELKIND_HAS_STORAGE(userIndexRelation->rd_rel->relkind))
		RelationDropStorage(userIndexRelation);

	/* ensure that stats are dropped if transaction commits */
	pgstat_drop_relation(userIndexRelation);

	/*
	 * Close and flush the index's relcache entry, to ensure relcache doesn't
	 * try to rebuild it while we're deleting catalog entries. We keep the
	 * lock though.
	 */
	index_close(userIndexRelation, NoLock);

	RelationForgetRelation(indexId);

	/*
	 * Updating pg_index might involve TOAST table access, so ensure we have a
	 * valid snapshot.
	 */
	PushActiveSnapshot(GetTransactionSnapshot());

	/*
	 * fix INDEX relation, and check for expressional index
	 */
	indexRelation = table_open(IndexRelationId, RowExclusiveLock);

	tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexId));
	if (!HeapTupleIsValid(tuple))
		elog(ERROR, "cache lookup failed for index %u", indexId);

	hasexprs = !heap_attisnull(tuple, Anum_pg_index_indexprs,
							   RelationGetDescr(indexRelation));

	CatalogTupleDelete(indexRelation, &tuple->t_self);

	ReleaseSysCache(tuple);
	table_close(indexRelation, RowExclusiveLock);

	PopActiveSnapshot();

	/*
	 * if it has any expression columns, we might have stored statistics about
	 * them.
	 */
	if (hasexprs)
		RemoveStatistics(indexId, 0);

	/*
	 * fix ATTRIBUTE relation
	 */
	DeleteAttributeTuples(indexId);

	/*
	 * fix RELATION relation
	 */
	DeleteRelationTuple(indexId);

	/*
	 * fix INHERITS relation
	 */
	DeleteInheritsTuple(indexId, InvalidOid, false, NULL);

	/*
	 * We are presently too lazy to attempt to compute the new correct value
	 * of relhasindex (the next VACUUM will fix it if necessary). So there is
	 * no need to update the pg_class tuple for the owning relation. But we
	 * must send out a shared-cache-inval notice on the owning relation to
	 * ensure other backends update their relcache lists of indexes.  (In the
	 * concurrent case, this is redundant but harmless.)
	 */
	CacheInvalidateRelcache(userHeapRelation);

	/*
	 * Close owning rel, but keep lock
	 */
	table_close(userHeapRelation, NoLock);

	/*
	 * Release the session locks before we go.
	 */
	if (concurrent)
	{
		UnlockRelationIdForSession(&heaprelid, ShareUpdateExclusiveLock);
		UnlockRelationIdForSession(&indexrelid, ShareUpdateExclusiveLock);
	}
}

/* ----------------------------------------------------------------
 *						index_build support
 * ----------------------------------------------------------------
 */

/* ----------------
 *		BuildIndexInfo
 *			Construct an IndexInfo record for an open index
 *
 * IndexInfo stores the information about the index that's needed by
 * FormIndexDatum, which is used for both index_build() and later insertion
 * of individual index tuples.  Normally we build an IndexInfo for an index
 * just once per command, and then use it for (potentially) many tuples.
 * ----------------
 */
IndexInfo *
BuildIndexInfo(Relation index)
{
	IndexInfo  *ii;
	Form_pg_index indexStruct = index->rd_index;
	int			i;
	int			numAtts;

	/* check the number of keys, and copy attr numbers into the IndexInfo */
	numAtts = indexStruct->indnatts;
	if (numAtts < 1 || numAtts > INDEX_MAX_KEYS)
		elog(ERROR, "invalid indnatts %d for index %u",
			 numAtts, RelationGetRelid(index));

	/*
	 * Create the node, fetching any expressions needed for expressional
	 * indexes and index predicate if any.
	 */
	ii = makeIndexInfo(indexStruct->indnatts,
					   indexStruct->indnkeyatts,
					   index->rd_rel->relam,
					   RelationGetIndexExpressions(index),
					   RelationGetIndexPredicate(index),
					   indexStruct->indisunique,
					   indexStruct->indnullsnotdistinct,
					   indexStruct->indisready,
					   false,
					   index->rd_indam->amsummarizing,
					   indexStruct->indisexclusion && indexStruct->indisunique);
	ii->ii_ExpressionsExpand = (List *)RelationGetIndexExpressionsExpand(index);
	ii->ii_PredicateExpand = (List *)RelationGetIndexPredicateExpand(index);

	/* fill in attribute numbers */
	for (i = 0; i < numAtts; i++)
		ii->ii_IndexAttrNumbers[i] = indexStruct->indkey.values[i];

	/* fetch exclusion constraint info if any */
	if (indexStruct->indisexclusion)
	{
		RelationGetExclusionInfo(index,
								 &ii->ii_ExclusionOps,
								 &ii->ii_ExclusionProcs,
								 &ii->ii_ExclusionStrats);
	}

	return ii;
}

/* ----------------
 *		BuildDummyIndexInfo
 *			Construct a dummy IndexInfo record for an open index
 *
 * This differs from the real BuildIndexInfo in that it will never run any
 * user-defined code that might exist in index expressions or predicates.
 * Instead of the real index expressions, we return null constants that have
 * the right types/typmods/collations.  Predicates and exclusion clauses are
 * just ignored.  This is sufficient for the purpose of truncating an index,
 * since we will not need to actually evaluate the expressions or predicates;
 * the only thing that's likely to be done with the data is construction of
 * a tupdesc describing the index's rowtype.
 * ----------------
 */
IndexInfo *
BuildDummyIndexInfo(Relation index)
{
	IndexInfo  *ii;
	Form_pg_index indexStruct = index->rd_index;
	int			i;
	int			numAtts;

	/* check the number of keys, and copy attr numbers into the IndexInfo */
	numAtts = indexStruct->indnatts;
	if (numAtts < 1 || numAtts > INDEX_MAX_KEYS)
		elog(ERROR, "invalid indnatts %d for index %u",
			 numAtts, RelationGetRelid(index));

	/*
	 * Create the node, using dummy index expressions, and pretending there is
	 * no predicate.
	 */
	ii = makeIndexInfo(indexStruct->indnatts,
					   indexStruct->indnkeyatts,
					   index->rd_rel->relam,
					   RelationGetDummyIndexExpressions(index),
					   NIL,
					   indexStruct->indisunique,
					   indexStruct->indnullsnotdistinct,
					   indexStruct->indisready,
					   false,
					   index->rd_indam->amsummarizing,
					   indexStruct->indisexclusion && indexStruct->indisunique);

	/* fill in attribute numbers */
	for (i = 0; i < numAtts; i++)
		ii->ii_IndexAttrNumbers[i] = indexStruct->indkey.values[i];

	/* We ignore the exclusion constraint if any */

	return ii;
}

/*
 * CompareIndexInfo
 *		Return whether the properties of two indexes (in different tables)
 *		indicate that they have the "same" definitions.
 *
 * Note: passing collations and opfamilies separately is a kludge.  Adding
 * them to IndexInfo may result in better coding here and elsewhere.
 *
 * Use build_attrmap_by_name(index2, index1) to build the attmap.
 */
bool
CompareIndexInfo(const IndexInfo *info1, const IndexInfo *info2,
				 const Oid *collations1, const Oid *collations2,
				 const Oid *opfamilies1, const Oid *opfamilies2,
				 const AttrMap *attmap)
{
	int			i;

	if (info1->ii_Unique != info2->ii_Unique)
		return false;

	if (info1->ii_NullsNotDistinct != info2->ii_NullsNotDistinct)
		return false;

	/* indexes are only equivalent if they have the same access method */
	if (info1->ii_Am != info2->ii_Am)
		return false;

	/* and same number of attributes */
	if (info1->ii_NumIndexAttrs != info2->ii_NumIndexAttrs)
		return false;

	/* and same number of key attributes */
	if (info1->ii_NumIndexKeyAttrs != info2->ii_NumIndexKeyAttrs)
		return false;

	/*
	 * and columns match through the attribute map (actual attribute numbers
	 * might differ!)  Note that this checks that index columns that are
	 * expressions appear in the same positions.  We will next compare the
	 * expressions themselves.
	 */
	for (i = 0; i < info1->ii_NumIndexAttrs; i++)
	{
		if (attmap->maplen < info2->ii_IndexAttrNumbers[i])
			elog(ERROR, "incorrect attribute map");

		/* ignore expressions for now (but check their collation/opfamily) */
		if (!(info1->ii_IndexAttrNumbers[i] == InvalidAttrNumber &&
			  info2->ii_IndexAttrNumbers[i] == InvalidAttrNumber))
		{
			/* fail if just one index has an expression in this column */
			if (info1->ii_IndexAttrNumbers[i] == InvalidAttrNumber ||
				info2->ii_IndexAttrNumbers[i] == InvalidAttrNumber)
				return false;

			/* both are columns, so check for match after mapping */
			if (attmap->attnums[info2->ii_IndexAttrNumbers[i] - 1] !=
				info1->ii_IndexAttrNumbers[i])
				return false;
		}

		/* collation and opfamily are not valid for included columns */
		if (i >= info1->ii_NumIndexKeyAttrs)
			continue;

		if (collations1[i] != collations2[i])
			return false;
		if (opfamilies1[i] != opfamilies2[i])
			return false;
	}

	/*
	 * For expression indexes: either both are expression indexes, or neither
	 * is; if they are, make sure the expressions match.
	 */
	if ((info1->ii_Expressions != NIL) != (info2->ii_Expressions != NIL))
		return false;
	if (info1->ii_Expressions != NIL)
	{
		bool		found_whole_row;
		Node	   *mapped;

		mapped = map_variable_attnos((Node *) info2->ii_Expressions,
									 1, 0, attmap,
									 InvalidOid, &found_whole_row);
		if (found_whole_row)
		{
			/*
			 * we could throw an error here, but seems out of scope for this
			 * routine.
			 */
			return false;
		}

		if (!equal(info1->ii_Expressions, mapped))
			return false;
	}

	/* Partial index predicates must be identical, if they exist */
	if ((info1->ii_Predicate == NULL) != (info2->ii_Predicate == NULL))
		return false;
	if (info1->ii_Predicate != NULL)
	{
		bool		found_whole_row;
		Node	   *mapped;

		mapped = map_variable_attnos((Node *) info2->ii_Predicate,
									 1, 0, attmap,
									 InvalidOid, &found_whole_row);
		if (found_whole_row)
		{
			/*
			 * we could throw an error here, but seems out of scope for this
			 * routine.
			 */
			return false;
		}
		if (!equal(info1->ii_Predicate, mapped))
			return false;
	}

	/* No support currently for comparing exclusion indexes. */
	if (info1->ii_ExclusionOps != NULL || info2->ii_ExclusionOps != NULL)
		return false;

	return true;
}

/* ----------------
 *		BuildSpeculativeIndexInfo
 *			Add extra state to IndexInfo record
 *
 * For unique indexes, we usually don't want to add info to the IndexInfo for
 * checking uniqueness, since the B-Tree AM handles that directly.  However, in
 * the case of speculative insertion and conflict detection in logical
 * replication, additional support is required.
 *
 * Do this processing here rather than in BuildIndexInfo() to not incur the
 * overhead in the common non-speculative cases.
 * ----------------
 */
void
BuildSpeculativeIndexInfo(Relation index, IndexInfo *ii)
{
	int			indnkeyatts;
	int			i;

	indnkeyatts = IndexRelationGetNumberOfKeyAttributes(index);

	/*
	 * fetch info for checking unique indexes
	 */
	Assert(ii->ii_Unique);

	ii->ii_UniqueOps = palloc_array(Oid, indnkeyatts);
	ii->ii_UniqueProcs = palloc_array(Oid, indnkeyatts);
	ii->ii_UniqueStrats = palloc_array(uint16, indnkeyatts);

	/*
	 * We have to look up the operator's strategy number.  This provides a
	 * cross-check that the operator does match the index.
	 */
	/* We need the func OIDs and strategy numbers too */
	for (i = 0; i < indnkeyatts; i++)
	{
		ii->ii_UniqueStrats[i] =
			IndexAmTranslateCompareType(COMPARE_EQ,
										index->rd_rel->relam,
										index->rd_opfamily[i],
										false);
		ii->ii_UniqueOps[i] =
			get_opfamily_member(index->rd_opfamily[i],
								index->rd_opcintype[i],
								index->rd_opcintype[i],
								ii->ii_UniqueStrats[i]);
		if (!OidIsValid(ii->ii_UniqueOps[i]))
			elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
				 ii->ii_UniqueStrats[i], index->rd_opcintype[i],
				 index->rd_opcintype[i], index->rd_opfamily[i]);
		ii->ii_UniqueProcs[i] = get_opcode(ii->ii_UniqueOps[i]);
	}
}

/* ----------------
 *		FormIndexDatum
 *			Construct values[] and isnull[] arrays for a new index tuple.
 *
 *	indexInfo		Info about the index
 *	slot			Heap tuple for which we must prepare an index entry
 *	estate			executor state for evaluating any index expressions
 *	values			Array of index Datums (output area)
 *	isnull			Array of is-null indicators (output area)
 *
 * When there are no index expressions, estate may be NULL.  Otherwise it
 * must be supplied, *and* the ecxt_scantuple slot of its per-tuple expr
 * context must point to the heap tuple passed in.
 *
 * Notice we don't actually call index_form_tuple() here; we just prepare
 * its input arrays values[] and isnull[].  This is because the index AM
 * may wish to alter the data before storage.
 * ----------------
 */
void
FormIndexDatum(IndexInfo *indexInfo,
			   TupleTableSlot *slot,
			   EState *estate,
			   Datum *values,
			   bool *isnull)
{
	ListCell   *indexpr_item;
	int			i;

	if (indexInfo->ii_Expressions != NIL &&
		indexInfo->ii_ExpressionsState == NIL)
	{
		/* First time through, set up expression evaluation state */
		indexInfo->ii_ExpressionsState =
			ExecPrepareExprList(indexInfo->ii_Expressions, estate);
		indexInfo->ii_ExpressionsExpandState =
			ExecPrepareExprList(indexInfo->ii_ExpressionsExpand, estate);
		/* Check caller has set up context correctly */
		Assert(GetPerTupleExprContext(estate)->ecxt_scantuple == slot);
	}
	indexpr_item = list_head(indexInfo->ii_ExpressionsExpandState);

	for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
	{
		int			keycol = indexInfo->ii_IndexAttrNumbers[i];
		Datum		iDatum;
		bool		isNull;

		if (keycol < 0)
			iDatum = slot_getsysattr(slot, keycol, &isNull);
		else if (keycol != 0)
		{
			TupleDesc			tupdesc = slot->tts_tupleDescriptor;
			Form_pg_attribute 	att = TupleDescAttr(tupdesc, keycol - 1);

			if (att->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL)
			{
				/*
				 * Plain index column; get the value we need directly from the
				 * heap tuple.
				*/
				iDatum = slot_getattr(slot, keycol, &isNull);
			}
			else
			{
				TupleConstr 	*constr = tupdesc->constr;

				for (int j = 0; j < constr->num_defval; j++)
				{
					AttrDefault 	*defval;
					Expr	   		*expr = NULL;
					ExprState  		*exprstate;

					defval = &constr->defval[j];
					if (defval->adnum == keycol)
					{
						expr = stringToNode(defval->adbin);
						exprstate = ExecPrepareExpr(expr, estate);
						iDatum = ExecEvalExprSwitchContext(exprstate,
														   GetPerTupleExprContext(estate),
														   &isNull);
						break;
					}
					Assert(j + 1 < constr->num_defval);
				}
			}
		}
		else
		{
			/*
			 * Index expression --- need to evaluate it.
			 */
			if (indexpr_item == NULL)
				elog(ERROR, "wrong number of index expressions");
			iDatum = ExecEvalExprSwitchContext((ExprState *) lfirst(indexpr_item),
											   GetPerTupleExprContext(estate),
											   &isNull);
			indexpr_item = lnext(indexInfo->ii_ExpressionsExpandState, indexpr_item);
		}
		values[i] = iDatum;
		isnull[i] = isNull;
	}

	if (indexpr_item != NULL)
		elog(ERROR, "wrong number of index expressions");
}


/*
 * index_update_stats --- update pg_class entry after CREATE INDEX or REINDEX
 *
 * This routine updates the pg_class row of either an index or its parent
 * relation after CREATE INDEX or REINDEX.  Its rather bizarre API is designed
 * to ensure we can do all the necessary work in just one update.
 *
 * hasindex: set relhasindex to this value
 * reltuples: if >= 0, set reltuples to this value; else no change
 *
 * If reltuples >= 0, relpages, relallvisible, and relallfrozen are also
 * updated (using RelationGetNumberOfBlocks() and visibilitymap_count()).
 *
 * NOTE: an important side-effect of this operation is that an SI invalidation
 * message is sent out to all backends --- including me --- causing relcache
 * entries to be flushed or updated with the new data.  This must happen even
 * if we find that no change is needed in the pg_class row.  When updating
 * a heap entry, this ensures that other backends find out about the new
 * index.  When updating an index, it's important because some index AMs
 * expect a relcache flush to occur after REINDEX.
 */
static void
index_update_stats(Relation rel,
				   bool hasindex,
				   double reltuples)
{
	bool		update_stats;
	BlockNumber relpages = 0;	/* keep compiler quiet */
	BlockNumber relallvisible = 0;
	BlockNumber relallfrozen = 0;
	Oid			relid = RelationGetRelid(rel);
	Relation	pg_class;
	ScanKeyData key[1];
	HeapTuple	tuple;
	void	   *state;
	Form_pg_class rd_rel;
	bool		dirty;

	/*
	 * As a special hack, if we are dealing with an empty table and the
	 * existing reltuples is -1, we leave that alone.  This ensures that
	 * creating an index as part of CREATE TABLE doesn't cause the table to
	 * prematurely look like it's been vacuumed.  The rd_rel we modify may
	 * differ from rel->rd_rel due to e.g. commit of concurrent GRANT, but the
	 * commands that change reltuples take locks conflicting with ours.  (Even
	 * if a command changed reltuples under a weaker lock, this affects only
	 * statistics for an empty table.)
	 */
	if (reltuples == 0 && rel->rd_rel->reltuples < 0)
		reltuples = -1;

	/*
	 * Don't update statistics during binary upgrade, because the indexes are
	 * created before the data is moved into place.
	 */
	update_stats = reltuples >= 0 && !IsBinaryUpgrade;

	/*
	 * If autovacuum is off, user may not be expecting table relstats to
	 * change.  This can be important when restoring a dump that includes
	 * statistics, as the table statistics may be restored before the index is
	 * created, and we want to preserve the restored table statistics.
	 */
	if (rel->rd_rel->relkind == RELKIND_RELATION ||
		rel->rd_rel->relkind == RELKIND_TOASTVALUE ||
		rel->rd_rel->relkind == RELKIND_MATVIEW)
	{
		if (AutoVacuumingActive())
		{
			StdRdOptions *options = (StdRdOptions *) rel->rd_options;

			if (options != NULL && !options->autovacuum.enabled)
				update_stats = false;
		}
		else
			update_stats = false;
	}

	/*
	 * Finish I/O and visibility map buffer locks before
	 * systable_inplace_update_begin() locks the pg_class buffer.  The rd_rel
	 * we modify may differ from rel->rd_rel due to e.g. commit of concurrent
	 * GRANT, but no command changes a relkind from non-index to index.  (Even
	 * if one did, relallvisible doesn't break functionality.)
	 */
	if (update_stats)
	{
		relpages = RelationGetNumberOfBlocks(rel);

		if (rel->rd_rel->relkind != RELKIND_INDEX)
			visibilitymap_count(rel, &relallvisible, &relallfrozen);
	}

	/*
	 * We always update the pg_class row using a non-transactional,
	 * overwrite-in-place update.  There are several reasons for this:
	 *
	 * 1. In bootstrap mode, we have no choice --- UPDATE wouldn't work.
	 *
	 * 2. We could be reindexing pg_class itself, in which case we can't move
	 * its pg_class row because CatalogTupleInsert/CatalogTupleUpdate might
	 * not know about all the indexes yet (see reindex_relation).
	 *
	 * 3. Because we execute CREATE INDEX with just share lock on the parent
	 * rel (to allow concurrent index creations), an ordinary update could
	 * suffer a tuple-concurrently-updated failure against another CREATE
	 * INDEX committing at about the same time.  We can avoid that by having
	 * them both do nontransactional updates (we assume they will both be
	 * trying to change the pg_class row to the same thing, so it doesn't
	 * matter which goes first).
	 *
	 * It is safe to use a non-transactional update even though our
	 * transaction could still fail before committing.  Setting relhasindex
	 * true is safe even if there are no indexes (VACUUM will eventually fix
	 * it).  And of course the new relpages and reltuples counts are correct
	 * regardless.  However, we don't want to change relpages (or
	 * relallvisible) if the caller isn't providing an updated reltuples
	 * count, because that would bollix the reltuples/relpages ratio which is
	 * what's really important.
	 */

	pg_class = table_open(RelationRelationId, RowExclusiveLock);

	ScanKeyInit(&key[0],
				Anum_pg_class_oid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(relid));
	systable_inplace_update_begin(pg_class, ClassOidIndexId, true, NULL,
								  1, key, &tuple, &state);

	if (!HeapTupleIsValid(tuple))
		elog(ERROR, "could not find tuple for relation %u", relid);
	rd_rel = (Form_pg_class) GETSTRUCT(tuple);

	/* Should this be a more comprehensive test? */
	Assert(rd_rel->relkind != RELKIND_PARTITIONED_INDEX);

	/* Apply required updates, if any, to copied tuple */

	dirty = false;
	if (rd_rel->relhasindex != hasindex)
	{
		rd_rel->relhasindex = hasindex;
		dirty = true;
	}

	if (update_stats)
	{
		if (rd_rel->relpages != (int32) relpages)
		{
			rd_rel->relpages = (int32) relpages;
			dirty = true;
		}
		if (rd_rel->reltuples != (float4) reltuples)
		{
			rd_rel->reltuples = (float4) reltuples;
			dirty = true;
		}
		if (rd_rel->relallvisible != (int32) relallvisible)
		{
			rd_rel->relallvisible = (int32) relallvisible;
			dirty = true;
		}
		if (rd_rel->relallfrozen != (int32) relallfrozen)
		{
			rd_rel->relallfrozen = (int32) relallfrozen;
			dirty = true;
		}
	}

	/*
	 * If anything changed, write out the tuple
	 */
	if (dirty)
	{
		systable_inplace_update_finish(state, tuple);
		/* the above sends transactional and immediate cache inval messages */
	}
	else
	{
		systable_inplace_update_cancel(state);

		/*
		 * While we didn't change relhasindex, CREATE INDEX needs a
		 * transactional inval for when the new index's catalog rows become
		 * visible.  Other CREATE INDEX and REINDEX code happens to also queue
		 * this inval, but keep this in case rare callers rely on this part of
		 * our API contract.
		 */
		CacheInvalidateRelcacheByTuple(tuple);
	}

	heap_freetuple(tuple);

	table_close(pg_class, RowExclusiveLock);
}


/*
 * index_build - invoke access-method-specific index build procedure
 *
 * On entry, the index's catalog entries are valid, and its physical disk
 * file has been created but is empty.  We call the AM-specific build
 * procedure to fill in the index contents.  We then update the pg_class
 * entries of the index and heap relation as needed, using statistics
 * returned by ambuild as well as data passed by the caller.
 *
 * isreindex indicates we are recreating a previously-existing index.
 * parallel indicates if parallelism may be useful.
 * progress indicates if the backend should update its progress info.
 *
 * Note: before Postgres 8.2, the passed-in heap and index Relations
 * were automatically closed by this routine.  This is no longer the case.
 * The caller opened 'em, and the caller should close 'em.
 */
void
index_build(Relation heapRelation,
			Relation indexRelation,
			IndexInfo *indexInfo,
			bool isreindex,
			bool parallel,
			bool progress)
{
	IndexBuildResult *stats;
	Oid			save_userid;
	int			save_sec_context;
	int			save_nestlevel;

	/*
	 * sanity checks
	 */
	Assert(RelationIsValid(indexRelation));
	Assert(indexRelation->rd_indam);
	Assert(indexRelation->rd_indam->ambuild);
	Assert(indexRelation->rd_indam->ambuildempty);

	/*
	 * Determine worker process details for parallel CREATE INDEX.  Currently,
	 * only btree, GIN, and BRIN have support for parallel builds.
	 *
	 * Note that planner considers parallel safety for us.
	 */
	if (parallel && IsNormalProcessingMode() &&
		indexRelation->rd_indam->amcanbuildparallel)
		indexInfo->ii_ParallelWorkers =
			plan_create_index_workers(RelationGetRelid(heapRelation),
									  RelationGetRelid(indexRelation));

	if (indexInfo->ii_ParallelWorkers == 0)
		ereport(DEBUG1,
				(errmsg_internal("building index \"%s\" on table \"%s\" serially",
								 RelationGetRelationName(indexRelation),
								 RelationGetRelationName(heapRelation))));
	else
		ereport(DEBUG1,
				(errmsg_internal("building index \"%s\" on table \"%s\" with request for %d parallel workers",
								 RelationGetRelationName(indexRelation),
								 RelationGetRelationName(heapRelation),
								 indexInfo->ii_ParallelWorkers)));

	/*
	 * Switch to the table owner's userid, so that any index functions are run
	 * as that user.  Also lock down security-restricted operations and
	 * arrange to make GUC variable changes local to this command.
	 */
	GetUserIdAndSecContext(&save_userid, &save_sec_context);
	SetUserIdAndSecContext(heapRelation->rd_rel->relowner,
						   save_sec_context | SECURITY_RESTRICTED_OPERATION);
	save_nestlevel = NewGUCNestLevel();
	RestrictSearchPath();

	/* Set up initial progress report status */
	if (progress)
	{
		const int	progress_index[] = {
			PROGRESS_CREATEIDX_PHASE,
			PROGRESS_CREATEIDX_SUBPHASE,
			PROGRESS_CREATEIDX_TUPLES_DONE,
			PROGRESS_CREATEIDX_TUPLES_TOTAL,
			PROGRESS_SCAN_BLOCKS_DONE,
			PROGRESS_SCAN_BLOCKS_TOTAL
		};
		const int64 progress_vals[] = {
			PROGRESS_CREATEIDX_PHASE_BUILD,
			PROGRESS_CREATEIDX_SUBPHASE_INITIALIZE,
			0, 0, 0, 0
		};

		pgstat_progress_update_multi_param(6, progress_index, progress_vals);
	}

	/*
	 * Call the access method's build procedure
	 */
	stats = indexRelation->rd_indam->ambuild(heapRelation, indexRelation,
											 indexInfo);
	Assert(stats);

	/*
	 * If this is an unlogged index, we may need to write out an init fork for
	 * it -- but we must first check whether one already exists.  If, for
	 * example, an unlogged relation is truncated in the transaction that
	 * created it, or truncated twice in a subsequent transaction, the
	 * relfilenumber won't change, and nothing needs to be done here.
	 */
	if (indexRelation->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
		!smgrexists(RelationGetSmgr(indexRelation), INIT_FORKNUM))
	{
		smgrcreate(RelationGetSmgr(indexRelation), INIT_FORKNUM, false);
		log_smgrcreate(&indexRelation->rd_locator, INIT_FORKNUM);
		indexRelation->rd_indam->ambuildempty(indexRelation);
	}

	/*
	 * If we found any potentially broken HOT chains, mark the index as not
	 * being usable until the current transaction is below the event horizon.
	 * See src/backend/access/heap/README.HOT for discussion.  While it might
	 * become safe to use the index earlier based on actual cleanup activity
	 * and other active transactions, the test for that would be much more
	 * complex and would require some form of blocking, so keep it simple and
	 * fast by just using the current transaction.
	 *
	 * However, when reindexing an existing index, we should do nothing here.
	 * Any HOT chains that are broken with respect to the index must predate
	 * the index's original creation, so there is no need to change the
	 * index's usability horizon.  Moreover, we *must not* try to change the
	 * index's pg_index entry while reindexing pg_index itself, and this
	 * optimization nicely prevents that.  The more complex rules needed for a
	 * reindex are handled separately after this function returns.
	 *
	 * We also need not set indcheckxmin during a concurrent index build,
	 * because we won't set indisvalid true until all transactions that care
	 * about the broken HOT chains are gone.
	 *
	 * Therefore, this code path can only be taken during non-concurrent
	 * CREATE INDEX.  Thus the fact that heap_update will set the pg_index
	 * tuple's xmin doesn't matter, because that tuple was created in the
	 * current transaction anyway.  That also means we don't need to worry
	 * about any concurrent readers of the tuple; no other transaction can see
	 * it yet.
	 */
	if (indexInfo->ii_BrokenHotChain &&
		!isreindex &&
		!indexInfo->ii_Concurrent)
	{
		Oid			indexId = RelationGetRelid(indexRelation);
		Relation	pg_index;
		HeapTuple	indexTuple;
		Form_pg_index indexForm;

		pg_index = table_open(IndexRelationId, RowExclusiveLock);

		indexTuple = SearchSysCacheCopy1(INDEXRELID,
										 ObjectIdGetDatum(indexId));
		if (!HeapTupleIsValid(indexTuple))
			elog(ERROR, "cache lookup failed for index %u", indexId);
		indexForm = (Form_pg_index) GETSTRUCT(indexTuple);

		/* If it's a new index, indcheckxmin shouldn't be set ... */
		Assert(!indexForm->indcheckxmin);

		indexForm->indcheckxmin = true;
		CatalogTupleUpdate(pg_index, &indexTuple->t_self, indexTuple);

		heap_freetuple(indexTuple);
		table_close(pg_index, RowExclusiveLock);
	}

	/*
	 * Update heap and index pg_class rows
	 */
	index_update_stats(heapRelation,
					   true,
					   stats->heap_tuples);

	index_update_stats(indexRelation,
					   false,
					   stats->index_tuples);

	/* Make the updated catalog row versions visible */
	CommandCounterIncrement();

	/*
	 * If it's for an exclusion constraint, make a second pass over the heap
	 * to verify that the constraint is satisfied.  We must not do this until
	 * the index is fully valid.  (Broken HOT chains shouldn't matter, though;
	 * see comments for IndexCheckExclusion.)
	 */
	if (indexInfo->ii_ExclusionOps != NULL)
		IndexCheckExclusion(heapRelation, indexRelation, indexInfo);

	/* Roll back any GUC changes executed by index functions */
	AtEOXact_GUC(false, save_nestlevel);

	/* Restore userid and security context */
	SetUserIdAndSecContext(save_userid, save_sec_context);
}

/*
 * IndexCheckExclusion - verify that a new exclusion constraint is satisfied
 *
 * When creating an exclusion constraint, we first build the index normally
 * and then rescan the heap to check for conflicts.  We assume that we only
 * need to validate tuples that are live according to an up-to-date snapshot,
 * and that these were correctly indexed even in the presence of broken HOT
 * chains.  This should be OK since we are holding at least ShareLock on the
 * table, meaning there can be no uncommitted updates from other transactions.
 * (Note: that wouldn't necessarily work for system catalogs, since many
 * operations release write lock early on the system catalogs.)
 */
static void
IndexCheckExclusion(Relation heapRelation,
					Relation indexRelation,
					IndexInfo *indexInfo)
{
	TableScanDesc scan;
	Datum		values[INDEX_MAX_KEYS];
	bool		isnull[INDEX_MAX_KEYS];
	ExprState  *predicateExpand;
	TupleTableSlot *slot;
	EState	   *estate;
	ExprContext *econtext;
	Snapshot	snapshot;

	/*
	 * If we are reindexing the target index, mark it as no longer being
	 * reindexed, to forestall an Assert in index_beginscan when we try to use
	 * the index for probes.  This is OK because the index is now fully valid.
	 */
	if (ReindexIsCurrentlyProcessingIndex(RelationGetRelid(indexRelation)))
		ResetReindexProcessing();

	/*
	 * Need an EState for evaluation of index expressions and partial-index
	 * predicates.  Also a slot to hold the current tuple.
	 */
	estate = CreateExecutorState();
	econtext = GetPerTupleExprContext(estate);
	slot = table_slot_create(heapRelation, NULL);

	/* Arrange for econtext's scan tuple to be the tuple under test */
	econtext->ecxt_scantuple = slot;

	/* Set up execution state for predicate, if any. */
	predicateExpand = ExecPrepareQual(indexInfo->ii_PredicateExpand, estate);

	/*
	 * Scan all live tuples in the base relation.
	 */
	snapshot = RegisterSnapshot(GetLatestSnapshot());
	scan = table_beginscan_strat(heapRelation,	/* relation */
								 snapshot,	/* snapshot */
								 0, /* number of keys */
								 NULL,	/* scan key */
								 true,	/* buffer access strategy OK */
								 true); /* syncscan OK */

	while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
	{
		CHECK_FOR_INTERRUPTS();

		/*
		 * In a partial index, ignore tuples that don't satisfy the predicate.
		 */
		if (predicateExpand != NULL)
		{
			if (!ExecQual(predicateExpand, econtext))
				continue;
		}

		/*
		 * Extract index column values, including computing expressions.
		 */
		FormIndexDatum(indexInfo,
					   slot,
					   estate,
					   values,
					   isnull);

		/*
		 * Check that this tuple has no conflicts.
		 */
		check_exclusion_constraint(heapRelation,
								   indexRelation, indexInfo,
								   &(slot->tts_tid), values, isnull,
								   estate, true);

		MemoryContextReset(econtext->ecxt_per_tuple_memory);
	}

	table_endscan(scan);
	UnregisterSnapshot(snapshot);

	ExecDropSingleTupleTableSlot(slot);

	FreeExecutorState(estate);

	/* These may have been pointing to the now-gone estate */
	indexInfo->ii_ExpressionsState = NIL;
	indexInfo->ii_ExpressionsExpandState = NIL;
	indexInfo->ii_PredicateState = NULL;
	indexInfo->ii_PredicateExpandState = NULL;
}

/*
 * validate_index - support code for concurrent index builds
 *
 * We do a concurrent index build by first inserting the catalog entry for the
 * index via index_create(), marking it not indisready and not indisvalid.
 * Then we commit our transaction and start a new one, then we wait for all
 * transactions that could have been modifying the table to terminate.  Now
 * we know that any subsequently-started transactions will see the index and
 * honor its constraints on HOT updates; so while existing HOT-chains might
 * be broken with respect to the index, no currently live tuple will have an
 * incompatible HOT update done to it.  We now build the index normally via
 * index_build(), while holding a weak lock that allows concurrent
 * insert/update/delete.  Also, we index only tuples that are valid
 * as of the start of the scan (see table_index_build_scan), whereas a normal
 * build takes care to include recently-dead tuples.  This is OK because
 * we won't mark the index valid until all transactions that might be able
 * to see those tuples are gone.  The reason for doing that is to avoid
 * bogus unique-index failures due to concurrent UPDATEs (we might see
 * different versions of the same row as being valid when we pass over them,
 * if we used HeapTupleSatisfiesVacuum).  This leaves us with an index that
 * does not contain any tuples added to the table while we built the index.
 *
 * Next, we mark the index "indisready" (but still not "indisvalid") and
 * commit the second transaction and start a third.  Again we wait for all
 * transactions that could have been modifying the table to terminate.  Now
 * we know that any subsequently-started transactions will see the index and
 * insert their new tuples into it.  We then take a new reference snapshot
 * which is passed to validate_index().  Any tuples that are valid according
 * to this snap, but are not in the index, must be added to the index.
 * (Any tuples committed live after the snap will be inserted into the
 * index by their originating transaction.  Any tuples committed dead before
 * the snap need not be indexed, because we will wait out all transactions
 * that might care about them before we mark the index valid.)
 *
 * validate_index() works by first gathering all the TIDs currently in the
 * index, using a bulkdelete callback that just stores the TIDs and doesn't
 * ever say "delete it".  (This should be faster than a plain indexscan;
 * also, not all index AMs support full-index indexscan.)  Then we sort the
 * TIDs, and finally scan the table doing a "merge join" against the TID list
 * to see which tuples are missing from the index.  Thus we will ensure that
 * all tuples valid according to the reference snapshot are in the index.
 *
 * Building a unique index this way is tricky: we might try to insert a
 * tuple that is already dead or is in process of being deleted, and we
 * mustn't have a uniqueness failure against an updated version of the same
 * row.  We could try to check the tuple to see if it's already dead and tell
 * index_insert() not to do the uniqueness check, but that still leaves us
 * with a race condition against an in-progress update.  To handle that,
 * we expect the index AM to recheck liveness of the to-be-inserted tuple
 * before it declares a uniqueness error.
 *
 * After completing validate_index(), we wait until all transactions that
 * were alive at the time of the reference snapshot are gone; this is
 * necessary to be sure there are none left with a transaction snapshot
 * older than the reference (and hence possibly able to see tuples we did
 * not index).  Then we mark the index "indisvalid" and commit.  Subsequent
 * transactions will be able to use it for queries.
 *
 * Doing two full table scans is a brute-force strategy.  We could try to be
 * cleverer, eg storing new tuples in a special area of the table (perhaps
 * making the table append-only by setting use_fsm).  However that would
 * add yet more locking issues.
 */
void
validate_index(Oid heapId, Oid indexId, Snapshot snapshot)
{
	Relation	heapRelation,
				indexRelation;
	IndexInfo  *indexInfo;
	IndexVacuumInfo ivinfo;
	ValidateIndexState state;
	Oid			save_userid;
	int			save_sec_context;
	int			save_nestlevel;

	{
		const int	progress_index[] = {
			PROGRESS_CREATEIDX_PHASE,
			PROGRESS_CREATEIDX_TUPLES_DONE,
			PROGRESS_CREATEIDX_TUPLES_TOTAL,
			PROGRESS_SCAN_BLOCKS_DONE,
			PROGRESS_SCAN_BLOCKS_TOTAL
		};
		const int64 progress_vals[] = {
			PROGRESS_CREATEIDX_PHASE_VALIDATE_IDXSCAN,
			0, 0, 0, 0
		};

		pgstat_progress_update_multi_param(5, progress_index, progress_vals);
	}

	/* Open and lock the parent heap relation */
	heapRelation = table_open(heapId, ShareUpdateExclusiveLock);

	/*
	 * Switch to the table owner's userid, so that any index functions are run
	 * as that user.  Also lock down security-restricted operations and
	 * arrange to make GUC variable changes local to this command.
	 */
	GetUserIdAndSecContext(&save_userid, &save_sec_context);
	SetUserIdAndSecContext(heapRelation->rd_rel->relowner,
						   save_sec_context | SECURITY_RESTRICTED_OPERATION);
	save_nestlevel = NewGUCNestLevel();
	RestrictSearchPath();

	indexRelation = index_open(indexId, RowExclusiveLock);

	/*
	 * Fetch info needed for index_insert.  (You might think this should be
	 * passed in from DefineIndex, but its copy is long gone due to having
	 * been built in a previous transaction.)
	 */
	indexInfo = BuildIndexInfo(indexRelation);

	/* mark build is concurrent just for consistency */
	indexInfo->ii_Concurrent = true;

	/*
	 * Scan the index and gather up all the TIDs into a tuplesort object.
	 */
	ivinfo.index = indexRelation;
	ivinfo.heaprel = heapRelation;
	ivinfo.analyze_only = false;
	ivinfo.report_progress = true;
	ivinfo.estimated_count = true;
	ivinfo.message_level = DEBUG2;
	ivinfo.num_heap_tuples = heapRelation->rd_rel->reltuples;
	ivinfo.strategy = NULL;

	/*
	 * Encode TIDs as int8 values for the sort, rather than directly sorting
	 * item pointers.  This can be significantly faster, primarily because TID
	 * is a pass-by-reference type on all platforms, whereas int8 is
	 * pass-by-value on most platforms.
	 */
	state.tuplesort = tuplesort_begin_datum(INT8OID, Int8LessOperator,
											InvalidOid, false,
											maintenance_work_mem,
											NULL, TUPLESORT_NONE);
	state.htups = state.itups = state.tups_inserted = 0;

	/* ambulkdelete updates progress metrics */
	(void) index_bulk_delete(&ivinfo, NULL,
							 validate_index_callback, &state);

	/* Execute the sort */
	{
		const int	progress_index[] = {
			PROGRESS_CREATEIDX_PHASE,
			PROGRESS_SCAN_BLOCKS_DONE,
			PROGRESS_SCAN_BLOCKS_TOTAL
		};
		const int64 progress_vals[] = {
			PROGRESS_CREATEIDX_PHASE_VALIDATE_SORT,
			0, 0
		};

		pgstat_progress_update_multi_param(3, progress_index, progress_vals);
	}
	tuplesort_performsort(state.tuplesort);

	/*
	 * Now scan the heap and "merge" it with the index
	 */
	pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE,
								 PROGRESS_CREATEIDX_PHASE_VALIDATE_TABLESCAN);
	table_index_validate_scan(heapRelation,
							  indexRelation,
							  indexInfo,
							  snapshot,
							  &state);

	/* Done with tuplesort object */
	tuplesort_end(state.tuplesort);

	/* Make sure to release resources cached in indexInfo (if needed). */
	index_insert_cleanup(indexRelation, indexInfo);

	elog(DEBUG2,
		 "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples",
		 state.htups, state.itups, state.tups_inserted);

	/* Roll back any GUC changes executed by index functions */
	AtEOXact_GUC(false, save_nestlevel);

	/* Restore userid and security context */
	SetUserIdAndSecContext(save_userid, save_sec_context);

	/* Close rels, but keep locks */
	index_close(indexRelation, NoLock);
	table_close(heapRelation, NoLock);
}

/*
 * validate_index_callback - bulkdelete callback to collect the index TIDs
 */
static bool
validate_index_callback(ItemPointer itemptr, void *opaque)
{
	ValidateIndexState *state = (ValidateIndexState *) opaque;
	int64		encoded = itemptr_encode(itemptr);

	tuplesort_putdatum(state->tuplesort, Int64GetDatum(encoded), false);
	state->itups += 1;
	return false;				/* never actually delete anything */
}

/*
 * index_set_state_flags - adjust pg_index state flags
 *
 * This is used during CREATE/DROP INDEX CONCURRENTLY to adjust the pg_index
 * flags that denote the index's state.
 *
 * Note that CatalogTupleUpdate() sends a cache invalidation message for the
 * tuple, so other sessions will hear about the update as soon as we commit.
 */
void
index_set_state_flags(Oid indexId, IndexStateFlagsAction action)
{
	Relation	pg_index;
	HeapTuple	indexTuple;
	Form_pg_index indexForm;

	/* Open pg_index and fetch a writable copy of the index's tuple */
	pg_index = table_open(IndexRelationId, RowExclusiveLock);

	indexTuple = SearchSysCacheCopy1(INDEXRELID,
									 ObjectIdGetDatum(indexId));
	if (!HeapTupleIsValid(indexTuple))
		elog(ERROR, "cache lookup failed for index %u", indexId);
	indexForm = (Form_pg_index) GETSTRUCT(indexTuple);

	/* Perform the requested state change on the copy */
	switch (action)
	{
		case INDEX_CREATE_SET_READY:
			/* Set indisready during a CREATE INDEX CONCURRENTLY sequence */
			Assert(indexForm->indislive);
			Assert(!indexForm->indisready);
			Assert(!indexForm->indisvalid);
			indexForm->indisready = true;
			break;
		case INDEX_CREATE_SET_VALID:
			/* Set indisvalid during a CREATE INDEX CONCURRENTLY sequence */
			Assert(indexForm->indislive);
			Assert(indexForm->indisready);
			Assert(!indexForm->indisvalid);
			indexForm->indisvalid = true;
			break;
		case INDEX_DROP_CLEAR_VALID:

			/*
			 * Clear indisvalid during a DROP INDEX CONCURRENTLY sequence
			 *
			 * If indisready == true we leave it set so the index still gets
			 * maintained by active transactions.  We only need to ensure that
			 * indisvalid is false.  (We don't assert that either is initially
			 * true, though, since we want to be able to retry a DROP INDEX
			 * CONCURRENTLY that failed partway through.)
			 *
			 * Note: the CLUSTER logic assumes that indisclustered cannot be
			 * set on any invalid index, so clear that flag too.  For
			 * cleanliness, also clear indisreplident.
			 */
			indexForm->indisvalid = false;
			indexForm->indisclustered = false;
			indexForm->indisreplident = false;
			break;
		case INDEX_DROP_SET_DEAD:

			/*
			 * Clear indisready/indislive during DROP INDEX CONCURRENTLY
			 *
			 * We clear both indisready and indislive, because we not only
			 * want to stop updates, we want to prevent sessions from touching
			 * the index at all.
			 */
			Assert(!indexForm->indisvalid);
			Assert(!indexForm->indisclustered);
			Assert(!indexForm->indisreplident);
			indexForm->indisready = false;
			indexForm->indislive = false;
			break;
	}

	/* ... and update it */
	CatalogTupleUpdate(pg_index, &indexTuple->t_self, indexTuple);

	table_close(pg_index, RowExclusiveLock);
}


/*
 * IndexGetRelation: given an index's relation OID, get the OID of the
 * relation it is an index on.  Uses the system cache.
 */
Oid
IndexGetRelation(Oid indexId, bool missing_ok)
{
	HeapTuple	tuple;
	Form_pg_index index;
	Oid			result;

	tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexId));
	if (!HeapTupleIsValid(tuple))
	{
		if (missing_ok)
			return InvalidOid;
		elog(ERROR, "cache lookup failed for index %u", indexId);
	}
	index = (Form_pg_index) GETSTRUCT(tuple);
	Assert(index->indexrelid == indexId);

	result = index->indrelid;
	ReleaseSysCache(tuple);
	return result;
}

/*
 * reindex_index - This routine is used to recreate a single index
 */
void
reindex_index(const ReindexStmt *stmt, Oid indexId,
			  bool skip_constraint_checks, char persistence,
			  const ReindexParams *params)
{
	Relation	iRel,
				heapRelation;
	Oid			heapId;
	Oid			save_userid;
	int			save_sec_context;
	int			save_nestlevel;
	IndexInfo  *indexInfo;
	volatile bool skipped_constraint = false;
	PGRUsage	ru0;
	bool		progress = ((params->options & REINDEXOPT_REPORT_PROGRESS) != 0);
	bool		set_tablespace = false;

	pg_rusage_init(&ru0);

	/*
	 * Open and lock the parent heap relation.  ShareLock is sufficient since
	 * we only need to be sure no schema or data changes are going on.
	 */
	heapId = IndexGetRelation(indexId,
							  (params->options & REINDEXOPT_MISSING_OK) != 0);
	/* if relation is missing, leave */
	if (!OidIsValid(heapId))
		return;

	if ((params->options & REINDEXOPT_MISSING_OK) != 0)
		heapRelation = try_table_open(heapId, ShareLock);
	else
		heapRelation = table_open(heapId, ShareLock);

	/* if relation is gone, leave */
	if (!heapRelation)
		return;

	/*
	 * Switch to the table owner's userid, so that any index functions are run
	 * as that user.  Also lock down security-restricted operations and
	 * arrange to make GUC variable changes local to this command.
	 */
	GetUserIdAndSecContext(&save_userid, &save_sec_context);
	SetUserIdAndSecContext(heapRelation->rd_rel->relowner,
						   save_sec_context | SECURITY_RESTRICTED_OPERATION);
	save_nestlevel = NewGUCNestLevel();
	RestrictSearchPath();

	if (progress)
	{
		const int	progress_cols[] = {
			PROGRESS_CREATEIDX_COMMAND,
			PROGRESS_CREATEIDX_INDEX_OID
		};
		const int64 progress_vals[] = {
			PROGRESS_CREATEIDX_COMMAND_REINDEX,
			indexId
		};

		pgstat_progress_start_command(PROGRESS_COMMAND_CREATE_INDEX,
									  heapId);
		pgstat_progress_update_multi_param(2, progress_cols, progress_vals);
	}

	/*
	 * Open the target index relation and get an exclusive lock on it, to
	 * ensure that no one else is touching this particular index.
	 */
	if ((params->options & REINDEXOPT_MISSING_OK) != 0)
		iRel = try_index_open(indexId, AccessExclusiveLock);
	else
		iRel = index_open(indexId, AccessExclusiveLock);

	/* if index relation is gone, leave */
	if (!iRel)
	{
		/* Roll back any GUC changes */
		AtEOXact_GUC(false, save_nestlevel);

		/* Restore userid and security context */
		SetUserIdAndSecContext(save_userid, save_sec_context);

		/* Close parent heap relation, but keep locks */
		table_close(heapRelation, NoLock);
		return;
	}

	if (progress)
		pgstat_progress_update_param(PROGRESS_CREATEIDX_ACCESS_METHOD_OID,
									 iRel->rd_rel->relam);

	/*
	 * If a statement is available, telling that this comes from a REINDEX
	 * command, collect the index for event triggers.
	 */
	if (stmt)
	{
		ObjectAddress address;

		ObjectAddressSet(address, RelationRelationId, indexId);
		EventTriggerCollectSimpleCommand(address,
										 InvalidObjectAddress,
										 (const Node *) stmt);
	}

	/*
	 * Partitioned indexes should never get processed here, as they have no
	 * physical storage.
	 */
	if (iRel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
		elog(ERROR, "cannot reindex partitioned index \"%s.%s\"",
			 get_namespace_name(RelationGetNamespace(iRel)),
			 RelationGetRelationName(iRel));

	/*
	 * Don't allow reindex on temp tables of other backends ... their local
	 * buffer manager is not going to cope.
	 */
	if (RELATION_IS_OTHER_TEMP(iRel))
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("cannot reindex temporary tables of other sessions")));

	/*
	 * Don't allow reindex of an invalid index on TOAST table.  This is a
	 * leftover from a failed REINDEX CONCURRENTLY, and if rebuilt it would
	 * not be possible to drop it anymore.
	 */
	if (IsToastNamespace(RelationGetNamespace(iRel)) &&
		!get_index_isvalid(indexId))
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("cannot reindex invalid index on TOAST table")));

	/*
	 * System relations cannot be moved even if allow_system_table_mods is
	 * enabled to keep things consistent with the concurrent case where all
	 * the indexes of a relation are processed in series, including indexes of
	 * toast relations.
	 *
	 * Note that this check is not part of CheckRelationTableSpaceMove() as it
	 * gets used for ALTER TABLE SET TABLESPACE that could cascade across
	 * toast relations.
	 */
	if (OidIsValid(params->tablespaceOid) &&
		IsSystemRelation(iRel))
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("cannot move system relation \"%s\"",
						RelationGetRelationName(iRel))));

	/* Check if the tablespace of this index needs to be changed */
	if (OidIsValid(params->tablespaceOid) &&
		CheckRelationTableSpaceMove(iRel, params->tablespaceOid))
		set_tablespace = true;

	/*
	 * Also check for active uses of the index in the current transaction; we
	 * don't want to reindex underneath an open indexscan.
	 */
	CheckTableNotInUse(iRel, "REINDEX INDEX");

	/* Set new tablespace, if requested */
	if (set_tablespace)
	{
		/* Update its pg_class row */
		SetRelationTableSpace(iRel, params->tablespaceOid, InvalidOid);

		/*
		 * Schedule unlinking of the old index storage at transaction commit.
		 */
		RelationDropStorage(iRel);
		RelationAssumeNewRelfilelocator(iRel);

		/* Make sure the reltablespace change is visible */
		CommandCounterIncrement();
	}

	/*
	 * All predicate locks on the index are about to be made invalid. Promote
	 * them to relation locks on the heap.
	 */
	TransferPredicateLocksToHeapRelation(iRel);

	/* Fetch info needed for index_build */
	indexInfo = BuildIndexInfo(iRel);

	/* If requested, skip checking uniqueness/exclusion constraints */
	if (skip_constraint_checks)
	{
		if (indexInfo->ii_Unique || indexInfo->ii_ExclusionOps != NULL)
			skipped_constraint = true;
		indexInfo->ii_Unique = false;
		indexInfo->ii_ExclusionOps = NULL;
		indexInfo->ii_ExclusionProcs = NULL;
		indexInfo->ii_ExclusionStrats = NULL;
	}

	/* Suppress use of the target index while rebuilding it */
	SetReindexProcessing(heapId, indexId);

	/* Create a new physical relation for the index */
	RelationSetNewRelfilenumber(iRel, persistence);

	/* Initialize the index and rebuild */
	/* Note: we do not need to re-establish pkey setting */
	index_build(heapRelation, iRel, indexInfo, true, true, progress);

	/* Re-allow use of target index */
	ResetReindexProcessing();

	/*
	 * If the index is marked invalid/not-ready/dead (ie, it's from a failed
	 * CREATE INDEX CONCURRENTLY, or a DROP INDEX CONCURRENTLY failed midway),
	 * and we didn't skip a uniqueness check, we can now mark it valid.  This
	 * allows REINDEX to be used to clean up in such cases.
	 *
	 * We can also reset indcheckxmin, because we have now done a
	 * non-concurrent index build, *except* in the case where index_build
	 * found some still-broken HOT chains. If it did, and we don't have to
	 * change any of the other flags, we just leave indcheckxmin alone (note
	 * that index_build won't have changed it, because this is a reindex).
	 * This is okay and desirable because not updating the tuple leaves the
	 * index's usability horizon (recorded as the tuple's xmin value) the same
	 * as it was.
	 *
	 * But, if the index was invalid/not-ready/dead and there were broken HOT
	 * chains, we had better force indcheckxmin true, because the normal
	 * argument that the HOT chains couldn't conflict with the index is
	 * suspect for an invalid index.  (A conflict is definitely possible if
	 * the index was dead.  It probably shouldn't happen otherwise, but let's
	 * be conservative.)  In this case advancing the usability horizon is
	 * appropriate.
	 *
	 * Another reason for avoiding unnecessary updates here is that while
	 * reindexing pg_index itself, we must not try to update tuples in it.
	 * pg_index's indexes should always have these flags in their clean state,
	 * so that won't happen.
	 */
	if (!skipped_constraint)
	{
		Relation	pg_index;
		HeapTuple	indexTuple;
		Form_pg_index indexForm;
		bool		index_bad;

		pg_index = table_open(IndexRelationId, RowExclusiveLock);

		indexTuple = SearchSysCacheCopy1(INDEXRELID,
										 ObjectIdGetDatum(indexId));
		if (!HeapTupleIsValid(indexTuple))
			elog(ERROR, "cache lookup failed for index %u", indexId);
		indexForm = (Form_pg_index) GETSTRUCT(indexTuple);

		index_bad = (!indexForm->indisvalid ||
					 !indexForm->indisready ||
					 !indexForm->indislive);
		if (index_bad ||
			(indexForm->indcheckxmin && !indexInfo->ii_BrokenHotChain))
		{
			if (!indexInfo->ii_BrokenHotChain)
				indexForm->indcheckxmin = false;
			else if (index_bad)
				indexForm->indcheckxmin = true;
			indexForm->indisvalid = true;
			indexForm->indisready = true;
			indexForm->indislive = true;
			CatalogTupleUpdate(pg_index, &indexTuple->t_self, indexTuple);

			/*
			 * Invalidate the relcache for the table, so that after we commit
			 * all sessions will refresh the table's index list.  This ensures
			 * that if anyone misses seeing the pg_index row during this
			 * update, they'll refresh their list before attempting any update
			 * on the table.
			 */
			CacheInvalidateRelcache(heapRelation);
		}

		table_close(pg_index, RowExclusiveLock);
	}

	/* Log what we did */
	if ((params->options & REINDEXOPT_VERBOSE) != 0)
		ereport(INFO,
				(errmsg("index \"%s\" was reindexed",
						get_rel_name(indexId)),
				 errdetail_internal("%s",
									pg_rusage_show(&ru0))));

	/* Roll back any GUC changes executed by index functions */
	AtEOXact_GUC(false, save_nestlevel);

	/* Restore userid and security context */
	SetUserIdAndSecContext(save_userid, save_sec_context);

	/* Close rels, but keep locks */
	index_close(iRel, NoLock);
	table_close(heapRelation, NoLock);

	if (progress)
		pgstat_progress_end_command();
}

/*
 * reindex_relation - This routine is used to recreate all indexes
 * of a relation (and optionally its toast relation too, if any).
 *
 * "flags" is a bitmask that can include any combination of these bits:
 *
 * REINDEX_REL_PROCESS_TOAST: if true, process the toast table too (if any).
 *
 * REINDEX_REL_SUPPRESS_INDEX_USE: if true, the relation was just completely
 * rebuilt by an operation such as VACUUM FULL or CLUSTER, and therefore its
 * indexes are inconsistent with it.  This makes things tricky if the relation
 * is a system catalog that we might consult during the reindexing.  To deal
 * with that case, we mark all of the indexes as pending rebuild so that they
 * won't be trusted until rebuilt.  The caller is required to call us *without*
 * having made the rebuilt table visible by doing CommandCounterIncrement;
 * we'll do CCI after having collected the index list.  (This way we can still
 * use catalog indexes while collecting the list.)
 *
 * REINDEX_REL_CHECK_CONSTRAINTS: if true, recheck unique and exclusion
 * constraint conditions, else don't.  To avoid deadlocks, VACUUM FULL or
 * CLUSTER on a system catalog must omit this flag.  REINDEX should be used to
 * rebuild an index if constraint inconsistency is suspected.  For optimal
 * performance, other callers should include the flag only after transforming
 * the data in a manner that risks a change in constraint validity.
 *
 * REINDEX_REL_FORCE_INDEXES_UNLOGGED: if true, set the persistence of the
 * rebuilt indexes to unlogged.
 *
 * REINDEX_REL_FORCE_INDEXES_PERMANENT: if true, set the persistence of the
 * rebuilt indexes to permanent.
 *
 * Returns true if any indexes were rebuilt (including toast table's index
 * when relevant).  Note that a CommandCounterIncrement will occur after each
 * index rebuild.
 */
bool
reindex_relation(const ReindexStmt *stmt, Oid relid, int flags,
				 const ReindexParams *params)
{
	Relation	rel;
	Oid			toast_relid;
	List	   *indexIds;
	char		persistence;
	bool		result = false;
	ListCell   *indexId;
	int			i;

	/*
	 * Open and lock the relation.  ShareLock is sufficient since we only need
	 * to prevent schema and data changes in it.  The lock level used here
	 * should match ReindexTable().
	 */
	if ((params->options & REINDEXOPT_MISSING_OK) != 0)
		rel = try_table_open(relid, ShareLock);
	else
		rel = table_open(relid, ShareLock);

	/* if relation is gone, leave */
	if (!rel)
		return false;

	/*
	 * Partitioned tables should never get processed here, as they have no
	 * physical storage.
	 */
	if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
		elog(ERROR, "cannot reindex partitioned table \"%s.%s\"",
			 get_namespace_name(RelationGetNamespace(rel)),
			 RelationGetRelationName(rel));

	toast_relid = rel->rd_rel->reltoastrelid;

	/*
	 * Get the list of index OIDs for this relation.  (We trust the relcache
	 * to get this with a sequential scan if ignoring system indexes.)
	 */
	indexIds = RelationGetIndexList(rel);

	if (flags & REINDEX_REL_SUPPRESS_INDEX_USE)
	{
		/* Suppress use of all the indexes until they are rebuilt */
		SetReindexPending(indexIds);

		/*
		 * Make the new heap contents visible --- now things might be
		 * inconsistent!
		 */
		CommandCounterIncrement();
	}

	/*
	 * Reindex the toast table, if any, before the main table.
	 *
	 * This helps in cases where a corruption in the toast table's index would
	 * otherwise error and stop REINDEX TABLE command when it tries to fetch a
	 * toasted datum.  This way. the toast table's index is rebuilt and fixed
	 * before it is used for reindexing the main table.
	 *
	 * It is critical to call reindex_relation() *after* the call to
	 * RelationGetIndexList() returning the list of indexes on the relation,
	 * because reindex_relation() will call CommandCounterIncrement() after
	 * every reindex_index().  See REINDEX_REL_SUPPRESS_INDEX_USE for more
	 * details.
	 */
	if ((flags & REINDEX_REL_PROCESS_TOAST) && OidIsValid(toast_relid))
	{
		/*
		 * Note that this should fail if the toast relation is missing, so
		 * reset REINDEXOPT_MISSING_OK.  Even if a new tablespace is set for
		 * the parent relation, the indexes on its toast table are not moved.
		 * This rule is enforced by setting tablespaceOid to InvalidOid.
		 */
		ReindexParams newparams = *params;

		newparams.options &= ~(REINDEXOPT_MISSING_OK);
		newparams.tablespaceOid = InvalidOid;
		result |= reindex_relation(stmt, toast_relid, flags, &newparams);
	}

	/*
	 * Compute persistence of indexes: same as that of owning rel, unless
	 * caller specified otherwise.
	 */
	if (flags & REINDEX_REL_FORCE_INDEXES_UNLOGGED)
		persistence = RELPERSISTENCE_UNLOGGED;
	else if (flags & REINDEX_REL_FORCE_INDEXES_PERMANENT)
		persistence = RELPERSISTENCE_PERMANENT;
	else
		persistence = rel->rd_rel->relpersistence;

	/* Reindex all the indexes. */
	i = 1;
	foreach(indexId, indexIds)
	{
		Oid			indexOid = lfirst_oid(indexId);
		Oid			indexNamespaceId = get_rel_namespace(indexOid);

		/*
		 * Skip any invalid indexes on a TOAST table.  These can only be
		 * duplicate leftovers from a failed REINDEX CONCURRENTLY, and if
		 * rebuilt it would not be possible to drop them anymore.
		 */
		if (IsToastNamespace(indexNamespaceId) &&
			!get_index_isvalid(indexOid))
		{
			ereport(WARNING,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("cannot reindex invalid index \"%s.%s\" on TOAST table, skipping",
							get_namespace_name(indexNamespaceId),
							get_rel_name(indexOid))));

			/*
			 * Remove this invalid toast index from the reindex pending list,
			 * as it is skipped here due to the hard failure that would happen
			 * in reindex_index(), should we try to process it.
			 */
			if (flags & REINDEX_REL_SUPPRESS_INDEX_USE)
				RemoveReindexPending(indexOid);
			continue;
		}

		reindex_index(stmt, indexOid, !(flags & REINDEX_REL_CHECK_CONSTRAINTS),
					  persistence, params);

		CommandCounterIncrement();

		/* Index should no longer be in the pending list */
		Assert(!ReindexIsProcessingIndex(indexOid));

		/* Set index rebuild count */
		pgstat_progress_update_param(PROGRESS_REPACK_INDEX_REBUILD_COUNT,
									 i);
		i++;
	}

	/*
	 * Close rel, but continue to hold the lock.
	 */
	table_close(rel, NoLock);

	result |= (indexIds != NIL);

	return result;
}


/* ----------------------------------------------------------------
 *		System index reindexing support
 *
 * When we are busy reindexing a system index, this code provides support
 * for preventing catalog lookups from using that index.  We also make use
 * of this to catch attempted uses of user indexes during reindexing of
 * those indexes.  This information is propagated to parallel workers;
 * attempting to change it during a parallel operation is not permitted.
 * ----------------------------------------------------------------
 */

static Oid	currentlyReindexedHeap = InvalidOid;
static Oid	currentlyReindexedIndex = InvalidOid;
static List *pendingReindexedIndexes = NIL;
static int	reindexingNestLevel = 0;

/*
 * ReindexIsProcessingHeap
 *		True if heap specified by OID is currently being reindexed.
 */
bool
ReindexIsProcessingHeap(Oid heapOid)
{
	return heapOid == currentlyReindexedHeap;
}

/*
 * ReindexIsCurrentlyProcessingIndex
 *		True if index specified by OID is currently being reindexed.
 */
static bool
ReindexIsCurrentlyProcessingIndex(Oid indexOid)
{
	return indexOid == currentlyReindexedIndex;
}

/*
 * ReindexIsProcessingIndex
 *		True if index specified by OID is currently being reindexed,
 *		or should be treated as invalid because it is awaiting reindex.
 */
bool
ReindexIsProcessingIndex(Oid indexOid)
{
	return indexOid == currentlyReindexedIndex ||
		list_member_oid(pendingReindexedIndexes, indexOid);
}

/*
 * SetReindexProcessing
 *		Set flag that specified heap/index are being reindexed.
 */
static void
SetReindexProcessing(Oid heapOid, Oid indexOid)
{
	Assert(OidIsValid(heapOid) && OidIsValid(indexOid));
	/* Reindexing is not re-entrant. */
	if (OidIsValid(currentlyReindexedHeap))
		elog(ERROR, "cannot reindex while reindexing");
	currentlyReindexedHeap = heapOid;
	currentlyReindexedIndex = indexOid;
	/* Index is no longer "pending" reindex. */
	RemoveReindexPending(indexOid);
	/* This may have been set already, but in case it isn't, do so now. */
	reindexingNestLevel = GetCurrentTransactionNestLevel();
}

/*
 * ResetReindexProcessing
 *		Unset reindexing status.
 */
static void
ResetReindexProcessing(void)
{
	currentlyReindexedHeap = InvalidOid;
	currentlyReindexedIndex = InvalidOid;
	/* reindexingNestLevel remains set till end of (sub)transaction */
}

/*
 * SetReindexPending
 *		Mark the given indexes as pending reindex.
 *
 * NB: we assume that the current memory context stays valid throughout.
 */
static void
SetReindexPending(List *indexes)
{
	/* Reindexing is not re-entrant. */
	if (pendingReindexedIndexes)
		elog(ERROR, "cannot reindex while reindexing");
	if (IsInParallelMode())
		elog(ERROR, "cannot modify reindex state during a parallel operation");
	pendingReindexedIndexes = list_copy(indexes);
	reindexingNestLevel = GetCurrentTransactionNestLevel();
}

/*
 * RemoveReindexPending
 *		Remove the given index from the pending list.
 */
static void
RemoveReindexPending(Oid indexOid)
{
	if (IsInParallelMode())
		elog(ERROR, "cannot modify reindex state during a parallel operation");
	pendingReindexedIndexes = list_delete_oid(pendingReindexedIndexes,
											  indexOid);
}

/*
 * ResetReindexState
 *		Clear all reindexing state during (sub)transaction abort.
 */
void
ResetReindexState(int nestLevel)
{
	/*
	 * Because reindexing is not re-entrant, we don't need to cope with nested
	 * reindexing states.  We just need to avoid messing up the outer-level
	 * state in case a subtransaction fails within a REINDEX.  So checking the
	 * current nest level against that of the reindex operation is sufficient.
	 */
	if (reindexingNestLevel >= nestLevel)
	{
		currentlyReindexedHeap = InvalidOid;
		currentlyReindexedIndex = InvalidOid;

		/*
		 * We needn't try to release the contents of pendingReindexedIndexes;
		 * that list should be in a transaction-lifespan context, so it will
		 * go away automatically.
		 */
		pendingReindexedIndexes = NIL;

		reindexingNestLevel = 0;
	}
}

/*
 * EstimateReindexStateSpace
 *		Estimate space needed to pass reindex state to parallel workers.
 */
Size
EstimateReindexStateSpace(void)
{
	return offsetof(SerializedReindexState, pendingReindexedIndexes)
		+ mul_size(sizeof(Oid), list_length(pendingReindexedIndexes));
}

/*
 * SerializeReindexState
 *		Serialize reindex state for parallel workers.
 */
void
SerializeReindexState(Size maxsize, char *start_address)
{
	SerializedReindexState *sistate = (SerializedReindexState *) start_address;
	int			c = 0;
	ListCell   *lc;

	sistate->currentlyReindexedHeap = currentlyReindexedHeap;
	sistate->currentlyReindexedIndex = currentlyReindexedIndex;
	sistate->numPendingReindexedIndexes = list_length(pendingReindexedIndexes);
	foreach(lc, pendingReindexedIndexes)
		sistate->pendingReindexedIndexes[c++] = lfirst_oid(lc);
}

/*
 * RestoreReindexState
 *		Restore reindex state in a parallel worker.
 */
void
RestoreReindexState(const void *reindexstate)
{
	const SerializedReindexState *sistate = (const SerializedReindexState *) reindexstate;
	int			c = 0;
	MemoryContext oldcontext;

	currentlyReindexedHeap = sistate->currentlyReindexedHeap;
	currentlyReindexedIndex = sistate->currentlyReindexedIndex;

	Assert(pendingReindexedIndexes == NIL);
	oldcontext = MemoryContextSwitchTo(TopMemoryContext);
	for (c = 0; c < sistate->numPendingReindexedIndexes; ++c)
		pendingReindexedIndexes =
			lappend_oid(pendingReindexedIndexes,
						sistate->pendingReindexedIndexes[c]);
	MemoryContextSwitchTo(oldcontext);

	/* Note the worker has its own transaction nesting level */
	reindexingNestLevel = GetCurrentTransactionNestLevel();
}
./indexcmds.c0000664000175000017500000044040315222105474012036 0ustar  xmanxman/*-------------------------------------------------------------------------
 *
 * indexcmds.c
 *	  POSTGRES define and remove index code.
 *
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/commands/indexcmds.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "access/amapi.h"
#include "access/attmap.h"
#include "access/gist.h"
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/reloptions.h"
#include "access/sysattr.h"
#include "access/tableam.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/index.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_am.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_database.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_type.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
#include "commands/progress.h"
#include "commands/tablecmds.h"
#include "commands/tablespace.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/optimizer.h"
#include "parser/parse_coerce.h"
#include "parser/parse_oper.h"
#include "parser/parse_utilcmd.h"
#include "partitioning/partdesc.h"
#include "pgstat.h"
#include "rewrite/rewriteManip.h"
#include "storage/lmgr.h"
#include "storage/proc.h"
#include "storage/procarray.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/guc.h"
#include "utils/injection_point.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/partcache.h"
#include "utils/pg_rusage.h"
#include "utils/regproc.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"


/* non-export function prototypes */
static bool CompareOpclassOptions(const Datum *opts1, const Datum *opts2, int natts);
static void CheckPredicate(Expr *predicate);
static void ComputeIndexAttrs(ParseState *pstate,
							  IndexInfo *indexInfo,
							  Oid *typeOids,
							  Oid *collationOids,
							  Oid *opclassOids,
							  Datum *opclassOptions,
							  int16 *colOptions,
							  const List *attList,
							  const List *exclusionOpNames,
							  Oid relId,
							  const char *accessMethodName,
							  Oid accessMethodId,
							  bool amcanorder,
							  bool isconstraint,
							  bool iswithoutoverlaps,
							  Oid ddl_userid,
							  int ddl_sec_context,
							  int *ddl_save_nestlevel);
static char *ChooseIndexName(const char *tabname, Oid namespaceId,
							 const List *colnames, const List *exclusionOpNames,
							 bool primary, bool isconstraint);
static char *ChooseIndexNameAddition(const List *colnames);
static List *ChooseIndexColumnNames(const List *indexElems);
static void ReindexIndex(const ReindexStmt *stmt, const ReindexParams *params,
						 bool isTopLevel);
static void RangeVarCallbackForReindexIndex(const RangeVar *relation,
											Oid relId, Oid oldRelId, void *arg);
static Oid	ReindexTable(const ReindexStmt *stmt, const ReindexParams *params,
						 bool isTopLevel);
static void ReindexMultipleTables(const ReindexStmt *stmt,
								  const ReindexParams *params);
static void reindex_error_callback(void *arg);
static void ReindexPartitions(const ReindexStmt *stmt, Oid relid,
							  const ReindexParams *params, bool isTopLevel);
static void ReindexMultipleInternal(const ReindexStmt *stmt, const List *relids,
									const ReindexParams *params);
static bool ReindexRelationConcurrently(const ReindexStmt *stmt,
										Oid relationOid,
										const ReindexParams *params);
static void update_relispartition(Oid relationId, bool newval);
static inline void set_indexsafe_procflags(void);

/*
 * callback argument type for RangeVarCallbackForReindexIndex()
 */
struct ReindexIndexCallbackState
{
	ReindexParams params;		/* options from statement */
	Oid			locked_table_oid;	/* tracks previously locked table */
};

/*
 * callback arguments for reindex_error_callback()
 */
typedef struct ReindexErrorInfo
{
	char	   *relname;
	char	   *relnamespace;
	char		relkind;
} ReindexErrorInfo;

/*
 * CheckIndexCompatible
 *		Determine whether an existing index definition is compatible with a
 *		prospective index definition, such that the existing index storage
 *		could become the storage of the new index, avoiding a rebuild.
 *
 * 'oldId': the OID of the existing index
 * 'accessMethodName': name of the AM to use.
 * 'attributeList': a list of IndexElem specifying columns and expressions
 *		to index on.
 * 'exclusionOpNames': list of names of exclusion-constraint operators,
 *		or NIL if not an exclusion constraint.
 * 'isWithoutOverlaps': true iff this index has a WITHOUT OVERLAPS clause.
 *
 * This is tailored to the needs of ALTER TABLE ALTER TYPE, which recreates
 * any indexes that depended on a changing column from their pg_get_indexdef
 * or pg_get_constraintdef definitions.  We omit some of the sanity checks of
 * DefineIndex.  We assume that the old and new indexes have the same number
 * of columns and that if one has an expression column or predicate, both do.
 * Errors arising from the attribute list still apply.
 *
 * Most column type changes that can skip a table rewrite do not invalidate
 * indexes.  We acknowledge this when all operator classes, collations and
 * exclusion operators match.  Though we could further permit intra-opfamily
 * changes for btree and hash indexes, that adds subtle complexity with no
 * concrete benefit for core types. Note, that INCLUDE columns aren't
 * checked by this function, for them it's enough that table rewrite is
 * skipped.
 *
 * When a comparison or exclusion operator has a polymorphic input type, the
 * actual input types must also match.  This defends against the possibility
 * that operators could vary behavior in response to get_fn_expr_argtype().
 * At present, this hazard is theoretical: check_exclusion_constraint() and
 * all core index access methods decline to set fn_expr for such calls.
 *
 * We do not yet implement a test to verify compatibility of expression
 * columns or predicates, so assume any such index is incompatible.
 */
bool
CheckIndexCompatible(Oid oldId,
					 const char *accessMethodName,
					 const List *attributeList,
					 const List *exclusionOpNames,
					 bool isWithoutOverlaps)
{
	bool		isconstraint;
	Oid		   *typeIds;
	Oid		   *collationIds;
	Oid		   *opclassIds;
	Datum	   *opclassOptions;
	Oid			accessMethodId;
	Oid			relationId;
	HeapTuple	tuple;
	Form_pg_index indexForm;
	Form_pg_am	accessMethodForm;
	const IndexAmRoutine *amRoutine;
	bool		amcanorder;
	bool		amsummarizing;
	int16	   *coloptions;
	IndexInfo  *indexInfo;
	int			numberOfAttributes;
	int			old_natts;
	bool		ret = true;
	oidvector  *old_indclass;
	oidvector  *old_indcollation;
	Relation	irel;
	int			i;
	Datum		d;

	/* Caller should already have the relation locked in some way. */
	relationId = IndexGetRelation(oldId, false);

	/*
	 * We can pretend isconstraint = false unconditionally.  It only serves to
	 * decide the text of an error message that should never happen for us.
	 */
	isconstraint = false;

	numberOfAttributes = list_length(attributeList);
	Assert(numberOfAttributes > 0);
	Assert(numberOfAttributes <= INDEX_MAX_KEYS);

	/* look up the access method */
	tuple = SearchSysCache1(AMNAME, PointerGetDatum(accessMethodName));
	if (!HeapTupleIsValid(tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("access method \"%s\" does not exist",
						accessMethodName)));
	accessMethodForm = (Form_pg_am) GETSTRUCT(tuple);
	accessMethodId = accessMethodForm->oid;
	amRoutine = GetIndexAmRoutine(accessMethodForm->amhandler);
	ReleaseSysCache(tuple);

	amcanorder = amRoutine->amcanorder;
	amsummarizing = amRoutine->amsummarizing;

	/*
	 * Compute the operator classes, collations, and exclusion operators for
	 * the new index, so we can test whether it's compatible with the existing
	 * one.  Note that ComputeIndexAttrs might fail here, but that's OK:
	 * DefineIndex would have failed later.  Our attributeList contains only
	 * key attributes, thus we're filling ii_NumIndexAttrs and
	 * ii_NumIndexKeyAttrs with same value.
	 */
	indexInfo = makeIndexInfo(numberOfAttributes, numberOfAttributes,
							  accessMethodId, NIL, NIL, false, false,
							  false, false, amsummarizing, isWithoutOverlaps);
	typeIds = palloc_array(Oid, numberOfAttributes);
	collationIds = palloc_array(Oid, numberOfAttributes);
	opclassIds = palloc_array(Oid, numberOfAttributes);
	opclassOptions = palloc_array(Datum, numberOfAttributes);
	coloptions = palloc_array(int16, numberOfAttributes);
	ComputeIndexAttrs(NULL, indexInfo,
					  typeIds, collationIds, opclassIds, opclassOptions,
					  coloptions, attributeList,
					  exclusionOpNames, relationId,
					  accessMethodName, accessMethodId,
					  amcanorder, isconstraint, isWithoutOverlaps, InvalidOid,
					  0, NULL);

	/* Get the soon-obsolete pg_index tuple. */
	tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(oldId));
	if (!HeapTupleIsValid(tuple))
		elog(ERROR, "cache lookup failed for index %u", oldId);
	indexForm = (Form_pg_index) GETSTRUCT(tuple);

	/*
	 * We don't assess expressions or predicates; assume incompatibility.
	 * Also, if the index is invalid for any reason, treat it as incompatible.
	 */
	if (!(heap_attisnull(tuple, Anum_pg_index_indpred, NULL) &&
		  heap_attisnull(tuple, Anum_pg_index_indexprs, NULL) &&
		  indexForm->indisvalid))
	{
		ReleaseSysCache(tuple);
		return false;
	}

	/* Any change in operator class or collation breaks compatibility. */
	old_natts = indexForm->indnkeyatts;
	Assert(old_natts == numberOfAttributes);

	d = SysCacheGetAttrNotNull(INDEXRELID, tuple, Anum_pg_index_indcollation);
	old_indcollation = (oidvector *) DatumGetPointer(d);

	d = SysCacheGetAttrNotNull(INDEXRELID, tuple, Anum_pg_index_indclass);
	old_indclass = (oidvector *) DatumGetPointer(d);

	ret = (memcmp(old_indclass->values, opclassIds, old_natts * sizeof(Oid)) == 0 &&
		   memcmp(old_indcollation->values, collationIds, old_natts * sizeof(Oid)) == 0);

	ReleaseSysCache(tuple);

	if (!ret)
		return false;

	/* For polymorphic opcintype, column type changes break compatibility. */
	irel = index_open(oldId, AccessShareLock);	/* caller probably has a lock */
	for (i = 0; i < old_natts; i++)
	{
		if (IsPolymorphicType(get_opclass_input_type(opclassIds[i])) &&
			TupleDescAttr(irel->rd_att, i)->atttypid != typeIds[i])
		{
			ret = false;
			break;
		}
	}

	/* Any change in opclass options break compatibility. */
	if (ret)
	{
		Datum	   *oldOpclassOptions = palloc_array(Datum, old_natts);

		for (i = 0; i < old_natts; i++)
			oldOpclassOptions[i] = get_attoptions(oldId, i + 1);

		ret = CompareOpclassOptions(oldOpclassOptions, opclassOptions, old_natts);

		pfree(oldOpclassOptions);
	}

	/* Any change in exclusion operator selections breaks compatibility. */
	if (ret && indexInfo->ii_ExclusionOps != NULL)
	{
		Oid		   *old_operators,
				   *old_procs;
		uint16	   *old_strats;

		RelationGetExclusionInfo(irel, &old_operators, &old_procs, &old_strats);
		ret = memcmp(old_operators, indexInfo->ii_ExclusionOps,
					 old_natts * sizeof(Oid)) == 0;

		/* Require an exact input type match for polymorphic operators. */
		if (ret)
		{
			for (i = 0; i < old_natts && ret; i++)
			{
				Oid			left,
							right;

				op_input_types(indexInfo->ii_ExclusionOps[i], &left, &right);
				if ((IsPolymorphicType(left) || IsPolymorphicType(right)) &&
					TupleDescAttr(irel->rd_att, i)->atttypid != typeIds[i])
				{
					ret = false;
					break;
				}
			}
		}
	}

	index_close(irel, NoLock);
	return ret;
}

/*
 * CompareOpclassOptions
 *
 * Compare per-column opclass options which are represented by arrays of text[]
 * datums.  Both elements of arrays and array themselves can be NULL.
 */
static bool
CompareOpclassOptions(const Datum *opts1, const Datum *opts2, int natts)
{
	int			i;
	FmgrInfo	fm;

	if (!opts1 && !opts2)
		return true;

	fmgr_info(F_ARRAY_EQ, &fm);
	for (i = 0; i < natts; i++)
	{
		Datum		opt1 = opts1 ? opts1[i] : (Datum) 0;
		Datum		opt2 = opts2 ? opts2[i] : (Datum) 0;

		if (opt1 == (Datum) 0)
		{
			if (opt2 == (Datum) 0)
				continue;
			else
				return false;
		}
		else if (opt2 == (Datum) 0)
			return false;

		/*
		 * Compare non-NULL text[] datums.  Use C collation to enforce binary
		 * equivalence of texts, because we don't know anything about the
		 * semantics of opclass options.
		 */
		if (!DatumGetBool(FunctionCall2Coll(&fm, C_COLLATION_OID, opt1, opt2)))
			return false;
	}

	return true;
}

/*
 * WaitForOlderSnapshots
 *
 * Wait for transactions that might have an older snapshot than the given xmin
 * limit, because it might not contain tuples deleted just before it has
 * been taken. Obtain a list of VXIDs of such transactions, and wait for them
 * individually. This is used when building an index concurrently.
 *
 * We can exclude any running transactions that have xmin > the xmin given;
 * their oldest snapshot must be newer than our xmin limit.
 * We can also exclude any transactions that have xmin = zero, since they
 * evidently have no live snapshot at all (and any one they might be in
 * process of taking is certainly newer than ours).  Transactions in other
 * DBs can be ignored too, since they'll never even be able to see the
 * index being worked on.
 *
 * We can also exclude autovacuum processes and processes running manual
 * lazy VACUUMs, because they won't be fazed by missing index entries
 * either.  (Manual ANALYZEs, however, can't be excluded because they
 * might be within transactions that are going to do arbitrary operations
 * later.)  Processes running CREATE INDEX CONCURRENTLY or REINDEX CONCURRENTLY
 * on indexes that are neither expressional nor partial are also safe to
 * ignore, since we know that those processes won't examine any data
 * outside the table they're indexing.
 *
 * Also, GetCurrentVirtualXIDs never reports our own vxid, so we need not
 * check for that.
 *
 * If a process goes idle-in-transaction with xmin zero, we do not need to
 * wait for it anymore, per the above argument.  We do not have the
 * infrastructure right now to stop waiting if that happens, but we can at
 * least avoid the folly of waiting when it is idle at the time we would
 * begin to wait.  We do this by repeatedly rechecking the output of
 * GetCurrentVirtualXIDs.  If, during any iteration, a particular vxid
 * doesn't show up in the output, we know we can forget about it.
 */
void
WaitForOlderSnapshots(TransactionId limitXmin, bool progress)
{
	int			n_old_snapshots;
	int			i;
	VirtualTransactionId *old_snapshots;

	old_snapshots = GetCurrentVirtualXIDs(limitXmin, true, false,
										  PROC_IS_AUTOVACUUM | PROC_IN_VACUUM
										  | PROC_IN_SAFE_IC,
										  &n_old_snapshots);
	if (progress)
		pgstat_progress_update_param(PROGRESS_WAITFOR_TOTAL, n_old_snapshots);

	for (i = 0; i < n_old_snapshots; i++)
	{
		if (!VirtualTransactionIdIsValid(old_snapshots[i]))
			continue;			/* found uninteresting in previous cycle */

		if (i > 0)
		{
			/* see if anything's changed ... */
			VirtualTransactionId *newer_snapshots;
			int			n_newer_snapshots;
			int			j;
			int			k;

			newer_snapshots = GetCurrentVirtualXIDs(limitXmin,
													true, false,
													PROC_IS_AUTOVACUUM | PROC_IN_VACUUM
													| PROC_IN_SAFE_IC,
													&n_newer_snapshots);
			for (j = i; j < n_old_snapshots; j++)
			{
				if (!VirtualTransactionIdIsValid(old_snapshots[j]))
					continue;	/* found uninteresting in previous cycle */
				for (k = 0; k < n_newer_snapshots; k++)
				{
					if (VirtualTransactionIdEquals(old_snapshots[j],
												   newer_snapshots[k]))
						break;
				}
				if (k >= n_newer_snapshots) /* not there anymore */
					SetInvalidVirtualTransactionId(old_snapshots[j]);
			}
			pfree(newer_snapshots);
		}

		if (VirtualTransactionIdIsValid(old_snapshots[i]))
		{
			/* If requested, publish who we're going to wait for. */
			if (progress)
			{
				PGPROC	   *holder = ProcNumberGetProc(old_snapshots[i].procNumber);

				if (holder)
					pgstat_progress_update_param(PROGRESS_WAITFOR_CURRENT_PID,
												 holder->pid);
			}
			VirtualXactLock(old_snapshots[i], true);
		}

		if (progress)
			pgstat_progress_update_param(PROGRESS_WAITFOR_DONE, i + 1);
	}
}


/*
 * DefineIndex
 *		Creates a new index.
 *
 * This function manages the current userid according to the needs of pg_dump.
 * Recreating old-database catalog entries in new-database is fine, regardless
 * of which users would have permission to recreate those entries now.  That's
 * just preservation of state.  Running opaque expressions, like calling a
 * function named in a catalog entry or evaluating a pg_node_tree in a catalog
 * entry, as anyone other than the object owner, is not fine.  To adhere to
 * those principles and to remain fail-safe, use the table owner userid for
 * most ACL checks.  Use the original userid for ACL checks reached without
 * traversing opaque expressions.  (pg_dump can predict such ACL checks from
 * catalogs.)  Overall, this is a mess.  Future DDL development should
 * consider offering one DDL command for catalog setup and a separate DDL
 * command for steps that run opaque expressions.
 *
 * 'pstate': ParseState struct (used only for error reports; pass NULL if
 *		not available)
 * 'tableId': the OID of the table relation on which the index is to be
 *		created
 * 'stmt': IndexStmt describing the properties of the new index.
 * 'indexRelationId': normally InvalidOid, but during bootstrap can be
 *		nonzero to specify a preselected OID for the index.
 * 'parentIndexId': the OID of the parent index; InvalidOid if not the child
 *		of a partitioned index.
 * 'parentConstraintId': the OID of the parent constraint; InvalidOid if not
 *		the child of a constraint (only used when recursing)
 * 'total_parts': total number of direct and indirect partitions of relation;
 *		pass -1 if not known or rel is not partitioned.
 * 'is_alter_table': this is due to an ALTER rather than a CREATE operation.
 * 'check_rights': check for CREATE rights in namespace and tablespace.  (This
 *		should be true except when ALTER is deleting/recreating an index.)
 * 'check_not_in_use': check for table not already in use in current session.
 *		This should be true unless caller is holding the table open, in which
 *		case the caller had better have checked it earlier.
 * 'skip_build': make the catalog entries but don't create the index files
 * 'quiet': suppress the NOTICE chatter ordinarily provided for constraints.
 *
 * Returns the object address of the created index.
 */
ObjectAddress
DefineIndex(ParseState *pstate,
			Oid tableId,
			const IndexStmt *stmt,
			Oid indexRelationId,
			Oid parentIndexId,
			Oid parentConstraintId,
			int total_parts,
			bool is_alter_table,
			bool check_rights,
			bool check_not_in_use,
			bool skip_build,
			bool quiet)
{
	bool		concurrent;
	char	   *indexRelationName;
	char	   *accessMethodName;
	Oid		   *typeIds;
	Oid		   *collationIds;
	Oid		   *opclassIds;
	Datum	   *opclassOptions;
	Oid			accessMethodId;
	Oid			namespaceId;
	Oid			tablespaceId;
	Oid			createdConstraintId = InvalidOid;
	List	   *indexColNames;
	List	   *allIndexParams;
	Relation	rel;
	HeapTuple	tuple;
	Form_pg_am	accessMethodForm;
	const IndexAmRoutine *amRoutine;
	bool		amcanorder;
	bool		amissummarizing;
	amoptions_function amoptions;
	bool		exclusion;
	bool		partitioned;
	bool		safe_index;
	Datum		reloptions;
	int16	   *coloptions;
	IndexInfo  *indexInfo;
	uint16		flags;
	uint16		constr_flags;
	int			numberOfAttributes;
	int			numberOfKeyAttributes;
	TransactionId limitXmin;
	ObjectAddress address;
	LockRelId	heaprelid;
	LOCKTAG		heaplocktag;
	LOCKMODE	lockmode;
	Snapshot	snapshot;
	Oid			root_save_userid;
	int			root_save_sec_context;
	int			root_save_nestlevel;

	root_save_nestlevel = NewGUCNestLevel();

	RestrictSearchPath();

	/*
	 * Some callers need us to run with an empty default_tablespace; this is a
	 * necessary hack to be able to reproduce catalog state accurately when
	 * recreating indexes after table-rewriting ALTER TABLE.
	 */
	if (stmt->reset_default_tblspc)
		(void) set_config_option("default_tablespace", "",
								 PGC_USERSET, PGC_S_SESSION,
								 GUC_ACTION_SAVE, true, 0, false);

	/*
	 * Force non-concurrent build on temporary relations, even if CONCURRENTLY
	 * was requested.  Other backends can't access a temporary relation, so
	 * there's no harm in grabbing a stronger lock, and a non-concurrent DROP
	 * is more efficient.  Do this before any use of the concurrent option is
	 * done.
	 */
	if (stmt->concurrent && get_rel_persistence(tableId) != RELPERSISTENCE_TEMP)
		concurrent = true;
	else
		concurrent = false;

	/*
	 * Start progress report.  If we're building a partition, this was already
	 * done.
	 */
	if (!OidIsValid(parentIndexId))
	{
		pgstat_progress_start_command(PROGRESS_COMMAND_CREATE_INDEX, tableId);
		pgstat_progress_update_param(PROGRESS_CREATEIDX_COMMAND,
									 concurrent ?
									 PROGRESS_CREATEIDX_COMMAND_CREATE_CONCURRENTLY :
									 PROGRESS_CREATEIDX_COMMAND_CREATE);
	}

	/*
	 * No index OID to report yet
	 */
	pgstat_progress_update_param(PROGRESS_CREATEIDX_INDEX_OID,
								 InvalidOid);

	/*
	 * count key attributes in index
	 */
	numberOfKeyAttributes = list_length(stmt->indexParams);

	/*
	 * Calculate the new list of index columns including both key columns and
	 * INCLUDE columns.  Later we can determine which of these are key
	 * columns, and which are just part of the INCLUDE list by checking the
	 * list position.  A list item in a position less than ii_NumIndexKeyAttrs
	 * is part of the key columns, and anything equal to and over is part of
	 * the INCLUDE columns.
	 */
	allIndexParams = list_concat_copy(stmt->indexParams,
									  stmt->indexIncludingParams);
	numberOfAttributes = list_length(allIndexParams);

	if (numberOfKeyAttributes <= 0)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
				 errmsg("must specify at least one column")));
	if (numberOfAttributes > INDEX_MAX_KEYS)
		ereport(ERROR,
				(errcode(ERRCODE_TOO_MANY_COLUMNS),
				 errmsg("cannot use more than %d columns in an index",
						INDEX_MAX_KEYS)));

	/*
	 * Only SELECT ... FOR UPDATE/SHARE are allowed while doing a standard
	 * index build; but for concurrent builds we allow INSERT/UPDATE/DELETE
	 * (but not VACUUM).
	 *
	 * NB: Caller is responsible for making sure that tableId refers to the
	 * relation on which the index should be built; except in bootstrap mode,
	 * this will typically require the caller to have already locked the
	 * relation.  To avoid lock upgrade hazards, that lock should be at least
	 * as strong as the one we take here.
	 *
	 * NB: If the lock strength here ever changes, code that is run by
	 * parallel workers under the control of certain particular ambuild
	 * functions will need to be updated, too.
	 */
	lockmode = concurrent ? ShareUpdateExclusiveLock : ShareLock;
	rel = table_open(tableId, lockmode);

	/*
	 * Switch to the table owner's userid, so that any index functions are run
	 * as that user.  Also lock down security-restricted operations.  We
	 * already arranged to make GUC variable changes local to this command.
	 */
	GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context);
	SetUserIdAndSecContext(rel->rd_rel->relowner,
						   root_save_sec_context | SECURITY_RESTRICTED_OPERATION);

	namespaceId = RelationGetNamespace(rel);

	/*
	 * It has exclusion constraint behavior if it's an EXCLUDE constraint or a
	 * temporal PRIMARY KEY/UNIQUE constraint
	 */
	exclusion = stmt->excludeOpNames || stmt->iswithoutoverlaps;

	/* Ensure that it makes sense to index this kind of relation */
	switch (rel->rd_rel->relkind)
	{
		case RELKIND_RELATION:
		case RELKIND_MATVIEW:
		case RELKIND_PARTITIONED_TABLE:
			/* OK */
			break;
		default:
			ereport(ERROR,
					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
					 errmsg("cannot create index on relation \"%s\"",
							RelationGetRelationName(rel)),
					 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
			break;
	}

	/*
	 * Establish behavior for partitioned tables, and verify sanity of
	 * parameters.
	 *
	 * We do not build an actual index in this case; we only create a few
	 * catalog entries.  The actual indexes are built by recursing for each
	 * partition.
	 */
	partitioned = rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE;
	if (partitioned)
	{
		/*
		 * Note: we check 'stmt->concurrent' rather than 'concurrent', so that
		 * the error is thrown also for temporary tables.  Seems better to be
		 * consistent, even though we could do it on temporary table because
		 * we're not actually doing it concurrently.
		 */
		if (stmt->concurrent)
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("cannot create index on partitioned table \"%s\" concurrently",
							RelationGetRelationName(rel))));
	}

	/*
	 * Don't try to CREATE INDEX on temp tables of other backends.
	 */
	if (RELATION_IS_OTHER_TEMP(rel))
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("cannot create indexes on temporary tables of other sessions")));

	/*
	 * Unless our caller vouches for having checked this already, insist that
	 * the table not be in use by our own session, either.  Otherwise we might
	 * fail to make entries in the new index (for instance, if an INSERT or
	 * UPDATE is in progress and has already made its list of target indexes).
	 */
	if (check_not_in_use)
		CheckTableNotInUse(rel, "CREATE INDEX");

	/*
	 * Verify we (still) have CREATE rights in the rel's namespace.
	 * (Presumably we did when the rel was created, but maybe not anymore.)
	 * Skip check if caller doesn't want it.  Also skip check if
	 * bootstrapping, since permissions machinery may not be working yet.
	 */
	if (check_rights && !IsBootstrapProcessingMode())
	{
		AclResult	aclresult;

		aclresult = object_aclcheck(NamespaceRelationId, namespaceId, root_save_userid,
									ACL_CREATE);
		if (aclresult != ACLCHECK_OK)
			aclcheck_error(aclresult, OBJECT_SCHEMA,
						   get_namespace_name(namespaceId));
	}

	/*
	 * Select tablespace to use.  If not specified, use default tablespace
	 * (which may in turn default to database's default).
	 */
	if (stmt->tableSpace)
	{
		tablespaceId = get_tablespace_oid(stmt->tableSpace, false);
		if (partitioned && tablespaceId == MyDatabaseTableSpace)
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("cannot specify default tablespace for partitioned relations")));
	}
	else
	{
		tablespaceId = GetDefaultTablespace(rel->rd_rel->relpersistence,
											partitioned);
		/* note InvalidOid is OK in this case */
	}

	/* Check tablespace permissions */
	if (check_rights &&
		OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
	{
		AclResult	aclresult;

		aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, root_save_userid,
									ACL_CREATE);
		if (aclresult != ACLCHECK_OK)
			aclcheck_error(aclresult, OBJECT_TABLESPACE,
						   get_tablespace_name(tablespaceId));
	}

	/*
	 * Force shared indexes into the pg_global tablespace.  This is a bit of a
	 * hack but seems simpler than marking them in the BKI commands.  On the
	 * other hand, if it's not shared, don't allow it to be placed there.
	 */
	if (rel->rd_rel->relisshared)
		tablespaceId = GLOBALTABLESPACE_OID;
	else if (tablespaceId == GLOBALTABLESPACE_OID)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("only shared relations can be placed in pg_global tablespace")));

	/*
	 * Choose the index column names.
	 */
	indexColNames = ChooseIndexColumnNames(allIndexParams);

	/*
	 * Select name for index if caller didn't specify
	 */
	indexRelationName = stmt->idxname;
	if (indexRelationName == NULL)
		indexRelationName = ChooseIndexName(RelationGetRelationName(rel),
											namespaceId,
											indexColNames,
											stmt->excludeOpNames,
											stmt->primary,
											stmt->isconstraint);

	/*
	 * look up the access method, verify it can handle the requested features
	 */
	accessMethodName = stmt->accessMethod;
	tuple = SearchSysCache1(AMNAME, PointerGetDatum(accessMethodName));
	if (!HeapTupleIsValid(tuple))
	{
		/*
		 * Hack to provide more-or-less-transparent updating of old RTREE
		 * indexes to GiST: if RTREE is requested and not found, use GIST.
		 */
		if (strcmp(accessMethodName, "rtree") == 0)
		{
			ereport(NOTICE,
					(errmsg("substituting access method \"gist\" for obsolete method \"rtree\"")));
			accessMethodName = "gist";
			tuple = SearchSysCache1(AMNAME, PointerGetDatum(accessMethodName));
		}

		if (!HeapTupleIsValid(tuple))
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_OBJECT),
					 errmsg("access method \"%s\" does not exist",
							accessMethodName)));
	}
	accessMethodForm = (Form_pg_am) GETSTRUCT(tuple);
	accessMethodId = accessMethodForm->oid;
	amRoutine = GetIndexAmRoutine(accessMethodForm->amhandler);

	pgstat_progress_update_param(PROGRESS_CREATEIDX_ACCESS_METHOD_OID,
								 accessMethodId);

	if (stmt->unique && !stmt->iswithoutoverlaps && !amRoutine->amcanunique)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("access method \"%s\" does not support unique indexes",
						accessMethodName)));
	if (stmt->indexIncludingParams != NIL && !amRoutine->amcaninclude)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("access method \"%s\" does not support included columns",
						accessMethodName)));
	if (numberOfKeyAttributes > 1 && !amRoutine->amcanmulticol)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("access method \"%s\" does not support multicolumn indexes",
						accessMethodName)));
	if (exclusion && amRoutine->amgettuple == NULL)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("access method \"%s\" does not support exclusion constraints",
						accessMethodName)));
	if (stmt->iswithoutoverlaps && strcmp(accessMethodName, "gist") != 0)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("access method \"%s\" does not support WITHOUT OVERLAPS constraints",
						accessMethodName)));

	amcanorder = amRoutine->amcanorder;
	amoptions = amRoutine->amoptions;
	amissummarizing = amRoutine->amsummarizing;

	ReleaseSysCache(tuple);

	/*
	 * Validate predicate, if given
	 */
	if (stmt->whereClause)
		CheckPredicate((Expr *) stmt->whereClause);

	/*
	 * Parse AM-specific options, convert to text array form, validate.
	 */
	reloptions = transformRelOptions((Datum) 0, stmt->options,
									 NULL, NULL, false, false);

	(void) index_reloptions(amoptions, reloptions, true);

	/*
	 * Prepare arguments for index_create, primarily an IndexInfo structure.
	 * Note that predicates must be in implicit-AND format.  In a concurrent
	 * build, mark it not-ready-for-inserts.
	 */
	indexInfo = makeIndexInfo(numberOfAttributes,
							  numberOfKeyAttributes,
							  accessMethodId,
							  NIL,	/* expressions, NIL for now */
							  make_ands_implicit((Expr *) stmt->whereClause),
							  stmt->unique,
							  stmt->nulls_not_distinct,
							  !concurrent,
							  concurrent,
							  amissummarizing,
							  stmt->iswithoutoverlaps);
	indexInfo->ii_PredicateExpand =
		ExpandVirtualGeneratedColumns(indexInfo->ii_PredicateExpand, rel, InvalidOid);

	typeIds = palloc_array(Oid, numberOfAttributes);
	collationIds = palloc_array(Oid, numberOfAttributes);
	opclassIds = palloc_array(Oid, numberOfAttributes);
	opclassOptions = palloc_array(Datum, numberOfAttributes);
	coloptions = palloc_array(int16, numberOfAttributes);
	ComputeIndexAttrs(pstate,
					  indexInfo,
					  typeIds, collationIds, opclassIds, opclassOptions,
					  coloptions, allIndexParams,
					  stmt->excludeOpNames, tableId,
					  accessMethodName, accessMethodId,
					  amcanorder, stmt->isconstraint, stmt->iswithoutoverlaps,
					  root_save_userid, root_save_sec_context,
					  &root_save_nestlevel);

	indexInfo->ii_ExpressionsExpand =
		ExpandVirtualGeneratedColumns(indexInfo->ii_ExpressionsExpand, rel, InvalidOid);
	/*
	 * Extra checks when creating a PRIMARY KEY index.
	 */
	if (stmt->primary)
		index_check_primary_key(rel, indexInfo, is_alter_table, stmt);

	/*
	 * If this table is partitioned and we're creating a unique index, primary
	 * key, or exclusion constraint, make sure that the partition key is a
	 * subset of the index's columns.  Otherwise it would be possible to
	 * violate uniqueness by putting values that ought to be unique in
	 * different partitions.
	 *
	 * We could lift this limitation if we had global indexes, but those have
	 * their own problems, so this is a useful feature combination.
	 */
	if (partitioned && (stmt->unique || exclusion))
	{
		PartitionKey key = RelationGetPartitionKey(rel);
		const char *constraint_type;
		int			i;

		if (stmt->primary)
			constraint_type = "PRIMARY KEY";
		else if (stmt->unique)
			constraint_type = "UNIQUE";
		else if (stmt->excludeOpNames)
			constraint_type = "EXCLUDE";
		else
		{
			elog(ERROR, "unknown constraint type");
			constraint_type = NULL; /* keep compiler quiet */
		}

		/*
		 * Verify that all the columns in the partition key appear in the
		 * unique key definition, with the same notion of equality.
		 */
		for (i = 0; i < key->partnatts; i++)
		{
			bool		found = false;
			int			eq_strategy;
			Oid			ptkey_eqop;
			int			j;

			/*
			 * Identify the equality operator associated with this partkey
			 * column.  For list and range partitioning, partkeys use btree
			 * operator classes; hash partitioning uses hash operator classes.
			 * (Keep this in sync with ComputePartitionAttrs!)
			 */
			if (key->strategy == PARTITION_STRATEGY_HASH)
				eq_strategy = HTEqualStrategyNumber;
			else
				eq_strategy = BTEqualStrategyNumber;

			ptkey_eqop = get_opfamily_member(key->partopfamily[i],
											 key->partopcintype[i],
											 key->partopcintype[i],
											 eq_strategy);
			if (!OidIsValid(ptkey_eqop))
				elog(ERROR, "missing operator %d(%u,%u) in partition opfamily %u",
					 eq_strategy, key->partopcintype[i], key->partopcintype[i],
					 key->partopfamily[i]);

			/*
			 * It may be possible to support UNIQUE constraints when partition
			 * keys are expressions, but is it worth it?  Give up for now.
			 */
			if (key->partattrs[i] == 0)
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("unsupported %s constraint with partition key definition",
								constraint_type),
						 errdetail("%s constraints cannot be used when partition keys include expressions.",
								   constraint_type)));

			/* Search the index column(s) for a match */
			for (j = 0; j < indexInfo->ii_NumIndexKeyAttrs; j++)
			{
				if (key->partattrs[i] == indexInfo->ii_IndexAttrNumbers[j])
				{
					/*
					 * Matched the column, now what about the collation and
					 * equality op?
					 */
					Oid			idx_opfamily;
					Oid			idx_opcintype;

					if (key->partcollation[i] != collationIds[j])
						continue;

					if (get_opclass_opfamily_and_input_type(opclassIds[j],
															&idx_opfamily,
															&idx_opcintype))
					{
						Oid			idx_eqop = InvalidOid;

						if (stmt->unique && !stmt->iswithoutoverlaps)
							idx_eqop = get_opfamily_member_for_cmptype(idx_opfamily,
																	   idx_opcintype,
																	   idx_opcintype,
																	   COMPARE_EQ);
						else if (exclusion)
							idx_eqop = indexInfo->ii_ExclusionOps[j];

						if (!idx_eqop)
							ereport(ERROR,
									errcode(ERRCODE_UNDEFINED_OBJECT),
									errmsg("could not identify an equality operator for type %s", format_type_be(idx_opcintype)),
									errdetail("There is no suitable operator in operator family \"%s\" for access method \"%s\".",
											  get_opfamily_name(idx_opfamily, false), get_am_name(get_opfamily_method(idx_opfamily))));

						if (ptkey_eqop == idx_eqop)
						{
							found = true;
							break;
						}
						else if (exclusion)
						{
							/*
							 * We found a match, but it's not an equality
							 * operator. Instead of failing below with an
							 * error message about a missing column, fail now
							 * and explain that the operator is wrong.
							 */
							Form_pg_attribute att = TupleDescAttr(RelationGetDescr(rel), key->partattrs[i] - 1);

							ereport(ERROR,
									(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
									 errmsg("cannot match partition key to index on column \"%s\" using non-equal operator \"%s\"",
											NameStr(att->attname),
											get_opname(indexInfo->ii_ExclusionOps[j]))));
						}
					}
				}
			}

			if (!found)
			{
				Form_pg_attribute att;

				att = TupleDescAttr(RelationGetDescr(rel),
									key->partattrs[i] - 1);
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				/* translator: %s is UNIQUE, PRIMARY KEY, etc */
						 errmsg("%s constraint on partitioned table must include all partitioning columns",
								constraint_type),
				/* translator: first %s is UNIQUE, PRIMARY KEY, etc */
						 errdetail("%s constraint on table \"%s\" lacks column \"%s\" which is part of the partition key.",
								   constraint_type, RelationGetRelationName(rel),
								   NameStr(att->attname))));
			}
		}
	}


	/*
	 * We disallow indexes on system columns.  They would not necessarily get
	 * updated correctly, and they don't seem useful anyway.
	 *
	 * Aisallow virtual generated columns in indexes (include using expression
	 * index instead).
	 */
	for (int i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
	{
		AttrNumber	attno = indexInfo->ii_IndexAttrNumbers[i];

		if (attno < 0)
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("index creation on system columns is not supported")));


		/*if (attno > 0 && i < numberOfKeyAttributes &&
			TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
			ereport(ERROR,
					errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					stmt->primary ?
					errmsg("primary keys on virtual generated columns are not supported") :
					stmt->isconstraint ?
					errmsg("unique constraints on virtual generated columns are not supported") :
					errmsg("indexes on virtual generated columns are not supported"));*/
	}

	/*
	 * Also check for system columns used in expressions or predicates.
	 */
	if (indexInfo->ii_Expressions || indexInfo->ii_Predicate)
	{
		Bitmapset  *indexattrs = NULL;

		pull_varattnos((Node *) indexInfo->ii_Expressions, 1, &indexattrs);
		pull_varattnos((Node *) indexInfo->ii_Predicate, 1, &indexattrs);

		for (int i = FirstLowInvalidHeapAttributeNumber + 1; i < 0; i++)
		{
			if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
							  indexattrs))
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("index creation on system columns is not supported")));
		}
	}

	/* Is index safe for others to ignore?  See set_indexsafe_procflags() */
	safe_index = indexInfo->ii_Expressions == NIL &&
		indexInfo->ii_Predicate == NIL;

	/*
	 * Report index creation if appropriate (delay this till after most of the
	 * error checks)
	 */
	if (stmt->isconstraint && !quiet)
	{
		const char *constraint_type;

		if (stmt->primary)
			constraint_type = "PRIMARY KEY";
		else if (stmt->unique)
			constraint_type = "UNIQUE";
		else if (stmt->excludeOpNames)
			constraint_type = "EXCLUDE";
		else
		{
			elog(ERROR, "unknown constraint type");
			constraint_type = NULL; /* keep compiler quiet */
		}

		ereport(DEBUG1,
				(errmsg_internal("%s %s will create implicit index \"%s\" for table \"%s\"",
								 is_alter_table ? "ALTER TABLE / ADD" : "CREATE TABLE /",
								 constraint_type,
								 indexRelationName, RelationGetRelationName(rel))));
	}

	/*
	 * A valid stmt->oldNumber implies that we already have a built form of
	 * the index.  The caller should also decline any index build.
	 */
	Assert(!RelFileNumberIsValid(stmt->oldNumber) || (skip_build && !concurrent));

	/*
	 * Make the catalog entries for the index, including constraints. This
	 * step also actually builds the index, except if caller requested not to
	 * or in concurrent mode, in which case it'll be done later, or doing a
	 * partitioned index (because those don't have storage).
	 */
	flags = constr_flags = 0;
	if (stmt->isconstraint)
		flags |= INDEX_CREATE_ADD_CONSTRAINT;
	if (skip_build || concurrent || partitioned)
		flags |= INDEX_CREATE_SKIP_BUILD;
	if (stmt->if_not_exists)
		flags |= INDEX_CREATE_IF_NOT_EXISTS;
	if (concurrent)
		flags |= INDEX_CREATE_CONCURRENT;
	if (partitioned)
		flags |= INDEX_CREATE_PARTITIONED;
	if (stmt->primary)
		flags |= INDEX_CREATE_IS_PRIMARY;

	/*
	 * If the table is partitioned, and recursion was declined but partitions
	 * exist, mark the index as invalid.
	 */
	if (partitioned && stmt->relation && !stmt->relation->inh)
	{
		PartitionDesc pd = RelationGetPartitionDesc(rel, true);

		if (pd->nparts != 0)
			flags |= INDEX_CREATE_INVALID;
	}

	if (stmt->deferrable)
		constr_flags |= INDEX_CONSTR_CREATE_DEFERRABLE;
	if (stmt->initdeferred)
		constr_flags |= INDEX_CONSTR_CREATE_INIT_DEFERRED;
	if (stmt->iswithoutoverlaps)
		constr_flags |= INDEX_CONSTR_CREATE_WITHOUT_OVERLAPS;

	indexRelationId =
		index_create(rel, indexRelationName, indexRelationId, parentIndexId,
					 parentConstraintId,
					 stmt->oldNumber, indexInfo, indexColNames,
					 accessMethodId, tablespaceId,
					 collationIds, opclassIds, opclassOptions,
					 coloptions, NULL, reloptions,
					 flags, constr_flags,
					 allowSystemTableMods, !check_rights,
					 &createdConstraintId);

	ObjectAddressSet(address, RelationRelationId, indexRelationId);

	if (!OidIsValid(indexRelationId))
	{
		/*
		 * Roll back any GUC changes executed by index functions.  Also revert
		 * to original default_tablespace if we changed it above.
		 */
		AtEOXact_GUC(false, root_save_nestlevel);

		/* Restore userid and security context */
		SetUserIdAndSecContext(root_save_userid, root_save_sec_context);

		table_close(rel, NoLock);

		/* If this is the top-level index, we're done */
		if (!OidIsValid(parentIndexId))
			pgstat_progress_end_command();

		return address;
	}

	/*
	 * Roll back any GUC changes executed by index functions, and keep
	 * subsequent changes local to this command.  This is essential if some
	 * index function changed a behavior-affecting GUC, e.g. search_path.
	 */
	AtEOXact_GUC(false, root_save_nestlevel);
	root_save_nestlevel = NewGUCNestLevel();
	RestrictSearchPath();

	/* Add any requested comment */
	if (stmt->idxcomment != NULL)
		CreateComments(indexRelationId, RelationRelationId, 0,
					   stmt->idxcomment);

	if (partitioned)
	{
		PartitionDesc partdesc;

		/*
		 * Unless caller specified to skip this step (via ONLY), process each
		 * partition to make sure they all contain a corresponding index.
		 *
		 * If we're called internally (no stmt->relation), recurse always.
		 */
		partdesc = RelationGetPartitionDesc(rel, true);
		if ((!stmt->relation || stmt->relation->inh) && partdesc->nparts > 0)
		{
			int			nparts = partdesc->nparts;
			Oid		   *part_oids = palloc_array(Oid, nparts);
			bool		invalidate_parent = false;
			Relation	parentIndex;
			TupleDesc	parentDesc;

			/*
			 * Report the total number of partitions at the start of the
			 * command; don't update it when being called recursively.
			 */
			if (!OidIsValid(parentIndexId))
			{
				/*
				 * When called by ProcessUtilitySlow, the number of partitions
				 * is passed in as an optimization; but other callers pass -1
				 * since they don't have the value handy.  This should count
				 * partitions the same way, ie one less than the number of
				 * relations find_all_inheritors reports.
				 *
				 * We assume we needn't ask find_all_inheritors to take locks,
				 * because that should have happened already for all callers.
				 * Even if it did not, this is safe as long as we don't try to
				 * touch the partitions here; the worst consequence would be a
				 * bogus progress-reporting total.
				 */
				if (total_parts < 0)
				{
					List	   *children = find_all_inheritors(tableId, NoLock, NULL);

					total_parts = list_length(children) - 1;
					list_free(children);
				}

				pgstat_progress_update_param(PROGRESS_CREATEIDX_PARTITIONS_TOTAL,
											 total_parts);
			}

			/* Make a local copy of partdesc->oids[], just for safety */
			memcpy(part_oids, partdesc->oids, sizeof(Oid) * nparts);

			/*
			 * We'll need an IndexInfo describing the parent index.  The one
			 * built above is almost good enough, but not quite, because (for
			 * example) its predicate expression if any hasn't been through
			 * expression preprocessing.  The most reliable way to get an
			 * IndexInfo that will match those for child indexes is to build
			 * it the same way, using BuildIndexInfo().
			 */
			parentIndex = index_open(indexRelationId, lockmode);
			indexInfo = BuildIndexInfo(parentIndex);

			parentDesc = RelationGetDescr(rel);

			/*
			 * For each partition, scan all existing indexes; if one matches
			 * our index definition and is not already attached to some other
			 * parent index, attach it to the one we just created.
			 *
			 * If none matches, build a new index by calling ourselves
			 * recursively with the same options (except for the index name).
			 */
			for (int i = 0; i < nparts; i++)
			{
				Oid			childRelid = part_oids[i];
				Relation	childrel;
				Oid			child_save_userid;
				int			child_save_sec_context;
				int			child_save_nestlevel;
				List	   *childidxs;
				ListCell   *cell;
				AttrMap    *attmap;
				bool		found = false;

				childrel = table_open(childRelid, lockmode);

				GetUserIdAndSecContext(&child_save_userid,
									   &child_save_sec_context);
				SetUserIdAndSecContext(childrel->rd_rel->relowner,
									   child_save_sec_context | SECURITY_RESTRICTED_OPERATION);
				child_save_nestlevel = NewGUCNestLevel();
				RestrictSearchPath();

				/*
				 * Don't try to create indexes on foreign tables, though. Skip
				 * those if a regular index, or fail if trying to create a
				 * constraint index.
				 */
				if (childrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
				{
					if (stmt->unique || stmt->primary)
						ereport(ERROR,
								(errcode(ERRCODE_WRONG_OBJECT_TYPE),
								 errmsg("cannot create unique index on partitioned table \"%s\"",
										RelationGetRelationName(rel)),
								 errdetail("Table \"%s\" contains partitions that are foreign tables.",
										   RelationGetRelationName(rel))));

					AtEOXact_GUC(false, child_save_nestlevel);
					SetUserIdAndSecContext(child_save_userid,
										   child_save_sec_context);
					table_close(childrel, lockmode);
					continue;
				}

				childidxs = RelationGetIndexList(childrel);
				attmap =
					build_attrmap_by_name(RelationGetDescr(childrel),
										  parentDesc,
										  false);

				foreach(cell, childidxs)
				{
					Oid			cldidxid = lfirst_oid(cell);
					Relation	cldidx;
					IndexInfo  *cldIdxInfo;

					/* this index is already partition of another one */
					if (has_superclass(cldidxid))
						continue;

					cldidx = index_open(cldidxid, lockmode);
					cldIdxInfo = BuildIndexInfo(cldidx);
					if (CompareIndexInfo(cldIdxInfo, indexInfo,
										 cldidx->rd_indcollation,
										 parentIndex->rd_indcollation,
										 cldidx->rd_opfamily,
										 parentIndex->rd_opfamily,
										 attmap))
					{
						Oid			cldConstrOid = InvalidOid;

						/*
						 * Found a match.
						 *
						 * If this index is being created in the parent
						 * because of a constraint, then the child needs to
						 * have a constraint also, so look for one.  If there
						 * is no such constraint, this index is no good, so
						 * keep looking.
						 */
						if (createdConstraintId != InvalidOid)
						{
							cldConstrOid =
								get_relation_idx_constraint_oid(childRelid,
																cldidxid);
							if (cldConstrOid == InvalidOid)
							{
								index_close(cldidx, lockmode);
								continue;
							}
						}

						/* Attach index to parent and we're done. */
						IndexSetParentIndex(cldidx, indexRelationId);
						if (createdConstraintId != InvalidOid)
							ConstraintSetParentConstraint(cldConstrOid,
														  createdConstraintId,
														  childRelid);

						if (!cldidx->rd_index->indisvalid)
							invalidate_parent = true;

						found = true;

						/*
						 * Report this partition as processed.  Note that if
						 * the partition has children itself, we'd ideally
						 * count the children and update the progress report
						 * for all of them; but that seems unduly expensive.
						 * Instead, the progress report will act like all such
						 * indirect children were processed in zero time at
						 * the end of the command.
						 */
						pgstat_progress_incr_param(PROGRESS_CREATEIDX_PARTITIONS_DONE, 1);

						/* keep lock till commit */
						index_close(cldidx, NoLock);
						break;
					}

					index_close(cldidx, lockmode);
				}

				list_free(childidxs);
				AtEOXact_GUC(false, child_save_nestlevel);
				SetUserIdAndSecContext(child_save_userid,
									   child_save_sec_context);
				table_close(childrel, NoLock);

				/*
				 * If no matching index was found, create our own.
				 */
				if (!found)
				{
					IndexStmt  *childStmt;
					ObjectAddress childAddr;

					/*
					 * Build an IndexStmt describing the desired child index
					 * in the same way that we do during ATTACH PARTITION.
					 * Notably, we rely on generateClonedIndexStmt to produce
					 * a search-path-independent representation, which the
					 * original IndexStmt might not be.
					 */
					childStmt = generateClonedIndexStmt(NULL,
														parentIndex,
														attmap,
														NULL);

					/*
					 * Recurse as the starting user ID.  Callee will use that
					 * for permission checks, then switch again.
					 */
					Assert(GetUserId() == child_save_userid);
					SetUserIdAndSecContext(root_save_userid,
										   root_save_sec_context);
					childAddr =
						DefineIndex(NULL,	/* original pstate not applicable */
									childRelid, childStmt,
									InvalidOid, /* no predefined OID */
									indexRelationId,	/* this is our child */
									createdConstraintId,
									-1,
									is_alter_table, check_rights,
									check_not_in_use,
									skip_build, quiet);
					SetUserIdAndSecContext(child_save_userid,
										   child_save_sec_context);

					/*
					 * Check if the index just created is valid or not, as it
					 * could be possible that it has been switched as invalid
					 * when recursing across multiple partition levels.
					 */
					if (!get_index_isvalid(childAddr.objectId))
						invalidate_parent = true;
				}

				free_attrmap(attmap);
			}

			index_close(parentIndex, lockmode);

			/*
			 * The pg_index row we inserted for this index was marked
			 * indisvalid=true.  But if we attached an existing index that is
			 * invalid, this is incorrect, so update our row to invalid too.
			 */
			if (invalidate_parent)
			{
				Relation	pg_index = table_open(IndexRelationId, RowExclusiveLock);
				HeapTuple	tup,
							newtup;

				tup = SearchSysCache1(INDEXRELID,
									  ObjectIdGetDatum(indexRelationId));
				if (!HeapTupleIsValid(tup))
					elog(ERROR, "cache lookup failed for index %u",
						 indexRelationId);
				newtup = heap_copytuple(tup);
				((Form_pg_index) GETSTRUCT(newtup))->indisvalid = false;
				CatalogTupleUpdate(pg_index, &tup->t_self, newtup);
				ReleaseSysCache(tup);
				table_close(pg_index, RowExclusiveLock);
				heap_freetuple(newtup);

				/*
				 * CCI here to make this update visible, in case this recurses
				 * across multiple partition levels.
				 */
				CommandCounterIncrement();
			}
		}

		/*
		 * Indexes on partitioned tables are not themselves built, so we're
		 * done here.
		 */
		AtEOXact_GUC(false, root_save_nestlevel);
		SetUserIdAndSecContext(root_save_userid, root_save_sec_context);
		table_close(rel, NoLock);
		if (!OidIsValid(parentIndexId))
			pgstat_progress_end_command();
		else
		{
			/* Update progress for an intermediate partitioned index itself */
			pgstat_progress_incr_param(PROGRESS_CREATEIDX_PARTITIONS_DONE, 1);
		}

		return address;
	}

	AtEOXact_GUC(false, root_save_nestlevel);
	SetUserIdAndSecContext(root_save_userid, root_save_sec_context);

	if (!concurrent)
	{
		/* Close the heap and we're done, in the non-concurrent case */
		table_close(rel, NoLock);

		/*
		 * If this is the top-level index, the command is done overall;
		 * otherwise, increment progress to report one child index is done.
		 */
		if (!OidIsValid(parentIndexId))
			pgstat_progress_end_command();
		else
			pgstat_progress_incr_param(PROGRESS_CREATEIDX_PARTITIONS_DONE, 1);

		return address;
	}

	/* save lockrelid and locktag for below, then close rel */
	heaprelid = rel->rd_lockInfo.lockRelId;
	SET_LOCKTAG_RELATION(heaplocktag, heaprelid.dbId, heaprelid.relId);
	table_close(rel, NoLock);

	/*
	 * For a concurrent build, it's important to make the catalog entries
	 * visible to other transactions before we start to build the index. That
	 * will prevent them from making incompatible HOT updates.  The new index
	 * will be marked not indisready and not indisvalid, so that no one else
	 * tries to either insert into it or use it for queries.
	 *
	 * We must commit our current transaction so that the index becomes
	 * visible; then start another.  Note that all the data structures we just
	 * built are lost in the commit.  The only data we keep past here are the
	 * relation IDs.
	 *
	 * Before committing, get a session-level lock on the table, to ensure
	 * that neither it nor the index can be dropped before we finish. This
	 * cannot block, even if someone else is waiting for access, because we
	 * already have the same lock within our transaction.
	 *
	 * Note: we don't currently bother with a session lock on the index,
	 * because there are no operations that could change its state while we
	 * hold lock on the parent table.  This might need to change later.
	 */
	LockRelationIdForSession(&heaprelid, ShareUpdateExclusiveLock);

	PopActiveSnapshot();
	CommitTransactionCommand();
	StartTransactionCommand();

	/* Tell concurrent index builds to ignore us, if index qualifies */
	if (safe_index)
		set_indexsafe_procflags();

	/*
	 * The index is now visible, so we can report the OID.  While on it,
	 * include the report for the beginning of phase 2.
	 */
	{
		const int	progress_cols[] = {
			PROGRESS_CREATEIDX_INDEX_OID,
			PROGRESS_CREATEIDX_PHASE
		};
		const int64 progress_vals[] = {
			indexRelationId,
			PROGRESS_CREATEIDX_PHASE_WAIT_1
		};

		pgstat_progress_update_multi_param(2, progress_cols, progress_vals);
	}

	/*
	 * Phase 2 of concurrent index build (see comments for validate_index()
	 * for an overview of how this works)
	 *
	 * Now we must wait until no running transaction could have the table open
	 * with the old list of indexes.  Use ShareLock to consider running
	 * transactions that hold locks that permit writing to the table.  Note we
	 * do not need to worry about xacts that open the table for writing after
	 * this point; they will see the new index when they open it.
	 *
	 * Note: the reason we use actual lock acquisition here, rather than just
	 * checking the ProcArray and sleeping, is that deadlock is possible if
	 * one of the transactions in question is blocked trying to acquire an
	 * exclusive lock on our table.  The lock code will detect deadlock and
	 * error out properly.
	 */
	WaitForLockers(heaplocktag, ShareLock, true);

	/*
	 * At this moment we are sure that there are no transactions with the
	 * table open for write that don't have this new index in their list of
	 * indexes.  We have waited out all the existing transactions and any new
	 * transaction will have the new index in its list, but the index is still
	 * marked as "not-ready-for-inserts".  The index is consulted while
	 * deciding HOT-safety though.  This arrangement ensures that no new HOT
	 * chains can be created where the new tuple and the old tuple in the
	 * chain have different index keys.
	 *
	 * We now take a new snapshot, and build the index using all tuples that
	 * are visible in this snapshot.  We can be sure that any HOT updates to
	 * these tuples will be compatible with the index, since any updates made
	 * by transactions that didn't know about the index are now committed or
	 * rolled back.  Thus, each visible tuple is either the end of its
	 * HOT-chain or the extension of the chain is HOT-safe for this index.
	 */

	/* Set ActiveSnapshot since functions in the indexes may need it */
	PushActiveSnapshot(GetTransactionSnapshot());

	/* Perform concurrent build of index */
	index_concurrently_build(tableId, indexRelationId);

	/* we can do away with our snapshot */
	PopActiveSnapshot();

	/*
	 * Commit this transaction to make the indisready update visible.
	 */
	CommitTransactionCommand();
	StartTransactionCommand();

	/* Tell concurrent index builds to ignore us, if index qualifies */
	if (safe_index)
		set_indexsafe_procflags();

	/*
	 * Phase 3 of concurrent index build
	 *
	 * We once again wait until no transaction can have the table open with
	 * the index marked as read-only for updates.
	 */
	pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE,
								 PROGRESS_CREATEIDX_PHASE_WAIT_2);
	WaitForLockers(heaplocktag, ShareLock, true);

	/*
	 * Now take the "reference snapshot" that will be used by validate_index()
	 * to filter candidate tuples.  Beware!  There might still be snapshots in
	 * use that treat some transaction as in-progress that our reference
	 * snapshot treats as committed.  If such a recently-committed transaction
	 * deleted tuples in the table, we will not include them in the index; yet
	 * those transactions which see the deleting one as still-in-progress will
	 * expect such tuples to be there once we mark the index as valid.
	 *
	 * We solve this by waiting for all endangered transactions to exit before
	 * we mark the index as valid.
	 *
	 * We also set ActiveSnapshot to this snap, since functions in indexes may
	 * need a snapshot.
	 */
	snapshot = RegisterSnapshot(GetTransactionSnapshot());
	PushActiveSnapshot(snapshot);

	/*
	 * Scan the index and the heap, insert any missing index entries.
	 */
	validate_index(tableId, indexRelationId, snapshot);

	/*
	 * Drop the reference snapshot.  We must do this before waiting out other
	 * snapshot holders, else we will deadlock against other processes also
	 * doing CREATE INDEX CONCURRENTLY, which would see our snapshot as one
	 * they must wait for.  But first, save the snapshot's xmin to use as
	 * limitXmin for GetCurrentVirtualXIDs().
	 */
	limitXmin = snapshot->xmin;

	PopActiveSnapshot();
	UnregisterSnapshot(snapshot);

	/*
	 * The snapshot subsystem could still contain registered snapshots that
	 * are holding back our process's advertised xmin; in particular, if
	 * default_transaction_isolation = serializable, there is a transaction
	 * snapshot that is still active.  The CatalogSnapshot is likewise a
	 * hazard.  To ensure no deadlocks, we must commit and start yet another
	 * transaction, and do our wait before any snapshot has been taken in it.
	 */
	CommitTransactionCommand();
	StartTransactionCommand();

	/* Tell concurrent index builds to ignore us, if index qualifies */
	if (safe_index)
		set_indexsafe_procflags();

	/* We should now definitely not be advertising any xmin. */
	Assert(MyProc->xmin == InvalidTransactionId);

	/*
	 * The index is now valid in the sense that it contains all currently
	 * interesting tuples.  But since it might not contain tuples deleted just
	 * before the reference snap was taken, we have to wait out any
	 * transactions that might have older snapshots.
	 */
	INJECTION_POINT("define-index-before-set-valid", NULL);
	pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE,
								 PROGRESS_CREATEIDX_PHASE_WAIT_3);
	WaitForOlderSnapshots(limitXmin, true);

	/*
	 * Updating pg_index might involve TOAST table access, so ensure we have a
	 * valid snapshot.
	 */
	PushActiveSnapshot(GetTransactionSnapshot());

	/*
	 * Index can now be marked valid -- update its pg_index entry
	 */
	index_set_state_flags(indexRelationId, INDEX_CREATE_SET_VALID);

	PopActiveSnapshot();

	/*
	 * The pg_index update will cause backends (including this one) to update
	 * relcache entries for the index itself, but we should also send a
	 * relcache inval on the parent table to force replanning of cached plans.
	 * Otherwise existing sessions might fail to use the new index where it
	 * would be useful.  (Note that our earlier commits did not create reasons
	 * to replan; so relcache flush on the index itself was sufficient.)
	 */
	CacheInvalidateRelcacheByRelid(heaprelid.relId);

	/*
	 * Last thing to do is release the session-level lock on the parent table.
	 */
	UnlockRelationIdForSession(&heaprelid, ShareUpdateExclusiveLock);

	pgstat_progress_end_command();

	return address;
}


/*
 * CheckPredicate
 *		Checks that the given partial-index predicate is valid.
 *
 * This used to also constrain the form of the predicate to forms that
 * indxpath.c could do something with.  However, that seems overly
 * restrictive.  One useful application of partial indexes is to apply
 * a UNIQUE constraint across a subset of a table, and in that scenario
 * any evaluable predicate will work.  So accept any predicate here
 * (except ones requiring a plan), and let indxpath.c fend for itself.
 */
static void
CheckPredicate(Expr *predicate)
{
	/*
	 * transformExpr() should have already rejected subqueries, aggregates,
	 * and window functions, based on the EXPR_KIND_ for a predicate.
	 */

	/*
	 * A predicate using mutable functions is probably wrong, for the same
	 * reasons that we don't allow an index expression to use one.
	 */
	if (contain_mutable_functions_after_planning(predicate))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
				 errmsg("functions in index predicate must be marked IMMUTABLE")));
}

/*
 * Compute per-index-column information, including indexed column numbers
 * or index expressions, opclasses and their options. Note, all output vectors
 * should be allocated for all columns, including "including" ones.
 *
 * If the caller switched to the table owner, ddl_userid is the role for ACL
 * checks reached without traversing opaque expressions.  Otherwise, it's
 * InvalidOid, and other ddl_* arguments are undefined.
 *
 * Upon returning from this function, callers must apply
 * ExpandVirtualGeneratedColumns() to ii_ExpressionsExpand
 * when necessary for actual expansion if ii_ExpressionsExpand
 * is not NIL or dummy.
 */
static void
ComputeIndexAttrs(ParseState *pstate,
				  IndexInfo *indexInfo,
				  Oid *typeOids,
				  Oid *collationOids,
				  Oid *opclassOids,
				  Datum *opclassOptions,
				  int16 *colOptions,
				  const List *attList,	/* list of IndexElem's */
				  const List *exclusionOpNames,
				  Oid relId,
				  const char *accessMethodName,
				  Oid accessMethodId,
				  bool amcanorder,
				  bool isconstraint,
				  bool iswithoutoverlaps,
				  Oid ddl_userid,
				  int ddl_sec_context,
				  int *ddl_save_nestlevel)
{
	ListCell   *nextExclOp;
	ListCell   *lc;
	int			attn;
	int			nkeycols = indexInfo->ii_NumIndexKeyAttrs;
	Oid			save_userid;
	int			save_sec_context;

	/* Allocate space for exclusion operator info, if needed */
	if (exclusionOpNames)
	{
		Assert(list_length(exclusionOpNames) == nkeycols);
		indexInfo->ii_ExclusionOps = palloc_array(Oid, nkeycols);
		indexInfo->ii_ExclusionProcs = palloc_array(Oid, nkeycols);
		indexInfo->ii_ExclusionStrats = palloc_array(uint16, nkeycols);
		nextExclOp = list_head(exclusionOpNames);
	}
	else
		nextExclOp = NULL;

	/*
	 * If this is a WITHOUT OVERLAPS constraint, we need space for exclusion
	 * ops, but we don't need to parse anything, so we can let nextExclOp be
	 * NULL. Note that for partitions/inheriting/LIKE, exclusionOpNames will
	 * be set, so we already allocated above.
	 */
	if (iswithoutoverlaps)
	{
		if (exclusionOpNames == NIL)
		{
			indexInfo->ii_ExclusionOps = palloc_array(Oid, nkeycols);
			indexInfo->ii_ExclusionProcs = palloc_array(Oid, nkeycols);
			indexInfo->ii_ExclusionStrats = palloc_array(uint16, nkeycols);
		}
		nextExclOp = NULL;
	}

	if (OidIsValid(ddl_userid))
		GetUserIdAndSecContext(&save_userid, &save_sec_context);

	/*
	 * process attributeList
	 */
	attn = 0;
	foreach(lc, attList)
	{
		IndexElem  *attribute = (IndexElem *) lfirst(lc);
		Oid			atttype;
		Oid			attcollation;

		/*
		 * Process the column-or-expression to be indexed.
		 */
		if (attribute->name != NULL)
		{
			/* Simple index attribute */
			HeapTuple	atttuple;
			Form_pg_attribute attform;

			Assert(attribute->expr == NULL);
			atttuple = SearchSysCacheAttName(relId, attribute->name);
			if (!HeapTupleIsValid(atttuple))
			{
				/* difference in error message spellings is historical */
				if (isconstraint)
					ereport(ERROR,
							(errcode(ERRCODE_UNDEFINED_COLUMN),
							 errmsg("column \"%s\" named in key does not exist",
									attribute->name),
							 parser_errposition(pstate, attribute->location)));
				else
					ereport(ERROR,
							(errcode(ERRCODE_UNDEFINED_COLUMN),
							 errmsg("column \"%s\" does not exist",
									attribute->name),
							 parser_errposition(pstate, attribute->location)));
			}
			attform = (Form_pg_attribute) GETSTRUCT(atttuple);
			indexInfo->ii_IndexAttrNumbers[attn] = attform->attnum;
			atttype = attform->atttypid;
			attcollation = attform->attcollation;
			ReleaseSysCache(atttuple);
		}
		else
		{
			/* Index expression */
			Node	   *expr = attribute->expr;

			Assert(expr != NULL);

			if (attn >= nkeycols)
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("expressions are not supported in included columns"),
						 parser_errposition(pstate, attribute->location)));
			atttype = exprType(expr);
			attcollation = exprCollation(expr);

			/*
			 * Strip any top-level COLLATE clause.  This ensures that we treat
			 * "x COLLATE y" and "(x COLLATE y)" alike.
			 */
			while (IsA(expr, CollateExpr))
				expr = (Node *) ((CollateExpr *) expr)->arg;

			if (IsA(expr, Var) &&
				((Var *) expr)->varattno != InvalidAttrNumber)
			{
				/*
				 * User wrote "(column)" or "(column COLLATE something)".
				 * Treat it like simple attribute anyway.
				 */
				indexInfo->ii_IndexAttrNumbers[attn] = ((Var *) expr)->varattno;
			}
			else
			{
				indexInfo->ii_IndexAttrNumbers[attn] = 0;	/* marks expression */
				indexInfo->ii_Expressions = lappend(indexInfo->ii_Expressions,
													expr);

				/*
				 * transformExpr() should have already rejected subqueries,
				 * aggregates, and window functions, based on the EXPR_KIND_
				 * for an index expression.
				 */

				/*
				 * An expression using mutable functions is probably wrong,
				 * since if you aren't going to get the same result for the
				 * same data every time, it's not clear what the index entries
				 * mean at all.
				 */
				if (contain_mutable_functions_after_planning((Expr *) expr))
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
							 errmsg("functions in index expression must be marked IMMUTABLE"),
							 parser_errposition(pstate, attribute->location)));
			}
		}

		typeOids[attn] = atttype;

		/*
		 * Included columns have no collation, no opclass and no ordering
		 * options.
		 */
		if (attn >= nkeycols)
		{
			if (attribute->collation)
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
						 errmsg("including column does not support a collation"),
						 parser_errposition(pstate, attribute->location)));
			if (attribute->opclass)
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
						 errmsg("including column does not support an operator class"),
						 parser_errposition(pstate, attribute->location)));
			if (attribute->ordering != SORTBY_DEFAULT)
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
						 errmsg("including column does not support ASC/DESC options"),
						 parser_errposition(pstate, attribute->location)));
			if (attribute->nulls_ordering != SORTBY_NULLS_DEFAULT)
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
						 errmsg("including column does not support NULLS FIRST/LAST options"),
						 parser_errposition(pstate, attribute->location)));

			opclassOids[attn] = InvalidOid;
			opclassOptions[attn] = (Datum) 0;
			colOptions[attn] = 0;
			collationOids[attn] = InvalidOid;
			attn++;

			continue;
		}

		/*
		 * Apply collation override if any.  Use of ddl_userid is necessary
		 * due to ACL checks therein, and it's safe because collations don't
		 * contain opaque expressions (or non-opaque expressions).
		 */
		if (attribute->collation)
		{
			if (OidIsValid(ddl_userid))
			{
				AtEOXact_GUC(false, *ddl_save_nestlevel);
				SetUserIdAndSecContext(ddl_userid, ddl_sec_context);
			}
			attcollation = get_collation_oid(attribute->collation, false);
			if (OidIsValid(ddl_userid))
			{
				SetUserIdAndSecContext(save_userid, save_sec_context);
				*ddl_save_nestlevel = NewGUCNestLevel();
				RestrictSearchPath();
			}
		}

		/*
		 * Check we have a collation iff it's a collatable type.  The only
		 * expected failures here are (1) COLLATE applied to a noncollatable
		 * type, or (2) index expression had an unresolved collation.  But we
		 * might as well code this to be a complete consistency check.
		 */
		if (type_is_collatable(atttype))
		{
			if (!OidIsValid(attcollation))
				ereport(ERROR,
						(errcode(ERRCODE_INDETERMINATE_COLLATION),
						 errmsg("could not determine which collation to use for index expression"),
						 errhint("Use the COLLATE clause to set the collation explicitly."),
						 parser_errposition(pstate, attribute->location)));
		}
		else
		{
			if (OidIsValid(attcollation))
				ereport(ERROR,
						(errcode(ERRCODE_DATATYPE_MISMATCH),
						 errmsg("collations are not supported by type %s",
								format_type_be(atttype)),
						 parser_errposition(pstate, attribute->location)));
		}

		collationOids[attn] = attcollation;

		/*
		 * Identify the opclass to use.  Use of ddl_userid is necessary due to
		 * ACL checks therein.  This is safe despite opclasses containing
		 * opaque expressions (specifically, functions), because only
		 * superusers can define opclasses.
		 */
		if (OidIsValid(ddl_userid))
		{
			AtEOXact_GUC(false, *ddl_save_nestlevel);
			SetUserIdAndSecContext(ddl_userid, ddl_sec_context);
		}
		opclassOids[attn] = ResolveOpClass(attribute->opclass,
										   atttype,
										   accessMethodName,
										   accessMethodId);
		if (OidIsValid(ddl_userid))
		{
			SetUserIdAndSecContext(save_userid, save_sec_context);
			*ddl_save_nestlevel = NewGUCNestLevel();
			RestrictSearchPath();
		}

		/*
		 * Identify the exclusion operator, if any.
		 */
		if (nextExclOp)
		{
			List	   *opname = (List *) lfirst(nextExclOp);
			Oid			opid;
			Oid			opfamily;
			int			strat;

			/*
			 * Find the operator --- it must accept the column datatype
			 * without runtime coercion (but binary compatibility is OK).
			 * Operators contain opaque expressions (specifically, functions).
			 * compatible_oper_opid() boils down to oper() and
			 * IsBinaryCoercible().  PostgreSQL would have security problems
			 * elsewhere if oper() started calling opaque expressions.
			 */
			if (OidIsValid(ddl_userid))
			{
				AtEOXact_GUC(false, *ddl_save_nestlevel);
				SetUserIdAndSecContext(ddl_userid, ddl_sec_context);
			}
			opid = compatible_oper_opid(opname, atttype, atttype, false);
			if (OidIsValid(ddl_userid))
			{
				SetUserIdAndSecContext(save_userid, save_sec_context);
				*ddl_save_nestlevel = NewGUCNestLevel();
				RestrictSearchPath();
			}

			/*
			 * Only allow commutative operators to be used in exclusion
			 * constraints. If X conflicts with Y, but Y does not conflict
			 * with X, bad things will happen.
			 */
			if (get_commutator(opid) != opid)
				ereport(ERROR,
						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
						 errmsg("operator %s is not commutative",
								format_operator(opid)),
						 errdetail("Only commutative operators can be used in exclusion constraints."),
						 parser_errposition(pstate, attribute->location)));

			/*
			 * Operator must be a member of the right opfamily, too
			 */
			opfamily = get_opclass_family(opclassOids[attn]);
			strat = get_op_opfamily_strategy(opid, opfamily);
			if (strat == 0)
				ereport(ERROR,
						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
						 errmsg("operator %s is not a member of operator family \"%s\"",
								format_operator(opid),
								get_opfamily_name(opfamily, false)),
						 errdetail("The exclusion operator must be related to the index operator class for the constraint."),
						 parser_errposition(pstate, attribute->location)));

			indexInfo->ii_ExclusionOps[attn] = opid;
			indexInfo->ii_ExclusionProcs[attn] = get_opcode(opid);
			indexInfo->ii_ExclusionStrats[attn] = strat;
			nextExclOp = lnext(exclusionOpNames, nextExclOp);
		}
		else if (iswithoutoverlaps)
		{
			CompareType cmptype;
			StrategyNumber strat;
			Oid			opid;

			if (attn == nkeycols - 1)
				cmptype = COMPARE_OVERLAP;
			else
				cmptype = COMPARE_EQ;
			GetOperatorFromCompareType(opclassOids[attn], InvalidOid, cmptype, &opid, &strat);
			indexInfo->ii_ExclusionOps[attn] = opid;
			indexInfo->ii_ExclusionProcs[attn] = get_opcode(opid);
			indexInfo->ii_ExclusionStrats[attn] = strat;
		}

		/*
		 * Set up the per-column options (indoption field).  For now, this is
		 * zero for any un-ordered index, while ordered indexes have DESC and
		 * NULLS FIRST/LAST options.
		 */
		colOptions[attn] = 0;
		if (amcanorder)
		{
			/* default ordering is ASC */
			if (attribute->ordering == SORTBY_DESC)
				colOptions[attn] |= INDOPTION_DESC;
			/* default null ordering is LAST for ASC, FIRST for DESC */
			if (attribute->nulls_ordering == SORTBY_NULLS_DEFAULT)
			{
				if (attribute->ordering == SORTBY_DESC)
					colOptions[attn] |= INDOPTION_NULLS_FIRST;
			}
			else if (attribute->nulls_ordering == SORTBY_NULLS_FIRST)
				colOptions[attn] |= INDOPTION_NULLS_FIRST;
		}
		else
		{
			/* index AM does not support ordering */
			if (attribute->ordering != SORTBY_DEFAULT)
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("access method \"%s\" does not support ASC/DESC options",
								accessMethodName),
						 parser_errposition(pstate, attribute->location)));
			if (attribute->nulls_ordering != SORTBY_NULLS_DEFAULT)
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("access method \"%s\" does not support NULLS FIRST/LAST options",
								accessMethodName),
						 parser_errposition(pstate, attribute->location)));
		}

		/* Set up the per-column opclass options (attoptions field). */
		if (attribute->opclassopts)
		{
			Assert(attn < nkeycols);

			opclassOptions[attn] =
				transformRelOptions((Datum) 0, attribute->opclassopts,
									NULL, NULL, false, false);
		}
		else
			opclassOptions[attn] = (Datum) 0;

		attn++;
	}

	indexInfo->ii_ExpressionsExpand =
		ExpandVirtualGeneratedColumns(copyObject(indexInfo->ii_Expressions),
											  NULL, relId);
}

/*
 * Resolve possibly-defaulted operator class specification
 *
 * Note: This is used to resolve operator class specifications in index and
 * partition key definitions.
 */
Oid
ResolveOpClass(const List *opclass, Oid attrType,
			   const char *accessMethodName, Oid accessMethodId)
{
	char	   *schemaname;
	char	   *opcname;
	HeapTuple	tuple;
	Form_pg_opclass opform;
	Oid			opClassId,
				opInputType;

	if (opclass == NIL)
	{
		/* no operator class specified, so find the default */
		opClassId = GetDefaultOpClass(attrType, accessMethodId);
		if (!OidIsValid(opClassId))
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_OBJECT),
					 errmsg("data type %s has no default operator class for access method \"%s\"",
							format_type_be(attrType), accessMethodName),
					 errhint("You must specify an operator class for the index or define a default operator class for the data type.")));
		return opClassId;
	}

	/*
	 * Specific opclass name given, so look up the opclass.
	 */

	/* deconstruct the name list */
	DeconstructQualifiedName(opclass, &schemaname, &opcname);

	if (schemaname)
	{
		/* Look in specific schema only */
		Oid			namespaceId;

		namespaceId = LookupExplicitNamespace(schemaname, false);
		tuple = SearchSysCache3(CLAAMNAMENSP,
								ObjectIdGetDatum(accessMethodId),
								PointerGetDatum(opcname),
								ObjectIdGetDatum(namespaceId));
	}
	else
	{
		/* Unqualified opclass name, so search the search path */
		opClassId = OpclassnameGetOpcid(accessMethodId, opcname);
		if (!OidIsValid(opClassId))
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_OBJECT),
					 errmsg("operator class \"%s\" does not exist for access method \"%s\"",
							opcname, accessMethodName)));
		tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opClassId));
	}

	if (!HeapTupleIsValid(tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("operator class \"%s\" does not exist for access method \"%s\"",
						NameListToString(opclass), accessMethodName)));

	/*
	 * Verify that the index operator class accepts this datatype.  Note we
	 * will accept binary compatibility.
	 */
	opform = (Form_pg_opclass) GETSTRUCT(tuple);
	opClassId = opform->oid;
	opInputType = opform->opcintype;

	if (!IsBinaryCoercible(attrType, opInputType))
		ereport(ERROR,
				(errcode(ERRCODE_DATATYPE_MISMATCH),
				 errmsg("operator class \"%s\" does not accept data type %s",
						NameListToString(opclass), format_type_be(attrType))));

	ReleaseSysCache(tuple);

	return opClassId;
}

/*
 * GetDefaultOpClass
 *
 * Given the OIDs of a datatype and an access method, find the default
 * operator class, if any.  Returns InvalidOid if there is none.
 */
Oid
GetDefaultOpClass(Oid type_id, Oid am_id)
{
	Oid			result = InvalidOid;
	int			nexact = 0;
	int			ncompatible = 0;
	int			ncompatiblepreferred = 0;
	Relation	rel;
	ScanKeyData skey[1];
	SysScanDesc scan;
	HeapTuple	tup;
	TYPCATEGORY tcategory;

	/* If it's a domain, look at the base type instead */
	type_id = getBaseType(type_id);

	tcategory = TypeCategory(type_id);

	/*
	 * We scan through all the opclasses available for the access method,
	 * looking for one that is marked default and matches the target type
	 * (either exactly or binary-compatibly, but prefer an exact match).
	 *
	 * We could find more than one binary-compatible match.  If just one is
	 * for a preferred type, use that one; otherwise we fail, forcing the user
	 * to specify which one he wants.  (The preferred-type special case is a
	 * kluge for varchar: it's binary-compatible to both text and bpchar, so
	 * we need a tiebreaker.)  If we find more than one exact match, then
	 * someone put bogus entries in pg_opclass.
	 */
	rel = table_open(OperatorClassRelationId, AccessShareLock);

	ScanKeyInit(&skey[0],
				Anum_pg_opclass_opcmethod,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(am_id));

	scan = systable_beginscan(rel, OpclassAmNameNspIndexId, true,
							  NULL, 1, skey);

	while (HeapTupleIsValid(tup = systable_getnext(scan)))
	{
		Form_pg_opclass opclass = (Form_pg_opclass) GETSTRUCT(tup);

		/* ignore altogether if not a default opclass */
		if (!opclass->opcdefault)
			continue;
		if (opclass->opcintype == type_id)
		{
			nexact++;
			result = opclass->oid;
		}
		else if (nexact == 0 &&
				 IsBinaryCoercible(type_id, opclass->opcintype))
		{
			if (IsPreferredType(tcategory, opclass->opcintype))
			{
				ncompatiblepreferred++;
				result = opclass->oid;
			}
			else if (ncompatiblepreferred == 0)
			{
				ncompatible++;
				result = opclass->oid;
			}
		}
	}

	systable_endscan(scan);

	table_close(rel, AccessShareLock);

	/* raise error if pg_opclass contains inconsistent data */
	if (nexact > 1)
		ereport(ERROR,
				(errcode(ERRCODE_DUPLICATE_OBJECT),
				 errmsg("there are multiple default operator classes for data type %s",
						format_type_be(type_id))));

	if (nexact == 1 ||
		ncompatiblepreferred == 1 ||
		(ncompatiblepreferred == 0 && ncompatible == 1))
		return result;

	return InvalidOid;
}

/*
 * GetOperatorFromCompareType
 *
 * opclass - the opclass to use
 * rhstype - the type for the right-hand side, or InvalidOid to use the type of the given opclass.
 * cmptype - kind of operator to find
 * opid - holds the operator we found
 * strat - holds the output strategy number
 *
 * Finds an operator from a CompareType.  This is used for temporal index
 * constraints (and other temporal features) to look up equality and overlaps
 * operators.  We ask an opclass support function to translate from the
 * compare type to the internal strategy numbers.  Raises ERROR on search
 * failure.
 */
void
GetOperatorFromCompareType(Oid opclass, Oid rhstype, CompareType cmptype,
						   Oid *opid, StrategyNumber *strat)
{
	Oid			amid;
	Oid			opfamily;
	Oid			opcintype;

	Assert(cmptype == COMPARE_EQ || cmptype == COMPARE_OVERLAP || cmptype == COMPARE_CONTAINED_BY);

	/*
	 * Use the opclass to get the opfamily, opcintype, and access method. If
	 * any of this fails, quit early.
	 */
	if (!get_opclass_opfamily_and_input_type(opclass, &opfamily, &opcintype))
		elog(ERROR, "cache lookup failed for opclass %u", opclass);

	amid = get_opclass_method(opclass);

	/*
	 * Ask the index AM to translate to its internal stratnum
	 */
	*strat = IndexAmTranslateCompareType(cmptype, amid, opfamily, true);
	if (*strat == InvalidStrategy)
		ereport(ERROR,
				errcode(ERRCODE_UNDEFINED_OBJECT),
				cmptype == COMPARE_EQ ? errmsg("could not identify an equality operator for type %s", format_type_be(opcintype)) :
				cmptype == COMPARE_OVERLAP ? errmsg("could not identify an overlaps operator for type %s", format_type_be(opcintype)) :
				cmptype == COMPARE_CONTAINED_BY ? errmsg("could not identify a contained-by operator for type %s", format_type_be(opcintype)) : 0,
				errdetail("Could not translate compare type %d for operator family \"%s\" of access method \"%s\".",
						  cmptype, get_opfamily_name(opfamily, false), get_am_name(amid)));

	/*
	 * We parameterize rhstype so foreign keys can ask for a <@ operator whose
	 * rhs matches the aggregate function. For example range_agg returns
	 * anymultirange.
	 */
	if (!OidIsValid(rhstype))
		rhstype = opcintype;
	*opid = get_opfamily_member(opfamily, opcintype, rhstype, *strat);

	if (!OidIsValid(*opid))
		ereport(ERROR,
				errcode(ERRCODE_UNDEFINED_OBJECT),
				cmptype == COMPARE_EQ ? errmsg("could not identify an equality operator for type %s", format_type_be(opcintype)) :
				cmptype == COMPARE_OVERLAP ? errmsg("could not identify an overlaps operator for type %s", format_type_be(opcintype)) :
				cmptype == COMPARE_CONTAINED_BY ? errmsg("could not identify a contained-by operator for type %s", format_type_be(opcintype)) : 0,
				errdetail("There is no suitable operator in operator family \"%s\" for access method \"%s\".",
						  get_opfamily_name(opfamily, false), get_am_name(amid)));
}

/*
 *	makeObjectName()
 *
 *	Create a name for an implicitly created index, sequence, constraint,
 *	extended statistics, etc.
 *
 *	The parameters are typically: the original table name, the original field
 *	name, and a "type" string (such as "seq" or "pkey").    The field name
 *	and/or type can be NULL if not relevant.
 *
 *	The result is a palloc'd string.
 *
 *	The basic result we want is "name1_name2_label", omitting "_name2" or
 *	"_label" when those parameters are NULL.  However, we must generate
 *	a name with less than NAMEDATALEN characters!  So, we truncate one or
 *	both names if necessary to make a short-enough string.  The label part
 *	is never truncated (so it had better be reasonably short).
 *
 *	The caller is responsible for checking uniqueness of the generated
 *	name and retrying as needed; retrying will be done by altering the
 *	"label" string (which is why we never truncate that part).
 */
char *
makeObjectName(const char *name1, const char *name2, const char *label)
{
	char	   *name;
	int			overhead = 0;	/* chars needed for label and underscores */
	int			availchars;		/* chars available for name(s) */
	int			name1chars;		/* chars allocated to name1 */
	int			name2chars;		/* chars allocated to name2 */
	int			ndx;

	name1chars = strlen(name1);
	if (name2)
	{
		name2chars = strlen(name2);
		overhead++;				/* allow for separating underscore */
	}
	else
		name2chars = 0;
	if (label)
		overhead += strlen(label) + 1;

	availchars = NAMEDATALEN - 1 - overhead;
	Assert(availchars > 0);		/* else caller chose a bad label */

	/*
	 * If we must truncate, preferentially truncate the longer name. This
	 * logic could be expressed without a loop, but it's simple and obvious as
	 * a loop.
	 */
	while (name1chars + name2chars > availchars)
	{
		if (name1chars > name2chars)
			name1chars--;
		else
			name2chars--;
	}

	name1chars = pg_mbcliplen(name1, name1chars, name1chars);
	if (name2)
		name2chars = pg_mbcliplen(name2, name2chars, name2chars);

	/* Now construct the string using the chosen lengths */
	name = palloc(name1chars + name2chars + overhead + 1);
	memcpy(name, name1, name1chars);
	ndx = name1chars;
	if (name2)
	{
		name[ndx++] = '_';
		memcpy(name + ndx, name2, name2chars);
		ndx += name2chars;
	}
	if (label)
	{
		name[ndx++] = '_';
		strcpy(name + ndx, label);
	}
	else
		name[ndx] = '\0';

	return name;
}

/*
 * Select a nonconflicting name for a new relation.  This is ordinarily
 * used to choose index names (which is why it's here) but it can also
 * be used for sequences, or any autogenerated relation kind.
 *
 * name1, name2, and label are used the same way as for makeObjectName(),
 * except that the label can't be NULL; digits will be appended to the label
 * if needed to create a name that is unique within the specified namespace.
 *
 * If isconstraint is true, we also avoid choosing a name matching any
 * existing constraint in the same namespace.  (This is stricter than what
 * Postgres itself requires, but the SQL standard says that constraint names
 * should be unique within schemas, so we follow that for autogenerated
 * constraint names.)
 *
 * Note: it is theoretically possible to get a collision anyway, if someone
 * else chooses the same name concurrently.  We shorten the race condition
 * window by checking for conflicting relations using SnapshotDirty, but
 * that doesn't close the window entirely.  This is fairly unlikely to be
 * a problem in practice, especially if one is holding an exclusive lock on
 * the relation identified by name1.  However, if choosing multiple names
 * within a single command, you'd better create the new object and do
 * CommandCounterIncrement before choosing the next one!
 *
 * Returns a palloc'd string.
 */
char *
ChooseRelationName(const char *name1, const char *name2,
				   const char *label, Oid namespaceid,
				   bool isconstraint)
{
	int			pass = 0;
	char	   *relname = NULL;
	char		modlabel[NAMEDATALEN];
	SnapshotData SnapshotDirty;
	Relation	pgclassrel;

	/* prepare to search pg_class with a dirty snapshot */
	InitDirtySnapshot(SnapshotDirty);
	pgclassrel = table_open(RelationRelationId, AccessShareLock);

	/* try the unmodified label first */
	strlcpy(modlabel, label, sizeof(modlabel));

	for (;;)
	{
		ScanKeyData key[2];
		SysScanDesc scan;
		bool		collides;

		relname = makeObjectName(name1, name2, modlabel);

		/* is there any conflicting relation name? */
		ScanKeyInit(&key[0],
					Anum_pg_class_relname,
					BTEqualStrategyNumber, F_NAMEEQ,
					CStringGetDatum(relname));
		ScanKeyInit(&key[1],
					Anum_pg_class_relnamespace,
					BTEqualStrategyNumber, F_OIDEQ,
					ObjectIdGetDatum(namespaceid));

		scan = systable_beginscan(pgclassrel, ClassNameNspIndexId,
								  true /* indexOK */ ,
								  &SnapshotDirty,
								  2, key);

		collides = HeapTupleIsValid(systable_getnext(scan));

		systable_endscan(scan);

		/* break out of loop if no conflict */
		if (!collides)
		{
			if (!isconstraint ||
				!ConstraintNameExists(relname, namespaceid))
				break;
		}

		/* found a conflict, so try a new name component */
		pfree(relname);
		snprintf(modlabel, sizeof(modlabel), "%s%d", label, ++pass);
	}

	table_close(pgclassrel, AccessShareLock);

	return relname;
}

/*
 * Select the name to be used for an index.
 *
 * The argument list is pretty ad-hoc :-(
 */
static char *
ChooseIndexName(const char *tabname, Oid namespaceId,
				const List *colnames, const List *exclusionOpNames,
				bool primary, bool isconstraint)
{
	char	   *indexname;

	if (primary)
	{
		/* the primary key's name does not depend on the specific column(s) */
		indexname = ChooseRelationName(tabname,
									   NULL,
									   "pkey",
									   namespaceId,
									   true);
	}
	else if (exclusionOpNames != NIL)
	{
		indexname = ChooseRelationName(tabname,
									   ChooseIndexNameAddition(colnames),
									   "excl",
									   namespaceId,
									   true);
	}
	else if (isconstraint)
	{
		indexname = ChooseRelationName(tabname,
									   ChooseIndexNameAddition(colnames),
									   "key",
									   namespaceId,
									   true);
	}
	else
	{
		indexname = ChooseRelationName(tabname,
									   ChooseIndexNameAddition(colnames),
									   "idx",
									   namespaceId,
									   false);
	}

	return indexname;
}

/*
 * Generate "name2" for a new index given the list of column names for it
 * (as produced by ChooseIndexColumnNames).  This will be passed to
 * ChooseRelationName along with the parent table name and a suitable label.
 *
 * We know that less than NAMEDATALEN characters will actually be used,
 * so we can truncate the result once we've generated that many.
 *
 * XXX See also ChooseForeignKeyConstraintNameAddition and
 * ChooseExtendedStatisticNameAddition.
 */
static char *
ChooseIndexNameAddition(const List *colnames)
{
	char		buf[NAMEDATALEN * 2];
	int			buflen = 0;
	ListCell   *lc;

	buf[0] = '\0';
	foreach(lc, colnames)
	{
		const char *name = (const char *) lfirst(lc);

		if (buflen > 0)
			buf[buflen++] = '_';	/* insert _ between names */

		/*
		 * At this point we have buflen <= NAMEDATALEN.  name should be less
		 * than NAMEDATALEN already, but use strlcpy for paranoia.
		 */
		strlcpy(buf + buflen, name, NAMEDATALEN);
		buflen += strlen(buf + buflen);
		if (buflen >= NAMEDATALEN)
			break;
	}
	return pstrdup(buf);
}

/*
 * Select the actual names to be used for the columns of an index, given the
 * list of IndexElems for the columns.  This is mostly about ensuring the
 * names are unique so we don't get a conflicting-attribute-names error.
 *
 * Returns a List of plain strings (char *, not String nodes).
 */
static List *
ChooseIndexColumnNames(const List *indexElems)
{
	List	   *result = NIL;
	ListCell   *lc;

	foreach(lc, indexElems)
	{
		IndexElem  *ielem = (IndexElem *) lfirst(lc);
		const char *origname;
		const char *curname;
		int			i;
		char		buf[NAMEDATALEN];

		/* Get the preliminary name from the IndexElem */
		if (ielem->indexcolname)
			origname = ielem->indexcolname; /* caller-specified name */
		else if (ielem->name)
			origname = ielem->name; /* simple column reference */
		else
			origname = "expr";	/* default name for expression */

		/* If it conflicts with any previous column, tweak it */
		curname = origname;
		for (i = 1;; i++)
		{
			ListCell   *lc2;
			char		nbuf[32];
			int			nlen;

			foreach(lc2, result)
			{
				if (strcmp(curname, (char *) lfirst(lc2)) == 0)
					break;
			}
			if (lc2 == NULL)
				break;			/* found nonconflicting name */

			sprintf(nbuf, "%d", i);

			/* Ensure generated names are shorter than NAMEDATALEN */
			nlen = pg_mbcliplen(origname, strlen(origname),
								NAMEDATALEN - 1 - strlen(nbuf));
			memcpy(buf, origname, nlen);
			strcpy(buf + nlen, nbuf);
			curname = buf;
		}

		/* And attach to the result list */
		result = lappend(result, pstrdup(curname));
	}
	return result;
}

/*
 * ExecReindex
 *
 * Primary entry point for manual REINDEX commands.  This is mainly a
 * preparation wrapper for the real operations that will happen in
 * each subroutine of REINDEX.
 */
void
ExecReindex(ParseState *pstate, const ReindexStmt *stmt, bool isTopLevel)
{
	ReindexParams params = {0};
	ListCell   *lc;
	bool		concurrently = false;
	bool		verbose = false;
	char	   *tablespacename = NULL;

	/* Parse option list */
	foreach(lc, stmt->params)
	{
		DefElem    *opt = (DefElem *) lfirst(lc);

		if (strcmp(opt->defname, "verbose") == 0)
			verbose = defGetBoolean(opt);
		else if (strcmp(opt->defname, "concurrently") == 0)
			concurrently = defGetBoolean(opt);
		else if (strcmp(opt->defname, "tablespace") == 0)
			tablespacename = defGetString(opt);
		else
			ereport(ERROR,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 errmsg("unrecognized %s option \"%s\"",
							"REINDEX", opt->defname),
					 parser_errposition(pstate, opt->location)));
	}

	if (concurrently)
		PreventInTransactionBlock(isTopLevel,
								  "REINDEX CONCURRENTLY");

	params.options =
		(verbose ? REINDEXOPT_VERBOSE : 0) |
		(concurrently ? REINDEXOPT_CONCURRENTLY : 0);

	/*
	 * Assign the tablespace OID to move indexes to, with InvalidOid to do
	 * nothing.
	 */
	if (tablespacename != NULL)
	{
		params.tablespaceOid = get_tablespace_oid(tablespacename, false);

		/* Check permissions except when moving to database's default */
		if (OidIsValid(params.tablespaceOid) &&
			params.tablespaceOid != MyDatabaseTableSpace)
		{
			AclResult	aclresult;

			aclresult = object_aclcheck(TableSpaceRelationId, params.tablespaceOid,
										GetUserId(), ACL_CREATE);
			if (aclresult != ACLCHECK_OK)
				aclcheck_error(aclresult, OBJECT_TABLESPACE,
							   get_tablespace_name(params.tablespaceOid));
		}
	}
	else
		params.tablespaceOid = InvalidOid;

	switch (stmt->kind)
	{
		case REINDEX_OBJECT_INDEX:
			ReindexIndex(stmt, &params, isTopLevel);
			break;
		case REINDEX_OBJECT_TABLE:
			ReindexTable(stmt, &params, isTopLevel);
			break;
		case REINDEX_OBJECT_SCHEMA:
		case REINDEX_OBJECT_SYSTEM:
		case REINDEX_OBJECT_DATABASE:

			/*
			 * This cannot run inside a user transaction block; if we were
			 * inside a transaction, then its commit- and
			 * start-transaction-command calls would not have the intended
			 * effect!
			 */
			PreventInTransactionBlock(isTopLevel,
									  (stmt->kind == REINDEX_OBJECT_SCHEMA) ? "REINDEX SCHEMA" :
									  (stmt->kind == REINDEX_OBJECT_SYSTEM) ? "REINDEX SYSTEM" :
									  "REINDEX DATABASE");
			ReindexMultipleTables(stmt, &params);
			break;
		default:
			elog(ERROR, "unrecognized object type: %d",
				 (int) stmt->kind);
			break;
	}
}

/*
 * ReindexIndex
 *		Recreate a specific index.
 */
static void
ReindexIndex(const ReindexStmt *stmt, const ReindexParams *params, bool isTopLevel)
{
	const RangeVar *indexRelation = stmt->relation;
	struct ReindexIndexCallbackState state;
	Oid			indOid;
	char		persistence;
	char		relkind;

	/*
	 * Find and lock index, and check permissions on table; use callback to
	 * obtain lock on table first, to avoid deadlock hazard.  The lock level
	 * used here must match the index lock obtained in reindex_index().
	 *
	 * If it's a temporary index, we will perform a non-concurrent reindex,
	 * even if CONCURRENTLY was requested.  In that case, reindex_index() will
	 * upgrade the lock, but that's OK, because other sessions can't hold
	 * locks on our temporary table.
	 */
	state.params = *params;
	state.locked_table_oid = InvalidOid;
	indOid = RangeVarGetRelidExtended(indexRelation,
									  (params->options & REINDEXOPT_CONCURRENTLY) != 0 ?
									  ShareUpdateExclusiveLock : AccessExclusiveLock,
									  0,
									  RangeVarCallbackForReindexIndex,
									  &state);

	/*
	 * Obtain the current persistence and kind of the existing index.  We
	 * already hold a lock on the index.
	 */
	persistence = get_rel_persistence(indOid);
	relkind = get_rel_relkind(indOid);

	if (relkind == RELKIND_PARTITIONED_INDEX)
		ReindexPartitions(stmt, indOid, params, isTopLevel);
	else if ((params->options & REINDEXOPT_CONCURRENTLY) != 0 &&
			 persistence != RELPERSISTENCE_TEMP)
		ReindexRelationConcurrently(stmt, indOid, params);
	else
	{
		ReindexParams newparams = *params;

		newparams.options |= REINDEXOPT_REPORT_PROGRESS;
		reindex_index(stmt, indOid, false, persistence, &newparams);
	}
}

/*
 * Check permissions on table before acquiring relation lock; also lock
 * the heap before the RangeVarGetRelidExtended takes the index lock, to avoid
 * deadlocks.
 */
static void
RangeVarCallbackForReindexIndex(const RangeVar *relation,
								Oid relId, Oid oldRelId, void *arg)
{
	char		relkind;
	struct ReindexIndexCallbackState *state = arg;
	LOCKMODE	table_lockmode;
	Oid			table_oid;
	AclResult	aclresult;

	/*
	 * Lock level here should match table lock in reindex_index() for
	 * non-concurrent case and table locks used by index_concurrently_*() for
	 * concurrent case.
	 */
	table_lockmode = (state->params.options & REINDEXOPT_CONCURRENTLY) != 0 ?
		ShareUpdateExclusiveLock : ShareLock;

	/*
	 * If we previously locked some other index's heap, and the name we're
	 * looking up no longer refers to that relation, release the now-useless
	 * lock.
	 */
	if (relId != oldRelId && OidIsValid(oldRelId))
	{
		UnlockRelationOid(state->locked_table_oid, table_lockmode);
		state->locked_table_oid = InvalidOid;
	}

	/* If the relation does not exist, there's nothing more to do. */
	if (!OidIsValid(relId))
		return;

	/* If the relation does exist, check whether it's an index. */
	relkind = get_rel_relkind(relId);
	if (relkind != RELKIND_INDEX &&
		relkind != RELKIND_PARTITIONED_INDEX)
		ereport(ERROR,
				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
				 errmsg("\"%s\" is not an index", relation->relname)));

	/* Look up the index's table. */
	table_oid = IndexGetRelation(relId, false);

	/*
	 * In the unlikely event that, upon retry, we get the same index OID with
	 * a different table OID, fail.  RangeVarGetRelidExtended() will have
	 * already locked the index in this case, and it won't retry again, so we
	 * can't lock the newly discovered table OID without risking deadlock.
	 * Also, while this corner case is indeed possible, it is extremely
	 * unlikely to happen in practice, so it's probably not worth any more
	 * effort than this.
	 */
	if (relId == oldRelId && table_oid != state->locked_table_oid)
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("index \"%s\" was concurrently dropped",
						relation->relname)));

	/* Check permissions. */
	aclresult = pg_class_aclcheck(table_oid, GetUserId(), ACL_MAINTAIN);
	if (aclresult != ACLCHECK_OK)
		aclcheck_error(aclresult, OBJECT_INDEX, relation->relname);

	/* Lock heap before index to avoid deadlock. */
	if (relId != oldRelId)
	{
		LockRelationOid(table_oid, table_lockmode);
		state->locked_table_oid = table_oid;
	}
}

/*
 * ReindexTable
 *		Recreate all indexes of a table (and of its toast table, if any)
 */
static Oid
ReindexTable(const ReindexStmt *stmt, const ReindexParams *params, bool isTopLevel)
{
	Oid			heapOid;
	bool		result;
	const RangeVar *relation = stmt->relation;

	/*
	 * The lock level used here should match reindex_relation().
	 *
	 * If it's a temporary table, we will perform a non-concurrent reindex,
	 * even if CONCURRENTLY was requested.  In that case, reindex_relation()
	 * will upgrade the lock, but that's OK, because other sessions can't hold
	 * locks on our temporary table.
	 */
	heapOid = RangeVarGetRelidExtended(relation,
									   (params->options & REINDEXOPT_CONCURRENTLY) != 0 ?
									   ShareUpdateExclusiveLock : ShareLock,
									   0,
									   RangeVarCallbackMaintainsTable, NULL);

	if (get_rel_relkind(heapOid) == RELKIND_PARTITIONED_TABLE)
		ReindexPartitions(stmt, heapOid, params, isTopLevel);
	else if ((params->options & REINDEXOPT_CONCURRENTLY) != 0 &&
			 get_rel_persistence(heapOid) != RELPERSISTENCE_TEMP)
	{
		result = ReindexRelationConcurrently(stmt, heapOid, params);

		if (!result)
			ereport(NOTICE,
					(errmsg("table \"%s\" has no indexes that can be reindexed concurrently",
							relation->relname)));
	}
	else
	{
		ReindexParams newparams = *params;

		newparams.options |= REINDEXOPT_REPORT_PROGRESS;
		result = reindex_relation(stmt, heapOid,
								  REINDEX_REL_PROCESS_TOAST |
								  REINDEX_REL_CHECK_CONSTRAINTS,
								  &newparams);
		if (!result)
			ereport(NOTICE,
					(errmsg("table \"%s\" has no indexes to reindex",
							relation->relname)));
	}

	return heapOid;
}

/*
 * ReindexMultipleTables
 *		Recreate indexes of tables selected by objectName/objectKind.
 *
 * To reduce the probability of deadlocks, each table is reindexed in a
 * separate transaction, so we can release the lock on it right away.
 * That means this must not be called within a user transaction block!
 */
static void
ReindexMultipleTables(const ReindexStmt *stmt, const ReindexParams *params)
{

	Oid			objectOid;
	Relation	relationRelation;
	TableScanDesc scan;
	ScanKeyData scan_keys[1];
	HeapTuple	tuple;
	MemoryContext private_context;
	MemoryContext old;
	List	   *relids = NIL;
	int			num_keys;
	bool		concurrent_warning = false;
	bool		tablespace_warning = false;
	const char *objectName = stmt->name;
	const ReindexObjectType objectKind = stmt->kind;

	Assert(objectKind == REINDEX_OBJECT_SCHEMA ||
		   objectKind == REINDEX_OBJECT_SYSTEM ||
		   objectKind == REINDEX_OBJECT_DATABASE);

	/*
	 * This matches the options enforced by the grammar, where the object name
	 * is optional for DATABASE and SYSTEM.
	 */
	Assert(objectName || objectKind != REINDEX_OBJECT_SCHEMA);

	if (objectKind == REINDEX_OBJECT_SYSTEM &&
		(params->options & REINDEXOPT_CONCURRENTLY) != 0)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("cannot reindex system catalogs concurrently")));

	/*
	 * Get OID of object to reindex, being the database currently being used
	 * by session for a database or for system catalogs, or the schema defined
	 * by caller. At the same time do permission checks that need different
	 * processing depending on the object type.
	 */
	if (objectKind == REINDEX_OBJECT_SCHEMA)
	{
		objectOid = get_namespace_oid(objectName, false);

		if (!object_ownercheck(NamespaceRelationId, objectOid, GetUserId()) &&
			!has_privs_of_role(GetUserId(), ROLE_PG_MAINTAIN))
			aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SCHEMA,
						   objectName);
	}
	else
	{
		objectOid = MyDatabaseId;

		if (objectName && strcmp(objectName, get_database_name(objectOid)) != 0)
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("can only reindex the currently open database")));
		if (!object_ownercheck(DatabaseRelationId, objectOid, GetUserId()) &&
			!has_privs_of_role(GetUserId(), ROLE_PG_MAINTAIN))
			aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_DATABASE,
						   get_database_name(objectOid));
	}

	/*
	 * Create a memory context that will survive forced transaction commits we
	 * do below.  Since it is a child of PortalContext, it will go away
	 * eventually even if we suffer an error; there's no need for special
	 * abort cleanup logic.
	 */
	private_context = AllocSetContextCreate(PortalContext,
											"ReindexMultipleTables",
											ALLOCSET_SMALL_SIZES);

	/*
	 * Define the search keys to find the objects to reindex. For a schema, we
	 * select target relations using relnamespace, something not necessary for
	 * a database-wide operation.
	 */
	if (objectKind == REINDEX_OBJECT_SCHEMA)
	{
		num_keys = 1;
		ScanKeyInit(&scan_keys[0],
					Anum_pg_class_relnamespace,
					BTEqualStrategyNumber, F_OIDEQ,
					ObjectIdGetDatum(objectOid));
	}
	else
		num_keys = 0;

	/*
	 * Scan pg_class to build a list of the relations we need to reindex.
	 *
	 * We only consider plain relations and materialized views here (toast
	 * rels will be processed indirectly by reindex_relation).
	 */
	relationRelation = table_open(RelationRelationId, AccessShareLock);
	scan = table_beginscan_catalog(relationRelation, num_keys, scan_keys);
	while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
	{
		Form_pg_class classtuple = (Form_pg_class) GETSTRUCT(tuple);
		Oid			relid = classtuple->oid;

		/*
		 * Only regular tables and matviews can have indexes, so ignore any
		 * other kind of relation.
		 *
		 * Partitioned tables/indexes are skipped but matching leaf partitions
		 * are processed.
		 */
		if (classtuple->relkind != RELKIND_RELATION &&
			classtuple->relkind != RELKIND_MATVIEW)
			continue;

		/* Skip temp tables of other backends; we can't reindex them at all */
		if (classtuple->relpersistence == RELPERSISTENCE_TEMP &&
			!isTempNamespace(classtuple->relnamespace))
			continue;

		/*
		 * Check user/system classification.  SYSTEM processes all the
		 * catalogs, and DATABASE processes everything that's not a catalog.
		 */
		if (objectKind == REINDEX_OBJECT_SYSTEM &&
			!IsCatalogRelationOid(relid))
			continue;
		else if (objectKind == REINDEX_OBJECT_DATABASE &&
				 IsCatalogRelationOid(relid))
			continue;

		/*
		 * We already checked privileges on the database or schema, but we
		 * further restrict reindexing shared catalogs to roles with the
		 * MAINTAIN privilege on the relation.
		 */
		if (classtuple->relisshared &&
			pg_class_aclcheck(relid, GetUserId(), ACL_MAINTAIN) != ACLCHECK_OK)
			continue;

		/*
		 * Skip system tables, since index_create() would reject indexing them
		 * concurrently (and it would likely fail if we tried).
		 */
		if ((params->options & REINDEXOPT_CONCURRENTLY) != 0 &&
			IsCatalogRelationOid(relid))
		{
			if (!concurrent_warning)
				ereport(WARNING,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("cannot reindex system catalogs concurrently, skipping all")));
			concurrent_warning = true;
			continue;
		}

		/*
		 * If a new tablespace is set, check if this relation has to be
		 * skipped.
		 */
		if (OidIsValid(params->tablespaceOid))
		{
			bool		skip_rel = false;

			/*
			 * Mapped relations cannot be moved to different tablespaces (in
			 * particular this eliminates all shared catalogs.).
			 */
			if (RELKIND_HAS_STORAGE(classtuple->relkind) &&
				!RelFileNumberIsValid(classtuple->relfilenode))
				skip_rel = true;

			/*
			 * A system relation is always skipped, even with
			 * allow_system_table_mods enabled.
			 */
			if (IsSystemClass(relid, classtuple))
				skip_rel = true;

			if (skip_rel)
			{
				if (!tablespace_warning)
					ereport(WARNING,
							(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
							 errmsg("cannot move system relations, skipping all")));
				tablespace_warning = true;
				continue;
			}
		}

		/* Save the list of relation OIDs in private context */
		old = MemoryContextSwitchTo(private_context);

		/*
		 * We always want to reindex pg_class first if it's selected to be
		 * reindexed.  This ensures that if there is any corruption in
		 * pg_class' indexes, they will be fixed before we process any other
		 * tables.  This is critical because reindexing itself will try to
		 * update pg_class.
		 */
		if (relid == RelationRelationId)
			relids = lcons_oid(relid, relids);
		else
			relids = lappend_oid(relids, relid);

		MemoryContextSwitchTo(old);
	}
	table_endscan(scan);
	table_close(relationRelation, AccessShareLock);

	/*
	 * Process each relation listed in a separate transaction.  Note that this
	 * commits and then starts a new transaction immediately.
	 */
	ReindexMultipleInternal(stmt, relids, params);

	MemoryContextDelete(private_context);
}

/*
 * Error callback specific to ReindexPartitions().
 */
static void
reindex_error_callback(void *arg)
{
	ReindexErrorInfo *errinfo = (ReindexErrorInfo *) arg;

	Assert(RELKIND_HAS_PARTITIONS(errinfo->relkind));

	if (errinfo->relkind == RELKIND_PARTITIONED_TABLE)
		errcontext("while reindexing partitioned table \"%s.%s\"",
				   errinfo->relnamespace, errinfo->relname);
	else if (errinfo->relkind == RELKIND_PARTITIONED_INDEX)
		errcontext("while reindexing partitioned index \"%s.%s\"",
				   errinfo->relnamespace, errinfo->relname);
}

/*
 * ReindexPartitions
 *
 * Reindex a set of partitions, per the partitioned index or table given
 * by the caller.
 */
static void
ReindexPartitions(const ReindexStmt *stmt, Oid relid, const ReindexParams *params, bool isTopLevel)
{
	List	   *partitions = NIL;
	char		relkind = get_rel_relkind(relid);
	char	   *relname = get_rel_name(relid);
	char	   *relnamespace = get_namespace_name(get_rel_namespace(relid));
	MemoryContext reindex_context;
	List	   *inhoids;
	ListCell   *lc;
	ErrorContextCallback errcallback;
	ReindexErrorInfo errinfo;

	Assert(RELKIND_HAS_PARTITIONS(relkind));

	/*
	 * Check if this runs in a transaction block, with an error callback to
	 * provide more context under which a problem happens.
	 */
	errinfo.relname = pstrdup(relname);
	errinfo.relnamespace = pstrdup(relnamespace);
	errinfo.relkind = relkind;
	errcallback.callback = reindex_error_callback;
	errcallback.arg = &errinfo;
	errcallback.previous = error_context_stack;
	error_context_stack = &errcallback;

	PreventInTransactionBlock(isTopLevel,
							  relkind == RELKIND_PARTITIONED_TABLE ?
							  "REINDEX TABLE" : "REINDEX INDEX");

	/* Pop the error context stack */
	error_context_stack = errcallback.previous;

	/*
	 * Create special memory context for cross-transaction storage.
	 *
	 * Since it is a child of PortalContext, it will go away eventually even
	 * if we suffer an error so there is no need for special abort cleanup
	 * logic.
	 */
	reindex_context = AllocSetContextCreate(PortalContext, "Reindex",
											ALLOCSET_DEFAULT_SIZES);

	/* ShareLock is enough to prevent schema modifications */
	inhoids = find_all_inheritors(relid, ShareLock, NULL);

	/*
	 * The list of relations to reindex are the physical partitions of the
	 * tree so discard any partitioned table or index.
	 */
	foreach(lc, inhoids)
	{
		Oid			partoid = lfirst_oid(lc);
		char		partkind = get_rel_relkind(partoid);
		MemoryContext old_context;

		/*
		 * This discards partitioned tables, partitioned indexes and foreign
		 * tables.
		 */
		if (!RELKIND_HAS_STORAGE(partkind))
			continue;

		Assert(partkind == RELKIND_INDEX ||
			   partkind == RELKIND_RELATION);

		/* Save partition OID */
		old_context = MemoryContextSwitchTo(reindex_context);
		partitions = lappend_oid(partitions, partoid);
		MemoryContextSwitchTo(old_context);
	}

	/*
	 * Process each partition listed in a separate transaction.  Note that
	 * this commits and then starts a new transaction immediately.
	 */
	ReindexMultipleInternal(stmt, partitions, params);

	/*
	 * Clean up working storage --- note we must do this after
	 * StartTransactionCommand, else we might be trying to delete the active
	 * context!
	 */
	MemoryContextDelete(reindex_context);
}

/*
 * ReindexMultipleInternal
 *
 * Reindex a list of relations, each one being processed in its own
 * transaction.  This commits the existing transaction immediately,
 * and starts a new transaction when finished.
 */
static void
ReindexMultipleInternal(const ReindexStmt *stmt, const List *relids, const ReindexParams *params)
{
	ListCell   *l;

	PopActiveSnapshot();
	CommitTransactionCommand();

	foreach(l, relids)
	{
		Oid			relid = lfirst_oid(l);
		char		relkind;
		char		relpersistence;

		StartTransactionCommand();

		/* functions in indexes may want a snapshot set */
		PushActiveSnapshot(GetTransactionSnapshot());

		/* check if the relation still exists */
		if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(relid)))
		{
			PopActiveSnapshot();
			CommitTransactionCommand();
			continue;
		}

		/*
		 * Check permissions except when moving to database's default if a new
		 * tablespace is chosen.  Note that this check also happens in
		 * ExecReindex(), but we do an extra check here as this runs across
		 * multiple transactions.
		 */
		if (OidIsValid(params->tablespaceOid) &&
			params->tablespaceOid != MyDatabaseTableSpace)
		{
			AclResult	aclresult;

			aclresult = object_aclcheck(TableSpaceRelationId, params->tablespaceOid,
										GetUserId(), ACL_CREATE);
			if (aclresult != ACLCHECK_OK)
				aclcheck_error(aclresult, OBJECT_TABLESPACE,
							   get_tablespace_name(params->tablespaceOid));
		}

		relkind = get_rel_relkind(relid);
		relpersistence = get_rel_persistence(relid);

		/*
		 * Partitioned tables and indexes can never be processed directly, and
		 * a list of their leaves should be built first.
		 */
		Assert(!RELKIND_HAS_PARTITIONS(relkind));

		if ((params->options & REINDEXOPT_CONCURRENTLY) != 0 &&
			relpersistence != RELPERSISTENCE_TEMP)
		{
			ReindexParams newparams = *params;

			newparams.options |= REINDEXOPT_MISSING_OK;
			(void) ReindexRelationConcurrently(stmt, relid, &newparams);
			if (ActiveSnapshotSet())
				PopActiveSnapshot();
			/* ReindexRelationConcurrently() does the verbose output */
		}
		else if (relkind == RELKIND_INDEX)
		{
			ReindexParams newparams = *params;

			newparams.options |=
				REINDEXOPT_REPORT_PROGRESS | REINDEXOPT_MISSING_OK;
			reindex_index(stmt, relid, false, relpersistence, &newparams);
			PopActiveSnapshot();
			/* reindex_index() does the verbose output */
		}
		else
		{
			bool		result;
			ReindexParams newparams = *params;

			newparams.options |=
				REINDEXOPT_REPORT_PROGRESS | REINDEXOPT_MISSING_OK;
			result = reindex_relation(stmt, relid,
									  REINDEX_REL_PROCESS_TOAST |
									  REINDEX_REL_CHECK_CONSTRAINTS,
									  &newparams);

			if (result && (params->options & REINDEXOPT_VERBOSE) != 0)
				ereport(INFO,
						(errmsg("table \"%s.%s\" was reindexed",
								get_namespace_name(get_rel_namespace(relid)),
								get_rel_name(relid))));

			PopActiveSnapshot();
		}

		CommitTransactionCommand();
	}

	StartTransactionCommand();
}


/*
 * ReindexRelationConcurrently - process REINDEX CONCURRENTLY for given
 * relation OID
 *
 * 'relationOid' can either belong to an index, a table or a materialized
 * view.  For tables and materialized views, all its indexes will be rebuilt,
 * excluding invalid indexes and any indexes used in exclusion constraints,
 * but including its associated toast table indexes.  For indexes, the index
 * itself will be rebuilt.
 *
 * The locks taken on parent tables and involved indexes are kept until the
 * transaction is committed, at which point a session lock is taken on each
 * relation.  Both of these protect against concurrent schema changes.
 *
 * Returns true if any indexes have been rebuilt (including toast table's
 * indexes, when relevant), otherwise returns false.
 *
 * NOTE: This cannot be used on temporary relations.  A concurrent build would
 * cause issues with ON COMMIT actions triggered by the transactions of the
 * concurrent build.  Temporary relations are not subject to concurrent
 * concerns, so there's no need for the more complicated concurrent build,
 * anyway, and a non-concurrent reindex is more efficient.
 */
static bool
ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const ReindexParams *params)
{
	typedef struct ReindexIndexInfo
	{
		Oid			indexId;
		Oid			tableId;
		Oid			amId;
		bool		safe;		/* for set_indexsafe_procflags */
	} ReindexIndexInfo;
	List	   *heapRelationIds = NIL;
	List	   *indexIds = NIL;
	List	   *newIndexIds = NIL;
	List	   *relationLocks = NIL;
	List	   *lockTags = NIL;
	ListCell   *lc,
			   *lc2;
	MemoryContext private_context;
	MemoryContext oldcontext;
	char		relkind;
	char	   *relationName = NULL;
	char	   *relationNamespace = NULL;
	PGRUsage	ru0;
	const int	progress_index[] = {
		PROGRESS_CREATEIDX_COMMAND,
		PROGRESS_CREATEIDX_PHASE,
		PROGRESS_CREATEIDX_INDEX_OID,
		PROGRESS_CREATEIDX_ACCESS_METHOD_OID
	};
	int64		progress_vals[4];

	/*
	 * Create a memory context that will survive forced transaction commits we
	 * do below.  Since it is a child of PortalContext, it will go away
	 * eventually even if we suffer an error; there's no need for special
	 * abort cleanup logic.
	 */
	private_context = AllocSetContextCreate(PortalContext,
											"ReindexConcurrent",
											ALLOCSET_SMALL_SIZES);

	if ((params->options & REINDEXOPT_VERBOSE) != 0)
	{
		/* Save data needed by REINDEX VERBOSE in private context */
		oldcontext = MemoryContextSwitchTo(private_context);

		relationName = get_rel_name(relationOid);
		relationNamespace = get_namespace_name(get_rel_namespace(relationOid));

		pg_rusage_init(&ru0);

		MemoryContextSwitchTo(oldcontext);
	}

	relkind = get_rel_relkind(relationOid);

	/*
	 * Extract the list of indexes that are going to be rebuilt based on the
	 * relation Oid given by caller.
	 */
	switch (relkind)
	{
		case RELKIND_RELATION:
		case RELKIND_MATVIEW:
		case RELKIND_TOASTVALUE:
			{
				/*
				 * In the case of a relation, find all its indexes including
				 * toast indexes.
				 */
				Relation	heapRelation;

				/* Save the list of relation OIDs in private context */
				oldcontext = MemoryContextSwitchTo(private_context);

				/* Track this relation for session locks */
				heapRelationIds = lappend_oid(heapRelationIds, relationOid);

				MemoryContextSwitchTo(oldcontext);

				if (IsCatalogRelationOid(relationOid))
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("cannot reindex system catalogs concurrently")));

				/* Open relation to get its indexes */
				if ((params->options & REINDEXOPT_MISSING_OK) != 0)
				{
					heapRelation = try_table_open(relationOid,
												  ShareUpdateExclusiveLock);
					/* leave if relation does not exist */
					if (!heapRelation)
						break;
				}
				else
					heapRelation = table_open(relationOid,
											  ShareUpdateExclusiveLock);

				if (OidIsValid(params->tablespaceOid) &&
					IsSystemRelation(heapRelation))
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("cannot move system relation \"%s\"",
									RelationGetRelationName(heapRelation))));

				/* Add all the valid indexes of relation to list */
				foreach(lc, RelationGetIndexList(heapRelation))
				{
					Oid			cellOid = lfirst_oid(lc);
					Relation	indexRelation = index_open(cellOid,
														   ShareUpdateExclusiveLock);

					if (!indexRelation->rd_index->indisvalid)
						ereport(WARNING,
								(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
								 errmsg("skipping reindex of invalid index \"%s.%s\"",
										get_namespace_name(get_rel_namespace(cellOid)),
										get_rel_name(cellOid)),
								 errhint("Use DROP INDEX or REINDEX INDEX.")));
					else if (indexRelation->rd_index->indisexclusion)
						ereport(WARNING,
								(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
								 errmsg("cannot reindex exclusion constraint index \"%s.%s\" concurrently, skipping",
										get_namespace_name(get_rel_namespace(cellOid)),
										get_rel_name(cellOid))));
					else
					{
						ReindexIndexInfo *idx;

						/* Save the list of relation OIDs in private context */
						oldcontext = MemoryContextSwitchTo(private_context);

						idx = palloc_object(ReindexIndexInfo);
						idx->indexId = cellOid;
						/* other fields set later */

						indexIds = lappend(indexIds, idx);

						MemoryContextSwitchTo(oldcontext);
					}

					index_close(indexRelation, NoLock);
				}

				/* Also add the toast indexes */
				if (OidIsValid(heapRelation->rd_rel->reltoastrelid))
				{
					Oid			toastOid = heapRelation->rd_rel->reltoastrelid;
					Relation	toastRelation = table_open(toastOid,
														   ShareUpdateExclusiveLock);

					/* Save the list of relation OIDs in private context */
					oldcontext = MemoryContextSwitchTo(private_context);

					/* Track this relation for session locks */
					heapRelationIds = lappend_oid(heapRelationIds, toastOid);

					MemoryContextSwitchTo(oldcontext);

					foreach(lc2, RelationGetIndexList(toastRelation))
					{
						Oid			cellOid = lfirst_oid(lc2);
						Relation	indexRelation = index_open(cellOid,
															   ShareUpdateExclusiveLock);

						if (!indexRelation->rd_index->indisvalid)
							ereport(WARNING,
									(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
									 errmsg("skipping reindex of invalid index \"%s.%s\"",
											get_namespace_name(get_rel_namespace(cellOid)),
											get_rel_name(cellOid)),
									 errhint("Use DROP INDEX or REINDEX INDEX.")));
						else
						{
							ReindexIndexInfo *idx;

							/*
							 * Save the list of relation OIDs in private
							 * context
							 */
							oldcontext = MemoryContextSwitchTo(private_context);

							idx = palloc_object(ReindexIndexInfo);
							idx->indexId = cellOid;
							indexIds = lappend(indexIds, idx);
							/* other fields set later */

							MemoryContextSwitchTo(oldcontext);
						}

						index_close(indexRelation, NoLock);
					}

					table_close(toastRelation, NoLock);
				}

				table_close(heapRelation, NoLock);
				break;
			}
		case RELKIND_INDEX:
			{
				Oid			heapId = IndexGetRelation(relationOid,
													  (params->options & REINDEXOPT_MISSING_OK) != 0);
				Relation	heapRelation;
				ReindexIndexInfo *idx;

				/* if relation is missing, leave */
				if (!OidIsValid(heapId))
					break;

				if (IsCatalogRelationOid(heapId))
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("cannot reindex system catalogs concurrently")));

				/*
				 * Don't allow reindex for an invalid index on TOAST table, as
				 * if rebuilt it would not be possible to drop it.  Match
				 * error message in reindex_index().
				 */
				if (IsToastNamespace(get_rel_namespace(relationOid)) &&
					!get_index_isvalid(relationOid))
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("cannot reindex invalid index on TOAST table")));

				/*
				 * Check if parent relation can be locked and if it exists,
				 * this needs to be done at this stage as the list of indexes
				 * to rebuild is not complete yet, and REINDEXOPT_MISSING_OK
				 * should not be used once all the session locks are taken.
				 */
				if ((params->options & REINDEXOPT_MISSING_OK) != 0)
				{
					heapRelation = try_table_open(heapId,
												  ShareUpdateExclusiveLock);
					/* leave if relation does not exist */
					if (!heapRelation)
						break;
				}
				else
					heapRelation = table_open(heapId,
											  ShareUpdateExclusiveLock);

				if (OidIsValid(params->tablespaceOid) &&
					IsSystemRelation(heapRelation))
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("cannot move system relation \"%s\"",
									get_rel_name(relationOid))));

				table_close(heapRelation, NoLock);

				/* Save the list of relation OIDs in private context */
				oldcontext = MemoryContextSwitchTo(private_context);

				/* Track the heap relation of this index for session locks */
				heapRelationIds = list_make1_oid(heapId);

				/*
				 * Save the list of relation OIDs in private context.  Note
				 * that invalid indexes are allowed here.
				 */
				idx = palloc_object(ReindexIndexInfo);
				idx->indexId = relationOid;
				indexIds = lappend(indexIds, idx);
				/* other fields set later */

				MemoryContextSwitchTo(oldcontext);
				break;
			}

		case RELKIND_PARTITIONED_TABLE:
		case RELKIND_PARTITIONED_INDEX:
		default:
			/* Return error if type of relation is not supported */
			ereport(ERROR,
					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
					 errmsg("cannot reindex this type of relation concurrently")));
			break;
	}

	/*
	 * Definitely no indexes, so leave.  Any checks based on
	 * REINDEXOPT_MISSING_OK should be done only while the list of indexes to
	 * work on is built as the session locks taken before this transaction
	 * commits will make sure that they cannot be dropped by a concurrent
	 * session until this operation completes.
	 */
	if (indexIds == NIL)
		return false;

	/* It's not a shared catalog, so refuse to move it to shared tablespace */
	if (params->tablespaceOid == GLOBALTABLESPACE_OID)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("cannot move non-shared relation to tablespace \"%s\"",
						get_tablespace_name(params->tablespaceOid))));

	Assert(heapRelationIds != NIL);

	/*-----
	 * Now we have all the indexes we want to process in indexIds.
	 *
	 * The phases now are:
	 *
	 * 1. create new indexes in the catalog
	 * 2. build new indexes
	 * 3. let new indexes catch up with tuples inserted in the meantime
	 * 4. swap index names
	 * 5. mark old indexes as dead
	 * 6. drop old indexes
	 *
	 * We process each phase for all indexes before moving to the next phase,
	 * for efficiency.
	 */

	/*
	 * Phase 1 of REINDEX CONCURRENTLY
	 *
	 * Create a new index with the same properties as the old one, but it is
	 * only registered in catalogs and will be built later.  Then get session
	 * locks on all involved tables.  See analogous code in DefineIndex() for
	 * more detailed comments.
	 */

	foreach(lc, indexIds)
	{
		char	   *concurrentName;
		ReindexIndexInfo *idx = lfirst(lc);
		ReindexIndexInfo *newidx;
		Oid			newIndexId;
		Relation	indexRel;
		Relation	heapRel;
		Oid			save_userid;
		int			save_sec_context;
		int			save_nestlevel;
		Relation	newIndexRel;
		LockRelId  *lockrelid;
		Oid			tablespaceid;

		indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock);
		heapRel = table_open(indexRel->rd_index->indrelid,
							 ShareUpdateExclusiveLock);

		/*
		 * Switch to the table owner's userid, so that any index functions are
		 * run as that user.  Also lock down security-restricted operations
		 * and arrange to make GUC variable changes local to this command.
		 */
		GetUserIdAndSecContext(&save_userid, &save_sec_context);
		SetUserIdAndSecContext(heapRel->rd_rel->relowner,
							   save_sec_context | SECURITY_RESTRICTED_OPERATION);
		save_nestlevel = NewGUCNestLevel();
		RestrictSearchPath();

		/* determine safety of this index for set_indexsafe_procflags */
		idx->safe = (RelationGetIndexExpressions(indexRel) == NIL &&
					 RelationGetIndexPredicate(indexRel) == NIL);

#ifdef USE_INJECTION_POINTS
		if (idx->safe)
			INJECTION_POINT("reindex-conc-index-safe", NULL);
		else
			INJECTION_POINT("reindex-conc-index-not-safe", NULL);
#endif

		idx->tableId = RelationGetRelid(heapRel);
		idx->amId = indexRel->rd_rel->relam;

		/* This function shouldn't be called for temporary relations. */
		if (indexRel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
			elog(ERROR, "cannot reindex a temporary table concurrently");

		pgstat_progress_start_command(PROGRESS_COMMAND_CREATE_INDEX, idx->tableId);

		progress_vals[0] = PROGRESS_CREATEIDX_COMMAND_REINDEX_CONCURRENTLY;
		progress_vals[1] = 0;	/* initializing */
		progress_vals[2] = idx->indexId;
		progress_vals[3] = idx->amId;
		pgstat_progress_update_multi_param(4, progress_index, progress_vals);

		/* Choose a temporary relation name for the new index */
		concurrentName = ChooseRelationName(get_rel_name(idx->indexId),
											NULL,
											"ccnew",
											get_rel_namespace(indexRel->rd_index->indrelid),
											false);

		/* Choose the new tablespace, indexes of toast tables are not moved */
		if (OidIsValid(params->tablespaceOid) &&
			heapRel->rd_rel->relkind != RELKIND_TOASTVALUE)
			tablespaceid = params->tablespaceOid;
		else
			tablespaceid = indexRel->rd_rel->reltablespace;

		/* Create new index definition based on given index */
		newIndexId = index_create_copy(heapRel,
									   INDEX_CREATE_CONCURRENT |
									   INDEX_CREATE_SKIP_BUILD |
									   INDEX_CREATE_SUPPRESS_PROGRESS,
									   idx->indexId,
									   tablespaceid,
									   concurrentName);

		/*
		 * Now open the relation of the new index, a session-level lock is
		 * also needed on it.
		 */
		newIndexRel = index_open(newIndexId, ShareUpdateExclusiveLock);

		/*
		 * Save the list of OIDs and locks in private context
		 */
		oldcontext = MemoryContextSwitchTo(private_context);

		newidx = palloc_object(ReindexIndexInfo);
		newidx->indexId = newIndexId;
		newidx->safe = idx->safe;
		newidx->tableId = idx->tableId;
		newidx->amId = idx->amId;

		newIndexIds = lappend(newIndexIds, newidx);

		/*
		 * Save lockrelid to protect each relation from drop then close
		 * relations. The lockrelid on parent relation is not taken here to
		 * avoid multiple locks taken on the same relation, instead we rely on
		 * parentRelationIds built earlier.
		 */
		lockrelid = palloc_object(LockRelId);
		*lockrelid = indexRel->rd_lockInfo.lockRelId;
		relationLocks = lappend(relationLocks, lockrelid);
		lockrelid = palloc_object(LockRelId);
		*lockrelid = newIndexRel->rd_lockInfo.lockRelId;
		relationLocks = lappend(relationLocks, lockrelid);

		MemoryContextSwitchTo(oldcontext);

		index_close(indexRel, NoLock);
		index_close(newIndexRel, NoLock);

		/* Roll back any GUC changes executed by index functions */
		AtEOXact_GUC(false, save_nestlevel);

		/* Restore userid and security context */
		SetUserIdAndSecContext(save_userid, save_sec_context);

		table_close(heapRel, NoLock);

		/*
		 * If a statement is available, telling that this comes from a REINDEX
		 * command, collect the new index for event triggers.
		 */
		if (stmt)
		{
			ObjectAddress address;

			ObjectAddressSet(address, RelationRelationId, newIndexId);
			EventTriggerCollectSimpleCommand(address,
											 InvalidObjectAddress,
											 (const Node *) stmt);
		}
	}

	/*
	 * Save the heap lock for following visibility checks with other backends
	 * might conflict with this session.
	 */
	foreach(lc, heapRelationIds)
	{
		Relation	heapRelation = table_open(lfirst_oid(lc), ShareUpdateExclusiveLock);
		LockRelId  *lockrelid;
		LOCKTAG    *heaplocktag;

		/* Save the list of locks in private context */
		oldcontext = MemoryContextSwitchTo(private_context);

		/* Add lockrelid of heap relation to the list of locked relations */
		lockrelid = palloc_object(LockRelId);
		*lockrelid = heapRelation->rd_lockInfo.lockRelId;
		relationLocks = lappend(relationLocks, lockrelid);

		heaplocktag = palloc_object(LOCKTAG);

		/* Save the LOCKTAG for this parent relation for the wait phase */
		SET_LOCKTAG_RELATION(*heaplocktag, lockrelid->dbId, lockrelid->relId);
		lockTags = lappend(lockTags, heaplocktag);

		MemoryContextSwitchTo(oldcontext);

		/* Close heap relation */
		table_close(heapRelation, NoLock);
	}

	/* Get a session-level lock on each table. */
	foreach(lc, relationLocks)
	{
		LockRelId  *lockrelid = (LockRelId *) lfirst(lc);

		LockRelationIdForSession(lockrelid, ShareUpdateExclusiveLock);
	}

	PopActiveSnapshot();
	CommitTransactionCommand();
	StartTransactionCommand();

	/*
	 * Because we don't take a snapshot in this transaction, there's no need
	 * to set the PROC_IN_SAFE_IC flag here.
	 */

	/*
	 * Phase 2 of REINDEX CONCURRENTLY
	 *
	 * Build the new indexes in a separate transaction for each index to avoid
	 * having open transactions for an unnecessary long time.  But before
	 * doing that, wait until no running transactions could have the table of
	 * the index open with the old list of indexes.  See "phase 2" in
	 * DefineIndex() for more details.
	 */

	pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE,
								 PROGRESS_CREATEIDX_PHASE_WAIT_1);
	WaitForLockersMultiple(lockTags, ShareLock, true);
	CommitTransactionCommand();

	foreach(lc, newIndexIds)
	{
		ReindexIndexInfo *newidx = lfirst(lc);

		/* Start new transaction for this index's concurrent build */
		StartTransactionCommand();

		/*
		 * Check for user-requested abort.  This is inside a transaction so as
		 * xact.c does not issue a useless WARNING, and ensures that
		 * session-level locks are cleaned up on abort.
		 */
		CHECK_FOR_INTERRUPTS();

		/* Tell concurrent indexing to ignore us, if index qualifies */
		if (newidx->safe)
			set_indexsafe_procflags();

		/* Set ActiveSnapshot since functions in the indexes may need it */
		PushActiveSnapshot(GetTransactionSnapshot());

		/*
		 * Update progress for the index to build, with the correct parent
		 * table involved.
		 */
		pgstat_progress_start_command(PROGRESS_COMMAND_CREATE_INDEX, newidx->tableId);
		progress_vals[0] = PROGRESS_CREATEIDX_COMMAND_REINDEX_CONCURRENTLY;
		progress_vals[1] = PROGRESS_CREATEIDX_PHASE_BUILD;
		progress_vals[2] = newidx->indexId;
		progress_vals[3] = newidx->amId;
		pgstat_progress_update_multi_param(4, progress_index, progress_vals);

		/* Perform concurrent build of new index */
		index_concurrently_build(newidx->tableId, newidx->indexId);

		PopActiveSnapshot();
		CommitTransactionCommand();
	}

	StartTransactionCommand();

	/*
	 * Because we don't take a snapshot or Xid in this transaction, there's no
	 * need to set the PROC_IN_SAFE_IC flag here.
	 */

	/*
	 * Phase 3 of REINDEX CONCURRENTLY
	 *
	 * During this phase the old indexes catch up with any new tuples that
	 * were created during the previous phase.  See "phase 3" in DefineIndex()
	 * for more details.
	 */

	pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE,
								 PROGRESS_CREATEIDX_PHASE_WAIT_2);
	WaitForLockersMultiple(lockTags, ShareLock, true);
	CommitTransactionCommand();

	foreach(lc, newIndexIds)
	{
		ReindexIndexInfo *newidx = lfirst(lc);
		TransactionId limitXmin;
		Snapshot	snapshot;

		StartTransactionCommand();

		/*
		 * Check for user-requested abort.  This is inside a transaction so as
		 * xact.c does not issue a useless WARNING, and ensures that
		 * session-level locks are cleaned up on abort.
		 */
		CHECK_FOR_INTERRUPTS();

		/* Tell concurrent indexing to ignore us, if index qualifies */
		if (newidx->safe)
			set_indexsafe_procflags();

		/*
		 * Take the "reference snapshot" that will be used by validate_index()
		 * to filter candidate tuples.
		 */
		snapshot = RegisterSnapshot(GetTransactionSnapshot());
		PushActiveSnapshot(snapshot);

		/*
		 * Update progress for the index to build, with the correct parent
		 * table involved.
		 */
		pgstat_progress_start_command(PROGRESS_COMMAND_CREATE_INDEX, newidx->tableId);
		progress_vals[0] = PROGRESS_CREATEIDX_COMMAND_REINDEX_CONCURRENTLY;
		progress_vals[1] = PROGRESS_CREATEIDX_PHASE_VALIDATE_IDXSCAN;
		progress_vals[2] = newidx->indexId;
		progress_vals[3] = newidx->amId;
		pgstat_progress_update_multi_param(4, progress_index, progress_vals);

		validate_index(newidx->tableId, newidx->indexId, snapshot);

		/*
		 * We can now do away with our active snapshot, we still need to save
		 * the xmin limit to wait for older snapshots.
		 */
		limitXmin = snapshot->xmin;

		PopActiveSnapshot();
		UnregisterSnapshot(snapshot);

		/*
		 * To ensure no deadlocks, we must commit and start yet another
		 * transaction, and do our wait before any snapshot has been taken in
		 * it.
		 */
		CommitTransactionCommand();
		StartTransactionCommand();

		/*
		 * The index is now valid in the sense that it contains all currently
		 * interesting tuples.  But since it might not contain tuples deleted
		 * just before the reference snap was taken, we have to wait out any
		 * transactions that might have older snapshots.
		 *
		 * Because we don't take a snapshot or Xid in this transaction,
		 * there's no need to set the PROC_IN_SAFE_IC flag here.
		 */
		pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE,
									 PROGRESS_CREATEIDX_PHASE_WAIT_3);
		WaitForOlderSnapshots(limitXmin, true);

		CommitTransactionCommand();
	}

	/*
	 * Phase 4 of REINDEX CONCURRENTLY
	 *
	 * Now that the new indexes have been validated, swap each new index with
	 * its corresponding old index.
	 *
	 * We mark the new indexes as valid and the old indexes as not valid at
	 * the same time to make sure we only get constraint violations from the
	 * indexes with the correct names.
	 */

	INJECTION_POINT("reindex-relation-concurrently-before-swap", NULL);
	StartTransactionCommand();

	/*
	 * Because this transaction only does catalog manipulations and doesn't do
	 * any index operations, we can set the PROC_IN_SAFE_IC flag here
	 * unconditionally.
	 */
	set_indexsafe_procflags();

	forboth(lc, indexIds, lc2, newIndexIds)
	{
		ReindexIndexInfo *oldidx = lfirst(lc);
		ReindexIndexInfo *newidx = lfirst(lc2);
		char	   *oldName;

		/*
		 * Check for user-requested abort.  This is inside a transaction so as
		 * xact.c does not issue a useless WARNING, and ensures that
		 * session-level locks are cleaned up on abort.
		 */
		CHECK_FOR_INTERRUPTS();

		/* Choose a relation name for old index */
		oldName = ChooseRelationName(get_rel_name(oldidx->indexId),
									 NULL,
									 "ccold",
									 get_rel_namespace(oldidx->tableId),
									 false);

		/*
		 * Swapping the indexes might involve TOAST table access, so ensure we
		 * have a valid snapshot.
		 */
		PushActiveSnapshot(GetTransactionSnapshot());

		/*
		 * Swap old index with the new one.  This also marks the new one as
		 * valid and the old one as not valid.
		 */
		index_concurrently_swap(newidx->indexId, oldidx->indexId, oldName);

		PopActiveSnapshot();

		/*
		 * Invalidate the relcache for the table, so that after this commit
		 * all sessions will refresh any cached plans that might reference the
		 * index.
		 */
		CacheInvalidateRelcacheByRelid(oldidx->tableId);

		/*
		 * CCI here so that subsequent iterations see the oldName in the
		 * catalog and can choose a nonconflicting name for their oldName.
		 * Otherwise, this could lead to conflicts if a table has two indexes
		 * whose names are equal for the first NAMEDATALEN-minus-a-few
		 * characters.
		 */
		CommandCounterIncrement();
	}

	/* Commit this transaction and make index swaps visible */
	CommitTransactionCommand();
	StartTransactionCommand();

	/*
	 * While we could set PROC_IN_SAFE_IC if all indexes qualified, there's no
	 * real need for that, because we only acquire an Xid after the wait is
	 * done, and that lasts for a very short period.
	 */

	/*
	 * Phase 5 of REINDEX CONCURRENTLY
	 *
	 * Mark the old indexes as dead.  First we must wait until no running
	 * transaction could be using the index for a query.  See also
	 * index_drop() for more details.
	 */

	INJECTION_POINT("reindex-relation-concurrently-before-set-dead", NULL);
	pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE,
								 PROGRESS_CREATEIDX_PHASE_WAIT_4);
	WaitForLockersMultiple(lockTags, AccessExclusiveLock, true);

	foreach(lc, indexIds)
	{
		ReindexIndexInfo *oldidx = lfirst(lc);

		/*
		 * Check for user-requested abort.  This is inside a transaction so as
		 * xact.c does not issue a useless WARNING, and ensures that
		 * session-level locks are cleaned up on abort.
		 */
		CHECK_FOR_INTERRUPTS();

		/*
		 * Updating pg_index might involve TOAST table access, so ensure we
		 * have a valid snapshot.
		 */
		PushActiveSnapshot(GetTransactionSnapshot());

		index_concurrently_set_dead(oldidx->tableId, oldidx->indexId);

		PopActiveSnapshot();
	}

	/* Commit this transaction to make the updates visible. */
	CommitTransactionCommand();
	StartTransactionCommand();

	/*
	 * While we could set PROC_IN_SAFE_IC if all indexes qualified, there's no
	 * real need for that, because we only acquire an Xid after the wait is
	 * done, and that lasts for a very short period.
	 */

	/*
	 * Phase 6 of REINDEX CONCURRENTLY
	 *
	 * Drop the old indexes.
	 */

	pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE,
								 PROGRESS_CREATEIDX_PHASE_WAIT_5);
	WaitForLockersMultiple(lockTags, AccessExclusiveLock, true);

	PushActiveSnapshot(GetTransactionSnapshot());

	{
		ObjectAddresses *objects = new_object_addresses();

		foreach(lc, indexIds)
		{
			ReindexIndexInfo *idx = lfirst(lc);
			ObjectAddress object;

			object.classId = RelationRelationId;
			object.objectId = idx->indexId;
			object.objectSubId = 0;

			add_exact_object_address(&object, objects);
		}

		/*
		 * Use PERFORM_DELETION_CONCURRENT_LOCK so that index_drop() uses the
		 * right lock level.
		 */
		performMultipleDeletions(objects, DROP_RESTRICT,
								 PERFORM_DELETION_CONCURRENT_LOCK | PERFORM_DELETION_INTERNAL);
	}

	PopActiveSnapshot();
	CommitTransactionCommand();

	/*
	 * Finally, release the session-level lock on the table.
	 */
	foreach(lc, relationLocks)
	{
		LockRelId  *lockrelid = (LockRelId *) lfirst(lc);

		UnlockRelationIdForSession(lockrelid, ShareUpdateExclusiveLock);
	}

	/* Start a new transaction to finish process properly */
	StartTransactionCommand();

	/* Log what we did */
	if ((params->options & REINDEXOPT_VERBOSE) != 0)
	{
		if (relkind == RELKIND_INDEX)
			ereport(INFO,
					(errmsg("index \"%s.%s\" was reindexed",
							relationNamespace, relationName),
					 errdetail("%s.",
							   pg_rusage_show(&ru0))));
		else
		{
			foreach(lc, newIndexIds)
			{
				ReindexIndexInfo *idx = lfirst(lc);
				Oid			indOid = idx->indexId;

				ereport(INFO,
						(errmsg("index \"%s.%s\" was reindexed",
								get_namespace_name(get_rel_namespace(indOid)),
								get_rel_name(indOid))));
				/* Don't show rusage here, since it's not per index. */
			}

			ereport(INFO,
					(errmsg("table \"%s.%s\" was reindexed",
							relationNamespace, relationName),
					 errdetail("%s.",
							   pg_rusage_show(&ru0))));
		}
	}

	MemoryContextDelete(private_context);

	pgstat_progress_end_command();

	return true;
}

/*
 * Insert or delete an appropriate pg_inherits tuple to make the given index
 * be a partition of the indicated parent index.
 *
 * This also corrects the pg_depend information for the affected index.
 */
void
IndexSetParentIndex(Relation partitionIdx, Oid parentOid)
{
	Relation	pg_inherits;
	ScanKeyData key[2];
	SysScanDesc scan;
	Oid			partRelid = RelationGetRelid(partitionIdx);
	HeapTuple	tuple;
	bool		fix_dependencies;

	/* Make sure this is an index */
	Assert(partitionIdx->rd_rel->relkind == RELKIND_INDEX ||
		   partitionIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);

	/*
	 * Scan pg_inherits for rows linking our index to some parent.
	 */
	pg_inherits = relation_open(InheritsRelationId, RowExclusiveLock);
	ScanKeyInit(&key[0],
				Anum_pg_inherits_inhrelid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(partRelid));
	ScanKeyInit(&key[1],
				Anum_pg_inherits_inhseqno,
				BTEqualStrategyNumber, F_INT4EQ,
				Int32GetDatum(1));
	scan = systable_beginscan(pg_inherits, InheritsRelidSeqnoIndexId, true,
							  NULL, 2, key);
	tuple = systable_getnext(scan);

	if (!HeapTupleIsValid(tuple))
	{
		if (parentOid == InvalidOid)
		{
			/*
			 * No pg_inherits row, and no parent wanted: nothing to do in this
			 * case.
			 */
			fix_dependencies = false;
		}
		else
		{
			StoreSingleInheritance(partRelid, parentOid, 1);
			fix_dependencies = true;
		}
	}
	else
	{
		Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(tuple);

		if (parentOid == InvalidOid)
		{
			/*
			 * There exists a pg_inherits row, which we want to clear; do so.
			 */
			CatalogTupleDelete(pg_inherits, &tuple->t_self);
			fix_dependencies = true;
		}
		else
		{
			/*
			 * A pg_inherits row exists.  If it's the same we want, then we're
			 * good; if it differs, that amounts to a corrupt catalog and
			 * should not happen.
			 */
			if (inhForm->inhparent != parentOid)
			{
				/* unexpected: we should not get called in this case */
				elog(ERROR, "bogus pg_inherit row: inhrelid %u inhparent %u",
					 inhForm->inhrelid, inhForm->inhparent);
			}

			/* already in the right state */
			fix_dependencies = false;
		}
	}

	/* done with pg_inherits */
	systable_endscan(scan);
	relation_close(pg_inherits, RowExclusiveLock);

	/* set relhassubclass if an index partition has been added to the parent */
	if (OidIsValid(parentOid))
	{
		LockRelationOid(parentOid, ShareUpdateExclusiveLock);
		SetRelationHasSubclass(parentOid, true);
	}

	/* set relispartition correctly on the partition */
	update_relispartition(partRelid, OidIsValid(parentOid));

	if (fix_dependencies)
	{
		/*
		 * Insert/delete pg_depend rows.  If setting a parent, add PARTITION
		 * dependencies on the parent index and the table; if removing a
		 * parent, delete PARTITION dependencies.
		 */
		if (OidIsValid(parentOid))
		{
			ObjectAddress partIdx;
			ObjectAddress parentIdx;
			ObjectAddress partitionTbl;

			ObjectAddressSet(partIdx, RelationRelationId, partRelid);
			ObjectAddressSet(parentIdx, RelationRelationId, parentOid);
			ObjectAddressSet(partitionTbl, RelationRelationId,
							 partitionIdx->rd_index->indrelid);
			recordDependencyOn(&partIdx, &parentIdx,
							   DEPENDENCY_PARTITION_PRI);
			recordDependencyOn(&partIdx, &partitionTbl,
							   DEPENDENCY_PARTITION_SEC);
		}
		else
		{
			deleteDependencyRecordsForClass(RelationRelationId, partRelid,
											RelationRelationId,
											DEPENDENCY_PARTITION_PRI);
			deleteDependencyRecordsForClass(RelationRelationId, partRelid,
											RelationRelationId,
											DEPENDENCY_PARTITION_SEC);
		}

		/* make our updates visible */
		CommandCounterIncrement();
	}
}

/*
 * Subroutine of IndexSetParentIndex to update the relispartition flag of the
 * given index to the given value.
 */
static void
update_relispartition(Oid relationId, bool newval)
{
	HeapTuple	tup;
	Relation	classRel;
	ItemPointerData otid;

	classRel = table_open(RelationRelationId, RowExclusiveLock);
	tup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relationId));
	if (!HeapTupleIsValid(tup))
		elog(ERROR, "cache lookup failed for relation %u", relationId);
	otid = tup->t_self;
	Assert(((Form_pg_class) GETSTRUCT(tup))->relispartition != newval);
	((Form_pg_class) GETSTRUCT(tup))->relispartition = newval;
	CatalogTupleUpdate(classRel, &otid, tup);
	UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
	heap_freetuple(tup);
	table_close(classRel, RowExclusiveLock);
}

/*
 * Set the PROC_IN_SAFE_IC flag in MyProc->statusFlags.
 *
 * When doing concurrent index builds, we can set this flag
 * to tell other processes concurrently running CREATE
 * INDEX CONCURRENTLY or REINDEX CONCURRENTLY to ignore us when
 * doing their waits for concurrent snapshots.  On one hand it
 * avoids pointlessly waiting for a process that's not interesting
 * anyway; but more importantly it avoids deadlocks in some cases.
 *
 * This can be done safely only for indexes that don't execute any
 * expressions that could access other tables, so index must not be
 * expressional nor partial.  Caller is responsible for only calling
 * this routine when that assumption holds true.
 *
 * (The flag is reset automatically at transaction end, so it must be
 * set for each transaction.)
 */
static inline void
set_indexsafe_procflags(void)
{
	/*
	 * This should only be called before installing xid or xmin in MyProc;
	 * otherwise, concurrent processes could see an Xmin that moves backwards.
	 */
	Assert(MyProc->xid == InvalidTransactionId &&
		   MyProc->xmin == InvalidTransactionId);

	LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
	MyProc->statusFlags |= PROC_IN_SAFE_IC;
	ProcGlobal->statusFlags[MyProc->pgxactoff] = MyProc->statusFlags;
	LWLockRelease(ProcArrayLock);
}
./indexing.c0000664000175000017500000002551115221144670011664 0ustar  xmanxman/*-------------------------------------------------------------------------
 *
 * indexing.c
 *	  This file contains routines to support indexes defined on system
 *	  catalogs.
 *
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/catalog/indexing.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/xact.h"
#include "catalog/index.h"
#include "catalog/indexing.h"
#include "executor/executor.h"
#include "utils/rel.h"


/*
 * CatalogOpenIndexes - open the indexes on a system catalog.
 *
 * When inserting or updating tuples in a system catalog, call this
 * to prepare to update the indexes for the catalog.
 *
 * In the current implementation, we share code for opening/closing the
 * indexes with execUtils.c.  But we do not use ExecInsertIndexTuples,
 * because we don't want to create an EState.  This implies that we
 * do not support partial or expressional indexes on system catalogs,
 * nor can we support generalized exclusion constraints.
 * This could be fixed with localized changes here if we wanted to pay
 * the extra overhead of building an EState.
 */
CatalogIndexState
CatalogOpenIndexes(Relation heapRel)
{
	ResultRelInfo *resultRelInfo;

	resultRelInfo = makeNode(ResultRelInfo);
	resultRelInfo->ri_RangeTableIndex = 0;	/* dummy */
	resultRelInfo->ri_RelationDesc = heapRel;
	resultRelInfo->ri_TrigDesc = NULL;	/* we don't fire triggers */

	ExecOpenIndices(resultRelInfo, false);

	return resultRelInfo;
}

/*
 * CatalogCloseIndexes - clean up resources allocated by CatalogOpenIndexes
 */
void
CatalogCloseIndexes(CatalogIndexState indstate)
{
	ExecCloseIndices(indstate);
	pfree(indstate);
}

/*
 * CatalogIndexInsert - insert index entries for one catalog tuple
 *
 * This should be called for each inserted or updated catalog tuple.
 *
 * This is effectively a cut-down version of ExecInsertIndexTuples.
 */
static void
CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple,
				   TU_UpdateIndexes updateIndexes)
{
	int			i;
	int			numIndexes;
	RelationPtr relationDescs;
	Relation	heapRelation;
	TupleTableSlot *slot;
	IndexInfo **indexInfoArray;
	Datum		values[INDEX_MAX_KEYS];
	bool		isnull[INDEX_MAX_KEYS];
	bool		onlySummarized = (updateIndexes == TU_Summarizing);

	/*
	 * HOT update does not require index inserts. But with asserts enabled we
	 * want to check that it'd be legal to currently insert into the
	 * table/index.
	 */
#ifndef USE_ASSERT_CHECKING
	if (HeapTupleIsHeapOnly(heapTuple) && !onlySummarized)
		return;
#endif

	/* When only updating summarized indexes, the tuple has to be HOT. */
	Assert((!onlySummarized) || HeapTupleIsHeapOnly(heapTuple));

	/*
	 * Get information from the state structure.  Fall out if nothing to do.
	 */
	numIndexes = indstate->ri_NumIndices;
	if (numIndexes == 0)
		return;
	relationDescs = indstate->ri_IndexRelationDescs;
	indexInfoArray = indstate->ri_IndexRelationInfo;
	heapRelation = indstate->ri_RelationDesc;

	/* Need a slot to hold the tuple being examined */
	slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation),
									&TTSOpsHeapTuple);
	ExecStoreHeapTuple(heapTuple, slot, false);

	/*
	 * for each index, form and insert the index tuple
	 */
	for (i = 0; i < numIndexes; i++)
	{
		IndexInfo  *indexInfo;
		Relation	index;

		indexInfo = indexInfoArray[i];
		index = relationDescs[i];

		/* If the index is marked as read-only, ignore it */
		if (!indexInfo->ii_ReadyForInserts)
			continue;

		/*
		 * Expressional and partial indexes on system catalogs are not
		 * supported, nor exclusion constraints, nor deferred uniqueness
		 */
		Assert(indexInfo->ii_Expressions == NIL);
		Assert(indexInfo->ii_ExpressionsExpand == NIL);
		Assert(indexInfo->ii_Predicate == NIL);
		Assert(indexInfo->ii_PredicateExpand == NIL);
		Assert(indexInfo->ii_ExclusionOps == NULL);
		Assert(index->rd_index->indimmediate);
		Assert(indexInfo->ii_NumIndexKeyAttrs != 0);

		/* see earlier check above */
#ifdef USE_ASSERT_CHECKING
		if (HeapTupleIsHeapOnly(heapTuple) && !onlySummarized)
		{
			Assert(!ReindexIsProcessingIndex(RelationGetRelid(index)));
			continue;
		}
#endif							/* USE_ASSERT_CHECKING */

		/*
		 * Skip insertions into non-summarizing indexes if we only need to
		 * update summarizing indexes.
		 */
		if (onlySummarized && !indexInfo->ii_Summarizing)
			continue;

		/*
		 * FormIndexDatum fills in its values and isnull parameters with the
		 * appropriate values for the column(s) of the index.
		 */
		FormIndexDatum(indexInfo,
					   slot,
					   NULL,	/* no expression eval to do */
					   values,
					   isnull);

		/*
		 * The index AM does the rest.
		 */
		index_insert(index,		/* index relation */
					 values,	/* array of index Datums */
					 isnull,	/* is-null flags */
					 &(heapTuple->t_self),	/* tid of heap tuple */
					 heapRelation,
					 index->rd_index->indisunique ?
					 UNIQUE_CHECK_YES : UNIQUE_CHECK_NO,
					 false,
					 indexInfo);
	}

	ExecDropSingleTupleTableSlot(slot);
}

/*
 * Subroutine to verify that catalog constraints are honored.
 *
 * Tuples inserted via CatalogTupleInsert/CatalogTupleUpdate are generally
 * "hand made", so that it's possible that they fail to satisfy constraints
 * that would be checked if they were being inserted by the executor.  That's
 * a coding error, so we only bother to check for it in assert-enabled builds.
 */
#ifdef USE_ASSERT_CHECKING

static void
CatalogTupleCheckConstraints(Relation heapRel, HeapTuple tup)
{
	/*
	 * Currently, the only constraints implemented for system catalogs are
	 * attnotnull constraints.
	 */
	if (HeapTupleHasNulls(tup))
	{
		TupleDesc	tupdesc = RelationGetDescr(heapRel);
		uint8	   *bp = tup->t_data->t_bits;

		for (int attnum = 0; attnum < tupdesc->natts; attnum++)
		{
			Form_pg_attribute thisatt = TupleDescAttr(tupdesc, attnum);

			Assert(!(thisatt->attnotnull && att_isnull(attnum, bp)));
		}
	}
}

#else							/* !USE_ASSERT_CHECKING */

#define CatalogTupleCheckConstraints(heapRel, tup)  ((void) 0)

#endif							/* USE_ASSERT_CHECKING */

/*
 * CatalogTupleInsert - do heap and indexing work for a new catalog tuple
 *
 * Insert the tuple data in "tup" into the specified catalog relation.
 *
 * This is a convenience routine for the common case of inserting a single
 * tuple in a system catalog; it inserts a new heap tuple, keeping indexes
 * current.  Avoid using it for multiple tuples, since opening the indexes
 * and building the index info structures is moderately expensive.
 * (Use CatalogTupleInsertWithInfo in such cases.)
 */
void
CatalogTupleInsert(Relation heapRel, HeapTuple tup)
{
	CatalogIndexState indstate;

	CatalogTupleCheckConstraints(heapRel, tup);

	indstate = CatalogOpenIndexes(heapRel);

	simple_heap_insert(heapRel, tup);

	CatalogIndexInsert(indstate, tup, TU_All);
	CatalogCloseIndexes(indstate);
}

/*
 * CatalogTupleInsertWithInfo - as above, but with caller-supplied index info
 *
 * This should be used when it's important to amortize CatalogOpenIndexes/
 * CatalogCloseIndexes work across multiple insertions.  At some point we
 * might cache the CatalogIndexState data somewhere (perhaps in the relcache)
 * so that callers needn't trouble over this ... but we don't do so today.
 */
void
CatalogTupleInsertWithInfo(Relation heapRel, HeapTuple tup,
						   CatalogIndexState indstate)
{
	CatalogTupleCheckConstraints(heapRel, tup);

	simple_heap_insert(heapRel, tup);

	CatalogIndexInsert(indstate, tup, TU_All);
}

/*
 * CatalogTuplesMultiInsertWithInfo - as above, but for multiple tuples
 *
 * Insert multiple tuples into the given catalog relation at once, with an
 * amortized cost of CatalogOpenIndexes.
 */
void
CatalogTuplesMultiInsertWithInfo(Relation heapRel, TupleTableSlot **slot,
								 int ntuples, CatalogIndexState indstate)
{
	/* Nothing to do */
	if (ntuples <= 0)
		return;

	heap_multi_insert(heapRel, slot, ntuples,
					  GetCurrentCommandId(true), 0, NULL);

	/*
	 * There is no equivalent to heap_multi_insert for the catalog indexes, so
	 * we must loop over and insert individually.
	 */
	for (int i = 0; i < ntuples; i++)
	{
		bool		should_free;
		HeapTuple	tuple;

		tuple = ExecFetchSlotHeapTuple(slot[i], true, &should_free);
		tuple->t_tableOid = slot[i]->tts_tableOid;
		CatalogIndexInsert(indstate, tuple, TU_All);

		if (should_free)
			heap_freetuple(tuple);
	}
}

/*
 * CatalogTupleUpdate - do heap and indexing work for updating a catalog tuple
 *
 * Update the tuple identified by "otid", replacing it with the data in "tup".
 *
 * This is a convenience routine for the common case of updating a single
 * tuple in a system catalog; it updates one heap tuple, keeping indexes
 * current.  Avoid using it for multiple tuples, since opening the indexes
 * and building the index info structures is moderately expensive.
 * (Use CatalogTupleUpdateWithInfo in such cases.)
 */
void
CatalogTupleUpdate(Relation heapRel, const ItemPointerData *otid, HeapTuple tup)
{
	CatalogIndexState indstate;
	TU_UpdateIndexes updateIndexes = TU_All;

	CatalogTupleCheckConstraints(heapRel, tup);

	indstate = CatalogOpenIndexes(heapRel);

	simple_heap_update(heapRel, otid, tup, &updateIndexes);

	CatalogIndexInsert(indstate, tup, updateIndexes);
	CatalogCloseIndexes(indstate);
}

/*
 * CatalogTupleUpdateWithInfo - as above, but with caller-supplied index info
 *
 * This should be used when it's important to amortize CatalogOpenIndexes/
 * CatalogCloseIndexes work across multiple updates.  At some point we
 * might cache the CatalogIndexState data somewhere (perhaps in the relcache)
 * so that callers needn't trouble over this ... but we don't do so today.
 */
void
CatalogTupleUpdateWithInfo(Relation heapRel, const ItemPointerData *otid, HeapTuple tup,
						   CatalogIndexState indstate)
{
	TU_UpdateIndexes updateIndexes = TU_All;

	CatalogTupleCheckConstraints(heapRel, tup);

	simple_heap_update(heapRel, otid, tup, &updateIndexes);

	CatalogIndexInsert(indstate, tup, updateIndexes);
}

/*
 * CatalogTupleDelete - do heap and indexing work for deleting a catalog tuple
 *
 * Delete the tuple identified by "tid" in the specified catalog.
 *
 * With Postgres heaps, there is no index work to do at deletion time;
 * cleanup will be done later by VACUUM.  However, callers of this function
 * shouldn't have to know that; we'd like a uniform abstraction for all
 * catalog tuple changes.  Hence, provide this currently-trivial wrapper.
 *
 * The abstraction is a bit leaky in that we don't provide an optimized
 * CatalogTupleDeleteWithInfo version, because there is currently nothing to
 * optimize.  If we ever need that, rather than touching a lot of call sites,
 * it might be better to do something about caching CatalogIndexState.
 */
void
CatalogTupleDelete(Relation heapRel, const ItemPointerData *tid)
{
	simple_heap_delete(heapRel, tid);
}
./indxpath.c0000664000175000017500000042536315221714454011712 0ustar  xmanxman/*-------------------------------------------------------------------------
 *
 * indxpath.c
 *	  Routines to determine which indexes are usable for scanning a
 *	  given relation, and create Paths accordingly.
 *
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/optimizer/path/indxpath.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/stratnum.h"
#include "access/sysattr.h"
#include "access/transam.h"
#include "catalog/pg_am.h"
#include "catalog/pg_amop.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/supportnodes.h"
#include "optimizer/cost.h"
#include "optimizer/optimizer.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/placeholder.h"
#include "optimizer/prep.h"
#include "optimizer/restrictinfo.h"
#include "utils/lsyscache.h"
#include "utils/selfuncs.h"


/* XXX see PartCollMatchesExprColl */
#define IndexCollMatchesExprColl(idxcollation, exprcollation) \
	((idxcollation) == InvalidOid || (idxcollation) == (exprcollation))

/* Whether we are looking for plain indexscan, bitmap scan, or either */
typedef enum
{
	ST_INDEXSCAN,				/* must support amgettuple */
	ST_BITMAPSCAN,				/* must support amgetbitmap */
	ST_ANYSCAN,					/* either is okay */
} ScanTypeControl;

/* Data structure for collecting qual clauses that match an index */
typedef struct
{
	bool		nonempty;		/* True if lists are not all empty */
	/* Lists of IndexClause nodes, one list per index column */
	List	   *indexclauses[INDEX_MAX_KEYS];
} IndexClauseSet;

/* Per-path data used within choose_bitmap_and() */
typedef struct
{
	Path	   *path;			/* IndexPath, BitmapAndPath, or BitmapOrPath */
	List	   *quals;			/* the WHERE clauses it uses */
	List	   *preds;			/* predicates of its partial index(es) */
	Bitmapset  *clauseids;		/* quals+preds represented as a bitmapset */
	bool		unclassifiable; /* has too many quals+preds to process? */
} PathClauseUsage;

/* Callback argument for ec_member_matches_indexcol */
typedef struct
{
	IndexOptInfo *index;		/* index we're considering */
	int			indexcol;		/* index column we want to match to */
} ec_member_matches_arg;


static void consider_index_join_clauses(PlannerInfo *root, RelOptInfo *rel,
										IndexOptInfo *index,
										IndexClauseSet *rclauseset,
										IndexClauseSet *jclauseset,
										IndexClauseSet *eclauseset,
										List **bitindexpaths);
static void consider_index_join_outer_rels(PlannerInfo *root, RelOptInfo *rel,
										   IndexOptInfo *index,
										   IndexClauseSet *rclauseset,
										   IndexClauseSet *jclauseset,
										   IndexClauseSet *eclauseset,
										   List **bitindexpaths,
										   List *indexjoinclauses,
										   int considered_clauses,
										   List **considered_relids);
static void get_join_index_paths(PlannerInfo *root, RelOptInfo *rel,
								 IndexOptInfo *index,
								 IndexClauseSet *rclauseset,
								 IndexClauseSet *jclauseset,
								 IndexClauseSet *eclauseset,
								 List **bitindexpaths,
								 Relids relids,
								 List **considered_relids);
static bool eclass_already_used(EquivalenceClass *parent_ec, Relids oldrelids,
								List *indexjoinclauses);
static void get_index_paths(PlannerInfo *root, RelOptInfo *rel,
							IndexOptInfo *index, IndexClauseSet *clauses,
							List **bitindexpaths);
static List *build_index_paths(PlannerInfo *root, RelOptInfo *rel,
							   IndexOptInfo *index, IndexClauseSet *clauses,
							   bool useful_predicate,
							   ScanTypeControl scantype,
							   bool *skip_nonnative_saop);
static List *build_paths_for_OR(PlannerInfo *root, RelOptInfo *rel,
								List *clauses, List *other_clauses);
static List *generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel,
									  List *clauses, List *other_clauses);
static Path *choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel,
							   List *paths);
static int	path_usage_comparator(const void *a, const void *b);
static Cost bitmap_scan_cost_est(PlannerInfo *root, RelOptInfo *rel,
								 Path *ipath);
static Cost bitmap_and_cost_est(PlannerInfo *root, RelOptInfo *rel,
								List *paths);
static PathClauseUsage *classify_index_clause_usage(Path *path,
													List **clauselist);
static void find_indexpath_quals(Path *bitmapqual, List **quals, List **preds);
static int	find_list_position(Node *node, List **nodelist);
static bool check_index_only(RelOptInfo *rel, IndexOptInfo *index);
static double get_loop_count(PlannerInfo *root, Index cur_relid, Relids outer_relids);
static double adjust_rowcount_for_semijoins(PlannerInfo *root,
											Index cur_relid,
											Index outer_relid,
											double rowcount);
static double approximate_joinrel_size(PlannerInfo *root, Relids relids);
static void match_restriction_clauses_to_index(PlannerInfo *root,
											   IndexOptInfo *index,
											   IndexClauseSet *clauseset);
static void match_join_clauses_to_index(PlannerInfo *root,
										RelOptInfo *rel, IndexOptInfo *index,
										IndexClauseSet *clauseset,
										List **joinorclauses);
static void match_eclass_clauses_to_index(PlannerInfo *root,
										  IndexOptInfo *index,
										  IndexClauseSet *clauseset);
static void match_clauses_to_index(PlannerInfo *root,
								   List *clauses,
								   IndexOptInfo *index,
								   IndexClauseSet *clauseset);
static void match_clause_to_index(PlannerInfo *root,
								  RestrictInfo *rinfo,
								  IndexOptInfo *index,
								  IndexClauseSet *clauseset);
static IndexClause *match_clause_to_indexcol(PlannerInfo *root,
											 RestrictInfo *rinfo,
											 int indexcol,
											 IndexOptInfo *index);
static bool IsBooleanOpfamily(Oid opfamily);
static IndexClause *match_boolean_index_clause(PlannerInfo *root,
											   RestrictInfo *rinfo,
											   int indexcol, IndexOptInfo *index);
static IndexClause *match_opclause_to_indexcol(PlannerInfo *root,
											   RestrictInfo *rinfo,
											   int indexcol,
											   IndexOptInfo *index);
static IndexClause *match_funcclause_to_indexcol(PlannerInfo *root,
												 RestrictInfo *rinfo,
												 int indexcol,
												 IndexOptInfo *index);
static IndexClause *get_index_clause_from_support(PlannerInfo *root,
												  RestrictInfo *rinfo,
												  Oid funcid,
												  int indexarg,
												  int indexcol,
												  IndexOptInfo *index);
static IndexClause *match_saopclause_to_indexcol(PlannerInfo *root,
												 RestrictInfo *rinfo,
												 int indexcol,
												 IndexOptInfo *index);
static IndexClause *match_rowcompare_to_indexcol(PlannerInfo *root,
												 RestrictInfo *rinfo,
												 int indexcol,
												 IndexOptInfo *index);
static IndexClause *match_orclause_to_indexcol(PlannerInfo *root,
											   RestrictInfo *rinfo,
											   int indexcol,
											   IndexOptInfo *index);
static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
												RestrictInfo *rinfo,
												int indexcol,
												IndexOptInfo *index,
												Oid expr_op,
												bool var_on_left);
static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
									List **orderby_clauses_p,
									List **clause_columns_p);
static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
										 int indexcol, Expr *clause, Oid pk_opfamily);
static bool ec_member_matches_indexcol(PlannerInfo *root, RelOptInfo *rel,
									   EquivalenceClass *ec, EquivalenceMember *em,
									   void *arg);


/*
 * create_index_paths()
 *	  Generate all interesting index paths for the given relation.
 *	  Candidate paths are added to the rel's pathlist (using add_path).
 *
 * To be considered for an index scan, an index must match one or more
 * restriction clauses or join clauses from the query's qual condition,
 * or match the query's ORDER BY condition, or have a predicate that
 * matches the query's qual condition.
 *
 * There are two basic kinds of index scans.  A "plain" index scan uses
 * only restriction clauses (possibly none at all) in its indexqual,
 * so it can be applied in any context.  A "parameterized" index scan uses
 * join clauses (plus restriction clauses, if available) in its indexqual.
 * When joining such a scan to one of the relations supplying the other
 * variables used in its indexqual, the parameterized scan must appear as
 * the inner relation of a nestloop join; it can't be used on the outer side,
 * nor in a merge or hash join.  In that context, values for the other rels'
 * attributes are available and fixed during any one scan of the indexpath.
 *
 * An IndexPath is generated and submitted to add_path() for each plain or
 * parameterized index scan this routine deems potentially interesting for
 * the current query.
 *
 * 'rel' is the relation for which we want to generate index paths
 *
 * Note: check_index_predicates() must have been run previously for this rel.
 *
 * Note: in cases involving LATERAL references in the relation's tlist, it's
 * possible that rel->lateral_relids is nonempty.  Currently, we include
 * lateral_relids into the parameterization reported for each path, but don't
 * take it into account otherwise.  The fact that any such rels *must* be
 * available as parameter sources perhaps should influence our choices of
 * index quals ... but for now, it doesn't seem worth troubling over.
 * In particular, comments below about "unparameterized" paths should be read
 * as meaning "unparameterized so far as the indexquals are concerned".
 */
void
create_index_paths(PlannerInfo *root, RelOptInfo *rel)
{
	List	   *indexpaths;
	List	   *bitindexpaths;
	List	   *bitjoinpaths;
	List	   *joinorclauses;
	IndexClauseSet rclauseset;
	IndexClauseSet jclauseset;
	IndexClauseSet eclauseset;
	ListCell   *lc;

	/* Skip the whole mess if no indexes */
	if (rel->indexlist == NIL)
		return;

	/* Bitmap paths are collected and then dealt with at the end */
	bitindexpaths = bitjoinpaths = joinorclauses = NIL;

	/* Examine each index in turn */
	foreach(lc, rel->indexlist)
	{
		IndexOptInfo *index = (IndexOptInfo *) lfirst(lc);

		/* Protect limited-size array in IndexClauseSets */
		Assert(index->nkeycolumns <= INDEX_MAX_KEYS);

		/*
		 * Ignore partial indexes that do not match the query.
		 * (generate_bitmap_or_paths() might be able to do something with
		 * them, but that's of no concern here.)
		 */
		if (index->indpred != NIL && !index->predOK)
			continue;

		/*
		 * Identify the restriction clauses that can match the index.
		 */
		MemSet(&rclauseset, 0, sizeof(rclauseset));
		match_restriction_clauses_to_index(root, index, &rclauseset);

		/*
		 * Build index paths from the restriction clauses.  These will be
		 * non-parameterized paths.  Plain paths go directly to add_path(),
		 * bitmap paths are added to bitindexpaths to be handled below.
		 */
		get_index_paths(root, rel, index, &rclauseset,
						&bitindexpaths);

		/*
		 * Identify the join clauses that can match the index.  For the moment
		 * we keep them separate from the restriction clauses.  Note that this
		 * step finds only "loose" join clauses that have not been merged into
		 * EquivalenceClasses.  Also, collect join OR clauses for later.
		 */
		MemSet(&jclauseset, 0, sizeof(jclauseset));
		match_join_clauses_to_index(root, rel, index,
									&jclauseset, &joinorclauses);

		/*
		 * Look for EquivalenceClasses that can generate joinclauses matching
		 * the index.
		 */
		MemSet(&eclauseset, 0, sizeof(eclauseset));
		match_eclass_clauses_to_index(root, index,
									  &eclauseset);

		/*
		 * If we found any plain or eclass join clauses, build parameterized
		 * index paths using them.
		 */
		if (jclauseset.nonempty || eclauseset.nonempty)
			consider_index_join_clauses(root, rel, index,
										&rclauseset,
										&jclauseset,
										&eclauseset,
										&bitjoinpaths);
	}

	/*
	 * Generate BitmapOrPaths for any suitable OR-clauses present in the
	 * restriction list.  Add these to bitindexpaths.
	 */
	indexpaths = generate_bitmap_or_paths(root, rel,
										  rel->baserestrictinfo, NIL);
	bitindexpaths = list_concat(bitindexpaths, indexpaths);

	/*
	 * Likewise, generate BitmapOrPaths for any suitable OR-clauses present in
	 * the joinclause list.  Add these to bitjoinpaths.
	 */
	indexpaths = generate_bitmap_or_paths(root, rel,
										  joinorclauses, rel->baserestrictinfo);
	bitjoinpaths = list_concat(bitjoinpaths, indexpaths);

	/*
	 * If we found anything usable, generate a BitmapHeapPath for the most
	 * promising combination of restriction bitmap index paths.  Note there
	 * will be only one such path no matter how many indexes exist.  This
	 * should be sufficient since there's basically only one figure of merit
	 * (total cost) for such a path.
	 */
	if (bitindexpaths != NIL)
	{
		Path	   *bitmapqual;
		BitmapHeapPath *bpath;

		bitmapqual = choose_bitmap_and(root, rel, bitindexpaths);
		bpath = create_bitmap_heap_path(root, rel, bitmapqual,
										rel->lateral_relids, 1.0, 0);
		add_path(rel, (Path *) bpath);

		/* create a partial bitmap heap path */
		if (rel->consider_parallel && rel->lateral_relids == NULL)
			create_partial_bitmap_paths(root, rel, bitmapqual);
	}

	/*
	 * Likewise, if we found anything usable, generate BitmapHeapPaths for the
	 * most promising combinations of join bitmap index paths.  Our strategy
	 * is to generate one such path for each distinct parameterization seen
	 * among the available bitmap index paths.  This may look pretty
	 * expensive, but usually there won't be very many distinct
	 * parameterizations.  (This logic is quite similar to that in
	 * consider_index_join_clauses, but we're working with whole paths not
	 * individual clauses.)
	 */
	if (bitjoinpaths != NIL)
	{
		List	   *all_path_outers;

		/* Identify each distinct parameterization seen in bitjoinpaths */
		all_path_outers = NIL;
		foreach(lc, bitjoinpaths)
		{
			Path	   *path = (Path *) lfirst(lc);
			Relids		required_outer = PATH_REQ_OUTER(path);

			all_path_outers = list_append_unique(all_path_outers,
												 required_outer);
		}

		/* Now, for each distinct parameterization set ... */
		foreach(lc, all_path_outers)
		{
			Relids		max_outers = (Relids) lfirst(lc);
			List	   *this_path_set;
			Path	   *bitmapqual;
			Relids		required_outer;
			double		loop_count;
			BitmapHeapPath *bpath;
			ListCell   *lcp;

			/* Identify all the bitmap join paths needing no more than that */
			this_path_set = NIL;
			foreach(lcp, bitjoinpaths)
			{
				Path	   *path = (Path *) lfirst(lcp);

				if (bms_is_subset(PATH_REQ_OUTER(path), max_outers))
					this_path_set = lappend(this_path_set, path);
			}

			/*
			 * Add in restriction bitmap paths, since they can be used
			 * together with any join paths.
			 */
			this_path_set = list_concat(this_path_set, bitindexpaths);

			/* Select best AND combination for this parameterization */
			bitmapqual = choose_bitmap_and(root, rel, this_path_set);

			/* And push that path into the mix */
			required_outer = PATH_REQ_OUTER(bitmapqual);
			loop_count = get_loop_count(root, rel->relid, required_outer);
			bpath = create_bitmap_heap_path(root, rel, bitmapqual,
											required_outer, loop_count, 0);
			add_path(rel, (Path *) bpath);
		}
	}
}

/*
 * consider_index_join_clauses
 *	  Given sets of join clauses for an index, decide which parameterized
 *	  index paths to build.
 *
 * Plain indexpaths are sent directly to add_path, while potential
 * bitmap indexpaths are added to *bitindexpaths for later processing.
 *
 * 'rel' is the index's heap relation
 * 'index' is the index for which we want to generate paths
 * 'rclauseset' is the collection of indexable restriction clauses
 * 'jclauseset' is the collection of indexable simple join clauses
 * 'eclauseset' is the collection of indexable clauses from EquivalenceClasses
 * '*bitindexpaths' is the list to add bitmap paths to
 */
static void
consider_index_join_clauses(PlannerInfo *root, RelOptInfo *rel,
							IndexOptInfo *index,
							IndexClauseSet *rclauseset,
							IndexClauseSet *jclauseset,
							IndexClauseSet *eclauseset,
							List **bitindexpaths)
{
	int			considered_clauses = 0;
	List	   *considered_relids = NIL;
	int			indexcol;

	/*
	 * The strategy here is to identify every potentially useful set of outer
	 * rels that can provide indexable join clauses.  For each such set,
	 * select all the join clauses available from those outer rels, add on all
	 * the indexable restriction clauses, and generate plain and/or bitmap
	 * index paths for that set of clauses.  This is based on the assumption
	 * that it's always better to apply a clause as an indexqual than as a
	 * filter (qpqual); which is where an available clause would end up being
	 * applied if we omit it from the indexquals.
	 *
	 * This looks expensive, but in most practical cases there won't be very
	 * many distinct sets of outer rels to consider.  As a safety valve when
	 * that's not true, we use a heuristic: limit the number of outer rel sets
	 * considered to a multiple of the number of clauses considered.  (We'll
	 * always consider using each individual join clause, though.)
	 *
	 * For simplicity in selecting relevant clauses, we represent each set of
	 * outer rels as a maximum set of clause_relids --- that is, the indexed
	 * relation itself is also included in the relids set.  considered_relids
	 * lists all relids sets we've already tried.
	 */
	for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++)
	{
		/* Consider each applicable simple join clause */
		considered_clauses += list_length(jclauseset->indexclauses[indexcol]);
		consider_index_join_outer_rels(root, rel, index,
									   rclauseset, jclauseset, eclauseset,
									   bitindexpaths,
									   jclauseset->indexclauses[indexcol],
									   considered_clauses,
									   &considered_relids);
		/* Consider each applicable eclass join clause */
		considered_clauses += list_length(eclauseset->indexclauses[indexcol]);
		consider_index_join_outer_rels(root, rel, index,
									   rclauseset, jclauseset, eclauseset,
									   bitindexpaths,
									   eclauseset->indexclauses[indexcol],
									   considered_clauses,
									   &considered_relids);
	}
}

/*
 * consider_index_join_outer_rels
 *	  Generate parameterized paths based on clause relids in the clause list.
 *
 * Workhorse for consider_index_join_clauses; see notes therein for rationale.
 *
 * 'rel', 'index', 'rclauseset', 'jclauseset', 'eclauseset', and
 *		'bitindexpaths' as above
 * 'indexjoinclauses' is a list of IndexClauses for join clauses
 * 'considered_clauses' is the total number of clauses considered (so far)
 * '*considered_relids' is a list of all relids sets already considered
 */
static void
consider_index_join_outer_rels(PlannerInfo *root, RelOptInfo *rel,
							   IndexOptInfo *index,
							   IndexClauseSet *rclauseset,
							   IndexClauseSet *jclauseset,
							   IndexClauseSet *eclauseset,
							   List **bitindexpaths,
							   List *indexjoinclauses,
							   int considered_clauses,
							   List **considered_relids)
{
	ListCell   *lc;

	/* Examine relids of each joinclause in the given list */
	foreach(lc, indexjoinclauses)
	{
		IndexClause *iclause = (IndexClause *) lfirst(lc);
		Relids		clause_relids = iclause->rinfo->clause_relids;
		EquivalenceClass *parent_ec = iclause->rinfo->parent_ec;
		int			num_considered_relids;

		/* If we already tried its relids set, no need to do so again */
		if (list_member(*considered_relids, clause_relids))
			continue;

		/*
		 * Generate the union of this clause's relids set with each
		 * previously-tried set.  This ensures we try this clause along with
		 * every interesting subset of previous clauses.  However, to avoid
		 * exponential growth of planning time when there are many clauses,
		 * limit the number of relid sets accepted to 10 * considered_clauses.
		 *
		 * Note: get_join_index_paths appends entries to *considered_relids,
		 * but we do not need to visit such newly-added entries within this
		 * loop, so we don't use foreach() here.  No real harm would be done
		 * if we did visit them, since the subset check would reject them; but
		 * it would waste some cycles.
		 */
		num_considered_relids = list_length(*considered_relids);
		for (int pos = 0; pos < num_considered_relids; pos++)
		{
			Relids		oldrelids = (Relids) list_nth(*considered_relids, pos);

			/*
			 * If either is a subset of the other, no new set is possible.
			 * This isn't a complete test for redundancy, but it's easy and
			 * cheap.  get_join_index_paths will check more carefully if we
			 * already generated the same relids set.
			 */
			if (bms_subset_compare(clause_relids, oldrelids) != BMS_DIFFERENT)
				continue;

			/*
			 * If this clause was derived from an equivalence class, the
			 * clause list may contain other clauses derived from the same
			 * eclass.  We should not consider that combining this clause with
			 * one of those clauses generates a usefully different
			 * parameterization; so skip if any clause derived from the same
			 * eclass would already have been included when using oldrelids.
			 */
			if (parent_ec &&
				eclass_already_used(parent_ec, oldrelids,
									indexjoinclauses))
				continue;

			/*
			 * If the number of relid sets considered exceeds our heuristic
			 * limit, stop considering combinations of clauses.  We'll still
			 * consider the current clause alone, though (below this loop).
			 */
			if (list_length(*considered_relids) >= 10 * considered_clauses)
				break;

			/* OK, try the union set */
			get_join_index_paths(root, rel, index,
								 rclauseset, jclauseset, eclauseset,
								 bitindexpaths,
								 bms_union(clause_relids, oldrelids),
								 considered_relids);
		}

		/* Also try this set of relids by itself */
		get_join_index_paths(root, rel, index,
							 rclauseset, jclauseset, eclauseset,
							 bitindexpaths,
							 clause_relids,
							 considered_relids);
	}
}

/*
 * get_join_index_paths
 *	  Generate index paths using clauses from the specified outer relations.
 *	  In addition to generating paths, relids is added to *considered_relids
 *	  if not already present.
 *
 * Workhorse for consider_index_join_clauses; see notes therein for rationale.
 *
 * 'rel', 'index', 'rclauseset', 'jclauseset', 'eclauseset',
 *		'bitindexpaths', 'considered_relids' as above
 * 'relids' is the current set of relids to consider (the target rel plus
 *		one or more outer rels)
 */
static void
get_join_index_paths(PlannerInfo *root, RelOptInfo *rel,
					 IndexOptInfo *index,
					 IndexClauseSet *rclauseset,
					 IndexClauseSet *jclauseset,
					 IndexClauseSet *eclauseset,
					 List **bitindexpaths,
					 Relids relids,
					 List **considered_relids)
{
	IndexClauseSet clauseset;
	int			indexcol;

	/* If we already considered this relids set, don't repeat the work */
	if (list_member(*considered_relids, relids))
		return;

	/* Identify indexclauses usable with this relids set */
	MemSet(&clauseset, 0, sizeof(clauseset));

	for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++)
	{
		ListCell   *lc;

		/* First find applicable simple join clauses */
		foreach(lc, jclauseset->indexclauses[indexcol])
		{
			IndexClause *iclause = (IndexClause *) lfirst(lc);

			if (bms_is_subset(iclause->rinfo->clause_relids, relids))
				clauseset.indexclauses[indexcol] =
					lappend(clauseset.indexclauses[indexcol], iclause);
		}

		/*
		 * Add applicable eclass join clauses.  The clauses generated for each
		 * column are redundant (cf generate_implied_equalities_for_column),
		 * so we need at most one.  This is the only exception to the general
		 * rule of using all available index clauses.
		 */
		foreach(lc, eclauseset->indexclauses[indexcol])
		{
			IndexClause *iclause = (IndexClause *) lfirst(lc);

			if (bms_is_subset(iclause->rinfo->clause_relids, relids))
			{
				clauseset.indexclauses[indexcol] =
					lappend(clauseset.indexclauses[indexcol], iclause);
				break;
			}
		}

		/* Add restriction clauses */
		clauseset.indexclauses[indexcol] =
			list_concat(clauseset.indexclauses[indexcol],
						rclauseset->indexclauses[indexcol]);

		if (clauseset.indexclauses[indexcol] != NIL)
			clauseset.nonempty = true;
	}

	/* We should have found something, else caller passed silly relids */
	Assert(clauseset.nonempty);

	/* Build index path(s) using the collected set of clauses */
	get_index_paths(root, rel, index, &clauseset, bitindexpaths);

	/*
	 * Remember we considered paths for this set of relids.
	 */
	*considered_relids = lappend(*considered_relids, relids);
}

/*
 * eclass_already_used
 *		True if any join clause usable with oldrelids was generated from
 *		the specified equivalence class.
 */
static bool
eclass_already_used(EquivalenceClass *parent_ec, Relids oldrelids,
					List *indexjoinclauses)
{
	ListCell   *lc;

	foreach(lc, indexjoinclauses)
	{
		IndexClause *iclause = (IndexClause *) lfirst(lc);
		RestrictInfo *rinfo = iclause->rinfo;

		if (rinfo->parent_ec == parent_ec &&
			bms_is_subset(rinfo->clause_relids, oldrelids))
			return true;
	}
	return false;
}


/*
 * get_index_paths
 *	  Given an index and a set of index clauses for it, construct IndexPaths.
 *
 * Plain indexpaths are sent directly to add_path, while potential
 * bitmap indexpaths are added to *bitindexpaths for later processing.
 *
 * This is a fairly simple frontend to build_index_paths().  Its reason for
 * existence is mainly to handle ScalarArrayOpExpr quals properly.  If the
 * index AM supports them natively, we should just include them in simple
 * index paths.  If not, we should exclude them while building simple index
 * paths, and then make a separate attempt to include them in bitmap paths.
 */
static void
get_index_paths(PlannerInfo *root, RelOptInfo *rel,
				IndexOptInfo *index, IndexClauseSet *clauses,
				List **bitindexpaths)
{
	List	   *indexpaths;
	bool		skip_nonnative_saop = false;
	ListCell   *lc;

	/*
	 * Build simple index paths using the clauses.  Allow ScalarArrayOpExpr
	 * clauses only if the index AM supports them natively.
	 */
	indexpaths = build_index_paths(root, rel,
								   index, clauses,
								   index->predOK,
								   ST_ANYSCAN,
								   &skip_nonnative_saop);

	/*
	 * Submit all the ones that can form plain IndexScan plans to add_path. (A
	 * plain IndexPath can represent either a plain IndexScan or an
	 * IndexOnlyScan, but for our purposes here that distinction does not
	 * matter.  However, some of the indexes might support only bitmap scans,
	 * and those we mustn't submit to add_path here.)
	 *
	 * Also, pick out the ones that are usable as bitmap scans.  For that, we
	 * must discard indexes that don't support bitmap scans, and we also are
	 * only interested in paths that have some selectivity; we should discard
	 * anything that was generated solely for ordering purposes.
	 */
	foreach(lc, indexpaths)
	{
		IndexPath  *ipath = (IndexPath *) lfirst(lc);

		if (index->amhasgettuple)
			add_path(rel, (Path *) ipath);

		if (index->amhasgetbitmap &&
			(ipath->path.pathkeys == NIL ||
			 ipath->indexselectivity < 1.0))
			*bitindexpaths = lappend(*bitindexpaths, ipath);
	}

	/*
	 * If there were ScalarArrayOpExpr clauses that the index can't handle
	 * natively, generate bitmap scan paths relying on executor-managed
	 * ScalarArrayOpExpr.
	 */
	if (skip_nonnative_saop)
	{
		indexpaths = build_index_paths(root, rel,
									   index, clauses,
									   false,
									   ST_BITMAPSCAN,
									   NULL);
		*bitindexpaths = list_concat(*bitindexpaths, indexpaths);
	}
}

/*
 * build_index_paths
 *	  Given an index and a set of index clauses for it, construct zero
 *	  or more IndexPaths. It also constructs zero or more partial IndexPaths.
 *
 * We return a list of paths because (1) this routine checks some cases
 * that should cause us to not generate any IndexPath, and (2) in some
 * cases we want to consider both a forward and a backward scan, so as
 * to obtain both sort orders.  Note that the paths are just returned
 * to the caller and not immediately fed to add_path().
 *
 * At top level, useful_predicate should be exactly the index's predOK flag
 * (ie, true if it has a predicate that was proven from the restriction
 * clauses).  When working on an arm of an OR clause, useful_predicate
 * should be true if the predicate required the current OR list to be proven.
 * Note that this routine should never be called at all if the index has an
 * unprovable predicate.
 *
 * scantype indicates whether we want to create plain indexscans, bitmap
 * indexscans, or both.  When it's ST_BITMAPSCAN, we will not consider
 * index ordering while deciding if a Path is worth generating.
 *
 * If skip_nonnative_saop is non-NULL, we ignore ScalarArrayOpExpr clauses
 * unless the index AM supports them directly, and we set *skip_nonnative_saop
 * to true if we found any such clauses (caller must initialize the variable
 * to false).  If it's NULL, we do not ignore ScalarArrayOpExpr clauses.
 *
 * 'rel' is the index's heap relation
 * 'index' is the index for which we want to generate paths
 * 'clauses' is the collection of indexable clauses (IndexClause nodes)
 * 'useful_predicate' indicates whether the index has a useful predicate
 * 'scantype' indicates whether we need plain or bitmap scan support
 * 'skip_nonnative_saop' indicates whether to accept SAOP if index AM doesn't
 */
static List *
build_index_paths(PlannerInfo *root, RelOptInfo *rel,
				  IndexOptInfo *index, IndexClauseSet *clauses,
				  bool useful_predicate,
				  ScanTypeControl scantype,
				  bool *skip_nonnative_saop)
{
	List	   *result = NIL;
	IndexPath  *ipath;
	List	   *index_clauses;
	Relids		outer_relids;
	double		loop_count;
	List	   *orderbyclauses;
	List	   *orderbyclausecols;
	List	   *index_pathkeys;
	List	   *useful_pathkeys;
	bool		pathkeys_possibly_useful;
	bool		index_is_ordered;
	bool		index_only_scan;
	int			indexcol;

	Assert(skip_nonnative_saop != NULL || scantype == ST_BITMAPSCAN);

	/*
	 * Check that index supports the desired scan type(s)
	 */
	switch (scantype)
	{
		case ST_INDEXSCAN:
			if (!index->amhasgettuple)
				return NIL;
			break;
		case ST_BITMAPSCAN:
			if (!index->amhasgetbitmap)
				return NIL;
			break;
		case ST_ANYSCAN:
			/* either or both are OK */
			break;
	}

	/*
	 * 1. Combine the per-column IndexClause lists into an overall list.
	 *
	 * In the resulting list, clauses are ordered by index key, so that the
	 * column numbers form a nondecreasing sequence.  (This order is depended
	 * on by btree and possibly other places.)  The list can be empty, if the
	 * index AM allows that.
	 *
	 * We also build a Relids set showing which outer rels are required by the
	 * selected clauses.  Any lateral_relids are included in that, but not
	 * otherwise accounted for.
	 */
	index_clauses = NIL;
	outer_relids = bms_copy(rel->lateral_relids);
	for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++)
	{
		ListCell   *lc;

		foreach(lc, clauses->indexclauses[indexcol])
		{
			IndexClause *iclause = (IndexClause *) lfirst(lc);
			RestrictInfo *rinfo = iclause->rinfo;

			if (skip_nonnative_saop && !index->amsearcharray &&
				IsA(rinfo->clause, ScalarArrayOpExpr))
			{
				/*
				 * Caller asked us to generate IndexPaths that omit any
				 * ScalarArrayOpExpr clauses when the underlying index AM
				 * lacks native support.
				 *
				 * We must omit this clause (and tell caller about it).
				 */
				*skip_nonnative_saop = true;
				continue;
			}

			/* OK to include this clause */
			index_clauses = lappend(index_clauses, iclause);
			outer_relids = bms_add_members(outer_relids,
										   rinfo->clause_relids);
		}

		/*
		 * If no clauses match the first index column, check for amoptionalkey
		 * restriction.  We can't generate a scan over an index with
		 * amoptionalkey = false unless there's at least one index clause.
		 * (When working on columns after the first, this test cannot fail. It
		 * is always okay for columns after the first to not have any
		 * clauses.)
		 */
		if (index_clauses == NIL && !index->amoptionalkey)
			return NIL;
	}

	/* We do not want the index's rel itself listed in outer_relids */
	outer_relids = bms_del_member(outer_relids, rel->relid);

	/* Compute loop_count for cost estimation purposes */
	loop_count = get_loop_count(root, rel->relid, outer_relids);

	/*
	 * 2. Compute pathkeys describing index's ordering, if any, then see how
	 * many of them are actually useful for this query.  This is not relevant
	 * if we are only trying to build bitmap indexscans.
	 */
	pathkeys_possibly_useful = (scantype != ST_BITMAPSCAN &&
								has_useful_pathkeys(root, rel));
	index_is_ordered = (index->sortopfamily != NULL);
	if (index_is_ordered && pathkeys_possibly_useful)
	{
		index_pathkeys = build_index_pathkeys(root, index,
											  ForwardScanDirection);
		useful_pathkeys = truncate_useless_pathkeys(root, rel,
													index_pathkeys);
		orderbyclauses = NIL;
		orderbyclausecols = NIL;
	}
	else if (index->amcanorderbyop && pathkeys_possibly_useful)
	{
		/*
		 * See if we can generate ordering operators for query_pathkeys or at
		 * least some prefix thereof.  Matching to just a prefix of the
		 * query_pathkeys will allow an incremental sort to be considered on
		 * the index's partially sorted results.
		 */
		match_pathkeys_to_index(index, root->query_pathkeys,
								&orderbyclauses,
								&orderbyclausecols);
		if (list_length(root->query_pathkeys) == list_length(orderbyclauses))
			useful_pathkeys = root->query_pathkeys;
		else
			useful_pathkeys = list_copy_head(root->query_pathkeys,
											 list_length(orderbyclauses));
	}
	else
	{
		useful_pathkeys = NIL;
		orderbyclauses = NIL;
		orderbyclausecols = NIL;
	}

	/*
	 * 3. Check if an index-only scan is possible.  If we're not building
	 * plain indexscans, this isn't relevant since bitmap scans don't support
	 * index data retrieval anyway.
	 */
	index_only_scan = (scantype != ST_BITMAPSCAN &&
					   check_index_only(rel, index));

	/*
	 * 4. Generate an indexscan path if there are relevant restriction clauses
	 * in the current clauses, OR the index ordering is potentially useful for
	 * later merging or final output ordering, OR the index has a useful
	 * predicate, OR an index-only scan is possible.
	 */
	if (index_clauses != NIL || useful_pathkeys != NIL || useful_predicate ||
		index_only_scan)
	{
		ipath = create_index_path(root, index,
								  index_clauses,
								  orderbyclauses,
								  orderbyclausecols,
								  useful_pathkeys,
								  ForwardScanDirection,
								  index_only_scan,
								  outer_relids,
								  loop_count,
								  false);
		result = lappend(result, ipath);

		/*
		 * If appropriate, consider parallel index scan.  We don't allow
		 * parallel index scan for bitmap index scans.
		 */
		if (index->amcanparallel &&
			rel->consider_parallel && outer_relids == NULL &&
			scantype != ST_BITMAPSCAN)
		{
			ipath = create_index_path(root, index,
									  index_clauses,
									  orderbyclauses,
									  orderbyclausecols,
									  useful_pathkeys,
									  ForwardScanDirection,
									  index_only_scan,
									  outer_relids,
									  loop_count,
									  true);

			/*
			 * if, after costing the path, we find that it's not worth using
			 * parallel workers, just free it.
			 */
			if (ipath->path.parallel_workers > 0)
				add_partial_path(rel, (Path *) ipath);
			else
				pfree(ipath);
		}
	}

	/*
	 * 5. If the index is ordered, a backwards scan might be interesting.
	 */
	if (index_is_ordered && pathkeys_possibly_useful)
	{
		index_pathkeys = build_index_pathkeys(root, index,
											  BackwardScanDirection);
		useful_pathkeys = truncate_useless_pathkeys(root, rel,
													index_pathkeys);
		if (useful_pathkeys != NIL)
		{
			ipath = create_index_path(root, index,
									  index_clauses,
									  NIL,
									  NIL,
									  useful_pathkeys,
									  BackwardScanDirection,
									  index_only_scan,
									  outer_relids,
									  loop_count,
									  false);
			result = lappend(result, ipath);

			/* If appropriate, consider parallel index scan */
			if (index->amcanparallel &&
				rel->consider_parallel && outer_relids == NULL &&
				scantype != ST_BITMAPSCAN)
			{
				ipath = create_index_path(root, index,
										  index_clauses,
										  NIL,
										  NIL,
										  useful_pathkeys,
										  BackwardScanDirection,
										  index_only_scan,
										  outer_relids,
										  loop_count,
										  true);

				/*
				 * if, after costing the path, we find that it's not worth
				 * using parallel workers, just free it.
				 */
				if (ipath->path.parallel_workers > 0)
					add_partial_path(rel, (Path *) ipath);
				else
					pfree(ipath);
			}
		}
	}

	return result;
}

/*
 * build_paths_for_OR
 *	  Given a list of restriction clauses from one arm of an OR clause,
 *	  construct all matching IndexPaths for the relation.
 *
 * Here we must scan all indexes of the relation, since a bitmap OR tree
 * can use multiple indexes.
 *
 * The caller actually supplies two lists of restriction clauses: some
 * "current" ones and some "other" ones.  Both lists can be used freely
 * to match keys of the index, but an index must use at least one of the
 * "current" clauses to be considered usable.  The motivation for this is
 * examples like
 *		WHERE (x = 42) AND (... OR (y = 52 AND z = 77) OR ....)
 * While we are considering the y/z subclause of the OR, we can use "x = 42"
 * as one of the available index conditions; but we shouldn't match the
 * subclause to any index on x alone, because such a Path would already have
 * been generated at the upper level.  So we could use an index on x,y,z
 * or an index on x,y for the OR subclause, but not an index on just x.
 * When dealing with a partial index, a match of the index predicate to
 * one of the "current" clauses also makes the index usable.
 *
 * 'rel' is the relation for which we want to generate index paths
 * 'clauses' is the current list of clauses (RestrictInfo nodes)
 * 'other_clauses' is the list of additional upper-level clauses
 */
static List *
build_paths_for_OR(PlannerInfo *root, RelOptInfo *rel,
				   List *clauses, List *other_clauses)
{
	List	   *result = NIL;
	List	   *all_clauses = NIL;	/* not computed till needed */
	ListCell   *lc;

	foreach(lc, rel->indexlist)
	{
		IndexOptInfo *index = (IndexOptInfo *) lfirst(lc);
		IndexClauseSet clauseset;
		List	   *indexpaths;
		bool		useful_predicate;

		/* Ignore index if it doesn't support bitmap scans */
		if (!index->amhasgetbitmap)
			continue;

		/*
		 * Ignore partial indexes that do not match the query.  If a partial
		 * index is marked predOK then we know it's OK.  Otherwise, we have to
		 * test whether the added clauses are sufficient to imply the
		 * predicate. If so, we can use the index in the current context.
		 *
		 * We set useful_predicate to true iff the predicate was proven using
		 * the current set of clauses.  This is needed to prevent matching a
		 * predOK index to an arm of an OR, which would be a legal but
		 * pointlessly inefficient plan.  (A better plan will be generated by
		 * just scanning the predOK index alone, no OR.)
		 */
		useful_predicate = false;
		if (index->indpredExpand != NIL)
		{
			if (index->predOK)
			{
				/* Usable, but don't set useful_predicate */
			}
			else
			{
				/* Form all_clauses if not done already */
				if (all_clauses == NIL)
					all_clauses = list_concat_copy(clauses, other_clauses);

				if (!predicate_implied_by(index->indpredExpand, all_clauses, false))
					continue;	/* can't use it at all */

				if (!predicate_implied_by(index->indpredExpand, other_clauses, false))
					useful_predicate = true;
			}
		}

		/*
		 * Identify the restriction clauses that can match the index.
		 */
		MemSet(&clauseset, 0, sizeof(clauseset));
		match_clauses_to_index(root, clauses, index, &clauseset);

		/*
		 * If no matches so far, and the index predicate isn't useful, we
		 * don't want it.
		 */
		if (!clauseset.nonempty && !useful_predicate)
			continue;

		/*
		 * Add "other" restriction clauses to the clauseset.
		 */
		match_clauses_to_index(root, other_clauses, index, &clauseset);

		/*
		 * Construct paths if possible.
		 */
		indexpaths = build_index_paths(root, rel,
									   index, &clauseset,
									   useful_predicate,
									   ST_BITMAPSCAN,
									   NULL);
		result = list_concat(result, indexpaths);
	}

	return result;
}

/*
 * Utility structure used to group similar OR-clause arguments in
 * group_similar_or_args().  It represents information about the OR-clause
 * argument and its matching index key.
 */
typedef struct
{
	int			indexnum;		/* index of the matching index, or -1 if no
								 * matching index */
	int			colnum;			/* index of the matching column, or -1 if no
								 * matching index */
	Oid			opno;			/* OID of the OpClause operator, or InvalidOid
								 * if not an OpExpr */
	Oid			inputcollid;	/* OID of the OpClause input collation */
	int			argindex;		/* index of the clause in the list of
								 * arguments */
	int			groupindex;		/* value of argindex for the first clause in
								 * the group of similar clauses */
} OrArgIndexMatch;

/*
 * Comparison function for OrArgIndexMatch which provides sort order placing
 * similar OR-clause arguments together.
 */
static int
or_arg_index_match_cmp(const void *a, const void *b)
{
	const OrArgIndexMatch *match_a = (const OrArgIndexMatch *) a;
	const OrArgIndexMatch *match_b = (const OrArgIndexMatch *) b;

	if (match_a->indexnum < match_b->indexnum)
		return -1;
	else if (match_a->indexnum > match_b->indexnum)
		return 1;

	if (match_a->colnum < match_b->colnum)
		return -1;
	else if (match_a->colnum > match_b->colnum)
		return 1;

	if (match_a->opno < match_b->opno)
		return -1;
	else if (match_a->opno > match_b->opno)
		return 1;

	if (match_a->inputcollid < match_b->inputcollid)
		return -1;
	else if (match_a->inputcollid > match_b->inputcollid)
		return 1;

	if (match_a->argindex < match_b->argindex)
		return -1;
	else if (match_a->argindex > match_b->argindex)
		return 1;

	return 0;
}

/*
 * Another comparison function for OrArgIndexMatch.  It sorts groups together
 * using groupindex.  The group items are then sorted by argindex.
 */
static int
or_arg_index_match_cmp_group(const void *a, const void *b)
{
	const OrArgIndexMatch *match_a = (const OrArgIndexMatch *) a;
	const OrArgIndexMatch *match_b = (const OrArgIndexMatch *) b;

	if (match_a->groupindex < match_b->groupindex)
		return -1;
	else if (match_a->groupindex > match_b->groupindex)
		return 1;

	if (match_a->argindex < match_b->argindex)
		return -1;
	else if (match_a->argindex > match_b->argindex)
		return 1;

	return 0;
}

/*
 * group_similar_or_args
 *		Transform incoming OR-restrictinfo into a list of sub-restrictinfos,
 *		each of them containing a subset of similar OR-clause arguments from
 *		the source rinfo.
 *
 * Similar OR-clause arguments are of the form "indexkey op constant" having
 * the same indexkey, operator, and collation.  Constant may comprise either
 * Const or Param.  It may be employed later, during the
 * match_clause_to_indexcol() to transform the whole OR-sub-rinfo to an SAOP
 * clause.
 *
 * Returns the processed list of OR-clause arguments.
 */
static List *
group_similar_or_args(PlannerInfo *root, RelOptInfo *rel, RestrictInfo *rinfo)
{
	int			n;
	int			i;
	int			group_start;
	OrArgIndexMatch *matches;
	bool		matched = false;
	ListCell   *lc;
	ListCell   *lc2;
	List	   *orargs;
	List	   *result = NIL;
	Index		relid = rel->relid;

	Assert(IsA(rinfo->orclause, BoolExpr));
	orargs = ((BoolExpr *) rinfo->orclause)->args;
	n = list_length(orargs);

	/*
	 * To avoid N^2 behavior, take utility pass along the list of OR-clause
	 * arguments.  For each argument, fill the OrArgIndexMatch structure,
	 * which will be used to sort these arguments at the next step.
	 */
	i = -1;
	matches = palloc_array(OrArgIndexMatch, n);
	foreach(lc, orargs)
	{
		Node	   *arg = lfirst(lc);
		RestrictInfo *argrinfo;
		OpExpr	   *clause;
		Oid			opno;
		Node	   *leftop,
				   *rightop;
		Node	   *nonConstExpr;
		int			indexnum;
		int			colnum;

		i++;
		matches[i].argindex = i;
		matches[i].groupindex = i;
		matches[i].indexnum = -1;
		matches[i].colnum = -1;
		matches[i].opno = InvalidOid;
		matches[i].inputcollid = InvalidOid;

		if (!IsA(arg, RestrictInfo))
			continue;

		argrinfo = castNode(RestrictInfo, arg);

		/* Only operator clauses can match  */
		if (!IsA(argrinfo->clause, OpExpr))
			continue;

		clause = (OpExpr *) argrinfo->clause;
		opno = clause->opno;

		/* Only binary operators can match  */
		if (list_length(clause->args) != 2)
			continue;

		/*
		 * Ignore any RelabelType node above the operands.  This is needed to
		 * be able to apply indexscanning in binary-compatible-operator cases.
		 * Note: we can assume there is at most one RelabelType node;
		 * eval_const_expressions() will have simplified if more than one.
		 */
		leftop = get_leftop(clause);
		if (IsA(leftop, RelabelType))
			leftop = (Node *) ((RelabelType *) leftop)->arg;

		rightop = get_rightop(clause);
		if (IsA(rightop, RelabelType))
			rightop = (Node *) ((RelabelType *) rightop)->arg;

		/*
		 * Check for clauses of the form: (indexkey operator constant) or
		 * (constant operator indexkey).  But we don't know a particular index
		 * yet.  Therefore, we try to distinguish the potential index key and
		 * constant first, then search for a matching index key among all
		 * indexes.
		 */
		if (bms_is_member(relid, argrinfo->right_relids) &&
			!bms_is_member(relid, argrinfo->left_relids) &&
			!contain_volatile_functions(leftop))
		{
			opno = get_commutator(opno);

			if (!OidIsValid(opno))
			{
				/* commutator doesn't exist, we can't reverse the order */
				continue;
			}
			nonConstExpr = rightop;
		}
		else if (bms_is_member(relid, argrinfo->left_relids) &&
				 !bms_is_member(relid, argrinfo->right_relids) &&
				 !contain_volatile_functions(rightop))
		{
			nonConstExpr = leftop;
		}
		else
		{
			continue;
		}

		/*
		 * Match non-constant part to the index key.  It's possible that a
		 * single non-constant part matches multiple index keys.  It's OK, we
		 * just stop with first matching index key.  Given that this choice is
		 * determined the same for every clause, we will group similar clauses
		 * together anyway.
		 */
		indexnum = 0;
		foreach(lc2, rel->indexlist)
		{
			IndexOptInfo *index = (IndexOptInfo *) lfirst(lc2);

			/*
			 * Ignore index if it doesn't support bitmap scans or SAOP
			 * clauses.
			 */
			if (!index->amhasgetbitmap || !index->amsearcharray)
				continue;

			for (colnum = 0; colnum < index->nkeycolumns; colnum++)
			{
				if (match_index_to_operand(nonConstExpr, colnum, index))
				{
					matches[i].indexnum = indexnum;
					matches[i].colnum = colnum;
					matches[i].opno = opno;
					matches[i].inputcollid = clause->inputcollid;
					matched = true;
					break;
				}
			}

			/*
			 * Stop looping through the indexes, if we managed to match
			 * nonConstExpr to any index column.
			 */
			if (matches[i].indexnum >= 0)
				break;
			indexnum++;
		}
	}

	/*
	 * Fast-path check: if no clause is matching to the index column, we can
	 * just give up at this stage and return the clause list as-is.
	 */
	if (!matched)
	{
		pfree(matches);
		return orargs;
	}

	/*
	 * Sort clauses to make similar clauses go together.  But at the same
	 * time, we would like to change the order of clauses as little as
	 * possible.  To do so, we reorder each group of similar clauses so that
	 * the first item of the group stays in place, and all the other items are
	 * moved after it.  So, if there are no similar clauses, the order of
	 * clauses stays the same.  When there are some groups, required
	 * reordering happens while the rest of the clauses remain in their
	 * places.  That is achieved by assigning a 'groupindex' to each clause:
	 * the number of the first item in the group in the original clause list.
	 */
	qsort(matches, n, sizeof(OrArgIndexMatch), or_arg_index_match_cmp);

	/* Assign groupindex to the sorted clauses */
	for (i = 1; i < n; i++)
	{
		/*
		 * When two clauses are similar and should belong to the same group,
		 * copy the 'groupindex' from the previous clause.  Given we are
		 * considering clauses in direct order, all the clauses would have a
		 * 'groupindex' equal to the 'groupindex' of the first clause in the
		 * group.
		 */
		if (matches[i].indexnum == matches[i - 1].indexnum &&
			matches[i].colnum == matches[i - 1].colnum &&
			matches[i].opno == matches[i - 1].opno &&
			matches[i].inputcollid == matches[i - 1].inputcollid &&
			matches[i].indexnum != -1)
			matches[i].groupindex = matches[i - 1].groupindex;
	}

	/* Re-sort clauses first by groupindex then by argindex */
	qsort(matches, n, sizeof(OrArgIndexMatch), or_arg_index_match_cmp_group);

	/*
	 * Group similar clauses into single sub-restrictinfo. Side effect: the
	 * resulting list of restrictions will be sorted by indexnum and colnum.
	 */
	group_start = 0;
	for (i = 1; i <= n; i++)
	{
		/* Check if it's a group boundary */
		if (group_start >= 0 &&
			(i == n ||
			 matches[i].indexnum != matches[group_start].indexnum ||
			 matches[i].colnum != matches[group_start].colnum ||
			 matches[i].opno != matches[group_start].opno ||
			 matches[i].inputcollid != matches[group_start].inputcollid ||
			 matches[i].indexnum == -1))
		{
			/*
			 * One clause in group: add it "as is" to the upper-level OR.
			 */
			if (i - group_start == 1)
			{
				result = lappend(result,
								 list_nth(orargs,
										  matches[group_start].argindex));
			}
			else
			{
				/*
				 * Two or more clauses in a group: create a nested OR.
				 */
				List	   *args = NIL;
				List	   *rargs = NIL;
				RestrictInfo *subrinfo;
				int			j;

				Assert(i - group_start >= 2);

				/* Construct the list of nested OR arguments */
				for (j = group_start; j < i; j++)
				{
					Node	   *arg = list_nth(orargs, matches[j].argindex);

					rargs = lappend(rargs, arg);
					if (IsA(arg, RestrictInfo))
						args = lappend(args, ((RestrictInfo *) arg)->clause);
					else
						args = lappend(args, arg);
				}

				/* Construct the nested OR and wrap it with RestrictInfo */
				subrinfo = make_plain_restrictinfo(root,
												   make_orclause(args),
												   make_orclause(rargs),
												   rinfo->is_pushed_down,
												   rinfo->has_clone,
												   rinfo->is_clone,
												   rinfo->pseudoconstant,
												   rinfo->security_level,
												   rinfo->required_relids,
												   rinfo->incompatible_relids,
												   rinfo->outer_relids);
				result = lappend(result, subrinfo);
			}

			group_start = i;
		}
	}
	pfree(matches);
	return result;
}

/*
 * make_bitmap_paths_for_or_group
 *		Generate bitmap paths for a group of similar OR-clause arguments
 *		produced by group_similar_or_args().
 *
 * This function considers two cases: (1) matching a group of clauses to
 * the index as a whole, and (2) matching the individual clauses one-by-one.
 * (1) typically comprises an optimal solution.  If not, (2) typically
 * comprises fair alternative.
 *
 * Ideally, we could consider all arbitrary splits of arguments into
 * subgroups, but that could lead to unacceptable computational complexity.
 * This is why we only consider two cases of above.
 */
static List *
make_bitmap_paths_for_or_group(PlannerInfo *root, RelOptInfo *rel,
							   RestrictInfo *ri, List *other_clauses)
{
	List	   *jointlist = NIL;
	List	   *splitlist = NIL;
	ListCell   *lc;
	List	   *orargs;
	List	   *args = ((BoolExpr *) ri->orclause)->args;
	Cost		jointcost = 0.0,
				splitcost = 0.0;
	Path	   *bitmapqual;
	List	   *indlist;

	/*
	 * First, try to match the whole group to the one index.
	 */
	orargs = list_make1(ri);
	indlist = build_paths_for_OR(root, rel,
								 orargs,
								 other_clauses);
	if (indlist != NIL)
	{
		bitmapqual = choose_bitmap_and(root, rel, indlist);
		jointcost = bitmapqual->total_cost;
		jointlist = list_make1(bitmapqual);
	}

	/*
	 * If we manage to find a bitmap scan, which uses the group of OR-clause
	 * arguments as a whole, we can skip matching OR-clause arguments
	 * one-by-one as long as there are no other clauses, which can bring more
	 * efficiency to one-by-one case.
	 */
	if (jointlist != NIL && other_clauses == NIL)
		return jointlist;

	/*
	 * Also try to match all containing clauses one-by-one.
	 */
	foreach(lc, args)
	{
		orargs = list_make1(lfirst(lc));

		indlist = build_paths_for_OR(root, rel,
									 orargs,
									 other_clauses);

		if (indlist == NIL)
		{
			splitlist = NIL;
			break;
		}

		bitmapqual = choose_bitmap_and(root, rel, indlist);
		splitcost += bitmapqual->total_cost;
		splitlist = lappend(splitlist, bitmapqual);
	}

	/*
	 * Pick the best option.
	 */
	if (splitlist == NIL)
		return jointlist;
	else if (jointlist == NIL)
		return splitlist;
	else
		return (jointcost < splitcost) ? jointlist : splitlist;
}


/*
 * generate_bitmap_or_paths
 *		Look through the list of clauses to find OR clauses, and generate
 *		a BitmapOrPath for each one we can handle that way.  Return a list
 *		of the generated BitmapOrPaths.
 *
 * other_clauses is a list of additional clauses that can be assumed true
 * for the purpose of generating indexquals, but are not to be searched for
 * ORs.  (See build_paths_for_OR() for motivation.)
 */
static List *
generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel,
						 List *clauses, List *other_clauses)
{
	List	   *result = NIL;
	List	   *all_clauses;
	ListCell   *lc;

	/*
	 * We can use both the current and other clauses as context for
	 * build_paths_for_OR; no need to remove ORs from the lists.
	 */
	all_clauses = list_concat_copy(clauses, other_clauses);

	foreach(lc, clauses)
	{
		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
		List	   *pathlist;
		Path	   *bitmapqual;
		ListCell   *j;
		List	   *groupedArgs;
		List	   *inner_other_clauses = NIL;

		/* Ignore RestrictInfos that aren't ORs */
		if (!restriction_is_or_clause(rinfo))
			continue;

		/*
		 * We must be able to match at least one index to each of the arms of
		 * the OR, else we can't use it.
		 */
		pathlist = NIL;

		/*
		 * Group the similar OR-clause arguments into dedicated RestrictInfos,
		 * because each of those RestrictInfos has a chance to match the index
		 * as a whole.
		 */
		groupedArgs = group_similar_or_args(root, rel, rinfo);

		if (groupedArgs != ((BoolExpr *) rinfo->orclause)->args)
		{
			/*
			 * Some parts of the rinfo were probably grouped.  In this case,
			 * we have a set of sub-rinfos that together are an exact
			 * duplicate of rinfo.  Thus, we need to remove the rinfo from
			 * other clauses. match_clauses_to_index detects duplicated
			 * iclauses by comparing pointers to original rinfos that would be
			 * different.  So, we must delete rinfo to avoid de-facto
			 * duplicated clauses in the index clauses list.
			 */
			inner_other_clauses = list_delete(list_copy(all_clauses), rinfo);
		}

		foreach(j, groupedArgs)
		{
			Node	   *orarg = (Node *) lfirst(j);
			List	   *indlist;

			/* OR arguments should be ANDs or sub-RestrictInfos */
			if (is_andclause(orarg))
			{
				List	   *andargs = ((BoolExpr *) orarg)->args;

				indlist = build_paths_for_OR(root, rel,
											 andargs,
											 all_clauses);

				/* Recurse in case there are sub-ORs */
				indlist = list_concat(indlist,
									  generate_bitmap_or_paths(root, rel,
															   andargs,
															   all_clauses));
			}
			else if (restriction_is_or_clause(castNode(RestrictInfo, orarg)))
			{
				RestrictInfo *ri = castNode(RestrictInfo, orarg);

				/*
				 * Generate bitmap paths for the group of similar OR-clause
				 * arguments.
				 */
				indlist = make_bitmap_paths_for_or_group(root,
														 rel, ri,
														 inner_other_clauses);

				if (indlist == NIL)
				{
					pathlist = NIL;
					break;
				}
				else
				{
					pathlist = list_concat(pathlist, indlist);
					continue;
				}
			}
			else
			{
				RestrictInfo *ri = castNode(RestrictInfo, orarg);
				List	   *orargs;

				orargs = list_make1(ri);

				indlist = build_paths_for_OR(root, rel,
											 orargs,
											 all_clauses);
			}

			/*
			 * If nothing matched this arm, we can't do anything with this OR
			 * clause.
			 */
			if (indlist == NIL)
			{
				pathlist = NIL;
				break;
			}

			/*
			 * OK, pick the most promising AND combination, and add it to
			 * pathlist.
			 */
			bitmapqual = choose_bitmap_and(root, rel, indlist);
			pathlist = lappend(pathlist, bitmapqual);
		}

		if (inner_other_clauses != NIL)
			list_free(inner_other_clauses);

		/*
		 * If we have a match for every arm, then turn them into a
		 * BitmapOrPath, and add to result list.
		 */
		if (pathlist != NIL)
		{
			bitmapqual = (Path *) create_bitmap_or_path(root, rel, pathlist);
			result = lappend(result, bitmapqual);
		}
	}

	return result;
}


/*
 * choose_bitmap_and
 *		Given a nonempty list of bitmap paths, AND them into one path.
 *
 * This is a nontrivial decision since we can legally use any subset of the
 * given path set.  We want to choose a good tradeoff between selectivity
 * and cost of computing the bitmap.
 *
 * The result is either a single one of the inputs, or a BitmapAndPath
 * combining multiple inputs.
 */
static Path *
choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, List *paths)
{
	int			npaths = list_length(paths);
	PathClauseUsage **pathinfoarray;
	PathClauseUsage *pathinfo;
	List	   *clauselist;
	List	   *bestpaths = NIL;
	Cost		bestcost = 0;
	int			i,
				j;
	ListCell   *l;

	Assert(npaths > 0);			/* else caller error */
	if (npaths == 1)
		return (Path *) linitial(paths);	/* easy case */

	/*
	 * In theory we should consider every nonempty subset of the given paths.
	 * In practice that seems like overkill, given the crude nature of the
	 * estimates, not to mention the possible effects of higher-level AND and
	 * OR clauses.  Moreover, it's completely impractical if there are a large
	 * number of paths, since the work would grow as O(2^N).
	 *
	 * As a heuristic, we first check for paths using exactly the same sets of
	 * WHERE clauses + index predicate conditions, and reject all but the
	 * cheapest-to-scan in any such group.  This primarily gets rid of indexes
	 * that include the interesting columns but also irrelevant columns.  (In
	 * situations where the DBA has gone overboard on creating variant
	 * indexes, this can make for a very large reduction in the number of
	 * paths considered further.)
	 *
	 * We then sort the surviving paths with the cheapest-to-scan first, and
	 * for each path, consider using that path alone as the basis for a bitmap
	 * scan.  Then we consider bitmap AND scans formed from that path plus
	 * each subsequent (higher-cost) path, adding on a subsequent path if it
	 * results in a reduction in the estimated total scan cost. This means we
	 * consider about O(N^2) rather than O(2^N) path combinations, which is
	 * quite tolerable, especially given than N is usually reasonably small
	 * because of the prefiltering step.  The cheapest of these is returned.
	 *
	 * We will only consider AND combinations in which no two indexes use the
	 * same WHERE clause.  This is a bit of a kluge: it's needed because
	 * costsize.c and clausesel.c aren't very smart about redundant clauses.
	 * They will usually double-count the redundant clauses, producing a
	 * too-small selectivity that makes a redundant AND step look like it
	 * reduces the total cost.  Perhaps someday that code will be smarter and
	 * we can remove this limitation.  (But note that this also defends
	 * against flat-out duplicate input paths, which can happen because
	 * match_join_clauses_to_index will find the same OR join clauses that
	 * extract_restriction_or_clauses has pulled OR restriction clauses out
	 * of.)
	 *
	 * For the same reason, we reject AND combinations in which an index
	 * predicate clause duplicates another clause.  Here we find it necessary
	 * to be even stricter: we'll reject a partial index if any of its
	 * predicate clauses are implied by the set of WHERE clauses and predicate
	 * clauses used so far.  This covers cases such as a condition "x = 42"
	 * used with a plain index, followed by a clauseless scan of a partial
	 * index "WHERE x >= 40 AND x < 50".  The partial index has been accepted
	 * only because "x = 42" was present, and so allowing it would partially
	 * double-count selectivity.  (We could use predicate_implied_by on
	 * regular qual clauses too, to have a more intelligent, but much more
	 * expensive, check for redundancy --- but in most cases simple equality
	 * seems to suffice.)
	 */

	/*
	 * Extract clause usage info and detect any paths that use exactly the
	 * same set of clauses; keep only the cheapest-to-scan of any such groups.
	 * The surviving paths are put into an array for qsort'ing.
	 */
	pathinfoarray = palloc_array(PathClauseUsage *, npaths);
	clauselist = NIL;
	npaths = 0;
	foreach(l, paths)
	{
		Path	   *ipath = (Path *) lfirst(l);

		pathinfo = classify_index_clause_usage(ipath, &clauselist);

		/* If it's unclassifiable, treat it as distinct from all others */
		if (pathinfo->unclassifiable)
		{
			pathinfoarray[npaths++] = pathinfo;
			continue;
		}

		for (i = 0; i < npaths; i++)
		{
			if (!pathinfoarray[i]->unclassifiable &&
				bms_equal(pathinfo->clauseids, pathinfoarray[i]->clauseids))
				break;
		}
		if (i < npaths)
		{
			/* duplicate clauseids, keep the cheaper one */
			Cost		ncost;
			Cost		ocost;
			Selectivity nselec;
			Selectivity oselec;

			cost_bitmap_tree_node(pathinfo->path, &ncost, &nselec);
			cost_bitmap_tree_node(pathinfoarray[i]->path, &ocost, &oselec);
			if (ncost < ocost)
				pathinfoarray[i] = pathinfo;
		}
		else
		{
			/* not duplicate clauseids, add to array */
			pathinfoarray[npaths++] = pathinfo;
		}
	}

	/* If only one surviving path, we're done */
	if (npaths == 1)
		return pathinfoarray[0]->path;

	/* Sort the surviving paths by index access cost */
	qsort(pathinfoarray, npaths, sizeof(PathClauseUsage *),
		  path_usage_comparator);

	/*
	 * For each surviving index, consider it as an "AND group leader", and see
	 * whether adding on any of the later indexes results in an AND path with
	 * cheaper total cost than before.  Then take the cheapest AND group.
	 *
	 * Note: paths that are either clauseless or unclassifiable will have
	 * empty clauseids, so that they will not be rejected by the clauseids
	 * filter here, nor will they cause later paths to be rejected by it.
	 */
	for (i = 0; i < npaths; i++)
	{
		Cost		costsofar;
		List	   *qualsofar;
		Bitmapset  *clauseidsofar;

		pathinfo = pathinfoarray[i];
		paths = list_make1(pathinfo->path);
		costsofar = bitmap_scan_cost_est(root, rel, pathinfo->path);
		qualsofar = list_concat_copy(pathinfo->quals, pathinfo->preds);
		clauseidsofar = bms_copy(pathinfo->clauseids);

		for (j = i + 1; j < npaths; j++)
		{
			Cost		newcost;

			pathinfo = pathinfoarray[j];
			/* Check for redundancy */
			if (bms_overlap(pathinfo->clauseids, clauseidsofar))
				continue;		/* consider it redundant */
			if (pathinfo->preds)
			{
				bool		redundant = false;

				/* we check each predicate clause separately */
				foreach(l, pathinfo->preds)
				{
					Node	   *np = (Node *) lfirst(l);

					if (predicate_implied_by(list_make1(np), qualsofar, false))
					{
						redundant = true;
						break;	/* out of inner foreach loop */
					}
				}
				if (redundant)
					continue;
			}
			/* tentatively add new path to paths, so we can estimate cost */
			paths = lappend(paths, pathinfo->path);
			newcost = bitmap_and_cost_est(root, rel, paths);
			if (newcost < costsofar)
			{
				/* keep new path in paths, update subsidiary variables */
				costsofar = newcost;
				qualsofar = list_concat(qualsofar, pathinfo->quals);
				qualsofar = list_concat(qualsofar, pathinfo->preds);
				clauseidsofar = bms_add_members(clauseidsofar,
												pathinfo->clauseids);
			}
			else
			{
				/* reject new path, remove it from paths list */
				paths = list_truncate(paths, list_length(paths) - 1);
			}
		}

		/* Keep the cheapest AND-group (or singleton) */
		if (i == 0 || costsofar < bestcost)
		{
			bestpaths = paths;
			bestcost = costsofar;
		}

		/* some easy cleanup (we don't try real hard though) */
		list_free(qualsofar);
	}

	if (list_length(bestpaths) == 1)
		return (Path *) linitial(bestpaths);	/* no need for AND */
	return (Path *) create_bitmap_and_path(root, rel, bestpaths);
}

/* qsort comparator to sort in increasing index access cost order */
static int
path_usage_comparator(const void *a, const void *b)
{
	PathClauseUsage *pa = *(PathClauseUsage *const *) a;
	PathClauseUsage *pb = *(PathClauseUsage *const *) b;
	Cost		acost;
	Cost		bcost;
	Selectivity aselec;
	Selectivity bselec;

	cost_bitmap_tree_node(pa->path, &acost, &aselec);
	cost_bitmap_tree_node(pb->path, &bcost, &bselec);

	/*
	 * If costs are the same, sort by selectivity.
	 */
	if (acost < bcost)
		return -1;
	if (acost > bcost)
		return 1;

	if (aselec < bselec)
		return -1;
	if (aselec > bselec)
		return 1;

	return 0;
}

/*
 * Estimate the cost of actually executing a bitmap scan with a single
 * index path (which could be a BitmapAnd or BitmapOr node).
 */
static Cost
bitmap_scan_cost_est(PlannerInfo *root, RelOptInfo *rel, Path *ipath)
{
	BitmapHeapPath bpath;

	/* Set up a dummy BitmapHeapPath */
	bpath.path.type = T_BitmapHeapPath;
	bpath.path.pathtype = T_BitmapHeapScan;
	bpath.path.parent = rel;
	bpath.path.pathtarget = rel->reltarget;
	bpath.path.param_info = ipath->param_info;
	bpath.path.pathkeys = NIL;
	bpath.bitmapqual = ipath;

	/*
	 * Check the cost of temporary path without considering parallelism.
	 * Parallel bitmap heap path will be considered at later stage.
	 */
	bpath.path.parallel_workers = 0;

	/* Now we can do cost_bitmap_heap_scan */
	cost_bitmap_heap_scan(&bpath.path, root, rel,
						  bpath.path.param_info,
						  ipath,
						  get_loop_count(root, rel->relid,
										 PATH_REQ_OUTER(ipath)));

	return bpath.path.total_cost;
}

/*
 * Estimate the cost of actually executing a BitmapAnd scan with the given
 * inputs.
 */
static Cost
bitmap_and_cost_est(PlannerInfo *root, RelOptInfo *rel, List *paths)
{
	BitmapAndPath *apath;

	/*
	 * Might as well build a real BitmapAndPath here, as the work is slightly
	 * too complicated to be worth repeating just to save one palloc.
	 */
	apath = create_bitmap_and_path(root, rel, paths);

	return bitmap_scan_cost_est(root, rel, (Path *) apath);
}


/*
 * classify_index_clause_usage
 *		Construct a PathClauseUsage struct describing the WHERE clauses and
 *		index predicate clauses used by the given indexscan path.
 *		We consider two clauses the same if they are equal().
 *
 * At some point we might want to migrate this info into the Path data
 * structure proper, but for the moment it's only needed within
 * choose_bitmap_and().
 *
 * *clauselist is used and expanded as needed to identify all the distinct
 * clauses seen across successive calls.  Caller must initialize it to NIL
 * before first call of a set.
 */
static PathClauseUsage *
classify_index_clause_usage(Path *path, List **clauselist)
{
	PathClauseUsage *result;
	Bitmapset  *clauseids;
	ListCell   *lc;

	result = palloc_object(PathClauseUsage);
	result->path = path;

	/* Recursively find the quals and preds used by the path */
	result->quals = NIL;
	result->preds = NIL;
	find_indexpath_quals(path, &result->quals, &result->preds);

	/*
	 * Some machine-generated queries have outlandish numbers of qual clauses.
	 * To avoid getting into O(N^2) behavior even in this preliminary
	 * classification step, we want to limit the number of entries we can
	 * accumulate in *clauselist.  Treat any path with more than 100 quals +
	 * preds as unclassifiable, which will cause calling code to consider it
	 * distinct from all other paths.
	 */
	if (list_length(result->quals) + list_length(result->preds) > 100)
	{
		result->clauseids = NULL;
		result->unclassifiable = true;
		return result;
	}

	/* Build up a bitmapset representing the quals and preds */
	clauseids = NULL;
	foreach(lc, result->quals)
	{
		Node	   *node = (Node *) lfirst(lc);

		clauseids = bms_add_member(clauseids,
								   find_list_position(node, clauselist));
	}
	foreach(lc, result->preds)
	{
		Node	   *node = (Node *) lfirst(lc);

		clauseids = bms_add_member(clauseids,
								   find_list_position(node, clauselist));
	}
	result->clauseids = clauseids;
	result->unclassifiable = false;

	return result;
}


/*
 * find_indexpath_quals
 *
 * Given the Path structure for a plain or bitmap indexscan, extract lists
 * of all the index clauses and index predicate conditions used in the Path.
 * These are appended to the initial contents of *quals and *preds (hence
 * caller should initialize those to NIL).
 *
 * Note we are not trying to produce an accurate representation of the AND/OR
 * semantics of the Path, but just find out all the base conditions used.
 *
 * The result lists contain pointers to the expressions used in the Path,
 * but all the list cells are freshly built, so it's safe to destructively
 * modify the lists (eg, by concat'ing with other lists).
 */
static void
find_indexpath_quals(Path *bitmapqual, List **quals, List **preds)
{
	if (IsA(bitmapqual, BitmapAndPath))
	{
		BitmapAndPath *apath = (BitmapAndPath *) bitmapqual;
		ListCell   *l;

		foreach(l, apath->bitmapquals)
		{
			find_indexpath_quals((Path *) lfirst(l), quals, preds);
		}
	}
	else if (IsA(bitmapqual, BitmapOrPath))
	{
		BitmapOrPath *opath = (BitmapOrPath *) bitmapqual;
		ListCell   *l;

		foreach(l, opath->bitmapquals)
		{
			find_indexpath_quals((Path *) lfirst(l), quals, preds);
		}
	}
	else if (IsA(bitmapqual, IndexPath))
	{
		IndexPath  *ipath = (IndexPath *) bitmapqual;
		ListCell   *l;

		foreach(l, ipath->indexclauses)
		{
			IndexClause *iclause = (IndexClause *) lfirst(l);

			*quals = lappend(*quals, iclause->rinfo->clause);
		}
		*preds = list_concat(*preds, ipath->indexinfo->indpredExpand);
	}
	else
		elog(ERROR, "unrecognized node type: %d", nodeTag(bitmapqual));
}


/*
 * find_list_position
 *		Return the given node's position (counting from 0) in the given
 *		list of nodes.  If it's not equal() to any existing list member,
 *		add it at the end, and return that position.
 */
static int
find_list_position(Node *node, List **nodelist)
{
	int			i;
	ListCell   *lc;

	i = 0;
	foreach(lc, *nodelist)
	{
		Node	   *oldnode = (Node *) lfirst(lc);

		if (equal(node, oldnode))
			return i;
		i++;
	}

	*nodelist = lappend(*nodelist, node);

	return i;
}


/*
 * check_index_only
 *		Determine whether an index-only scan is possible for this index.
 */
static bool
check_index_only(RelOptInfo *rel, IndexOptInfo *index)
{
	bool		result;
	Bitmapset  *attrs_used = NULL;
	Bitmapset  *index_canreturn_attrs = NULL;
	ListCell   *lc;
	int			i;

	/* If we're not allowed to consider index-only scans, give up now */
	if ((rel->pgs_mask & PGS_CONSIDER_INDEXONLY) == 0)
		return false;

	/*
	 * Check that all needed attributes of the relation are available from the
	 * index.
	 */

	/*
	 * First, identify all the attributes needed for joins or final output.
	 * Note: we must look at rel's targetlist, not the attr_needed data,
	 * because attr_needed isn't computed for inheritance child rels.
	 */
	pull_varattnos((Node *) rel->reltarget->exprs, rel->relid, &attrs_used);

	/*
	 * Add all the attributes used by restriction clauses; but consider only
	 * those clauses not implied by the index predicate, since ones that are
	 * so implied don't need to be checked explicitly in the plan.
	 *
	 * Note: attributes used only in index quals would not be needed at
	 * runtime either, if we are certain that the index is not lossy.  However
	 * it'd be complicated to account for that accurately, and it doesn't
	 * matter in most cases, since we'd conclude that such attributes are
	 * available from the index anyway.
	 */
	foreach(lc, index->indrestrictinfo)
	{
		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);

		pull_varattnos((Node *) rinfo->clause, rel->relid, &attrs_used);
	}

	/*
	 * Construct a bitmapset of columns that the index can return back in an
	 * index-only scan.
	 */
	for (i = 0; i < index->ncolumns; i++)
	{
		int			attno = index->indexkeys[i];

		/*
		 * For the moment, we just ignore index expressions.  It might be nice
		 * to do something with them, later.
		 */
		if (attno == 0)
			continue;

		if (index->canreturn[i])
			index_canreturn_attrs =
				bms_add_member(index_canreturn_attrs,
							   attno - FirstLowInvalidHeapAttributeNumber);
	}

	/* Do we have all the necessary attributes? */
	result = bms_is_subset(attrs_used, index_canreturn_attrs);

	bms_free(attrs_used);
	bms_free(index_canreturn_attrs);

	return result;
}

/*
 * get_loop_count
 *		Choose the loop count estimate to use for costing a parameterized path
 *		with the given set of outer relids.
 *
 * Since we produce parameterized paths before we've begun to generate join
 * relations, it's impossible to predict exactly how many times a parameterized
 * path will be iterated; we don't know the size of the relation that will be
 * on the outside of the nestloop.  However, we should try to account for
 * multiple iterations somehow in costing the path.  The heuristic embodied
 * here is to use the rowcount of the smallest other base relation needed in
 * the join clauses used by the path.  (We could alternatively consider the
 * largest one, but that seems too optimistic.)  This is of course the right
 * answer for single-other-relation cases, and it seems like a reasonable
 * zero-order approximation for multiway-join cases.
 *
 * In addition, we check to see if the other side of each join clause is on
 * the inside of some semijoin that the current relation is on the outside of.
 * If so, the only way that a parameterized path could be used is if the
 * semijoin RHS has been unique-ified, so we should use the number of unique
 * RHS rows rather than using the relation's raw rowcount.
 *
 * Note: for this to work, allpaths.c must establish all baserel size
 * estimates before it begins to compute paths, or at least before it
 * calls create_index_paths().
 */
static double
get_loop_count(PlannerInfo *root, Index cur_relid, Relids outer_relids)
{
	double		result;
	int			outer_relid;

	/* For a non-parameterized path, just return 1.0 quickly */
	if (outer_relids == NULL)
		return 1.0;

	result = 0.0;
	outer_relid = -1;
	while ((outer_relid = bms_next_member(outer_relids, outer_relid)) >= 0)
	{
		RelOptInfo *outer_rel;
		double		rowcount;

		/* Paranoia: ignore bogus relid indexes */
		if (outer_relid >= root->simple_rel_array_size)
			continue;
		outer_rel = root->simple_rel_array[outer_relid];
		if (outer_rel == NULL)
			continue;
		Assert(outer_rel->relid == outer_relid);	/* sanity check on array */

		/* Other relation could be proven empty, if so ignore */
		if (IS_DUMMY_REL(outer_rel))
			continue;

		/* Otherwise, rel's rows estimate should be valid by now */
		Assert(outer_rel->rows > 0);

		/* Check to see if rel is on the inside of any semijoins */
		rowcount = adjust_rowcount_for_semijoins(root,
												 cur_relid,
												 outer_relid,
												 outer_rel->rows);

		/* Remember smallest row count estimate among the outer rels */
		if (result == 0.0 || result > rowcount)
			result = rowcount;
	}
	/* Return 1.0 if we found no valid relations (shouldn't happen) */
	return (result > 0.0) ? result : 1.0;
}

/*
 * Check to see if outer_relid is on the inside of any semijoin that cur_relid
 * is on the outside of.  If so, replace rowcount with the estimated number of
 * unique rows from the semijoin RHS (assuming that's smaller, which it might
 * not be).  The estimate is crude but it's the best we can do at this stage
 * of the proceedings.
 */
static double
adjust_rowcount_for_semijoins(PlannerInfo *root,
							  Index cur_relid,
							  Index outer_relid,
							  double rowcount)
{
	ListCell   *lc;

	foreach(lc, root->join_info_list)
	{
		SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(lc);

		if (sjinfo->jointype == JOIN_SEMI &&
			bms_is_member(cur_relid, sjinfo->syn_lefthand) &&
			bms_is_member(outer_relid, sjinfo->syn_righthand))
		{
			/* Estimate number of unique-ified rows */
			double		nraw;
			double		nunique;

			nraw = approximate_joinrel_size(root, sjinfo->syn_righthand);
			nunique = estimate_num_groups(root,
										  sjinfo->semi_rhs_exprs,
										  nraw,
										  NULL,
										  NULL);
			if (rowcount > nunique)
				rowcount = nunique;
		}
	}
	return rowcount;
}

/*
 * Make an approximate estimate of the size of a joinrel.
 *
 * We don't have enough info at this point to get a good estimate, so we
 * just multiply the base relation sizes together.  Fortunately, this is
 * the right answer anyway for the most common case with a single relation
 * on the RHS of a semijoin.  Also, estimate_num_groups() has only a weak
 * dependency on its input_rows argument (it basically uses it as a clamp).
 * So we might be able to get a fairly decent end result even with a severe
 * overestimate of the RHS's raw size.
 */
static double
approximate_joinrel_size(PlannerInfo *root, Relids relids)
{
	double		rowcount = 1.0;
	int			relid;

	relid = -1;
	while ((relid = bms_next_member(relids, relid)) >= 0)
	{
		RelOptInfo *rel;

		/* Paranoia: ignore bogus relid indexes */
		if (relid >= root->simple_rel_array_size)
			continue;
		rel = root->simple_rel_array[relid];
		if (rel == NULL)
			continue;
		Assert(rel->relid == relid);	/* sanity check on array */

		/* Relation could be proven empty, if so ignore */
		if (IS_DUMMY_REL(rel))
			continue;

		/* Otherwise, rel's rows estimate should be valid by now */
		Assert(rel->rows > 0);

		/* Accumulate product */
		rowcount *= rel->rows;
	}
	return rowcount;
}


/****************************************************************************
 *				----  ROUTINES TO CHECK QUERY CLAUSES  ----
 ****************************************************************************/

/*
 * match_restriction_clauses_to_index
 *	  Identify restriction clauses for the rel that match the index.
 *	  Matching clauses are added to *clauseset.
 */
static void
match_restriction_clauses_to_index(PlannerInfo *root,
								   IndexOptInfo *index,
								   IndexClauseSet *clauseset)
{
	/* We can ignore clauses that are implied by the index predicate */
	match_clauses_to_index(root, index->indrestrictinfo, index, clauseset);
}

/*
 * match_join_clauses_to_index
 *	  Identify join clauses for the rel that match the index.
 *	  Matching clauses are added to *clauseset.
 *	  Also, add any potentially usable join OR clauses to *joinorclauses.
 *	  They also might be processed by match_clause_to_index() as a whole.
 */
static void
match_join_clauses_to_index(PlannerInfo *root,
							RelOptInfo *rel, IndexOptInfo *index,
							IndexClauseSet *clauseset,
							List **joinorclauses)
{
	ListCell   *lc;

	/* Scan the rel's join clauses */
	foreach(lc, rel->joininfo)
	{
		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);

		/* Check if clause can be moved to this rel */
		if (!join_clause_is_movable_to(rinfo, rel))
			continue;

		/*
		 * Potentially usable, so see if it matches the index or is an OR. Use
		 * list_append_unique_ptr() here to avoid possible duplicates when
		 * processing the same clauses with different indexes.
		 */
		if (restriction_is_or_clause(rinfo))
			*joinorclauses = list_append_unique_ptr(*joinorclauses, rinfo);

		match_clause_to_index(root, rinfo, index, clauseset);
	}
}

/*
 * match_eclass_clauses_to_index
 *	  Identify EquivalenceClass join clauses for the rel that match the index.
 *	  Matching clauses are added to *clauseset.
 */
static void
match_eclass_clauses_to_index(PlannerInfo *root, IndexOptInfo *index,
							  IndexClauseSet *clauseset)
{
	int			indexcol;

	/* No work if rel is not in any such ECs */
	if (!index->rel->has_eclass_joins)
		return;

	for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++)
	{
		ec_member_matches_arg arg;
		List	   *clauses;

		/* Generate clauses, skipping any that join to lateral_referencers */
		arg.index = index;
		arg.indexcol = indexcol;
		clauses = generate_implied_equalities_for_column(root,
														 index->rel,
														 ec_member_matches_indexcol,
														 &arg,
														 index->rel->lateral_referencers);

		/*
		 * We have to check whether the results actually do match the index,
		 * since for non-btree indexes the EC's equality operators might not
		 * be in the index opclass (cf ec_member_matches_indexcol).
		 */
		match_clauses_to_index(root, clauses, index, clauseset);
	}
}

/*
 * match_clauses_to_index
 *	  Perform match_clause_to_index() for each clause in a list.
 *	  Matching clauses are added to *clauseset.
 */
static void
match_clauses_to_index(PlannerInfo *root,
					   List *clauses,
					   IndexOptInfo *index,
					   IndexClauseSet *clauseset)
{
	ListCell   *lc;

	foreach(lc, clauses)
	{
		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);

		match_clause_to_index(root, rinfo, index, clauseset);
	}
}

/*
 * match_clause_to_index
 *	  Test whether a qual clause can be used with an index.
 *
 * If the clause is usable, add an IndexClause entry for it to the appropriate
 * list in *clauseset.  (*clauseset must be initialized to zeroes before first
 * call.)
 *
 * Note: in some circumstances we may find the same RestrictInfos coming from
 * multiple places.  Defend against redundant outputs by refusing to add a
 * clause twice (pointer equality should be a good enough check for this).
 *
 * Note: it's possible that a badly-defined index could have multiple matching
 * columns.  We always select the first match if so; this avoids scenarios
 * wherein we get an inflated idea of the index's selectivity by using the
 * same clause multiple times with different index columns.
 */
static void
match_clause_to_index(PlannerInfo *root,
					  RestrictInfo *rinfo,
					  IndexOptInfo *index,
					  IndexClauseSet *clauseset)
{
	int			indexcol;

	/*
	 * Never match pseudoconstants to indexes.  (Normally a match could not
	 * happen anyway, since a pseudoconstant clause couldn't contain a Var,
	 * but what if someone builds an expression index on a constant? It's not
	 * totally unreasonable to do so with a partial index, either.)
	 */
	if (rinfo->pseudoconstant)
		return;

	/*
	 * If clause can't be used as an indexqual because it must wait till after
	 * some lower-security-level restriction clause, reject it.
	 */
	if (!restriction_is_securely_promotable(rinfo, index->rel))
		return;

	/* OK, check each index key column for a match */
	for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++)
	{
		IndexClause *iclause;
		ListCell   *lc;

		/* Ignore duplicates */
		foreach(lc, clauseset->indexclauses[indexcol])
		{
			iclause = (IndexClause *) lfirst(lc);

			if (iclause->rinfo == rinfo)
				return;
		}

		/* OK, try to match the clause to the index column */
		iclause = match_clause_to_indexcol(root,
										   rinfo,
										   indexcol,
										   index);
		if (iclause)
		{
			/* Success, so record it */
			clauseset->indexclauses[indexcol] =
				lappend(clauseset->indexclauses[indexcol], iclause);
			clauseset->nonempty = true;
			return;
		}
	}
}

/*
 * match_clause_to_indexcol()
 *	  Determine whether a restriction clause matches a column of an index,
 *	  and if so, build an IndexClause node describing the details.
 *
 *	  To match an index normally, an operator clause:
 *
 *	  (1)  must be in the form (indexkey op const) or (const op indexkey);
 *		   and
 *	  (2)  must contain an operator which is in the index's operator family
 *		   for this column; and
 *	  (3)  must match the collation of the index, if collation is relevant.
 *
 *	  Our definition of "const" is exceedingly liberal: we allow anything that
 *	  doesn't involve a volatile function or a Var of the index's relation.
 *	  In particular, Vars belonging to other relations of the query are
 *	  accepted here, since a clause of that form can be used in a
 *	  parameterized indexscan.  It's the responsibility of higher code levels
 *	  to manage restriction and join clauses appropriately.
 *
 *	  Note: we do need to check for Vars of the index's relation on the
 *	  "const" side of the clause, since clauses like (a.f1 OP (b.f2 OP a.f3))
 *	  are not processable by a parameterized indexscan on a.f1, whereas
 *	  something like (a.f1 OP (b.f2 OP c.f3)) is.
 *
 *	  Presently, the executor can only deal with indexquals that have the
 *	  indexkey on the left, so we can only use clauses that have the indexkey
 *	  on the right if we can commute the clause to put the key on the left.
 *	  We handle that by generating an IndexClause with the correctly-commuted
 *	  opclause as a derived indexqual.
 *
 *	  If the index has a collation, the clause must have the same collation.
 *	  For collation-less indexes, we assume it doesn't matter; this is
 *	  necessary for cases like "hstore ? text", wherein hstore's operators
 *	  don't care about collation but the clause will get marked with a
 *	  collation anyway because of the text argument.  (This logic is
 *	  embodied in the macro IndexCollMatchesExprColl.)
 *
 *	  It is also possible to match RowCompareExpr clauses to indexes (but
 *	  currently, only btree indexes handle this).
 *
 *	  It is also possible to match ScalarArrayOpExpr clauses to indexes, when
 *	  the clause is of the form "indexkey op ANY (arrayconst)".
 *
 *	  It is also possible to match a list of OR clauses if it might be
 *	  transformed into a single ScalarArrayOpExpr clause.  On success,
 *	  the returning index clause will contain a transformed clause.
 *
 *	  For boolean indexes, it is also possible to match the clause directly
 *	  to the indexkey; or perhaps the clause is (NOT indexkey).
 *
 *	  And, last but not least, some operators and functions can be processed
 *	  to derive (typically lossy) indexquals from a clause that isn't in
 *	  itself indexable.  If we see that any operand of an OpExpr or FuncExpr
 *	  matches the index key, and the function has a planner support function
 *	  attached to it, we'll invoke the support function to see if such an
 *	  indexqual can be built.
 *
 * 'rinfo' is the clause to be tested (as a RestrictInfo node).
 * 'indexcol' is a column number of 'index' (counting from 0).
 * 'index' is the index of interest.
 *
 * Returns an IndexClause if the clause can be used with this index key,
 * or NULL if not.
 *
 * NOTE:  This routine always returns NULL if the clause is an AND clause.
 * Higher-level routines deal with OR and AND clauses. OR clause can be
 * matched as a whole by match_orclause_to_indexcol() though.
 */
static IndexClause *
match_clause_to_indexcol(PlannerInfo *root,
						 RestrictInfo *rinfo,
						 int indexcol,
						 IndexOptInfo *index)
{
	IndexClause *iclause;
	Expr	   *clause = rinfo->clause;
	Oid			opfamily;

	Assert(indexcol < index->nkeycolumns);

	/*
	 * Historically this code has coped with NULL clauses.  That's probably
	 * not possible anymore, but we might as well continue to cope.
	 */
	if (clause == NULL)
		return NULL;

	/* First check for boolean-index cases. */
	opfamily = index->opfamily[indexcol];
	if (IsBooleanOpfamily(opfamily))
	{
		iclause = match_boolean_index_clause(root, rinfo, indexcol, index);
		if (iclause)
			return iclause;
	}

	/*
	 * Clause must be an opclause, funcclause, ScalarArrayOpExpr,
	 * RowCompareExpr, or OR-clause that could be converted to SAOP.  Or, if
	 * the index supports it, we can handle IS NULL/NOT NULL clauses.
	 */
	if (IsA(clause, OpExpr))
	{
		return match_opclause_to_indexcol(root, rinfo, indexcol, index);
	}
	else if (IsA(clause, FuncExpr))
	{
		return match_funcclause_to_indexcol(root, rinfo, indexcol, index);
	}
	else if (IsA(clause, ScalarArrayOpExpr))
	{
		return match_saopclause_to_indexcol(root, rinfo, indexcol, index);
	}
	else if (IsA(clause, RowCompareExpr))
	{
		return match_rowcompare_to_indexcol(root, rinfo, indexcol, index);
	}
	else if (restriction_is_or_clause(rinfo))
	{
		return match_orclause_to_indexcol(root, rinfo, indexcol, index);
	}
	else if (index->amsearchnulls && IsA(clause, NullTest))
	{
		NullTest   *nt = (NullTest *) clause;

		if (!nt->argisrow &&
			match_index_to_operand((Node *) nt->arg, indexcol, index))
		{
			iclause = makeNode(IndexClause);
			iclause->rinfo = rinfo;
			iclause->indexquals = list_make1(rinfo);
			iclause->lossy = false;
			iclause->indexcol = indexcol;
			iclause->indexcols = NIL;
			return iclause;
		}
	}

	return NULL;
}

/*
 * IsBooleanOpfamily
 *	  Detect whether an opfamily supports boolean equality as an operator.
 *
 * If the opfamily OID is in the range of built-in objects, we can rely
 * on hard-wired knowledge of which built-in opfamilies support this.
 * For extension opfamilies, there's no choice but to do a catcache lookup.
 */
static bool
IsBooleanOpfamily(Oid opfamily)
{
	if (opfamily < FirstNormalObjectId)
		return IsBuiltinBooleanOpfamily(opfamily);
	else
		return op_in_opfamily(BooleanEqualOperator, opfamily);
}

/*
 * match_boolean_index_clause
 *	  Recognize restriction clauses that can be matched to a boolean index.
 *
 * The idea here is that, for an index on a boolean column that supports the
 * BooleanEqualOperator, we can transform a plain reference to the indexkey
 * into "indexkey = true", or "NOT indexkey" into "indexkey = false", etc,
 * so as to make the expression indexable using the index's "=" operator.
 * Since Postgres 8.1, we must do this because constant simplification does
 * the reverse transformation; without this code there'd be no way to use
 * such an index at all.
 *
 * This should be called only when IsBooleanOpfamily() recognizes the
 * index's operator family.  We check to see if the clause matches the
 * index's key, and if so, build a suitable IndexClause.
 */
static IndexClause *
match_boolean_index_clause(PlannerInfo *root,
						   RestrictInfo *rinfo,
						   int indexcol,
						   IndexOptInfo *index)
{
	Node	   *clause = (Node *) rinfo->clause;
	Expr	   *op = NULL;

	/* Direct match? */
	if (match_index_to_operand(clause, indexcol, index))
	{
		/* convert to indexkey = TRUE */
		op = make_opclause(BooleanEqualOperator, BOOLOID, false,
						   (Expr *) clause,
						   (Expr *) makeBoolConst(true, false),
						   InvalidOid, InvalidOid);
	}
	/* NOT clause? */
	else if (is_notclause(clause))
	{
		Node	   *arg = (Node *) get_notclausearg((Expr *) clause);

		if (match_index_to_operand(arg, indexcol, index))
		{
			/* convert to indexkey = FALSE */
			op = make_opclause(BooleanEqualOperator, BOOLOID, false,
							   (Expr *) arg,
							   (Expr *) makeBoolConst(false, false),
							   InvalidOid, InvalidOid);
		}
	}

	/*
	 * Since we only consider clauses at top level of WHERE, we can convert
	 * indexkey IS TRUE and indexkey IS FALSE to index searches as well.  The
	 * different meaning for NULL isn't important.
	 */
	else if (clause && IsA(clause, BooleanTest))
	{
		BooleanTest *btest = (BooleanTest *) clause;
		Node	   *arg = (Node *) btest->arg;

		if (btest->booltesttype == IS_TRUE &&
			match_index_to_operand(arg, indexcol, index))
		{
			/* convert to indexkey = TRUE */
			op = make_opclause(BooleanEqualOperator, BOOLOID, false,
							   (Expr *) arg,
							   (Expr *) makeBoolConst(true, false),
							   InvalidOid, InvalidOid);
		}
		else if (btest->booltesttype == IS_FALSE &&
				 match_index_to_operand(arg, indexcol, index))
		{
			/* convert to indexkey = FALSE */
			op = make_opclause(BooleanEqualOperator, BOOLOID, false,
							   (Expr *) arg,
							   (Expr *) makeBoolConst(false, false),
							   InvalidOid, InvalidOid);
		}
	}

	/*
	 * If we successfully made an operator clause from the given qual, we must
	 * wrap it in an IndexClause.  It's not lossy.
	 */
	if (op)
	{
		IndexClause *iclause = makeNode(IndexClause);

		iclause->rinfo = rinfo;
		iclause->indexquals = list_make1(make_simple_restrictinfo(root, op));
		iclause->lossy = false;
		iclause->indexcol = indexcol;
		iclause->indexcols = NIL;
		return iclause;
	}

	return NULL;
}

/*
 * match_opclause_to_indexcol()
 *	  Handles the OpExpr case for match_clause_to_indexcol(),
 *	  which see for comments.
 */
static IndexClause *
match_opclause_to_indexcol(PlannerInfo *root,
						   RestrictInfo *rinfo,
						   int indexcol,
						   IndexOptInfo *index)
{
	IndexClause *iclause;
	OpExpr	   *clause = (OpExpr *) rinfo->clause;
	Node	   *leftop,
			   *rightop;
	Oid			expr_op;
	Oid			expr_coll;
	Index		index_relid;
	Oid			opfamily;
	Oid			idxcollation;

	/*
	 * Only binary operators need apply.  (In theory, a planner support
	 * function could do something with a unary operator, but it seems
	 * unlikely to be worth the cycles to check.)
	 */
	if (list_length(clause->args) != 2)
		return NULL;

	leftop = (Node *) linitial(clause->args);
	rightop = (Node *) lsecond(clause->args);
	expr_op = clause->opno;
	expr_coll = clause->inputcollid;

	index_relid = index->rel->relid;
	opfamily = index->opfamily[indexcol];
	idxcollation = index->indexcollations[indexcol];

	/*
	 * Check for clauses of the form: (indexkey operator constant) or
	 * (constant operator indexkey).  See match_clause_to_indexcol's notes
	 * about const-ness.
	 *
	 * Note that we don't ask the support function about clauses that don't
	 * have one of these forms.  Again, in principle it might be possible to
	 * do something, but it seems unlikely to be worth the cycles to check.
	 */
	if (match_index_to_operand(leftop, indexcol, index) &&
		!bms_is_member(index_relid, rinfo->right_relids) &&
		!contain_volatile_functions(rightop))
	{
		if (IndexCollMatchesExprColl(idxcollation, expr_coll) &&
			op_in_opfamily(expr_op, opfamily))
		{
			iclause = makeNode(IndexClause);
			iclause->rinfo = rinfo;
			iclause->indexquals = list_make1(rinfo);
			iclause->lossy = false;
			iclause->indexcol = indexcol;
			iclause->indexcols = NIL;
			return iclause;
		}

		/*
		 * If we didn't find a member of the index's opfamily, try the support
		 * function for the operator's underlying function.
		 */
		set_opfuncid(clause);	/* make sure we have opfuncid */
		return get_index_clause_from_support(root,
											 rinfo,
											 clause->opfuncid,
											 0, /* indexarg on left */
											 indexcol,
											 index);
	}

	if (match_index_to_operand(rightop, indexcol, index) &&
		!bms_is_member(index_relid, rinfo->left_relids) &&
		!contain_volatile_functions(leftop))
	{
		if (IndexCollMatchesExprColl(idxcollation, expr_coll))
		{
			Oid			comm_op = get_commutator(expr_op);

			if (OidIsValid(comm_op) &&
				op_in_opfamily(comm_op, opfamily))
			{
				RestrictInfo *commrinfo;

				/* Build a commuted OpExpr and RestrictInfo */
				commrinfo = commute_restrictinfo(rinfo, comm_op);

				/* Make an IndexClause showing that as a derived qual */
				iclause = makeNode(IndexClause);
				iclause->rinfo = rinfo;
				iclause->indexquals = list_make1(commrinfo);
				iclause->lossy = false;
				iclause->indexcol = indexcol;
				iclause->indexcols = NIL;
				return iclause;
			}
		}

		/*
		 * If we didn't find a member of the index's opfamily, try the support
		 * function for the operator's underlying function.
		 */
		set_opfuncid(clause);	/* make sure we have opfuncid */
		return get_index_clause_from_support(root,
											 rinfo,
											 clause->opfuncid,
											 1, /* indexarg on right */
											 indexcol,
											 index);
	}

	return NULL;
}

/*
 * match_funcclause_to_indexcol()
 *	  Handles the FuncExpr case for match_clause_to_indexcol(),
 *	  which see for comments.
 */
static IndexClause *
match_funcclause_to_indexcol(PlannerInfo *root,
							 RestrictInfo *rinfo,
							 int indexcol,
							 IndexOptInfo *index)
{
	FuncExpr   *clause = (FuncExpr *) rinfo->clause;
	int			indexarg;
	ListCell   *lc;

	/*
	 * We have no built-in intelligence about function clauses, but if there's
	 * a planner support function, it might be able to do something.  But, to
	 * cut down on wasted planning cycles, only call the support function if
	 * at least one argument matches the target index column.
	 *
	 * Note that we don't insist on the other arguments being pseudoconstants;
	 * the support function has to check that.  This is to allow cases where
	 * only some of the other arguments need to be included in the indexqual.
	 */
	indexarg = 0;
	foreach(lc, clause->args)
	{
		Node	   *op = (Node *) lfirst(lc);

		if (match_index_to_operand(op, indexcol, index))
		{
			return get_index_clause_from_support(root,
												 rinfo,
												 clause->funcid,
												 indexarg,
												 indexcol,
												 index);
		}

		indexarg++;
	}

	return NULL;
}

/*
 * get_index_clause_from_support()
 *		If the function has a planner support function, try to construct
 *		an IndexClause using indexquals created by the support function.
 */
static IndexClause *
get_index_clause_from_support(PlannerInfo *root,
							  RestrictInfo *rinfo,
							  Oid funcid,
							  int indexarg,
							  int indexcol,
							  IndexOptInfo *index)
{
	Oid			prosupport = get_func_support(funcid);
	SupportRequestIndexCondition req;
	List	   *sresult;

	if (!OidIsValid(prosupport))
		return NULL;

	req.type = T_SupportRequestIndexCondition;
	req.root = root;
	req.funcid = funcid;
	req.node = (Node *) rinfo->clause;
	req.indexarg = indexarg;
	req.index = index;
	req.indexcol = indexcol;
	req.opfamily = index->opfamily[indexcol];
	req.indexcollation = index->indexcollations[indexcol];

	req.lossy = true;			/* default assumption */

	sresult = (List *)
		DatumGetPointer(OidFunctionCall1(prosupport,
										 PointerGetDatum(&req)));

	if (sresult != NIL)
	{
		IndexClause *iclause = makeNode(IndexClause);
		List	   *indexquals = NIL;
		ListCell   *lc;

		/*
		 * The support function API says it should just give back bare
		 * clauses, so here we must wrap each one in a RestrictInfo.
		 */
		foreach(lc, sresult)
		{
			Expr	   *clause = (Expr *) lfirst(lc);

			indexquals = lappend(indexquals,
								 make_simple_restrictinfo(root, clause));
		}

		iclause->rinfo = rinfo;
		iclause->indexquals = indexquals;
		iclause->lossy = req.lossy;
		iclause->indexcol = indexcol;
		iclause->indexcols = NIL;

		return iclause;
	}

	return NULL;
}

/*
 * match_saopclause_to_indexcol()
 *	  Handles the ScalarArrayOpExpr case for match_clause_to_indexcol(),
 *	  which see for comments.
 */
static IndexClause *
match_saopclause_to_indexcol(PlannerInfo *root,
							 RestrictInfo *rinfo,
							 int indexcol,
							 IndexOptInfo *index)
{
	ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) rinfo->clause;
	Node	   *leftop,
			   *rightop;
	Relids		right_relids;
	Oid			expr_op;
	Oid			expr_coll;
	Index		index_relid;
	Oid			opfamily;
	Oid			idxcollation;

	/* We only accept ANY clauses, not ALL */
	if (!saop->useOr)
		return NULL;
	leftop = (Node *) linitial(saop->args);
	rightop = (Node *) lsecond(saop->args);
	right_relids = pull_varnos(root, rightop);
	expr_op = saop->opno;
	expr_coll = saop->inputcollid;

	index_relid = index->rel->relid;
	opfamily = index->opfamily[indexcol];
	idxcollation = index->indexcollations[indexcol];

	/*
	 * We must have indexkey on the left and a pseudo-constant array argument.
	 */
	if (match_index_to_operand(leftop, indexcol, index) &&
		!bms_is_member(index_relid, right_relids) &&
		!contain_volatile_functions(rightop))
	{
		if (IndexCollMatchesExprColl(idxcollation, expr_coll) &&
			op_in_opfamily(expr_op, opfamily))
		{
			IndexClause *iclause = makeNode(IndexClause);

			iclause->rinfo = rinfo;
			iclause->indexquals = list_make1(rinfo);
			iclause->lossy = false;
			iclause->indexcol = indexcol;
			iclause->indexcols = NIL;
			return iclause;
		}

		/*
		 * We do not currently ask support functions about ScalarArrayOpExprs,
		 * though in principle we could.
		 */
	}

	return NULL;
}

/*
 * match_rowcompare_to_indexcol()
 *	  Handles the RowCompareExpr case for match_clause_to_indexcol(),
 *	  which see for comments.
 *
 * In this routine we check whether the first column of the row comparison
 * matches the target index column.  This is sufficient to guarantee that some
 * index condition can be constructed from the RowCompareExpr --- the rest
 * is handled by expand_indexqual_rowcompare().
 */
static IndexClause *
match_rowcompare_to_indexcol(PlannerInfo *root,
							 RestrictInfo *rinfo,
							 int indexcol,
							 IndexOptInfo *index)
{
	RowCompareExpr *clause = (RowCompareExpr *) rinfo->clause;
	Index		index_relid;
	Oid			opfamily;
	Oid			idxcollation;
	Node	   *leftop,
			   *rightop;
	bool		var_on_left;
	Oid			expr_op;
	Oid			expr_coll;

	/* Forget it if we're not dealing with a btree index */
	if (index->relam != BTREE_AM_OID)
		return NULL;

	index_relid = index->rel->relid;
	opfamily = index->opfamily[indexcol];
	idxcollation = index->indexcollations[indexcol];

	/*
	 * We could do the matching on the basis of insisting that the opfamily
	 * shown in the RowCompareExpr be the same as the index column's opfamily,
	 * but that could fail in the presence of reverse-sort opfamilies: it'd be
	 * a matter of chance whether RowCompareExpr had picked the forward or
	 * reverse-sort family.  So look only at the operator, and match if it is
	 * a member of the index's opfamily (after commutation, if the indexkey is
	 * on the right).  We'll worry later about whether any additional
	 * operators are matchable to the index.
	 */
	leftop = (Node *) linitial(clause->largs);
	rightop = (Node *) linitial(clause->rargs);
	expr_op = linitial_oid(clause->opnos);
	expr_coll = linitial_oid(clause->inputcollids);

	/* Collations must match, if relevant */
	if (!IndexCollMatchesExprColl(idxcollation, expr_coll))
		return NULL;

	/*
	 * These syntactic tests are the same as in match_opclause_to_indexcol()
	 */
	if (match_index_to_operand(leftop, indexcol, index) &&
		!bms_is_member(index_relid, pull_varnos(root, rightop)) &&
		!contain_volatile_functions(rightop))
	{
		/* OK, indexkey is on left */
		var_on_left = true;
	}
	else if (match_index_to_operand(rightop, indexcol, index) &&
			 !bms_is_member(index_relid, pull_varnos(root, leftop)) &&
			 !contain_volatile_functions(leftop))
	{
		/* indexkey is on right, so commute the operator */
		expr_op = get_commutator(expr_op);
		if (expr_op == InvalidOid)
			return NULL;
		var_on_left = false;
	}
	else
		return NULL;

	/* We're good if the operator is the right type of opfamily member */
	switch (get_op_opfamily_strategy(expr_op, opfamily))
	{
		case BTLessStrategyNumber:
		case BTLessEqualStrategyNumber:
		case BTGreaterEqualStrategyNumber:
		case BTGreaterStrategyNumber:
			return expand_indexqual_rowcompare(root,
											   rinfo,
											   indexcol,
											   index,
											   expr_op,
											   var_on_left);
	}

	return NULL;
}

/*
 * match_orclause_to_indexcol()
 *	  Handles the OR-expr case for match_clause_to_indexcol() in the case
 *	  when it could be transformed to ScalarArrayOpExpr.
 *
 * In this routine, we attempt to transform a list of OR-clause args into a
 * single SAOP expression matching the target index column.  On success,
 * return an IndexClause containing the transformed expression.
 * Return NULL if the transformation fails.
 */
static IndexClause *
match_orclause_to_indexcol(PlannerInfo *root,
						   RestrictInfo *rinfo,
						   int indexcol,
						   IndexOptInfo *index)
{
	BoolExpr   *orclause = (BoolExpr *) rinfo->orclause;
	List	   *consts = NIL;
	Node	   *indexExpr = NULL;
	Oid			matchOpno = InvalidOid;
	Oid			consttype = InvalidOid;
	Oid			arraytype = InvalidOid;
	Oid			inputcollid = InvalidOid;
	bool		firstTime = true;
	bool		haveNonConst = false;
	Index		indexRelid = index->rel->relid;
	ScalarArrayOpExpr *saopexpr;
	IndexClause *iclause;
	ListCell   *lc;

	/* Forget it if index doesn't support SAOP clauses */
	if (!index->amsearcharray)
		return NULL;

	/*
	 * Try to convert a list of OR-clauses to a single SAOP expression. Each
	 * OR entry must be in the form: (indexkey operator constant) or (constant
	 * operator indexkey).  Operators of all the entries must match.  On
	 * discovery of anything unsupported, we give up by breaking out of the
	 * loop immediately and returning NULL.
	 */
	foreach(lc, orclause->args)
	{
		RestrictInfo *subRinfo = (RestrictInfo *) lfirst(lc);
		OpExpr	   *subClause;
		Oid			opno;
		Node	   *leftop,
				   *rightop;
		Node	   *constExpr;

		/* If it's not a RestrictInfo (i.e. it's a sub-AND), we can't use it */
		if (!IsA(subRinfo, RestrictInfo))
			break;

		/* Only operator clauses can match */
		if (!IsA(subRinfo->clause, OpExpr))
			break;

		subClause = (OpExpr *) subRinfo->clause;
		opno = subClause->opno;

		/* Only binary operators can match */
		if (list_length(subClause->args) != 2)
			break;

		/*
		 * Check for clauses of the form: (indexkey operator constant) or
		 * (constant operator indexkey).  These tests should agree with
		 * match_opclause_to_indexcol.
		 */
		leftop = (Node *) linitial(subClause->args);
		rightop = (Node *) lsecond(subClause->args);
		if (match_index_to_operand(leftop, indexcol, index) &&
			!bms_is_member(indexRelid, subRinfo->right_relids) &&
			!contain_volatile_functions(rightop))
		{
			indexExpr = leftop;
			constExpr = rightop;
		}
		else if (match_index_to_operand(rightop, indexcol, index) &&
				 !bms_is_member(indexRelid, subRinfo->left_relids) &&
				 !contain_volatile_functions(leftop))
		{
			opno = get_commutator(opno);
			if (!OidIsValid(opno))
			{
				/* commutator doesn't exist, we can't reverse the order */
				break;
			}
			indexExpr = rightop;
			constExpr = leftop;
		}
		else
		{
			break;
		}

		/*
		 * Save information about the operator, type, and collation for the
		 * first matching qual.  Then, check that subsequent quals match the
		 * first.
		 */
		if (firstTime)
		{
			matchOpno = opno;
			consttype = exprType(constExpr);
			arraytype = get_array_type(consttype);
			inputcollid = subClause->inputcollid;

			/*
			 * Check that the operator is presented in the opfamily and that
			 * the expression collation matches the index collation.  Also,
			 * there must be an array type to construct an array later.
			 */
			if (!IndexCollMatchesExprColl(index->indexcollations[indexcol],
										  inputcollid) ||
				!op_in_opfamily(matchOpno, index->opfamily[indexcol]) ||
				!OidIsValid(arraytype))
				break;

			/*
			 * Disallow if either type is RECORD, mainly because we can't be
			 * positive that all the RHS expressions are the same record type.
			 */
			if (consttype == RECORDOID || exprType(indexExpr) == RECORDOID)
				break;

			firstTime = false;
		}
		else
		{
			if (matchOpno != opno ||
				inputcollid != subClause->inputcollid ||
				consttype != exprType(constExpr))
				break;
		}

		/*
		 * The righthand inputs don't necessarily have to be plain Consts, but
		 * make_SAOP_expr needs to know if any are not.
		 */
		if (!IsA(constExpr, Const))
			haveNonConst = true;

		consts = lappend(consts, constExpr);
	}

	/*
	 * Handle failed conversion from breaking out of the loop because of an
	 * unsupported qual.  Also check that we have an indexExpr, just in case
	 * the OR list was somehow empty (it shouldn't be).  Return NULL to
	 * indicate the conversion failed.
	 */
	if (lc != NULL || indexExpr == NULL)
	{
		list_free(consts);		/* might as well */
		return NULL;
	}

	/*
	 * Build the new SAOP node.  We use the indexExpr from the last OR arm;
	 * since all the arms passed match_index_to_operand, it shouldn't matter
	 * which one we use.  But using "inputcollid" twice is a bit of a cheat:
	 * we might end up with an array Const node that is labeled with a
	 * collation despite its elements being of a noncollatable type.  But
	 * nothing is likely to complain about that, so we don't bother being more
	 * accurate.
	 */
	saopexpr = make_SAOP_expr(matchOpno, indexExpr, consttype, inputcollid,
							  inputcollid, consts, haveNonConst);
	Assert(saopexpr != NULL);

	/*
	 * Finally, build an IndexClause based on the SAOP node.  It's not lossy.
	 */
	iclause = makeNode(IndexClause);
	iclause->rinfo = rinfo;
	iclause->indexquals = list_make1(make_simple_restrictinfo(root,
															  (Expr *) saopexpr));
	iclause->lossy = false;
	iclause->indexcol = indexcol;
	iclause->indexcols = NIL;
	return iclause;
}

/*
 * expand_indexqual_rowcompare --- expand a single indexqual condition
 *		that is a RowCompareExpr
 *
 * It's already known that the first column of the row comparison matches
 * the specified column of the index.  We can use additional columns of the
 * row comparison as index qualifications, so long as they match the index
 * in the "same direction", ie, the indexkeys are all on the same side of the
 * clause and the operators are all the same-type members of the opfamilies.
 *
 * If all the columns of the RowCompareExpr match in this way, we just use it
 * as-is, except for possibly commuting it to put the indexkeys on the left.
 *
 * Otherwise, we build a shortened RowCompareExpr (if more than one
 * column matches) or a simple OpExpr (if the first-column match is all
 * there is).  In these cases the modified clause is always "<=" or ">="
 * even when the original was "<" or ">" --- this is necessary to match all
 * the rows that could match the original.  (We are building a lossy version
 * of the row comparison when we do this, so we set lossy = true.)
 *
 * Note: this is really just the last half of match_rowcompare_to_indexcol,
 * but we split it out for comprehensibility.
 */
static IndexClause *
expand_indexqual_rowcompare(PlannerInfo *root,
							RestrictInfo *rinfo,
							int indexcol,
							IndexOptInfo *index,
							Oid expr_op,
							bool var_on_left)
{
	IndexClause *iclause = makeNode(IndexClause);
	RowCompareExpr *clause = (RowCompareExpr *) rinfo->clause;
	int			op_strategy;
	Oid			op_lefttype;
	Oid			op_righttype;
	int			matching_cols;
	List	   *expr_ops;
	List	   *opfamilies;
	List	   *lefttypes;
	List	   *righttypes;
	List	   *new_ops;
	List	   *var_args;
	List	   *non_var_args;

	iclause->rinfo = rinfo;
	iclause->indexcol = indexcol;

	if (var_on_left)
	{
		var_args = clause->largs;
		non_var_args = clause->rargs;
	}
	else
	{
		var_args = clause->rargs;
		non_var_args = clause->largs;
	}

	get_op_opfamily_properties(expr_op, index->opfamily[indexcol], false,
							   &op_strategy,
							   &op_lefttype,
							   &op_righttype);

	/* Initialize returned list of which index columns are used */
	iclause->indexcols = list_make1_int(indexcol);

	/* Build lists of ops, opfamilies and operator datatypes in case needed */
	expr_ops = list_make1_oid(expr_op);
	opfamilies = list_make1_oid(index->opfamily[indexcol]);
	lefttypes = list_make1_oid(op_lefttype);
	righttypes = list_make1_oid(op_righttype);

	/*
	 * See how many of the remaining columns match some index column in the
	 * same way.  As in match_clause_to_indexcol(), the "other" side of any
	 * potential index condition is OK as long as it doesn't use Vars from the
	 * indexed relation.
	 */
	matching_cols = 1;

	while (matching_cols < list_length(var_args))
	{
		Node	   *varop = (Node *) list_nth(var_args, matching_cols);
		Node	   *constop = (Node *) list_nth(non_var_args, matching_cols);
		int			i;

		expr_op = list_nth_oid(clause->opnos, matching_cols);
		if (!var_on_left)
		{
			/* indexkey is on right, so commute the operator */
			expr_op = get_commutator(expr_op);
			if (expr_op == InvalidOid)
				break;			/* operator is not usable */
		}
		if (bms_is_member(index->rel->relid, pull_varnos(root, constop)))
			break;				/* no good, Var on wrong side */
		if (contain_volatile_functions(constop))
			break;				/* no good, volatile comparison value */

		/*
		 * The Var side can match any key column of the index.
		 */
		for (i = 0; i < index->nkeycolumns; i++)
		{
			if (match_index_to_operand(varop, i, index) &&
				get_op_opfamily_strategy(expr_op,
										 index->opfamily[i]) == op_strategy &&
				IndexCollMatchesExprColl(index->indexcollations[i],
										 list_nth_oid(clause->inputcollids,
													  matching_cols)))
				break;
		}
		if (i >= index->nkeycolumns)
			break;				/* no match found */

		/* Add column number to returned list */
		iclause->indexcols = lappend_int(iclause->indexcols, i);

		/* Add operator info to lists */
		get_op_opfamily_properties(expr_op, index->opfamily[i], false,
								   &op_strategy,
								   &op_lefttype,
								   &op_righttype);
		expr_ops = lappend_oid(expr_ops, expr_op);
		opfamilies = lappend_oid(opfamilies, index->opfamily[i]);
		lefttypes = lappend_oid(lefttypes, op_lefttype);
		righttypes = lappend_oid(righttypes, op_righttype);

		/* This column matches, keep scanning */
		matching_cols++;
	}

	/* Result is non-lossy if all columns are usable as index quals */
	iclause->lossy = (matching_cols != list_length(clause->opnos));

	/*
	 * We can use rinfo->clause as-is if we have var on left and it's all
	 * usable as index quals.
	 */
	if (var_on_left && !iclause->lossy)
		iclause->indexquals = list_make1(rinfo);
	else
	{
		/*
		 * We have to generate a modified rowcompare (possibly just one
		 * OpExpr).  The painful part of this is changing < to <= or > to >=,
		 * so deal with that first.
		 */
		if (!iclause->lossy)
		{
			/* very easy, just use the commuted operators */
			new_ops = expr_ops;
		}
		else if (op_strategy == BTLessEqualStrategyNumber ||
				 op_strategy == BTGreaterEqualStrategyNumber)
		{
			/* easy, just use the same (possibly commuted) operators */
			new_ops = list_truncate(expr_ops, matching_cols);
		}
		else
		{
			ListCell   *opfamilies_cell;
			ListCell   *lefttypes_cell;
			ListCell   *righttypes_cell;

			if (op_strategy == BTLessStrategyNumber)
				op_strategy = BTLessEqualStrategyNumber;
			else if (op_strategy == BTGreaterStrategyNumber)
				op_strategy = BTGreaterEqualStrategyNumber;
			else
				elog(ERROR, "unexpected strategy number %d", op_strategy);
			new_ops = NIL;
			forthree(opfamilies_cell, opfamilies,
					 lefttypes_cell, lefttypes,
					 righttypes_cell, righttypes)
			{
				Oid			opfam = lfirst_oid(opfamilies_cell);
				Oid			lefttype = lfirst_oid(lefttypes_cell);
				Oid			righttype = lfirst_oid(righttypes_cell);

				expr_op = get_opfamily_member(opfam, lefttype, righttype,
											  op_strategy);
				if (!OidIsValid(expr_op))	/* should not happen */
					elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
						 op_strategy, lefttype, righttype, opfam);
				new_ops = lappend_oid(new_ops, expr_op);
			}
		}

		/* If we have more than one matching col, create a subset rowcompare */
		if (matching_cols > 1)
		{
			RowCompareExpr *rc = makeNode(RowCompareExpr);

			rc->cmptype = (CompareType) op_strategy;
			rc->opnos = new_ops;
			rc->opfamilies = list_copy_head(clause->opfamilies,
											matching_cols);
			rc->inputcollids = list_copy_head(clause->inputcollids,
											  matching_cols);
			rc->largs = list_copy_head(var_args, matching_cols);
			rc->rargs = list_copy_head(non_var_args, matching_cols);
			iclause->indexquals = list_make1(make_simple_restrictinfo(root,
																	  (Expr *) rc));
		}
		else
		{
			Expr	   *op;

			/* We don't report an index column list in this case */
			iclause->indexcols = NIL;

			op = make_opclause(linitial_oid(new_ops), BOOLOID, false,
							   copyObject(linitial(var_args)),
							   copyObject(linitial(non_var_args)),
							   InvalidOid,
							   linitial_oid(clause->inputcollids));
			iclause->indexquals = list_make1(make_simple_restrictinfo(root, op));
		}
	}

	return iclause;
}


/****************************************************************************
 *				----  ROUTINES TO CHECK ORDERING OPERATORS	----
 ****************************************************************************/

/*
 * match_pathkeys_to_index
 *		For the given 'index' and 'pathkeys', output a list of suitable ORDER
 *		BY expressions, each of the form "indexedcol operator pseudoconstant",
 *		along with an integer list of the index column numbers (zero based)
 *		that each clause would be used with.
 *
 * This attempts to find an ORDER BY and index column number for all items in
 * the pathkey list, however, if we're unable to match any given pathkey to an
 * index column, we return just the ones matched by the function so far.  This
 * allows callers who are interested in partial matches to get them.  Callers
 * can determine a partial match vs a full match by checking the outputted
 * list lengths.  A full match will have one item in the output lists for each
 * item in the given 'pathkeys' list.
 */
static void
match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
						List **orderby_clauses_p,
						List **clause_columns_p)
{
	ListCell   *lc1;

	*orderby_clauses_p = NIL;	/* set default results */
	*clause_columns_p = NIL;

	/* Only indexes with the amcanorderbyop property are interesting here */
	if (!index->amcanorderbyop)
		return;

	foreach(lc1, pathkeys)
	{
		PathKey    *pathkey = (PathKey *) lfirst(lc1);
		bool		found = false;
		EquivalenceMemberIterator it;
		EquivalenceMember *member;


		/* Pathkey must request default sort order for the target opfamily */
		if (pathkey->pk_cmptype != COMPARE_LT || pathkey->pk_nulls_first)
			return;

		/* If eclass is volatile, no hope of using an indexscan */
		if (pathkey->pk_eclass->ec_has_volatile)
			return;

		/*
		 * Try to match eclass member expression(s) to index.  Note that child
		 * EC members are considered, but only when they belong to the target
		 * relation.  (Unlike regular members, the same expression could be a
		 * child member of more than one EC.  Therefore, the same index could
		 * be considered to match more than one pathkey list, which is OK
		 * here.  See also get_eclass_for_sort_expr.)
		 */
		setup_eclass_member_iterator(&it, pathkey->pk_eclass,
									 index->rel->relids);
		while ((member = eclass_member_iterator_next(&it)) != NULL)
		{
			int			indexcol;

			/* No possibility of match if it references other relations */
			if (!bms_equal(member->em_relids, index->rel->relids))
				continue;

			/*
			 * We allow any column of the index to match each pathkey; they
			 * don't have to match left-to-right as you might expect.  This is
			 * correct for GiST, and it doesn't matter for SP-GiST because
			 * that doesn't handle multiple columns anyway, and no other
			 * existing AMs support amcanorderbyop.  We might need different
			 * logic in future for other implementations.
			 */
			for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++)
			{
				Expr	   *expr;

				expr = match_clause_to_ordering_op(index,
												   indexcol,
												   member->em_expr,
												   pathkey->pk_opfamily);
				if (expr)
				{
					*orderby_clauses_p = lappend(*orderby_clauses_p, expr);
					*clause_columns_p = lappend_int(*clause_columns_p, indexcol);
					found = true;
					break;
				}
			}

			if (found)			/* don't want to look at remaining members */
				break;
		}

		/*
		 * Return the matches found so far when this pathkey couldn't be
		 * matched to the index.
		 */
		if (!found)
			return;
	}
}

/*
 * match_clause_to_ordering_op
 *	  Determines whether an ordering operator expression matches an
 *	  index column.
 *
 *	  This is similar to, but simpler than, match_clause_to_indexcol.
 *	  We only care about simple OpExpr cases.  The input is a bare
 *	  expression that is being ordered by, which must be of the form
 *	  (indexkey op const) or (const op indexkey) where op is an ordering
 *	  operator for the column's opfamily.
 *
 * 'index' is the index of interest.
 * 'indexcol' is a column number of 'index' (counting from 0).
 * 'clause' is the ordering expression to be tested.
 * 'pk_opfamily' is the btree opfamily describing the required sort order.
 *
 * Note that we currently do not consider the collation of the ordering
 * operator's result.  In practical cases the result type will be numeric
 * and thus have no collation, and it's not very clear what to match to
 * if it did have a collation.  The index's collation should match the
 * ordering operator's input collation, not its result.
 *
 * If successful, return 'clause' as-is if the indexkey is on the left,
 * otherwise a commuted copy of 'clause'.  If no match, return NULL.
 */
static Expr *
match_clause_to_ordering_op(IndexOptInfo *index,
							int indexcol,
							Expr *clause,
							Oid pk_opfamily)
{
	Oid			opfamily;
	Oid			idxcollation;
	Node	   *leftop,
			   *rightop;
	Oid			expr_op;
	Oid			expr_coll;
	Oid			sortfamily;
	bool		commuted;

	Assert(indexcol < index->nkeycolumns);

	opfamily = index->opfamily[indexcol];
	idxcollation = index->indexcollations[indexcol];

	/*
	 * Clause must be a binary opclause.
	 */
	if (!is_opclause(clause))
		return NULL;
	leftop = get_leftop(clause);
	rightop = get_rightop(clause);
	if (!leftop || !rightop)
		return NULL;
	expr_op = ((OpExpr *) clause)->opno;
	expr_coll = ((OpExpr *) clause)->inputcollid;

	/*
	 * We can forget the whole thing right away if wrong collation.
	 */
	if (!IndexCollMatchesExprColl(idxcollation, expr_coll))
		return NULL;

	/*
	 * Check for clauses of the form: (indexkey operator constant) or
	 * (constant operator indexkey).
	 */
	if (match_index_to_operand(leftop, indexcol, index) &&
		!contain_var_clause(rightop) &&
		!contain_volatile_functions(rightop))
	{
		commuted = false;
	}
	else if (match_index_to_operand(rightop, indexcol, index) &&
			 !contain_var_clause(leftop) &&
			 !contain_volatile_functions(leftop))
	{
		/* Might match, but we need a commuted operator */
		expr_op = get_commutator(expr_op);
		if (expr_op == InvalidOid)
			return NULL;
		commuted = true;
	}
	else
		return NULL;

	/*
	 * Is the (commuted) operator an ordering operator for the opfamily? And
	 * if so, does it yield the right sorting semantics?
	 */
	sortfamily = get_op_opfamily_sortfamily(expr_op, opfamily);
	if (sortfamily != pk_opfamily)
		return NULL;

	/* We have a match.  Return clause or a commuted version thereof. */
	if (commuted)
	{
		OpExpr	   *newclause = makeNode(OpExpr);

		/* flat-copy all the fields of clause */
		memcpy(newclause, clause, sizeof(OpExpr));

		/* commute it */
		newclause->opno = expr_op;
		newclause->opfuncid = InvalidOid;
		newclause->args = list_make2(rightop, leftop);

		clause = (Expr *) newclause;
	}

	return clause;
}


/****************************************************************************
 *				----  ROUTINES TO DO PARTIAL INDEX PREDICATE TESTS	----
 ****************************************************************************/

/*
 * check_index_predicates
 *		Set the predicate-derived IndexOptInfo fields for each index
 *		of the specified relation.
 *
 * predOK is set true if the index is partial and its predicate is satisfied
 * for this query, ie the query's WHERE clauses imply the predicate.
 *
 * indrestrictinfo is set to the relation's baserestrictinfo list less any
 * conditions that are implied by the index's predicate.  (Obviously, for a
 * non-partial index, this is the same as baserestrictinfo.)  Such conditions
 * can be dropped from the plan when using the index, in certain cases.
 *
 * At one time it was possible for this to get re-run after adding more
 * restrictions to the rel, thus possibly letting us prove more indexes OK.
 * That doesn't happen any more (at least not in the core code's usage),
 * but this code still supports it in case extensions want to mess with the
 * baserestrictinfo list.  We assume that adding more restrictions can't make
 * an index not predOK.  We must recompute indrestrictinfo each time, though,
 * to make sure any newly-added restrictions get into it if needed.
 */
void
check_index_predicates(PlannerInfo *root, RelOptInfo *rel)
{
	List	   *clauselist;
	bool		have_partial;
	bool		is_target_rel;
	Relids		otherrels;
	ListCell   *lc;

	/* Indexes are available only on base or "other" member relations. */
	Assert(IS_SIMPLE_REL(rel));

	/*
	 * Initialize the indrestrictinfo lists to be identical to
	 * baserestrictinfo, and check whether there are any partial indexes.  If
	 * not, this is all we need to do.
	 */
	have_partial = false;
	foreach(lc, rel->indexlist)
	{
		IndexOptInfo *index = (IndexOptInfo *) lfirst(lc);

		index->indrestrictinfo = rel->baserestrictinfo;
		if (index->indpred)
			have_partial = true;
	}
	if (!have_partial)
		return;

	/*
	 * Construct a list of clauses that we can assume true for the purpose of
	 * proving the index(es) usable.  Restriction clauses for the rel are
	 * always usable, and so are any join clauses that are "movable to" this
	 * rel.  Also, we can consider any EC-derivable join clauses (which must
	 * be "movable to" this rel, by definition).
	 */
	clauselist = list_copy(rel->baserestrictinfo);

	/* Scan the rel's join clauses */
	foreach(lc, rel->joininfo)
	{
		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);

		/* Check if clause can be moved to this rel */
		if (!join_clause_is_movable_to(rinfo, rel))
			continue;

		clauselist = lappend(clauselist, rinfo);
	}

	/*
	 * Add on any equivalence-derivable join clauses.  Computing the correct
	 * relid sets for generate_join_implied_equalities is slightly tricky
	 * because the rel could be a child rel rather than a true baserel, and in
	 * that case we must subtract its parents' relid(s) from all_query_rels.
	 * Additionally, we mustn't consider clauses that are only computable
	 * after outer joins that can null the rel.
	 */
	if (rel->reloptkind == RELOPT_OTHER_MEMBER_REL)
		otherrels = bms_difference(root->all_query_rels,
								   find_childrel_parents(root, rel));
	else
		otherrels = bms_difference(root->all_query_rels, rel->relids);
	otherrels = bms_del_members(otherrels, rel->nulling_relids);

	if (!bms_is_empty(otherrels))
		clauselist =
			list_concat(clauselist,
						generate_join_implied_equalities(root,
														 bms_union(rel->relids,
																   otherrels),
														 otherrels,
														 rel,
														 NULL));

	/*
	 * Normally we remove quals that are implied by a partial index's
	 * predicate from indrestrictinfo, indicating that they need not be
	 * checked explicitly by an indexscan plan using this index.  However, if
	 * the rel is a target relation of UPDATE/DELETE/MERGE/SELECT FOR UPDATE,
	 * we cannot remove such quals from the plan, because they need to be in
	 * the plan so that they will be properly rechecked by EvalPlanQual
	 * testing.  Some day we might want to remove such quals from the main
	 * plan anyway and pass them through to EvalPlanQual via a side channel;
	 * but for now, we just don't remove implied quals at all for target
	 * relations.
	 */
	is_target_rel = (bms_is_member(rel->relid, root->all_result_relids) ||
					 get_plan_rowmark(root->rowMarks, rel->relid) != NULL);

	/*
	 * Now try to prove each index predicate true, and compute the
	 * indrestrictinfo lists for partial indexes.  Note that we compute the
	 * indrestrictinfo list even for non-predOK indexes; this might seem
	 * wasteful, but we may be able to use such indexes in OR clauses, cf
	 * generate_bitmap_or_paths().
	 */
	foreach(lc, rel->indexlist)
	{
		IndexOptInfo *index = (IndexOptInfo *) lfirst(lc);
		ListCell   *lcr;

		if (index->indpredExpand == NIL)
			continue;			/* ignore non-partial indexes here */

		if (!index->predOK)		/* don't repeat work if already proven OK */
			index->predOK = predicate_implied_by(index->indpredExpand, clauselist,
												 false);

		/* If rel is an update target, leave indrestrictinfo as set above */
		if (is_target_rel)
			continue;

		/*
		 * If index is !amoptionalkey, also leave indrestrictinfo as set
		 * above.  Otherwise we risk removing all quals for the first index
		 * key and then not being able to generate an indexscan at all.  It
		 * would be better to be more selective, but we've not yet identified
		 * which if any of the quals match the first index key.
		 */
		if (!index->amoptionalkey)
			continue;

		/* Else compute indrestrictinfo as the non-implied quals */
		index->indrestrictinfo = NIL;
		foreach(lcr, rel->baserestrictinfo)
		{
			RestrictInfo *rinfo = (RestrictInfo *) lfirst(lcr);

			/* predicate_implied_by() assumes first arg is immutable */
			if (contain_mutable_functions((Node *) rinfo->clause) ||
				!predicate_implied_by(list_make1(rinfo->clause),
									  index->indpredExpand, false))
				index->indrestrictinfo = lappend(index->indrestrictinfo, rinfo);
		}
	}
}

/****************************************************************************
 *				----  ROUTINES TO CHECK EXTERNALLY-VISIBLE CONDITIONS  ----
 ****************************************************************************/

/*
 * ec_member_matches_indexcol
 *	  Test whether an EquivalenceClass member matches an index column.
 *
 * This is a callback for use by generate_implied_equalities_for_column.
 */
static bool
ec_member_matches_indexcol(PlannerInfo *root, RelOptInfo *rel,
						   EquivalenceClass *ec, EquivalenceMember *em,
						   void *arg)
{
	IndexOptInfo *index = ((ec_member_matches_arg *) arg)->index;
	int			indexcol = ((ec_member_matches_arg *) arg)->indexcol;
	Oid			curFamily;
	Oid			curCollation;

	Assert(indexcol < index->nkeycolumns);

	curFamily = index->opfamily[indexcol];
	curCollation = index->indexcollations[indexcol];

	/*
	 * If it's a btree index, we can reject it if its opfamily isn't
	 * compatible with the EC, since no clause generated from the EC could be
	 * used with the index.  For non-btree indexes, we can't easily tell
	 * whether clauses generated from the EC could be used with the index, so
	 * don't check the opfamily.  This might mean we return "true" for a
	 * useless EC, so we have to recheck the results of
	 * generate_implied_equalities_for_column; see
	 * match_eclass_clauses_to_index.
	 */
	if (index->relam == BTREE_AM_OID &&
		!list_member_oid(ec->ec_opfamilies, curFamily))
		return false;

	/* We insist on collation match for all index types, though */
	if (!IndexCollMatchesExprColl(curCollation, ec->ec_collation))
		return false;

	return match_index_to_operand((Node *) em->em_expr, indexcol, index);
}

/*
 * relation_has_unique_index_for
 *	  Determine whether the relation provably has at most one row satisfying
 *	  a set of equality conditions, because the conditions constrain all
 *	  columns of some unique index.
 *
 * The conditions are provided as a list of RestrictInfo nodes, where the
 * caller has already determined that each condition is a mergejoinable
 * equality with an expression in this relation on one side, and an
 * expression not involving this relation on the other.  The transient
 * outer_is_left flag is used to identify which side we should look at:
 * left side if outer_is_left is false, right side if it is true.
 *
 * The caller need only supply equality conditions arising from joins;
 * this routine automatically adds in any usable baserestrictinfo clauses.
 * (Note that the passed-in restrictlist will be destructively modified!)
 *
 * If extra_clauses isn't NULL, return baserestrictinfo clauses which were used
 * to derive uniqueness.
 */
bool
relation_has_unique_index_for(PlannerInfo *root, RelOptInfo *rel,
							  List *restrictlist, List **extra_clauses)
{
	ListCell   *ic;

	/* Short-circuit if no indexes... */
	if (rel->indexlist == NIL)
		return false;

	/*
	 * Examine the rel's restriction clauses for usable var = const clauses
	 * that we can add to the restrictlist.
	 */
	foreach(ic, rel->baserestrictinfo)
	{
		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(ic);

		/*
		 * Note: can_join won't be set for a restriction clause, but
		 * mergeopfamilies will be if it has a mergejoinable operator and
		 * doesn't contain volatile functions.
		 */
		if (restrictinfo->mergeopfamilies == NIL)
			continue;			/* not mergejoinable */

		/*
		 * The clause certainly doesn't refer to anything but the given rel.
		 * If either side is pseudoconstant then we can use it.
		 */
		if (bms_is_empty(restrictinfo->left_relids))
		{
			/* righthand side is inner */
			restrictinfo->outer_is_left = true;
		}
		else if (bms_is_empty(restrictinfo->right_relids))
		{
			/* lefthand side is inner */
			restrictinfo->outer_is_left = false;
		}
		else
			continue;

		/* OK, add to list */
		restrictlist = lappend(restrictlist, restrictinfo);
	}

	/* Short-circuit the easy case */
	if (restrictlist == NIL)
		return false;

	/* Examine each index of the relation ... */
	foreach(ic, rel->indexlist)
	{
		IndexOptInfo *ind = (IndexOptInfo *) lfirst(ic);
		int			c;
		List	   *exprs = NIL;

		/*
		 * If the index is not unique, or not immediately enforced, or if it's
		 * a partial index, it's useless here.  We're unable to make use of
		 * predOK partial unique indexes due to the fact that
		 * check_index_predicates() also makes use of join predicates to
		 * determine if the partial index is usable. Here we need proofs that
		 * hold true before any joins are evaluated.
		 */
		if (!ind->unique || !ind->immediate || ind->indpred != NIL)
			continue;

		/*
		 * Try to find each index column in the list of conditions.  This is
		 * O(N^2) or worse, but we expect all the lists to be short.
		 */
		for (c = 0; c < ind->nkeycolumns; c++)
		{
			ListCell   *lc;

			foreach(lc, restrictlist)
			{
				RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
				Node	   *rexpr;

				/*
				 * The condition's equality operator must be a member of the
				 * index opfamily, else it is not asserting the right kind of
				 * equality behavior for this index.  We check this first
				 * since it's probably the cheapest test.
				 */
				if (!list_member_oid(rinfo->mergeopfamilies, ind->opfamily[c]))
					continue;

				/*
				 * The index's collation must agree with the clause's input
				 * collation on equality, else the index's uniqueness does not
				 * imply uniqueness under the clause's equality semantics.
				 */
				if (!collations_agree_on_equality(ind->indexcollations[c],
												  exprInputCollation((Node *) rinfo->clause)))
					continue;

				/* OK, see if the condition operand matches the index key */
				if (rinfo->outer_is_left)
					rexpr = get_rightop(rinfo->clause);
				else
					rexpr = get_leftop(rinfo->clause);

				if (match_index_to_operand(rexpr, c, ind))
				{
					if (bms_membership(rinfo->clause_relids) == BMS_SINGLETON)
					{
						MemoryContext oldMemCtx =
							MemoryContextSwitchTo(root->planner_cxt);

						/*
						 * Add filter clause into a list allowing caller to
						 * know if uniqueness have made not only by join
						 * clauses.
						 */
						Assert(bms_is_empty(rinfo->left_relids) ||
							   bms_is_empty(rinfo->right_relids));
						if (extra_clauses)
							exprs = lappend(exprs, rinfo);
						MemoryContextSwitchTo(oldMemCtx);
					}

					break;		/* found a match; column is unique */
				}
			}

			if (lc == NULL)
				break;			/* no match; this index doesn't help us */
		}

		/* Matched all key columns of this index? */
		if (c == ind->nkeycolumns)
		{
			if (extra_clauses)
				*extra_clauses = exprs;
			return true;
		}
	}

	return false;
}

/*
 * indexcol_is_bool_constant_for_query
 *
 * If an index column is constrained to have a constant value by the query's
 * WHERE conditions, then it's irrelevant for sort-order considerations.
 * Usually that means we have a restriction clause WHERE indexcol = constant,
 * which gets turned into an EquivalenceClass containing a constant, which
 * is recognized as redundant by build_index_pathkeys().  But if the index
 * column is a boolean variable (or expression), then we are not going to
 * see WHERE indexcol = constant, because expression preprocessing will have
 * simplified that to "WHERE indexcol" or "WHERE NOT indexcol".  So we are not
 * going to have a matching EquivalenceClass (unless the query also contains
 * "ORDER BY indexcol").  To allow such cases to work the same as they would
 * for non-boolean values, this function is provided to detect whether the
 * specified index column matches a boolean restriction clause.
 */
bool
indexcol_is_bool_constant_for_query(PlannerInfo *root,
									IndexOptInfo *index,
									int indexcol)
{
	ListCell   *lc;

	/* If the index isn't boolean, we can't possibly get a match */
	if (!IsBooleanOpfamily(index->opfamily[indexcol]))
		return false;

	/* Check each restriction clause for the index's rel */
	foreach(lc, index->rel->baserestrictinfo)
	{
		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);

		/*
		 * As in match_clause_to_indexcol, never match pseudoconstants to
		 * indexes.  (It might be semantically okay to do so here, but the
		 * odds of getting a match are negligible, so don't waste the cycles.)
		 */
		if (rinfo->pseudoconstant)
			continue;

		/* See if we can match the clause's expression to the index column */
		if (match_boolean_index_clause(root, rinfo, indexcol, index))
			return true;
	}

	return false;
}


/****************************************************************************
 *				----  ROUTINES TO CHECK OPERANDS  ----
 ****************************************************************************/

/*
 * match_index_to_operand()
 *	  Generalized test for a match between an index's key
 *	  and the operand on one side of a restriction or join clause.
 *
 * operand: the nodetree to be compared to the index
 * indexcol: the column number of the index (counting from 0)
 * index: the index of interest
 *
 * Note that we aren't interested in collations here; the caller must check
 * for a collation match, if it's dealing with an operator where that matters.
 *
 * This is exported for use in selfuncs.c.
 */
bool
match_index_to_operand(Node *operand,
					   int indexcol,
					   IndexOptInfo *index)
{
	int			indkey;

	/*
	 * Ignore any PlaceHolderVar node contained in the operand.  This is
	 * needed to be able to apply indexscanning in cases where the operand (or
	 * a subtree) has been wrapped in PlaceHolderVars to enforce separate
	 * identity or as a result of outer joins.
	 */
	operand = strip_noop_phvs(operand);

	/*
	 * Ignore any RelabelType node above the operand.  This is needed to be
	 * able to apply indexscanning in binary-compatible-operator cases.
	 *
	 * Note: we must handle nested RelabelType nodes here.  While
	 * eval_const_expressions() will have simplified them to at most one
	 * layer, our prior stripping of PlaceHolderVars may have brought separate
	 * RelabelTypes into adjacency.
	 */
	while (operand && IsA(operand, RelabelType))
		operand = (Node *) ((RelabelType *) operand)->arg;

	indkey = index->indexkeys[indexcol];
	if (indkey != 0)
	{
		/*
		 * Simple index column; operand must be a matching Var.
		 */
		if (operand && IsA(operand, Var) &&
			index->rel->relid == ((Var *) operand)->varno &&
			indkey == ((Var *) operand)->varattno &&
			((Var *) operand)->varnullingrels == NULL)
			return true;
	}
	else
	{
		/*
		 * Index expression; find the correct expression.  (This search could
		 * be avoided, at the cost of complicating all the callers of this
		 * routine; doesn't seem worth it.)
		 */
		ListCell   *indexpr_item;
		int			i;
		Node	   *indexkey;

		indexpr_item = list_head(index->indexprsExpand);
		for (i = 0; i < indexcol; i++)
		{
			if (index->indexkeys[i] == 0)
			{
				if (indexpr_item == NULL)
					elog(ERROR, "wrong number of index expressions");
				indexpr_item = lnext(index->indexprsExpand, indexpr_item);
			}
		}
		if (indexpr_item == NULL)
			elog(ERROR, "wrong number of index expressions");
		indexkey = (Node *) lfirst(indexpr_item);

		/*
		 * Does it match the operand?  Again, strip any relabeling.
		 */
		if (indexkey && IsA(indexkey, RelabelType))
			indexkey = (Node *) ((RelabelType *) indexkey)->arg;

		if (equal(indexkey, operand))
			return true;
	}

	return false;
}

/*
 * is_pseudo_constant_for_index()
 *	  Test whether the given expression can be used as an indexscan
 *	  comparison value.
 *
 * An indexscan comparison value must not contain any volatile functions,
 * and it can't contain any Vars of the index's own table.  Vars of
 * other tables are okay, though; in that case we'd be producing an
 * indexqual usable in a parameterized indexscan.  This is, therefore,
 * a weaker condition than is_pseudo_constant_clause().
 *
 * This function is exported for use by planner support functions,
 * which will have available the IndexOptInfo, but not any RestrictInfo
 * infrastructure.  It is making the same test made by functions above
 * such as match_opclause_to_indexcol(), but those rely where possible
 * on RestrictInfo information about variable membership.
 *
 * expr: the nodetree to be checked
 * index: the index of interest
 */
bool
is_pseudo_constant_for_index(PlannerInfo *root, Node *expr, IndexOptInfo *index)
{
	/* pull_varnos is cheaper than volatility check, so do that first */
	if (bms_is_member(index->rel->relid, pull_varnos(root, expr)))
		return false;			/* no good, contains Var of table */
	if (contain_volatile_functions(expr))
		return false;			/* no good, volatile comparison value */
	return true;
}
./makefuncs.c0000664000175000017500000005701715221615341012037 0ustar  xmanxman/*-------------------------------------------------------------------------
 *
 * makefuncs.c
 *	  creator functions for various nodes. The functions here are for the
 *	  most frequently created nodes.
 *
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/nodes/makefuncs.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "catalog/pg_class.h"
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "utils/lsyscache.h"


/*
 * makeA_Expr -
 *		makes an A_Expr node
 */
A_Expr *
makeA_Expr(A_Expr_Kind kind, List *name,
		   Node *lexpr, Node *rexpr, int location)
{
	A_Expr	   *a = makeNode(A_Expr);

	a->kind = kind;
	a->name = name;
	a->lexpr = lexpr;
	a->rexpr = rexpr;
	a->location = location;
	return a;
}

/*
 * makeSimpleA_Expr -
 *		As above, given a simple (unqualified) operator name
 */
A_Expr *
makeSimpleA_Expr(A_Expr_Kind kind, char *name,
				 Node *lexpr, Node *rexpr, int location)
{
	A_Expr	   *a = makeNode(A_Expr);

	a->kind = kind;
	a->name = list_make1(makeString(name));
	a->lexpr = lexpr;
	a->rexpr = rexpr;
	a->location = location;
	return a;
}

/*
 * makeVar -
 *	  creates a Var node
 */
Var *
makeVar(int varno,
		AttrNumber varattno,
		Oid vartype,
		int32 vartypmod,
		Oid varcollid,
		Index varlevelsup)
{
	Var		   *var = makeNode(Var);

	var->varno = varno;
	var->varattno = varattno;
	var->vartype = vartype;
	var->vartypmod = vartypmod;
	var->varcollid = varcollid;
	var->varlevelsup = varlevelsup;

	/*
	 * Only a few callers need to make Var nodes with varreturningtype
	 * different from VAR_RETURNING_DEFAULT, non-null varnullingrels, or with
	 * varnosyn/varattnosyn different from varno/varattno.  We don't provide
	 * separate arguments for them, but just initialize them to sensible
	 * default values.  This reduces code clutter and chance of error for most
	 * callers.
	 */
	var->varreturningtype = VAR_RETURNING_DEFAULT;
	var->varnullingrels = NULL;
	var->varnosyn = (Index) varno;
	var->varattnosyn = varattno;

	/* Likewise, we just set location to "unknown" here */
	var->location = -1;

	return var;
}

/*
 * makeVarFromTargetEntry -
 *		convenience function to create a same-level Var node from a
 *		TargetEntry
 */
Var *
makeVarFromTargetEntry(int varno,
					   TargetEntry *tle)
{
	return makeVar(varno,
				   tle->resno,
				   exprType((Node *) tle->expr),
				   exprTypmod((Node *) tle->expr),
				   exprCollation((Node *) tle->expr),
				   0);
}

/*
 * makeWholeRowVar -
 *	  creates a Var node representing a whole row of the specified RTE
 *
 * A whole-row reference is a Var with varno set to the correct range
 * table entry, and varattno == 0 to signal that it references the whole
 * tuple.  (Use of zero here is unclean, since it could easily be confused
 * with error cases, but it's not worth changing now.)  The vartype indicates
 * a rowtype; either a named composite type, or a domain over a named
 * composite type (only possible if the RTE is a function returning that),
 * or RECORD.  This function encapsulates the logic for determining the
 * correct rowtype OID to use.
 *
 * If allowScalar is true, then for the case where the RTE is a single function
 * returning a non-composite result type, we produce a normal Var referencing
 * the function's result directly, instead of the single-column composite
 * value that the whole-row notation might otherwise suggest.
 */
Var *
makeWholeRowVar(RangeTblEntry *rte,
				int varno,
				Index varlevelsup,
				bool allowScalar)
{
	Var		   *result;
	Oid			toid;
	Node	   *fexpr;

	switch (rte->rtekind)
	{
		case RTE_RELATION:
			/* relation: the rowtype is a named composite type */
			toid = get_rel_type_id(rte->relid);
			if (!OidIsValid(toid))
				ereport(ERROR,
						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
						 errmsg("relation \"%s\" does not have a composite type",
								get_rel_name(rte->relid))));
			result = makeVar(varno,
							 InvalidAttrNumber,
							 toid,
							 -1,
							 InvalidOid,
							 varlevelsup);
			break;

		case RTE_SUBQUERY:

			/*
			 * For a standard subquery, the Var should be of RECORD type.
			 * However, if we're looking at a subquery that was expanded from
			 * a view or SRF (only possible during planning), we must use the
			 * appropriate rowtype, so that the resulting Var has the same
			 * type that we would have produced from the original RTE.
			 */
			if (OidIsValid(rte->relid))
			{
				/* Subquery was expanded from a view */
				toid = get_rel_type_id(rte->relid);
				if (!OidIsValid(toid))
					ereport(ERROR,
							(errcode(ERRCODE_WRONG_OBJECT_TYPE),
							 errmsg("relation \"%s\" does not have a composite type",
									get_rel_name(rte->relid))));
			}
			else if (rte->functions)
			{
				/*
				 * Subquery was expanded from a set-returning function.  That
				 * would not have happened if there's more than one function
				 * or ordinality was requested.  We also needn't worry about
				 * the allowScalar case, since the planner doesn't use that.
				 * Otherwise this must match the RTE_FUNCTION code below.
				 */
				Assert(!allowScalar);
				fexpr = ((RangeTblFunction *) linitial(rte->functions))->funcexpr;
				toid = exprType(fexpr);
				if (!type_is_rowtype(toid))
					toid = RECORDOID;
			}
			else
			{
				/* Normal subquery-in-FROM */
				toid = RECORDOID;
			}
			result = makeVar(varno,
							 InvalidAttrNumber,
							 toid,
							 -1,
							 InvalidOid,
							 varlevelsup);
			break;

		case RTE_FUNCTION:

			/*
			 * If there's more than one function, or ordinality is requested,
			 * force a RECORD result, since there's certainly more than one
			 * column involved and it can't be a known named type.
			 */
			if (rte->funcordinality || list_length(rte->functions) != 1)
			{
				/* always produces an anonymous RECORD result */
				result = makeVar(varno,
								 InvalidAttrNumber,
								 RECORDOID,
								 -1,
								 InvalidOid,
								 varlevelsup);
				break;
			}

			fexpr = ((RangeTblFunction *) linitial(rte->functions))->funcexpr;
			toid = exprType(fexpr);
			if (type_is_rowtype(toid))
			{
				/* func returns composite; same as relation case */
				result = makeVar(varno,
								 InvalidAttrNumber,
								 toid,
								 -1,
								 InvalidOid,
								 varlevelsup);
			}
			else if (allowScalar)
			{
				/* func returns scalar; just return its output as-is */
				result = makeVar(varno,
								 1,
								 toid,
								 -1,
								 exprCollation(fexpr),
								 varlevelsup);
			}
			else
			{
				/* func returns scalar, but we want a composite result */
				result = makeVar(varno,
								 InvalidAttrNumber,
								 RECORDOID,
								 -1,
								 InvalidOid,
								 varlevelsup);
			}
			break;

		default:

			/*
			 * RTE is a join, tablefunc, VALUES, CTE, etc.  We represent these
			 * cases as a whole-row Var of RECORD type.  (Note that in most
			 * cases the Var will be expanded to a RowExpr during planning,
			 * but that is not our concern here.)
			 */
			result = makeVar(varno,
							 InvalidAttrNumber,
							 RECORDOID,
							 -1,
							 InvalidOid,
							 varlevelsup);
			break;
	}

	return result;
}

/*
 * makeTargetEntry -
 *	  creates a TargetEntry node
 */
TargetEntry *
makeTargetEntry(Expr *expr,
				AttrNumber resno,
				char *resname,
				bool resjunk)
{
	TargetEntry *tle = makeNode(TargetEntry);

	tle->expr = expr;
	tle->resno = resno;
	tle->resname = resname;

	/*
	 * We always set these fields to 0. If the caller wants to change them he
	 * must do so explicitly.  Few callers do that, so omitting these
	 * arguments reduces the chance of error.
	 */
	tle->ressortgroupref = 0;
	tle->resorigtbl = InvalidOid;
	tle->resorigcol = 0;

	tle->resjunk = resjunk;

	return tle;
}

/*
 * flatCopyTargetEntry -
 *	  duplicate a TargetEntry, but don't copy substructure
 *
 * This is commonly used when we just want to modify the resno or substitute
 * a new expression.
 */
TargetEntry *
flatCopyTargetEntry(TargetEntry *src_tle)
{
	TargetEntry *tle = makeNode(TargetEntry);

	Assert(IsA(src_tle, TargetEntry));
	memcpy(tle, src_tle, sizeof(TargetEntry));
	return tle;
}

/*
 * makeFromExpr -
 *	  creates a FromExpr node
 */
FromExpr *
makeFromExpr(List *fromlist, Node *quals)
{
	FromExpr   *f = makeNode(FromExpr);

	f->fromlist = fromlist;
	f->quals = quals;
	return f;
}

/*
 * makeConst -
 *	  creates a Const node
 */
Const *
makeConst(Oid consttype,
		  int32 consttypmod,
		  Oid constcollid,
		  int constlen,
		  Datum constvalue,
		  bool constisnull,
		  bool constbyval)
{
	Const	   *cnst = makeNode(Const);

	/*
	 * If it's a varlena value, force it to be in non-expanded (non-toasted)
	 * format; this avoids any possible dependency on external values and
	 * improves consistency of representation, which is important for equal().
	 */
	if (!constisnull && constlen == -1)
		constvalue = PointerGetDatum(PG_DETOAST_DATUM(constvalue));

	cnst->consttype = consttype;
	cnst->consttypmod = consttypmod;
	cnst->constcollid = constcollid;
	cnst->constlen = constlen;
	cnst->constvalue = constvalue;
	cnst->constisnull = constisnull;
	cnst->constbyval = constbyval;
	cnst->location = -1;		/* "unknown" */

	return cnst;
}

/*
 * makeNullConst -
 *	  creates a Const node representing a NULL of the specified type/typmod
 *
 * This is a convenience routine that just saves a lookup of the type's
 * storage properties.
 */
Const *
makeNullConst(Oid consttype, int32 consttypmod, Oid constcollid)
{
	int16		typLen;
	bool		typByVal;

	get_typlenbyval(consttype, &typLen, &typByVal);
	return makeConst(consttype,
					 consttypmod,
					 constcollid,
					 (int) typLen,
					 (Datum) 0,
					 true,
					 typByVal);
}

/*
 * makeBoolConst -
 *	  creates a Const node representing a boolean value (can be NULL too)
 */
Node *
makeBoolConst(bool value, bool isnull)
{
	/* note that pg_type.h hardwires size of bool as 1 ... duplicate it */
	return (Node *) makeConst(BOOLOID, -1, InvalidOid, 1,
							  BoolGetDatum(value), isnull, true);
}

/*
 * makeBoolExpr -
 *	  creates a BoolExpr node
 */
Expr *
makeBoolExpr(BoolExprType boolop, List *args, int location)
{
	BoolExpr   *b = makeNode(BoolExpr);

	b->boolop = boolop;
	b->args = args;
	b->location = location;

	return (Expr *) b;
}

/*
 * makeAlias -
 *	  creates an Alias node
 *
 * NOTE: the given name is copied, but the colnames list (if any) isn't.
 */
Alias *
makeAlias(const char *aliasname, List *colnames)
{
	Alias	   *a = makeNode(Alias);

	a->aliasname = pstrdup(aliasname);
	a->colnames = colnames;

	return a;
}

/*
 * makeRelabelType -
 *	  creates a RelabelType node
 */
RelabelType *
makeRelabelType(Expr *arg, Oid rtype, int32 rtypmod, Oid rcollid,
				CoercionForm rformat)
{
	RelabelType *r = makeNode(RelabelType);

	r->arg = arg;
	r->resulttype = rtype;
	r->resulttypmod = rtypmod;
	r->resultcollid = rcollid;
	r->relabelformat = rformat;
	r->location = -1;

	return r;
}

/*
 * makeRangeVar -
 *	  creates a RangeVar node (rather oversimplified case)
 */
RangeVar *
makeRangeVar(char *schemaname, char *relname, int location)
{
	RangeVar   *r = makeNode(RangeVar);

	r->catalogname = NULL;
	r->schemaname = schemaname;
	r->relname = relname;
	r->inh = true;
	r->relpersistence = RELPERSISTENCE_PERMANENT;
	r->alias = NULL;
	r->location = location;

	return r;
}

/*
 * makeNotNullConstraint -
 *		creates a Constraint node for NOT NULL constraints
 */
Constraint *
makeNotNullConstraint(String *colname)
{
	Constraint *notnull;

	notnull = makeNode(Constraint);
	notnull->contype = CONSTR_NOTNULL;
	notnull->conname = NULL;
	notnull->is_no_inherit = false;
	notnull->deferrable = false;
	notnull->initdeferred = false;
	notnull->location = -1;
	notnull->keys = list_make1(colname);
	notnull->is_enforced = true;
	notnull->skip_validation = false;
	notnull->initially_valid = true;

	return notnull;
}

/*
 * makeTypeName -
 *	build a TypeName node for an unqualified name.
 *
 * typmod is defaulted, but can be changed later by caller.
 */
TypeName *
makeTypeName(char *typnam)
{
	return makeTypeNameFromNameList(list_make1(makeString(typnam)));
}

/*
 * makeTypeNameFromNameList -
 *	build a TypeName node for a String list representing a qualified name.
 *
 * typmod is defaulted, but can be changed later by caller.
 */
TypeName *
makeTypeNameFromNameList(List *names)
{
	TypeName   *n = makeNode(TypeName);

	n->names = names;
	n->typmods = NIL;
	n->typemod = -1;
	n->location = -1;
	return n;
}

/*
 * makeTypeNameFromOid -
 *	build a TypeName node to represent a type already known by OID/typmod.
 */
TypeName *
makeTypeNameFromOid(Oid typeOid, int32 typmod)
{
	TypeName   *n = makeNode(TypeName);

	n->typeOid = typeOid;
	n->typemod = typmod;
	n->location = -1;
	return n;
}

/*
 * makeColumnDef -
 *	build a ColumnDef node to represent a simple column definition.
 *
 * Type and collation are specified by OID.
 * Other properties are all basic to start with.
 */
ColumnDef *
makeColumnDef(const char *colname, Oid typeOid, int32 typmod, Oid collOid)
{
	ColumnDef  *n = makeNode(ColumnDef);

	n->colname = pstrdup(colname);
	n->typeName = makeTypeNameFromOid(typeOid, typmod);
	n->inhcount = 0;
	n->is_local = true;
	n->is_not_null = false;
	n->is_from_type = false;
	n->storage = 0;
	n->raw_default = NULL;
	n->cooked_default = NULL;
	n->collClause = NULL;
	n->collOid = collOid;
	n->constraints = NIL;
	n->fdwoptions = NIL;
	n->location = -1;

	return n;
}

/*
 * makeFuncExpr -
 *	build an expression tree representing a function call.
 *
 * The argument expressions must have been transformed already.
 */
FuncExpr *
makeFuncExpr(Oid funcid, Oid rettype, List *args,
			 Oid funccollid, Oid inputcollid, CoercionForm fformat)
{
	FuncExpr   *funcexpr;

	funcexpr = makeNode(FuncExpr);
	funcexpr->funcid = funcid;
	funcexpr->funcresulttype = rettype;
	funcexpr->funcretset = false;	/* only allowed case here */
	funcexpr->funcvariadic = false; /* only allowed case here */
	funcexpr->funcformat = fformat;
	funcexpr->funccollid = funccollid;
	funcexpr->inputcollid = inputcollid;
	funcexpr->args = args;
	funcexpr->location = -1;

	return funcexpr;
}

/*
 * makeStringConst -
 * 	build a A_Const node of type T_String for given string
 */
Node *
makeStringConst(char *str, int location)
{
	A_Const    *n = makeNode(A_Const);

	n->val.sval.type = T_String;
	n->val.sval.sval = str;
	n->location = location;

	return (Node *) n;
}

/*
 * makeDefElem -
 *	build a DefElem node
 *
 * This is sufficient for the "typical" case with an unqualified option name
 * and no special action.
 */
DefElem *
makeDefElem(char *name, Node *arg, int location)
{
	DefElem    *res = makeNode(DefElem);

	res->defnamespace = NULL;
	res->defname = name;
	res->arg = arg;
	res->defaction = DEFELEM_UNSPEC;
	res->location = location;

	return res;
}

/*
 * makeDefElemExtended -
 *	build a DefElem node with all fields available to be specified
 */
DefElem *
makeDefElemExtended(char *nameSpace, char *name, Node *arg,
					DefElemAction defaction, int location)
{
	DefElem    *res = makeNode(DefElem);

	res->defnamespace = nameSpace;
	res->defname = name;
	res->arg = arg;
	res->defaction = defaction;
	res->location = location;

	return res;
}

/*
 * makeFuncCall -
 *
 * Initialize a FuncCall struct with the information every caller must
 * supply.  Any non-default parameters have to be inserted by the caller.
 */
FuncCall *
makeFuncCall(List *name, List *args, CoercionForm funcformat, int location)
{
	FuncCall   *n = makeNode(FuncCall);

	n->funcname = name;
	n->args = args;
	n->agg_order = NIL;
	n->agg_filter = NULL;
	n->over = NULL;
	n->agg_within_group = false;
	n->agg_star = false;
	n->agg_distinct = false;
	n->func_variadic = false;
	n->funcformat = funcformat;
	n->location = location;
	return n;
}

/*
 * make_opclause
 *	  Creates an operator clause given its operator info, left operand
 *	  and right operand (pass NULL to create single-operand clause),
 *	  and collation info.
 */
Expr *
make_opclause(Oid opno, Oid opresulttype, bool opretset,
			  Expr *leftop, Expr *rightop,
			  Oid opcollid, Oid inputcollid)
{
	OpExpr	   *expr = makeNode(OpExpr);

	expr->opno = opno;
	expr->opfuncid = InvalidOid;
	expr->opresulttype = opresulttype;
	expr->opretset = opretset;
	expr->opcollid = opcollid;
	expr->inputcollid = inputcollid;
	if (rightop)
		expr->args = list_make2(leftop, rightop);
	else
		expr->args = list_make1(leftop);
	expr->location = -1;
	return (Expr *) expr;
}

/*
 * make_andclause
 *
 * Creates an 'and' clause given a list of its subclauses.
 */
Expr *
make_andclause(List *andclauses)
{
	BoolExpr   *expr = makeNode(BoolExpr);

	expr->boolop = AND_EXPR;
	expr->args = andclauses;
	expr->location = -1;
	return (Expr *) expr;
}

/*
 * make_orclause
 *
 * Creates an 'or' clause given a list of its subclauses.
 */
Expr *
make_orclause(List *orclauses)
{
	BoolExpr   *expr = makeNode(BoolExpr);

	expr->boolop = OR_EXPR;
	expr->args = orclauses;
	expr->location = -1;
	return (Expr *) expr;
}

/*
 * make_notclause
 *
 * Create a 'not' clause given the expression to be negated.
 */
Expr *
make_notclause(Expr *notclause)
{
	BoolExpr   *expr = makeNode(BoolExpr);

	expr->boolop = NOT_EXPR;
	expr->args = list_make1(notclause);
	expr->location = -1;
	return (Expr *) expr;
}

/*
 * make_and_qual
 *
 * Variant of make_andclause for ANDing two qual conditions together.
 * Qual conditions have the property that a NULL nodetree is interpreted
 * as 'true'.
 *
 * NB: this makes no attempt to preserve AND/OR flatness; so it should not
 * be used on a qual that has already been run through prepqual.c.
 */
Node *
make_and_qual(Node *qual1, Node *qual2)
{
	if (qual1 == NULL)
		return qual2;
	if (qual2 == NULL)
		return qual1;
	return (Node *) make_andclause(list_make2(qual1, qual2));
}

/*
 * The planner and executor usually represent qualification expressions
 * as lists of boolean expressions with implicit AND semantics.
 *
 * These functions convert between an AND-semantics expression list and the
 * ordinary representation of a boolean expression.
 *
 * Note that an empty list is considered equivalent to TRUE.
 */
Expr *
make_ands_explicit(List *andclauses)
{
	if (andclauses == NIL)
		return (Expr *) makeBoolConst(true, false);
	else if (list_length(andclauses) == 1)
		return (Expr *) linitial(andclauses);
	else
		return make_andclause(andclauses);
}

List *
make_ands_implicit(Expr *clause)
{
	/*
	 * NB: because the parser sets the qual field to NULL in a query that has
	 * no WHERE clause, we must consider a NULL input clause as TRUE, even
	 * though one might more reasonably think it FALSE.
	 */
	if (clause == NULL)
		return NIL;				/* NULL -> NIL list == TRUE */
	else if (is_andclause(clause))
		return ((BoolExpr *) clause)->args;
	else if (IsA(clause, Const) &&
			 !((Const *) clause)->constisnull &&
			 DatumGetBool(((Const *) clause)->constvalue))
		return NIL;				/* constant TRUE input -> NIL list */
	else
		return list_make1(clause);
}

/*
 * makeIndexInfo
 *	  create an IndexInfo node.  Upon returning from this function,
 * callers must apply ExpandVirtualGeneratedColumns() 
 * or RelationGetIndexExpressionsExpand or RelationGetIndexPredicateExpand() to
 * ii_ExpressionsExpand and ii_PredicateExpand as needed for actual
 * expansion when they are not NIL or dummy.
 */
IndexInfo *
makeIndexInfo(int numattrs, int numkeyattrs, Oid amoid, List *expressions,
			  List *predicates, bool unique, bool nulls_not_distinct,
			  bool isready, bool concurrent, bool summarizing,
			  bool withoutoverlaps)
{
	IndexInfo  *n = makeNode(IndexInfo);

	n->ii_NumIndexAttrs = numattrs;
	n->ii_NumIndexKeyAttrs = numkeyattrs;
	Assert(n->ii_NumIndexKeyAttrs != 0);
	Assert(n->ii_NumIndexKeyAttrs <= n->ii_NumIndexAttrs);
	n->ii_Unique = unique;
	n->ii_NullsNotDistinct = nulls_not_distinct;
	n->ii_ReadyForInserts = isready;
	n->ii_CheckedUnchanged = false;
	n->ii_IndexUnchanged = false;
	n->ii_Concurrent = concurrent;
	n->ii_Summarizing = summarizing;
	n->ii_WithoutOverlaps = withoutoverlaps;

	/* summarizing indexes cannot contain non-key attributes */
	Assert(!summarizing || (numkeyattrs == numattrs));

	/* expressions */
	n->ii_Expressions = expressions;
	n->ii_ExpressionsExpand = copyObject(expressions);
	n->ii_ExpressionsState = NIL;
	n->ii_ExpressionsExpandState = NIL;

	/* predicates  */
	n->ii_Predicate = predicates;
	n->ii_PredicateExpand = copyObject(predicates);
	n->ii_PredicateState = NULL;
	n->ii_PredicateExpandState = NULL;

	/* exclusion constraints */
	n->ii_ExclusionOps = NULL;
	n->ii_ExclusionProcs = NULL;
	n->ii_ExclusionStrats = NULL;

	/* speculative inserts */
	n->ii_UniqueOps = NULL;
	n->ii_UniqueProcs = NULL;
	n->ii_UniqueStrats = NULL;

	/* initialize index-build state to default */
	n->ii_BrokenHotChain = false;
	n->ii_ParallelWorkers = 0;

	/* set up for possible use by index AM */
	n->ii_Am = amoid;
	n->ii_AmCache = NULL;
	n->ii_Context = CurrentMemoryContext;

	return n;
}

/*
 * makeGroupingSet
 *
 */
GroupingSet *
makeGroupingSet(GroupingSetKind kind, List *content, int location)
{
	GroupingSet *n = makeNode(GroupingSet);

	n->kind = kind;
	n->content = content;
	n->location = location;
	return n;
}

/*
 * makeVacuumRelation -
 *	  create a VacuumRelation node
 */
VacuumRelation *
makeVacuumRelation(RangeVar *relation, Oid oid, List *va_cols)
{
	VacuumRelation *v = makeNode(VacuumRelation);

	v->relation = relation;
	v->oid = oid;
	v->va_cols = va_cols;
	return v;
}

/*
 * makeJsonFormat -
 *	  creates a JsonFormat node
 */
JsonFormat *
makeJsonFormat(JsonFormatType type, JsonEncoding encoding, int location)
{
	JsonFormat *jf = makeNode(JsonFormat);

	jf->format_type = type;
	jf->encoding = encoding;
	jf->location = location;

	return jf;
}

/*
 * makeJsonValueExpr -
 *	  creates a JsonValueExpr node
 */
JsonValueExpr *
makeJsonValueExpr(Expr *raw_expr, Expr *formatted_expr,
				  JsonFormat *format)
{
	JsonValueExpr *jve = makeNode(JsonValueExpr);

	jve->raw_expr = raw_expr;
	jve->formatted_expr = formatted_expr;
	jve->format = format;

	return jve;
}

/*
 * makeJsonBehavior -
 *	  creates a JsonBehavior node
 */
JsonBehavior *
makeJsonBehavior(JsonBehaviorType btype, Node *expr, int location)
{
	JsonBehavior *behavior = makeNode(JsonBehavior);

	behavior->btype = btype;
	behavior->expr = expr;
	behavior->location = location;

	return behavior;
}

/*
 * makeJsonKeyValue -
 *	  creates a JsonKeyValue node
 */
Node *
makeJsonKeyValue(Node *key, Node *value)
{
	JsonKeyValue *n = makeNode(JsonKeyValue);

	n->key = (Expr *) key;
	n->value = castNode(JsonValueExpr, value);

	return (Node *) n;
}

/*
 * makeJsonIsPredicate -
 *	  creates a JsonIsPredicate node
 */
Node *
makeJsonIsPredicate(Node *expr, JsonFormat *format, JsonValueType item_type,
					bool unique_keys, Oid exprBaseType, int location)
{
	JsonIsPredicate *n = makeNode(JsonIsPredicate);

	Assert(expr != NULL);

	n->expr = expr;
	n->format = format;
	n->item_type = item_type;
	n->unique_keys = unique_keys;
	n->exprBaseType = exprBaseType;
	n->location = location;

	return (Node *) n;
}

/*
 * makeJsonTablePathSpec -
 *		Make JsonTablePathSpec node from given path string and name (if any)
 */
JsonTablePathSpec *
makeJsonTablePathSpec(char *string, char *name, int string_location,
					  int name_location)
{
	JsonTablePathSpec *pathspec = makeNode(JsonTablePathSpec);

	Assert(string != NULL);
	pathspec->string = makeStringConst(string, string_location);
	if (name != NULL)
		pathspec->name = pstrdup(name);

	pathspec->name_location = name_location;
	pathspec->location = string_location;

	return pathspec;
}

/*
 * makeJsonTablePath -
 *		Make JsonTablePath node for given path string and name
 */
JsonTablePath *
makeJsonTablePath(Const *pathvalue, char *pathname)
{
	JsonTablePath *path = makeNode(JsonTablePath);

	Assert(IsA(pathvalue, Const));
	path->value = pathvalue;
	path->name = pathname;

	return path;
}
./pathnodes.h0000664000175000017500000045113115221466421012053 0ustar  xmanxman/*-------------------------------------------------------------------------
 *
 * pathnodes.h
 *	  Definitions for planner's internal data structures, especially Paths.
 *
 * We don't support copying RelOptInfo, IndexOptInfo, or Path nodes.
 * There are some subsidiary structs that are useful to copy, though.
 *
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/nodes/pathnodes.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef PATHNODES_H
#define PATHNODES_H

#include "access/sdir.h"
#include "lib/stringinfo.h"
#include "nodes/params.h"
#include "nodes/parsenodes.h"
#include "storage/block.h"

/*
 * Path generation strategies.
 *
 * These constants are used to specify the set of strategies that the planner
 * should use, either for the query as a whole or for a specific baserel or
 * joinrel. The various planner-related enable_* GUCs are used to set the
 * PlannerGlobal's default_pgs_mask, and that in turn is used to set each
 * RelOptInfo's pgs_mask. In both cases, extensions can use hooks to modify the
 * default value.  Not every strategy listed here has a corresponding enable_*
 * GUC; those that don't are always allowed unless disabled by an extension.
 * Not all strategies are relevant for every RelOptInfo; e.g. PGS_SEQSCAN
 * doesn't affect joinrels one way or the other.
 *
 * In most cases, disabling a path generation strategy merely means that any
 * paths generated using that strategy are marked as disabled, but in some
 * cases, path generation is skipped altogether. The latter strategy is only
 * permissible when it can't result in planner failure -- for instance, we
 * couldn't do this for sequential scans on a plain rel, because there might
 * not be any other possible path. Nevertheless, the behaviors in each
 * individual case are to some extent the result of historical accident,
 * chosen to match the preexisting behaviors of the enable_* GUCs.
 *
 * In a few cases, we have more than one bit for the same strategy, controlling
 * different aspects of the planner behavior. When PGS_CONSIDER_INDEXONLY is
 * unset, we don't even consider index-only scans, and any such scans that
 * would have been generated become index scans instead. On the other hand,
 * unsetting PGS_INDEXSCAN or PGS_INDEXONLYSCAN causes generated paths of the
 * corresponding types to be marked as disabled. Similarly, unsetting
 * PGS_CONSIDER_PARTITIONWISE prevents any sort of thinking about partitionwise
 * joins for the current rel, which incidentally will preclude higher-level
 * joinrels from building partitionwise paths using paths taken from the
 * current rel's children. On the other hand, unsetting PGS_APPEND or
 * PGS_MERGE_APPEND will only arrange to disable paths of the corresponding
 * types if they are generated at the level of the current rel.
 *
 * Finally, unsetting PGS_CONSIDER_NONPARTIAL disables all non-partial paths
 * except those that use Gather or Gather Merge. In most other cases, a
 * plugin can nudge the planner toward a particular strategy by disabling
 * all of the others, but that doesn't work here: unsetting PGS_SEQSCAN,
 * for instance, would disable both partial and non-partial sequential scans.
 */
#define PGS_SEQSCAN					0x00000001
#define PGS_INDEXSCAN				0x00000002
#define PGS_INDEXONLYSCAN			0x00000004
#define PGS_BITMAPSCAN				0x00000008
#define PGS_TIDSCAN					0x00000010
#define PGS_FOREIGNJOIN				0x00000020
#define PGS_MERGEJOIN_PLAIN			0x00000040
#define PGS_MERGEJOIN_MATERIALIZE	0x00000080
#define PGS_NESTLOOP_PLAIN			0x00000100
#define PGS_NESTLOOP_MATERIALIZE	0x00000200
#define PGS_NESTLOOP_MEMOIZE		0x00000400
#define PGS_HASHJOIN				0x00000800
#define PGS_APPEND					0x00001000
#define PGS_MERGE_APPEND			0x00002000
#define PGS_GATHER					0x00004000
#define PGS_GATHER_MERGE			0x00008000
#define PGS_CONSIDER_INDEXONLY		0x00010000
#define PGS_CONSIDER_PARTITIONWISE	0x00020000
#define PGS_CONSIDER_NONPARTIAL		0x00040000

/*
 * Convenience macros for useful combination of the bits defined above.
 */
#define PGS_SCAN_ANY		\
	(PGS_SEQSCAN | PGS_INDEXSCAN | PGS_INDEXONLYSCAN | PGS_BITMAPSCAN | \
	 PGS_TIDSCAN)
#define PGS_MERGEJOIN_ANY	\
	(PGS_MERGEJOIN_PLAIN | PGS_MERGEJOIN_MATERIALIZE)
#define PGS_NESTLOOP_ANY	\
	(PGS_NESTLOOP_PLAIN | PGS_NESTLOOP_MATERIALIZE | PGS_NESTLOOP_MEMOIZE)
#define PGS_JOIN_ANY		\
	(PGS_FOREIGNJOIN | PGS_MERGEJOIN_ANY | PGS_NESTLOOP_ANY | PGS_HASHJOIN)

/*
 * Relids
 *		Set of relation identifiers (indexes into the rangetable).
 */
typedef Bitmapset *Relids;

/*
 * When looking for a "cheapest path", this enum specifies whether we want
 * cheapest startup cost or cheapest total cost.
 */
typedef enum CostSelector
{
	STARTUP_COST, TOTAL_COST
} CostSelector;

/*
 * The cost estimate produced by cost_qual_eval() includes both a one-time
 * (startup) cost, and a per-tuple cost.
 */
typedef struct QualCost
{
	Cost		startup;		/* one-time cost */
	Cost		per_tuple;		/* per-evaluation cost */
} QualCost;

/*
 * Costing aggregate function execution requires these statistics about
 * the aggregates to be executed by a given Agg node.  Note that the costs
 * include the execution costs of the aggregates' argument expressions as
 * well as the aggregate functions themselves.  Also, the fields must be
 * defined so that initializing the struct to zeroes with memset is correct.
 */
typedef struct AggClauseCosts
{
	QualCost	transCost;		/* total per-input-row execution costs */
	QualCost	finalCost;		/* total per-aggregated-row costs */
	Size		transitionSpace;	/* space for pass-by-ref transition data */
} AggClauseCosts;

/*
 * This enum identifies the different types of "upper" (post-scan/join)
 * relations that we might deal with during planning.
 */
typedef enum UpperRelationKind
{
	UPPERREL_SETOP,				/* result of UNION/INTERSECT/EXCEPT, if any */
	UPPERREL_PARTIAL_GROUP_AGG, /* result of partial grouping/aggregation, if
								 * any */
	UPPERREL_GROUP_AGG,			/* result of grouping/aggregation, if any */
	UPPERREL_WINDOW,			/* result of window functions, if any */
	UPPERREL_PARTIAL_DISTINCT,	/* result of partial "SELECT DISTINCT", if any */
	UPPERREL_DISTINCT,			/* result of "SELECT DISTINCT", if any */
	UPPERREL_ORDERED,			/* result of ORDER BY, if any */
	UPPERREL_FINAL,				/* result of any remaining top-level actions */
	/* NB: UPPERREL_FINAL must be last enum entry; it's used to size arrays */
} UpperRelationKind;

/*----------
 * PlannerGlobal
 *		Global information for planning/optimization
 *
 * PlannerGlobal holds state for an entire planner invocation; this state
 * is shared across all levels of sub-Queries that exist in the command being
 * planned.
 *
 * Not all fields are printed.  (In some cases, there is no print support for
 * the field type; in others, doing so would lead to infinite recursion.)
 *----------
 */
typedef struct PlannerGlobal
{
	pg_node_attr(no_copy_equal, no_read, no_query_jumble)

	NodeTag		type;

	/* Param values provided to planner() */
	ParamListInfo boundParams pg_node_attr(read_write_ignore);

	/* Plans for SubPlan nodes */
	List	   *subplans;

	/* Paths from which the SubPlan Plans were made */
	List	   *subpaths;

	/* PlannerInfos for SubPlan nodes */
	List	   *subroots pg_node_attr(read_write_ignore);

	/* names already used for subplans (list of C strings) */
	List	   *subplanNames pg_node_attr(read_write_ignore);

	/* indices of subplans that require REWIND */
	Bitmapset  *rewindPlanIDs;

	/* "flat" rangetable for executor */
	List	   *finalrtable;

	/*
	 * RT indexes of all relation RTEs in finalrtable (RTE_RELATION and
	 * RTE_SUBQUERY RTEs of views)
	 */
	Bitmapset  *allRelids;

	/*
	 * RT indexes of all leaf partitions in nodes that support pruning and are
	 * subject to runtime pruning at plan initialization time ("initial"
	 * pruning).
	 */
	Bitmapset  *prunableRelids;

	/* "flat" list of RTEPermissionInfos */
	List	   *finalrteperminfos;

	/* list of SubPlanRTInfo nodes */
	List	   *subrtinfos;

	/* "flat" list of PlanRowMarks */
	List	   *finalrowmarks;

	/* "flat" list of integer RT indexes */
	List	   *resultRelations;

	/* "flat" list of AppendRelInfos */
	List	   *appendRelations;

	/* "flat" list of PartitionPruneInfos */
	List	   *partPruneInfos;

	/* OIDs of relations the plan depends on */
	List	   *relationOids;

	/* other dependencies, as PlanInvalItems */
	List	   *invalItems;

	/* type OIDs for PARAM_EXEC Params */
	List	   *paramExecTypes;

	/* info about nodes elided from the plan during setrefs processing */
	List	   *elidedNodes;

	/* highest PlaceHolderVar ID assigned */
	Index		lastPHId;

	/* highest PlanRowMark ID assigned */
	Index		lastRowMarkId;

	/* highest plan node ID assigned */
	int			lastPlanNodeId;

	/* redo plan when TransactionXmin changes? */
	bool		transientPlan;

	/* is plan specific to current role? */
	bool		dependsOnRole;

	/* parallel mode potentially OK? */
	bool		parallelModeOK;

	/* parallel mode actually required? */
	bool		parallelModeNeeded;

	/* worst PROPARALLEL hazard level */
	char		maxParallelHazard;

	/* mask of allowed path generation strategies */
	uint64		default_pgs_mask;

	/* partition descriptors */
	PartitionDirectory partition_directory pg_node_attr(read_write_ignore);

	/* hash table for NOT NULL attnums of relations */
	struct HTAB *rel_notnullatts_hash pg_node_attr(read_write_ignore);

	/* extension state */
	void	  **extension_state pg_node_attr(read_write_ignore);
	int			extension_state_allocated;
} PlannerGlobal;

/* macro for fetching the Plan associated with a SubPlan node */
#define planner_subplan_get_plan(root, subplan) \
	((Plan *) list_nth((root)->glob->subplans, (subplan)->plan_id - 1))


/*----------
 * PlannerInfo
 *		Per-query information for planning/optimization
 *
 * This struct is conventionally called "root" in all the planner routines.
 * It holds links to all of the planner's working state, in addition to the
 * original Query.  Note that at present the planner extensively modifies
 * the passed-in Query data structure; someday that should stop.
 *
 * Not all fields are printed.  (In some cases, there is no print support for
 * the field type; in others, doing so would lead to infinite recursion or
 * bloat dump output more than seems useful.)
 *
 * NOTE: When adding new entries containing relids and relid bitmapsets,
 * remember to check that they will be correctly processed by
 * the remove_self_join_rel function - relid of removing relation will be
 * correctly replaced with the keeping one.
 *----------
 */
typedef struct PlannerInfo PlannerInfo;

struct PlannerInfo
{
	pg_node_attr(no_copy_equal, no_read, no_query_jumble)

	NodeTag		type;

	/* the Query being planned */
	Query	   *parse;

	/* global info for current planner run */
	PlannerGlobal *glob;

	/* 1 at the outermost Query */
	Index		query_level;

	/* NULL at outermost Query */
	PlannerInfo *parent_root pg_node_attr(read_write_ignore);

	/* Subplan name for EXPLAIN and debugging purposes (NULL at top level) */
	char	   *plan_name;

	/*
	 * If this PlannerInfo exists to consider an alternative implementation
	 * strategy for a portion of the query that could also be implemented by
	 * some other PlannerInfo, this is the plan_name for that other
	 * PlannerInfo. When we are considering the first or only alternative, it
	 * is the same as plan_name.
	 *
	 * Currently, we set this to a value other than plan_name only when
	 * considering a MinMaxAggPath or a hashed SubPlan.
	 */
	char	   *alternative_plan_name;

	/*
	 * plan_params contains the expressions that this query level needs to
	 * make available to a lower query level that is currently being planned.
	 * outer_params contains the paramIds of PARAM_EXEC Params that outer
	 * query levels will make available to this query level.
	 */
	/* list of PlannerParamItems, see below */
	List	   *plan_params;
	Bitmapset  *outer_params;

	/*
	 * simple_rel_array holds pointers to "base rels" and "other rels" (see
	 * comments for RelOptInfo for more info).  It is indexed by rangetable
	 * index (so entry 0 is always wasted).  Entries can be NULL when an RTE
	 * does not correspond to a base relation, such as a join RTE or an
	 * unreferenced view RTE; or if the RelOptInfo hasn't been made yet.
	 */
	struct RelOptInfo **simple_rel_array pg_node_attr(array_size(simple_rel_array_size));
	/* allocated size of array */
	int			simple_rel_array_size;

	/*
	 * simple_rte_array is the same length as simple_rel_array and holds
	 * pointers to the associated rangetable entries.  Using this is a shade
	 * faster than using rt_fetch(), mostly due to fewer indirections.  (Not
	 * printed because it'd be redundant with parse->rtable.)
	 */
	RangeTblEntry **simple_rte_array pg_node_attr(read_write_ignore);

	/*
	 * append_rel_array is the same length as the above arrays, and holds
	 * pointers to the corresponding AppendRelInfo entry indexed by
	 * child_relid, or NULL if the rel is not an appendrel child.  The array
	 * itself is not allocated if append_rel_list is empty.  (Not printed
	 * because it'd be redundant with append_rel_list.)
	 */
	struct AppendRelInfo **append_rel_array pg_node_attr(read_write_ignore);

	/*
	 * all_baserels is a Relids set of all base relids (but not joins or
	 * "other" rels) in the query.  This is computed in deconstruct_jointree.
	 */
	Relids		all_baserels;

	/*
	 * outer_join_rels is a Relids set of all outer-join relids in the query.
	 * This is computed in deconstruct_jointree.
	 */
	Relids		outer_join_rels;

	/*
	 * all_query_rels is a Relids set of all base relids and outer join relids
	 * (but not "other" relids) in the query.  This is the Relids identifier
	 * of the final join we need to form.  This is computed in
	 * deconstruct_jointree.
	 */
	Relids		all_query_rels;

	/*
	 * join_rel_list is a list of all join-relation RelOptInfos we have
	 * considered in this planning run.  For small problems we just scan the
	 * list to do lookups, but when there are many join relations we build a
	 * hash table for faster lookups.  The hash table is present and valid
	 * when join_rel_hash is not NULL.  Note that we still maintain the list
	 * even when using the hash table for lookups; this simplifies life for
	 * GEQO.
	 */
	List	   *join_rel_list;
	struct HTAB *join_rel_hash pg_node_attr(read_write_ignore);

	/*
	 * When doing a dynamic-programming-style join search, join_rel_level[k]
	 * is a list of all join-relation RelOptInfos of level k, and
	 * join_cur_level is the current level.  New join-relation RelOptInfos are
	 * automatically added to the join_rel_level[join_cur_level] list.
	 * join_rel_level is NULL if not in use.
	 *
	 * Note: we've already printed all baserel and joinrel RelOptInfos above,
	 * so we don't dump join_rel_level or other lists of RelOptInfos.
	 */
	/* lists of join-relation RelOptInfos */
	List	  **join_rel_level pg_node_attr(read_write_ignore);
	/* index of list being extended */
	int			join_cur_level;

	/* init SubPlans for query */
	List	   *init_plans;

	/*
	 * per-CTE-item list of subplan IDs (or -1 if no subplan was made for that
	 * CTE)
	 */
	List	   *cte_plan_ids;

	/* List of Lists of Params for MULTIEXPR subquery outputs */
	List	   *multiexpr_params;

	/* list of JoinDomains used in the query (higher ones first) */
	List	   *join_domains;

	/* list of active EquivalenceClasses */
	List	   *eq_classes;

	/* set true once ECs are canonical */
	bool		ec_merging_done;

	/* list of "canonical" PathKeys */
	List	   *canon_pathkeys;

	/*
	 * list of OuterJoinClauseInfos for mergejoinable outer join clauses
	 * w/nonnullable var on left
	 */
	List	   *left_join_clauses;

	/*
	 * list of OuterJoinClauseInfos for mergejoinable outer join clauses
	 * w/nonnullable var on right
	 */
	List	   *right_join_clauses;

	/*
	 * list of OuterJoinClauseInfos for mergejoinable full join clauses
	 */
	List	   *full_join_clauses;

	/* list of SpecialJoinInfos */
	List	   *join_info_list;

	/* counter for assigning RestrictInfo serial numbers */
	int			last_rinfo_serial;

	/*
	 * all_result_relids is empty for SELECT, otherwise it contains at least
	 * parse->resultRelation.  For UPDATE/DELETE/MERGE across an inheritance
	 * or partitioning tree, the result rel's child relids are added.  When
	 * using multi-level partitioning, intermediate partitioned rels are
	 * included. leaf_result_relids is similar except that only actual result
	 * tables, not partitioned tables, are included in it.
	 */
	/* set of all result relids */
	Relids		all_result_relids;
	/* set of all leaf relids */
	Relids		leaf_result_relids;

	/*
	 * list of AppendRelInfos
	 *
	 * Note: for AppendRelInfos describing partitions of a partitioned table,
	 * we guarantee that partitions that come earlier in the partitioned
	 * table's PartitionDesc will appear earlier in append_rel_list.
	 */
	List	   *append_rel_list;

	/* list of RowIdentityVarInfos */
	List	   *row_identity_vars;

	/* list of PlanRowMarks */
	List	   *rowMarks;

	/* list of PlaceHolderInfos */
	List	   *placeholder_list;

	/* list of AggClauseInfos */
	List	   *agg_clause_list;

	/* list of GroupingExprInfos */
	List	   *group_expr_list;

	/* list of plain Vars contained in targetlist and havingQual */
	List	   *tlist_vars;

	/* array of PlaceHolderInfos indexed by phid */
	struct PlaceHolderInfo **placeholder_array pg_node_attr(read_write_ignore, array_size(placeholder_array_size));
	/* allocated size of array */
	int			placeholder_array_size pg_node_attr(read_write_ignore);

	/* list of ForeignKeyOptInfos */
	List	   *fkey_list;

	/* desired pathkeys for query_planner() */
	List	   *query_pathkeys;

	/* groupClause pathkeys, if any */
	List	   *group_pathkeys;

	/*
	 * The number of elements in the group_pathkeys list which belong to the
	 * GROUP BY clause.  Additional ones belong to ORDER BY / DISTINCT
	 * aggregates.
	 */
	int			num_groupby_pathkeys;

	/* pathkeys of bottom window, if any */
	List	   *window_pathkeys;
	/* distinctClause pathkeys, if any */
	List	   *distinct_pathkeys;
	/* sortClause pathkeys, if any */
	List	   *sort_pathkeys;
	/* set operator pathkeys, if any */
	List	   *setop_pathkeys;

	/* Canonicalised partition schemes used in the query. */
	List	   *part_schemes pg_node_attr(read_write_ignore);

	/* RelOptInfos we are now trying to join */
	List	   *initial_rels pg_node_attr(read_write_ignore);

	/*
	 * Upper-rel RelOptInfos. Use fetch_upper_rel() to get any particular
	 * upper rel.
	 */
	List	   *upper_rels[UPPERREL_FINAL + 1] pg_node_attr(read_write_ignore);

	/* Result tlists chosen by grouping_planner for upper-stage processing */
	struct PathTarget *upper_targets[UPPERREL_FINAL + 1] pg_node_attr(read_write_ignore);

	/*
	 * The fully-processed groupClause is kept here.  It differs from
	 * parse->groupClause in that we remove any items that we can prove
	 * redundant, so that only the columns named here actually need to be
	 * compared to determine grouping.  Note that it's possible for *all* the
	 * items to be proven redundant, implying that there is only one group
	 * containing all the query's rows.  Hence, if you want to check whether
	 * GROUP BY was specified, test for nonempty parse->groupClause, not for
	 * nonempty processed_groupClause.  Optimizer chooses specific order of
	 * group-by clauses during the upper paths generation process, attempting
	 * to use different strategies to minimize number of sorts or engage
	 * incremental sort.  See preprocess_groupclause() and
	 * get_useful_group_keys_orderings() for details.
	 *
	 * Currently, when grouping sets are specified we do not attempt to
	 * optimize the groupClause, so that processed_groupClause will be
	 * identical to parse->groupClause.
	 */
	List	   *processed_groupClause;

	/*
	 * The fully-processed distinctClause is kept here.  It differs from
	 * parse->distinctClause in that we remove any items that we can prove
	 * redundant, so that only the columns named here actually need to be
	 * compared to determine uniqueness.  Note that it's possible for *all*
	 * the items to be proven redundant, implying that there should be only
	 * one output row.  Hence, if you want to check whether DISTINCT was
	 * specified, test for nonempty parse->distinctClause, not for nonempty
	 * processed_distinctClause.
	 */
	List	   *processed_distinctClause;

	/*
	 * The fully-processed targetlist is kept here.  It differs from
	 * parse->targetList in that (for INSERT) it's been reordered to match the
	 * target table, and defaults have been filled in.  Also, additional
	 * resjunk targets may be present.  preprocess_targetlist() does most of
	 * that work, but note that more resjunk targets can get added during
	 * appendrel expansion.  (Hence, upper_targets mustn't get set up till
	 * after that.)
	 */
	List	   *processed_tlist;

	/*
	 * For UPDATE, this list contains the target table's attribute numbers to
	 * which the first N entries of processed_tlist are to be assigned.  (Any
	 * additional entries in processed_tlist must be resjunk.)  DO NOT use the
	 * resnos in processed_tlist to identify the UPDATE target columns.
	 */
	List	   *update_colnos;

	/*
	 * Fields filled during create_plan() for use in setrefs.c
	 */
	/* for GroupingFunc fixup (can't print: array length not known here) */
	AttrNumber *grouping_map pg_node_attr(read_write_ignore);
	/* List of MinMaxAggInfos */
	List	   *minmax_aggs;

	/* context holding PlannerInfo */
	MemoryContext planner_cxt pg_node_attr(read_write_ignore);

	/* # of pages in all non-dummy tables of query */
	Cardinality total_table_pages;

	/* tuple_fraction passed to query_planner */
	Selectivity tuple_fraction;
	/* limit_tuples passed to query_planner */
	Cardinality limit_tuples;

	/*
	 * Minimum security_level for quals. Note: qual_security_level is zero if
	 * there are no securityQuals.
	 */
	Index		qual_security_level;

	/* true if any RTEs are RTE_JOIN kind */
	bool		hasJoinRTEs;
	/* true if any RTEs are marked LATERAL */
	bool		hasLateralRTEs;
	/* true if havingQual was non-null */
	bool		hasHavingQual;
	/* true if any RestrictInfo has pseudoconstant = true */
	bool		hasPseudoConstantQuals;
	/* true if we've made any of those */
	bool		hasAlternativeSubPlans;
	/* true once we're no longer allowed to add PlaceHolderInfos */
	bool		placeholdersFrozen;
	/* true if planning a recursive WITH item */
	bool		hasRecursion;
	/* true if a planner extension may replan this subquery */
	bool		assumeReplanning;

	/*
	 * The rangetable index for the RTE_GROUP RTE, or 0 if there is no
	 * RTE_GROUP RTE.
	 */
	int			group_rtindex;

	/*
	 * Information about aggregates. Filled by preprocess_aggrefs().
	 */
	/* AggInfo structs */
	List	   *agginfos;
	/* AggTransInfo structs */
	List	   *aggtransinfos;
	/* number of aggs with DISTINCT/ORDER BY/WITHIN GROUP */
	int			numOrderedAggs;
	/* does any agg not support partial mode? */
	bool		hasNonPartialAggs;
	/* is any partial agg non-serializable? */
	bool		hasNonSerialAggs;

	/*
	 * These fields are used only when hasRecursion is true:
	 */
	/* PARAM_EXEC ID for the work table */
	int			wt_param_id;
	/* a path for non-recursive term */
	struct Path *non_recursive_path;

	/*
	 * These fields are workspace for createplan.c
	 */
	/* outer rels above current node */
	Relids		curOuterRels;
	/* not-yet-assigned NestLoopParams */
	List	   *curOuterParams;

	/*
	 * These fields are workspace for setrefs.c.  Each is an array
	 * corresponding to glob->subplans.  (We could probably teach
	 * gen_node_support.pl how to determine the array length, but it doesn't
	 * seem worth the trouble, so just mark them read_write_ignore.)
	 */
	bool	   *isAltSubplan pg_node_attr(read_write_ignore);
	bool	   *isUsedSubplan pg_node_attr(read_write_ignore);

	/* PartitionPruneInfos added in this query's plan. */
	List	   *partPruneInfos;

	/* extension state */
	void	  **extension_state pg_node_attr(read_write_ignore);
	int			extension_state_allocated;
};


/*
 * In places where it's known that simple_rte_array[] must have been prepared
 * already, we just index into it to fetch RTEs.  In code that might be
 * executed before or after entering query_planner(), use this macro.
 */
#define planner_rt_fetch(rti, root) \
	((root)->simple_rte_array ? (root)->simple_rte_array[rti] : \
	 rt_fetch(rti, (root)->parse->rtable))

/*
 * If multiple relations are partitioned the same way, all such partitions
 * will have a pointer to the same PartitionScheme.  A list of PartitionScheme
 * objects is attached to the PlannerInfo.  By design, the partition scheme
 * incorporates only the general properties of the partition method (LIST vs.
 * RANGE, number of partitioning columns and the type information for each)
 * and not the specific bounds.
 *
 * We store the opclass-declared input data types instead of the partition key
 * datatypes since the former rather than the latter are used to compare
 * partition bounds. Since partition key data types and the opclass declared
 * input data types are expected to be binary compatible (per ResolveOpClass),
 * both of those should have same byval and length properties.
 */
typedef struct PartitionSchemeData
{
	char		strategy;		/* partition strategy */
	int16		partnatts;		/* number of partition attributes */
	Oid		   *partopfamily;	/* OIDs of operator families */
	Oid		   *partopcintype;	/* OIDs of opclass declared input data types */
	Oid		   *partcollation;	/* OIDs of partitioning collations */

	/* Cached information about partition key data types. */
	int16	   *parttyplen;
	bool	   *parttypbyval;

	/* Cached information about partition comparison functions. */
	struct FmgrInfo *partsupfunc;
} PartitionSchemeData;

typedef struct PartitionSchemeData *PartitionScheme;

/*----------
 * RelOptInfo
 *		Per-relation information for planning/optimization
 *
 * For planning purposes, a "base rel" is either a plain relation (a table)
 * or the output of a sub-SELECT or function that appears in the range table.
 * In either case it is uniquely identified by an RT index.  A "joinrel"
 * is the joining of two or more base rels.  A joinrel is identified by
 * the set of RT indexes for its component baserels, along with RT indexes
 * for any outer joins it has computed.  We create RelOptInfo nodes for each
 * baserel and joinrel, and store them in the PlannerInfo's simple_rel_array
 * and join_rel_list respectively.
 *
 * Note that there is only one joinrel for any given set of component
 * baserels, no matter what order we assemble them in; so an unordered
 * set is the right datatype to identify it with.
 *
 * We also have "other rels", which are like base rels in that they refer to
 * single RT indexes; but they are not part of the join tree, and are given
 * a different RelOptKind to identify them.
 * Currently the only kind of otherrels are those made for member relations
 * of an "append relation", that is an inheritance set or UNION ALL subquery.
 * An append relation has a parent RTE that is a base rel, which represents
 * the entire append relation.  The member RTEs are otherrels.  The parent
 * is present in the query join tree but the members are not.  The member
 * RTEs and otherrels are used to plan the scans of the individual tables or
 * subqueries of the append set; then the parent baserel is given Append
 * and/or MergeAppend paths comprising the best paths for the individual
 * member rels.  (See comments for AppendRelInfo for more information.)
 *
 * At one time we also made otherrels to represent join RTEs, for use in
 * handling join alias Vars.  Currently this is not needed because all join
 * alias Vars are expanded to non-aliased form during preprocess_expression.
 *
 * We also have relations representing joins between child relations of
 * different partitioned tables. These relations are not added to
 * join_rel_level lists as they are not joined directly by the dynamic
 * programming algorithm.
 *
 * There is also a RelOptKind for "upper" relations, which are RelOptInfos
 * that describe post-scan/join processing steps, such as aggregation.
 * Many of the fields in these RelOptInfos are meaningless, but their Path
 * fields always hold Paths showing ways to do that processing step.
 *
 * Parts of this data structure are specific to various scan and join
 * mechanisms.  It didn't seem worth creating new node types for them.
 *
 *		relids - Set of relation identifiers (RT indexes).  This is a base
 *				 relation if there is just one, a join relation if more;
 *				 in the join case, RT indexes of any outer joins formed
 *				 at or below this join are included along with baserels
 *		rows - estimated number of tuples in the relation after restriction
 *			   clauses have been applied (ie, output rows of a plan for it)
 *		consider_startup - true if there is any value in keeping plain paths for
 *						   this rel on the basis of having cheap startup cost
 *		consider_param_startup - the same for parameterized paths
 *		reltarget - Default Path output tlist for this rel; normally contains
 *					Var and PlaceHolderVar nodes for the values we need to
 *					output from this relation.
 *					List is in no particular order, but all rels of an
 *					appendrel set must use corresponding orders.
 *					NOTE: in an appendrel child relation, may contain
 *					arbitrary expressions pulled up from a subquery!
 *		pathlist - List of Path nodes, one for each potentially useful
 *				   method of generating the relation
 *		ppilist - ParamPathInfo nodes for parameterized Paths, if any
 *		cheapest_startup_path - the pathlist member with lowest startup cost
 *			(regardless of ordering) among the unparameterized paths;
 *			or NULL if there is no unparameterized path
 *		cheapest_total_path - the pathlist member with lowest total cost
 *			(regardless of ordering) among the unparameterized paths;
 *			or if there is no unparameterized path, the path with lowest
 *			total cost among the paths with minimum parameterization
 *		cheapest_parameterized_paths - best paths for their parameterizations;
 *			always includes cheapest_total_path, even if that's unparameterized
 *		direct_lateral_relids - rels this rel has direct LATERAL references to
 *		lateral_relids - required outer rels for LATERAL, as a Relids set
 *			(includes both direct and indirect lateral references)
 *
 * If the relation is a base relation it will have these fields set:
 *
 *		relid - RTE index (this is redundant with the relids field, but
 *				is provided for convenience of access)
 *		rtekind - copy of RTE's rtekind field
 *		min_attr, max_attr - range of valid AttrNumbers for rel
 *		attr_needed - array of bitmapsets indicating the highest joinrel
 *				in which each attribute is needed; if bit 0 is set then
 *				the attribute is needed as part of final targetlist
 *		attr_widths - cache space for per-attribute width estimates;
 *					  zero means not computed yet
 *		notnullattnums - zero-based set containing attnums of NOT NULL
 *						 columns (not populated for rels corresponding to
 *						 non-partitioned inh==true RTEs)
 *		nulling_relids - relids of outer joins that can null this rel
 *		lateral_vars - lateral cross-references of rel, if any (list of
 *					   Vars and PlaceHolderVars)
 *		lateral_referencers - relids of rels that reference this one laterally
 *				(includes both direct and indirect lateral references)
 *		indexlist - list of IndexOptInfo nodes for relation's indexes
 *					(always NIL if it's not a table or partitioned table)
 *		pages - number of disk pages in relation (zero if not a table)
 *		tuples - number of tuples in relation (not considering restrictions)
 *		allvisfrac - fraction of disk pages that are marked all-visible
 *		eclass_indexes - EquivalenceClasses that mention this rel (filled
 *						 only after EC merging is complete)
 *		subroot - PlannerInfo for subquery (NULL if it's not a subquery)
 *		subplan_params - list of PlannerParamItems to be passed to subquery
 *
 *		Note: for a subquery, tuples and subroot are not set immediately
 *		upon creation of the RelOptInfo object; they are filled in when
 *		set_subquery_pathlist processes the object.
 *
 *		For otherrels that are appendrel members, these fields are filled
 *		in just as for a baserel, except we don't bother with lateral_vars.
 *
 * If the relation is either a foreign table or a join of foreign tables that
 * all belong to the same foreign server and are assigned to the same user to
 * check access permissions as (cf checkAsUser), these fields will be set:
 *
 *		serverid - OID of foreign server, if foreign table (else InvalidOid)
 *		userid - OID of user to check access as (InvalidOid means current user)
 *		useridiscurrent - we've assumed that userid equals current user
 *		fdwroutine - function hooks for FDW, if foreign table (else NULL)
 *		fdw_private - private state for FDW, if foreign table (else NULL)
 *
 * Two fields are used to cache knowledge acquired during the join search
 * about whether this rel is provably unique when being joined to given other
 * relation(s), ie, it can have at most one row matching any given row from
 * that join relation.  Currently we only attempt such proofs, and thus only
 * populate these fields, for base rels; but someday they might be used for
 * join rels too:
 *
 *		unique_for_rels - list of UniqueRelInfo, each one being a set of other
 *					rels for which this one has been proven unique
 *		non_unique_for_rels - list of Relid sets, each one being a set of
 *					other rels for which we have tried and failed to prove
 *					this one unique
 *
 * Three fields are used to cache information about unique-ification of this
 * relation.  This is used to support semijoins where the relation appears on
 * the RHS: the relation is first unique-ified, and then a regular join is
 * performed:
 *
 *		unique_rel - the unique-ified version of the relation, containing paths
 *					that produce unique (no duplicates) output from relation;
 *					NULL if not yet requested
 *		unique_pathkeys - pathkeys that represent the ordering requirements for
 *					the relation's output in sort-based unique-ification
 *					implementations
 *		unique_groupclause - a list of SortGroupClause nodes that represent the
 *					columns to be grouped on in hash-based unique-ification
 *					implementations
 *
 * The presence of the following fields depends on the restrictions
 * and joins that the relation participates in:
 *
 *		baserestrictinfo - List of RestrictInfo nodes, containing info about
 *					each non-join qualification clause in which this relation
 *					participates (only used for base rels)
 *		baserestrictcost - Estimated cost of evaluating the baserestrictinfo
 *					clauses at a single tuple (only used for base rels)
 *		baserestrict_min_security - Smallest security_level found among
 *					clauses in baserestrictinfo
 *		joininfo  - List of RestrictInfo nodes, containing info about each
 *					join clause in which this relation participates (but
 *					note this excludes clauses that might be derivable from
 *					EquivalenceClasses)
 *		has_eclass_joins - flag that EquivalenceClass joins are possible
 *
 * Note: Keeping a restrictinfo list in the RelOptInfo is useful only for
 * base rels, because for a join rel the set of clauses that are treated as
 * restrict clauses varies depending on which sub-relations we choose to join.
 * (For example, in a 3-base-rel join, a clause relating rels 1 and 2 must be
 * treated as a restrictclause if we join {1} and {2 3} to make {1 2 3}; but
 * if we join {1 2} and {3} then that clause will be a restrictclause in {1 2}
 * and should not be processed again at the level of {1 2 3}.)	Therefore,
 * the restrictinfo list in the join case appears in individual JoinPaths
 * (field joinrestrictinfo), not in the parent relation.  But it's OK for
 * the RelOptInfo to store the joininfo list, because that is the same
 * for a given rel no matter how we form it.
 *
 * We store baserestrictcost in the RelOptInfo (for base relations) because
 * we know we will need it at least once (to price the sequential scan)
 * and may need it multiple times to price index scans.
 *
 * A join relation is considered to be partitioned if it is formed from a
 * join of two relations that are partitioned, have matching partitioning
 * schemes, and are joined on an equijoin of the partitioning columns.
 * Under those conditions we can consider the join relation to be partitioned
 * by either relation's partitioning keys, though some care is needed if
 * either relation can be forced to null by outer-joining.  For example, an
 * outer join like (A LEFT JOIN B ON A.a = B.b) may produce rows with B.b
 * NULL.  These rows may not fit the partitioning conditions imposed on B.
 * Hence, strictly speaking, the join is not partitioned by B.b and thus
 * partition keys of an outer join should include partition key expressions
 * from the non-nullable side only.  However, if a subsequent join uses
 * strict comparison operators (and all commonly-used equijoin operators are
 * strict), the presence of nulls doesn't cause a problem: such rows couldn't
 * match anything on the other side and thus they don't create a need to do
 * any cross-partition sub-joins.  Hence we can treat such values as still
 * partitioning the join output for the purpose of additional partitionwise
 * joining, so long as a strict join operator is used by the next join.
 *
 * If the relation is partitioned, these fields will be set:
 *
 *		part_scheme - Partitioning scheme of the relation
 *		nparts - Number of partitions
 *		boundinfo - Partition bounds
 *		partbounds_merged - true if partition bounds are merged ones
 *		partition_qual - Partition constraint if not the root
 *		part_rels - RelOptInfos for each partition
 *		all_partrels - Relids set of all partition relids
 *		partexprs, nullable_partexprs - Partition key expressions
 *
 * The partexprs and nullable_partexprs arrays each contain
 * part_scheme->partnatts elements.  Each of the elements is a list of
 * partition key expressions.  For partitioned base relations, there is one
 * expression in each partexprs element, and nullable_partexprs is empty.
 * For partitioned join relations, each base relation within the join
 * contributes one partition key expression per partitioning column;
 * that expression goes in the partexprs[i] list if the base relation
 * is not nullable by this join or any lower outer join, or in the
 * nullable_partexprs[i] list if the base relation is nullable.
 * Furthermore, FULL JOINs add extra nullable_partexprs expressions
 * corresponding to COALESCE expressions of the left and right join columns,
 * to simplify matching join clauses to those lists.
 *
 * Not all fields are printed.  (In some cases, there is no print support for
 * the field type.)
 *----------
 */

/* Bitmask of flags supported by table AMs */
#define AMFLAG_HAS_TID_RANGE (1 << 0)

typedef enum RelOptKind
{
	RELOPT_BASEREL,
	RELOPT_JOINREL,
	RELOPT_OTHER_MEMBER_REL,
	RELOPT_OTHER_JOINREL,
	RELOPT_UPPER_REL,
	RELOPT_OTHER_UPPER_REL,
} RelOptKind;

/*
 * Is the given relation a simple relation i.e a base or "other" member
 * relation?
 */
#define IS_SIMPLE_REL(rel) \
	((rel)->reloptkind == RELOPT_BASEREL || \
	 (rel)->reloptkind == RELOPT_OTHER_MEMBER_REL)

/* Is the given relation a join relation? */
#define IS_JOIN_REL(rel)	\
	((rel)->reloptkind == RELOPT_JOINREL || \
	 (rel)->reloptkind == RELOPT_OTHER_JOINREL)

/* Is the given relation an upper relation? */
#define IS_UPPER_REL(rel)	\
	((rel)->reloptkind == RELOPT_UPPER_REL || \
	 (rel)->reloptkind == RELOPT_OTHER_UPPER_REL)

/* Is the given relation an "other" relation? */
#define IS_OTHER_REL(rel) \
	((rel)->reloptkind == RELOPT_OTHER_MEMBER_REL || \
	 (rel)->reloptkind == RELOPT_OTHER_JOINREL || \
	 (rel)->reloptkind == RELOPT_OTHER_UPPER_REL)

typedef struct RelOptInfo
{
	pg_node_attr(no_copy_equal, no_read, no_query_jumble)

	NodeTag		type;

	RelOptKind	reloptkind;

	/*
	 * all relations included in this RelOptInfo; set of base + OJ relids
	 * (rangetable indexes)
	 */
	Relids		relids;

	/*
	 * size estimates generated by planner
	 */
	/* estimated number of result tuples */
	Cardinality rows;

	/*
	 * per-relation planner control
	 */
	/* keep cheap-startup-cost paths? */
	bool		consider_startup;
	/* ditto, for parameterized paths? */
	bool		consider_param_startup;
	/* consider parallel paths? */
	bool		consider_parallel;
	/* path generation strategy mask */
	uint64		pgs_mask;

	/*
	 * default result targetlist for Paths scanning this relation; list of
	 * Vars/Exprs, cost, width
	 */
	struct PathTarget *reltarget;

	/*
	 * materialization information
	 */
	List	   *pathlist;		/* Path structures */
	List	   *ppilist;		/* ParamPathInfos used in pathlist */
	List	   *partial_pathlist;	/* partial Paths */
	struct Path *cheapest_startup_path;
	struct Path *cheapest_total_path;
	List	   *cheapest_parameterized_paths;

	/*
	 * parameterization information needed for both base rels and join rels
	 * (see also lateral_vars and lateral_referencers)
	 */
	/* rels directly laterally referenced */
	Relids		direct_lateral_relids;
	/* minimum parameterization of rel */
	Relids		lateral_relids;

	/*
	 * information about a base rel (not set for join rels!)
	 */
	Index		relid;
	/* containing tablespace */
	Oid			reltablespace;
	/* RELATION, SUBQUERY, FUNCTION, etc */
	RTEKind		rtekind;
	/* smallest attrno of rel (often <0) */
	AttrNumber	min_attr;
	/* largest attrno of rel */
	AttrNumber	max_attr;
	/* array indexed [min_attr .. max_attr] */
	Relids	   *attr_needed pg_node_attr(read_write_ignore);
	/* array indexed [min_attr .. max_attr] */
	int32	   *attr_widths pg_node_attr(read_write_ignore);
	/* zero-based set containing attnums of NOT NULL columns */
	Bitmapset  *notnullattnums;
	/* relids of outer joins that can null this baserel */
	Relids		nulling_relids;
	/* LATERAL Vars and PHVs referenced by rel */
	List	   *lateral_vars;
	/* rels that reference this baserel laterally */
	Relids		lateral_referencers;
	/* list of IndexOptInfo */
	List	   *indexlist;
	/* list of StatisticExtInfo */
	List	   *statlist;
	/* size estimates derived from pg_class */
	BlockNumber pages;
	Cardinality tuples;
	double		allvisfrac;
	/* indexes in PlannerInfo's eq_classes list of ECs that mention this rel */
	Bitmapset  *eclass_indexes;
	PlannerInfo *subroot;		/* if subquery */
	List	   *subplan_params; /* if subquery */
	/* wanted number of parallel workers */
	int			rel_parallel_workers;
	/* Bitmask of optional features supported by the table AM */
	uint32		amflags;

	/*
	 * Information about foreign tables and foreign joins
	 */
	/* identifies server for the table or join */
	Oid			serverid;
	/* identifies user to check access as; 0 means to check as current user */
	Oid			userid;
	/* join is only valid for current user */
	bool		useridiscurrent;
	/* use "struct FdwRoutine" to avoid including fdwapi.h here */
	struct FdwRoutine *fdwroutine pg_node_attr(read_write_ignore);
	void	   *fdw_private pg_node_attr(read_write_ignore);

	/*
	 * cache space for remembering if we have proven this relation unique
	 */
	/* known unique for these other relid set(s) given in UniqueRelInfo(s) */
	List	   *unique_for_rels;
	/* known not unique for these set(s) */
	List	   *non_unique_for_rels;

	/*
	 * information about unique-ification of this relation
	 */
	/* the unique-ified version of the relation */
	struct RelOptInfo *unique_rel;
	/* pathkeys for sort-based unique-ification implementations */
	List	   *unique_pathkeys;
	/* SortGroupClause nodes for hash-based unique-ification implementations */
	List	   *unique_groupclause;

	/*
	 * used by various scans and joins:
	 */
	/* RestrictInfo structures (if base rel) */
	List	   *baserestrictinfo;
	/* cost of evaluating the above */
	QualCost	baserestrictcost;
	/* min security_level found in baserestrictinfo */
	Index		baserestrict_min_security;
	/* RestrictInfo structures for join clauses involving this rel */
	List	   *joininfo;
	/* T means joininfo is incomplete */
	bool		has_eclass_joins;

	/*
	 * used by partitionwise joins:
	 */
	/* consider partitionwise join paths? (if partitioned rel) */
	bool		consider_partitionwise_join;

	/*
	 * used by eager aggregation:
	 */
	/* information needed to create grouped paths */
	struct RelAggInfo *agg_info;
	/* the partially-aggregated version of the relation */
	struct RelOptInfo *grouped_rel;

	/*
	 * inheritance links, if this is an otherrel (otherwise NULL):
	 */
	/* Immediate parent relation (dumping it would be too verbose) */
	struct RelOptInfo *parent pg_node_attr(read_write_ignore);
	/* Topmost parent relation (dumping it would be too verbose) */
	struct RelOptInfo *top_parent pg_node_attr(read_write_ignore);
	/* Relids of topmost parent (redundant, but handy) */
	Relids		top_parent_relids;

	/*
	 * used for partitioned relations:
	 */
	/* Partitioning scheme */
	PartitionScheme part_scheme pg_node_attr(read_write_ignore);

	/*
	 * Number of partitions; -1 if not yet set; in case of a join relation 0
	 * means it's considered unpartitioned
	 */
	int			nparts;
	/* Partition bounds */
	struct PartitionBoundInfoData *boundinfo pg_node_attr(read_write_ignore);
	/* True if partition bounds were created by partition_bounds_merge() */
	bool		partbounds_merged;
	/* Partition constraint, if not the root */
	List	   *partition_qual;

	/*
	 * Array of RelOptInfos of partitions, stored in the same order as bounds
	 * (don't print, too bulky and duplicative)
	 */
	struct RelOptInfo **part_rels pg_node_attr(read_write_ignore);

	/*
	 * Bitmap with members acting as indexes into the part_rels[] array to
	 * indicate which partitions survived partition pruning.
	 */
	Bitmapset  *live_parts;
	/* Relids set of all partition relids */
	Relids		all_partrels;

	/*
	 * These arrays are of length partkey->partnatts, which we don't have at
	 * hand, so don't try to print
	 */

	/* Non-nullable partition key expressions */
	List	  **partexprs pg_node_attr(read_write_ignore);
	/* Nullable partition key expressions */
	List	  **nullable_partexprs pg_node_attr(read_write_ignore);

	/* extension state */
	void	  **extension_state pg_node_attr(read_write_ignore);
	int			extension_state_allocated;
} RelOptInfo;

/*
 * Is given relation partitioned?
 *
 * It's not enough to test whether rel->part_scheme is set, because it might
 * be that the basic partitioning properties of the input relations matched
 * but the partition bounds did not.  Also, if we are able to prove a rel
 * dummy (empty), we should henceforth treat it as unpartitioned.
 */
#define IS_PARTITIONED_REL(rel) \
	((rel)->part_scheme && (rel)->boundinfo && (rel)->nparts > 0 && \
	 (rel)->part_rels && !IS_DUMMY_REL(rel))

/*
 * Convenience macro to make sure that a partitioned relation has all the
 * required members set.
 */
#define REL_HAS_ALL_PART_PROPS(rel)	\
	((rel)->part_scheme && (rel)->boundinfo && (rel)->nparts > 0 && \
	 (rel)->part_rels && (rel)->partexprs && (rel)->nullable_partexprs)

/*
 * Is given relation unique-ified?
 *
 * When the nominal jointype is JOIN_INNER, sjinfo->jointype is JOIN_SEMI, and
 * the given rel is exactly the RHS of the semijoin, it indicates that the rel
 * has been unique-ified.
 */
#define RELATION_WAS_MADE_UNIQUE(rel, sjinfo, nominal_jointype) \
	((nominal_jointype) == JOIN_INNER && (sjinfo)->jointype == JOIN_SEMI && \
	 bms_equal((sjinfo)->syn_righthand, (rel)->relids))

/*
 * Is the given relation a grouped relation?
 */
#define IS_GROUPED_REL(rel) \
	((rel)->agg_info != NULL)

/*
 * RelAggInfo
 *		Information needed to create paths for a grouped relation.
 *
 * "target" is the default result targetlist for Paths scanning this grouped
 * relation; list of Vars/Exprs, cost, width.
 *
 * "agg_input" is the output tlist for the paths that provide input to the
 * grouped paths.  One difference from the reltarget of the non-grouped
 * relation is that agg_input has its sortgrouprefs[] initialized.
 *
 * "group_clauses" and "group_exprs" are lists of SortGroupClauses and the
 * corresponding grouping expressions.
 *
 * "apply_agg_at" tracks the set of relids at which partial aggregation is
 * applied in the paths of this grouped relation.
 *
 * "grouped_rows" is the estimated number of result tuples of the grouped
 * relation.
 *
 * "agg_useful" is a flag to indicate whether the grouped paths are considered
 * useful.  It is set true if the average partial group size is no less than
 * min_eager_agg_group_size, suggesting a significant row count reduction.
 */
typedef struct RelAggInfo
{
	pg_node_attr(no_copy_equal, no_read, no_query_jumble)

	NodeTag		type;

	/* the output tlist for the grouped paths */
	struct PathTarget *target;

	/* the output tlist for the input paths */
	struct PathTarget *agg_input;

	/* a list of SortGroupClauses */
	List	   *group_clauses;
	/* a list of grouping expressions */
	List	   *group_exprs;

	/* the set of relids partial aggregation is applied at */
	Relids		apply_agg_at;

	/* estimated number of result tuples */
	Cardinality grouped_rows;

	/* the grouped paths are considered useful? */
	bool		agg_useful;
} RelAggInfo;

/*
 * IndexOptInfo
 *		Per-index information for planning/optimization
 *
 *		indexkeys[] and canreturn[] each have ncolumns entries.
 *
 *		indexcollations[], opfamily[], and opcintype[] each have nkeycolumns
 *		entries.  These don't contain any information about INCLUDE columns.
 *
 *		sortopfamily[], reverse_sort[], and nulls_first[] have
 *		nkeycolumns entries, if the index is ordered; but if it is unordered,
 *		those pointers are NULL.
 *
 *		Zeroes in the indexkeys[] array indicate index columns that are
 *		expressions; there is one element in indexprs for each such column.
 *
 *		For an ordered index, reverse_sort[] and nulls_first[] describe the
 *		sort ordering of a forward indexscan; we can also consider a backward
 *		indexscan, which will generate the reverse ordering.
 *
 *		The indexprs and indpred expressions have been run through
 *		prepqual.c and eval_const_expressions() for ease of matching to
 *		WHERE clauses. indpred is in implicit-AND form.
 *
 *		indextlist is a TargetEntry list representing the index columns.
 *		It provides an equivalent base-relation Var for each simple column,
 *		and links to the matching indexprs element for each expression column.
 *
 *		While most of these fields are filled when the IndexOptInfo is created
 *		(by plancat.c), indrestrictinfo and predOK are set later, in
 *		check_index_predicates().
 */

struct IndexPath;				/* forward declaration */

typedef struct IndexOptInfo
{
	pg_node_attr(no_copy_equal, no_read, no_query_jumble)

	NodeTag		type;

	/* OID of the index relation */
	Oid			indexoid;
	/* tablespace of index (not table) */
	Oid			reltablespace;
	/* back-link to index's table; don't print, else infinite recursion */
	RelOptInfo *rel pg_node_attr(read_write_ignore);

	/*
	 * index-size statistics (from pg_class and elsewhere)
	 */
	/* number of disk pages in index */
	BlockNumber pages;
	/* number of index tuples in index */
	Cardinality tuples;
	/* index tree height, or -1 if unknown */
	int			tree_height;

	/*
	 * index descriptor information
	 */
	/* number of columns in index */
	int			ncolumns;
	/* number of key columns in index */
	int			nkeycolumns;

	/*
	 * table column numbers of index's columns (both key and included
	 * columns), or 0 for expression columns
	 */
	int		   *indexkeys pg_node_attr(array_size(ncolumns));
	/* OIDs of collations of index columns */
	Oid		   *indexcollations pg_node_attr(array_size(nkeycolumns));
	/* OIDs of operator families for columns */
	Oid		   *opfamily pg_node_attr(array_size(nkeycolumns));
	/* OIDs of opclass declared input data types */
	Oid		   *opcintype pg_node_attr(array_size(nkeycolumns));
	/* OIDs of btree opfamilies, if orderable.  NULL if partitioned index */
	Oid		   *sortopfamily pg_node_attr(array_size(nkeycolumns));
	/* is sort order descending? or NULL if partitioned index */
	bool	   *reverse_sort pg_node_attr(array_size(nkeycolumns));
	/* do NULLs come first in the sort order? or NULL if partitioned index */
	bool	   *nulls_first pg_node_attr(array_size(nkeycolumns));
	/* opclass-specific options for columns */
	bytea	  **opclassoptions pg_node_attr(read_write_ignore);
	/* which index cols can be returned in an index-only scan? */
	bool	   *canreturn pg_node_attr(array_size(ncolumns));
	/* OID of the access method (in pg_am) */
	Oid			relam;

	/*
	 * expressions for non-simple index columns; redundant to print since we
	 * print indextlist
	 */
	List	   *indexprs pg_node_attr(read_write_ignore);
	List	   *indexprsExpand pg_node_attr(read_write_ignore);
	/* predicate if a partial index, else NIL */
	List	   *indpred;
	List	   *indpredExpand;

	/* targetlist representing index columns */
	List	   *indextlist;

	/*
	 * parent relation's baserestrictinfo list, less any conditions implied by
	 * the index's predicate (unless it's a target rel, see comments in
	 * check_index_predicates())
	 */
	List	   *indrestrictinfo;

	/* true if index predicate matches query */
	bool		predOK;
	/* true if a unique index */
	bool		unique;
	/* true if the index was defined with NULLS NOT DISTINCT */
	bool		nullsnotdistinct;
	/* is uniqueness enforced immediately? */
	bool		immediate;
	/* true if paths using this index should be marked disabled */
	bool		disabled;
	/* true if index doesn't really exist */
	bool		hypothetical;

	/*
	 * Remaining fields are copied from the index AM's API struct
	 * (IndexAmRoutine).  These fields are not set for partitioned indexes.
	 */
	bool		amcanorderbyop;
	bool		amoptionalkey;
	bool		amsearcharray;
	bool		amsearchnulls;
	/* does AM have amgettuple interface? */
	bool		amhasgettuple;
	/* does AM have amgetbitmap interface? */
	bool		amhasgetbitmap;
	bool		amcanparallel;
	/* does AM have ammarkpos interface? */
	bool		amcanmarkpos;
	/* AM's cost estimator */
	/* Rather than include amapi.h here, we declare amcostestimate like this */
	void		(*amcostestimate) (struct PlannerInfo *, struct IndexPath *, double, Cost *, Cost *, Selectivity *, double *, double *) pg_node_attr(read_write_ignore);
} IndexOptInfo;

/*
 * ForeignKeyOptInfo
 *		Per-foreign-key information for planning/optimization
 *
 * The per-FK-column arrays can be fixed-size because we allow at most
 * INDEX_MAX_KEYS columns in a foreign key constraint.  Each array has
 * nkeys valid entries.
 */
typedef struct ForeignKeyOptInfo
{
	pg_node_attr(custom_read_write, no_copy_equal, no_read, no_query_jumble)

	NodeTag		type;

	/*
	 * Basic data about the foreign key (fetched from catalogs):
	 */

	/* RT index of the referencing table */
	Index		con_relid;
	/* RT index of the referenced table */
	Index		ref_relid;
	/* number of columns in the foreign key */
	int			nkeys;
	/* cols in referencing table */
	AttrNumber	conkey[INDEX_MAX_KEYS] pg_node_attr(array_size(nkeys));
	/* cols in referenced table */
	AttrNumber	confkey[INDEX_MAX_KEYS] pg_node_attr(array_size(nkeys));
	/* PK = FK operator OIDs */
	Oid			conpfeqop[INDEX_MAX_KEYS] pg_node_attr(array_size(nkeys));

	/*
	 * Derived info about whether FK's equality conditions match the query:
	 */

	/* # of FK cols matched by ECs */
	int			nmatched_ec;
	/* # of these ECs that are ec_has_const */
	int			nconst_ec;
	/* # of FK cols matched by non-EC rinfos */
	int			nmatched_rcols;
	/* total # of non-EC rinfos matched to FK */
	int			nmatched_ri;
	/* Pointer to eclass matching each column's condition, if there is one */
	struct EquivalenceClass *eclass[INDEX_MAX_KEYS];
	/* Pointer to eclass member for the referencing Var, if there is one */
	struct EquivalenceMember *fk_eclass_member[INDEX_MAX_KEYS];
	/* List of non-EC RestrictInfos matching each column's condition */
	List	   *rinfos[INDEX_MAX_KEYS];
} ForeignKeyOptInfo;

/*
 * StatisticExtInfo
 *		Information about extended statistics for planning/optimization
 *
 * Each pg_statistic_ext row is represented by one or more nodes of this
 * type, or even zero if ANALYZE has not computed them.
 */
typedef struct StatisticExtInfo
{
	pg_node_attr(no_copy_equal, no_read, no_query_jumble)

	NodeTag		type;

	/* OID of the statistics row */
	Oid			statOid;

	/* includes child relations */
	bool		inherit;

	/* back-link to statistic's table; don't print, else infinite recursion */
	RelOptInfo *rel pg_node_attr(read_write_ignore);

	/* statistics kind of this entry */
	char		kind;

	/* attnums of the columns covered */
	Bitmapset  *keys;

	/* expressions */
	List	   *exprs;
} StatisticExtInfo;

/*
 * JoinDomains
 *
 * A "join domain" defines the scope of applicability of deductions made via
 * the EquivalenceClass mechanism.  Roughly speaking, a join domain is a set
 * of base+OJ relations that are inner-joined together.  More precisely, it is
 * the set of relations at which equalities deduced from an EquivalenceClass
 * can be enforced or should be expected to hold.  The topmost JoinDomain
 * covers the whole query (so its jd_relids should equal all_query_rels).
 * An outer join creates a new JoinDomain that includes all base+OJ relids
 * within its nullable side, but (by convention) not the OJ's own relid.
 * A FULL join creates two new JoinDomains, one for each side.
 *
 * Notice that a rel that is below outer join(s) will thus appear to belong
 * to multiple join domains.  However, any of its Vars that appear in
 * EquivalenceClasses belonging to higher join domains will have nullingrel
 * bits preventing them from being evaluated at the rel's scan level, so that
 * we will not be able to derive enforceable-at-the-rel-scan-level clauses
 * from such ECs.  We define the join domain relid sets this way so that
 * domains can be said to be "higher" or "lower" when one domain relid set
 * includes another.
 *
 * The JoinDomains for a query are computed in deconstruct_jointree.
 * We do not copy JoinDomain structs once made, so they can be compared
 * for equality by simple pointer equality.
 */
typedef struct JoinDomain
{
	pg_node_attr(no_copy_equal, no_read, no_query_jumble)

	NodeTag		type;

	Relids		jd_relids;		/* all relids contained within the domain */
} JoinDomain;

/*
 * EquivalenceClasses
 *
 * Whenever we identify a mergejoinable equality clause A = B that is
 * not an outer-join clause, we create an EquivalenceClass containing
 * the expressions A and B to record this knowledge.  If we later find another
 * equivalence B = C, we add C to the existing EquivalenceClass; this may
 * require merging two existing EquivalenceClasses.  At the end of the qual
 * distribution process, we have sets of values that are known all transitively
 * equal to each other, where "equal" is according to the rules of the btree
 * operator family(s) shown in ec_opfamilies, as well as the collation shown
 * by ec_collation.  (We restrict an EC to contain only equalities whose
 * operators belong to the same set of opfamilies.  This could probably be
 * relaxed, but for now it's not worth the trouble, since nearly all equality
 * operators belong to only one btree opclass anyway.  Similarly, we suppose
 * that all or none of the input datatypes are collatable, so that a single
 * collation value is sufficient.)
 *
 * Strictly speaking, deductions from an EquivalenceClass hold only within
 * a "join domain", that is a set of relations that are innerjoined together
 * (see JoinDomain above).  For the most part we don't need to account for
 * this explicitly, because equality clauses from different join domains
 * will contain Vars that are not equal() because they have different
 * nullingrel sets, and thus we will never falsely merge ECs from different
 * join domains.  But Var-free (pseudoconstant) expressions lack that safety
 * feature.  We handle that by marking "const" EC members with the JoinDomain
 * of the clause they came from; two nominally-equal const members will be
 * considered different if they came from different JoinDomains.  This ensures
 * no false EquivalenceClass merges will occur.
 *
 * We also use EquivalenceClasses as the base structure for PathKeys, letting
 * us represent knowledge about different sort orderings being equivalent.
 * Since every PathKey must reference an EquivalenceClass, we will end up
 * with single-member EquivalenceClasses whenever a sort key expression has
 * not been equivalenced to anything else.  It is also possible that such an
 * EquivalenceClass will contain a volatile expression ("ORDER BY random()"),
 * which is a case that can't arise otherwise since clauses containing
 * volatile functions are never considered mergejoinable.  We mark such
 * EquivalenceClasses specially to prevent them from being merged with
 * ordinary EquivalenceClasses.  Also, for volatile expressions we have
 * to be careful to match the EquivalenceClass to the correct targetlist
 * entry: consider SELECT random() AS a, random() AS b ... ORDER BY b,a.
 * So we record the SortGroupRef of the originating sort clause.
 *
 * Derived equality clauses are stored in ec_derives_list. For small queries,
 * this list is scanned directly during lookup. For larger queries -- e.g.,
 * with many partitions or joins -- a hash table (ec_derives_hash) is built
 * when the list grows beyond a threshold, for faster lookup. When present,
 * the hash table contains the same RestrictInfos and is maintained alongside
 * the list. We retain the list even when the hash is used to simplify
 * serialization (e.g., in _outEquivalenceClass()) and support
 * EquivalenceClass merging.
 *
 * In contrast, ec_sources holds equality clauses that appear directly in the
 * query. These are typically few and do not require a hash table for lookup.
 *
 * 'ec_members' is a List of all !em_is_child EquivalenceMembers in the class.
 * EquivalenceMembers for any RELOPT_OTHER_MEMBER_REL and RELOPT_OTHER_JOINREL
 * relations are stored in the 'ec_childmembers' array in the index
 * corresponding to the relid, or first component relid in the case of
 * RELOPT_OTHER_JOINRELs.  'ec_childmembers' is NULL if the class has no child
 * EquivalenceMembers.
 *
 * For code wishing to look at EquivalenceMembers, if only parent-level
 * members are needed, then a simple foreach loop over ec_members is
 * sufficient.  When child members are also required, it is best to use the
 * functionality provided by EquivalenceMemberIterator.  This visits all
 * parent members and only the relevant child members.  The reason for this
 * is that large numbers of child EquivalenceMembers can exist in queries to
 * partitioned tables with many partitions.  The functionality provided by
 * EquivalenceMemberIterator allows efficient access to EquivalenceMembers
 * which belong to specific child relids.  See the header comments for
 * EquivalenceMemberIterator below for further details.
 *
 * NB: if ec_merged isn't NULL, this class has been merged into another, and
 * should be ignored in favor of using the pointed-to class.
 *
 * NB: EquivalenceClasses are never copied after creation.  Therefore,
 * copyObject() copies pointers to them as pointers, and equal() compares
 * pointers to EquivalenceClasses via pointer equality.  This is implemented
 * by putting copy_as_scalar and equal_as_scalar attributes on fields that
 * are pointers to EquivalenceClasses.  The same goes for EquivalenceMembers.
 */
typedef struct EquivalenceClass
{
	pg_node_attr(custom_read_write, no_copy_equal, no_read, no_query_jumble)

	NodeTag		type;

	List	   *ec_opfamilies;	/* btree operator family OIDs */
	Oid			ec_collation;	/* collation, if datatypes are collatable */
	int			ec_childmembers_size;	/* # elements in ec_childmembers */
	List	   *ec_members;		/* list of EquivalenceMembers */
	List	  **ec_childmembers;	/* array of Lists of child members */
	List	   *ec_sources;		/* list of generating RestrictInfos */
	List	   *ec_derives_list;	/* list of derived RestrictInfos */
	struct derives_hash *ec_derives_hash;	/* optional hash table for fast
											 * lookup; contains same
											 * RestrictInfos as list */
	Relids		ec_relids;		/* all relids appearing in ec_members, except
								 * for child members (see below) */
	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
	bool		ec_has_volatile;	/* the (sole) member is a volatile expr */
	bool		ec_broken;		/* failed to generate needed clauses? */
	Index		ec_sortref;		/* originating sortclause label, or 0 */
	Index		ec_min_security;	/* minimum security_level in ec_sources */
	Index		ec_max_security;	/* maximum security_level in ec_sources */
	struct EquivalenceClass *ec_merged; /* set if merged into another EC */
} EquivalenceClass;

/*
 * If an EC contains a constant, any PathKey depending on it must be
 * redundant, since there's only one possible value of the key.
 */
#define EC_MUST_BE_REDUNDANT(eclass)  \
	((eclass)->ec_has_const)

/*
 * EquivalenceMember - one member expression of an EquivalenceClass
 *
 * em_is_child signifies that this element was built by transposing a member
 * for an appendrel parent relation to represent the corresponding expression
 * for an appendrel child.  These members are used for determining the
 * pathkeys of scans on the child relation and for explicitly sorting the
 * child when necessary to build a MergeAppend path for the whole appendrel
 * tree.  An em_is_child member has no impact on the properties of the EC as a
 * whole; in particular the EC's ec_relids field does NOT include the child
 * relation.  em_is_child members aren't stored in the ec_members List of the
 * EC and instead they're stored and indexed by the relids of the child
 * relation they represent in ec_childmembers.  An em_is_child member
 * should never be marked em_is_const nor cause ec_has_const or
 * ec_has_volatile to be set, either.  Thus, em_is_child members are not
 * really full-fledged members of the EC, but just reflections or
 * doppelgangers of real members.  Most operations on EquivalenceClasses
 * should ignore em_is_child members by only inspecting members in the
 * ec_members list.  Callers that require inspecting child members should do
 * so using an EquivalenceMemberIterator and should test em_relids to make
 * sure they only consider relevant members.
 *
 * em_datatype is usually the same as exprType(em_expr), but can be
 * different when dealing with a binary-compatible opfamily; in particular
 * anyarray_ops would never work without this.  Use em_datatype when
 * looking up a specific btree operator to work with this expression.
 */
typedef struct EquivalenceMember
{
	pg_node_attr(no_copy_equal, no_read, no_query_jumble)

	NodeTag		type;

	Expr	   *em_expr;		/* the expression represented */
	Relids		em_relids;		/* all relids appearing in em_expr */
	bool		em_is_const;	/* expression is pseudoconstant? */
	bool		em_is_child;	/* derived version for a child relation? */
	Oid			em_datatype;	/* the "nominal type" used by the opfamily */
	JoinDomain *em_jdomain;		/* join domain containing the source clause */
	/* if em_is_child is true, this links to corresponding EM for top parent */
	struct EquivalenceMember *em_parent pg_node_attr(read_write_ignore);
} EquivalenceMember;

/*
 * EquivalenceMemberIterator
 *
 * EquivalenceMemberIterator allows efficient access to sets of
 * EquivalenceMembers for callers which require access to child members.
 * Because partitioning workloads can result in large numbers of child
 * members, the child members are not stored in the EquivalenceClass's
 * ec_members List.  Instead, these are stored in the EquivalenceClass's
 * ec_childmembers array of Lists.  The functionality provided by
 * EquivalenceMemberIterator aims to provide efficient access to parent
 * members and child members belonging to specific child relids.
 *
 * Currently, there is only one way to initialize and iterate over an
 * EquivalenceMemberIterator and that is via the setup_eclass_member_iterator
 * and eclass_member_iterator_next functions.  The iterator object is
 * generally a local variable which is passed by address to
 * setup_eclass_member_iterator.  The calling function defines which
 * EquivalenceClass the iterator should be looking at and which child
 * relids to also return members for.  child_relids can be passed as NULL, but
 * the caller may as well just perform a foreach loop over ec_members as only
 * parent-level members will be returned in that case.
 *
 * When calling the next function on an EquivalenceMemberIterator, all
 * parent-level EquivalenceMembers are returned first, followed by all child
 * members for the specified 'child_relids' for all child members which were
 * indexed by any of the specified 'child_relids' in add_child_eq_member().
 *
 * Code using the iterator method of finding EquivalenceMembers will generally
 * always want to ensure the returned member matches their search criteria
 * rather than relying on the filtering to be done for them as all parent
 * members are returned and for members belonging to RELOPT_OTHER_JOINREL
 * rels, the member's em_relids may be a superset of the specified
 * 'child_relids', which might not be what the caller wants.
 *
 * The most common way to use this iterator is as follows:
 * -----
 * EquivalenceMemberIterator		it;
 * EquivalenceMember			   *em;
 *
 * setup_eclass_member_iterator(&it, ec, child_relids);
 * while ((em = eclass_member_iterator_next(&it)) != NULL)
 * {
 *		...
 * }
 * -----
 * It is not valid to call eclass_member_iterator_next() after it has returned
 * NULL for any given EquivalenceMemberIterator.  Individual fields within
 * the EquivalenceMemberIterator struct must not be accessed by callers.
 */
typedef struct
{
	EquivalenceClass *ec;		/* The EquivalenceClass to iterate over */
	int			current_relid;	/* Current relid position within 'relids'. -1
								 * when still looping over ec_members and -2
								 * at the end of iteration */
	Relids		child_relids;	/* Relids of child relations of interest.
								 * Non-child rels are ignored */
	ListCell   *current_cell;	/* Next cell to return within current_list */
	List	   *current_list;	/* Current list of members being returned */
} EquivalenceMemberIterator;

/*
 * PathKeys
 *
 * The sort ordering of a path is represented by a list of PathKey nodes.
 * An empty list implies no known ordering.  Otherwise the first item
 * represents the primary sort key, the second the first secondary sort key,
 * etc.  The value being sorted is represented by linking to an
 * EquivalenceClass containing that value and including pk_opfamily among its
 * ec_opfamilies.  The EquivalenceClass tells which collation to use, too.
 * This is a convenient method because it makes it trivial to detect
 * equivalent and closely-related orderings. (See optimizer/README for more
 * information.)
 *
 * Note: pk_cmptype is either COMPARE_LT (for ASC) or COMPARE_GT (for DESC).
 */
typedef struct PathKey
{
	pg_node_attr(no_read, no_query_jumble)

	NodeTag		type;

	/* the value that is ordered */
	EquivalenceClass *pk_eclass pg_node_attr(copy_as_scalar, equal_as_scalar);
	Oid			pk_opfamily;	/* index opfamily defining the ordering */
	CompareType pk_cmptype;		/* sort direction (ASC or DESC) */
	bool		pk_nulls_first; /* do NULLs come before normal values? */
} PathKey;

/*
 * Contains an order of group-by clauses and the corresponding list of
 * pathkeys.
 *
 * The elements of 'clauses' list should have the same order as the head of
 * 'pathkeys' list.  The tleSortGroupRef of the clause should be equal to
 * ec_sortref of the pathkey equivalence class.  If there are redundant
 * clauses with the same tleSortGroupRef, they must be grouped together.
 */
typedef struct GroupByOrdering
{
	NodeTag		type;

	List	   *pathkeys;
	List	   *clauses;
} GroupByOrdering;

/*
 * VolatileFunctionStatus -- allows nodes to cache their
 * contain_volatile_functions properties. VOLATILITY_UNKNOWN means not yet
 * determined.
 */
typedef enum VolatileFunctionStatus
{
	VOLATILITY_UNKNOWN = 0,
	VOLATILITY_VOLATILE,
	VOLATILITY_NOVOLATILE,
} VolatileFunctionStatus;

/*
 * PathTarget
 *
 * This struct contains what we need to know during planning about the
 * targetlist (output columns) that a Path will compute.  Each RelOptInfo
 * includes a default PathTarget, which its individual Paths may simply
 * reference.  However, in some cases a Path may compute outputs different
 * from other Paths, and in that case we make a custom PathTarget for it.
 * For example, an indexscan might return index expressions that would
 * otherwise need to be explicitly calculated.  (Note also that "upper"
 * relations generally don't have useful default PathTargets.)
 *
 * exprs contains bare expressions; they do not have TargetEntry nodes on top,
 * though those will appear in finished Plans.
 *
 * sortgrouprefs[] is an array of the same length as exprs, containing the
 * corresponding sort/group refnos, or zeroes for expressions not referenced
 * by sort/group clauses.  If sortgrouprefs is NULL (which it generally is in
 * RelOptInfo.reltarget targets; only upper-level Paths contain this info),
 * we have not identified sort/group columns in this tlist.  This allows us to
 * deal with sort/group refnos when needed with less expense than including
 * TargetEntry nodes in the exprs list.
 */
typedef struct PathTarget
{
	pg_node_attr(no_copy_equal, no_read, no_query_jumble)

	NodeTag		type;

	/* list of expressions to be computed */
	List	   *exprs;

	/* corresponding sort/group refnos, or 0 */
	Index	   *sortgrouprefs pg_node_attr(array_size(exprs));

	/* cost of evaluating the expressions */
	QualCost	cost;

	/* estimated avg width of result tuples */
	int			width;

	/* indicates if exprs contain any volatile functions */
	VolatileFunctionStatus has_volatile_expr;
} PathTarget;

/* Convenience macro to get a sort/group refno from a PathTarget */
#define get_pathtarget_sortgroupref(target, colno) \
	((target)->sortgrouprefs ? (target)->sortgrouprefs[colno] : (Index) 0)


/*
 * ParamPathInfo
 *
 * All parameterized paths for a given relation with given required outer rels
 * link to a single ParamPathInfo, which stores common information such as
 * the estimated rowcount for this parameterization.  We do this partly to
 * avoid recalculations, but mostly to ensure that the estimated rowcount
 * is in fact the same for every such path.
 *
 * Note: ppi_clauses is only used in ParamPathInfos for base relation paths;
 * in join cases it's NIL because the set of relevant clauses varies depending
 * on how the join is formed.  The relevant clauses will appear in each
 * parameterized join path's joinrestrictinfo list, instead.  ParamPathInfos
 * for append relations don't bother with this, either.
 *
 * ppi_serials is the set of rinfo_serial numbers for quals that are enforced
 * by this path.  As with ppi_clauses, it's only maintained for baserels.
 * (We could construct it on-the-fly from ppi_clauses, but it seems better
 * to materialize a copy.)
 */
typedef struct ParamPathInfo
{
	pg_node_attr(no_copy_equal, no_read, no_query_jumble)

	NodeTag		type;

	Relids		ppi_req_outer;	/* rels supplying parameters used by path */
	Cardinality ppi_rows;		/* estimated number of result tuples */
	List	   *ppi_clauses;	/* join clauses available from outer rels */
	Bitmapset  *ppi_serials;	/* set of rinfo_serial for enforced quals */
} ParamPathInfo;


/*
 * Type "Path" is used as-is for sequential-scan paths, as well as some other
 * simple plan types that we don't need any extra information in the path for.
 * For other path types it is the first component of a larger struct.
 *
 * "pathtype" is the NodeTag of the Plan node we could build from this Path.
 * It is partially redundant with the Path's NodeTag, but allows us to use
 * the same Path type for multiple Plan types when there is no need to
 * distinguish the Plan type during path processing.
 *
 * "parent" identifies the relation this Path scans, and "pathtarget"
 * describes the precise set of output columns the Path would compute.
 * In simple cases all Paths for a given rel share the same targetlist,
 * which we represent by having path->pathtarget equal to parent->reltarget.
 *
 * "param_info", if not NULL, links to a ParamPathInfo that identifies outer
 * relation(s) that provide parameter values to each scan of this path.
 * That means this path can only be joined to those rels by means of nestloop
 * joins with this path on the inside.  Also note that a parameterized path
 * is responsible for testing all "movable" joinclauses involving this rel
 * and the specified outer rel(s).
 *
 * "rows" is the same as parent->rows in simple paths, but in parameterized
 * paths it can be less than parent->rows, reflecting the fact that we've
 * filtered by extra join conditions.
 *
 * "pathkeys" is a List of PathKey nodes (see above), describing the sort
 * ordering of the path's output rows.
 *
 * We do not support copying Path trees, mainly because the circular linkages
 * between RelOptInfo and Path nodes can't be handled easily in a simple
 * depth-first traversal.  We also don't have read support at the moment.
 */
typedef struct Path
{
	pg_node_attr(no_copy_equal, no_read, no_query_jumble)

	NodeTag		type;

	/* tag identifying scan/join method */
	NodeTag		pathtype;

	/*
	 * the relation this path can build
	 *
	 * We do NOT print the parent, else we'd be in infinite recursion.  We can
	 * print the parent's relids for identification purposes, though.
	 */
	RelOptInfo *parent pg_node_attr(write_only_relids);

	/*
	 * list of Vars/Exprs, cost, width
	 *
	 * We print the pathtarget only if it's not the default one for the rel.
	 */
	PathTarget *pathtarget pg_node_attr(write_only_nondefault_pathtarget);

	/*
	 * parameterization info, or NULL if none
	 *
	 * We do not print the whole of param_info, since it's printed via
	 * RelOptInfo; it's sufficient and less cluttering to print just the
	 * required outer relids.
	 */
	ParamPathInfo *param_info pg_node_attr(write_only_req_outer);

	/* engage parallel-aware logic? */
	bool		parallel_aware;
	/* OK to use as part of parallel plan? */
	bool		parallel_safe;
	/* desired # of workers; 0 = not parallel */
	int			parallel_workers;

	/* estimated size/costs for path (see costsize.c for more info) */
	Cardinality rows;			/* estimated number of result tuples */
	int			disabled_nodes; /* count of disabled nodes */
	Cost		startup_cost;	/* cost expended before fetching any tuples */
	Cost		total_cost;		/* total cost (assuming all tuples fetched) */

	/* sort ordering of path's output; a List of PathKey nodes; see above */
	List	   *pathkeys;
} Path;

/* Macro for extracting a path's parameterization relids; beware double eval */
#define PATH_REQ_OUTER(path)  \
	((path)->param_info ? (path)->param_info->ppi_req_outer : (Relids) NULL)

/*----------
 * IndexPath represents an index scan over a single index.
 *
 * This struct is used for both regular indexscans and index-only scans;
 * path.pathtype is T_IndexScan or T_IndexOnlyScan to show which is meant.
 *
 * 'indexinfo' is the index to be scanned.
 *
 * 'indexclauses' is a list of IndexClause nodes, each representing one
 * index-checkable restriction, with implicit AND semantics across the list.
 * An empty list implies a full index scan.
 *
 * 'indexorderbys', if not NIL, is a list of ORDER BY expressions that have
 * been found to be usable as ordering operators for an amcanorderbyop index.
 * The list must match the path's pathkeys, ie, one expression per pathkey
 * in the same order.  These are not RestrictInfos, just bare expressions,
 * since they generally won't yield booleans.  It's guaranteed that each
 * expression has the index key on the left side of the operator.
 *
 * 'indexorderbycols' is an integer list of index column numbers (zero-based)
 * of the same length as 'indexorderbys', showing which index column each
 * ORDER BY expression is meant to be used with.  (There is no restriction
 * on which index column each ORDER BY can be used with.)
 *
 * 'indexscandir' is one of:
 *		ForwardScanDirection: forward scan of an index
 *		BackwardScanDirection: backward scan of an ordered index
 * Unordered indexes will always have an indexscandir of ForwardScanDirection.
 *
 * 'indextotalcost' and 'indexselectivity' are saved in the IndexPath so that
 * we need not recompute them when considering using the same index in a
 * bitmap index/heap scan (see BitmapHeapPath).  The costs of the IndexPath
 * itself represent the costs of an IndexScan or IndexOnlyScan plan type.
 *----------
 */
typedef struct IndexPath
{
	Path		path;
	IndexOptInfo *indexinfo;
	List	   *indexclauses;
	List	   *indexorderbys;
	List	   *indexorderbycols;
	ScanDirection indexscandir;
	Cost		indextotalcost;
	Selectivity indexselectivity;
} IndexPath;

/*
 * Each IndexClause references a RestrictInfo node from the query's WHERE
 * or JOIN conditions, and shows how that restriction can be applied to
 * the particular index.  We support both indexclauses that are directly
 * usable by the index machinery, which are typically of the form
 * "indexcol OP pseudoconstant", and those from which an indexable qual
 * can be derived.  The simplest such transformation is that a clause
 * of the form "pseudoconstant OP indexcol" can be commuted to produce an
 * indexable qual (the index machinery expects the indexcol to be on the
 * left always).  Another example is that we might be able to extract an
 * indexable range condition from a LIKE condition, as in "x LIKE 'foo%bar'"
 * giving rise to "x >= 'foo' AND x < 'fop'".  Derivation of such lossy
 * conditions is done by a planner support function attached to the
 * indexclause's top-level function or operator.
 *
 * indexquals is a list of RestrictInfos for the directly-usable index
 * conditions associated with this IndexClause.  In the simplest case
 * it's a one-element list whose member is iclause->rinfo.  Otherwise,
 * it contains one or more directly-usable indexqual conditions extracted
 * from the given clause.  The 'lossy' flag indicates whether the
 * indexquals are semantically equivalent to the original clause, or
 * represent a weaker condition.
 *
 * Normally, indexcol is the index of the single index column the clause
 * works on, and indexcols is NIL.  But if the clause is a RowCompareExpr,
 * indexcol is the index of the leading column, and indexcols is a list of
 * all the affected columns.  (Note that indexcols matches up with the
 * columns of the actual indexable RowCompareExpr in indexquals, which
 * might be different from the original in rinfo.)
 *
 * An IndexPath's IndexClause list is required to be ordered by index
 * column, i.e. the indexcol values must form a nondecreasing sequence.
 * (The order of multiple clauses for the same index column is unspecified.)
 */
typedef struct IndexClause
{
	pg_node_attr(no_copy_equal, no_read, no_query_jumble)

	NodeTag		type;
	struct RestrictInfo *rinfo; /* original restriction or join clause */
	List	   *indexquals;		/* indexqual(s) derived from it */
	bool		lossy;			/* are indexquals a lossy version of clause? */
	AttrNumber	indexcol;		/* index column the clause uses (zero-based) */
	List	   *indexcols;		/* multiple index columns, if RowCompare */
} IndexClause;

/*
 * BitmapHeapPath represents one or more indexscans that generate TID bitmaps
 * instead of directly accessing the heap, followed by AND/OR combinations
 * to produce a single bitmap, followed by a heap scan that uses the bitmap.
 * Note that the output is always considered unordered, since it will come
 * out in physical heap order no matter what the underlying indexes did.
 *
 * The individual indexscans are represented by IndexPath nodes, and any
 * logic on top of them is represented by a tree of BitmapAndPath and
 * BitmapOrPath nodes.  Notice that we can use the same IndexPath node both
 * to represent a regular (or index-only) index scan plan, and as the child
 * of a BitmapHeapPath that represents scanning the same index using a
 * BitmapIndexScan.  The startup_cost and total_cost figures of an IndexPath
 * always represent the costs to use it as a regular (or index-only)
 * IndexScan.  The costs of a BitmapIndexScan can be computed using the
 * IndexPath's indextotalcost and indexselectivity.
 */
typedef struct BitmapHeapPath
{
	Path		path;
	Path	   *bitmapqual;		/* IndexPath, BitmapAndPath, BitmapOrPath */
} BitmapHeapPath;

/*
 * BitmapAndPath represents a BitmapAnd plan node; it can only appear as
 * part of the substructure of a BitmapHeapPath.  The Path structure is
 * a bit more heavyweight than we really need for this, but for simplicity
 * we make it a derivative of Path anyway.
 */
typedef struct BitmapAndPath
{
	Path		path;
	List	   *bitmapquals;	/* IndexPaths and BitmapOrPaths */
	Selectivity bitmapselectivity;
} BitmapAndPath;

/*
 * BitmapOrPath represents a BitmapOr plan node; it can only appear as
 * part of the substructure of a BitmapHeapPath.  The Path structure is
 * a bit more heavyweight than we really need for this, but for simplicity
 * we make it a derivative of Path anyway.
 */
typedef struct BitmapOrPath
{
	Path		path;
	List	   *bitmapquals;	/* IndexPaths and BitmapAndPaths */
	Selectivity bitmapselectivity;
} BitmapOrPath;

/*
 * TidPath represents a scan by TID
 *
 * tidquals is an implicitly OR'ed list of qual expressions of the form
 * "CTID = pseudoconstant", or "CTID = ANY(pseudoconstant_array)",
 * or a CurrentOfExpr for the relation.
 */
typedef struct TidPath
{
	Path		path;
	List	   *tidquals;		/* qual(s) involving CTID = something */
} TidPath;

/*
 * TidRangePath represents a scan by a contiguous range of TIDs
 *
 * tidrangequals is an implicitly AND'ed list of qual expressions of the form
 * "CTID relop pseudoconstant", where relop is one of >,>=,<,<=.
 */
typedef struct TidRangePath
{
	Path		path;
	List	   *tidrangequals;
} TidRangePath;

/*
 * SubqueryScanPath represents a scan of an unflattened subquery-in-FROM
 *
 * Note that the subpath comes from a different planning domain; for example
 * RTE indexes within it mean something different from those known to the
 * SubqueryScanPath.  path.parent->subroot is the planning context needed to
 * interpret the subpath.
 */
typedef struct SubqueryScanPath
{
	Path		path;
	Path	   *subpath;		/* path representing subquery execution */
} SubqueryScanPath;

/*
 * ForeignPath represents a potential scan of a foreign table, foreign join
 * or foreign upper-relation.
 *
 * In the case of a foreign join, fdw_restrictinfo stores the RestrictInfos to
 * apply to the join, which are used by createplan.c to get pseudoconstant
 * clauses evaluated as one-time quals in a gating Result plan node.
 *
 * fdw_private stores FDW private data about the scan.  While fdw_private is
 * not actually touched by the core code during normal operations, it's
 * generally a good idea to use a representation that can be dumped by
 * nodeToString(), so that you can examine the structure during debugging
 * with tools like pprint().
 */
typedef struct ForeignPath
{
	Path		path;
	Path	   *fdw_outerpath;
	List	   *fdw_restrictinfo;
	List	   *fdw_private;
} ForeignPath;

/*
 * CustomPath represents a table scan or a table join done by some out-of-core
 * extension.
 *
 * We provide a set of hooks here - which the provider must take care to set
 * up correctly - to allow extensions to supply their own methods of scanning
 * a relation or join relations.  For example, a provider might provide GPU
 * acceleration, a cache-based scan, or some other kind of logic we haven't
 * dreamed up yet.
 *
 * CustomPaths can be injected into the planning process for a base or join
 * relation by set_rel_pathlist_hook or set_join_pathlist_hook functions,
 * respectively.
 *
 * In the case of a table join, custom_restrictinfo stores the RestrictInfos
 * to apply to the join, which are used by createplan.c to get pseudoconstant
 * clauses evaluated as one-time quals in a gating Result plan node.
 *
 * Core code must avoid assuming that the CustomPath is only as large as
 * the structure declared here; providers are allowed to make it the first
 * element in a larger structure.  (Since the planner never copies Paths,
 * this doesn't add any complication.)  However, for consistency with the
 * FDW case, we provide a "custom_private" field in CustomPath; providers
 * may prefer to use that rather than define another struct type.
 */

struct CustomPathMethods;

typedef struct CustomPath
{
	Path		path;
	uint32		flags;			/* mask of CUSTOMPATH_* flags, see
								 * nodes/extensible.h */
	List	   *custom_paths;	/* list of child Path nodes, if any */
	List	   *custom_restrictinfo;
	List	   *custom_private;
	const struct CustomPathMethods *methods;
} CustomPath;

/*
 * AppendPath represents an Append plan, ie, successive execution of
 * several member plans.
 *
 * For partial Append, 'subpaths' contains non-partial subpaths followed by
 * partial subpaths.
 *
 * Whenever accumulate_append_subpath() allows us to consolidate multiple
 * levels of Append paths down to one, we store the RTI sets for the omitted
 * paths in child_append_relid_sets. This is not necessary for planning or
 * execution; we do it for the benefit of code that wants to inspect the
 * final plan and understand how it came to be.
 *
 * Note: it is possible for "subpaths" to contain only one, or even no,
 * elements.  These cases are optimized during create_append_plan.
 * In particular, an AppendPath with no subpaths is a "dummy" path that
 * is created to represent the case that a relation is provably empty.
 * (This is a convenient representation because it means that when we build
 * an appendrel and find that all its children have been excluded, no extra
 * action is needed to recognize the relation as dummy.)
 */
typedef struct AppendPath
{
	Path		path;
	List	   *subpaths;		/* list of component Paths */
	/* Index of first partial path in subpaths; list_length(subpaths) if none */
	int			first_partial_path;
	Cardinality limit_tuples;	/* hard limit on output tuples, or -1 */
	List	   *child_append_relid_sets;
} AppendPath;

#define IS_DUMMY_APPEND(p) \
	(IsA((p), AppendPath) && ((AppendPath *) (p))->subpaths == NIL)

/*
 * A relation that's been proven empty will have one path that is dummy
 * (but might have projection paths on top).  For historical reasons,
 * this is provided as a macro that wraps is_dummy_rel().
 */
#define IS_DUMMY_REL(r) is_dummy_rel(r)
extern bool is_dummy_rel(RelOptInfo *rel);

/*
 * MergeAppendPath represents a MergeAppend plan, ie, the merging of sorted
 * results from several member plans to produce similarly-sorted output.
 *
 * child_append_relid_sets has the same meaning here as for AppendPath.
 */
typedef struct MergeAppendPath
{
	Path		path;
	List	   *subpaths;		/* list of component Paths */
	Cardinality limit_tuples;	/* hard limit on output tuples, or -1 */
	List	   *child_append_relid_sets;
} MergeAppendPath;

/*
 * GroupResultPath represents use of a Result plan node to compute the
 * output of a degenerate GROUP BY case, wherein we know we should produce
 * exactly one row, which might then be filtered by a HAVING qual.
 *
 * Note that quals is a list of bare clauses, not RestrictInfos.
 */
typedef struct GroupResultPath
{
	Path		path;
	List	   *quals;
} GroupResultPath;

/*
 * MaterialPath represents use of a Material plan node, i.e., caching of
 * the output of its subpath.  This is used when the subpath is expensive
 * and needs to be scanned repeatedly, or when we need mark/restore ability
 * and the subpath doesn't have it.
 */
typedef struct MaterialPath
{
	Path		path;
	Path	   *subpath;
} MaterialPath;

/*
 * MemoizePath represents a Memoize plan node, i.e., a cache that caches
 * tuples from parameterized paths to save the underlying node from having to
 * be rescanned for parameter values which are already cached.
 */
typedef struct MemoizePath
{
	Path		path;
	Path	   *subpath;		/* outerpath to cache tuples from */
	List	   *hash_operators; /* OIDs of hash equality ops for cache keys */
	List	   *param_exprs;	/* expressions that are cache keys */
	bool		singlerow;		/* true if the cache entry is to be marked as
								 * complete after caching the first record. */
	bool		binary_mode;	/* true when cache key should be compared bit
								 * by bit, false when using hash equality ops */
	uint32		est_entries;	/* The maximum number of entries that the
								 * planner expects will fit in the cache, or 0
								 * if unknown */
	Cardinality est_calls;		/* expected number of rescans */
	Cardinality est_unique_keys;	/* estimated unique keys, for EXPLAIN */
	double		est_hit_ratio;	/* estimated cache hit ratio, for EXPLAIN */
} MemoizePath;

/*
 * GatherPath runs several copies of a plan in parallel and collects the
 * results.  The parallel leader may also execute the plan, unless the
 * single_copy flag is set.
 */
typedef struct GatherPath
{
	Path		path;
	Path	   *subpath;		/* path for each worker */
	bool		single_copy;	/* don't execute path more than once */
	int			num_workers;	/* number of workers sought to help */
} GatherPath;

/*
 * GatherMergePath runs several copies of a plan in parallel and collects
 * the results, preserving their common sort order.
 */
typedef struct GatherMergePath
{
	Path		path;
	Path	   *subpath;		/* path for each worker */
	int			num_workers;	/* number of workers sought to help */
} GatherMergePath;


/*
 * All join-type paths share these fields.
 */

typedef struct JoinPath
{
	pg_node_attr(abstract)

	Path		path;

	JoinType	jointype;

	bool		inner_unique;	/* each outer tuple provably matches no more
								 * than one inner tuple */

	Path	   *outerjoinpath;	/* path for the outer side of the join */
	Path	   *innerjoinpath;	/* path for the inner side of the join */

	List	   *joinrestrictinfo;	/* RestrictInfos to apply to join */

	/*
	 * See the notes for RelOptInfo and ParamPathInfo to understand why
	 * joinrestrictinfo is needed in JoinPath, and can't be merged into the
	 * parent RelOptInfo.
	 */
} JoinPath;

/*
 * A nested-loop path needs no special fields.
 */

typedef struct NestPath
{
	JoinPath	jpath;
} NestPath;

/*
 * A mergejoin path has these fields.
 *
 * Unlike other path types, a MergePath node doesn't represent just a single
 * run-time plan node: it can represent up to four.  Aside from the MergeJoin
 * node itself, there can be a Sort node for the outer input, a Sort node
 * for the inner input, and/or a Material node for the inner input.  We could
 * represent these nodes by separate path nodes, but considering how many
 * different merge paths are investigated during a complex join problem,
 * it seems better to avoid unnecessary palloc overhead.
 *
 * path_mergeclauses lists the clauses (in the form of RestrictInfos)
 * that will be used in the merge.
 *
 * Note that the mergeclauses are a subset of the parent relation's
 * restriction-clause list.  Any join clauses that are not mergejoinable
 * appear only in the parent's restrict list, and must be checked by a
 * qpqual at execution time.
 *
 * outersortkeys (resp. innersortkeys) is NIL if the outer path
 * (resp. inner path) is already ordered appropriately for the
 * mergejoin.  If it is not NIL then it is a PathKeys list describing
 * the ordering that must be created by an explicit Sort node.
 *
 * outer_presorted_keys is the number of presorted keys of the outer
 * path that match outersortkeys.  It is used to determine whether
 * explicit incremental sort can be applied when outersortkeys is not
 * NIL.  We do not track the number of presorted keys of the inner
 * path, as incremental sort currently does not support mark/restore.
 *
 * skip_mark_restore is true if the executor need not do mark/restore calls.
 * Mark/restore overhead is usually required, but can be skipped if we know
 * that the executor need find only one match per outer tuple, and that the
 * mergeclauses are sufficient to identify a match.  In such cases the
 * executor can immediately advance the outer relation after processing a
 * match, and therefore it need never back up the inner relation.
 *
 * materialize_inner is true if a Material node should be placed atop the
 * inner input.  This may appear with or without an inner Sort step.
 */

typedef struct MergePath
{
	JoinPath	jpath;
	List	   *path_mergeclauses;	/* join clauses to be used for merge */
	List	   *outersortkeys;	/* keys for explicit sort, if any */
	List	   *innersortkeys;	/* keys for explicit sort, if any */
	int			outer_presorted_keys;	/* number of presorted keys of the
										 * outer path */
	bool		skip_mark_restore;	/* can executor skip mark/restore? */
	bool		materialize_inner;	/* add Materialize to inner? */
} MergePath;

/*
 * A hashjoin path has these fields.
 *
 * The remarks above for mergeclauses apply for hashclauses as well.
 *
 * Hashjoin does not care what order its inputs appear in, so we have
 * no need for sortkeys.
 */

typedef struct HashPath
{
	JoinPath	jpath;
	List	   *path_hashclauses;	/* join clauses used for hashing */
	int			num_batches;	/* number of batches expected */
	Cardinality inner_rows_total;	/* total inner rows expected */
} HashPath;

/*
 * ProjectionPath represents a projection (that is, targetlist computation)
 *
 * Nominally, this path node represents using a Result plan node to do a
 * projection step.  However, if the input plan node supports projection,
 * we can just modify its output targetlist to do the required calculations
 * directly, and not need a Result.  In some places in the planner we can just
 * jam the desired PathTarget into the input path node (and adjust its cost
 * accordingly), so we don't need a ProjectionPath.  But in other places
 * it's necessary to not modify the input path node, so we need a separate
 * ProjectionPath node, which is marked dummy to indicate that we intend to
 * assign the work to the input plan node.  The estimated cost for the
 * ProjectionPath node will account for whether a Result will be used or not.
 */
typedef struct ProjectionPath
{
	Path		path;
	Path	   *subpath;		/* path representing input source */
	bool		dummypp;		/* true if no separate Result is needed */
} ProjectionPath;

/*
 * ProjectSetPath represents evaluation of a targetlist that includes
 * set-returning function(s), which will need to be implemented by a
 * ProjectSet plan node.
 */
typedef struct ProjectSetPath
{
	Path		path;
	Path	   *subpath;		/* path representing input source */
} ProjectSetPath;

/*
 * SortPath represents an explicit sort step
 *
 * The sort keys are, by definition, the same as path.pathkeys.
 *
 * Note: the Sort plan node cannot project, so path.pathtarget must be the
 * same as the input's pathtarget.
 */
typedef struct SortPath
{
	Path		path;
	Path	   *subpath;		/* path representing input source */
} SortPath;

/*
 * IncrementalSortPath represents an incremental sort step
 *
 * This is like a regular sort, except some leading key columns are assumed
 * to be ordered already.
 */
typedef struct IncrementalSortPath
{
	SortPath	spath;
	int			nPresortedCols; /* number of presorted columns */
} IncrementalSortPath;

/*
 * GroupPath represents grouping (of presorted input)
 *
 * groupClause represents the columns to be grouped on; the input path
 * must be at least that well sorted.
 *
 * We can also apply a qual to the grouped rows (equivalent of HAVING)
 */
typedef struct GroupPath
{
	Path		path;
	Path	   *subpath;		/* path representing input source */
	List	   *groupClause;	/* a list of SortGroupClause's */
	List	   *qual;			/* quals (HAVING quals), if any */
} GroupPath;

/*
 * UniquePath represents adjacent-duplicate removal (in presorted input)
 *
 * The columns to be compared are the first numkeys columns of the path's
 * pathkeys.  The input is presumed already sorted that way.
 */
typedef struct UniquePath
{
	Path		path;
	Path	   *subpath;		/* path representing input source */
	int			numkeys;		/* number of pathkey columns to compare */
} UniquePath;

/*
 * AggPath represents generic computation of aggregate functions
 *
 * This may involve plain grouping (but not grouping sets), using either
 * sorted or hashed grouping; for the AGG_SORTED case, the input must be
 * appropriately presorted.
 */
typedef struct AggPath
{
	Path		path;
	Path	   *subpath;		/* path representing input source */
	AggStrategy aggstrategy;	/* basic strategy, see nodes.h */
	AggSplit	aggsplit;		/* agg-splitting mode, see nodes.h */
	Cardinality numGroups;		/* estimated number of groups in input */
	uint64		transitionSpace;	/* for pass-by-ref transition data */
	List	   *groupClause;	/* a list of SortGroupClause's */
	List	   *qual;			/* quals (HAVING quals), if any */
} AggPath;

/*
 * Various annotations used for grouping sets in the planner.
 */

typedef struct GroupingSetData
{
	pg_node_attr(no_copy_equal, no_read, no_query_jumble)

	NodeTag		type;
	List	   *set;			/* grouping set as list of sortgrouprefs */
	Cardinality numGroups;		/* est. number of result groups */
} GroupingSetData;

typedef struct RollupData
{
	pg_node_attr(no_copy_equal, no_read, no_query_jumble)

	NodeTag		type;
	List	   *groupClause;	/* applicable subset of parse->groupClause */
	List	   *gsets;			/* lists of integer indexes into groupClause */
	List	   *gsets_data;		/* list of GroupingSetData */
	Cardinality numGroups;		/* est. number of result groups */
	bool		hashable;		/* can be hashed */
	bool		is_hashed;		/* to be implemented as a hashagg */
} RollupData;

/*
 * GroupingSetsPath represents a GROUPING SETS aggregation
 */

typedef struct GroupingSetsPath
{
	Path		path;
	Path	   *subpath;		/* path representing input source */
	AggStrategy aggstrategy;	/* basic strategy */
	List	   *rollups;		/* list of RollupData */
	List	   *qual;			/* quals (HAVING quals), if any */
	uint64		transitionSpace;	/* for pass-by-ref transition data */
} GroupingSetsPath;

/*
 * MinMaxAggPath represents computation of MIN/MAX aggregates from indexes
 */
typedef struct MinMaxAggPath
{
	Path		path;
	List	   *mmaggregates;	/* list of MinMaxAggInfo */
	List	   *quals;			/* HAVING quals, if any */
} MinMaxAggPath;

/*
 * WindowAggPath represents generic computation of window functions
 */
typedef struct WindowAggPath
{
	Path		path;
	Path	   *subpath;		/* path representing input source */
	WindowClause *winclause;	/* WindowClause we'll be using */
	List	   *qual;			/* lower-level WindowAgg runconditions */
	List	   *runCondition;	/* OpExpr List to short-circuit execution */
	bool		topwindow;		/* false for all apart from the WindowAgg
								 * that's closest to the root of the plan */
} WindowAggPath;

/*
 * SetOpPath represents a set-operation, that is INTERSECT or EXCEPT
 */
typedef struct SetOpPath
{
	Path		path;
	Path	   *leftpath;		/* paths representing input sources */
	Path	   *rightpath;
	SetOpCmd	cmd;			/* what to do, see nodes.h */
	SetOpStrategy strategy;		/* how to do it, see nodes.h */
	List	   *groupList;		/* SortGroupClauses identifying target cols */
	Cardinality numGroups;		/* estimated number of groups in left input */
} SetOpPath;

/*
 * RecursiveUnionPath represents a recursive UNION node
 */
typedef struct RecursiveUnionPath
{
	Path		path;
	Path	   *leftpath;		/* paths representing input sources */
	Path	   *rightpath;
	List	   *distinctList;	/* SortGroupClauses identifying target cols */
	int			wtParam;		/* ID of Param representing work table */
	Cardinality numGroups;		/* estimated number of groups in input */
} RecursiveUnionPath;

/*
 * LockRowsPath represents acquiring row locks for SELECT FOR UPDATE/SHARE
 */
typedef struct LockRowsPath
{
	Path		path;
	Path	   *subpath;		/* path representing input source */
	List	   *rowMarks;		/* a list of PlanRowMark's */
	int			epqParam;		/* ID of Param for EvalPlanQual re-eval */
} LockRowsPath;

/*
 * ModifyTablePath represents performing INSERT/UPDATE/DELETE/MERGE
 *
 * We represent most things that will be in the ModifyTable plan node
 * literally, except we have a child Path not Plan.  But analysis of the
 * OnConflictExpr is deferred to createplan.c, as is collection of FDW data.
 */
typedef struct ModifyTablePath
{
	Path		path;
	Path	   *subpath;		/* Path producing source data */
	CmdType		operation;		/* INSERT, UPDATE, DELETE, or MERGE */
	bool		canSetTag;		/* do we set the command tag/es_processed? */
	Index		nominalRelation;	/* Parent RT index for use of EXPLAIN */
	Index		rootRelation;	/* Root RT index, if partitioned/inherited */
	List	   *resultRelations;	/* integer list of RT indexes */
	List	   *updateColnosLists;	/* per-target-table update_colnos lists */
	List	   *withCheckOptionLists;	/* per-target-table WCO lists */
	List	   *returningLists; /* per-target-table RETURNING tlists */
	List	   *rowMarks;		/* PlanRowMarks (non-locking only) */
	OnConflictExpr *onconflict; /* ON CONFLICT clause, or NULL */
	ForPortionOfExpr *forPortionOf; /* FOR PORTION OF clause for UPDATE/DELETE */
	int			epqParam;		/* ID of Param for EvalPlanQual re-eval */
	List	   *mergeActionLists;	/* per-target-table lists of actions for
									 * MERGE */
	List	   *mergeJoinConditions;	/* per-target-table join conditions
										 * for MERGE */
} ModifyTablePath;

/*
 * LimitPath represents applying LIMIT/OFFSET restrictions
 */
typedef struct LimitPath
{
	Path		path;
	Path	   *subpath;		/* path representing input source */
	Node	   *limitOffset;	/* OFFSET parameter, or NULL if none */
	Node	   *limitCount;		/* COUNT parameter, or NULL if none */
	LimitOption limitOption;	/* FETCH FIRST with ties or exact number */
} LimitPath;


/*
 * Restriction clause info.
 *
 * We create one of these for each AND sub-clause of a restriction condition
 * (WHERE or JOIN/ON clause).  Since the restriction clauses are logically
 * ANDed, we can use any one of them or any subset of them to filter out
 * tuples, without having to evaluate the rest.  The RestrictInfo node itself
 * stores data used by the optimizer while choosing the best query plan.
 *
 * If a restriction clause references a single base relation, it will appear
 * in the baserestrictinfo list of the RelOptInfo for that base rel.
 *
 * If a restriction clause references more than one base+OJ relation, it will
 * appear in the joininfo list of every RelOptInfo that describes a strict
 * subset of the relations mentioned in the clause.  The joininfo lists are
 * used to drive join tree building by selecting plausible join candidates.
 * The clause cannot actually be applied until we have built a join rel
 * containing all the relations it references, however.
 *
 * When we construct a join rel that includes all the relations referenced
 * in a multi-relation restriction clause, we place that clause into the
 * joinrestrictinfo lists of paths for the join rel, if neither left nor
 * right sub-path includes all relations referenced in the clause.  The clause
 * will be applied at that join level, and will not propagate any further up
 * the join tree.  (Note: the "predicate migration" code was once intended to
 * push restriction clauses up and down the plan tree based on evaluation
 * costs, but it's dead code and is unlikely to be resurrected in the
 * foreseeable future.)
 *
 * Note that in the presence of more than two rels, a multi-rel restriction
 * might reach different heights in the join tree depending on the join
 * sequence we use.  So, these clauses cannot be associated directly with
 * the join RelOptInfo, but must be kept track of on a per-join-path basis.
 *
 * RestrictInfos that represent equivalence conditions (i.e., mergejoinable
 * equalities that are not outerjoin-delayed) are handled a bit differently.
 * Initially we attach them to the EquivalenceClasses that are derived from
 * them.  When we construct a scan or join path, we look through all the
 * EquivalenceClasses and generate derived RestrictInfos representing the
 * minimal set of conditions that need to be checked for this particular scan
 * or join to enforce that all members of each EquivalenceClass are in fact
 * equal in all rows emitted by the scan or join.
 *
 * The clause_relids field lists the base plus outer-join RT indexes that
 * actually appear in the clause.  required_relids lists the minimum set of
 * relids needed to evaluate the clause; while this is often equal to
 * clause_relids, it can be more.  We will add relids to required_relids when
 * we need to force an outer join ON clause to be evaluated exactly at the
 * level of the outer join, which is true except when it is a "degenerate"
 * condition that references only Vars from the nullable side of the join.
 *
 * RestrictInfo nodes contain a flag to indicate whether a qual has been
 * pushed down to a lower level than its original syntactic placement in the
 * join tree would suggest.  If an outer join prevents us from pushing a qual
 * down to its "natural" semantic level (the level associated with just the
 * base rels used in the qual) then we mark the qual with a "required_relids"
 * value including more than just the base rels it actually uses.  By
 * pretending that the qual references all the rels required to form the outer
 * join, we prevent it from being evaluated below the outer join's joinrel.
 * When we do form the outer join's joinrel, we still need to distinguish
 * those quals that are actually in that join's JOIN/ON condition from those
 * that appeared elsewhere in the tree and were pushed down to the join rel
 * because they used no other rels.  That's what the is_pushed_down flag is
 * for; it tells us that a qual is not an OUTER JOIN qual for the set of base
 * rels listed in required_relids.  A clause that originally came from WHERE
 * or an INNER JOIN condition will *always* have its is_pushed_down flag set.
 * It's possible for an OUTER JOIN clause to be marked is_pushed_down too,
 * if we decide that it can be pushed down into the nullable side of the join.
 * In that case it acts as a plain filter qual for wherever it gets evaluated.
 * (In short, is_pushed_down is only false for non-degenerate outer join
 * conditions.  Possibly we should rename it to reflect that meaning?  But
 * see also the comments for RINFO_IS_PUSHED_DOWN, below.)
 *
 * There is also an incompatible_relids field, which is a set of outer-join
 * relids above which we cannot evaluate the clause (because they might null
 * Vars it uses that should not be nulled yet).  In principle this could be
 * filled in any RestrictInfo as the set of OJ relids that appear above the
 * clause and null Vars that it uses.  In practice we only bother to populate
 * it for "clone" clauses, as it's currently only needed to prevent multiple
 * clones of the same clause from being accepted for evaluation at the same
 * join level.
 *
 * There is also an outer_relids field, which is NULL except for outer join
 * clauses; for those, it is the set of relids on the outer side of the
 * clause's outer join.  (These are rels that the clause cannot be applied to
 * in parameterized scans, since pushing it into the join's outer side would
 * lead to wrong answers.)
 *
 * To handle security-barrier conditions efficiently, we mark RestrictInfo
 * nodes with a security_level field, in which higher values identify clauses
 * coming from less-trusted sources.  The exact semantics are that a clause
 * cannot be evaluated before another clause with a lower security_level value
 * unless the first clause is leakproof.  As with outer-join clauses, this
 * creates a reason for clauses to sometimes need to be evaluated higher in
 * the join tree than their contents would suggest; and even at a single plan
 * node, this rule constrains the order of application of clauses.
 *
 * In general, the referenced clause might be arbitrarily complex.  The
 * kinds of clauses we can handle as indexscan quals, mergejoin clauses,
 * or hashjoin clauses are limited (e.g., no volatile functions).  The code
 * for each kind of path is responsible for identifying the restrict clauses
 * it can use and ignoring the rest.  Clauses not implemented by an indexscan,
 * mergejoin, or hashjoin will be placed in the plan qual or joinqual field
 * of the finished Plan node, where they will be enforced by general-purpose
 * qual-expression-evaluation code.  (But we are still entitled to count
 * their selectivity when estimating the result tuple count, if we
 * can guess what it is...)
 *
 * When the referenced clause is an OR clause, we generate a modified copy
 * in which additional RestrictInfo nodes are inserted below the top-level
 * OR/AND structure.  This is a convenience for OR indexscan processing:
 * indexquals taken from either the top level or an OR subclause will have
 * associated RestrictInfo nodes.
 *
 * The can_join flag is set true if the clause looks potentially useful as
 * a merge or hash join clause, that is if it is a binary opclause with
 * nonoverlapping sets of relids referenced in the left and right sides.
 * (Whether the operator is actually merge or hash joinable isn't checked,
 * however.)
 *
 * The pseudoconstant flag is set true if the clause contains no Vars of
 * the current query level and no volatile functions.  Such a clause can be
 * pulled out and used as a one-time qual in a gating Result node.  We keep
 * pseudoconstant clauses in the same lists as other RestrictInfos so that
 * the regular clause-pushing machinery can assign them to the correct join
 * level, but they need to be treated specially for cost and selectivity
 * estimates.  Note that a pseudoconstant clause can never be an indexqual
 * or merge or hash join clause, so it's of no interest to large parts of
 * the planner.
 *
 * When we generate multiple versions of a clause so as to have versions
 * that will work after commuting some left joins per outer join identity 3,
 * we mark the one with the fewest nullingrels bits with has_clone = true,
 * and the rest with is_clone = true.  This allows proper filtering of
 * these redundant clauses, so that we apply only one version of them.
 *
 * When join clauses are generated from EquivalenceClasses, there may be
 * several equally valid ways to enforce join equivalence, of which we need
 * apply only one.  We mark clauses of this kind by setting parent_ec to
 * point to the generating EquivalenceClass.  Multiple clauses with the same
 * parent_ec in the same join are redundant.
 *
 * Most fields are ignored for equality, since they may not be set yet, and
 * should be derivable from the clause anyway.
 *
 * parent_ec, left_ec, right_ec are not printed, lest it lead to infinite
 * recursion in plan tree dump.
 */

typedef struct RestrictInfo
{
	pg_node_attr(no_read, no_query_jumble)

	NodeTag		type;

	/* the represented clause of WHERE or JOIN */
	Expr	   *clause;

	/* true if clause was pushed down in level */
	bool		is_pushed_down;

	/* see comment above */
	bool		can_join pg_node_attr(equal_ignore);

	/* see comment above */
	bool		pseudoconstant pg_node_attr(equal_ignore);

	/* see comment above */
	bool		has_clone;
	bool		is_clone;

	/* true if known to contain no leaked Vars */
	bool		leakproof pg_node_attr(equal_ignore);

	/* indicates if clause contains any volatile functions */
	VolatileFunctionStatus has_volatile pg_node_attr(equal_ignore);

	/* see comment above */
	Index		security_level;

	/* number of base rels in clause_relids */
	int			num_base_rels pg_node_attr(equal_ignore);

	/* The relids (varnos+varnullingrels) actually referenced in the clause: */
	Relids		clause_relids pg_node_attr(equal_ignore);

	/* The set of relids required to evaluate the clause: */
	Relids		required_relids;

	/* Relids above which we cannot evaluate the clause (see comment above) */
	Relids		incompatible_relids;

	/* If an outer-join clause, the outer-side relations, else NULL: */
	Relids		outer_relids;

	/*
	 * Relids in the left/right side of the clause.  These fields are set for
	 * any binary opclause.
	 */
	Relids		left_relids pg_node_attr(equal_ignore);
	Relids		right_relids pg_node_attr(equal_ignore);

	/*
	 * Modified clause with RestrictInfos.  This field is NULL unless clause
	 * is an OR clause.
	 */
	Expr	   *orclause pg_node_attr(equal_ignore);

	/*----------
	 * Serial number of this RestrictInfo.  This is unique within the current
	 * PlannerInfo context, with a few critical exceptions:
	 * 1. When we generate multiple clones of the same qual condition to
	 * cope with outer join identity 3, all the clones get the same serial
	 * number.  This reflects that we only want to apply one of them in any
	 * given plan.
	 * 2. If we manufacture a commuted version of a qual to use as an index
	 * condition, it copies the original's rinfo_serial, since it is in
	 * practice the same condition.
	 * 3. If we reduce a qual to constant-FALSE, the new constant-FALSE qual
	 * copies the original's rinfo_serial, since it is in practice the same
	 * condition.
	 * 4. RestrictInfos made for a child relation copy their parent's
	 * rinfo_serial.  Likewise, when an EquivalenceClass makes a derived
	 * equality clause for a child relation, it copies the rinfo_serial of
	 * the matching equality clause for the parent.  This allows detection
	 * of redundant pushed-down equality clauses.
	 *----------
	 */
	int			rinfo_serial;

	/*
	 * Generating EquivalenceClass.  This field is NULL unless clause is
	 * potentially redundant.
	 */
	EquivalenceClass *parent_ec pg_node_attr(copy_as_scalar, equal_ignore, read_write_ignore);

	/*
	 * cache space for cost and selectivity
	 */

	/* eval cost of clause; -1 if not yet set */
	QualCost	eval_cost pg_node_attr(equal_ignore);

	/* selectivity for "normal" (JOIN_INNER) semantics; -1 if not yet set */
	Selectivity norm_selec pg_node_attr(equal_ignore);
	/* selectivity for outer join semantics; -1 if not yet set */
	Selectivity outer_selec pg_node_attr(equal_ignore);

	/*
	 * opfamilies containing clause operator; valid if clause is
	 * mergejoinable, else NIL
	 */
	List	   *mergeopfamilies pg_node_attr(equal_ignore);

	/*
	 * cache space for mergeclause processing; NULL if not yet set
	 */

	/* EquivalenceClass containing lefthand */
	EquivalenceClass *left_ec pg_node_attr(copy_as_scalar, equal_ignore, read_write_ignore);
	/* EquivalenceClass containing righthand */
	EquivalenceClass *right_ec pg_node_attr(copy_as_scalar, equal_ignore, read_write_ignore);
	/* EquivalenceMember for lefthand */
	EquivalenceMember *left_em pg_node_attr(copy_as_scalar, equal_ignore);
	/* EquivalenceMember for righthand */
	EquivalenceMember *right_em pg_node_attr(copy_as_scalar, equal_ignore);

	/*
	 * List of MergeScanSelCache structs.  Those aren't Nodes, so hard to
	 * copy; instead replace with NIL.  That has the effect that copying will
	 * just reset the cache.  Likewise, can't compare or print them.
	 */
	List	   *scansel_cache pg_node_attr(copy_as(NIL), equal_ignore, read_write_ignore);

	/*
	 * transient workspace for use while considering a specific join path; T =
	 * outer var on left, F = on right
	 */
	bool		outer_is_left pg_node_attr(equal_ignore);

	/*
	 * copy of clause operator; valid if clause is hashjoinable, else
	 * InvalidOid
	 */
	Oid			hashjoinoperator pg_node_attr(equal_ignore);

	/*
	 * cache space for hashclause processing; -1 if not yet set
	 */
	/* avg bucketsize of left side */
	Selectivity left_bucketsize pg_node_attr(equal_ignore);
	/* avg bucketsize of right side */
	Selectivity right_bucketsize pg_node_attr(equal_ignore);
	/* left side's most common val's freq */
	Selectivity left_mcvfreq pg_node_attr(equal_ignore);
	/* right side's most common val's freq */
	Selectivity right_mcvfreq pg_node_attr(equal_ignore);

	/* hash equality operators used for memoize nodes, else InvalidOid */
	Oid			left_hasheqoperator pg_node_attr(equal_ignore);
	Oid			right_hasheqoperator pg_node_attr(equal_ignore);
} RestrictInfo;

/*
 * This macro embodies the correct way to test whether a RestrictInfo is
 * "pushed down" to a given outer join, that is, should be treated as a filter
 * clause rather than a join clause at that outer join.  This is certainly so
 * if is_pushed_down is true; but examining that is not sufficient anymore,
 * because outer-join clauses will get pushed down to lower outer joins when
 * we generate a path for the lower outer join that is parameterized by the
 * LHS of the upper one.  We can detect such a clause by noting that its
 * required_relids exceed the scope of the join.
 */
#define RINFO_IS_PUSHED_DOWN(rinfo, joinrelids) \
	((rinfo)->is_pushed_down || \
	 !bms_is_subset((rinfo)->required_relids, joinrelids))

/*
 * Since mergejoinscansel() is a relatively expensive function, and would
 * otherwise be invoked many times while planning a large join tree,
 * we go out of our way to cache its results.  Each mergejoinable
 * RestrictInfo carries a list of the specific sort orderings that have
 * been considered for use with it, and the resulting selectivities.
 */
typedef struct MergeScanSelCache
{
	/* Ordering details (cache lookup key) */
	Oid			opfamily;		/* index opfamily defining the ordering */
	Oid			collation;		/* collation for the ordering */
	CompareType cmptype;		/* sort direction (ASC or DESC) */
	bool		nulls_first;	/* do NULLs come before normal values? */
	/* Results */
	Selectivity leftstartsel;	/* first-join fraction for clause left side */
	Selectivity leftendsel;		/* last-join fraction for clause left side */
	Selectivity rightstartsel;	/* first-join fraction for clause right side */
	Selectivity rightendsel;	/* last-join fraction for clause right side */
} MergeScanSelCache;

/*
 * Placeholder node for an expression to be evaluated below the top level
 * of a plan tree.  This is used during planning to represent the contained
 * expression.  At the end of the planning process it is replaced by either
 * the contained expression or a Var referring to a lower-level evaluation of
 * the contained expression.  Generally the evaluation occurs below an outer
 * join, and Var references above the outer join might thereby yield NULL
 * instead of the expression value.
 *
 * phrels and phlevelsup correspond to the varno/varlevelsup fields of a
 * plain Var, except that phrels has to be a relid set since the evaluation
 * level of a PlaceHolderVar might be a join rather than a base relation.
 * Likewise, phnullingrels corresponds to varnullingrels.
 *
 * Although the planner treats this as an expression node type, it is not
 * recognized by the parser or executor, so we declare it here rather than
 * in primnodes.h.
 *
 * We intentionally do not compare phexpr.  Two PlaceHolderVars with the
 * same ID and levelsup should be considered equal even if the contained
 * expressions have managed to mutate to different states.  This will
 * happen during final plan construction when there are nested PHVs, since
 * the inner PHV will get replaced by a Param in some copies of the outer
 * PHV.  Another way in which it can happen is that initplan sublinks
 * could get replaced by differently-numbered Params when sublink folding
 * is done.  (The end result of such a situation would be some
 * unreferenced initplans, which is annoying but not really a problem.)
 * On the same reasoning, there is no need to examine phrels.  But we do
 * need to compare phnullingrels, as that represents effects that are
 * external to the original value of the PHV.
 */

typedef struct PlaceHolderVar
{
	pg_node_attr(no_query_jumble)

	Expr		xpr;

	/* the represented expression */
	Expr	   *phexpr pg_node_attr(equal_ignore);

	/* base+OJ relids syntactically within expr src */
	Relids		phrels pg_node_attr(equal_ignore);

	/* RT indexes of outer joins that can null PHV's value */
	Relids		phnullingrels;

	/* ID for PHV (unique within planner run) */
	Index		phid;

	/* > 0 if PHV belongs to outer query */
	Index		phlevelsup;
} PlaceHolderVar;

/*
 * "Special join" info.
 *
 * One-sided outer joins constrain the order of joining partially but not
 * completely.  We flatten such joins into the planner's top-level list of
 * relations to join, but record information about each outer join in a
 * SpecialJoinInfo struct.  These structs are kept in the PlannerInfo node's
 * join_info_list.
 *
 * Similarly, semijoins and antijoins created by flattening IN (subselect)
 * and EXISTS(subselect) clauses create partial constraints on join order.
 * These are likewise recorded in SpecialJoinInfo structs.
 *
 * We make SpecialJoinInfos for FULL JOINs even though there is no flexibility
 * of planning for them, because this simplifies make_join_rel()'s API.
 *
 * min_lefthand and min_righthand are the sets of base+OJ relids that must be
 * available on each side when performing the special join.
 * It is not valid for either min_lefthand or min_righthand to be empty sets;
 * if they were, this would break the logic that enforces join order.
 *
 * syn_lefthand and syn_righthand are the sets of base+OJ relids that are
 * syntactically below this special join.  (These are needed to help compute
 * min_lefthand and min_righthand for higher joins.)
 *
 * jointype is never JOIN_RIGHT; a RIGHT JOIN is handled by switching
 * the inputs to make it a LEFT JOIN.  It's never JOIN_RIGHT_SEMI or
 * JOIN_RIGHT_ANTI either.  So the allowed values of jointype in a
 * join_info_list member are only LEFT, FULL, SEMI, or ANTI.
 *
 * ojrelid is the RT index of the join RTE representing this outer join,
 * if there is one.  It is zero when jointype is INNER or SEMI, and can be
 * zero for jointype ANTI (if the join was transformed from a SEMI join).
 * One use for this field is that when constructing the output targetlist of a
 * join relation that implements this OJ, we add ojrelid to the varnullingrels
 * and phnullingrels fields of nullable (RHS) output columns, so that the
 * output Vars and PlaceHolderVars correctly reflect the nulling that has
 * potentially happened to them.
 *
 * commute_above_l is filled with the relids of syntactically-higher outer
 * joins that have been found to commute with this one per outer join identity
 * 3 (see optimizer/README), when this join is in the LHS of the upper join
 * (so, this is the lower join in the first form of the identity).
 *
 * commute_above_r is filled with the relids of syntactically-higher outer
 * joins that have been found to commute with this one per outer join identity
 * 3, when this join is in the RHS of the upper join (so, this is the lower
 * join in the second form of the identity).
 *
 * commute_below_l is filled with the relids of syntactically-lower outer
 * joins that have been found to commute with this one per outer join identity
 * 3 and are in the LHS of this join (so, this is the upper join in the first
 * form of the identity).
 *
 * commute_below_r is filled with the relids of syntactically-lower outer
 * joins that have been found to commute with this one per outer join identity
 * 3 and are in the RHS of this join (so, this is the upper join in the second
 * form of the identity).
 *
 * lhs_strict is true if the special join's condition cannot succeed when the
 * LHS variables are all NULL (this means that an outer join can commute with
 * upper-level outer joins even if it appears in their RHS).  We don't bother
 * to set lhs_strict for FULL JOINs, however.
 *
 * For a semijoin, we also extract the join operators and their RHS arguments
 * and set semi_operators, semi_rhs_exprs, semi_can_btree, and semi_can_hash.
 * This is done in support of possibly unique-ifying the RHS, so we don't
 * bother unless at least one of semi_can_btree and semi_can_hash can be set
 * true.  (You might expect that this information would be computed during
 * join planning; but it's helpful to have it available during planning of
 * parameterized table scans, so we store it in the SpecialJoinInfo structs.)
 *
 * For purposes of join selectivity estimation, we create transient
 * SpecialJoinInfo structures for regular inner joins; so it is possible
 * to have jointype == JOIN_INNER in such a structure, even though this is
 * not allowed within join_info_list.  We also create transient
 * SpecialJoinInfos with jointype == JOIN_INNER for outer joins, since for
 * cost estimation purposes it is sometimes useful to know the join size under
 * plain innerjoin semantics.  Note that lhs_strict and the semi_xxx fields
 * are not set meaningfully within such structs.
 *
 * We also create transient SpecialJoinInfos for child joins during
 * partitionwise join planning, which are also not present in join_info_list.
 */
typedef struct SpecialJoinInfo
{
	pg_node_attr(no_read, no_query_jumble)

	NodeTag		type;
	Relids		min_lefthand;	/* base+OJ relids in minimum LHS for join */
	Relids		min_righthand;	/* base+OJ relids in minimum RHS for join */
	Relids		syn_lefthand;	/* base+OJ relids syntactically within LHS */
	Relids		syn_righthand;	/* base+OJ relids syntactically within RHS */
	JoinType	jointype;		/* always INNER, LEFT, FULL, SEMI, or ANTI */
	Index		ojrelid;		/* outer join's RT index; 0 if none */
	Relids		commute_above_l;	/* commuting OJs above this one, if LHS */
	Relids		commute_above_r;	/* commuting OJs above this one, if RHS */
	Relids		commute_below_l;	/* commuting OJs in this one's LHS */
	Relids		commute_below_r;	/* commuting OJs in this one's RHS */
	bool		lhs_strict;		/* joinclause is strict for some LHS rel */
	/* Remaining fields are set only for JOIN_SEMI jointype: */
	bool		semi_can_btree; /* true if semi_operators are all btree */
	bool		semi_can_hash;	/* true if semi_operators are all hash */
	List	   *semi_operators; /* OIDs of equality join operators */
	List	   *semi_rhs_exprs; /* righthand-side expressions of these ops */
} SpecialJoinInfo;

/*
 * Transient outer-join clause info.
 *
 * We set aside every outer join ON clause that looks mergejoinable,
 * and process it specially at the end of qual distribution.
 */
typedef struct OuterJoinClauseInfo
{
	pg_node_attr(no_copy_equal, no_read, no_query_jumble)

	NodeTag		type;
	RestrictInfo *rinfo;		/* a mergejoinable outer-join clause */
	SpecialJoinInfo *sjinfo;	/* the outer join's SpecialJoinInfo */
} OuterJoinClauseInfo;

/*
 * Append-relation info.
 *
 * When we expand an inheritable table or a UNION-ALL subselect into an
 * "append relation" (essentially, a list of child RTEs), we build an
 * AppendRelInfo for each child RTE.  The list of AppendRelInfos indicates
 * which child RTEs must be included when expanding the parent, and each node
 * carries information needed to translate between columns of the parent and
 * columns of the child.
 *
 * These structs are kept in the PlannerInfo node's append_rel_list, with
 * append_rel_array[] providing a convenient lookup method for the struct
 * associated with a particular child relid (there can be only one, though
 * parent rels may have many entries in append_rel_list).
 *
 * Note: after completion of the planner prep phase, any given RTE is an
 * append parent having entries in append_rel_list if and only if its
 * "inh" flag is set.  We clear "inh" for plain tables that turn out not
 * to have inheritance children, and (in an abuse of the original meaning
 * of the flag) we set "inh" for subquery RTEs that turn out to be
 * flattenable UNION ALL queries.  This lets us avoid useless searches
 * of append_rel_list.
 *
 * Note: the data structure assumes that append-rel members are single
 * baserels.  This is OK for inheritance, but it prevents us from pulling
 * up a UNION ALL member subquery if it contains a join.  While that could
 * be fixed with a more complex data structure, at present there's not much
 * point because no improvement in the plan could result.
 */

typedef struct AppendRelInfo
{
	pg_node_attr(no_query_jumble)

	NodeTag		type;

	/*
	 * These fields uniquely identify this append relationship.  There can be
	 * (in fact, always should be) multiple AppendRelInfos for the same
	 * parent_relid, but never more than one per child_relid, since a given
	 * RTE cannot be a child of more than one append parent.
	 */
	Index		parent_relid;	/* RT index of append parent rel */
	Index		child_relid;	/* RT index of append child rel */

	/*
	 * For an inheritance appendrel, the parent and child are both regular
	 * relations, and we store their rowtype OIDs here for use in translating
	 * whole-row Vars.  For a UNION-ALL appendrel, the parent and child are
	 * both subqueries with no named rowtype, and we store InvalidOid here.
	 */
	Oid			parent_reltype; /* OID of parent's composite type */
	Oid			child_reltype;	/* OID of child's composite type */

	/*
	 * The N'th element of this list is a Var or expression representing the
	 * child column corresponding to the N'th column of the parent. This is
	 * used to translate Vars referencing the parent rel into references to
	 * the child.  A list element is NULL if it corresponds to a dropped
	 * column of the parent (this is only possible for inheritance cases, not
	 * UNION ALL).  The list elements are always simple Vars for inheritance
	 * cases, but can be arbitrary expressions in UNION ALL cases.
	 *
	 * Notice we only store entries for user columns (attno > 0).  Whole-row
	 * Vars are special-cased, and system columns (attno < 0) need no special
	 * translation since their attnos are the same for all tables.
	 *
	 * Caution: the Vars have varlevelsup = 0.  Be careful to adjust as needed
	 * when copying into a subquery.
	 */
	List	   *translated_vars;	/* Expressions in the child's Vars */

	/*
	 * This array simplifies translations in the reverse direction, from
	 * child's column numbers to parent's.  The entry at [ccolno - 1] is the
	 * 1-based parent column number for child column ccolno, or zero if that
	 * child column is dropped or doesn't exist in the parent.
	 */
	int			num_child_cols; /* length of array */
	AttrNumber *parent_colnos pg_node_attr(array_size(num_child_cols));

	/*
	 * We store the parent table's OID here for inheritance, or InvalidOid for
	 * UNION ALL.  This is only needed to help in generating error messages if
	 * an attempt is made to reference a dropped parent column.
	 */
	Oid			parent_reloid;	/* OID of parent relation */
} AppendRelInfo;

/*
 * Information about a row-identity "resjunk" column in UPDATE/DELETE/MERGE.
 *
 * In partitioned UPDATE/DELETE/MERGE it's important for child partitions to
 * share row-identity columns whenever possible, so as not to chew up too many
 * targetlist columns.  We use these structs to track which identity columns
 * have been requested.  In the finished plan, each of these will give rise
 * to one resjunk entry in the targetlist of the ModifyTable's subplan node.
 *
 * All the Vars stored in RowIdentityVarInfos must have varno ROWID_VAR, for
 * convenience of detecting duplicate requests.  We'll replace that, in the
 * final plan, with the varno of the generating rel.
 *
 * Outside this list, a Var with varno ROWID_VAR and varattno k is a reference
 * to the k-th element of the row_identity_vars list (k counting from 1).
 * We add such a reference to root->processed_tlist when creating the entry,
 * and it propagates into the plan tree from there.
 */
typedef struct RowIdentityVarInfo
{
	pg_node_attr(no_copy_equal, no_read, no_query_jumble)

	NodeTag		type;

	Var		   *rowidvar;		/* Var to be evaluated (but varno=ROWID_VAR) */
	int32		rowidwidth;		/* estimated average width */
	char	   *rowidname;		/* name of the resjunk column */
	Relids		rowidrels;		/* RTE indexes of target rels using this */
} RowIdentityVarInfo;

/*
 * One element of the list passed to query_is_distinct_for().  Each entry
 * names a subquery output column that the caller needs to be distinct over,
 * plus the upper-level equality operator and its input collation, so that
 * the subquery's own DISTINCT/GROUP BY/set-op clauses can be compared for
 * compatibility.
 */
typedef struct DistinctColInfo
{
	int			colno;			/* subquery output column resno */
	Oid			opid;			/* upper-level equality operator */
	Oid			collid;			/* input collation of opid */
} DistinctColInfo;

/*
 * For each distinct placeholder expression generated during planning, we
 * store a PlaceHolderInfo node in the PlannerInfo node's placeholder_list.
 * This stores info that is needed centrally rather than in each copy of the
 * PlaceHolderVar.  The phid fields identify which PlaceHolderInfo goes with
 * each PlaceHolderVar.  Note that phid is unique throughout a planner run,
 * not just within a query level --- this is so that we need not reassign ID's
 * when pulling a subquery into its parent.
 *
 * The idea is to evaluate the expression at (only) the ph_eval_at join level,
 * then allow it to bubble up like a Var until the ph_needed join level.
 * ph_needed has the same definition as attr_needed for a regular Var.
 *
 * The PlaceHolderVar's expression might contain LATERAL references to vars
 * coming from outside its syntactic scope.  If so, those rels are *not*
 * included in ph_eval_at, but they are recorded in ph_lateral.
 *
 * Notice that when ph_eval_at is a join rather than a single baserel, the
 * PlaceHolderInfo may create constraints on join order: the ph_eval_at join
 * has to be formed below any outer joins that should null the PlaceHolderVar.
 *
 * We create a PlaceHolderInfo only after determining that the PlaceHolderVar
 * is actually referenced in the plan tree, so that unreferenced placeholders
 * don't result in unnecessary constraints on join order.
 */

typedef struct PlaceHolderInfo
{
	pg_node_attr(no_read, no_query_jumble)

	NodeTag		type;

	/* ID for PH (unique within planner run) */
	Index		phid;

	/*
	 * copy of PlaceHolderVar tree (should be redundant for comparison, could
	 * be ignored)
	 */
	PlaceHolderVar *ph_var;

	/* lowest level we can evaluate value at */
	Relids		ph_eval_at;

	/* relids of contained lateral refs, if any */
	Relids		ph_lateral;

	/* highest level the value is needed at */
	Relids		ph_needed;

	/* estimated attribute width */
	int32		ph_width;
} PlaceHolderInfo;

/*
 * This struct describes one potentially index-optimizable MIN/MAX aggregate
 * function.  MinMaxAggPath contains a list of these, and if we accept that
 * path, the list is stored into root->minmax_aggs for use during setrefs.c.
 */
typedef struct MinMaxAggInfo
{
	pg_node_attr(no_copy_equal, no_read, no_query_jumble)

	NodeTag		type;

	/* pg_proc Oid of the aggregate */
	Oid			aggfnoid;

	/* Oid of its sort operator */
	Oid			aggsortop;

	/* expression we are aggregating on */
	Expr	   *target;

	/*
	 * modified "root" for planning the subquery; not printed, too large, not
	 * interesting enough
	 */
	PlannerInfo *subroot pg_node_attr(read_write_ignore);

	/* access path for subquery */
	Path	   *path;

	/* estimated cost to fetch first row */
	Cost		pathcost;

	/* param for subplan's output */
	Param	   *param;
} MinMaxAggInfo;

/*
 * For each distinct Aggref node that appears in the targetlist and HAVING
 * clauses, we store an AggClauseInfo node in the PlannerInfo node's
 * agg_clause_list.  Each AggClauseInfo records the set of relations referenced
 * by the aggregate expression.  This information is used to determine how far
 * the aggregate can be safely pushed down in the join tree.
 */
typedef struct AggClauseInfo
{
	pg_node_attr(no_read, no_query_jumble)

	NodeTag		type;

	/* the Aggref expr */
	Aggref	   *aggref;

	/* lowest level we can evaluate this aggregate at */
	Relids		agg_eval_at;
} AggClauseInfo;

/*
 * For each grouping expression that appears in grouping clauses, we store a
 * GroupingExprInfo node in the PlannerInfo node's group_expr_list.  Each
 * GroupingExprInfo records the expression being grouped on, its sortgroupref,
 * and the EquivalenceClass it belongs to.  This information is necessary to
 * reproduce correct grouping semantics at different levels of the join tree.
 */
typedef struct GroupingExprInfo
{
	pg_node_attr(no_read, no_query_jumble)

	NodeTag		type;

	/* the represented expression */
	Expr	   *expr;

	/* the tleSortGroupRef of the corresponding SortGroupClause */
	Index		sortgroupref;

	/* the equivalence class the expression belongs to */
	EquivalenceClass *ec pg_node_attr(copy_as_scalar, equal_as_scalar);
} GroupingExprInfo;

/*
 * At runtime, PARAM_EXEC slots are used to pass values around from one plan
 * node to another.  They can be used to pass values down into subqueries (for
 * outer references in subqueries), or up out of subqueries (for the results
 * of a subplan), or from a NestLoop plan node into its inner relation (when
 * the inner scan is parameterized with values from the outer relation).
 * The planner is responsible for assigning nonconflicting PARAM_EXEC IDs to
 * the PARAM_EXEC Params it generates.
 *
 * Outer references are managed via root->plan_params, which is a list of
 * PlannerParamItems.  While planning a subquery, each parent query level's
 * plan_params contains the values required from it by the current subquery.
 * During create_plan(), we use plan_params to track values that must be
 * passed from outer to inner sides of NestLoop plan nodes.
 *
 * The item a PlannerParamItem represents can be one of three kinds:
 *
 * A Var: the slot represents a variable of this level that must be passed
 * down because subqueries have outer references to it, or must be passed
 * from a NestLoop node to its inner scan.  The varlevelsup value in the Var
 * will always be zero.
 *
 * A PlaceHolderVar: this works much like the Var case, except that the
 * entry is a PlaceHolderVar node with a contained expression.  The PHV
 * will have phlevelsup = 0, and the contained expression is adjusted
 * to match in level.
 *
 * An Aggref (with an expression tree representing its argument): the slot
 * represents an aggregate expression that is an outer reference for some
 * subquery.  The Aggref itself has agglevelsup = 0, and its argument tree
 * is adjusted to match in level.
 *
 * Note: we detect duplicate Var and PlaceHolderVar parameters and coalesce
 * them into one slot, but we do not bother to do that for Aggrefs.
 * The scope of duplicate-elimination only extends across the set of
 * parameters passed from one query level into a single subquery, or for
 * nestloop parameters across the set of nestloop parameters used in a single
 * query level.  So there is no possibility of a PARAM_EXEC slot being used
 * for conflicting purposes.
 *
 * In addition, PARAM_EXEC slots are assigned for Params representing outputs
 * from subplans (values that are setParam items for those subplans).  These
 * IDs need not be tracked via PlannerParamItems, since we do not need any
 * duplicate-elimination nor later processing of the represented expressions.
 * Instead, we just record the assignment of the slot number by appending to
 * root->glob->paramExecTypes.
 */
typedef struct PlannerParamItem
{
	pg_node_attr(no_copy_equal, no_read, no_query_jumble)

	NodeTag		type;

	Node	   *item;			/* the Var, PlaceHolderVar, or Aggref */
	int			paramId;		/* its assigned PARAM_EXEC slot number */
} PlannerParamItem;

/*
 * When making cost estimates for a SEMI/ANTI/inner_unique join, there are
 * some correction factors that are needed in both nestloop and hash joins
 * to account for the fact that the executor can stop scanning inner rows
 * as soon as it finds a match to the current outer row.  These numbers
 * depend only on the selected outer and inner join relations, not on the
 * particular paths used for them, so it's worthwhile to calculate them
 * just once per relation pair not once per considered path.  This struct
 * is filled by compute_semi_anti_join_factors and must be passed along
 * to the join cost estimation functions.
 *
 * outer_match_frac is the fraction of the outer tuples that are
 *		expected to have at least one match.
 * match_count is the average number of matches expected for
 *		outer tuples that have at least one match.
 */
typedef struct SemiAntiJoinFactors
{
	Selectivity outer_match_frac;
	Selectivity match_count;
} SemiAntiJoinFactors;

/*
 * Struct for extra information passed to subroutines of add_paths_to_joinrel
 *
 * restrictlist contains all of the RestrictInfo nodes for restriction
 *		clauses that apply to this join
 * mergeclause_list is a list of RestrictInfo nodes for available
 *		mergejoin clauses in this join
 * inner_unique is true if each outer tuple provably matches no more
 *		than one inner tuple
 * sjinfo is extra info about special joins for selectivity estimation
 * semifactors is as shown above (only valid for SEMI/ANTI/inner_unique joins)
 * param_source_rels are OK targets for parameterization of result paths
 * pgs_mask is a bitmask of PGS_* constants to limit the join strategy
 */
typedef struct JoinPathExtraData
{
	List	   *restrictlist;
	List	   *mergeclause_list;
	bool		inner_unique;
	SpecialJoinInfo *sjinfo;
	SemiAntiJoinFactors semifactors;
	Relids		param_source_rels;
	uint64		pgs_mask;
} JoinPathExtraData;

/*
 * Various flags indicating what kinds of grouping are possible.
 *
 * GROUPING_CAN_USE_SORT should be set if it's possible to perform
 * sort-based implementations of grouping.  When grouping sets are in use,
 * this will be true if sorting is potentially usable for any of the grouping
 * sets, even if it's not usable for all of them.
 *
 * GROUPING_CAN_USE_HASH should be set if it's possible to perform
 * hash-based implementations of grouping.
 *
 * GROUPING_CAN_PARTIAL_AGG should be set if the aggregation is of a type
 * for which we support partial aggregation (not, for example, grouping sets).
 * It says nothing about parallel-safety or the availability of suitable paths.
 */
#define GROUPING_CAN_USE_SORT       0x0001
#define GROUPING_CAN_USE_HASH       0x0002
#define GROUPING_CAN_PARTIAL_AGG	0x0004

/*
 * What kind of partitionwise aggregation is in use?
 *
 * PARTITIONWISE_AGGREGATE_NONE: Not used.
 *
 * PARTITIONWISE_AGGREGATE_FULL: Aggregate each partition separately, and
 * append the results.
 *
 * PARTITIONWISE_AGGREGATE_PARTIAL: Partially aggregate each partition
 * separately, append the results, and then finalize aggregation.
 */
typedef enum
{
	PARTITIONWISE_AGGREGATE_NONE,
	PARTITIONWISE_AGGREGATE_FULL,
	PARTITIONWISE_AGGREGATE_PARTIAL,
} PartitionwiseAggregateType;

/*
 * Struct for extra information passed to subroutines of create_grouping_paths
 *
 * flags indicating what kinds of grouping are possible.
 * partial_costs_set is true if the agg_partial_costs and agg_final_costs
 * 		have been initialized.
 * agg_partial_costs gives partial aggregation costs.
 * agg_final_costs gives finalization costs.
 * target_parallel_safe is true if target is parallel safe.
 * havingQual gives list of quals to be applied after aggregation.
 * targetList gives list of columns to be projected.
 * patype is the type of partitionwise aggregation that is being performed.
 */
typedef struct
{
	/* Data which remains constant once set. */
	int			flags;
	bool		partial_costs_set;
	AggClauseCosts agg_partial_costs;
	AggClauseCosts agg_final_costs;

	/* Data which may differ across partitions. */
	bool		target_parallel_safe;
	Node	   *havingQual;
	List	   *targetList;
	PartitionwiseAggregateType patype;
} GroupPathExtraData;

/*
 * Struct for extra information passed to subroutines of grouping_planner
 *
 * limit_needed is true if we actually need a Limit plan node.
 * limit_tuples is an estimated bound on the number of output tuples,
 *		or -1 if no LIMIT or couldn't estimate.
 * count_est and offset_est are the estimated values of the LIMIT and OFFSET
 * 		expressions computed by preprocess_limit() (see comments for
 * 		preprocess_limit() for more information).
 */
typedef struct
{
	bool		limit_needed;
	Cardinality limit_tuples;
	int64		count_est;
	int64		offset_est;
} FinalPathExtraData;

/*
 * For speed reasons, cost estimation for join paths is performed in two
 * phases: the first phase tries to quickly derive a lower bound for the
 * join cost, and then we check if that's sufficient to reject the path.
 * If not, we come back for a more refined cost estimate.  The first phase
 * fills a JoinCostWorkspace struct with its preliminary cost estimates
 * and possibly additional intermediate values.  The second phase takes
 * these values as inputs to avoid repeating work.
 *
 * (Ideally we'd declare this in cost.h, but it's also needed in pathnode.h,
 * so seems best to put it here.)
 */
typedef struct JoinCostWorkspace
{
	/* Preliminary cost estimates --- must not be larger than final ones! */
	int			disabled_nodes;
	Cost		startup_cost;	/* cost expended before fetching any tuples */
	Cost		total_cost;		/* total cost (assuming all tuples fetched) */

	/* Fields below here should be treated as private to costsize.c */
	Cost		run_cost;		/* non-startup cost components */

	/* private for cost_nestloop code */
	Cost		inner_run_cost; /* also used by cost_mergejoin code */
	Cost		inner_rescan_run_cost;

	/* private for cost_mergejoin code */
	Cardinality outer_rows;
	Cardinality inner_rows;
	Cardinality outer_skip_rows;
	Cardinality inner_skip_rows;

	/* private for cost_hashjoin code */
	int			numbuckets;
	int			numbatches;
	Cardinality inner_rows_total;
} JoinCostWorkspace;

/*
 * AggInfo holds information about an aggregate that needs to be computed.
 * Multiple Aggrefs in a query can refer to the same AggInfo by having the
 * same 'aggno' value, so that the aggregate is computed only once.
 */
typedef struct AggInfo
{
	pg_node_attr(no_copy_equal, no_read, no_query_jumble)

	NodeTag		type;

	/*
	 * List of Aggref exprs that this state value is for.
	 *
	 * There will always be at least one, but there can be multiple identical
	 * Aggref's sharing the same per-agg.
	 */
	List	   *aggrefs;

	/* Transition state number for this aggregate */
	int			transno;

	/*
	 * "shareable" is false if this agg cannot share state values with other
	 * aggregates because the final function is read-write.
	 */
	bool		shareable;

	/* Oid of the final function, or InvalidOid if none */
	Oid			finalfn_oid;
} AggInfo;

/*
 * AggTransInfo holds information about transition state that is used by one
 * or more aggregates in the query.  Multiple aggregates can share the same
 * transition state, if they have the same inputs and the same transition
 * function.  Aggrefs that share the same transition info have the same
 * 'aggtransno' value.
 */
typedef struct AggTransInfo
{
	pg_node_attr(no_copy_equal, no_read, no_query_jumble)

	NodeTag		type;

	/* Inputs for this transition state */
	List	   *args;
	Expr	   *aggfilter;

	/* Oid of the state transition function */
	Oid			transfn_oid;

	/* Oid of the serialization function, or InvalidOid if none */
	Oid			serialfn_oid;

	/* Oid of the deserialization function, or InvalidOid if none */
	Oid			deserialfn_oid;

	/* Oid of the combine function, or InvalidOid if none */
	Oid			combinefn_oid;

	/* Oid of state value's datatype */
	Oid			aggtranstype;

	/* Additional data about transtype */
	int32		aggtranstypmod;
	int			transtypeLen;
	bool		transtypeByVal;

	/* Space-consumption estimate */
	int32		aggtransspace;

	/* Initial value from pg_aggregate entry */
	Datum		initValue pg_node_attr(read_write_ignore);
	bool		initValueIsNull;
} AggTransInfo;

/*
 * UniqueRelInfo caches a fact that a relation is unique when being joined
 * to other relation(s).
 */
typedef struct UniqueRelInfo
{
	pg_node_attr(no_copy_equal, no_read, no_query_jumble)

	NodeTag		type;

	/*
	 * The relation in consideration is unique when being joined with this set
	 * of other relation(s).
	 */
	Relids		outerrelids;

	/*
	 * The relation in consideration is unique when considering only clauses
	 * suitable for self-join (passed split_selfjoin_quals()).
	 */
	bool		self_join;

	/*
	 * Additional clauses from a baserestrictinfo list that were used to prove
	 * the uniqueness.   We cache it for the self-join checking procedure: a
	 * self-join can be removed if the outer relation contains strictly the
	 * same set of clauses.
	 */
	List	   *extra_clauses;
} UniqueRelInfo;

#endif							/* PATHNODES_H */
./plancat.c0000664000175000017500000025513215221505613011503 0ustar  xmanxman/*-------------------------------------------------------------------------
 *
 * plancat.c
 *	   routines for accessing the system catalogs
 *
 *
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/optimizer/util/plancat.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include <math.h>

#include "access/genam.h"
#include "access/htup_details.h"
#include "access/nbtree.h"
#include "access/sysattr.h"
#include "access/table.h"
#include "access/tableam.h"
#include "access/transam.h"
#include "access/xlog.h"
#include "catalog/catalog.h"
#include "catalog/heap.h"
#include "catalog/pg_am.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_statistic_ext_data.h"
#include "foreign/fdwapi.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/supportnodes.h"
#include "optimizer/cost.h"
#include "optimizer/optimizer.h"
#include "optimizer/plancat.h"
#include "parser/parse_relation.h"
#include "parser/parsetree.h"
#include "partitioning/partdesc.h"
#include "rewrite/rewriteHandler.h"
#include "rewrite/rewriteManip.h"
#include "statistics/statistics.h"
#include "storage/bufmgr.h"
#include "tcop/tcopprot.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/partcache.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"

/* GUC parameter */
int			constraint_exclusion = CONSTRAINT_EXCLUSION_PARTITION;

typedef struct NotnullHashEntry
{
	Oid			relid;			/* OID of the relation */
	Bitmapset  *notnullattnums; /* attnums of NOT NULL columns */
} NotnullHashEntry;


static void get_relation_foreign_keys(PlannerInfo *root, RelOptInfo *rel,
									  Relation relation, bool inhparent);
static bool infer_collation_opclass_match(InferenceElem *elem, Relation idxRel,
										  List *idxExprs);
static List *get_relation_constraints(PlannerInfo *root,
									  Oid relationObjectId, RelOptInfo *rel,
									  bool include_noinherit,
									  bool include_notnull,
									  bool include_partition);
static List *build_index_tlist(PlannerInfo *root, IndexOptInfo *index,
							   Relation heapRelation);
static List *get_relation_statistics(PlannerInfo *root, RelOptInfo *rel,
									 Relation relation);
static void set_relation_partition_info(PlannerInfo *root, RelOptInfo *rel,
										Relation relation);
static PartitionScheme find_partition_scheme(PlannerInfo *root,
											 Relation relation);
static void set_baserel_partition_key_exprs(Relation relation,
											RelOptInfo *rel);
static void set_baserel_partition_constraint(Relation relation,
											 RelOptInfo *rel);


/*
 * get_relation_info -
 *	  Retrieves catalog information for a given relation.
 *
 * Given the Oid of the relation, return the following info into fields
 * of the RelOptInfo struct:
 *
 *	min_attr	lowest valid AttrNumber
 *	max_attr	highest valid AttrNumber
 *	indexlist	list of IndexOptInfos for relation's indexes
 *	statlist	list of StatisticExtInfo for relation's statistic objects
 *	serverid	if it's a foreign table, the server OID
 *	fdwroutine	if it's a foreign table, the FDW function pointers
 *	pages		number of pages
 *	tuples		number of tuples
 *	rel_parallel_workers user-defined number of parallel workers
 *
 * Also, add information about the relation's foreign keys to root->fkey_list.
 *
 * Also, initialize the attr_needed[] and attr_widths[] arrays.  In most
 * cases these are left as zeroes, but sometimes we need to compute attr
 * widths here, and we may as well cache the results for costsize.c.
 *
 * If inhparent is true, all we need to do is set up the attr arrays:
 * the RelOptInfo actually represents the appendrel formed by an inheritance
 * tree, and so the parent rel's physical size and index information isn't
 * important for it, however, for partitioned tables, we do populate the
 * indexlist as the planner uses unique indexes as unique proofs for certain
 * optimizations.
 */
void
get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
				  RelOptInfo *rel)
{
	Index		varno = rel->relid;
	Relation	relation;
	bool		hasindex;
	List	   *indexinfos = NIL;

	/*
	 * We need not lock the relation since it was already locked, either by
	 * the rewriter or when expand_inherited_rtentry() added it to the query's
	 * rangetable.
	 */
	relation = table_open(relationObjectId, NoLock);

	/*
	 * Relations without a table AM can be used in a query only if they are of
	 * special-cased relkinds.  This check prevents us from crashing later if,
	 * for example, a view's ON SELECT rule has gone missing.  Note that
	 * table_open() already rejected indexes and composite types; spell the
	 * error the same way it does.
	 */
	if (!relation->rd_tableam)
	{
		if (!(relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE ||
			  relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE))
			ereport(ERROR,
					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
					 errmsg("cannot open relation \"%s\"",
							RelationGetRelationName(relation)),
					 errdetail_relkind_not_supported(relation->rd_rel->relkind)));
	}

	/* Temporary and unlogged relations are inaccessible during recovery. */
	if (!RelationIsPermanent(relation) && RecoveryInProgress())
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("cannot access temporary or unlogged relations during recovery")));

	rel->min_attr = FirstLowInvalidHeapAttributeNumber + 1;
	rel->max_attr = RelationGetNumberOfAttributes(relation);
	rel->reltablespace = RelationGetForm(relation)->reltablespace;

	Assert(rel->max_attr >= rel->min_attr);
	rel->attr_needed = (Relids *)
		palloc0((rel->max_attr - rel->min_attr + 1) * sizeof(Relids));
	rel->attr_widths = (int32 *)
		palloc0((rel->max_attr - rel->min_attr + 1) * sizeof(int32));

	/*
	 * Record which columns are defined as NOT NULL.  We leave this
	 * unpopulated for non-partitioned inheritance parent relations as it's
	 * ambiguous as to what it means.  Some child tables may have a NOT NULL
	 * constraint for a column while others may not.  We could work harder and
	 * build a unioned set of all child relations notnullattnums, but there's
	 * currently no need.  The RelOptInfo corresponding to the !inh
	 * RangeTblEntry does get populated.
	 */
	if (!inhparent || relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
		rel->notnullattnums = find_relation_notnullatts(root, relationObjectId);

	/*
	 * Estimate relation size --- unless it's an inheritance parent, in which
	 * case the size we want is not the rel's own size but the size of its
	 * inheritance tree.  That will be computed in set_append_rel_size().
	 */
	if (!inhparent)
		estimate_rel_size(relation, rel->attr_widths - rel->min_attr,
						  &rel->pages, &rel->tuples, &rel->allvisfrac);

	/* Retrieve the parallel_workers reloption, or -1 if not set. */
	rel->rel_parallel_workers = RelationGetParallelWorkers(relation, -1);

	/*
	 * Make list of indexes.  Ignore indexes on system catalogs if told to.
	 * Don't bother with indexes from traditional inheritance parents.  For
	 * partitioned tables, we need a list of at least unique indexes as these
	 * serve as unique proofs for certain planner optimizations.  However,
	 * let's not discriminate here and just record all partitioned indexes
	 * whether they're unique indexes or not.
	 */
	if ((inhparent && relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
		|| (IgnoreSystemIndexes && IsSystemRelation(relation)))
		hasindex = false;
	else
		hasindex = relation->rd_rel->relhasindex;

	if (hasindex)
	{
		List	   *indexoidlist;
		LOCKMODE	lmode;
		ListCell   *l;

		indexoidlist = RelationGetIndexList(relation);

		/*
		 * For each index, we get the same type of lock that the executor will
		 * need, and do not release it.  This saves a couple of trips to the
		 * shared lock manager while not creating any real loss of
		 * concurrency, because no schema changes could be happening on the
		 * index while we hold lock on the parent rel, and no lock type used
		 * for queries blocks any other kind of index operation.
		 */
		lmode = root->simple_rte_array[varno]->rellockmode;

		foreach(l, indexoidlist)
		{
			Oid			indexoid = lfirst_oid(l);
			Relation	indexRelation;
			Form_pg_index index;
			const IndexAmRoutine *amroutine = NULL;
			IndexOptInfo *info;
			int			ncolumns,
						nkeycolumns;
			int			i;

			/*
			 * Extract info from the relation descriptor for the index.
			 */
			indexRelation = index_open(indexoid, lmode);
			index = indexRelation->rd_index;

			/*
			 * Ignore invalid indexes, since they can't safely be used for
			 * queries.  Note that this is OK because the data structure we
			 * are constructing is only used by the planner --- the executor
			 * still needs to insert into "invalid" indexes, if they're marked
			 * indisready.
			 */
			if (!index->indisvalid)
			{
				index_close(indexRelation, NoLock);
				continue;
			}

			/*
			 * If the index is valid, but cannot yet be used, ignore it; but
			 * mark the plan we are generating as transient. See
			 * src/backend/access/heap/README.HOT for discussion.
			 */
			if (index->indcheckxmin &&
				!TransactionIdPrecedes(HeapTupleHeaderGetXmin(indexRelation->rd_indextuple->t_data),
									   TransactionXmin))
			{
				root->glob->transientPlan = true;
				index_close(indexRelation, NoLock);
				continue;
			}

			info = makeNode(IndexOptInfo);

			info->indexoid = index->indexrelid;
			info->reltablespace =
				RelationGetForm(indexRelation)->reltablespace;
			info->rel = rel;
			info->ncolumns = ncolumns = index->indnatts;
			info->nkeycolumns = nkeycolumns = index->indnkeyatts;

			info->indexkeys = palloc_array(int, ncolumns);
			info->indexcollations = palloc_array(Oid, nkeycolumns);
			info->opfamily = palloc_array(Oid, nkeycolumns);
			info->opcintype = palloc_array(Oid, nkeycolumns);
			info->canreturn = palloc_array(bool, ncolumns);

			for (i = 0; i < ncolumns; i++)
			{
				info->indexkeys[i] = index->indkey.values[i];
				info->canreturn[i] = index_can_return(indexRelation, i + 1);
			}

			for (i = 0; i < nkeycolumns; i++)
			{
				info->opfamily[i] = indexRelation->rd_opfamily[i];
				info->opcintype[i] = indexRelation->rd_opcintype[i];
				info->indexcollations[i] = indexRelation->rd_indcollation[i];
			}

			info->relam = indexRelation->rd_rel->relam;

			/*
			 * We don't have an AM for partitioned indexes, so we'll just
			 * NULLify the AM related fields for those.
			 */
			if (indexRelation->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
			{
				/* We copy just the fields we need, not all of rd_indam */
				amroutine = indexRelation->rd_indam;
				info->amcanorderbyop = amroutine->amcanorderbyop;
				info->amoptionalkey = amroutine->amoptionalkey;
				info->amsearcharray = amroutine->amsearcharray;
				info->amsearchnulls = amroutine->amsearchnulls;
				info->amcanparallel = amroutine->amcanparallel;
				info->amhasgettuple = (amroutine->amgettuple != NULL);
				info->amhasgetbitmap = amroutine->amgetbitmap != NULL &&
					relation->rd_tableam->scan_bitmap_next_tuple != NULL;
				info->amcanmarkpos = (amroutine->ammarkpos != NULL &&
									  amroutine->amrestrpos != NULL);
				info->amcostestimate = amroutine->amcostestimate;
				Assert(info->amcostestimate != NULL);

				/* Fetch index opclass options */
				info->opclassoptions = RelationGetIndexAttOptions(indexRelation, true);

				/*
				 * Fetch the ordering information for the index, if any.
				 */
				if (info->relam == BTREE_AM_OID)
				{
					/*
					 * If it's a btree index, we can use its opfamily OIDs
					 * directly as the sort ordering opfamily OIDs.
					 */
					Assert(amroutine->amcanorder);

					info->sortopfamily = info->opfamily;
					info->reverse_sort = palloc_array(bool, nkeycolumns);
					info->nulls_first = palloc_array(bool, nkeycolumns);

					for (i = 0; i < nkeycolumns; i++)
					{
						int16		opt = indexRelation->rd_indoption[i];

						info->reverse_sort[i] = (opt & INDOPTION_DESC) != 0;
						info->nulls_first[i] = (opt & INDOPTION_NULLS_FIRST) != 0;
					}
				}
				else if (amroutine->amcanorder)
				{
					/*
					 * Otherwise, identify the corresponding btree opfamilies
					 * by trying to map this index's "<" operators into btree.
					 * Since "<" uniquely defines the behavior of a sort
					 * order, this is a sufficient test.
					 *
					 * XXX This method is rather slow and complicated.  It'd
					 * be better to have a way to explicitly declare the
					 * corresponding btree opfamily for each opfamily of the
					 * other index type.
					 */
					info->sortopfamily = palloc_array(Oid, nkeycolumns);
					info->reverse_sort = palloc_array(bool, nkeycolumns);
					info->nulls_first = palloc_array(bool, nkeycolumns);

					for (i = 0; i < nkeycolumns; i++)
					{
						int16		opt = indexRelation->rd_indoption[i];
						Oid			ltopr;
						Oid			opfamily;
						Oid			opcintype;
						CompareType cmptype;

						info->reverse_sort[i] = (opt & INDOPTION_DESC) != 0;
						info->nulls_first[i] = (opt & INDOPTION_NULLS_FIRST) != 0;

						ltopr = get_opfamily_member_for_cmptype(info->opfamily[i],
																info->opcintype[i],
																info->opcintype[i],
																COMPARE_LT);
						if (OidIsValid(ltopr) &&
							get_ordering_op_properties(ltopr,
													   &opfamily,
													   &opcintype,
													   &cmptype) &&
							opcintype == info->opcintype[i] &&
							cmptype == COMPARE_LT)
						{
							/* Successful mapping */
							info->sortopfamily[i] = opfamily;
						}
						else
						{
							/* Fail ... quietly treat index as unordered */
							info->sortopfamily = NULL;
							info->reverse_sort = NULL;
							info->nulls_first = NULL;
							break;
						}
					}
				}
				else
				{
					info->sortopfamily = NULL;
					info->reverse_sort = NULL;
					info->nulls_first = NULL;
				}
			}
			else
			{
				info->amcanorderbyop = false;
				info->amoptionalkey = false;
				info->amsearcharray = false;
				info->amsearchnulls = false;
				info->amcanparallel = false;
				info->amhasgettuple = false;
				info->amhasgetbitmap = false;
				info->amcanmarkpos = false;
				info->amcostestimate = NULL;

				info->sortopfamily = NULL;
				info->reverse_sort = NULL;
				info->nulls_first = NULL;
			}

			/*
			 * Fetch the index expressions and predicate, if any.  We must
			 * modify the copies we obtain from the relcache to have the
			 * correct varno for the parent relation, so that they match up
			 * correctly against qual clauses.
			 *
			 * After fixing the varnos, we need to run the index expressions
			 * and predicate through const-simplification again, using a valid
			 * "root".  This ensures that NullTest quals for Vars can be
			 * properly reduced.
			 */
			info->indexprs = RelationGetIndexExpressions(indexRelation);
			info->indexprsExpand = RelationGetIndexExpressionsExpand(indexRelation);
			info->indpred = RelationGetIndexPredicate(indexRelation);
			info->indpredExpand = RelationGetIndexPredicateExpand(indexRelation);
			if (info->indexprs)
			{
				if (varno != 1)
				{
					ChangeVarNodes((Node *) info->indexprs, 1, varno, 0);
					ChangeVarNodes((Node *) info->indexprsExpand, 1, varno, 0);
				}

				info->indexprs = (List *)
					eval_const_expressions(root, (Node *) info->indexprs);
				info->indexprsExpand = (List *)
					eval_const_expressions(root, (Node *) info->indexprsExpand);
			}
			if (info->indpred)
			{
				if (varno != 1)
				{
					ChangeVarNodes((Node *) info->indpred, 1, varno, 0);
					ChangeVarNodes((Node *) info->indpredExpand, 1, varno, 0);
				}

				info->indpred = (List *)
					eval_const_expressions(root,
										   (Node *) make_ands_explicit(info->indpred));
				info->indpredExpand = (List *)
					eval_const_expressions(root,
										   (Node *) make_ands_explicit(info->indpredExpand));
				info->indpred = make_ands_implicit((Expr *) info->indpred);
				info->indpredExpand = make_ands_implicit((Expr *) info->indpredExpand);
			}

			/* Build targetlist using the completed indexprs data */
			info->indextlist = build_index_tlist(root, info, relation);

			info->indrestrictinfo = NIL;	/* set later, in indxpath.c */
			info->predOK = false;	/* set later, in indxpath.c */
			info->unique = index->indisunique;
			info->nullsnotdistinct = index->indnullsnotdistinct;
			info->immediate = index->indimmediate;
			info->hypothetical = false;

			/*
			 * Estimate the index size.  If it's not a partial index, we lock
			 * the number-of-tuples estimate to equal the parent table; if it
			 * is partial then we have to use the same methods as we would for
			 * a table, except we can be sure that the index is not larger
			 * than the table.  We must ignore partitioned indexes here as
			 * there are not physical indexes.
			 */
			if (indexRelation->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
			{
				if (info->indpred == NIL)
				{
					info->pages = RelationGetNumberOfBlocks(indexRelation);
					info->tuples = rel->tuples;
				}
				else
				{
					double		allvisfrac; /* dummy */

					estimate_rel_size(indexRelation, NULL,
									  &info->pages, &info->tuples, &allvisfrac);
					if (info->tuples > rel->tuples)
						info->tuples = rel->tuples;
				}

				/*
				 * Get tree height while we have the index open
				 */
				if (amroutine->amgettreeheight)
				{
					info->tree_height = amroutine->amgettreeheight(indexRelation);
				}
				else
				{
					/* For other index types, just set it to "unknown" for now */
					info->tree_height = -1;
				}
			}
			else
			{
				/* Zero these out for partitioned indexes */
				info->pages = 0;
				info->tuples = 0.0;
				info->tree_height = -1;
			}

			index_close(indexRelation, NoLock);

			/*
			 * We've historically used lcons() here.  It'd make more sense to
			 * use lappend(), but that causes the planner to change behavior
			 * in cases where two indexes seem equally attractive.  For now,
			 * stick with lcons() --- few tables should have so many indexes
			 * that the O(N^2) behavior of lcons() is really a problem.
			 */
			indexinfos = lcons(info, indexinfos);
		}

		list_free(indexoidlist);
	}

	rel->indexlist = indexinfos;

	rel->statlist = get_relation_statistics(root, rel, relation);

	/* Grab foreign-table info using the relcache, while we have it */
	if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
	{
		/* Check if the access to foreign tables is restricted */
		if (unlikely((restrict_nonsystem_relation_kind & RESTRICT_RELKIND_FOREIGN_TABLE) != 0))
		{
			/* there must not be built-in foreign tables */
			Assert(RelationGetRelid(relation) >= FirstNormalObjectId);

			ereport(ERROR,
					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
					 errmsg("access to non-system foreign table is restricted")));
		}

		rel->serverid = GetForeignServerIdByRelId(RelationGetRelid(relation));
		rel->fdwroutine = GetFdwRoutineForRelation(relation, true);
	}
	else
	{
		rel->serverid = InvalidOid;
		rel->fdwroutine = NULL;
	}

	/* Collect info about relation's foreign keys, if relevant */
	get_relation_foreign_keys(root, rel, relation, inhparent);

	/* Collect info about functions implemented by the rel's table AM. */
	if (relation->rd_tableam &&
		relation->rd_tableam->scan_set_tidrange != NULL &&
		relation->rd_tableam->scan_getnextslot_tidrange != NULL)
		rel->amflags |= AMFLAG_HAS_TID_RANGE;

	/*
	 * Collect info about relation's partitioning scheme, if any. Only
	 * inheritance parents may be partitioned.
	 */
	if (inhparent && relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
		set_relation_partition_info(root, rel, relation);

	table_close(relation, NoLock);
}

/*
 * get_relation_foreign_keys -
 *	  Retrieves foreign key information for a given relation.
 *
 * ForeignKeyOptInfos for relevant foreign keys are created and added to
 * root->fkey_list.  We do this now while we have the relcache entry open.
 * We could sometimes avoid making useless ForeignKeyOptInfos if we waited
 * until all RelOptInfos have been built, but the cost of re-opening the
 * relcache entries would probably exceed any savings.
 */
static void
get_relation_foreign_keys(PlannerInfo *root, RelOptInfo *rel,
						  Relation relation, bool inhparent)
{
	List	   *rtable = root->parse->rtable;
	List	   *cachedfkeys;
	ListCell   *lc;

	/*
	 * If it's not a baserel, we don't care about its FKs.  Also, if the query
	 * references only a single relation, we can skip the lookup since no FKs
	 * could satisfy the requirements below.
	 */
	if (rel->reloptkind != RELOPT_BASEREL ||
		list_length(rtable) < 2)
		return;

	/*
	 * If it's the parent of an inheritance tree, ignore its FKs.  We could
	 * make useful FK-based deductions if we found that all members of the
	 * inheritance tree have equivalent FK constraints, but detecting that
	 * would require code that hasn't been written.
	 */
	if (inhparent)
		return;

	/*
	 * Extract data about relation's FKs from the relcache.  Note that this
	 * list belongs to the relcache and might disappear in a cache flush, so
	 * we must not do any further catalog access within this function.
	 */
	cachedfkeys = RelationGetFKeyList(relation);

	/*
	 * Figure out which FKs are of interest for this query, and create
	 * ForeignKeyOptInfos for them.  We want only FKs that reference some
	 * other RTE of the current query.  In queries containing self-joins,
	 * there might be more than one other RTE for a referenced table, and we
	 * should make a ForeignKeyOptInfo for each occurrence.
	 *
	 * Ideally, we would ignore RTEs that correspond to non-baserels, but it's
	 * too hard to identify those here, so we might end up making some useless
	 * ForeignKeyOptInfos.  If so, match_foreign_keys_to_quals() will remove
	 * them again.
	 */
	foreach(lc, cachedfkeys)
	{
		ForeignKeyCacheInfo *cachedfk = (ForeignKeyCacheInfo *) lfirst(lc);
		Index		rti;
		ListCell   *lc2;

		/* conrelid should always be that of the table we're considering */
		Assert(cachedfk->conrelid == RelationGetRelid(relation));

		/* skip constraints currently not enforced */
		if (!cachedfk->conenforced)
			continue;

		/* Scan to find other RTEs matching confrelid */
		rti = 0;
		foreach(lc2, rtable)
		{
			RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc2);
			ForeignKeyOptInfo *info;

			rti++;
			/* Ignore if not the correct table */
			if (rte->rtekind != RTE_RELATION ||
				rte->relid != cachedfk->confrelid)
				continue;
			/* Ignore if it's an inheritance parent; doesn't really match */
			if (rte->inh)
				continue;
			/* Ignore self-referential FKs; we only care about joins */
			if (rti == rel->relid)
				continue;

			/* OK, let's make an entry */
			info = makeNode(ForeignKeyOptInfo);
			info->con_relid = rel->relid;
			info->ref_relid = rti;
			info->nkeys = cachedfk->nkeys;
			memcpy(info->conkey, cachedfk->conkey, sizeof(info->conkey));
			memcpy(info->confkey, cachedfk->confkey, sizeof(info->confkey));
			memcpy(info->conpfeqop, cachedfk->conpfeqop, sizeof(info->conpfeqop));
			/* zero out fields to be filled by match_foreign_keys_to_quals */
			info->nmatched_ec = 0;
			info->nconst_ec = 0;
			info->nmatched_rcols = 0;
			info->nmatched_ri = 0;
			memset(info->eclass, 0, sizeof(info->eclass));
			memset(info->fk_eclass_member, 0, sizeof(info->fk_eclass_member));
			memset(info->rinfos, 0, sizeof(info->rinfos));

			root->fkey_list = lappend(root->fkey_list, info);
		}
	}
}

/*
 * get_relation_notnullatts -
 *	  Retrieves column not-null constraint information for a given relation.
 *
 * We do this while we have the relcache entry open, and store the column
 * not-null constraint information in a hash table based on the relation OID.
 */
void
get_relation_notnullatts(PlannerInfo *root, Relation relation)
{
	Oid			relid = RelationGetRelid(relation);
	NotnullHashEntry *hentry;
	bool		found;
	Bitmapset  *notnullattnums = NULL;

	/* bail out if the relation has no not-null constraints */
	if (relation->rd_att->constr == NULL ||
		!relation->rd_att->constr->has_not_null)
		return;

	/* create the hash table if it hasn't been created yet */
	if (root->glob->rel_notnullatts_hash == NULL)
	{
		HTAB	   *hashtab;
		HASHCTL		hash_ctl;

		hash_ctl.keysize = sizeof(Oid);
		hash_ctl.entrysize = sizeof(NotnullHashEntry);
		hash_ctl.hcxt = CurrentMemoryContext;

		hashtab = hash_create("Relation NOT NULL attnums",
							  64L,	/* arbitrary initial size */
							  &hash_ctl,
							  HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);

		root->glob->rel_notnullatts_hash = hashtab;
	}

	/*
	 * Create a hash entry for this relation OID, if we don't have one
	 * already.
	 */
	hentry = (NotnullHashEntry *) hash_search(root->glob->rel_notnullatts_hash,
											  &relid,
											  HASH_ENTER,
											  &found);

	/* bail out if a hash entry already exists for this relation OID */
	if (found)
		return;

	/* collect the column not-null constraint information for this relation */
	for (int i = 0; i < relation->rd_att->natts; i++)
	{
		CompactAttribute *attr = TupleDescCompactAttr(relation->rd_att, i);

		Assert(attr->attnullability != ATTNULLABLE_UNKNOWN);

		if (attr->attnullability == ATTNULLABLE_VALID)
		{
			notnullattnums = bms_add_member(notnullattnums, i + 1);

			/*
			 * Per RemoveAttributeById(), dropped columns will have their
			 * attnotnull unset, so we needn't check for dropped columns in
			 * the above condition.
			 */
			Assert(!attr->attisdropped);
		}
	}

	/* ... and initialize the new hash entry */
	hentry->notnullattnums = notnullattnums;
}

/*
 * find_relation_notnullatts -
 *	  Searches the hash table and returns the column not-null constraint
 *	  information for a given relation.
 */
Bitmapset *
find_relation_notnullatts(PlannerInfo *root, Oid relid)
{
	NotnullHashEntry *hentry;
	bool		found;

	if (root->glob->rel_notnullatts_hash == NULL)
		return NULL;

	hentry = (NotnullHashEntry *) hash_search(root->glob->rel_notnullatts_hash,
											  &relid,
											  HASH_FIND,
											  &found);
	if (!found)
		return NULL;

	return hentry->notnullattnums;
}

/*
 * infer_arbiter_indexes -
 *	  Determine the unique indexes used to arbitrate speculative insertion.
 *
 * Uses user-supplied inference clause expressions and predicate to match a
 * unique index from those defined and ready on the heap relation (target).
 * An exact match is required on columns/expressions (although they can appear
 * in any order).  However, the predicate given by the user need only restrict
 * insertion to a subset of some part of the table covered by some particular
 * unique index (in particular, a partial unique index) in order to be
 * inferred.
 *
 * The implementation does not consider which B-Tree operator class any
 * particular available unique index attribute uses, unless one was specified
 * in the inference specification. The same is true of collations.  In
 * particular, there is no system dependency on the default operator class for
 * the purposes of inference.  If no opclass (or collation) is specified, then
 * all matching indexes (that may or may not match the default in terms of
 * each attribute opclass/collation) are used for inference.
 */
List *
infer_arbiter_indexes(PlannerInfo *root)
{
	OnConflictExpr *onconflict = root->parse->onConflict;

	/* Iteration state */
	Index		varno;
	RangeTblEntry *rte;
	Relation	relation;
	Oid			indexOidFromConstraint = InvalidOid;
	List	   *indexList;
	List	   *indexRelList = NIL;

	/*
	 * Required attributes and expressions used to match indexes to the clause
	 * given by the user.  In the ON CONFLICT ON CONSTRAINT case, we compute
	 * these from that constraint's index to match all other indexes, to
	 * account for the case where that index is being concurrently reindexed.
	 */
	List	   *inferIndexExprs = (List *) onconflict->arbiterWhere;
	Bitmapset  *inferAttrs = NULL;
	List	   *inferElems = NIL;

	/* Results */
	List	   *results = NIL;
	bool		foundValid = false;

	/*
	 * Quickly return NIL for ON CONFLICT DO NOTHING without an inference
	 * specification or named constraint.  ON CONFLICT DO SELECT/UPDATE
	 * statements must always provide one or the other (but parser ought to
	 * have caught that already).
	 */
	if (onconflict->arbiterElems == NIL &&
		onconflict->constraint == InvalidOid)
		return NIL;

	/*
	 * We need not lock the relation since it was already locked, either by
	 * the rewriter or when expand_inherited_rtentry() added it to the query's
	 * rangetable.
	 */
	varno = root->parse->resultRelation;
	rte = rt_fetch(varno, root->parse->rtable);

	relation = table_open(rte->relid, NoLock);

	/*
	 * Build normalized/BMS representation of plain indexed attributes, as
	 * well as a separate list of expression items.  This simplifies matching
	 * the cataloged definition of indexes.
	 */
	foreach_ptr(InferenceElem, elem, onconflict->arbiterElems)
	{
		Var		   *var;
		int			attno;

		/* we cannot also have a constraint name, per grammar */
		Assert(!OidIsValid(onconflict->constraint));

		if (!IsA(elem->expr, Var))
		{
			/* If not a plain Var, just shove it in inferElems for now */
			inferElems = lappend(inferElems, elem->expr);
			continue;
		}

		var = (Var *) elem->expr;
		attno = var->varattno;

		if (attno == 0)
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("whole row unique index inference specifications are not supported")));

		inferAttrs = bms_add_member(inferAttrs,
									attno - FirstLowInvalidHeapAttributeNumber);
	}

	/*
	 * Next, open all the indexes.  We need this list for two things: first,
	 * if an ON CONSTRAINT clause was given, and that constraint's index is
	 * undergoing REINDEX CONCURRENTLY, then we need to consider all matches
	 * for that index.  Second, if an attribute list was specified in the ON
	 * CONFLICT clause, we use the list to find the indexes whose attributes
	 * match that list.
	 */
	indexList = RelationGetIndexList(relation);
	foreach_oid(indexoid, indexList)
	{
		Relation	idxRel;

		/* obtain the same lock type that the executor will ultimately use */
		idxRel = index_open(indexoid, rte->rellockmode);
		indexRelList = lappend(indexRelList, idxRel);
	}

	/*
	 * If a constraint was named in the command, look up its index.  We don't
	 * return it immediately because we need some additional sanity checks,
	 * and also because we need to include other indexes as arbiters to
	 * account for REINDEX CONCURRENTLY processing it.
	 */
	if (onconflict->constraint != InvalidOid)
	{
		/* we cannot also have an explicit list of elements, per grammar */
		Assert(onconflict->arbiterElems == NIL);

		indexOidFromConstraint = get_constraint_index(onconflict->constraint);
		if (indexOidFromConstraint == InvalidOid)
			ereport(ERROR,
					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
					 errmsg("constraint in ON CONFLICT clause has no associated index")));

		/*
		 * Find the named constraint index to extract its attributes and
		 * predicates.
		 */
		foreach_ptr(RelationData, idxRel, indexRelList)
		{
			Form_pg_index idxForm = idxRel->rd_index;

			if (indexOidFromConstraint == idxForm->indexrelid)
			{
				/* Found it. */
				Assert(idxForm->indisready);

				/*
				 * Set up inferElems and inferIndexExprs to match the
				 * constraint index, so that we can match them in the loop
				 * below.
				 */
				for (int natt = 0; natt < idxForm->indnkeyatts; natt++)
				{
					int			attno;

					attno = idxRel->rd_index->indkey.values[natt];
					if (attno != InvalidAttrNumber)
						inferAttrs =
							bms_add_member(inferAttrs,
										   attno - FirstLowInvalidHeapAttributeNumber);
				}

				inferElems = RelationGetIndexExpressionsExpand(idxRel);
				inferIndexExprs = RelationGetIndexPredicateExpand(idxRel);
				break;
			}
		}
	}

	/*
	 * Using that representation, iterate through the list of indexes on the
	 * target relation to find matches.
	 */
	foreach_ptr(RelationData, idxRel, indexRelList)
	{
		Form_pg_index idxForm;
		Bitmapset  *indexedAttrs;
		List	   *idxExprs;
		List	   *predExprs;
		AttrNumber	natt;
		bool		match;

		/*
		 * Extract info from the relation descriptor for the index.
		 *
		 * Let executor complain about !indimmediate case directly, because
		 * enforcement needs to occur there anyway when an inference clause is
		 * omitted.
		 */
		idxForm = idxRel->rd_index;

		/*
		 * Ignore indexes that aren't indisready, because we cannot trust
		 * their catalog structure yet.  However, if any indexes are marked
		 * indisready but not yet indisvalid, we still consider them, because
		 * they might turn valid while we're running.  Doing it this way
		 * allows a concurrent transaction with a slightly later catalog
		 * snapshot infer the same set of indexes, which is critical to
		 * prevent spurious 'duplicate key' errors.
		 *
		 * However, another critical aspect is that a unique index that isn't
		 * yet marked indisvalid=true might not be complete yet, meaning it
		 * wouldn't detect possible duplicate rows.  In order to prevent false
		 * negatives, we require that we include in the set of inferred
		 * indexes at least one index that is marked valid.
		 */
		if (!idxForm->indisready)
			continue;

		/*
		 * Ignore invalid indexes for partitioned tables.  It's possible that
		 * some partitions don't have the index (yet), and then we would not
		 * find a match during ExecInitPartitionInfo.
		 */
		if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
			!idxForm->indisvalid)
			continue;

		/*
		 * Note that we do not perform a check against indcheckxmin (like e.g.
		 * get_relation_info()) here to eliminate candidates, because
		 * uniqueness checking only cares about the most recently committed
		 * tuple versions.
		 */

		/*
		 * Look for match for "ON constraint_name" variant, which may not be a
		 * unique constraint.  This can only be a constraint name.
		 */
		if (indexOidFromConstraint == idxForm->indexrelid)
		{
			/*
			 * ON CONFLICT DO UPDATE and ON CONFLICT DO SELECT are not
			 * supported with exclusion constraints.
			 */
			if (idxForm->indisexclusion &&
				(onconflict->action == ONCONFLICT_UPDATE ||
				 onconflict->action == ONCONFLICT_SELECT))
				ereport(ERROR,
						errcode(ERRCODE_WRONG_OBJECT_TYPE),
						errmsg("ON CONFLICT DO %s not supported with exclusion constraints",
							   onconflict->action == ONCONFLICT_UPDATE ? "UPDATE" : "SELECT"));

			/* Consider this one a match already */
			results = lappend_oid(results, idxForm->indexrelid);
			foundValid |= idxForm->indisvalid;
			continue;
		}
		else if (indexOidFromConstraint != InvalidOid)
		{
			/*
			 * In the case of "ON constraint_name DO SELECT/UPDATE" we need to
			 * skip non-unique candidates.
			 */
			if (!idxForm->indisunique &&
				(onconflict->action == ONCONFLICT_UPDATE ||
				 onconflict->action == ONCONFLICT_SELECT))
				continue;
		}
		else
		{
			/*
			 * Only considering conventional inference at this point (not
			 * named constraints), so index under consideration can be
			 * immediately skipped if it's not unique.
			 */
			if (!idxForm->indisunique)
				continue;
		}

		/*
		 * So-called unique constraints with WITHOUT OVERLAPS are really
		 * exclusion constraints, so skip those too.
		 */
		if (idxForm->indisexclusion)
			continue;

		/* Build BMS representation of plain (non expression) index attrs */
		indexedAttrs = NULL;
		for (natt = 0; natt < idxForm->indnkeyatts; natt++)
		{
			int			attno = idxRel->rd_index->indkey.values[natt];

			if (attno != 0)
				indexedAttrs = bms_add_member(indexedAttrs,
											  attno - FirstLowInvalidHeapAttributeNumber);
		}

		/* Non-expression attributes (if any) must match */
		if (!bms_equal(indexedAttrs, inferAttrs))
			continue;

		/* Expression attributes (if any) must match */
		idxExprs = RelationGetIndexExpressionsExpand(idxRel);
		if (idxExprs)
		{
			if (varno != 1)
				ChangeVarNodes((Node *) idxExprs, 1, varno, 0);

			idxExprs = (List *) eval_const_expressions(root, (Node *) idxExprs);
		}

		/*
		 * If arbiterElems are present, check them.  (Note that if a
		 * constraint name was given in the command line, this list is NIL.)
		 */
		match = true;
		foreach_ptr(InferenceElem, elem, onconflict->arbiterElems)
		{
			/*
			 * Ensure that collation/opclass aspects of inference expression
			 * element match.  Even though this loop is primarily concerned
			 * with matching expressions, it is a convenient point to check
			 * this for both expressions and ordinary (non-expression)
			 * attributes appearing as inference elements.
			 */
			if (!infer_collation_opclass_match(elem, idxRel, idxExprs))
			{
				match = false;
				break;
			}

			/*
			 * Plain Vars don't factor into count of expression elements, and
			 * the question of whether or not they satisfy the index
			 * definition has already been considered (they must).
			 */
			if (IsA(elem->expr, Var))
				continue;

			/*
			 * Might as well avoid redundant check in the rare cases where
			 * infer_collation_opclass_match() is required to do real work.
			 * Otherwise, check that element expression appears in cataloged
			 * index definition.
			 */
			if (elem->infercollid != InvalidOid ||
				elem->inferopclass != InvalidOid ||
				list_member(idxExprs, elem->expr))
				continue;

			match = false;
			break;
		}
		if (!match)
			continue;

		/*
		 * In case of inference from an attribute list, ensure that the
		 * expression elements from inference clause are not missing any
		 * cataloged expressions.  This does the right thing when unique
		 * indexes redundantly repeat the same attribute, or if attributes
		 * redundantly appear multiple times within an inference clause.
		 *
		 * In case a constraint was named, ensure the candidate has an equal
		 * set of expressions as the named constraint's index.
		 */
		if (list_difference(idxExprs, inferElems) != NIL)
			continue;

		predExprs = RelationGetIndexPredicateExpand(idxRel);
		if (predExprs)
		{
			if (varno != 1)
				ChangeVarNodes((Node *) predExprs, 1, varno, 0);

			predExprs = (List *)
				eval_const_expressions(root,
									   (Node *) make_ands_explicit(predExprs));
			predExprs = make_ands_implicit((Expr *) predExprs);
		}

		/*
		 * Partial indexes affect each form of ON CONFLICT differently: if a
		 * constraint was named, then the predicates must be identical.  In
		 * conventional inference, the index's predicate must be implied by
		 * the WHERE clause.
		 */
		if (OidIsValid(indexOidFromConstraint))
		{
			if (list_difference(predExprs, inferIndexExprs) != NIL)
				continue;
		}
		else
		{
			if (!predicate_implied_by(predExprs, inferIndexExprs, false))
				continue;
		}

		/* All good -- consider this index a match */
		results = lappend_oid(results, idxForm->indexrelid);
		foundValid |= idxForm->indisvalid;
	}

	/* Close all indexes */
	foreach_ptr(RelationData, idxRel, indexRelList)
	{
		index_close(idxRel, NoLock);
	}

	list_free(indexList);
	list_free(indexRelList);
	table_close(relation, NoLock);

	/* We require at least one indisvalid index */
	if (results == NIL || !foundValid)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
				 errmsg("there is no unique or exclusion constraint matching the ON CONFLICT specification")));

	return results;
}

/*
 * infer_collation_opclass_match - ensure infer element opclass/collation match
 *
 * Given unique index inference element from inference specification, if
 * collation was specified, or if opclass was specified, verify that there is
 * at least one matching indexed attribute (occasionally, there may be more).
 * Skip this in the common case where inference specification does not include
 * collation or opclass (instead matching everything, regardless of cataloged
 * collation/opclass of indexed attribute).
 *
 * At least historically, Postgres has not offered collations or opclasses
 * with alternative-to-default notions of equality, so these additional
 * criteria should only be required infrequently.
 *
 * Don't give up immediately when an inference element matches some attribute
 * cataloged as indexed but not matching additional opclass/collation
 * criteria.  This is done so that the implementation is as forgiving as
 * possible of redundancy within cataloged index attributes (or, less
 * usefully, within inference specification elements).  If collations actually
 * differ between apparently redundantly indexed attributes (redundant within
 * or across indexes), then there really is no redundancy as such.
 *
 * Note that if an inference element specifies an opclass and a collation at
 * once, both must match in at least one particular attribute within index
 * catalog definition in order for that inference element to be considered
 * inferred/satisfied.
 */
static bool
infer_collation_opclass_match(InferenceElem *elem, Relation idxRel,
							  List *idxExprs)
{
	AttrNumber	natt;
	Oid			inferopfamily = InvalidOid; /* OID of opclass opfamily */
	Oid			inferopcinputtype = InvalidOid; /* OID of opclass input type */
	int			nplain = 0;		/* # plain attrs observed */

	/*
	 * If inference specification element lacks collation/opclass, then no
	 * need to check for exact match.
	 */
	if (elem->infercollid == InvalidOid && elem->inferopclass == InvalidOid)
		return true;

	/*
	 * Lookup opfamily and input type, for matching indexes
	 */
	if (elem->inferopclass)
	{
		inferopfamily = get_opclass_family(elem->inferopclass);
		inferopcinputtype = get_opclass_input_type(elem->inferopclass);
	}

	for (natt = 1; natt <= idxRel->rd_att->natts; natt++)
	{
		Oid			opfamily = idxRel->rd_opfamily[natt - 1];
		Oid			opcinputtype = idxRel->rd_opcintype[natt - 1];
		Oid			collation = idxRel->rd_indcollation[natt - 1];
		int			attno = idxRel->rd_index->indkey.values[natt - 1];

		if (attno != 0)
			nplain++;

		if (elem->inferopclass != InvalidOid &&
			(inferopfamily != opfamily || inferopcinputtype != opcinputtype))
		{
			/* Attribute needed to match opclass, but didn't */
			continue;
		}

		if (elem->infercollid != InvalidOid &&
			elem->infercollid != collation)
		{
			/* Attribute needed to match collation, but didn't */
			continue;
		}

		/* If one matching index att found, good enough -- return true */
		if (IsA(elem->expr, Var))
		{
			if (((Var *) elem->expr)->varattno == attno)
				return true;
		}
		else if (attno == 0)
		{
			Node	   *nattExpr = list_nth(idxExprs, (natt - 1) - nplain);

			/*
			 * Note that unlike routines like match_index_to_operand() we
			 * don't need to care about RelabelType.  Neither the index
			 * definition nor the inference clause should contain them.
			 */
			if (equal(elem->expr, nattExpr))
				return true;
		}
	}

	return false;
}

/*
 * estimate_rel_size - estimate # pages and # tuples in a table or index
 *
 * We also estimate the fraction of the pages that are marked all-visible in
 * the visibility map, for use in estimation of index-only scans.
 *
 * If attr_widths isn't NULL, it points to the zero-index entry of the
 * relation's attr_widths[] cache; we fill this in if we have need to compute
 * the attribute widths for estimation purposes.
 */
void
estimate_rel_size(Relation rel, int32 *attr_widths,
				  BlockNumber *pages, double *tuples, double *allvisfrac)
{
	BlockNumber curpages;
	BlockNumber relpages;
	double		reltuples;
	BlockNumber relallvisible;
	double		density;

	if (RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind))
	{
		table_relation_estimate_size(rel, attr_widths, pages, tuples,
									 allvisfrac);
	}
	else if (rel->rd_rel->relkind == RELKIND_INDEX)
	{
		/*
		 * XXX: It'd probably be good to move this into a callback, individual
		 * index types e.g. know if they have a metapage.
		 */

		/* it has storage, ok to call the smgr */
		curpages = RelationGetNumberOfBlocks(rel);

		/* report estimated # pages */
		*pages = curpages;
		/* quick exit if rel is clearly empty */
		if (curpages == 0)
		{
			*tuples = 0;
			*allvisfrac = 0;
			return;
		}

		/* coerce values in pg_class to more desirable types */
		relpages = (BlockNumber) rel->rd_rel->relpages;
		reltuples = (double) rel->rd_rel->reltuples;
		relallvisible = (BlockNumber) rel->rd_rel->relallvisible;

		/*
		 * Discount the metapage while estimating the number of tuples. This
		 * is a kluge because it assumes more than it ought to about index
		 * structure.  Currently it's OK for btree, hash, and GIN indexes but
		 * suspect for GiST indexes.
		 */
		if (relpages > 0)
		{
			curpages--;
			relpages--;
		}

		/* estimate number of tuples from previous tuple density */
		if (reltuples >= 0 && relpages > 0)
			density = reltuples / (double) relpages;
		else
		{
			/*
			 * If we have no data because the relation was never vacuumed,
			 * estimate tuple width from attribute datatypes.  We assume here
			 * that the pages are completely full, which is OK for tables
			 * (since they've presumably not been VACUUMed yet) but is
			 * probably an overestimate for indexes.  Fortunately
			 * get_relation_info() can clamp the overestimate to the parent
			 * table's size.
			 *
			 * Note: this code intentionally disregards alignment
			 * considerations, because (a) that would be gilding the lily
			 * considering how crude the estimate is, and (b) it creates
			 * platform dependencies in the default plans which are kind of a
			 * headache for regression testing.
			 *
			 * XXX: Should this logic be more index specific?
			 */
			int32		tuple_width;

			tuple_width = get_rel_data_width(rel, attr_widths);
			tuple_width += MAXALIGN(SizeofHeapTupleHeader);
			tuple_width += sizeof(ItemIdData);
			/* note: integer division is intentional here */
			density = (BLCKSZ - SizeOfPageHeaderData) / tuple_width;
		}
		*tuples = rint(density * (double) curpages);

		/*
		 * We use relallvisible as-is, rather than scaling it up like we do
		 * for the pages and tuples counts, on the theory that any pages added
		 * since the last VACUUM are most likely not marked all-visible.  But
		 * costsize.c wants it converted to a fraction.
		 */
		if (relallvisible == 0 || curpages <= 0)
			*allvisfrac = 0;
		else if ((double) relallvisible >= curpages)
			*allvisfrac = 1;
		else
			*allvisfrac = (double) relallvisible / curpages;
	}
	else
	{
		/*
		 * Just use whatever's in pg_class.  This covers foreign tables,
		 * sequences, and also relkinds without storage (shouldn't get here?);
		 * see initializations in AddNewRelationTuple().  Note that FDW must
		 * cope if reltuples is -1!
		 */
		*pages = rel->rd_rel->relpages;
		*tuples = rel->rd_rel->reltuples;
		*allvisfrac = 0;
	}
}


/*
 * get_rel_data_width
 *
 * Estimate the average width of (the data part of) the relation's tuples.
 *
 * If attr_widths isn't NULL, it points to the zero-index entry of the
 * relation's attr_widths[] cache; use and update that cache as appropriate.
 *
 * Currently we ignore dropped columns.  Ideally those should be included
 * in the result, but we haven't got any way to get info about them; and
 * since they might be mostly NULLs, treating them as zero-width is not
 * necessarily the wrong thing anyway.
 */
int32
get_rel_data_width(Relation rel, int32 *attr_widths)
{
	int64		tuple_width = 0;
	int			i;

	for (i = 1; i <= RelationGetNumberOfAttributes(rel); i++)
	{
		Form_pg_attribute att = TupleDescAttr(rel->rd_att, i - 1);
		int32		item_width;

		if (att->attisdropped)
			continue;

		/* use previously cached data, if any */
		if (attr_widths != NULL && attr_widths[i] > 0)
		{
			tuple_width += attr_widths[i];
			continue;
		}

		/* This should match set_rel_width() in costsize.c */
		item_width = get_attavgwidth(RelationGetRelid(rel), i);
		if (item_width <= 0)
		{
			item_width = get_typavgwidth(att->atttypid, att->atttypmod);
			Assert(item_width > 0);
		}
		if (attr_widths != NULL)
			attr_widths[i] = item_width;
		tuple_width += item_width;
	}

	return clamp_width_est(tuple_width);
}

/*
 * get_relation_data_width
 *
 * External API for get_rel_data_width: same behavior except we have to
 * open the relcache entry.
 */
int32
get_relation_data_width(Oid relid, int32 *attr_widths)
{
	int32		result;
	Relation	relation;

	/* As above, assume relation is already locked */
	relation = table_open(relid, NoLock);

	result = get_rel_data_width(relation, attr_widths);

	table_close(relation, NoLock);

	return result;
}


/*
 * get_relation_constraints
 *
 * Retrieve the applicable constraint expressions of the given relation.
 * Only constraints that have been validated are considered.
 *
 * Returns a List (possibly empty) of constraint expressions.  Each one
 * has been canonicalized, and its Vars are changed to have the varno
 * indicated by rel->relid.  This allows the expressions to be easily
 * compared to expressions taken from WHERE.
 *
 * If include_noinherit is true, it's okay to include constraints that
 * are marked NO INHERIT.
 *
 * If include_notnull is true, "col IS NOT NULL" expressions are generated
 * and added to the result for each column that's marked attnotnull.
 *
 * If include_partition is true, and the relation is a partition,
 * also include the partitioning constraints.
 *
 * Note: at present this is invoked at most once per relation per planner
 * run, and in many cases it won't be invoked at all, so there seems no
 * point in caching the data in RelOptInfo.
 */
static List *
get_relation_constraints(PlannerInfo *root,
						 Oid relationObjectId, RelOptInfo *rel,
						 bool include_noinherit,
						 bool include_notnull,
						 bool include_partition)
{
	List	   *result = NIL;
	Index		varno = rel->relid;
	Relation	relation;
	TupleConstr *constr;

	/*
	 * We assume the relation has already been safely locked.
	 */
	relation = table_open(relationObjectId, NoLock);

	constr = relation->rd_att->constr;
	if (constr != NULL)
	{
		int			num_check = constr->num_check;
		int			i;

		for (i = 0; i < num_check; i++)
		{
			Node	   *cexpr;

			/*
			 * If this constraint hasn't been fully validated yet, we must
			 * ignore it here.
			 */
			if (!constr->check[i].ccvalid)
				continue;

			/*
			 * NOT ENFORCED constraints are always marked as invalid, which
			 * should have been ignored.
			 */
			Assert(constr->check[i].ccenforced);

			/*
			 * Also ignore if NO INHERIT and we weren't told that that's safe.
			 */
			if (constr->check[i].ccnoinherit && !include_noinherit)
				continue;

			cexpr = stringToNode(constr->check[i].ccbin);

			/*
			 * Fix Vars to have the desired varno.  This must be done before
			 * const-simplification because eval_const_expressions reduces
			 * NullTest for Vars based on varno.
			 */
			if (varno != 1)
				ChangeVarNodes(cexpr, 1, varno, 0);

			/*
			 * Run each expression through const-simplification and
			 * canonicalization.  This is not just an optimization, but is
			 * necessary, because we will be comparing it to
			 * similarly-processed qual clauses, and may fail to detect valid
			 * matches without this.  This must match the processing done to
			 * qual clauses in preprocess_expression()!  (We can skip the
			 * stuff involving subqueries, however, since we don't allow any
			 * in check constraints.)
			 */
			cexpr = eval_const_expressions(root, cexpr);

			cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);

			/*
			 * Finally, convert to implicit-AND format (that is, a List) and
			 * append the resulting item(s) to our output list.
			 */
			result = list_concat(result,
								 make_ands_implicit((Expr *) cexpr));
		}

		/* Add NOT NULL constraints in expression form, if requested */
		if (include_notnull && constr->has_not_null)
		{
			int			natts = relation->rd_att->natts;

			for (i = 1; i <= natts; i++)
			{
				CompactAttribute *att = TupleDescCompactAttr(relation->rd_att, i - 1);

				if (att->attnullability == ATTNULLABLE_VALID && !att->attisdropped)
				{
					Form_pg_attribute wholeatt = TupleDescAttr(relation->rd_att, i - 1);
					NullTest   *ntest = makeNode(NullTest);

					ntest->arg = (Expr *) makeVar(varno,
												  i,
												  wholeatt->atttypid,
												  wholeatt->atttypmod,
												  wholeatt->attcollation,
												  0);
					ntest->nulltesttype = IS_NOT_NULL;

					/*
					 * argisrow=false is correct even for a composite column,
					 * because attnotnull does not represent a SQL-spec IS NOT
					 * NULL test in such a case, just IS DISTINCT FROM NULL.
					 */
					ntest->argisrow = false;
					ntest->location = -1;
					result = lappend(result, ntest);
				}
			}
		}
	}

	/*
	 * Add partitioning constraints, if requested.
	 */
	if (include_partition && relation->rd_rel->relispartition)
	{
		/* make sure rel->partition_qual is set */
		set_baserel_partition_constraint(relation, rel);
		result = list_concat(result, rel->partition_qual);
	}

	/*
	 * Expand virtual generated columns in the constraint expressions.
	 */
	if (result)
		result = (List *) expand_generated_columns_in_expr((Node *) result,
														   relation,
														   varno);

	table_close(relation, NoLock);

	return result;
}

/*
 * Try loading data for the statistics object.
 *
 * We don't know if the data (specified by statOid and inh value) exist.
 * The result is stored in stainfos list.
 */
static void
get_relation_statistics_worker(List **stainfos, RelOptInfo *rel,
							   Oid statOid, bool inh,
							   Bitmapset *keys, List *exprs)
{
	Form_pg_statistic_ext_data dataForm;
	HeapTuple	dtup;

	dtup = SearchSysCache2(STATEXTDATASTXOID,
						   ObjectIdGetDatum(statOid), BoolGetDatum(inh));
	if (!HeapTupleIsValid(dtup))
		return;

	dataForm = (Form_pg_statistic_ext_data) GETSTRUCT(dtup);

	/* add one StatisticExtInfo for each kind built */
	if (statext_is_kind_built(dtup, STATS_EXT_NDISTINCT))
	{
		StatisticExtInfo *info = makeNode(StatisticExtInfo);

		info->statOid = statOid;
		info->inherit = dataForm->stxdinherit;
		info->rel = rel;
		info->kind = STATS_EXT_NDISTINCT;
		info->keys = bms_copy(keys);
		info->exprs = exprs;

		*stainfos = lappend(*stainfos, info);
	}

	if (statext_is_kind_built(dtup, STATS_EXT_DEPENDENCIES))
	{
		StatisticExtInfo *info = makeNode(StatisticExtInfo);

		info->statOid = statOid;
		info->inherit = dataForm->stxdinherit;
		info->rel = rel;
		info->kind = STATS_EXT_DEPENDENCIES;
		info->keys = bms_copy(keys);
		info->exprs = exprs;

		*stainfos = lappend(*stainfos, info);
	}

	if (statext_is_kind_built(dtup, STATS_EXT_MCV))
	{
		StatisticExtInfo *info = makeNode(StatisticExtInfo);

		info->statOid = statOid;
		info->inherit = dataForm->stxdinherit;
		info->rel = rel;
		info->kind = STATS_EXT_MCV;
		info->keys = bms_copy(keys);
		info->exprs = exprs;

		*stainfos = lappend(*stainfos, info);
	}

	if (statext_is_kind_built(dtup, STATS_EXT_EXPRESSIONS))
	{
		StatisticExtInfo *info = makeNode(StatisticExtInfo);

		info->statOid = statOid;
		info->inherit = dataForm->stxdinherit;
		info->rel = rel;
		info->kind = STATS_EXT_EXPRESSIONS;
		info->keys = bms_copy(keys);
		info->exprs = exprs;

		*stainfos = lappend(*stainfos, info);
	}

	ReleaseSysCache(dtup);
}

/*
 * get_relation_statistics
 *		Retrieve extended statistics defined on the table.
 *
 * Returns a List (possibly empty) of StatisticExtInfo objects describing
 * the statistics.  Note that this doesn't load the actual statistics data,
 * just the identifying metadata.  Only stats actually built are considered.
 */
static List *
get_relation_statistics(PlannerInfo *root, RelOptInfo *rel,
						Relation relation)
{
	Index		varno = rel->relid;
	List	   *statoidlist;
	List	   *stainfos = NIL;
	ListCell   *l;

	statoidlist = RelationGetStatExtList(relation);

	foreach(l, statoidlist)
	{
		Oid			statOid = lfirst_oid(l);
		Form_pg_statistic_ext staForm;
		HeapTuple	htup;
		Bitmapset  *keys = NULL;
		List	   *exprs = NIL;
		int			i;

		htup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statOid));
		if (!HeapTupleIsValid(htup))
			elog(ERROR, "cache lookup failed for statistics object %u", statOid);
		staForm = (Form_pg_statistic_ext) GETSTRUCT(htup);

		/*
		 * First, build the array of columns covered.  This is ultimately
		 * wasted if no stats within the object have actually been built, but
		 * it doesn't seem worth troubling over that case.
		 */
		for (i = 0; i < staForm->stxkeys.dim1; i++)
			keys = bms_add_member(keys, staForm->stxkeys.values[i]);

		/*
		 * Preprocess expressions (if any). We read the expressions, fix the
		 * varnos, and run them through eval_const_expressions.
		 *
		 * XXX We don't know yet if there are any data for this stats object,
		 * with either stxdinherit value. But it's reasonable to assume there
		 * is at least one of those, possibly both. So it's better to process
		 * keys and expressions here.
		 */
		{
			bool		isnull;
			Datum		datum;

			/* decode expression (if any) */
			datum = SysCacheGetAttr(STATEXTOID, htup,
									Anum_pg_statistic_ext_stxexprs, &isnull);

			if (!isnull)
			{
				char	   *exprsString;

				exprsString = TextDatumGetCString(datum);
				exprs = (List *) stringToNode(exprsString);
				pfree(exprsString);

				/* Expand virtual generated columns in the expressions */
				exprs = (List *) expand_generated_columns_in_expr((Node *) exprs, relation, 1);

				/*
				 * Modify the copies we obtain from the relcache to have the
				 * correct varno for the parent relation, so that they match
				 * up correctly against qual clauses.
				 *
				 * This must be done before const-simplification because
				 * eval_const_expressions reduces NullTest for Vars based on
				 * varno.
				 */
				if (varno != 1)
					ChangeVarNodes((Node *) exprs, 1, varno, 0);

				/*
				 * Run the expressions through eval_const_expressions. This is
				 * not just an optimization, but is necessary, because the
				 * planner will be comparing them to similarly-processed qual
				 * clauses, and may fail to detect valid matches without this.
				 * We must not use canonicalize_qual, however, since these
				 * aren't qual expressions.
				 */
				exprs = (List *) eval_const_expressions(root, (Node *) exprs);

				/* May as well fix opfuncids too */
				fix_opfuncids((Node *) exprs);
			}
		}

		/* extract statistics for possible values of stxdinherit flag */

		get_relation_statistics_worker(&stainfos, rel, statOid, true, keys, exprs);

		get_relation_statistics_worker(&stainfos, rel, statOid, false, keys, exprs);

		ReleaseSysCache(htup);
		bms_free(keys);
	}

	list_free(statoidlist);

	return stainfos;
}

/*
 * relation_excluded_by_constraints
 *
 * Detect whether the relation need not be scanned because it has either
 * self-inconsistent restrictions, or restrictions inconsistent with the
 * relation's applicable constraints.
 *
 * Note: this examines only rel->relid, rel->reloptkind, and
 * rel->baserestrictinfo; therefore it can be called before filling in
 * other fields of the RelOptInfo.
 */
bool
relation_excluded_by_constraints(PlannerInfo *root,
								 RelOptInfo *rel, RangeTblEntry *rte)
{
	bool		include_noinherit;
	bool		include_notnull;
	bool		include_partition = false;
	List	   *safe_restrictions;
	List	   *constraint_pred;
	List	   *safe_constraints;
	ListCell   *lc;

	/* As of now, constraint exclusion works only with simple relations. */
	Assert(IS_SIMPLE_REL(rel));

	/*
	 * If there are no base restriction clauses, we have no hope of proving
	 * anything below, so fall out quickly.
	 */
	if (rel->baserestrictinfo == NIL)
		return false;

	/*
	 * Regardless of the setting of constraint_exclusion, detect
	 * constant-FALSE-or-NULL restriction clauses.  Although const-folding
	 * will reduce "anything AND FALSE" to just "FALSE", the baserestrictinfo
	 * list can still have other members besides the FALSE constant, due to
	 * qual pushdown and other mechanisms; so check them all.  This doesn't
	 * fire very often, but it seems cheap enough to be worth doing anyway.
	 * (Without this, we'd miss some optimizations that 9.5 and earlier found
	 * via much more roundabout methods.)
	 */
	foreach(lc, rel->baserestrictinfo)
	{
		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
		Expr	   *clause = rinfo->clause;

		if (clause && IsA(clause, Const) &&
			(((Const *) clause)->constisnull ||
			 !DatumGetBool(((Const *) clause)->constvalue)))
			return true;
	}

	/*
	 * Skip further tests, depending on constraint_exclusion.
	 */
	switch (constraint_exclusion)
	{
		case CONSTRAINT_EXCLUSION_OFF:
			/* In 'off' mode, never make any further tests */
			return false;

		case CONSTRAINT_EXCLUSION_PARTITION:

			/*
			 * When constraint_exclusion is set to 'partition' we only handle
			 * appendrel members.  Partition pruning has already been applied,
			 * so there is no need to consider the rel's partition constraints
			 * here.
			 */
			if (rel->reloptkind == RELOPT_OTHER_MEMBER_REL)
				break;			/* appendrel member, so process it */
			return false;

		case CONSTRAINT_EXCLUSION_ON:

			/*
			 * In 'on' mode, always apply constraint exclusion.  If we are
			 * considering a baserel that is a partition (i.e., it was
			 * directly named rather than expanded from a parent table), then
			 * its partition constraints haven't been considered yet, so
			 * include them in the processing here.
			 */
			if (rel->reloptkind == RELOPT_BASEREL)
				include_partition = true;
			break;				/* always try to exclude */
	}

	/*
	 * Check for self-contradictory restriction clauses.  We dare not make
	 * deductions with non-immutable functions, but any immutable clauses that
	 * are self-contradictory allow us to conclude the scan is unnecessary.
	 *
	 * Note: strip off RestrictInfo because predicate_refuted_by() isn't
	 * expecting to see any in its predicate argument.
	 */
	safe_restrictions = NIL;
	foreach(lc, rel->baserestrictinfo)
	{
		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);

		if (!contain_mutable_functions((Node *) rinfo->clause))
			safe_restrictions = lappend(safe_restrictions, rinfo->clause);
	}

	/*
	 * We can use weak refutation here, since we're comparing restriction
	 * clauses with restriction clauses.
	 */
	if (predicate_refuted_by(safe_restrictions, safe_restrictions, true))
		return true;

	/*
	 * Only plain relations have constraints, so stop here for other rtekinds.
	 */
	if (rte->rtekind != RTE_RELATION)
		return false;

	/*
	 * If we are scanning just this table, we can use NO INHERIT constraints,
	 * but not if we're scanning its children too.  (Note that partitioned
	 * tables should never have NO INHERIT constraints; but it's not necessary
	 * for us to assume that here.)
	 */
	include_noinherit = !rte->inh;

	/*
	 * Currently, attnotnull constraints must be treated as NO INHERIT unless
	 * this is a partitioned table.  In future we might track their
	 * inheritance status more accurately, allowing this to be refined.
	 *
	 * XXX do we need/want to change this?
	 */
	include_notnull = (!rte->inh || rte->relkind == RELKIND_PARTITIONED_TABLE);

	/*
	 * Fetch the appropriate set of constraint expressions.
	 */
	constraint_pred = get_relation_constraints(root, rte->relid, rel,
											   include_noinherit,
											   include_notnull,
											   include_partition);

	/*
	 * We do not currently enforce that CHECK constraints contain only
	 * immutable functions, so it's necessary to check here. We daren't draw
	 * conclusions from plan-time evaluation of non-immutable functions. Since
	 * they're ANDed, we can just ignore any mutable constraints in the list,
	 * and reason about the rest.
	 */
	safe_constraints = NIL;
	foreach(lc, constraint_pred)
	{
		Node	   *pred = (Node *) lfirst(lc);

		if (!contain_mutable_functions(pred))
			safe_constraints = lappend(safe_constraints, pred);
	}

	/*
	 * The constraints are effectively ANDed together, so we can just try to
	 * refute the entire collection at once.  This may allow us to make proofs
	 * that would fail if we took them individually.
	 *
	 * Note: we use rel->baserestrictinfo, not safe_restrictions as might seem
	 * an obvious optimization.  Some of the clauses might be OR clauses that
	 * have volatile and nonvolatile subclauses, and it's OK to make
	 * deductions with the nonvolatile parts.
	 *
	 * We need strong refutation because we have to prove that the constraints
	 * would yield false, not just NULL.
	 */
	if (predicate_refuted_by(safe_constraints, rel->baserestrictinfo, false))
		return true;

	return false;
}


/*
 * build_physical_tlist
 *
 * Build a targetlist consisting of exactly the relation's user attributes,
 * in order.  The executor can special-case such tlists to avoid a projection
 * step at runtime, so we use such tlists preferentially for scan nodes.
 *
 * Exception: if there are any dropped or missing columns, we punt and return
 * NIL.  Ideally we would like to handle these cases too.  However this
 * creates problems for ExecTypeFromTL, which may be asked to build a tupdesc
 * for a tlist that includes vars of no-longer-existent types.  In theory we
 * could dig out the required info from the pg_attribute entries of the
 * relation, but that data is not readily available to ExecTypeFromTL.
 * For now, we don't apply the physical-tlist optimization when there are
 * dropped cols.
 *
 * We also support building a "physical" tlist for subqueries, functions,
 * values lists, table expressions, and CTEs, since the same optimization can
 * occur in SubqueryScan, FunctionScan, ValuesScan, CteScan, TableFunc,
 * NamedTuplestoreScan, and WorkTableScan nodes.
 */
List *
build_physical_tlist(PlannerInfo *root, RelOptInfo *rel)
{
	List	   *tlist = NIL;
	Index		varno = rel->relid;
	RangeTblEntry *rte = planner_rt_fetch(varno, root);
	Relation	relation;
	Query	   *subquery;
	Var		   *var;
	ListCell   *l;
	int			attrno,
				numattrs;
	List	   *colvars;

	switch (rte->rtekind)
	{
		case RTE_RELATION:
			/* Assume we already have adequate lock */
			relation = table_open(rte->relid, NoLock);

			numattrs = RelationGetNumberOfAttributes(relation);
			for (attrno = 1; attrno <= numattrs; attrno++)
			{
				Form_pg_attribute att_tup = TupleDescAttr(relation->rd_att,
														  attrno - 1);

				if (att_tup->attisdropped || att_tup->atthasmissing)
				{
					/* found a dropped or missing col, so punt */
					tlist = NIL;
					break;
				}

				var = makeVar(varno,
							  attrno,
							  att_tup->atttypid,
							  att_tup->atttypmod,
							  att_tup->attcollation,
							  0);

				tlist = lappend(tlist,
								makeTargetEntry((Expr *) var,
												attrno,
												NULL,
												false));
			}

			table_close(relation, NoLock);
			break;

		case RTE_SUBQUERY:
			subquery = rte->subquery;
			foreach(l, subquery->targetList)
			{
				TargetEntry *tle = (TargetEntry *) lfirst(l);

				/*
				 * A resjunk column of the subquery can be reflected as
				 * resjunk in the physical tlist; we need not punt.
				 */
				var = makeVarFromTargetEntry(varno, tle);

				tlist = lappend(tlist,
								makeTargetEntry((Expr *) var,
												tle->resno,
												NULL,
												tle->resjunk));
			}
			break;

		case RTE_FUNCTION:
		case RTE_TABLEFUNC:
		case RTE_VALUES:
		case RTE_CTE:
		case RTE_NAMEDTUPLESTORE:
		case RTE_RESULT:
			/* Not all of these can have dropped cols, but share code anyway */
			expandRTE(rte, varno, 0, VAR_RETURNING_DEFAULT, -1,
					  true /* include dropped */ , NULL, &colvars);
			foreach(l, colvars)
			{
				var = (Var *) lfirst(l);

				/*
				 * A non-Var in expandRTE's output means a dropped column;
				 * must punt.
				 */
				if (!IsA(var, Var))
				{
					tlist = NIL;
					break;
				}

				tlist = lappend(tlist,
								makeTargetEntry((Expr *) var,
												var->varattno,
												NULL,
												false));
			}
			break;

		default:
			/* caller error */
			elog(ERROR, "unsupported RTE kind %d in build_physical_tlist",
				 (int) rte->rtekind);
			break;
	}

	return tlist;
}

/*
 * build_index_tlist
 *
 * Build a targetlist representing the columns of the specified index.
 * Each column is represented by a Var for the corresponding base-relation
 * column, or an expression in base-relation Vars, as appropriate.
 *
 * There are never any dropped columns in indexes, so unlike
 * build_physical_tlist, we need no failure case.
 */
static List *
build_index_tlist(PlannerInfo *root, IndexOptInfo *index,
				  Relation heapRelation)
{
	List	   *tlist = NIL;
	Index		varno = index->rel->relid;
	ListCell   *indexpr_item;
	int			i;

	indexpr_item = list_head(index->indexprs);
	for (i = 0; i < index->ncolumns; i++)
	{
		int			indexkey = index->indexkeys[i];
		Expr	   *indexvar;

		if (indexkey != 0)
		{
			/* simple column */
			const FormData_pg_attribute *att_tup;

			if (indexkey < 0)
				att_tup = SystemAttributeDefinition(indexkey);
			else
				att_tup = TupleDescAttr(heapRelation->rd_att, indexkey - 1);

			indexvar = (Expr *) makeVar(varno,
										indexkey,
										att_tup->atttypid,
										att_tup->atttypmod,
										att_tup->attcollation,
										0);
		}
		else
		{
			/* expression column */
			if (indexpr_item == NULL)
				elog(ERROR, "wrong number of index expressions");
			indexvar = (Expr *) lfirst(indexpr_item);
			indexpr_item = lnext(index->indexprs, indexpr_item);
		}

		tlist = lappend(tlist,
						makeTargetEntry(indexvar,
										i + 1,
										NULL,
										false));
	}
	if (indexpr_item != NULL)
		elog(ERROR, "wrong number of index expressions");

	return tlist;
}

/*
 * restriction_selectivity
 *
 * Returns the selectivity of a specified restriction operator clause.
 * This code executes registered procedures stored in the
 * operator relation, by calling the function manager.
 *
 * See clause_selectivity() for the meaning of the additional parameters.
 */
Selectivity
restriction_selectivity(PlannerInfo *root,
						Oid operatorid,
						List *args,
						Oid inputcollid,
						int varRelid)
{
	RegProcedure oprrest = get_oprrest(operatorid);
	float8		result;

	/*
	 * if the oprrest procedure is missing for whatever reason, use a
	 * selectivity of 0.5
	 */
	if (!oprrest)
		return (Selectivity) 0.5;

	result = DatumGetFloat8(OidFunctionCall4Coll(oprrest,
												 inputcollid,
												 PointerGetDatum(root),
												 ObjectIdGetDatum(operatorid),
												 PointerGetDatum(args),
												 Int32GetDatum(varRelid)));

	if (result < 0.0 || result > 1.0)
		elog(ERROR, "invalid restriction selectivity: %f", result);

	return (Selectivity) result;
}

/*
 * join_selectivity
 *
 * Returns the selectivity of a specified join operator clause.
 * This code executes registered procedures stored in the
 * operator relation, by calling the function manager.
 *
 * See clause_selectivity() for the meaning of the additional parameters.
 */
Selectivity
join_selectivity(PlannerInfo *root,
				 Oid operatorid,
				 List *args,
				 Oid inputcollid,
				 JoinType jointype,
				 SpecialJoinInfo *sjinfo)
{
	RegProcedure oprjoin = get_oprjoin(operatorid);
	float8		result;

	/*
	 * if the oprjoin procedure is missing for whatever reason, use a
	 * selectivity of 0.5
	 */
	if (!oprjoin)
		return (Selectivity) 0.5;

	result = DatumGetFloat8(OidFunctionCall5Coll(oprjoin,
												 inputcollid,
												 PointerGetDatum(root),
												 ObjectIdGetDatum(operatorid),
												 PointerGetDatum(args),
												 Int16GetDatum(jointype),
												 PointerGetDatum(sjinfo)));

	if (result < 0.0 || result > 1.0)
		elog(ERROR, "invalid join selectivity: %f", result);

	return (Selectivity) result;
}

/*
 * function_selectivity
 *
 * Attempt to estimate the selectivity of a specified boolean function clause
 * by asking its support function.  If the function lacks support, return -1.
 *
 * See clause_selectivity() for the meaning of the additional parameters.
 */
Selectivity
function_selectivity(PlannerInfo *root,
					 Oid funcid,
					 List *args,
					 Oid inputcollid,
					 bool is_join,
					 int varRelid,
					 JoinType jointype,
					 SpecialJoinInfo *sjinfo)
{
	RegProcedure prosupport = get_func_support(funcid);
	SupportRequestSelectivity req;
	SupportRequestSelectivity *sresult;

	if (!prosupport)
		return (Selectivity) -1;	/* no support function */

	req.type = T_SupportRequestSelectivity;
	req.root = root;
	req.funcid = funcid;
	req.args = args;
	req.inputcollid = inputcollid;
	req.is_join = is_join;
	req.varRelid = varRelid;
	req.jointype = jointype;
	req.sjinfo = sjinfo;
	req.selectivity = -1;		/* to catch failure to set the value */

	sresult = (SupportRequestSelectivity *)
		DatumGetPointer(OidFunctionCall1(prosupport,
										 PointerGetDatum(&req)));

	if (sresult != &req)
		return (Selectivity) -1;	/* function did not honor request */

	if (req.selectivity < 0.0 || req.selectivity > 1.0)
		elog(ERROR, "invalid function selectivity: %f", req.selectivity);

	return (Selectivity) req.selectivity;
}

/*
 * add_function_cost
 *
 * Get an estimate of the execution cost of a function, and *add* it to
 * the contents of *cost.  The estimate may include both one-time and
 * per-tuple components, since QualCost does.
 *
 * The funcid must always be supplied.  If it is being called as the
 * implementation of a specific parsetree node (FuncExpr, OpExpr,
 * WindowFunc, etc), pass that as "node", else pass NULL.
 *
 * In some usages root might be NULL, too.
 */
void
add_function_cost(PlannerInfo *root, Oid funcid, Node *node,
				  QualCost *cost)
{
	HeapTuple	proctup;
	Form_pg_proc procform;

	proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
	if (!HeapTupleIsValid(proctup))
		elog(ERROR, "cache lookup failed for function %u", funcid);
	procform = (Form_pg_proc) GETSTRUCT(proctup);

	if (OidIsValid(procform->prosupport))
	{
		SupportRequestCost req;
		SupportRequestCost *sresult;

		req.type = T_SupportRequestCost;
		req.root = root;
		req.funcid = funcid;
		req.node = node;

		/* Initialize cost fields so that support function doesn't have to */
		req.startup = 0;
		req.per_tuple = 0;

		sresult = (SupportRequestCost *)
			DatumGetPointer(OidFunctionCall1(procform->prosupport,
											 PointerGetDatum(&req)));

		if (sresult == &req)
		{
			/* Success, so accumulate support function's estimate into *cost */
			cost->startup += req.startup;
			cost->per_tuple += req.per_tuple;
			ReleaseSysCache(proctup);
			return;
		}
	}

	/* No support function, or it failed, so rely on procost */
	cost->per_tuple += procform->procost * cpu_operator_cost;

	ReleaseSysCache(proctup);
}

/*
 * get_function_rows
 *
 * Get an estimate of the number of rows returned by a set-returning function.
 *
 * The funcid must always be supplied.  In current usage, the calling node
 * will always be supplied, and will be either a FuncExpr or OpExpr.
 * But it's a good idea to not fail if it's NULL.
 *
 * In some usages root might be NULL, too.
 *
 * Note: this returns the unfiltered result of the support function, if any.
 * It's usually a good idea to apply clamp_row_est() to the result, but we
 * leave it to the caller to do so.
 */
double
get_function_rows(PlannerInfo *root, Oid funcid, Node *node)
{
	HeapTuple	proctup;
	Form_pg_proc procform;
	double		result;

	proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
	if (!HeapTupleIsValid(proctup))
		elog(ERROR, "cache lookup failed for function %u", funcid);
	procform = (Form_pg_proc) GETSTRUCT(proctup);

	Assert(procform->proretset);	/* else caller error */

	if (OidIsValid(procform->prosupport))
	{
		SupportRequestRows req;
		SupportRequestRows *sresult;

		req.type = T_SupportRequestRows;
		req.root = root;
		req.funcid = funcid;
		req.node = node;

		req.rows = 0;			/* just for sanity */

		sresult = (SupportRequestRows *)
			DatumGetPointer(OidFunctionCall1(procform->prosupport,
											 PointerGetDatum(&req)));

		if (sresult == &req)
		{
			/* Success */
			ReleaseSysCache(proctup);
			return req.rows;
		}
	}

	/* No support function, or it failed, so rely on prorows */
	result = procform->prorows;

	ReleaseSysCache(proctup);

	return result;
}

/*
 * has_unique_index
 *
 * Detect whether there is a unique index on the specified attribute
 * of the specified relation, thus allowing us to conclude that all
 * the (non-null) values of the attribute are distinct.
 *
 * This function does not check the index's indimmediate property, which
 * means that uniqueness may transiently fail to hold intra-transaction.
 * That's appropriate when we are making statistical estimates, but beware
 * of using this for any correctness proofs.
 */
bool
has_unique_index(RelOptInfo *rel, AttrNumber attno)
{
	ListCell   *ilist;

	foreach(ilist, rel->indexlist)
	{
		IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);

		/*
		 * Note: ignore partial indexes, since they don't allow us to conclude
		 * that all attr values are distinct, *unless* they are marked predOK
		 * which means we know the index's predicate is satisfied by the
		 * query. We don't take any interest in expressional indexes either.
		 * Also, a multicolumn unique index doesn't allow us to conclude that
		 * just the specified attr is unique.
		 */
		if (index->unique &&
			index->nkeycolumns == 1 &&
			index->indexkeys[0] == attno &&
			(index->indpred == NIL || index->predOK))
			return true;
	}
	return false;
}


/*
 * has_row_triggers
 *
 * Detect whether the specified relation has any row-level triggers for event.
 */
bool
has_row_triggers(PlannerInfo *root, Index rti, CmdType event)
{
	RangeTblEntry *rte = planner_rt_fetch(rti, root);
	Relation	relation;
	TriggerDesc *trigDesc;
	bool		result = false;

	/* Assume we already have adequate lock */
	relation = table_open(rte->relid, NoLock);

	trigDesc = relation->trigdesc;
	switch (event)
	{
		case CMD_INSERT:
			if (trigDesc &&
				(trigDesc->trig_insert_after_row ||
				 trigDesc->trig_insert_before_row))
				result = true;
			break;
		case CMD_UPDATE:
			if (trigDesc &&
				(trigDesc->trig_update_after_row ||
				 trigDesc->trig_update_before_row))
				result = true;
			break;
		case CMD_DELETE:
			if (trigDesc &&
				(trigDesc->trig_delete_after_row ||
				 trigDesc->trig_delete_before_row))
				result = true;
			break;
			/* There is no separate event for MERGE, only INSERT/UPDATE/DELETE */
		case CMD_MERGE:
			result = false;
			break;
		default:
			elog(ERROR, "unrecognized CmdType: %d", (int) event);
			break;
	}

	table_close(relation, NoLock);
	return result;
}

/*
 * has_transition_tables
 *
 * Detect whether the specified relation has any transition tables for event.
 */
bool
has_transition_tables(PlannerInfo *root, Index rti, CmdType event)
{
	RangeTblEntry *rte = planner_rt_fetch(rti, root);
	Relation	relation;
	TriggerDesc *trigDesc;
	bool		result = false;

	Assert(rte->rtekind == RTE_RELATION);

	/* Currently foreign tables cannot have transition tables */
	if (rte->relkind == RELKIND_FOREIGN_TABLE)
		return result;

	/* Assume we already have adequate lock */
	relation = table_open(rte->relid, NoLock);

	trigDesc = relation->trigdesc;
	switch (event)
	{
		case CMD_INSERT:
			if (trigDesc &&
				trigDesc->trig_insert_new_table)
				result = true;
			break;
		case CMD_UPDATE:
			if (trigDesc &&
				(trigDesc->trig_update_old_table ||
				 trigDesc->trig_update_new_table))
				result = true;
			break;
		case CMD_DELETE:
			if (trigDesc &&
				trigDesc->trig_delete_old_table)
				result = true;
			break;
			/* There is no separate event for MERGE, only INSERT/UPDATE/DELETE */
		case CMD_MERGE:
			result = false;
			break;
		default:
			elog(ERROR, "unrecognized CmdType: %d", (int) event);
			break;
	}

	table_close(relation, NoLock);
	return result;
}

/*
 * has_stored_generated_columns
 *
 * Does table identified by RTI have any STORED GENERATED columns?
 */
bool
has_stored_generated_columns(PlannerInfo *root, Index rti)
{
	RangeTblEntry *rte = planner_rt_fetch(rti, root);
	Relation	relation;
	TupleDesc	tupdesc;
	bool		result = false;

	/* Assume we already have adequate lock */
	relation = table_open(rte->relid, NoLock);

	tupdesc = RelationGetDescr(relation);
	result = tupdesc->constr && tupdesc->constr->has_generated_stored;

	table_close(relation, NoLock);

	return result;
}

/*
 * get_dependent_generated_columns
 *
 * Get the column numbers of any STORED GENERATED columns of the relation
 * that depend on any column listed in target_cols.  Both the input and
 * result bitmapsets contain column numbers offset by
 * FirstLowInvalidHeapAttributeNumber.
 */
Bitmapset *
get_dependent_generated_columns(PlannerInfo *root, Index rti,
								Bitmapset *target_cols)
{
	Bitmapset  *dependentCols = NULL;
	RangeTblEntry *rte = planner_rt_fetch(rti, root);
	Relation	relation;
	TupleDesc	tupdesc;
	TupleConstr *constr;

	/* Assume we already have adequate lock */
	relation = table_open(rte->relid, NoLock);

	tupdesc = RelationGetDescr(relation);
	constr = tupdesc->constr;

	if (constr && constr->has_generated_stored)
	{
		for (int i = 0; i < constr->num_defval; i++)
		{
			AttrDefault *defval = &constr->defval[i];
			Node	   *expr;
			Bitmapset  *attrs_used = NULL;

			/* skip if not generated column */
			if (!TupleDescCompactAttr(tupdesc, defval->adnum - 1)->attgenerated)
				continue;

			/* identify columns this generated column depends on */
			expr = stringToNode(defval->adbin);
			pull_varattnos(expr, 1, &attrs_used);

			if (bms_overlap(target_cols, attrs_used))
				dependentCols = bms_add_member(dependentCols,
											   defval->adnum - FirstLowInvalidHeapAttributeNumber);
		}
	}

	table_close(relation, NoLock);

	return dependentCols;
}

/*
 * set_relation_partition_info
 *
 * Set partitioning scheme and related information for a partitioned table.
 */
static void
set_relation_partition_info(PlannerInfo *root, RelOptInfo *rel,
							Relation relation)
{
	PartitionDesc partdesc;

	/*
	 * Create the PartitionDirectory infrastructure if we didn't already.
	 */
	if (root->glob->partition_directory == NULL)
	{
		root->glob->partition_directory =
			CreatePartitionDirectory(CurrentMemoryContext, true);
	}

	partdesc = PartitionDirectoryLookup(root->glob->partition_directory,
										relation);
	rel->part_scheme = find_partition_scheme(root, relation);
	Assert(partdesc != NULL && rel->part_scheme != NULL);
	rel->boundinfo = partdesc->boundinfo;
	rel->nparts = partdesc->nparts;
	set_baserel_partition_key_exprs(relation, rel);
	set_baserel_partition_constraint(relation, rel);
}

/*
 * find_partition_scheme
 *
 * Find or create a PartitionScheme for this Relation.
 */
static PartitionScheme
find_partition_scheme(PlannerInfo *root, Relation relation)
{
	PartitionKey partkey = RelationGetPartitionKey(relation);
	ListCell   *lc;
	int			partnatts,
				i;
	PartitionScheme part_scheme;

	/* A partitioned table should have a partition key. */
	Assert(partkey != NULL);

	partnatts = partkey->partnatts;

	/* Search for a matching partition scheme and return if found one. */
	foreach(lc, root->part_schemes)
	{
		part_scheme = lfirst(lc);

		/* Match partitioning strategy and number of keys. */
		if (partkey->strategy != part_scheme->strategy ||
			partnatts != part_scheme->partnatts)
			continue;

		/* Match partition key type properties. */
		if (memcmp(partkey->partopfamily, part_scheme->partopfamily,
				   sizeof(Oid) * partnatts) != 0 ||
			memcmp(partkey->partopcintype, part_scheme->partopcintype,
				   sizeof(Oid) * partnatts) != 0 ||
			memcmp(partkey->partcollation, part_scheme->partcollation,
				   sizeof(Oid) * partnatts) != 0)
			continue;

		/*
		 * Length and byval information should match when partopcintype
		 * matches.
		 */
		Assert(memcmp(partkey->parttyplen, part_scheme->parttyplen,
					  sizeof(int16) * partnatts) == 0);
		Assert(memcmp(partkey->parttypbyval, part_scheme->parttypbyval,
					  sizeof(bool) * partnatts) == 0);

		/*
		 * If partopfamily and partopcintype matched, must have the same
		 * partition comparison functions.  Note that we cannot reliably
		 * Assert the equality of function structs themselves for they might
		 * be different across PartitionKey's, so just Assert for the function
		 * OIDs.
		 */
#ifdef USE_ASSERT_CHECKING
		for (i = 0; i < partkey->partnatts; i++)
			Assert(partkey->partsupfunc[i].fn_oid ==
				   part_scheme->partsupfunc[i].fn_oid);
#endif

		/* Found matching partition scheme. */
		return part_scheme;
	}

	/*
	 * Did not find matching partition scheme. Create one copying relevant
	 * information from the relcache. We need to copy the contents of the
	 * array since the relcache entry may not survive after we have closed the
	 * relation.
	 */
	part_scheme = palloc0_object(PartitionSchemeData);
	part_scheme->strategy = partkey->strategy;
	part_scheme->partnatts = partkey->partnatts;

	part_scheme->partopfamily = palloc_array(Oid, partnatts);
	memcpy(part_scheme->partopfamily, partkey->partopfamily,
		   sizeof(Oid) * partnatts);

	part_scheme->partopcintype = palloc_array(Oid, partnatts);
	memcpy(part_scheme->partopcintype, partkey->partopcintype,
		   sizeof(Oid) * partnatts);

	part_scheme->partcollation = palloc_array(Oid, partnatts);
	memcpy(part_scheme->partcollation, partkey->partcollation,
		   sizeof(Oid) * partnatts);

	part_scheme->parttyplen = palloc_array(int16, partnatts);
	memcpy(part_scheme->parttyplen, partkey->parttyplen,
		   sizeof(int16) * partnatts);

	part_scheme->parttypbyval = palloc_array(bool, partnatts);
	memcpy(part_scheme->parttypbyval, partkey->parttypbyval,
		   sizeof(bool) * partnatts);

	part_scheme->partsupfunc = palloc_array(FmgrInfo, partnatts);
	for (i = 0; i < partnatts; i++)
		fmgr_info_copy(&part_scheme->partsupfunc[i], &partkey->partsupfunc[i],
					   CurrentMemoryContext);

	/* Add the partitioning scheme to PlannerInfo. */
	root->part_schemes = lappend(root->part_schemes, part_scheme);

	return part_scheme;
}

/*
 * set_baserel_partition_key_exprs
 *
 * Builds partition key expressions for the given base relation and fills
 * rel->partexprs.
 */
static void
set_baserel_partition_key_exprs(Relation relation,
								RelOptInfo *rel)
{
	PartitionKey partkey = RelationGetPartitionKey(relation);
	int			partnatts;
	int			cnt;
	List	  **partexprs;
	ListCell   *lc;
	Index		varno = rel->relid;

	Assert(IS_SIMPLE_REL(rel) && rel->relid > 0);

	/* A partitioned table should have a partition key. */
	Assert(partkey != NULL);

	partnatts = partkey->partnatts;
	partexprs = palloc_array(List *, partnatts);
	lc = list_head(partkey->partexprs);

	for (cnt = 0; cnt < partnatts; cnt++)
	{
		Expr	   *partexpr;
		AttrNumber	attno = partkey->partattrs[cnt];

		if (attno != InvalidAttrNumber)
		{
			/* Single column partition key is stored as a Var node. */
			Assert(attno > 0);

			partexpr = (Expr *) makeVar(varno, attno,
										partkey->parttypid[cnt],
										partkey->parttypmod[cnt],
										partkey->parttypcoll[cnt], 0);
		}
		else
		{
			if (lc == NULL)
				elog(ERROR, "wrong number of partition key expressions");

			/* Re-stamp the expression with given varno. */
			partexpr = (Expr *) copyObject(lfirst(lc));
			ChangeVarNodes((Node *) partexpr, 1, varno, 0);
			lc = lnext(partkey->partexprs, lc);
		}

		/* Base relations have a single expression per key. */
		partexprs[cnt] = list_make1(partexpr);
	}

	rel->partexprs = partexprs;

	/*
	 * A base relation does not have nullable partition key expressions, since
	 * no outer join is involved.  We still allocate an array of empty
	 * expression lists to keep partition key expression handling code simple.
	 * See build_joinrel_partition_info() and match_expr_to_partition_keys().
	 */
	rel->nullable_partexprs = palloc0_array(List *, partnatts);
}

/*
 * set_baserel_partition_constraint
 *
 * Builds the partition constraint for the given base relation and sets it
 * in the given RelOptInfo.  All Var nodes are restamped with the relid of the
 * given relation.
 */
static void
set_baserel_partition_constraint(Relation relation, RelOptInfo *rel)
{
	List	   *partconstr;

	if (rel->partition_qual)	/* already done */
		return;

	/*
	 * Run the partition quals through const-simplification similar to check
	 * constraints.  We skip canonicalize_qual, though, because partition
	 * quals should be in canonical form already; also, since the qual is in
	 * implicit-AND format, we'd have to explicitly convert it to explicit-AND
	 * format and back again.
	 */
	partconstr = RelationGetPartitionQual(relation);
	if (partconstr)
	{
		partconstr = (List *) expression_planner((Expr *) partconstr);
		if (rel->relid != 1)
			ChangeVarNodes((Node *) partconstr, 1, rel->relid, 0);
		rel->partition_qual = partconstr;
	}
}
./rel.h0000664000175000017500000006256715221166247010666 0ustar  xmanxman/*-------------------------------------------------------------------------
 *
 * rel.h
 *	  POSTGRES relation descriptor (a/k/a relcache entry) definitions.
 *
 *
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/utils/rel.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef REL_H
#define REL_H

#include "access/tupdesc.h"
#include "access/xlog.h"
#include "catalog/catalog.h"
#include "catalog/pg_class.h"
#include "catalog/pg_index.h"
#include "catalog/pg_publication.h"
#include "nodes/bitmapset.h"
#include "partitioning/partdefs.h"
#include "rewrite/prs2lock.h"
#include "storage/block.h"
#include "storage/relfilelocator.h"
#include "storage/smgr.h"
#include "utils/relcache.h"
#include "utils/reltrigger.h"


/*
 * LockRelId and LockInfo really belong to lmgr.h, but it's more convenient
 * to declare them here so we can have a LockInfoData field in a Relation.
 */

typedef struct LockRelId
{
	Oid			relId;			/* a relation identifier */
	Oid			dbId;			/* a database identifier */
} LockRelId;

typedef struct LockInfoData
{
	LockRelId	lockRelId;
} LockInfoData;

typedef LockInfoData *LockInfo;

/*
 * Here are the contents of a relation cache entry.
 */

typedef struct RelationData
{
	RelFileLocator rd_locator;	/* relation physical identifier */
	SMgrRelation rd_smgr;		/* cached file handle, or NULL */
	int			rd_refcnt;		/* reference count */
	ProcNumber	rd_backend;		/* owning backend's proc number, if temp rel */
	bool		rd_islocaltemp; /* rel is a temp rel of this session */
	bool		rd_isnailed;	/* rel is nailed in cache */
	bool		rd_isvalid;		/* relcache entry is valid */
	bool		rd_indexvalid;	/* is rd_indexlist valid? (also rd_pkindex and
								 * rd_replidindex) */
	bool		rd_statvalid;	/* is rd_statlist valid? */

	/*----------
	 * rd_createSubid is the ID of the highest subtransaction the rel has
	 * survived into or zero if the rel or its storage was created before the
	 * current top transaction.  (IndexStmt.oldNumber leads to the case of a new
	 * rel with an old rd_locator.)  rd_firstRelfilelocatorSubid is the ID of the
	 * highest subtransaction an rd_locator change has survived into or zero if
	 * rd_locator matches the value it had at the start of the current top
	 * transaction.  (Rolling back the subtransaction that
	 * rd_firstRelfilelocatorSubid denotes would restore rd_locator to the value it
	 * had at the start of the current top transaction.  Rolling back any
	 * lower subtransaction would not.)  Their accuracy is critical to
	 * RelationNeedsWAL().
	 *
	 * rd_newRelfilelocatorSubid is the ID of the highest subtransaction the
	 * most-recent relfilenumber change has survived into or zero if not changed
	 * in the current transaction (or we have forgotten changing it).  This
	 * field is accurate when non-zero, but it can be zero when a relation has
	 * multiple new relfilenumbers within a single transaction, with one of them
	 * occurring in a subsequently aborted subtransaction, e.g.
	 *		BEGIN;
	 *		TRUNCATE t;
	 *		SAVEPOINT save;
	 *		TRUNCATE t;
	 *		ROLLBACK TO save;
	 *		-- rd_newRelfilelocatorSubid is now forgotten
	 *
	 * If every rd_*Subid field is zero, they are read-only outside
	 * relcache.c.  Files that trigger rd_locator changes by updating
	 * pg_class.reltablespace and/or pg_class.relfilenode call
	 * RelationAssumeNewRelfilelocator() to update rd_*Subid.
	 *
	 * rd_droppedSubid is the ID of the highest subtransaction that a drop of
	 * the rel has survived into.  In entries visible outside relcache.c, this
	 * is always zero.
	 */
	SubTransactionId rd_createSubid;	/* rel was created in current xact */
	SubTransactionId rd_newRelfilelocatorSubid; /* highest subxact changing
												 * rd_locator to current value */
	SubTransactionId rd_firstRelfilelocatorSubid;	/* highest subxact
													 * changing rd_locator to
													 * any value */
	SubTransactionId rd_droppedSubid;	/* dropped with another Subid set */

	Form_pg_class rd_rel;		/* RELATION tuple */
	TupleDesc	rd_att;			/* tuple descriptor */
	Oid			rd_id;			/* relation's object id */
	LockInfoData rd_lockInfo;	/* lock mgr's info for locking relation */
	RuleLock   *rd_rules;		/* rewrite rules */
	MemoryContext rd_rulescxt;	/* private memory cxt for rd_rules, if any */
	TriggerDesc *trigdesc;		/* Trigger info, or NULL if rel has none */
	/* use "struct" here to avoid needing to include rowsecurity.h: */
	struct RowSecurityDesc *rd_rsdesc;	/* row security policies, or NULL */

	/* data managed by RelationGetFKeyList: */
	List	   *rd_fkeylist;	/* list of ForeignKeyCacheInfo (see below) */
	bool		rd_fkeyvalid;	/* true if list has been computed */

	/* data managed by RelationGetPartitionKey: */
	PartitionKey rd_partkey;	/* partition key, or NULL */
	MemoryContext rd_partkeycxt;	/* private context for rd_partkey, if any */

	/* data managed by RelationGetPartitionDesc: */
	PartitionDesc rd_partdesc;	/* partition descriptor, or NULL */
	MemoryContext rd_pdcxt;		/* private context for rd_partdesc, if any */

	/* Same as above, for partdescs that omit detached partitions */
	PartitionDesc rd_partdesc_nodetached;	/* partdesc w/o detached parts */
	MemoryContext rd_pddcxt;	/* for rd_partdesc_nodetached, if any */

	/*
	 * pg_inherits.xmin of the partition that was excluded in
	 * rd_partdesc_nodetached.  This informs a future user of that partdesc:
	 * if this value is not in progress for the active snapshot, then the
	 * partdesc can be used, otherwise they have to build a new one.  (This
	 * matches what find_inheritance_children_extended would do).
	 */
	TransactionId rd_partdesc_nodetached_xmin;

	/* data managed by RelationGetPartitionQual: */
	List	   *rd_partcheck;	/* partition CHECK quals */
	bool		rd_partcheckvalid;	/* true if list has been computed */
	MemoryContext rd_partcheckcxt;	/* private cxt for rd_partcheck, if any */

	/* data managed by RelationGetIndexList: */
	List	   *rd_indexlist;	/* list of OIDs of indexes on relation */
	Oid			rd_pkindex;		/* OID of (deferrable?) primary key, if any */
	bool		rd_ispkdeferrable;	/* is rd_pkindex a deferrable PK? */
	Oid			rd_replidindex; /* OID of replica identity index, if any */

	/* data managed by RelationGetStatExtList: */
	List	   *rd_statlist;	/* list of OIDs of extended stats */

	/* data managed by RelationGetIndexAttrBitmap: */
	bool		rd_attrsvalid;	/* are bitmaps of attrs valid? */
	Bitmapset  *rd_keyattr;		/* cols that can be ref'd by foreign keys */
	Bitmapset  *rd_pkattr;		/* cols included in primary key */
	Bitmapset  *rd_idattr;		/* included in replica identity index */
	Bitmapset  *rd_hotblockingattr; /* cols blocking HOT update */
	Bitmapset  *rd_summarizedattr;	/* cols indexed by summarizing indexes */

	PublicationDesc *rd_pubdesc;	/* publication descriptor, or NULL */

	/*
	 * rd_options is set whenever rd_rel is loaded into the relcache entry.
	 * Note that you can NOT look into rd_rel for this data.  NULL means "use
	 * defaults".
	 */
	bytea	   *rd_options;		/* parsed pg_class.reloptions */

	/*
	 * Oid of the handler for this relation. For an index this is a function
	 * returning IndexAmRoutine, for table like relations a function returning
	 * TableAmRoutine.  This is stored separately from rd_indam, rd_tableam as
	 * its lookup requires syscache access, but during relcache bootstrap we
	 * need to be able to initialize rd_tableam without syscache lookups.
	 */
	Oid			rd_amhandler;	/* OID of index AM's handler function */

	/*
	 * Table access method.
	 */
	const struct TableAmRoutine *rd_tableam;

	/* These are non-NULL only for an index relation: */
	Form_pg_index rd_index;		/* pg_index tuple describing this index */
	/* use "struct" here to avoid needing to include htup.h: */
	struct HeapTupleData *rd_indextuple;	/* all of pg_index tuple */

	/*
	 * index access support info (used only for an index relation)
	 *
	 * Note: only default support procs for each opclass are cached, namely
	 * those with lefttype and righttype equal to the opclass's opcintype. The
	 * arrays are indexed by support function number, which is a sufficient
	 * identifier given that restriction.
	 */
	MemoryContext rd_indexcxt;	/* private memory cxt for this stuff */
	/* use "struct" here to avoid needing to include amapi.h: */
	const struct IndexAmRoutine *rd_indam;	/* index AM's API struct */
	Oid		   *rd_opfamily;	/* OIDs of op families for each index col */
	Oid		   *rd_opcintype;	/* OIDs of opclass declared input data types */
	RegProcedure *rd_support;	/* OIDs of support procedures */
	struct FmgrInfo *rd_supportinfo;	/* lookup info for support procedures */
	int16	   *rd_indoption;	/* per-column AM-specific flags */
	List	   *rd_indexprs;	/* index expression trees, if any */
	List	   *rd_indexprsExpand;	/* expanded index expression trees, if any */
	List	   *rd_indpred;		/* index predicate tree, if any */
	List	   *rd_indpredExpand;		/* expanded index predicate tree, if any */
	Oid		   *rd_exclops;		/* OIDs of exclusion operators, if any */
	Oid		   *rd_exclprocs;	/* OIDs of exclusion ops' procs, if any */
	uint16	   *rd_exclstrats;	/* exclusion ops' strategy numbers, if any */
	Oid		   *rd_indcollation;	/* OIDs of index collations */
	bytea	  **rd_opcoptions;	/* parsed opclass-specific options */

	/*
	 * rd_amcache is available for index and table AMs to cache private data
	 * about the relation.  This must be just a cache since it may get reset
	 * at any time (in particular, it will get reset by a relcache inval
	 * message for the relation).  If used, it must point to a single memory
	 * chunk palloc'd in CacheMemoryContext, or in rd_indexcxt for an index
	 * relation.  A relcache reset will include freeing that chunk and setting
	 * rd_amcache = NULL.
	 */
	void	   *rd_amcache;		/* available for use by index/table AM */

	/*
	 * foreign-table support
	 *
	 * rd_fdwroutine must point to a single memory chunk palloc'd in
	 * CacheMemoryContext.  It will be freed and reset to NULL on a relcache
	 * reset.
	 */

	/* use "struct" here to avoid needing to include fdwapi.h: */
	struct FdwRoutine *rd_fdwroutine;	/* cached function pointers, or NULL */

	/*
	 * Hack for CLUSTER, rewriting ALTER TABLE, etc: when writing a new
	 * version of a table, we need to make any toast pointers inserted into it
	 * have the existing toast table's OID, not the OID of the transient toast
	 * table.  If rd_toastoid isn't InvalidOid, it is the OID to place in
	 * toast pointers inserted into this rel.  (Note it's set on the new
	 * version of the main heap, not the toast table itself.)  This also
	 * causes toast_save_datum() to try to preserve toast value OIDs.
	 */
	Oid			rd_toastoid;	/* Real TOAST table's OID, or InvalidOid */

	bool		pgstat_enabled; /* should relation stats be counted */
	/* use "struct" here to avoid needing to include pgstat.h: */
	struct PgStat_TableStatus *pgstat_info; /* statistics collection area */
} RelationData;


/*
 * ForeignKeyCacheInfo
 *		Information the relcache can cache about foreign key constraints
 *
 * This is basically just an image of relevant columns from pg_constraint.
 * We make it a subclass of Node so that copyObject() can be used on a list
 * of these, but we also ensure it is a "flat" object without substructure,
 * so that list_free_deep() is sufficient to free such a list.
 * The per-FK-column arrays can be fixed-size because we allow at most
 * INDEX_MAX_KEYS columns in a foreign key constraint.
 *
 * Currently, we mostly cache fields of interest to the planner, but the set
 * of fields has already grown the constraint OID for other uses.
 */
typedef struct ForeignKeyCacheInfo
{
	pg_node_attr(no_equal, no_read, no_query_jumble)

	NodeTag		type;
	/* oid of the constraint itself */
	Oid			conoid;
	/* relation constrained by the foreign key */
	Oid			conrelid;
	/* relation referenced by the foreign key */
	Oid			confrelid;
	/* number of columns in the foreign key */
	int			nkeys;

	/* Is enforced ? */
	bool		conenforced;

	/*
	 * these arrays each have nkeys valid entries:
	 */
	/* cols in referencing table */
	AttrNumber	conkey[INDEX_MAX_KEYS] pg_node_attr(array_size(nkeys));
	/* cols in referenced table */
	AttrNumber	confkey[INDEX_MAX_KEYS] pg_node_attr(array_size(nkeys));
	/* PK = FK operator OIDs */
	Oid			conpfeqop[INDEX_MAX_KEYS] pg_node_attr(array_size(nkeys));
} ForeignKeyCacheInfo;


/*
 * StdRdOptions
 *		Standard contents of rd_options for heaps.
 *
 * RelationGetFillFactor() and RelationGetTargetPageFreeSpace() can only
 * be applied to relations that use this format or a superset for
 * private options data.
 */
 /* autovacuum-related reloptions. */
typedef struct AutoVacOpts
{
	bool		enabled;

	int			autovacuum_parallel_workers;
	int			vacuum_threshold;
	int			vacuum_max_threshold;
	int			vacuum_ins_threshold;
	int			analyze_threshold;
	int			vacuum_cost_limit;
	int			freeze_min_age;
	int			freeze_max_age;
	int			freeze_table_age;
	int			multixact_freeze_min_age;
	int			multixact_freeze_max_age;
	int			multixact_freeze_table_age;
	int			log_vacuum_min_duration;
	int			log_analyze_min_duration;
	float8		vacuum_cost_delay;
	float8		vacuum_scale_factor;
	float8		vacuum_ins_scale_factor;
	float8		analyze_scale_factor;
} AutoVacOpts;

/* StdRdOptions->vacuum_index_cleanup values */
typedef enum StdRdOptIndexCleanup
{
	STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO = 0,
	STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF,
	STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON,
} StdRdOptIndexCleanup;

typedef struct StdRdOptions
{
	int32		vl_len_;		/* varlena header (do not touch directly!) */
	int			fillfactor;		/* page fill factor in percent (0..100) */
	int			toast_tuple_target; /* target for tuple toasting */
	AutoVacOpts autovacuum;		/* autovacuum-related options */
	bool		user_catalog_table; /* use as an additional catalog relation */
	int			parallel_workers;	/* max number of parallel workers */
	StdRdOptIndexCleanup vacuum_index_cleanup;	/* controls index vacuuming */
	pg_ternary	vacuum_truncate;	/* enables vacuum to truncate a relation */

	/*
	 * Fraction of pages in a relation that vacuum can eagerly scan and fail
	 * to freeze. 0 if disabled, -1 if unspecified.
	 */
	double		vacuum_max_eager_freeze_failure_rate;
} StdRdOptions;

#define HEAP_MIN_FILLFACTOR			10
#define HEAP_DEFAULT_FILLFACTOR		100

/*
 * RelationGetToastTupleTarget
 *		Returns the relation's toast_tuple_target.  Note multiple eval of argument!
 */
#define RelationGetToastTupleTarget(relation, defaulttarg) \
	((relation)->rd_options ? \
	 ((StdRdOptions *) (relation)->rd_options)->toast_tuple_target : (defaulttarg))

/*
 * RelationGetFillFactor
 *		Returns the relation's fillfactor.  Note multiple eval of argument!
 */
#define RelationGetFillFactor(relation, defaultff) \
	((relation)->rd_options ? \
	 ((StdRdOptions *) (relation)->rd_options)->fillfactor : (defaultff))

/*
 * RelationGetTargetPageUsage
 *		Returns the relation's desired space usage per page in bytes.
 */
#define RelationGetTargetPageUsage(relation, defaultff) \
	(BLCKSZ * RelationGetFillFactor(relation, defaultff) / 100)

/*
 * RelationGetTargetPageFreeSpace
 *		Returns the relation's desired freespace per page in bytes.
 */
#define RelationGetTargetPageFreeSpace(relation, defaultff) \
	(BLCKSZ * (100 - RelationGetFillFactor(relation, defaultff)) / 100)

/*
 * RelationIsUsedAsCatalogTable
 *		Returns whether the relation should be treated as a catalog table
 *		from the pov of logical decoding.  Note multiple eval of argument!
 */
#define RelationIsUsedAsCatalogTable(relation)	\
	((relation)->rd_options && \
	 ((relation)->rd_rel->relkind == RELKIND_RELATION || \
	  (relation)->rd_rel->relkind == RELKIND_MATVIEW) ? \
	 ((StdRdOptions *) (relation)->rd_options)->user_catalog_table : false)

/*
 * RelationGetParallelWorkers
 *		Returns the relation's parallel_workers reloption setting.
 *		Note multiple eval of argument!
 */
#define RelationGetParallelWorkers(relation, defaultpw) \
	((relation)->rd_options ? \
	 ((StdRdOptions *) (relation)->rd_options)->parallel_workers : (defaultpw))

/* ViewOptions->check_option values */
typedef enum ViewOptCheckOption
{
	VIEW_OPTION_CHECK_OPTION_NOT_SET,
	VIEW_OPTION_CHECK_OPTION_LOCAL,
	VIEW_OPTION_CHECK_OPTION_CASCADED,
} ViewOptCheckOption;

/*
 * ViewOptions
 *		Contents of rd_options for views
 */
typedef struct ViewOptions
{
	int32		vl_len_;		/* varlena header (do not touch directly!) */
	bool		security_barrier;
	bool		security_invoker;
	ViewOptCheckOption check_option;
} ViewOptions;

/*
 * RelationIsSecurityView
 *		Returns whether the relation is security view, or not.  Note multiple
 *		eval of argument!
 */
#define RelationIsSecurityView(relation)									\
	(AssertMacro(relation->rd_rel->relkind == RELKIND_VIEW),				\
	 (relation)->rd_options ?												\
	  ((ViewOptions *) (relation)->rd_options)->security_barrier : false)

/*
 * RelationHasSecurityInvoker
 *		Returns true if the relation has the security_invoker property set.
 *		Note multiple eval of argument!
 */
#define RelationHasSecurityInvoker(relation)								\
	(AssertMacro(relation->rd_rel->relkind == RELKIND_VIEW),				\
	 (relation)->rd_options ?												\
	  ((ViewOptions *) (relation)->rd_options)->security_invoker : false)

/*
 * RelationHasCheckOption
 *		Returns true if the relation is a view defined with either the local
 *		or the cascaded check option.  Note multiple eval of argument!
 */
#define RelationHasCheckOption(relation)									\
	(AssertMacro(relation->rd_rel->relkind == RELKIND_VIEW),				\
	 (relation)->rd_options &&												\
	 ((ViewOptions *) (relation)->rd_options)->check_option !=				\
	 VIEW_OPTION_CHECK_OPTION_NOT_SET)

/*
 * RelationHasLocalCheckOption
 *		Returns true if the relation is a view defined with the local check
 *		option.  Note multiple eval of argument!
 */
#define RelationHasLocalCheckOption(relation)								\
	(AssertMacro(relation->rd_rel->relkind == RELKIND_VIEW),				\
	 (relation)->rd_options &&												\
	 ((ViewOptions *) (relation)->rd_options)->check_option ==				\
	 VIEW_OPTION_CHECK_OPTION_LOCAL)

/*
 * RelationHasCascadedCheckOption
 *		Returns true if the relation is a view defined with the cascaded check
 *		option.  Note multiple eval of argument!
 */
#define RelationHasCascadedCheckOption(relation)							\
	(AssertMacro(relation->rd_rel->relkind == RELKIND_VIEW),				\
	 (relation)->rd_options &&												\
	 ((ViewOptions *) (relation)->rd_options)->check_option ==				\
	  VIEW_OPTION_CHECK_OPTION_CASCADED)

/*
 * RelationIsValid
 *		True iff relation descriptor is valid.
 */
#define RelationIsValid(relation) ((relation) != NULL)

/*
 * RelationHasReferenceCountZero
 *		True iff relation reference count is zero.
 *
 * Note:
 *		Assumes relation descriptor is valid.
 */
#define RelationHasReferenceCountZero(relation) \
		((bool)((relation)->rd_refcnt == 0))

/*
 * RelationGetForm
 *		Returns pg_class tuple for a relation.
 *
 * Note:
 *		Assumes relation descriptor is valid.
 */
#define RelationGetForm(relation) ((relation)->rd_rel)

/*
 * RelationGetRelid
 *		Returns the OID of the relation
 */
#define RelationGetRelid(relation) ((relation)->rd_id)

/*
 * RelationGetNumberOfAttributes
 *		Returns the total number of attributes in a relation.
 */
#define RelationGetNumberOfAttributes(relation) ((relation)->rd_rel->relnatts)

/*
 * IndexRelationGetNumberOfAttributes
 *		Returns the number of attributes in an index.
 */
#define IndexRelationGetNumberOfAttributes(relation) \
		((relation)->rd_index->indnatts)

/*
 * IndexRelationGetNumberOfKeyAttributes
 *		Returns the number of key attributes in an index.
 */
#define IndexRelationGetNumberOfKeyAttributes(relation) \
		((relation)->rd_index->indnkeyatts)

/*
 * RelationGetDescr
 *		Returns tuple descriptor for a relation.
 */
#define RelationGetDescr(relation) ((relation)->rd_att)

/*
 * RelationGetRelationName
 *		Returns the rel's name.
 *
 * Note that the name is only unique within the containing namespace.
 */
#define RelationGetRelationName(relation) \
	(NameStr((relation)->rd_rel->relname))

/*
 * RelationGetNamespace
 *		Returns the rel's namespace OID.
 */
#define RelationGetNamespace(relation) \
	((relation)->rd_rel->relnamespace)

/*
 * RelationIsMapped
 *		True if the relation uses the relfilenumber map.  Note multiple eval
 *		of argument!
 */
#define RelationIsMapped(relation) \
	(RELKIND_HAS_STORAGE((relation)->rd_rel->relkind) && \
	 ((relation)->rd_rel->relfilenode == InvalidRelFileNumber))

#ifndef FRONTEND
/*
 * RelationGetSmgr
 *		Returns smgr file handle for a relation, opening it if needed.
 *
 * Very little code is authorized to touch rel->rd_smgr directly.  Instead
 * use this function to fetch its value.
 */
static inline SMgrRelation
RelationGetSmgr(Relation rel)
{
	if (unlikely(rel->rd_smgr == NULL))
	{
		rel->rd_smgr = smgropen(rel->rd_locator, rel->rd_backend);
		smgrpin(rel->rd_smgr);
	}
	return rel->rd_smgr;
}

/*
 * RelationCloseSmgr
 *		Close the relation at the smgr level, if not already done.
 */
static inline void
RelationCloseSmgr(Relation relation)
{
	if (relation->rd_smgr != NULL)
	{
		smgrunpin(relation->rd_smgr);
		smgrclose(relation->rd_smgr);
		relation->rd_smgr = NULL;
	}
}
#endif							/* !FRONTEND */

/*
 * RelationGetTargetBlock
 *		Fetch relation's current insertion target block.
 *
 * Returns InvalidBlockNumber if there is no current target block.  Note
 * that the target block status is discarded on any smgr-level invalidation,
 * so there's no need to re-open the smgr handle if it's not currently open.
 */
#define RelationGetTargetBlock(relation) \
	( (relation)->rd_smgr != NULL ? (relation)->rd_smgr->smgr_targblock : InvalidBlockNumber )

/*
 * RelationSetTargetBlock
 *		Set relation's current insertion target block.
 */
#define RelationSetTargetBlock(relation, targblock) \
	do { \
		RelationGetSmgr(relation)->smgr_targblock = (targblock); \
	} while (0)

/*
 * RelationIsPermanent
 *		True if relation is permanent.
 */
#define RelationIsPermanent(relation) \
	((relation)->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT)

/*
 * RelationNeedsWAL
 *		True if relation needs WAL.
 *
 * Returns false if wal_level = minimal and this relation is created or
 * truncated in the current transaction.  See "Skipping WAL for New
 * RelFileLocator" in src/backend/access/transam/README.
 */
#define RelationNeedsWAL(relation)										\
	(RelationIsPermanent(relation) && (XLogIsNeeded() ||				\
	  (relation->rd_createSubid == InvalidSubTransactionId &&			\
	   relation->rd_firstRelfilelocatorSubid == InvalidSubTransactionId)))

/*
 * RelationUsesLocalBuffers
 *		True if relation's pages are stored in local buffers.
 */
#define RelationUsesLocalBuffers(relation) \
	((relation)->rd_rel->relpersistence == RELPERSISTENCE_TEMP)

/*
 * RELATION_IS_LOCAL
 *		If a rel is either temp or newly created in the current transaction,
 *		it can be assumed to be accessible only to the current backend.
 *		This is typically used to decide that we can skip acquiring locks.
 *
 * Beware of multiple eval of argument
 */
#define RELATION_IS_LOCAL(relation) \
	((relation)->rd_islocaltemp || \
	 (relation)->rd_createSubid != InvalidSubTransactionId)

/*
 * RELATION_IS_OTHER_TEMP
 *		Test for a temporary relation that belongs to some other session.
 *
 * Reading another session's temp-table data through never works right:
 * the owning session keeps the data in its private local buffer pool,
 * which we cannot access.  Existing buffer-manager entry points
 * (ReadBuffer_common(), StartReadBuffersImpl(), read_stream_begin_impl(),
 * and PrefetchBuffer()) already enforce this; any new buffer-access entry
 * points must do the same.  Command-level code (TRUNCATE, ALTER TABLE,
 * VACUUM, CLUSTER, REINDEX, ...) additionally uses this macro for
 * command-specific error messages.
 *
 * Beware of multiple eval of argument
 */
#define RELATION_IS_OTHER_TEMP(relation) \
	((relation)->rd_rel->relpersistence == RELPERSISTENCE_TEMP && \
	 !(relation)->rd_islocaltemp)


/*
 * RelationIsScannable
 *		Currently can only be false for a materialized view which has not been
 *		populated by its query.  This is likely to get more complicated later,
 *		so use a macro which looks like a function.
 */
#define RelationIsScannable(relation) ((relation)->rd_rel->relispopulated)

/*
 * RelationIsPopulated
 *		Currently, we don't physically distinguish the "populated" and
 *		"scannable" properties of matviews, but that may change later.
 *		Hence, use the appropriate one of these macros in code tests.
 */
#define RelationIsPopulated(relation) ((relation)->rd_rel->relispopulated)

/*
 * RelationIsAccessibleInLogicalDecoding
 *		True if we need to log enough information to have access via
 *		decoding snapshot.
 */
#define RelationIsAccessibleInLogicalDecoding(relation) \
	(XLogLogicalInfoActive() && \
	 RelationNeedsWAL(relation) && \
	 (IsCatalogRelation(relation) || RelationIsUsedAsCatalogTable(relation)))

/*
 * RelationIsLogicallyLogged
 *		True if we need to log enough information to extract the data from the
 *		WAL stream.
 *
 * We don't log information for unlogged tables (since they don't WAL log
 * anyway), for foreign tables (since they don't WAL log, either),
 * and for system tables (their content is hard to make sense of, and
 * it would complicate decoding slightly for little gain). Note that we *do*
 * log information for user defined catalog tables since they presumably are
 * interesting to the user...
 */
#define RelationIsLogicallyLogged(relation) \
	(XLogLogicalInfoActive() && \
	 RelationNeedsWAL(relation) && \
	 (relation)->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&	\
	 !IsCatalogRelation(relation))

/* routines in utils/cache/relcache.c */
extern void RelationIncrementReferenceCount(Relation rel);
extern void RelationDecrementReferenceCount(Relation rel);

#endif							/* REL_H */
./relcache.c0000664000175000017500000067147415222150025011634 0ustar  xmanxman/*-------------------------------------------------------------------------
 *
 * relcache.c
 *	  POSTGRES relation descriptor cache code
 *
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/cache/relcache.c
 *
 *-------------------------------------------------------------------------
 */
/*
 * INTERFACE ROUTINES
 *		RelationCacheInitialize			- initialize relcache (to empty)
 *		RelationCacheInitializePhase2	- initialize shared-catalog entries
 *		RelationCacheInitializePhase3	- finish initializing relcache
 *		RelationIdGetRelation			- get a reldesc by relation id
 *		RelationClose					- close an open relation
 *
 * NOTES
 *		The following code contains many undocumented hacks.  Please be
 *		careful....
 */
#include "postgres.h"

#include <sys/file.h>
#include <fcntl.h>
#include <unistd.h>

#include "access/htup_details.h"
#include "access/multixact.h"
#include "access/parallel.h"
#include "access/reloptions.h"
#include "access/sysattr.h"
#include "access/table.h"
#include "access/tableam.h"
#include "access/tupdesc_details.h"
#include "access/xact.h"
#include "catalog/binary_upgrade.h"
#include "catalog/catalog.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/partition.h"
#include "catalog/pg_am.h"
#include "catalog/pg_amproc.h"
#include "catalog/pg_attrdef.h"
#include "catalog/pg_auth_members.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_database.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_publication.h"
#include "catalog/pg_rewrite.h"
#include "catalog/pg_shseclabel.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_subscription.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
#include "catalog/schemapg.h"
#include "catalog/storage.h"
#include "commands/policy.h"
#include "commands/publicationcmds.h"
#include "commands/trigger.h"
#include "common/int.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/optimizer.h"
#include "pgstat.h"
#include "rewrite/rewriteDefine.h"
#include "rewrite/rowsecurity.h"
#include "storage/fd.h"
#include "storage/lmgr.h"
#include "storage/lock.h"
#include "storage/smgr.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/catcache.h"
#include "utils/datum.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/relmapper.h"
#include "utils/resowner.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "rewrite/rewriteHandler.h"

#define RELCACHE_INIT_FILEMAGIC		0x573266	/* version ID value */

/*
 * Whether to bother checking if relation cache memory needs to be freed
 * eagerly.  See also RelationBuildDesc() and pg_config_manual.h.
 */
#if defined(RECOVER_RELATION_BUILD_MEMORY) && (RECOVER_RELATION_BUILD_MEMORY != 0)
#define MAYBE_RECOVER_RELATION_BUILD_MEMORY 1
#else
#define RECOVER_RELATION_BUILD_MEMORY 0
#ifdef DISCARD_CACHES_ENABLED
#define MAYBE_RECOVER_RELATION_BUILD_MEMORY 1
#endif
#endif

/*
 *		hardcoded tuple descriptors, contents generated by genbki.pl
 */
static const FormData_pg_attribute Desc_pg_class[Natts_pg_class] = {Schema_pg_class};
static const FormData_pg_attribute Desc_pg_attribute[Natts_pg_attribute] = {Schema_pg_attribute};
static const FormData_pg_attribute Desc_pg_proc[Natts_pg_proc] = {Schema_pg_proc};
static const FormData_pg_attribute Desc_pg_type[Natts_pg_type] = {Schema_pg_type};
static const FormData_pg_attribute Desc_pg_database[Natts_pg_database] = {Schema_pg_database};
static const FormData_pg_attribute Desc_pg_authid[Natts_pg_authid] = {Schema_pg_authid};
static const FormData_pg_attribute Desc_pg_auth_members[Natts_pg_auth_members] = {Schema_pg_auth_members};
static const FormData_pg_attribute Desc_pg_index[Natts_pg_index] = {Schema_pg_index};
static const FormData_pg_attribute Desc_pg_shseclabel[Natts_pg_shseclabel] = {Schema_pg_shseclabel};
static const FormData_pg_attribute Desc_pg_subscription[Natts_pg_subscription] = {Schema_pg_subscription};

/*
 *		Hash tables that index the relation cache
 *
 *		We used to index the cache by both name and OID, but now there
 *		is only an index by OID.
 */
typedef struct relidcacheent
{
	Oid			reloid;
	Relation	reldesc;
} RelIdCacheEnt;

static HTAB *RelationIdCache;

/*
 * This flag is false until we have prepared the critical relcache entries
 * that are needed to do indexscans on the tables read by relcache building.
 */
bool		criticalRelcachesBuilt = false;

/*
 * This flag is false until we have prepared the critical relcache entries
 * for shared catalogs (which are the tables needed for login).
 */
bool		criticalSharedRelcachesBuilt = false;

/*
 * This counter counts relcache inval events received since backend startup
 * (but only for rels that are actually in cache).  Presently, we use it only
 * to detect whether data about to be written by write_relcache_init_file()
 * might already be obsolete.
 */
static long relcacheInvalsReceived = 0L;

/*
 * in_progress_list is a stack of ongoing RelationBuildDesc() calls.  CREATE
 * INDEX CONCURRENTLY makes catalog changes under ShareUpdateExclusiveLock.
 * It critically relies on each backend absorbing those changes no later than
 * next transaction start.  Hence, RelationBuildDesc() loops until it finishes
 * without accepting a relevant invalidation.  (Most invalidation consumers
 * don't do this.)
 */
typedef struct inprogressent
{
	Oid			reloid;			/* OID of relation being built */
	bool		invalidated;	/* whether an invalidation arrived for it */
} InProgressEnt;

static InProgressEnt *in_progress_list;
static int	in_progress_list_len;
static int	in_progress_list_maxlen;

/*
 * eoxact_list[] stores the OIDs of relations that (might) need AtEOXact
 * cleanup work.  This list intentionally has limited size; if it overflows,
 * we fall back to scanning the whole hashtable.  There is no value in a very
 * large list because (1) at some point, a hash_seq_search scan is faster than
 * retail lookups, and (2) the value of this is to reduce EOXact work for
 * short transactions, which can't have dirtied all that many tables anyway.
 * EOXactListAdd() does not bother to prevent duplicate list entries, so the
 * cleanup processing must be idempotent.
 */
#define MAX_EOXACT_LIST 32
static Oid	eoxact_list[MAX_EOXACT_LIST];
static int	eoxact_list_len = 0;
static bool eoxact_list_overflowed = false;

#define EOXactListAdd(rel) \
	do { \
		if (eoxact_list_len < MAX_EOXACT_LIST) \
			eoxact_list[eoxact_list_len++] = (rel)->rd_id; \
		else \
			eoxact_list_overflowed = true; \
	} while (0)

/*
 * EOXactTupleDescArray stores TupleDescs that (might) need AtEOXact
 * cleanup work.  The array expands as needed; there is no hashtable because
 * we don't need to access individual items except at EOXact.
 */
static TupleDesc *EOXactTupleDescArray;
static int	NextEOXactTupleDescNum = 0;
static int	EOXactTupleDescArrayLen = 0;

/*
 *		macros to manipulate the lookup hashtable
 */
#define RelationCacheInsert(RELATION, replace_allowed)	\
do { \
	RelIdCacheEnt *hentry; bool found; \
	hentry = (RelIdCacheEnt *) hash_search(RelationIdCache, \
										   &((RELATION)->rd_id), \
										   HASH_ENTER, &found); \
	if (found) \
	{ \
		/* see comments in RelationBuildDesc and RelationBuildLocalRelation */ \
		Relation _old_rel = hentry->reldesc; \
		Assert(replace_allowed); \
		hentry->reldesc = (RELATION); \
		if (RelationHasReferenceCountZero(_old_rel)) \
			RelationDestroyRelation(_old_rel, false); \
		else if (!IsBootstrapProcessingMode()) \
			elog(WARNING, "leaking still-referenced relcache entry for \"%s\"", \
				 RelationGetRelationName(_old_rel)); \
	} \
	else \
		hentry->reldesc = (RELATION); \
} while(0)

#define RelationIdCacheLookup(ID, RELATION) \
do { \
	RelIdCacheEnt *hentry; \
	hentry = (RelIdCacheEnt *) hash_search(RelationIdCache, \
										   &(ID), \
										   HASH_FIND, NULL); \
	if (hentry) \
		RELATION = hentry->reldesc; \
	else \
		RELATION = NULL; \
} while(0)

#define RelationCacheDelete(RELATION) \
do { \
	RelIdCacheEnt *hentry; \
	hentry = (RelIdCacheEnt *) hash_search(RelationIdCache, \
										   &((RELATION)->rd_id), \
										   HASH_REMOVE, NULL); \
	if (hentry == NULL) \
		elog(WARNING, "failed to delete relcache entry for OID %u", \
			 (RELATION)->rd_id); \
} while(0)


/*
 * Special cache for opclass-related information
 *
 * Note: only default support procs get cached, ie, those with
 * lefttype = righttype = opcintype.
 */
typedef struct opclasscacheent
{
	Oid			opclassoid;		/* lookup key: OID of opclass */
	bool		valid;			/* set true after successful fill-in */
	StrategyNumber numSupport;	/* max # of support procs (from pg_am) */
	Oid			opcfamily;		/* OID of opclass's family */
	Oid			opcintype;		/* OID of opclass's declared input type */
	RegProcedure *supportProcs; /* OIDs of support procedures */
} OpClassCacheEnt;

static HTAB *OpClassCache = NULL;


/* non-export function prototypes */

static void RelationCloseCleanup(Relation relation);
static void RelationDestroyRelation(Relation relation, bool remember_tupdesc);
static void RelationInvalidateRelation(Relation relation);
static void RelationClearRelation(Relation relation);
static void RelationRebuildRelation(Relation relation);

static void RelationReloadIndexInfo(Relation relation);
static void RelationReloadNailed(Relation relation);
static void RelationFlushRelation(Relation relation);
static void RememberToFreeTupleDescAtEOX(TupleDesc td);
#ifdef USE_ASSERT_CHECKING
static void AssertPendingSyncConsistency(Relation relation);
#endif
static void AtEOXact_cleanup(Relation relation, bool isCommit);
static void AtEOSubXact_cleanup(Relation relation, bool isCommit,
								SubTransactionId mySubid, SubTransactionId parentSubid);
static bool load_relcache_init_file(bool shared);
static void write_relcache_init_file(bool shared);
static void write_item(const void *data, Size len, FILE *fp);

static void formrdesc(const char *relationName, Oid relationReltype,
					  bool isshared, int natts, const FormData_pg_attribute *attrs);

static HeapTuple ScanPgRelation(Oid targetRelId, bool indexOK, bool force_non_historic);
static Relation AllocateRelationDesc(Form_pg_class relp);
static void RelationParseRelOptions(Relation relation, HeapTuple tuple);
static void RelationBuildTupleDesc(Relation relation);
static Relation RelationBuildDesc(Oid targetRelId, bool insertIt);
static void RelationInitPhysicalAddr(Relation relation);
static void load_critical_index(Oid indexoid, Oid heapoid);
static TupleDesc GetPgClassDescriptor(void);
static TupleDesc GetPgIndexDescriptor(void);
static void AttrDefaultFetch(Relation relation, int ndef);
static int	AttrDefaultCmp(const void *a, const void *b);
static void CheckNNConstraintFetch(Relation relation);
static int	CheckConstraintCmp(const void *a, const void *b);
static void InitIndexAmRoutine(Relation relation);
static void IndexSupportInitialize(oidvector *indclass,
								   RegProcedure *indexSupport,
								   Oid *opFamily,
								   Oid *opcInType,
								   StrategyNumber maxSupportNumber,
								   AttrNumber maxAttributeNumber);
static OpClassCacheEnt *LookupOpclassInfo(Oid operatorClassOid,
										  StrategyNumber numSupport);
static void RelationCacheInitFileRemoveInDir(const char *tblspcpath);
static void unlink_initfile(const char *initfilename, int elevel);


/*
 *		ScanPgRelation
 *
 *		This is used by RelationBuildDesc to find a pg_class
 *		tuple matching targetRelId.  The caller must hold at least
 *		AccessShareLock on the target relid to prevent concurrent-update
 *		scenarios; it isn't guaranteed that all scans used to build the
 *		relcache entry will use the same snapshot.  If, for example,
 *		an attribute were to be added after scanning pg_class and before
 *		scanning pg_attribute, relnatts wouldn't match.
 *
 *		NB: the returned tuple has been copied into palloc'd storage
 *		and must eventually be freed with heap_freetuple.
 */
static HeapTuple
ScanPgRelation(Oid targetRelId, bool indexOK, bool force_non_historic)
{
	HeapTuple	pg_class_tuple;
	Relation	pg_class_desc;
	SysScanDesc pg_class_scan;
	ScanKeyData key[1];
	Snapshot	snapshot = NULL;

	/*
	 * If something goes wrong during backend startup, we might find ourselves
	 * trying to read pg_class before we've selected a database.  That ain't
	 * gonna work, so bail out with a useful error message.  If this happens,
	 * it probably means a relcache entry that needs to be nailed isn't.
	 */
	if (!OidIsValid(MyDatabaseId))
		elog(FATAL, "cannot read pg_class without having selected a database");

	/*
	 * form a scan key
	 */
	ScanKeyInit(&key[0],
				Anum_pg_class_oid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(targetRelId));

	/*
	 * Open pg_class and fetch a tuple.  Force heap scan if we haven't yet
	 * built the critical relcache entries (this includes initdb and startup
	 * without a pg_internal.init file).  The caller can also force a heap
	 * scan by setting indexOK == false.
	 */
	pg_class_desc = table_open(RelationRelationId, AccessShareLock);

	/*
	 * The caller might need a tuple that's newer than what's visible to the
	 * historic snapshot; currently the only case requiring to do so is
	 * looking up the relfilenumber of non mapped system relations during
	 * decoding.
	 */
	if (force_non_historic)
		snapshot = RegisterSnapshot(GetNonHistoricCatalogSnapshot(RelationRelationId));

	pg_class_scan = systable_beginscan(pg_class_desc, ClassOidIndexId,
									   indexOK && criticalRelcachesBuilt,
									   snapshot,
									   1, key);

	pg_class_tuple = systable_getnext(pg_class_scan);

	/*
	 * Must copy tuple before releasing buffer.
	 */
	if (HeapTupleIsValid(pg_class_tuple))
		pg_class_tuple = heap_copytuple(pg_class_tuple);

	/* all done */
	systable_endscan(pg_class_scan);

	if (snapshot)
		UnregisterSnapshot(snapshot);

	table_close(pg_class_desc, AccessShareLock);

	return pg_class_tuple;
}

/*
 *		AllocateRelationDesc
 *
 *		This is used to allocate memory for a new relation descriptor
 *		and initialize the rd_rel field from the given pg_class tuple.
 */
static Relation
AllocateRelationDesc(Form_pg_class relp)
{
	Relation	relation;
	MemoryContext oldcxt;
	Form_pg_class relationForm;

	/* Relcache entries must live in CacheMemoryContext */
	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);

	/*
	 * allocate and zero space for new relation descriptor
	 */
	relation = palloc0_object(RelationData);

	/* make sure relation is marked as having no open file yet */
	relation->rd_smgr = NULL;

	/*
	 * Copy the relation tuple form
	 *
	 * We only allocate space for the fixed fields, ie, CLASS_TUPLE_SIZE. The
	 * variable-length fields (relacl, reloptions) are NOT stored in the
	 * relcache --- there'd be little point in it, since we don't copy the
	 * tuple's nulls bitmap and hence wouldn't know if the values are valid.
	 * Bottom line is that relacl *cannot* be retrieved from the relcache. Get
	 * it from the syscache if you need it.  The same goes for the original
	 * form of reloptions (however, we do store the parsed form of reloptions
	 * in rd_options).
	 */
	relationForm = (Form_pg_class) palloc(CLASS_TUPLE_SIZE);

	memcpy(relationForm, relp, CLASS_TUPLE_SIZE);

	/* initialize relation tuple form */
	relation->rd_rel = relationForm;

	/* and allocate attribute tuple form storage */
	relation->rd_att = CreateTemplateTupleDesc(relationForm->relnatts);
	/* which we mark as a reference-counted tupdesc */
	relation->rd_att->tdrefcount = 1;

	MemoryContextSwitchTo(oldcxt);

	return relation;
}

/*
 * RelationParseRelOptions
 *		Convert pg_class.reloptions into pre-parsed rd_options
 *
 * tuple is the real pg_class tuple (not rd_rel!) for relation
 *
 * Note: rd_rel and (if an index) rd_indam must be valid already
 */
static void
RelationParseRelOptions(Relation relation, HeapTuple tuple)
{
	bytea	   *options;
	amoptions_function amoptsfn;

	relation->rd_options = NULL;

	/*
	 * Look up any AM-specific parse function; fall out if relkind should not
	 * have options.
	 */
	switch (relation->rd_rel->relkind)
	{
		case RELKIND_RELATION:
		case RELKIND_TOASTVALUE:
		case RELKIND_VIEW:
		case RELKIND_MATVIEW:
		case RELKIND_PARTITIONED_TABLE:
			amoptsfn = NULL;
			break;
		case RELKIND_INDEX:
		case RELKIND_PARTITIONED_INDEX:
			amoptsfn = relation->rd_indam->amoptions;
			break;
		default:
			return;
	}

	/*
	 * Fetch reloptions from tuple; have to use a hardwired descriptor because
	 * we might not have any other for pg_class yet (consider executing this
	 * code for pg_class itself)
	 */
	options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptsfn);

	/*
	 * Copy parsed data into CacheMemoryContext.  To guard against the
	 * possibility of leaks in the reloptions code, we want to do the actual
	 * parsing in the caller's memory context and copy the results into
	 * CacheMemoryContext after the fact.
	 */
	if (options)
	{
		relation->rd_options = MemoryContextAlloc(CacheMemoryContext,
												  VARSIZE(options));
		memcpy(relation->rd_options, options, VARSIZE(options));
		pfree(options);
	}
}

/*
 *		RelationBuildTupleDesc
 *
 *		Form the relation's tuple descriptor from information in
 *		the pg_attribute, pg_attrdef & pg_constraint system catalogs.
 */
static void
RelationBuildTupleDesc(Relation relation)
{
	HeapTuple	pg_attribute_tuple;
	Relation	pg_attribute_desc;
	SysScanDesc pg_attribute_scan;
	ScanKeyData skey[2];
	int			need;
	TupleConstr *constr;
	AttrMissing *attrmiss = NULL;
	int			ndef = 0;

	/* fill rd_att's type ID fields (compare heap.c's AddNewRelationTuple) */
	relation->rd_att->tdtypeid =
		relation->rd_rel->reltype ? relation->rd_rel->reltype : RECORDOID;
	relation->rd_att->tdtypmod = -1;	/* just to be sure */

	constr = (TupleConstr *) MemoryContextAllocZero(CacheMemoryContext,
													sizeof(TupleConstr));

	/*
	 * Form a scan key that selects only user attributes (attnum > 0).
	 * (Eliminating system attribute rows at the index level is lots faster
	 * than fetching them.)
	 */
	ScanKeyInit(&skey[0],
				Anum_pg_attribute_attrelid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(RelationGetRelid(relation)));
	ScanKeyInit(&skey[1],
				Anum_pg_attribute_attnum,
				BTGreaterStrategyNumber, F_INT2GT,
				Int16GetDatum(0));

	/*
	 * Open pg_attribute and begin a scan.  Force heap scan if we haven't yet
	 * built the critical relcache entries (this includes initdb and startup
	 * without a pg_internal.init file).
	 */
	pg_attribute_desc = table_open(AttributeRelationId, AccessShareLock);
	pg_attribute_scan = systable_beginscan(pg_attribute_desc,
										   AttributeRelidNumIndexId,
										   criticalRelcachesBuilt,
										   NULL,
										   2, skey);

	/*
	 * add attribute data to relation->rd_att
	 */
	need = RelationGetNumberOfAttributes(relation);

	while (HeapTupleIsValid(pg_attribute_tuple = systable_getnext(pg_attribute_scan)))
	{
		Form_pg_attribute attp;
		int			attnum;

		attp = (Form_pg_attribute) GETSTRUCT(pg_attribute_tuple);

		attnum = attp->attnum;
		if (attnum <= 0 || attnum > RelationGetNumberOfAttributes(relation))
			elog(ERROR, "invalid attribute number %d for relation \"%s\"",
				 attp->attnum, RelationGetRelationName(relation));

		memcpy(TupleDescAttr(relation->rd_att, attnum - 1),
			   attp,
			   ATTRIBUTE_FIXED_PART_SIZE);

		populate_compact_attribute(relation->rd_att, attnum - 1);

		/* Update constraint/default info */
		if (attp->attnotnull)
			constr->has_not_null = true;
		if (attp->attgenerated == ATTRIBUTE_GENERATED_STORED)
			constr->has_generated_stored = true;
		if (attp->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
			constr->has_generated_virtual = true;
		if (attp->atthasdef)
			ndef++;

		/* If the column has a "missing" value, put it in the attrmiss array */
		if (attp->atthasmissing)
		{
			Datum		missingval;
			bool		missingNull;

			/* Do we have a missing value? */
			missingval = heap_getattr(pg_attribute_tuple,
									  Anum_pg_attribute_attmissingval,
									  pg_attribute_desc->rd_att,
									  &missingNull);
			if (!missingNull)
			{
				/* Yes, fetch from the array */
				MemoryContext oldcxt;
				bool		is_null;
				int			one = 1;
				Datum		missval;

				if (attrmiss == NULL)
					attrmiss = (AttrMissing *)
						MemoryContextAllocZero(CacheMemoryContext,
											   relation->rd_rel->relnatts *
											   sizeof(AttrMissing));

				missval = array_get_element(missingval,
											1,
											&one,
											-1,
											attp->attlen,
											attp->attbyval,
											attp->attalign,
											&is_null);
				Assert(!is_null);
				if (attp->attbyval)
				{
					/* for copy by val just copy the datum direct */
					attrmiss[attnum - 1].am_value = missval;
				}
				else
				{
					/* otherwise copy in the correct context */
					oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
					attrmiss[attnum - 1].am_value = datumCopy(missval,
															  attp->attbyval,
															  attp->attlen);
					MemoryContextSwitchTo(oldcxt);
				}
				attrmiss[attnum - 1].am_present = true;
			}
		}
		need--;
		if (need == 0)
			break;
	}

	/*
	 * end the scan and close the attribute relation
	 */
	systable_endscan(pg_attribute_scan);
	table_close(pg_attribute_desc, AccessShareLock);

	if (need != 0)
		elog(ERROR, "pg_attribute catalog is missing %d attribute(s) for relation OID %u",
			 need, RelationGetRelid(relation));

	/*
	 * Set up constraint/default info
	 */
	if (constr->has_not_null ||
		constr->has_generated_stored ||
		constr->has_generated_virtual ||
		ndef > 0 ||
		attrmiss ||
		relation->rd_rel->relchecks > 0)
	{
		bool		is_catalog = IsCatalogRelation(relation);

		relation->rd_att->constr = constr;

		if (ndef > 0)			/* DEFAULTs */
			AttrDefaultFetch(relation, ndef);
		else
			constr->num_defval = 0;

		constr->missing = attrmiss;

		/* CHECK and NOT NULLs */
		if (relation->rd_rel->relchecks > 0 ||
			(!is_catalog && constr->has_not_null))
			CheckNNConstraintFetch(relation);

		/*
		 * Any not-null constraint that wasn't marked invalid by
		 * CheckNNConstraintFetch must necessarily be valid; make it so in the
		 * CompactAttribute array.
		 */
		if (!is_catalog)
		{
			for (int i = 0; i < relation->rd_rel->relnatts; i++)
			{
				CompactAttribute *attr;

				attr = TupleDescCompactAttr(relation->rd_att, i);

				if (attr->attnullability == ATTNULLABLE_UNKNOWN)
					attr->attnullability = ATTNULLABLE_VALID;
				else
					Assert(attr->attnullability == ATTNULLABLE_INVALID ||
						   attr->attnullability == ATTNULLABLE_UNRESTRICTED);
			}
		}

		if (relation->rd_rel->relchecks == 0)
			constr->num_check = 0;
	}
	else
	{
		pfree(constr);
		relation->rd_att->constr = NULL;
	}

	TupleDescFinalize(relation->rd_att);
}

/*
 *		RelationBuildRuleLock
 *
 *		Form the relation's rewrite rules from information in
 *		the pg_rewrite system catalog.
 *
 * Note: The rule parsetrees are potentially very complex node structures.
 * To allow these trees to be freed when the relcache entry is flushed,
 * we make a private memory context to hold the RuleLock information for
 * each relcache entry that has associated rules.  The context is used
 * just for rule info, not for any other subsidiary data of the relcache
 * entry, because that keeps the update logic in RelationRebuildRelation()
 * manageable.  The other subsidiary data structures are simple enough
 * to be easy to free explicitly, anyway.
 *
 * Note: The relation's reloptions must have been extracted first.
 */
static void
RelationBuildRuleLock(Relation relation)
{
	MemoryContext rulescxt;
	MemoryContext oldcxt;
	HeapTuple	rewrite_tuple;
	Relation	rewrite_desc;
	TupleDesc	rewrite_tupdesc;
	SysScanDesc rewrite_scan;
	ScanKeyData key;
	RuleLock   *rulelock;
	int			numlocks;
	RewriteRule **rules;
	int			maxlocks;

	/*
	 * Make the private context.  Assume it'll not contain much data.
	 */
	rulescxt = AllocSetContextCreate(CacheMemoryContext,
									 "relation rules",
									 ALLOCSET_SMALL_SIZES);
	relation->rd_rulescxt = rulescxt;
	MemoryContextCopyAndSetIdentifier(rulescxt,
									  RelationGetRelationName(relation));

	/*
	 * allocate an array to hold the rewrite rules (the array is extended if
	 * necessary)
	 */
	maxlocks = 4;
	rules = (RewriteRule **)
		MemoryContextAlloc(rulescxt, sizeof(RewriteRule *) * maxlocks);
	numlocks = 0;

	/*
	 * form a scan key
	 */
	ScanKeyInit(&key,
				Anum_pg_rewrite_ev_class,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(RelationGetRelid(relation)));

	/*
	 * open pg_rewrite and begin a scan
	 *
	 * Note: since we scan the rules using RewriteRelRulenameIndexId, we will
	 * be reading the rules in name order, except possibly during
	 * emergency-recovery operations (ie, IgnoreSystemIndexes). This in turn
	 * ensures that rules will be fired in name order.
	 */
	rewrite_desc = table_open(RewriteRelationId, AccessShareLock);
	rewrite_tupdesc = RelationGetDescr(rewrite_desc);
	rewrite_scan = systable_beginscan(rewrite_desc,
									  RewriteRelRulenameIndexId,
									  true, NULL,
									  1, &key);

	while (HeapTupleIsValid(rewrite_tuple = systable_getnext(rewrite_scan)))
	{
		Form_pg_rewrite rewrite_form = (Form_pg_rewrite) GETSTRUCT(rewrite_tuple);
		bool		isnull;
		Datum		rule_datum;
		char	   *rule_str;
		RewriteRule *rule;
		Oid			check_as_user;

		rule = (RewriteRule *) MemoryContextAlloc(rulescxt,
												  sizeof(RewriteRule));

		rule->ruleId = rewrite_form->oid;

		rule->event = rewrite_form->ev_type - '0';
		rule->enabled = rewrite_form->ev_enabled;
		rule->isInstead = rewrite_form->is_instead;

		/*
		 * Must use heap_getattr to fetch ev_action and ev_qual.  Also, the
		 * rule strings are often large enough to be toasted.  To avoid
		 * leaking memory in the caller's context, do the detoasting here so
		 * we can free the detoasted version.
		 */
		rule_datum = heap_getattr(rewrite_tuple,
								  Anum_pg_rewrite_ev_action,
								  rewrite_tupdesc,
								  &isnull);
		Assert(!isnull);
		rule_str = TextDatumGetCString(rule_datum);
		oldcxt = MemoryContextSwitchTo(rulescxt);
		rule->actions = (List *) stringToNode(rule_str);
		MemoryContextSwitchTo(oldcxt);
		pfree(rule_str);

		rule_datum = heap_getattr(rewrite_tuple,
								  Anum_pg_rewrite_ev_qual,
								  rewrite_tupdesc,
								  &isnull);
		Assert(!isnull);
		rule_str = TextDatumGetCString(rule_datum);
		oldcxt = MemoryContextSwitchTo(rulescxt);
		rule->qual = (Node *) stringToNode(rule_str);
		MemoryContextSwitchTo(oldcxt);
		pfree(rule_str);

		/*
		 * If this is a SELECT rule defining a view, and the view has
		 * "security_invoker" set, we must perform all permissions checks on
		 * relations referred to by the rule as the invoking user.
		 *
		 * In all other cases (including non-SELECT rules on security invoker
		 * views), perform the permissions checks as the relation owner.
		 */
		if (rule->event == CMD_SELECT &&
			relation->rd_rel->relkind == RELKIND_VIEW &&
			RelationHasSecurityInvoker(relation))
			check_as_user = InvalidOid;
		else
			check_as_user = relation->rd_rel->relowner;

		/*
		 * Scan through the rule's actions and set the checkAsUser field on
		 * all RTEPermissionInfos. We have to look at the qual as well, in
		 * case it contains sublinks.
		 *
		 * The reason for doing this when the rule is loaded, rather than when
		 * it is stored, is that otherwise ALTER TABLE OWNER would have to
		 * grovel through stored rules to update checkAsUser fields. Scanning
		 * the rule tree during load is relatively cheap (compared to
		 * constructing it in the first place), so we do it here.
		 */
		setRuleCheckAsUser((Node *) rule->actions, check_as_user);
		setRuleCheckAsUser(rule->qual, check_as_user);

		if (numlocks >= maxlocks)
		{
			maxlocks *= 2;
			rules = (RewriteRule **)
				repalloc(rules, sizeof(RewriteRule *) * maxlocks);
		}
		rules[numlocks++] = rule;
	}

	/*
	 * end the scan and close the attribute relation
	 */
	systable_endscan(rewrite_scan);
	table_close(rewrite_desc, AccessShareLock);

	/*
	 * there might not be any rules (if relhasrules is out-of-date)
	 */
	if (numlocks == 0)
	{
		relation->rd_rules = NULL;
		relation->rd_rulescxt = NULL;
		MemoryContextDelete(rulescxt);
		return;
	}

	/*
	 * form a RuleLock and insert into relation
	 */
	rulelock = (RuleLock *) MemoryContextAlloc(rulescxt, sizeof(RuleLock));
	rulelock->numLocks = numlocks;
	rulelock->rules = rules;

	relation->rd_rules = rulelock;
}

/*
 *		equalRuleLocks
 *
 *		Determine whether two RuleLocks are equivalent
 *
 *		Probably this should be in the rules code someplace...
 */
static bool
equalRuleLocks(RuleLock *rlock1, RuleLock *rlock2)
{
	int			i;

	/*
	 * As of 7.3 we assume the rule ordering is repeatable, because
	 * RelationBuildRuleLock should read 'em in a consistent order.  So just
	 * compare corresponding slots.
	 */
	if (rlock1 != NULL)
	{
		if (rlock2 == NULL)
			return false;
		if (rlock1->numLocks != rlock2->numLocks)
			return false;
		for (i = 0; i < rlock1->numLocks; i++)
		{
			RewriteRule *rule1 = rlock1->rules[i];
			RewriteRule *rule2 = rlock2->rules[i];

			if (rule1->ruleId != rule2->ruleId)
				return false;
			if (rule1->event != rule2->event)
				return false;
			if (rule1->enabled != rule2->enabled)
				return false;
			if (rule1->isInstead != rule2->isInstead)
				return false;
			if (!equal(rule1->qual, rule2->qual))
				return false;
			if (!equal(rule1->actions, rule2->actions))
				return false;
		}
	}
	else if (rlock2 != NULL)
		return false;
	return true;
}

/*
 *		equalPolicy
 *
 *		Determine whether two policies are equivalent
 */
static bool
equalPolicy(RowSecurityPolicy *policy1, RowSecurityPolicy *policy2)
{
	int			i;
	Oid		   *r1,
			   *r2;

	if (policy1 != NULL)
	{
		if (policy2 == NULL)
			return false;

		if (policy1->polcmd != policy2->polcmd)
			return false;
		if (policy1->hassublinks != policy2->hassublinks)
			return false;
		if (strcmp(policy1->policy_name, policy2->policy_name) != 0)
			return false;
		if (ARR_DIMS(policy1->roles)[0] != ARR_DIMS(policy2->roles)[0])
			return false;

		r1 = (Oid *) ARR_DATA_PTR(policy1->roles);
		r2 = (Oid *) ARR_DATA_PTR(policy2->roles);

		for (i = 0; i < ARR_DIMS(policy1->roles)[0]; i++)
		{
			if (r1[i] != r2[i])
				return false;
		}

		if (!equal(policy1->qual, policy2->qual))
			return false;
		if (!equal(policy1->with_check_qual, policy2->with_check_qual))
			return false;
	}
	else if (policy2 != NULL)
		return false;

	return true;
}

/*
 *		equalRSDesc
 *
 *		Determine whether two RowSecurityDesc's are equivalent
 */
static bool
equalRSDesc(RowSecurityDesc *rsdesc1, RowSecurityDesc *rsdesc2)
{
	ListCell   *lc,
			   *rc;

	if (rsdesc1 == NULL && rsdesc2 == NULL)
		return true;

	if ((rsdesc1 != NULL && rsdesc2 == NULL) ||
		(rsdesc1 == NULL && rsdesc2 != NULL))
		return false;

	if (list_length(rsdesc1->policies) != list_length(rsdesc2->policies))
		return false;

	/* RelationBuildRowSecurity should build policies in order */
	forboth(lc, rsdesc1->policies, rc, rsdesc2->policies)
	{
		RowSecurityPolicy *l = (RowSecurityPolicy *) lfirst(lc);
		RowSecurityPolicy *r = (RowSecurityPolicy *) lfirst(rc);

		if (!equalPolicy(l, r))
			return false;
	}

	return true;
}

/*
 *		RelationBuildDesc
 *
 *		Build a relation descriptor.  The caller must hold at least
 *		AccessShareLock on the target relid.
 *
 *		The new descriptor is inserted into the hash table if insertIt is true.
 *
 *		Returns NULL if no pg_class row could be found for the given relid
 *		(suggesting we are trying to access a just-deleted relation).
 *		Any other error is reported via elog.
 */
static Relation
RelationBuildDesc(Oid targetRelId, bool insertIt)
{
	int			in_progress_offset;
	Relation	relation;
	Oid			relid;
	HeapTuple	pg_class_tuple;
	Form_pg_class relp;

	/*
	 * This function and its subroutines can allocate a good deal of transient
	 * data in CurrentMemoryContext.  Traditionally we've just leaked that
	 * data, reasoning that the caller's context is at worst of transaction
	 * scope, and relcache loads shouldn't happen so often that it's essential
	 * to recover transient data before end of statement/transaction.  However
	 * that's definitely not true when debug_discard_caches is active, and
	 * perhaps it's not true in other cases.
	 *
	 * When debug_discard_caches is active or when forced to by
	 * RECOVER_RELATION_BUILD_MEMORY=1, arrange to allocate the junk in a
	 * temporary context that we'll free before returning.  Make it a child of
	 * caller's context so that it will get cleaned up appropriately if we
	 * error out partway through.
	 */
#ifdef MAYBE_RECOVER_RELATION_BUILD_MEMORY
	MemoryContext tmpcxt = NULL;
	MemoryContext oldcxt = NULL;

	if (RECOVER_RELATION_BUILD_MEMORY || debug_discard_caches > 0)
	{
		tmpcxt = AllocSetContextCreate(CurrentMemoryContext,
									   "RelationBuildDesc workspace",
									   ALLOCSET_DEFAULT_SIZES);
		oldcxt = MemoryContextSwitchTo(tmpcxt);
	}
#endif

	/* Register to catch invalidation messages */
	if (in_progress_list_len >= in_progress_list_maxlen)
	{
		int			allocsize;

		allocsize = in_progress_list_maxlen * 2;
		in_progress_list = repalloc(in_progress_list,
									allocsize * sizeof(*in_progress_list));
		in_progress_list_maxlen = allocsize;
	}
	in_progress_offset = in_progress_list_len++;
	in_progress_list[in_progress_offset].reloid = targetRelId;
retry:
	in_progress_list[in_progress_offset].invalidated = false;

	/*
	 * find the tuple in pg_class corresponding to the given relation id
	 */
	pg_class_tuple = ScanPgRelation(targetRelId, true, false);

	/*
	 * if no such tuple exists, return NULL
	 */
	if (!HeapTupleIsValid(pg_class_tuple))
	{
#ifdef MAYBE_RECOVER_RELATION_BUILD_MEMORY
		if (tmpcxt)
		{
			/* Return to caller's context, and blow away the temporary context */
			MemoryContextSwitchTo(oldcxt);
			MemoryContextDelete(tmpcxt);
		}
#endif
		Assert(in_progress_offset + 1 == in_progress_list_len);
		in_progress_list_len--;
		return NULL;
	}

	/*
	 * get information from the pg_class_tuple
	 */
	relp = (Form_pg_class) GETSTRUCT(pg_class_tuple);
	relid = relp->oid;
	Assert(relid == targetRelId);

	/*
	 * allocate storage for the relation descriptor, and copy pg_class_tuple
	 * to relation->rd_rel.
	 */
	relation = AllocateRelationDesc(relp);

	/*
	 * initialize the relation's relation id (relation->rd_id)
	 */
	RelationGetRelid(relation) = relid;

	/*
	 * Normal relations are not nailed into the cache.  Since we don't flush
	 * new relations, it won't be new.  It could be temp though.
	 */
	relation->rd_refcnt = 0;
	relation->rd_isnailed = false;
	relation->rd_createSubid = InvalidSubTransactionId;
	relation->rd_newRelfilelocatorSubid = InvalidSubTransactionId;
	relation->rd_firstRelfilelocatorSubid = InvalidSubTransactionId;
	relation->rd_droppedSubid = InvalidSubTransactionId;
	switch (relation->rd_rel->relpersistence)
	{
		case RELPERSISTENCE_UNLOGGED:
		case RELPERSISTENCE_PERMANENT:
			relation->rd_backend = INVALID_PROC_NUMBER;
			relation->rd_islocaltemp = false;
			break;
		case RELPERSISTENCE_TEMP:
			if (isTempOrTempToastNamespace(relation->rd_rel->relnamespace))
			{
				relation->rd_backend = ProcNumberForTempRelations();
				relation->rd_islocaltemp = true;
			}
			else
			{
				/*
				 * If it's a temp table, but not one of ours, we have to use
				 * the slow, grotty method to figure out the owning backend.
				 *
				 * Note: it's possible that rd_backend gets set to
				 * MyProcNumber here, in case we are looking at a pg_class
				 * entry left over from a crashed backend that coincidentally
				 * had the same ProcNumber we're using.  We should *not*
				 * consider such a table to be "ours"; this is why we need the
				 * separate rd_islocaltemp flag.  The pg_class entry will get
				 * flushed if/when we clean out the corresponding temp table
				 * namespace in preparation for using it.
				 */
				relation->rd_backend =
					GetTempNamespaceProcNumber(relation->rd_rel->relnamespace);
				Assert(relation->rd_backend != INVALID_PROC_NUMBER);
				relation->rd_islocaltemp = false;
			}
			break;
		default:
			elog(ERROR, "invalid relpersistence: %c",
				 relation->rd_rel->relpersistence);
			break;
	}

	/*
	 * initialize the tuple descriptor (relation->rd_att).
	 */
	RelationBuildTupleDesc(relation);

	/* foreign key data is not loaded till asked for */
	relation->rd_fkeylist = NIL;
	relation->rd_fkeyvalid = false;

	/* partitioning data is not loaded till asked for */
	relation->rd_partkey = NULL;
	relation->rd_partkeycxt = NULL;
	relation->rd_partdesc = NULL;
	relation->rd_partdesc_nodetached = NULL;
	relation->rd_partdesc_nodetached_xmin = InvalidTransactionId;
	relation->rd_pdcxt = NULL;
	relation->rd_pddcxt = NULL;
	relation->rd_partcheck = NIL;
	relation->rd_partcheckvalid = false;
	relation->rd_partcheckcxt = NULL;

	/*
	 * initialize access method information
	 */
	if (relation->rd_rel->relkind == RELKIND_INDEX ||
		relation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
		RelationInitIndexAccessInfo(relation);
	else if (RELKIND_HAS_TABLE_AM(relation->rd_rel->relkind) ||
			 relation->rd_rel->relkind == RELKIND_SEQUENCE)
		RelationInitTableAccessMethod(relation);
	else if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
	{
		/*
		 * Do nothing: access methods are a setting that partitions can
		 * inherit.
		 */
	}
	else
		Assert(relation->rd_rel->relam == InvalidOid);

	/* extract reloptions if any */
	RelationParseRelOptions(relation, pg_class_tuple);

	/*
	 * Fetch rules and triggers that affect this relation.
	 *
	 * Note that RelationBuildRuleLock() relies on this being done after
	 * extracting the relation's reloptions.
	 */
	if (relation->rd_rel->relhasrules)
		RelationBuildRuleLock(relation);
	else
	{
		relation->rd_rules = NULL;
		relation->rd_rulescxt = NULL;
	}

	if (relation->rd_rel->relhastriggers)
		RelationBuildTriggers(relation);
	else
		relation->trigdesc = NULL;

	if (relation->rd_rel->relrowsecurity)
		RelationBuildRowSecurity(relation);
	else
		relation->rd_rsdesc = NULL;

	/*
	 * initialize the relation lock manager information
	 */
	RelationInitLockInfo(relation); /* see lmgr.c */

	/*
	 * initialize physical addressing information for the relation
	 */
	RelationInitPhysicalAddr(relation);

	/* make sure relation is marked as having no open file yet */
	relation->rd_smgr = NULL;

	/*
	 * now we can free the memory allocated for pg_class_tuple
	 */
	heap_freetuple(pg_class_tuple);

	/*
	 * If an invalidation arrived mid-build, start over.  Between here and the
	 * end of this function, don't add code that does or reasonably could read
	 * system catalogs.  That range must be free from invalidation processing
	 * for the !insertIt case.  For the insertIt case, RelationCacheInsert()
	 * will enroll this relation in ordinary relcache invalidation processing,
	 */
	if (in_progress_list[in_progress_offset].invalidated)
	{
		RelationDestroyRelation(relation, false);
		goto retry;
	}
	Assert(in_progress_offset + 1 == in_progress_list_len);
	in_progress_list_len--;

	/*
	 * Insert newly created relation into relcache hash table, if requested.
	 *
	 * There is one scenario in which we might find a hashtable entry already
	 * present, even though our caller failed to find it: if the relation is a
	 * system catalog or index that's used during relcache load, we might have
	 * recursively created the same relcache entry during the preceding steps.
	 * So allow RelationCacheInsert to delete any already-present relcache
	 * entry for the same OID.  The already-present entry should have refcount
	 * zero (else somebody forgot to close it); in the event that it doesn't,
	 * we'll elog a WARNING and leak the already-present entry.
	 */
	if (insertIt)
		RelationCacheInsert(relation, true);

	/* It's fully valid */
	relation->rd_isvalid = true;

#ifdef MAYBE_RECOVER_RELATION_BUILD_MEMORY
	if (tmpcxt)
	{
		/* Return to caller's context, and blow away the temporary context */
		MemoryContextSwitchTo(oldcxt);
		MemoryContextDelete(tmpcxt);
	}
#endif

	return relation;
}

/*
 * Initialize the physical addressing info (RelFileLocator) for a relcache entry
 *
 * Note: at the physical level, relations in the pg_global tablespace must
 * be treated as shared, even if relisshared isn't set.  Hence we do not
 * look at relisshared here.
 */
static void
RelationInitPhysicalAddr(Relation relation)
{
	RelFileNumber oldnumber = relation->rd_locator.relNumber;

	/* these relations kinds never have storage */
	if (!RELKIND_HAS_STORAGE(relation->rd_rel->relkind))
		return;

	if (relation->rd_rel->reltablespace)
		relation->rd_locator.spcOid = relation->rd_rel->reltablespace;
	else
		relation->rd_locator.spcOid = MyDatabaseTableSpace;
	if (relation->rd_locator.spcOid == GLOBALTABLESPACE_OID)
		relation->rd_locator.dbOid = InvalidOid;
	else
		relation->rd_locator.dbOid = MyDatabaseId;

	if (relation->rd_rel->relfilenode)
	{
		/*
		 * Even if we are using a decoding snapshot that doesn't represent the
		 * current state of the catalog we need to make sure the filenode
		 * points to the current file since the older file will be gone (or
		 * truncated). The new file will still contain older rows so lookups
		 * in them will work correctly. This wouldn't work correctly if
		 * rewrites were allowed to change the schema in an incompatible way,
		 * but those are prevented both on catalog tables and on user tables
		 * declared as additional catalog tables.
		 */
		if (HistoricSnapshotActive()
			&& RelationIsAccessibleInLogicalDecoding(relation)
			&& IsTransactionState())
		{
			HeapTuple	phys_tuple;
			Form_pg_class physrel;

			phys_tuple = ScanPgRelation(RelationGetRelid(relation),
										RelationGetRelid(relation) != ClassOidIndexId,
										true);
			if (!HeapTupleIsValid(phys_tuple))
				elog(ERROR, "could not find pg_class entry for %u",
					 RelationGetRelid(relation));
			physrel = (Form_pg_class) GETSTRUCT(phys_tuple);

			relation->rd_rel->reltablespace = physrel->reltablespace;
			relation->rd_rel->relfilenode = physrel->relfilenode;
			heap_freetuple(phys_tuple);
		}

		relation->rd_locator.relNumber = relation->rd_rel->relfilenode;
	}
	else
	{
		/* Consult the relation mapper */
		relation->rd_locator.relNumber =
			RelationMapOidToFilenumber(relation->rd_id,
									   relation->rd_rel->relisshared);
		if (!RelFileNumberIsValid(relation->rd_locator.relNumber))
			elog(ERROR, "could not find relation mapping for relation \"%s\", OID %u",
				 RelationGetRelationName(relation), relation->rd_id);
	}

	/*
	 * For RelationNeedsWAL() to answer correctly on parallel workers, restore
	 * rd_firstRelfilelocatorSubid.  No subtransactions start or end while in
	 * parallel mode, so the specific SubTransactionId does not matter.
	 */
	if (IsParallelWorker() && oldnumber != relation->rd_locator.relNumber)
	{
		if (RelFileLocatorSkippingWAL(relation->rd_locator))
			relation->rd_firstRelfilelocatorSubid = TopSubTransactionId;
		else
			relation->rd_firstRelfilelocatorSubid = InvalidSubTransactionId;
	}
}

/*
 * Fill in the IndexAmRoutine for an index relation.
 *
 * relation's rd_amhandler and rd_indexcxt must be valid already.
 */
static void
InitIndexAmRoutine(Relation relation)
{
	MemoryContext oldctx;

	/*
	 * We formerly specified that the amhandler should return a palloc'd
	 * struct.  That's now deprecated in favor of returning a pointer to a
	 * static struct, but to avoid completely breaking old external AMs, run
	 * the amhandler in the relation's rd_indexcxt.
	 */
	oldctx = MemoryContextSwitchTo(relation->rd_indexcxt);
	relation->rd_indam = GetIndexAmRoutine(relation->rd_amhandler);
	MemoryContextSwitchTo(oldctx);
}

/*
 * Initialize index-access-method support data for an index relation
 */
void
RelationInitIndexAccessInfo(Relation relation)
{
	HeapTuple	tuple;
	Form_pg_am	aform;
	Datum		indcollDatum;
	Datum		indclassDatum;
	Datum		indoptionDatum;
	bool		isnull;
	oidvector  *indcoll;
	oidvector  *indclass;
	int2vector *indoption;
	MemoryContext indexcxt;
	MemoryContext oldcontext;
	int			indnatts;
	int			indnkeyatts;
	uint16		amsupport;

	/*
	 * Make a copy of the pg_index entry for the index.  Since pg_index
	 * contains variable-length and possibly-null fields, we have to do this
	 * honestly rather than just treating it as a Form_pg_index struct.
	 */
	tuple = SearchSysCache1(INDEXRELID,
							ObjectIdGetDatum(RelationGetRelid(relation)));
	if (!HeapTupleIsValid(tuple))
		elog(ERROR, "cache lookup failed for index %u",
			 RelationGetRelid(relation));
	oldcontext = MemoryContextSwitchTo(CacheMemoryContext);
	relation->rd_indextuple = heap_copytuple(tuple);
	relation->rd_index = (Form_pg_index) GETSTRUCT(relation->rd_indextuple);
	MemoryContextSwitchTo(oldcontext);
	ReleaseSysCache(tuple);

	/*
	 * Look up the index's access method, save the OID of its handler function
	 */
	Assert(relation->rd_rel->relam != InvalidOid);
	tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(relation->rd_rel->relam));
	if (!HeapTupleIsValid(tuple))
		elog(ERROR, "cache lookup failed for access method %u",
			 relation->rd_rel->relam);
	aform = (Form_pg_am) GETSTRUCT(tuple);
	relation->rd_amhandler = aform->amhandler;
	ReleaseSysCache(tuple);

	indnatts = RelationGetNumberOfAttributes(relation);
	if (indnatts != IndexRelationGetNumberOfAttributes(relation))
		elog(ERROR, "relnatts disagrees with indnatts for index %u",
			 RelationGetRelid(relation));
	indnkeyatts = IndexRelationGetNumberOfKeyAttributes(relation);

	/*
	 * Make the private context to hold index access info.  The reason we need
	 * a context, and not just a couple of pallocs, is so that we won't leak
	 * any subsidiary info attached to fmgr lookup records.
	 */
	indexcxt = AllocSetContextCreate(CacheMemoryContext,
									 "index info",
									 ALLOCSET_SMALL_SIZES);
	relation->rd_indexcxt = indexcxt;
	MemoryContextCopyAndSetIdentifier(indexcxt,
									  RelationGetRelationName(relation));

	/*
	 * Now we can fetch the index AM's API struct
	 */
	InitIndexAmRoutine(relation);

	/*
	 * Allocate arrays to hold data. Opclasses are not used for included
	 * columns, so allocate them for indnkeyatts only.
	 */
	relation->rd_opfamily = (Oid *)
		MemoryContextAllocZero(indexcxt, indnkeyatts * sizeof(Oid));
	relation->rd_opcintype = (Oid *)
		MemoryContextAllocZero(indexcxt, indnkeyatts * sizeof(Oid));

	amsupport = relation->rd_indam->amsupport;
	if (amsupport > 0)
	{
		int			nsupport = indnatts * amsupport;

		relation->rd_support = (RegProcedure *)
			MemoryContextAllocZero(indexcxt, nsupport * sizeof(RegProcedure));
		relation->rd_supportinfo = (FmgrInfo *)
			MemoryContextAllocZero(indexcxt, nsupport * sizeof(FmgrInfo));
	}
	else
	{
		relation->rd_support = NULL;
		relation->rd_supportinfo = NULL;
	}

	relation->rd_indcollation = (Oid *)
		MemoryContextAllocZero(indexcxt, indnkeyatts * sizeof(Oid));

	relation->rd_indoption = (int16 *)
		MemoryContextAllocZero(indexcxt, indnkeyatts * sizeof(int16));

	/*
	 * indcollation cannot be referenced directly through the C struct,
	 * because it comes after the variable-width indkey field.  Must extract
	 * the datum the hard way...
	 */
	indcollDatum = fastgetattr(relation->rd_indextuple,
							   Anum_pg_index_indcollation,
							   GetPgIndexDescriptor(),
							   &isnull);
	Assert(!isnull);
	indcoll = (oidvector *) DatumGetPointer(indcollDatum);
	memcpy(relation->rd_indcollation, indcoll->values, indnkeyatts * sizeof(Oid));

	/*
	 * indclass cannot be referenced directly through the C struct, because it
	 * comes after the variable-width indkey field.  Must extract the datum
	 * the hard way...
	 */
	indclassDatum = fastgetattr(relation->rd_indextuple,
								Anum_pg_index_indclass,
								GetPgIndexDescriptor(),
								&isnull);
	Assert(!isnull);
	indclass = (oidvector *) DatumGetPointer(indclassDatum);

	/*
	 * Fill the support procedure OID array, as well as the info about
	 * opfamilies and opclass input types.  (aminfo and supportinfo are left
	 * as zeroes, and are filled on-the-fly when used)
	 */
	IndexSupportInitialize(indclass, relation->rd_support,
						   relation->rd_opfamily, relation->rd_opcintype,
						   amsupport, indnkeyatts);

	/*
	 * Similarly extract indoption and copy it to the cache entry
	 */
	indoptionDatum = fastgetattr(relation->rd_indextuple,
								 Anum_pg_index_indoption,
								 GetPgIndexDescriptor(),
								 &isnull);
	Assert(!isnull);
	indoption = (int2vector *) DatumGetPointer(indoptionDatum);
	memcpy(relation->rd_indoption, indoption->values, indnkeyatts * sizeof(int16));

	(void) RelationGetIndexAttOptions(relation, false);

	/*
	 * expressions, predicate, exclusion caches will be filled later
	 */
	relation->rd_indexprs = NIL;
	relation->rd_indexprsExpand = NIL;
	relation->rd_indpred = NIL;
	relation->rd_indpredExpand = NIL;
	relation->rd_exclops = NULL;
	relation->rd_exclprocs = NULL;
	relation->rd_exclstrats = NULL;
	relation->rd_amcache = NULL;
}

/*
 * IndexSupportInitialize
 *		Initializes an index's cached opclass information,
 *		given the index's pg_index.indclass entry.
 *
 * Data is returned into *indexSupport, *opFamily, and *opcInType,
 * which are arrays allocated by the caller.
 *
 * The caller also passes maxSupportNumber and maxAttributeNumber, since these
 * indicate the size of the arrays it has allocated --- but in practice these
 * numbers must always match those obtainable from the system catalog entries
 * for the index and access method.
 */
static void
IndexSupportInitialize(oidvector *indclass,
					   RegProcedure *indexSupport,
					   Oid *opFamily,
					   Oid *opcInType,
					   StrategyNumber maxSupportNumber,
					   AttrNumber maxAttributeNumber)
{
	int			attIndex;

	for (attIndex = 0; attIndex < maxAttributeNumber; attIndex++)
	{
		OpClassCacheEnt *opcentry;

		if (!OidIsValid(indclass->values[attIndex]))
			elog(ERROR, "bogus pg_index tuple");

		/* look up the info for this opclass, using a cache */
		opcentry = LookupOpclassInfo(indclass->values[attIndex],
									 maxSupportNumber);

		/* copy cached data into relcache entry */
		opFamily[attIndex] = opcentry->opcfamily;
		opcInType[attIndex] = opcentry->opcintype;
		if (maxSupportNumber > 0)
			memcpy(&indexSupport[attIndex * maxSupportNumber],
				   opcentry->supportProcs,
				   maxSupportNumber * sizeof(RegProcedure));
	}
}

/*
 * LookupOpclassInfo
 *
 * This routine maintains a per-opclass cache of the information needed
 * by IndexSupportInitialize().  This is more efficient than relying on
 * the catalog cache, because we can load all the info about a particular
 * opclass in a single indexscan of pg_amproc.
 *
 * The information from pg_am about expected range of support function
 * numbers is passed in, rather than being looked up, mainly because the
 * caller will have it already.
 *
 * Note there is no provision for flushing the cache.  This is OK at the
 * moment because there is no way to ALTER any interesting properties of an
 * existing opclass --- all you can do is drop it, which will result in
 * a useless but harmless dead entry in the cache.  To support altering
 * opclass membership (not the same as opfamily membership!), we'd need to
 * be able to flush this cache as well as the contents of relcache entries
 * for indexes.
 */
static OpClassCacheEnt *
LookupOpclassInfo(Oid operatorClassOid,
				  StrategyNumber numSupport)
{
	OpClassCacheEnt *opcentry;
	bool		found;
	Relation	rel;
	SysScanDesc scan;
	ScanKeyData skey[3];
	HeapTuple	htup;
	bool		indexOK;

	if (OpClassCache == NULL)
	{
		/* First time through: initialize the opclass cache */
		HASHCTL		ctl;

		/* Also make sure CacheMemoryContext exists */
		if (!CacheMemoryContext)
			CreateCacheMemoryContext();

		ctl.keysize = sizeof(Oid);
		ctl.entrysize = sizeof(OpClassCacheEnt);
		OpClassCache = hash_create("Operator class cache", 64,
								   &ctl, HASH_ELEM | HASH_BLOBS);
	}

	opcentry = (OpClassCacheEnt *) hash_search(OpClassCache,
											   &operatorClassOid,
											   HASH_ENTER, &found);

	if (!found)
	{
		/* Initialize new entry */
		opcentry->valid = false;	/* until known OK */
		opcentry->numSupport = numSupport;
		opcentry->supportProcs = NULL;	/* filled below */
	}
	else
	{
		Assert(numSupport == opcentry->numSupport);
	}

	/*
	 * When aggressively testing cache-flush hazards, we disable the operator
	 * class cache and force reloading of the info on each call.  This models
	 * no real-world behavior, since the cache entries are never invalidated
	 * otherwise.  However it can be helpful for detecting bugs in the cache
	 * loading logic itself, such as reliance on a non-nailed index.  Given
	 * the limited use-case and the fact that this adds a great deal of
	 * expense, we enable it only for high values of debug_discard_caches.
	 */
#ifdef DISCARD_CACHES_ENABLED
	if (debug_discard_caches > 2)
		opcentry->valid = false;
#endif

	if (opcentry->valid)
		return opcentry;

	/*
	 * Need to fill in new entry.  First allocate space, unless we already did
	 * so in some previous attempt.
	 */
	if (opcentry->supportProcs == NULL && numSupport > 0)
		opcentry->supportProcs = (RegProcedure *)
			MemoryContextAllocZero(CacheMemoryContext,
								   numSupport * sizeof(RegProcedure));

	/*
	 * To avoid infinite recursion during startup, force heap scans if we're
	 * looking up info for the opclasses used by the indexes we would like to
	 * reference here.
	 */
	indexOK = criticalRelcachesBuilt ||
		(operatorClassOid != OID_BTREE_OPS_OID &&
		 operatorClassOid != INT2_BTREE_OPS_OID);

	/*
	 * We have to fetch the pg_opclass row to determine its opfamily and
	 * opcintype, which are needed to look up related operators and functions.
	 * It'd be convenient to use the syscache here, but that probably doesn't
	 * work while bootstrapping.
	 */
	ScanKeyInit(&skey[0],
				Anum_pg_opclass_oid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(operatorClassOid));
	rel = table_open(OperatorClassRelationId, AccessShareLock);
	scan = systable_beginscan(rel, OpclassOidIndexId, indexOK,
							  NULL, 1, skey);

	if (HeapTupleIsValid(htup = systable_getnext(scan)))
	{
		Form_pg_opclass opclassform = (Form_pg_opclass) GETSTRUCT(htup);

		opcentry->opcfamily = opclassform->opcfamily;
		opcentry->opcintype = opclassform->opcintype;
	}
	else
		elog(ERROR, "could not find tuple for opclass %u", operatorClassOid);

	systable_endscan(scan);
	table_close(rel, AccessShareLock);

	/*
	 * Scan pg_amproc to obtain support procs for the opclass.  We only fetch
	 * the default ones (those with lefttype = righttype = opcintype).
	 */
	if (numSupport > 0)
	{
		ScanKeyInit(&skey[0],
					Anum_pg_amproc_amprocfamily,
					BTEqualStrategyNumber, F_OIDEQ,
					ObjectIdGetDatum(opcentry->opcfamily));
		ScanKeyInit(&skey[1],
					Anum_pg_amproc_amproclefttype,
					BTEqualStrategyNumber, F_OIDEQ,
					ObjectIdGetDatum(opcentry->opcintype));
		ScanKeyInit(&skey[2],
					Anum_pg_amproc_amprocrighttype,
					BTEqualStrategyNumber, F_OIDEQ,
					ObjectIdGetDatum(opcentry->opcintype));
		rel = table_open(AccessMethodProcedureRelationId, AccessShareLock);
		scan = systable_beginscan(rel, AccessMethodProcedureIndexId, indexOK,
								  NULL, 3, skey);

		while (HeapTupleIsValid(htup = systable_getnext(scan)))
		{
			Form_pg_amproc amprocform = (Form_pg_amproc) GETSTRUCT(htup);

			if (amprocform->amprocnum <= 0 ||
				(StrategyNumber) amprocform->amprocnum > numSupport)
				elog(ERROR, "invalid amproc number %d for opclass %u",
					 amprocform->amprocnum, operatorClassOid);

			opcentry->supportProcs[amprocform->amprocnum - 1] =
				amprocform->amproc;
		}

		systable_endscan(scan);
		table_close(rel, AccessShareLock);
	}

	opcentry->valid = true;
	return opcentry;
}

/*
 * Fill in the TableAmRoutine for a relation
 *
 * relation's rd_amhandler must be valid already.
 */
static void
InitTableAmRoutine(Relation relation)
{
	relation->rd_tableam = GetTableAmRoutine(relation->rd_amhandler);
}

/*
 * Initialize table access method support for a table like relation
 */
void
RelationInitTableAccessMethod(Relation relation)
{
	HeapTuple	tuple;
	Form_pg_am	aform;

	if (relation->rd_rel->relkind == RELKIND_SEQUENCE)
	{
		/*
		 * Sequences are currently accessed like heap tables, but it doesn't
		 * seem prudent to show that in the catalog. So just overwrite it
		 * here.
		 */
		Assert(relation->rd_rel->relam == InvalidOid);
		relation->rd_amhandler = F_HEAP_TABLEAM_HANDLER;
	}
	else if (IsCatalogRelation(relation))
	{
		/*
		 * Avoid doing a syscache lookup for catalog tables.
		 */
		Assert(relation->rd_rel->relam == HEAP_TABLE_AM_OID);
		relation->rd_amhandler = F_HEAP_TABLEAM_HANDLER;
	}
	else
	{
		/*
		 * Look up the table access method, save the OID of its handler
		 * function.
		 */
		Assert(relation->rd_rel->relam != InvalidOid);
		tuple = SearchSysCache1(AMOID,
								ObjectIdGetDatum(relation->rd_rel->relam));
		if (!HeapTupleIsValid(tuple))
			elog(ERROR, "cache lookup failed for access method %u",
				 relation->rd_rel->relam);
		aform = (Form_pg_am) GETSTRUCT(tuple);
		relation->rd_amhandler = aform->amhandler;
		ReleaseSysCache(tuple);
	}

	/*
	 * Now we can fetch the table AM's API struct
	 */
	InitTableAmRoutine(relation);
}

/*
 *		formrdesc
 *
 *		This is a special cut-down version of RelationBuildDesc(),
 *		used while initializing the relcache.
 *		The relation descriptor is built just from the supplied parameters,
 *		without actually looking at any system table entries.  We cheat
 *		quite a lot since we only need to work for a few basic system
 *		catalogs.
 *
 * The catalogs this is used for can't have constraints (except attnotnull),
 * default values, rules, or triggers, since we don't cope with any of that.
 * (Well, actually, this only matters for properties that need to be valid
 * during bootstrap or before RelationCacheInitializePhase3 runs, and none of
 * these properties matter then...)
 *
 * NOTE: we assume we are already switched into CacheMemoryContext.
 */
static void
formrdesc(const char *relationName, Oid relationReltype,
		  bool isshared,
		  int natts, const FormData_pg_attribute *attrs)
{
	Relation	relation;
	int			i;
	bool		has_not_null;

	/*
	 * allocate new relation desc, clear all fields of reldesc
	 */
	relation = palloc0_object(RelationData);

	/* make sure relation is marked as having no open file yet */
	relation->rd_smgr = NULL;

	/*
	 * initialize reference count: 1 because it is nailed in cache
	 */
	relation->rd_refcnt = 1;

	/*
	 * all entries built with this routine are nailed-in-cache; none are for
	 * new or temp relations.
	 */
	relation->rd_isnailed = true;
	relation->rd_createSubid = InvalidSubTransactionId;
	relation->rd_newRelfilelocatorSubid = InvalidSubTransactionId;
	relation->rd_firstRelfilelocatorSubid = InvalidSubTransactionId;
	relation->rd_droppedSubid = InvalidSubTransactionId;
	relation->rd_backend = INVALID_PROC_NUMBER;
	relation->rd_islocaltemp = false;

	/*
	 * initialize relation tuple form
	 *
	 * The data we insert here is pretty incomplete/bogus, but it'll serve to
	 * get us launched.  RelationCacheInitializePhase3() will read the real
	 * data from pg_class and replace what we've done here.  Note in
	 * particular that relowner is left as zero; this cues
	 * RelationCacheInitializePhase3 that the real data isn't there yet.
	 */
	relation->rd_rel = (Form_pg_class) palloc0(CLASS_TUPLE_SIZE);

	namestrcpy(&relation->rd_rel->relname, relationName);
	relation->rd_rel->relnamespace = PG_CATALOG_NAMESPACE;
	relation->rd_rel->reltype = relationReltype;

	/*
	 * It's important to distinguish between shared and non-shared relations,
	 * even at bootstrap time, to make sure we know where they are stored.
	 */
	relation->rd_rel->relisshared = isshared;
	if (isshared)
		relation->rd_rel->reltablespace = GLOBALTABLESPACE_OID;

	/* formrdesc is used only for permanent relations */
	relation->rd_rel->relpersistence = RELPERSISTENCE_PERMANENT;

	/* ... and they're always populated, too */
	relation->rd_rel->relispopulated = true;

	relation->rd_rel->relreplident = REPLICA_IDENTITY_NOTHING;
	relation->rd_rel->relpages = 0;
	relation->rd_rel->reltuples = -1;
	relation->rd_rel->relallvisible = 0;
	relation->rd_rel->relallfrozen = 0;
	relation->rd_rel->relkind = RELKIND_RELATION;
	relation->rd_rel->relnatts = (int16) natts;

	/*
	 * initialize attribute tuple form
	 *
	 * Unlike the case with the relation tuple, this data had better be right
	 * because it will never be replaced.  The data comes from
	 * src/include/catalog/ headers via genbki.pl.
	 */
	relation->rd_att = CreateTemplateTupleDesc(natts);
	relation->rd_att->tdrefcount = 1;	/* mark as refcounted */

	relation->rd_att->tdtypeid = relationReltype;
	relation->rd_att->tdtypmod = -1;	/* just to be sure */

	/*
	 * initialize tuple desc info
	 */
	has_not_null = false;
	for (i = 0; i < natts; i++)
	{
		memcpy(TupleDescAttr(relation->rd_att, i),
			   &attrs[i],
			   ATTRIBUTE_FIXED_PART_SIZE);
		has_not_null |= attrs[i].attnotnull;

		populate_compact_attribute(relation->rd_att, i);
	}

	TupleDescFinalize(relation->rd_att);

	/* mark not-null status */
	if (has_not_null)
	{
		TupleConstr *constr = palloc0_object(TupleConstr);

		constr->has_not_null = true;
		relation->rd_att->constr = constr;
	}

	/*
	 * initialize relation id from info in att array (my, this is ugly)
	 */
	RelationGetRelid(relation) = TupleDescAttr(relation->rd_att, 0)->attrelid;

	/*
	 * All relations made with formrdesc are mapped.  This is necessarily so
	 * because there is no other way to know what filenumber they currently
	 * have.  In bootstrap mode, add them to the initial relation mapper data,
	 * specifying that the initial filenumber is the same as the OID.
	 */
	relation->rd_rel->relfilenode = InvalidRelFileNumber;
	if (IsBootstrapProcessingMode())
		RelationMapUpdateMap(RelationGetRelid(relation),
							 RelationGetRelid(relation),
							 isshared, true);

	/*
	 * initialize the relation lock manager information
	 */
	RelationInitLockInfo(relation); /* see lmgr.c */

	/*
	 * initialize physical addressing information for the relation
	 */
	RelationInitPhysicalAddr(relation);

	/*
	 * initialize the table am handler
	 */
	relation->rd_rel->relam = HEAP_TABLE_AM_OID;
	relation->rd_tableam = GetHeapamTableAmRoutine();

	/*
	 * initialize the rel-has-index flag, using hardwired knowledge
	 */
	if (IsBootstrapProcessingMode())
	{
		/* In bootstrap mode, we have no indexes */
		relation->rd_rel->relhasindex = false;
	}
	else
	{
		/* Otherwise, all the rels formrdesc is used for have indexes */
		relation->rd_rel->relhasindex = true;
	}

	/*
	 * add new reldesc to relcache
	 */
	RelationCacheInsert(relation, false);

	/* It's fully valid */
	relation->rd_isvalid = true;
}

#ifdef USE_ASSERT_CHECKING
/*
 *		AssertCouldGetRelation
 *
 *		Check safety of calling RelationIdGetRelation().
 *
 *		In code that reads catalogs in the event of a cache miss, call this
 *		before checking the cache.
 */
void
AssertCouldGetRelation(void)
{
	Assert(IsTransactionState());
	AssertBufferLocksPermitCatalogRead();
}
#endif


/* ----------------------------------------------------------------
 *				 Relation Descriptor Lookup Interface
 * ----------------------------------------------------------------
 */

/*
 *		RelationIdGetRelation
 *
 *		Lookup a reldesc by OID; make one if not already in cache.
 *
 *		Returns NULL if no pg_class row could be found for the given relid
 *		(suggesting we are trying to access a just-deleted relation).
 *		Any other error is reported via elog.
 *
 *		NB: caller should already have at least AccessShareLock on the
 *		relation ID, else there are nasty race conditions.
 *
 *		NB: relation ref count is incremented, or set to 1 if new entry.
 *		Caller should eventually decrement count.  (Usually,
 *		that happens by calling RelationClose().)
 */
Relation
RelationIdGetRelation(Oid relationId)
{
	Relation	rd;

	AssertCouldGetRelation();

	/*
	 * first try to find reldesc in the cache
	 */
	RelationIdCacheLookup(relationId, rd);

	if (RelationIsValid(rd))
	{
		/* return NULL for dropped relations */
		if (rd->rd_droppedSubid != InvalidSubTransactionId)
		{
			Assert(!rd->rd_isvalid);
			return NULL;
		}

		RelationIncrementReferenceCount(rd);
		/* revalidate cache entry if necessary */
		if (!rd->rd_isvalid)
		{
			RelationRebuildRelation(rd);

			/*
			 * Normally entries need to be valid here, but before the relcache
			 * has been initialized, not enough infrastructure exists to
			 * perform pg_class lookups. The structure of such entries doesn't
			 * change, but we still want to update the rd_rel entry. So
			 * rd_isvalid = false is left in place for a later lookup.
			 */
			Assert(rd->rd_isvalid ||
				   (rd->rd_isnailed && !criticalRelcachesBuilt));
		}
		return rd;
	}

	/*
	 * no reldesc in the cache, so have RelationBuildDesc() build one and add
	 * it.
	 */
	rd = RelationBuildDesc(relationId, true);
	if (RelationIsValid(rd))
		RelationIncrementReferenceCount(rd);
	return rd;
}

/*
 * Returns a schema-qualified name of the relation.
 */
char *
RelationGetQualifiedRelationName(Relation rel)
{
	return get_qualified_objname(RelationGetNamespace(rel),
								 RelationGetRelationName(rel));
}

/* ----------------------------------------------------------------
 *				cache invalidation support routines
 * ----------------------------------------------------------------
 */

/* ResourceOwner callbacks to track relcache references */
static void ResOwnerReleaseRelation(Datum res);
static char *ResOwnerPrintRelCache(Datum res);

static const ResourceOwnerDesc relref_resowner_desc =
{
	.name = "relcache reference",
	.release_phase = RESOURCE_RELEASE_BEFORE_LOCKS,
	.release_priority = RELEASE_PRIO_RELCACHE_REFS,
	.ReleaseResource = ResOwnerReleaseRelation,
	.DebugPrint = ResOwnerPrintRelCache
};

/* Convenience wrappers over ResourceOwnerRemember/Forget */
static inline void
ResourceOwnerRememberRelationRef(ResourceOwner owner, Relation rel)
{
	ResourceOwnerRemember(owner, PointerGetDatum(rel), &relref_resowner_desc);
}
static inline void
ResourceOwnerForgetRelationRef(ResourceOwner owner, Relation rel)
{
	ResourceOwnerForget(owner, PointerGetDatum(rel), &relref_resowner_desc);
}

/*
 * RelationIncrementReferenceCount
 *		Increments relation reference count.
 *
 * Note: bootstrap mode has its own weird ideas about relation refcount
 * behavior; we ought to fix it someday, but for now, just disable
 * reference count ownership tracking in bootstrap mode.
 */
void
RelationIncrementReferenceCount(Relation rel)
{
	ResourceOwnerEnlarge(CurrentResourceOwner);
	rel->rd_refcnt += 1;
	if (!IsBootstrapProcessingMode())
		ResourceOwnerRememberRelationRef(CurrentResourceOwner, rel);
}

/*
 * RelationDecrementReferenceCount
 *		Decrements relation reference count.
 */
void
RelationDecrementReferenceCount(Relation rel)
{
	Assert(rel->rd_refcnt > 0);
	rel->rd_refcnt -= 1;
	if (!IsBootstrapProcessingMode())
		ResourceOwnerForgetRelationRef(CurrentResourceOwner, rel);
}

/*
 * RelationClose - close an open relation
 *
 *	Actually, we just decrement the refcount.
 *
 *	NOTE: if compiled with -DRELCACHE_FORCE_RELEASE then relcache entries
 *	will be freed as soon as their refcount goes to zero.  In combination
 *	with aset.c's CLOBBER_FREED_MEMORY option, this provides a good test
 *	to catch references to already-released relcache entries.  It slows
 *	things down quite a bit, however.
 */
void
RelationClose(Relation relation)
{
	/* Note: no locking manipulations needed */
	RelationDecrementReferenceCount(relation);

	RelationCloseCleanup(relation);
}

static void
RelationCloseCleanup(Relation relation)
{
	/*
	 * If the relation is no longer open in this session, we can clean up any
	 * stale partition descriptors it has.  This is unlikely, so check to see
	 * if there are child contexts before expending a call to mcxt.c.
	 */
	if (RelationHasReferenceCountZero(relation))
	{
		if (relation->rd_pdcxt != NULL &&
			relation->rd_pdcxt->firstchild != NULL)
			MemoryContextDeleteChildren(relation->rd_pdcxt);

		if (relation->rd_pddcxt != NULL &&
			relation->rd_pddcxt->firstchild != NULL)
			MemoryContextDeleteChildren(relation->rd_pddcxt);
	}

#ifdef RELCACHE_FORCE_RELEASE
	if (RelationHasReferenceCountZero(relation) &&
		relation->rd_createSubid == InvalidSubTransactionId &&
		relation->rd_firstRelfilelocatorSubid == InvalidSubTransactionId)
		RelationClearRelation(relation);
#endif
}

/*
 * RelationReloadIndexInfo - reload minimal information for an open index
 *
 *	This function is used only for indexes.  A relcache inval on an index
 *	can mean that its pg_class or pg_index row changed.  There are only
 *	very limited changes that are allowed to an existing index's schema,
 *	so we can update the relcache entry without a complete rebuild; which
 *	is fortunate because we can't rebuild an index entry that is "nailed"
 *	and/or in active use.  We support full replacement of the pg_class row,
 *	as well as updates of a few simple fields of the pg_index row.
 *
 *	We assume that at the time we are called, we have at least AccessShareLock
 *	on the target index.
 *
 *	If the target index is an index on pg_class or pg_index, we'd better have
 *	previously gotten at least AccessShareLock on its underlying catalog,
 *	else we are at risk of deadlock against someone trying to exclusive-lock
 *	the heap and index in that order.  This is ensured in current usage by
 *	only applying this to indexes being opened or having positive refcount.
 */
static void
RelationReloadIndexInfo(Relation relation)
{
	bool		indexOK;
	HeapTuple	pg_class_tuple;
	Form_pg_class relp;

	/* Should be called only for invalidated, live indexes */
	Assert((relation->rd_rel->relkind == RELKIND_INDEX ||
			relation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX) &&
		   !relation->rd_isvalid &&
		   relation->rd_droppedSubid == InvalidSubTransactionId);

	/*
	 * If it's a shared index, we might be called before backend startup has
	 * finished selecting a database, in which case we have no way to read
	 * pg_class yet.  However, a shared index can never have any significant
	 * schema updates, so it's okay to mostly ignore the invalidation signal.
	 * Its physical relfilenumber might've changed, but that's all.  Update
	 * the physical relfilenumber, mark it valid and return without doing
	 * anything more.
	 */
	if (relation->rd_rel->relisshared && !criticalRelcachesBuilt)
	{
		RelationInitPhysicalAddr(relation);
		relation->rd_isvalid = true;
		return;
	}

	/*
	 * Read the pg_class row
	 *
	 * Don't try to use an indexscan of pg_class_oid_index to reload the info
	 * for pg_class_oid_index ...
	 */
	indexOK = (RelationGetRelid(relation) != ClassOidIndexId);
	pg_class_tuple = ScanPgRelation(RelationGetRelid(relation), indexOK, false);
	if (!HeapTupleIsValid(pg_class_tuple))
		elog(ERROR, "could not find pg_class tuple for index %u",
			 RelationGetRelid(relation));
	relp = (Form_pg_class) GETSTRUCT(pg_class_tuple);
	memcpy(relation->rd_rel, relp, CLASS_TUPLE_SIZE);
	/* Reload reloptions in case they changed */
	if (relation->rd_options)
		pfree(relation->rd_options);
	RelationParseRelOptions(relation, pg_class_tuple);
	/* done with pg_class tuple */
	heap_freetuple(pg_class_tuple);
	/* We must recalculate physical address in case it changed */
	RelationInitPhysicalAddr(relation);

	/*
	 * For a non-system index, there are fields of the pg_index row that are
	 * allowed to change, so re-read that row and update the relcache entry.
	 * Most of the info derived from pg_index (such as support function lookup
	 * info) cannot change, and indeed the whole point of this routine is to
	 * update the relcache entry without clobbering that data; so wholesale
	 * replacement is not appropriate.
	 */
	if (!IsSystemRelation(relation))
	{
		HeapTuple	tuple;
		Form_pg_index index;

		tuple = SearchSysCache1(INDEXRELID,
								ObjectIdGetDatum(RelationGetRelid(relation)));
		if (!HeapTupleIsValid(tuple))
			elog(ERROR, "cache lookup failed for index %u",
				 RelationGetRelid(relation));
		index = (Form_pg_index) GETSTRUCT(tuple);

		/*
		 * Basically, let's just copy all the bool fields.  There are one or
		 * two of these that can't actually change in the current code, but
		 * it's not worth it to track exactly which ones they are.  None of
		 * the array fields are allowed to change, though.
		 */
		relation->rd_index->indisunique = index->indisunique;
		relation->rd_index->indnullsnotdistinct = index->indnullsnotdistinct;
		relation->rd_index->indisprimary = index->indisprimary;
		relation->rd_index->indisexclusion = index->indisexclusion;
		relation->rd_index->indimmediate = index->indimmediate;
		relation->rd_index->indisclustered = index->indisclustered;
		relation->rd_index->indisvalid = index->indisvalid;
		relation->rd_index->indcheckxmin = index->indcheckxmin;
		relation->rd_index->indisready = index->indisready;
		relation->rd_index->indislive = index->indislive;
		relation->rd_index->indisreplident = index->indisreplident;

		/* Copy xmin too, as that is needed to make sense of indcheckxmin */
		HeapTupleHeaderSetXmin(relation->rd_indextuple->t_data,
							   HeapTupleHeaderGetXmin(tuple->t_data));

		ReleaseSysCache(tuple);
	}

	/* Okay, now it's valid again */
	relation->rd_isvalid = true;
}

/*
 * RelationReloadNailed - reload minimal information for nailed relations.
 *
 * The structure of a nailed relation can never change (which is good, because
 * we rely on knowing their structure to be able to read catalog content). But
 * some parts, e.g. pg_class.relfrozenxid, are still important to have
 * accurate content for. Therefore those need to be reloaded after the arrival
 * of invalidations.
 */
static void
RelationReloadNailed(Relation relation)
{
	/* Should be called only for invalidated, nailed relations */
	Assert(!relation->rd_isvalid);
	Assert(relation->rd_isnailed);
	/* nailed indexes are handled by RelationReloadIndexInfo() */
	Assert(relation->rd_rel->relkind == RELKIND_RELATION);
	AssertCouldGetRelation();

	/*
	 * Redo RelationInitPhysicalAddr in case it is a mapped relation whose
	 * mapping changed.
	 */
	RelationInitPhysicalAddr(relation);

	/*
	 * Reload a non-index entry.  We can't easily do so if relcaches aren't
	 * yet built, but that's fine because at that stage the attributes that
	 * need to be current (like relfrozenxid) aren't yet accessed.  To ensure
	 * the entry will later be revalidated, we leave it in invalid state, but
	 * allow use (cf. RelationIdGetRelation()).
	 */
	if (criticalRelcachesBuilt)
	{
		HeapTuple	pg_class_tuple;
		Form_pg_class relp;

		/*
		 * NB: Mark the entry as valid before starting to scan, to avoid
		 * self-recursion when re-building pg_class.
		 */
		relation->rd_isvalid = true;

		pg_class_tuple = ScanPgRelation(RelationGetRelid(relation),
										true, false);
		relp = (Form_pg_class) GETSTRUCT(pg_class_tuple);
		memcpy(relation->rd_rel, relp, CLASS_TUPLE_SIZE);
		heap_freetuple(pg_class_tuple);

		/*
		 * Again mark as valid, to protect against concurrently arriving
		 * invalidations.
		 */
		relation->rd_isvalid = true;
	}
}

/*
 * RelationDestroyRelation
 *
 *	Physically delete a relation cache entry and all subsidiary data.
 *	Caller must already have unhooked the entry from the hash table.
 */
static void
RelationDestroyRelation(Relation relation, bool remember_tupdesc)
{
	Assert(RelationHasReferenceCountZero(relation));

	/*
	 * Make sure smgr and lower levels close the relation's files, if they
	 * weren't closed already.  (This was probably done by caller, but let's
	 * just be real sure.)
	 */
	RelationCloseSmgr(relation);

	/* break mutual link with stats entry */
	pgstat_unlink_relation(relation);

	/*
	 * Free all the subsidiary data structures of the relcache entry, then the
	 * entry itself.
	 */
	if (relation->rd_rel)
		pfree(relation->rd_rel);
	/* can't use DecrTupleDescRefCount here */
	Assert(relation->rd_att->tdrefcount > 0);
	if (--relation->rd_att->tdrefcount == 0)
	{
		/*
		 * If we Rebuilt a relcache entry during a transaction then its
		 * possible we did that because the TupDesc changed as the result of
		 * an ALTER TABLE that ran at less than AccessExclusiveLock. It's
		 * possible someone copied that TupDesc, in which case the copy would
		 * point to free'd memory. So if we rebuild an entry we keep the
		 * TupDesc around until end of transaction, to be safe.
		 */
		if (remember_tupdesc)
			RememberToFreeTupleDescAtEOX(relation->rd_att);
		else
			FreeTupleDesc(relation->rd_att);
	}
	FreeTriggerDesc(relation->trigdesc);
	list_free_deep(relation->rd_fkeylist);
	list_free(relation->rd_indexlist);
	list_free(relation->rd_statlist);
	bms_free(relation->rd_keyattr);
	bms_free(relation->rd_pkattr);
	bms_free(relation->rd_idattr);
	bms_free(relation->rd_hotblockingattr);
	bms_free(relation->rd_summarizedattr);
	if (relation->rd_pubdesc)
		pfree(relation->rd_pubdesc);
	if (relation->rd_options)
		pfree(relation->rd_options);
	if (relation->rd_indextuple)
		pfree(relation->rd_indextuple);
	if (relation->rd_amcache)
		pfree(relation->rd_amcache);
	if (relation->rd_fdwroutine)
		pfree(relation->rd_fdwroutine);
	if (relation->rd_indexcxt)
		MemoryContextDelete(relation->rd_indexcxt);
	if (relation->rd_rulescxt)
		MemoryContextDelete(relation->rd_rulescxt);
	if (relation->rd_rsdesc)
		MemoryContextDelete(relation->rd_rsdesc->rscxt);
	if (relation->rd_partkeycxt)
		MemoryContextDelete(relation->rd_partkeycxt);
	if (relation->rd_pdcxt)
		MemoryContextDelete(relation->rd_pdcxt);
	if (relation->rd_pddcxt)
		MemoryContextDelete(relation->rd_pddcxt);
	if (relation->rd_partcheckcxt)
		MemoryContextDelete(relation->rd_partcheckcxt);
	pfree(relation);
}

/*
 * RelationInvalidateRelation - mark a relation cache entry as invalid
 *
 * An entry that's marked as invalid will be reloaded on next access.
 */
static void
RelationInvalidateRelation(Relation relation)
{
	/*
	 * Make sure smgr and lower levels close the relation's files, if they
	 * weren't closed already.  If the relation is not getting deleted, the
	 * next smgr access should reopen the files automatically.  This ensures
	 * that the low-level file access state is updated after, say, a vacuum
	 * truncation.
	 */
	RelationCloseSmgr(relation);

	/* Free AM cached data, if any */
	if (relation->rd_amcache)
		pfree(relation->rd_amcache);
	relation->rd_amcache = NULL;

	relation->rd_isvalid = false;
}

/*
 * RelationClearRelation - physically blow away a relation cache entry
 *
 * The caller must ensure that the entry is no longer needed, i.e. its
 * reference count is zero.  Also, the rel or its storage must not be created
 * in the current transaction (rd_createSubid and rd_firstRelfilelocatorSubid
 * must not be set).
 */
static void
RelationClearRelation(Relation relation)
{
	Assert(RelationHasReferenceCountZero(relation));
	Assert(!relation->rd_isnailed);

	/*
	 * Relations created in the same transaction must never be removed, see
	 * RelationFlushRelation.
	 */
	Assert(relation->rd_createSubid == InvalidSubTransactionId);
	Assert(relation->rd_firstRelfilelocatorSubid == InvalidSubTransactionId);
	Assert(relation->rd_droppedSubid == InvalidSubTransactionId);

	/* first mark it as invalid */
	RelationInvalidateRelation(relation);

	/* Remove it from the hash table */
	RelationCacheDelete(relation);

	/* And release storage */
	RelationDestroyRelation(relation, false);
}

/*
 * RelationRebuildRelation - rebuild a relation cache entry in place
 *
 * Reset and rebuild a relation cache entry from scratch (that is, from
 * catalog entries).  This is used when we are notified of a change to an open
 * relation (one with refcount > 0).  The entry is reconstructed without
 * moving the physical RelationData record, so that the refcount holder's
 * pointer is still valid.
 *
 * NB: when rebuilding, we'd better hold some lock on the relation, else the
 * catalog data we need to read could be changing under us.  Also, a rel to be
 * rebuilt had better have refcnt > 0.  This is because a sinval reset could
 * happen while we're accessing the catalogs, and the rel would get blown away
 * underneath us by RelationCacheInvalidate if it has zero refcnt.
 */
static void
RelationRebuildRelation(Relation relation)
{
	Assert(!RelationHasReferenceCountZero(relation));
	AssertCouldGetRelation();
	/* there is no reason to ever rebuild a dropped relation */
	Assert(relation->rd_droppedSubid == InvalidSubTransactionId);

	/* Close and mark it as invalid until we've finished the rebuild */
	RelationInvalidateRelation(relation);

	/*
	 * Indexes only have a limited number of possible schema changes, and we
	 * don't want to use the full-blown procedure because it's a headache for
	 * indexes that reload itself depends on.
	 *
	 * As an exception, use the full procedure if the index access info hasn't
	 * been initialized yet.  Index creation relies on that: it first builds
	 * the relcache entry with RelationBuildLocalRelation(), creates the
	 * pg_index tuple only after that, and then relies on
	 * CommandCounterIncrement to load the pg_index contents.
	 */
	if ((relation->rd_rel->relkind == RELKIND_INDEX ||
		 relation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX) &&
		relation->rd_indexcxt != NULL)
	{
		RelationReloadIndexInfo(relation);
		return;
	}
	/* Nailed relations are handled separately. */
	else if (relation->rd_isnailed)
	{
		RelationReloadNailed(relation);
		return;
	}
	else
	{
		/*
		 * Our strategy for rebuilding an open relcache entry is to build a
		 * new entry from scratch, swap its contents with the old entry, and
		 * finally delete the new entry (along with any infrastructure swapped
		 * over from the old entry).  This is to avoid trouble in case an
		 * error causes us to lose control partway through.  The old entry
		 * will still be marked !rd_isvalid, so we'll try to rebuild it again
		 * on next access.  Meanwhile it's not any less valid than it was
		 * before, so any code that might expect to continue accessing it
		 * isn't hurt by the rebuild failure.  (Consider for example a
		 * subtransaction that ALTERs a table and then gets canceled partway
		 * through the cache entry rebuild.  The outer transaction should
		 * still see the not-modified cache entry as valid.)  The worst
		 * consequence of an error is leaking the necessarily-unreferenced new
		 * entry, and this shouldn't happen often enough for that to be a big
		 * problem.
		 *
		 * When rebuilding an open relcache entry, we must preserve ref count,
		 * rd_*Subid, and rd_toastoid state.  Also attempt to preserve the
		 * pg_class entry (rd_rel), tupledesc, rewrite-rule, partition key,
		 * and partition descriptor substructures in place, because various
		 * places assume that these structures won't move while they are
		 * working with an open relcache entry.  (Note:  the refcount
		 * mechanism for tupledescs might someday allow us to remove this hack
		 * for the tupledesc.)
		 *
		 * Note that this process does not touch CurrentResourceOwner; which
		 * is good because whatever ref counts the entry may have do not
		 * necessarily belong to that resource owner.
		 */
		Relation	newrel;
		Oid			save_relid = RelationGetRelid(relation);
		bool		keep_tupdesc;
		bool		keep_rules;
		bool		keep_policies;
		bool		keep_partkey;

		/* Build temporary entry, but don't link it into hashtable */
		newrel = RelationBuildDesc(save_relid, false);

		/*
		 * Between here and the end of the swap, don't add code that does or
		 * reasonably could read system catalogs.  That range must be free
		 * from invalidation processing.  See RelationBuildDesc() manipulation
		 * of in_progress_list.
		 */

		if (newrel == NULL)
		{
			/*
			 * We can validly get here, if we're using a historic snapshot in
			 * which a relation, accessed from outside logical decoding, is
			 * still invisible. In that case it's fine to just mark the
			 * relation as invalid and return - it'll fully get reloaded by
			 * the cache reset at the end of logical decoding (or at the next
			 * access).  During normal processing we don't want to ignore this
			 * case as it shouldn't happen there, as explained below.
			 */
			if (HistoricSnapshotActive())
				return;

			/*
			 * This shouldn't happen as dropping a relation is intended to be
			 * impossible if still referenced (cf. CheckTableNotInUse()). But
			 * if we get here anyway, we can't just delete the relcache entry,
			 * as it possibly could get accessed later (as e.g. the error
			 * might get trapped and handled via a subtransaction rollback).
			 */
			elog(ERROR, "relation %u deleted while still in use", save_relid);
		}

		/*
		 * If we were to, again, have cases of the relkind of a relcache entry
		 * changing, we would need to ensure that pgstats does not get
		 * confused.
		 */
		Assert(relation->rd_rel->relkind == newrel->rd_rel->relkind);

		keep_tupdesc = equalTupleDescs(relation->rd_att, newrel->rd_att);
		keep_rules = equalRuleLocks(relation->rd_rules, newrel->rd_rules);
		keep_policies = equalRSDesc(relation->rd_rsdesc, newrel->rd_rsdesc);
		/* partkey is immutable once set up, so we can always keep it */
		keep_partkey = (relation->rd_partkey != NULL);

		/*
		 * Perform swapping of the relcache entry contents.  Within this
		 * process the old entry is momentarily invalid, so there *must* be no
		 * possibility of CHECK_FOR_INTERRUPTS within this sequence. Do it in
		 * all-in-line code for safety.
		 *
		 * Since the vast majority of fields should be swapped, our method is
		 * to swap the whole structures and then re-swap those few fields we
		 * didn't want swapped.
		 */
#define SWAPFIELD(fldtype, fldname) \
		do { \
			fldtype _tmp = newrel->fldname; \
			newrel->fldname = relation->fldname; \
			relation->fldname = _tmp; \
		} while (0)

		/* swap all Relation struct fields */
		{
			RelationData tmpstruct;

			memcpy(&tmpstruct, newrel, sizeof(RelationData));
			memcpy(newrel, relation, sizeof(RelationData));
			memcpy(relation, &tmpstruct, sizeof(RelationData));
		}

		/* rd_smgr must not be swapped, due to back-links from smgr level */
		SWAPFIELD(SMgrRelation, rd_smgr);
		/* rd_refcnt must be preserved */
		SWAPFIELD(int, rd_refcnt);
		/* isnailed shouldn't change */
		Assert(newrel->rd_isnailed == relation->rd_isnailed);
		/* creation sub-XIDs must be preserved */
		SWAPFIELD(SubTransactionId, rd_createSubid);
		SWAPFIELD(SubTransactionId, rd_newRelfilelocatorSubid);
		SWAPFIELD(SubTransactionId, rd_firstRelfilelocatorSubid);
		SWAPFIELD(SubTransactionId, rd_droppedSubid);
		/* un-swap rd_rel pointers, swap contents instead */
		SWAPFIELD(Form_pg_class, rd_rel);
		/* ... but actually, we don't have to update newrel->rd_rel */
		memcpy(relation->rd_rel, newrel->rd_rel, CLASS_TUPLE_SIZE);
		/* preserve old tupledesc, rules, policies if no logical change */
		if (keep_tupdesc)
			SWAPFIELD(TupleDesc, rd_att);
		if (keep_rules)
		{
			SWAPFIELD(RuleLock *, rd_rules);
			SWAPFIELD(MemoryContext, rd_rulescxt);
		}
		if (keep_policies)
			SWAPFIELD(RowSecurityDesc *, rd_rsdesc);
		/* toast OID override must be preserved */
		SWAPFIELD(Oid, rd_toastoid);
		/* pgstat_info / enabled must be preserved */
		SWAPFIELD(struct PgStat_TableStatus *, pgstat_info);
		SWAPFIELD(bool, pgstat_enabled);
		/* preserve old partition key if we have one */
		if (keep_partkey)
		{
			SWAPFIELD(PartitionKey, rd_partkey);
			SWAPFIELD(MemoryContext, rd_partkeycxt);
		}
		if (newrel->rd_pdcxt != NULL || newrel->rd_pddcxt != NULL)
		{
			/*
			 * We are rebuilding a partitioned relation with a non-zero
			 * reference count, so we must keep the old partition descriptor
			 * around, in case there's a PartitionDirectory with a pointer to
			 * it.  This means we can't free the old rd_pdcxt yet.  (This is
			 * necessary because RelationGetPartitionDesc hands out direct
			 * pointers to the relcache's data structure, unlike our usual
			 * practice which is to hand out copies.  We'd have the same
			 * problem with rd_partkey, except that we always preserve that
			 * once created.)
			 *
			 * To ensure that it's not leaked completely, re-attach it to the
			 * new reldesc, or make it a child of the new reldesc's rd_pdcxt
			 * in the unlikely event that there is one already.  (Compare hack
			 * in RelationBuildPartitionDesc.)  RelationClose will clean up
			 * any such contexts once the reference count reaches zero.
			 *
			 * In the case where the reference count is zero, this code is not
			 * reached, which should be OK because in that case there should
			 * be no PartitionDirectory with a pointer to the old entry.
			 *
			 * Note that newrel and relation have already been swapped, so the
			 * "old" partition descriptor is actually the one hanging off of
			 * newrel.
			 */
			relation->rd_partdesc = NULL;	/* ensure rd_partdesc is invalid */
			relation->rd_partdesc_nodetached = NULL;
			relation->rd_partdesc_nodetached_xmin = InvalidTransactionId;
			if (relation->rd_pdcxt != NULL) /* probably never happens */
				MemoryContextSetParent(newrel->rd_pdcxt, relation->rd_pdcxt);
			else
				relation->rd_pdcxt = newrel->rd_pdcxt;
			if (relation->rd_pddcxt != NULL)
				MemoryContextSetParent(newrel->rd_pddcxt, relation->rd_pddcxt);
			else
				relation->rd_pddcxt = newrel->rd_pddcxt;
			/* drop newrel's pointers so we don't destroy it below */
			newrel->rd_partdesc = NULL;
			newrel->rd_partdesc_nodetached = NULL;
			newrel->rd_partdesc_nodetached_xmin = InvalidTransactionId;
			newrel->rd_pdcxt = NULL;
			newrel->rd_pddcxt = NULL;
		}

#undef SWAPFIELD

		/* And now we can throw away the temporary entry */
		RelationDestroyRelation(newrel, !keep_tupdesc);
	}
}

/*
 * RelationFlushRelation
 *
 *	 Rebuild the relation if it is open (refcount > 0), else blow it away.
 *	 This is used when we receive a cache invalidation event for the rel.
 */
static void
RelationFlushRelation(Relation relation)
{
	if (relation->rd_createSubid != InvalidSubTransactionId ||
		relation->rd_firstRelfilelocatorSubid != InvalidSubTransactionId)
	{
		/*
		 * New relcache entries are always rebuilt, not flushed; else we'd
		 * forget the "new" status of the relation.  Ditto for the
		 * new-relfilenumber status.
		 */
		if (IsTransactionState() && relation->rd_droppedSubid == InvalidSubTransactionId)
		{
			/*
			 * The rel could have zero refcnt here, so temporarily increment
			 * the refcnt to ensure it's safe to rebuild it.  We can assume
			 * that the current transaction has some lock on the rel already.
			 */
			RelationIncrementReferenceCount(relation);
			RelationRebuildRelation(relation);
			RelationDecrementReferenceCount(relation);
		}
		else
			RelationInvalidateRelation(relation);
	}
	else
	{
		/*
		 * Pre-existing rels can be dropped from the relcache if not open.
		 *
		 * If the entry is in use, rebuild it if possible.  If we're not
		 * inside a valid transaction, we can't do any catalog access so it's
		 * not possible to rebuild yet.  Just mark it as invalid in that case,
		 * so that the rebuild will occur when the entry is next opened.
		 *
		 * Note: it's possible that we come here during subtransaction abort,
		 * and the reason for wanting to rebuild is that the rel is open in
		 * the outer transaction.  In that case it might seem unsafe to not
		 * rebuild immediately, since whatever code has the rel already open
		 * will keep on using the relcache entry as-is.  However, in such a
		 * case the outer transaction should be holding a lock that's
		 * sufficient to prevent any significant change in the rel's schema,
		 * so the existing entry contents should be good enough for its
		 * purposes; at worst we might be behind on statistics updates or the
		 * like.  (See also CheckTableNotInUse() and its callers.)
		 */
		if (RelationHasReferenceCountZero(relation))
			RelationClearRelation(relation);
		else if (!IsTransactionState())
			RelationInvalidateRelation(relation);
		else if (relation->rd_isnailed && relation->rd_refcnt == 1)
		{
			/*
			 * A nailed relation with refcnt == 1 is unused.  We cannot clear
			 * it, but there's also no need no need to rebuild it immediately.
			 */
			RelationInvalidateRelation(relation);
		}
		else
			RelationRebuildRelation(relation);
	}
}

/*
 * RelationForgetRelation - caller reports that it dropped the relation
 */
void
RelationForgetRelation(Oid rid)
{
	Relation	relation;

	RelationIdCacheLookup(rid, relation);

	if (!relation)
		return;					/* not in cache, nothing to do */

	if (!RelationHasReferenceCountZero(relation))
		elog(ERROR, "relation %u is still open", rid);

	Assert(relation->rd_droppedSubid == InvalidSubTransactionId);
	if (relation->rd_createSubid != InvalidSubTransactionId ||
		relation->rd_firstRelfilelocatorSubid != InvalidSubTransactionId)
	{
		/*
		 * In the event of subtransaction rollback, we must not forget
		 * rd_*Subid.  Mark the entry "dropped" and invalidate it, instead of
		 * destroying it right away.  (If we're in a top transaction, we could
		 * opt to destroy the entry.)
		 */
		relation->rd_droppedSubid = GetCurrentSubTransactionId();
		RelationInvalidateRelation(relation);
	}
	else
		RelationClearRelation(relation);
}

/*
 *		RelationCacheInvalidateEntry
 *
 *		This routine is invoked for SI cache flush messages.
 *
 * Any relcache entry matching the relid must be flushed.  (Note: caller has
 * already determined that the relid belongs to our database or is a shared
 * relation.)
 *
 * We used to skip local relations, on the grounds that they could
 * not be targets of cross-backend SI update messages; but it seems
 * safer to process them, so that our *own* SI update messages will
 * have the same effects during CommandCounterIncrement for both
 * local and nonlocal relations.
 */
void
RelationCacheInvalidateEntry(Oid relationId)
{
	Relation	relation;

	RelationIdCacheLookup(relationId, relation);

	if (relation)
	{
		relcacheInvalsReceived++;
		RelationFlushRelation(relation);
	}
	else
	{
		int			i;

		for (i = 0; i < in_progress_list_len; i++)
			if (in_progress_list[i].reloid == relationId)
				in_progress_list[i].invalidated = true;
	}
}

/*
 * RelationCacheInvalidate
 *	 Blow away cached relation descriptors that have zero reference counts,
 *	 and rebuild those with positive reference counts.  Also reset the smgr
 *	 relation cache and re-read relation mapping data.
 *
 *	 Apart from debug_discard_caches, this is currently used only to recover
 *	 from SI message buffer overflow, so we do not touch relations having
 *	 new-in-transaction relfilenumbers; they cannot be targets of cross-backend
 *	 SI updates (and our own updates now go through a separate linked list
 *	 that isn't limited by the SI message buffer size).
 *
 *	 We do this in two phases: the first pass deletes deletable items, and
 *	 the second one rebuilds the rebuildable items.  This is essential for
 *	 safety, because hash_seq_search only copes with concurrent deletion of
 *	 the element it is currently visiting.  If a second SI overflow were to
 *	 occur while we are walking the table, resulting in recursive entry to
 *	 this routine, we could crash because the inner invocation blows away
 *	 the entry next to be visited by the outer scan.  But this way is OK,
 *	 because (a) during the first pass we won't process any more SI messages,
 *	 so hash_seq_search will complete safely; (b) during the second pass we
 *	 only hold onto pointers to nondeletable entries.
 *
 *	 The two-phase approach also makes it easy to update relfilenumbers for
 *	 mapped relations before we do anything else, and to ensure that the
 *	 second pass processes nailed-in-cache items before other nondeletable
 *	 items.  This should ensure that system catalogs are up to date before
 *	 we attempt to use them to reload information about other open relations.
 *
 *	 After those two phases of work having immediate effects, we normally
 *	 signal any RelationBuildDesc() on the stack to start over.  However, we
 *	 don't do this if called as part of debug_discard_caches.  Otherwise,
 *	 RelationBuildDesc() would become an infinite loop.
 */
void
RelationCacheInvalidate(bool debug_discard)
{
	HASH_SEQ_STATUS status;
	RelIdCacheEnt *idhentry;
	Relation	relation;
	List	   *rebuildFirstList = NIL;
	List	   *rebuildList = NIL;
	ListCell   *l;
	int			i;

	/*
	 * Reload relation mapping data before starting to reconstruct cache.
	 */
	RelationMapInvalidateAll();

	/* Phase 1 */
	hash_seq_init(&status, RelationIdCache);

	while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL)
	{
		relation = idhentry->reldesc;

		/*
		 * Ignore new relations; no other backend will manipulate them before
		 * we commit.  Likewise, before replacing a relation's relfilelocator,
		 * we shall have acquired AccessExclusiveLock and drained any
		 * applicable pending invalidations.
		 */
		if (relation->rd_createSubid != InvalidSubTransactionId ||
			relation->rd_firstRelfilelocatorSubid != InvalidSubTransactionId)
			continue;

		relcacheInvalsReceived++;

		if (RelationHasReferenceCountZero(relation))
		{
			/* Delete this entry immediately */
			RelationClearRelation(relation);
		}
		else
		{
			/*
			 * If it's a mapped relation, immediately update its rd_locator in
			 * case its relfilenumber changed.  We must do this during phase 1
			 * in case the relation is consulted during rebuild of other
			 * relcache entries in phase 2.  It's safe since consulting the
			 * map doesn't involve any access to relcache entries.
			 */
			if (RelationIsMapped(relation))
			{
				RelationCloseSmgr(relation);
				RelationInitPhysicalAddr(relation);
			}

			/*
			 * Add this entry to list of stuff to rebuild in second pass.
			 * pg_class goes to the front of rebuildFirstList while
			 * pg_class_oid_index goes to the back of rebuildFirstList, so
			 * they are done first and second respectively.  Other nailed
			 * relations go to the front of rebuildList, so they'll be done
			 * next in no particular order; and everything else goes to the
			 * back of rebuildList.
			 */
			if (RelationGetRelid(relation) == RelationRelationId)
				rebuildFirstList = lcons(relation, rebuildFirstList);
			else if (RelationGetRelid(relation) == ClassOidIndexId)
				rebuildFirstList = lappend(rebuildFirstList, relation);
			else if (relation->rd_isnailed)
				rebuildList = lcons(relation, rebuildList);
			else
				rebuildList = lappend(rebuildList, relation);
		}
	}

	/*
	 * We cannot destroy the SMgrRelations as there might still be references
	 * to them, but close the underlying file descriptors.
	 */
	smgrreleaseall();

	/*
	 * Phase 2: rebuild (or invalidate) the items found to need rebuild in
	 * phase 1
	 */
	foreach(l, rebuildFirstList)
	{
		relation = (Relation) lfirst(l);
		if (!IsTransactionState() || (relation->rd_isnailed && relation->rd_refcnt == 1))
			RelationInvalidateRelation(relation);
		else
			RelationRebuildRelation(relation);
	}
	list_free(rebuildFirstList);
	foreach(l, rebuildList)
	{
		relation = (Relation) lfirst(l);
		if (!IsTransactionState() || (relation->rd_isnailed && relation->rd_refcnt == 1))
			RelationInvalidateRelation(relation);
		else
			RelationRebuildRelation(relation);
	}
	list_free(rebuildList);

	if (!debug_discard)
		/* Any RelationBuildDesc() on the stack must start over. */
		for (i = 0; i < in_progress_list_len; i++)
			in_progress_list[i].invalidated = true;
}

static void
RememberToFreeTupleDescAtEOX(TupleDesc td)
{
	if (EOXactTupleDescArray == NULL)
	{
		MemoryContext oldcxt;

		oldcxt = MemoryContextSwitchTo(CacheMemoryContext);

		EOXactTupleDescArray = (TupleDesc *) palloc(16 * sizeof(TupleDesc));
		EOXactTupleDescArrayLen = 16;
		NextEOXactTupleDescNum = 0;
		MemoryContextSwitchTo(oldcxt);
	}
	else if (NextEOXactTupleDescNum >= EOXactTupleDescArrayLen)
	{
		int32		newlen = EOXactTupleDescArrayLen * 2;

		Assert(EOXactTupleDescArrayLen > 0);

		EOXactTupleDescArray = (TupleDesc *) repalloc(EOXactTupleDescArray,
													  newlen * sizeof(TupleDesc));
		EOXactTupleDescArrayLen = newlen;
	}

	EOXactTupleDescArray[NextEOXactTupleDescNum++] = td;
}

#ifdef USE_ASSERT_CHECKING
static void
AssertPendingSyncConsistency(Relation relation)
{
	bool		relcache_verdict =
		RelationIsPermanent(relation) &&
		((relation->rd_createSubid != InvalidSubTransactionId &&
		  RELKIND_HAS_STORAGE(relation->rd_rel->relkind)) ||
		 relation->rd_firstRelfilelocatorSubid != InvalidSubTransactionId);

	Assert(relcache_verdict == RelFileLocatorSkippingWAL(relation->rd_locator));

	if (relation->rd_droppedSubid != InvalidSubTransactionId)
		Assert(!relation->rd_isvalid &&
			   (relation->rd_createSubid != InvalidSubTransactionId ||
				relation->rd_firstRelfilelocatorSubid != InvalidSubTransactionId));
}

/*
 * AssertPendingSyncs_RelationCache
 *
 *	Assert that relcache.c and storage.c agree on whether to skip WAL.
 */
void
AssertPendingSyncs_RelationCache(void)
{
	HASH_SEQ_STATUS status;
	LOCALLOCK  *locallock;
	Relation   *rels;
	int			maxrels;
	int			nrels;
	RelIdCacheEnt *idhentry;
	int			i;

	/*
	 * Open every relation that this transaction has locked.  If, for some
	 * relation, storage.c is skipping WAL and relcache.c is not skipping WAL,
	 * a CommandCounterIncrement() typically yields a local invalidation
	 * message that destroys the relcache entry.  By recreating such entries
	 * here, we detect the problem.
	 */
	PushActiveSnapshot(GetTransactionSnapshot());
	maxrels = 1;
	rels = palloc(maxrels * sizeof(*rels));
	nrels = 0;
	hash_seq_init(&status, GetLockMethodLocalHash());
	while ((locallock = (LOCALLOCK *) hash_seq_search(&status)) != NULL)
	{
		Oid			relid;
		Relation	r;

		if (locallock->nLocks <= 0)
			continue;
		if ((LockTagType) locallock->tag.lock.locktag_type !=
			LOCKTAG_RELATION)
			continue;
		relid = locallock->tag.lock.locktag_field2;
		r = RelationIdGetRelation(relid);
		if (!RelationIsValid(r))
			continue;
		if (nrels >= maxrels)
		{
			maxrels *= 2;
			rels = repalloc(rels, maxrels * sizeof(*rels));
		}
		rels[nrels++] = r;
	}

	hash_seq_init(&status, RelationIdCache);
	while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL)
		AssertPendingSyncConsistency(idhentry->reldesc);

	for (i = 0; i < nrels; i++)
		RelationClose(rels[i]);
	PopActiveSnapshot();
}
#endif

/*
 * AtEOXact_RelationCache
 *
 *	Clean up the relcache at main-transaction commit or abort.
 *
 * Note: this must be called *before* processing invalidation messages.
 * In the case of abort, we don't want to try to rebuild any invalidated
 * cache entries (since we can't safely do database accesses).  Therefore
 * we must reset refcnts before handling pending invalidations.
 *
 * As of PostgreSQL 8.1, relcache refcnts should get released by the
 * ResourceOwner mechanism.  This routine just does a debugging
 * cross-check that no pins remain.  However, we also need to do special
 * cleanup when the current transaction created any relations or made use
 * of forced index lists.
 */
void
AtEOXact_RelationCache(bool isCommit)
{
	HASH_SEQ_STATUS status;
	RelIdCacheEnt *idhentry;
	int			i;

	/*
	 * Forget in_progress_list.  This is relevant when we're aborting due to
	 * an error during RelationBuildDesc().
	 */
	Assert(in_progress_list_len == 0 || !isCommit);
	in_progress_list_len = 0;

	/*
	 * Unless the eoxact_list[] overflowed, we only need to examine the rels
	 * listed in it.  Otherwise fall back on a hash_seq_search scan.
	 *
	 * For simplicity, eoxact_list[] entries are not deleted till end of
	 * top-level transaction, even though we could remove them at
	 * subtransaction end in some cases, or remove relations from the list if
	 * they are cleared for other reasons.  Therefore we should expect the
	 * case that list entries are not found in the hashtable; if not, there's
	 * nothing to do for them.
	 */
	if (eoxact_list_overflowed)
	{
		hash_seq_init(&status, RelationIdCache);
		while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL)
		{
			AtEOXact_cleanup(idhentry->reldesc, isCommit);
		}
	}
	else
	{
		for (i = 0; i < eoxact_list_len; i++)
		{
			idhentry = (RelIdCacheEnt *) hash_search(RelationIdCache,
													 &eoxact_list[i],
													 HASH_FIND,
													 NULL);
			if (idhentry != NULL)
				AtEOXact_cleanup(idhentry->reldesc, isCommit);
		}
	}

	if (EOXactTupleDescArrayLen > 0)
	{
		Assert(EOXactTupleDescArray != NULL);
		for (i = 0; i < NextEOXactTupleDescNum; i++)
			FreeTupleDesc(EOXactTupleDescArray[i]);
		pfree(EOXactTupleDescArray);
		EOXactTupleDescArray = NULL;
	}

	/* Now we're out of the transaction and can clear the lists */
	eoxact_list_len = 0;
	eoxact_list_overflowed = false;
	NextEOXactTupleDescNum = 0;
	EOXactTupleDescArrayLen = 0;
}

/*
 * AtEOXact_cleanup
 *
 *	Clean up a single rel at main-transaction commit or abort
 *
 * NB: this processing must be idempotent, because EOXactListAdd() doesn't
 * bother to prevent duplicate entries in eoxact_list[].
 */
static void
AtEOXact_cleanup(Relation relation, bool isCommit)
{
	bool		clear_relcache = false;

	/*
	 * The relcache entry's ref count should be back to its normal
	 * not-in-a-transaction state: 0 unless it's nailed in cache.
	 *
	 * In bootstrap mode, this is NOT true, so don't check it --- the
	 * bootstrap code expects relations to stay open across start/commit
	 * transaction calls.  (That seems bogus, but it's not worth fixing.)
	 *
	 * Note: ideally this check would be applied to every relcache entry, not
	 * just those that have eoxact work to do.  But it's not worth forcing a
	 * scan of the whole relcache just for this.  (Moreover, doing so would
	 * mean that assert-enabled testing never tests the hash_search code path
	 * above, which seems a bad idea.)
	 */
#ifdef USE_ASSERT_CHECKING
	if (!IsBootstrapProcessingMode())
	{
		int			expected_refcnt;

		expected_refcnt = relation->rd_isnailed ? 1 : 0;
		Assert(relation->rd_refcnt == expected_refcnt);
	}
#endif

	/*
	 * Is the relation live after this transaction ends?
	 *
	 * During commit, clear the relcache entry if it is preserved after
	 * relation drop, in order not to orphan the entry.  During rollback,
	 * clear the relcache entry if the relation is created in the current
	 * transaction since it isn't interesting any longer once we are out of
	 * the transaction.
	 */
	clear_relcache =
		(isCommit ?
		 relation->rd_droppedSubid != InvalidSubTransactionId :
		 relation->rd_createSubid != InvalidSubTransactionId);

	/*
	 * Since we are now out of the transaction, reset the subids to zero. That
	 * also lets RelationClearRelation() drop the relcache entry.
	 */
	relation->rd_createSubid = InvalidSubTransactionId;
	relation->rd_newRelfilelocatorSubid = InvalidSubTransactionId;
	relation->rd_firstRelfilelocatorSubid = InvalidSubTransactionId;
	relation->rd_droppedSubid = InvalidSubTransactionId;

	if (clear_relcache)
	{
		if (RelationHasReferenceCountZero(relation))
		{
			RelationClearRelation(relation);
			return;
		}
		else
		{
			/*
			 * Hmm, somewhere there's a (leaked?) reference to the relation.
			 * We daren't remove the entry for fear of dereferencing a
			 * dangling pointer later.  Bleat, and mark it as not belonging to
			 * the current transaction.  Hopefully it'll get cleaned up
			 * eventually.  This must be just a WARNING to avoid
			 * error-during-error-recovery loops.
			 */
			elog(WARNING, "cannot remove relcache entry for \"%s\" because it has nonzero refcount",
				 RelationGetRelationName(relation));
		}
	}
}

/*
 * AtEOSubXact_RelationCache
 *
 *	Clean up the relcache at sub-transaction commit or abort.
 *
 * Note: this must be called *before* processing invalidation messages.
 */
void
AtEOSubXact_RelationCache(bool isCommit, SubTransactionId mySubid,
						  SubTransactionId parentSubid)
{
	HASH_SEQ_STATUS status;
	RelIdCacheEnt *idhentry;
	int			i;

	/*
	 * Forget in_progress_list.  This is relevant when we're aborting due to
	 * an error during RelationBuildDesc().  We don't commit subtransactions
	 * during RelationBuildDesc().
	 */
	Assert(in_progress_list_len == 0 || !isCommit);
	in_progress_list_len = 0;

	/*
	 * Unless the eoxact_list[] overflowed, we only need to examine the rels
	 * listed in it.  Otherwise fall back on a hash_seq_search scan.  Same
	 * logic as in AtEOXact_RelationCache.
	 */
	if (eoxact_list_overflowed)
	{
		hash_seq_init(&status, RelationIdCache);
		while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL)
		{
			AtEOSubXact_cleanup(idhentry->reldesc, isCommit,
								mySubid, parentSubid);
		}
	}
	else
	{
		for (i = 0; i < eoxact_list_len; i++)
		{
			idhentry = (RelIdCacheEnt *) hash_search(RelationIdCache,
													 &eoxact_list[i],
													 HASH_FIND,
													 NULL);
			if (idhentry != NULL)
				AtEOSubXact_cleanup(idhentry->reldesc, isCommit,
									mySubid, parentSubid);
		}
	}

	/* Don't reset the list; we still need more cleanup later */
}

/*
 * AtEOSubXact_cleanup
 *
 *	Clean up a single rel at subtransaction commit or abort
 *
 * NB: this processing must be idempotent, because EOXactListAdd() doesn't
 * bother to prevent duplicate entries in eoxact_list[].
 */
static void
AtEOSubXact_cleanup(Relation relation, bool isCommit,
					SubTransactionId mySubid, SubTransactionId parentSubid)
{
	/*
	 * Is it a relation created in the current subtransaction?
	 *
	 * During subcommit, mark it as belonging to the parent, instead, as long
	 * as it has not been dropped. Otherwise simply delete the relcache entry.
	 * --- it isn't interesting any longer.
	 */
	if (relation->rd_createSubid == mySubid)
	{
		/*
		 * Valid rd_droppedSubid means the corresponding relation is dropped
		 * but the relcache entry is preserved for at-commit pending sync. We
		 * need to drop it explicitly here not to make the entry orphan.
		 */
		Assert(relation->rd_droppedSubid == mySubid ||
			   relation->rd_droppedSubid == InvalidSubTransactionId);
		if (isCommit && relation->rd_droppedSubid == InvalidSubTransactionId)
			relation->rd_createSubid = parentSubid;
		else if (RelationHasReferenceCountZero(relation))
		{
			/* allow the entry to be removed */
			relation->rd_createSubid = InvalidSubTransactionId;
			relation->rd_newRelfilelocatorSubid = InvalidSubTransactionId;
			relation->rd_firstRelfilelocatorSubid = InvalidSubTransactionId;
			relation->rd_droppedSubid = InvalidSubTransactionId;
			RelationClearRelation(relation);
			return;
		}
		else
		{
			/*
			 * Hmm, somewhere there's a (leaked?) reference to the relation.
			 * We daren't remove the entry for fear of dereferencing a
			 * dangling pointer later.  Bleat, and transfer it to the parent
			 * subtransaction so we can try again later.  This must be just a
			 * WARNING to avoid error-during-error-recovery loops.
			 */
			relation->rd_createSubid = parentSubid;
			elog(WARNING, "cannot remove relcache entry for \"%s\" because it has nonzero refcount",
				 RelationGetRelationName(relation));
		}
	}

	/*
	 * Likewise, update or drop any new-relfilenumber-in-subtransaction record
	 * or drop record.
	 */
	if (relation->rd_newRelfilelocatorSubid == mySubid)
	{
		if (isCommit)
			relation->rd_newRelfilelocatorSubid = parentSubid;
		else
			relation->rd_newRelfilelocatorSubid = InvalidSubTransactionId;
	}

	if (relation->rd_firstRelfilelocatorSubid == mySubid)
	{
		if (isCommit)
			relation->rd_firstRelfilelocatorSubid = parentSubid;
		else
			relation->rd_firstRelfilelocatorSubid = InvalidSubTransactionId;
	}

	if (relation->rd_droppedSubid == mySubid)
	{
		if (isCommit)
			relation->rd_droppedSubid = parentSubid;
		else
			relation->rd_droppedSubid = InvalidSubTransactionId;
	}
}


/*
 *		RelationBuildLocalRelation
 *			Build a relcache entry for an about-to-be-created relation,
 *			and enter it into the relcache.
 */
Relation
RelationBuildLocalRelation(const char *relname,
						   Oid relnamespace,
						   TupleDesc tupDesc,
						   Oid relid,
						   Oid accessmtd,
						   RelFileNumber relfilenumber,
						   Oid reltablespace,
						   bool shared_relation,
						   bool mapped_relation,
						   char relpersistence,
						   char relkind)
{
	Relation	rel;
	MemoryContext oldcxt;
	int			natts = tupDesc->natts;
	int			i;
	bool		has_not_null;
	bool		nailit;

	Assert(natts >= 0);

	/*
	 * check for creation of a rel that must be nailed in cache.
	 *
	 * XXX this list had better match the relations specially handled in
	 * RelationCacheInitializePhase2/3.
	 */
	switch (relid)
	{
		case DatabaseRelationId:
		case AuthIdRelationId:
		case AuthMemRelationId:
		case RelationRelationId:
		case AttributeRelationId:
		case ProcedureRelationId:
		case TypeRelationId:
			nailit = true;
			break;
		default:
			nailit = false;
			break;
	}

	/*
	 * check that hardwired list of shared rels matches what's in the
	 * bootstrap .bki file.  If you get a failure here during initdb, you
	 * probably need to fix IsSharedRelation() to match whatever you've done
	 * to the set of shared relations.
	 */
	if (shared_relation != IsSharedRelation(relid))
		elog(ERROR, "shared_relation flag for \"%s\" does not match IsSharedRelation(%u)",
			 relname, relid);

	/* Shared relations had better be mapped, too */
	Assert(mapped_relation || !shared_relation);

	/*
	 * switch to the cache context to create the relcache entry.
	 */
	if (!CacheMemoryContext)
		CreateCacheMemoryContext();

	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);

	/*
	 * allocate a new relation descriptor and fill in basic state fields.
	 */
	rel = palloc0_object(RelationData);

	/* make sure relation is marked as having no open file yet */
	rel->rd_smgr = NULL;

	/* mark it nailed if appropriate */
	rel->rd_isnailed = nailit;

	rel->rd_refcnt = nailit ? 1 : 0;

	/* it's being created in this transaction */
	rel->rd_createSubid = GetCurrentSubTransactionId();
	rel->rd_newRelfilelocatorSubid = InvalidSubTransactionId;
	rel->rd_firstRelfilelocatorSubid = InvalidSubTransactionId;
	rel->rd_droppedSubid = InvalidSubTransactionId;

	/*
	 * create a new tuple descriptor from the one passed in.  We do this
	 * partly to copy it into the cache context, and partly because the new
	 * relation can't have any defaults or constraints yet; they have to be
	 * added in later steps, because they require additions to multiple system
	 * catalogs.  We can copy attnotnull constraints here, however.
	 */
	rel->rd_att = CreateTupleDescCopy(tupDesc);
	rel->rd_att->tdrefcount = 1;	/* mark as refcounted */
	has_not_null = false;
	for (i = 0; i < natts; i++)
	{
		Form_pg_attribute satt = TupleDescAttr(tupDesc, i);
		Form_pg_attribute datt = TupleDescAttr(rel->rd_att, i);

		datt->attidentity = satt->attidentity;
		datt->attgenerated = satt->attgenerated;
		datt->attnotnull = satt->attnotnull;
		has_not_null |= satt->attnotnull;
		populate_compact_attribute(rel->rd_att, i);

		if (satt->attnotnull)
		{
			CompactAttribute *scatt = TupleDescCompactAttr(tupDesc, i);
			CompactAttribute *dcatt = TupleDescCompactAttr(rel->rd_att, i);

			dcatt->attnullability = scatt->attnullability;
		}
	}

	if (has_not_null)
	{
		TupleConstr *constr = palloc0_object(TupleConstr);

		constr->has_not_null = true;
		rel->rd_att->constr = constr;
	}

	/*
	 * initialize relation tuple form (caller may add/override data later)
	 */
	rel->rd_rel = (Form_pg_class) palloc0(CLASS_TUPLE_SIZE);

	namestrcpy(&rel->rd_rel->relname, relname);
	rel->rd_rel->relnamespace = relnamespace;

	rel->rd_rel->relkind = relkind;
	rel->rd_rel->relnatts = natts;
	rel->rd_rel->reltype = InvalidOid;
	/* needed when bootstrapping: */
	rel->rd_rel->relowner = BOOTSTRAP_SUPERUSERID;

	/* set up persistence and relcache fields dependent on it */
	rel->rd_rel->relpersistence = relpersistence;
	switch (relpersistence)
	{
		case RELPERSISTENCE_UNLOGGED:
		case RELPERSISTENCE_PERMANENT:
			rel->rd_backend = INVALID_PROC_NUMBER;
			rel->rd_islocaltemp = false;
			break;
		case RELPERSISTENCE_TEMP:
			Assert(isTempOrTempToastNamespace(relnamespace));
			rel->rd_backend = ProcNumberForTempRelations();
			rel->rd_islocaltemp = true;
			break;
		default:
			elog(ERROR, "invalid relpersistence: %c", relpersistence);
			break;
	}

	/* if it's a materialized view, it's not populated initially */
	if (relkind == RELKIND_MATVIEW)
		rel->rd_rel->relispopulated = false;
	else
		rel->rd_rel->relispopulated = true;

	/* set replica identity -- system catalogs and non-tables don't have one */
	if (!IsCatalogNamespace(relnamespace) &&
		(relkind == RELKIND_RELATION ||
		 relkind == RELKIND_MATVIEW ||
		 relkind == RELKIND_PARTITIONED_TABLE))
		rel->rd_rel->relreplident = REPLICA_IDENTITY_DEFAULT;
	else
		rel->rd_rel->relreplident = REPLICA_IDENTITY_NOTHING;

	/*
	 * Insert relation physical and logical identifiers (OIDs) into the right
	 * places.  For a mapped relation, we set relfilenumber to zero and rely
	 * on RelationInitPhysicalAddr to consult the map.
	 */
	rel->rd_rel->relisshared = shared_relation;

	RelationGetRelid(rel) = relid;

	for (i = 0; i < natts; i++)
		TupleDescAttr(rel->rd_att, i)->attrelid = relid;

	TupleDescFinalize(rel->rd_att);

	rel->rd_rel->reltablespace = reltablespace;

	if (mapped_relation)
	{
		rel->rd_rel->relfilenode = InvalidRelFileNumber;
		/* Add it to the active mapping information */
		RelationMapUpdateMap(relid, relfilenumber, shared_relation, true);
	}
	else
		rel->rd_rel->relfilenode = relfilenumber;

	RelationInitLockInfo(rel);	/* see lmgr.c */

	RelationInitPhysicalAddr(rel);

	rel->rd_rel->relam = accessmtd;

	/*
	 * RelationInitTableAccessMethod will do syscache lookups, so we mustn't
	 * run it in CacheMemoryContext.  Fortunately, the remaining steps don't
	 * require a long-lived current context.
	 */
	MemoryContextSwitchTo(oldcxt);

	if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_SEQUENCE)
		RelationInitTableAccessMethod(rel);

	/*
	 * Leave index access method uninitialized, because the pg_index row has
	 * not been inserted at this stage of index creation yet.  The cache
	 * invalidation after pg_index row has been inserted will initialize it.
	 */

	/*
	 * Okay to insert into the relcache hash table.
	 *
	 * Ordinarily, there should certainly not be an existing hash entry for
	 * the same OID; but during bootstrap, when we create a "real" relcache
	 * entry for one of the bootstrap relations, we'll be overwriting the
	 * phony one created with formrdesc.  So allow that to happen for nailed
	 * rels.
	 */
	RelationCacheInsert(rel, nailit);

	/*
	 * Flag relation as needing eoxact cleanup (to clear rd_createSubid). We
	 * can't do this before storing relid in it.
	 */
	EOXactListAdd(rel);

	/* It's fully valid */
	rel->rd_isvalid = true;

	/*
	 * Caller expects us to pin the returned entry.
	 */
	RelationIncrementReferenceCount(rel);

	return rel;
}


/*
 * RelationSetNewRelfilenumber
 *
 * Assign a new relfilenumber (physical file name), and possibly a new
 * persistence setting, to the relation.
 *
 * This allows a full rewrite of the relation to be done with transactional
 * safety (since the filenumber assignment can be rolled back).  Note however
 * that there is no simple way to access the relation's old data for the
 * remainder of the current transaction.  This limits the usefulness to cases
 * such as TRUNCATE or rebuilding an index from scratch.
 *
 * Caller must already hold exclusive lock on the relation.
 */
void
RelationSetNewRelfilenumber(Relation relation, char persistence)
{
	RelFileNumber newrelfilenumber;
	Relation	pg_class;
	ItemPointerData otid;
	HeapTuple	tuple;
	Form_pg_class classform;
	MultiXactId minmulti = InvalidMultiXactId;
	TransactionId freezeXid = InvalidTransactionId;
	RelFileLocator newrlocator;

	if (!IsBinaryUpgrade)
	{
		/* Allocate a new relfilenumber */
		newrelfilenumber = GetNewRelFileNumber(relation->rd_rel->reltablespace,
											   NULL, persistence);
	}
	else if (relation->rd_rel->relkind == RELKIND_INDEX)
	{
		if (!OidIsValid(binary_upgrade_next_index_pg_class_relfilenumber))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("index relfilenumber value not set when in binary upgrade mode")));

		newrelfilenumber = binary_upgrade_next_index_pg_class_relfilenumber;
		binary_upgrade_next_index_pg_class_relfilenumber = InvalidOid;
	}
	else if (relation->rd_rel->relkind == RELKIND_RELATION)
	{
		if (!OidIsValid(binary_upgrade_next_heap_pg_class_relfilenumber))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("heap relfilenumber value not set when in binary upgrade mode")));

		newrelfilenumber = binary_upgrade_next_heap_pg_class_relfilenumber;
		binary_upgrade_next_heap_pg_class_relfilenumber = InvalidOid;
	}
	else
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("unexpected request for new relfilenumber in binary upgrade mode")));

	/*
	 * Get a writable copy of the pg_class tuple for the given relation.
	 */
	pg_class = table_open(RelationRelationId, RowExclusiveLock);

	tuple = SearchSysCacheLockedCopy1(RELOID,
									  ObjectIdGetDatum(RelationGetRelid(relation)));
	if (!HeapTupleIsValid(tuple))
		elog(ERROR, "could not find tuple for relation %u",
			 RelationGetRelid(relation));
	otid = tuple->t_self;
	classform = (Form_pg_class) GETSTRUCT(tuple);

	/*
	 * Schedule unlinking of the old storage at transaction commit, except
	 * when performing a binary upgrade, when we must do it immediately.
	 */
	if (IsBinaryUpgrade)
	{
		SMgrRelation srel;

		/*
		 * During a binary upgrade, we use this code path to ensure that
		 * pg_largeobject and its index have the same relfilenumbers as in the
		 * old cluster. This is necessary because pg_upgrade treats
		 * pg_largeobject like a user table, not a system table. It is however
		 * possible that a table or index may need to end up with the same
		 * relfilenumber in the new cluster as what it had in the old cluster.
		 * Hence, we can't wait until commit time to remove the old storage.
		 *
		 * In general, this function needs to have transactional semantics,
		 * and removing the old storage before commit time surely isn't.
		 * However, it doesn't really matter, because if a binary upgrade
		 * fails at this stage, the new cluster will need to be recreated
		 * anyway.
		 */
		srel = smgropen(relation->rd_locator, relation->rd_backend);
		smgrdounlinkall(&srel, 1, false);
		smgrclose(srel);
	}
	else
	{
		/* Not a binary upgrade, so just schedule it to happen later. */
		RelationDropStorage(relation);
	}

	/*
	 * Create storage for the main fork of the new relfilenumber.  If it's a
	 * table-like object, call into the table AM to do so, which'll also
	 * create the table's init fork if needed.
	 *
	 * NOTE: If relevant for the AM, any conflict in relfilenumber value will
	 * be caught here, if GetNewRelFileNumber messes up for any reason.
	 */
	newrlocator = relation->rd_locator;
	newrlocator.relNumber = newrelfilenumber;

	if (RELKIND_HAS_TABLE_AM(relation->rd_rel->relkind))
	{
		table_relation_set_new_filelocator(relation, &newrlocator,
										   persistence,
										   &freezeXid, &minmulti);
	}
	else if (RELKIND_HAS_STORAGE(relation->rd_rel->relkind))
	{
		/* handle these directly, at least for now */
		SMgrRelation srel;

		srel = RelationCreateStorage(newrlocator, persistence, true);
		smgrclose(srel);
	}
	else
	{
		/* we shouldn't be called for anything else */
		elog(ERROR, "relation \"%s\" does not have storage",
			 RelationGetRelationName(relation));
	}

	/*
	 * If we're dealing with a mapped index, pg_class.relfilenode doesn't
	 * change; instead we have to send the update to the relation mapper.
	 *
	 * For mapped indexes, we don't actually change the pg_class entry at all;
	 * this is essential when reindexing pg_class itself.  That leaves us with
	 * possibly-inaccurate values of relpages etc, but those will be fixed up
	 * later.
	 */
	if (RelationIsMapped(relation))
	{
		/* This case is only supported for indexes */
		Assert(relation->rd_rel->relkind == RELKIND_INDEX);

		/* Since we're not updating pg_class, these had better not change */
		Assert(classform->relfrozenxid == freezeXid);
		Assert(classform->relminmxid == minmulti);
		Assert(classform->relpersistence == persistence);

		/*
		 * In some code paths it's possible that the tuple update we'd
		 * otherwise do here is the only thing that would assign an XID for
		 * the current transaction.  However, we must have an XID to delete
		 * files, so make sure one is assigned.
		 */
		(void) GetCurrentTransactionId();

		/* Do the deed */
		RelationMapUpdateMap(RelationGetRelid(relation),
							 newrelfilenumber,
							 relation->rd_rel->relisshared,
							 false);

		/* Since we're not updating pg_class, must trigger inval manually */
		CacheInvalidateRelcache(relation);
	}
	else
	{
		/* Normal case, update the pg_class entry */
		classform->relfilenode = newrelfilenumber;

		/* relpages etc. never change for sequences */
		if (relation->rd_rel->relkind != RELKIND_SEQUENCE)
		{
			classform->relpages = 0;	/* it's empty until further notice */
			classform->reltuples = -1;
			classform->relallvisible = 0;
			classform->relallfrozen = 0;
		}
		classform->relfrozenxid = freezeXid;
		classform->relminmxid = minmulti;
		classform->relpersistence = persistence;

		CatalogTupleUpdate(pg_class, &otid, tuple);
	}

	UnlockTuple(pg_class, &otid, InplaceUpdateTupleLock);
	heap_freetuple(tuple);

	table_close(pg_class, RowExclusiveLock);

	/*
	 * Make the pg_class row change or relation map change visible.  This will
	 * cause the relcache entry to get updated, too.
	 */
	CommandCounterIncrement();

	RelationAssumeNewRelfilelocator(relation);
}

/*
 * RelationAssumeNewRelfilelocator
 *
 * Code that modifies pg_class.reltablespace or pg_class.relfilenode must call
 * this.  The call shall precede any code that might insert WAL records whose
 * replay would modify bytes in the new RelFileLocator, and the call shall follow
 * any WAL modifying bytes in the prior RelFileLocator.  See struct RelationData.
 * Ideally, call this as near as possible to the CommandCounterIncrement()
 * that makes the pg_class change visible (before it or after it); that
 * minimizes the chance of future development adding a forbidden WAL insertion
 * between RelationAssumeNewRelfilelocator() and CommandCounterIncrement().
 */
void
RelationAssumeNewRelfilelocator(Relation relation)
{
	relation->rd_newRelfilelocatorSubid = GetCurrentSubTransactionId();
	if (relation->rd_firstRelfilelocatorSubid == InvalidSubTransactionId)
		relation->rd_firstRelfilelocatorSubid = relation->rd_newRelfilelocatorSubid;

	/* Flag relation as needing eoxact cleanup (to clear these fields) */
	EOXactListAdd(relation);
}


/*
 *		RelationCacheInitialize
 *
 *		This initializes the relation descriptor cache.  At the time
 *		that this is invoked, we can't do database access yet (mainly
 *		because the transaction subsystem is not up); all we are doing
 *		is making an empty cache hashtable.  This must be done before
 *		starting the initialization transaction, because otherwise
 *		AtEOXact_RelationCache would crash if that transaction aborts
 *		before we can get the relcache set up.
 */

#define INITRELCACHESIZE		400

void
RelationCacheInitialize(void)
{
	HASHCTL		ctl;
	int			allocsize;

	/*
	 * make sure cache memory context exists
	 */
	if (!CacheMemoryContext)
		CreateCacheMemoryContext();

	/*
	 * create hashtable that indexes the relcache
	 */
	ctl.keysize = sizeof(Oid);
	ctl.entrysize = sizeof(RelIdCacheEnt);
	RelationIdCache = hash_create("Relcache by OID", INITRELCACHESIZE,
								  &ctl, HASH_ELEM | HASH_BLOBS);

	/*
	 * reserve enough in_progress_list slots for many cases
	 */
	allocsize = 4;
	in_progress_list =
		MemoryContextAlloc(CacheMemoryContext,
						   allocsize * sizeof(*in_progress_list));
	in_progress_list_maxlen = allocsize;

	/*
	 * relation mapper needs to be initialized too
	 */
	RelationMapInitialize();
}

/*
 *		RelationCacheInitializePhase2
 *
 *		This is called to prepare for access to shared catalogs during startup.
 *		We must at least set up nailed reldescs for pg_database, pg_authid,
 *		pg_auth_members, and pg_shseclabel. Ideally we'd like to have reldescs
 *		for their indexes, too.  We attempt to load this information from the
 *		shared relcache init file.  If that's missing or broken, just make
 *		phony entries for the catalogs themselves.
 *		RelationCacheInitializePhase3 will clean up as needed.
 */
void
RelationCacheInitializePhase2(void)
{
	MemoryContext oldcxt;

	/*
	 * relation mapper needs initialized too
	 */
	RelationMapInitializePhase2();

	/*
	 * In bootstrap mode, the shared catalogs aren't there yet anyway, so do
	 * nothing.
	 */
	if (IsBootstrapProcessingMode())
		return;

	/*
	 * switch to cache memory context
	 */
	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);

	/*
	 * Try to load the shared relcache cache file.  If unsuccessful, bootstrap
	 * the cache with pre-made descriptors for the critical shared catalogs.
	 */
	if (!load_relcache_init_file(true))
	{
		formrdesc("pg_database", DatabaseRelation_Rowtype_Id, true,
				  Natts_pg_database, Desc_pg_database);
		formrdesc("pg_authid", AuthIdRelation_Rowtype_Id, true,
				  Natts_pg_authid, Desc_pg_authid);
		formrdesc("pg_auth_members", AuthMemRelation_Rowtype_Id, true,
				  Natts_pg_auth_members, Desc_pg_auth_members);
		formrdesc("pg_shseclabel", SharedSecLabelRelation_Rowtype_Id, true,
				  Natts_pg_shseclabel, Desc_pg_shseclabel);
		formrdesc("pg_subscription", SubscriptionRelation_Rowtype_Id, true,
				  Natts_pg_subscription, Desc_pg_subscription);

#define NUM_CRITICAL_SHARED_RELS	5	/* fix if you change list above */
	}

	MemoryContextSwitchTo(oldcxt);
}

/*
 *		RelationCacheInitializePhase3
 *
 *		This is called as soon as the catcache and transaction system
 *		are functional and we have determined MyDatabaseId.  At this point
 *		we can actually read data from the database's system catalogs.
 *		We first try to read pre-computed relcache entries from the local
 *		relcache init file.  If that's missing or broken, make phony entries
 *		for the minimum set of nailed-in-cache relations.  Then (unless
 *		bootstrapping) make sure we have entries for the critical system
 *		indexes.  Once we've done all this, we have enough infrastructure to
 *		open any system catalog or use any catcache.  The last step is to
 *		rewrite the cache files if needed.
 */
void
RelationCacheInitializePhase3(void)
{
	HASH_SEQ_STATUS status;
	RelIdCacheEnt *idhentry;
	MemoryContext oldcxt;
	bool		needNewCacheFile = !criticalSharedRelcachesBuilt;

	/*
	 * relation mapper needs initialized too
	 */
	RelationMapInitializePhase3();

	/*
	 * switch to cache memory context
	 */
	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);

	/*
	 * Try to load the local relcache cache file.  If unsuccessful, bootstrap
	 * the cache with pre-made descriptors for the critical "nailed-in" system
	 * catalogs.
	 */
	if (IsBootstrapProcessingMode() ||
		!load_relcache_init_file(false))
	{
		needNewCacheFile = true;

		formrdesc("pg_class", RelationRelation_Rowtype_Id, false,
				  Natts_pg_class, Desc_pg_class);
		formrdesc("pg_attribute", AttributeRelation_Rowtype_Id, false,
				  Natts_pg_attribute, Desc_pg_attribute);
		formrdesc("pg_proc", ProcedureRelation_Rowtype_Id, false,
				  Natts_pg_proc, Desc_pg_proc);
		formrdesc("pg_type", TypeRelation_Rowtype_Id, false,
				  Natts_pg_type, Desc_pg_type);

#define NUM_CRITICAL_LOCAL_RELS 4	/* fix if you change list above */
	}

	MemoryContextSwitchTo(oldcxt);

	/* In bootstrap mode, the faked-up formrdesc info is all we'll have */
	if (IsBootstrapProcessingMode())
		return;

	/*
	 * If we didn't get the critical system indexes loaded into relcache, do
	 * so now.  These are critical because the catcache and/or opclass cache
	 * depend on them for fetches done during relcache load.  Thus, we have an
	 * infinite-recursion problem.  We can break the recursion by doing
	 * heapscans instead of indexscans at certain key spots. To avoid hobbling
	 * performance, we only want to do that until we have the critical indexes
	 * loaded into relcache.  Thus, the flag criticalRelcachesBuilt is used to
	 * decide whether to do heapscan or indexscan at the key spots, and we set
	 * it true after we've loaded the critical indexes.
	 *
	 * The critical indexes are marked as "nailed in cache", partly to make it
	 * easy for load_relcache_init_file to count them, but mainly because we
	 * cannot flush and rebuild them once we've set criticalRelcachesBuilt to
	 * true.  (NOTE: perhaps it would be possible to reload them by
	 * temporarily setting criticalRelcachesBuilt to false again.  For now,
	 * though, we just nail 'em in.)
	 *
	 * RewriteRelRulenameIndexId and TriggerRelidNameIndexId are not critical
	 * in the same way as the others, because the critical catalogs don't
	 * (currently) have any rules or triggers, and so these indexes can be
	 * rebuilt without inducing recursion.  However they are used during
	 * relcache load when a rel does have rules or triggers, so we choose to
	 * nail them for performance reasons.
	 */
	if (!criticalRelcachesBuilt)
	{
		load_critical_index(ClassOidIndexId,
							RelationRelationId);
		load_critical_index(AttributeRelidNumIndexId,
							AttributeRelationId);
		load_critical_index(IndexRelidIndexId,
							IndexRelationId);
		load_critical_index(OpclassOidIndexId,
							OperatorClassRelationId);
		load_critical_index(AccessMethodProcedureIndexId,
							AccessMethodProcedureRelationId);
		load_critical_index(RewriteRelRulenameIndexId,
							RewriteRelationId);
		load_critical_index(TriggerRelidNameIndexId,
							TriggerRelationId);

#define NUM_CRITICAL_LOCAL_INDEXES	7	/* fix if you change list above */

		criticalRelcachesBuilt = true;
	}

	/*
	 * Process critical shared indexes too.
	 *
	 * DatabaseNameIndexId isn't critical for relcache loading, but rather for
	 * initial lookup of MyDatabaseId, without which we'll never find any
	 * non-shared catalogs at all.  Autovacuum calls InitPostgres with a
	 * database OID, so it instead depends on DatabaseOidIndexId.  We also
	 * need to nail up some indexes on pg_authid and pg_auth_members for use
	 * during client authentication.  SharedSecLabelObjectIndexId isn't
	 * critical for the core system, but authentication hooks might be
	 * interested in it.
	 */
	if (!criticalSharedRelcachesBuilt)
	{
		load_critical_index(DatabaseNameIndexId,
							DatabaseRelationId);
		load_critical_index(DatabaseOidIndexId,
							DatabaseRelationId);
		load_critical_index(AuthIdRolnameIndexId,
							AuthIdRelationId);
		load_critical_index(AuthIdOidIndexId,
							AuthIdRelationId);
		load_critical_index(AuthMemMemRoleIndexId,
							AuthMemRelationId);
		load_critical_index(SharedSecLabelObjectIndexId,
							SharedSecLabelRelationId);

#define NUM_CRITICAL_SHARED_INDEXES 6	/* fix if you change list above */

		criticalSharedRelcachesBuilt = true;
	}

	/*
	 * Now, scan all the relcache entries and update anything that might be
	 * wrong in the results from formrdesc or the relcache cache file. If we
	 * faked up relcache entries using formrdesc, then read the real pg_class
	 * rows and replace the fake entries with them. Also, if any of the
	 * relcache entries have rules, triggers, or security policies, load that
	 * info the hard way since it isn't recorded in the cache file.
	 *
	 * Whenever we access the catalogs to read data, there is a possibility of
	 * a shared-inval cache flush causing relcache entries to be removed.
	 * Since hash_seq_search only guarantees to still work after the *current*
	 * entry is removed, it's unsafe to continue the hashtable scan afterward.
	 * We handle this by restarting the scan from scratch after each access.
	 * This is theoretically O(N^2), but the number of entries that actually
	 * need to be fixed is small enough that it doesn't matter.
	 */
	hash_seq_init(&status, RelationIdCache);

	while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL)
	{
		Relation	relation = idhentry->reldesc;
		bool		restart = false;

		/*
		 * Make sure *this* entry doesn't get flushed while we work with it.
		 */
		RelationIncrementReferenceCount(relation);

		/*
		 * If it's a faked-up entry, read the real pg_class tuple.
		 */
		if (relation->rd_rel->relowner == InvalidOid)
		{
			HeapTuple	htup;
			Form_pg_class relp;

			htup = SearchSysCache1(RELOID,
								   ObjectIdGetDatum(RelationGetRelid(relation)));
			if (!HeapTupleIsValid(htup))
				ereport(FATAL,
						errcode(ERRCODE_UNDEFINED_OBJECT),
						errmsg_internal("cache lookup failed for relation %u",
										RelationGetRelid(relation)));
			relp = (Form_pg_class) GETSTRUCT(htup);

			/*
			 * Copy tuple to relation->rd_rel. (See notes in
			 * AllocateRelationDesc())
			 */
			memcpy((char *) relation->rd_rel, (char *) relp, CLASS_TUPLE_SIZE);

			/* Update rd_options while we have the tuple */
			if (relation->rd_options)
				pfree(relation->rd_options);
			RelationParseRelOptions(relation, htup);

			/*
			 * Check the values in rd_att were set up correctly.  (We cannot
			 * just copy them over now: formrdesc must have set up the rd_att
			 * data correctly to start with, because it may already have been
			 * copied into one or more catcache entries.)
			 */
			Assert(relation->rd_att->tdtypeid == relp->reltype);
			Assert(relation->rd_att->tdtypmod == -1);

			ReleaseSysCache(htup);

			/* relowner had better be OK now, else we'll loop forever */
			if (relation->rd_rel->relowner == InvalidOid)
				elog(ERROR, "invalid relowner in pg_class entry for \"%s\"",
					 RelationGetRelationName(relation));

			restart = true;
		}

		/*
		 * Fix data that isn't saved in relcache cache file.
		 *
		 * relhasrules or relhastriggers could possibly be wrong or out of
		 * date.  If we don't actually find any rules or triggers, clear the
		 * local copy of the flag so that we don't get into an infinite loop
		 * here.  We don't make any attempt to fix the pg_class entry, though.
		 */
		if (relation->rd_rel->relhasrules && relation->rd_rules == NULL)
		{
			RelationBuildRuleLock(relation);
			if (relation->rd_rules == NULL)
				relation->rd_rel->relhasrules = false;
			restart = true;
		}
		if (relation->rd_rel->relhastriggers && relation->trigdesc == NULL)
		{
			RelationBuildTriggers(relation);
			if (relation->trigdesc == NULL)
				relation->rd_rel->relhastriggers = false;
			restart = true;
		}

		/*
		 * Re-load the row security policies if the relation has them, since
		 * they are not preserved in the cache.  Note that we can never NOT
		 * have a policy while relrowsecurity is true,
		 * RelationBuildRowSecurity will create a single default-deny policy
		 * if there is no policy defined in pg_policy.
		 */
		if (relation->rd_rel->relrowsecurity && relation->rd_rsdesc == NULL)
		{
			RelationBuildRowSecurity(relation);

			Assert(relation->rd_rsdesc != NULL);
			restart = true;
		}

		/* Reload tableam data if needed */
		if (relation->rd_tableam == NULL &&
			(RELKIND_HAS_TABLE_AM(relation->rd_rel->relkind) || relation->rd_rel->relkind == RELKIND_SEQUENCE))
		{
			RelationInitTableAccessMethod(relation);
			Assert(relation->rd_tableam != NULL);

			restart = true;
		}

		/* Release hold on the relation */
		RelationDecrementReferenceCount(relation);

		/* Now, restart the hashtable scan if needed */
		if (restart)
		{
			hash_seq_term(&status);
			hash_seq_init(&status, RelationIdCache);
		}
	}

	/*
	 * Lastly, write out new relcache cache files if needed.  We don't bother
	 * to distinguish cases where only one of the two needs an update.
	 */
	if (needNewCacheFile)
	{
		/*
		 * Force all the catcaches to finish initializing and thereby open the
		 * catalogs and indexes they use.  This will preload the relcache with
		 * entries for all the most important system catalogs and indexes, so
		 * that the init files will be most useful for future backends.
		 */
		InitCatalogCachePhase2();

		/* now write the files */
		write_relcache_init_file(true);
		write_relcache_init_file(false);
	}
}

/*
 * Load one critical system index into the relcache
 *
 * indexoid is the OID of the target index, heapoid is the OID of the catalog
 * it belongs to.
 */
static void
load_critical_index(Oid indexoid, Oid heapoid)
{
	Relation	ird;

	/*
	 * We must lock the underlying catalog before locking the index to avoid
	 * deadlock, since RelationBuildDesc might well need to read the catalog,
	 * and if anyone else is exclusive-locking this catalog and index they'll
	 * be doing it in that order.
	 */
	LockRelationOid(heapoid, AccessShareLock);
	LockRelationOid(indexoid, AccessShareLock);
	ird = RelationBuildDesc(indexoid, true);
	if (ird == NULL)
		ereport(PANIC,
				errcode(ERRCODE_DATA_CORRUPTED),
				errmsg_internal("could not open critical system index %u", indexoid));
	ird->rd_isnailed = true;
	ird->rd_refcnt = 1;
	UnlockRelationOid(indexoid, AccessShareLock);
	UnlockRelationOid(heapoid, AccessShareLock);

	(void) RelationGetIndexAttOptions(ird, false);
}

/*
 * GetPgClassDescriptor -- get a predefined tuple descriptor for pg_class
 * GetPgIndexDescriptor -- get a predefined tuple descriptor for pg_index
 *
 * We need this kluge because we have to be able to access non-fixed-width
 * fields of pg_class and pg_index before we have the standard catalog caches
 * available.  We use predefined data that's set up in just the same way as
 * the bootstrapped reldescs used by formrdesc().  The resulting tupdesc is
 * not 100% kosher: it does not have the correct rowtype OID in tdtypeid, nor
 * does it have a TupleConstr field.  But it's good enough for the purpose of
 * extracting fields.
 */
static TupleDesc
BuildHardcodedDescriptor(int natts, const FormData_pg_attribute *attrs)
{
	TupleDesc	result;
	MemoryContext oldcxt;
	int			i;

	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);

	result = CreateTemplateTupleDesc(natts);
	result->tdtypeid = RECORDOID;	/* not right, but we don't care */
	result->tdtypmod = -1;

	for (i = 0; i < natts; i++)
	{
		memcpy(TupleDescAttr(result, i), &attrs[i], ATTRIBUTE_FIXED_PART_SIZE);

		populate_compact_attribute(result, i);
	}

	TupleDescFinalize(result);

	/* Note: we don't bother to set up a TupleConstr entry */

	MemoryContextSwitchTo(oldcxt);

	return result;
}

static TupleDesc
GetPgClassDescriptor(void)
{
	static TupleDesc pgclassdesc = NULL;

	/* Already done? */
	if (pgclassdesc == NULL)
		pgclassdesc = BuildHardcodedDescriptor(Natts_pg_class,
											   Desc_pg_class);

	return pgclassdesc;
}

static TupleDesc
GetPgIndexDescriptor(void)
{
	static TupleDesc pgindexdesc = NULL;

	/* Already done? */
	if (pgindexdesc == NULL)
		pgindexdesc = BuildHardcodedDescriptor(Natts_pg_index,
											   Desc_pg_index);

	return pgindexdesc;
}

/*
 * Load any default attribute value definitions for the relation.
 *
 * ndef is the number of attributes that were marked atthasdef.
 *
 * Note: we don't make it a hard error to be missing some pg_attrdef records.
 * We can limp along as long as nothing needs to use the default value.  Code
 * that fails to find an expected AttrDefault record should throw an error.
 */
static void
AttrDefaultFetch(Relation relation, int ndef)
{
	AttrDefault *attrdef;
	Relation	adrel;
	SysScanDesc adscan;
	ScanKeyData skey;
	HeapTuple	htup;
	int			found = 0;

	/* Allocate array with room for as many entries as expected */
	attrdef = (AttrDefault *)
		MemoryContextAllocZero(CacheMemoryContext,
							   ndef * sizeof(AttrDefault));

	/* Search pg_attrdef for relevant entries */
	ScanKeyInit(&skey,
				Anum_pg_attrdef_adrelid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(RelationGetRelid(relation)));

	adrel = table_open(AttrDefaultRelationId, AccessShareLock);
	adscan = systable_beginscan(adrel, AttrDefaultIndexId, true,
								NULL, 1, &skey);

	while (HeapTupleIsValid(htup = systable_getnext(adscan)))
	{
		Form_pg_attrdef adform = (Form_pg_attrdef) GETSTRUCT(htup);
		Datum		val;
		bool		isnull;

		/* protect limited size of array */
		if (found >= ndef)
		{
			elog(WARNING, "unexpected pg_attrdef record found for attribute %d of relation \"%s\"",
				 adform->adnum, RelationGetRelationName(relation));
			break;
		}

		val = fastgetattr(htup,
						  Anum_pg_attrdef_adbin,
						  adrel->rd_att, &isnull);
		if (isnull)
			elog(WARNING, "null adbin for attribute %d of relation \"%s\"",
				 adform->adnum, RelationGetRelationName(relation));
		else
		{
			/* detoast and convert to cstring in caller's context */
			char	   *s = TextDatumGetCString(val);

			attrdef[found].adnum = adform->adnum;
			attrdef[found].adbin = MemoryContextStrdup(CacheMemoryContext, s);
			pfree(s);
			found++;
		}
	}

	systable_endscan(adscan);
	table_close(adrel, AccessShareLock);

	if (found != ndef)
		elog(WARNING, "%d pg_attrdef record(s) missing for relation \"%s\"",
			 ndef - found, RelationGetRelationName(relation));

	/*
	 * Sort the AttrDefault entries by adnum, for the convenience of
	 * equalTupleDescs().  (Usually, they already will be in order, but this
	 * might not be so if systable_getnext isn't using an index.)
	 */
	if (found > 1)
		qsort(attrdef, found, sizeof(AttrDefault), AttrDefaultCmp);

	/* Install array only after it's fully valid */
	relation->rd_att->constr->defval = attrdef;
	relation->rd_att->constr->num_defval = found;
}

/*
 * qsort comparator to sort AttrDefault entries by adnum
 */
static int
AttrDefaultCmp(const void *a, const void *b)
{
	const AttrDefault *ada = (const AttrDefault *) a;
	const AttrDefault *adb = (const AttrDefault *) b;

	return pg_cmp_s16(ada->adnum, adb->adnum);
}

/*
 * Load any check constraints for the relation, and update not-null validity
 * of invalid constraints.
 *
 * As with defaults, if we don't find the expected number of them, just warn
 * here.  The executor should throw an error if an INSERT/UPDATE is attempted.
 */
static void
CheckNNConstraintFetch(Relation relation)
{
	ConstrCheck *check;
	int			ncheck = relation->rd_rel->relchecks;
	Relation	conrel;
	SysScanDesc conscan;
	ScanKeyData skey[1];
	HeapTuple	htup;
	int			found = 0;

	/* Allocate array with room for as many entries as expected, if needed */
	if (ncheck > 0)
		check = (ConstrCheck *)
			MemoryContextAllocZero(CacheMemoryContext,
								   ncheck * sizeof(ConstrCheck));
	else
		check = NULL;

	/* Search pg_constraint for relevant entries */
	ScanKeyInit(&skey[0],
				Anum_pg_constraint_conrelid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(RelationGetRelid(relation)));

	conrel = table_open(ConstraintRelationId, AccessShareLock);
	conscan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId, true,
								 NULL, 1, skey);

	while (HeapTupleIsValid(htup = systable_getnext(conscan)))
	{
		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(htup);
		Datum		val;
		bool		isnull;

		/*
		 * If this is a not-null constraint, then only look at it if it's
		 * invalid, and if so, mark the TupleDesc entry as known invalid.
		 * Otherwise move on.  We'll mark any remaining columns that are still
		 * in UNKNOWN state as known valid later.  This allows us not to have
		 * to extract the attnum from this constraint tuple in the vast
		 * majority of cases.
		 */
		if (conform->contype == CONSTRAINT_NOTNULL)
		{
			if (!conform->convalidated)
			{
				AttrNumber	attnum;

				attnum = extractNotNullColumn(htup);
				Assert(relation->rd_att->compact_attrs[attnum - 1].attnullability ==
					   ATTNULLABLE_UNKNOWN);
				relation->rd_att->compact_attrs[attnum - 1].attnullability =
					ATTNULLABLE_INVALID;
			}

			continue;
		}

		/* For what follows, consider check constraints only */
		if (conform->contype != CONSTRAINT_CHECK)
			continue;

		/* protect limited size of array */
		if (found >= ncheck)
		{
			elog(WARNING, "unexpected pg_constraint record found for relation \"%s\"",
				 RelationGetRelationName(relation));
			break;
		}

		/* Grab and test conbin is actually set */
		val = fastgetattr(htup,
						  Anum_pg_constraint_conbin,
						  conrel->rd_att, &isnull);
		if (isnull)
			elog(WARNING, "null conbin for relation \"%s\"",
				 RelationGetRelationName(relation));
		else
		{
			/* detoast and convert to cstring in caller's context */
			char	   *s = TextDatumGetCString(val);

			check[found].ccenforced = conform->conenforced;
			check[found].ccvalid = conform->convalidated;
			check[found].ccnoinherit = conform->connoinherit;
			check[found].ccname = MemoryContextStrdup(CacheMemoryContext,
													  NameStr(conform->conname));
			check[found].ccbin = MemoryContextStrdup(CacheMemoryContext, s);

			pfree(s);
			found++;
		}
	}

	systable_endscan(conscan);
	table_close(conrel, AccessShareLock);

	if (found != ncheck)
		elog(WARNING, "%d pg_constraint record(s) missing for relation \"%s\"",
			 ncheck - found, RelationGetRelationName(relation));

	/*
	 * Sort the records by name.  This ensures that CHECKs are applied in a
	 * deterministic order, and it also makes equalTupleDescs() faster.
	 */
	if (found > 1)
		qsort(check, found, sizeof(ConstrCheck), CheckConstraintCmp);

	/* Install array only after it's fully valid */
	relation->rd_att->constr->check = check;
	relation->rd_att->constr->num_check = found;
}

/*
 * qsort comparator to sort ConstrCheck entries by name
 */
static int
CheckConstraintCmp(const void *a, const void *b)
{
	const ConstrCheck *ca = (const ConstrCheck *) a;
	const ConstrCheck *cb = (const ConstrCheck *) b;

	return strcmp(ca->ccname, cb->ccname);
}

/*
 * RelationGetFKeyList -- get a list of foreign key info for the relation
 *
 * Returns a list of ForeignKeyCacheInfo structs, one per FK constraining
 * the given relation.  This data is a direct copy of relevant fields from
 * pg_constraint.  The list items are in no particular order.
 *
 * CAUTION: the returned list is part of the relcache's data, and could
 * vanish in a relcache entry reset.  Callers must inspect or copy it
 * before doing anything that might trigger a cache flush, such as
 * system catalog accesses.  copyObject() can be used if desired.
 * (We define it this way because current callers want to filter and
 * modify the list entries anyway, so copying would be a waste of time.)
 */
List *
RelationGetFKeyList(Relation relation)
{
	List	   *result;
	Relation	conrel;
	SysScanDesc conscan;
	ScanKeyData skey;
	HeapTuple	htup;
	List	   *oldlist;
	MemoryContext oldcxt;

	/* Quick exit if we already computed the list. */
	if (relation->rd_fkeyvalid)
		return relation->rd_fkeylist;

	/*
	 * We build the list we intend to return (in the caller's context) while
	 * doing the scan.  After successfully completing the scan, we copy that
	 * list into the relcache entry.  This avoids cache-context memory leakage
	 * if we get some sort of error partway through.
	 */
	result = NIL;

	/* Prepare to scan pg_constraint for entries having conrelid = this rel. */
	ScanKeyInit(&skey,
				Anum_pg_constraint_conrelid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(RelationGetRelid(relation)));

	conrel = table_open(ConstraintRelationId, AccessShareLock);
	conscan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId, true,
								 NULL, 1, &skey);

	while (HeapTupleIsValid(htup = systable_getnext(conscan)))
	{
		Form_pg_constraint constraint = (Form_pg_constraint) GETSTRUCT(htup);
		ForeignKeyCacheInfo *info;

		/* consider only foreign keys */
		if (constraint->contype != CONSTRAINT_FOREIGN)
			continue;

		info = makeNode(ForeignKeyCacheInfo);
		info->conoid = constraint->oid;
		info->conrelid = constraint->conrelid;
		info->confrelid = constraint->confrelid;
		info->conenforced = constraint->conenforced;

		DeconstructFkConstraintRow(htup, &info->nkeys,
								   info->conkey,
								   info->confkey,
								   info->conpfeqop,
								   NULL, NULL, NULL, NULL);

		/* Add FK's node to the result list */
		result = lappend(result, info);
	}

	systable_endscan(conscan);
	table_close(conrel, AccessShareLock);

	/* Now save a copy of the completed list in the relcache entry. */
	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
	oldlist = relation->rd_fkeylist;
	relation->rd_fkeylist = copyObject(result);
	relation->rd_fkeyvalid = true;
	MemoryContextSwitchTo(oldcxt);

	/* Don't leak the old list, if there is one */
	list_free_deep(oldlist);

	return result;
}

/*
 * RelationGetIndexList -- get a list of OIDs of indexes on this relation
 *
 * The index list is created only if someone requests it.  We scan pg_index
 * to find relevant indexes, and add the list to the relcache entry so that
 * we won't have to compute it again.  Note that shared cache inval of a
 * relcache entry will delete the old list and set rd_indexvalid to false,
 * so that we must recompute the index list on next request.  This handles
 * creation or deletion of an index.
 *
 * Indexes that are marked not indislive are omitted from the returned list.
 * Such indexes are expected to be dropped momentarily, and should not be
 * touched at all by any caller of this function.
 *
 * The returned list is guaranteed to be sorted in order by OID.  This is
 * needed by the executor, since for index types that we obtain exclusive
 * locks on when updating the index, all backends must lock the indexes in
 * the same order or we will get deadlocks (see ExecOpenIndices()).  Any
 * consistent ordering would do, but ordering by OID is easy.
 *
 * Since shared cache inval causes the relcache's copy of the list to go away,
 * we return a copy of the list palloc'd in the caller's context.  The caller
 * may list_free() the returned list after scanning it. This is necessary
 * since the caller will typically be doing syscache lookups on the relevant
 * indexes, and syscache lookup could cause SI messages to be processed!
 *
 * In exactly the same way, we update rd_pkindex, which is the OID of the
 * relation's primary key index if any, else InvalidOid; and rd_replidindex,
 * which is the pg_class OID of an index to be used as the relation's
 * replication identity index, or InvalidOid if there is no such index.
 */
List *
RelationGetIndexList(Relation relation)
{
	Relation	indrel;
	SysScanDesc indscan;
	ScanKeyData skey;
	HeapTuple	htup;
	List	   *result;
	List	   *oldlist;
	char		replident = relation->rd_rel->relreplident;
	Oid			pkeyIndex = InvalidOid;
	Oid			candidateIndex = InvalidOid;
	bool		pkdeferrable = false;
	MemoryContext oldcxt;

	/* Quick exit if we already computed the list. */
	if (relation->rd_indexvalid)
		return list_copy(relation->rd_indexlist);

	/*
	 * We build the list we intend to return (in the caller's context) while
	 * doing the scan.  After successfully completing the scan, we copy that
	 * list into the relcache entry.  This avoids cache-context memory leakage
	 * if we get some sort of error partway through.
	 */
	result = NIL;

	/* Prepare to scan pg_index for entries having indrelid = this rel. */
	ScanKeyInit(&skey,
				Anum_pg_index_indrelid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(RelationGetRelid(relation)));

	indrel = table_open(IndexRelationId, AccessShareLock);
	indscan = systable_beginscan(indrel, IndexIndrelidIndexId, true,
								 NULL, 1, &skey);

	while (HeapTupleIsValid(htup = systable_getnext(indscan)))
	{
		Form_pg_index index = (Form_pg_index) GETSTRUCT(htup);

		/*
		 * Ignore any indexes that are currently being dropped.  This will
		 * prevent them from being searched, inserted into, or considered in
		 * HOT-safety decisions.  It's unsafe to touch such an index at all
		 * since its catalog entries could disappear at any instant.
		 */
		if (!index->indislive)
			continue;

		/* add index's OID to result list */
		result = lappend_oid(result, index->indexrelid);

		/*
		 * Non-unique or predicate indexes aren't interesting for either oid
		 * indexes or replication identity indexes, so don't check them.
		 * Deferred ones are not useful for replication identity either; but
		 * we do include them if they are PKs.
		 */
		if (!index->indisunique ||
			!heap_attisnull(htup, Anum_pg_index_indpred, NULL))
			continue;

		/*
		 * Remember primary key index, if any.  For regular tables we do this
		 * only if the index is valid; but for partitioned tables, then we do
		 * it even if it's invalid.
		 *
		 * The reason for returning invalid primary keys for partitioned
		 * tables is that we need it to prevent drop of not-null constraints
		 * that may underlie such a primary key, which is only a problem for
		 * partitioned tables.
		 */
		if (index->indisprimary &&
			(index->indisvalid ||
			 relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE))
		{
			pkeyIndex = index->indexrelid;
			pkdeferrable = !index->indimmediate;
		}

		if (!index->indimmediate)
			continue;

		if (!index->indisvalid)
			continue;

		/* remember explicitly chosen replica index */
		if (index->indisreplident)
			candidateIndex = index->indexrelid;
	}

	systable_endscan(indscan);

	table_close(indrel, AccessShareLock);

	/* Sort the result list into OID order, per API spec. */
	list_sort(result, list_oid_cmp);

	/* Now save a copy of the completed list in the relcache entry. */
	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
	oldlist = relation->rd_indexlist;
	relation->rd_indexlist = list_copy(result);
	relation->rd_pkindex = pkeyIndex;
	relation->rd_ispkdeferrable = pkdeferrable;
	if (replident == REPLICA_IDENTITY_DEFAULT && OidIsValid(pkeyIndex) && !pkdeferrable)
		relation->rd_replidindex = pkeyIndex;
	else if (replident == REPLICA_IDENTITY_INDEX && OidIsValid(candidateIndex))
		relation->rd_replidindex = candidateIndex;
	else
		relation->rd_replidindex = InvalidOid;
	relation->rd_indexvalid = true;
	MemoryContextSwitchTo(oldcxt);

	/* Don't leak the old list, if there is one */
	list_free(oldlist);

	return result;
}

/*
 * RelationGetStatExtList
 *		get a list of OIDs of statistics objects on this relation
 *
 * The statistics list is created only if someone requests it, in a way
 * similar to RelationGetIndexList().  We scan pg_statistic_ext to find
 * relevant statistics, and add the list to the relcache entry so that we
 * won't have to compute it again.  Note that shared cache inval of a
 * relcache entry will delete the old list and set rd_statvalid to 0,
 * so that we must recompute the statistics list on next request.  This
 * handles creation or deletion of a statistics object.
 *
 * The returned list is guaranteed to be sorted in order by OID, although
 * this is not currently needed.
 *
 * Since shared cache inval causes the relcache's copy of the list to go away,
 * we return a copy of the list palloc'd in the caller's context.  The caller
 * may list_free() the returned list after scanning it. This is necessary
 * since the caller will typically be doing syscache lookups on the relevant
 * statistics, and syscache lookup could cause SI messages to be processed!
 */
List *
RelationGetStatExtList(Relation relation)
{
	Relation	indrel;
	SysScanDesc indscan;
	ScanKeyData skey;
	HeapTuple	htup;
	List	   *result;
	List	   *oldlist;
	MemoryContext oldcxt;

	/* Quick exit if we already computed the list. */
	if (relation->rd_statvalid != 0)
		return list_copy(relation->rd_statlist);

	/*
	 * We build the list we intend to return (in the caller's context) while
	 * doing the scan.  After successfully completing the scan, we copy that
	 * list into the relcache entry.  This avoids cache-context memory leakage
	 * if we get some sort of error partway through.
	 */
	result = NIL;

	/*
	 * Prepare to scan pg_statistic_ext for entries having stxrelid = this
	 * rel.
	 */
	ScanKeyInit(&skey,
				Anum_pg_statistic_ext_stxrelid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(RelationGetRelid(relation)));

	indrel = table_open(StatisticExtRelationId, AccessShareLock);
	indscan = systable_beginscan(indrel, StatisticExtRelidIndexId, true,
								 NULL, 1, &skey);

	while (HeapTupleIsValid(htup = systable_getnext(indscan)))
	{
		Oid			oid = ((Form_pg_statistic_ext) GETSTRUCT(htup))->oid;

		result = lappend_oid(result, oid);
	}

	systable_endscan(indscan);

	table_close(indrel, AccessShareLock);

	/* Sort the result list into OID order, per API spec. */
	list_sort(result, list_oid_cmp);

	/* Now save a copy of the completed list in the relcache entry. */
	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
	oldlist = relation->rd_statlist;
	relation->rd_statlist = list_copy(result);

	relation->rd_statvalid = true;
	MemoryContextSwitchTo(oldcxt);

	/* Don't leak the old list, if there is one */
	list_free(oldlist);

	return result;
}

/*
 * RelationGetPrimaryKeyIndex -- get OID of the relation's primary key index
 *
 * Returns InvalidOid if there is no such index, or if the primary key is
 * DEFERRABLE and the caller isn't OK with that.
 */
Oid
RelationGetPrimaryKeyIndex(Relation relation, bool deferrable_ok)
{
	List	   *ilist;

	if (!relation->rd_indexvalid)
	{
		/* RelationGetIndexList does the heavy lifting. */
		ilist = RelationGetIndexList(relation);
		list_free(ilist);
		Assert(relation->rd_indexvalid);
	}

	if (deferrable_ok)
		return relation->rd_pkindex;
	else if (relation->rd_ispkdeferrable)
		return InvalidOid;
	return relation->rd_pkindex;
}

/*
 * RelationGetReplicaIndex -- get OID of the relation's replica identity index
 *
 * Returns InvalidOid if there is no such index.
 */
Oid
RelationGetReplicaIndex(Relation relation)
{
	List	   *ilist;

	if (!relation->rd_indexvalid)
	{
		/* RelationGetIndexList does the heavy lifting. */
		ilist = RelationGetIndexList(relation);
		list_free(ilist);
		Assert(relation->rd_indexvalid);
	}

	return relation->rd_replidindex;
}

/*
 * RelationGetIndexExpressions -- get the index expressions for an index
 *
 * We cache the result of transforming pg_index.indexprs into a node tree.
 * If the rel is not an index or has no expressional columns, we return NIL.
 * Otherwise, the returned tree is copied into the caller's memory context.
 * (We don't want to return a pointer to the relcache copy, since it could
 * disappear due to relcache invalidation.)
 */
List *
RelationGetIndexExpressions(Relation relation)
{
	List	   *result;
	Datum		exprsDatum;
	bool		isnull;
	char	   *exprsString;
	MemoryContext oldcxt;

	/* Quick exit if we already computed the result. */
	if (relation->rd_indexprs)
		return copyObject(relation->rd_indexprs);

	/* Quick exit if there is nothing to do. */
	if (relation->rd_indextuple == NULL ||
		heap_attisnull(relation->rd_indextuple, Anum_pg_index_indexprs, NULL))
		return NIL;

	/*
	 * We build the tree we intend to return in the caller's context. After
	 * successfully completing the work, we copy it into the relcache entry.
	 * This avoids problems if we get some sort of error partway through.
	 */
	exprsDatum = heap_getattr(relation->rd_indextuple,
							  Anum_pg_index_indexprs,
							  GetPgIndexDescriptor(),
							  &isnull);
	Assert(!isnull);
	exprsString = TextDatumGetCString(exprsDatum);
	result = (List *) stringToNode(exprsString);
	pfree(exprsString);

	/*
	 * Run the expressions through eval_const_expressions. This is not just an
	 * optimization, but is necessary, because the planner will be comparing
	 * them to similarly-processed qual clauses, and may fail to detect valid
	 * matches without this.  We must not use canonicalize_qual, however,
	 * since these aren't qual expressions.
	 */
	result = (List *) eval_const_expressions(NULL, (Node *) result);

	/* May as well fix opfuncids too */
	fix_opfuncids((Node *) result);

	/* Now save a copy of the completed tree in the relcache entry. */
	oldcxt = MemoryContextSwitchTo(relation->rd_indexcxt);
	relation->rd_indexprs = copyObject(result);
	MemoryContextSwitchTo(oldcxt);

	return result;
}

List *
RelationGetIndexExpressionsExpand(Relation relation)
{
	List	   *result;
	MemoryContext oldcxt;
	bool		isnull;
	Datum		heapRelidDatum;
	Oid         heapRelid;

	/* Quick exit if we already computed the result. */
	if (relation->rd_indexprsExpand)
		return copyObject(relation->rd_indexprsExpand);

	/* Quick exit if there is nothing to do. */
	if (relation->rd_indextuple == NULL ||
		heap_attisnull(relation->rd_indextuple, Anum_pg_index_indexprs, NULL))
		return NIL;

	result = RelationGetIndexExpressions(relation);
	if (result == NIL)
		return NIL;

	heapRelidDatum = heap_getattr(relation->rd_indextuple,
							 Anum_pg_index_indrelid,
							 GetPgIndexDescriptor(),
							 &isnull);
	Assert(!isnull);
	heapRelid = DatumGetObjectId(heapRelidDatum);

	result = ExpandVirtualGeneratedColumns(result, NULL, heapRelid);

	/* Now save a copy of the completed tree in the relcache entry. */
	oldcxt = MemoryContextSwitchTo(relation->rd_indexcxt);
	relation->rd_indexprsExpand = copyObject(result);
	MemoryContextSwitchTo(oldcxt);

	return result;
}

/*
 * RelationGetDummyIndexExpressions -- get dummy expressions for an index
 *
 * Return a list of dummy expressions (just Const nodes) with the same
 * types/typmods/collations as the index's real expressions.  This is
 * useful in situations where we don't want to run any user-defined code.
 */
List *
RelationGetDummyIndexExpressions(Relation relation)
{
	List	   *result;
	Datum		exprsDatum;
	bool		isnull;
	char	   *exprsString;
	List	   *rawExprs;
	ListCell   *lc;

	/* Quick exit if there is nothing to do. */
	if (relation->rd_indextuple == NULL ||
		heap_attisnull(relation->rd_indextuple, Anum_pg_index_indexprs, NULL))
		return NIL;

	/* Extract raw node tree(s) from index tuple. */
	exprsDatum = heap_getattr(relation->rd_indextuple,
							  Anum_pg_index_indexprs,
							  GetPgIndexDescriptor(),
							  &isnull);
	Assert(!isnull);
	exprsString = TextDatumGetCString(exprsDatum);
	rawExprs = (List *) stringToNode(exprsString);
	pfree(exprsString);

	/* Construct null Consts; the typlen and typbyval are arbitrary. */
	result = NIL;
	foreach(lc, rawExprs)
	{
		Node	   *rawExpr = (Node *) lfirst(lc);

		result = lappend(result,
						 makeConst(exprType(rawExpr),
								   exprTypmod(rawExpr),
								   exprCollation(rawExpr),
								   1,
								   (Datum) 0,
								   true,
								   true));
	}

	return result;
}

/*
 * RelationGetIndexPredicate -- get the index predicate for an index
 *
 * We cache the result of transforming pg_index.indpred into an implicit-AND
 * node tree (suitable for use in planning).
 * If the rel is not an index or has no predicate, we return NIL.
 * Otherwise, the returned tree is copied into the caller's memory context.
 * (We don't want to return a pointer to the relcache copy, since it could
 * disappear due to relcache invalidation.)
 */
List *
RelationGetIndexPredicate(Relation relation)
{
	List	   *result;
	Datum		predDatum;
	bool		isnull;
	char	   *predString;
	MemoryContext oldcxt;

	/* Quick exit if we already computed the result. */
	if (relation->rd_indpred)
		return copyObject(relation->rd_indpred);

	/* Quick exit if there is nothing to do. */
	if (relation->rd_indextuple == NULL ||
		heap_attisnull(relation->rd_indextuple, Anum_pg_index_indpred, NULL))
		return NIL;

	/*
	 * We build the tree we intend to return in the caller's context. After
	 * successfully completing the work, we copy it into the relcache entry.
	 * This avoids problems if we get some sort of error partway through.
	 */
	predDatum = heap_getattr(relation->rd_indextuple,
							 Anum_pg_index_indpred,
							 GetPgIndexDescriptor(),
							 &isnull);
	Assert(!isnull);
	predString = TextDatumGetCString(predDatum);
	result = (List *) stringToNode(predString);
	pfree(predString);

	/*
	 * Run the expression through const-simplification and canonicalization.
	 * This is not just an optimization, but is necessary, because the planner
	 * will be comparing it to similarly-processed qual clauses, and may fail
	 * to detect valid matches without this.  This must match the processing
	 * done to qual clauses in preprocess_expression()!  (We can skip the
	 * stuff involving subqueries, however, since we don't allow any in index
	 * predicates.)
	 */
	result = (List *) eval_const_expressions(NULL, (Node *) result);

	result = (List *) canonicalize_qual((Expr *) result, false);

	/* Also convert to implicit-AND format */
	result = make_ands_implicit((Expr *) result);

	/* May as well fix opfuncids too */
	fix_opfuncids((Node *) result);

	/* Now save a copy of the completed tree in the relcache entry. */
	oldcxt = MemoryContextSwitchTo(relation->rd_indexcxt);
	relation->rd_indpred = copyObject(result);
	MemoryContextSwitchTo(oldcxt);

	return result;
}

List *
RelationGetIndexPredicateExpand(Relation relation)
{
	List	   *result;
	MemoryContext oldcxt;
	bool		isnull;
	Datum		heapRelidDatum;
	Oid         heapRelid;

	/* Quick exit if we already computed the result. */
	if (relation->rd_indpredExpand)
		return copyObject(relation->rd_indpredExpand);

	/* Quick exit if there is nothing to do. */
	if (relation->rd_indextuple == NULL ||
		heap_attisnull(relation->rd_indextuple, Anum_pg_index_indpred, NULL))
		return NIL;

	result = RelationGetIndexPredicate(relation);
	if (result == NIL)
		return NIL;

	heapRelidDatum = heap_getattr(relation->rd_indextuple,
							 Anum_pg_index_indrelid,
							 GetPgIndexDescriptor(),
							 &isnull);
	Assert(!isnull);
	heapRelid = DatumGetObjectId(heapRelidDatum);

	result = ExpandVirtualGeneratedColumns(result, NULL, heapRelid);

	/* Now save a copy of the completed tree in the relcache entry. */
	oldcxt = MemoryContextSwitchTo(relation->rd_indexcxt);
	relation->rd_indpredExpand = copyObject(result);
	MemoryContextSwitchTo(oldcxt);

	return result;
}

/*
 * RelationGetIndexAttrBitmap -- get a bitmap of index attribute numbers
 *
 * The result has a bit set for each attribute used anywhere in the index
 * definitions of all the indexes on this relation.  (This includes not only
 * simple index keys, but attributes used in expressions and partial-index
 * predicates.)
 *
 * Depending on attrKind, a bitmap covering attnums for certain columns is
 * returned:
 *	INDEX_ATTR_BITMAP_KEY			Columns in non-partial unique indexes not
 *									in expressions (i.e., usable for FKs)
 *	INDEX_ATTR_BITMAP_PRIMARY_KEY	Columns in the table's primary key
 *									(beware: even if PK is deferrable!)
 *	INDEX_ATTR_BITMAP_IDENTITY_KEY	Columns in the table's replica identity
 *									index (empty if FULL)
 *	INDEX_ATTR_BITMAP_HOT_BLOCKING	Columns that block updates from being HOT
 *	INDEX_ATTR_BITMAP_SUMMARIZED	Columns included in summarizing indexes
 *
 * Attribute numbers are offset by FirstLowInvalidHeapAttributeNumber so that
 * we can include system attributes (e.g., OID) in the bitmap representation.
 *
 * Deferred indexes are considered for the primary key, but not for replica
 * identity.
 *
 * Caller had better hold at least RowExclusiveLock on the target relation
 * to ensure it is safe (deadlock-free) for us to take locks on the relation's
 * indexes.  Note that since the introduction of CREATE INDEX CONCURRENTLY,
 * that lock level doesn't guarantee a stable set of indexes, so we have to
 * be prepared to retry here in case of a change in the set of indexes.
 *
 * The returned result is palloc'd in the caller's memory context and should
 * be bms_free'd when not needed anymore.
 */
Bitmapset *
RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind)
{
	Bitmapset  *uindexattrs;	/* columns in unique indexes */
	Bitmapset  *pkindexattrs;	/* columns in the primary index */
	Bitmapset  *idindexattrs;	/* columns in the replica identity */
	Bitmapset  *hotblockingattrs;	/* columns with HOT blocking indexes */
	Bitmapset  *summarizedattrs;	/* columns with summarizing indexes */
	List	   *indexoidlist;
	List	   *newindexoidlist;
	Oid			relpkindex;
	Oid			relreplindex;
	ListCell   *l;
	MemoryContext oldcxt;

	/* Quick exit if we already computed the result. */
	if (relation->rd_attrsvalid)
	{
		switch (attrKind)
		{
			case INDEX_ATTR_BITMAP_KEY:
				return bms_copy(relation->rd_keyattr);
			case INDEX_ATTR_BITMAP_PRIMARY_KEY:
				return bms_copy(relation->rd_pkattr);
			case INDEX_ATTR_BITMAP_IDENTITY_KEY:
				return bms_copy(relation->rd_idattr);
			case INDEX_ATTR_BITMAP_HOT_BLOCKING:
				return bms_copy(relation->rd_hotblockingattr);
			case INDEX_ATTR_BITMAP_SUMMARIZED:
				return bms_copy(relation->rd_summarizedattr);
			default:
				elog(ERROR, "unknown attrKind %u", attrKind);
		}
	}

	/* Fast path if definitely no indexes */
	if (!RelationGetForm(relation)->relhasindex)
		return NULL;

	/*
	 * Get cached list of index OIDs. If we have to start over, we do so here.
	 */
restart:
	indexoidlist = RelationGetIndexList(relation);

	/* Fall out if no indexes (but relhasindex was set) */
	if (indexoidlist == NIL)
		return NULL;

	/*
	 * Copy the rd_pkindex and rd_replidindex values computed by
	 * RelationGetIndexList before proceeding.  This is needed because a
	 * relcache flush could occur inside index_open below, resetting the
	 * fields managed by RelationGetIndexList.  We need to do the work with
	 * stable values of these fields.
	 */
	relpkindex = relation->rd_pkindex;
	relreplindex = relation->rd_replidindex;

	/*
	 * For each index, add referenced attributes to indexattrs.
	 *
	 * Note: we consider all indexes returned by RelationGetIndexList, even if
	 * they are not indisready or indisvalid.  This is important because an
	 * index for which CREATE INDEX CONCURRENTLY has just started must be
	 * included in HOT-safety decisions (see README.HOT).  If a DROP INDEX
	 * CONCURRENTLY is far enough along that we should ignore the index, it
	 * won't be returned at all by RelationGetIndexList.
	 */
	uindexattrs = NULL;
	pkindexattrs = NULL;
	idindexattrs = NULL;
	hotblockingattrs = NULL;
	summarizedattrs = NULL;
	foreach(l, indexoidlist)
	{
		Oid			indexOid = lfirst_oid(l);
		Relation	indexDesc;
		Datum		datum;
		bool		isnull;
		Node	   *indexExpressions;
		Node	   *indexPredicate;
		int			i;
		bool		isKey;		/* candidate key */
		bool		isPK;		/* primary key */
		bool		isIDKey;	/* replica identity index */
		Bitmapset **attrs;

		indexDesc = index_open(indexOid, AccessShareLock);

		/*
		 * Extract index expressions and index predicate.  Note: Don't use
		 * RelationGetIndexExpressions()/RelationGetIndexPredicate(), because
		 * those might run constant expressions evaluation, which needs a
		 * snapshot, which we might not have here.  (Also, it's probably more
		 * sound to collect the bitmaps before any transformations that might
		 * eliminate columns, but the practical impact of this is limited.)
		 */

		datum = heap_getattr(indexDesc->rd_indextuple, Anum_pg_index_indexprs,
							 GetPgIndexDescriptor(), &isnull);
		if (!isnull)
			indexExpressions = stringToNode(TextDatumGetCString(datum));
		else
			indexExpressions = NULL;

		datum = heap_getattr(indexDesc->rd_indextuple, Anum_pg_index_indpred,
							 GetPgIndexDescriptor(), &isnull);
		if (!isnull)
			indexPredicate = stringToNode(TextDatumGetCString(datum));
		else
			indexPredicate = NULL;

		/* Can this index be referenced by a foreign key? */
		isKey = indexDesc->rd_index->indisunique &&
			indexExpressions == NULL &&
			indexPredicate == NULL;

		/* Is this a primary key? */
		isPK = (indexOid == relpkindex);

		/* Is this index the configured (or default) replica identity? */
		isIDKey = (indexOid == relreplindex);

		/*
		 * If the index is summarizing, it doesn't block HOT updates, but we
		 * may still need to update it (if the attributes were modified). So
		 * decide which bitmap we'll update in the following loop.
		 */
		if (indexDesc->rd_indam->amsummarizing)
			attrs = &summarizedattrs;
		else
			attrs = &hotblockingattrs;

		/* Collect simple attribute references */
		for (i = 0; i < indexDesc->rd_index->indnatts; i++)
		{
			int			attrnum = indexDesc->rd_index->indkey.values[i];

			/*
			 * Since we have covering indexes with non-key columns, we must
			 * handle them accurately here. non-key columns must be added into
			 * hotblockingattrs or summarizedattrs, since they are in index,
			 * and update shouldn't miss them.
			 *
			 * Summarizing indexes do not block HOT, but do need to be updated
			 * when the column value changes, thus require a separate
			 * attribute bitmapset.
			 *
			 * Obviously, non-key columns couldn't be referenced by foreign
			 * key or identity key. Hence we do not include them into
			 * uindexattrs, pkindexattrs and idindexattrs bitmaps.
			 */
			if (attrnum != 0)
			{
				*attrs = bms_add_member(*attrs,
										attrnum - FirstLowInvalidHeapAttributeNumber);

				if (isKey && i < indexDesc->rd_index->indnkeyatts)
					uindexattrs = bms_add_member(uindexattrs,
												 attrnum - FirstLowInvalidHeapAttributeNumber);

				if (isPK && i < indexDesc->rd_index->indnkeyatts)
					pkindexattrs = bms_add_member(pkindexattrs,
												  attrnum - FirstLowInvalidHeapAttributeNumber);

				if (isIDKey && i < indexDesc->rd_index->indnkeyatts)
					idindexattrs = bms_add_member(idindexattrs,
												  attrnum - FirstLowInvalidHeapAttributeNumber);
			}
		}

		/* Collect all attributes used in expressions, too */
		pull_varattnos(indexExpressions, 1, attrs);

		/* Collect all attributes in the index predicate, too */
		pull_varattnos(indexPredicate, 1, attrs);

		index_close(indexDesc, AccessShareLock);
	}

	/*
	 * During one of the index_opens in the above loop, we might have received
	 * a relcache flush event on this relcache entry, which might have been
	 * signaling a change in the rel's index list.  If so, we'd better start
	 * over to ensure we deliver up-to-date attribute bitmaps.
	 */
	newindexoidlist = RelationGetIndexList(relation);
	if (equal(indexoidlist, newindexoidlist) &&
		relpkindex == relation->rd_pkindex &&
		relreplindex == relation->rd_replidindex)
	{
		/* Still the same index set, so proceed */
		list_free(newindexoidlist);
		list_free(indexoidlist);
	}
	else
	{
		/* Gotta do it over ... might as well not leak memory */
		list_free(newindexoidlist);
		list_free(indexoidlist);
		bms_free(uindexattrs);
		bms_free(pkindexattrs);
		bms_free(idindexattrs);
		bms_free(hotblockingattrs);
		bms_free(summarizedattrs);

		goto restart;
	}

	/* Don't leak the old values of these bitmaps, if any */
	relation->rd_attrsvalid = false;
	bms_free(relation->rd_keyattr);
	relation->rd_keyattr = NULL;
	bms_free(relation->rd_pkattr);
	relation->rd_pkattr = NULL;
	bms_free(relation->rd_idattr);
	relation->rd_idattr = NULL;
	bms_free(relation->rd_hotblockingattr);
	relation->rd_hotblockingattr = NULL;
	bms_free(relation->rd_summarizedattr);
	relation->rd_summarizedattr = NULL;

	/*
	 * Now save copies of the bitmaps in the relcache entry.  We intentionally
	 * set rd_attrsvalid last, because that's the one that signals validity of
	 * the values; if we run out of memory before making that copy, we won't
	 * leave the relcache entry looking like the other ones are valid but
	 * empty.
	 */
	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
	relation->rd_keyattr = bms_copy(uindexattrs);
	relation->rd_pkattr = bms_copy(pkindexattrs);
	relation->rd_idattr = bms_copy(idindexattrs);
	relation->rd_hotblockingattr = bms_copy(hotblockingattrs);
	relation->rd_summarizedattr = bms_copy(summarizedattrs);
	relation->rd_attrsvalid = true;
	MemoryContextSwitchTo(oldcxt);

	/* We return our original working copy for caller to play with */
	switch (attrKind)
	{
		case INDEX_ATTR_BITMAP_KEY:
			return uindexattrs;
		case INDEX_ATTR_BITMAP_PRIMARY_KEY:
			return pkindexattrs;
		case INDEX_ATTR_BITMAP_IDENTITY_KEY:
			return idindexattrs;
		case INDEX_ATTR_BITMAP_HOT_BLOCKING:
			return hotblockingattrs;
		case INDEX_ATTR_BITMAP_SUMMARIZED:
			return summarizedattrs;
		default:
			elog(ERROR, "unknown attrKind %u", attrKind);
			return NULL;
	}
}

/*
 * RelationGetIdentityKeyBitmap -- get a bitmap of replica identity attribute
 * numbers
 *
 * A bitmap of index attribute numbers for the configured replica identity
 * index is returned.
 *
 * See also comments of RelationGetIndexAttrBitmap().
 *
 * This is a special purpose function used during logical replication. Here,
 * unlike RelationGetIndexAttrBitmap(), we don't acquire a lock on the required
 * index as we build the cache entry using a historic snapshot and all the
 * later changes are absorbed while decoding WAL. Due to this reason, we don't
 * need to retry here in case of a change in the set of indexes.
 */
Bitmapset *
RelationGetIdentityKeyBitmap(Relation relation)
{
	Bitmapset  *idindexattrs = NULL;	/* columns in the replica identity */
	Relation	indexDesc;
	int			i;
	Oid			replidindex;
	MemoryContext oldcxt;

	/* Quick exit if we already computed the result */
	if (relation->rd_idattr != NULL)
		return bms_copy(relation->rd_idattr);

	/* Fast path if definitely no indexes */
	if (!RelationGetForm(relation)->relhasindex)
		return NULL;

	/* Historic snapshot must be set. */
	Assert(HistoricSnapshotActive());

	replidindex = RelationGetReplicaIndex(relation);

	/* Fall out if there is no replica identity index */
	if (!OidIsValid(replidindex))
		return NULL;

	/* Look up the description for the replica identity index */
	indexDesc = RelationIdGetRelation(replidindex);

	if (!RelationIsValid(indexDesc))
		elog(ERROR, "could not open relation with OID %u",
			 relation->rd_replidindex);

	/* Add referenced attributes to idindexattrs */
	for (i = 0; i < indexDesc->rd_index->indnatts; i++)
	{
		int			attrnum = indexDesc->rd_index->indkey.values[i];

		/*
		 * We don't include non-key columns into idindexattrs bitmaps. See
		 * RelationGetIndexAttrBitmap.
		 */
		if (attrnum != 0)
		{
			if (i < indexDesc->rd_index->indnkeyatts)
				idindexattrs = bms_add_member(idindexattrs,
											  attrnum - FirstLowInvalidHeapAttributeNumber);
		}
	}

	RelationClose(indexDesc);

	/* Don't leak the old values of these bitmaps, if any */
	bms_free(relation->rd_idattr);
	relation->rd_idattr = NULL;

	/* Now save copy of the bitmap in the relcache entry */
	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
	relation->rd_idattr = bms_copy(idindexattrs);
	MemoryContextSwitchTo(oldcxt);

	/* We return our original working copy for caller to play with */
	return idindexattrs;
}

/*
 * RelationGetExclusionInfo -- get info about index's exclusion constraint
 *
 * This should be called only for an index that is known to have an associated
 * exclusion constraint or primary key/unique constraint using WITHOUT
 * OVERLAPS.
 *
 * It returns arrays (palloc'd in caller's context) of the exclusion operator
 * OIDs, their underlying functions' OIDs, and their strategy numbers in the
 * index's opclasses.  We cache all this information since it requires a fair
 * amount of work to get.
 */
void
RelationGetExclusionInfo(Relation indexRelation,
						 Oid **operators,
						 Oid **procs,
						 uint16 **strategies)
{
	int			indnkeyatts;
	Oid		   *ops;
	Oid		   *funcs;
	uint16	   *strats;
	Relation	conrel;
	SysScanDesc conscan;
	ScanKeyData skey[1];
	HeapTuple	htup;
	bool		found;
	MemoryContext oldcxt;
	int			i;

	indnkeyatts = IndexRelationGetNumberOfKeyAttributes(indexRelation);

	/* Allocate result space in caller context */
	*operators = ops = palloc_array(Oid, indnkeyatts);
	*procs = funcs = palloc_array(Oid, indnkeyatts);
	*strategies = strats = palloc_array(uint16, indnkeyatts);

	/* Quick exit if we have the data cached already */
	if (indexRelation->rd_exclstrats != NULL)
	{
		memcpy(ops, indexRelation->rd_exclops, sizeof(Oid) * indnkeyatts);
		memcpy(funcs, indexRelation->rd_exclprocs, sizeof(Oid) * indnkeyatts);
		memcpy(strats, indexRelation->rd_exclstrats, sizeof(uint16) * indnkeyatts);
		return;
	}

	/*
	 * Search pg_constraint for the constraint associated with the index. To
	 * make this not too painfully slow, we use the index on conrelid; that
	 * will hold the parent relation's OID not the index's own OID.
	 *
	 * Note: if we wanted to rely on the constraint name matching the index's
	 * name, we could just do a direct lookup using pg_constraint's unique
	 * index.  For the moment it doesn't seem worth requiring that.
	 */
	ScanKeyInit(&skey[0],
				Anum_pg_constraint_conrelid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(indexRelation->rd_index->indrelid));

	conrel = table_open(ConstraintRelationId, AccessShareLock);
	conscan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId, true,
								 NULL, 1, skey);
	found = false;

	while (HeapTupleIsValid(htup = systable_getnext(conscan)))
	{
		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(htup);
		Datum		val;
		bool		isnull;
		ArrayType  *arr;
		int			nelem;

		/* We want the exclusion constraint owning the index */
		if ((conform->contype != CONSTRAINT_EXCLUSION &&
			 !(conform->conperiod && (conform->contype == CONSTRAINT_PRIMARY
									  || conform->contype == CONSTRAINT_UNIQUE))) ||
			conform->conindid != RelationGetRelid(indexRelation))
			continue;

		/* There should be only one */
		if (found)
			elog(ERROR, "unexpected exclusion constraint record found for rel %s",
				 RelationGetRelationName(indexRelation));
		found = true;

		/* Extract the operator OIDS from conexclop */
		val = fastgetattr(htup,
						  Anum_pg_constraint_conexclop,
						  conrel->rd_att, &isnull);
		if (isnull)
			elog(ERROR, "null conexclop for rel %s",
				 RelationGetRelationName(indexRelation));

		arr = DatumGetArrayTypeP(val);	/* ensure not toasted */
		nelem = ARR_DIMS(arr)[0];
		if (ARR_NDIM(arr) != 1 ||
			nelem != indnkeyatts ||
			ARR_HASNULL(arr) ||
			ARR_ELEMTYPE(arr) != OIDOID)
			elog(ERROR, "conexclop is not a 1-D Oid array");

		memcpy(ops, ARR_DATA_PTR(arr), sizeof(Oid) * indnkeyatts);
	}

	systable_endscan(conscan);
	table_close(conrel, AccessShareLock);

	if (!found)
		elog(ERROR, "exclusion constraint record missing for rel %s",
			 RelationGetRelationName(indexRelation));

	/* We need the func OIDs and strategy numbers too */
	for (i = 0; i < indnkeyatts; i++)
	{
		funcs[i] = get_opcode(ops[i]);
		strats[i] = get_op_opfamily_strategy(ops[i],
											 indexRelation->rd_opfamily[i]);
		/* shouldn't fail, since it was checked at index creation */
		if (strats[i] == InvalidStrategy)
			elog(ERROR, "could not find strategy for operator %u in family %u",
				 ops[i], indexRelation->rd_opfamily[i]);
	}

	/* Save a copy of the results in the relcache entry. */
	oldcxt = MemoryContextSwitchTo(indexRelation->rd_indexcxt);
	indexRelation->rd_exclops = palloc_array(Oid, indnkeyatts);
	indexRelation->rd_exclprocs = palloc_array(Oid, indnkeyatts);
	indexRelation->rd_exclstrats = palloc_array(uint16, indnkeyatts);
	memcpy(indexRelation->rd_exclops, ops, sizeof(Oid) * indnkeyatts);
	memcpy(indexRelation->rd_exclprocs, funcs, sizeof(Oid) * indnkeyatts);
	memcpy(indexRelation->rd_exclstrats, strats, sizeof(uint16) * indnkeyatts);
	MemoryContextSwitchTo(oldcxt);
}

/*
 * Get the publication information for the given relation.
 *
 * Traverse all the publications which the relation is in to get the
 * publication actions and validate:
 * 1. The row filter expressions for such publications if any. We consider the
 *    row filter expression as invalid if it references any column which is not
 *    part of REPLICA IDENTITY.
 * 2. The column list for such publication if any. We consider the column list
 * 	  invalid if REPLICA IDENTITY contains any column that is not part of it.
 * 3. The generated columns of the relation for such publications. We consider
 *    any reference of an unpublished generated column in REPLICA IDENTITY as
 *    invalid.
 *
 * To avoid fetching the publication information repeatedly, we cache the
 * publication actions, row filter validation information, column list
 * validation information, and generated column validation information.
 */
void
RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc)
{
	List	   *puboids = NIL;
	List	   *exceptpuboids = NIL;
	List	   *alltablespuboids;
	ListCell   *lc;
	MemoryContext oldcxt;
	Oid			schemaid;
	List	   *ancestors = NIL;
	Oid			relid = RelationGetRelid(relation);

	/*
	 * If not publishable, it publishes no actions.  (pgoutput_change() will
	 * ignore it.)
	 */
	if (!is_publishable_relation(relation))
	{
		memset(pubdesc, 0, sizeof(PublicationDesc));
		pubdesc->rf_valid_for_update = true;
		pubdesc->rf_valid_for_delete = true;
		pubdesc->cols_valid_for_update = true;
		pubdesc->cols_valid_for_delete = true;
		pubdesc->gencols_valid_for_update = true;
		pubdesc->gencols_valid_for_delete = true;
		return;
	}

	if (relation->rd_pubdesc)
	{
		memcpy(pubdesc, relation->rd_pubdesc, sizeof(PublicationDesc));
		return;
	}

	memset(pubdesc, 0, sizeof(PublicationDesc));
	pubdesc->rf_valid_for_update = true;
	pubdesc->rf_valid_for_delete = true;
	pubdesc->cols_valid_for_update = true;
	pubdesc->cols_valid_for_delete = true;
	pubdesc->gencols_valid_for_update = true;
	pubdesc->gencols_valid_for_delete = true;

	/* Fetch the publication membership info. */
	puboids = GetRelationIncludedPublications(relid);
	schemaid = RelationGetNamespace(relation);
	puboids = list_concat_unique_oid(puboids, GetSchemaPublications(schemaid));

	if (relation->rd_rel->relispartition)
	{
		Oid			last_ancestor_relid;

		/* Add publications that the ancestors are in too. */
		ancestors = get_partition_ancestors(relid);
		last_ancestor_relid = llast_oid(ancestors);

		foreach(lc, ancestors)
		{
			Oid			ancestor = lfirst_oid(lc);

			puboids = list_concat_unique_oid(puboids,
											 GetRelationIncludedPublications(ancestor));
			schemaid = get_rel_namespace(ancestor);
			puboids = list_concat_unique_oid(puboids,
											 GetSchemaPublications(schemaid));
		}

		/*
		 * Only the top-most ancestor can appear in the EXCEPT clause.
		 * Therefore, for a partition, exclusion must be evaluated at the
		 * top-most ancestor.
		 */
		exceptpuboids = GetRelationExcludedPublications(last_ancestor_relid);
	}
	else
	{
		/*
		 * For a regular table or a root partitioned table, check exclusion on
		 * table itself.
		 */
		exceptpuboids = GetRelationExcludedPublications(relid);
	}

	alltablespuboids = GetAllTablesPublications();
	puboids = list_concat_unique_oid(puboids,
									 list_difference_oid(alltablespuboids,
														 exceptpuboids));
	foreach(lc, puboids)
	{
		Oid			pubid = lfirst_oid(lc);
		HeapTuple	tup;
		Form_pg_publication pubform;
		bool		invalid_column_list;
		bool		invalid_gen_col;

		tup = SearchSysCache1(PUBLICATIONOID, ObjectIdGetDatum(pubid));

		if (!HeapTupleIsValid(tup))
			elog(ERROR, "cache lookup failed for publication %u", pubid);

		pubform = (Form_pg_publication) GETSTRUCT(tup);

		pubdesc->pubactions.pubinsert |= pubform->pubinsert;
		pubdesc->pubactions.pubupdate |= pubform->pubupdate;
		pubdesc->pubactions.pubdelete |= pubform->pubdelete;
		pubdesc->pubactions.pubtruncate |= pubform->pubtruncate;

		/*
		 * Check if all columns referenced in the filter expression are part
		 * of the REPLICA IDENTITY index or not.
		 *
		 * If the publication is FOR ALL TABLES then it means the table has no
		 * row filters and we can skip the validation.
		 */
		if (!pubform->puballtables &&
			(pubform->pubupdate || pubform->pubdelete) &&
			pub_rf_contains_invalid_column(pubid, relation, ancestors,
										   pubform->pubviaroot))
		{
			if (pubform->pubupdate)
				pubdesc->rf_valid_for_update = false;
			if (pubform->pubdelete)
				pubdesc->rf_valid_for_delete = false;
		}

		/*
		 * Check if all columns are part of the REPLICA IDENTITY index or not.
		 *
		 * Check if all generated columns included in the REPLICA IDENTITY are
		 * published.
		 */
		if ((pubform->pubupdate || pubform->pubdelete) &&
			pub_contains_invalid_column(pubid, relation, ancestors,
										pubform->pubviaroot,
										pubform->pubgencols,
										&invalid_column_list,
										&invalid_gen_col))
		{
			if (pubform->pubupdate)
			{
				pubdesc->cols_valid_for_update = !invalid_column_list;
				pubdesc->gencols_valid_for_update = !invalid_gen_col;
			}

			if (pubform->pubdelete)
			{
				pubdesc->cols_valid_for_delete = !invalid_column_list;
				pubdesc->gencols_valid_for_delete = !invalid_gen_col;
			}
		}

		ReleaseSysCache(tup);

		/*
		 * If we know everything is replicated and the row filter is invalid
		 * for update and delete, there is no point to check for other
		 * publications.
		 */
		if (pubdesc->pubactions.pubinsert && pubdesc->pubactions.pubupdate &&
			pubdesc->pubactions.pubdelete && pubdesc->pubactions.pubtruncate &&
			!pubdesc->rf_valid_for_update && !pubdesc->rf_valid_for_delete)
			break;

		/*
		 * If we know everything is replicated and the column list is invalid
		 * for update and delete, there is no point to check for other
		 * publications.
		 */
		if (pubdesc->pubactions.pubinsert && pubdesc->pubactions.pubupdate &&
			pubdesc->pubactions.pubdelete && pubdesc->pubactions.pubtruncate &&
			!pubdesc->cols_valid_for_update && !pubdesc->cols_valid_for_delete)
			break;

		/*
		 * If we know everything is replicated and replica identity has an
		 * unpublished generated column, there is no point to check for other
		 * publications.
		 */
		if (pubdesc->pubactions.pubinsert && pubdesc->pubactions.pubupdate &&
			pubdesc->pubactions.pubdelete && pubdesc->pubactions.pubtruncate &&
			!pubdesc->gencols_valid_for_update &&
			!pubdesc->gencols_valid_for_delete)
			break;
	}

	if (relation->rd_pubdesc)
	{
		pfree(relation->rd_pubdesc);
		relation->rd_pubdesc = NULL;
	}

	/* Now save copy of the descriptor in the relcache entry. */
	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
	relation->rd_pubdesc = palloc_object(PublicationDesc);
	memcpy(relation->rd_pubdesc, pubdesc, sizeof(PublicationDesc));
	MemoryContextSwitchTo(oldcxt);
}

static bytea **
CopyIndexAttOptions(bytea **srcopts, int natts)
{
	bytea	  **opts = palloc_array(bytea *, natts);

	for (int i = 0; i < natts; i++)
	{
		bytea	   *opt = srcopts[i];

		opts[i] = !opt ? NULL : (bytea *)
			DatumGetPointer(datumCopy(PointerGetDatum(opt), false, -1));
	}

	return opts;
}

/*
 * RelationGetIndexAttOptions
 *		get AM/opclass-specific options for an index parsed into a binary form
 */
bytea	  **
RelationGetIndexAttOptions(Relation relation, bool copy)
{
	MemoryContext oldcxt;
	bytea	  **opts = relation->rd_opcoptions;
	Oid			relid = RelationGetRelid(relation);
	int			natts = RelationGetNumberOfAttributes(relation);	/* XXX
																	 * IndexRelationGetNumberOfKeyAttributes */
	int			i;

	/* Try to copy cached options. */
	if (opts)
		return copy ? CopyIndexAttOptions(opts, natts) : opts;

	/* Get and parse opclass options. */
	opts = palloc0_array(bytea *, natts);

	for (i = 0; i < natts; i++)
	{
		if (criticalRelcachesBuilt && relid != AttributeRelidNumIndexId)
		{
			Datum		attoptions = get_attoptions(relid, i + 1);

			opts[i] = index_opclass_options(relation, i + 1, attoptions, false);

			if (attoptions != (Datum) 0)
				pfree(DatumGetPointer(attoptions));
		}
	}

	/* Copy parsed options to the cache. */
	oldcxt = MemoryContextSwitchTo(relation->rd_indexcxt);
	relation->rd_opcoptions = CopyIndexAttOptions(opts, natts);
	MemoryContextSwitchTo(oldcxt);

	if (copy)
		return opts;

	for (i = 0; i < natts; i++)
	{
		if (opts[i])
			pfree(opts[i]);
	}

	pfree(opts);

	return relation->rd_opcoptions;
}

/*
 * Routines to support ereport() reports of relation-related errors
 *
 * These could have been put into elog.c, but it seems like a module layering
 * violation to have elog.c calling relcache or syscache stuff --- and we
 * definitely don't want elog.h including rel.h.  So we put them here.
 */

/*
 * errtable --- stores schema_name and table_name of a table
 * within the current errordata.
 */
int
errtable(Relation rel)
{
	err_generic_string(PG_DIAG_SCHEMA_NAME,
					   get_namespace_name(RelationGetNamespace(rel)));
	err_generic_string(PG_DIAG_TABLE_NAME, RelationGetRelationName(rel));

	return 0;					/* return value does not matter */
}

/*
 * errtablecol --- stores schema_name, table_name and column_name
 * of a table column within the current errordata.
 *
 * The column is specified by attribute number --- for most callers, this is
 * easier and less error-prone than getting the column name for themselves.
 */
int
errtablecol(Relation rel, int attnum)
{
	TupleDesc	reldesc = RelationGetDescr(rel);
	const char *colname;

	/* Use reldesc if it's a user attribute, else consult the catalogs */
	if (attnum > 0 && attnum <= reldesc->natts)
		colname = NameStr(TupleDescAttr(reldesc, attnum - 1)->attname);
	else
		colname = get_attname(RelationGetRelid(rel), attnum, false);

	return errtablecolname(rel, colname);
}

/*
 * errtablecolname --- stores schema_name, table_name and column_name
 * of a table column within the current errordata, where the column name is
 * given directly rather than extracted from the relation's catalog data.
 *
 * Don't use this directly unless errtablecol() is inconvenient for some
 * reason.  This might possibly be needed during intermediate states in ALTER
 * TABLE, for instance.
 */
int
errtablecolname(Relation rel, const char *colname)
{
	errtable(rel);
	err_generic_string(PG_DIAG_COLUMN_NAME, colname);

	return 0;					/* return value does not matter */
}

/*
 * errtableconstraint --- stores schema_name, table_name and constraint_name
 * of a table-related constraint within the current errordata.
 */
int
errtableconstraint(Relation rel, const char *conname)
{
	errtable(rel);
	err_generic_string(PG_DIAG_CONSTRAINT_NAME, conname);

	return 0;					/* return value does not matter */
}


/*
 *	load_relcache_init_file, write_relcache_init_file
 *
 *		In late 1992, we started regularly having databases with more than
 *		a thousand classes in them.  With this number of classes, it became
 *		critical to do indexed lookups on the system catalogs.
 *
 *		Bootstrapping these lookups is very hard.  We want to be able to
 *		use an index on pg_attribute, for example, but in order to do so,
 *		we must have read pg_attribute for the attributes in the index,
 *		which implies that we need to use the index.
 *
 *		In order to get around the problem, we do the following:
 *
 *		   +  When the database system is initialized (at initdb time), we
 *			  don't use indexes.  We do sequential scans.
 *
 *		   +  When the backend is started up in normal mode, we load an image
 *			  of the appropriate relation descriptors, in internal format,
 *			  from an initialization file in the data/base/... directory.
 *
 *		   +  If the initialization file isn't there, then we create the
 *			  relation descriptors using sequential scans and write 'em to
 *			  the initialization file for use by subsequent backends.
 *
 *		As of Postgres 9.0, there is one local initialization file in each
 *		database, plus one shared initialization file for shared catalogs.
 *
 *		We could dispense with the initialization files and just build the
 *		critical reldescs the hard way on every backend startup, but that
 *		slows down backend startup noticeably.
 *
 *		We can in fact go further, and save more relcache entries than
 *		just the ones that are absolutely critical; this allows us to speed
 *		up backend startup by not having to build such entries the hard way.
 *		Presently, all the catalog and index entries that are referred to
 *		by catcaches are stored in the initialization files.
 *
 *		The same mechanism that detects when catcache and relcache entries
 *		need to be invalidated (due to catalog updates) also arranges to
 *		unlink the initialization files when the contents may be out of date.
 *		The files will then be rebuilt during the next backend startup.
 */

/*
 * load_relcache_init_file -- attempt to load cache from the shared
 * or local cache init file
 *
 * If successful, return true and set criticalRelcachesBuilt or
 * criticalSharedRelcachesBuilt to true.
 * If not successful, return false.
 *
 * NOTE: we assume we are already switched into CacheMemoryContext.
 */
static bool
load_relcache_init_file(bool shared)
{
	FILE	   *fp;
	char		initfilename[MAXPGPATH];
	Relation   *rels;
	int			relno,
				num_rels,
				max_rels,
				nailed_rels,
				nailed_indexes,
				magic;
	int			i;

	if (shared)
		snprintf(initfilename, sizeof(initfilename), "global/%s",
				 RELCACHE_INIT_FILENAME);
	else
		snprintf(initfilename, sizeof(initfilename), "%s/%s",
				 DatabasePath, RELCACHE_INIT_FILENAME);

	fp = AllocateFile(initfilename, PG_BINARY_R);
	if (fp == NULL)
		return false;

	/*
	 * Read the index relcache entries from the file.  Note we will not enter
	 * any of them into the cache if the read fails partway through; this
	 * helps to guard against broken init files.
	 */
	max_rels = 100;
	rels = (Relation *) palloc(max_rels * sizeof(Relation));
	num_rels = 0;
	nailed_rels = nailed_indexes = 0;

	/* check for correct magic number (compatible version) */
	if (fread(&magic, 1, sizeof(magic), fp) != sizeof(magic))
		goto read_failed;
	if (magic != RELCACHE_INIT_FILEMAGIC)
		goto read_failed;

	for (relno = 0;; relno++)
	{
		Size		len;
		size_t		nread;
		Relation	rel;
		Form_pg_class relform;
		bool		has_not_null;

		/* first read the relation descriptor length */
		nread = fread(&len, 1, sizeof(len), fp);
		if (nread != sizeof(len))
		{
			if (nread == 0)
				break;			/* end of file */
			goto read_failed;
		}

		/* safety check for incompatible relcache layout */
		if (len != sizeof(RelationData))
			goto read_failed;

		/* allocate another relcache header */
		if (num_rels >= max_rels)
		{
			max_rels *= 2;
			rels = (Relation *) repalloc(rels, max_rels * sizeof(Relation));
		}

		rel = rels[num_rels++] = (Relation) palloc(len);

		/* then, read the Relation structure */
		if (fread(rel, 1, len, fp) != len)
			goto read_failed;

		/* next read the relation tuple form */
		if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
			goto read_failed;

		relform = (Form_pg_class) palloc(len);
		if (fread(relform, 1, len, fp) != len)
			goto read_failed;

		rel->rd_rel = relform;

		/* initialize attribute tuple forms */
		rel->rd_att = CreateTemplateTupleDesc(relform->relnatts);
		rel->rd_att->tdrefcount = 1;	/* mark as refcounted */

		rel->rd_att->tdtypeid = relform->reltype ? relform->reltype : RECORDOID;
		rel->rd_att->tdtypmod = -1; /* just to be sure */

		/* next read all the attribute tuple form data entries */
		has_not_null = false;
		for (i = 0; i < relform->relnatts; i++)
		{
			Form_pg_attribute attr = TupleDescAttr(rel->rd_att, i);

			if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
				goto read_failed;
			if (len != ATTRIBUTE_FIXED_PART_SIZE)
				goto read_failed;
			if (fread(attr, 1, len, fp) != len)
				goto read_failed;

			has_not_null |= attr->attnotnull;

			populate_compact_attribute(rel->rd_att, i);
		}

		TupleDescFinalize(rel->rd_att);

		/* next read the access method specific field */
		if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
			goto read_failed;
		if (len > 0)
		{
			rel->rd_options = palloc(len);
			if (fread(rel->rd_options, 1, len, fp) != len)
				goto read_failed;
			if (len != VARSIZE(rel->rd_options))
				goto read_failed;	/* sanity check */
		}
		else
		{
			rel->rd_options = NULL;
		}

		/* mark not-null status */
		if (has_not_null)
		{
			TupleConstr *constr = palloc0_object(TupleConstr);

			constr->has_not_null = true;
			rel->rd_att->constr = constr;
		}

		/*
		 * If it's an index, there's more to do.  Note we explicitly ignore
		 * partitioned indexes here.
		 */
		if (rel->rd_rel->relkind == RELKIND_INDEX)
		{
			MemoryContext indexcxt;
			Oid		   *opfamily;
			Oid		   *opcintype;
			RegProcedure *support;
			int			nsupport;
			int16	   *indoption;
			Oid		   *indcollation;

			/* Count nailed indexes to ensure we have 'em all */
			if (rel->rd_isnailed)
				nailed_indexes++;

			/* read the pg_index tuple */
			if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
				goto read_failed;

			rel->rd_indextuple = (HeapTuple) palloc(len);
			if (fread(rel->rd_indextuple, 1, len, fp) != len)
				goto read_failed;

			/* Fix up internal pointers in the tuple -- see heap_copytuple */
			rel->rd_indextuple->t_data = (HeapTupleHeader) ((char *) rel->rd_indextuple + HEAPTUPLESIZE);
			rel->rd_index = (Form_pg_index) GETSTRUCT(rel->rd_indextuple);

			/*
			 * prepare index info context --- parameters should match
			 * RelationInitIndexAccessInfo
			 */
			indexcxt = AllocSetContextCreate(CacheMemoryContext,
											 "index info",
											 ALLOCSET_SMALL_SIZES);
			rel->rd_indexcxt = indexcxt;
			MemoryContextCopyAndSetIdentifier(indexcxt,
											  RelationGetRelationName(rel));

			/*
			 * Now we can fetch the index AM's API struct.  (We can't store
			 * that in the init file, since it contains function pointers that
			 * might vary across server executions.  Fortunately, it should be
			 * safe to call the amhandler even while bootstrapping indexes.)
			 */
			InitIndexAmRoutine(rel);

			/* read the vector of opfamily OIDs */
			if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
				goto read_failed;

			opfamily = (Oid *) MemoryContextAlloc(indexcxt, len);
			if (fread(opfamily, 1, len, fp) != len)
				goto read_failed;

			rel->rd_opfamily = opfamily;

			/* read the vector of opcintype OIDs */
			if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
				goto read_failed;

			opcintype = (Oid *) MemoryContextAlloc(indexcxt, len);
			if (fread(opcintype, 1, len, fp) != len)
				goto read_failed;

			rel->rd_opcintype = opcintype;

			/* read the vector of support procedure OIDs */
			if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
				goto read_failed;
			support = (RegProcedure *) MemoryContextAlloc(indexcxt, len);
			if (fread(support, 1, len, fp) != len)
				goto read_failed;

			rel->rd_support = support;

			/* read the vector of collation OIDs */
			if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
				goto read_failed;

			indcollation = (Oid *) MemoryContextAlloc(indexcxt, len);
			if (fread(indcollation, 1, len, fp) != len)
				goto read_failed;

			rel->rd_indcollation = indcollation;

			/* read the vector of indoption values */
			if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
				goto read_failed;

			indoption = (int16 *) MemoryContextAlloc(indexcxt, len);
			if (fread(indoption, 1, len, fp) != len)
				goto read_failed;

			rel->rd_indoption = indoption;

			/* read the vector of opcoptions values */
			rel->rd_opcoptions = (bytea **)
				MemoryContextAllocZero(indexcxt, sizeof(*rel->rd_opcoptions) * relform->relnatts);

			for (i = 0; i < relform->relnatts; i++)
			{
				if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
					goto read_failed;

				if (len > 0)
				{
					rel->rd_opcoptions[i] = (bytea *) MemoryContextAlloc(indexcxt, len);
					if (fread(rel->rd_opcoptions[i], 1, len, fp) != len)
						goto read_failed;
				}
			}

			/* set up zeroed fmgr-info vector */
			nsupport = relform->relnatts * rel->rd_indam->amsupport;
			rel->rd_supportinfo = (FmgrInfo *)
				MemoryContextAllocZero(indexcxt, nsupport * sizeof(FmgrInfo));
		}
		else
		{
			/* Count nailed rels to ensure we have 'em all */
			if (rel->rd_isnailed)
				nailed_rels++;

			/* Load table AM data */
			if (RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind) || rel->rd_rel->relkind == RELKIND_SEQUENCE)
				RelationInitTableAccessMethod(rel);

			Assert(rel->rd_index == NULL);
			Assert(rel->rd_indextuple == NULL);
			Assert(rel->rd_indexcxt == NULL);
			Assert(rel->rd_indam == NULL);
			Assert(rel->rd_opfamily == NULL);
			Assert(rel->rd_opcintype == NULL);
			Assert(rel->rd_support == NULL);
			Assert(rel->rd_supportinfo == NULL);
			Assert(rel->rd_indoption == NULL);
			Assert(rel->rd_indcollation == NULL);
			Assert(rel->rd_opcoptions == NULL);
		}

		/*
		 * Rules and triggers are not saved (mainly because the internal
		 * format is complex and subject to change).  They must be rebuilt if
		 * needed by RelationCacheInitializePhase3.  This is not expected to
		 * be a big performance hit since few system catalogs have such. Ditto
		 * for RLS policy data, partition info, index expressions, predicates,
		 * exclusion info, and FDW info.
		 */
		rel->rd_rules = NULL;
		rel->rd_rulescxt = NULL;
		rel->trigdesc = NULL;
		rel->rd_rsdesc = NULL;
		rel->rd_partkey = NULL;
		rel->rd_partkeycxt = NULL;
		rel->rd_partdesc = NULL;
		rel->rd_partdesc_nodetached = NULL;
		rel->rd_partdesc_nodetached_xmin = InvalidTransactionId;
		rel->rd_pdcxt = NULL;
		rel->rd_pddcxt = NULL;
		rel->rd_partcheck = NIL;
		rel->rd_partcheckvalid = false;
		rel->rd_partcheckcxt = NULL;
		rel->rd_indexprs = NIL;
		rel->rd_indexprsExpand = NIL;
		rel->rd_indpred = NIL;
		rel->rd_indpredExpand = NIL;
		rel->rd_exclops = NULL;
		rel->rd_exclprocs = NULL;
		rel->rd_exclstrats = NULL;
		rel->rd_fdwroutine = NULL;

		/*
		 * Reset transient-state fields in the relcache entry
		 */
		rel->rd_smgr = NULL;
		if (rel->rd_isnailed)
			rel->rd_refcnt = 1;
		else
			rel->rd_refcnt = 0;
		rel->rd_indexvalid = false;
		rel->rd_indexlist = NIL;
		rel->rd_pkindex = InvalidOid;
		rel->rd_replidindex = InvalidOid;
		rel->rd_attrsvalid = false;
		rel->rd_keyattr = NULL;
		rel->rd_pkattr = NULL;
		rel->rd_idattr = NULL;
		rel->rd_pubdesc = NULL;
		rel->rd_statvalid = false;
		rel->rd_statlist = NIL;
		rel->rd_fkeyvalid = false;
		rel->rd_fkeylist = NIL;
		rel->rd_createSubid = InvalidSubTransactionId;
		rel->rd_newRelfilelocatorSubid = InvalidSubTransactionId;
		rel->rd_firstRelfilelocatorSubid = InvalidSubTransactionId;
		rel->rd_droppedSubid = InvalidSubTransactionId;
		rel->rd_amcache = NULL;
		rel->pgstat_info = NULL;

		/*
		 * Recompute lock and physical addressing info.  This is needed in
		 * case the pg_internal.init file was copied from some other database
		 * by CREATE DATABASE.
		 */
		RelationInitLockInfo(rel);
		RelationInitPhysicalAddr(rel);
	}

	/*
	 * We reached the end of the init file without apparent problem.  Did we
	 * get the right number of nailed items?  This is a useful crosscheck in
	 * case the set of critical rels or indexes changes.  However, that should
	 * not happen in a normally-running system, so let's bleat if it does.
	 *
	 * For the shared init file, we're called before client authentication is
	 * done, which means that elog(WARNING) will go only to the postmaster
	 * log, where it's easily missed.  To ensure that developers notice bad
	 * values of NUM_CRITICAL_SHARED_RELS/NUM_CRITICAL_SHARED_INDEXES, we put
	 * an Assert(false) there.
	 */
	if (shared)
	{
		if (nailed_rels != NUM_CRITICAL_SHARED_RELS ||
			nailed_indexes != NUM_CRITICAL_SHARED_INDEXES)
		{
			elog(WARNING, "found %d nailed shared rels and %d nailed shared indexes in init file, but expected %d and %d respectively",
				 nailed_rels, nailed_indexes,
				 NUM_CRITICAL_SHARED_RELS, NUM_CRITICAL_SHARED_INDEXES);
			/* Make sure we get developers' attention about this */
			Assert(false);
			/* In production builds, recover by bootstrapping the relcache */
			goto read_failed;
		}
	}
	else
	{
		if (nailed_rels != NUM_CRITICAL_LOCAL_RELS ||
			nailed_indexes != NUM_CRITICAL_LOCAL_INDEXES)
		{
			elog(WARNING, "found %d nailed rels and %d nailed indexes in init file, but expected %d and %d respectively",
				 nailed_rels, nailed_indexes,
				 NUM_CRITICAL_LOCAL_RELS, NUM_CRITICAL_LOCAL_INDEXES);
			/* We don't need an Assert() in this case */
			goto read_failed;
		}
	}

	/*
	 * OK, all appears well.
	 *
	 * Now insert all the new relcache entries into the cache.
	 */
	for (relno = 0; relno < num_rels; relno++)
	{
		RelationCacheInsert(rels[relno], false);
	}

	pfree(rels);
	FreeFile(fp);

	if (shared)
		criticalSharedRelcachesBuilt = true;
	else
		criticalRelcachesBuilt = true;
	return true;

	/*
	 * init file is broken, so do it the hard way.  We don't bother trying to
	 * free the clutter we just allocated; it's not in the relcache so it
	 * won't hurt.
	 */
read_failed:
	pfree(rels);
	FreeFile(fp);

	return false;
}

/*
 * Write out a new initialization file with the current contents
 * of the relcache (either shared rels or local rels, as indicated).
 */
static void
write_relcache_init_file(bool shared)
{
	FILE	   *fp;
	char		tempfilename[MAXPGPATH];
	char		finalfilename[MAXPGPATH];
	int			magic;
	HASH_SEQ_STATUS status;
	RelIdCacheEnt *idhentry;
	int			i;

	/*
	 * If we have already received any relcache inval events, there's no
	 * chance of succeeding so we may as well skip the whole thing.
	 */
	if (relcacheInvalsReceived != 0L)
		return;

	/*
	 * We must write a temporary file and rename it into place. Otherwise,
	 * another backend starting at about the same time might crash trying to
	 * read the partially-complete file.
	 */
	if (shared)
	{
		snprintf(tempfilename, sizeof(tempfilename), "global/%s.%d",
				 RELCACHE_INIT_FILENAME, MyProcPid);
		snprintf(finalfilename, sizeof(finalfilename), "global/%s",
				 RELCACHE_INIT_FILENAME);
	}
	else
	{
		snprintf(tempfilename, sizeof(tempfilename), "%s/%s.%d",
				 DatabasePath, RELCACHE_INIT_FILENAME, MyProcPid);
		snprintf(finalfilename, sizeof(finalfilename), "%s/%s",
				 DatabasePath, RELCACHE_INIT_FILENAME);
	}

	unlink(tempfilename);		/* in case it exists w/wrong permissions */

	fp = AllocateFile(tempfilename, PG_BINARY_W);
	if (fp == NULL)
	{
		/*
		 * We used to consider this a fatal error, but we might as well
		 * continue with backend startup ...
		 */
		ereport(WARNING,
				(errcode_for_file_access(),
				 errmsg("could not create relation-cache initialization file \"%s\": %m",
						tempfilename),
				 errdetail("Continuing anyway, but there's something wrong.")));
		return;
	}

	/*
	 * Write a magic number to serve as a file version identifier.  We can
	 * change the magic number whenever the relcache layout changes.
	 */
	magic = RELCACHE_INIT_FILEMAGIC;
	if (fwrite(&magic, 1, sizeof(magic), fp) != sizeof(magic))
		ereport(FATAL,
				errcode_for_file_access(),
				errmsg_internal("could not write init file: %m"));

	/*
	 * Write all the appropriate reldescs (in no particular order).
	 */
	hash_seq_init(&status, RelationIdCache);

	while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL)
	{
		Relation	rel = idhentry->reldesc;
		Form_pg_class relform = rel->rd_rel;

		/* ignore if not correct group */
		if (relform->relisshared != shared)
			continue;

		/*
		 * Ignore if not supposed to be in init file.  We can allow any shared
		 * relation that's been loaded so far to be in the shared init file,
		 * but unshared relations must be ones that should be in the local
		 * file per RelationIdIsInInitFile.  (Note: if you want to change the
		 * criterion for rels to be kept in the init file, see also inval.c.
		 * The reason for filtering here is to be sure that we don't put
		 * anything into the local init file for which a relcache inval would
		 * not cause invalidation of that init file.)
		 */
		if (!shared && !RelationIdIsInInitFile(RelationGetRelid(rel)))
		{
			/* Nailed rels had better get stored. */
			Assert(!rel->rd_isnailed);
			continue;
		}

		/* first write the relcache entry proper */
		write_item(rel, sizeof(RelationData), fp);

		/* next write the relation tuple form */
		write_item(relform, CLASS_TUPLE_SIZE, fp);

		/* next, do all the attribute tuple form data entries */
		for (i = 0; i < relform->relnatts; i++)
		{
			write_item(TupleDescAttr(rel->rd_att, i),
					   ATTRIBUTE_FIXED_PART_SIZE, fp);
		}

		/* next, do the access method specific field */
		write_item(rel->rd_options,
				   (rel->rd_options ? VARSIZE(rel->rd_options) : 0),
				   fp);

		/*
		 * If it's an index, there's more to do. Note we explicitly ignore
		 * partitioned indexes here.
		 */
		if (rel->rd_rel->relkind == RELKIND_INDEX)
		{
			/* write the pg_index tuple */
			/* we assume this was created by heap_copytuple! */
			write_item(rel->rd_indextuple,
					   HEAPTUPLESIZE + rel->rd_indextuple->t_len,
					   fp);

			/* write the vector of opfamily OIDs */
			write_item(rel->rd_opfamily,
					   relform->relnatts * sizeof(Oid),
					   fp);

			/* write the vector of opcintype OIDs */
			write_item(rel->rd_opcintype,
					   relform->relnatts * sizeof(Oid),
					   fp);

			/* write the vector of support procedure OIDs */
			write_item(rel->rd_support,
					   relform->relnatts * (rel->rd_indam->amsupport * sizeof(RegProcedure)),
					   fp);

			/* write the vector of collation OIDs */
			write_item(rel->rd_indcollation,
					   relform->relnatts * sizeof(Oid),
					   fp);

			/* write the vector of indoption values */
			write_item(rel->rd_indoption,
					   relform->relnatts * sizeof(int16),
					   fp);

			Assert(rel->rd_opcoptions);

			/* write the vector of opcoptions values */
			for (i = 0; i < relform->relnatts; i++)
			{
				bytea	   *opt = rel->rd_opcoptions[i];

				write_item(opt, opt ? VARSIZE(opt) : 0, fp);
			}
		}
	}

	if (FreeFile(fp))
		ereport(FATAL,
				errcode_for_file_access(),
				errmsg_internal("could not write init file: %m"));

	/*
	 * Now we have to check whether the data we've so painstakingly
	 * accumulated is already obsolete due to someone else's just-committed
	 * catalog changes.  If so, we just delete the temp file and leave it to
	 * the next backend to try again.  (Our own relcache entries will be
	 * updated by SI message processing, but we can't be sure whether what we
	 * wrote out was up-to-date.)
	 *
	 * This mustn't run concurrently with the code that unlinks an init file
	 * and sends SI messages, so grab a serialization lock for the duration.
	 */
	LWLockAcquire(RelCacheInitLock, LW_EXCLUSIVE);

	/* Make sure we have seen all incoming SI messages */
	AcceptInvalidationMessages();

	/*
	 * If we have received any SI relcache invals since backend start, assume
	 * we may have written out-of-date data.
	 */
	if (relcacheInvalsReceived == 0L)
	{
		/*
		 * OK, rename the temp file to its final name, deleting any
		 * previously-existing init file.
		 *
		 * Note: a failure here is possible under Cygwin, if some other
		 * backend is holding open an unlinked-but-not-yet-gone init file. So
		 * treat this as a noncritical failure; just remove the useless temp
		 * file on failure.
		 */
		if (rename(tempfilename, finalfilename) < 0)
			unlink(tempfilename);
	}
	else
	{
		/* Delete the already-obsolete temp file */
		unlink(tempfilename);
	}

	LWLockRelease(RelCacheInitLock);
}

/* write a chunk of data preceded by its length */
static void
write_item(const void *data, Size len, FILE *fp)
{
	if (fwrite(&len, 1, sizeof(len), fp) != sizeof(len))
		ereport(FATAL,
				errcode_for_file_access(),
				errmsg_internal("could not write init file: %m"));
	if (len > 0 && fwrite(data, 1, len, fp) != len)
		ereport(FATAL,
				errcode_for_file_access(),
				errmsg_internal("could not write init file: %m"));
}

/*
 * Determine whether a given relation (identified by OID) is one of the ones
 * we should store in a relcache init file.
 *
 * We must cache all nailed rels, and for efficiency we should cache every rel
 * that supports a syscache.  The former set is almost but not quite a subset
 * of the latter. The special cases are relations where
 * RelationCacheInitializePhase2/3 chooses to nail for efficiency reasons, but
 * which do not support any syscache.
 */
bool
RelationIdIsInInitFile(Oid relationId)
{
	if (relationId == SharedSecLabelRelationId ||
		relationId == TriggerRelidNameIndexId ||
		relationId == DatabaseNameIndexId ||
		relationId == SharedSecLabelObjectIndexId)
	{
		/*
		 * If this Assert fails, we don't need the applicable special case
		 * anymore.
		 */
		Assert(!RelationSupportsSysCache(relationId));
		return true;
	}
	return RelationSupportsSysCache(relationId);
}

/*
 * Invalidate (remove) the init file during commit of a transaction that
 * changed one or more of the relation cache entries that are kept in the
 * local init file.
 *
 * To be safe against concurrent inspection or rewriting of the init file,
 * we must take RelCacheInitLock, then remove the old init file, then send
 * the SI messages that include relcache inval for such relations, and then
 * release RelCacheInitLock.  This serializes the whole affair against
 * write_relcache_init_file, so that we can be sure that any other process
 * that's concurrently trying to create a new init file won't move an
 * already-stale version into place after we unlink.  Also, because we unlink
 * before sending the SI messages, a backend that's currently starting cannot
 * read the now-obsolete init file and then miss the SI messages that will
 * force it to update its relcache entries.  (This works because the backend
 * startup sequence gets into the sinval array before trying to load the init
 * file.)
 *
 * We take the lock and do the unlink in RelationCacheInitFilePreInvalidate,
 * then release the lock in RelationCacheInitFilePostInvalidate.  Caller must
 * send any pending SI messages between those calls.
 */
void
RelationCacheInitFilePreInvalidate(void)
{
	char		localinitfname[MAXPGPATH];
	char		sharedinitfname[MAXPGPATH];

	if (DatabasePath)
		snprintf(localinitfname, sizeof(localinitfname), "%s/%s",
				 DatabasePath, RELCACHE_INIT_FILENAME);
	snprintf(sharedinitfname, sizeof(sharedinitfname), "global/%s",
			 RELCACHE_INIT_FILENAME);

	LWLockAcquire(RelCacheInitLock, LW_EXCLUSIVE);

	/*
	 * The files might not be there if no backend has been started since the
	 * last removal.  But complain about failures other than ENOENT with
	 * ERROR.  Fortunately, it's not too late to abort the transaction if we
	 * can't get rid of the would-be-obsolete init file.
	 */
	if (DatabasePath)
		unlink_initfile(localinitfname, ERROR);
	unlink_initfile(sharedinitfname, ERROR);
}

void
RelationCacheInitFilePostInvalidate(void)
{
	LWLockRelease(RelCacheInitLock);
}

/*
 * Remove the init files during postmaster startup.
 *
 * We used to keep the init files across restarts, but that is unsafe in PITR
 * scenarios, and even in simple crash-recovery cases there are windows for
 * the init files to become out-of-sync with the database.  So now we just
 * remove them during startup and expect the first backend launch to rebuild
 * them.  Of course, this has to happen in each database of the cluster.
 */
void
RelationCacheInitFileRemove(void)
{
	const char *tblspcdir = PG_TBLSPC_DIR;
	DIR		   *dir;
	struct dirent *de;
	char		path[MAXPGPATH + sizeof(PG_TBLSPC_DIR) + sizeof(TABLESPACE_VERSION_DIRECTORY)];

	snprintf(path, sizeof(path), "global/%s",
			 RELCACHE_INIT_FILENAME);
	unlink_initfile(path, LOG);

	/* Scan everything in the default tablespace */
	RelationCacheInitFileRemoveInDir("base");

	/* Scan the tablespace link directory to find non-default tablespaces */
	dir = AllocateDir(tblspcdir);

	while ((de = ReadDirExtended(dir, tblspcdir, LOG)) != NULL)
	{
		if (strspn(de->d_name, "0123456789") == strlen(de->d_name))
		{
			/* Scan the tablespace dir for per-database dirs */
			snprintf(path, sizeof(path), "%s/%s/%s",
					 tblspcdir, de->d_name, TABLESPACE_VERSION_DIRECTORY);
			RelationCacheInitFileRemoveInDir(path);
		}
	}

	FreeDir(dir);
}

/* Process one per-tablespace directory for RelationCacheInitFileRemove */
static void
RelationCacheInitFileRemoveInDir(const char *tblspcpath)
{
	DIR		   *dir;
	struct dirent *de;
	char		initfilename[MAXPGPATH * 2];

	/* Scan the tablespace directory to find per-database directories */
	dir = AllocateDir(tblspcpath);

	while ((de = ReadDirExtended(dir, tblspcpath, LOG)) != NULL)
	{
		if (strspn(de->d_name, "0123456789") == strlen(de->d_name))
		{
			/* Try to remove the init file in each database */
			snprintf(initfilename, sizeof(initfilename), "%s/%s/%s",
					 tblspcpath, de->d_name, RELCACHE_INIT_FILENAME);
			unlink_initfile(initfilename, LOG);
		}
	}

	FreeDir(dir);
}

static void
unlink_initfile(const char *initfilename, int elevel)
{
	if (unlink(initfilename) < 0)
	{
		/* It might not be there, but log any error other than ENOENT */
		if (errno != ENOENT)
			ereport(elevel,
					(errcode_for_file_access(),
					 errmsg("could not remove cache file \"%s\": %m",
							initfilename)));
	}
}

/*
 * ResourceOwner callbacks
 */
static char *
ResOwnerPrintRelCache(Datum res)
{
	Relation	rel = (Relation) DatumGetPointer(res);

	return psprintf("relation \"%s\"", RelationGetRelationName(rel));
}

static void
ResOwnerReleaseRelation(Datum res)
{
	Relation	rel = (Relation) DatumGetPointer(res);

	/*
	 * This reference has already been removed from the resource owner, so
	 * just decrement reference count without calling
	 * ResourceOwnerForgetRelationRef.
	 */
	Assert(rel->rd_refcnt > 0);
	rel->rd_refcnt -= 1;

	RelationCloseCleanup((Relation) DatumGetPointer(res));
}

List *
ExpandVirtualGeneratedColumns(List *list, Relation heapRelation, Oid heapRelId)
{
	bool 				 opened_relation = false;
	TupleDesc			 tupdesc;

	if (list == NIL || (heapRelation == NULL && heapRelId == InvalidOid))
		return list;

	if (heapRelation == NULL)
	{
		heapRelation = table_open(heapRelId, NoLock);
		opened_relation = true;
	}

	tupdesc = RelationGetDescr(heapRelation);
	if ((tupdesc->constr && tupdesc->constr->has_generated_virtual))
	{
		int				 j;
		Bitmapset		*indexattrs = NULL;

		pull_varattnos((Node *)list, 1, &indexattrs);

		j = -1;
		while ((j = bms_next_member(indexattrs, j)) >= 0)
		{
			AttrNumber	attno = j + FirstLowInvalidHeapAttributeNumber;

			if (attno > 0 &&
				TupleDescAttr(tupdesc, attno - 1)->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
			{
				list = (List *)expand_generated_columns_in_expr((Node *)list, heapRelation, 1);
				break;
			}
		}
	}

	if (opened_relation)
		table_close(heapRelation, NoLock);

	return list;
}
./relcache.h0000664000175000017500000001224415222103244011622 0ustar  xmanxman/*-------------------------------------------------------------------------
 *
 * relcache.h
 *	  Relation descriptor cache definitions.
 *
 *
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/utils/relcache.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef RELCACHE_H
#define RELCACHE_H

#include "access/tupdesc.h"
#include "common/relpath.h"
#include "nodes/bitmapset.h"


/*
 * Name of relcache init file(s), used to speed up backend startup
 */
#define RELCACHE_INIT_FILENAME	"pg_internal.init"

typedef struct RelationData *Relation;

/* ----------------
 *		RelationPtr is used in the executor to support index scans
 *		where we have to keep track of several index relations in an
 *		array.  -cim 9/10/89
 * ----------------
 */
typedef Relation *RelationPtr;

/*
 * Routines to open (lookup) and close a relcache entry
 */
#ifdef USE_ASSERT_CHECKING
extern void AssertCouldGetRelation(void);
#else
static inline void
AssertCouldGetRelation(void)
{
}
#endif
extern Relation RelationIdGetRelation(Oid relationId);
extern char *RelationGetQualifiedRelationName(Relation rel);
extern void RelationClose(Relation relation);

/*
 * Routines to compute/retrieve additional cached information
 */
extern List *RelationGetFKeyList(Relation relation);
extern List *RelationGetIndexList(Relation relation);
extern List *RelationGetStatExtList(Relation relation);
extern Oid	RelationGetPrimaryKeyIndex(Relation relation, bool deferrable_ok);
extern Oid	RelationGetReplicaIndex(Relation relation);
extern List *RelationGetIndexExpressions(Relation relation);
extern List *RelationGetIndexExpressionsExpand(Relation relation);
extern List *RelationGetDummyIndexExpressions(Relation relation);
extern List *RelationGetIndexPredicate(Relation relation);
extern List *RelationGetIndexPredicateExpand(Relation relation);
extern bytea **RelationGetIndexAttOptions(Relation relation, bool copy);

/*
 * Which set of columns to return by RelationGetIndexAttrBitmap.
 */
typedef enum IndexAttrBitmapKind
{
	INDEX_ATTR_BITMAP_KEY,
	INDEX_ATTR_BITMAP_PRIMARY_KEY,
	INDEX_ATTR_BITMAP_IDENTITY_KEY,
	INDEX_ATTR_BITMAP_HOT_BLOCKING,
	INDEX_ATTR_BITMAP_SUMMARIZED,
} IndexAttrBitmapKind;

extern Bitmapset *RelationGetIndexAttrBitmap(Relation relation,
											 IndexAttrBitmapKind attrKind);

extern Bitmapset *RelationGetIdentityKeyBitmap(Relation relation);

extern void RelationGetExclusionInfo(Relation indexRelation,
									 Oid **operators,
									 Oid **procs,
									 uint16 **strategies);

extern void RelationInitIndexAccessInfo(Relation relation);

/* caller must include pg_publication.h */
struct PublicationDesc;
extern void RelationBuildPublicationDesc(Relation relation,
										 struct PublicationDesc *pubdesc);

extern void RelationInitTableAccessMethod(Relation relation);

/*
 * Routines to support ereport() reports of relation-related errors
 */
extern int	errtable(Relation rel);
extern int	errtablecol(Relation rel, int attnum);
extern int	errtablecolname(Relation rel, const char *colname);
extern int	errtableconstraint(Relation rel, const char *conname);

/*
 * Routines for backend startup
 */
extern void RelationCacheInitialize(void);
extern void RelationCacheInitializePhase2(void);
extern void RelationCacheInitializePhase3(void);

/*
 * Routine to create a relcache entry for an about-to-be-created relation
 */
extern Relation RelationBuildLocalRelation(const char *relname,
										   Oid relnamespace,
										   TupleDesc tupDesc,
										   Oid relid,
										   Oid accessmtd,
										   RelFileNumber relfilenumber,
										   Oid reltablespace,
										   bool shared_relation,
										   bool mapped_relation,
										   char relpersistence,
										   char relkind);

/*
 * Routines to manage assignment of new relfilenumber to a relation
 */
extern void RelationSetNewRelfilenumber(Relation relation, char persistence);
extern void RelationAssumeNewRelfilelocator(Relation relation);

/*
 * Routines for flushing/rebuilding relcache entries in various scenarios
 */
extern void RelationForgetRelation(Oid rid);

extern void RelationCacheInvalidateEntry(Oid relationId);

extern void RelationCacheInvalidate(bool debug_discard);

#ifdef USE_ASSERT_CHECKING
extern void AssertPendingSyncs_RelationCache(void);
#else
#define AssertPendingSyncs_RelationCache() do {} while (0)
#endif
extern void AtEOXact_RelationCache(bool isCommit);
extern void AtEOSubXact_RelationCache(bool isCommit, SubTransactionId mySubid,
									  SubTransactionId parentSubid);

/*
 * Routines to help manage rebuilding of relcache init files
 */
extern bool RelationIdIsInInitFile(Oid relationId);
extern void RelationCacheInitFilePreInvalidate(void);
extern void RelationCacheInitFilePostInvalidate(void);
extern void RelationCacheInitFileRemove(void);

/* should be used only by relcache.c and catcache.c */
extern PGDLLIMPORT bool criticalRelcachesBuilt;

/* should be used only by relcache.c and postinit.c */
extern PGDLLIMPORT bool criticalSharedRelcachesBuilt;

extern List *ExpandVirtualGeneratedColumns(List *list, Relation heapRelation, Oid heapRelId);

#endif							/* RELCACHE_H */
./selfuncs.c0000664000175000017500000106541215221500744011704 0ustar  xmanxman/*-------------------------------------------------------------------------
 *
 * selfuncs.c
 *	  Selectivity functions and index cost estimation functions for
 *	  standard operators and index access methods.
 *
 *	  Selectivity routines are registered in the pg_operator catalog
 *	  in the "oprrest" and "oprjoin" attributes.
 *
 *	  Index cost functions are located via the index AM's API struct,
 *	  which is obtained from the handler function registered in pg_am.
 *
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/adt/selfuncs.c
 *
 *-------------------------------------------------------------------------
 */

/*----------
 * Operator selectivity estimation functions are called to estimate the
 * selectivity of WHERE clauses whose top-level operator is their operator.
 * We divide the problem into two cases:
 *		Restriction clause estimation: the clause involves vars of just
 *			one relation.
 *		Join clause estimation: the clause involves vars of multiple rels.
 * Join selectivity estimation is far more difficult and usually less accurate
 * than restriction estimation.
 *
 * When dealing with the inner scan of a nestloop join, we consider the
 * join's joinclauses as restriction clauses for the inner relation, and
 * treat vars of the outer relation as parameters (a/k/a constants of unknown
 * values).  So, restriction estimators need to be able to accept an argument
 * telling which relation is to be treated as the variable.
 *
 * The call convention for a restriction estimator (oprrest function) is
 *
 *		Selectivity oprrest (PlannerInfo *root,
 *							 Oid operator,
 *							 List *args,
 *							 int varRelid);
 *
 * root: general information about the query (rtable and RelOptInfo lists
 * are particularly important for the estimator).
 * operator: OID of the specific operator in question.
 * args: argument list from the operator clause.
 * varRelid: if not zero, the relid (rtable index) of the relation to
 * be treated as the variable relation.  May be zero if the args list
 * is known to contain vars of only one relation.
 *
 * This is represented at the SQL level (in pg_proc) as
 *
 *		float8 oprrest (internal, oid, internal, int4);
 *
 * The result is a selectivity, that is, a fraction (0 to 1) of the rows
 * of the relation that are expected to produce a TRUE result for the
 * given operator.
 *
 * The call convention for a join estimator (oprjoin function) is similar
 * except that varRelid is not needed, and instead join information is
 * supplied:
 *
 *		Selectivity oprjoin (PlannerInfo *root,
 *							 Oid operator,
 *							 List *args,
 *							 JoinType jointype,
 *							 SpecialJoinInfo *sjinfo);
 *
 *		float8 oprjoin (internal, oid, internal, int2, internal);
 *
 * (Before Postgres 8.4, join estimators had only the first four of these
 * parameters.  That signature is still allowed, but deprecated.)  The
 * relationship between jointype and sjinfo is explained in the comments for
 * clause_selectivity() --- the short version is that jointype is usually
 * best ignored in favor of examining sjinfo.
 *
 * Join selectivity for regular inner and outer joins is defined as the
 * fraction (0 to 1) of the cross product of the relations that is expected
 * to produce a TRUE result for the given operator.  For both semi and anti
 * joins, however, the selectivity is defined as the fraction of the left-hand
 * side relation's rows that are expected to have a match (ie, at least one
 * row with a TRUE result) in the right-hand side.
 *
 * For both oprrest and oprjoin functions, the operator's input collation OID
 * (if any) is passed using the standard fmgr mechanism, so that the estimator
 * function can fetch it with PG_GET_COLLATION().  Note, however, that all
 * statistics in pg_statistic are currently built using the relevant column's
 * collation.
 *----------
 */

#include "postgres.h"

#include <ctype.h>
#include <math.h>

#include "access/brin.h"
#include "access/brin_page.h"
#include "access/gin.h"
#include "access/table.h"
#include "access/tableam.h"
#include "access/visibilitymap.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_statistic.h"
#include "catalog/pg_statistic_ext.h"
#include "executor/nodeAgg.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/optimizer.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/plancat.h"
#include "parser/parse_clause.h"
#include "parser/parse_relation.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteManip.h"
#include "statistics/statistics.h"
#include "storage/bufmgr.h"
#include "utils/acl.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/datum.h"
#include "utils/fmgroids.h"
#include "utils/index_selfuncs.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/pg_locale.h"
#include "utils/rel.h"
#include "utils/selfuncs.h"
#include "utils/snapmgr.h"
#include "utils/spccache.h"
#include "utils/syscache.h"
#include "utils/timestamp.h"
#include "utils/typcache.h"

#define DEFAULT_PAGE_CPU_MULTIPLIER 50.0

/*
 * In production builds, switch to hash-based MCV matching when the lists are
 * large enough to amortize hash setup cost.  (This threshold is compared to
 * the sum of the lengths of the two MCV lists.  This is simplistic but seems
 * to work well enough.)  In debug builds, we use a smaller threshold so that
 * the regression tests cover both paths well.
 */
#ifndef USE_ASSERT_CHECKING
#define EQJOINSEL_MCV_HASH_THRESHOLD 200
#else
#define EQJOINSEL_MCV_HASH_THRESHOLD 20
#endif

/* Entries in the simplehash hash table used by eqjoinsel_find_matches */
typedef struct MCVHashEntry
{
	Datum		value;			/* the value represented by this entry */
	int			index;			/* its index in the relevant AttStatsSlot */
	uint32		hash;			/* hash code for the Datum */
	char		status;			/* status code used by simplehash.h */
} MCVHashEntry;

/* private_data for the simplehash hash table */
typedef struct MCVHashContext
{
	FunctionCallInfo equal_fcinfo;	/* the equality join operator */
	FunctionCallInfo hash_fcinfo;	/* the hash function to use */
	bool		op_is_reversed; /* equality compares hash type to probe type */
	bool		insert_mode;	/* doing inserts or lookups? */
	bool		hash_typbyval;	/* typbyval of hashed data type */
	int16		hash_typlen;	/* typlen of hashed data type */
} MCVHashContext;

/* forward reference */
typedef struct MCVHashTable_hash MCVHashTable_hash;

/* Hooks for plugins to get control when we ask for stats */
get_relation_stats_hook_type get_relation_stats_hook = NULL;
get_index_stats_hook_type get_index_stats_hook = NULL;

static double eqsel_internal(PG_FUNCTION_ARGS, bool negate);
static double eqjoinsel_inner(FmgrInfo *eqproc, Oid collation,
							  Oid hashLeft, Oid hashRight,
							  VariableStatData *vardata1, VariableStatData *vardata2,
							  double nd1, double nd2,
							  bool isdefault1, bool isdefault2,
							  AttStatsSlot *sslot1, AttStatsSlot *sslot2,
							  Form_pg_statistic stats1, Form_pg_statistic stats2,
							  bool have_mcvs1, bool have_mcvs2,
							  bool *hasmatch1, bool *hasmatch2,
							  int *p_nmatches);
static double eqjoinsel_semi(FmgrInfo *eqproc, Oid collation,
							 Oid hashLeft, Oid hashRight,
							 bool op_is_reversed,
							 VariableStatData *vardata1, VariableStatData *vardata2,
							 double nd1, double nd2,
							 bool isdefault1, bool isdefault2,
							 AttStatsSlot *sslot1, AttStatsSlot *sslot2,
							 Form_pg_statistic stats1, Form_pg_statistic stats2,
							 bool have_mcvs1, bool have_mcvs2,
							 bool *hasmatch1, bool *hasmatch2,
							 int *p_nmatches,
							 RelOptInfo *inner_rel);
static void eqjoinsel_find_matches(FmgrInfo *eqproc, Oid collation,
								   Oid hashLeft, Oid hashRight,
								   bool op_is_reversed,
								   AttStatsSlot *sslot1, AttStatsSlot *sslot2,
								   int nvalues1, int nvalues2,
								   bool *hasmatch1, bool *hasmatch2,
								   int *p_nmatches, double *p_matchprodfreq);
static uint32 hash_mcv(MCVHashTable_hash *tab, Datum key);
static bool mcvs_equal(MCVHashTable_hash *tab, Datum key0, Datum key1);
static bool estimate_multivariate_ndistinct(PlannerInfo *root,
											RelOptInfo *rel, List **varinfos, double *ndistinct);
static bool convert_to_scalar(Datum value, Oid valuetypid, Oid collid,
							  double *scaledvalue,
							  Datum lobound, Datum hibound, Oid boundstypid,
							  double *scaledlobound, double *scaledhibound);
static double convert_numeric_to_scalar(Datum value, Oid typid, bool *failure);
static void convert_string_to_scalar(char *value,
									 double *scaledvalue,
									 char *lobound,
									 double *scaledlobound,
									 char *hibound,
									 double *scaledhibound);
static void convert_bytea_to_scalar(Datum value,
									double *scaledvalue,
									Datum lobound,
									double *scaledlobound,
									Datum hibound,
									double *scaledhibound);
static double convert_one_string_to_scalar(char *value,
										   int rangelo, int rangehi);
static double convert_one_bytea_to_scalar(unsigned char *value, int valuelen,
										  int rangelo, int rangehi);
static char *convert_string_datum(Datum value, Oid typid, Oid collid,
								  bool *failure);
static double convert_timevalue_to_scalar(Datum value, Oid typid,
										  bool *failure);
static Node *strip_all_phvs_deep(PlannerInfo *root, Node *node);
static bool contain_placeholder_walker(Node *node, void *context);
static Node *strip_all_phvs_mutator(Node *node, void *context);
static void examine_simple_variable(PlannerInfo *root, Var *var,
									VariableStatData *vardata);
static void examine_indexcol_variable(PlannerInfo *root, IndexOptInfo *index,
									  int indexcol, VariableStatData *vardata);
static bool get_variable_range(PlannerInfo *root, VariableStatData *vardata,
							   Oid sortop, Oid collation,
							   Datum *min, Datum *max);
static void get_stats_slot_range(AttStatsSlot *sslot,
								 Oid opfuncoid, FmgrInfo *opproc,
								 Oid collation, int16 typLen, bool typByVal,
								 Datum *min, Datum *max, bool *p_have_data);
static bool get_actual_variable_range(PlannerInfo *root,
									  VariableStatData *vardata,
									  Oid sortop, Oid collation,
									  Datum *min, Datum *max);
static bool get_actual_variable_endpoint(Relation heapRel,
										 Relation indexRel,
										 ScanDirection indexscandir,
										 ScanKey scankeys,
										 int16 typLen,
										 bool typByVal,
										 TupleTableSlot *tableslot,
										 MemoryContext outercontext,
										 Datum *endpointDatum);
static RelOptInfo *find_join_input_rel(PlannerInfo *root, Relids relids);
static double btcost_correlation(IndexOptInfo *index,
								 VariableStatData *vardata);

/* Define support routines for MCV hash tables */
#define SH_PREFIX				MCVHashTable
#define SH_ELEMENT_TYPE			MCVHashEntry
#define SH_KEY_TYPE				Datum
#define SH_KEY					value
#define SH_HASH_KEY(tab,key)	hash_mcv(tab, key)
#define SH_EQUAL(tab,key0,key1)	mcvs_equal(tab, key0, key1)
#define SH_SCOPE				static inline
#define SH_STORE_HASH
#define SH_GET_HASH(tab,ent)	(ent)->hash
#define SH_DEFINE
#define SH_DECLARE
#include "lib/simplehash.h"


/*
 *		eqsel			- Selectivity of "=" for any data types.
 *
 * Note: this routine is also used to estimate selectivity for some
 * operators that are not "=" but have comparable selectivity behavior,
 * such as "~=" (geometric approximate-match).  Even for "=", we must
 * keep in mind that the left and right datatypes may differ.
 */
Datum
eqsel(PG_FUNCTION_ARGS)
{
	PG_RETURN_FLOAT8((float8) eqsel_internal(fcinfo, false));
}

/*
 * Common code for eqsel() and neqsel()
 */
static double
eqsel_internal(PG_FUNCTION_ARGS, bool negate)
{
	PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
	Oid			operator = PG_GETARG_OID(1);
	List	   *args = (List *) PG_GETARG_POINTER(2);
	int			varRelid = PG_GETARG_INT32(3);
	Oid			collation = PG_GET_COLLATION();
	VariableStatData vardata;
	Node	   *other;
	bool		varonleft;
	double		selec;

	/*
	 * When asked about <>, we do the estimation using the corresponding =
	 * operator, then convert to <> via "1.0 - eq_selectivity - nullfrac".
	 */
	if (negate)
	{
		operator = get_negator(operator);
		if (!OidIsValid(operator))
		{
			/* Use default selectivity (should we raise an error instead?) */
			return 1.0 - DEFAULT_EQ_SEL;
		}
	}

	/*
	 * If expression is not variable = something or something = variable, then
	 * punt and return a default estimate.
	 */
	if (!get_restriction_variable(root, args, varRelid,
								  &vardata, &other, &varonleft))
		return negate ? (1.0 - DEFAULT_EQ_SEL) : DEFAULT_EQ_SEL;

	/*
	 * We can do a lot better if the something is a constant.  (Note: the
	 * Const might result from estimation rather than being a simple constant
	 * in the query.)
	 */
	if (IsA(other, Const))
		selec = var_eq_const(&vardata, operator, collation,
							 ((Const *) other)->constvalue,
							 ((Const *) other)->constisnull,
							 varonleft, negate);
	else
		selec = var_eq_non_const(&vardata, operator, collation, other,
								 varonleft, negate);

	ReleaseVariableStats(vardata);

	return selec;
}

/*
 * var_eq_const --- eqsel for var = const case
 *
 * This is exported so that some other estimation functions can use it.
 */
double
var_eq_const(VariableStatData *vardata, Oid oproid, Oid collation,
			 Datum constval, bool constisnull,
			 bool varonleft, bool negate)
{
	double		selec;
	double		nullfrac = 0.0;
	bool		isdefault;
	Oid			opfuncoid;

	/*
	 * If the constant is NULL, assume operator is strict and return zero, ie,
	 * operator will never return TRUE.  (It's zero even for a negator op.)
	 */
	if (constisnull)
		return 0.0;

	/*
	 * Grab the nullfrac for use below.  Note we allow use of nullfrac
	 * regardless of security check.
	 */
	if (HeapTupleIsValid(vardata->statsTuple))
	{
		Form_pg_statistic stats;

		stats = (Form_pg_statistic) GETSTRUCT(vardata->statsTuple);
		nullfrac = stats->stanullfrac;
	}

	/*
	 * If we matched the var to a unique index, DISTINCT or GROUP-BY clause,
	 * assume there is exactly one match regardless of anything else.  (This
	 * is slightly bogus, since the index or clause's equality operator might
	 * be different from ours, but it's much more likely to be right than
	 * ignoring the information.)
	 */
	if (vardata->isunique && vardata->rel && vardata->rel->tuples >= 1.0)
	{
		selec = 1.0 / vardata->rel->tuples;
	}
	else if (HeapTupleIsValid(vardata->statsTuple) &&
			 statistic_proc_security_check(vardata,
										   (opfuncoid = get_opcode(oproid))))
	{
		AttStatsSlot sslot;
		bool		match = false;
		int			i;

		/*
		 * Is the constant "=" to any of the column's most common values?
		 * (Although the given operator may not really be "=", we will assume
		 * that seeing whether it returns TRUE is an appropriate test.  If you
		 * don't like this, maybe you shouldn't be using eqsel for your
		 * operator...)
		 */
		if (get_attstatsslot(&sslot, vardata->statsTuple,
							 STATISTIC_KIND_MCV, InvalidOid,
							 ATTSTATSSLOT_VALUES | ATTSTATSSLOT_NUMBERS))
		{
			LOCAL_FCINFO(fcinfo, 2);
			FmgrInfo	eqproc;

			fmgr_info(opfuncoid, &eqproc);

			/*
			 * Save a few cycles by setting up the fcinfo struct just once.
			 * Using FunctionCallInvoke directly also avoids failure if the
			 * eqproc returns NULL, though really equality functions should
			 * never do that.
			 */
			InitFunctionCallInfoData(*fcinfo, &eqproc, 2, collation,
									 NULL, NULL);
			fcinfo->args[0].isnull = false;
			fcinfo->args[1].isnull = false;
			/* be careful to apply operator right way 'round */
			if (varonleft)
				fcinfo->args[1].value = constval;
			else
				fcinfo->args[0].value = constval;

			for (i = 0; i < sslot.nvalues; i++)
			{
				Datum		fresult;

				if (varonleft)
					fcinfo->args[0].value = sslot.values[i];
				else
					fcinfo->args[1].value = sslot.values[i];
				fcinfo->isnull = false;
				fresult = FunctionCallInvoke(fcinfo);
				if (!fcinfo->isnull && DatumGetBool(fresult))
				{
					match = true;
					break;
				}
			}
		}
		else
		{
			/* no most-common-value info available */
			i = 0;				/* keep compiler quiet */
		}

		if (match)
		{
			/*
			 * Constant is "=" to this common value.  We know selectivity
			 * exactly (or as exactly as ANALYZE could calculate it, anyway).
			 */
			selec = sslot.numbers[i];
		}
		else
		{
			/*
			 * Comparison is against a constant that is neither NULL nor any
			 * of the common values.  Its selectivity cannot be more than
			 * this:
			 */
			double		sumcommon = 0.0;
			double		otherdistinct;

			for (i = 0; i < sslot.nnumbers; i++)
				sumcommon += sslot.numbers[i];
			selec = 1.0 - sumcommon - nullfrac;
			CLAMP_PROBABILITY(selec);

			/*
			 * and in fact it's probably a good deal less. We approximate that
			 * all the not-common values share this remaining fraction
			 * equally, so we divide by the number of other distinct values.
			 */
			otherdistinct = get_variable_numdistinct(vardata, &isdefault) -
				sslot.nnumbers;
			if (otherdistinct > 1)
				selec /= otherdistinct;

			/*
			 * Another cross-check: selectivity shouldn't be estimated as more
			 * than the least common "most common value".
			 */
			if (sslot.nnumbers > 0 && selec > sslot.numbers[sslot.nnumbers - 1])
				selec = sslot.numbers[sslot.nnumbers - 1];
		}

		free_attstatsslot(&sslot);
	}
	else
	{
		/*
		 * No ANALYZE stats available, so make a guess using estimated number
		 * of distinct values and assuming they are equally common. (The guess
		 * is unlikely to be very good, but we do know a few special cases.)
		 */
		selec = 1.0 / get_variable_numdistinct(vardata, &isdefault);
	}

	/* now adjust if we wanted <> rather than = */
	if (negate)
		selec = 1.0 - selec - nullfrac;

	/* result should be in range, but make sure... */
	CLAMP_PROBABILITY(selec);

	return selec;
}

/*
 * var_eq_non_const --- eqsel for var = something-other-than-const case
 *
 * This is exported so that some other estimation functions can use it.
 */
double
var_eq_non_const(VariableStatData *vardata, Oid oproid, Oid collation,
				 Node *other,
				 bool varonleft, bool negate)
{
	double		selec;
	double		nullfrac = 0.0;
	bool		isdefault;

	/*
	 * Grab the nullfrac for use below.
	 */
	if (HeapTupleIsValid(vardata->statsTuple))
	{
		Form_pg_statistic stats;

		stats = (Form_pg_statistic) GETSTRUCT(vardata->statsTuple);
		nullfrac = stats->stanullfrac;
	}

	/*
	 * If we matched the var to a unique index, DISTINCT or GROUP-BY clause,
	 * assume there is exactly one match regardless of anything else.  (This
	 * is slightly bogus, since the index or clause's equality operator might
	 * be different from ours, but it's much more likely to be right than
	 * ignoring the information.)
	 */
	if (vardata->isunique && vardata->rel && vardata->rel->tuples >= 1.0)
	{
		selec = 1.0 / vardata->rel->tuples;
	}
	else if (HeapTupleIsValid(vardata->statsTuple))
	{
		double		ndistinct;
		AttStatsSlot sslot;

		/*
		 * Search is for a value that we do not know a priori, but we will
		 * assume it is not NULL.  Estimate the selectivity as non-null
		 * fraction divided by number of distinct values, so that we get a
		 * result averaged over all possible values whether common or
		 * uncommon.  (Essentially, we are assuming that the not-yet-known
		 * comparison value is equally likely to be any of the possible
		 * values, regardless of their frequency in the table.  Is that a good
		 * idea?)
		 */
		selec = 1.0 - nullfrac;
		ndistinct = get_variable_numdistinct(vardata, &isdefault);
		if (ndistinct > 1)
			selec /= ndistinct;

		/*
		 * Cross-check: selectivity should never be estimated as more than the
		 * most common value's.
		 */
		if (get_attstatsslot(&sslot, vardata->statsTuple,
							 STATISTIC_KIND_MCV, InvalidOid,
							 ATTSTATSSLOT_NUMBERS))
		{
			if (sslot.nnumbers > 0 && selec > sslot.numbers[0])
				selec = sslot.numbers[0];
			free_attstatsslot(&sslot);
		}
	}
	else
	{
		/*
		 * No ANALYZE stats available, so make a guess using estimated number
		 * of distinct values and assuming they are equally common. (The guess
		 * is unlikely to be very good, but we do know a few special cases.)
		 */
		selec = 1.0 / get_variable_numdistinct(vardata, &isdefault);
	}

	/* now adjust if we wanted <> rather than = */
	if (negate)
		selec = 1.0 - selec - nullfrac;

	/* result should be in range, but make sure... */
	CLAMP_PROBABILITY(selec);

	return selec;
}

/*
 *		neqsel			- Selectivity of "!=" for any data types.
 *
 * This routine is also used for some operators that are not "!="
 * but have comparable selectivity behavior.  See above comments
 * for eqsel().
 */
Datum
neqsel(PG_FUNCTION_ARGS)
{
	PG_RETURN_FLOAT8((float8) eqsel_internal(fcinfo, true));
}

/*
 *	scalarineqsel		- Selectivity of "<", "<=", ">", ">=" for scalars.
 *
 * This is the guts of scalarltsel/scalarlesel/scalargtsel/scalargesel.
 * The isgt and iseq flags distinguish which of the four cases apply.
 *
 * The caller has commuted the clause, if necessary, so that we can treat
 * the variable as being on the left.  The caller must also make sure that
 * the other side of the clause is a non-null Const, and dissect that into
 * a value and datatype.  (This definition simplifies some callers that
 * want to estimate against a computed value instead of a Const node.)
 *
 * This routine works for any datatype (or pair of datatypes) known to
 * convert_to_scalar().  If it is applied to some other datatype,
 * it will return an approximate estimate based on assuming that the constant
 * value falls in the middle of the bin identified by binary search.
 */
static double
scalarineqsel(PlannerInfo *root, Oid operator, bool isgt, bool iseq,
			  Oid collation,
			  VariableStatData *vardata, Datum constval, Oid consttype)
{
	Form_pg_statistic stats;
	FmgrInfo	opproc;
	double		mcv_selec,
				hist_selec,
				sumcommon;
	double		selec;

	if (!HeapTupleIsValid(vardata->statsTuple))
	{
		/*
		 * No stats are available.  Typically this means we have to fall back
		 * on the default estimate; but if the variable is CTID then we can
		 * make an estimate based on comparing the constant to the table size.
		 */
		if (vardata->var && IsA(vardata->var, Var) &&
			((Var *) vardata->var)->varattno == SelfItemPointerAttributeNumber)
		{
			ItemPointer itemptr;
			double		block;
			double		density;

			/*
			 * If the relation's empty, we're going to include all of it.
			 * (This is mostly to avoid divide-by-zero below.)
			 */
			if (vardata->rel->pages == 0)
				return 1.0;

			itemptr = (ItemPointer) DatumGetPointer(constval);
			block = ItemPointerGetBlockNumberNoCheck(itemptr);

			/*
			 * Determine the average number of tuples per page (density).
			 *
			 * Since the last page will, on average, be only half full, we can
			 * estimate it to have half as many tuples as earlier pages.  So
			 * give it half the weight of a regular page.
			 */
			density = vardata->rel->tuples / (vardata->rel->pages - 0.5);

			/* If target is the last page, use half the density. */
			if (block >= vardata->rel->pages - 1)
				density *= 0.5;

			/*
			 * Using the average tuples per page, calculate how far into the
			 * page the itemptr is likely to be and adjust block accordingly,
			 * by adding that fraction of a whole block (but never more than a
			 * whole block, no matter how high the itemptr's offset is).  Here
			 * we are ignoring the possibility of dead-tuple line pointers,
			 * which is fairly bogus, but we lack the info to do better.
			 */
			if (density > 0.0)
			{
				OffsetNumber offset = ItemPointerGetOffsetNumberNoCheck(itemptr);

				block += Min(offset / density, 1.0);
			}

			/*
			 * Convert relative block number to selectivity.  Again, the last
			 * page has only half weight.
			 */
			selec = block / (vardata->rel->pages - 0.5);

			/*
			 * The calculation so far gave us a selectivity for the "<=" case.
			 * We'll have one fewer tuple for "<" and one additional tuple for
			 * ">=", the latter of which we'll reverse the selectivity for
			 * below, so we can simply subtract one tuple for both cases.  The
			 * cases that need this adjustment can be identified by iseq being
			 * equal to isgt.
			 */
			if (iseq == isgt && vardata->rel->tuples >= 1.0)
				selec -= (1.0 / vardata->rel->tuples);

			/* Finally, reverse the selectivity for the ">", ">=" cases. */
			if (isgt)
				selec = 1.0 - selec;

			CLAMP_PROBABILITY(selec);
			return selec;
		}

		/* no stats available, so default result */
		return DEFAULT_INEQ_SEL;
	}
	stats = (Form_pg_statistic) GETSTRUCT(vardata->statsTuple);

	fmgr_info(get_opcode(operator), &opproc);

	/*
	 * If we have most-common-values info, add up the fractions of the MCV
	 * entries that satisfy MCV OP CONST.  These fractions contribute directly
	 * to the result selectivity.  Also add up the total fraction represented
	 * by MCV entries.
	 */
	mcv_selec = mcv_selectivity(vardata, &opproc, collation, constval, true,
								&sumcommon);

	/*
	 * If there is a histogram, determine which bin the constant falls in, and
	 * compute the resulting contribution to selectivity.
	 */
	hist_selec = ineq_histogram_selectivity(root, vardata,
											operator, &opproc, isgt, iseq,
											collation,
											constval, consttype);

	/*
	 * Now merge the results from the MCV and histogram calculations,
	 * realizing that the histogram covers only the non-null values that are
	 * not listed in MCV.
	 */
	selec = 1.0 - stats->stanullfrac - sumcommon;

	if (hist_selec >= 0.0)
		selec *= hist_selec;
	else
	{
		/*
		 * If no histogram but there are values not accounted for by MCV,
		 * arbitrarily assume half of them will match.
		 */
		selec *= 0.5;
	}

	selec += mcv_selec;

	/* result should be in range, but make sure... */
	CLAMP_PROBABILITY(selec);

	return selec;
}

/*
 *	mcv_selectivity			- Examine the MCV list for selectivity estimates
 *
 * Determine the fraction of the variable's MCV population that satisfies
 * the predicate (VAR OP CONST), or (CONST OP VAR) if !varonleft.  Also
 * compute the fraction of the total column population represented by the MCV
 * list.  This code will work for any boolean-returning predicate operator.
 *
 * The function result is the MCV selectivity, and the fraction of the
 * total population is returned into *sumcommonp.  Zeroes are returned
 * if there is no MCV list.
 */
double
mcv_selectivity(VariableStatData *vardata, FmgrInfo *opproc, Oid collation,
				Datum constval, bool varonleft,
				double *sumcommonp)
{
	double		mcv_selec,
				sumcommon;
	AttStatsSlot sslot;
	int			i;

	mcv_selec = 0.0;
	sumcommon = 0.0;

	if (HeapTupleIsValid(vardata->statsTuple) &&
		statistic_proc_security_check(vardata, opproc->fn_oid) &&
		get_attstatsslot(&sslot, vardata->statsTuple,
						 STATISTIC_KIND_MCV, InvalidOid,
						 ATTSTATSSLOT_VALUES | ATTSTATSSLOT_NUMBERS))
	{
		LOCAL_FCINFO(fcinfo, 2);

		/*
		 * We invoke the opproc "by hand" so that we won't fail on NULL
		 * results.  Such cases won't arise for normal comparison functions,
		 * but generic_restriction_selectivity could perhaps be used with
		 * operators that can return NULL.  A small side benefit is to not
		 * need to re-initialize the fcinfo struct from scratch each time.
		 */
		InitFunctionCallInfoData(*fcinfo, opproc, 2, collation,
								 NULL, NULL);
		fcinfo->args[0].isnull = false;
		fcinfo->args[1].isnull = false;
		/* be careful to apply operator right way 'round */
		if (varonleft)
			fcinfo->args[1].value = constval;
		else
			fcinfo->args[0].value = constval;

		for (i = 0; i < sslot.nvalues; i++)
		{
			Datum		fresult;

			if (varonleft)
				fcinfo->args[0].value = sslot.values[i];
			else
				fcinfo->args[1].value = sslot.values[i];
			fcinfo->isnull = false;
			fresult = FunctionCallInvoke(fcinfo);
			if (!fcinfo->isnull && DatumGetBool(fresult))
				mcv_selec += sslot.numbers[i];
			sumcommon += sslot.numbers[i];
		}
		free_attstatsslot(&sslot);
	}

	*sumcommonp = sumcommon;
	return mcv_selec;
}

/*
 *	histogram_selectivity	- Examine the histogram for selectivity estimates
 *
 * Determine the fraction of the variable's histogram entries that satisfy
 * the predicate (VAR OP CONST), or (CONST OP VAR) if !varonleft.
 *
 * This code will work for any boolean-returning predicate operator, whether
 * or not it has anything to do with the histogram sort operator.  We are
 * essentially using the histogram just as a representative sample.  However,
 * small histograms are unlikely to be all that representative, so the caller
 * should be prepared to fall back on some other estimation approach when the
 * histogram is missing or very small.  It may also be prudent to combine this
 * approach with another one when the histogram is small.
 *
 * If the actual histogram size is not at least min_hist_size, we won't bother
 * to do the calculation at all.  Also, if the n_skip parameter is > 0, we
 * ignore the first and last n_skip histogram elements, on the grounds that
 * they are outliers and hence not very representative.  Typical values for
 * these parameters are 10 and 1.
 *
 * The function result is the selectivity, or -1 if there is no histogram
 * or it's smaller than min_hist_size.
 *
 * The output parameter *hist_size receives the actual histogram size,
 * or zero if no histogram.  Callers may use this number to decide how
 * much faith to put in the function result.
 *
 * Note that the result disregards both the most-common-values (if any) and
 * null entries.  The caller is expected to combine this result with
 * statistics for those portions of the column population.  It may also be
 * prudent to clamp the result range, ie, disbelieve exact 0 or 1 outputs.
 */
double
histogram_selectivity(VariableStatData *vardata,
					  FmgrInfo *opproc, Oid collation,
					  Datum constval, bool varonleft,
					  int min_hist_size, int n_skip,
					  int *hist_size)
{
	double		result;
	AttStatsSlot sslot;

	/* check sanity of parameters */
	Assert(n_skip >= 0);
	Assert(min_hist_size > 2 * n_skip);

	if (HeapTupleIsValid(vardata->statsTuple) &&
		statistic_proc_security_check(vardata, opproc->fn_oid) &&
		get_attstatsslot(&sslot, vardata->statsTuple,
						 STATISTIC_KIND_HISTOGRAM, InvalidOid,
						 ATTSTATSSLOT_VALUES))
	{
		*hist_size = sslot.nvalues;
		if (sslot.nvalues >= min_hist_size)
		{
			LOCAL_FCINFO(fcinfo, 2);
			int			nmatch = 0;
			int			i;

			/*
			 * We invoke the opproc "by hand" so that we won't fail on NULL
			 * results.  Such cases won't arise for normal comparison
			 * functions, but generic_restriction_selectivity could perhaps be
			 * used with operators that can return NULL.  A small side benefit
			 * is to not need to re-initialize the fcinfo struct from scratch
			 * each time.
			 */
			InitFunctionCallInfoData(*fcinfo, opproc, 2, collation,
									 NULL, NULL);
			fcinfo->args[0].isnull = false;
			fcinfo->args[1].isnull = false;
			/* be careful to apply operator right way 'round */
			if (varonleft)
				fcinfo->args[1].value = constval;
			else
				fcinfo->args[0].value = constval;

			for (i = n_skip; i < sslot.nvalues - n_skip; i++)
			{
				Datum		fresult;

				if (varonleft)
					fcinfo->args[0].value = sslot.values[i];
				else
					fcinfo->args[1].value = sslot.values[i];
				fcinfo->isnull = false;
				fresult = FunctionCallInvoke(fcinfo);
				if (!fcinfo->isnull && DatumGetBool(fresult))
					nmatch++;
			}
			result = ((double) nmatch) / ((double) (sslot.nvalues - 2 * n_skip));
		}
		else
			result = -1;
		free_attstatsslot(&sslot);
	}
	else
	{
		*hist_size = 0;
		result = -1;
	}

	return result;
}

/*
 *	generic_restriction_selectivity		- Selectivity for almost anything
 *
 * This function estimates selectivity for operators that we don't have any
 * special knowledge about, but are on data types that we collect standard
 * MCV and/or histogram statistics for.  (Additional assumptions are that
 * the operator is strict and immutable, or at least stable.)
 *
 * If we have "VAR OP CONST" or "CONST OP VAR", selectivity is estimated by
 * applying the operator to each element of the column's MCV and/or histogram
 * stats, and merging the results using the assumption that the histogram is
 * a reasonable random sample of the column's non-MCV population.  Note that
 * if the operator's semantics are related to the histogram ordering, this
 * might not be such a great assumption; other functions such as
 * scalarineqsel() are probably a better match in such cases.
 *
 * Otherwise, fall back to the default selectivity provided by the caller.
 */
double
generic_restriction_selectivity(PlannerInfo *root, Oid oproid, Oid collation,
								List *args, int varRelid,
								double default_selectivity)
{
	double		selec;
	VariableStatData vardata;
	Node	   *other;
	bool		varonleft;

	/*
	 * If expression is not variable OP something or something OP variable,
	 * then punt and return the default estimate.
	 */
	if (!get_restriction_variable(root, args, varRelid,
								  &vardata, &other, &varonleft))
		return default_selectivity;

	/*
	 * If the something is a NULL constant, assume operator is strict and
	 * return zero, ie, operator will never return TRUE.
	 */
	if (IsA(other, Const) &&
		((Const *) other)->constisnull)
	{
		ReleaseVariableStats(vardata);
		return 0.0;
	}

	if (IsA(other, Const))
	{
		/* Variable is being compared to a known non-null constant */
		Datum		constval = ((Const *) other)->constvalue;
		FmgrInfo	opproc;
		double		mcvsum;
		double		mcvsel;
		double		nullfrac;
		int			hist_size;

		fmgr_info(get_opcode(oproid), &opproc);

		/*
		 * Calculate the selectivity for the column's most common values.
		 */
		mcvsel = mcv_selectivity(&vardata, &opproc, collation,
								 constval, varonleft,
								 &mcvsum);

		/*
		 * If the histogram is large enough, see what fraction of it matches
		 * the query, and assume that's representative of the non-MCV
		 * population.  Otherwise use the default selectivity for the non-MCV
		 * population.
		 */
		selec = histogram_selectivity(&vardata, &opproc, collation,
									  constval, varonleft,
									  10, 1, &hist_size);
		if (selec < 0)
		{
			/* Nope, fall back on default */
			selec = default_selectivity;
		}
		else if (hist_size < 100)
		{
			/*
			 * For histogram sizes from 10 to 100, we combine the histogram
			 * and default selectivities, putting increasingly more trust in
			 * the histogram for larger sizes.
			 */
			double		hist_weight = hist_size / 100.0;

			selec = selec * hist_weight +
				default_selectivity * (1.0 - hist_weight);
		}

		/* In any case, don't believe extremely small or large estimates. */
		if (selec < 0.0001)
			selec = 0.0001;
		else if (selec > 0.9999)
			selec = 0.9999;

		/* Don't forget to account for nulls. */
		if (HeapTupleIsValid(vardata.statsTuple))
			nullfrac = ((Form_pg_statistic) GETSTRUCT(vardata.statsTuple))->stanullfrac;
		else
			nullfrac = 0.0;

		/*
		 * Now merge the results from the MCV and histogram calculations,
		 * realizing that the histogram covers only the non-null values that
		 * are not listed in MCV.
		 */
		selec *= 1.0 - nullfrac - mcvsum;
		selec += mcvsel;
	}
	else
	{
		/* Comparison value is not constant, so we can't do anything */
		selec = default_selectivity;
	}

	ReleaseVariableStats(vardata);

	/* result should be in range, but make sure... */
	CLAMP_PROBABILITY(selec);

	return selec;
}

/*
 *	ineq_histogram_selectivity	- Examine the histogram for scalarineqsel
 *
 * Determine the fraction of the variable's histogram population that
 * satisfies the inequality condition, ie, VAR < (or <=, >, >=) CONST.
 * The isgt and iseq flags distinguish which of the four cases apply.
 *
 * While opproc could be looked up from the operator OID, common callers
 * also need to call it separately, so we make the caller pass both.
 *
 * Returns -1 if there is no histogram (valid results will always be >= 0).
 *
 * Note that the result disregards both the most-common-values (if any) and
 * null entries.  The caller is expected to combine this result with
 * statistics for those portions of the column population.
 *
 * This is exported so that some other estimation functions can use it.
 */
double
ineq_histogram_selectivity(PlannerInfo *root,
						   VariableStatData *vardata,
						   Oid opoid, FmgrInfo *opproc, bool isgt, bool iseq,
						   Oid collation,
						   Datum constval, Oid consttype)
{
	double		hist_selec;
	AttStatsSlot sslot;

	hist_selec = -1.0;

	/*
	 * Someday, ANALYZE might store more than one histogram per rel/att,
	 * corresponding to more than one possible sort ordering defined for the
	 * column type.  Right now, we know there is only one, so just grab it and
	 * see if it matches the query.
	 *
	 * Note that we can't use opoid as search argument; the staop appearing in
	 * pg_statistic will be for the relevant '<' operator, but what we have
	 * might be some other inequality operator such as '>='.  (Even if opoid
	 * is a '<' operator, it could be cross-type.)  Hence we must use
	 * comparison_ops_are_compatible() to see if the operators match.
	 */
	if (HeapTupleIsValid(vardata->statsTuple) &&
		statistic_proc_security_check(vardata, opproc->fn_oid) &&
		get_attstatsslot(&sslot, vardata->statsTuple,
						 STATISTIC_KIND_HISTOGRAM, InvalidOid,
						 ATTSTATSSLOT_VALUES))
	{
		if (sslot.nvalues > 1 &&
			sslot.stacoll == collation &&
			comparison_ops_are_compatible(sslot.staop, opoid))
		{
			/*
			 * Use binary search to find the desired location, namely the
			 * right end of the histogram bin containing the comparison value,
			 * which is the leftmost entry for which the comparison operator
			 * succeeds (if isgt) or fails (if !isgt).
			 *
			 * In this loop, we pay no attention to whether the operator iseq
			 * or not; that detail will be mopped up below.  (We cannot tell,
			 * anyway, whether the operator thinks the values are equal.)
			 *
			 * If the binary search accesses the first or last histogram
			 * entry, we try to replace that endpoint with the true column min
			 * or max as found by get_actual_variable_range().  This
			 * ameliorates misestimates when the min or max is moving as a
			 * result of changes since the last ANALYZE.  Note that this could
			 * result in effectively including MCVs into the histogram that
			 * weren't there before, but we don't try to correct for that.
			 */
			double		histfrac;
			int			lobound = 0;	/* first possible slot to search */
			int			hibound = sslot.nvalues;	/* last+1 slot to search */
			bool		have_end = false;

			/*
			 * If there are only two histogram entries, we'll want up-to-date
			 * values for both.  (If there are more than two, we need at most
			 * one of them to be updated, so we deal with that within the
			 * loop.)
			 */
			if (sslot.nvalues == 2)
				have_end = get_actual_variable_range(root,
													 vardata,
													 sslot.staop,
													 collation,
													 &sslot.values[0],
													 &sslot.values[1]);

			while (lobound < hibound)
			{
				int			probe = (lobound + hibound) / 2;
				bool		ltcmp;

				/*
				 * If we find ourselves about to compare to the first or last
				 * histogram entry, first try to replace it with the actual
				 * current min or max (unless we already did so above).
				 */
				if (probe == 0 && sslot.nvalues > 2)
					have_end = get_actual_variable_range(root,
														 vardata,
														 sslot.staop,
														 collation,
														 &sslot.values[0],
														 NULL);
				else if (probe == sslot.nvalues - 1 && sslot.nvalues > 2)
					have_end = get_actual_variable_range(root,
														 vardata,
														 sslot.staop,
														 collation,
														 NULL,
														 &sslot.values[probe]);

				ltcmp = DatumGetBool(FunctionCall2Coll(opproc,
													   collation,
													   sslot.values[probe],
													   constval));
				if (isgt)
					ltcmp = !ltcmp;
				if (ltcmp)
					lobound = probe + 1;
				else
					hibound = probe;
			}

			if (lobound <= 0)
			{
				/*
				 * Constant is below lower histogram boundary.  More
				 * precisely, we have found that no entry in the histogram
				 * satisfies the inequality clause (if !isgt) or they all do
				 * (if isgt).  We estimate that that's true of the entire
				 * table, so set histfrac to 0.0 (which we'll flip to 1.0
				 * below, if isgt).
				 */
				histfrac = 0.0;
			}
			else if (lobound >= sslot.nvalues)
			{
				/*
				 * Inverse case: constant is above upper histogram boundary.
				 */
				histfrac = 1.0;
			}
			else
			{
				/* We have values[i-1] <= constant <= values[i]. */
				int			i = lobound;
				double		eq_selec = 0;
				double		val,
							high,
							low;
				double		binfrac;

				/*
				 * In the cases where we'll need it below, obtain an estimate
				 * of the selectivity of "x = constval".  We use a calculation
				 * similar to what var_eq_const() does for a non-MCV constant,
				 * ie, estimate that all distinct non-MCV values occur equally
				 * often.  But multiplication by "1.0 - sumcommon - nullfrac"
				 * will be done by our caller, so we shouldn't do that here.
				 * Therefore we can't try to clamp the estimate by reference
				 * to the least common MCV; the result would be too small.
				 *
				 * Note: since this is effectively assuming that constval
				 * isn't an MCV, it's logically dubious if constval in fact is
				 * one.  But we have to apply *some* correction for equality,
				 * and anyway we cannot tell if constval is an MCV, since we
				 * don't have a suitable equality operator at hand.
				 */
				if (i == 1 || isgt == iseq)
				{
					double		otherdistinct;
					bool		isdefault;
					AttStatsSlot mcvslot;

					/* Get estimated number of distinct values */
					otherdistinct = get_variable_numdistinct(vardata,
															 &isdefault);

					/* Subtract off the number of known MCVs */
					if (get_attstatsslot(&mcvslot, vardata->statsTuple,
										 STATISTIC_KIND_MCV, InvalidOid,
										 ATTSTATSSLOT_NUMBERS))
					{
						otherdistinct -= mcvslot.nnumbers;
						free_attstatsslot(&mcvslot);
					}

					/* If result doesn't seem sane, leave eq_selec at 0 */
					if (otherdistinct > 1)
						eq_selec = 1.0 / otherdistinct;
				}

				/*
				 * Convert the constant and the two nearest bin boundary
				 * values to a uniform comparison scale, and do a linear
				 * interpolation within this bin.
				 */
				if (convert_to_scalar(constval, consttype, collation,
									  &val,
									  sslot.values[i - 1], sslot.values[i],
									  vardata->vartype,
									  &low, &high))
				{
					if (high <= low)
					{
						/* cope if bin boundaries appear identical */
						binfrac = 0.5;
					}
					else if (val <= low)
						binfrac = 0.0;
					else if (val >= high)
						binfrac = 1.0;
					else
					{
						binfrac = (val - low) / (high - low);

						/*
						 * Watch out for the possibility that we got a NaN or
						 * Infinity from the division.  This can happen
						 * despite the previous checks, if for example "low"
						 * is -Infinity.
						 */
						if (isnan(binfrac) ||
							binfrac < 0.0 || binfrac > 1.0)
							binfrac = 0.5;
					}
				}
				else
				{
					/*
					 * Ideally we'd produce an error here, on the grounds that
					 * the given operator shouldn't have scalarXXsel
					 * registered as its selectivity func unless we can deal
					 * with its operand types.  But currently, all manner of
					 * stuff is invoking scalarXXsel, so give a default
					 * estimate until that can be fixed.
					 */
					binfrac = 0.5;
				}

				/*
				 * Now, compute the overall selectivity across the values
				 * represented by the histogram.  We have i-1 full bins and
				 * binfrac partial bin below the constant.
				 */
				histfrac = (double) (i - 1) + binfrac;
				histfrac /= (double) (sslot.nvalues - 1);

				/*
				 * At this point, histfrac is an estimate of the fraction of
				 * the population represented by the histogram that satisfies
				 * "x <= constval".  Somewhat remarkably, this statement is
				 * true regardless of which operator we were doing the probes
				 * with, so long as convert_to_scalar() delivers reasonable
				 * results.  If the probe constant is equal to some histogram
				 * entry, we would have considered the bin to the left of that
				 * entry if probing with "<" or ">=", or the bin to the right
				 * if probing with "<=" or ">"; but binfrac would have come
				 * out as 1.0 in the first case and 0.0 in the second, leading
				 * to the same histfrac in either case.  For probe constants
				 * between histogram entries, we find the same bin and get the
				 * same estimate with any operator.
				 *
				 * The fact that the estimate corresponds to "x <= constval"
				 * and not "x < constval" is because of the way that ANALYZE
				 * constructs the histogram: each entry is, effectively, the
				 * rightmost value in its sample bucket.  So selectivity
				 * values that are exact multiples of 1/(histogram_size-1)
				 * should be understood as estimates including a histogram
				 * entry plus everything to its left.
				 *
				 * However, that breaks down for the first histogram entry,
				 * which necessarily is the leftmost value in its sample
				 * bucket.  That means the first histogram bin is slightly
				 * narrower than the rest, by an amount equal to eq_selec.
				 * Another way to say that is that we want "x <= leftmost" to
				 * be estimated as eq_selec not zero.  So, if we're dealing
				 * with the first bin (i==1), rescale to make that true while
				 * adjusting the rest of that bin linearly.
				 */
				if (i == 1)
					histfrac += eq_selec * (1.0 - binfrac);

				/*
				 * "x <= constval" is good if we want an estimate for "<=" or
				 * ">", but if we are estimating for "<" or ">=", we now need
				 * to decrease the estimate by eq_selec.
				 */
				if (isgt == iseq)
					histfrac -= eq_selec;
			}

			/*
			 * Now the estimate is finished for "<" and "<=" cases.  If we are
			 * estimating for ">" or ">=", flip it.
			 */
			hist_selec = isgt ? (1.0 - histfrac) : histfrac;

			/*
			 * The histogram boundaries are only approximate to begin with,
			 * and may well be out of date anyway.  Therefore, don't believe
			 * extremely small or large selectivity estimates --- unless we
			 * got actual current endpoint values from the table, in which
			 * case just do the usual sanity clamp.  Somewhat arbitrarily, we
			 * set the cutoff for other cases at a hundredth of the histogram
			 * resolution.
			 */
			if (have_end)
				CLAMP_PROBABILITY(hist_selec);
			else
			{
				double		cutoff = 0.01 / (double) (sslot.nvalues - 1);

				if (hist_selec < cutoff)
					hist_selec = cutoff;
				else if (hist_selec > 1.0 - cutoff)
					hist_selec = 1.0 - cutoff;
			}
		}
		else if (sslot.nvalues > 1)
		{
			/*
			 * If we get here, we have a histogram but it's not sorted the way
			 * we want.  Do a brute-force search to see how many of the
			 * entries satisfy the comparison condition, and take that
			 * fraction as our estimate.  (This is identical to the inner loop
			 * of histogram_selectivity; maybe share code?)
			 */
			LOCAL_FCINFO(fcinfo, 2);
			int			nmatch = 0;

			InitFunctionCallInfoData(*fcinfo, opproc, 2, collation,
									 NULL, NULL);
			fcinfo->args[0].isnull = false;
			fcinfo->args[1].isnull = false;
			fcinfo->args[1].value = constval;
			for (int i = 0; i < sslot.nvalues; i++)
			{
				Datum		fresult;

				fcinfo->args[0].value = sslot.values[i];
				fcinfo->isnull = false;
				fresult = FunctionCallInvoke(fcinfo);
				if (!fcinfo->isnull && DatumGetBool(fresult))
					nmatch++;
			}
			hist_selec = ((double) nmatch) / ((double) sslot.nvalues);

			/*
			 * As above, clamp to a hundredth of the histogram resolution.
			 * This case is surely even less trustworthy than the normal one,
			 * so we shouldn't believe exact 0 or 1 selectivity.  (Maybe the
			 * clamp should be more restrictive in this case?)
			 */
			{
				double		cutoff = 0.01 / (double) (sslot.nvalues - 1);

				if (hist_selec < cutoff)
					hist_selec = cutoff;
				else if (hist_selec > 1.0 - cutoff)
					hist_selec = 1.0 - cutoff;
			}
		}

		free_attstatsslot(&sslot);
	}

	return hist_selec;
}

/*
 * Common wrapper function for the selectivity estimators that simply
 * invoke scalarineqsel().
 */
static Datum
scalarineqsel_wrapper(PG_FUNCTION_ARGS, bool isgt, bool iseq)
{
	PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
	Oid			operator = PG_GETARG_OID(1);
	List	   *args = (List *) PG_GETARG_POINTER(2);
	int			varRelid = PG_GETARG_INT32(3);
	Oid			collation = PG_GET_COLLATION();
	VariableStatData vardata;
	Node	   *other;
	bool		varonleft;
	Datum		constval;
	Oid			consttype;
	double		selec;

	/*
	 * If expression is not variable op something or something op variable,
	 * then punt and return a default estimate.
	 */
	if (!get_restriction_variable(root, args, varRelid,
								  &vardata, &other, &varonleft))
		PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL);

	/*
	 * Can't do anything useful if the something is not a constant, either.
	 */
	if (!IsA(other, Const))
	{
		ReleaseVariableStats(vardata);
		PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL);
	}

	/*
	 * If the constant is NULL, assume operator is strict and return zero, ie,
	 * operator will never return TRUE.
	 */
	if (((Const *) other)->constisnull)
	{
		ReleaseVariableStats(vardata);
		PG_RETURN_FLOAT8(0.0);
	}
	constval = ((Const *) other)->constvalue;
	consttype = ((Const *) other)->consttype;

	/*
	 * Force the var to be on the left to simplify logic in scalarineqsel.
	 */
	if (!varonleft)
	{
		operator = get_commutator(operator);
		if (!operator)
		{
			/* Use default selectivity (should we raise an error instead?) */
			ReleaseVariableStats(vardata);
			PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL);
		}
		isgt = !isgt;
	}

	/* The rest of the work is done by scalarineqsel(). */
	selec = scalarineqsel(root, operator, isgt, iseq, collation,
						  &vardata, constval, consttype);

	ReleaseVariableStats(vardata);

	PG_RETURN_FLOAT8((float8) selec);
}

/*
 *		scalarltsel		- Selectivity of "<" for scalars.
 */
Datum
scalarltsel(PG_FUNCTION_ARGS)
{
	return scalarineqsel_wrapper(fcinfo, false, false);
}

/*
 *		scalarlesel		- Selectivity of "<=" for scalars.
 */
Datum
scalarlesel(PG_FUNCTION_ARGS)
{
	return scalarineqsel_wrapper(fcinfo, false, true);
}

/*
 *		scalargtsel		- Selectivity of ">" for scalars.
 */
Datum
scalargtsel(PG_FUNCTION_ARGS)
{
	return scalarineqsel_wrapper(fcinfo, true, false);
}

/*
 *		scalargesel		- Selectivity of ">=" for scalars.
 */
Datum
scalargesel(PG_FUNCTION_ARGS)
{
	return scalarineqsel_wrapper(fcinfo, true, true);
}

/*
 *		boolvarsel		- Selectivity of Boolean variable.
 *
 * This can actually be called on any boolean-valued expression.  If it
 * involves only Vars of the specified relation, and if there are statistics
 * about the Var or expression (the latter is possible if it's indexed) then
 * we'll produce a real estimate; otherwise it's just a default.
 */
Selectivity
boolvarsel(PlannerInfo *root, Node *arg, int varRelid)
{
	VariableStatData vardata;
	double		selec;

	examine_variable(root, arg, varRelid, &vardata);
	if (HeapTupleIsValid(vardata.statsTuple))
	{
		/*
		 * A boolean variable V is equivalent to the clause V = 't', so we
		 * compute the selectivity as if that is what we have.
		 */
		selec = var_eq_const(&vardata, BooleanEqualOperator, InvalidOid,
							 BoolGetDatum(true), false, true, false);
	}
	else if (is_funcclause(arg))
	{
		/*
		 * If we have no stats and it's a function call, estimate 0.3333333.
		 * This seems a pretty unprincipled choice, but Postgres has been
		 * using that estimate for function calls since 1992.  The hoariness
		 * of this behavior suggests that we should not be in too much hurry
		 * to use another value.
		 */
		selec = 0.3333333;
	}
	else
	{
		/* Otherwise, the default estimate is 0.5 */
		selec = 0.5;
	}
	ReleaseVariableStats(vardata);
	return selec;
}

/*
 *		booltestsel		- Selectivity of BooleanTest Node.
 */
Selectivity
booltestsel(PlannerInfo *root, BoolTestType booltesttype, Node *arg,
			int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo)
{
	VariableStatData vardata;
	double		selec;

	examine_variable(root, arg, varRelid, &vardata);

	if (HeapTupleIsValid(vardata.statsTuple))
	{
		Form_pg_statistic stats;
		double		freq_null;
		AttStatsSlot sslot;

		stats = (Form_pg_statistic) GETSTRUCT(vardata.statsTuple);
		freq_null = stats->stanullfrac;

		if (get_attstatsslot(&sslot, vardata.statsTuple,
							 STATISTIC_KIND_MCV, InvalidOid,
							 ATTSTATSSLOT_VALUES | ATTSTATSSLOT_NUMBERS)
			&& sslot.nnumbers > 0)
		{
			double		freq_true;
			double		freq_false;

			/*
			 * Get first MCV frequency and derive frequency for true.
			 */
			if (DatumGetBool(sslot.values[0]))
				freq_true = sslot.numbers[0];
			else
				freq_true = 1.0 - sslot.numbers[0] - freq_null;

			/*
			 * Next derive frequency for false. Then use these as appropriate
			 * to derive frequency for each case.
			 */
			freq_false = 1.0 - freq_true - freq_null;

			switch (booltesttype)
			{
				case IS_UNKNOWN:
					/* select only NULL values */
					selec = freq_null;
					break;
				case IS_NOT_UNKNOWN:
					/* select non-NULL values */
					selec = 1.0 - freq_null;
					break;
				case IS_TRUE:
					/* select only TRUE values */
					selec = freq_true;
					break;
				case IS_NOT_TRUE:
					/* select non-TRUE values */
					selec = 1.0 - freq_true;
					break;
				case IS_FALSE:
					/* select only FALSE values */
					selec = freq_false;
					break;
				case IS_NOT_FALSE:
					/* select non-FALSE values */
					selec = 1.0 - freq_false;
					break;
				default:
					elog(ERROR, "unrecognized booltesttype: %d",
						 (int) booltesttype);
					selec = 0.0;	/* Keep compiler quiet */
					break;
			}

			free_attstatsslot(&sslot);
		}
		else
		{
			/*
			 * No most-common-value info available. Still have null fraction
			 * information, so use it for IS [NOT] UNKNOWN. Otherwise adjust
			 * for null fraction and assume a 50-50 split of TRUE and FALSE.
			 */
			switch (booltesttype)
			{
				case IS_UNKNOWN:
					/* select only NULL values */
					selec = freq_null;
					break;
				case IS_NOT_UNKNOWN:
					/* select non-NULL values */
					selec = 1.0 - freq_null;
					break;
				case IS_TRUE:
				case IS_FALSE:
					/* Assume we select half of the non-NULL values */
					selec = (1.0 - freq_null) / 2.0;
					break;
				case IS_NOT_TRUE:
				case IS_NOT_FALSE:
					/* Assume we select NULLs plus half of the non-NULLs */
					/* equiv. to freq_null + (1.0 - freq_null) / 2.0 */
					selec = (freq_null + 1.0) / 2.0;
					break;
				default:
					elog(ERROR, "unrecognized booltesttype: %d",
						 (int) booltesttype);
					selec = 0.0;	/* Keep compiler quiet */
					break;
			}
		}
	}
	else
	{
		/*
		 * If we can't get variable statistics for the argument, perhaps
		 * clause_selectivity can do something with it.  We ignore the
		 * possibility of a NULL value when using clause_selectivity, and just
		 * assume the value is either TRUE or FALSE.
		 */
		switch (booltesttype)
		{
			case IS_UNKNOWN:
				selec = DEFAULT_UNK_SEL;
				break;
			case IS_NOT_UNKNOWN:
				selec = DEFAULT_NOT_UNK_SEL;
				break;
			case IS_TRUE:
			case IS_NOT_FALSE:
				selec = (double) clause_selectivity(root, arg,
													varRelid,
													jointype, sjinfo);
				break;
			case IS_FALSE:
			case IS_NOT_TRUE:
				selec = 1.0 - (double) clause_selectivity(root, arg,
														  varRelid,
														  jointype, sjinfo);
				break;
			default:
				elog(ERROR, "unrecognized booltesttype: %d",
					 (int) booltesttype);
				selec = 0.0;	/* Keep compiler quiet */
				break;
		}
	}

	ReleaseVariableStats(vardata);

	/* result should be in range, but make sure... */
	CLAMP_PROBABILITY(selec);

	return (Selectivity) selec;
}

/*
 *		nulltestsel		- Selectivity of NullTest Node.
 */
Selectivity
nulltestsel(PlannerInfo *root, NullTestType nulltesttype, Node *arg,
			int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo)
{
	VariableStatData vardata;
	double		selec;

	examine_variable(root, arg, varRelid, &vardata);

	if (HeapTupleIsValid(vardata.statsTuple))
	{
		Form_pg_statistic stats;
		double		freq_null;

		stats = (Form_pg_statistic) GETSTRUCT(vardata.statsTuple);
		freq_null = stats->stanullfrac;

		switch (nulltesttype)
		{
			case IS_NULL:

				/*
				 * Use freq_null directly.
				 */
				selec = freq_null;
				break;
			case IS_NOT_NULL:

				/*
				 * Select not unknown (not null) values. Calculate from
				 * freq_null.
				 */
				selec = 1.0 - freq_null;
				break;
			default:
				elog(ERROR, "unrecognized nulltesttype: %d",
					 (int) nulltesttype);
				return (Selectivity) 0; /* keep compiler quiet */
		}
	}
	else if (vardata.var && IsA(vardata.var, Var) &&
			 ((Var *) vardata.var)->varattno < 0)
	{
		/*
		 * There are no stats for system columns, but we know they are never
		 * NULL.
		 */
		selec = (nulltesttype == IS_NULL) ? 0.0 : 1.0;
	}
	else
	{
		/*
		 * No ANALYZE stats available, so make a guess
		 */
		switch (nulltesttype)
		{
			case IS_NULL:
				selec = DEFAULT_UNK_SEL;
				break;
			case IS_NOT_NULL:
				selec = DEFAULT_NOT_UNK_SEL;
				break;
			default:
				elog(ERROR, "unrecognized nulltesttype: %d",
					 (int) nulltesttype);
				return (Selectivity) 0; /* keep compiler quiet */
		}
	}

	ReleaseVariableStats(vardata);

	/* result should be in range, but make sure... */
	CLAMP_PROBABILITY(selec);

	return (Selectivity) selec;
}

/*
 * strip_array_coercion - strip binary-compatible relabeling from an array expr
 *
 * For array values, the parser normally generates ArrayCoerceExpr conversions,
 * but it seems possible that RelabelType might show up.  Also, the planner
 * is not currently tense about collapsing stacked ArrayCoerceExpr nodes,
 * so we need to be ready to deal with more than one level.
 */
static Node *
strip_array_coercion(Node *node)
{
	for (;;)
	{
		if (node && IsA(node, ArrayCoerceExpr))
		{
			ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;

			/*
			 * If the per-element expression is just a RelabelType on top of
			 * CaseTestExpr, then we know it's a binary-compatible relabeling.
			 */
			if (IsA(acoerce->elemexpr, RelabelType) &&
				IsA(((RelabelType *) acoerce->elemexpr)->arg, CaseTestExpr))
				node = (Node *) acoerce->arg;
			else
				break;
		}
		else if (node && IsA(node, RelabelType))
		{
			/* We don't really expect this case, but may as well cope */
			node = (Node *) ((RelabelType *) node)->arg;
		}
		else
			break;
	}
	return node;
}

/*
 *		scalararraysel		- Selectivity of ScalarArrayOpExpr Node.
 */
Selectivity
scalararraysel(PlannerInfo *root,
			   ScalarArrayOpExpr *clause,
			   bool is_join_clause,
			   int varRelid,
			   JoinType jointype,
			   SpecialJoinInfo *sjinfo)
{
	Oid			operator = clause->opno;
	bool		useOr = clause->useOr;
	bool		isEquality = false;
	bool		isInequality = false;
	Node	   *leftop;
	Node	   *rightop;
	Oid			nominal_element_type;
	Oid			nominal_element_collation;
	TypeCacheEntry *typentry;
	RegProcedure oprsel;
	FmgrInfo	oprselproc;
	Selectivity s1;
	Selectivity s1disjoint;

	/* First, deconstruct the expression */
	Assert(list_length(clause->args) == 2);
	leftop = (Node *) linitial(clause->args);
	rightop = (Node *) lsecond(clause->args);

	/* aggressively reduce both sides to constants */
	leftop = estimate_expression_value(root, leftop);
	rightop = estimate_expression_value(root, rightop);

	/* get nominal (after relabeling) element type of rightop */
	nominal_element_type = get_base_element_type(exprType(rightop));
	if (!OidIsValid(nominal_element_type))
		return (Selectivity) 0.5;	/* probably shouldn't happen */
	/* get nominal collation, too, for generating constants */
	nominal_element_collation = exprCollation(rightop);

	/* look through any binary-compatible relabeling of rightop */
	rightop = strip_array_coercion(rightop);

	/*
	 * Detect whether the operator is the default equality or inequality
	 * operator of the array element type.
	 */
	typentry = lookup_type_cache(nominal_element_type, TYPECACHE_EQ_OPR);
	if (OidIsValid(typentry->eq_opr))
	{
		if (operator == typentry->eq_opr)
			isEquality = true;
		else if (get_negator(operator) == typentry->eq_opr)
			isInequality = true;
	}

	/*
	 * If it is equality or inequality, we might be able to estimate this as a
	 * form of array containment; for instance "const = ANY(column)" can be
	 * treated as "ARRAY[const] <@ column".  scalararraysel_containment tries
	 * that, and returns the selectivity estimate if successful, or -1 if not.
	 */
	if ((isEquality || isInequality) && !is_join_clause)
	{
		s1 = scalararraysel_containment(root, leftop, rightop,
										nominal_element_type,
										isEquality, useOr, varRelid);
		if (s1 >= 0.0)
			return s1;
	}

	/*
	 * Look up the underlying operator's selectivity estimator. Punt if it
	 * hasn't got one.
	 */
	if (is_join_clause)
		oprsel = get_oprjoin(operator);
	else
		oprsel = get_oprrest(operator);
	if (!oprsel)
		return (Selectivity) 0.5;
	fmgr_info(oprsel, &oprselproc);

	/*
	 * In the array-containment check above, we must only believe that an
	 * operator is equality or inequality if it is the default btree equality
	 * operator (or its negator) for the element type, since those are the
	 * operators that array containment will use.  But in what follows, we can
	 * be a little laxer, and also believe that any operators using eqsel() or
	 * neqsel() as selectivity estimator act like equality or inequality.
	 */
	if (oprsel == F_EQSEL || oprsel == F_EQJOINSEL)
		isEquality = true;
	else if (oprsel == F_NEQSEL || oprsel == F_NEQJOINSEL)
		isInequality = true;

	/*
	 * We consider three cases:
	 *
	 * 1. rightop is an Array constant: deconstruct the array, apply the
	 * operator's selectivity function for each array element, and merge the
	 * results in the same way that clausesel.c does for AND/OR combinations.
	 *
	 * 2. rightop is an ARRAY[] construct: apply the operator's selectivity
	 * function for each element of the ARRAY[] construct, and merge.
	 *
	 * 3. otherwise, make a guess ...
	 */
	if (rightop && IsA(rightop, Const))
	{
		Datum		arraydatum = ((Const *) rightop)->constvalue;
		bool		arrayisnull = ((Const *) rightop)->constisnull;
		ArrayType  *arrayval;
		int16		elmlen;
		bool		elmbyval;
		char		elmalign;
		int			num_elems;
		Datum	   *elem_values;
		bool	   *elem_nulls;
		int			i;

		if (arrayisnull)		/* qual can't succeed if null array */
			return (Selectivity) 0.0;
		arrayval = DatumGetArrayTypeP(arraydatum);

		/*
		 * When the array contains a NULL constant, same as var_eq_const, we
		 * assume the operator is strict and nothing will match, thus return
		 * 0.0.
		 */
		if (!useOr && array_contains_nulls(arrayval))
			return (Selectivity) 0.0;

		get_typlenbyvalalign(ARR_ELEMTYPE(arrayval),
							 &elmlen, &elmbyval, &elmalign);
		deconstruct_array(arrayval,
						  ARR_ELEMTYPE(arrayval),
						  elmlen, elmbyval, elmalign,
						  &elem_values, &elem_nulls, &num_elems);

		/*
		 * For generic operators, we assume the probability of success is
		 * independent for each array element.  But for "= ANY" or "<> ALL",
		 * if the array elements are distinct (which'd typically be the case)
		 * then the probabilities are disjoint, and we should just sum them.
		 *
		 * If we were being really tense we would try to confirm that the
		 * elements are all distinct, but that would be expensive and it
		 * doesn't seem to be worth the cycles; it would amount to penalizing
		 * well-written queries in favor of poorly-written ones.  However, we
		 * do protect ourselves a little bit by checking whether the
		 * disjointness assumption leads to an impossible (out of range)
		 * probability; if so, we fall back to the normal calculation.
		 */
		s1 = s1disjoint = (useOr ? 0.0 : 1.0);

		for (i = 0; i < num_elems; i++)
		{
			List	   *args;
			Selectivity s2;

			args = list_make2(leftop,
							  makeConst(nominal_element_type,
										-1,
										nominal_element_collation,
										elmlen,
										elem_values[i],
										elem_nulls[i],
										elmbyval));
			if (is_join_clause)
				s2 = DatumGetFloat8(FunctionCall5Coll(&oprselproc,
													  clause->inputcollid,
													  PointerGetDatum(root),
													  ObjectIdGetDatum(operator),
													  PointerGetDatum(args),
													  Int16GetDatum(jointype),
													  PointerGetDatum(sjinfo)));
			else
				s2 = DatumGetFloat8(FunctionCall4Coll(&oprselproc,
													  clause->inputcollid,
													  PointerGetDatum(root),
													  ObjectIdGetDatum(operator),
													  PointerGetDatum(args),
													  Int32GetDatum(varRelid)));

			if (useOr)
			{
				s1 = s1 + s2 - s1 * s2;
				if (isEquality)
					s1disjoint += s2;
			}
			else
			{
				s1 = s1 * s2;
				if (isInequality)
					s1disjoint += s2 - 1.0;
			}
		}

		/* accept disjoint-probability estimate if in range */
		if ((useOr ? isEquality : isInequality) &&
			s1disjoint >= 0.0 && s1disjoint <= 1.0)
			s1 = s1disjoint;
	}
	else if (rightop && IsA(rightop, ArrayExpr) &&
			 !((ArrayExpr *) rightop)->multidims)
	{
		ArrayExpr  *arrayexpr = (ArrayExpr *) rightop;
		int16		elmlen;
		bool		elmbyval;
		ListCell   *l;

		get_typlenbyval(arrayexpr->element_typeid,
						&elmlen, &elmbyval);

		/*
		 * We use the assumption of disjoint probabilities here too, although
		 * the odds of equal array elements are rather higher if the elements
		 * are not all constants (which they won't be, else constant folding
		 * would have reduced the ArrayExpr to a Const).  In this path it's
		 * critical to have the sanity check on the s1disjoint estimate.
		 */
		s1 = s1disjoint = (useOr ? 0.0 : 1.0);

		foreach(l, arrayexpr->elements)
		{
			Node	   *elem = (Node *) lfirst(l);
			List	   *args;
			Selectivity s2;

			/*
			 * When the array contains a NULL constant, same as var_eq_const,
			 * we assume the operator is strict and nothing will match, thus
			 * return 0.0.
			 */
			if (!useOr && IsA(elem, Const) && ((Const *) elem)->constisnull)
				return (Selectivity) 0.0;

			/*
			 * Theoretically, if elem isn't of nominal_element_type we should
			 * insert a RelabelType, but it seems unlikely that any operator
			 * estimation function would really care ...
			 */
			args = list_make2(leftop, elem);
			if (is_join_clause)
				s2 = DatumGetFloat8(FunctionCall5Coll(&oprselproc,
													  clause->inputcollid,
													  PointerGetDatum(root),
													  ObjectIdGetDatum(operator),
													  PointerGetDatum(args),
													  Int16GetDatum(jointype),
													  PointerGetDatum(sjinfo)));
			else
				s2 = DatumGetFloat8(FunctionCall4Coll(&oprselproc,
													  clause->inputcollid,
													  PointerGetDatum(root),
													  ObjectIdGetDatum(operator),
													  PointerGetDatum(args),
													  Int32GetDatum(varRelid)));

			if (useOr)
			{
				s1 = s1 + s2 - s1 * s2;
				if (isEquality)
					s1disjoint += s2;
			}
			else
			{
				s1 = s1 * s2;
				if (isInequality)
					s1disjoint += s2 - 1.0;
			}
		}

		/* accept disjoint-probability estimate if in range */
		if ((useOr ? isEquality : isInequality) &&
			s1disjoint >= 0.0 && s1disjoint <= 1.0)
			s1 = s1disjoint;
	}
	else
	{
		CaseTestExpr *dummyexpr;
		List	   *args;
		Selectivity s2;
		int			i;

		/*
		 * We need a dummy rightop to pass to the operator selectivity
		 * routine.  It can be pretty much anything that doesn't look like a
		 * constant; CaseTestExpr is a convenient choice.
		 */
		dummyexpr = makeNode(CaseTestExpr);
		dummyexpr->typeId = nominal_element_type;
		dummyexpr->typeMod = -1;
		dummyexpr->collation = clause->inputcollid;
		args = list_make2(leftop, dummyexpr);
		if (is_join_clause)
			s2 = DatumGetFloat8(FunctionCall5Coll(&oprselproc,
												  clause->inputcollid,
												  PointerGetDatum(root),
												  ObjectIdGetDatum(operator),
												  PointerGetDatum(args),
												  Int16GetDatum(jointype),
												  PointerGetDatum(sjinfo)));
		else
			s2 = DatumGetFloat8(FunctionCall4Coll(&oprselproc,
												  clause->inputcollid,
												  PointerGetDatum(root),
												  ObjectIdGetDatum(operator),
												  PointerGetDatum(args),
												  Int32GetDatum(varRelid)));
		s1 = useOr ? 0.0 : 1.0;

		/*
		 * Arbitrarily assume 10 elements in the eventual array value (see
		 * also estimate_array_length).  We don't risk an assumption of
		 * disjoint probabilities here.
		 */
		for (i = 0; i < 10; i++)
		{
			if (useOr)
				s1 = s1 + s2 - s1 * s2;
			else
				s1 = s1 * s2;
		}
	}

	/* result should be in range, but make sure... */
	CLAMP_PROBABILITY(s1);

	return s1;
}

/*
 * Estimate number of elements in the array yielded by an expression.
 *
 * Note: the result is integral, but we use "double" to avoid overflow
 * concerns.  Most callers will use it in double-type expressions anyway.
 *
 * Note: in some code paths root can be passed as NULL, resulting in
 * slightly worse estimates.
 */
double
estimate_array_length(PlannerInfo *root, Node *arrayexpr)
{
	/* look through any binary-compatible relabeling of arrayexpr */
	arrayexpr = strip_array_coercion(arrayexpr);

	if (arrayexpr && IsA(arrayexpr, Const))
	{
		Datum		arraydatum = ((Const *) arrayexpr)->constvalue;
		bool		arrayisnull = ((Const *) arrayexpr)->constisnull;
		ArrayType  *arrayval;

		if (arrayisnull)
			return 0;
		arrayval = DatumGetArrayTypeP(arraydatum);
		return ArrayGetNItems(ARR_NDIM(arrayval), ARR_DIMS(arrayval));
	}
	else if (arrayexpr && IsA(arrayexpr, ArrayExpr) &&
			 !((ArrayExpr *) arrayexpr)->multidims)
	{
		return list_length(((ArrayExpr *) arrayexpr)->elements);
	}
	else if (arrayexpr && root)
	{
		/* See if we can find any statistics about it */
		VariableStatData vardata;
		AttStatsSlot sslot;
		double		nelem = 0;

		/*
		 * Skip calling examine_variable for Var with varno 0, which has no
		 * valid relation entry and would error in find_base_rel.  Such a Var
		 * can appear when a nested set operation's output type doesn't match
		 * the parent's expected type, because recurse_set_operations builds a
		 * projection target list using generate_setop_tlist with varno 0, and
		 * if the required type coercion involves an ArrayCoerceExpr, we can
		 * be called on that Var.
		 */
		if (IsA(arrayexpr, Var) && ((Var *) arrayexpr)->varno == 0)
			return 10;			/* default guess, should match scalararraysel */

		examine_variable(root, arrayexpr, 0, &vardata);
		if (HeapTupleIsValid(vardata.statsTuple))
		{
			/*
			 * Found stats, so use the average element count, which is stored
			 * in the last stanumbers element of the DECHIST statistics.
			 * Actually that is the average count of *distinct* elements;
			 * perhaps we should scale it up somewhat?
			 */
			if (get_attstatsslot(&sslot, vardata.statsTuple,
								 STATISTIC_KIND_DECHIST, InvalidOid,
								 ATTSTATSSLOT_NUMBERS))
			{
				if (sslot.nnumbers > 0)
					nelem = clamp_row_est(sslot.numbers[sslot.nnumbers - 1]);
				free_attstatsslot(&sslot);
			}
		}
		ReleaseVariableStats(vardata);

		if (nelem > 0)
			return nelem;
	}

	/* Else use a default guess --- this should match scalararraysel */
	return 10;
}

/*
 *		rowcomparesel		- Selectivity of RowCompareExpr Node.
 *
 * We estimate RowCompare selectivity by considering just the first (high
 * order) columns, which makes it equivalent to an ordinary OpExpr.  While
 * this estimate could be refined by considering additional columns, it
 * seems unlikely that we could do a lot better without multi-column
 * statistics.
 */
Selectivity
rowcomparesel(PlannerInfo *root,
			  RowCompareExpr *clause,
			  int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo)
{
	Selectivity s1;
	Oid			opno = linitial_oid(clause->opnos);
	Oid			inputcollid = linitial_oid(clause->inputcollids);
	List	   *opargs;
	bool		is_join_clause;

	/* Build equivalent arg list for single operator */
	opargs = list_make2(linitial(clause->largs), linitial(clause->rargs));

	/*
	 * Decide if it's a join clause.  This should match clausesel.c's
	 * treat_as_join_clause(), except that we intentionally consider only the
	 * leading columns and not the rest of the clause.
	 */
	if (varRelid != 0)
	{
		/*
		 * Caller is forcing restriction mode (eg, because we are examining an
		 * inner indexscan qual).
		 */
		is_join_clause = false;
	}
	else if (sjinfo == NULL)
	{
		/*
		 * It must be a restriction clause, since it's being evaluated at a
		 * scan node.
		 */
		is_join_clause = false;
	}
	else
	{
		/*
		 * Otherwise, it's a join if there's more than one base relation used.
		 */
		is_join_clause = (NumRelids(root, (Node *) opargs) > 1);
	}

	if (is_join_clause)
	{
		/* Estimate selectivity for a join clause. */
		s1 = join_selectivity(root, opno,
							  opargs,
							  inputcollid,
							  jointype,
							  sjinfo);
	}
	else
	{
		/* Estimate selectivity for a restriction clause. */
		s1 = restriction_selectivity(root, opno,
									 opargs,
									 inputcollid,
									 varRelid);
	}

	return s1;
}

/*
 *		eqjoinsel		- Join selectivity of "="
 */
Datum
eqjoinsel(PG_FUNCTION_ARGS)
{
	PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
	Oid			operator = PG_GETARG_OID(1);
	List	   *args = (List *) PG_GETARG_POINTER(2);

#ifdef NOT_USED
	JoinType	jointype = (JoinType) PG_GETARG_INT16(3);
#endif
	SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) PG_GETARG_POINTER(4);
	Oid			collation = PG_GET_COLLATION();
	double		selec;
	double		selec_inner;
	VariableStatData vardata1;
	VariableStatData vardata2;
	double		nd1;
	double		nd2;
	bool		isdefault1;
	bool		isdefault2;
	Oid			opfuncoid;
	FmgrInfo	eqproc;
	Oid			hashLeft = InvalidOid;
	Oid			hashRight = InvalidOid;
	AttStatsSlot sslot1;
	AttStatsSlot sslot2;
	Form_pg_statistic stats1 = NULL;
	Form_pg_statistic stats2 = NULL;
	bool		have_mcvs1 = false;
	bool		have_mcvs2 = false;
	bool	   *hasmatch1 = NULL;
	bool	   *hasmatch2 = NULL;
	int			nmatches = 0;
	bool		get_mcv_stats;
	bool		join_is_reversed;
	RelOptInfo *inner_rel;

	get_join_variables(root, args, sjinfo,
					   &vardata1, &vardata2, &join_is_reversed);

	nd1 = get_variable_numdistinct(&vardata1, &isdefault1);
	nd2 = get_variable_numdistinct(&vardata2, &isdefault2);

	opfuncoid = get_opcode(operator);

	memset(&sslot1, 0, sizeof(sslot1));
	memset(&sslot2, 0, sizeof(sslot2));

	/*
	 * There is no use in fetching one side's MCVs if we lack MCVs for the
	 * other side, so do a quick check to verify that both stats exist.
	 */
	get_mcv_stats = (HeapTupleIsValid(vardata1.statsTuple) &&
					 HeapTupleIsValid(vardata2.statsTuple) &&
					 get_attstatsslot(&sslot1, vardata1.statsTuple,
									  STATISTIC_KIND_MCV, InvalidOid,
									  0) &&
					 get_attstatsslot(&sslot2, vardata2.statsTuple,
									  STATISTIC_KIND_MCV, InvalidOid,
									  0));

	if (HeapTupleIsValid(vardata1.statsTuple))
	{
		/* note we allow use of nullfrac regardless of security check */
		stats1 = (Form_pg_statistic) GETSTRUCT(vardata1.statsTuple);
		if (get_mcv_stats &&
			statistic_proc_security_check(&vardata1, opfuncoid))
			have_mcvs1 = get_attstatsslot(&sslot1, vardata1.statsTuple,
										  STATISTIC_KIND_MCV, InvalidOid,
										  ATTSTATSSLOT_VALUES | ATTSTATSSLOT_NUMBERS);
	}

	if (HeapTupleIsValid(vardata2.statsTuple))
	{
		/* note we allow use of nullfrac regardless of security check */
		stats2 = (Form_pg_statistic) GETSTRUCT(vardata2.statsTuple);
		if (get_mcv_stats &&
			statistic_proc_security_check(&vardata2, opfuncoid))
			have_mcvs2 = get_attstatsslot(&sslot2, vardata2.statsTuple,
										  STATISTIC_KIND_MCV, InvalidOid,
										  ATTSTATSSLOT_VALUES | ATTSTATSSLOT_NUMBERS);
	}

	/* Prepare info usable by both eqjoinsel_inner and eqjoinsel_semi */
	if (have_mcvs1 && have_mcvs2)
	{
		fmgr_info(opfuncoid, &eqproc);
		hasmatch1 = (bool *) palloc0(sslot1.nvalues * sizeof(bool));
		hasmatch2 = (bool *) palloc0(sslot2.nvalues * sizeof(bool));

		/*
		 * If the MCV lists are long enough to justify hashing, try to look up
		 * hash functions for the join operator.
		 */
		if ((sslot1.nvalues + sslot2.nvalues) >= EQJOINSEL_MCV_HASH_THRESHOLD)
			(void) get_op_hash_functions_ext(operator,
											 exprType((Node *) linitial(args)),
											 &hashLeft, &hashRight);
	}
	else
		memset(&eqproc, 0, sizeof(eqproc)); /* silence uninit-var warnings */

	/* We need to compute the inner-join selectivity in all cases */
	selec_inner = eqjoinsel_inner(&eqproc, collation,
								  hashLeft, hashRight,
								  &vardata1, &vardata2,
								  nd1, nd2,
								  isdefault1, isdefault2,
								  &sslot1, &sslot2,
								  stats1, stats2,
								  have_mcvs1, have_mcvs2,
								  hasmatch1, hasmatch2,
								  &nmatches);

	switch (sjinfo->jointype)
	{
		case JOIN_INNER:
		case JOIN_LEFT:
		case JOIN_FULL:
			selec = selec_inner;
			break;
		case JOIN_SEMI:
		case JOIN_ANTI:

			/*
			 * Look up the join's inner relation.  min_righthand is sufficient
			 * information because neither SEMI nor ANTI joins permit any
			 * reassociation into or out of their RHS, so the righthand will
			 * always be exactly that set of rels.
			 */
			inner_rel = find_join_input_rel(root, sjinfo->min_righthand);

			if (!join_is_reversed)
				selec = eqjoinsel_semi(&eqproc, collation,
									   hashLeft, hashRight,
									   false,
									   &vardata1, &vardata2,
									   nd1, nd2,
									   isdefault1, isdefault2,
									   &sslot1, &sslot2,
									   stats1, stats2,
									   have_mcvs1, have_mcvs2,
									   hasmatch1, hasmatch2,
									   &nmatches,
									   inner_rel);
			else
				selec = eqjoinsel_semi(&eqproc, collation,
									   hashLeft, hashRight,
									   true,
									   &vardata2, &vardata1,
									   nd2, nd1,
									   isdefault2, isdefault1,
									   &sslot2, &sslot1,
									   stats2, stats1,
									   have_mcvs2, have_mcvs1,
									   hasmatch2, hasmatch1,
									   &nmatches,
									   inner_rel);

			/*
			 * We should never estimate the output of a semijoin to be more
			 * rows than we estimate for an inner join with the same input
			 * rels and join condition; it's obviously impossible for that to
			 * happen.  The former estimate is N1 * Ssemi while the latter is
			 * N1 * N2 * Sinner, so we may clamp Ssemi <= N2 * Sinner.  Doing
			 * this is worthwhile because of the shakier estimation rules we
			 * use in eqjoinsel_semi, particularly in cases where it has to
			 * punt entirely.
			 */
			selec = Min(selec, inner_rel->rows * selec_inner);
			break;
		default:
			/* other values not expected here */
			elog(ERROR, "unrecognized join type: %d",
				 (int) sjinfo->jointype);
			selec = 0;			/* keep compiler quiet */
			break;
	}

	free_attstatsslot(&sslot1);
	free_attstatsslot(&sslot2);

	ReleaseVariableStats(vardata1);
	ReleaseVariableStats(vardata2);

	if (hasmatch1)
		pfree(hasmatch1);
	if (hasmatch2)
		pfree(hasmatch2);

	CLAMP_PROBABILITY(selec);

	PG_RETURN_FLOAT8((float8) selec);
}

/*
 * eqjoinsel_inner --- eqjoinsel for normal inner join
 *
 * In addition to computing the selectivity estimate, this will fill
 * hasmatch1[], hasmatch2[], and *p_nmatches (if have_mcvs1 && have_mcvs2).
 * We may be able to re-use that data in eqjoinsel_semi.
 *
 * We also use this for LEFT/FULL outer joins; it's not presently clear
 * that it's worth trying to distinguish them here.
 */
static double
eqjoinsel_inner(FmgrInfo *eqproc, Oid collation,
				Oid hashLeft, Oid hashRight,
				VariableStatData *vardata1, VariableStatData *vardata2,
				double nd1, double nd2,
				bool isdefault1, bool isdefault2,
				AttStatsSlot *sslot1, AttStatsSlot *sslot2,
				Form_pg_statistic stats1, Form_pg_statistic stats2,
				bool have_mcvs1, bool have_mcvs2,
				bool *hasmatch1, bool *hasmatch2,
				int *p_nmatches)
{
	double		selec;

	if (have_mcvs1 && have_mcvs2)
	{
		/*
		 * We have most-common-value lists for both relations.  Run through
		 * the lists to see which MCVs actually join to each other with the
		 * given operator.  This allows us to determine the exact join
		 * selectivity for the portion of the relations represented by the MCV
		 * lists.  We still have to estimate for the remaining population, but
		 * in a skewed distribution this gives us a big leg up in accuracy.
		 * For motivation see the analysis in Y. Ioannidis and S.
		 * Christodoulakis, "On the propagation of errors in the size of join
		 * results", Technical Report 1018, Computer Science Dept., University
		 * of Wisconsin, Madison, March 1991 (available from ftp.cs.wisc.edu).
		 */
		double		nullfrac1 = stats1->stanullfrac;
		double		nullfrac2 = stats2->stanullfrac;
		double		matchprodfreq,
					matchfreq1,
					matchfreq2,
					unmatchfreq1,
					unmatchfreq2,
					otherfreq1,
					otherfreq2,
					totalsel1,
					totalsel2;
		int			i,
					nmatches;

		/* Fill the match arrays */
		eqjoinsel_find_matches(eqproc, collation,
							   hashLeft, hashRight,
							   false,
							   sslot1, sslot2,
							   sslot1->nvalues, sslot2->nvalues,
							   hasmatch1, hasmatch2,
							   p_nmatches, &matchprodfreq);
		nmatches = *p_nmatches;
		CLAMP_PROBABILITY(matchprodfreq);

		/* Sum up frequencies of matched and unmatched MCVs */
		matchfreq1 = unmatchfreq1 = 0.0;
		for (i = 0; i < sslot1->nvalues; i++)
		{
			if (hasmatch1[i])
				matchfreq1 += sslot1->numbers[i];
			else
				unmatchfreq1 += sslot1->numbers[i];
		}
		CLAMP_PROBABILITY(matchfreq1);
		CLAMP_PROBABILITY(unmatchfreq1);
		matchfreq2 = unmatchfreq2 = 0.0;
		for (i = 0; i < sslot2->nvalues; i++)
		{
			if (hasmatch2[i])
				matchfreq2 += sslot2->numbers[i];
			else
				unmatchfreq2 += sslot2->numbers[i];
		}
		CLAMP_PROBABILITY(matchfreq2);
		CLAMP_PROBABILITY(unmatchfreq2);

		/*
		 * Compute total frequency of non-null values that are not in the MCV
		 * lists.
		 */
		otherfreq1 = 1.0 - nullfrac1 - matchfreq1 - unmatchfreq1;
		otherfreq2 = 1.0 - nullfrac2 - matchfreq2 - unmatchfreq2;
		CLAMP_PROBABILITY(otherfreq1);
		CLAMP_PROBABILITY(otherfreq2);

		/*
		 * We can estimate the total selectivity from the point of view of
		 * relation 1 as: the known selectivity for matched MCVs, plus
		 * unmatched MCVs that are assumed to match against random members of
		 * relation 2's non-MCV population, plus non-MCV values that are
		 * assumed to match against random members of relation 2's unmatched
		 * MCVs plus non-MCV values.
		 */
		totalsel1 = matchprodfreq;
		if (nd2 > sslot2->nvalues)
			totalsel1 += unmatchfreq1 * otherfreq2 / (nd2 - sslot2->nvalues);
		if (nd2 > nmatches)
			totalsel1 += otherfreq1 * (otherfreq2 + unmatchfreq2) /
				(nd2 - nmatches);
		/* Same estimate from the point of view of relation 2. */
		totalsel2 = matchprodfreq;
		if (nd1 > sslot1->nvalues)
			totalsel2 += unmatchfreq2 * otherfreq1 / (nd1 - sslot1->nvalues);
		if (nd1 > nmatches)
			totalsel2 += otherfreq2 * (otherfreq1 + unmatchfreq1) /
				(nd1 - nmatches);

		/*
		 * Use the smaller of the two estimates.  This can be justified in
		 * essentially the same terms as given below for the no-stats case: to
		 * a first approximation, we are estimating from the point of view of
		 * the relation with smaller nd.
		 */
		selec = (totalsel1 < totalsel2) ? totalsel1 : totalsel2;
	}
	else
	{
		/*
		 * We do not have MCV lists for both sides.  Estimate the join
		 * selectivity as MIN(1/nd1,1/nd2)*(1-nullfrac1)*(1-nullfrac2). This
		 * is plausible if we assume that the join operator is strict and the
		 * non-null values are about equally distributed: a given non-null
		 * tuple of rel1 will join to either zero or N2*(1-nullfrac2)/nd2 rows
		 * of rel2, so total join rows are at most
		 * N1*(1-nullfrac1)*N2*(1-nullfrac2)/nd2 giving a join selectivity of
		 * not more than (1-nullfrac1)*(1-nullfrac2)/nd2. By the same logic it
		 * is not more than (1-nullfrac1)*(1-nullfrac2)/nd1, so the expression
		 * with MIN() is an upper bound.  Using the MIN() means we estimate
		 * from the point of view of the relation with smaller nd (since the
		 * larger nd is determining the MIN).  It is reasonable to assume that
		 * most tuples in this rel will have join partners, so the bound is
		 * probably reasonably tight and should be taken as-is.
		 *
		 * XXX Can we be smarter if we have an MCV list for just one side? It
		 * seems that if we assume equal distribution for the other side, we
		 * end up with the same answer anyway.
		 */
		double		nullfrac1 = stats1 ? stats1->stanullfrac : 0.0;
		double		nullfrac2 = stats2 ? stats2->stanullfrac : 0.0;

		selec = (1.0 - nullfrac1) * (1.0 - nullfrac2);
		if (nd1 > nd2)
			selec /= nd1;
		else
			selec /= nd2;
	}

	return selec;
}

/*
 * eqjoinsel_semi --- eqjoinsel for semi join
 *
 * (Also used for anti join, which we are supposed to estimate the same way.)
 * Caller has ensured that vardata1 is the LHS variable; however, eqproc
 * is for the original join operator, which might now need to have the inputs
 * swapped in order to apply correctly.  Also, if have_mcvs1 && have_mcvs2
 * then hasmatch1[], hasmatch2[], and *p_nmatches were filled by
 * eqjoinsel_inner.
 */
static double
eqjoinsel_semi(FmgrInfo *eqproc, Oid collation,
			   Oid hashLeft, Oid hashRight,
			   bool op_is_reversed,
			   VariableStatData *vardata1, VariableStatData *vardata2,
			   double nd1, double nd2,
			   bool isdefault1, bool isdefault2,
			   AttStatsSlot *sslot1, AttStatsSlot *sslot2,
			   Form_pg_statistic stats1, Form_pg_statistic stats2,
			   bool have_mcvs1, bool have_mcvs2,
			   bool *hasmatch1, bool *hasmatch2,
			   int *p_nmatches,
			   RelOptInfo *inner_rel)
{
	double		selec;

	/*
	 * We clamp nd2 to be not more than what we estimate the inner relation's
	 * size to be.  This is intuitively somewhat reasonable since obviously
	 * there can't be more than that many distinct values coming from the
	 * inner rel.  The reason for the asymmetry (ie, that we don't clamp nd1
	 * likewise) is that this is the only pathway by which restriction clauses
	 * applied to the inner rel will affect the join result size estimate,
	 * since set_joinrel_size_estimates will multiply SEMI/ANTI selectivity by
	 * only the outer rel's size.  If we clamped nd1 we'd be double-counting
	 * the selectivity of outer-rel restrictions.
	 *
	 * We can apply this clamping both with respect to the base relation from
	 * which the join variable comes (if there is just one), and to the
	 * immediate inner input relation of the current join.
	 *
	 * If we clamp, we can treat nd2 as being a non-default estimate; it's not
	 * great, maybe, but it didn't come out of nowhere either.  This is most
	 * helpful when the inner relation is empty and consequently has no stats.
	 */
	if (vardata2->rel)
	{
		if (nd2 >= vardata2->rel->rows)
		{
			nd2 = vardata2->rel->rows;
			isdefault2 = false;
		}
	}
	if (nd2 >= inner_rel->rows)
	{
		nd2 = inner_rel->rows;
		isdefault2 = false;
	}

	if (have_mcvs1 && have_mcvs2)
	{
		/*
		 * We have most-common-value lists for both relations.  Run through
		 * the lists to see which MCVs actually join to each other with the
		 * given operator.  This allows us to determine the exact join
		 * selectivity for the portion of the relations represented by the MCV
		 * lists.  We still have to estimate for the remaining population, but
		 * in a skewed distribution this gives us a big leg up in accuracy.
		 */
		double		nullfrac1 = stats1->stanullfrac;
		double		matchprodfreq,
					matchfreq1,
					uncertainfrac,
					uncertain;
		int			i,
					nmatches,
					clamped_nvalues2;

		/*
		 * The clamping above could have resulted in nd2 being less than
		 * sslot2->nvalues; in which case, we assume that precisely the nd2
		 * most common values in the relation will appear in the join input,
		 * and so compare to only the first nd2 members of the MCV list.  Of
		 * course this is frequently wrong, but it's the best bet we can make.
		 */
		clamped_nvalues2 = Min(sslot2->nvalues, nd2);

		/*
		 * If we did not set clamped_nvalues2 to less than sslot2->nvalues,
		 * then the hasmatch1[] and hasmatch2[] match flags computed by
		 * eqjoinsel_inner are still perfectly applicable, so we need not
		 * re-do the matching work.  Note that it does not matter if
		 * op_is_reversed: we'd get the same answers.
		 *
		 * If we did clamp, then a different set of sslot2 values is to be
		 * compared, so we have to re-do the matching.
		 */
		if (clamped_nvalues2 != sslot2->nvalues)
		{
			/* Must re-zero the arrays */
			memset(hasmatch1, 0, sslot1->nvalues * sizeof(bool));
			memset(hasmatch2, 0, clamped_nvalues2 * sizeof(bool));
			/* Re-fill the match arrays */
			eqjoinsel_find_matches(eqproc, collation,
								   hashLeft, hashRight,
								   op_is_reversed,
								   sslot1, sslot2,
								   sslot1->nvalues, clamped_nvalues2,
								   hasmatch1, hasmatch2,
								   p_nmatches, &matchprodfreq);
		}
		nmatches = *p_nmatches;

		/* Sum up frequencies of matched MCVs */
		matchfreq1 = 0.0;
		for (i = 0; i < sslot1->nvalues; i++)
		{
			if (hasmatch1[i])
				matchfreq1 += sslot1->numbers[i];
		}
		CLAMP_PROBABILITY(matchfreq1);

		/*
		 * Now we need to estimate the fraction of relation 1 that has at
		 * least one join partner.  We know for certain that the matched MCVs
		 * do, so that gives us a lower bound, but we're really in the dark
		 * about everything else.  Our crude approach is: if nd1 <= nd2 then
		 * assume all non-null rel1 rows have join partners, else assume for
		 * the uncertain rows that a fraction nd2/nd1 have join partners. We
		 * can discount the known-matched MCVs from the distinct-values counts
		 * before doing the division.
		 *
		 * Crude as the above is, it's completely useless if we don't have
		 * reliable ndistinct values for both sides.  Hence, if either nd1 or
		 * nd2 is default, punt and assume half of the uncertain rows have
		 * join partners.
		 */
		if (!isdefault1 && !isdefault2)
		{
			nd1 -= nmatches;
			nd2 -= nmatches;
			if (nd1 <= nd2 || nd2 < 0)
				uncertainfrac = 1.0;
			else
				uncertainfrac = nd2 / nd1;
		}
		else
			uncertainfrac = 0.5;
		uncertain = 1.0 - matchfreq1 - nullfrac1;
		CLAMP_PROBABILITY(uncertain);
		selec = matchfreq1 + uncertainfrac * uncertain;
	}
	else
	{
		/*
		 * Without MCV lists for both sides, we can only use the heuristic
		 * about nd1 vs nd2.
		 */
		double		nullfrac1 = stats1 ? stats1->stanullfrac : 0.0;

		if (!isdefault1 && !isdefault2)
		{
			if (nd1 <= nd2 || nd2 < 0)
				selec = 1.0 - nullfrac1;
			else
				selec = (nd2 / nd1) * (1.0 - nullfrac1);
		}
		else
			selec = 0.5 * (1.0 - nullfrac1);
	}

	return selec;
}

/*
 * Identify matching MCVs for eqjoinsel_inner or eqjoinsel_semi.
 *
 * Inputs:
 *	eqproc: FmgrInfo for equality function to use (might be reversed)
 *	collation: OID of collation to use
 *	hashLeft, hashRight: OIDs of hash functions associated with equality op,
 *		or InvalidOid if we're not to use hashing
 *	op_is_reversed: indicates that eqproc compares right type to left type
 *	sslot1, sslot2: MCV values for the lefthand and righthand inputs
 *	nvalues1, nvalues2: number of values to be considered (can be less than
 *		sslotN->nvalues, but not more)
 * Outputs:
 *	hasmatch1[], hasmatch2[]: pre-zeroed arrays of lengths nvalues1, nvalues2;
 *		entries are set to true if that MCV has a match on the other side
 *	*p_nmatches: receives number of MCV pairs that match
 *	*p_matchprodfreq: receives sum(sslot1->numbers[i] * sslot2->numbers[j])
 *		for matching MCVs
 *
 * Note that hashLeft is for the eqproc's left-hand input type, hashRight
 * for its right, regardless of op_is_reversed.
 *
 * Note we assume that each MCV will match at most one member of the other
 * MCV list.  If the operator isn't really equality, there could be multiple
 * matches --- but we don't look for them, both for speed and because the
 * math wouldn't add up...
 */
static void
eqjoinsel_find_matches(FmgrInfo *eqproc, Oid collation,
					   Oid hashLeft, Oid hashRight,
					   bool op_is_reversed,
					   AttStatsSlot *sslot1, AttStatsSlot *sslot2,
					   int nvalues1, int nvalues2,
					   bool *hasmatch1, bool *hasmatch2,
					   int *p_nmatches, double *p_matchprodfreq)
{
	LOCAL_FCINFO(fcinfo, 2);
	double		matchprodfreq = 0.0;
	int			nmatches = 0;

	/*
	 * Save a few cycles by setting up the fcinfo struct just once.  Using
	 * FunctionCallInvoke directly also avoids failure if the eqproc returns
	 * NULL, though really equality functions should never do that.
	 */
	InitFunctionCallInfoData(*fcinfo, eqproc, 2, collation,
							 NULL, NULL);
	fcinfo->args[0].isnull = false;
	fcinfo->args[1].isnull = false;

	if (OidIsValid(hashLeft) && OidIsValid(hashRight))
	{
		/* Use a hash table to speed up the matching */
		LOCAL_FCINFO(hash_fcinfo, 1);
		FmgrInfo	hash_proc;
		MCVHashContext hashContext;
		MCVHashTable_hash *hashTable;
		AttStatsSlot *statsProbe;
		AttStatsSlot *statsHash;
		bool	   *hasMatchProbe;
		bool	   *hasMatchHash;
		int			nvaluesProbe;
		int			nvaluesHash;

		/* Make sure we build the hash table on the smaller array. */
		if (sslot1->nvalues >= sslot2->nvalues)
		{
			statsProbe = sslot1;
			statsHash = sslot2;
			hasMatchProbe = hasmatch1;
			hasMatchHash = hasmatch2;
			nvaluesProbe = nvalues1;
			nvaluesHash = nvalues2;
		}
		else
		{
			/* We'll have to reverse the direction of use of the operator. */
			op_is_reversed = !op_is_reversed;
			statsProbe = sslot2;
			statsHash = sslot1;
			hasMatchProbe = hasmatch2;
			hasMatchHash = hasmatch1;
			nvaluesProbe = nvalues2;
			nvaluesHash = nvalues1;
		}

		/*
		 * Build the hash table on the smaller array, using the appropriate
		 * hash function for its data type.
		 */
		fmgr_info(op_is_reversed ? hashLeft : hashRight, &hash_proc);
		InitFunctionCallInfoData(*hash_fcinfo, &hash_proc, 1, collation,
								 NULL, NULL);
		hash_fcinfo->args[0].isnull = false;

		hashContext.equal_fcinfo = fcinfo;
		hashContext.hash_fcinfo = hash_fcinfo;
		hashContext.op_is_reversed = op_is_reversed;
		hashContext.insert_mode = true;
		get_typlenbyval(statsHash->valuetype,
						&hashContext.hash_typlen,
						&hashContext.hash_typbyval);

		hashTable = MCVHashTable_create(CurrentMemoryContext,
										nvaluesHash,
										&hashContext);

		for (int i = 0; i < nvaluesHash; i++)
		{
			bool		found = false;
			MCVHashEntry *entry = MCVHashTable_insert(hashTable,
													  statsHash->values[i],
													  &found);

			/*
			 * MCVHashTable_insert will only report "found" if the new value
			 * is equal to some previous one per datum_image_eq().  That
			 * probably shouldn't happen, since we're not expecting duplicates
			 * in the MCV list.  If we do find a dup, just ignore it, leaving
			 * the hash entry's index pointing at the first occurrence.  That
			 * matches the behavior that the non-hashed code path would have.
			 */
			if (likely(!found))
				entry->index = i;
		}

		/*
		 * Prepare to probe the hash table.  If the probe values are of a
		 * different data type, then we need to change hash functions.  (This
		 * code relies on the assumption that since we defined SH_STORE_HASH,
		 * simplehash.h will never need to compute hash values for existing
		 * hash table entries.)
		 */
		hashContext.insert_mode = false;
		if (hashLeft != hashRight)
		{
			fmgr_info(op_is_reversed ? hashRight : hashLeft, &hash_proc);
			/* Resetting hash_fcinfo is probably unnecessary, but be safe */
			InitFunctionCallInfoData(*hash_fcinfo, &hash_proc, 1, collation,
									 NULL, NULL);
			hash_fcinfo->args[0].isnull = false;
		}

		/* Look up each probe value in turn. */
		for (int i = 0; i < nvaluesProbe; i++)
		{
			MCVHashEntry *entry = MCVHashTable_lookup(hashTable,
													  statsProbe->values[i]);

			/* As in the other code path, skip already-matched hash entries */
			if (entry != NULL && !hasMatchHash[entry->index])
			{
				hasMatchHash[entry->index] = hasMatchProbe[i] = true;
				nmatches++;
				matchprodfreq += statsHash->numbers[entry->index] * statsProbe->numbers[i];
			}
		}

		MCVHashTable_destroy(hashTable);
	}
	else
	{
		/* We're not to use hashing, so do it the O(N^2) way */
		int			index1,
					index2;

		/* Set up to supply the values in the order the operator expects */
		if (op_is_reversed)
		{
			index1 = 1;
			index2 = 0;
		}
		else
		{
			index1 = 0;
			index2 = 1;
		}

		for (int i = 0; i < nvalues1; i++)
		{
			fcinfo->args[index1].value = sslot1->values[i];

			for (int j = 0; j < nvalues2; j++)
			{
				Datum		fresult;

				if (hasmatch2[j])
					continue;
				fcinfo->args[index2].value = sslot2->values[j];
				fcinfo->isnull = false;
				fresult = FunctionCallInvoke(fcinfo);
				if (!fcinfo->isnull && DatumGetBool(fresult))
				{
					hasmatch1[i] = hasmatch2[j] = true;
					matchprodfreq += sslot1->numbers[i] * sslot2->numbers[j];
					nmatches++;
					break;
				}
			}
		}
	}

	*p_nmatches = nmatches;
	*p_matchprodfreq = matchprodfreq;
}

/*
 * Support functions for the hash tables used by eqjoinsel_find_matches
 */
static uint32
hash_mcv(MCVHashTable_hash *tab, Datum key)
{
	MCVHashContext *context = (MCVHashContext *) tab->private_data;
	FunctionCallInfo fcinfo = context->hash_fcinfo;
	Datum		fresult;

	fcinfo->args[0].value = key;
	fcinfo->isnull = false;
	fresult = FunctionCallInvoke(fcinfo);
	Assert(!fcinfo->isnull);
	return DatumGetUInt32(fresult);
}

static bool
mcvs_equal(MCVHashTable_hash *tab, Datum key0, Datum key1)
{
	MCVHashContext *context = (MCVHashContext *) tab->private_data;

	if (context->insert_mode)
	{
		/*
		 * During the insertion step, any comparisons will be between two
		 * Datums of the hash table's data type, so if the given operator is
		 * cross-type it will be the wrong thing to use.  Fortunately, we can
		 * use datum_image_eq instead.  The MCV values should all be distinct
		 * anyway, so it's mostly pro-forma to compare them at all.
		 */
		return datum_image_eq(key0, key1,
							  context->hash_typbyval, context->hash_typlen);
	}
	else
	{
		FunctionCallInfo fcinfo = context->equal_fcinfo;
		Datum		fresult;

		/*
		 * Apply the operator the correct way around.  Although simplehash.h
		 * doesn't document this explicitly, during lookups key0 is from the
		 * hash table while key1 is the probe value, so we should compare them
		 * in that order only if op_is_reversed.
		 */
		if (context->op_is_reversed)
		{
			fcinfo->args[0].value = key0;
			fcinfo->args[1].value = key1;
		}
		else
		{
			fcinfo->args[0].value = key1;
			fcinfo->args[1].value = key0;
		}
		fcinfo->isnull = false;
		fresult = FunctionCallInvoke(fcinfo);
		return (!fcinfo->isnull && DatumGetBool(fresult));
	}
}

/*
 *		neqjoinsel		- Join selectivity of "!="
 */
Datum
neqjoinsel(PG_FUNCTION_ARGS)
{
	PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
	Oid			operator = PG_GETARG_OID(1);
	List	   *args = (List *) PG_GETARG_POINTER(2);
	JoinType	jointype = (JoinType) PG_GETARG_INT16(3);
	SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) PG_GETARG_POINTER(4);
	Oid			collation = PG_GET_COLLATION();
	float8		result;

	if (jointype == JOIN_SEMI || jointype == JOIN_ANTI)
	{
		/*
		 * For semi-joins, if there is more than one distinct value in the RHS
		 * relation then every non-null LHS row must find a row to join since
		 * it can only be equal to one of them.  We'll assume that there is
		 * always more than one distinct RHS value for the sake of stability,
		 * though in theory we could have special cases for empty RHS
		 * (selectivity = 0) and single-distinct-value RHS (selectivity =
		 * fraction of LHS that has the same value as the single RHS value).
		 *
		 * For anti-joins, if we use the same assumption that there is more
		 * than one distinct key in the RHS relation, then every non-null LHS
		 * row must be suppressed by the anti-join.
		 *
		 * So either way, the selectivity estimate should be 1 - nullfrac.
		 */
		VariableStatData leftvar;
		VariableStatData rightvar;
		bool		reversed;
		HeapTuple	statsTuple;
		double		nullfrac;

		get_join_variables(root, args, sjinfo, &leftvar, &rightvar, &reversed);
		statsTuple = reversed ? rightvar.statsTuple : leftvar.statsTuple;
		if (HeapTupleIsValid(statsTuple))
			nullfrac = ((Form_pg_statistic) GETSTRUCT(statsTuple))->stanullfrac;
		else
			nullfrac = 0.0;
		ReleaseVariableStats(leftvar);
		ReleaseVariableStats(rightvar);

		result = 1.0 - nullfrac;
	}
	else
	{
		/*
		 * We want 1 - eqjoinsel() where the equality operator is the one
		 * associated with this != operator, that is, its negator.
		 */
		Oid			eqop = get_negator(operator);

		if (eqop)
		{
			result =
				DatumGetFloat8(DirectFunctionCall5Coll(eqjoinsel,
													   collation,
													   PointerGetDatum(root),
													   ObjectIdGetDatum(eqop),
													   PointerGetDatum(args),
													   Int16GetDatum(jointype),
													   PointerGetDatum(sjinfo)));
		}
		else
		{
			/* Use default selectivity (should we raise an error instead?) */
			result = DEFAULT_EQ_SEL;
		}
		result = 1.0 - result;
	}

	PG_RETURN_FLOAT8(result);
}

/*
 *		scalarltjoinsel - Join selectivity of "<" for scalars
 */
Datum
scalarltjoinsel(PG_FUNCTION_ARGS)
{
	PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL);
}

/*
 *		scalarlejoinsel - Join selectivity of "<=" for scalars
 */
Datum
scalarlejoinsel(PG_FUNCTION_ARGS)
{
	PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL);
}

/*
 *		scalargtjoinsel - Join selectivity of ">" for scalars
 */
Datum
scalargtjoinsel(PG_FUNCTION_ARGS)
{
	PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL);
}

/*
 *		scalargejoinsel - Join selectivity of ">=" for scalars
 */
Datum
scalargejoinsel(PG_FUNCTION_ARGS)
{
	PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL);
}


/*
 * mergejoinscansel			- Scan selectivity of merge join.
 *
 * A merge join will stop as soon as it exhausts either input stream.
 * Therefore, if we can estimate the ranges of both input variables,
 * we can estimate how much of the input will actually be read.  This
 * can have a considerable impact on the cost when using indexscans.
 *
 * Also, we can estimate how much of each input has to be read before the
 * first join pair is found, which will affect the join's startup time.
 *
 * clause should be a clause already known to be mergejoinable.  opfamily,
 * cmptype, and nulls_first specify the sort ordering being used.
 *
 * The outputs are:
 *		*leftstart is set to the fraction of the left-hand variable expected
 *		 to be scanned before the first join pair is found (0 to 1).
 *		*leftend is set to the fraction of the left-hand variable expected
 *		 to be scanned before the join terminates (0 to 1).
 *		*rightstart, *rightend similarly for the right-hand variable.
 */
void
mergejoinscansel(PlannerInfo *root, Node *clause,
				 Oid opfamily, CompareType cmptype, bool nulls_first,
				 Selectivity *leftstart, Selectivity *leftend,
				 Selectivity *rightstart, Selectivity *rightend)
{
	Node	   *left,
			   *right;
	VariableStatData leftvar,
				rightvar;
	Oid			opmethod;
	int			op_strategy;
	Oid			op_lefttype;
	Oid			op_righttype;
	Oid			opno,
				collation,
				lsortop,
				rsortop,
				lstatop,
				rstatop,
				ltop,
				leop,
				revltop,
				revleop;
	StrategyNumber ltstrat,
				lestrat,
				gtstrat,
				gestrat;
	bool		isgt;
	Datum		leftmin,
				leftmax,
				rightmin,
				rightmax;
	double		selec;

	/* Set default results if we can't figure anything out. */
	/* XXX should default "start" fraction be a bit more than 0? */
	*leftstart = *rightstart = 0.0;
	*leftend = *rightend = 1.0;

	/* Deconstruct the merge clause */
	if (!is_opclause(clause))
		return;					/* shouldn't happen */
	opno = ((OpExpr *) clause)->opno;
	collation = ((OpExpr *) clause)->inputcollid;
	left = get_leftop((Expr *) clause);
	right = get_rightop((Expr *) clause);
	if (!right)
		return;					/* shouldn't happen */

	/* Look for stats for the inputs */
	examine_variable(root, left, 0, &leftvar);
	examine_variable(root, right, 0, &rightvar);

	opmethod = get_opfamily_method(opfamily);

	/* Extract the operator's declared left/right datatypes */
	get_op_opfamily_properties(opno, opfamily, false,
							   &op_strategy,
							   &op_lefttype,
							   &op_righttype);
	Assert(IndexAmTranslateStrategy(op_strategy, opmethod, opfamily, true) == COMPARE_EQ);

	/*
	 * Look up the various operators we need.  If we don't find them all, it
	 * probably means the opfamily is broken, but we just fail silently.
	 *
	 * Note: we expect that pg_statistic histograms will be sorted by the '<'
	 * operator, regardless of which sort direction we are considering.
	 */
	switch (cmptype)
	{
		case COMPARE_LT:
			isgt = false;
			ltstrat = IndexAmTranslateCompareType(COMPARE_LT, opmethod, opfamily, true);
			lestrat = IndexAmTranslateCompareType(COMPARE_LE, opmethod, opfamily, true);
			if (op_lefttype == op_righttype)
			{
				/* easy case */
				ltop = get_opfamily_member(opfamily,
										   op_lefttype, op_righttype,
										   ltstrat);
				leop = get_opfamily_member(opfamily,
										   op_lefttype, op_righttype,
										   lestrat);
				lsortop = ltop;
				rsortop = ltop;
				lstatop = lsortop;
				rstatop = rsortop;
				revltop = ltop;
				revleop = leop;
			}
			else
			{
				ltop = get_opfamily_member(opfamily,
										   op_lefttype, op_righttype,
										   ltstrat);
				leop = get_opfamily_member(opfamily,
										   op_lefttype, op_righttype,
										   lestrat);
				lsortop = get_opfamily_member(opfamily,
											  op_lefttype, op_lefttype,
											  ltstrat);
				rsortop = get_opfamily_member(opfamily,
											  op_righttype, op_righttype,
											  ltstrat);
				lstatop = lsortop;
				rstatop = rsortop;
				revltop = get_opfamily_member(opfamily,
											  op_righttype, op_lefttype,
											  ltstrat);
				revleop = get_opfamily_member(opfamily,
											  op_righttype, op_lefttype,
											  lestrat);
			}
			break;
		case COMPARE_GT:
			/* descending-order case */
			isgt = true;
			ltstrat = IndexAmTranslateCompareType(COMPARE_LT, opmethod, opfamily, true);
			gtstrat = IndexAmTranslateCompareType(COMPARE_GT, opmethod, opfamily, true);
			gestrat = IndexAmTranslateCompareType(COMPARE_GE, opmethod, opfamily, true);
			if (op_lefttype == op_righttype)
			{
				/* easy case */
				ltop = get_opfamily_member(opfamily,
										   op_lefttype, op_righttype,
										   gtstrat);
				leop = get_opfamily_member(opfamily,
										   op_lefttype, op_righttype,
										   gestrat);
				lsortop = ltop;
				rsortop = ltop;
				lstatop = get_opfamily_member(opfamily,
											  op_lefttype, op_lefttype,
											  ltstrat);
				rstatop = lstatop;
				revltop = ltop;
				revleop = leop;
			}
			else
			{
				ltop = get_opfamily_member(opfamily,
										   op_lefttype, op_righttype,
										   gtstrat);
				leop = get_opfamily_member(opfamily,
										   op_lefttype, op_righttype,
										   gestrat);
				lsortop = get_opfamily_member(opfamily,
											  op_lefttype, op_lefttype,
											  gtstrat);
				rsortop = get_opfamily_member(opfamily,
											  op_righttype, op_righttype,
											  gtstrat);
				lstatop = get_opfamily_member(opfamily,
											  op_lefttype, op_lefttype,
											  ltstrat);
				rstatop = get_opfamily_member(opfamily,
											  op_righttype, op_righttype,
											  ltstrat);
				revltop = get_opfamily_member(opfamily,
											  op_righttype, op_lefttype,
											  gtstrat);
				revleop = get_opfamily_member(opfamily,
											  op_righttype, op_lefttype,
											  gestrat);
			}
			break;
		default:
			goto fail;			/* shouldn't get here */
	}

	if (!OidIsValid(lsortop) ||
		!OidIsValid(rsortop) ||
		!OidIsValid(lstatop) ||
		!OidIsValid(rstatop) ||
		!OidIsValid(ltop) ||
		!OidIsValid(leop) ||
		!OidIsValid(revltop) ||
		!OidIsValid(revleop))
		goto fail;				/* insufficient info in catalogs */

	/* Try to get ranges of both inputs */
	if (!isgt)
	{
		if (!get_variable_range(root, &leftvar, lstatop, collation,
								&leftmin, &leftmax))
			goto fail;			/* no range available from stats */
		if (!get_variable_range(root, &rightvar, rstatop, collation,
								&rightmin, &rightmax))
			goto fail;			/* no range available from stats */
	}
	else
	{
		/* need to swap the max and min */
		if (!get_variable_range(root, &leftvar, lstatop, collation,
								&leftmax, &leftmin))
			goto fail;			/* no range available from stats */
		if (!get_variable_range(root, &rightvar, rstatop, collation,
								&rightmax, &rightmin))
			goto fail;			/* no range available from stats */
	}

	/*
	 * Now, the fraction of the left variable that will be scanned is the
	 * fraction that's <= the right-side maximum value.  But only believe
	 * non-default estimates, else stick with our 1.0.
	 */
	selec = scalarineqsel(root, leop, isgt, true, collation, &leftvar,
						  rightmax, op_righttype);
	if (selec != DEFAULT_INEQ_SEL)
		*leftend = selec;

	/* And similarly for the right variable. */
	selec = scalarineqsel(root, revleop, isgt, true, collation, &rightvar,
						  leftmax, op_lefttype);
	if (selec != DEFAULT_INEQ_SEL)
		*rightend = selec;

	/*
	 * Only one of the two "end" fractions can really be less than 1.0;
	 * believe the smaller estimate and reset the other one to exactly 1.0. If
	 * we get exactly equal estimates (as can easily happen with self-joins),
	 * believe neither.
	 */
	if (*leftend > *rightend)
		*leftend = 1.0;
	else if (*leftend < *rightend)
		*rightend = 1.0;
	else
		*leftend = *rightend = 1.0;

	/*
	 * Also, the fraction of the left variable that will be scanned before the
	 * first join pair is found is the fraction that's < the right-side
	 * minimum value.  But only believe non-default estimates, else stick with
	 * our own default.
	 */
	selec = scalarineqsel(root, ltop, isgt, false, collation, &leftvar,
						  rightmin, op_righttype);
	if (selec != DEFAULT_INEQ_SEL)
		*leftstart = selec;

	/* And similarly for the right variable. */
	selec = scalarineqsel(root, revltop, isgt, false, collation, &rightvar,
						  leftmin, op_lefttype);
	if (selec != DEFAULT_INEQ_SEL)
		*rightstart = selec;

	/*
	 * Only one of the two "start" fractions can really be more than zero;
	 * believe the larger estimate and reset the other one to exactly 0.0. If
	 * we get exactly equal estimates (as can easily happen with self-joins),
	 * believe neither.
	 */
	if (*leftstart < *rightstart)
		*leftstart = 0.0;
	else if (*leftstart > *rightstart)
		*rightstart = 0.0;
	else
		*leftstart = *rightstart = 0.0;

	/*
	 * If the sort order is nulls-first, we're going to have to skip over any
	 * nulls too.  These would not have been counted by scalarineqsel, and we
	 * can safely add in this fraction regardless of whether we believe
	 * scalarineqsel's results or not.  But be sure to clamp the sum to 1.0!
	 */
	if (nulls_first)
	{
		Form_pg_statistic stats;

		if (HeapTupleIsValid(leftvar.statsTuple))
		{
			stats = (Form_pg_statistic) GETSTRUCT(leftvar.statsTuple);
			*leftstart += stats->stanullfrac;
			CLAMP_PROBABILITY(*leftstart);
			*leftend += stats->stanullfrac;
			CLAMP_PROBABILITY(*leftend);
		}
		if (HeapTupleIsValid(rightvar.statsTuple))
		{
			stats = (Form_pg_statistic) GETSTRUCT(rightvar.statsTuple);
			*rightstart += stats->stanullfrac;
			CLAMP_PROBABILITY(*rightstart);
			*rightend += stats->stanullfrac;
			CLAMP_PROBABILITY(*rightend);
		}
	}

	/* Disbelieve start >= end, just in case that can happen */
	if (*leftstart >= *leftend)
	{
		*leftstart = 0.0;
		*leftend = 1.0;
	}
	if (*rightstart >= *rightend)
	{
		*rightstart = 0.0;
		*rightend = 1.0;
	}

fail:
	ReleaseVariableStats(leftvar);
	ReleaseVariableStats(rightvar);
}


/*
 *	matchingsel -- generic matching-operator selectivity support
 *
 * Use these for any operators that (a) are on data types for which we collect
 * standard statistics, and (b) have behavior for which the default estimate
 * (twice DEFAULT_EQ_SEL) is sane.  Typically that is good for match-like
 * operators.
 */

Datum
matchingsel(PG_FUNCTION_ARGS)
{
	PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
	Oid			operator = PG_GETARG_OID(1);
	List	   *args = (List *) PG_GETARG_POINTER(2);
	int			varRelid = PG_GETARG_INT32(3);
	Oid			collation = PG_GET_COLLATION();
	double		selec;

	/* Use generic restriction selectivity logic. */
	selec = generic_restriction_selectivity(root, operator, collation,
											args, varRelid,
											DEFAULT_MATCHING_SEL);

	PG_RETURN_FLOAT8((float8) selec);
}

Datum
matchingjoinsel(PG_FUNCTION_ARGS)
{
	/* Just punt, for the moment. */
	PG_RETURN_FLOAT8(DEFAULT_MATCHING_SEL);
}


/*
 * Helper routine for estimate_num_groups: add an item to a list of
 * GroupVarInfos, but only if it's not known equal to any of the existing
 * entries.
 */
typedef struct
{
	Node	   *var;			/* might be an expression, not just a Var */
	RelOptInfo *rel;			/* relation it belongs to */
	double		ndistinct;		/* # distinct values */
	bool		isdefault;		/* true if DEFAULT_NUM_DISTINCT was used */
} GroupVarInfo;

static List *
add_unique_group_var(PlannerInfo *root, List *varinfos,
					 Node *var, VariableStatData *vardata)
{
	GroupVarInfo *varinfo;
	double		ndistinct;
	bool		isdefault;
	ListCell   *lc;

	ndistinct = get_variable_numdistinct(vardata, &isdefault);

	/*
	 * The nullingrels bits within the var could cause the same var to be
	 * counted multiple times if it's marked with different nullingrels.  They
	 * could also prevent us from matching the var to the expressions in
	 * extended statistics (see estimate_multivariate_ndistinct).  So strip
	 * them out first.
	 */
	var = remove_nulling_relids(var, root->outer_join_rels, NULL);

	foreach(lc, varinfos)
	{
		varinfo = (GroupVarInfo *) lfirst(lc);

		/* Drop exact duplicates */
		if (equal(var, varinfo->var))
			return varinfos;

		/*
		 * Drop known-equal vars, but only if they belong to different
		 * relations (see comments for estimate_num_groups).  We aren't too
		 * fussy about the semantics of "equal" here.
		 */
		if (vardata->rel != varinfo->rel &&
			exprs_known_equal(root, var, varinfo->var, InvalidOid))
		{
			if (varinfo->ndistinct <= ndistinct)
			{
				/* Keep older item, forget new one */
				return varinfos;
			}
			else
			{
				/* Delete the older item */
				varinfos = foreach_delete_current(varinfos, lc);
			}
		}
	}

	varinfo = palloc_object(GroupVarInfo);

	varinfo->var = var;
	varinfo->rel = vardata->rel;
	varinfo->ndistinct = ndistinct;
	varinfo->isdefault = isdefault;
	varinfos = lappend(varinfos, varinfo);
	return varinfos;
}

/*
 * estimate_num_groups		- Estimate number of groups in a grouped query
 *
 * Given a query having a GROUP BY clause, estimate how many groups there
 * will be --- ie, the number of distinct combinations of the GROUP BY
 * expressions.
 *
 * This routine is also used to estimate the number of rows emitted by
 * a DISTINCT filtering step; that is an isomorphic problem.  (Note:
 * actually, we only use it for DISTINCT when there's no grouping or
 * aggregation ahead of the DISTINCT.)
 *
 * Inputs:
 *	root - the query
 *	groupExprs - list of expressions being grouped by
 *	input_rows - number of rows estimated to arrive at the group/unique
 *		filter step
 *	pgset - NULL, or a List** pointing to a grouping set to filter the
 *		groupExprs against
 *
 * Outputs:
 *	estinfo - When passed as non-NULL, the function will set bits in the
 *		"flags" field in order to provide callers with additional information
 *		about the estimation.  Currently, we only set the SELFLAG_USED_DEFAULT
 *		bit if we used any default values in the estimation.
 *
 * Given the lack of any cross-correlation statistics in the system, it's
 * impossible to do anything really trustworthy with GROUP BY conditions
 * involving multiple Vars.  We should however avoid assuming the worst
 * case (all possible cross-product terms actually appear as groups) since
 * very often the grouped-by Vars are highly correlated.  Our current approach
 * is as follows:
 *	1.  Expressions yielding boolean are assumed to contribute two groups,
 *		independently of their content, and are ignored in the subsequent
 *		steps.  This is mainly because tests like "col IS NULL" break the
 *		heuristic used in step 2 especially badly.
 *	2.  Reduce the given expressions to a list of unique Vars used.  For
 *		example, GROUP BY a, a + b is treated the same as GROUP BY a, b.
 *		It is clearly correct not to count the same Var more than once.
 *		It is also reasonable to treat f(x) the same as x: f() cannot
 *		increase the number of distinct values (unless it is volatile,
 *		which we consider unlikely for grouping), but it probably won't
 *		reduce the number of distinct values much either.
 *		As a special case, if a GROUP BY expression can be matched to an
 *		expressional index for which we have statistics, then we treat the
 *		whole expression as though it were just a Var.
 *	3.  If the list contains Vars of different relations that are known equal
 *		due to equivalence classes, then drop all but one of the Vars from each
 *		known-equal set, keeping the one with smallest estimated # of values
 *		(since the extra values of the others can't appear in joined rows).
 *		Note the reason we only consider Vars of different relations is that
 *		if we considered ones of the same rel, we'd be double-counting the
 *		restriction selectivity of the equality in the next step.
 *	4.  For Vars within a single source rel, we multiply together the numbers
 *		of values, clamp to the number of rows in the rel (divided by 10 if
 *		more than one Var), and then multiply by a factor based on the
 *		selectivity of the restriction clauses for that rel.  When there's
 *		more than one Var, the initial product is probably too high (it's the
 *		worst case) but clamping to a fraction of the rel's rows seems to be a
 *		helpful heuristic for not letting the estimate get out of hand.  (The
 *		factor of 10 is derived from pre-Postgres-7.4 practice.)  The factor
 *		we multiply by to adjust for the restriction selectivity assumes that
 *		the restriction clauses are independent of the grouping, which may not
 *		be a valid assumption, but it's hard to do better.
 *	5.  If there are Vars from multiple rels, we repeat step 4 for each such
 *		rel, and multiply the results together.
 * Note that rels not containing grouped Vars are ignored completely, as are
 * join clauses.  Such rels cannot increase the number of groups, and we
 * assume such clauses do not reduce the number either (somewhat bogus,
 * but we don't have the info to do better).
 */
double
estimate_num_groups(PlannerInfo *root, List *groupExprs, double input_rows,
					List **pgset, EstimationInfo *estinfo)
{
	List	   *varinfos = NIL;
	double		srf_multiplier = 1.0;
	double		numdistinct;
	ListCell   *l;
	int			i;

	/* Zero the estinfo output parameter, if non-NULL */
	if (estinfo != NULL)
		memset(estinfo, 0, sizeof(EstimationInfo));

	/*
	 * We don't ever want to return an estimate of zero groups, as that tends
	 * to lead to division-by-zero and other unpleasantness.  The input_rows
	 * estimate is usually already at least 1, but clamp it just in case it
	 * isn't.
	 */
	input_rows = clamp_row_est(input_rows);

	/*
	 * If no grouping columns, there's exactly one group.  (This can't happen
	 * for normal cases with GROUP BY or DISTINCT, but it is possible for
	 * corner cases with set operations.)
	 */
	if (groupExprs == NIL || (pgset && *pgset == NIL))
		return 1.0;

	/*
	 * Count groups derived from boolean grouping expressions.  For other
	 * expressions, find the unique Vars used, treating an expression as a Var
	 * if we can find stats for it.  For each one, record the statistical
	 * estimate of number of distinct values (total in its table, without
	 * regard for filtering).
	 */
	numdistinct = 1.0;

	i = 0;
	foreach(l, groupExprs)
	{
		Node	   *groupexpr = (Node *) lfirst(l);
		double		this_srf_multiplier;
		VariableStatData vardata;
		List	   *varshere;
		ListCell   *l2;

		/* is expression in this grouping set? */
		if (pgset && !list_member_int(*pgset, i++))
			continue;

		/*
		 * Set-returning functions in grouping columns are a bit problematic.
		 * The code below will effectively ignore their SRF nature and come up
		 * with a numdistinct estimate as though they were scalar functions.
		 * We compensate by scaling up the end result by the largest SRF
		 * rowcount estimate.  (This will be an overestimate if the SRF
		 * produces multiple copies of any output value, but it seems best to
		 * assume the SRF's outputs are distinct.  In any case, it's probably
		 * pointless to worry too much about this without much better
		 * estimates for SRF output rowcounts than we have today.)
		 */
		this_srf_multiplier = expression_returns_set_rows(root, groupexpr);
		if (srf_multiplier < this_srf_multiplier)
			srf_multiplier = this_srf_multiplier;

		/* Short-circuit for expressions returning boolean */
		if (exprType(groupexpr) == BOOLOID)
		{
			numdistinct *= 2.0;
			continue;
		}

		/*
		 * If examine_variable is able to deduce anything about the GROUP BY
		 * expression, treat it as a single variable even if it's really more
		 * complicated.
		 *
		 * XXX This has the consequence that if there's a statistics object on
		 * the expression, we don't split it into individual Vars. This
		 * affects our selection of statistics in
		 * estimate_multivariate_ndistinct, because it's probably better to
		 * use more accurate estimate for each expression and treat them as
		 * independent, than to combine estimates for the extracted variables
		 * when we don't know how that relates to the expressions.
		 */
		examine_variable(root, groupexpr, 0, &vardata);
		if (HeapTupleIsValid(vardata.statsTuple) || vardata.isunique)
		{
			varinfos = add_unique_group_var(root, varinfos,
											groupexpr, &vardata);
			ReleaseVariableStats(vardata);
			continue;
		}
		ReleaseVariableStats(vardata);

		/*
		 * Else pull out the component Vars.  Handle PlaceHolderVars by
		 * recursing into their arguments (effectively assuming that the
		 * PlaceHolderVar doesn't change the number of groups, which boils
		 * down to ignoring the possible addition of nulls to the result set).
		 */
		varshere = pull_var_clause(groupexpr,
								   PVC_RECURSE_AGGREGATES |
								   PVC_RECURSE_WINDOWFUNCS |
								   PVC_RECURSE_PLACEHOLDERS);

		/*
		 * If we find any variable-free GROUP BY item, then either it is a
		 * constant (and we can ignore it) or it contains a volatile function;
		 * in the latter case we punt and assume that each input row will
		 * yield a distinct group.
		 */
		if (varshere == NIL)
		{
			if (contain_volatile_functions(groupexpr))
				return input_rows;
			continue;
		}

		/*
		 * Else add variables to varinfos list
		 */
		foreach(l2, varshere)
		{
			Node	   *var = (Node *) lfirst(l2);

			examine_variable(root, var, 0, &vardata);
			varinfos = add_unique_group_var(root, varinfos, var, &vardata);
			ReleaseVariableStats(vardata);
		}
	}

	/*
	 * If now no Vars, we must have an all-constant or all-boolean GROUP BY
	 * list.
	 */
	if (varinfos == NIL)
	{
		/* Apply SRF multiplier as we would do in the long path */
		numdistinct *= srf_multiplier;
		/* Round off */
		numdistinct = ceil(numdistinct);
		/* Guard against out-of-range answers */
		if (numdistinct > input_rows)
			numdistinct = input_rows;
		if (numdistinct < 1.0)
			numdistinct = 1.0;
		return numdistinct;
	}

	/*
	 * Group Vars by relation and estimate total numdistinct.
	 *
	 * For each iteration of the outer loop, we process the frontmost Var in
	 * varinfos, plus all other Vars in the same relation.  We remove these
	 * Vars from the newvarinfos list for the next iteration. This is the
	 * easiest way to group Vars of same rel together.
	 */
	do
	{
		GroupVarInfo *varinfo1 = (GroupVarInfo *) linitial(varinfos);
		RelOptInfo *rel = varinfo1->rel;
		double		reldistinct = 1;
		double		relmaxndistinct = reldistinct;
		int			relvarcount = 0;
		List	   *newvarinfos = NIL;
		List	   *relvarinfos = NIL;

		/*
		 * Split the list of varinfos in two - one for the current rel, one
		 * for remaining Vars on other rels.
		 */
		relvarinfos = lappend(relvarinfos, varinfo1);
		for_each_from(l, varinfos, 1)
		{
			GroupVarInfo *varinfo2 = (GroupVarInfo *) lfirst(l);

			if (varinfo2->rel == varinfo1->rel)
			{
				/* varinfos on current rel */
				relvarinfos = lappend(relvarinfos, varinfo2);
			}
			else
			{
				/* not time to process varinfo2 yet */
				newvarinfos = lappend(newvarinfos, varinfo2);
			}
		}

		/*
		 * Get the numdistinct estimate for the Vars of this rel.  We
		 * iteratively search for multivariate n-distinct with maximum number
		 * of vars; assuming that each var group is independent of the others,
		 * we multiply them together.  Any remaining relvarinfos after no more
		 * multivariate matches are found are assumed independent too, so
		 * their individual ndistinct estimates are multiplied also.
		 *
		 * While iterating, count how many separate numdistinct values we
		 * apply.  We apply a fudge factor below, but only if we multiplied
		 * more than one such values.
		 */
		while (relvarinfos)
		{
			double		mvndistinct;

			if (estimate_multivariate_ndistinct(root, rel, &relvarinfos,
												&mvndistinct))
			{
				reldistinct *= mvndistinct;
				if (relmaxndistinct < mvndistinct)
					relmaxndistinct = mvndistinct;
				relvarcount++;
			}
			else
			{
				foreach(l, relvarinfos)
				{
					GroupVarInfo *varinfo2 = (GroupVarInfo *) lfirst(l);

					reldistinct *= varinfo2->ndistinct;
					if (relmaxndistinct < varinfo2->ndistinct)
						relmaxndistinct = varinfo2->ndistinct;
					relvarcount++;

					/*
					 * When varinfo2's isdefault is set then we'd better set
					 * the SELFLAG_USED_DEFAULT bit in the EstimationInfo.
					 */
					if (estinfo != NULL && varinfo2->isdefault)
						estinfo->flags |= SELFLAG_USED_DEFAULT;
				}

				/* we're done with this relation */
				relvarinfos = NIL;
			}
		}

		/*
		 * Sanity check --- don't divide by zero if empty relation.
		 */
		Assert(IS_SIMPLE_REL(rel));
		if (rel->tuples > 0)
		{
			/*
			 * Clamp to size of rel, or size of rel / 10 if multiple Vars. The
			 * fudge factor is because the Vars are probably correlated but we
			 * don't know by how much.  We should never clamp to less than the
			 * largest ndistinct value for any of the Vars, though, since
			 * there will surely be at least that many groups.
			 */
			double		clamp = rel->tuples;

			if (relvarcount > 1)
			{
				clamp *= 0.1;
				if (clamp < relmaxndistinct)
				{
					clamp = relmaxndistinct;
					/* for sanity in case some ndistinct is too large: */
					if (clamp > rel->tuples)
						clamp = rel->tuples;
				}
			}
			if (reldistinct > clamp)
				reldistinct = clamp;

			/*
			 * Update the estimate based on the restriction selectivity,
			 * guarding against division by zero when reldistinct is zero.
			 * Also skip this if we know that we are returning all rows.
			 */
			if (reldistinct > 0 && rel->rows < rel->tuples)
			{
				/*
				 * Given a table containing N rows with n distinct values in a
				 * uniform distribution, if we select p rows at random then
				 * the expected number of distinct values selected is
				 *
				 * n * (1 - product((N-N/n-i)/(N-i), i=0..p-1))
				 *
				 * = n * (1 - (N-N/n)! / (N-N/n-p)! * (N-p)! / N!)
				 *
				 * See "Approximating block accesses in database
				 * organizations", S. B. Yao, Communications of the ACM,
				 * Volume 20 Issue 4, April 1977 Pages 260-261.
				 *
				 * Alternatively, re-arranging the terms from the factorials,
				 * this may be written as
				 *
				 * n * (1 - product((N-p-i)/(N-i), i=0..N/n-1))
				 *
				 * This form of the formula is more efficient to compute in
				 * the common case where p is larger than N/n.  Additionally,
				 * as pointed out by Dell'Era, if i << N for all terms in the
				 * product, it can be approximated by
				 *
				 * n * (1 - ((N-p)/N)^(N/n))
				 *
				 * See "Expected distinct values when selecting from a bag
				 * without replacement", Alberto Dell'Era,
				 * http://www.adellera.it/investigations/distinct_balls/.
				 *
				 * The condition i << N is equivalent to n >> 1, so this is a
				 * good approximation when the number of distinct values in
				 * the table is large.  It turns out that this formula also
				 * works well even when n is small.
				 */
				reldistinct *=
					(1 - pow((rel->tuples - rel->rows) / rel->tuples,
							 rel->tuples / reldistinct));
			}
			reldistinct = clamp_row_est(reldistinct);

			/*
			 * Update estimate of total distinct groups.
			 */
			numdistinct *= reldistinct;
		}

		varinfos = newvarinfos;
	} while (varinfos != NIL);

	/* Now we can account for the effects of any SRFs */
	numdistinct *= srf_multiplier;

	/* Round off */
	numdistinct = ceil(numdistinct);

	/* Guard against out-of-range answers */
	if (numdistinct > input_rows)
		numdistinct = input_rows;
	if (numdistinct < 1.0)
		numdistinct = 1.0;

	return numdistinct;
}

/*
 * Try to estimate the bucket size of the hash join inner side when the join
 * condition contains two or more clauses by employing extended statistics.
 *
 * The main idea of this approach is that the distinct value generated by
 * multivariate estimation on two or more columns would provide less bucket size
 * than estimation on one separate column.
 *
 * IMPORTANT: It is crucial to synchronize the approach of combining different
 * estimations with the caller's method.
 *
 * Return a list of clauses that didn't fetch any extended statistics.
 */
List *
estimate_multivariate_bucketsize(PlannerInfo *root, RelOptInfo *inner,
								 List *hashclauses,
								 Selectivity *innerbucketsize)
{
	List	   *clauses;
	List	   *otherclauses;
	double		ndistinct;

	if (list_length(hashclauses) <= 1)
	{
		/*
		 * Nothing to do for a single clause.  Could we employ univariate
		 * extended stat here?
		 */
		return hashclauses;
	}

	/* "clauses" is the list of hashclauses we've not dealt with yet */
	clauses = list_copy(hashclauses);
	/* "otherclauses" holds clauses we are going to return to caller */
	otherclauses = NIL;
	/* current estimate of ndistinct */
	ndistinct = 1.0;
	while (clauses != NIL)
	{
		ListCell   *lc;
		int			relid = -1;
		List	   *varinfos = NIL;
		List	   *origin_rinfos = NIL;
		double		mvndistinct;
		List	   *origin_varinfos;
		int			group_relid = -1;
		RelOptInfo *group_rel = NULL;
		ListCell   *lc1,
				   *lc2;

		/*
		 * Find clauses, referencing the same single base relation and try to
		 * estimate such a group with extended statistics.  Create varinfo for
		 * an approved clause, push it to otherclauses, if it can't be
		 * estimated here or ignore to process at the next iteration.
		 */
		foreach(lc, clauses)
		{
			RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
			Node	   *expr;
			Relids		relids;
			GroupVarInfo *varinfo;

			/*
			 * Find the inner side of the join, which we need to estimate the
			 * number of buckets.  Use outer_is_left because the
			 * clause_sides_match_join routine has called on hash clauses.
			 */
			relids = rinfo->outer_is_left ?
				rinfo->right_relids : rinfo->left_relids;
			expr = rinfo->outer_is_left ?
				get_rightop(rinfo->clause) : get_leftop(rinfo->clause);

			if (bms_get_singleton_member(relids, &relid) &&
				root->simple_rel_array[relid]->statlist != NIL)
			{
				bool		is_duplicate = false;

				/*
				 * This inner-side expression references only one relation.
				 * Extended statistics on this clause can exist.
				 */
				if (group_relid < 0)
				{
					RangeTblEntry *rte = root->simple_rte_array[relid];

					if (!rte || (rte->relkind != RELKIND_RELATION &&
								 rte->relkind != RELKIND_MATVIEW &&
								 rte->relkind != RELKIND_FOREIGN_TABLE &&
								 rte->relkind != RELKIND_PARTITIONED_TABLE))
					{
						/* Extended statistics can't exist in principle */
						otherclauses = lappend(otherclauses, rinfo);
						clauses = foreach_delete_current(clauses, lc);
						continue;
					}

					group_relid = relid;
					group_rel = root->simple_rel_array[relid];
				}
				else if (group_relid != relid)
				{
					/*
					 * Being in the group forming state we don't need other
					 * clauses.
					 */
					continue;
				}

				/*
				 * We're going to add the new clause to the varinfos list.  We
				 * might re-use add_unique_group_var(), but we don't do so for
				 * two reasons.
				 *
				 * 1) We must keep the origin_rinfos list ordered exactly the
				 * same way as varinfos.
				 *
				 * 2) add_unique_group_var() is designed for
				 * estimate_num_groups(), where a larger number of groups is
				 * worse.   While estimating the number of hash buckets, we
				 * have the opposite: a lesser number of groups is worse.
				 * Therefore, we don't have to remove "known equal" vars: the
				 * removed var may valuably contribute to the multivariate
				 * statistics to grow the number of groups.
				 */

				/*
				 * Clear nullingrels to correctly match hash keys.  See
				 * add_unique_group_var()'s comment for details.
				 */
				expr = remove_nulling_relids(expr, root->outer_join_rels, NULL);

				/*
				 * Detect and exclude exact duplicates from the list of hash
				 * keys (like add_unique_group_var does).
				 */
				foreach(lc1, varinfos)
				{
					varinfo = (GroupVarInfo *) lfirst(lc1);

					if (!equal(expr, varinfo->var))
						continue;

					is_duplicate = true;
					break;
				}

				if (is_duplicate)
				{
					/*
					 * Skip exact duplicates. Adding them to the otherclauses
					 * list also doesn't make sense.
					 */
					continue;
				}

				/*
				 * Initialize GroupVarInfo.  We only use it to call
				 * estimate_multivariate_ndistinct(), which doesn't care about
				 * ndistinct and isdefault fields.  Thus, skip these fields.
				 */
				varinfo = palloc0_object(GroupVarInfo);
				varinfo->var = expr;
				varinfo->rel = root->simple_rel_array[relid];
				varinfos = lappend(varinfos, varinfo);

				/*
				 * Remember the link to RestrictInfo for the case the clause
				 * is failed to be estimated.
				 */
				origin_rinfos = lappend(origin_rinfos, rinfo);
			}
			else
			{
				/* This clause can't be estimated with extended statistics */
				otherclauses = lappend(otherclauses, rinfo);
			}

			clauses = foreach_delete_current(clauses, lc);
		}

		if (list_length(varinfos) < 2)
		{
			/*
			 * Multivariate statistics doesn't apply to single columns except
			 * for expressions, but it has not been implemented yet.
			 */
			otherclauses = list_concat(otherclauses, origin_rinfos);
			list_free_deep(varinfos);
			list_free(origin_rinfos);
			continue;
		}

		Assert(group_rel != NULL);

		/* Employ the extended statistics. */
		origin_varinfos = varinfos;
		for (;;)
		{
			bool		estimated = estimate_multivariate_ndistinct(root,
																	group_rel,
																	&varinfos,
																	&mvndistinct);

			if (!estimated)
				break;

			/*
			 * We've got an estimation.  Use ndistinct value in a consistent
			 * way - according to the caller's logic (see
			 * final_cost_hashjoin).
			 */
			if (ndistinct < mvndistinct)
				ndistinct = mvndistinct;
			Assert(ndistinct >= 1.0);
		}

		Assert(list_length(origin_varinfos) == list_length(origin_rinfos));

		/* Collect unmatched clauses as otherclauses. */
		forboth(lc1, origin_varinfos, lc2, origin_rinfos)
		{
			GroupVarInfo *vinfo = lfirst(lc1);

			if (!list_member_ptr(varinfos, vinfo))
				/* Already estimated */
				continue;

			/* Can't be estimated here - push to the returning list */
			otherclauses = lappend(otherclauses, lfirst(lc2));
		}
	}

	*innerbucketsize = 1.0 / ndistinct;
	return otherclauses;
}

/*
 * Estimate hash bucket statistics when the specified expression is used
 * as a hash key for the given number of buckets.
 *
 * This attempts to determine two values:
 *
 * 1. The frequency of the most common value of the expression (returns
 * zero into *mcv_freq if we can't get that).  This will be frequency
 * relative to the entire underlying table.
 *
 * 2. The "bucketsize fraction", ie, average number of entries in a bucket
 * divided by total number of tuples to be hashed.
 *
 * XXX This is really pretty bogus since we're effectively assuming that the
 * distribution of hash keys will be the same after applying restriction
 * clauses as it was in the underlying relation.  However, we are not nearly
 * smart enough to figure out how the restrict clauses might change the
 * distribution, so this will have to do for now.
 *
 * We are passed the number of buckets the executor will use for the given
 * input relation.  If the data were perfectly distributed, with the same
 * number of tuples going into each available bucket, then the bucketsize
 * fraction would be 1/nbuckets.  But this happy state of affairs will occur
 * only if (a) there are at least nbuckets distinct data values, and (b)
 * we have a not-too-skewed data distribution.  Otherwise the buckets will
 * be nonuniformly occupied.  If the other relation in the join has a key
 * distribution similar to this one's, then the most-loaded buckets are
 * exactly those that will be probed most often.  Therefore, the "average"
 * bucket size for costing purposes should really be taken as something close
 * to the "worst case" bucket size.  We try to estimate this by adjusting the
 * fraction if there are too few distinct data values, and then clamping to
 * at least the bucket size implied by the most common value's frequency.
 *
 * If no statistics are available, use a default estimate of 0.1.  This will
 * discourage use of a hash rather strongly if the inner relation is large,
 * which is what we want.  We do not want to hash unless we know that the
 * inner rel is well-dispersed (or the alternatives seem much worse).
 *
 * The caller should also check that the mcv_freq is not so large that the
 * most common value would by itself require an impractically large bucket.
 * In a hash join, the executor can split buckets if they get too big, but
 * obviously that doesn't help for a bucket that contains many duplicates of
 * the same value.
 */
void
estimate_hash_bucket_stats(PlannerInfo *root, Node *hashkey, double nbuckets,
						   Selectivity *mcv_freq,
						   Selectivity *bucketsize_frac)
{
	VariableStatData vardata;
	double		estfract,
				ndistinct;
	bool		isdefault;
	AttStatsSlot sslot;

	examine_variable(root, hashkey, 0, &vardata);

	/* Initialize *mcv_freq to "unknown" */
	*mcv_freq = 0.0;

	/* Look up the frequency of the most common value, if available */
	if (HeapTupleIsValid(vardata.statsTuple))
	{
		if (get_attstatsslot(&sslot, vardata.statsTuple,
							 STATISTIC_KIND_MCV, InvalidOid,
							 ATTSTATSSLOT_NUMBERS))
		{
			/*
			 * The first MCV stat is for the most common value.
			 */
			if (sslot.nnumbers > 0)
				*mcv_freq = sslot.numbers[0];
			free_attstatsslot(&sslot);
		}
		else if (get_attstatsslot(&sslot, vardata.statsTuple,
								  STATISTIC_KIND_HISTOGRAM, InvalidOid,
								  0))
		{
			/*
			 * If there are no recorded MCVs, but we do have a histogram, then
			 * assume that ANALYZE determined that the column is unique.
			 */
			if (vardata.rel && vardata.rel->tuples > 0)
				*mcv_freq = 1.0 / vardata.rel->tuples;
		}
	}

	/* Get number of distinct values */
	ndistinct = get_variable_numdistinct(&vardata, &isdefault);

	/*
	 * If ndistinct isn't real, punt.  We normally return 0.1, but if the
	 * mcv_freq is known to be even higher than that, use it instead.
	 */
	if (isdefault)
	{
		*bucketsize_frac = (Selectivity) Max(0.1, *mcv_freq);
		ReleaseVariableStats(vardata);
		return;
	}

	/*
	 * Adjust ndistinct to account for restriction clauses.  Observe we are
	 * assuming that the data distribution is affected uniformly by the
	 * restriction clauses!
	 *
	 * XXX Possibly better way, but much more expensive: multiply by
	 * selectivity of rel's restriction clauses that mention the target Var.
	 */
	if (vardata.rel && vardata.rel->tuples > 0)
	{
		ndistinct *= vardata.rel->rows / vardata.rel->tuples;
		ndistinct = clamp_row_est(ndistinct);
	}

	/*
	 * Initial estimate of bucketsize fraction is 1/nbuckets as long as the
	 * number of buckets is less than the expected number of distinct values;
	 * otherwise it is 1/ndistinct.
	 */
	if (ndistinct > nbuckets)
		estfract = 1.0 / nbuckets;
	else
		estfract = 1.0 / ndistinct;

	/*
	 * Clamp the bucketsize fraction to be not less than the MCV frequency,
	 * since whichever bucket the MCV values end up in will have at least that
	 * size.  This has no effect if *mcv_freq is still zero.
	 */
	estfract = Max(estfract, *mcv_freq);

	*bucketsize_frac = (Selectivity) estfract;

	ReleaseVariableStats(vardata);
}

/*
 * estimate_hashagg_tablesize
 *	  estimate the number of bytes that a hash aggregate hashtable will
 *	  require based on the agg_costs, path width and number of groups.
 *
 * We return the result as "double" to forestall any possible overflow
 * problem in the multiplication by dNumGroups.
 *
 * XXX this may be over-estimating the size now that hashagg knows to omit
 * unneeded columns from the hashtable.  Also for mixed-mode grouping sets,
 * grouping columns not in the hashed set are counted here even though hashagg
 * won't store them.  Is this a problem?
 */
double
estimate_hashagg_tablesize(PlannerInfo *root, Path *path,
						   const AggClauseCosts *agg_costs, double dNumGroups)
{
	Size		hashentrysize;

	hashentrysize = hash_agg_entry_size(list_length(root->aggtransinfos),
										path->pathtarget->width,
										agg_costs->transitionSpace);

	/*
	 * Note that this disregards the effect of fill-factor and growth policy
	 * of the hash table.  That's probably ok, given that the default
	 * fill-factor is relatively high.  It'd be hard to meaningfully factor in
	 * "double-in-size" growth policies here.
	 */
	return hashentrysize * dNumGroups;
}


/*-------------------------------------------------------------------------
 *
 * Support routines
 *
 *-------------------------------------------------------------------------
 */

/*
 * Find the best matching ndistinct extended statistics for the given list of
 * GroupVarInfos.
 *
 * Callers must ensure that the given GroupVarInfos all belong to 'rel' and
 * the GroupVarInfos list does not contain any duplicate Vars or expressions.
 *
 * When statistics are found that match > 1 of the given GroupVarInfo, the
 * *ndistinct parameter is set according to the ndistinct estimate and a new
 * list is built with the matching GroupVarInfos removed, which is output via
 * the *varinfos parameter before returning true.  When no matching stats are
 * found, false is returned and the *varinfos and *ndistinct parameters are
 * left untouched.
 */
static bool
estimate_multivariate_ndistinct(PlannerInfo *root, RelOptInfo *rel,
								List **varinfos, double *ndistinct)
{
	ListCell   *lc;
	int			nmatches_vars;
	int			nmatches_exprs;
	Oid			statOid = InvalidOid;
	MVNDistinct *stats;
	StatisticExtInfo *matched_info = NULL;
	RangeTblEntry *rte = planner_rt_fetch(rel->relid, root);

	/* bail out immediately if the table has no extended statistics */
	if (!rel->statlist)
		return false;

	/* look for the ndistinct statistics object matching the most vars */
	nmatches_vars = 0;			/* we require at least two matches */
	nmatches_exprs = 0;
	foreach(lc, rel->statlist)
	{
		ListCell   *lc2;
		StatisticExtInfo *info = (StatisticExtInfo *) lfirst(lc);
		int			nshared_vars = 0;
		int			nshared_exprs = 0;

		/* skip statistics of other kinds */
		if (info->kind != STATS_EXT_NDISTINCT)
			continue;

		/* skip statistics with mismatching stxdinherit value */
		if (info->inherit != rte->inh)
			continue;

		/*
		 * Determine how many expressions (and variables in non-matched
		 * expressions) match. We'll then use these numbers to pick the
		 * statistics object that best matches the clauses.
		 */
		foreach(lc2, *varinfos)
		{
			ListCell   *lc3;
			GroupVarInfo *varinfo = (GroupVarInfo *) lfirst(lc2);
			AttrNumber	attnum;

			Assert(varinfo->rel == rel);

			/* simple Var, search in statistics keys directly */
			if (IsA(varinfo->var, Var))
			{
				attnum = ((Var *) varinfo->var)->varattno;

				/*
				 * Ignore system attributes - we don't support statistics on
				 * them, so can't match them (and it'd fail as the values are
				 * negative).
				 */
				if (!AttrNumberIsForUserDefinedAttr(attnum))
					continue;

				if (bms_is_member(attnum, info->keys))
					nshared_vars++;

				continue;
			}

			/* expression - see if it's in the statistics object */
			foreach(lc3, info->exprs)
			{
				Node	   *expr = (Node *) lfirst(lc3);

				if (equal(varinfo->var, expr))
				{
					nshared_exprs++;
					break;
				}
			}
		}

		/*
		 * The ndistinct extended statistics contain estimates for a minimum
		 * of pairs of columns which the statistics are defined on and
		 * certainly not single columns.  Here we skip unless we managed to
		 * match to at least two columns.
		 */
		if (nshared_vars + nshared_exprs < 2)
			continue;

		/*
		 * Check if these statistics are a better match than the previous best
		 * match and if so, take note of the StatisticExtInfo.
		 *
		 * The statslist is sorted by statOid, so the StatisticExtInfo we
		 * select as the best match is deterministic even when multiple sets
		 * of statistics match equally as well.
		 */
		if ((nshared_exprs > nmatches_exprs) ||
			(((nshared_exprs == nmatches_exprs)) && (nshared_vars > nmatches_vars)))
		{
			statOid = info->statOid;
			nmatches_vars = nshared_vars;
			nmatches_exprs = nshared_exprs;
			matched_info = info;
		}
	}

	/* No match? */
	if (statOid == InvalidOid)
		return false;

	Assert(nmatches_vars + nmatches_exprs > 1);

	stats = statext_ndistinct_load(statOid, rte->inh);

	/*
	 * If we have a match, search it for the specific item that matches (there
	 * must be one), and construct the output values.
	 */
	if (stats)
	{
		int			i;
		List	   *newlist = NIL;
		MVNDistinctItem *item = NULL;
		ListCell   *lc2;
		Bitmapset  *matched = NULL;
		AttrNumber	attnum_offset;

		/*
		 * How much we need to offset the attnums? If there are no
		 * expressions, no offset is needed. Otherwise offset enough to move
		 * the lowest one (which is equal to number of expressions) to 1.
		 */
		if (matched_info->exprs)
			attnum_offset = (list_length(matched_info->exprs) + 1);
		else
			attnum_offset = 0;

		/* see what actually matched */
		foreach(lc2, *varinfos)
		{
			ListCell   *lc3;
			int			idx;
			bool		found = false;

			GroupVarInfo *varinfo = (GroupVarInfo *) lfirst(lc2);

			/*
			 * Process a simple Var expression, by matching it to keys
			 * directly. If there's a matching expression, we'll try matching
			 * it later.
			 */
			if (IsA(varinfo->var, Var))
			{
				AttrNumber	attnum = ((Var *) varinfo->var)->varattno;

				/*
				 * Ignore expressions on system attributes. Can't rely on the
				 * bms check for negative values.
				 */
				if (!AttrNumberIsForUserDefinedAttr(attnum))
					continue;

				/* Is the variable covered by the statistics object? */
				if (!bms_is_member(attnum, matched_info->keys))
					continue;

				attnum = attnum + attnum_offset;

				/* ensure sufficient offset */
				Assert(AttrNumberIsForUserDefinedAttr(attnum));

				matched = bms_add_member(matched, attnum);

				found = true;
			}

			/*
			 * XXX Maybe we should allow searching the expressions even if we
			 * found an attribute matching the expression? That would handle
			 * trivial expressions like "(a)" but it seems fairly useless.
			 */
			if (found)
				continue;

			/* expression - see if it's in the statistics object */
			idx = 0;
			foreach(lc3, matched_info->exprs)
			{
				Node	   *expr = (Node *) lfirst(lc3);

				if (equal(varinfo->var, expr))
				{
					AttrNumber	attnum = -(idx + 1);

					attnum = attnum + attnum_offset;

					/* ensure sufficient offset */
					Assert(AttrNumberIsForUserDefinedAttr(attnum));

					matched = bms_add_member(matched, attnum);

					/* there should be just one matching expression */
					break;
				}

				idx++;
			}
		}

		/* Find the specific item that exactly matches the combination */
		for (i = 0; i < stats->nitems; i++)
		{
			int			j;
			MVNDistinctItem *tmpitem = &stats->items[i];

			if (tmpitem->nattributes != bms_num_members(matched))
				continue;

			/* assume it's the right item */
			item = tmpitem;

			/* check that all item attributes/expressions fit the match */
			for (j = 0; j < tmpitem->nattributes; j++)
			{
				AttrNumber	attnum = tmpitem->attributes[j];

				/*
				 * Thanks to how we constructed the matched bitmap above, we
				 * can just offset all attnums the same way.
				 */
				attnum = attnum + attnum_offset;

				if (!bms_is_member(attnum, matched))
				{
					/* nah, it's not this item */
					item = NULL;
					break;
				}
			}

			/*
			 * If the item has all the matched attributes, we know it's the
			 * right one - there can't be a better one. matching more.
			 */
			if (item)
				break;
		}

		/*
		 * Make sure we found an item. There has to be one, because ndistinct
		 * statistics includes all combinations of attributes.
		 */
		if (!item)
			elog(ERROR, "corrupt MVNDistinct entry");

		/* Form the output varinfo list, keeping only unmatched ones */
		foreach(lc, *varinfos)
		{
			GroupVarInfo *varinfo = (GroupVarInfo *) lfirst(lc);
			ListCell   *lc3;
			bool		found = false;

			/*
			 * Let's look at plain variables first, because it's the most
			 * common case and the check is quite cheap. We can simply get the
			 * attnum and check (with an offset) matched bitmap.
			 */
			if (IsA(varinfo->var, Var))
			{
				AttrNumber	attnum = ((Var *) varinfo->var)->varattno;

				/*
				 * If it's a system attribute, we're done. We don't support
				 * extended statistics on system attributes, so it's clearly
				 * not matched. Just keep the expression and continue.
				 */
				if (!AttrNumberIsForUserDefinedAttr(attnum))
				{
					newlist = lappend(newlist, varinfo);
					continue;
				}

				/* apply the same offset as above */
				attnum += attnum_offset;

				/* if it's not matched, keep the varinfo */
				if (!bms_is_member(attnum, matched))
					newlist = lappend(newlist, varinfo);

				/* The rest of the loop deals with complex expressions. */
				continue;
			}

			/*
			 * Process complex expressions, not just simple Vars.
			 *
			 * First, we search for an exact match of an expression. If we
			 * find one, we can just discard the whole GroupVarInfo, with all
			 * the variables we extracted from it.
			 *
			 * Otherwise we inspect the individual vars, and try matching it
			 * to variables in the item.
			 */
			foreach(lc3, matched_info->exprs)
			{
				Node	   *expr = (Node *) lfirst(lc3);

				if (equal(varinfo->var, expr))
				{
					found = true;
					break;
				}
			}

			/* found exact match, skip */
			if (found)
				continue;

			newlist = lappend(newlist, varinfo);
		}

		*varinfos = newlist;
		*ndistinct = item->ndistinct;
		return true;
	}

	return false;
}

/*
 * convert_to_scalar
 *	  Convert non-NULL values of the indicated types to the comparison
 *	  scale needed by scalarineqsel().
 *	  Returns "true" if successful.
 *
 * XXX this routine is a hack: ideally we should look up the conversion
 * subroutines in pg_type.
 *
 * All numeric datatypes are simply converted to their equivalent
 * "double" values.  (NUMERIC values that are outside the range of "double"
 * are clamped to +/- HUGE_VAL.)
 *
 * String datatypes are converted by convert_string_to_scalar(),
 * which is explained below.  The reason why this routine deals with
 * three values at a time, not just one, is that we need it for strings.
 *
 * The bytea datatype is just enough different from strings that it has
 * to be treated separately.
 *
 * The several datatypes representing absolute times are all converted
 * to Timestamp, which is actually an int64, and then we promote that to
 * a double.  Note this will give correct results even for the "special"
 * values of Timestamp, since those are chosen to compare correctly;
 * see timestamp_cmp.
 *
 * The several datatypes representing relative times (intervals) are all
 * converted to measurements expressed in seconds.
 */
static bool
convert_to_scalar(Datum value, Oid valuetypid, Oid collid, double *scaledvalue,
				  Datum lobound, Datum hibound, Oid boundstypid,
				  double *scaledlobound, double *scaledhibound)
{
	bool		failure = false;

	/*
	 * Both the valuetypid and the boundstypid should exactly match the
	 * declared input type(s) of the operator we are invoked for.  However,
	 * extensions might try to use scalarineqsel as estimator for operators
	 * with input type(s) we don't handle here; in such cases, we want to
	 * return false, not fail.  In any case, we mustn't assume that valuetypid
	 * and boundstypid are identical.
	 *
	 * XXX The histogram we are interpolating between points of could belong
	 * to a column that's only binary-compatible with the declared type. In
	 * essence we are assuming that the semantics of binary-compatible types
	 * are enough alike that we can use a histogram generated with one type's
	 * operators to estimate selectivity for the other's.  This is outright
	 * wrong in some cases --- in particular signed versus unsigned
	 * interpretation could trip us up.  But it's useful enough in the
	 * majority of cases that we do it anyway.  Should think about more
	 * rigorous ways to do it.
	 */
	switch (valuetypid)
	{
			/*
			 * Built-in numeric types
			 */
		case BOOLOID:
		case INT2OID:
		case INT4OID:
		case INT8OID:
		case FLOAT4OID:
		case FLOAT8OID:
		case NUMERICOID:
		case OIDOID:
		case REGPROCOID:
		case REGPROCEDUREOID:
		case REGOPEROID:
		case REGOPERATOROID:
		case REGCLASSOID:
		case REGTYPEOID:
		case REGCOLLATIONOID:
		case REGCONFIGOID:
		case REGDICTIONARYOID:
		case REGROLEOID:
		case REGNAMESPACEOID:
		case REGDATABASEOID:
			*scaledvalue = convert_numeric_to_scalar(value, valuetypid,
													 &failure);
			*scaledlobound = convert_numeric_to_scalar(lobound, boundstypid,
													   &failure);
			*scaledhibound = convert_numeric_to_scalar(hibound, boundstypid,
													   &failure);
			return !failure;

			/*
			 * Built-in string types
			 */
		case CHAROID:
		case BPCHAROID:
		case VARCHAROID:
		case TEXTOID:
		case NAMEOID:
			{
				char	   *valstr = convert_string_datum(value, valuetypid,
														  collid, &failure);
				char	   *lostr = convert_string_datum(lobound, boundstypid,
														 collid, &failure);
				char	   *histr = convert_string_datum(hibound, boundstypid,
														 collid, &failure);

				/*
				 * Bail out if any of the values is not of string type.  We
				 * might leak converted strings for the other value(s), but
				 * that's not worth troubling over.
				 */
				if (failure)
					return false;

				convert_string_to_scalar(valstr, scaledvalue,
										 lostr, scaledlobound,
										 histr, scaledhibound);
				pfree(valstr);
				pfree(lostr);
				pfree(histr);
				return true;
			}

			/*
			 * Built-in bytea type
			 */
		case BYTEAOID:
			{
				/* We only support bytea vs bytea comparison */
				if (boundstypid != BYTEAOID)
					return false;
				convert_bytea_to_scalar(value, scaledvalue,
										lobound, scaledlobound,
										hibound, scaledhibound);
				return true;
			}

			/*
			 * Built-in time types
			 */
		case TIMESTAMPOID:
		case TIMESTAMPTZOID:
		case DATEOID:
		case INTERVALOID:
		case TIMEOID:
		case TIMETZOID:
			*scaledvalue = convert_timevalue_to_scalar(value, valuetypid,
													   &failure);
			*scaledlobound = convert_timevalue_to_scalar(lobound, boundstypid,
														 &failure);
			*scaledhibound = convert_timevalue_to_scalar(hibound, boundstypid,
														 &failure);
			return !failure;

			/*
			 * Built-in network types
			 */
		case INETOID:
		case CIDROID:
		case MACADDROID:
		case MACADDR8OID:
			*scaledvalue = convert_network_to_scalar(value, valuetypid,
													 &failure);
			*scaledlobound = convert_network_to_scalar(lobound, boundstypid,
													   &failure);
			*scaledhibound = convert_network_to_scalar(hibound, boundstypid,
													   &failure);
			return !failure;
	}
	/* Don't know how to convert */
	*scaledvalue = *scaledlobound = *scaledhibound = 0;
	return false;
}

/*
 * Do convert_to_scalar()'s work for any numeric data type.
 *
 * On failure (e.g., unsupported typid), set *failure to true;
 * otherwise, that variable is not changed.
 */
static double
convert_numeric_to_scalar(Datum value, Oid typid, bool *failure)
{
	switch (typid)
	{
		case BOOLOID:
			return (double) DatumGetBool(value);
		case INT2OID:
			return (double) DatumGetInt16(value);
		case INT4OID:
			return (double) DatumGetInt32(value);
		case INT8OID:
			return (double) DatumGetInt64(value);
		case FLOAT4OID:
			return (double) DatumGetFloat4(value);
		case FLOAT8OID:
			return (double) DatumGetFloat8(value);
		case NUMERICOID:
			/* Note: out-of-range values will be clamped to +-HUGE_VAL */
			return (double)
				DatumGetFloat8(DirectFunctionCall1(numeric_float8_no_overflow,
												   value));
		case OIDOID:
		case REGPROCOID:
		case REGPROCEDUREOID:
		case REGOPEROID:
		case REGOPERATOROID:
		case REGCLASSOID:
		case REGTYPEOID:
		case REGCOLLATIONOID:
		case REGCONFIGOID:
		case REGDICTIONARYOID:
		case REGROLEOID:
		case REGNAMESPACEOID:
		case REGDATABASEOID:
			/* we can treat OIDs as integers... */
			return (double) DatumGetObjectId(value);
	}

	*failure = true;
	return 0;
}

/*
 * Do convert_to_scalar()'s work for any character-string data type.
 *
 * String datatypes are converted to a scale that ranges from 0 to 1,
 * where we visualize the bytes of the string as fractional digits.
 *
 * We do not want the base to be 256, however, since that tends to
 * generate inflated selectivity estimates; few databases will have
 * occurrences of all 256 possible byte values at each position.
 * Instead, use the smallest and largest byte values seen in the bounds
 * as the estimated range for each byte, after some fudging to deal with
 * the fact that we probably aren't going to see the full range that way.
 *
 * An additional refinement is that we discard any common prefix of the
 * three strings before computing the scaled values.  This allows us to
 * "zoom in" when we encounter a narrow data range.  An example is a phone
 * number database where all the values begin with the same area code.
 * (Actually, the bounds will be adjacent histogram-bin-boundary values,
 * so this is more likely to happen than you might think.)
 */
static void
convert_string_to_scalar(char *value,
						 double *scaledvalue,
						 char *lobound,
						 double *scaledlobound,
						 char *hibound,
						 double *scaledhibound)
{
	int			rangelo,
				rangehi;
	char	   *sptr;

	rangelo = rangehi = (unsigned char) hibound[0];
	for (sptr = lobound; *sptr; sptr++)
	{
		if (rangelo > (unsigned char) *sptr)
			rangelo = (unsigned char) *sptr;
		if (rangehi < (unsigned char) *sptr)
			rangehi = (unsigned char) *sptr;
	}
	for (sptr = hibound; *sptr; sptr++)
	{
		if (rangelo > (unsigned char) *sptr)
			rangelo = (unsigned char) *sptr;
		if (rangehi < (unsigned char) *sptr)
			rangehi = (unsigned char) *sptr;
	}
	/* If range includes any upper-case ASCII chars, make it include all */
	if (rangelo <= 'Z' && rangehi >= 'A')
	{
		if (rangelo > 'A')
			rangelo = 'A';
		if (rangehi < 'Z')
			rangehi = 'Z';
	}
	/* Ditto lower-case */
	if (rangelo <= 'z' && rangehi >= 'a')
	{
		if (rangelo > 'a')
			rangelo = 'a';
		if (rangehi < 'z')
			rangehi = 'z';
	}
	/* Ditto digits */
	if (rangelo <= '9' && rangehi >= '0')
	{
		if (rangelo > '0')
			rangelo = '0';
		if (rangehi < '9')
			rangehi = '9';
	}

	/*
	 * If range includes less than 10 chars, assume we have not got enough
	 * data, and make it include regular ASCII set.
	 */
	if (rangehi - rangelo < 9)
	{
		rangelo = ' ';
		rangehi = 127;
	}

	/*
	 * Now strip any common prefix of the three strings.
	 */
	while (*lobound)
	{
		if (*lobound != *hibound || *lobound != *value)
			break;
		lobound++, hibound++, value++;
	}

	/*
	 * Now we can do the conversions.
	 */
	*scaledvalue = convert_one_string_to_scalar(value, rangelo, rangehi);
	*scaledlobound = convert_one_string_to_scalar(lobound, rangelo, rangehi);
	*scaledhibound = convert_one_string_to_scalar(hibound, rangelo, rangehi);
}

static double
convert_one_string_to_scalar(char *value, int rangelo, int rangehi)
{
	int			slen = strlen(value);
	double		num,
				denom,
				base;

	if (slen <= 0)
		return 0.0;				/* empty string has scalar value 0 */

	/*
	 * There seems little point in considering more than a dozen bytes from
	 * the string.  Since base is at least 10, that will give us nominal
	 * resolution of at least 12 decimal digits, which is surely far more
	 * precision than this estimation technique has got anyway (especially in
	 * non-C locales).  Also, even with the maximum possible base of 256, this
	 * ensures denom cannot grow larger than 256^13 = 2.03e31, which will not
	 * overflow on any known machine.
	 */
	if (slen > 12)
		slen = 12;

	/* Convert initial characters to fraction */
	base = rangehi - rangelo + 1;
	num = 0.0;
	denom = base;
	while (slen-- > 0)
	{
		int			ch = (unsigned char) *value++;

		if (ch < rangelo)
			ch = rangelo - 1;
		else if (ch > rangehi)
			ch = rangehi + 1;
		num += ((double) (ch - rangelo)) / denom;
		denom *= base;
	}

	return num;
}

/*
 * Convert a string-type Datum into a palloc'd, null-terminated string.
 *
 * On failure (e.g., unsupported typid), set *failure to true;
 * otherwise, that variable is not changed.  (We'll return NULL on failure.)
 *
 * When using a non-C locale, we must pass the string through pg_strxfrm()
 * before continuing, so as to generate correct locale-specific results.
 */
static char *
convert_string_datum(Datum value, Oid typid, Oid collid, bool *failure)
{
	char	   *val;
	pg_locale_t mylocale;

	switch (typid)
	{
		case CHAROID:
			val = (char *) palloc(2);
			val[0] = DatumGetChar(value);
			val[1] = '\0';
			break;
		case BPCHAROID:
		case VARCHAROID:
		case TEXTOID:
			val = TextDatumGetCString(value);
			break;
		case NAMEOID:
			{
				NameData   *nm = (NameData *) DatumGetPointer(value);

				val = pstrdup(NameStr(*nm));
				break;
			}
		default:
			*failure = true;
			return NULL;
	}

	mylocale = pg_newlocale_from_collation(collid);

	if (!mylocale->collate_is_c)
	{
		char	   *xfrmstr;
		size_t		xfrmlen;
		size_t		xfrmlen2 PG_USED_FOR_ASSERTS_ONLY;

		/*
		 * XXX: We could guess at a suitable output buffer size and only call
		 * pg_strxfrm() twice if our guess is too small.
		 *
		 * XXX: strxfrm doesn't support UTF-8 encoding on Win32, it can return
		 * bogus data or set an error. This is not really a problem unless it
		 * crashes since it will only give an estimation error and nothing
		 * fatal.
		 *
		 * XXX: we do not check pg_strxfrm_enabled(). On some platforms and in
		 * some cases, libc strxfrm() may return the wrong results, but that
		 * will only lead to an estimation error.
		 */
		xfrmlen = pg_strxfrm(NULL, val, 0, mylocale);
#ifdef WIN32

		/*
		 * On Windows, strxfrm returns INT_MAX when an error occurs. Instead
		 * of trying to allocate this much memory (and fail), just return the
		 * original string unmodified as if we were in the C locale.
		 */
		if (xfrmlen == INT_MAX)
			return val;
#endif
		xfrmstr = (char *) palloc(xfrmlen + 1);
		xfrmlen2 = pg_strxfrm(xfrmstr, val, xfrmlen + 1, mylocale);

		/*
		 * Some systems (e.g., glibc) can return a smaller value from the
		 * second call than the first; thus the Assert must be <= not ==.
		 */
		Assert(xfrmlen2 <= xfrmlen);
		pfree(val);
		val = xfrmstr;
	}

	return val;
}

/*
 * Do convert_to_scalar()'s work for any bytea data type.
 *
 * Very similar to convert_string_to_scalar except we can't assume
 * null-termination and therefore pass explicit lengths around.
 *
 * Also, assumptions about likely "normal" ranges of characters have been
 * removed - a data range of 0..255 is always used, for now.  (Perhaps
 * someday we will add information about actual byte data range to
 * pg_statistic.)
 */
static void
convert_bytea_to_scalar(Datum value,
						double *scaledvalue,
						Datum lobound,
						double *scaledlobound,
						Datum hibound,
						double *scaledhibound)
{
	bytea	   *valuep = DatumGetByteaPP(value);
	bytea	   *loboundp = DatumGetByteaPP(lobound);
	bytea	   *hiboundp = DatumGetByteaPP(hibound);
	int			rangelo,
				rangehi,
				valuelen = VARSIZE_ANY_EXHDR(valuep),
				loboundlen = VARSIZE_ANY_EXHDR(loboundp),
				hiboundlen = VARSIZE_ANY_EXHDR(hiboundp),
				i,
				minlen;
	unsigned char *valstr = (unsigned char *) VARDATA_ANY(valuep);
	unsigned char *lostr = (unsigned char *) VARDATA_ANY(loboundp);
	unsigned char *histr = (unsigned char *) VARDATA_ANY(hiboundp);

	/*
	 * Assume bytea data is uniformly distributed across all byte values.
	 */
	rangelo = 0;
	rangehi = 255;

	/*
	 * Now strip any common prefix of the three strings.
	 */
	minlen = Min(Min(valuelen, loboundlen), hiboundlen);
	for (i = 0; i < minlen; i++)
	{
		if (*lostr != *histr || *lostr != *valstr)
			break;
		lostr++, histr++, valstr++;
		loboundlen--, hiboundlen--, valuelen--;
	}

	/*
	 * Now we can do the conversions.
	 */
	*scaledvalue = convert_one_bytea_to_scalar(valstr, valuelen, rangelo, rangehi);
	*scaledlobound = convert_one_bytea_to_scalar(lostr, loboundlen, rangelo, rangehi);
	*scaledhibound = convert_one_bytea_to_scalar(histr, hiboundlen, rangelo, rangehi);
}

static double
convert_one_bytea_to_scalar(unsigned char *value, int valuelen,
							int rangelo, int rangehi)
{
	double		num,
				denom,
				base;

	if (valuelen <= 0)
		return 0.0;				/* empty string has scalar value 0 */

	/*
	 * Since base is 256, need not consider more than about 10 chars (even
	 * this many seems like overkill)
	 */
	if (valuelen > 10)
		valuelen = 10;

	/* Convert initial characters to fraction */
	base = rangehi - rangelo + 1;
	num = 0.0;
	denom = base;
	while (valuelen-- > 0)
	{
		int			ch = *value++;

		if (ch < rangelo)
			ch = rangelo - 1;
		else if (ch > rangehi)
			ch = rangehi + 1;
		num += ((double) (ch - rangelo)) / denom;
		denom *= base;
	}

	return num;
}

/*
 * Do convert_to_scalar()'s work for any timevalue data type.
 *
 * On failure (e.g., unsupported typid), set *failure to true;
 * otherwise, that variable is not changed.
 */
static double
convert_timevalue_to_scalar(Datum value, Oid typid, bool *failure)
{
	switch (typid)
	{
		case TIMESTAMPOID:
			return DatumGetTimestamp(value);
		case TIMESTAMPTZOID:
			return DatumGetTimestampTz(value);
		case DATEOID:
			return date2timestamp_no_overflow(DatumGetDateADT(value));
		case INTERVALOID:
			{
				Interval   *interval = DatumGetIntervalP(value);

				/*
				 * Convert the month part of Interval to days using assumed
				 * average month length of 365.25/12.0 days.  Not too
				 * accurate, but plenty good enough for our purposes.
				 *
				 * This also works for infinite intervals, which just have all
				 * fields set to INT_MIN/INT_MAX, and so will produce a result
				 * smaller/larger than any finite interval.
				 */
				return interval->time + interval->day * (double) USECS_PER_DAY +
					interval->month * ((DAYS_PER_YEAR / (double) MONTHS_PER_YEAR) * USECS_PER_DAY);
			}
		case TIMEOID:
			return DatumGetTimeADT(value);
		case TIMETZOID:
			{
				TimeTzADT  *timetz = DatumGetTimeTzADTP(value);

				/* use GMT-equivalent time */
				return (double) (timetz->time + (timetz->zone * 1000000.0));
			}
	}

	*failure = true;
	return 0;
}


/*
 * get_restriction_variable
 *		Examine the args of a restriction clause to see if it's of the
 *		form (variable op pseudoconstant) or (pseudoconstant op variable),
 *		where "variable" could be either a Var or an expression in vars of a
 *		single relation.  If so, extract information about the variable,
 *		and also indicate which side it was on and the other argument.
 *
 * Inputs:
 *	root: the planner info
 *	args: clause argument list
 *	varRelid: see specs for restriction selectivity functions
 *
 * Outputs: (these are valid only if true is returned)
 *	*vardata: gets information about variable (see examine_variable)
 *	*other: gets other clause argument, aggressively reduced to a constant
 *	*varonleft: set true if variable is on the left, false if on the right
 *
 * Returns true if a variable is identified, otherwise false.
 *
 * Note: if there are Vars on both sides of the clause, we must fail, because
 * callers are expecting that the other side will act like a pseudoconstant.
 */
bool
get_restriction_variable(PlannerInfo *root, List *args, int varRelid,
						 VariableStatData *vardata, Node **other,
						 bool *varonleft)
{
	Node	   *left,
			   *right;
	VariableStatData rdata;

	/* Fail if not a binary opclause (probably shouldn't happen) */
	if (list_length(args) != 2)
		return false;

	left = (Node *) linitial(args);
	right = (Node *) lsecond(args);

	/*
	 * Examine both sides.  Note that when varRelid is nonzero, Vars of other
	 * relations will be treated as pseudoconstants.
	 */
	examine_variable(root, left, varRelid, vardata);
	examine_variable(root, right, varRelid, &rdata);

	/*
	 * If one side is a variable and the other not, we win.
	 */
	if (vardata->rel && rdata.rel == NULL)
	{
		*varonleft = true;
		*other = estimate_expression_value(root, rdata.var);
		/* Assume we need no ReleaseVariableStats(rdata) here */
		return true;
	}

	if (vardata->rel == NULL && rdata.rel)
	{
		*varonleft = false;
		*other = estimate_expression_value(root, vardata->var);
		/* Assume we need no ReleaseVariableStats(*vardata) here */
		*vardata = rdata;
		return true;
	}

	/* Oops, clause has wrong structure (probably var op var) */
	ReleaseVariableStats(*vardata);
	ReleaseVariableStats(rdata);

	return false;
}

/*
 * get_join_variables
 *		Apply examine_variable() to each side of a join clause.
 *		Also, attempt to identify whether the join clause has the same
 *		or reversed sense compared to the SpecialJoinInfo.
 *
 * We consider the join clause "normal" if it is "lhs_var OP rhs_var",
 * or "reversed" if it is "rhs_var OP lhs_var".  In complicated cases
 * where we can't tell for sure, we default to assuming it's normal.
 */
void
get_join_variables(PlannerInfo *root, List *args, SpecialJoinInfo *sjinfo,
				   VariableStatData *vardata1, VariableStatData *vardata2,
				   bool *join_is_reversed)
{
	Node	   *left,
			   *right;

	if (list_length(args) != 2)
		elog(ERROR, "join operator should take two arguments");

	left = (Node *) linitial(args);
	right = (Node *) lsecond(args);

	examine_variable(root, left, 0, vardata1);
	examine_variable(root, right, 0, vardata2);

	if (vardata1->rel &&
		bms_is_subset(vardata1->rel->relids, sjinfo->syn_righthand))
		*join_is_reversed = true;	/* var1 is on RHS */
	else if (vardata2->rel &&
			 bms_is_subset(vardata2->rel->relids, sjinfo->syn_lefthand))
		*join_is_reversed = true;	/* var2 is on LHS */
	else
		*join_is_reversed = false;
}

/* statext_expressions_load copies the tuple, so just pfree it. */
static void
ReleaseDummy(HeapTuple tuple)
{
	pfree(tuple);
}

/*
 * examine_variable
 *		Try to look up statistical data about an expression.
 *		Fill in a VariableStatData struct to describe the expression.
 *
 * Inputs:
 *	root: the planner info
 *	node: the expression tree to examine
 *	varRelid: see specs for restriction selectivity functions
 *
 * Outputs: *vardata is filled as follows:
 *	var: the input expression (with any phvs or binary relabeling stripped,
 *		if it is or contains a variable; but otherwise unchanged)
 *	rel: RelOptInfo for relation containing variable; NULL if expression
 *		contains no Vars (NOTE this could point to a RelOptInfo of a
 *		subquery, not one in the current query).
 *	statsTuple: the pg_statistic entry for the variable, if one exists;
 *		otherwise NULL.
 *	freefunc: pointer to a function to release statsTuple with.
 *	vartype: exposed type of the expression; this should always match
 *		the declared input type of the operator we are estimating for.
 *	atttype, atttypmod: actual type/typmod of the "var" expression.  This is
 *		commonly the same as the exposed type of the variable argument,
 *		but can be different in binary-compatible-type cases.
 *	isunique: true if we were able to match the var to a unique index, a
 *		single-column DISTINCT or GROUP-BY clause, implying its values are
 *		unique for this query.  (Caution: this should be trusted for
 *		statistical purposes only, since we do not check indimmediate nor
 *		verify that the exact same definition of equality applies.)
 *	acl_ok: true if current user has permission to read all table rows from
 *		the column(s) underlying the pg_statistic entry.  This is consulted by
 *		statistic_proc_security_check().
 *
 * Caller is responsible for doing ReleaseVariableStats() before exiting.
 */
void
examine_variable(PlannerInfo *root, Node *node, int varRelid,
				 VariableStatData *vardata)
{
	Node	   *basenode;
	Relids		varnos;
	Relids		basevarnos;
	RelOptInfo *onerel;

	/* Make sure we don't return dangling pointers in vardata */
	MemSet(vardata, 0, sizeof(VariableStatData));

	/* Save the exposed type of the expression */
	vardata->vartype = exprType(node);

	/*
	 * PlaceHolderVars are transparent for the purpose of statistics lookup;
	 * they do not alter the value distribution of the underlying expression.
	 * However, they can obscure the structure, preventing us from recognizing
	 * matches to base columns, index expressions, or extended statistics.  So
	 * strip them out first.
	 */
	basenode = strip_all_phvs_deep(root, node);

	/*
	 * Look inside any binary-compatible relabeling.  We need to handle nested
	 * RelabelType nodes here, because the prior stripping of PlaceHolderVars
	 * may have brought separate RelabelTypes into adjacency.
	 */
	while (IsA(basenode, RelabelType))
		basenode = (Node *) ((RelabelType *) basenode)->arg;

	/* Fast path for a simple Var */
	if (IsA(basenode, Var) &&
		(varRelid == 0 || varRelid == ((Var *) basenode)->varno))
	{
		Var		   *var = (Var *) basenode;

		/* Set up result fields other than the stats tuple */
		vardata->var = basenode;	/* return Var without phvs or relabeling */
		vardata->rel = find_base_rel(root, var->varno);
		vardata->atttype = var->vartype;
		vardata->atttypmod = var->vartypmod;
		vardata->isunique = has_unique_index(vardata->rel, var->varattno);

		/* Try to locate some stats */
		examine_simple_variable(root, var, vardata);

		return;
	}

	/*
	 * Okay, it's a more complicated expression.  Determine variable
	 * membership.  Note that when varRelid isn't zero, only vars of that
	 * relation are considered "real" vars.
	 */
	varnos = pull_varnos(root, basenode);
	basevarnos = bms_difference(varnos, root->outer_join_rels);

	onerel = NULL;

	if (bms_is_empty(basevarnos))
	{
		/* No Vars at all ... must be pseudo-constant clause */
	}
	else
	{
		int			relid;

		/* Check if the expression is in vars of a single base relation */
		if (bms_get_singleton_member(basevarnos, &relid))
		{
			if (varRelid == 0 || varRelid == relid)
			{
				onerel = find_base_rel(root, relid);
				vardata->rel = onerel;
				node = basenode;	/* strip any phvs or relabeling */
			}
			/* else treat it as a constant */
		}
		else
		{
			/* varnos has multiple relids */
			if (varRelid == 0)
			{
				/* treat it as a variable of a join relation */
				vardata->rel = find_join_rel(root, varnos);
				node = basenode;	/* strip any phvs or relabeling */
			}
			else if (bms_is_member(varRelid, varnos))
			{
				/* ignore the vars belonging to other relations */
				vardata->rel = find_base_rel(root, varRelid);
				node = basenode;	/* strip any phvs or relabeling */
				/* note: no point in expressional-index search here */
			}
			/* else treat it as a constant */
		}
	}

	bms_free(basevarnos);

	vardata->var = node;
	vardata->atttype = exprType(node);
	vardata->atttypmod = exprTypmod(node);

	if (onerel)
	{
		/*
		 * We have an expression in vars of a single relation.  Try to match
		 * it to expressional index columns, in hopes of finding some
		 * statistics.
		 *
		 * Note that we consider all index columns including INCLUDE columns,
		 * since there could be stats for such columns.  But the test for
		 * uniqueness needs to be warier.
		 *
		 * XXX it's conceivable that there are multiple matches with different
		 * index opfamilies; if so, we need to pick one that matches the
		 * operator we are estimating for.  FIXME later.
		 */
		ListCell   *ilist;
		ListCell   *slist;

		/*
		 * The nullingrels bits within the expression could prevent us from
		 * matching it to expressional index columns or to the expressions in
		 * extended statistics.  So strip them out first.
		 */
		if (bms_overlap(varnos, root->outer_join_rels))
			node = remove_nulling_relids(node, root->outer_join_rels, NULL);

		foreach(ilist, onerel->indexlist)
		{
			IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);
			ListCell   *indexpr_item;
			int			pos;

			indexpr_item = list_head(index->indexprs);
			if (indexpr_item == NULL)
				continue;		/* no expressions here... */

			for (pos = 0; pos < index->ncolumns; pos++)
			{
				if (index->indexkeys[pos] == 0)
				{
					Node	   *indexkey;

					if (indexpr_item == NULL)
						elog(ERROR, "too few entries in indexprs list");
					indexkey = (Node *) lfirst(indexpr_item);
					if (indexkey && IsA(indexkey, RelabelType))
						indexkey = (Node *) ((RelabelType *) indexkey)->arg;
					if (equal(node, indexkey))
					{
						/*
						 * Found a match ... is it a unique index? Tests here
						 * should match has_unique_index().
						 */
						if (index->unique &&
							index->nkeycolumns == 1 &&
							pos == 0 &&
							(index->indpred == NIL || index->predOK))
							vardata->isunique = true;

						/*
						 * Has it got stats?  We only consider stats for
						 * non-partial indexes, since partial indexes probably
						 * don't reflect whole-relation statistics; the above
						 * check for uniqueness is the only info we take from
						 * a partial index.
						 *
						 * An index stats hook, however, must make its own
						 * decisions about what to do with partial indexes.
						 */
						if (get_index_stats_hook &&
							(*get_index_stats_hook) (root, index->indexoid,
													 pos + 1, vardata))
						{
							/*
							 * The hook took control of acquiring a stats
							 * tuple.  If it did supply a tuple, it'd better
							 * have supplied a freefunc.
							 */
							if (HeapTupleIsValid(vardata->statsTuple) &&
								!vardata->freefunc)
								elog(ERROR, "no function provided to release variable stats with");
						}
						else if (index->indpred == NIL)
						{
							vardata->statsTuple =
								SearchSysCache3(STATRELATTINH,
												ObjectIdGetDatum(index->indexoid),
												Int16GetDatum(pos + 1),
												BoolGetDatum(false));
							vardata->freefunc = ReleaseSysCache;

							if (HeapTupleIsValid(vardata->statsTuple))
							{
								/*
								 * Test if user has permission to access all
								 * rows from the index's table.
								 *
								 * For simplicity, we insist on the whole
								 * table being selectable, rather than trying
								 * to identify which column(s) the index
								 * depends on.
								 *
								 * Note that for an inheritance child,
								 * permissions are checked on the inheritance
								 * root parent, and whole-table select
								 * privilege on the parent doesn't quite
								 * guarantee that the user could read all
								 * columns of the child.  But in practice it's
								 * unlikely that any interesting security
								 * violation could result from allowing access
								 * to the expression index's stats, so we
								 * allow it anyway.  See similar code in
								 * examine_simple_variable() for additional
								 * comments.
								 */
								vardata->acl_ok =
									all_rows_selectable(root,
														index->rel->relid,
														NULL);
							}
							else
							{
								/* suppress leakproofness checks later */
								vardata->acl_ok = true;
							}
						}
						if (vardata->statsTuple)
							break;
					}
					indexpr_item = lnext(index->indexprs, indexpr_item);
				}
			}
			if (vardata->statsTuple)
				break;
		}

		/*
		 * Search extended statistics for one with a matching expression.
		 * There might be multiple ones, so just grab the first one. In the
		 * future, we might consider the statistics target (and pick the most
		 * accurate statistics) and maybe some other parameters.
		 */
		foreach(slist, onerel->statlist)
		{
			StatisticExtInfo *info = (StatisticExtInfo *) lfirst(slist);
			RangeTblEntry *rte = planner_rt_fetch(onerel->relid, root);
			ListCell   *expr_item;
			int			pos;

			/*
			 * Stop once we've found statistics for the expression (either
			 * from extended stats, or for an index in the preceding loop).
			 */
			if (vardata->statsTuple)
				break;

			/* skip stats without per-expression stats */
			if (info->kind != STATS_EXT_EXPRESSIONS)
				continue;

			/* skip stats with mismatching stxdinherit value */
			if (info->inherit != rte->inh)
				continue;

			pos = 0;
			foreach(expr_item, info->exprs)
			{
				Node	   *expr = (Node *) lfirst(expr_item);

				Assert(expr);

				/* strip RelabelType before comparing it */
				if (expr && IsA(expr, RelabelType))
					expr = (Node *) ((RelabelType *) expr)->arg;

				/* found a match, see if we can extract pg_statistic row */
				if (equal(node, expr))
				{
					/*
					 * XXX Not sure if we should cache the tuple somewhere.
					 * Now we just create a new copy every time.
					 */
					vardata->statsTuple =
						statext_expressions_load(info->statOid, rte->inh, pos);

					/* Nothing to release if no data found */
					if (vardata->statsTuple != NULL)
					{
						vardata->freefunc = ReleaseDummy;
					}

					/*
					 * Test if user has permission to access all rows from the
					 * table.
					 *
					 * For simplicity, we insist on the whole table being
					 * selectable, rather than trying to identify which
					 * column(s) the statistics object depends on.
					 *
					 * Note that for an inheritance child, permissions are
					 * checked on the inheritance root parent, and whole-table
					 * select privilege on the parent doesn't quite guarantee
					 * that the user could read all columns of the child.  But
					 * in practice it's unlikely that any interesting security
					 * violation could result from allowing access to the
					 * expression stats, so we allow it anyway.  See similar
					 * code in examine_simple_variable() for additional
					 * comments.
					 */
					vardata->acl_ok = all_rows_selectable(root,
														  onerel->relid,
														  NULL);

					break;
				}

				pos++;
			}
		}
	}

	bms_free(varnos);
}

/*
 * strip_all_phvs_deep
 *		Deeply strip all PlaceHolderVars in an expression.
 *
 * As a performance optimization, we first use a lightweight walker to check
 * for the presence of any PlaceHolderVars.  The expensive mutator is invoked
 * only if a PlaceHolderVar is found, avoiding unnecessary memory allocation
 * and tree copying in the common case where no PlaceHolderVars are present.
 */
static Node *
strip_all_phvs_deep(PlannerInfo *root, Node *node)
{
	/* If there are no PHVs anywhere, we needn't work hard */
	if (root->glob->lastPHId == 0)
		return node;

	if (!contain_placeholder_walker(node, NULL))
		return node;
	return strip_all_phvs_mutator(node, NULL);
}

/*
 * contain_placeholder_walker
 *		Lightweight walker to check if an expression contains any
 *		PlaceHolderVars
 */
static bool
contain_placeholder_walker(Node *node, void *context)
{
	if (node == NULL)
		return false;
	if (IsA(node, PlaceHolderVar))
		return true;

	return expression_tree_walker(node, contain_placeholder_walker, context);
}

/*
 * strip_all_phvs_mutator
 *		Mutator to deeply strip all PlaceHolderVars
 */
static Node *
strip_all_phvs_mutator(Node *node, void *context)
{
	if (node == NULL)
		return NULL;
	if (IsA(node, PlaceHolderVar))
	{
		/* Strip it and recurse into its contained expression */
		PlaceHolderVar *phv = (PlaceHolderVar *) node;

		return strip_all_phvs_mutator((Node *) phv->phexpr, context);
	}

	return expression_tree_mutator(node, strip_all_phvs_mutator, context);
}

/*
 * examine_simple_variable
 *		Handle a simple Var for examine_variable
 *
 * This is split out as a subroutine so that we can recurse to deal with
 * Vars referencing subqueries (either sub-SELECT-in-FROM or CTE style).
 *
 * We already filled in all the fields of *vardata except for the stats tuple.
 */
static void
examine_simple_variable(PlannerInfo *root, Var *var,
						VariableStatData *vardata)
{
	RangeTblEntry *rte = root->simple_rte_array[var->varno];

	Assert(IsA(rte, RangeTblEntry));

	if (get_relation_stats_hook &&
		(*get_relation_stats_hook) (root, rte, var->varattno, vardata))
	{
		/*
		 * The hook took control of acquiring a stats tuple.  If it did supply
		 * a tuple, it'd better have supplied a freefunc.
		 */
		if (HeapTupleIsValid(vardata->statsTuple) &&
			!vardata->freefunc)
			elog(ERROR, "no function provided to release variable stats with");
	}
	else if (rte->rtekind == RTE_RELATION)
	{
		/*
		 * Plain table or parent of an inheritance appendrel, so look up the
		 * column in pg_statistic
		 */
		vardata->statsTuple = SearchSysCache3(STATRELATTINH,
											  ObjectIdGetDatum(rte->relid),
											  Int16GetDatum(var->varattno),
											  BoolGetDatum(rte->inh));
		vardata->freefunc = ReleaseSysCache;

		if (HeapTupleIsValid(vardata->statsTuple))
		{
			/*
			 * Test if user has permission to read all rows from this column.
			 *
			 * This requires that the user has the appropriate SELECT
			 * privileges and that there are no securityQuals from security
			 * barrier views or RLS policies.  If that's not the case, then we
			 * only permit leakproof functions to be passed pg_statistic data
			 * in vardata, otherwise the functions might reveal data that the
			 * user doesn't have permission to see --- see
			 * statistic_proc_security_check().
			 */
			vardata->acl_ok =
				all_rows_selectable(root, var->varno,
									bms_make_singleton(var->varattno - FirstLowInvalidHeapAttributeNumber));
		}
		else
		{
			/* suppress any possible leakproofness checks later */
			vardata->acl_ok = true;
		}
	}
	else if ((rte->rtekind == RTE_SUBQUERY && !rte->inh) ||
			 (rte->rtekind == RTE_CTE && !rte->self_reference))
	{
		/*
		 * Plain subquery (not one that was converted to an appendrel) or
		 * non-recursive CTE.  In either case, we can try to find out what the
		 * Var refers to within the subquery.  We skip this for appendrel and
		 * recursive-CTE cases because any column stats we did find would
		 * likely not be very relevant.
		 */
		PlannerInfo *subroot;
		Query	   *subquery;
		List	   *subtlist;
		TargetEntry *ste;

		/*
		 * Punt if it's a whole-row var rather than a plain column reference.
		 */
		if (var->varattno == InvalidAttrNumber)
			return;

		/*
		 * Otherwise, find the subquery's planner subroot.
		 */
		if (rte->rtekind == RTE_SUBQUERY)
		{
			RelOptInfo *rel;

			/*
			 * Fetch RelOptInfo for subquery.  Note that we don't change the
			 * rel returned in vardata, since caller expects it to be a rel of
			 * the caller's query level.  Because we might already be
			 * recursing, we can't use that rel pointer either, but have to
			 * look up the Var's rel afresh.
			 */
			rel = find_base_rel(root, var->varno);

			subroot = rel->subroot;
		}
		else
		{
			/* CTE case is more difficult */
			PlannerInfo *cteroot;
			Index		levelsup;
			int			ndx;
			int			plan_id;
			ListCell   *lc;

			/*
			 * Find the referenced CTE, and locate the subroot previously made
			 * for it.
			 */
			levelsup = rte->ctelevelsup;
			cteroot = root;
			while (levelsup-- > 0)
			{
				cteroot = cteroot->parent_root;
				if (!cteroot)	/* shouldn't happen */
					elog(ERROR, "bad levelsup for CTE \"%s\"", rte->ctename);
			}

			/*
			 * Note: cte_plan_ids can be shorter than cteList, if we are still
			 * working on planning the CTEs (ie, this is a side-reference from
			 * another CTE).  So we mustn't use forboth here.
			 */
			ndx = 0;
			foreach(lc, cteroot->parse->cteList)
			{
				CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);

				if (strcmp(cte->ctename, rte->ctename) == 0)
					break;
				ndx++;
			}
			if (lc == NULL)		/* shouldn't happen */
				elog(ERROR, "could not find CTE \"%s\"", rte->ctename);
			if (ndx >= list_length(cteroot->cte_plan_ids))
				elog(ERROR, "could not find plan for CTE \"%s\"", rte->ctename);
			plan_id = list_nth_int(cteroot->cte_plan_ids, ndx);
			if (plan_id <= 0)
				elog(ERROR, "no plan was made for CTE \"%s\"", rte->ctename);
			subroot = list_nth(root->glob->subroots, plan_id - 1);
		}

		/* If the subquery hasn't been planned yet, we have to punt */
		if (subroot == NULL)
			return;
		Assert(IsA(subroot, PlannerInfo));

		/*
		 * We must use the subquery parsetree as mangled by the planner, not
		 * the raw version from the RTE, because we need a Var that will refer
		 * to the subroot's live RelOptInfos.  For instance, if any subquery
		 * pullup happened during planning, Vars in the targetlist might have
		 * gotten replaced, and we need to see the replacement expressions.
		 */
		subquery = subroot->parse;
		Assert(IsA(subquery, Query));

		/*
		 * Punt if subquery uses set operations or grouping sets, as these
		 * will mash underlying columns' stats beyond recognition.  (Set ops
		 * are particularly nasty; if we forged ahead, we would return stats
		 * relevant to only the leftmost subselect...)	DISTINCT is also
		 * problematic, but we check that later because there is a possibility
		 * of learning something even with it.
		 */
		if (subquery->setOperations ||
			subquery->groupingSets)
			return;

		/* Get the subquery output expression referenced by the upper Var */
		if (subquery->returningList)
			subtlist = subquery->returningList;
		else
			subtlist = subquery->targetList;
		ste = get_tle_by_resno(subtlist, var->varattno);
		if (ste == NULL || ste->resjunk)
			elog(ERROR, "subquery %s does not have attribute %d",
				 rte->eref->aliasname, var->varattno);
		var = (Var *) ste->expr;

		/*
		 * If subquery uses DISTINCT, we can't make use of any stats for the
		 * variable ... but, if it's the only DISTINCT column, we are entitled
		 * to consider it unique.  We do the test this way so that it works
		 * for cases involving DISTINCT ON.
		 */
		if (subquery->distinctClause)
		{
			if (list_length(subquery->distinctClause) == 1 &&
				targetIsInSortList(ste, InvalidOid, subquery->distinctClause))
				vardata->isunique = true;
			/* cannot go further */
			return;
		}

		/* The same idea as with DISTINCT clause works for a GROUP-BY too */
		if (subquery->groupClause)
		{
			if (list_length(subquery->groupClause) == 1 &&
				targetIsInSortList(ste, InvalidOid, subquery->groupClause))
				vardata->isunique = true;
			/* cannot go further */
			return;
		}

		/*
		 * If the sub-query originated from a view with the security_barrier
		 * attribute, we must not look at the variable's statistics, though it
		 * seems all right to notice the existence of a DISTINCT clause. So
		 * stop here.
		 *
		 * This is probably a harsher restriction than necessary; it's
		 * certainly OK for the selectivity estimator (which is a C function,
		 * and therefore omnipotent anyway) to look at the statistics.  But
		 * many selectivity estimators will happily *invoke the operator
		 * function* to try to work out a good estimate - and that's not OK.
		 * So for now, don't dig down for stats.
		 */
		if (rte->security_barrier)
			return;

		/* Can only handle a simple Var of subquery's query level */
		if (var && IsA(var, Var) &&
			var->varlevelsup == 0)
		{
			/*
			 * OK, recurse into the subquery.  Note that the original setting
			 * of vardata->isunique (which will surely be false) is left
			 * unchanged in this situation.  That's what we want, since even
			 * if the underlying column is unique, the subquery may have
			 * joined to other tables in a way that creates duplicates.
			 */
			examine_simple_variable(subroot, var, vardata);
		}
	}
	else
	{
		/*
		 * Otherwise, the Var comes from a FUNCTION or VALUES RTE.  (We won't
		 * see RTE_JOIN here because join alias Vars have already been
		 * flattened.)	There's not much we can do with function outputs, but
		 * maybe someday try to be smarter about VALUES.
		 */
	}
}

/*
 * all_rows_selectable
 *		Test whether the user has permission to select all rows from a given
 *		relation.
 *
 * Inputs:
 *	root: the planner info
 *	varno: the index of the relation (assumed to be an RTE_RELATION)
 *	varattnos: the attributes for which permission is required, or NULL if
 *		whole-table access is required
 *
 * Returns true if the user has the required select permissions, and there are
 * no securityQuals from security barrier views or RLS policies.
 *
 * Note that if the relation is an inheritance child relation, securityQuals
 * and access permissions are checked against the inheritance root parent (the
 * relation actually mentioned in the query) --- see the comments in
 * expand_single_inheritance_child() for an explanation of why it has to be
 * done this way.
 *
 * If varattnos is non-NULL, its attribute numbers should be offset by
 * FirstLowInvalidHeapAttributeNumber so that system attributes can be
 * checked.  If varattnos is NULL, only table-level SELECT privileges are
 * checked, not any column-level privileges.
 *
 * Note: if the relation is accessed via a view, this function actually tests
 * whether the view owner has permission to select from the relation.  To
 * ensure that the current user has permission, it is also necessary to check
 * that the current user has permission to select from the view, which we do
 * at planner-startup --- see subquery_planner().
 *
 * This is exported so that other estimation functions can use it.
 */
bool
all_rows_selectable(PlannerInfo *root, Index varno, Bitmapset *varattnos)
{
	RelOptInfo *rel = find_base_rel_noerr(root, varno);
	RangeTblEntry *rte = planner_rt_fetch(varno, root);
	Oid			userid;
	int			varattno;

	Assert(rte->rtekind == RTE_RELATION);

	/*
	 * Determine the user ID to use for privilege checks (either the current
	 * user or the view owner, if we're accessing the table via a view).
	 *
	 * Normally the relation will have an associated RelOptInfo from which we
	 * can find the userid, but it might not if it's a RETURNING Var for an
	 * INSERT target relation.  In that case use the RTEPermissionInfo
	 * associated with the RTE.
	 *
	 * If we navigate up to a parent relation, we keep using the same userid,
	 * since it's the same in all relations of a given inheritance tree.
	 */
	if (rel)
		userid = rel->userid;
	else
	{
		RTEPermissionInfo *perminfo;

		perminfo = getRTEPermissionInfo(root->parse->rteperminfos, rte);
		userid = perminfo->checkAsUser;
	}
	if (!OidIsValid(userid))
		userid = GetUserId();

	/*
	 * Permissions and securityQuals must be checked on the table actually
	 * mentioned in the query, so if this is an inheritance child, navigate up
	 * to the inheritance root parent.  If the user can read the whole table
	 * or the required columns there, then they can read from the child table
	 * too.  For per-column checks, we must find out which of the root
	 * parent's attributes the child relation's attributes correspond to.
	 */
	if (root->append_rel_array != NULL)
	{
		AppendRelInfo *appinfo;

		appinfo = root->append_rel_array[varno];

		/*
		 * Partitions are mapped to their immediate parent, not the root
		 * parent, so must be ready to walk up multiple AppendRelInfos.  But
		 * stop if we hit a parent that is not RTE_RELATION --- that's a
		 * flattened UNION ALL subquery, not an inheritance parent.
		 */
		while (appinfo &&
			   planner_rt_fetch(appinfo->parent_relid,
								root)->rtekind == RTE_RELATION)
		{
			Bitmapset  *parent_varattnos = NULL;

			/*
			 * For each child attribute, find the corresponding parent
			 * attribute.  In rare cases, the attribute may be local to the
			 * child table, in which case, we've got to live with having no
			 * access to this column.
			 */
			varattno = -1;
			while ((varattno = bms_next_member(varattnos, varattno)) >= 0)
			{
				AttrNumber	attno;
				AttrNumber	parent_attno;

				attno = varattno + FirstLowInvalidHeapAttributeNumber;

				if (attno == InvalidAttrNumber)
				{
					/*
					 * Whole-row reference, so must map each column of the
					 * child to the parent table.
					 */
					for (attno = 1; attno <= appinfo->num_child_cols; attno++)
					{
						parent_attno = appinfo->parent_colnos[attno - 1];
						if (parent_attno == 0)
							return false;	/* attr is local to child */
						parent_varattnos =
							bms_add_member(parent_varattnos,
										   parent_attno - FirstLowInvalidHeapAttributeNumber);
					}
				}
				else
				{
					if (attno < 0)
					{
						/* System attnos are the same in all tables */
						parent_attno = attno;
					}
					else
					{
						if (attno > appinfo->num_child_cols)
							return false;	/* safety check */
						parent_attno = appinfo->parent_colnos[attno - 1];
						if (parent_attno == 0)
							return false;	/* attr is local to child */
					}
					parent_varattnos =
						bms_add_member(parent_varattnos,
									   parent_attno - FirstLowInvalidHeapAttributeNumber);
				}
			}

			/* If the parent is itself a child, continue up */
			varno = appinfo->parent_relid;
			varattnos = parent_varattnos;
			appinfo = root->append_rel_array[varno];
		}

		/* Perform the access check on this parent rel */
		rte = planner_rt_fetch(varno, root);
		Assert(rte->rtekind == RTE_RELATION);
	}

	/*
	 * For all rows to be accessible, there must be no securityQuals from
	 * security barrier views or RLS policies.
	 */
	if (rte->securityQuals != NIL)
		return false;

	/*
	 * Test for table-level SELECT privilege.
	 *
	 * If varattnos is non-NULL, this is sufficient to give access to all
	 * requested attributes, even for a child table, since we have verified
	 * that all required child columns have matching parent columns.
	 *
	 * If varattnos is NULL (whole-table access requested), this doesn't
	 * necessarily guarantee that the user can read all columns of a child
	 * table, but we allow it anyway (see comments in examine_variable()) and
	 * don't bother checking any column privileges.
	 */
	if (pg_class_aclcheck(rte->relid, userid, ACL_SELECT) == ACLCHECK_OK)
		return true;

	if (varattnos == NULL)
		return false;			/* whole-table access requested */

	/*
	 * Don't have table-level SELECT privilege, so check per-column
	 * privileges.
	 */
	varattno = -1;
	while ((varattno = bms_next_member(varattnos, varattno)) >= 0)
	{
		AttrNumber	attno = varattno + FirstLowInvalidHeapAttributeNumber;

		if (attno == InvalidAttrNumber)
		{
			/* Whole-row reference, so must have access to all columns */
			if (pg_attribute_aclcheck_all(rte->relid, userid, ACL_SELECT,
										  ACLMASK_ALL) != ACLCHECK_OK)
				return false;
		}
		else
		{
			if (pg_attribute_aclcheck(rte->relid, attno, userid,
									  ACL_SELECT) != ACLCHECK_OK)
				return false;
		}
	}

	/* If we reach here, have all required column privileges */
	return true;
}

/*
 * examine_indexcol_variable
 *		Try to look up statistical data about an index column/expression.
 *		Fill in a VariableStatData struct to describe the column.
 *
 * Inputs:
 *	root: the planner info
 *	index: the index whose column we're interested in
 *	indexcol: 0-based index column number (subscripts index->indexkeys[])
 *
 * Outputs: *vardata is filled as follows:
 *	var: the input expression (with any binary relabeling stripped, if
 *		it is or contains a variable; but otherwise the type is preserved)
 *	rel: RelOptInfo for table relation containing variable.
 *	statsTuple: the pg_statistic entry for the variable, if one exists;
 *		otherwise NULL.
 *	freefunc: pointer to a function to release statsTuple with.
 *
 * Caller is responsible for doing ReleaseVariableStats() before exiting.
 */
static void
examine_indexcol_variable(PlannerInfo *root, IndexOptInfo *index,
						  int indexcol, VariableStatData *vardata)
{
	AttrNumber	colnum;
	Oid			relid;

	if (index->indexkeys[indexcol] != 0)
	{
		/* Simple variable --- look to stats for the underlying table */
		RangeTblEntry *rte = planner_rt_fetch(index->rel->relid, root);

		Assert(rte->rtekind == RTE_RELATION);
		relid = rte->relid;
		Assert(relid != InvalidOid);
		colnum = index->indexkeys[indexcol];
		vardata->rel = index->rel;

		if (get_relation_stats_hook &&
			(*get_relation_stats_hook) (root, rte, colnum, vardata))
		{
			/*
			 * The hook took control of acquiring a stats tuple.  If it did
			 * supply a tuple, it'd better have supplied a freefunc.
			 */
			if (HeapTupleIsValid(vardata->statsTuple) &&
				!vardata->freefunc)
				elog(ERROR, "no function provided to release variable stats with");
		}
		else
		{
			vardata->statsTuple = SearchSysCache3(STATRELATTINH,
												  ObjectIdGetDatum(relid),
												  Int16GetDatum(colnum),
												  BoolGetDatum(rte->inh));
			vardata->freefunc = ReleaseSysCache;
		}
	}
	else
	{
		/* Expression --- maybe there are stats for the index itself */
		relid = index->indexoid;
		colnum = indexcol + 1;

		if (get_index_stats_hook &&
			(*get_index_stats_hook) (root, relid, colnum, vardata))
		{
			/*
			 * The hook took control of acquiring a stats tuple.  If it did
			 * supply a tuple, it'd better have supplied a freefunc.
			 */
			if (HeapTupleIsValid(vardata->statsTuple) &&
				!vardata->freefunc)
				elog(ERROR, "no function provided to release variable stats with");
		}
		else
		{
			vardata->statsTuple = SearchSysCache3(STATRELATTINH,
												  ObjectIdGetDatum(relid),
												  Int16GetDatum(colnum),
												  BoolGetDatum(false));
			vardata->freefunc = ReleaseSysCache;
		}
	}
}

/*
 * Check whether it is permitted to call func_oid passing some of the
 * pg_statistic data in vardata.  We allow this if either of the following
 * conditions is met: (1) the user has SELECT privileges on the table or
 * column underlying the pg_statistic data and there are no securityQuals from
 * security barrier views or RLS policies, or (2) the function is marked
 * leakproof.
 */
bool
statistic_proc_security_check(VariableStatData *vardata, Oid func_oid)
{
	if (vardata->acl_ok)
		return true;			/* have SELECT privs and no securityQuals */

	if (!OidIsValid(func_oid))
		return false;

	if (get_func_leakproof(func_oid))
		return true;

	ereport(DEBUG2,
			(errmsg_internal("not using statistics because function \"%s\" is not leakproof",
							 get_func_name(func_oid))));
	return false;
}

/*
 * get_variable_numdistinct
 *	  Estimate the number of distinct values of a variable.
 *
 * vardata: results of examine_variable
 * *isdefault: set to true if the result is a default rather than based on
 * anything meaningful.
 *
 * NB: be careful to produce a positive integral result, since callers may
 * compare the result to exact integer counts, or might divide by it.
 */
double
get_variable_numdistinct(VariableStatData *vardata, bool *isdefault)
{
	double		stadistinct;
	double		stanullfrac = 0.0;
	double		ntuples;

	*isdefault = false;

	/*
	 * Determine the stadistinct value to use.  There are cases where we can
	 * get an estimate even without a pg_statistic entry, or can get a better
	 * value than is in pg_statistic.  Grab stanullfrac too if we can find it
	 * (otherwise, assume no nulls, for lack of any better idea).
	 */
	if (HeapTupleIsValid(vardata->statsTuple))
	{
		/* Use the pg_statistic entry */
		Form_pg_statistic stats;

		stats = (Form_pg_statistic) GETSTRUCT(vardata->statsTuple);
		stadistinct = stats->stadistinct;
		stanullfrac = stats->stanullfrac;
	}
	else if (vardata->vartype == BOOLOID)
	{
		/*
		 * Special-case boolean columns: presumably, two distinct values.
		 *
		 * Are there any other datatypes we should wire in special estimates
		 * for?
		 */
		stadistinct = 2.0;
	}
	else if (vardata->rel && vardata->rel->rtekind == RTE_VALUES)
	{
		/*
		 * If the Var represents a column of a VALUES RTE, assume it's unique.
		 * This could of course be very wrong, but it should tend to be true
		 * in well-written queries.  We could consider examining the VALUES'
		 * contents to get some real statistics; but that only works if the
		 * entries are all constants, and it would be pretty expensive anyway.
		 */
		stadistinct = -1.0;		/* unique (and all non null) */
	}
	else
	{
		/*
		 * We don't keep statistics for system columns, but in some cases we
		 * can infer distinctness anyway.
		 */
		if (vardata->var && IsA(vardata->var, Var))
		{
			switch (((Var *) vardata->var)->varattno)
			{
				case SelfItemPointerAttributeNumber:
					stadistinct = -1.0; /* unique (and all non null) */
					break;
				case TableOidAttributeNumber:
					stadistinct = 1.0;	/* only 1 value */
					break;
				default:
					stadistinct = 0.0;	/* means "unknown" */
					break;
			}
		}
		else
			stadistinct = 0.0;	/* means "unknown" */

		/*
		 * XXX consider using estimate_num_groups on expressions?
		 */
	}

	/*
	 * If there is a unique index, DISTINCT or GROUP-BY clause for the
	 * variable, assume it is unique no matter what pg_statistic says; the
	 * statistics could be out of date, or we might have found a partial
	 * unique index that proves the var is unique for this query.  However,
	 * we'd better still believe the null-fraction statistic.
	 */
	if (vardata->isunique)
		stadistinct = -1.0 * (1.0 - stanullfrac);

	/*
	 * If we had an absolute estimate, use that.
	 */
	if (stadistinct > 0.0)
		return clamp_row_est(stadistinct);

	/*
	 * Otherwise we need to get the relation size; punt if not available.
	 */
	if (vardata->rel == NULL)
	{
		*isdefault = true;
		return DEFAULT_NUM_DISTINCT;
	}
	ntuples = vardata->rel->tuples;
	if (ntuples <= 0.0)
	{
		*isdefault = true;
		return DEFAULT_NUM_DISTINCT;
	}

	/*
	 * If we had a relative estimate, use that.
	 */
	if (stadistinct < 0.0)
		return clamp_row_est(-stadistinct * ntuples);

	/*
	 * With no data, estimate ndistinct = ntuples if the table is small, else
	 * use default.  We use DEFAULT_NUM_DISTINCT as the cutoff for "small" so
	 * that the behavior isn't discontinuous.
	 */
	if (ntuples < DEFAULT_NUM_DISTINCT)
		return clamp_row_est(ntuples);

	*isdefault = true;
	return DEFAULT_NUM_DISTINCT;
}

/*
 * get_variable_range
 *		Estimate the minimum and maximum value of the specified variable.
 *		If successful, store values in *min and *max, and return true.
 *		If no data available, return false.
 *
 * sortop is the "<" comparison operator to use.  This should generally
 * be "<" not ">", as only the former is likely to be found in pg_statistic.
 * The collation must be specified too.
 */
static bool
get_variable_range(PlannerInfo *root, VariableStatData *vardata,
				   Oid sortop, Oid collation,
				   Datum *min, Datum *max)
{
	Datum		tmin = 0;
	Datum		tmax = 0;
	bool		have_data = false;
	int16		typLen;
	bool		typByVal;
	Oid			opfuncoid;
	FmgrInfo	opproc;
	AttStatsSlot sslot;

	/*
	 * XXX It's very tempting to try to use the actual column min and max, if
	 * we can get them relatively-cheaply with an index probe.  However, since
	 * this function is called many times during join planning, that could
	 * have unpleasant effects on planning speed.  Need more investigation
	 * before enabling this.
	 */
#ifdef NOT_USED
	if (get_actual_variable_range(root, vardata, sortop, collation, min, max))
		return true;
#endif

	if (!HeapTupleIsValid(vardata->statsTuple))
	{
		/* no stats available, so default result */
		return false;
	}

	/*
	 * If we can't apply the sortop to the stats data, just fail.  In
	 * principle, if there's a histogram and no MCVs, we could return the
	 * histogram endpoints without ever applying the sortop ... but it's
	 * probably not worth trying, because whatever the caller wants to do with
	 * the endpoints would likely fail the security check too.
	 */
	if (!statistic_proc_security_check(vardata,
									   (opfuncoid = get_opcode(sortop))))
		return false;

	opproc.fn_oid = InvalidOid; /* mark this as not looked up yet */

	get_typlenbyval(vardata->atttype, &typLen, &typByVal);

	/*
	 * If there is a histogram with the ordering we want, grab the first and
	 * last values.
	 */
	if (get_attstatsslot(&sslot, vardata->statsTuple,
						 STATISTIC_KIND_HISTOGRAM, sortop,
						 ATTSTATSSLOT_VALUES))
	{
		if (sslot.stacoll == collation && sslot.nvalues > 0)
		{
			tmin = datumCopy(sslot.values[0], typByVal, typLen);
			tmax = datumCopy(sslot.values[sslot.nvalues - 1], typByVal, typLen);
			have_data = true;
		}
		free_attstatsslot(&sslot);
	}

	/*
	 * Otherwise, if there is a histogram with some other ordering, scan it
	 * and get the min and max values according to the ordering we want.  This
	 * of course may not find values that are really extremal according to our
	 * ordering, but it beats ignoring available data.
	 */
	if (!have_data &&
		get_attstatsslot(&sslot, vardata->statsTuple,
						 STATISTIC_KIND_HISTOGRAM, InvalidOid,
						 ATTSTATSSLOT_VALUES))
	{
		get_stats_slot_range(&sslot, opfuncoid, &opproc,
							 collation, typLen, typByVal,
							 &tmin, &tmax, &have_data);
		free_attstatsslot(&sslot);
	}

	/*
	 * If we have most-common-values info, look for extreme MCVs.  This is
	 * needed even if we also have a histogram, since the histogram excludes
	 * the MCVs.  However, if we *only* have MCVs and no histogram, we should
	 * be pretty wary of deciding that that is a full representation of the
	 * data.  Proceed only if the MCVs represent the whole table (to within
	 * roundoff error).
	 */
	if (get_attstatsslot(&sslot, vardata->statsTuple,
						 STATISTIC_KIND_MCV, InvalidOid,
						 have_data ? ATTSTATSSLOT_VALUES :
						 (ATTSTATSSLOT_VALUES | ATTSTATSSLOT_NUMBERS)))
	{
		bool		use_mcvs = have_data;

		if (!have_data)
		{
			double		sumcommon = 0.0;
			double		nullfrac;
			int			i;

			for (i = 0; i < sslot.nnumbers; i++)
				sumcommon += sslot.numbers[i];
			nullfrac = ((Form_pg_statistic) GETSTRUCT(vardata->statsTuple))->stanullfrac;
			if (sumcommon + nullfrac > 0.99999)
				use_mcvs = true;
		}

		if (use_mcvs)
			get_stats_slot_range(&sslot, opfuncoid, &opproc,
								 collation, typLen, typByVal,
								 &tmin, &tmax, &have_data);
		free_attstatsslot(&sslot);
	}

	*min = tmin;
	*max = tmax;
	return have_data;
}

/*
 * get_stats_slot_range: scan sslot for min/max values
 *
 * Subroutine for get_variable_range: update min/max/have_data according
 * to what we find in the statistics array.
 */
static void
get_stats_slot_range(AttStatsSlot *sslot, Oid opfuncoid, FmgrInfo *opproc,
					 Oid collation, int16 typLen, bool typByVal,
					 Datum *min, Datum *max, bool *p_have_data)
{
	Datum		tmin = *min;
	Datum		tmax = *max;
	bool		have_data = *p_have_data;
	bool		found_tmin = false;
	bool		found_tmax = false;

	/* Look up the comparison function, if we didn't already do so */
	if (opproc->fn_oid != opfuncoid)
		fmgr_info(opfuncoid, opproc);

	/* Scan all the slot's values */
	for (int i = 0; i < sslot->nvalues; i++)
	{
		if (!have_data)
		{
			tmin = tmax = sslot->values[i];
			found_tmin = found_tmax = true;
			*p_have_data = have_data = true;
			continue;
		}
		if (DatumGetBool(FunctionCall2Coll(opproc,
										   collation,
										   sslot->values[i], tmin)))
		{
			tmin = sslot->values[i];
			found_tmin = true;
		}
		if (DatumGetBool(FunctionCall2Coll(opproc,
										   collation,
										   tmax, sslot->values[i])))
		{
			tmax = sslot->values[i];
			found_tmax = true;
		}
	}

	/*
	 * Copy the slot's values, if we found new extreme values.
	 */
	if (found_tmin)
		*min = datumCopy(tmin, typByVal, typLen);
	if (found_tmax)
		*max = datumCopy(tmax, typByVal, typLen);
}


/*
 * get_actual_variable_range
 *		Attempt to identify the current *actual* minimum and/or maximum
 *		of the specified variable, by looking for a suitable btree index
 *		and fetching its low and/or high values.
 *		If successful, store values in *min and *max, and return true.
 *		(Either pointer can be NULL if that endpoint isn't needed.)
 *		If unsuccessful, return false.
 *
 * sortop is the "<" comparison operator to use.
 * collation is the required collation.
 */
static bool
get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata,
						  Oid sortop, Oid collation,
						  Datum *min, Datum *max)
{
	bool		have_data = false;
	RelOptInfo *rel = vardata->rel;
	RangeTblEntry *rte;
	ListCell   *lc;

	/* No hope if no relation or it doesn't have indexes */
	if (rel == NULL || rel->indexlist == NIL)
		return false;
	/* If it has indexes it must be a plain relation */
	rte = root->simple_rte_array[rel->relid];
	Assert(rte->rtekind == RTE_RELATION);

	/* ignore partitioned tables.  Any indexes here are not real indexes */
	if (rte->relkind == RELKIND_PARTITIONED_TABLE)
		return false;

	/* Search through the indexes to see if any match our problem */
	foreach(lc, rel->indexlist)
	{
		IndexOptInfo *index = (IndexOptInfo *) lfirst(lc);
		ScanDirection indexscandir;
		StrategyNumber strategy;

		/* Ignore non-ordering indexes */
		if (index->sortopfamily == NULL)
			continue;

		/*
		 * Ignore partial indexes --- we only want stats that cover the entire
		 * relation.
		 */
		if (index->indpred != NIL)
			continue;

		/*
		 * The index list might include hypothetical indexes inserted by a
		 * get_relation_info hook --- don't try to access them.
		 */
		if (index->hypothetical)
			continue;

		/*
		 * get_actual_variable_endpoint uses the index-only-scan machinery, so
		 * ignore indexes that can't use it on their first column.
		 */
		if (!index->canreturn[0])
			continue;

		/*
		 * The first index column must match the desired variable, sortop, and
		 * collation --- but we can use a descending-order index.
		 */
		if (collation != index->indexcollations[0])
			continue;			/* test first 'cause it's cheapest */
		if (!match_index_to_operand(vardata->var, 0, index))
			continue;
		strategy = get_op_opfamily_strategy(sortop, index->sortopfamily[0]);
		switch (IndexAmTranslateStrategy(strategy, index->relam, index->sortopfamily[0], true))
		{
			case COMPARE_LT:
				if (index->reverse_sort[0])
					indexscandir = BackwardScanDirection;
				else
					indexscandir = ForwardScanDirection;
				break;
			case COMPARE_GT:
				if (index->reverse_sort[0])
					indexscandir = ForwardScanDirection;
				else
					indexscandir = BackwardScanDirection;
				break;
			default:
				/* index doesn't match the sortop */
				continue;
		}

		/*
		 * Found a suitable index to extract data from.  Set up some data that
		 * can be used by both invocations of get_actual_variable_endpoint.
		 */
		{
			MemoryContext tmpcontext;
			MemoryContext oldcontext;
			Relation	heapRel;
			Relation	indexRel;
			TupleTableSlot *slot;
			int16		typLen;
			bool		typByVal;
			ScanKeyData scankeys[1];

			/* Make sure any cruft gets recycled when we're done */
			tmpcontext = AllocSetContextCreate(CurrentMemoryContext,
											   "get_actual_variable_range workspace",
											   ALLOCSET_DEFAULT_SIZES);
			oldcontext = MemoryContextSwitchTo(tmpcontext);

			/*
			 * Open the table and index so we can read from them.  We should
			 * already have some type of lock on each.
			 */
			heapRel = table_open(rte->relid, NoLock);
			indexRel = index_open(index->indexoid, NoLock);

			/* build some stuff needed for indexscan execution */
			slot = table_slot_create(heapRel, NULL);
			get_typlenbyval(vardata->atttype, &typLen, &typByVal);

			/* set up an IS NOT NULL scan key so that we ignore nulls */
			ScanKeyEntryInitialize(&scankeys[0],
								   SK_ISNULL | SK_SEARCHNOTNULL,
								   1,	/* index col to scan */
								   InvalidStrategy, /* no strategy */
								   InvalidOid,	/* no strategy subtype */
								   InvalidOid,	/* no collation */
								   InvalidOid,	/* no reg proc for this */
								   (Datum) 0);	/* constant */

			/* If min is requested ... */
			if (min)
			{
				have_data = get_actual_variable_endpoint(heapRel,
														 indexRel,
														 indexscandir,
														 scankeys,
														 typLen,
														 typByVal,
														 slot,
														 oldcontext,
														 min);
			}
			else
			{
				/* If min not requested, still want to fetch max */
				have_data = true;
			}

			/* If max is requested, and we didn't already fail ... */
			if (max && have_data)
			{
				/* scan in the opposite direction; all else is the same */
				have_data = get_actual_variable_endpoint(heapRel,
														 indexRel,
														 -indexscandir,
														 scankeys,
														 typLen,
														 typByVal,
														 slot,
														 oldcontext,
														 max);
			}

			/* Clean everything up */
			ExecDropSingleTupleTableSlot(slot);

			index_close(indexRel, NoLock);
			table_close(heapRel, NoLock);

			MemoryContextSwitchTo(oldcontext);
			MemoryContextDelete(tmpcontext);

			/* And we're done */
			break;
		}
	}

	return have_data;
}

/*
 * Get one endpoint datum (min or max depending on indexscandir) from the
 * specified index.  Return true if successful, false if not.
 * On success, endpoint value is stored to *endpointDatum (and copied into
 * outercontext).
 *
 * scankeys is a 1-element scankey array set up to reject nulls.
 * typLen/typByVal describe the datatype of the index's first column.
 * tableslot is a slot suitable to hold table tuples, in case we need
 * to probe the heap.
 * (We could compute these values locally, but that would mean computing them
 * twice when get_actual_variable_range needs both the min and the max.)
 *
 * Failure occurs either when the index is empty, or we decide that it's
 * taking too long to find a suitable tuple.
 */
static bool
get_actual_variable_endpoint(Relation heapRel,
							 Relation indexRel,
							 ScanDirection indexscandir,
							 ScanKey scankeys,
							 int16 typLen,
							 bool typByVal,
							 TupleTableSlot *tableslot,
							 MemoryContext outercontext,
							 Datum *endpointDatum)
{
	bool		have_data = false;
	SnapshotData SnapshotNonVacuumable;
	IndexScanDesc index_scan;
	Buffer		vmbuffer = InvalidBuffer;
	BlockNumber last_heap_block = InvalidBlockNumber;
	int			n_visited_heap_pages = 0;
	ItemPointer tid;
	Datum		values[INDEX_MAX_KEYS];
	bool		isnull[INDEX_MAX_KEYS];
	MemoryContext oldcontext;

	/*
	 * We use the index-only-scan machinery for this.  With mostly-static
	 * tables that's a win because it avoids a heap visit.  It's also a win
	 * for dynamic data, but the reason is less obvious; read on for details.
	 *
	 * In principle, we should scan the index with our current active
	 * snapshot, which is the best approximation we've got to what the query
	 * will see when executed.  But that won't be exact if a new snap is taken
	 * before running the query, and it can be very expensive if a lot of
	 * recently-dead or uncommitted rows exist at the beginning or end of the
	 * index (because we'll laboriously fetch each one and reject it).
	 * Instead, we use SnapshotNonVacuumable.  That will accept recently-dead
	 * and uncommitted rows as well as normal visible rows.  On the other
	 * hand, it will reject known-dead rows, and thus not give a bogus answer
	 * when the extreme value has been deleted (unless the deletion was quite
	 * recent); that case motivates not using SnapshotAny here.
	 *
	 * A crucial point here is that SnapshotNonVacuumable, with
	 * GlobalVisTestFor(heapRel) as horizon, yields the inverse of the
	 * condition that the indexscan will use to decide that index entries are
	 * killable (see heap_hot_search_buffer()).  Therefore, if the snapshot
	 * rejects a tuple (or more precisely, all tuples of a HOT chain) and we
	 * have to continue scanning past it, we know that the indexscan will mark
	 * that index entry killed.  That means that the next
	 * get_actual_variable_endpoint() call will not have to re-consider that
	 * index entry.  In this way we avoid repetitive work when this function
	 * is used a lot during planning.
	 *
	 * But using SnapshotNonVacuumable creates a hazard of its own.  In a
	 * recently-created index, some index entries may point at "broken" HOT
	 * chains in which not all the tuple versions contain data matching the
	 * index entry.  The live tuple version(s) certainly do match the index,
	 * but SnapshotNonVacuumable can accept recently-dead tuple versions that
	 * don't match.  Hence, if we took data from the selected heap tuple, we
	 * might get a bogus answer that's not close to the index extremal value,
	 * or could even be NULL.  We avoid this hazard because we take the data
	 * from the index entry not the heap.
	 *
	 * Despite all this care, there are situations where we might find many
	 * non-visible tuples near the end of the index.  We don't want to expend
	 * a huge amount of time here, so we give up once we've read too many heap
	 * pages.  When we fail for that reason, the caller will end up using
	 * whatever extremal value is recorded in pg_statistic.
	 */
	InitNonVacuumableSnapshot(SnapshotNonVacuumable,
							  GlobalVisTestFor(heapRel));

	index_scan = index_beginscan(heapRel, indexRel,
								 &SnapshotNonVacuumable, NULL,
								 1, 0,
								 SO_NONE);
	/* Set it up for index-only scan */
	index_scan->xs_want_itup = true;
	index_rescan(index_scan, scankeys, 1, NULL, 0);

	/* Fetch first/next tuple in specified direction */
	while ((tid = index_getnext_tid(index_scan, indexscandir)) != NULL)
	{
		BlockNumber block = ItemPointerGetBlockNumber(tid);

		if (!VM_ALL_VISIBLE(heapRel,
							block,
							&vmbuffer))
		{
			/* Rats, we have to visit the heap to check visibility */
			if (!index_fetch_heap(index_scan, tableslot))
			{
				/*
				 * No visible tuple for this index entry, so we need to
				 * advance to the next entry.  Before doing so, count heap
				 * page fetches and give up if we've done too many.
				 *
				 * We don't charge a page fetch if this is the same heap page
				 * as the previous tuple.  This is on the conservative side,
				 * since other recently-accessed pages are probably still in
				 * buffers too; but it's good enough for this heuristic.
				 */
#define VISITED_PAGES_LIMIT 100

				if (block != last_heap_block)
				{
					last_heap_block = block;
					n_visited_heap_pages++;
					if (n_visited_heap_pages > VISITED_PAGES_LIMIT)
						break;
				}

				continue;		/* no visible tuple, try next index entry */
			}

			/* We don't actually need the heap tuple for anything */
			ExecClearTuple(tableslot);

			/*
			 * We don't care whether there's more than one visible tuple in
			 * the HOT chain; if any are visible, that's good enough.
			 */
		}

		/*
		 * We expect that the index will return data in IndexTuple not
		 * HeapTuple format.
		 */
		if (!index_scan->xs_itup)
			elog(ERROR, "no data returned for index-only scan");

		/*
		 * We do not yet support recheck here.
		 */
		if (index_scan->xs_recheck)
			break;

		/* OK to deconstruct the index tuple */
		index_deform_tuple(index_scan->xs_itup,
						   index_scan->xs_itupdesc,
						   values, isnull);

		/* Shouldn't have got a null, but be careful */
		if (isnull[0])
			elog(ERROR, "found unexpected null value in index \"%s\"",
				 RelationGetRelationName(indexRel));

		/* Copy the index column value out to caller's context */
		oldcontext = MemoryContextSwitchTo(outercontext);
		*endpointDatum = datumCopy(values[0], typByVal, typLen);
		MemoryContextSwitchTo(oldcontext);
		have_data = true;
		break;
	}

	if (vmbuffer != InvalidBuffer)
		ReleaseBuffer(vmbuffer);
	index_endscan(index_scan);

	return have_data;
}

/*
 * find_join_input_rel
 *		Look up the input relation for a join.
 *
 * We assume that the input relation's RelOptInfo must have been constructed
 * already.
 */
static RelOptInfo *
find_join_input_rel(PlannerInfo *root, Relids relids)
{
	RelOptInfo *rel = NULL;

	if (!bms_is_empty(relids))
	{
		int			relid;

		if (bms_get_singleton_member(relids, &relid))
			rel = find_base_rel(root, relid);
		else
			rel = find_join_rel(root, relids);
	}

	if (rel == NULL)
		elog(ERROR, "could not find RelOptInfo for given relids");

	return rel;
}


/*-------------------------------------------------------------------------
 *
 * Index cost estimation functions
 *
 *-------------------------------------------------------------------------
 */

/*
 * Extract the actual indexquals (as RestrictInfos) from an IndexClause list
 */
List *
get_quals_from_indexclauses(List *indexclauses)
{
	List	   *result = NIL;
	ListCell   *lc;

	foreach(lc, indexclauses)
	{
		IndexClause *iclause = lfirst_node(IndexClause, lc);
		ListCell   *lc2;

		foreach(lc2, iclause->indexquals)
		{
			RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc2);

			result = lappend(result, rinfo);
		}
	}
	return result;
}

/*
 * Compute the total evaluation cost of the comparison operands in a list
 * of index qual expressions.  Since we know these will be evaluated just
 * once per scan, there's no need to distinguish startup from per-row cost.
 *
 * This can be used either on the result of get_quals_from_indexclauses(),
 * or directly on an indexorderbys list.  In both cases, we expect that the
 * index key expression is on the left side of binary clauses.
 */
Cost
index_other_operands_eval_cost(PlannerInfo *root, List *indexquals)
{
	Cost		qual_arg_cost = 0;
	ListCell   *lc;

	foreach(lc, indexquals)
	{
		Expr	   *clause = (Expr *) lfirst(lc);
		Node	   *other_operand;
		QualCost	index_qual_cost;

		/*
		 * Index quals will have RestrictInfos, indexorderbys won't.  Look
		 * through RestrictInfo if present.
		 */
		if (IsA(clause, RestrictInfo))
			clause = ((RestrictInfo *) clause)->clause;

		if (IsA(clause, OpExpr))
		{
			OpExpr	   *op = (OpExpr *) clause;

			other_operand = (Node *) lsecond(op->args);
		}
		else if (IsA(clause, RowCompareExpr))
		{
			RowCompareExpr *rc = (RowCompareExpr *) clause;

			other_operand = (Node *) rc->rargs;
		}
		else if (IsA(clause, ScalarArrayOpExpr))
		{
			ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;

			other_operand = (Node *) lsecond(saop->args);
		}
		else if (IsA(clause, NullTest))
		{
			other_operand = NULL;
		}
		else
		{
			elog(ERROR, "unsupported indexqual type: %d",
				 (int) nodeTag(clause));
			other_operand = NULL;	/* keep compiler quiet */
		}

		cost_qual_eval_node(&index_qual_cost, other_operand, root);
		qual_arg_cost += index_qual_cost.startup + index_qual_cost.per_tuple;
	}
	return qual_arg_cost;
}

/*
 * Compute generic index access cost estimates.
 *
 * See struct GenericCosts in selfuncs.h for more info.
 */
void
genericcostestimate(PlannerInfo *root,
					IndexPath *path,
					double loop_count,
					GenericCosts *costs)
{
	IndexOptInfo *index = path->indexinfo;
	List	   *indexQuals = get_quals_from_indexclauses(path->indexclauses);
	List	   *indexOrderBys = path->indexorderbys;
	Cost		indexStartupCost;
	Cost		indexTotalCost;
	Selectivity indexSelectivity;
	double		indexCorrelation;
	double		numIndexPages;
	double		numIndexTuples;
	double		spc_random_page_cost;
	double		num_sa_scans;
	double		num_outer_scans;
	double		num_scans;
	double		qual_op_cost;
	double		qual_arg_cost;
	List	   *selectivityQuals;
	ListCell   *l;

	/*
	 * If the index is partial, AND the index predicate with the explicitly
	 * given indexquals to produce a more accurate idea of the index
	 * selectivity.
	 */
	selectivityQuals = add_predicate_to_index_quals(index, indexQuals);

	/*
	 * If caller didn't give us an estimate for ScalarArrayOpExpr index scans,
	 * just assume that the number of index descents is the number of distinct
	 * combinations of array elements from all of the scan's SAOP clauses.
	 */
	num_sa_scans = costs->num_sa_scans;
	if (num_sa_scans < 1)
	{
		num_sa_scans = 1;
		foreach(l, indexQuals)
		{
			RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);

			if (IsA(rinfo->clause, ScalarArrayOpExpr))
			{
				ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) rinfo->clause;
				double		alength = estimate_array_length(root, lsecond(saop->args));

				if (alength > 1)
					num_sa_scans *= alength;
			}
		}
	}

	/* Estimate the fraction of main-table tuples that will be visited */
	indexSelectivity = clauselist_selectivity(root, selectivityQuals,
											  index->rel->relid,
											  JOIN_INNER,
											  NULL);

	/*
	 * If caller didn't give us an estimate, estimate the number of index
	 * tuples that will be visited.  We do it in this rather peculiar-looking
	 * way in order to get the right answer for partial indexes.
	 */
	numIndexTuples = costs->numIndexTuples;
	if (numIndexTuples <= 0.0)
	{
		numIndexTuples = indexSelectivity * index->rel->tuples;

		/*
		 * The above calculation counts all the tuples visited across all
		 * scans induced by ScalarArrayOpExpr nodes.  We want to consider the
		 * average per-indexscan number, so adjust.  This is a handy place to
		 * round to integer, too.  (If caller supplied tuple estimate, it's
		 * responsible for handling these considerations.)
		 */
		numIndexTuples = rint(numIndexTuples / num_sa_scans);
	}

	/*
	 * We can bound the number of tuples by the index size in any case. Also,
	 * always estimate at least one tuple is touched, even when
	 * indexSelectivity estimate is tiny.
	 */
	if (numIndexTuples > index->tuples)
		numIndexTuples = index->tuples;
	if (numIndexTuples < 1.0)
		numIndexTuples = 1.0;

	/*
	 * Estimate the number of index pages that will be retrieved.
	 *
	 * We use the simplistic method of taking a pro-rata fraction of the total
	 * number of index leaf pages.  We disregard any overhead such as index
	 * metapages or upper tree levels.
	 *
	 * In practice access to upper index levels is often nearly free because
	 * those tend to stay in cache under load; moreover, the cost involved is
	 * highly dependent on index type.  We therefore ignore such costs here
	 * and leave it to the caller to add a suitable charge if needed.
	 */
	if (index->pages > costs->numNonLeafPages && index->tuples > 1)
		numIndexPages =
			ceil(numIndexTuples * (index->pages - costs->numNonLeafPages)
				 / index->tuples);
	else
		numIndexPages = 1.0;

	/* fetch estimated page cost for tablespace containing index */
	get_tablespace_page_costs(index->reltablespace,
							  &spc_random_page_cost,
							  NULL);

	/*
	 * Now compute the disk access costs.
	 *
	 * The above calculations are all per-index-scan.  However, if we are in a
	 * nestloop inner scan, we can expect the scan to be repeated (with
	 * different search keys) for each row of the outer relation.  Likewise,
	 * ScalarArrayOpExpr quals result in multiple index scans.  This creates
	 * the potential for cache effects to reduce the number of disk page
	 * fetches needed.  We want to estimate the average per-scan I/O cost in
	 * the presence of caching.
	 *
	 * We use the Mackert-Lohman formula (see costsize.c for details) to
	 * estimate the total number of page fetches that occur.  While this
	 * wasn't what it was designed for, it seems a reasonable model anyway.
	 * Note that we are counting pages not tuples anymore, so we take N = T =
	 * index size, as if there were one "tuple" per page.
	 */
	num_outer_scans = loop_count;
	num_scans = num_sa_scans * num_outer_scans;

	if (num_scans > 1)
	{
		double		pages_fetched;

		/* total page fetches ignoring cache effects */
		pages_fetched = numIndexPages * num_scans;

		/* use Mackert and Lohman formula to adjust for cache effects */
		pages_fetched = index_pages_fetched(pages_fetched,
											index->pages,
											(double) index->pages,
											root);

		/*
		 * Now compute the total disk access cost, and then report a pro-rated
		 * share for each outer scan.  (Don't pro-rate for ScalarArrayOpExpr,
		 * since that's internal to the indexscan.)
		 */
		indexTotalCost = (pages_fetched * spc_random_page_cost)
			/ num_outer_scans;
	}
	else
	{
		/*
		 * For a single index scan, we just charge spc_random_page_cost per
		 * page touched.
		 */
		indexTotalCost = numIndexPages * spc_random_page_cost;
	}

	/*
	 * CPU cost: any complex expressions in the indexquals will need to be
	 * evaluated once at the start of the scan to reduce them to runtime keys
	 * to pass to the index AM (see nodeIndexscan.c).  We model the per-tuple
	 * CPU costs as cpu_index_tuple_cost plus one cpu_operator_cost per
	 * indexqual operator.  Because we have numIndexTuples as a per-scan
	 * number, we have to multiply by num_sa_scans to get the correct result
	 * for ScalarArrayOpExpr cases.  Similarly add in costs for any index
	 * ORDER BY expressions.
	 *
	 * Note: this neglects the possible costs of rechecking lossy operators.
	 * Detecting that that might be needed seems more expensive than it's
	 * worth, though, considering all the other inaccuracies here ...
	 */
	qual_arg_cost = index_other_operands_eval_cost(root, indexQuals) +
		index_other_operands_eval_cost(root, indexOrderBys);
	qual_op_cost = cpu_operator_cost *
		(list_length(indexQuals) + list_length(indexOrderBys));

	indexStartupCost = qual_arg_cost;
	indexTotalCost += qual_arg_cost;
	indexTotalCost += numIndexTuples * num_sa_scans * (cpu_index_tuple_cost + qual_op_cost);

	/*
	 * Generic assumption about index correlation: there isn't any.
	 */
	indexCorrelation = 0.0;

	/*
	 * Return everything to caller.
	 */
	costs->indexStartupCost = indexStartupCost;
	costs->indexTotalCost = indexTotalCost;
	costs->indexSelectivity = indexSelectivity;
	costs->indexCorrelation = indexCorrelation;
	costs->numIndexPages = numIndexPages;
	costs->numIndexTuples = numIndexTuples;
	costs->spc_random_page_cost = spc_random_page_cost;
	costs->num_sa_scans = num_sa_scans;
}

/*
 * If the index is partial, add its predicate to the given qual list.
 *
 * ANDing the index predicate with the explicitly given indexquals produces
 * a more accurate idea of the index's selectivity.  However, we need to be
 * careful not to insert redundant clauses, because clauselist_selectivity()
 * is easily fooled into computing a too-low selectivity estimate.  Our
 * approach is to add only the predicate clause(s) that cannot be proven to
 * be implied by the given indexquals.  This successfully handles cases such
 * as a qual "x = 42" used with a partial index "WHERE x >= 40 AND x < 50".
 * There are many other cases where we won't detect redundancy, leading to a
 * too-low selectivity estimate, which will bias the system in favor of using
 * partial indexes where possible.  That is not necessarily bad though.
 *
 * Note that indexQuals contains RestrictInfo nodes while the indpred
 * does not, so the output list will be mixed.  This is OK for both
 * predicate_implied_by() and clauselist_selectivity(), but might be
 * problematic if the result were passed to other things.
 */
List *
add_predicate_to_index_quals(IndexOptInfo *index, List *indexQuals)
{
	List	   *predExtraQuals = NIL;
	ListCell   *lc;

	if (index->indpredExpand == NIL)
		return indexQuals;

	foreach(lc, index->indpredExpand)
	{
		Node	   *predQual = (Node *) lfirst(lc);
		List	   *oneQual = list_make1(predQual);

		if (!predicate_implied_by(oneQual, indexQuals, false))
			predExtraQuals = list_concat(predExtraQuals, oneQual);
	}
	return list_concat(predExtraQuals, indexQuals);
}

/*
 * Estimate correlation of btree index's first column.
 *
 * If we can get an estimate of the first column's ordering correlation C
 * from pg_statistic, estimate the index correlation as C for a single-column
 * index, or C * 0.75 for multiple columns.  The idea here is that multiple
 * columns dilute the importance of the first column's ordering, but don't
 * negate it entirely.
 *
 * We already filled in the stats tuple for *vardata when called.
 */
static double
btcost_correlation(IndexOptInfo *index, VariableStatData *vardata)
{
	Oid			sortop;
	AttStatsSlot sslot;
	double		indexCorrelation = 0;

	Assert(HeapTupleIsValid(vardata->statsTuple));

	sortop = get_opfamily_member(index->opfamily[0],
								 index->opcintype[0],
								 index->opcintype[0],
								 BTLessStrategyNumber);
	if (OidIsValid(sortop) &&
		get_attstatsslot(&sslot, vardata->statsTuple,
						 STATISTIC_KIND_CORRELATION, sortop,
						 ATTSTATSSLOT_NUMBERS))
	{
		double		varCorrelation;

		Assert(sslot.nnumbers == 1);
		varCorrelation = sslot.numbers[0];

		if (index->reverse_sort[0])
			varCorrelation = -varCorrelation;

		if (index->nkeycolumns > 1)
			indexCorrelation = varCorrelation * 0.75;
		else
			indexCorrelation = varCorrelation;

		free_attstatsslot(&sslot);
	}

	return indexCorrelation;
}

void
btcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
			   Cost *indexStartupCost, Cost *indexTotalCost,
			   Selectivity *indexSelectivity, double *indexCorrelation,
			   double *indexPages)
{
	IndexOptInfo *index = path->indexinfo;
	GenericCosts costs = {0};
	VariableStatData vardata = {0};
	double		numIndexTuples;
	Cost		descentCost;
	List	   *indexBoundQuals;
	List	   *indexSkipQuals;
	int			indexcol;
	bool		eqQualHere;
	bool		found_row_compare;
	bool		found_array;
	bool		found_is_null_op;
	bool		have_correlation = false;
	double		num_sa_scans;
	double		correlation = 0.0;
	ListCell   *lc;

	/*
	 * For a btree scan, only leading '=' quals plus inequality quals for the
	 * immediately next attribute contribute to index selectivity (these are
	 * the "boundary quals" that determine the starting and stopping points of
	 * the index scan).  Additional quals can suppress visits to the heap, so
	 * it's OK to count them in indexSelectivity, but they should not count
	 * for estimating numIndexTuples.  So we must examine the given indexquals
	 * to find out which ones count as boundary quals.  We rely on the
	 * knowledge that they are given in index column order.  Note that nbtree
	 * preprocessing can add skip arrays that act as leading '=' quals in the
	 * absence of ordinary input '=' quals, so in practice _most_ input quals
	 * are able to act as index bound quals (which we take into account here).
	 *
	 * For a RowCompareExpr, we consider only the first column, just as
	 * rowcomparesel() does.
	 *
	 * If there's a SAOP or skip array in the quals, we'll actually perform up
	 * to N index descents (not just one), but the underlying array key's
	 * operator can be considered to act the same as it normally does.
	 */
	indexBoundQuals = NIL;
	indexSkipQuals = NIL;
	indexcol = 0;
	eqQualHere = false;
	found_row_compare = false;
	found_array = false;
	found_is_null_op = false;
	num_sa_scans = 1;
	foreach(lc, path->indexclauses)
	{
		IndexClause *iclause = lfirst_node(IndexClause, lc);
		ListCell   *lc2;

		if (indexcol < iclause->indexcol)
		{
			double		num_sa_scans_prev_cols = num_sa_scans;

			/*
			 * Beginning of a new column's quals.
			 *
			 * Skip scans use skip arrays, which are ScalarArrayOp style
			 * arrays that generate their elements procedurally and on demand.
			 * Given a multi-column index on "(a, b)", and an SQL WHERE clause
			 * "WHERE b = 42", a skip scan will effectively use an indexqual
			 * "WHERE a = ANY('{every col a value}') AND b = 42".  (Obviously,
			 * the array on "a" must also return "IS NULL" matches, since our
			 * WHERE clause used no strict operator on "a").
			 *
			 * Here we consider how nbtree will backfill skip arrays for any
			 * index columns that lacked an '=' qual.  This maintains our
			 * num_sa_scans estimate, and determines if this new column (the
			 * "iclause->indexcol" column, not the prior "indexcol" column)
			 * can have its RestrictInfos/quals added to indexBoundQuals.
			 *
			 * We'll need to handle columns that have inequality quals, where
			 * the skip array generates values from a range constrained by the
			 * quals (not every possible value).  We've been maintaining
			 * indexSkipQuals to help with this; it will now contain all of
			 * the prior column's quals (that is, indexcol's quals) when they
			 * might be used for this.
			 */
			if (found_row_compare)
			{
				/*
				 * Skip arrays can't be added after a RowCompare input qual
				 * due to limitations in nbtree
				 */
				break;
			}
			if (eqQualHere)
			{
				/*
				 * Don't need to add a skip array for an indexcol that already
				 * has an '=' qual/equality constraint
				 */
				indexcol++;
				indexSkipQuals = NIL;
			}
			eqQualHere = false;

			while (indexcol < iclause->indexcol)
			{
				double		ndistinct;
				bool		isdefault = true;

				found_array = true;

				/*
				 * A skipped attribute's ndistinct forms the basis of our
				 * estimate of the total number of "array elements" used by
				 * its skip array at runtime.  Look that up first.
				 */
				examine_indexcol_variable(root, index, indexcol, &vardata);
				ndistinct = get_variable_numdistinct(&vardata, &isdefault);

				if (indexcol == 0)
				{
					/*
					 * Get an estimate of the leading column's correlation in
					 * passing (avoids rereading variable stats below)
					 */
					if (HeapTupleIsValid(vardata.statsTuple))
						correlation = btcost_correlation(index, &vardata);
					have_correlation = true;
				}

				ReleaseVariableStats(vardata);

				/*
				 * If ndistinct is a default estimate, conservatively assume
				 * that no skipping will happen at runtime
				 */
				if (isdefault)
				{
					num_sa_scans = num_sa_scans_prev_cols;
					break;		/* done building indexBoundQuals */
				}

				/*
				 * Apply indexcol's indexSkipQuals selectivity to ndistinct
				 */
				if (indexSkipQuals != NIL)
				{
					List	   *partialSkipQuals;
					Selectivity ndistinctfrac;

					/*
					 * If the index is partial, AND the index predicate with
					 * the index-bound quals to produce a more accurate idea
					 * of the number of distinct values for prior indexcol
					 */
					partialSkipQuals = add_predicate_to_index_quals(index,
																	indexSkipQuals);

					ndistinctfrac = clauselist_selectivity(root, partialSkipQuals,
														   index->rel->relid,
														   JOIN_INNER,
														   NULL);

					/*
					 * If ndistinctfrac is selective (on its own), the scan is
					 * unlikely to benefit from repositioning itself using
					 * later quals.  Do not allow iclause->indexcol's quals to
					 * be added to indexBoundQuals (it would increase descent
					 * costs, without lowering numIndexTuples costs by much).
					 */
					if (ndistinctfrac < DEFAULT_RANGE_INEQ_SEL)
					{
						num_sa_scans = num_sa_scans_prev_cols;
						break;	/* done building indexBoundQuals */
					}

					/* Adjust ndistinct downward */
					ndistinct = rint(ndistinct * ndistinctfrac);
					ndistinct = Max(ndistinct, 1);
				}

				/*
				 * When there's no inequality quals, account for the need to
				 * find an initial value by counting -inf/+inf as a value.
				 *
				 * We don't charge anything extra for possible next/prior key
				 * index probes, which are sometimes used to find the next
				 * valid skip array element (ahead of using the located
				 * element value to relocate the scan to the next position
				 * that might contain matching tuples).  It seems hard to do
				 * better here.  Use of the skip support infrastructure often
				 * avoids most next/prior key probes.  But even when it can't,
				 * there's a decent chance that most individual next/prior key
				 * probes will locate a leaf page whose key space overlaps all
				 * of the scan's keys (even the lower-order keys) -- which
				 * also avoids the need for a separate, extra index descent.
				 * Note also that these probes are much cheaper than non-probe
				 * primitive index scans: they're reliably very selective.
				 */
				if (indexSkipQuals == NIL)
					ndistinct += 1;

				/*
				 * Update num_sa_scans estimate by multiplying by ndistinct.
				 *
				 * We make the pessimistic assumption that there is no
				 * naturally occurring cross-column correlation.  This is
				 * often wrong, but it seems best to err on the side of not
				 * expecting skipping to be helpful...
				 */
				num_sa_scans *= ndistinct;

				/*
				 * ...but back out of adding this latest group of 1 or more
				 * skip arrays when num_sa_scans exceeds the total number of
				 * index pages (revert to num_sa_scans from before indexcol).
				 * This causes a sharp discontinuity in cost (as a function of
				 * the indexcol's ndistinct), but that is representative of
				 * actual runtime costs.
				 *
				 * Note that skipping is helpful when each primitive index
				 * scan only manages to skip over 1 or 2 irrelevant leaf pages
				 * on average.  Skip arrays bring savings in CPU costs due to
				 * the scan not needing to evaluate indexquals against every
				 * tuple, which can greatly exceed any savings in I/O costs.
				 * This test is a test of whether num_sa_scans implies that
				 * we're past the point where the ability to skip ceases to
				 * lower the scan's costs (even qual evaluation CPU costs).
				 */
				if (index->pages < num_sa_scans)
				{
					num_sa_scans = num_sa_scans_prev_cols;
					break;		/* done building indexBoundQuals */
				}

				indexcol++;
				indexSkipQuals = NIL;
			}

			/*
			 * Finished considering the need to add skip arrays to bridge an
			 * initial eqQualHere gap between the old and new index columns
			 * (or there was no initial eqQualHere gap in the first place).
			 *
			 * If an initial gap could not be bridged, then new column's quals
			 * (i.e. iclause->indexcol's quals) won't go into indexBoundQuals,
			 * and so won't affect our final numIndexTuples estimate.
			 */
			if (indexcol != iclause->indexcol)
				break;			/* done building indexBoundQuals */
		}

		Assert(indexcol == iclause->indexcol);

		/* Examine each indexqual associated with this index clause */
		foreach(lc2, iclause->indexquals)
		{
			RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc2);
			Expr	   *clause = rinfo->clause;
			Oid			clause_op = InvalidOid;
			int			op_strategy;

			if (IsA(clause, OpExpr))
			{
				OpExpr	   *op = (OpExpr *) clause;

				clause_op = op->opno;
			}
			else if (IsA(clause, RowCompareExpr))
			{
				RowCompareExpr *rc = (RowCompareExpr *) clause;

				clause_op = linitial_oid(rc->opnos);
				found_row_compare = true;
			}
			else if (IsA(clause, ScalarArrayOpExpr))
			{
				ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
				Node	   *other_operand = (Node *) lsecond(saop->args);
				double		alength = estimate_array_length(root, other_operand);

				clause_op = saop->opno;
				found_array = true;
				/* estimate SA descents by indexBoundQuals only */
				if (alength > 1)
					num_sa_scans *= alength;
			}
			else if (IsA(clause, NullTest))
			{
				NullTest   *nt = (NullTest *) clause;

				if (nt->nulltesttype == IS_NULL)
				{
					found_is_null_op = true;
					/* IS NULL is like = for selectivity/skip scan purposes */
					eqQualHere = true;
				}
			}
			else
				elog(ERROR, "unsupported indexqual type: %d",
					 (int) nodeTag(clause));

			/* check for equality operator */
			if (OidIsValid(clause_op))
			{
				op_strategy = get_op_opfamily_strategy(clause_op,
													   index->opfamily[indexcol]);
				Assert(op_strategy != 0);	/* not a member of opfamily?? */
				if (op_strategy == BTEqualStrategyNumber)
					eqQualHere = true;
			}

			indexBoundQuals = lappend(indexBoundQuals, rinfo);

			/*
			 * We apply inequality selectivities to estimate index descent
			 * costs with scans that use skip arrays.  Save this indexcol's
			 * RestrictInfos if it looks like they'll be needed for that.
			 */
			if (!eqQualHere && !found_row_compare &&
				indexcol < index->nkeycolumns - 1)
				indexSkipQuals = lappend(indexSkipQuals, rinfo);
		}
	}

	/*
	 * If index is unique and we found an '=' clause for each column, we can
	 * just assume numIndexTuples = 1 and skip the expensive
	 * clauselist_selectivity calculations.  However, an array or NullTest
	 * always invalidates that theory (even when eqQualHere has been set).
	 */
	if (index->unique &&
		indexcol == index->nkeycolumns - 1 &&
		eqQualHere &&
		!found_array &&
		!found_is_null_op)
		numIndexTuples = 1.0;
	else
	{
		List	   *selectivityQuals;
		Selectivity btreeSelectivity;

		/*
		 * If the index is partial, AND the index predicate with the
		 * index-bound quals to produce a more accurate idea of the number of
		 * rows covered by the bound conditions.
		 */
		selectivityQuals = add_predicate_to_index_quals(index, indexBoundQuals);

		btreeSelectivity = clauselist_selectivity(root, selectivityQuals,
												  index->rel->relid,
												  JOIN_INNER,
												  NULL);
		numIndexTuples = btreeSelectivity * index->rel->tuples;

		/*
		 * btree automatically combines individual array element primitive
		 * index scans whenever the tuples covered by the next set of array
		 * keys are close to tuples covered by the current set.  That puts a
		 * natural ceiling on the worst case number of descents -- there
		 * cannot possibly be more than one descent per leaf page scanned.
		 *
		 * Clamp the number of descents to at most 1/3 the number of index
		 * pages.  This avoids implausibly high estimates with low selectivity
		 * paths, where scans usually require only one or two descents.  This
		 * is most likely to help when there are several SAOP clauses, where
		 * naively accepting the total number of distinct combinations of
		 * array elements as the number of descents would frequently lead to
		 * wild overestimates.
		 *
		 * We somewhat arbitrarily don't just make the cutoff the total number
		 * of leaf pages (we make it 1/3 the total number of pages instead) to
		 * give the btree code credit for its ability to continue on the leaf
		 * level with low selectivity scans.
		 *
		 * Note: num_sa_scans includes both ScalarArrayOp array elements and
		 * skip array elements whose qual affects our numIndexTuples estimate.
		 */
		num_sa_scans = Min(num_sa_scans, ceil(index->pages * 0.3333333));
		num_sa_scans = Max(num_sa_scans, 1);

		/*
		 * As in genericcostestimate(), we have to adjust for any array quals
		 * included in indexBoundQuals, and then round to integer.
		 *
		 * It is tempting to make genericcostestimate behave as if array
		 * clauses work in almost the same way as scalar operators during
		 * btree scans, making the top-level scan look like a continuous scan
		 * (as opposed to num_sa_scans-many primitive index scans).  After
		 * all, btree scans mostly work like that at runtime.  However, such a
		 * scheme would badly bias genericcostestimate's simplistic approach
		 * to calculating numIndexPages through prorating.
		 *
		 * Stick with the approach taken by non-native SAOP scans for now.
		 * genericcostestimate will use the Mackert-Lohman formula to
		 * compensate for repeat page fetches, even though that definitely
		 * won't happen during btree scans (not for leaf pages, at least).
		 * We're usually very pessimistic about the number of primitive index
		 * scans that will be required, but it's not clear how to do better.
		 */
		numIndexTuples = rint(numIndexTuples / num_sa_scans);
	}

	/*
	 * Now do generic index cost estimation.
	 *
	 * While we expended effort to make realistic estimates of numIndexTuples
	 * and num_sa_scans, we are content to count only the btree metapage as
	 * non-leaf.  btree fanout is typically high enough that upper pages are
	 * few relative to leaf pages, so accounting for them would move the
	 * estimates at most a percent or two.  Given the uncertainty in just how
	 * many upper pages exist in a particular index, we'll skip trying to
	 * handle that.
	 */
	costs.numIndexTuples = numIndexTuples;
	costs.num_sa_scans = num_sa_scans;
	costs.numNonLeafPages = 1;

	genericcostestimate(root, path, loop_count, &costs);

	/*
	 * Add a CPU-cost component to represent the costs of initial btree
	 * descent.  We don't charge any I/O cost for touching upper btree levels,
	 * since they tend to stay in cache, but we still have to do about log2(N)
	 * comparisons to descend a btree of N leaf tuples.  We charge one
	 * cpu_operator_cost per comparison.
	 *
	 * If there are SAOP or skip array keys, charge this once per estimated
	 * index descent.  The ones after the first one are not startup cost so
	 * far as the overall plan goes, so just add them to "total" cost.
	 */
	if (index->tuples > 1)		/* avoid computing log(0) */
	{
		descentCost = ceil(log(index->tuples) / log(2.0)) * cpu_operator_cost;
		costs.indexStartupCost += descentCost;
		costs.indexTotalCost += costs.num_sa_scans * descentCost;
	}

	/*
	 * Even though we're not charging I/O cost for touching upper btree pages,
	 * it's still reasonable to charge some CPU cost per page descended
	 * through.  Moreover, if we had no such charge at all, bloated indexes
	 * would appear to have the same search cost as unbloated ones, at least
	 * in cases where only a single leaf page is expected to be visited.  This
	 * cost is somewhat arbitrarily set at 50x cpu_operator_cost per page
	 * touched.  The number of such pages is btree tree height plus one (ie,
	 * we charge for the leaf page too).  As above, charge once per estimated
	 * SAOP/skip array descent.
	 */
	descentCost = (index->tree_height + 1) * DEFAULT_PAGE_CPU_MULTIPLIER * cpu_operator_cost;
	costs.indexStartupCost += descentCost;
	costs.indexTotalCost += costs.num_sa_scans * descentCost;

	if (!have_correlation)
	{
		examine_indexcol_variable(root, index, 0, &vardata);
		if (HeapTupleIsValid(vardata.statsTuple))
			costs.indexCorrelation = btcost_correlation(index, &vardata);
		ReleaseVariableStats(vardata);
	}
	else
	{
		/* btcost_correlation already called earlier on */
		costs.indexCorrelation = correlation;
	}

	*indexStartupCost = costs.indexStartupCost;
	*indexTotalCost = costs.indexTotalCost;
	*indexSelectivity = costs.indexSelectivity;
	*indexCorrelation = costs.indexCorrelation;
	*indexPages = costs.numIndexPages;
}

void
hashcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
				 Cost *indexStartupCost, Cost *indexTotalCost,
				 Selectivity *indexSelectivity, double *indexCorrelation,
				 double *indexPages)
{
	GenericCosts costs = {0};

	/* As in btcostestimate, count only the metapage as non-leaf */
	costs.numNonLeafPages = 1;

	genericcostestimate(root, path, loop_count, &costs);

	/*
	 * A hash index has no descent costs as such, since the index AM can go
	 * directly to the target bucket after computing the hash value.  There
	 * are a couple of other hash-specific costs that we could conceivably add
	 * here, though:
	 *
	 * Ideally we'd charge spc_random_page_cost for each page in the target
	 * bucket, not just the numIndexPages pages that genericcostestimate
	 * thought we'd visit.  However in most cases we don't know which bucket
	 * that will be.  There's no point in considering the average bucket size
	 * because the hash AM makes sure that's always one page.
	 *
	 * Likewise, we could consider charging some CPU for each index tuple in
	 * the bucket, if we knew how many there were.  But the per-tuple cost is
	 * just a hash value comparison, not a general datatype-dependent
	 * comparison, so any such charge ought to be quite a bit less than
	 * cpu_operator_cost; which makes it probably not worth worrying about.
	 *
	 * A bigger issue is that chance hash-value collisions will result in
	 * wasted probes into the heap.  We don't currently attempt to model this
	 * cost on the grounds that it's rare, but maybe it's not rare enough.
	 * (Any fix for this ought to consider the generic lossy-operator problem,
	 * though; it's not entirely hash-specific.)
	 */

	*indexStartupCost = costs.indexStartupCost;
	*indexTotalCost = costs.indexTotalCost;
	*indexSelectivity = costs.indexSelectivity;
	*indexCorrelation = costs.indexCorrelation;
	*indexPages = costs.numIndexPages;
}

void
gistcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
				 Cost *indexStartupCost, Cost *indexTotalCost,
				 Selectivity *indexSelectivity, double *indexCorrelation,
				 double *indexPages)
{
	IndexOptInfo *index = path->indexinfo;
	GenericCosts costs = {0};
	Cost		descentCost;

	/* GiST has no metapage, so we treat all pages as leaf pages */

	genericcostestimate(root, path, loop_count, &costs);

	/*
	 * We model index descent costs similarly to those for btree, but to do
	 * that we first need an idea of the tree height.  We somewhat arbitrarily
	 * assume that the fanout is 100, meaning the tree height is at most
	 * log100(index->pages).
	 *
	 * Although this computation isn't really expensive enough to require
	 * caching, we might as well use index->tree_height to cache it.
	 */
	if (index->tree_height < 0) /* unknown? */
	{
		if (index->pages > 1)	/* avoid computing log(0) */
			index->tree_height = (int) (log(index->pages) / log(100.0));
		else
			index->tree_height = 0;
	}

	/*
	 * Add a CPU-cost component to represent the costs of initial descent. We
	 * just use log(N) here not log2(N) since the branching factor isn't
	 * necessarily two anyway.  As for btree, charge once per SA scan.
	 */
	if (index->tuples > 1)		/* avoid computing log(0) */
	{
		descentCost = ceil(log(index->tuples)) * cpu_operator_cost;
		costs.indexStartupCost += descentCost;
		costs.indexTotalCost += costs.num_sa_scans * descentCost;
	}

	/*
	 * Likewise add a per-page charge, calculated the same as for btrees.
	 */
	descentCost = (index->tree_height + 1) * DEFAULT_PAGE_CPU_MULTIPLIER * cpu_operator_cost;
	costs.indexStartupCost += descentCost;
	costs.indexTotalCost += costs.num_sa_scans * descentCost;

	*indexStartupCost = costs.indexStartupCost;
	*indexTotalCost = costs.indexTotalCost;
	*indexSelectivity = costs.indexSelectivity;
	*indexCorrelation = costs.indexCorrelation;
	*indexPages = costs.numIndexPages;
}

void
spgcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
				Cost *indexStartupCost, Cost *indexTotalCost,
				Selectivity *indexSelectivity, double *indexCorrelation,
				double *indexPages)
{
	IndexOptInfo *index = path->indexinfo;
	GenericCosts costs = {0};
	Cost		descentCost;

	/* As in btcostestimate, count only the metapage as non-leaf */
	costs.numNonLeafPages = 1;

	genericcostestimate(root, path, loop_count, &costs);

	/*
	 * We model index descent costs similarly to those for btree, but to do
	 * that we first need an idea of the tree height.  We somewhat arbitrarily
	 * assume that the fanout is 100, meaning the tree height is at most
	 * log100(index->pages).
	 *
	 * Although this computation isn't really expensive enough to require
	 * caching, we might as well use index->tree_height to cache it.
	 */
	if (index->tree_height < 0) /* unknown? */
	{
		if (index->pages > 1)	/* avoid computing log(0) */
			index->tree_height = (int) (log(index->pages) / log(100.0));
		else
			index->tree_height = 0;
	}

	/*
	 * Add a CPU-cost component to represent the costs of initial descent. We
	 * just use log(N) here not log2(N) since the branching factor isn't
	 * necessarily two anyway.  As for btree, charge once per SA scan.
	 */
	if (index->tuples > 1)		/* avoid computing log(0) */
	{
		descentCost = ceil(log(index->tuples)) * cpu_operator_cost;
		costs.indexStartupCost += descentCost;
		costs.indexTotalCost += costs.num_sa_scans * descentCost;
	}

	/*
	 * Likewise add a per-page charge, calculated the same as for btrees.
	 */
	descentCost = (index->tree_height + 1) * DEFAULT_PAGE_CPU_MULTIPLIER * cpu_operator_cost;
	costs.indexStartupCost += descentCost;
	costs.indexTotalCost += costs.num_sa_scans * descentCost;

	*indexStartupCost = costs.indexStartupCost;
	*indexTotalCost = costs.indexTotalCost;
	*indexSelectivity = costs.indexSelectivity;
	*indexCorrelation = costs.indexCorrelation;
	*indexPages = costs.numIndexPages;
}


/*
 * Support routines for gincostestimate
 */

typedef struct
{
	bool		attHasFullScan[INDEX_MAX_KEYS];
	bool		attHasNormalScan[INDEX_MAX_KEYS];
	double		partialEntries;
	double		exactEntries;
	double		searchEntries;
	double		arrayScans;
} GinQualCounts;

/*
 * Estimate the number of index terms that need to be searched for while
 * testing the given GIN query, and increment the counts in *counts
 * appropriately.  If the query is unsatisfiable, return false.
 */
static bool
gincost_pattern(IndexOptInfo *index, int indexcol,
				Oid clause_op, Datum query,
				GinQualCounts *counts)
{
	FmgrInfo	flinfo;
	Oid			extractProcOid;
	Oid			collation;
	int			strategy_op;
	Oid			lefttype,
				righttype;
	int32		nentries = 0;
	bool	   *partial_matches = NULL;
	Pointer    *extra_data = NULL;
	bool	   *nullFlags = NULL;
	int32		searchMode = GIN_SEARCH_MODE_DEFAULT;
	int32		i;

	Assert(indexcol < index->nkeycolumns);

	/*
	 * Get the operator's strategy number and declared input data types within
	 * the index opfamily.  (We don't need the latter, but we use
	 * get_op_opfamily_properties because it will throw error if it fails to
	 * find a matching pg_amop entry.)
	 */
	get_op_opfamily_properties(clause_op, index->opfamily[indexcol], false,
							   &strategy_op, &lefttype, &righttype);

	/*
	 * GIN always uses the "default" support functions, which are those with
	 * lefttype == righttype == the opclass' opcintype (see
	 * IndexSupportInitialize in relcache.c).
	 */
	extractProcOid = get_opfamily_proc(index->opfamily[indexcol],
									   index->opcintype[indexcol],
									   index->opcintype[indexcol],
									   GIN_EXTRACTQUERY_PROC);

	if (!OidIsValid(extractProcOid))
	{
		/* should not happen; throw same error as index_getprocinfo */
		elog(ERROR, "missing support function %d for attribute %d of index \"%s\"",
			 GIN_EXTRACTQUERY_PROC, indexcol + 1,
			 get_rel_name(index->indexoid));
	}

	/*
	 * Choose collation to pass to extractProc (should match initGinState).
	 */
	if (OidIsValid(index->indexcollations[indexcol]))
		collation = index->indexcollations[indexcol];
	else
		collation = DEFAULT_COLLATION_OID;

	fmgr_info(extractProcOid, &flinfo);

	set_fn_opclass_options(&flinfo, index->opclassoptions[indexcol]);

	FunctionCall7Coll(&flinfo,
					  collation,
					  query,
					  PointerGetDatum(&nentries),
					  UInt16GetDatum(strategy_op),
					  PointerGetDatum(&partial_matches),
					  PointerGetDatum(&extra_data),
					  PointerGetDatum(&nullFlags),
					  PointerGetDatum(&searchMode));

	if (nentries <= 0 && searchMode == GIN_SEARCH_MODE_DEFAULT)
	{
		/* No match is possible */
		return false;
	}

	for (i = 0; i < nentries; i++)
	{
		/*
		 * For partial match we haven't any information to estimate number of
		 * matched entries in index, so, we just estimate it as 100
		 */
		if (partial_matches && partial_matches[i])
			counts->partialEntries += 100;
		else
			counts->exactEntries++;

		counts->searchEntries++;
	}

	if (searchMode == GIN_SEARCH_MODE_DEFAULT)
	{
		counts->attHasNormalScan[indexcol] = true;
	}
	else if (searchMode == GIN_SEARCH_MODE_INCLUDE_EMPTY)
	{
		/* Treat "include empty" like an exact-match item */
		counts->attHasNormalScan[indexcol] = true;
		counts->exactEntries++;
		counts->searchEntries++;
	}
	else
	{
		/* It's GIN_SEARCH_MODE_ALL */
		counts->attHasFullScan[indexcol] = true;
	}

	return true;
}

/*
 * Estimate the number of index terms that need to be searched for while
 * testing the given GIN index clause, and increment the counts in *counts
 * appropriately.  If the query is unsatisfiable, return false.
 */
static bool
gincost_opexpr(PlannerInfo *root,
			   IndexOptInfo *index,
			   int indexcol,
			   OpExpr *clause,
			   GinQualCounts *counts)
{
	Oid			clause_op = clause->opno;
	Node	   *operand = (Node *) lsecond(clause->args);

	/* aggressively reduce to a constant, and look through relabeling */
	operand = estimate_expression_value(root, operand);

	if (IsA(operand, RelabelType))
		operand = (Node *) ((RelabelType *) operand)->arg;

	/*
	 * It's impossible to call extractQuery method for unknown operand. So
	 * unless operand is a Const we can't do much; just assume there will be
	 * one ordinary search entry from the operand at runtime.
	 */
	if (!IsA(operand, Const))
	{
		counts->exactEntries++;
		counts->searchEntries++;
		return true;
	}

	/* If Const is null, there can be no matches */
	if (((Const *) operand)->constisnull)
		return false;

	/* Otherwise, apply extractQuery and get the actual term counts */
	return gincost_pattern(index, indexcol, clause_op,
						   ((Const *) operand)->constvalue,
						   counts);
}

/*
 * Estimate the number of index terms that need to be searched for while
 * testing the given GIN index clause, and increment the counts in *counts
 * appropriately.  If the query is unsatisfiable, return false.
 *
 * A ScalarArrayOpExpr will give rise to N separate indexscans at runtime,
 * each of which involves one value from the RHS array, plus all the
 * non-array quals (if any).  To model this, we average the counts across
 * the RHS elements, and add the averages to the counts in *counts (which
 * correspond to per-indexscan costs).  We also multiply counts->arrayScans
 * by N, causing gincostestimate to scale up its estimates accordingly.
 */
static bool
gincost_scalararrayopexpr(PlannerInfo *root,
						  IndexOptInfo *index,
						  int indexcol,
						  ScalarArrayOpExpr *clause,
						  double numIndexEntries,
						  GinQualCounts *counts)
{
	Oid			clause_op = clause->opno;
	Node	   *rightop = (Node *) lsecond(clause->args);
	ArrayType  *arrayval;
	int16		elmlen;
	bool		elmbyval;
	char		elmalign;
	int			numElems;
	Datum	   *elemValues;
	bool	   *elemNulls;
	GinQualCounts arraycounts;
	int			numPossible = 0;
	int			i;

	Assert(clause->useOr);

	/* aggressively reduce to a constant, and look through relabeling */
	rightop = estimate_expression_value(root, rightop);

	if (IsA(rightop, RelabelType))
		rightop = (Node *) ((RelabelType *) rightop)->arg;

	/*
	 * It's impossible to call extractQuery method for unknown operand. So
	 * unless operand is a Const we can't do much; just assume there will be
	 * one ordinary search entry from each array entry at runtime, and fall
	 * back on a probably-bad estimate of the number of array entries.
	 */
	if (!IsA(rightop, Const))
	{
		counts->exactEntries++;
		counts->searchEntries++;
		counts->arrayScans *= estimate_array_length(root, rightop);
		return true;
	}

	/* If Const is null, there can be no matches */
	if (((Const *) rightop)->constisnull)
		return false;

	/* Otherwise, extract the array elements and iterate over them */
	arrayval = DatumGetArrayTypeP(((Const *) rightop)->constvalue);
	get_typlenbyvalalign(ARR_ELEMTYPE(arrayval),
						 &elmlen, &elmbyval, &elmalign);
	deconstruct_array(arrayval,
					  ARR_ELEMTYPE(arrayval),
					  elmlen, elmbyval, elmalign,
					  &elemValues, &elemNulls, &numElems);

	memset(&arraycounts, 0, sizeof(arraycounts));

	for (i = 0; i < numElems; i++)
	{
		GinQualCounts elemcounts;

		/* NULL can't match anything, so ignore, as the executor will */
		if (elemNulls[i])
			continue;

		/* Otherwise, apply extractQuery and get the actual term counts */
		memset(&elemcounts, 0, sizeof(elemcounts));

		if (gincost_pattern(index, indexcol, clause_op, elemValues[i],
							&elemcounts))
		{
			/* We ignore array elements that are unsatisfiable patterns */
			numPossible++;

			if (elemcounts.attHasFullScan[indexcol] &&
				!elemcounts.attHasNormalScan[indexcol])
			{
				/*
				 * Full index scan will be required.  We treat this as if
				 * every key in the index had been listed in the query; is
				 * that reasonable?
				 */
				elemcounts.partialEntries = 0;
				elemcounts.exactEntries = numIndexEntries;
				elemcounts.searchEntries = numIndexEntries;
			}
			arraycounts.partialEntries += elemcounts.partialEntries;
			arraycounts.exactEntries += elemcounts.exactEntries;
			arraycounts.searchEntries += elemcounts.searchEntries;
		}
	}

	if (numPossible == 0)
	{
		/* No satisfiable patterns in the array */
		return false;
	}

	/*
	 * Now add the averages to the global counts.  This will give us an
	 * estimate of the average number of terms searched for in each indexscan,
	 * including contributions from both array and non-array quals.
	 */
	counts->partialEntries += arraycounts.partialEntries / numPossible;
	counts->exactEntries += arraycounts.exactEntries / numPossible;
	counts->searchEntries += arraycounts.searchEntries / numPossible;

	counts->arrayScans *= numPossible;

	return true;
}

/*
 * GIN has search behavior completely different from other index types
 */
void
gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
				Cost *indexStartupCost, Cost *indexTotalCost,
				Selectivity *indexSelectivity, double *indexCorrelation,
				double *indexPages)
{
	IndexOptInfo *index = path->indexinfo;
	List	   *indexQuals = get_quals_from_indexclauses(path->indexclauses);
	List	   *selectivityQuals;
	double		numPages = index->pages,
				numTuples = index->tuples;
	double		numEntryPages,
				numDataPages,
				numPendingPages,
				numEntries;
	GinQualCounts counts;
	bool		matchPossible;
	bool		fullIndexScan;
	double		partialScale;
	double		entryPagesFetched,
				dataPagesFetched,
				dataPagesFetchedBySel;
	double		qual_op_cost,
				qual_arg_cost,
				spc_random_page_cost,
				outer_scans;
	Cost		descentCost;
	Relation	indexRel;
	GinStatsData ginStats;
	ListCell   *lc;
	int			i;

	/*
	 * Obtain statistical information from the meta page, if possible.  Else
	 * set ginStats to zeroes, and we'll cope below.
	 */
	if (!index->hypothetical)
	{
		/* Lock should have already been obtained in plancat.c */
		indexRel = index_open(index->indexoid, NoLock);
		ginGetStats(indexRel, &ginStats);
		index_close(indexRel, NoLock);
	}
	else
	{
		memset(&ginStats, 0, sizeof(ginStats));
	}

	/*
	 * Assuming we got valid (nonzero) stats at all, nPendingPages can be
	 * trusted, but the other fields are data as of the last VACUUM.  We can
	 * scale them up to account for growth since then, but that method only
	 * goes so far; in the worst case, the stats might be for a completely
	 * empty index, and scaling them will produce pretty bogus numbers.
	 * Somewhat arbitrarily, set the cutoff for doing scaling at 4X growth; if
	 * it's grown more than that, fall back to estimating things only from the
	 * assumed-accurate index size.  But we'll trust nPendingPages in any case
	 * so long as it's not clearly insane, ie, more than the index size.
	 */
	if (ginStats.nPendingPages < numPages)
		numPendingPages = ginStats.nPendingPages;
	else
		numPendingPages = 0;

	if (numPages > 0 && ginStats.nTotalPages <= numPages &&
		ginStats.nTotalPages > numPages / 4 &&
		ginStats.nEntryPages > 0 && ginStats.nEntries > 0)
	{
		/*
		 * OK, the stats seem close enough to sane to be trusted.  But we
		 * still need to scale them by the ratio numPages / nTotalPages to
		 * account for growth since the last VACUUM.
		 */
		double		scale = numPages / ginStats.nTotalPages;

		numEntryPages = ceil(ginStats.nEntryPages * scale);
		numDataPages = ceil(ginStats.nDataPages * scale);
		numEntries = ceil(ginStats.nEntries * scale);
		/* ensure we didn't round up too much */
		numEntryPages = Min(numEntryPages, numPages - numPendingPages);
		numDataPages = Min(numDataPages,
						   numPages - numPendingPages - numEntryPages);
	}
	else
	{
		/*
		 * We might get here because it's a hypothetical index, or an index
		 * created pre-9.1 and never vacuumed since upgrading (in which case
		 * its stats would read as zeroes), or just because it's grown too
		 * much since the last VACUUM for us to put our faith in scaling.
		 *
		 * Invent some plausible internal statistics based on the index page
		 * count (and clamp that to at least 10 pages, just in case).  We
		 * estimate that 90% of the index is entry pages, and the rest is data
		 * pages.  Estimate 100 entries per entry page; this is rather bogus
		 * since it'll depend on the size of the keys, but it's more robust
		 * than trying to predict the number of entries per heap tuple.
		 */
		numPages = Max(numPages, 10);
		numEntryPages = floor((numPages - numPendingPages) * 0.90);
		numDataPages = numPages - numPendingPages - numEntryPages;
		numEntries = floor(numEntryPages * 100);
	}

	/* In an empty index, numEntries could be zero.  Avoid divide-by-zero */
	if (numEntries < 1)
		numEntries = 1;

	/*
	 * If the index is partial, AND the index predicate with the index-bound
	 * quals to produce a more accurate idea of the number of rows covered by
	 * the bound conditions.
	 */
	selectivityQuals = add_predicate_to_index_quals(index, indexQuals);

	/* Estimate the fraction of main-table tuples that will be visited */
	*indexSelectivity = clauselist_selectivity(root, selectivityQuals,
											   index->rel->relid,
											   JOIN_INNER,
											   NULL);

	/* fetch estimated page cost for tablespace containing index */
	get_tablespace_page_costs(index->reltablespace,
							  &spc_random_page_cost,
							  NULL);

	/*
	 * Generic assumption about index correlation: there isn't any.
	 */
	*indexCorrelation = 0.0;

	/*
	 * Examine quals to estimate number of search entries & partial matches
	 */
	memset(&counts, 0, sizeof(counts));
	counts.arrayScans = 1;
	matchPossible = true;

	foreach(lc, path->indexclauses)
	{
		IndexClause *iclause = lfirst_node(IndexClause, lc);
		ListCell   *lc2;

		foreach(lc2, iclause->indexquals)
		{
			RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc2);
			Expr	   *clause = rinfo->clause;

			if (IsA(clause, OpExpr))
			{
				matchPossible = gincost_opexpr(root,
											   index,
											   iclause->indexcol,
											   (OpExpr *) clause,
											   &counts);
				if (!matchPossible)
					break;
			}
			else if (IsA(clause, ScalarArrayOpExpr))
			{
				matchPossible = gincost_scalararrayopexpr(root,
														  index,
														  iclause->indexcol,
														  (ScalarArrayOpExpr *) clause,
														  numEntries,
														  &counts);
				if (!matchPossible)
					break;
			}
			else
			{
				/* shouldn't be anything else for a GIN index */
				elog(ERROR, "unsupported GIN indexqual type: %d",
					 (int) nodeTag(clause));
			}
		}
	}

	/* Fall out if there were any provably-unsatisfiable quals */
	if (!matchPossible)
	{
		*indexStartupCost = 0;
		*indexTotalCost = 0;
		*indexSelectivity = 0;
		return;
	}

	/*
	 * If attribute has a full scan and at the same time doesn't have normal
	 * scan, then we'll have to scan all non-null entries of that attribute.
	 * Currently, we don't have per-attribute statistics for GIN.  Thus, we
	 * must assume the whole GIN index has to be scanned in this case.
	 */
	fullIndexScan = false;
	for (i = 0; i < index->nkeycolumns; i++)
	{
		if (counts.attHasFullScan[i] && !counts.attHasNormalScan[i])
		{
			fullIndexScan = true;
			break;
		}
	}

	if (fullIndexScan || indexQuals == NIL)
	{
		/*
		 * Full index scan will be required.  We treat this as if every key in
		 * the index had been listed in the query; is that reasonable?
		 */
		counts.partialEntries = 0;
		counts.exactEntries = numEntries;
		counts.searchEntries = numEntries;
	}

	/* Will we have more than one iteration of a nestloop scan? */
	outer_scans = loop_count;

	/*
	 * Compute cost to begin scan, first of all, pay attention to pending
	 * list.
	 */
	entryPagesFetched = numPendingPages;

	/*
	 * Estimate number of entry pages read.  We need to do
	 * counts.searchEntries searches.  Use a power function as it should be,
	 * but tuples on leaf pages usually is much greater. Here we include all
	 * searches in entry tree, including search of first entry in partial
	 * match algorithm
	 */
	entryPagesFetched += ceil(counts.searchEntries * rint(pow(numEntryPages, 0.15)));

	/*
	 * Add an estimate of entry pages read by partial match algorithm. It's a
	 * scan over leaf pages in entry tree.  We haven't any useful stats here,
	 * so estimate it as proportion.  Because counts.partialEntries is really
	 * pretty bogus (see code above), it's possible that it is more than
	 * numEntries; clamp the proportion to ensure sanity.
	 */
	partialScale = counts.partialEntries / numEntries;
	partialScale = Min(partialScale, 1.0);

	entryPagesFetched += ceil(numEntryPages * partialScale);

	/*
	 * Partial match algorithm reads all data pages before doing actual scan,
	 * so it's a startup cost.  Again, we haven't any useful stats here, so
	 * estimate it as proportion.
	 */
	dataPagesFetched = ceil(numDataPages * partialScale);

	*indexStartupCost = 0;
	*indexTotalCost = 0;

	/*
	 * Add a CPU-cost component to represent the costs of initial entry btree
	 * descent.  We don't charge any I/O cost for touching upper btree levels,
	 * since they tend to stay in cache, but we still have to do about log2(N)
	 * comparisons to descend a btree of N leaf tuples.  We charge one
	 * cpu_operator_cost per comparison.
	 *
	 * If there are ScalarArrayOpExprs, charge this once per SA scan.  The
	 * ones after the first one are not startup cost so far as the overall
	 * plan is concerned, so add them only to "total" cost.
	 */
	if (numEntries > 1)			/* avoid computing log(0) */
	{
		descentCost = ceil(log(numEntries) / log(2.0)) * cpu_operator_cost;
		*indexStartupCost += descentCost * counts.searchEntries;
		*indexTotalCost += counts.arrayScans * descentCost * counts.searchEntries;
	}

	/*
	 * Add a cpu cost per entry-page fetched. This is not amortized over a
	 * loop.
	 */
	*indexStartupCost += entryPagesFetched * DEFAULT_PAGE_CPU_MULTIPLIER * cpu_operator_cost;
	*indexTotalCost += entryPagesFetched * counts.arrayScans * DEFAULT_PAGE_CPU_MULTIPLIER * cpu_operator_cost;

	/*
	 * Add a cpu cost per data-page fetched. This is also not amortized over a
	 * loop. Since those are the data pages from the partial match algorithm,
	 * charge them as startup cost.
	 */
	*indexStartupCost += DEFAULT_PAGE_CPU_MULTIPLIER * cpu_operator_cost * dataPagesFetched;

	/*
	 * Since we add the startup cost to the total cost later on, remove the
	 * initial arrayscan from the total.
	 */
	*indexTotalCost += dataPagesFetched * (counts.arrayScans - 1) * DEFAULT_PAGE_CPU_MULTIPLIER * cpu_operator_cost;

	/*
	 * Calculate cache effects if more than one scan due to nestloops or array
	 * quals.  The result is pro-rated per nestloop scan, but the array qual
	 * factor shouldn't be pro-rated (compare genericcostestimate).
	 */
	if (outer_scans > 1 || counts.arrayScans > 1)
	{
		entryPagesFetched *= outer_scans * counts.arrayScans;
		entryPagesFetched = index_pages_fetched(entryPagesFetched,
												(BlockNumber) numEntryPages,
												numEntryPages, root);
		entryPagesFetched /= outer_scans;
		dataPagesFetched *= outer_scans * counts.arrayScans;
		dataPagesFetched = index_pages_fetched(dataPagesFetched,
											   (BlockNumber) numDataPages,
											   numDataPages, root);
		dataPagesFetched /= outer_scans;
	}

	/*
	 * Here we use random page cost because logically-close pages could be far
	 * apart on disk.
	 */
	*indexStartupCost += (entryPagesFetched + dataPagesFetched) * spc_random_page_cost;

	/*
	 * Now compute the number of data pages fetched during the scan.
	 *
	 * We assume every entry to have the same number of items, and that there
	 * is no overlap between them. (XXX: tsvector and array opclasses collect
	 * statistics on the frequency of individual keys; it would be nice to use
	 * those here.)
	 */
	dataPagesFetched = ceil(numDataPages * counts.exactEntries / numEntries);

	/*
	 * If there is a lot of overlap among the entries, in particular if one of
	 * the entries is very frequent, the above calculation can grossly
	 * under-estimate.  As a simple cross-check, calculate a lower bound based
	 * on the overall selectivity of the quals.  At a minimum, we must read
	 * one item pointer for each matching entry.
	 *
	 * The width of each item pointer varies, based on the level of
	 * compression.  We don't have statistics on that, but an average of
	 * around 3 bytes per item is fairly typical.
	 */
	dataPagesFetchedBySel = ceil(*indexSelectivity *
								 (numTuples / (BLCKSZ / 3)));
	if (dataPagesFetchedBySel > dataPagesFetched)
		dataPagesFetched = dataPagesFetchedBySel;

	/* Add one page cpu-cost to the startup cost */
	*indexStartupCost += DEFAULT_PAGE_CPU_MULTIPLIER * cpu_operator_cost * counts.searchEntries;

	/*
	 * Add once again a CPU-cost for those data pages, before amortizing for
	 * cache.
	 */
	*indexTotalCost += dataPagesFetched * counts.arrayScans * DEFAULT_PAGE_CPU_MULTIPLIER * cpu_operator_cost;

	/* Account for cache effects, the same as above */
	if (outer_scans > 1 || counts.arrayScans > 1)
	{
		dataPagesFetched *= outer_scans * counts.arrayScans;
		dataPagesFetched = index_pages_fetched(dataPagesFetched,
											   (BlockNumber) numDataPages,
											   numDataPages, root);
		dataPagesFetched /= outer_scans;
	}

	/* And apply random_page_cost as the cost per page */
	*indexTotalCost += *indexStartupCost +
		dataPagesFetched * spc_random_page_cost;

	/*
	 * Add on index qual eval costs, much as in genericcostestimate. We charge
	 * cpu but we can disregard indexorderbys, since GIN doesn't support
	 * those.
	 */
	qual_arg_cost = index_other_operands_eval_cost(root, indexQuals);
	qual_op_cost = cpu_operator_cost * list_length(indexQuals);

	*indexStartupCost += qual_arg_cost;
	*indexTotalCost += qual_arg_cost;

	/*
	 * Add a cpu cost per search entry, corresponding to the actual visited
	 * entries.
	 */
	*indexTotalCost += (counts.searchEntries * counts.arrayScans) * (qual_op_cost);
	/* Now add a cpu cost per tuple in the posting lists / trees */
	*indexTotalCost += (numTuples * *indexSelectivity) * (cpu_index_tuple_cost);
	*indexPages = dataPagesFetched;
}

/*
 * BRIN has search behavior completely different from other index types
 */
void
brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
				 Cost *indexStartupCost, Cost *indexTotalCost,
				 Selectivity *indexSelectivity, double *indexCorrelation,
				 double *indexPages)
{
	IndexOptInfo *index = path->indexinfo;
	List	   *indexQuals = get_quals_from_indexclauses(path->indexclauses);
	double		numPages = index->pages;
	RelOptInfo *baserel = index->rel;
	RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root);
	Cost		spc_seq_page_cost;
	Cost		spc_random_page_cost;
	double		qual_arg_cost;
	double		qualSelectivity;
	BrinStatsData statsData;
	double		indexRanges;
	double		minimalRanges;
	double		estimatedRanges;
	double		selec;
	Relation	indexRel;
	ListCell   *l;
	VariableStatData vardata;

	Assert(rte->rtekind == RTE_RELATION);

	/* fetch estimated page cost for the tablespace containing the index */
	get_tablespace_page_costs(index->reltablespace,
							  &spc_random_page_cost,
							  &spc_seq_page_cost);

	/*
	 * Obtain some data from the index itself, if possible.  Otherwise invent
	 * some plausible internal statistics based on the relation page count.
	 */
	if (!index->hypothetical)
	{
		/*
		 * A lock should have already been obtained on the index in plancat.c.
		 */
		indexRel = index_open(index->indexoid, NoLock);
		brinGetStats(indexRel, &statsData);
		index_close(indexRel, NoLock);

		/* work out the actual number of ranges in the index */
		indexRanges = Max(ceil((double) baserel->pages /
							   statsData.pagesPerRange), 1.0);
	}
	else
	{
		/*
		 * Assume default number of pages per range, and estimate the number
		 * of ranges based on that.
		 */
		indexRanges = Max(ceil((double) baserel->pages /
							   BRIN_DEFAULT_PAGES_PER_RANGE), 1.0);

		statsData.pagesPerRange = BRIN_DEFAULT_PAGES_PER_RANGE;
		statsData.revmapNumPages = (indexRanges / REVMAP_PAGE_MAXITEMS) + 1;
	}

	/*
	 * Compute index correlation
	 *
	 * Because we can use all index quals equally when scanning, we can use
	 * the largest correlation (in absolute value) among columns used by the
	 * query.  Start at zero, the worst possible case.  If we cannot find any
	 * correlation statistics, we will keep it as 0.
	 */
	*indexCorrelation = 0;

	foreach(l, path->indexclauses)
	{
		IndexClause *iclause = lfirst_node(IndexClause, l);
		AttrNumber	attnum = index->indexkeys[iclause->indexcol];

		/* attempt to lookup stats in relation for this index column */
		if (attnum != 0)
		{
			/* Simple variable -- look to stats for the underlying table */
			if (get_relation_stats_hook &&
				(*get_relation_stats_hook) (root, rte, attnum, &vardata))
			{
				/*
				 * The hook took control of acquiring a stats tuple.  If it
				 * did supply a tuple, it'd better have supplied a freefunc.
				 */
				if (HeapTupleIsValid(vardata.statsTuple) && !vardata.freefunc)
					elog(ERROR,
						 "no function provided to release variable stats with");
			}
			else
			{
				vardata.statsTuple =
					SearchSysCache3(STATRELATTINH,
									ObjectIdGetDatum(rte->relid),
									Int16GetDatum(attnum),
									BoolGetDatum(false));
				vardata.freefunc = ReleaseSysCache;
			}
		}
		else
		{
			/*
			 * Looks like we've found an expression column in the index. Let's
			 * see if there's any stats for it.
			 */

			/* get the attnum from the 0-based index. */
			attnum = iclause->indexcol + 1;

			if (get_index_stats_hook &&
				(*get_index_stats_hook) (root, index->indexoid, attnum, &vardata))
			{
				/*
				 * The hook took control of acquiring a stats tuple.  If it
				 * did supply a tuple, it'd better have supplied a freefunc.
				 */
				if (HeapTupleIsValid(vardata.statsTuple) &&
					!vardata.freefunc)
					elog(ERROR, "no function provided to release variable stats with");
			}
			else
			{
				vardata.statsTuple = SearchSysCache3(STATRELATTINH,
													 ObjectIdGetDatum(index->indexoid),
													 Int16GetDatum(attnum),
													 BoolGetDatum(false));
				vardata.freefunc = ReleaseSysCache;
			}
		}

		if (HeapTupleIsValid(vardata.statsTuple))
		{
			AttStatsSlot sslot;

			if (get_attstatsslot(&sslot, vardata.statsTuple,
								 STATISTIC_KIND_CORRELATION, InvalidOid,
								 ATTSTATSSLOT_NUMBERS))
			{
				double		varCorrelation = 0.0;

				if (sslot.nnumbers > 0)
					varCorrelation = fabs(sslot.numbers[0]);

				if (varCorrelation > *indexCorrelation)
					*indexCorrelation = varCorrelation;

				free_attstatsslot(&sslot);
			}
		}

		ReleaseVariableStats(vardata);
	}

	qualSelectivity = clauselist_selectivity(root, indexQuals,
											 baserel->relid,
											 JOIN_INNER, NULL);

	/*
	 * Now calculate the minimum possible ranges we could match with if all of
	 * the rows were in the perfect order in the table's heap.
	 */
	minimalRanges = ceil(indexRanges * qualSelectivity);

	/*
	 * Now estimate the number of ranges that we'll touch by using the
	 * indexCorrelation from the stats. Careful not to divide by zero (note
	 * we're using the absolute value of the correlation).
	 */
	if (*indexCorrelation < 1.0e-10)
		estimatedRanges = indexRanges;
	else
		estimatedRanges = Min(minimalRanges / *indexCorrelation, indexRanges);

	/* we expect to visit this portion of the table */
	selec = estimatedRanges / indexRanges;

	CLAMP_PROBABILITY(selec);

	*indexSelectivity = selec;

	/*
	 * Compute the index qual costs, much as in genericcostestimate, to add to
	 * the index costs.  We can disregard indexorderbys, since BRIN doesn't
	 * support those.
	 */
	qual_arg_cost = index_other_operands_eval_cost(root, indexQuals);

	/*
	 * Compute the startup cost as the cost to read the whole revmap
	 * sequentially, including the cost to execute the index quals.
	 */
	*indexStartupCost =
		spc_seq_page_cost * statsData.revmapNumPages * loop_count;
	*indexStartupCost += qual_arg_cost;

	/*
	 * To read a BRIN index there might be a bit of back and forth over
	 * regular pages, as revmap might point to them out of sequential order;
	 * calculate the total cost as reading the whole index in random order.
	 */
	*indexTotalCost = *indexStartupCost +
		spc_random_page_cost * (numPages - statsData.revmapNumPages) * loop_count;

	/*
	 * Charge a small amount per range tuple which we expect to match to. This
	 * is meant to reflect the costs of manipulating the bitmap. The BRIN scan
	 * will set a bit for each page in the range when we find a matching
	 * range, so we must multiply the charge by the number of pages in the
	 * range.
	 */
	*indexTotalCost += 0.1 * cpu_operator_cost * estimatedRanges *
		statsData.pagesPerRange;

	*indexPages = index->pages;
}
./toasting.c0000664000175000017500000003170515221603750011710 0ustar  xmanxman/*-------------------------------------------------------------------------
 *
 * toasting.c
 *	  This file contains routines to support creation of toast tables
 *
 *
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *	  src/backend/catalog/toasting.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/genam.h"
#include "access/heapam.h"
#include "access/toast_compression.h"
#include "access/xact.h"
#include "catalog/binary_upgrade.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/namespace.h"
#include "catalog/pg_am.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/toasting.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "utils/fmgroids.h"
#include "utils/rel.h"
#include "utils/syscache.h"

static void CheckAndCreateToastTable(Oid relOid, Datum reloptions,
									 LOCKMODE lockmode, bool check,
									 Oid OIDOldToast);
static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
							   Datum reloptions, LOCKMODE lockmode, bool check,
							   Oid OIDOldToast);
static bool needs_toast_table(Relation rel);


/*
 * CreateToastTable variants
 *		If the table needs a toast table, and doesn't already have one,
 *		then create a toast table for it.
 *
 * reloptions for the toast table can be passed, too.  Pass (Datum) 0
 * for default reloptions.
 *
 * We expect the caller to have verified that the relation is a table and have
 * already done any necessary permission checks.  Callers expect this function
 * to end with CommandCounterIncrement if it makes any changes.
 */
void
AlterTableCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode)
{
	CheckAndCreateToastTable(relOid, reloptions, lockmode, true, InvalidOid);
}

void
NewHeapCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode,
						Oid OIDOldToast)
{
	CheckAndCreateToastTable(relOid, reloptions, lockmode, false, OIDOldToast);
}

void
NewRelationCreateToastTable(Oid relOid, Datum reloptions)
{
	CheckAndCreateToastTable(relOid, reloptions, AccessExclusiveLock, false,
							 InvalidOid);
}

static void
CheckAndCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode,
						 bool check, Oid OIDOldToast)
{
	Relation	rel;

	rel = table_open(relOid, lockmode);

	/* create_toast_table does all the work */
	(void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions, lockmode,
							  check, OIDOldToast);

	table_close(rel, NoLock);
}

/*
 * Create a toast table during bootstrap
 *
 * Here we need to prespecify the OIDs of the toast table and its index
 */
void
BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid)
{
	Relation	rel;

	rel = table_openrv(makeRangeVar(NULL, relName, -1), AccessExclusiveLock);

	if (rel->rd_rel->relkind != RELKIND_RELATION &&
		rel->rd_rel->relkind != RELKIND_MATVIEW)
		elog(ERROR, "\"%s\" is not a table or materialized view",
			 relName);

	/* create_toast_table does all the work */
	if (!create_toast_table(rel, toastOid, toastIndexOid, (Datum) 0,
							AccessExclusiveLock, false, InvalidOid))
		elog(ERROR, "\"%s\" does not require a toast table",
			 relName);

	table_close(rel, NoLock);
}


/*
 * create_toast_table --- internal workhorse
 *
 * rel is already opened and locked
 * toastOid and toastIndexOid are normally InvalidOid, but during
 * bootstrap they can be nonzero to specify hand-assigned OIDs
 */
static bool
create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
				   Datum reloptions, LOCKMODE lockmode, bool check,
				   Oid OIDOldToast)
{
	Oid			relOid = RelationGetRelid(rel);
	HeapTuple	reltup;
	TupleDesc	tupdesc;
	bool		shared_relation;
	bool		mapped_relation;
	Relation	toast_rel;
	Relation	class_rel;
	Oid			toast_relid;
	Oid			namespaceid;
	char		toast_relname[NAMEDATALEN];
	char		toast_idxname[NAMEDATALEN];
	IndexInfo  *indexInfo;
	Oid			collationIds[2];
	Oid			opclassIds[2];
	int16		coloptions[2];
	ObjectAddress baseobject,
				toastobject;

	/*
	 * Is it already toasted?
	 */
	if (rel->rd_rel->reltoastrelid != InvalidOid)
		return false;

	/*
	 * Check to see whether the table actually needs a TOAST table.
	 */
	if (!IsBinaryUpgrade)
	{
		/* Normal mode, normal check */
		if (!needs_toast_table(rel))
			return false;
	}
	else
	{
		/*
		 * In binary-upgrade mode, create a TOAST table if and only if
		 * pg_upgrade told us to (ie, a TOAST table OID has been provided).
		 *
		 * This indicates that the old cluster had a TOAST table for the
		 * current table.  We must create a TOAST table to receive the old
		 * TOAST file, even if the table seems not to need one.
		 *
		 * Contrariwise, if the old cluster did not have a TOAST table, we
		 * should be able to get along without one even if the new version's
		 * needs_toast_table rules suggest we should have one.  There is a lot
		 * of daylight between where we will create a TOAST table and where
		 * one is really necessary to avoid failures, so small cross-version
		 * differences in the when-to-create heuristic shouldn't be a problem.
		 * If we tried to create a TOAST table anyway, we would have the
		 * problem that it might take up an OID that will conflict with some
		 * old-cluster table we haven't seen yet.
		 */
		if (!OidIsValid(binary_upgrade_next_toast_pg_class_oid))
			return false;
	}

	/*
	 * If requested check lockmode is sufficient. This is a cross check in
	 * case of errors or conflicting decisions in earlier code.
	 */
	if (check && lockmode != AccessExclusiveLock)
		elog(ERROR, "AccessExclusiveLock required to add toast table.");

	/*
	 * Create the toast table and its index
	 */
	snprintf(toast_relname, sizeof(toast_relname),
			 "pg_toast_%u", relOid);
	snprintf(toast_idxname, sizeof(toast_idxname),
			 "pg_toast_%u_index", relOid);

	/* this is pretty painful...  need a tuple descriptor */
	tupdesc = CreateTemplateTupleDesc(3);
	TupleDescInitEntry(tupdesc, (AttrNumber) 1,
					   "chunk_id",
					   OIDOID,
					   -1, 0);
	TupleDescInitEntry(tupdesc, (AttrNumber) 2,
					   "chunk_seq",
					   INT4OID,
					   -1, 0);
	TupleDescInitEntry(tupdesc, (AttrNumber) 3,
					   "chunk_data",
					   BYTEAOID,
					   -1, 0);

	/*
	 * Ensure that the toast table doesn't itself get toasted, or we'll be
	 * toast :-(.  This is essential for chunk_data because type bytea is
	 * toastable; hit the other two just to be sure.
	 */
	TupleDescAttr(tupdesc, 0)->attstorage = TYPSTORAGE_PLAIN;
	TupleDescAttr(tupdesc, 1)->attstorage = TYPSTORAGE_PLAIN;
	TupleDescAttr(tupdesc, 2)->attstorage = TYPSTORAGE_PLAIN;

	/* Toast field should not be compressed */
	TupleDescAttr(tupdesc, 0)->attcompression = InvalidCompressionMethod;
	TupleDescAttr(tupdesc, 1)->attcompression = InvalidCompressionMethod;
	TupleDescAttr(tupdesc, 2)->attcompression = InvalidCompressionMethod;

	populate_compact_attribute(tupdesc, 0);
	populate_compact_attribute(tupdesc, 1);
	populate_compact_attribute(tupdesc, 2);

	TupleDescFinalize(tupdesc);

	/*
	 * Toast tables for regular relations go in pg_toast; those for temp
	 * relations go into the per-backend temp-toast-table namespace.
	 */
	if (isTempOrTempToastNamespace(rel->rd_rel->relnamespace))
		namespaceid = GetTempToastNamespace();
	else
		namespaceid = PG_TOAST_NAMESPACE;

	/* Toast table is shared if and only if its parent is. */
	shared_relation = rel->rd_rel->relisshared;

	/* It's mapped if and only if its parent is, too */
	mapped_relation = RelationIsMapped(rel);

	toast_relid = heap_create_with_catalog(toast_relname,
										   namespaceid,
										   rel->rd_rel->reltablespace,
										   toastOid,
										   InvalidOid,
										   InvalidOid,
										   rel->rd_rel->relowner,
										   table_relation_toast_am(rel),
										   tupdesc,
										   NIL,
										   RELKIND_TOASTVALUE,
										   rel->rd_rel->relpersistence,
										   shared_relation,
										   mapped_relation,
										   ONCOMMIT_NOOP,
										   reloptions,
										   false,
										   true,
										   true,
										   OIDOldToast,
										   NULL);
	Assert(toast_relid != InvalidOid);

	/* make the toast relation visible, else table_open will fail */
	CommandCounterIncrement();

	/* ShareLock is not really needed here, but take it anyway */
	toast_rel = table_open(toast_relid, ShareLock);

	/*
	 * Create unique index on chunk_id, chunk_seq.
	 *
	 * NOTE: the normal TOAST access routines could actually function with a
	 * single-column index on chunk_id only. However, the slice access
	 * routines use both columns for faster access to an individual chunk. In
	 * addition, we want it to be unique as a check against the possibility of
	 * duplicate TOAST chunk OIDs. The index might also be a little more
	 * efficient this way, since btree isn't all that happy with large numbers
	 * of equal keys.
	 */

	indexInfo = makeNode(IndexInfo);
	indexInfo->ii_NumIndexAttrs = 2;
	indexInfo->ii_NumIndexKeyAttrs = 2;
	indexInfo->ii_IndexAttrNumbers[0] = 1;
	indexInfo->ii_IndexAttrNumbers[1] = 2;
	indexInfo->ii_Expressions = NIL;
	indexInfo->ii_ExpressionsExpand = NIL;
	indexInfo->ii_ExpressionsState = NIL;
	indexInfo->ii_ExpressionsExpandState = NIL;
	indexInfo->ii_Predicate = NIL;
	indexInfo->ii_PredicateExpand = NIL;
	indexInfo->ii_PredicateState = NULL;
	indexInfo->ii_PredicateExpandState = NULL;
	indexInfo->ii_ExclusionOps = NULL;
	indexInfo->ii_ExclusionProcs = NULL;
	indexInfo->ii_ExclusionStrats = NULL;
	indexInfo->ii_Unique = true;
	indexInfo->ii_NullsNotDistinct = false;
	indexInfo->ii_ReadyForInserts = true;
	indexInfo->ii_CheckedUnchanged = false;
	indexInfo->ii_IndexUnchanged = false;
	indexInfo->ii_Concurrent = false;
	indexInfo->ii_BrokenHotChain = false;
	indexInfo->ii_ParallelWorkers = 0;
	indexInfo->ii_Am = BTREE_AM_OID;
	indexInfo->ii_AmCache = NULL;
	indexInfo->ii_Context = CurrentMemoryContext;

	collationIds[0] = InvalidOid;
	collationIds[1] = InvalidOid;

	opclassIds[0] = OID_BTREE_OPS_OID;
	opclassIds[1] = INT4_BTREE_OPS_OID;

	coloptions[0] = 0;
	coloptions[1] = 0;

	index_create(toast_rel, toast_idxname, toastIndexOid, InvalidOid,
				 InvalidOid, InvalidOid,
				 indexInfo,
				 list_make2("chunk_id", "chunk_seq"),
				 BTREE_AM_OID,
				 rel->rd_rel->reltablespace,
				 collationIds, opclassIds, NULL, coloptions, NULL, (Datum) 0,
				 INDEX_CREATE_IS_PRIMARY, 0, true, true, NULL);

	table_close(toast_rel, NoLock);

	/*
	 * Store the toast table's OID in the parent relation's pg_class row
	 */
	class_rel = table_open(RelationRelationId, RowExclusiveLock);

	if (!IsBootstrapProcessingMode())
	{
		/* normal case, use a transactional update */
		reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid));
		if (!HeapTupleIsValid(reltup))
			elog(ERROR, "cache lookup failed for relation %u", relOid);

		((Form_pg_class) GETSTRUCT(reltup))->reltoastrelid = toast_relid;

		CatalogTupleUpdate(class_rel, &reltup->t_self, reltup);
	}
	else
	{
		/* While bootstrapping, we cannot UPDATE, so overwrite in-place */

		ScanKeyData key[1];
		void	   *state;

		ScanKeyInit(&key[0],
					Anum_pg_class_oid,
					BTEqualStrategyNumber, F_OIDEQ,
					ObjectIdGetDatum(relOid));
		systable_inplace_update_begin(class_rel, ClassOidIndexId, true,
									  NULL, 1, key, &reltup, &state);
		if (!HeapTupleIsValid(reltup))
			elog(ERROR, "cache lookup failed for relation %u", relOid);

		((Form_pg_class) GETSTRUCT(reltup))->reltoastrelid = toast_relid;

		systable_inplace_update_finish(state, reltup);
	}

	heap_freetuple(reltup);

	table_close(class_rel, RowExclusiveLock);

	/*
	 * Register dependency from the toast table to the main, so that the toast
	 * table will be deleted if the main is.  Skip this in bootstrap mode.
	 */
	if (!IsBootstrapProcessingMode())
	{
		baseobject.classId = RelationRelationId;
		baseobject.objectId = relOid;
		baseobject.objectSubId = 0;
		toastobject.classId = RelationRelationId;
		toastobject.objectId = toast_relid;
		toastobject.objectSubId = 0;

		recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL);
	}

	/*
	 * Make changes visible
	 */
	CommandCounterIncrement();

	return true;
}

/*
 * Check to see whether the table needs a TOAST table.
 */
static bool
needs_toast_table(Relation rel)
{
	/*
	 * No need to create a TOAST table for partitioned tables.
	 */
	if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
		return false;

	/*
	 * We cannot allow toasting a shared relation after initdb (because
	 * there's no way to mark it toasted in other databases' pg_class).
	 */
	if (rel->rd_rel->relisshared && !IsBootstrapProcessingMode())
		return false;

	/*
	 * Ignore attempts to create toast tables on catalog tables after initdb.
	 * Which catalogs get toast tables is explicitly chosen in catalog/pg_*.h.
	 * (We could get here via some ALTER TABLE command if the catalog doesn't
	 * have a toast table.)
	 */
	if (IsCatalogRelation(rel) && !IsBootstrapProcessingMode())
		return false;

	/* Otherwise, let the AM decide. */
	return table_relation_needs_toast_table(rel);
}