mirror of
https://github.com/postgres/postgres.git
synced 2025-11-09 06:21:09 +03:00
Divide functions into three volatility classes (immutable, stable, and
volatile), rather than the old cachable/noncachable distinction. This allows indexscan optimizations in many places where we formerly didn't. Also, add a pronamespace column to pg_proc (it doesn't do anything yet, however).
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/indexing.c,v 1.86 2002/03/29 19:06:00 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/indexing.c,v 1.87 2002/04/05 00:31:24 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -64,7 +64,7 @@ char *Name_pg_opclass_indices[Num_pg_opclass_indices] =
|
||||
char *Name_pg_operator_indices[Num_pg_operator_indices] =
|
||||
{OperatorOidIndex, OperatorNameIndex};
|
||||
char *Name_pg_proc_indices[Num_pg_proc_indices] =
|
||||
{ProcedureOidIndex, ProcedureNameIndex};
|
||||
{ProcedureOidIndex, ProcedureNameNspIndex};
|
||||
char *Name_pg_relcheck_indices[Num_pg_relcheck_indices] =
|
||||
{RelCheckIndex};
|
||||
char *Name_pg_rewrite_indices[Num_pg_rewrite_indices] =
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.67 2002/03/29 19:06:02 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.68 2002/04/05 00:31:25 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -40,17 +40,17 @@ static void checkretval(Oid rettype, List *queryTreeList);
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
Oid
|
||||
ProcedureCreate(char *procedureName,
|
||||
ProcedureCreate(const char *procedureName,
|
||||
Oid procNamespace,
|
||||
bool replace,
|
||||
bool returnsSet,
|
||||
Oid returnType,
|
||||
Oid languageObjectId,
|
||||
char *prosrc,
|
||||
char *probin,
|
||||
const char *prosrc,
|
||||
const char *probin,
|
||||
bool trusted,
|
||||
bool canCache,
|
||||
bool isStrict,
|
||||
char volatility,
|
||||
int32 byte_pct,
|
||||
int32 perbyte_cpu,
|
||||
int32 percall_cpu,
|
||||
@@ -167,7 +167,7 @@ ProcedureCreate(char *procedureName,
|
||||
*/
|
||||
if (parameterCount == 1 && OidIsValid(typev[0]) &&
|
||||
(relid = typeidTypeRelid(typev[0])) != 0 &&
|
||||
get_attnum(relid, procedureName) != InvalidAttrNumber)
|
||||
get_attnum(relid, (char *) procedureName) != InvalidAttrNumber)
|
||||
elog(ERROR, "method %s already an attribute of type %s",
|
||||
procedureName, typeidTypeName(typev[0]));
|
||||
|
||||
@@ -180,7 +180,9 @@ ProcedureCreate(char *procedureName,
|
||||
|
||||
if (languageObjectId == SQLlanguageId)
|
||||
{
|
||||
querytree_list = pg_parse_and_rewrite(prosrc, typev, parameterCount);
|
||||
querytree_list = pg_parse_and_rewrite((char *) prosrc,
|
||||
typev,
|
||||
parameterCount);
|
||||
/* typecheck return value */
|
||||
checkretval(returnType, querytree_list);
|
||||
}
|
||||
@@ -196,12 +198,11 @@ ProcedureCreate(char *procedureName,
|
||||
* of backwards compatibility, accept an empty 'prosrc' value as
|
||||
* meaning the supplied SQL function name.
|
||||
*/
|
||||
|
||||
if (languageObjectId == INTERNALlanguageId)
|
||||
{
|
||||
if (strlen(prosrc) == 0)
|
||||
prosrc = procedureName;
|
||||
if (fmgr_internal_function(prosrc) == InvalidOid)
|
||||
if (fmgr_internal_function((char *) prosrc) == InvalidOid)
|
||||
elog(ERROR,
|
||||
"there is no built-in function named \"%s\"",
|
||||
prosrc);
|
||||
@@ -216,7 +217,6 @@ ProcedureCreate(char *procedureName,
|
||||
* called, but it seems friendlier to verify the library's validity at
|
||||
* CREATE FUNCTION time.
|
||||
*/
|
||||
|
||||
if (languageObjectId == ClanguageId)
|
||||
{
|
||||
void *libraryhandle;
|
||||
@@ -224,9 +224,11 @@ ProcedureCreate(char *procedureName,
|
||||
/* If link symbol is specified as "-", substitute procedure name */
|
||||
if (strcmp(prosrc, "-") == 0)
|
||||
prosrc = procedureName;
|
||||
(void) load_external_function(probin, prosrc, true,
|
||||
(void) load_external_function((char *) probin,
|
||||
(char *) prosrc,
|
||||
true,
|
||||
&libraryhandle);
|
||||
(void) fetch_finfo_record(libraryhandle, prosrc);
|
||||
(void) fetch_finfo_record(libraryhandle, (char *) prosrc);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -242,18 +244,18 @@ ProcedureCreate(char *procedureName,
|
||||
|
||||
i = 0;
|
||||
namestrcpy(&procname, procedureName);
|
||||
values[i++] = NameGetDatum(&procname);
|
||||
values[i++] = Int32GetDatum(GetUserId());
|
||||
values[i++] = ObjectIdGetDatum(languageObjectId);
|
||||
/* XXX isinherited is always false for now */
|
||||
values[i++] = BoolGetDatum(false);
|
||||
values[i++] = BoolGetDatum(trusted);
|
||||
values[i++] = BoolGetDatum(canCache);
|
||||
values[i++] = BoolGetDatum(isStrict);
|
||||
values[i++] = UInt16GetDatum(parameterCount);
|
||||
values[i++] = BoolGetDatum(returnsSet);
|
||||
values[i++] = ObjectIdGetDatum(returnType);
|
||||
values[i++] = PointerGetDatum(typev);
|
||||
values[i++] = NameGetDatum(&procname); /* proname */
|
||||
values[i++] = ObjectIdGetDatum(procNamespace); /* pronamespace */
|
||||
values[i++] = Int32GetDatum(GetUserId()); /* proowner */
|
||||
values[i++] = ObjectIdGetDatum(languageObjectId); /* prolang */
|
||||
values[i++] = BoolGetDatum(false); /* proisinh (unused) */
|
||||
values[i++] = BoolGetDatum(trusted); /* proistrusted */
|
||||
values[i++] = BoolGetDatum(isStrict); /* proisstrict */
|
||||
values[i++] = CharGetDatum(volatility); /* provolatile */
|
||||
values[i++] = UInt16GetDatum(parameterCount); /* pronargs */
|
||||
values[i++] = BoolGetDatum(returnsSet); /* proretset */
|
||||
values[i++] = ObjectIdGetDatum(returnType); /* prorettype */
|
||||
values[i++] = PointerGetDatum(typev); /* proargtypes */
|
||||
values[i++] = Int32GetDatum(byte_pct); /* probyte_pct */
|
||||
values[i++] = Int32GetDatum(perbyte_cpu); /* properbyte_cpu */
|
||||
values[i++] = Int32GetDatum(percall_cpu); /* propercall_cpu */
|
||||
@@ -262,6 +264,8 @@ ProcedureCreate(char *procedureName,
|
||||
CStringGetDatum(prosrc));
|
||||
values[i++] = DirectFunctionCall1(textin, /* probin */
|
||||
CStringGetDatum(probin));
|
||||
/* proacl will be handled below */
|
||||
|
||||
|
||||
rel = heap_openr(ProcedureRelationName, RowExclusiveLock);
|
||||
tupDesc = rel->rd_att;
|
||||
@@ -294,7 +298,7 @@ ProcedureCreate(char *procedureName,
|
||||
elog(ERROR, "ProcedureCreate: cannot change return type of existing function."
|
||||
"\n\tUse DROP FUNCTION first.");
|
||||
|
||||
/* do not change existing permissions */
|
||||
/* do not change existing permissions, either */
|
||||
replaces[Anum_pg_proc_proacl-1] = ' ';
|
||||
|
||||
/* Okay, do it... */
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.72 2002/03/29 19:06:06 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.73 2002/04/05 00:31:25 tgl Exp $
|
||||
*
|
||||
* DESCRIPTION
|
||||
* The "DefineFoo" routines take the parse tree and pick out the
|
||||
@@ -145,7 +145,7 @@ static void
|
||||
compute_full_attributes(List *parameters,
|
||||
int32 *byte_pct_p, int32 *perbyte_cpu_p,
|
||||
int32 *percall_cpu_p, int32 *outin_ratio_p,
|
||||
bool *canCache_p, bool *isStrict_p)
|
||||
bool *isStrict_p, char *volatility_p)
|
||||
{
|
||||
/*-------------
|
||||
* Interpret the parameters *parameters and return their contents as
|
||||
@@ -156,18 +156,15 @@ compute_full_attributes(List *parameters,
|
||||
*
|
||||
* Note: currently, only two of these parameters actually do anything:
|
||||
*
|
||||
* * canCache means the optimizer's constant-folder is allowed to
|
||||
* pre-evaluate the function when all its inputs are constants.
|
||||
*
|
||||
* * isStrict means the function should not be called when any NULL
|
||||
* inputs are present; instead a NULL result value should be assumed.
|
||||
*
|
||||
* * volatility tells the optimizer whether the function's result can
|
||||
* be assumed to be repeatable over multiple evaluations.
|
||||
*
|
||||
* The other four parameters are not used anywhere. They used to be
|
||||
* used in the "expensive functions" optimizer, but that's been dead code
|
||||
* for a long time.
|
||||
*
|
||||
* Since canCache and isStrict are useful for any function, we now allow
|
||||
* attributes to be supplied for all functions regardless of language.
|
||||
*------------
|
||||
*/
|
||||
List *pl;
|
||||
@@ -177,17 +174,26 @@ compute_full_attributes(List *parameters,
|
||||
*perbyte_cpu_p = PERBYTE_CPU;
|
||||
*percall_cpu_p = PERCALL_CPU;
|
||||
*outin_ratio_p = OUTIN_RATIO;
|
||||
*canCache_p = false;
|
||||
*isStrict_p = false;
|
||||
*volatility_p = PROVOLATILE_VOLATILE;
|
||||
|
||||
foreach(pl, parameters)
|
||||
{
|
||||
DefElem *param = (DefElem *) lfirst(pl);
|
||||
|
||||
if (strcasecmp(param->defname, "iscachable") == 0)
|
||||
*canCache_p = true;
|
||||
else if (strcasecmp(param->defname, "isstrict") == 0)
|
||||
if (strcasecmp(param->defname, "isstrict") == 0)
|
||||
*isStrict_p = true;
|
||||
else if (strcasecmp(param->defname, "isimmutable") == 0)
|
||||
*volatility_p = PROVOLATILE_IMMUTABLE;
|
||||
else if (strcasecmp(param->defname, "isstable") == 0)
|
||||
*volatility_p = PROVOLATILE_STABLE;
|
||||
else if (strcasecmp(param->defname, "isvolatile") == 0)
|
||||
*volatility_p = PROVOLATILE_VOLATILE;
|
||||
else if (strcasecmp(param->defname, "iscachable") == 0)
|
||||
{
|
||||
/* obsolete spelling of isImmutable */
|
||||
*volatility_p = PROVOLATILE_IMMUTABLE;
|
||||
}
|
||||
else if (strcasecmp(param->defname, "trusted") == 0)
|
||||
{
|
||||
/*
|
||||
@@ -273,8 +279,8 @@ CreateFunction(ProcedureStmt *stmt)
|
||||
perbyte_cpu,
|
||||
percall_cpu,
|
||||
outin_ratio;
|
||||
bool canCache,
|
||||
isStrict;
|
||||
bool isStrict;
|
||||
char volatility;
|
||||
HeapTuple languageTuple;
|
||||
Form_pg_language languageStruct;
|
||||
|
||||
@@ -311,7 +317,7 @@ CreateFunction(ProcedureStmt *stmt)
|
||||
|
||||
compute_full_attributes(stmt->withClause,
|
||||
&byte_pct, &perbyte_cpu, &percall_cpu,
|
||||
&outin_ratio, &canCache, &isStrict);
|
||||
&outin_ratio, &isStrict, &volatility);
|
||||
|
||||
interpret_AS_clause(languageOid, languageName, stmt->as,
|
||||
&prosrc_str, &probin_str);
|
||||
@@ -329,8 +335,8 @@ CreateFunction(ProcedureStmt *stmt)
|
||||
prosrc_str, /* converted to text later */
|
||||
probin_str, /* converted to text later */
|
||||
true, /* (obsolete "trusted") */
|
||||
canCache,
|
||||
isStrict,
|
||||
volatility,
|
||||
byte_pct,
|
||||
perbyte_cpu,
|
||||
percall_cpu,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.66 2002/03/31 06:26:30 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.67 2002/04/05 00:31:26 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "catalog/index.h"
|
||||
#include "catalog/namespace.h"
|
||||
#include "catalog/pg_opclass.h"
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "commands/defrem.h"
|
||||
#include "miscadmin.h"
|
||||
#include "optimizer/clauses.h"
|
||||
@@ -233,11 +234,11 @@ CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid)
|
||||
elog(ERROR, "Cannot use aggregate in index predicate");
|
||||
|
||||
/*
|
||||
* A predicate using noncachable functions is probably wrong, for the
|
||||
* A predicate using mutable functions is probably wrong, for the
|
||||
* same reasons that we don't allow a functional index to use one.
|
||||
*/
|
||||
if (contain_noncachable_functions((Node *) predList))
|
||||
elog(ERROR, "Cannot use non-cachable function in index predicate");
|
||||
if (contain_mutable_functions((Node *) predList))
|
||||
elog(ERROR, "Functions in index predicate must be marked isImmutable");
|
||||
}
|
||||
|
||||
|
||||
@@ -320,13 +321,13 @@ FuncIndexArgs(IndexInfo *indexInfo,
|
||||
}
|
||||
|
||||
/*
|
||||
* Require that the function be marked cachable. Using a noncachable
|
||||
* Require that the function be marked immutable. Using a mutable
|
||||
* function for a functional index is highly questionable, since if
|
||||
* you aren't going to get the same result for the same data every
|
||||
* time, it's not clear what the index entries mean at all.
|
||||
*/
|
||||
if (!func_iscachable(funcid))
|
||||
elog(ERROR, "DefineIndex: index function must be marked iscachable");
|
||||
if (func_volatile(funcid) != PROVOLATILE_IMMUTABLE)
|
||||
elog(ERROR, "DefineIndex: index function must be marked isImmutable");
|
||||
|
||||
/* Process opclass, using func return type as default type */
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.114 2002/03/20 19:44:09 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.115 2002/04/05 00:31:26 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -783,7 +783,7 @@ match_clause_to_indexkey(RelOptInfo *rel,
|
||||
* Check for an indexqual that could be handled by a nestloop
|
||||
* join. We need the index key to be compared against an
|
||||
* expression that uses none of the indexed relation's vars and
|
||||
* contains no non-cachable functions.
|
||||
* contains no volatile functions.
|
||||
*/
|
||||
if (match_index_to_operand(indexkey, leftop, rel, index))
|
||||
{
|
||||
@@ -792,7 +792,7 @@ match_clause_to_indexkey(RelOptInfo *rel,
|
||||
|
||||
isIndexable =
|
||||
!intMember(lfirsti(rel->relids), othervarnos) &&
|
||||
!contain_noncachable_functions((Node *) rightop) &&
|
||||
!contain_volatile_functions((Node *) rightop) &&
|
||||
is_indexable_operator(clause, opclass, true);
|
||||
freeList(othervarnos);
|
||||
return isIndexable;
|
||||
@@ -804,7 +804,7 @@ match_clause_to_indexkey(RelOptInfo *rel,
|
||||
|
||||
isIndexable =
|
||||
!intMember(lfirsti(rel->relids), othervarnos) &&
|
||||
!contain_noncachable_functions((Node *) leftop) &&
|
||||
!contain_volatile_functions((Node *) leftop) &&
|
||||
is_indexable_operator(clause, opclass, false);
|
||||
freeList(othervarnos);
|
||||
return isIndexable;
|
||||
@@ -1142,7 +1142,7 @@ static const StrategyNumber
|
||||
* implies another. A simple and general way is to see if they are
|
||||
* equal(); this works for any kind of expression. (Actually, there
|
||||
* is an implied assumption that the functions in the expression are
|
||||
* cachable, ie dependent only on their input arguments --- but this
|
||||
* immutable, ie dependent only on their input arguments --- but this
|
||||
* was checked for the predicate by CheckPredicate().)
|
||||
*
|
||||
* Our other way works only for (binary boolean) operators that are
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.95 2002/03/21 16:00:44 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.96 2002/04/05 00:31:27 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@@ -52,7 +52,8 @@ static bool contain_subplans_walker(Node *node, void *context);
|
||||
static bool pull_subplans_walker(Node *node, List **listptr);
|
||||
static bool check_subplans_for_ungrouped_vars_walker(Node *node,
|
||||
check_subplans_for_ungrouped_vars_context * context);
|
||||
static bool contain_noncachable_functions_walker(Node *node, void *context);
|
||||
static bool contain_mutable_functions_walker(Node *node, void *context);
|
||||
static bool contain_volatile_functions_walker(Node *node, void *context);
|
||||
static Node *eval_const_expressions_mutator(Node *node, void *context);
|
||||
static Expr *simplify_op_or_func(Expr *expr, List *args);
|
||||
|
||||
@@ -698,29 +699,29 @@ check_subplans_for_ungrouped_vars_walker(Node *node,
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* Check clauses for noncachable functions
|
||||
* Check clauses for mutable functions
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* contain_noncachable_functions
|
||||
* Recursively search for noncachable functions within a clause.
|
||||
* contain_mutable_functions
|
||||
* Recursively search for mutable functions within a clause.
|
||||
*
|
||||
* Returns true if any noncachable function (or operator implemented by a
|
||||
* noncachable function) is found. This test is needed so that we don't
|
||||
* Returns true if any mutable function (or operator implemented by a
|
||||
* mutable function) is found. This test is needed so that we don't
|
||||
* mistakenly think that something like "WHERE random() < 0.5" can be treated
|
||||
* as a constant qualification.
|
||||
*
|
||||
* XXX we do not examine sublinks/subplans to see if they contain uses of
|
||||
* noncachable functions. It's not real clear if that is correct or not...
|
||||
* mutable functions. It's not real clear if that is correct or not...
|
||||
*/
|
||||
bool
|
||||
contain_noncachable_functions(Node *clause)
|
||||
contain_mutable_functions(Node *clause)
|
||||
{
|
||||
return contain_noncachable_functions_walker(clause, NULL);
|
||||
return contain_mutable_functions_walker(clause, NULL);
|
||||
}
|
||||
|
||||
static bool
|
||||
contain_noncachable_functions_walker(Node *node, void *context)
|
||||
contain_mutable_functions_walker(Node *node, void *context)
|
||||
{
|
||||
if (node == NULL)
|
||||
return false;
|
||||
@@ -731,18 +732,67 @@ contain_noncachable_functions_walker(Node *node, void *context)
|
||||
switch (expr->opType)
|
||||
{
|
||||
case OP_EXPR:
|
||||
if (!op_iscachable(((Oper *) expr->oper)->opno))
|
||||
if (op_volatile(((Oper *) expr->oper)->opno) != PROVOLATILE_IMMUTABLE)
|
||||
return true;
|
||||
break;
|
||||
case FUNC_EXPR:
|
||||
if (!func_iscachable(((Func *) expr->oper)->funcid))
|
||||
if (func_volatile(((Func *) expr->oper)->funcid) != PROVOLATILE_IMMUTABLE)
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return expression_tree_walker(node, contain_noncachable_functions_walker,
|
||||
return expression_tree_walker(node, contain_mutable_functions_walker,
|
||||
context);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* Check clauses for volatile functions
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* contain_volatile_functions
|
||||
* Recursively search for volatile functions within a clause.
|
||||
*
|
||||
* Returns true if any volatile function (or operator implemented by a
|
||||
* volatile function) is found. This test prevents invalid conversions
|
||||
* of volatile expressions into indexscan quals.
|
||||
*
|
||||
* XXX we do not examine sublinks/subplans to see if they contain uses of
|
||||
* volatile functions. It's not real clear if that is correct or not...
|
||||
*/
|
||||
bool
|
||||
contain_volatile_functions(Node *clause)
|
||||
{
|
||||
return contain_volatile_functions_walker(clause, NULL);
|
||||
}
|
||||
|
||||
static bool
|
||||
contain_volatile_functions_walker(Node *node, void *context)
|
||||
{
|
||||
if (node == NULL)
|
||||
return false;
|
||||
if (IsA(node, Expr))
|
||||
{
|
||||
Expr *expr = (Expr *) node;
|
||||
|
||||
switch (expr->opType)
|
||||
{
|
||||
case OP_EXPR:
|
||||
if (op_volatile(((Oper *) expr->oper)->opno) == PROVOLATILE_VOLATILE)
|
||||
return true;
|
||||
break;
|
||||
case FUNC_EXPR:
|
||||
if (func_volatile(((Func *) expr->oper)->funcid) == PROVOLATILE_VOLATILE)
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return expression_tree_walker(node, contain_volatile_functions_walker,
|
||||
context);
|
||||
}
|
||||
|
||||
@@ -754,23 +804,25 @@ contain_noncachable_functions_walker(Node *node, void *context)
|
||||
/*
|
||||
* is_pseudo_constant_clause
|
||||
* Detect whether a clause is "constant", ie, it contains no variables
|
||||
* of the current query level and no uses of noncachable functions.
|
||||
* of the current query level and no uses of volatile functions.
|
||||
* Such a clause is not necessarily a true constant: it can still contain
|
||||
* Params and outer-level Vars. However, its value will be constant over
|
||||
* any one scan of the current query, so it can be used as an indexscan
|
||||
* key or (if a top-level qual) can be pushed up to become a gating qual.
|
||||
* Params and outer-level Vars, not to mention functions whose results
|
||||
* may vary from one statement to the next. However, the clause's value
|
||||
* will be constant over any one scan of the current query, so it can be
|
||||
* used as an indexscan key or (if a top-level qual) can be pushed up to
|
||||
* become a gating qual.
|
||||
*/
|
||||
bool
|
||||
is_pseudo_constant_clause(Node *clause)
|
||||
{
|
||||
/*
|
||||
* We could implement this check in one recursive scan. But since the
|
||||
* check for noncachable functions is both moderately expensive and
|
||||
* check for volatile functions is both moderately expensive and
|
||||
* unlikely to fail, it seems better to look for Vars first and only
|
||||
* check for noncachable functions if we find no Vars.
|
||||
* check for volatile functions if we find no Vars.
|
||||
*/
|
||||
if (!contain_var_clause(clause) &&
|
||||
!contain_noncachable_functions(clause))
|
||||
!contain_volatile_functions(clause))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@@ -1019,7 +1071,7 @@ CommuteClause(Expr *clause)
|
||||
*
|
||||
* We do understand that certain functions may deliver non-constant
|
||||
* results even with constant inputs, "nextval()" being the classic
|
||||
* example. Functions that are not marked "proiscachable" in pg_proc
|
||||
* example. Functions that are not marked "immutable" in pg_proc
|
||||
* will not be pre-evaluated here, although we will reduce their
|
||||
* arguments as far as possible. Functions that are the arguments
|
||||
* of Iter nodes are also not evaluated.
|
||||
@@ -1412,7 +1464,7 @@ simplify_op_or_func(Expr *expr, List *args)
|
||||
Oid result_typeid;
|
||||
HeapTuple func_tuple;
|
||||
Form_pg_proc funcform;
|
||||
bool proiscachable;
|
||||
char provolatile;
|
||||
bool proisstrict;
|
||||
bool proretset;
|
||||
int16 resultTypLen;
|
||||
@@ -1447,7 +1499,7 @@ simplify_op_or_func(Expr *expr, List *args)
|
||||
|
||||
/*
|
||||
* Get the function procedure's OID and look to see whether it is
|
||||
* marked proiscachable.
|
||||
* marked immutable.
|
||||
*
|
||||
* XXX would it be better to take the result type from the pg_proc tuple,
|
||||
* rather than the Oper or Func node?
|
||||
@@ -1469,7 +1521,7 @@ simplify_op_or_func(Expr *expr, List *args)
|
||||
}
|
||||
|
||||
/*
|
||||
* we could use func_iscachable() here, but we need several fields out
|
||||
* we could use func_volatile() here, but we need several fields out
|
||||
* of the func tuple, so might as well just look it up once.
|
||||
*/
|
||||
func_tuple = SearchSysCache(PROCOID,
|
||||
@@ -1478,12 +1530,12 @@ simplify_op_or_func(Expr *expr, List *args)
|
||||
if (!HeapTupleIsValid(func_tuple))
|
||||
elog(ERROR, "Function OID %u does not exist", funcid);
|
||||
funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
|
||||
proiscachable = funcform->proiscachable;
|
||||
provolatile = funcform->provolatile;
|
||||
proisstrict = funcform->proisstrict;
|
||||
proretset = funcform->proretset;
|
||||
ReleaseSysCache(func_tuple);
|
||||
|
||||
if (!proiscachable)
|
||||
if (provolatile != PROVOLATILE_IMMUTABLE)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.122 2002/03/29 22:10:33 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.123 2002/04/05 00:31:27 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -525,7 +525,7 @@ func_get_candidates(char *funcname, int nargs)
|
||||
(RegProcedure) F_INT2EQ,
|
||||
Int16GetDatum(nargs));
|
||||
|
||||
funcscan = systable_beginscan(heapRelation, ProcedureNameIndex, true,
|
||||
funcscan = systable_beginscan(heapRelation, ProcedureNameNspIndex, true,
|
||||
SnapshotNow, 2, skey);
|
||||
|
||||
while (HeapTupleIsValid(tuple = systable_getnext(funcscan)))
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#
|
||||
#
|
||||
# IDENTIFICATION
|
||||
# $Header: /cvsroot/pgsql/src/backend/utils/Attic/Gen_fmgrtab.sh,v 1.21 2002/02/18 23:11:20 petere Exp $
|
||||
# $Header: /cvsroot/pgsql/src/backend/utils/Attic/Gen_fmgrtab.sh,v 1.22 2002/04/05 00:31:28 tgl Exp $
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
@@ -99,7 +99,7 @@ sed -e 's/^.*OID[^=]*=[^0-9]*//' \
|
||||
-e 's/[ ]*).*$//' | \
|
||||
$AWK '
|
||||
/^#/ { print; next; }
|
||||
$4 == "12" { print; next; }' > $CPPTMPFILE
|
||||
$5 == "12" { print; next; }' > $CPPTMPFILE
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
cleanup
|
||||
@@ -232,7 +232,7 @@ $AWK 'BEGIN {
|
||||
Bool["f"] = "false"
|
||||
}
|
||||
{ printf (" { %d, \"%s\", %d, %s, %s, %s },\n"), \
|
||||
$1, $(NF-2), $9, Bool[$8], Bool[$10], $(NF-2)
|
||||
$1, $(NF-2), $10, Bool[$8], Bool[$11], $(NF-2)
|
||||
}' $RAWFILE >> "$$-$TABLEFILE"
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/regproc.c,v 1.64 2001/10/25 05:49:45 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/regproc.c,v 1.65 2002/04/05 00:31:29 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -42,13 +42,13 @@ regprocin(PG_FUNCTION_ARGS)
|
||||
char *pro_name_or_oid = PG_GETARG_CSTRING(0);
|
||||
RegProcedure result = InvalidOid;
|
||||
int matches = 0;
|
||||
ScanKeyData skey[1];
|
||||
|
||||
if (pro_name_or_oid[0] == '-' && pro_name_or_oid[1] == '\0')
|
||||
PG_RETURN_OID(InvalidOid);
|
||||
|
||||
if (pro_name_or_oid[0] >= '0' &&
|
||||
pro_name_or_oid[0] <= '9')
|
||||
pro_name_or_oid[0] <= '9' &&
|
||||
strspn(pro_name_or_oid, "0123456789") == strlen(pro_name_or_oid))
|
||||
{
|
||||
Oid searchOid;
|
||||
|
||||
@@ -61,67 +61,33 @@ regprocin(PG_FUNCTION_ARGS)
|
||||
elog(ERROR, "No procedure with oid %s", pro_name_or_oid);
|
||||
matches = 1;
|
||||
}
|
||||
else if (!IsIgnoringSystemIndexes())
|
||||
{
|
||||
Relation hdesc;
|
||||
Relation idesc;
|
||||
IndexScanDesc sd;
|
||||
RetrieveIndexResult indexRes;
|
||||
HeapTupleData tuple;
|
||||
Buffer buffer;
|
||||
|
||||
ScanKeyEntryInitialize(&skey[0], 0x0,
|
||||
(AttrNumber) 1,
|
||||
(RegProcedure) F_NAMEEQ,
|
||||
CStringGetDatum(pro_name_or_oid));
|
||||
|
||||
hdesc = heap_openr(ProcedureRelationName, AccessShareLock);
|
||||
idesc = index_openr(ProcedureNameIndex);
|
||||
sd = index_beginscan(idesc, false, 1, skey);
|
||||
|
||||
while ((indexRes = index_getnext(sd, ForwardScanDirection)))
|
||||
{
|
||||
tuple.t_datamcxt = NULL;
|
||||
tuple.t_data = NULL;
|
||||
tuple.t_self = indexRes->heap_iptr;
|
||||
heap_fetch(hdesc, SnapshotNow, &tuple, &buffer, sd);
|
||||
pfree(indexRes);
|
||||
if (tuple.t_data != NULL)
|
||||
{
|
||||
result = (RegProcedure) tuple.t_data->t_oid;
|
||||
ReleaseBuffer(buffer);
|
||||
if (++matches > 1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
index_endscan(sd);
|
||||
index_close(idesc);
|
||||
heap_close(hdesc, AccessShareLock);
|
||||
}
|
||||
else
|
||||
{
|
||||
Relation proc;
|
||||
HeapScanDesc procscan;
|
||||
HeapTuple proctup;
|
||||
Relation hdesc;
|
||||
ScanKeyData skey[1];
|
||||
SysScanDesc funcscan;
|
||||
HeapTuple tuple;
|
||||
|
||||
ScanKeyEntryInitialize(&skey[0], 0x0,
|
||||
(AttrNumber) Anum_pg_proc_proname,
|
||||
(RegProcedure) F_NAMEEQ,
|
||||
CStringGetDatum(pro_name_or_oid));
|
||||
|
||||
proc = heap_openr(ProcedureRelationName, AccessShareLock);
|
||||
procscan = heap_beginscan(proc, 0, SnapshotNow, 1, skey);
|
||||
hdesc = heap_openr(ProcedureRelationName, AccessShareLock);
|
||||
|
||||
while (HeapTupleIsValid(proctup = heap_getnext(procscan, 0)))
|
||||
funcscan = systable_beginscan(hdesc, ProcedureNameNspIndex, true,
|
||||
SnapshotNow, 1, skey);
|
||||
|
||||
while (HeapTupleIsValid(tuple = systable_getnext(funcscan)))
|
||||
{
|
||||
result = proctup->t_data->t_oid;
|
||||
result = (RegProcedure) tuple->t_data->t_oid;
|
||||
if (++matches > 1)
|
||||
break;
|
||||
}
|
||||
|
||||
heap_endscan(procscan);
|
||||
heap_close(proc, AccessShareLock);
|
||||
systable_endscan(funcscan);
|
||||
|
||||
heap_close(hdesc, AccessShareLock);
|
||||
}
|
||||
|
||||
if (matches > 1)
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/sets.c,v 1.41 2002/03/29 19:06:14 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/sets.c,v 1.42 2002/04/05 00:31:29 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -61,8 +61,8 @@ SetDefine(char *querystr, Oid elemType)
|
||||
querystr, /* sourceCode */
|
||||
fileName, /* fileName */
|
||||
true, /* trusted */
|
||||
false, /* canCache (assume unsafe) */
|
||||
false, /* isStrict (irrelevant, no args) */
|
||||
PROVOLATILE_VOLATILE, /* assume unsafe */
|
||||
100, /* byte_pct */
|
||||
0, /* perbyte_cpu */
|
||||
0, /* percall_cpu */
|
||||
|
||||
24
src/backend/utils/cache/lsyscache.c
vendored
24
src/backend/utils/cache/lsyscache.c
vendored
@@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.68 2002/04/02 01:03:07 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.69 2002/04/05 00:31:30 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Eventually, the index information should go through here, too.
|
||||
@@ -470,19 +470,19 @@ op_hashjoinable(Oid opno, Oid ltype, Oid rtype)
|
||||
}
|
||||
|
||||
/*
|
||||
* op_iscachable
|
||||
* op_volatile
|
||||
*
|
||||
* Get the proiscachable flag for the operator's underlying function.
|
||||
* Get the provolatile flag for the operator's underlying function.
|
||||
*/
|
||||
bool
|
||||
op_iscachable(Oid opno)
|
||||
char
|
||||
op_volatile(Oid opno)
|
||||
{
|
||||
RegProcedure funcid = get_opcode(opno);
|
||||
|
||||
if (funcid == (RegProcedure) InvalidOid)
|
||||
elog(ERROR, "Operator OID %u does not exist", opno);
|
||||
|
||||
return func_iscachable((Oid) funcid);
|
||||
return func_volatile((Oid) funcid);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -613,14 +613,14 @@ get_func_rettype(Oid funcid)
|
||||
}
|
||||
|
||||
/*
|
||||
* func_iscachable
|
||||
* Given procedure id, return the function's proiscachable flag.
|
||||
* func_volatile
|
||||
* Given procedure id, return the function's provolatile flag.
|
||||
*/
|
||||
bool
|
||||
func_iscachable(Oid funcid)
|
||||
char
|
||||
func_volatile(Oid funcid)
|
||||
{
|
||||
HeapTuple tp;
|
||||
bool result;
|
||||
char result;
|
||||
|
||||
tp = SearchSysCache(PROCOID,
|
||||
ObjectIdGetDatum(funcid),
|
||||
@@ -628,7 +628,7 @@ func_iscachable(Oid funcid)
|
||||
if (!HeapTupleIsValid(tp))
|
||||
elog(ERROR, "Function OID %u does not exist", funcid);
|
||||
|
||||
result = ((Form_pg_proc) GETSTRUCT(tp))->proiscachable;
|
||||
result = ((Form_pg_proc) GETSTRUCT(tp))->provolatile;
|
||||
ReleaseSysCache(tp);
|
||||
return result;
|
||||
}
|
||||
|
||||
4
src/backend/utils/cache/syscache.c
vendored
4
src/backend/utils/cache/syscache.c
vendored
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/cache/syscache.c,v 1.72 2002/03/31 06:26:32 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/cache/syscache.c,v 1.73 2002/04/05 00:31:31 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* These routines allow the parser/planner/executor to perform
|
||||
@@ -304,7 +304,7 @@ static const struct cachedesc cacheinfo[] = {
|
||||
0
|
||||
}},
|
||||
{ProcedureRelationName, /* PROCNAME */
|
||||
ProcedureNameIndex,
|
||||
ProcedureNameNspIndex, /* XXX very temporary */
|
||||
0,
|
||||
3,
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user