1
0
mirror of https://github.com/postgres/postgres.git synced 2025-08-21 10:42:50 +03:00

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.
This commit is contained in:
Tom Lane
2007-09-03 00:39:26 +00:00
parent fcfe801ab8
commit 2abae34a2e
23 changed files with 2583 additions and 2151 deletions

View File

@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.246 2007/08/01 22:45:07 tgl Exp $
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.247 2007/09/03 00:39:13 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -117,7 +117,8 @@ typedef struct TransactionStateData
int savepointLevel; /* savepoint level */
TransState state; /* low-level state */
TBlockState blockState; /* high-level state */
int nestingLevel; /* nest depth */
int nestingLevel; /* transaction nesting depth */
int gucNestLevel; /* GUC context nesting depth */
MemoryContext curTransactionContext; /* my xact-lifetime context */
ResourceOwner curTransactionOwner; /* my query resources */
List *childXids; /* subcommitted child XIDs */
@@ -141,7 +142,8 @@ static TransactionStateData TopTransactionStateData = {
TRANS_DEFAULT, /* transaction state */
TBLOCK_DEFAULT, /* transaction block state from the client
* perspective */
0, /* nesting level */
0, /* transaction nesting depth */
0, /* GUC context nesting depth */
NULL, /* cur transaction context */
NULL, /* cur transaction resource owner */
NIL, /* subcommitted child Xids */
@@ -1499,6 +1501,7 @@ StartTransaction(void)
* initialize current transaction state fields
*/
s->nestingLevel = 1;
s->gucNestLevel = 1;
s->childXids = NIL;
/*
@@ -1513,6 +1516,7 @@ StartTransaction(void)
/*
* initialize other subsystems for new transaction
*/
AtStart_GUC();
AtStart_Inval();
AtStart_Cache();
AfterTriggerBeginXact();
@@ -1699,7 +1703,7 @@ CommitTransaction(void)
/* Check we've released all catcache entries */
AtEOXact_CatCache(true);
AtEOXact_GUC(true, false);
AtEOXact_GUC(true, 1);
AtEOXact_SPI(true);
AtEOXact_on_commit_actions(true);
AtEOXact_Namespace(true);
@@ -1721,6 +1725,7 @@ CommitTransaction(void)
s->transactionId = InvalidTransactionId;
s->subTransactionId = InvalidSubTransactionId;
s->nestingLevel = 0;
s->gucNestLevel = 0;
s->childXids = NIL;
/*
@@ -1920,7 +1925,7 @@ PrepareTransaction(void)
AtEOXact_CatCache(true);
/* PREPARE acts the same as COMMIT as far as GUC is concerned */
AtEOXact_GUC(true, false);
AtEOXact_GUC(true, 1);
AtEOXact_SPI(true);
AtEOXact_on_commit_actions(true);
AtEOXact_Namespace(true);
@@ -1941,6 +1946,7 @@ PrepareTransaction(void)
s->transactionId = InvalidTransactionId;
s->subTransactionId = InvalidSubTransactionId;
s->nestingLevel = 0;
s->gucNestLevel = 0;
s->childXids = NIL;
/*
@@ -2075,7 +2081,7 @@ AbortTransaction(void)
false, true);
AtEOXact_CatCache(false);
AtEOXact_GUC(false, false);
AtEOXact_GUC(false, 1);
AtEOXact_SPI(false);
AtEOXact_on_commit_actions(false);
AtEOXact_Namespace(false);
@@ -2124,6 +2130,7 @@ CleanupTransaction(void)
s->transactionId = InvalidTransactionId;
s->subTransactionId = InvalidSubTransactionId;
s->nestingLevel = 0;
s->gucNestLevel = 0;
s->childXids = NIL;
/*
@@ -3788,7 +3795,7 @@ CommitSubTransaction(void)
RESOURCE_RELEASE_AFTER_LOCKS,
true, false);
AtEOXact_GUC(true, true);
AtEOXact_GUC(true, s->gucNestLevel);
AtEOSubXact_SPI(true, s->subTransactionId);
AtEOSubXact_on_commit_actions(true, s->subTransactionId,
s->parent->subTransactionId);
@@ -3901,7 +3908,7 @@ AbortSubTransaction(void)
RESOURCE_RELEASE_AFTER_LOCKS,
false, false);
AtEOXact_GUC(false, true);
AtEOXact_GUC(false, s->gucNestLevel);
AtEOSubXact_SPI(false, s->subTransactionId);
AtEOSubXact_on_commit_actions(false, s->subTransactionId,
s->parent->subTransactionId);
@@ -4017,6 +4024,7 @@ PushTransaction(void)
s->subTransactionId = currentSubTransactionId;
s->parent = p;
s->nestingLevel = p->nestingLevel + 1;
s->gucNestLevel = NewGUCNestLevel();
s->savepointLevel = p->savepointLevel;
s->state = TRANS_DEFAULT;
s->blockState = TBLOCK_SUBBEGIN;

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.86 2007/04/02 03:49:37 tgl Exp $
* $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.87 2007/09/03 00:39:14 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -212,6 +212,7 @@ AggregateCreate(const char *aggName,
PointerGetDatum(NULL), /* allParamTypes */
PointerGetDatum(NULL), /* parameterModes */
PointerGetDatum(NULL), /* parameterNames */
PointerGetDatum(NULL), /* proconfig */
1, /* procost */
0); /* prorows */

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.145 2007/06/06 23:00:37 tgl Exp $
* $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.146 2007/09/03 00:39:14 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -49,9 +49,9 @@ static bool match_prosrc_to_literal(const char *prosrc, const char *literal,
/* ----------------------------------------------------------------
* ProcedureCreate
*
* Note: allParameterTypes, parameterModes, parameterNames are either arrays
* of the proper types or NULL. We declare them Datum, not "ArrayType *",
* to avoid importing array.h into pg_proc.h.
* Note: allParameterTypes, parameterModes, parameterNames, and proconfig
* are either arrays of the proper types or NULL. We declare them Datum,
* not "ArrayType *", to avoid importing array.h into pg_proc.h.
* ----------------------------------------------------------------
*/
Oid
@@ -72,6 +72,7 @@ ProcedureCreate(const char *procedureName,
Datum allParameterTypes,
Datum parameterModes,
Datum parameterNames,
Datum proconfig,
float4 procost,
float4 prorows)
{
@@ -251,6 +252,10 @@ ProcedureCreate(const char *procedureName,
CStringGetDatum(prosrc));
values[Anum_pg_proc_probin - 1] = DirectFunctionCall1(textin,
CStringGetDatum(probin));
if (proconfig != PointerGetDatum(NULL))
values[Anum_pg_proc_proconfig - 1] = proconfig;
else
nulls[Anum_pg_proc_proconfig - 1] = 'n';
/* start out with empty permissions */
nulls[Anum_pg_proc_proacl - 1] = 'n';

View File

@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.83 2007/04/02 03:49:37 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.84 2007/09/03 00:39:15 tgl Exp $
*
* DESCRIPTION
* These routines take the parse tree and pick out the
@@ -50,10 +50,14 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/guc.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
static void AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId);
static void AlterFunctionOwner_internal(Relation rel, HeapTuple tup,
Oid newOwnerId);
/*
* Examine the RETURNS clause of the CREATE FUNCTION statement
@@ -267,13 +271,15 @@ examine_parameter_list(List *parameters, Oid languageOid,
* FUNCTION and ALTER FUNCTION and return it via one of the out
* parameters. Returns true if the passed option was recognized. If
* the out parameter we were going to assign to points to non-NULL,
* raise a duplicate error.
* raise a duplicate-clause error. (We don't try to detect duplicate
* SET parameters though --- if you're redundant, the last one wins.)
*/
static bool
compute_common_attribute(DefElem *defel,
DefElem **volatility_item,
DefElem **strict_item,
DefElem **security_item,
List **set_items,
DefElem **cost_item,
DefElem **rows_item)
{
@@ -298,6 +304,10 @@ compute_common_attribute(DefElem *defel,
*security_item = defel;
}
else if (strcmp(defel->defname, "set") == 0)
{
*set_items = lappend(*set_items, defel->arg);
}
else if (strcmp(defel->defname, "cost") == 0)
{
if (*cost_item)
@@ -343,6 +353,51 @@ interpret_func_volatility(DefElem *defel)
}
}
/*
* Update a proconfig value according to a list of SET and RESET items.
*
* The input and result may be NULL to signify a null entry.
*/
static ArrayType *
update_proconfig_value(ArrayType *a, List *set_items)
{
ListCell *l;
foreach(l, set_items)
{
Node *sitem = (Node *) lfirst(l);
if (IsA(sitem, VariableSetStmt))
{
VariableSetStmt *sstmt = (VariableSetStmt *) sitem;
if (sstmt->args)
{
char *valuestr;
valuestr = flatten_set_variable_args(sstmt->name, sstmt->args);
a = GUCArrayAdd(a, sstmt->name, valuestr);
}
else /* SET TO DEFAULT */
a = GUCArrayDelete(a, sstmt->name);
}
else if (IsA(sitem, VariableResetStmt))
{
VariableResetStmt *rstmt = (VariableResetStmt *) sitem;
if (strcmp(rstmt->name, "all") == 0)
a = NULL; /* RESET ALL */
else
a = GUCArrayDelete(a, rstmt->name);
}
else
elog(ERROR, "unexpected node type: %d", nodeTag(sitem));
}
return a;
}
/*
* Dissect the list of options assembled in gram.y into function
* attributes.
@@ -354,6 +409,7 @@ compute_attributes_sql_style(List *options,
char *volatility_p,
bool *strict_p,
bool *security_definer,
ArrayType **proconfig,
float4 *procost,
float4 *prorows)
{
@@ -363,6 +419,7 @@ compute_attributes_sql_style(List *options,
DefElem *volatility_item = NULL;
DefElem *strict_item = NULL;
DefElem *security_item = NULL;
List *set_items = NIL;
DefElem *cost_item = NULL;
DefElem *rows_item = NULL;
@@ -390,6 +447,7 @@ compute_attributes_sql_style(List *options,
&volatility_item,
&strict_item,
&security_item,
&set_items,
&cost_item,
&rows_item))
{
@@ -429,6 +487,8 @@ compute_attributes_sql_style(List *options,
*strict_p = intVal(strict_item->arg);
if (security_item)
*security_definer = intVal(security_item->arg);
if (set_items)
*proconfig = update_proconfig_value(NULL, set_items);
if (cost_item)
{
*procost = defGetNumeric(cost_item);
@@ -557,6 +617,7 @@ CreateFunction(CreateFunctionStmt *stmt)
bool isStrict,
security;
char volatility;
ArrayType *proconfig;
float4 procost;
float4 prorows;
HeapTuple languageTuple;
@@ -577,6 +638,7 @@ CreateFunction(CreateFunctionStmt *stmt)
isStrict = false;
security = false;
volatility = PROVOLATILE_VOLATILE;
proconfig = NULL;
procost = -1; /* indicates not set */
prorows = -1; /* indicates not set */
@@ -584,7 +646,7 @@ CreateFunction(CreateFunctionStmt *stmt)
compute_attributes_sql_style(stmt->options,
&as_clause, &language,
&volatility, &isStrict, &security,
&procost, &prorows);
&proconfig, &procost, &prorows);
/* Convert language name to canonical case */
languageName = case_translate_language_name(language);
@@ -736,6 +798,7 @@ CreateFunction(CreateFunctionStmt *stmt)
PointerGetDatum(allParameterTypes),
PointerGetDatum(parameterModes),
PointerGetDatum(parameterNames),
PointerGetDatum(proconfig),
procost,
prorows);
}
@@ -1084,6 +1147,7 @@ AlterFunction(AlterFunctionStmt *stmt)
DefElem *volatility_item = NULL;
DefElem *strict_item = NULL;
DefElem *security_def_item = NULL;
List *set_items = NIL;
DefElem *cost_item = NULL;
DefElem *rows_item = NULL;
@@ -1121,6 +1185,7 @@ AlterFunction(AlterFunctionStmt *stmt)
&volatility_item,
&strict_item,
&security_def_item,
&set_items,
&cost_item,
&rows_item) == false)
elog(ERROR, "option \"%s\" not recognized", defel->defname);
@@ -1152,6 +1217,40 @@ AlterFunction(AlterFunctionStmt *stmt)
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("ROWS is not applicable when function does not return a set")));
}
if (set_items)
{
Datum datum;
bool isnull;
ArrayType *a;
Datum repl_val[Natts_pg_proc];
char repl_null[Natts_pg_proc];
char repl_repl[Natts_pg_proc];
/* extract existing proconfig setting */
datum = SysCacheGetAttr(PROCOID, tup, Anum_pg_proc_proconfig, &isnull);
a = isnull ? NULL : DatumGetArrayTypeP(datum);
/* update according to each SET or RESET item, left to right */
a = update_proconfig_value(a, set_items);
/* update the tuple */
memset(repl_repl, ' ', sizeof(repl_repl));
repl_repl[Anum_pg_proc_proconfig - 1] = 'r';
if (a == NULL)
{
repl_val[Anum_pg_proc_proconfig - 1] = (Datum) 0;
repl_null[Anum_pg_proc_proconfig - 1] = 'n';
}
else
{
repl_val[Anum_pg_proc_proconfig - 1] = PointerGetDatum(a);
repl_null[Anum_pg_proc_proconfig - 1] = ' ';
}
tup = heap_modifytuple(tup, RelationGetDescr(rel),
repl_val, repl_null, repl_repl);
}
/* Do the update */
simple_heap_update(rel, &tup->t_self, tup);

View File

@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/proclang.c,v 1.72 2007/03/26 16:58:38 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/proclang.c,v 1.73 2007/09/03 00:39:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -142,6 +142,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
PointerGetDatum(NULL),
PointerGetDatum(NULL),
PointerGetDatum(NULL),
PointerGetDatum(NULL),
1,
0);
}
@@ -174,6 +175,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
PointerGetDatum(NULL),
PointerGetDatum(NULL),
PointerGetDatum(NULL),
PointerGetDatum(NULL),
1,
0);
}

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.247 2007/06/23 22:12:50 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.248 2007/09/03 00:39:15 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -19,6 +19,7 @@
#include "postgres.h"
#include "access/heapam.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_language.h"
#include "catalog/pg_operator.h"
@@ -2923,6 +2924,7 @@ inline_function(Oid funcid, Oid result_type, List *args,
if (funcform->prolang != SQLlanguageId ||
funcform->prosecdef ||
funcform->proretset ||
!heap_attisnull(func_tuple, Anum_pg_proc_proconfig) ||
funcform->pronargs != list_length(args))
return NULL;

View File

@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.600 2007/08/22 05:13:50 tgl Exp $
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.601 2007/09/03 00:39:16 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -4270,6 +4270,16 @@ common_func_opt_item:
{
$$ = makeDefElem("rows", (Node *)$2);
}
| SET set_rest
{
/* we abuse the normal content of a DefElem here */
$$ = makeDefElem("set", (Node *)$2);
}
| VariableResetStmt
{
/* we abuse the normal content of a DefElem here */
$$ = makeDefElem("set", $1);
}
;
createfunc_opt_item:
@@ -4564,13 +4574,13 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name
n->newname = $6;
$$ = (Node *)n;
}
| ALTER FUNCTION func_name func_args RENAME TO name
| ALTER FUNCTION function_with_argtypes RENAME TO name
{
RenameStmt *n = makeNode(RenameStmt);
n->renameType = OBJECT_FUNCTION;
n->object = $3;
n->objarg = extractArgTypes($4);
n->newname = $7;
n->object = $3->funcname;
n->objarg = $3->funcargs;
n->newname = $6;
$$ = (Node *)n;
}
| ALTER GROUP_P RoleId RENAME TO RoleId
@@ -4755,13 +4765,13 @@ AlterObjectSchemaStmt:
n->newschema = $6;
$$ = (Node *)n;
}
| ALTER FUNCTION func_name func_args SET SCHEMA name
| ALTER FUNCTION function_with_argtypes SET SCHEMA name
{
AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
n->objectType = OBJECT_FUNCTION;
n->object = $3;
n->objarg = extractArgTypes($4);
n->newschema = $7;
n->object = $3->funcname;
n->objarg = $3->funcargs;
n->newschema = $6;
$$ = (Node *)n;
}
| ALTER SEQUENCE relation_expr SET SCHEMA name
@@ -4829,13 +4839,13 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId
n->newowner = $6;
$$ = (Node *)n;
}
| ALTER FUNCTION func_name func_args OWNER TO RoleId
| ALTER FUNCTION function_with_argtypes OWNER TO RoleId
{
AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
n->objectType = OBJECT_FUNCTION;
n->object = $3;
n->objarg = extractArgTypes($4);
n->newowner = $7;
n->object = $3->funcname;
n->objarg = $3->funcargs;
n->newowner = $6;
$$ = (Node *)n;
}
| ALTER opt_procedural LANGUAGE name OWNER TO RoleId

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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)