Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * shippable.c
4 : : * Determine which database objects are shippable to a remote server.
5 : : *
6 : : * We need to determine whether particular functions, operators, and indeed
7 : : * data types are shippable to a remote server for execution --- that is,
8 : : * do they exist and have the same behavior remotely as they do locally?
9 : : * Built-in objects are generally considered shippable. Other objects can
10 : : * be shipped if they are declared as such by the user.
11 : : *
12 : : * Note: there are additional filter rules that prevent shipping mutable
13 : : * functions or functions using nonportable collations. Those considerations
14 : : * need not be accounted for here.
15 : : *
16 : : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
17 : : *
18 : : * IDENTIFICATION
19 : : * contrib/postgres_fdw/shippable.c
20 : : *
21 : : *-------------------------------------------------------------------------
22 : : */
23 : :
24 : : #include "postgres.h"
25 : :
26 : : #include "access/transam.h"
27 : : #include "catalog/dependency.h"
28 : : #include "postgres_fdw.h"
29 : : #include "utils/hsearch.h"
30 : : #include "utils/inval.h"
31 : : #include "utils/syscache.h"
32 : :
33 : : /* Hash table for caching the results of shippability lookups */
34 : : static HTAB *ShippableCacheHash = NULL;
35 : :
36 : : /*
37 : : * Hash key for shippability lookups. We include the FDW server OID because
38 : : * decisions may differ per-server. Otherwise, objects are identified by
39 : : * their (local!) OID and catalog OID.
40 : : */
41 : : typedef struct
42 : : {
43 : : /* XXX we assume this struct contains no padding bytes */
44 : : Oid objid; /* function/operator/type OID */
45 : : Oid classid; /* OID of its catalog (pg_proc, etc) */
46 : : Oid serverid; /* FDW server we are concerned with */
47 : : } ShippableCacheKey;
48 : :
49 : : typedef struct
50 : : {
51 : : ShippableCacheKey key; /* hash key - must be first */
52 : : bool shippable;
53 : : } ShippableCacheEntry;
54 : :
55 : :
56 : : /*
57 : : * Flush cache entries when pg_foreign_server is updated.
58 : : *
59 : : * We do this because of the possibility of ALTER SERVER being used to change
60 : : * a server's extensions option. We do not currently bother to check whether
61 : : * objects' extension membership changes once a shippability decision has been
62 : : * made for them, however.
63 : : */
64 : : static void
3085 tgl@sss.pgh.pa.us 65 :CBC 128 : InvalidateShippableCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
66 : : {
67 : : HASH_SEQ_STATUS status;
68 : : ShippableCacheEntry *entry;
69 : :
70 : : /*
71 : : * In principle we could flush only cache entries relating to the
72 : : * pg_foreign_server entry being outdated; but that would be more
73 : : * complicated, and it's probably not worth the trouble. So for now, just
74 : : * flush all entries.
75 : : */
76 : 128 : hash_seq_init(&status, ShippableCacheHash);
77 [ + + ]: 143 : while ((entry = (ShippableCacheEntry *) hash_seq_search(&status)) != NULL)
78 : : {
79 [ - + ]: 15 : if (hash_search(ShippableCacheHash,
433 peter@eisentraut.org 80 : 15 : &entry->key,
81 : : HASH_REMOVE,
82 : : NULL) == NULL)
3085 tgl@sss.pgh.pa.us 83 [ # # ]:UBC 0 : elog(ERROR, "hash table corrupted");
84 : : }
3085 tgl@sss.pgh.pa.us 85 :CBC 128 : }
86 : :
87 : : /*
88 : : * Initialize the backend-lifespan cache of shippability decisions.
89 : : */
90 : : static void
91 : 2 : InitializeShippableCache(void)
92 : : {
93 : : HASHCTL ctl;
94 : :
95 : : /* Create the hash table. */
96 : 2 : ctl.keysize = sizeof(ShippableCacheKey);
97 : 2 : ctl.entrysize = sizeof(ShippableCacheEntry);
98 : 2 : ShippableCacheHash =
99 : 2 : hash_create("Shippability cache", 256, &ctl, HASH_ELEM | HASH_BLOBS);
100 : :
101 : : /* Set up invalidation callback on pg_foreign_server. */
102 : 2 : CacheRegisterSyscacheCallback(FOREIGNSERVEROID,
103 : : InvalidateShippableCacheCallback,
104 : : (Datum) 0);
105 : 2 : }
106 : :
107 : : /*
108 : : * Returns true if given object (operator/function/type) is shippable
109 : : * according to the server options.
110 : : *
111 : : * Right now "shippability" is exclusively a function of whether the object
112 : : * belongs to an extension declared by the user. In the future we could
113 : : * additionally have a list of functions/operators declared one at a time.
114 : : */
115 : : static bool
116 : 18 : lookup_shippable(Oid objectId, Oid classId, PgFdwRelationInfo *fpinfo)
117 : : {
118 : : Oid extensionOid;
119 : :
120 : : /*
121 : : * Is object a member of some extension? (Note: this is a fairly
122 : : * expensive lookup, which is why we try to cache the results.)
123 : : */
124 : 18 : extensionOid = getExtensionOfObject(classId, objectId);
125 : :
126 : : /* If so, is that extension in fpinfo->shippable_extensions? */
127 [ + + + - ]: 27 : if (OidIsValid(extensionOid) &&
128 : 9 : list_member_oid(fpinfo->shippable_extensions, extensionOid))
129 : 9 : return true;
130 : :
131 : 9 : return false;
132 : : }
133 : :
134 : : /*
135 : : * Return true if given object is one of PostgreSQL's built-in objects.
136 : : *
137 : : * We use FirstGenbkiObjectId as the cutoff, so that we only consider
138 : : * objects with hand-assigned OIDs to be "built in", not for instance any
139 : : * function or type defined in the information_schema.
140 : : *
141 : : * Our constraints for dealing with types are tighter than they are for
142 : : * functions or operators: we want to accept only types that are in pg_catalog,
143 : : * else deparse_type_name might incorrectly fail to schema-qualify their names.
144 : : * Thus we must exclude information_schema types.
145 : : *
146 : : * XXX there is a problem with this, which is that the set of built-in
147 : : * objects expands over time. Something that is built-in to us might not
148 : : * be known to the remote server, if it's of an older version. But keeping
149 : : * track of that would be a huge exercise.
150 : : */
151 : : bool
152 : 10499 : is_builtin(Oid objectId)
153 : : {
1949 andres@anarazel.de 154 : 10499 : return (objectId < FirstGenbkiObjectId);
155 : : }
156 : :
157 : : /*
158 : : * is_shippable
159 : : * Is this object (function/operator/type) shippable to foreign server?
160 : : */
161 : : bool
3085 tgl@sss.pgh.pa.us 162 : 9972 : is_shippable(Oid objectId, Oid classId, PgFdwRelationInfo *fpinfo)
163 : : {
164 : : ShippableCacheKey key;
165 : : ShippableCacheEntry *entry;
166 : :
167 : : /* Built-in objects are presumed shippable. */
168 [ + + ]: 9972 : if (is_builtin(objectId))
169 : 9862 : return true;
170 : :
171 : : /* Otherwise, give up if user hasn't specified any shippable extensions. */
172 [ + + ]: 110 : if (fpinfo->shippable_extensions == NIL)
173 : 58 : return false;
174 : :
175 : : /* Initialize cache if first time through. */
176 [ + + ]: 52 : if (!ShippableCacheHash)
177 : 2 : InitializeShippableCache();
178 : :
179 : : /* Set up cache hash key */
180 : 52 : key.objid = objectId;
181 : 52 : key.classid = classId;
182 : 52 : key.serverid = fpinfo->server->serverid;
183 : :
184 : : /* See if we already cached the result. */
185 : : entry = (ShippableCacheEntry *)
331 186 : 52 : hash_search(ShippableCacheHash, &key, HASH_FIND, NULL);
187 : :
3085 188 [ + + ]: 52 : if (!entry)
189 : : {
190 : : /* Not found in cache, so perform shippability lookup. */
191 : 18 : bool shippable = lookup_shippable(objectId, classId, fpinfo);
192 : :
193 : : /*
194 : : * Don't create a new hash entry until *after* we have the shippable
195 : : * result in hand, as the underlying catalog lookups might trigger a
196 : : * cache invalidation.
197 : : */
198 : : entry = (ShippableCacheEntry *)
331 199 : 18 : hash_search(ShippableCacheHash, &key, HASH_ENTER, NULL);
200 : :
3085 201 : 18 : entry->shippable = shippable;
202 : : }
203 : :
204 : 52 : return entry->shippable;
205 : : }
|