From 6eb8d255d217c7f0ed2a89e75ae5cb14653c64eb Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sat, 2 Oct 1999 21:33:33 +0000
Subject: [PATCH] Allow CREATE FUNCTION's WITH clause to be used for all
 language types, not just C, so that ISCACHABLE attribute can be specified for
 user-defined functions.  Get rid of ParamString node type, which wasn't
 actually being generated by gram.y anymore, even though define.c thought that
 was what it was getting.  Clean up minor bug in dfmgr.c (premature
 heap_close).

---
 src/backend/commands/define.c    | 189 +++++++++++++++----------------
 src/backend/commands/indexcmds.c |  16 ++-
 src/backend/parser/gram.y        |   3 +-
 src/backend/utils/fmgr/dfmgr.c   |  14 ++-
 src/bin/psql/psqlHelp.h          |  12 +-
 src/include/nodes/nodes.h        |   4 +-
 src/include/nodes/parsenodes.h   |  16 +--
 7 files changed, 117 insertions(+), 137 deletions(-)

diff --git a/src/backend/commands/define.c b/src/backend/commands/define.c
index 509d5026d30..2aa197654c7 100644
--- a/src/backend/commands/define.c
+++ b/src/backend/commands/define.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.35 1999/09/28 04:34:40 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.36 1999/10/02 21:33:24 tgl Exp $
  *
  * DESCRIPTION
  *	  The "DefineFoo" routines take the parse tree and pick out the
@@ -53,6 +53,7 @@
 #include "utils/syscache.h"
 
 static char *defGetString(DefElem *def);
+static double defGetNumeric(DefElem *def);
 static int	defGetTypeLength(DefElem *def);
 
 #define DEFAULT_TYPDELIM		','
@@ -103,9 +104,8 @@ compute_return_type(const Node *returnType,
 }
 
 
-
 static void
-compute_full_attributes(const List *parameters, int32 *byte_pct_p,
+compute_full_attributes(List *parameters, int32 *byte_pct_p,
 						int32 *perbyte_cpu_p, int32 *percall_cpu_p,
 						int32 *outin_ratio_p, bool *canCache_p)
 {
@@ -113,7 +113,17 @@ compute_full_attributes(const List *parameters, int32 *byte_pct_p,
   Interpret the parameters *parameters and return their contents as
   *byte_pct_p, etc.
 
-  These are the full parameters of a C or internal function.
+  These parameters supply optional information about a function.
+  All have defaults if not specified.
+
+  Note: as of version 6.6, canCache is used (if set, the optimizer's
+  constant-folder is allowed to pre-evaluate the function if all its
+  inputs are constant).  The other four are not used.  They used to be
+  used in the "expensive functions" optimizer, but that's been dead code
+  for a long time.
+
+  Since canCache is useful for any function, we now allow attributes to be
+  supplied for all functions regardless of language.
 ---------------------------------------------------------------------------*/
 	List	   *pl;
 
@@ -122,58 +132,33 @@ compute_full_attributes(const List *parameters, int32 *byte_pct_p,
 	*perbyte_cpu_p = PERBYTE_CPU;
 	*percall_cpu_p = PERCALL_CPU;
 	*outin_ratio_p = OUTIN_RATIO;
+	*canCache_p = false;
 
-	foreach(pl, (List *) parameters)
+	foreach(pl, parameters)
 	{
-		ParamString *param = (ParamString *) lfirst(pl);
+		DefElem *param = (DefElem *) lfirst(pl);
 
-		if (strcasecmp(param->name, "iscachable") == 0)
+		if (strcasecmp(param->defname, "iscachable") == 0)
 			*canCache_p = true;
-		else if (strcasecmp(param->name, "trusted") == 0)
+		else if (strcasecmp(param->defname, "trusted") == 0)
 		{
-
 			/*
 			 * we don't have untrusted functions any more. The 4.2
 			 * implementation is lousy anyway so I took it out. -ay 10/94
 			 */
 			elog(ERROR, "untrusted function has been decommissioned.");
 		}
-		else if (strcasecmp(param->name, "byte_pct") == 0)
-		{
-
-			/*
-			 * * handle expensive function parameters
-			 */
-			*byte_pct_p = atoi(param->val);
-		}
-		else if (strcasecmp(param->name, "perbyte_cpu") == 0)
-		{
-			if (sscanf(param->val, "%d", perbyte_cpu_p) == 0)
-			{
-				int			count;
-				char	   *ptr;
-
-				for (count = 0, ptr = param->val; *ptr != '\0'; ptr++)
-					if (*ptr == '!')
-						count++;
-				*perbyte_cpu_p = (int) pow(10.0, (double) count);
-			}
-		}
-		else if (strcasecmp(param->name, "percall_cpu") == 0)
-		{
-			if (sscanf(param->val, "%d", percall_cpu_p) == 0)
-			{
-				int			count;
-				char	   *ptr;
-
-				for (count = 0, ptr = param->val; *ptr != '\0'; ptr++)
-					if (*ptr == '!')
-						count++;
-				*percall_cpu_p = (int) pow(10.0, (double) count);
-			}
-		}
-		else if (strcasecmp(param->name, "outin_ratio") == 0)
-			*outin_ratio_p = atoi(param->val);
+		else if (strcasecmp(param->defname, "byte_pct") == 0)
+			*byte_pct_p = (int) defGetNumeric(param);
+		else if (strcasecmp(param->defname, "perbyte_cpu") == 0)
+			*perbyte_cpu_p = (int) defGetNumeric(param);
+		else if (strcasecmp(param->defname, "percall_cpu") == 0)
+			*percall_cpu_p = (int) defGetNumeric(param);
+		else if (strcasecmp(param->defname, "outin_ratio") == 0)
+			*outin_ratio_p = (int) defGetNumeric(param);
+		else
+			elog(NOTICE, "Unrecognized function attribute '%s' ignored",
+				 param->defname);
 	}
 }
 
@@ -215,8 +200,8 @@ interpret_AS_clause(const char *languageName, const List *as,
 		*probin_str_p = "-";
 
 		if (lnext(as) != NULL)
-			elog(ERROR, "CREATE FUNCTION: parse error in 'AS %s, %s'.",
-				 strVal(lfirst(as)), strVal(lsecond(as)));
+			elog(ERROR, "CREATE FUNCTION: only one AS item needed for %s language",
+				 languageName);
 	}
 }
 
@@ -246,39 +231,37 @@ CreateFunction(ProcedureStmt *stmt, CommandDest dest)
 	 * or "SQL"
 	 */
 
+	bool		returnsSet;
+	/* The function returns a set of values, as opposed to a singleton. */
+
+	bool		lanisPL = false;
+
 	/*
-	 * The following are attributes of the function, as expressed in the
-	 * CREATE FUNCTION statement, where applicable.
+	 * The following are optional user-supplied attributes of the function.
 	 */
 	int32		byte_pct,
 				perbyte_cpu,
 				percall_cpu,
 				outin_ratio;
 	bool		canCache;
-	bool		returnsSet;
-
-	bool		lanisPL = false;
-
-	/* The function returns a set of values, as opposed to a singleton. */
 
 
 	case_translate_language_name(stmt->language, languageName);
 
-	compute_return_type(stmt->returnType, &prorettype, &returnsSet);
-
 	if (strcmp(languageName, "C") == 0 ||
 		strcmp(languageName, "internal") == 0)
 	{
-		compute_full_attributes(stmt->withClause,
-								&byte_pct, &perbyte_cpu, &percall_cpu,
-								&outin_ratio, &canCache);
+		if (!superuser())
+			elog(ERROR,
+				 "Only users with Postgres superuser privilege are "
+				 "permitted to create a function "
+				 "in the '%s' language.  Others may use the 'sql' language "
+				 "or the created procedural languages.",
+				 languageName);
 	}
 	else if (strcmp(languageName, "sql") == 0)
 	{
-		/* query optimizer groks sql, these are meaningless */
-		perbyte_cpu = percall_cpu = 0;
-		byte_pct = outin_ratio = 100;
-		canCache = false;
+		/* No security check needed for SQL functions */
 	}
 	else
 	{
@@ -321,47 +304,34 @@ CreateFunction(ProcedureStmt *stmt, CommandDest dest)
 		}
 
 		lanisPL = true;
-
-		/*
-		 * These are meaningless
-		 */
-		perbyte_cpu = percall_cpu = 0;
-		byte_pct = outin_ratio = 100;
-		canCache = false;
 	}
 
-	interpret_AS_clause(languageName, stmt->as, &prosrc_str, &probin_str);
+	compute_return_type(stmt->returnType, &prorettype, &returnsSet);
 
-	if (strcmp(languageName, "sql") != 0 && lanisPL == false && !superuser())
-		elog(ERROR,
-			 "Only users with Postgres superuser privilege are permitted "
-			 "to create a function "
-			 "in the '%s' language.  Others may use the 'sql' language "
-			 "or the created procedural languages.",
-			 languageName);
-	/* Above does not return. */
-	else
-	{
+	compute_full_attributes(stmt->withClause,
+							&byte_pct, &perbyte_cpu, &percall_cpu,
+							&outin_ratio, &canCache);
 
-		/*
-		 * And now that we have all the parameters, and know we're
-		 * permitted to do so, go ahead and create the function.
-		 */
-		ProcedureCreate(stmt->funcname,
-						returnsSet,
-						prorettype,
-						languageName,
-						prosrc_str,		/* converted to text later */
-						probin_str,		/* converted to text later */
-						canCache,
-						true,	/* (obsolete "trusted") */
-						byte_pct,
-						perbyte_cpu,
-						percall_cpu,
-						outin_ratio,
-						stmt->defArgs,
-						dest);
-	}
+	interpret_AS_clause(languageName, stmt->as, &prosrc_str, &probin_str);
+
+	/*
+	 * And now that we have all the parameters, and know we're
+	 * permitted to do so, go ahead and create the function.
+	 */
+	ProcedureCreate(stmt->funcname,
+					returnsSet,
+					prorettype,
+					languageName,
+					prosrc_str,		/* converted to text later */
+					probin_str,		/* converted to text later */
+					canCache,
+					true,			/* (obsolete "trusted") */
+					byte_pct,
+					perbyte_cpu,
+					percall_cpu,
+					outin_ratio,
+					stmt->defArgs,
+					dest);
 }
 
 
@@ -734,6 +704,25 @@ defGetString(DefElem *def)
 	return strVal(def->arg);
 }
 
+static double
+defGetNumeric(DefElem *def)
+{
+	if (def->arg == NULL)
+		elog(ERROR, "Define: \"%s\" requires a numeric value",
+			 def->defname);
+	switch (nodeTag(def->arg))
+	{
+		case T_Integer:
+			return (double) intVal(def->arg);
+		case T_Float:
+			return floatVal(def->arg);
+		default:
+			elog(ERROR, "Define: \"%s\" requires a numeric value",
+				 def->defname);
+	}
+	return 0;					/* keep compiler quiet */
+}
+
 static int
 defGetTypeLength(DefElem *def)
 {
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 113854311d6..5f82c2b5321 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.11 1999/09/18 19:06:40 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.12 1999/10/02 21:33:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -50,7 +50,7 @@ static char *GetDefaultOpClass(Oid atttypid);
  *
  * 'attributeList' is a list of IndexElem specifying either a functional
  *		index or a list of attributes to index on.
- * 'parameterList' is a list of ParamString specified in the with clause.
+ * 'parameterList' is a list of DefElem specified in the with clause.
  * 'predicate' is the qual specified in the where clause.
  * 'rangetable' is for the predicate
  *
@@ -116,22 +116,20 @@ DefineIndex(char *heapRelationName,
 	}
 	accessMethodId = tuple->t_data->t_oid;
 
-
 	/*
-	 * Handle parameters [param list is now different (NOT USED, really) -
-	 * ay 10/94]
-	 *
 	 * WITH clause reinstated to handle lossy indices. -- JMH, 7/22/96
 	 */
 	foreach(pl, parameterList)
 	{
-		ParamString *param = (ParamString *) lfirst(pl);
+		DefElem *param = (DefElem *) lfirst(pl);
 
-		if (!strcasecmp(param->name, "islossy"))
+		if (!strcasecmp(param->defname, "islossy"))
 			lossy = TRUE;
+		else
+			elog(NOTICE, "Unrecognized index attribute '%s' ignored",
+				 param->defname);
 	}
 
-
 	/*
 	 * Convert the partial-index predicate from parsetree form to plan
 	 * form, so it can be readily evaluated during index creation. Note:
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b6583197be3..96f480ea0ca 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.104 1999/09/29 16:06:06 wieck Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.105 1999/10/02 21:33:21 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -103,7 +103,6 @@ Oid	param_type(int t); /* used in parse_expr.c */
 
 	TypeName			*typnam;
 	DefElem				*defelt;
-	ParamString			*param;
 	SortGroupBy			*sortgroupby;
 	JoinExpr			*joinexpr;
 	IndexElem			*ielem;
diff --git a/src/backend/utils/fmgr/dfmgr.c b/src/backend/utils/fmgr/dfmgr.c
index a69d20a0476..15b22bdfa41 100644
--- a/src/backend/utils/fmgr/dfmgr.c
+++ b/src/backend/utils/fmgr/dfmgr.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/fmgr/dfmgr.c,v 1.34 1999/09/28 11:27:13 vadim Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/fmgr/dfmgr.c,v 1.35 1999/10/02 21:33:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -42,15 +42,16 @@ fmgr_dynamic(Oid procedureId, int *pronargs)
 	HeapTuple	procedureTuple;
 	Form_pg_proc procedureStruct;
 	char	   *proname,
-			   *probinstring,
-			   *prosrcstring,
-			   *linksymbol;
+			   *linksymbol,
+			   *probinstring;
+	char	   *prosrcstring = NULL;
 	Datum		probinattr;
 	Datum		prosrcattr;
 	func_ptr	user_fn;
 	Relation	rel;
 	bool		isnull;
 
+	/* Implement simple one-element cache for function lookups */
 	if (procedureId == procedureId_save)
 	{
 		*pronargs = pronargs_save;
@@ -91,8 +92,6 @@ fmgr_dynamic(Oid procedureId, int *pronargs)
 	}
 	probinstring = textout((struct varlena *) probinattr);
 
-	heap_close(rel, AccessShareLock);
-
 	prosrcattr = heap_getattr(procedureTuple,
 							  Anum_pg_proc_prosrc,
 							  RelationGetDescr(rel), &isnull);
@@ -118,9 +117,12 @@ fmgr_dynamic(Oid procedureId, int *pronargs)
 			linksymbol = prosrcstring;
 	}
 
+	heap_close(rel, AccessShareLock);
+
 	user_fn = handle_load(probinstring, linksymbol);
 
 	pfree(probinstring);
+	if (prosrcstring) pfree(prosrcstring);
 
 	procedureId_save = procedureId;
 	user_fn_save = user_fn;
diff --git a/src/bin/psql/psqlHelp.h b/src/bin/psql/psqlHelp.h
index 63268c2cbd9..3b39f7b2d8e 100644
--- a/src/bin/psql/psqlHelp.h
+++ b/src/bin/psql/psqlHelp.h
@@ -5,7 +5,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: psqlHelp.h,v 1.76 1999/09/28 04:34:48 momjian Exp $
+ * $Id: psqlHelp.h,v 1.77 1999/10/02 21:33:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -88,14 +88,16 @@ static struct _helpStruct QL_HELP[] = {
 		"create a user-defined function",
 	"\
 \tCREATE FUNCTION function_name ([type1, ...typeN]) RETURNS return_type\n\
-\tAS 'sql-queries'|'builtin_function_name'|'object_filename'\n\
-\tLANGUAGE 'sql'|'internal'|'c';\n\
+\t[WITH ( attributes )]\n\
+\tAS 'sql_queries'|'builtin_function_name'|'procedural_commands'\n\
+\tLANGUAGE 'sql'|'internal'|'procedural_language_name';\n\
 \n\
 OR\n\
 \n\
 \tCREATE FUNCTION function_name ([type1, ...typeN]) RETURNS return_type\n\
-\tAS 'object_filename', 'link_symbol'\n\
-\tLANGUAGE 'c';"},
+\t[WITH ( attributes )]\n\
+\tAS 'object_filename' [, 'link_symbol']\n\
+\tLANGUAGE 'C';"},
 	{"create index",
 		"construct an index",
 	"\
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 68d82473244..08705ea9f6e 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodes.h,v 1.53 1999/09/29 16:06:23 wieck Exp $
+ * $Id: nodes.h,v 1.54 1999/10/02 21:33:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -194,7 +194,7 @@ typedef enum NodeTag
 	T_FuncCall,
 	T_A_Indices,
 	T_ResTarget,
-	T_ParamString,
+	T_ParamString,		/* not used anymore */
 	T_RelExpr,
 	T_SortGroupBy,
 	T_RangeVar,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index a736c1af67e..463ea1518e0 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsenodes.h,v 1.81 1999/09/29 16:06:23 wieck Exp $
+ * $Id: parsenodes.h,v 1.82 1999/10/02 21:33:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -346,7 +346,7 @@ typedef struct IndexStmt
 	char	   *relname;		/* name of relation to index on */
 	char	   *accessMethod;	/* name of acess methood (eg. btree) */
 	List	   *indexParams;	/* a list of IndexElem */
-	List	   *withClause;		/* a list of ParamString */
+	List	   *withClause;		/* a list of DefElem */
 	Node	   *whereClause;	/* qualifications */
 	List	   *rangetable;		/* range table, filled in by
 								 * transformStmt() */
@@ -367,7 +367,7 @@ typedef struct ProcedureStmt
 								 * (as Value *) */
 	Node	   *returnType;		/* the return type (as a string or a
 								 * TypeName (ie.setof) */
-	List	   *withClause;		/* a list of ParamString */
+	List	   *withClause;		/* a list of DefElem */
 	List	   *as;				/* the SQL statement or filename */
 	char	   *language;		/* C or SQL */
 } ProcedureStmt;
@@ -862,16 +862,6 @@ typedef struct ResTarget
 								 * assign */
 } ResTarget;
 
-/*
- * ParamString - used in WITH clauses
- */
-typedef struct ParamString
-{
-	NodeTag		type;
-	char	   *name;
-	char	   *val;
-} ParamString;
-
 /*
  * RelExpr - relation expressions
  */
-- 
GitLab