diff options
author | Tom Lane | 2007-09-03 00:39:26 +0000 |
---|---|---|
committer | Tom Lane | 2007-09-03 00:39:26 +0000 |
commit | 2abae34a2e8fde42be731b4e18d44cd08901464d (patch) | |
tree | f555c8fc5d653c24ccc407a06f46e5f1bbbf40f7 /src/backend/utils | |
parent | fcfe801ab84c124d4103f9afb1140c9c2558cb54 (diff) |
Implement function-local GUC parameter settings, as per recent discussion.
There are still some loose ends: I didn't do anything about the SET FROM
CURRENT idea yet, and it's not real clear whether we are happy with the
interaction of SET LOCAL with function-local settings. The documentation
is a bit spartan, too.
Diffstat (limited to 'src/backend/utils')
-rw-r--r-- | src/backend/utils/Gen_fmgrtab.sh | 14 | ||||
-rw-r--r-- | src/backend/utils/fmgr/fmgr.c | 91 | ||||
-rw-r--r-- | src/backend/utils/init/miscinit.c | 9 | ||||
-rw-r--r-- | src/backend/utils/init/postinit.c | 9 | ||||
-rw-r--r-- | src/backend/utils/misc/guc.c | 85 |
5 files changed, 154 insertions, 54 deletions
diff --git a/src/backend/utils/Gen_fmgrtab.sh b/src/backend/utils/Gen_fmgrtab.sh index 5543da5a61f..0c9021e6af3 100644 --- a/src/backend/utils/Gen_fmgrtab.sh +++ b/src/backend/utils/Gen_fmgrtab.sh @@ -9,7 +9,7 @@ # # # IDENTIFICATION -# $PostgreSQL: pgsql/src/backend/utils/Gen_fmgrtab.sh,v 1.36 2007/06/05 21:31:06 tgl Exp $ +# $PostgreSQL: pgsql/src/backend/utils/Gen_fmgrtab.sh,v 1.37 2007/09/03 00:39:17 tgl Exp $ # #------------------------------------------------------------------------- @@ -134,12 +134,12 @@ cat > "$$-$OIDSFILE" <<FuNkYfMgRsTuFf */ FuNkYfMgRsTuFf -# Note assumption here that prosrc == $(NF-2). +# Note assumption here that prosrc == $(NF-3). tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' < $SORTEDFILE | \ $AWK ' BEGIN { OFS = ""; } - { if (seenit[$(NF-2)]++ == 0) print "#define F_", $(NF-2), " ", $1; }' >> "$$-$OIDSFILE" + { if (seenit[$(NF-3)]++ == 0) print "#define F_", $(NF-3), " ", $1; }' >> "$$-$OIDSFILE" if [ $? -ne 0 ]; then cleanup @@ -184,9 +184,9 @@ cat > "$$-$TABLEFILE" <<FuNkYfMgRtAbStUfF FuNkYfMgRtAbStUfF -# Note assumption here that prosrc == $(NF-2). +# Note assumption here that prosrc == $(NF-3). -$AWK '{ print "extern Datum", $(NF-2), "(PG_FUNCTION_ARGS);"; }' $SORTEDFILE >> "$$-$TABLEFILE" +$AWK '{ print "extern Datum", $(NF-3), "(PG_FUNCTION_ARGS);"; }' $SORTEDFILE >> "$$-$TABLEFILE" if [ $? -ne 0 ]; then cleanup @@ -204,7 +204,7 @@ FuNkYfMgRtAbStUfF # may seem tedious, but avoid the temptation to write a quick x?y:z # conditional expression instead. Not all awks have conditional expressions. # -# Note assumptions here that prosrc == $(NF-2), pronargs == $13, +# Note assumptions here that prosrc == $(NF-3), pronargs == $13, # proisstrict == $10, proretset == $11 $AWK 'BEGIN { @@ -212,7 +212,7 @@ $AWK 'BEGIN { Bool["f"] = "false" } { printf (" { %d, \"%s\", %d, %s, %s, %s },\n"), \ - $1, $(NF-2), $13, Bool[$10], Bool[$11], $(NF-2) + $1, $(NF-3), $13, Bool[$10], Bool[$11], $(NF-3) }' $SORTEDFILE >> "$$-$TABLEFILE" if [ $? -ne 0 ]; then diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c index 4294e10f5db..f2d49eaf536 100644 --- a/src/backend/utils/fmgr/fmgr.c +++ b/src/backend/utils/fmgr/fmgr.c @@ -8,13 +8,14 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.108 2007/07/31 15:49:49 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.109 2007/09/03 00:39:18 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" +#include "access/heapam.h" #include "access/tuptoaster.h" #include "catalog/pg_language.h" #include "catalog/pg_proc.h" @@ -23,9 +24,11 @@ #include "parser/parse_expr.h" #include "utils/builtins.h" #include "utils/fmgrtab.h" +#include "utils/guc.h" #include "utils/lsyscache.h" #include "utils/syscache.h" + /* * Declaration for old-style function pointer type. This is now used only * in fmgr_oldstyle() and is no longer exported. @@ -212,7 +215,13 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt, finfo->fn_strict = procedureStruct->proisstrict; finfo->fn_retset = procedureStruct->proretset; - if (procedureStruct->prosecdef && !ignore_security) + /* + * If it has prosecdef set, or non-null proconfig, use + * fmgr_security_definer call handler. + */ + if (!ignore_security && + (procedureStruct->prosecdef || + !heap_attisnull(procedureTuple, Anum_pg_proc_proconfig))) { finfo->fn_addr = fmgr_security_definer; finfo->fn_oid = functionId; @@ -826,34 +835,45 @@ fmgr_oldstyle(PG_FUNCTION_ARGS) /* - * Support for security definer functions + * Support for security-definer and proconfig-using functions. We support + * both of these features using the same call handler, because they are + * often used together and it would be inefficient (as well as notationally + * messy) to have two levels of call handler involved. */ - struct fmgr_security_definer_cache { - FmgrInfo flinfo; - Oid userid; + FmgrInfo flinfo; /* lookup info for target function */ + Oid userid; /* userid to set, or InvalidOid */ + ArrayType *proconfig; /* GUC values to set, or NULL */ }; /* - * Function handler for security definer functions. We extract the - * OID of the actual function and do a fmgr lookup again. Then we - * look up the owner of the function and cache both the fmgr info and - * the owner ID. During the call we temporarily replace the flinfo - * with the cached/looked-up one, while keeping the outer fcinfo - * (which contains all the actual arguments, etc.) intact. + * Function handler for security-definer/proconfig functions. We extract the + * OID of the actual function and do a fmgr lookup again. Then we fetch the + * pg_proc row and copy the owner ID and proconfig fields. (All this info + * is cached for the duration of the current query.) To execute a call, + * we temporarily replace the flinfo with the cached/looked-up one, while + * keeping the outer fcinfo (which contains all the actual arguments, etc.) + * intact. This is not re-entrant, but then the fcinfo itself can't be used + * re-entrantly anyway. */ static Datum fmgr_security_definer(PG_FUNCTION_ARGS) { Datum result; - FmgrInfo *save_flinfo; struct fmgr_security_definer_cache *volatile fcache; + FmgrInfo *save_flinfo; Oid save_userid; - HeapTuple tuple; + volatile int save_nestlevel; if (!fcinfo->flinfo->fn_extra) { + HeapTuple tuple; + Form_pg_proc procedureStruct; + Datum datum; + bool isnull; + MemoryContext oldcxt; + fcache = MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt, sizeof(*fcache)); @@ -867,7 +887,20 @@ fmgr_security_definer(PG_FUNCTION_ARGS) if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for function %u", fcinfo->flinfo->fn_oid); - fcache->userid = ((Form_pg_proc) GETSTRUCT(tuple))->proowner; + procedureStruct = (Form_pg_proc) GETSTRUCT(tuple); + + if (procedureStruct->prosecdef) + fcache->userid = procedureStruct->proowner; + + datum = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_proconfig, + &isnull); + if (!isnull) + { + oldcxt = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt); + fcache->proconfig = DatumGetArrayTypePCopy(datum); + MemoryContextSwitchTo(oldcxt); + } + ReleaseSysCache(tuple); fcinfo->flinfo->fn_extra = fcache; @@ -876,25 +909,47 @@ fmgr_security_definer(PG_FUNCTION_ARGS) fcache = fcinfo->flinfo->fn_extra; save_flinfo = fcinfo->flinfo; + /* GetUserId is cheap enough that no harm in a wasted call */ save_userid = GetUserId(); + if (fcache->proconfig) /* Need a new GUC nesting level */ + save_nestlevel = NewGUCNestLevel(); + else + save_nestlevel = 0; /* keep compiler quiet */ PG_TRY(); { fcinfo->flinfo = &fcache->flinfo; - SetUserId(fcache->userid); + + if (OidIsValid(fcache->userid)) + SetUserId(fcache->userid); + + if (fcache->proconfig) + { + /* The options are processed as if by SET LOCAL var = val */ + ProcessGUCArray(fcache->proconfig, + (superuser() ? PGC_SUSET : PGC_USERSET), + PGC_S_SESSION, + true); + } result = FunctionCallInvoke(fcinfo); } PG_CATCH(); { fcinfo->flinfo = save_flinfo; - SetUserId(save_userid); + if (fcache->proconfig) + AtEOXact_GUC(false, save_nestlevel); + if (OidIsValid(fcache->userid)) + SetUserId(save_userid); PG_RE_THROW(); } PG_END_TRY(); fcinfo->flinfo = save_flinfo; - SetUserId(save_userid); + if (fcache->proconfig) + AtEOXact_GUC(true, save_nestlevel); + if (OidIsValid(fcache->userid)) + SetUserId(save_userid); return result; } diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index 32f7cc06415..599fc9938b6 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/init/miscinit.c,v 1.162 2007/02/15 23:23:23 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/utils/init/miscinit.c,v 1.163 2007/09/03 00:39:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -448,7 +448,12 @@ InitializeSessionUserId(const char *rolename) { ArrayType *a = DatumGetArrayTypeP(datum); - ProcessGUCArray(a, PGC_S_USER); + /* + * We process all the options at SUSET level. We assume that the + * right to insert an option into pg_authid was checked when it was + * inserted. + */ + ProcessGUCArray(a, PGC_SUSET, PGC_S_USER, false); } ReleaseSysCache(roleTup); diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index ff70c8d6a66..965e94a5387 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/init/postinit.c,v 1.176 2007/05/27 05:37:49 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/init/postinit.c,v 1.177 2007/09/03 00:39:18 tgl Exp $ * * *------------------------------------------------------------------------- @@ -250,7 +250,12 @@ CheckMyDatabase(const char *name, bool am_superuser) { ArrayType *a = DatumGetArrayTypeP(datum); - ProcessGUCArray(a, PGC_S_DATABASE); + /* + * We process all the options at SUSET level. We assume that the + * right to insert an option into pg_database was checked when it + * was inserted. + */ + ProcessGUCArray(a, PGC_SUSET, PGC_S_DATABASE, false); } } diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index e70768aa840..b8c7b854945 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -10,7 +10,7 @@ * Written by Peter Eisentraut <peter_e@gmx.net>. * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.414 2007/08/21 01:11:19 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.415 2007/09/03 00:39:19 tgl Exp $ * *-------------------------------------------------------------------- */ @@ -2495,6 +2495,8 @@ static bool guc_dirty; /* TRUE if need to do commit/abort work */ static bool reporting_enabled; /* TRUE to enable GUC_REPORT */ +static int GUCNestLevel = 0; /* 1 when in main transaction */ + static int guc_var_compare(const void *a, const void *b); static int guc_name_compare(const char *namea, const char *nameb); @@ -3388,17 +3390,16 @@ ResetAllOptions(void) static void push_old_value(struct config_generic * gconf) { - int my_level = GetCurrentTransactionNestLevel(); GucStack *stack; /* If we're not inside a transaction, do nothing */ - if (my_level == 0) + if (GUCNestLevel == 0) return; for (;;) { /* Done if we already pushed it at this nesting depth */ - if (gconf->stack && gconf->stack->nest_level >= my_level) + if (gconf->stack && gconf->stack->nest_level >= GUCNestLevel) return; /* @@ -3457,20 +3458,53 @@ push_old_value(struct config_generic * gconf) } /* - * Do GUC processing at transaction or subtransaction commit or abort. + * Do GUC processing at main transaction start. */ void -AtEOXact_GUC(bool isCommit, bool isSubXact) +AtStart_GUC(void) +{ + /* + * The nest level should be 0 between transactions; if it isn't, + * somebody didn't call AtEOXact_GUC, or called it with the wrong + * nestLevel. We throw a warning but make no other effort to clean up. + */ + if (GUCNestLevel != 0) + elog(WARNING, "GUC nest level = %d at transaction start", + GUCNestLevel); + GUCNestLevel = 1; +} + +/* + * Enter a new nesting level for GUC values. This is called at subtransaction + * start and when entering a function that has proconfig settings. NOTE that + * we must not risk error here, else subtransaction start will be unhappy. + */ +int +NewGUCNestLevel(void) +{ + return ++GUCNestLevel; +} + +/* + * Do GUC processing at transaction or subtransaction commit or abort, or + * when exiting a function that has proconfig settings. (The name is thus + * a bit of a misnomer; perhaps it should be ExitGUCNestLevel or some such.) + * During abort, we discard all GUC settings that were applied at nesting + * levels >= nestLevel. nestLevel == 1 corresponds to the main transaction. + */ +void +AtEOXact_GUC(bool isCommit, int nestLevel) { - int my_level; int i; + Assert(nestLevel > 0 && nestLevel <= GUCNestLevel); + /* Quick exit if nothing's changed in this transaction */ if (!guc_dirty) + { + GUCNestLevel = nestLevel - 1; return; - - my_level = GetCurrentTransactionNestLevel(); - Assert(isSubXact ? (my_level > 1) : (my_level == 1)); + } for (i = 0; i < num_guc_variables; i++) { @@ -3491,9 +3525,9 @@ AtEOXact_GUC(bool isCommit, bool isSubXact) /* Assert that we stacked old value before changing it */ Assert(stack != NULL && (my_status & GUC_HAVE_STACK)); /* However, the last change may have been at an outer xact level */ - if (stack->nest_level < my_level) + if (stack->nest_level < nestLevel) continue; - Assert(stack->nest_level == my_level); + Assert(stack->nest_level == nestLevel); /* * We will pop the stack entry. Start by restoring outer xact status @@ -3677,7 +3711,7 @@ AtEOXact_GUC(bool isCommit, bool isSubXact) set_string_field(conf, &stack->tentative_val.stringval, NULL); /* Don't store tentative value separately after commit */ - if (!isSubXact) + if (nestLevel == 1) set_string_field(conf, &conf->tentative_val, NULL); break; } @@ -3691,7 +3725,7 @@ AtEOXact_GUC(bool isCommit, bool isSubXact) * If we're now out of all xact levels, forget TENTATIVE status bit; * there's nothing tentative about the value anymore. */ - if (!isSubXact) + if (nestLevel == 1) { Assert(gconf->stack == NULL); gconf->status = 0; @@ -3708,8 +3742,11 @@ AtEOXact_GUC(bool isCommit, bool isSubXact) * that all outer transaction levels will have stacked values to deal * with.) */ - if (!isSubXact) + if (nestLevel == 1) guc_dirty = false; + + /* Update nesting level */ + GUCNestLevel = nestLevel - 1; } @@ -6078,11 +6115,14 @@ ParseLongOption(const char *string, char **name, char **value) /* - * Handle options fetched from pg_database.datconfig or pg_authid.rolconfig. + * Handle options fetched from pg_database.datconfig, pg_authid.rolconfig, + * pg_proc.proconfig, etc. Caller must specify proper context/source/local. + * * The array parameter must be an array of TEXT (it must not be NULL). */ void -ProcessGUCArray(ArrayType *array, GucSource source) +ProcessGUCArray(ArrayType *array, + GucContext context, GucSource source, bool isLocal) { int i; @@ -6090,7 +6130,6 @@ ProcessGUCArray(ArrayType *array, GucSource source) Assert(ARR_ELEMTYPE(array) == TEXTOID); Assert(ARR_NDIM(array) == 1); Assert(ARR_LBOUND(array)[0] == 1); - Assert(source == PGC_S_DATABASE || source == PGC_S_USER); for (i = 1; i <= ARR_DIMS(array)[0]; i++) { @@ -6117,17 +6156,13 @@ ProcessGUCArray(ArrayType *array, GucSource source) { ereport(WARNING, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("could not parse setting for parameter \"%s\"", name))); + errmsg("could not parse setting for parameter \"%s\"", + name))); free(name); continue; } - /* - * We process all these options at SUSET level. We assume that the - * right to insert an option into pg_database or pg_authid was checked - * when it was inserted. - */ - SetConfigOption(name, value, PGC_SUSET, source); + (void) set_config_option(name, value, context, source, isLocal, true); free(name); if (value) |