You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

shippable.c 6.3KB

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