1 /*-------------------------------------------------------------------------
2  *
3  * queryenvironment.c
4  *	  Query environment, to store context-specific values like ephemeral named
5  *	  relations.  Initial use is for named tuplestores for delta information
6  *	  from "normal" relations.
7  *
8  * The initial implementation uses a list because the number of such relations
9  * in any one context is expected to be very small.  If that becomes a
10  * performance problem, the implementation can be changed with no other impact
11  * on callers, since this is an opaque structure.  This is the reason to
12  * require a create function.
13  *
14  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
15  * Portions Copyright (c) 1994, Regents of the University of California
16  *
17  *
18  * IDENTIFICATION
19  *	  src/backend/backend/utils/misc/queryenvironment.c
20  *
21  *-------------------------------------------------------------------------
22  */
23 #include "postgres.h"
24 
25 #include "access/heapam.h"
26 #include "utils/queryenvironment.h"
27 #include "utils/rel.h"
28 
29 /*
30  * Private state of a query environment.
31  */
32 struct QueryEnvironment
33 {
34 	List	   *namedRelList;
35 };
36 
37 
38 QueryEnvironment *
create_queryEnv()39 create_queryEnv()
40 {
41 	return (QueryEnvironment *) palloc0(sizeof(QueryEnvironment));
42 }
43 
44 EphemeralNamedRelationMetadata
get_visible_ENR_metadata(QueryEnvironment * queryEnv,const char * refname)45 get_visible_ENR_metadata(QueryEnvironment *queryEnv, const char *refname)
46 {
47 	EphemeralNamedRelation enr;
48 
49 	Assert(refname != NULL);
50 
51 	if (queryEnv == NULL)
52 		return NULL;
53 
54 	enr = get_ENR(queryEnv, refname);
55 
56 	if (enr)
57 		return &(enr->md);
58 
59 	return NULL;
60 }
61 
62 /*
63  * Register a named relation for use in the given environment.
64  *
65  * If this is intended exclusively for planning purposes, the tstate field can
66  * be left NULL;
67  */
68 void
register_ENR(QueryEnvironment * queryEnv,EphemeralNamedRelation enr)69 register_ENR(QueryEnvironment *queryEnv, EphemeralNamedRelation enr)
70 {
71 	Assert(enr != NULL);
72 	Assert(get_ENR(queryEnv, enr->md.name) == NULL);
73 
74 	queryEnv->namedRelList = lappend(queryEnv->namedRelList, enr);
75 }
76 
77 /*
78  * Unregister an ephemeral relation by name.  This will probably be a rarely
79  * used function, but seems like it should be provided "just in case".
80  */
81 void
unregister_ENR(QueryEnvironment * queryEnv,const char * name)82 unregister_ENR(QueryEnvironment *queryEnv, const char *name)
83 {
84 	EphemeralNamedRelation match;
85 
86 	match = get_ENR(queryEnv, name);
87 	if (match)
88 		queryEnv->namedRelList = list_delete(queryEnv->namedRelList, match);
89 }
90 
91 /*
92  * This returns an ENR if there is a name match in the given collection.  It
93  * must quietly return NULL if no match is found.
94  */
95 EphemeralNamedRelation
get_ENR(QueryEnvironment * queryEnv,const char * name)96 get_ENR(QueryEnvironment *queryEnv, const char *name)
97 {
98 	ListCell   *lc;
99 
100 	Assert(name != NULL);
101 
102 	if (queryEnv == NULL)
103 		return NULL;
104 
105 	foreach(lc, queryEnv->namedRelList)
106 	{
107 		EphemeralNamedRelation enr = (EphemeralNamedRelation) lfirst(lc);
108 
109 		if (strcmp(enr->md.name, name) == 0)
110 			return enr;
111 	}
112 
113 	return NULL;
114 }
115 
116 /*
117  * Gets the TupleDesc for a Ephemeral Named Relation, based on which field was
118  * filled.
119  *
120  * When the TupleDesc is based on a relation from the catalogs, we count on
121  * that relation being used at the same time, so that appropriate locks will
122  * already be held.  Locking here would be too late anyway.
123  */
124 TupleDesc
ENRMetadataGetTupDesc(EphemeralNamedRelationMetadata enrmd)125 ENRMetadataGetTupDesc(EphemeralNamedRelationMetadata enrmd)
126 {
127 	TupleDesc	tupdesc;
128 
129 	/* One, and only one, of these fields must be filled. */
130 	Assert((enrmd->reliddesc == InvalidOid) != (enrmd->tupdesc == NULL));
131 
132 	if (enrmd->tupdesc != NULL)
133 		tupdesc = enrmd->tupdesc;
134 	else
135 	{
136 		Relation	relation;
137 
138 		relation = heap_open(enrmd->reliddesc, NoLock);
139 		tupdesc = relation->rd_att;
140 		heap_close(relation, NoLock);
141 	}
142 
143 	return tupdesc;
144 }
145