1 /*-------------------------------------------------------------------------
4 * Determine which database objects are shippable to a remote server.
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.
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.
16 * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
19 * contrib/postgres_fdw/shippable.c
21 *-------------------------------------------------------------------------
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"
33 /* Hash table for caching the results of shippability lookups */
34 static HTAB
*ShippableCacheHash
= NULL
;
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.
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 */
51 ShippableCacheKey key
; /* hash key - must be first */
53 } ShippableCacheEntry
;
57 * Flush cache entries when pg_foreign_server is updated.
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.
65 InvalidateShippableCacheCallback(Datum arg
, int cacheid
, uint32 hashvalue
)
67 HASH_SEQ_STATUS status
;
68 ShippableCacheEntry
*entry
;
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
76 hash_seq_init(&status
, ShippableCacheHash
);
77 while ((entry
= (ShippableCacheEntry
*) hash_seq_search(&status
)) != NULL
)
79 if (hash_search(ShippableCacheHash
,
83 elog(ERROR
, "hash table corrupted");
88 * Initialize the backend-lifespan cache of shippability decisions.
91 InitializeShippableCache(void)
95 /* Create the hash table. */
96 ctl
.keysize
= sizeof(ShippableCacheKey
);
97 ctl
.entrysize
= sizeof(ShippableCacheEntry
);
99 hash_create("Shippability cache", 256, &ctl
, HASH_ELEM
| HASH_BLOBS
);
101 /* Set up invalidation callback on pg_foreign_server. */
102 CacheRegisterSyscacheCallback(FOREIGNSERVEROID
,
103 InvalidateShippableCacheCallback
,
108 * Returns true if given object (operator/function/type) is shippable
109 * according to the server options.
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.
116 lookup_shippable(Oid objectId
, Oid classId
, PgFdwRelationInfo
*fpinfo
)
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.)
124 extensionOid
= getExtensionOfObject(classId
, objectId
);
126 /* If so, is that extension in fpinfo->shippable_extensions? */
127 if (OidIsValid(extensionOid
) &&
128 list_member_oid(fpinfo
->shippable_extensions
, extensionOid
))
135 * Return true if given object is one of PostgreSQL's built-in objects.
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.
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.
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.
152 is_builtin(Oid objectId
)
154 return (objectId
< FirstGenbkiObjectId
);
159 * Is this object (function/operator/type) shippable to foreign server?
162 is_shippable(Oid objectId
, Oid classId
, PgFdwRelationInfo
*fpinfo
)
164 ShippableCacheKey key
;
165 ShippableCacheEntry
*entry
;
167 /* Built-in objects are presumed shippable. */
168 if (is_builtin(objectId
))
171 /* Otherwise, give up if user hasn't specified any shippable extensions. */
172 if (fpinfo
->shippable_extensions
== NIL
)
175 /* Initialize cache if first time through. */
176 if (!ShippableCacheHash
)
177 InitializeShippableCache();
179 /* Set up cache hash key */
180 key
.objid
= objectId
;
181 key
.classid
= classId
;
182 key
.serverid
= fpinfo
->server
->serverid
;
184 /* See if we already cached the result. */
185 entry
= (ShippableCacheEntry
*)
186 hash_search(ShippableCacheHash
, &key
, HASH_FIND
, NULL
);
190 /* Not found in cache, so perform shippability lookup. */
191 bool shippable
= lookup_shippable(objectId
, classId
, fpinfo
);
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.
198 entry
= (ShippableCacheEntry
*)
199 hash_search(ShippableCacheHash
, &key
, HASH_ENTER
, NULL
);
201 entry
->shippable
= shippable
;
204 return entry
->shippable
;