mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-08-17 10:23:24 +02:00
Create a new header optimizer/optimizer.h, which exposes just the planner functions that can be used "at arm's length", without need to access Paths or the other planner-internal data structures defined in nodes/relation.h. This is intended to provide the whole planner API seen by most of the rest of the system; although FDWs still need to use additional stuff, and more thought is also needed about just what selfuncs.c should rely on. The main point of doing this now is to limit the amount of new #include baggage that will be needed by "planner support functions", which I expect to introduce later, and which will be in relevant datatype modules rather than anywhere near the planner. This commit just moves relevant declarations into optimizer.h from other header files (a couple of which go away because everything got moved), and adjusts #include lists to match. There's further cleanup that could be done if we want to decide that some stuff being exposed by optimizer.h doesn't belong in the planner at all, but I'll leave that for another day. Discussion: https://postgr.es/m/11460.1548706639@sss.pgh.pa.us
358 lines
9.5 KiB
C
358 lines
9.5 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* partition.c
|
|
* Partitioning related data structures and functions.
|
|
*
|
|
* Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* src/backend/catalog/partition.c
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "access/genam.h"
|
|
#include "access/htup_details.h"
|
|
#include "access/table.h"
|
|
#include "access/tupconvert.h"
|
|
#include "access/sysattr.h"
|
|
#include "catalog/indexing.h"
|
|
#include "catalog/partition.h"
|
|
#include "catalog/pg_inherits.h"
|
|
#include "catalog/pg_partitioned_table.h"
|
|
#include "nodes/makefuncs.h"
|
|
#include "optimizer/optimizer.h"
|
|
#include "partitioning/partbounds.h"
|
|
#include "rewrite/rewriteManip.h"
|
|
#include "utils/fmgroids.h"
|
|
#include "utils/partcache.h"
|
|
#include "utils/rel.h"
|
|
#include "utils/syscache.h"
|
|
|
|
|
|
static Oid get_partition_parent_worker(Relation inhRel, Oid relid);
|
|
static void get_partition_ancestors_worker(Relation inhRel, Oid relid,
|
|
List **ancestors);
|
|
|
|
/*
|
|
* get_partition_parent
|
|
* Obtain direct parent of given relation
|
|
*
|
|
* Returns inheritance parent of a partition by scanning pg_inherits
|
|
*
|
|
* Note: Because this function assumes that the relation whose OID is passed
|
|
* as an argument will have precisely one parent, it should only be called
|
|
* when it is known that the relation is a partition.
|
|
*/
|
|
Oid
|
|
get_partition_parent(Oid relid)
|
|
{
|
|
Relation catalogRelation;
|
|
Oid result;
|
|
|
|
catalogRelation = table_open(InheritsRelationId, AccessShareLock);
|
|
|
|
result = get_partition_parent_worker(catalogRelation, relid);
|
|
|
|
if (!OidIsValid(result))
|
|
elog(ERROR, "could not find tuple for parent of relation %u", relid);
|
|
|
|
table_close(catalogRelation, AccessShareLock);
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* get_partition_parent_worker
|
|
* Scan the pg_inherits relation to return the OID of the parent of the
|
|
* given relation
|
|
*/
|
|
static Oid
|
|
get_partition_parent_worker(Relation inhRel, Oid relid)
|
|
{
|
|
SysScanDesc scan;
|
|
ScanKeyData key[2];
|
|
Oid result = InvalidOid;
|
|
HeapTuple tuple;
|
|
|
|
ScanKeyInit(&key[0],
|
|
Anum_pg_inherits_inhrelid,
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
ObjectIdGetDatum(relid));
|
|
ScanKeyInit(&key[1],
|
|
Anum_pg_inherits_inhseqno,
|
|
BTEqualStrategyNumber, F_INT4EQ,
|
|
Int32GetDatum(1));
|
|
|
|
scan = systable_beginscan(inhRel, InheritsRelidSeqnoIndexId, true,
|
|
NULL, 2, key);
|
|
tuple = systable_getnext(scan);
|
|
if (HeapTupleIsValid(tuple))
|
|
{
|
|
Form_pg_inherits form = (Form_pg_inherits) GETSTRUCT(tuple);
|
|
|
|
result = form->inhparent;
|
|
}
|
|
|
|
systable_endscan(scan);
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* get_partition_ancestors
|
|
* Obtain ancestors of given relation
|
|
*
|
|
* Returns a list of ancestors of the given relation.
|
|
*
|
|
* Note: Because this function assumes that the relation whose OID is passed
|
|
* as an argument and each ancestor will have precisely one parent, it should
|
|
* only be called when it is known that the relation is a partition.
|
|
*/
|
|
List *
|
|
get_partition_ancestors(Oid relid)
|
|
{
|
|
List *result = NIL;
|
|
Relation inhRel;
|
|
|
|
inhRel = table_open(InheritsRelationId, AccessShareLock);
|
|
|
|
get_partition_ancestors_worker(inhRel, relid, &result);
|
|
|
|
table_close(inhRel, AccessShareLock);
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* get_partition_ancestors_worker
|
|
* recursive worker for get_partition_ancestors
|
|
*/
|
|
static void
|
|
get_partition_ancestors_worker(Relation inhRel, Oid relid, List **ancestors)
|
|
{
|
|
Oid parentOid;
|
|
|
|
/* Recursion ends at the topmost level, ie., when there's no parent */
|
|
parentOid = get_partition_parent_worker(inhRel, relid);
|
|
if (parentOid == InvalidOid)
|
|
return;
|
|
|
|
*ancestors = lappend_oid(*ancestors, parentOid);
|
|
get_partition_ancestors_worker(inhRel, parentOid, ancestors);
|
|
}
|
|
|
|
/*
|
|
* map_partition_varattnos - maps varattno of any Vars in expr from the
|
|
* attno's of 'from_rel' to the attno's of 'to_rel' partition, each of which
|
|
* may be either a leaf partition or a partitioned table, but both of which
|
|
* must be from the same partitioning hierarchy.
|
|
*
|
|
* Even though all of the same column names must be present in all relations
|
|
* in the hierarchy, and they must also have the same types, the attnos may
|
|
* be different.
|
|
*
|
|
* If found_whole_row is not NULL, *found_whole_row returns whether a
|
|
* whole-row variable was found in the input expression.
|
|
*
|
|
* Note: this will work on any node tree, so really the argument and result
|
|
* should be declared "Node *". But a substantial majority of the callers
|
|
* are working on Lists, so it's less messy to do the casts internally.
|
|
*/
|
|
List *
|
|
map_partition_varattnos(List *expr, int fromrel_varno,
|
|
Relation to_rel, Relation from_rel,
|
|
bool *found_whole_row)
|
|
{
|
|
bool my_found_whole_row = false;
|
|
|
|
if (expr != NIL)
|
|
{
|
|
AttrNumber *part_attnos;
|
|
|
|
part_attnos = convert_tuples_by_name_map(RelationGetDescr(to_rel),
|
|
RelationGetDescr(from_rel),
|
|
gettext_noop("could not convert row type"));
|
|
expr = (List *) map_variable_attnos((Node *) expr,
|
|
fromrel_varno, 0,
|
|
part_attnos,
|
|
RelationGetDescr(from_rel)->natts,
|
|
RelationGetForm(to_rel)->reltype,
|
|
&my_found_whole_row);
|
|
}
|
|
|
|
if (found_whole_row)
|
|
*found_whole_row = my_found_whole_row;
|
|
|
|
return expr;
|
|
}
|
|
|
|
/*
|
|
* Checks if any of the 'attnums' is a partition key attribute for rel
|
|
*
|
|
* Sets *used_in_expr if any of the 'attnums' is found to be referenced in some
|
|
* partition key expression. It's possible for a column to be both used
|
|
* directly and as part of an expression; if that happens, *used_in_expr may
|
|
* end up as either true or false. That's OK for current uses of this
|
|
* function, because *used_in_expr is only used to tailor the error message
|
|
* text.
|
|
*/
|
|
bool
|
|
has_partition_attrs(Relation rel, Bitmapset *attnums, bool *used_in_expr)
|
|
{
|
|
PartitionKey key;
|
|
int partnatts;
|
|
List *partexprs;
|
|
ListCell *partexprs_item;
|
|
int i;
|
|
|
|
if (attnums == NULL || rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
|
|
return false;
|
|
|
|
key = RelationGetPartitionKey(rel);
|
|
partnatts = get_partition_natts(key);
|
|
partexprs = get_partition_exprs(key);
|
|
|
|
partexprs_item = list_head(partexprs);
|
|
for (i = 0; i < partnatts; i++)
|
|
{
|
|
AttrNumber partattno = get_partition_col_attnum(key, i);
|
|
|
|
if (partattno != 0)
|
|
{
|
|
if (bms_is_member(partattno - FirstLowInvalidHeapAttributeNumber,
|
|
attnums))
|
|
{
|
|
if (used_in_expr)
|
|
*used_in_expr = false;
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Arbitrary expression */
|
|
Node *expr = (Node *) lfirst(partexprs_item);
|
|
Bitmapset *expr_attrs = NULL;
|
|
|
|
/* Find all attributes referenced */
|
|
pull_varattnos(expr, 1, &expr_attrs);
|
|
partexprs_item = lnext(partexprs_item);
|
|
|
|
if (bms_overlap(attnums, expr_attrs))
|
|
{
|
|
if (used_in_expr)
|
|
*used_in_expr = true;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* get_default_oid_from_partdesc
|
|
*
|
|
* Given a partition descriptor, return the OID of the default partition, if
|
|
* one exists; else, return InvalidOid.
|
|
*/
|
|
Oid
|
|
get_default_oid_from_partdesc(PartitionDesc partdesc)
|
|
{
|
|
if (partdesc && partdesc->boundinfo &&
|
|
partition_bound_has_default(partdesc->boundinfo))
|
|
return partdesc->oids[partdesc->boundinfo->default_index];
|
|
|
|
return InvalidOid;
|
|
}
|
|
|
|
/*
|
|
* get_default_partition_oid
|
|
*
|
|
* Given a relation OID, return the OID of the default partition, if one
|
|
* exists. Use get_default_oid_from_partdesc where possible, for
|
|
* efficiency.
|
|
*/
|
|
Oid
|
|
get_default_partition_oid(Oid parentId)
|
|
{
|
|
HeapTuple tuple;
|
|
Oid defaultPartId = InvalidOid;
|
|
|
|
tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(parentId));
|
|
|
|
if (HeapTupleIsValid(tuple))
|
|
{
|
|
Form_pg_partitioned_table part_table_form;
|
|
|
|
part_table_form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
|
|
defaultPartId = part_table_form->partdefid;
|
|
ReleaseSysCache(tuple);
|
|
}
|
|
|
|
return defaultPartId;
|
|
}
|
|
|
|
/*
|
|
* update_default_partition_oid
|
|
*
|
|
* Update pg_partition_table.partdefid with a new default partition OID.
|
|
*/
|
|
void
|
|
update_default_partition_oid(Oid parentId, Oid defaultPartId)
|
|
{
|
|
HeapTuple tuple;
|
|
Relation pg_partitioned_table;
|
|
Form_pg_partitioned_table part_table_form;
|
|
|
|
pg_partitioned_table = table_open(PartitionedRelationId, RowExclusiveLock);
|
|
|
|
tuple = SearchSysCacheCopy1(PARTRELID, ObjectIdGetDatum(parentId));
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
elog(ERROR, "cache lookup failed for partition key of relation %u",
|
|
parentId);
|
|
|
|
part_table_form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
|
|
part_table_form->partdefid = defaultPartId;
|
|
CatalogTupleUpdate(pg_partitioned_table, &tuple->t_self, tuple);
|
|
|
|
heap_freetuple(tuple);
|
|
table_close(pg_partitioned_table, RowExclusiveLock);
|
|
}
|
|
|
|
/*
|
|
* get_proposed_default_constraint
|
|
*
|
|
* This function returns the negation of new_part_constraints, which
|
|
* would be an integral part of the default partition constraints after
|
|
* addition of the partition to which the new_part_constraints belongs.
|
|
*/
|
|
List *
|
|
get_proposed_default_constraint(List *new_part_constraints)
|
|
{
|
|
Expr *defPartConstraint;
|
|
|
|
defPartConstraint = make_ands_explicit(new_part_constraints);
|
|
|
|
/*
|
|
* Derive the partition constraints of default partition by negating the
|
|
* given partition constraints. The partition constraint never evaluates
|
|
* to NULL, so negating it like this is safe.
|
|
*/
|
|
defPartConstraint = makeBoolExpr(NOT_EXPR,
|
|
list_make1(defPartConstraint),
|
|
-1);
|
|
|
|
/* Simplify, to put the negated expression into canonical form */
|
|
defPartConstraint =
|
|
(Expr *) eval_const_expressions(NULL,
|
|
(Node *) defPartConstraint);
|
|
defPartConstraint = canonicalize_qual(defPartConstraint, true);
|
|
|
|
return make_ands_implicit(defPartConstraint);
|
|
}
|