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
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers