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