mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-01-30 19:00:29 +08:00
b3817f5f77
Invent a new flag bit HASH_STRINGS to specify C-string hashing, which
was formerly the default; and add assertions insisting that exactly
one of the bits HASH_STRINGS, HASH_BLOBS, and HASH_FUNCTION be set.
This is in hopes of preventing recurrences of the type of oversight
fixed in commit a1b8aa1e4
(i.e., mistakenly omitting HASH_BLOBS).
Also, when HASH_STRINGS is specified, insist that the keysize be
more than 8 bytes. This is a heuristic, but it should catch
accidental use of HASH_STRINGS for integer or pointer keys.
(Nearly all existing use-cases set the keysize to NAMEDATALEN or
more, so there's little reason to think this restriction should
be problematic.)
Tweak hash_create() to insist that the HASH_ELEM flag be set, and
remove the defaults it had for keysize and entrysize. Since those
defaults were undocumented and basically useless, no callers
omitted HASH_ELEM anyway.
Also, remove memset's zeroing the HASHCTL parameter struct from
those callers that had one. This has never been really necessary,
and while it wasn't a bad coding convention it was confusing that
some callers did it and some did not. We might as well save a few
cycles by standardizing on "not".
Also improve the documentation for hash_create().
In passing, improve reinit.c's usage of a hash table by storing
the key as a binary Oid rather than a string; and, since that's
a temporary hash table, allocate it in CurrentMemoryContext for
neatness.
Discussion: https://postgr.es/m/590625.1607878171@sss.pgh.pa.us
212 lines
6.3 KiB
C
212 lines
6.3 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* shippable.c
|
|
* Determine which database objects are shippable to a remote server.
|
|
*
|
|
* We need to determine whether particular functions, operators, and indeed
|
|
* data types are shippable to a remote server for execution --- that is,
|
|
* do they exist and have the same behavior remotely as they do locally?
|
|
* Built-in objects are generally considered shippable. Other objects can
|
|
* be shipped if they are white-listed by the user.
|
|
*
|
|
* Note: there are additional filter rules that prevent shipping mutable
|
|
* functions or functions using nonportable collations. Those considerations
|
|
* need not be accounted for here.
|
|
*
|
|
* Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
|
|
*
|
|
* IDENTIFICATION
|
|
* contrib/postgres_fdw/shippable.c
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
#include "access/transam.h"
|
|
#include "catalog/dependency.h"
|
|
#include "postgres_fdw.h"
|
|
#include "utils/hsearch.h"
|
|
#include "utils/inval.h"
|
|
#include "utils/syscache.h"
|
|
|
|
/* Hash table for caching the results of shippability lookups */
|
|
static HTAB *ShippableCacheHash = NULL;
|
|
|
|
/*
|
|
* Hash key for shippability lookups. We include the FDW server OID because
|
|
* decisions may differ per-server. Otherwise, objects are identified by
|
|
* their (local!) OID and catalog OID.
|
|
*/
|
|
typedef struct
|
|
{
|
|
/* XXX we assume this struct contains no padding bytes */
|
|
Oid objid; /* function/operator/type OID */
|
|
Oid classid; /* OID of its catalog (pg_proc, etc) */
|
|
Oid serverid; /* FDW server we are concerned with */
|
|
} ShippableCacheKey;
|
|
|
|
typedef struct
|
|
{
|
|
ShippableCacheKey key; /* hash key - must be first */
|
|
bool shippable;
|
|
} ShippableCacheEntry;
|
|
|
|
|
|
/*
|
|
* Flush cache entries when pg_foreign_server is updated.
|
|
*
|
|
* We do this because of the possibility of ALTER SERVER being used to change
|
|
* a server's extensions option. We do not currently bother to check whether
|
|
* objects' extension membership changes once a shippability decision has been
|
|
* made for them, however.
|
|
*/
|
|
static void
|
|
InvalidateShippableCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
|
|
{
|
|
HASH_SEQ_STATUS status;
|
|
ShippableCacheEntry *entry;
|
|
|
|
/*
|
|
* In principle we could flush only cache entries relating to the
|
|
* pg_foreign_server entry being outdated; but that would be more
|
|
* complicated, and it's probably not worth the trouble. So for now, just
|
|
* flush all entries.
|
|
*/
|
|
hash_seq_init(&status, ShippableCacheHash);
|
|
while ((entry = (ShippableCacheEntry *) hash_seq_search(&status)) != NULL)
|
|
{
|
|
if (hash_search(ShippableCacheHash,
|
|
(void *) &entry->key,
|
|
HASH_REMOVE,
|
|
NULL) == NULL)
|
|
elog(ERROR, "hash table corrupted");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Initialize the backend-lifespan cache of shippability decisions.
|
|
*/
|
|
static void
|
|
InitializeShippableCache(void)
|
|
{
|
|
HASHCTL ctl;
|
|
|
|
/* Create the hash table. */
|
|
ctl.keysize = sizeof(ShippableCacheKey);
|
|
ctl.entrysize = sizeof(ShippableCacheEntry);
|
|
ShippableCacheHash =
|
|
hash_create("Shippability cache", 256, &ctl, HASH_ELEM | HASH_BLOBS);
|
|
|
|
/* Set up invalidation callback on pg_foreign_server. */
|
|
CacheRegisterSyscacheCallback(FOREIGNSERVEROID,
|
|
InvalidateShippableCacheCallback,
|
|
(Datum) 0);
|
|
}
|
|
|
|
/*
|
|
* Returns true if given object (operator/function/type) is shippable
|
|
* according to the server options.
|
|
*
|
|
* Right now "shippability" is exclusively a function of whether the object
|
|
* belongs to an extension declared by the user. In the future we could
|
|
* additionally have a whitelist of functions/operators declared one at a time.
|
|
*/
|
|
static bool
|
|
lookup_shippable(Oid objectId, Oid classId, PgFdwRelationInfo *fpinfo)
|
|
{
|
|
Oid extensionOid;
|
|
|
|
/*
|
|
* Is object a member of some extension? (Note: this is a fairly
|
|
* expensive lookup, which is why we try to cache the results.)
|
|
*/
|
|
extensionOid = getExtensionOfObject(classId, objectId);
|
|
|
|
/* If so, is that extension in fpinfo->shippable_extensions? */
|
|
if (OidIsValid(extensionOid) &&
|
|
list_member_oid(fpinfo->shippable_extensions, extensionOid))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Return true if given object is one of PostgreSQL's built-in objects.
|
|
*
|
|
* We use FirstGenbkiObjectId as the cutoff, so that we only consider
|
|
* objects with hand-assigned OIDs to be "built in", not for instance any
|
|
* function or type defined in the information_schema.
|
|
*
|
|
* Our constraints for dealing with types are tighter than they are for
|
|
* functions or operators: we want to accept only types that are in pg_catalog,
|
|
* else deparse_type_name might incorrectly fail to schema-qualify their names.
|
|
* Thus we must exclude information_schema types.
|
|
*
|
|
* XXX there is a problem with this, which is that the set of built-in
|
|
* objects expands over time. Something that is built-in to us might not
|
|
* be known to the remote server, if it's of an older version. But keeping
|
|
* track of that would be a huge exercise.
|
|
*/
|
|
bool
|
|
is_builtin(Oid objectId)
|
|
{
|
|
return (objectId < FirstGenbkiObjectId);
|
|
}
|
|
|
|
/*
|
|
* is_shippable
|
|
* Is this object (function/operator/type) shippable to foreign server?
|
|
*/
|
|
bool
|
|
is_shippable(Oid objectId, Oid classId, PgFdwRelationInfo *fpinfo)
|
|
{
|
|
ShippableCacheKey key;
|
|
ShippableCacheEntry *entry;
|
|
|
|
/* Built-in objects are presumed shippable. */
|
|
if (is_builtin(objectId))
|
|
return true;
|
|
|
|
/* Otherwise, give up if user hasn't specified any shippable extensions. */
|
|
if (fpinfo->shippable_extensions == NIL)
|
|
return false;
|
|
|
|
/* Initialize cache if first time through. */
|
|
if (!ShippableCacheHash)
|
|
InitializeShippableCache();
|
|
|
|
/* Set up cache hash key */
|
|
key.objid = objectId;
|
|
key.classid = classId;
|
|
key.serverid = fpinfo->server->serverid;
|
|
|
|
/* See if we already cached the result. */
|
|
entry = (ShippableCacheEntry *)
|
|
hash_search(ShippableCacheHash,
|
|
(void *) &key,
|
|
HASH_FIND,
|
|
NULL);
|
|
|
|
if (!entry)
|
|
{
|
|
/* Not found in cache, so perform shippability lookup. */
|
|
bool shippable = lookup_shippable(objectId, classId, fpinfo);
|
|
|
|
/*
|
|
* Don't create a new hash entry until *after* we have the shippable
|
|
* result in hand, as the underlying catalog lookups might trigger a
|
|
* cache invalidation.
|
|
*/
|
|
entry = (ShippableCacheEntry *)
|
|
hash_search(ShippableCacheHash,
|
|
(void *) &key,
|
|
HASH_ENTER,
|
|
NULL);
|
|
|
|
entry->shippable = shippable;
|
|
}
|
|
|
|
return entry->shippable;
|
|
}
|