LCOV - code coverage report
Current view: top level - contrib/postgres_fdw - shippable.c (source / functions) Hit Total Coverage
Test: PostgreSQL 12devel Lines: 40 41 97.6 %
Date: 2018-10-17 03:20:55 Functions: 5 5 100.0 %
Legend: Lines: hit not hit

          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 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          28 : 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          28 :     hash_seq_init(&status, ShippableCacheHash);
      79          28 :     while ((entry = (ShippableCacheEntry *) hash_seq_search(&status)) != NULL)
      80             :     {
      81          22 :         if (hash_search(ShippableCacheHash,
      82          22 :                         (void *) &entry->key,
      83             :                         HASH_REMOVE,
      84             :                         NULL) == NULL)
      85           0 :             elog(ERROR, "hash table corrupted");
      86             :     }
      87          28 : }
      88             : 
      89             : /*
      90             :  * Initialize the backend-lifespan cache of shippability decisions.
      91             :  */
      92             : static void
      93           2 : InitializeShippableCache(void)
      94             : {
      95             :     HASHCTL     ctl;
      96             : 
      97             :     /* Create the hash table. */
      98           2 :     MemSet(&ctl, 0, sizeof(ctl));
      99           2 :     ctl.keysize = sizeof(ShippableCacheKey);
     100           2 :     ctl.entrysize = sizeof(ShippableCacheEntry);
     101           2 :     ShippableCacheHash =
     102           2 :         hash_create("Shippability cache", 256, &ctl, HASH_ELEM | HASH_BLOBS);
     103             : 
     104             :     /* Set up invalidation callback on pg_foreign_server. */
     105           2 :     CacheRegisterSyscacheCallback(FOREIGNSERVEROID,
     106             :                                   InvalidateShippableCacheCallback,
     107             :                                   (Datum) 0);
     108           2 : }
     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          22 : 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          22 :     extensionOid = getExtensionOfObject(classId, objectId);
     128             : 
     129             :     /* If so, is that extension in fpinfo->shippable_extensions? */
     130          32 :     if (OidIsValid(extensionOid) &&
     131          10 :         list_member_oid(fpinfo->shippable_extensions, extensionOid))
     132          10 :         return true;
     133             : 
     134          12 :     return false;
     135             : }
     136             : 
     137             : /*
     138             :  * Return true if given object is one of PostgreSQL's built-in objects.
     139             :  *
     140             :  * We use FirstBootstrapObjectId 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       11958 : is_builtin(Oid objectId)
     156             : {
     157       11958 :     return (objectId < FirstBootstrapObjectId);
     158             : }
     159             : 
     160             : /*
     161             :  * is_shippable
     162             :  *     Is this object (function/operator/type) shippable to foreign server?
     163             :  */
     164             : bool
     165       11464 : is_shippable(Oid objectId, Oid classId, PgFdwRelationInfo *fpinfo)
     166             : {
     167             :     ShippableCacheKey key;
     168             :     ShippableCacheEntry *entry;
     169             : 
     170             :     /* Built-in objects are presumed shippable. */
     171       11464 :     if (is_builtin(objectId))
     172       11346 :         return true;
     173             : 
     174             :     /* Otherwise, give up if user hasn't specified any shippable extensions. */
     175         118 :     if (fpinfo->shippable_extensions == NIL)
     176          38 :         return false;
     177             : 
     178             :     /* Initialize cache if first time through. */
     179          80 :     if (!ShippableCacheHash)
     180           2 :         InitializeShippableCache();
     181             : 
     182             :     /* Set up cache hash key */
     183          80 :     key.objid = objectId;
     184          80 :     key.classid = classId;
     185          80 :     key.serverid = fpinfo->server->serverid;
     186             : 
     187             :     /* See if we already cached the result. */
     188          80 :     entry = (ShippableCacheEntry *)
     189          80 :         hash_search(ShippableCacheHash,
     190             :                     (void *) &key,
     191             :                     HASH_FIND,
     192             :                     NULL);
     193             : 
     194          80 :     if (!entry)
     195             :     {
     196             :         /* Not found in cache, so perform shippability lookup. */
     197          22 :         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          22 :         entry = (ShippableCacheEntry *)
     205          22 :             hash_search(ShippableCacheHash,
     206             :                         (void *) &key,
     207             :                         HASH_ENTER,
     208             :                         NULL);
     209             : 
     210          22 :         entry->shippable = shippable;
     211             :     }
     212             : 
     213          80 :     return entry->shippable;
     214             : }

Generated by: LCOV version 1.13