Files
Michael Paquier 39866242e5 Fix a set of typos and grammar issues across the tree
This batch is similar to 0c38161559 and addresses a variety of code
style issues, including grammar mistakes, typos, inconsistent variable
names in function declarations, and incorrect function names in comments
and documentation.  These fixes have accumulated on the community
mailing lists since the commit mentioned above.

Notably, Alexander Lakhin previously submitted a patch identifying many
of the trivial typos and grammar issues that had been reported on
pgsql-hackers.  His patch covered a somewhat large portion of the issues
addressed here, though not all of them.

The documentation changes only affect HEAD.
2026-04-21 14:46:22 +09:00

358 lines
8.8 KiB
C

/*-------------------------------------------------------------------------
*
* pgpa_ast.c
* additional supporting code related to plan advice parsing
*
* Copyright (c) 2016-2026, PostgreSQL Global Development Group
*
* contrib/pg_plan_advice/pgpa_ast.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "pgpa_ast.h"
#include "funcapi.h"
#include "utils/array.h"
#include "utils/builtins.h"
static bool pgpa_identifiers_cover_target(int nrids, pgpa_identifier *rids,
pgpa_advice_target *target,
bool *rids_used);
/*
* Get a C string that corresponds to the specified advice tag.
*/
char *
pgpa_cstring_advice_tag(pgpa_advice_tag_type advice_tag)
{
switch (advice_tag)
{
case PGPA_TAG_BITMAP_HEAP_SCAN:
return "BITMAP_HEAP_SCAN";
case PGPA_TAG_DO_NOT_SCAN:
return "DO_NOT_SCAN";
case PGPA_TAG_FOREIGN_JOIN:
return "FOREIGN_JOIN";
case PGPA_TAG_GATHER:
return "GATHER";
case PGPA_TAG_GATHER_MERGE:
return "GATHER_MERGE";
case PGPA_TAG_HASH_JOIN:
return "HASH_JOIN";
case PGPA_TAG_INDEX_ONLY_SCAN:
return "INDEX_ONLY_SCAN";
case PGPA_TAG_INDEX_SCAN:
return "INDEX_SCAN";
case PGPA_TAG_JOIN_ORDER:
return "JOIN_ORDER";
case PGPA_TAG_MERGE_JOIN_MATERIALIZE:
return "MERGE_JOIN_MATERIALIZE";
case PGPA_TAG_MERGE_JOIN_PLAIN:
return "MERGE_JOIN_PLAIN";
case PGPA_TAG_NESTED_LOOP_MATERIALIZE:
return "NESTED_LOOP_MATERIALIZE";
case PGPA_TAG_NESTED_LOOP_MEMOIZE:
return "NESTED_LOOP_MEMOIZE";
case PGPA_TAG_NESTED_LOOP_PLAIN:
return "NESTED_LOOP_PLAIN";
case PGPA_TAG_NO_GATHER:
return "NO_GATHER";
case PGPA_TAG_PARTITIONWISE:
return "PARTITIONWISE";
case PGPA_TAG_SEMIJOIN_NON_UNIQUE:
return "SEMIJOIN_NON_UNIQUE";
case PGPA_TAG_SEMIJOIN_UNIQUE:
return "SEMIJOIN_UNIQUE";
case PGPA_TAG_SEQ_SCAN:
return "SEQ_SCAN";
case PGPA_TAG_TID_SCAN:
return "TID_SCAN";
}
pg_unreachable();
return NULL;
}
/*
* Convert an advice tag, formatted as a string that has already been
* downcased as appropriate, to a pgpa_advice_tag_type.
*
* If we succeed, set *fail = false and return the result; if we fail,
* set *fail = true and return an arbitrary value.
*/
pgpa_advice_tag_type
pgpa_parse_advice_tag(const char *tag, bool *fail)
{
*fail = false;
switch (tag[0])
{
case 'b':
if (strcmp(tag, "bitmap_heap_scan") == 0)
return PGPA_TAG_BITMAP_HEAP_SCAN;
break;
case 'd':
if (strcmp(tag, "do_not_scan") == 0)
return PGPA_TAG_DO_NOT_SCAN;
break;
case 'f':
if (strcmp(tag, "foreign_join") == 0)
return PGPA_TAG_FOREIGN_JOIN;
break;
case 'g':
if (strcmp(tag, "gather") == 0)
return PGPA_TAG_GATHER;
if (strcmp(tag, "gather_merge") == 0)
return PGPA_TAG_GATHER_MERGE;
break;
case 'h':
if (strcmp(tag, "hash_join") == 0)
return PGPA_TAG_HASH_JOIN;
break;
case 'i':
if (strcmp(tag, "index_scan") == 0)
return PGPA_TAG_INDEX_SCAN;
if (strcmp(tag, "index_only_scan") == 0)
return PGPA_TAG_INDEX_ONLY_SCAN;
break;
case 'j':
if (strcmp(tag, "join_order") == 0)
return PGPA_TAG_JOIN_ORDER;
break;
case 'm':
if (strcmp(tag, "merge_join_materialize") == 0)
return PGPA_TAG_MERGE_JOIN_MATERIALIZE;
if (strcmp(tag, "merge_join_plain") == 0)
return PGPA_TAG_MERGE_JOIN_PLAIN;
break;
case 'n':
if (strcmp(tag, "nested_loop_materialize") == 0)
return PGPA_TAG_NESTED_LOOP_MATERIALIZE;
if (strcmp(tag, "nested_loop_memoize") == 0)
return PGPA_TAG_NESTED_LOOP_MEMOIZE;
if (strcmp(tag, "nested_loop_plain") == 0)
return PGPA_TAG_NESTED_LOOP_PLAIN;
if (strcmp(tag, "no_gather") == 0)
return PGPA_TAG_NO_GATHER;
break;
case 'p':
if (strcmp(tag, "partitionwise") == 0)
return PGPA_TAG_PARTITIONWISE;
break;
case 's':
if (strcmp(tag, "semijoin_non_unique") == 0)
return PGPA_TAG_SEMIJOIN_NON_UNIQUE;
if (strcmp(tag, "semijoin_unique") == 0)
return PGPA_TAG_SEMIJOIN_UNIQUE;
if (strcmp(tag, "seq_scan") == 0)
return PGPA_TAG_SEQ_SCAN;
break;
case 't':
if (strcmp(tag, "tid_scan") == 0)
return PGPA_TAG_TID_SCAN;
break;
}
/* didn't work out */
*fail = true;
/* return an arbitrary value to unwind the call stack */
return PGPA_TAG_SEQ_SCAN;
}
/*
* Format a pgpa_advice_target as a string and append result to a StringInfo.
*/
void
pgpa_format_advice_target(StringInfo str, pgpa_advice_target *target)
{
if (target->ttype != PGPA_TARGET_IDENTIFIER)
{
bool first = true;
char *delims;
if (target->ttype == PGPA_TARGET_UNORDERED_LIST)
delims = "{}";
else
delims = "()";
appendStringInfoChar(str, delims[0]);
foreach_ptr(pgpa_advice_target, child_target, target->children)
{
if (first)
first = false;
else
appendStringInfoChar(str, ' ');
pgpa_format_advice_target(str, child_target);
}
appendStringInfoChar(str, delims[1]);
}
else
{
const char *rt_identifier;
rt_identifier = pgpa_identifier_string(&target->rid);
appendStringInfoString(str, rt_identifier);
}
}
/*
* Format a pgpa_index_target as a string and append result to a StringInfo.
*/
void
pgpa_format_index_target(StringInfo str, pgpa_index_target *itarget)
{
if (itarget->indnamespace != NULL)
appendStringInfo(str, "%s.",
quote_identifier(itarget->indnamespace));
appendStringInfoString(str, quote_identifier(itarget->indname));
}
/*
* Determine whether two pgpa_index_target objects are exactly identical.
*/
bool
pgpa_index_targets_equal(pgpa_index_target *i1, pgpa_index_target *i2)
{
/* indnamespace can be NULL, and two NULL values are equal */
if ((i1->indnamespace != NULL || i2->indnamespace != NULL) &&
(i1->indnamespace == NULL || i2->indnamespace == NULL ||
strcmp(i1->indnamespace, i2->indnamespace) != 0))
return false;
if (strcmp(i1->indname, i2->indname) != 0)
return false;
return true;
}
/*
* Check whether an identifier matches an any part of an advice target.
*/
bool
pgpa_identifier_matches_target(pgpa_identifier *rid, pgpa_advice_target *target)
{
/* For non-identifiers, check all descendants. */
if (target->ttype != PGPA_TARGET_IDENTIFIER)
{
foreach_ptr(pgpa_advice_target, child_target, target->children)
{
if (pgpa_identifier_matches_target(rid, child_target))
return true;
}
return false;
}
/* Straightforward comparisons of alias name and occurrence number. */
if (strcmp(rid->alias_name, target->rid.alias_name) != 0)
return false;
if (rid->occurrence != target->rid.occurrence)
return false;
/*
* If a relation identifier mentions a partition name, it should also
* specify a partition schema. But the target may leave the schema NULL to
* match anything.
*/
Assert(rid->partnsp != NULL || rid->partrel == NULL);
if (rid->partnsp != NULL && target->rid.partnsp != NULL &&
strcmp(rid->partnsp, target->rid.partnsp) != 0)
return false;
/*
* These fields can be NULL on either side, but NULL only matches another
* NULL.
*/
if (!strings_equal_or_both_null(rid->partrel, target->rid.partrel))
return false;
if (!strings_equal_or_both_null(rid->plan_name, target->rid.plan_name))
return false;
return true;
}
/*
* Match identifiers to advice targets and return an enum value indicating
* the relationship between the set of keys and the set of targets.
*
* See the comments for pgpa_itm_type.
*/
pgpa_itm_type
pgpa_identifiers_match_target(int nrids, pgpa_identifier *rids,
pgpa_advice_target *target)
{
bool all_rids_used = true;
bool any_rids_used = false;
bool all_targets_used;
bool *rids_used = palloc0_array(bool, nrids);
all_targets_used =
pgpa_identifiers_cover_target(nrids, rids, target, rids_used);
for (int i = 0; i < nrids; ++i)
{
if (rids_used[i])
any_rids_used = true;
else
all_rids_used = false;
}
if (all_rids_used)
{
if (all_targets_used)
return PGPA_ITM_EQUAL;
else
return PGPA_ITM_KEYS_ARE_SUBSET;
}
else
{
if (all_targets_used)
return PGPA_ITM_TARGETS_ARE_SUBSET;
else if (any_rids_used)
return PGPA_ITM_INTERSECTING;
else
return PGPA_ITM_DISJOINT;
}
}
/*
* Returns true if every target or sub-target is matched by at least one
* identifier, and otherwise false.
*
* Also sets rids_used[i] = true for each identifier that matches at least one
* target.
*/
static bool
pgpa_identifiers_cover_target(int nrids, pgpa_identifier *rids,
pgpa_advice_target *target, bool *rids_used)
{
bool result = false;
if (target->ttype != PGPA_TARGET_IDENTIFIER)
{
result = true;
foreach_ptr(pgpa_advice_target, child_target, target->children)
{
if (!pgpa_identifiers_cover_target(nrids, rids, child_target,
rids_used))
result = false;
}
}
else
{
for (int i = 0; i < nrids; ++i)
{
if (pgpa_identifier_matches_target(&rids[i], target))
{
rids_used[i] = true;
result = true;
}
}
}
return result;
}