diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c index fe4893a..65d90fb 100644 --- a/contrib/postgres_fdw/connection.c +++ b/contrib/postgres_fdw/connection.c @@ -235,10 +235,10 @@ connect_pg_server(ForeignServer *server, UserMapping *user) /* * Construct connection params from generic options of ForeignServer * and UserMapping. (Some of them might not be libpq options, in - * which case we'll just waste a few array slots.) Add 3 extra slots - * for fallback_application_name, client_encoding, end marker. + * which case we'll just waste a few array slots.) Add 4 extra slots + * for fallback_application_name, client_encoding, guc, end marker. */ - n = list_length(server->options) + list_length(user->options) + 3; + n = list_length(server->options) + list_length(user->options) + 4; keywords = (const char **) palloc(n * sizeof(char *)); values = (const char **) palloc(n * sizeof(char *)); @@ -247,6 +247,8 @@ connect_pg_server(ForeignServer *server, UserMapping *user) keywords + n, values + n); n += ExtractConnectionOptions(user->options, keywords + n, values + n); + n += ExtractGucOptions(server->options, + keywords + n, values + n); /* Use "postgres_fdw" as fallback_application_name. */ keywords[n] = "fallback_application_name"; @@ -286,6 +288,16 @@ connect_pg_server(ForeignServer *server, UserMapping *user) /* Prepare new session for use */ configure_remote_session(conn); + /* Free memory for "options" parameter */ + for (int i = 0; i < n; i++) + { + if (strcmp(keywords[i], "options") == 0) + { + pfree((void *)(values[i])); + break; + } + } + pfree(keywords); pfree(values); } diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index d912bd9..c529a00 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -8607,3 +8607,28 @@ SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 -- Clean-up RESET enable_partitionwise_aggregate; +-- =================================================================== +-- test for work_mem option +-- =================================================================== +BEGIN; +CREATE SERVER workmem1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS( work_mem '64kB' ); +SELECT count(*) +FROM pg_foreign_server +WHERE srvname = 'workmem1' +AND srvoptions @> array['work_mem=64kB']; + count +------- + 1 +(1 row) + +ALTER SERVER workmem1 OPTIONS( SET work_mem '8MB' ); +SELECT count(*) +FROM pg_foreign_server +WHERE srvname = 'workmem1' +AND srvoptions @> array['work_mem=8MB']; + count +------- + 1 +(1 row) + +ROLLBACK; diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c index 6854f1b..67adb79 100644 --- a/contrib/postgres_fdw/option.c +++ b/contrib/postgres_fdw/option.c @@ -22,7 +22,7 @@ #include "commands/extension.h" #include "utils/builtins.h" #include "utils/varlena.h" - +#include "utils/guc.h" /* * Describes the valid options for objects that this wrapper uses. @@ -32,6 +32,7 @@ typedef struct PgFdwOption const char *keyword; Oid optcontext; /* OID of catalog in which option may appear */ bool is_libpq_opt; /* true if it's used in libpq */ + bool is_guc_opt; /* true if it's used for GUC setting */ } PgFdwOption; /* @@ -52,7 +53,7 @@ static PQconninfoOption *libpq_options; static void InitPgFdwOptions(void); static bool is_valid_option(const char *keyword, Oid context); static bool is_libpq_option(const char *keyword); - +static bool is_guc_option(const char *keyword); /* * Validate the generic options given to a FOREIGN DATA WRAPPER, SERVER, @@ -143,6 +144,19 @@ postgres_fdw_validator(PG_FUNCTION_ARGS) errmsg("%s requires a non-negative integer value", def->defname))); } + else if (strcmp(def->defname, "work_mem") == 0) + { + int work_mem = 0; + bool result; + result = parse_int(defGetString(def), &work_mem, GUC_UNIT_KB, NULL); + if ((!result) || ((work_mem < 64) || (work_mem > MAX_KILOBYTES))) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("invalid value for option '%s'", + def->defname))); + } + } } PG_RETURN_VOID(); @@ -160,24 +174,27 @@ InitPgFdwOptions(void) /* non-libpq FDW-specific FDW options */ static const PgFdwOption non_libpq_options[] = { - {"schema_name", ForeignTableRelationId, false}, - {"table_name", ForeignTableRelationId, false}, - {"column_name", AttributeRelationId, false}, + {"schema_name", ForeignTableRelationId, false, false}, + {"table_name", ForeignTableRelationId, false, false}, + {"column_name", AttributeRelationId, false, false}, /* use_remote_estimate is available on both server and table */ - {"use_remote_estimate", ForeignServerRelationId, false}, - {"use_remote_estimate", ForeignTableRelationId, false}, + {"use_remote_estimate", ForeignServerRelationId, false, false}, + {"use_remote_estimate", ForeignTableRelationId, false, false}, /* cost factors */ - {"fdw_startup_cost", ForeignServerRelationId, false}, - {"fdw_tuple_cost", ForeignServerRelationId, false}, + {"fdw_startup_cost", ForeignServerRelationId, false, false}, + {"fdw_tuple_cost", ForeignServerRelationId, false, false}, /* shippable extensions */ - {"extensions", ForeignServerRelationId, false}, + {"extensions", ForeignServerRelationId, false, false}, /* updatable is available on both server and table */ - {"updatable", ForeignServerRelationId, false}, - {"updatable", ForeignTableRelationId, false}, + {"updatable", ForeignServerRelationId, false, false}, + {"updatable", ForeignTableRelationId, false, false}, /* fetch_size is available on both server and table */ - {"fetch_size", ForeignServerRelationId, false}, - {"fetch_size", ForeignTableRelationId, false}, - {NULL, InvalidOid, false} + {"fetch_size", ForeignServerRelationId, false, false}, + {"fetch_size", ForeignTableRelationId, false, false}, + /* for GUC settings */ + /* work_mem */ + {"work_mem", ForeignServerRelationId, false, true}, + {NULL, InvalidOid, false, false} }; /* Prevent redundant initialization. */ @@ -241,6 +258,7 @@ InitPgFdwOptions(void) else popt->optcontext = ForeignServerRelationId; popt->is_libpq_opt = true; + popt->is_guc_opt = false; popt++; } @@ -289,6 +307,25 @@ is_libpq_option(const char *keyword) } /* + * Check whether the given option is one of the valid GUC options. + */ +static bool +is_guc_option(const char *keyword) +{ + PgFdwOption *opt; + + Assert(postgres_fdw_options); /* must be initialized already */ + + for (opt = postgres_fdw_options; opt->keyword; opt++) + { + if (opt->is_guc_opt && strcmp(opt->keyword, keyword) == 0) + return true; + } + + return false; +} + +/* * Generate key-value arrays which include only libpq options from the * given list (which can contain any kind of options). Caller must have * allocated large-enough arrays. Returns number of options found. @@ -319,6 +356,42 @@ ExtractConnectionOptions(List *defelems, const char **keywords, } /* + * Generate key-value arrays which include for GUC options from the + * given list (which can contain any kind of options). Caller must have + * allocated large-enough arrays. Returns number of options found. + */ +int +ExtractGucOptions(List *defelems, const char **keywords, + const char **values) +{ + ListCell *lc; + bool is_guc = false; + StringInfoData buf; + static const char options[] = "options"; + + initStringInfo(&buf); + + foreach(lc, defelems) + { + DefElem *d = (DefElem *) lfirst(lc); + + if (is_guc_option(d->defname)) + { + is_guc = true; + appendStringInfo(&buf, " -c %s=%s ", d->defname, defGetString(d)); + } + } + + if (is_guc) + { + keywords[0] = options; + values[0] = buf.data; + } + + return (is_guc ? 1 : 0); +} + +/* * Parse a comma-separated string and return a List of the OIDs of the * extensions named in the string. If any names in the list cannot be * found, report a warning if warnOnMissing is true, else just silently diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h index 70b538e..01e94a1 100644 --- a/contrib/postgres_fdw/postgres_fdw.h +++ b/contrib/postgres_fdw/postgres_fdw.h @@ -130,6 +130,9 @@ extern int ExtractConnectionOptions(List *defelems, const char **values); extern List *ExtractExtensionList(const char *extensionsString, bool warnOnMissing); +extern int ExtractGucOptions(List *defelems, + const char **keywords, + const char **values); /* in deparse.c */ extern void classifyConditions(PlannerInfo *root, diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql index c0b0dd9..c450eca 100644 --- a/contrib/postgres_fdw/sql/postgres_fdw.sql +++ b/contrib/postgres_fdw/sql/postgres_fdw.sql @@ -2348,3 +2348,24 @@ SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 -- Clean-up RESET enable_partitionwise_aggregate; + +-- =================================================================== +-- test for work_mem option +-- =================================================================== +BEGIN; + +CREATE SERVER workmem1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS( work_mem '64kB' ); + +SELECT count(*) +FROM pg_foreign_server +WHERE srvname = 'workmem1' +AND srvoptions @> array['work_mem=64kB']; + +ALTER SERVER workmem1 OPTIONS( SET work_mem '8MB' ); + +SELECT count(*) +FROM pg_foreign_server +WHERE srvname = 'workmem1' +AND srvoptions @> array['work_mem=8MB']; + +ROLLBACK; diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml index 54b5e98..50a508e 100644 --- a/doc/src/sgml/postgres-fdw.sgml +++ b/doc/src/sgml/postgres-fdw.sgml @@ -313,6 +313,15 @@ + + work_mem + + + This option specifies the size of working memory using . + The default value is depend on th remote instance setting. + + +