Re: [PATCH] postgres_fdw extension support

From: Andres Freund <andres(at)anarazel(dot)de>
To: Michael Paquier <michael(dot)paquier(at)gmail(dot)com>
Cc: Paul Ramsey <pramsey(at)cleverelephant(dot)ca>, Simon Riggs <simon(at)2ndquadrant(dot)com>, Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>, Pgsql Hackers <pgsql-hackers(at)postgresql(dot)org>, Robert Haas <robertmhaas(at)gmail(dot)com>
Subject: Re: [PATCH] postgres_fdw extension support
Date: 2015-10-03 15:49:03
Message-ID: 20151003154903.GC30738@alap3.anarazel.de
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi,

On 2015-10-01 11:46:43 +0900, Michael Paquier wrote:
> diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
> index 7547ec2..864bf53 100644
> --- a/contrib/postgres_fdw/option.c
> +++ b/contrib/postgres_fdw/option.c
> @@ -19,6 +19,8 @@
> #include "catalog/pg_foreign_table.h"
> #include "catalog/pg_user_mapping.h"
> #include "commands/defrem.h"
> +#include "commands/extension.h"
> +#include "utils/builtins.h"
>
>
> /*
> @@ -124,6 +126,11 @@ postgres_fdw_validator(PG_FUNCTION_ARGS)
> errmsg("%s requires a non-negative numeric value",
> def->defname)));
> }
> + else if (strcmp(def->defname, "extensions") == 0)
> + {
> + /* this must have already-installed extensions */

I don't understand that comment.

> PG_RETURN_VOID();
> @@ -153,6 +160,8 @@ InitPgFdwOptions(void)
> /* updatable is available on both server and table */
> {"updatable", ForeignServerRelationId, false},
> {"updatable", ForeignTableRelationId, false},
> + /* extensions is available on server */
> + {"extensions", ForeignServerRelationId, false},

awkward spelling in comment.

> +/*
> + * Parse a comma-separated string and return a List of the Oids of the
> + * extensions in the string. If an extension provided cannot be looked
> + * up in the catalog (it hasn't been installed or doesn't exist) then
> + * throw up an error.
> + */

s/throw up an error/throw an error/ or raise an error.

> +/*
> + * FDW-specific planner information kept in RelOptInfo.fdw_private for a
> + * foreign table. This information is collected by postgresGetForeignRelSize.
> + */
> +typedef struct PgFdwRelationInfo
> +{
> + /* baserestrictinfo clauses, broken down into safe and unsafe subsets. */
> + List *remote_conds;
> + List *local_conds;
> +
> + /* Bitmap of attr numbers we need to fetch from the remote server. */
> + Bitmapset *attrs_used;
> +
> + /* Cost and selectivity of local_conds. */
> + QualCost local_conds_cost;
> + Selectivity local_conds_sel;
> +
> + /* Estimated size and cost for a scan with baserestrictinfo quals. */
> + double rows;
> + int width;
> + Cost startup_cost;
> + Cost total_cost;
> +
> + /* Options extracted from catalogs. */
> + bool use_remote_estimate;
> + Cost fdw_startup_cost;
> + Cost fdw_tuple_cost;
> +
> + /* Optional extensions to support (list of oid) */

*oids

> +/* objid is the lookup key, must appear first */
> +typedef struct
> +{
> + /* extension the object appears within, or InvalidOid if none */
> + Oid objid;
> +} ShippableCacheKey;
> +
> +typedef struct
> +{
> + /* lookup key - must be first */
> + ShippableCacheKey key;
> + bool shippable;
> +} ShippableCacheEntry;
> +
> +/*
> + * Flush all cache entries when pg_foreign_server is updated.
> + */
> +static void
> +InvalidateShippableCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
> +{
> + HASH_SEQ_STATUS status;
> + ShippableCacheEntry *entry;
> +
> + 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");
> + }
> +}

> +/*
> + * Returns true if given operator/function is part of an extension declared in
> + * the server options.
> + */
> +static bool
> +lookup_shippable(Oid objnumber, List *extension_list)
> +{
> + static int nkeys = 1;
> + ScanKeyData key[nkeys];
> + HeapTuple tup;
> + Relation depRel;
> + SysScanDesc scan;
> + bool is_shippable = false;
> +
> + /* Always return false if we don't have any declared extensions */

Imo there's nothing declared here...

> + if (extension_list == NIL)
> + return false;
> +
> + /* We need this relation to scan */

Not sure what that means.

> + depRel = heap_open(DependRelationId, RowExclusiveLock);
> +
> + /*
> + * Scan the system dependency table for all entries this object
> + * depends on, then iterate through and see if one of them
> + * is an extension declared by the user in the options
> + */
> + ScanKeyInit(&key[0],
> + Anum_pg_depend_objid,
> + BTEqualStrategyNumber, F_OIDEQ,
> + ObjectIdGetDatum(objnumber));
> +
> + scan = systable_beginscan(depRel, DependDependerIndexId, true,
> + GetCatalogSnapshot(depRel->rd_id), nkeys, key);
> +
> + while (HeapTupleIsValid(tup = systable_getnext(scan)))
> + {
> + Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup);
> +
> + if (foundDep->deptype == DEPENDENCY_EXTENSION &&
> + list_member_oid(extension_list, foundDep->refobjid))
> + {
> + is_shippable = true;
> + break;
> + }
> + }

Hm.

> +/*
> + * is_shippable
> + * Is this object (proc/op/type) shippable to foreign server?
> + * Check cache first, then look-up whether (proc/op/type) is
> + * part of a declared extension if it is not cached.
> + */
> +bool
> +is_shippable(Oid objnumber, List *extension_list)
> +{
> + ShippableCacheKey key;
> + ShippableCacheEntry *entry;
> +
> + /* Always return false if we don't have any declared extensions */
> + if (extension_list == NIL)
> + return false;

I again dislike declared here ;)

> + /* Find existing cache, if any. */
> + if (!ShippableCacheHash)
> + InitializeShippableCache();
> +
> + /* Zero out the key */
> + memset(&key, 0, sizeof(key));
> +
> + key.objid = objnumber;

Hm. Oids can conflict between different system relations. Shouldn't the
key be over class (i.e. pg_proc, pg_type etc.) and object id?

> + entry = (ShippableCacheEntry *)
> + hash_search(ShippableCacheHash,
> + (void *) &key,
> + HASH_FIND,
> + NULL);
> +
> + /* Not found in ShippableCacheHash cache. Construct new entry. */
> + if (!entry)
> + {
> + /*
> + * Right now "shippability" is exclusively a function of whether
> + * the obj (proc/op/type) is in an extension declared by the user.
> + * In the future we could additionally have a whitelist of functions
> + * declared one at a time.
> + */
> + bool shippable = lookup_shippable(objnumber, extension_list);
> +
> + entry = (ShippableCacheEntry *)
> + hash_search(ShippableCacheHash,
> + (void *) &key,
> + HASH_ENTER,
> + NULL);
> +
> + entry->shippable = shippable;
> + }
>

I suggest adding a comment that we consciously are only HASH_ENTERing
the key after doing the lookup_shippable(), to avoid the issue that the
lookup might accept cache invalidations and thus delete the entry again.

Greetings,

Andres Freund

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Petr Jelinek 2015-10-03 15:56:07 Re: creating extension including dependencies
Previous Message Andres Freund 2015-10-03 15:34:23 Re: WIP: Rework access method interface