| /*------------------------------------------------------------------------- |
| * |
| * queryenvironment.c |
| * Query environment, to store context-specific values like ephemeral named |
| * relations. Initial use is for named tuplestores for delta information |
| * from "normal" relations. |
| * |
| * The initial implementation uses a list because the number of such relations |
| * in any one context is expected to be very small. If that becomes a |
| * performance problem, the implementation can be changed with no other impact |
| * on callers, since this is an opaque structure. This is the reason to |
| * require a create function. |
| * |
| * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group |
| * Portions Copyright (c) 1994, Regents of the University of California |
| * |
| * |
| * IDENTIFICATION |
| * src/backend/utils/misc/queryenvironment.c |
| * |
| *------------------------------------------------------------------------- |
| */ |
| #include "postgres.h" |
| |
| #include "access/table.h" |
| #include "utils/queryenvironment.h" |
| #include "utils/rel.h" |
| #include "commands/matview.h" |
| |
| /* |
| * Private state of a query environment. |
| */ |
| struct QueryEnvironment |
| { |
| List *namedRelList; |
| Size snaplen; |
| Oid matviewOid; |
| Oid tableid; |
| char *snapname; |
| }; |
| |
| static List* construct_ENRList(QueryEnvironment *queryEnv); |
| |
| QueryEnvironment * |
| create_queryEnv(void) |
| { |
| return (QueryEnvironment *) palloc0(sizeof(QueryEnvironment)); |
| } |
| |
| /* |
| * Configure the query environment with the given parameters. |
| */ |
| void |
| configure_queryEnv(QueryEnvironment *queryEnv, Oid matviewOid, Oid tableid, char* snapname, Size snaplen) |
| { |
| if (queryEnv == NULL) |
| return; |
| queryEnv->matviewOid = matviewOid; |
| queryEnv->tableid = tableid; |
| queryEnv->snaplen = snaplen; |
| queryEnv->snapname = snapname; |
| } |
| |
| |
| /* |
| * Construct a list of EphemeralNamedRelationInfo objects from the namedRelList |
| * in the given query environment. |
| * |
| * Parameters: |
| * - queryEnv: the query environment |
| * |
| * Returns: |
| * - a list of EphemeralNamedRelationInfo objects |
| */ |
| static List* |
| construct_ENRList(QueryEnvironment *queryEnv) |
| { |
| ListCell *lc; |
| List *enrList = NIL; |
| Assert(queryEnv); |
| |
| foreach(lc, queryEnv->namedRelList) |
| { |
| EphemeralNamedRelation enr = (EphemeralNamedRelation) lfirst(lc); |
| EphemeralNamedRelationInfo *node = palloc0(sizeof(EphemeralNamedRelationInfo)); |
| node->type = T_EphemeralNamedRelationInfo; |
| node->natts = enr->md.tupdesc->natts; |
| node->name = enr->md.name; |
| node->reliddesc = enr->md.reliddesc; |
| node->tuple = CreateTupleDescCopy(enr->md.tupdesc); |
| node->enrtype = enr->md.enrtype; |
| node->enrtuples = enr->md.enrtuples; |
| enrList = lappend(enrList, node); |
| } |
| |
| return enrList; |
| } |
| |
| /* |
| * Add preassigned EphemeralNamedRelationInfo objects to the given query environment. |
| * |
| * Parameters: |
| * - queryEnv: the query environment |
| * - enrs: a list of EphemeralNamedRelationInfo objects to add |
| */ |
| void |
| AddPreassignedENR(QueryEnvironment *queryEnv, List* enrs) |
| { |
| ListCell *lc; |
| |
| foreach(lc, enrs) |
| { |
| EphemeralNamedRelationInfo *enrinfo = (EphemeralNamedRelationInfo *) lfirst(lc); |
| |
| EphemeralNamedRelation enr = (EphemeralNamedRelation) palloc0(sizeof(EphemeralNamedRelationData)); |
| enr->md.name = enrinfo->name; |
| enr->md.reliddesc = enrinfo->reliddesc; |
| enr->md.tupdesc = enrinfo->tuple; |
| enr->md.enrtype = enrinfo->enrtype; |
| enr->md.enrtuples = enrinfo->enrtuples; |
| enr->reldata = NULL; |
| if (get_visible_ENR_metadata(queryEnv, enr->md.name) == NULL) |
| register_ENR(queryEnv, enr); |
| else |
| pfree(enr); |
| } |
| return; |
| } |
| |
| /* |
| * Fill the QueryDispatchDesc structure with information from the given query environment. |
| * |
| * Parameters: |
| * - queryEnv: the query environment |
| * - ddesc: the QueryDispatchDesc structure to fill |
| */ |
| void |
| FillQueryDispatchDesc(QueryEnvironment *queryEnv, QueryDispatchDesc *ddesc) |
| { |
| if (queryEnv == NULL) |
| return; |
| ddesc->namedRelList = construct_ENRList(queryEnv); |
| ddesc->snaplen = queryEnv->snaplen; |
| if (queryEnv->snaplen > 0) |
| { |
| ddesc->snapname = queryEnv->snapname; |
| ddesc->matviewOid = queryEnv->matviewOid; |
| ddesc->tableid = queryEnv->tableid; |
| } |
| } |
| |
| EphemeralNamedRelationMetadata |
| get_visible_ENR_metadata(QueryEnvironment *queryEnv, const char *refname) |
| { |
| EphemeralNamedRelation enr; |
| |
| Assert(refname != NULL); |
| |
| if (queryEnv == NULL) |
| return NULL; |
| |
| enr = get_ENR(queryEnv, refname); |
| |
| if (enr) |
| return &(enr->md); |
| |
| return NULL; |
| } |
| |
| /* |
| * Register a named relation for use in the given environment. |
| * |
| * If this is intended exclusively for planning purposes, the tstate field can |
| * be left NULL; |
| */ |
| void |
| register_ENR(QueryEnvironment *queryEnv, EphemeralNamedRelation enr) |
| { |
| Assert(enr != NULL); |
| Assert(get_ENR(queryEnv, enr->md.name) == NULL); |
| |
| queryEnv->namedRelList = lappend(queryEnv->namedRelList, enr); |
| } |
| |
| /* |
| * Unregister an ephemeral relation by name. This will probably be a rarely |
| * used function, but seems like it should be provided "just in case". |
| */ |
| void |
| unregister_ENR(QueryEnvironment *queryEnv, const char *name) |
| { |
| EphemeralNamedRelation match; |
| |
| match = get_ENR(queryEnv, name); |
| if (match) |
| queryEnv->namedRelList = list_delete(queryEnv->namedRelList, match); |
| } |
| |
| /* |
| * This returns an ENR if there is a name match in the given collection. It |
| * must quietly return NULL if no match is found. |
| */ |
| EphemeralNamedRelation |
| get_ENR(QueryEnvironment *queryEnv, const char *name) |
| { |
| ListCell *lc; |
| |
| Assert(name != NULL); |
| |
| if (queryEnv == NULL) |
| return NULL; |
| |
| foreach(lc, queryEnv->namedRelList) |
| { |
| EphemeralNamedRelation enr = (EphemeralNamedRelation) lfirst(lc); |
| |
| if (strcmp(enr->md.name, name) == 0) |
| return enr; |
| } |
| |
| return NULL; |
| } |
| |
| /* |
| * Gets the TupleDesc for a Ephemeral Named Relation, based on which field was |
| * filled. |
| * |
| * When the TupleDesc is based on a relation from the catalogs, we count on |
| * that relation being used at the same time, so that appropriate locks will |
| * already be held. Locking here would be too late anyway. |
| */ |
| TupleDesc |
| ENRMetadataGetTupDesc(EphemeralNamedRelationMetadata enrmd) |
| { |
| TupleDesc tupdesc; |
| |
| /* not only one, but use reliddesc field first. */ |
| Assert(enrmd->reliddesc || enrmd->tupdesc); |
| |
| if (enrmd->tupdesc != NULL) |
| tupdesc = enrmd->tupdesc; |
| else |
| { |
| Relation relation; |
| |
| relation = table_open(enrmd->reliddesc, NoLock); |
| tupdesc = relation->rd_att; |
| table_close(relation, NoLock); |
| } |
| |
| return tupdesc; |
| } |