diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/access/transam/xact.c | 24 | ||||
-rw-r--r-- | src/backend/catalog/pg_aggregate.c | 3 | ||||
-rw-r--r-- | src/backend/catalog/pg_proc.c | 13 | ||||
-rw-r--r-- | src/backend/commands/functioncmds.c | 107 | ||||
-rw-r--r-- | src/backend/commands/proclang.c | 4 | ||||
-rw-r--r-- | src/backend/optimizer/util/clauses.c | 4 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 36 | ||||
-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 |
12 files changed, 313 insertions, 86 deletions
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 117525b5ac4..18787d17770 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -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; diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c index b60cd7714f5..3c161ba5120 100644 --- a/src/backend/catalog/pg_aggregate.c +++ b/src/backend/catalog/pg_aggregate.c @@ -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 */ diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index 9db3ef4edbb..6b5a7d0fd94 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -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'; diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index 0949feab1f2..a6768ab83c2 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -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) @@ -344,6 +354,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); diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c index e396e0018d0..c77d2e946d6 100644 --- a/src/backend/commands/proclang.c +++ b/src/backend/commands/proclang.c @@ -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); } diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 41215446a35..4316ddaf5ad 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -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; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 38ddf4bd486..9f9f644168f 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -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 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) |