PostgreSQL Source Code  git master
shippable.c
Go to the documentation of this file.
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-2019, 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 */
48 
49 typedef struct
50 {
51  ShippableCacheKey key; /* hash key - must be first */
52  bool shippable;
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
66 {
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  hash_seq_init(&status, ShippableCacheHash);
77  while ((entry = (ShippableCacheEntry *) hash_seq_search(&status)) != NULL)
78  {
79  if (hash_search(ShippableCacheHash,
80  (void *) &entry->key,
82  NULL) == NULL)
83  elog(ERROR, "hash table corrupted");
84  }
85 }
86 
87 /*
88  * Initialize the backend-lifespan cache of shippability decisions.
89  */
90 static void
92 {
93  HASHCTL ctl;
94 
95  /* Create the hash table. */
96  MemSet(&ctl, 0, sizeof(ctl));
97  ctl.keysize = sizeof(ShippableCacheKey);
98  ctl.entrysize = sizeof(ShippableCacheEntry);
99  ShippableCacheHash =
100  hash_create("Shippability cache", 256, &ctl, HASH_ELEM | HASH_BLOBS);
101 
102  /* Set up invalidation callback on pg_foreign_server. */
105  (Datum) 0);
106 }
107 
108 /*
109  * Returns true if given object (operator/function/type) is shippable
110  * according to the server options.
111  *
112  * Right now "shippability" is exclusively a function of whether the object
113  * belongs to an extension declared by the user. In the future we could
114  * additionally have a whitelist of functions/operators declared one at a time.
115  */
116 static bool
117 lookup_shippable(Oid objectId, Oid classId, PgFdwRelationInfo *fpinfo)
118 {
119  Oid extensionOid;
120 
121  /*
122  * Is object a member of some extension? (Note: this is a fairly
123  * expensive lookup, which is why we try to cache the results.)
124  */
125  extensionOid = getExtensionOfObject(classId, objectId);
126 
127  /* If so, is that extension in fpinfo->shippable_extensions? */
128  if (OidIsValid(extensionOid) &&
129  list_member_oid(fpinfo->shippable_extensions, extensionOid))
130  return true;
131 
132  return false;
133 }
134 
135 /*
136  * Return true if given object is one of PostgreSQL's built-in objects.
137  *
138  * We use FirstGenbkiObjectId as the cutoff, so that we only consider
139  * objects with hand-assigned OIDs to be "built in", not for instance any
140  * function or type defined in the information_schema.
141  *
142  * Our constraints for dealing with types are tighter than they are for
143  * functions or operators: we want to accept only types that are in pg_catalog,
144  * else deparse_type_name might incorrectly fail to schema-qualify their names.
145  * Thus we must exclude information_schema types.
146  *
147  * XXX there is a problem with this, which is that the set of built-in
148  * objects expands over time. Something that is built-in to us might not
149  * be known to the remote server, if it's of an older version. But keeping
150  * track of that would be a huge exercise.
151  */
152 bool
153 is_builtin(Oid objectId)
154 {
155  return (objectId < FirstGenbkiObjectId);
156 }
157 
158 /*
159  * is_shippable
160  * Is this object (function/operator/type) shippable to foreign server?
161  */
162 bool
163 is_shippable(Oid objectId, Oid classId, PgFdwRelationInfo *fpinfo)
164 {
166  ShippableCacheEntry *entry;
167 
168  /* Built-in objects are presumed shippable. */
169  if (is_builtin(objectId))
170  return true;
171 
172  /* Otherwise, give up if user hasn't specified any shippable extensions. */
173  if (fpinfo->shippable_extensions == NIL)
174  return false;
175 
176  /* Initialize cache if first time through. */
177  if (!ShippableCacheHash)
179 
180  /* Set up cache hash key */
181  key.objid = objectId;
182  key.classid = classId;
183  key.serverid = fpinfo->server->serverid;
184 
185  /* See if we already cached the result. */
186  entry = (ShippableCacheEntry *)
187  hash_search(ShippableCacheHash,
188  (void *) &key,
189  HASH_FIND,
190  NULL);
191 
192  if (!entry)
193  {
194  /* Not found in cache, so perform shippability lookup. */
195  bool shippable = lookup_shippable(objectId, classId, fpinfo);
196 
197  /*
198  * Don't create a new hash entry until *after* we have the shippable
199  * result in hand, as the underlying catalog lookups might trigger a
200  * cache invalidation.
201  */
202  entry = (ShippableCacheEntry *)
203  hash_search(ShippableCacheHash,
204  (void *) &key,
205  HASH_ENTER,
206  NULL);
207 
208  entry->shippable = shippable;
209  }
210 
211  return entry->shippable;
212 }
#define NIL
Definition: pg_list.h:65
Oid getExtensionOfObject(Oid classId, Oid objectId)
Definition: pg_depend.c:610
bool is_builtin(Oid objectId)
Definition: shippable.c:153
#define HASH_ELEM
Definition: hsearch.h:87
ForeignServer * server
Definition: postgres_fdw.h:84
Size entrysize
Definition: hsearch.h:73
#define MemSet(start, val, len)
Definition: c.h:962
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:906
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:645
static void InitializeShippableCache(void)
Definition: shippable.c:91
static bool lookup_shippable(Oid objectId, Oid classId, PgFdwRelationInfo *fpinfo)
Definition: shippable.c:117
Definition: dynahash.c:208
#define ERROR
Definition: elog.h:43
unsigned int uint32
Definition: c.h:359
#define HASH_BLOBS
Definition: hsearch.h:88
void CacheRegisterSyscacheCallback(int cacheid, SyscacheCallbackFunction func, Datum arg)
Definition: inval.c:1426
HTAB * hash_create(const char *tabname, long nelem, HASHCTL *info, int flags)
Definition: dynahash.c:316
uintptr_t Datum
Definition: postgres.h:367
bool is_shippable(Oid objectId, Oid classId, PgFdwRelationInfo *fpinfo)
Definition: shippable.c:163
Size keysize
Definition: hsearch.h:72
bool list_member_oid(const List *list, Oid datum)
Definition: list.c:675
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1389
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1379
static void InvalidateShippableCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
Definition: shippable.c:65
#define FirstGenbkiObjectId
Definition: transam.h:139
static HTAB * ShippableCacheHash
Definition: shippable.c:34
#define elog(elevel,...)
Definition: elog.h:228
void * arg
ShippableCacheKey key
Definition: shippable.c:51
static void static void status(const char *fmt,...) pg_attribute_printf(1
Definition: pg_regress.c:226
List * shippable_extensions
Definition: postgres_fdw.h:80
Oid serverid
Definition: foreign.h:36