* pg_proc.c
* routines to support manipulation of the pg_proc relation
*
- * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.147 2007/11/15 21:14:33 momjian Exp $
+ * src/backend/catalog/pg_proc.c
*
*-------------------------------------------------------------------------
*/
#include "access/xact.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
#include "catalog/pg_language.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_proc.h"
+#include "catalog/pg_proc_fn.h"
#include "catalog/pg_type.h"
#include "executor/functions.h"
#include "funcapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
+#include "nodes/nodeFuncs.h"
#include "parser/parse_type.h"
#include "tcop/pquery.h"
#include "tcop/tcopprot.h"
Datum fmgr_c_validator(PG_FUNCTION_ARGS);
Datum fmgr_sql_validator(PG_FUNCTION_ARGS);
+typedef struct
+{
+ char *proname;
+ char *prosrc;
+} parse_error_callback_arg;
+
static void sql_function_parse_error_callback(void *arg);
static int match_prosrc_to_query(const char *prosrc, const char *queryText,
int cursorpos);
*
* 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.
+ * not "ArrayType *", to avoid importing array.h into pg_proc_fn.h.
* ----------------------------------------------------------------
*/
Oid
const char *prosrc,
const char *probin,
bool isAgg,
+ bool isWindowFunc,
bool security_definer,
bool isStrict,
char volatility,
Datum allParameterTypes,
Datum parameterModes,
Datum parameterNames,
+ List *parameterDefaults,
Datum proconfig,
float4 procost,
float4 prorows)
bool genericOutParam = false;
bool internalInParam = false;
bool internalOutParam = false;
+ Oid variadicType = InvalidOid;
+ Oid proowner = GetUserId();
+ Acl *proacl = NULL;
Relation rel;
HeapTuple tup;
HeapTuple oldtup;
- char nulls[Natts_pg_proc];
+ bool nulls[Natts_pg_proc];
Datum values[Natts_pg_proc];
- char replaces[Natts_pg_proc];
+ bool replaces[Natts_pg_proc];
Oid relid;
NameData procname;
TupleDesc tupDesc;
* sanity checks
*/
Assert(PointerIsValid(prosrc));
- Assert(PointerIsValid(probin));
parameterCount = parameterTypes->dim1;
if (parameterCount < 0 || parameterCount > FUNC_MAX_ARGS)
ereport(ERROR,
(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
- errmsg("functions cannot have more than %d arguments",
- FUNC_MAX_ARGS)));
+ errmsg_plural("functions cannot have more than %d argument",
+ "functions cannot have more than %d arguments",
+ FUNC_MAX_ARGS,
+ FUNC_MAX_ARGS)));
/* note: the above is correct, we do NOT count output arguments */
if (allParameterTypes != PointerGetDatum(NULL))
procedureName,
format_type_be(parameterTypes->values[0]))));
+ if (parameterModes != PointerGetDatum(NULL))
+ {
+ /*
+ * We expect the array to be a 1-D CHAR array; verify that. We don't
+ * need to use deconstruct_array() since the array data is just going
+ * to look like a C array of char values.
+ */
+ ArrayType *modesArray = (ArrayType *) DatumGetPointer(parameterModes);
+ char *modes;
+
+ if (ARR_NDIM(modesArray) != 1 ||
+ ARR_DIMS(modesArray)[0] != allParamCount ||
+ ARR_HASNULL(modesArray) ||
+ ARR_ELEMTYPE(modesArray) != CHAROID)
+ elog(ERROR, "parameterModes is not a 1-D char array");
+ modes = (char *) ARR_DATA_PTR(modesArray);
+
+ /*
+ * Only the last input parameter can be variadic; if it is, save its
+ * element type. Errors here are just elog since caller should have
+ * checked this already.
+ */
+ for (i = 0; i < allParamCount; i++)
+ {
+ switch (modes[i])
+ {
+ case PROARGMODE_IN:
+ case PROARGMODE_INOUT:
+ if (OidIsValid(variadicType))
+ elog(ERROR, "variadic parameter must be last");
+ break;
+ case PROARGMODE_OUT:
+ case PROARGMODE_TABLE:
+ /* okay */
+ break;
+ case PROARGMODE_VARIADIC:
+ if (OidIsValid(variadicType))
+ elog(ERROR, "variadic parameter must be last");
+ switch (allParams[i])
+ {
+ case ANYOID:
+ variadicType = ANYOID;
+ break;
+ case ANYARRAYOID:
+ variadicType = ANYELEMENTOID;
+ break;
+ default:
+ variadicType = get_element_type(allParams[i]);
+ if (!OidIsValid(variadicType))
+ elog(ERROR, "variadic parameter is not an array");
+ break;
+ }
+ break;
+ default:
+ elog(ERROR, "invalid parameter mode '%c'", modes[i]);
+ break;
+ }
+ }
+ }
+
/*
* All seems OK; prepare the data to be inserted into pg_proc.
*/
for (i = 0; i < Natts_pg_proc; ++i)
{
- nulls[i] = ' ';
+ nulls[i] = false;
values[i] = (Datum) 0;
- replaces[i] = 'r';
+ replaces[i] = true;
}
namestrcpy(&procname, procedureName);
values[Anum_pg_proc_proname - 1] = NameGetDatum(&procname);
values[Anum_pg_proc_pronamespace - 1] = ObjectIdGetDatum(procNamespace);
- values[Anum_pg_proc_proowner - 1] = ObjectIdGetDatum(GetUserId());
+ values[Anum_pg_proc_proowner - 1] = ObjectIdGetDatum(proowner);
values[Anum_pg_proc_prolang - 1] = ObjectIdGetDatum(languageObjectId);
values[Anum_pg_proc_procost - 1] = Float4GetDatum(procost);
values[Anum_pg_proc_prorows - 1] = Float4GetDatum(prorows);
+ values[Anum_pg_proc_provariadic - 1] = ObjectIdGetDatum(variadicType);
+ values[Anum_pg_proc_protransform - 1] = ObjectIdGetDatum(InvalidOid);
values[Anum_pg_proc_proisagg - 1] = BoolGetDatum(isAgg);
+ values[Anum_pg_proc_proiswindow - 1] = BoolGetDatum(isWindowFunc);
values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(security_definer);
values[Anum_pg_proc_proisstrict - 1] = BoolGetDatum(isStrict);
values[Anum_pg_proc_proretset - 1] = BoolGetDatum(returnsSet);
values[Anum_pg_proc_provolatile - 1] = CharGetDatum(volatility);
values[Anum_pg_proc_pronargs - 1] = UInt16GetDatum(parameterCount);
+ values[Anum_pg_proc_pronargdefaults - 1] = UInt16GetDatum(list_length(parameterDefaults));
values[Anum_pg_proc_prorettype - 1] = ObjectIdGetDatum(returnType);
values[Anum_pg_proc_proargtypes - 1] = PointerGetDatum(parameterTypes);
if (allParameterTypes != PointerGetDatum(NULL))
values[Anum_pg_proc_proallargtypes - 1] = allParameterTypes;
else
- nulls[Anum_pg_proc_proallargtypes - 1] = 'n';
+ nulls[Anum_pg_proc_proallargtypes - 1] = true;
if (parameterModes != PointerGetDatum(NULL))
values[Anum_pg_proc_proargmodes - 1] = parameterModes;
else
- nulls[Anum_pg_proc_proargmodes - 1] = 'n';
+ nulls[Anum_pg_proc_proargmodes - 1] = true;
if (parameterNames != PointerGetDatum(NULL))
values[Anum_pg_proc_proargnames - 1] = parameterNames;
else
- nulls[Anum_pg_proc_proargnames - 1] = 'n';
- values[Anum_pg_proc_prosrc - 1] = DirectFunctionCall1(textin,
- CStringGetDatum(prosrc));
- values[Anum_pg_proc_probin - 1] = DirectFunctionCall1(textin,
- CStringGetDatum(probin));
+ nulls[Anum_pg_proc_proargnames - 1] = true;
+ if (parameterDefaults != NIL)
+ values[Anum_pg_proc_proargdefaults - 1] = CStringGetTextDatum(nodeToString(parameterDefaults));
+ else
+ nulls[Anum_pg_proc_proargdefaults - 1] = true;
+ values[Anum_pg_proc_prosrc - 1] = CStringGetTextDatum(prosrc);
+ if (probin)
+ values[Anum_pg_proc_probin - 1] = CStringGetTextDatum(probin);
+ else
+ nulls[Anum_pg_proc_probin - 1] = true;
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';
+ nulls[Anum_pg_proc_proconfig - 1] = true;
+ /* proacl will be determined later */
rel = heap_open(ProcedureRelationId, RowExclusiveLock);
tupDesc = RelationGetDescr(rel);
/* Check for pre-existing definition */
- oldtup = SearchSysCache(PROCNAMEARGSNSP,
- PointerGetDatum(procedureName),
- PointerGetDatum(parameterTypes),
- ObjectIdGetDatum(procNamespace),
- 0);
+ oldtup = SearchSysCache3(PROCNAMEARGSNSP,
+ PointerGetDatum(procedureName),
+ PointerGetDatum(parameterTypes),
+ ObjectIdGetDatum(procNamespace));
if (HeapTupleIsValid(oldtup))
{
/* There is one; okay to replace it? */
Form_pg_proc oldproc = (Form_pg_proc) GETSTRUCT(oldtup);
+ Datum proargnames;
+ bool isnull;
if (!replace)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_FUNCTION),
errmsg("function \"%s\" already exists with same argument types",
procedureName)));
- if (!pg_proc_ownercheck(HeapTupleGetOid(oldtup), GetUserId()))
+ if (!pg_proc_ownercheck(HeapTupleGetOid(oldtup), proowner))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
procedureName);
errhint("Use DROP FUNCTION first.")));
}
- /* Can't change aggregate status, either */
+ /*
+ * If there were any named input parameters, check to make sure the
+ * names have not been changed, as this could break existing calls. We
+ * allow adding names to formerly unnamed parameters, though.
+ */
+ proargnames = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup,
+ Anum_pg_proc_proargnames,
+ &isnull);
+ if (!isnull)
+ {
+ Datum proargmodes;
+ char **old_arg_names;
+ char **new_arg_names;
+ int n_old_arg_names;
+ int n_new_arg_names;
+ int j;
+
+ proargmodes = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup,
+ Anum_pg_proc_proargmodes,
+ &isnull);
+ if (isnull)
+ proargmodes = PointerGetDatum(NULL); /* just to be sure */
+
+ n_old_arg_names = get_func_input_arg_names(proargnames,
+ proargmodes,
+ &old_arg_names);
+ n_new_arg_names = get_func_input_arg_names(parameterNames,
+ parameterModes,
+ &new_arg_names);
+ for (j = 0; j < n_old_arg_names; j++)
+ {
+ if (old_arg_names[j] == NULL)
+ continue;
+ if (j >= n_new_arg_names || new_arg_names[j] == NULL ||
+ strcmp(old_arg_names[j], new_arg_names[j]) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("cannot change name of input parameter \"%s\"",
+ old_arg_names[j]),
+ errhint("Use DROP FUNCTION first.")));
+ }
+ }
+
+ /*
+ * If there are existing defaults, check compatibility: redefinition
+ * must not remove any defaults nor change their types. (Removing a
+ * default might cause a function to fail to satisfy an existing call.
+ * Changing type would only be possible if the associated parameter is
+ * polymorphic, and in such cases a change of default type might alter
+ * the resolved output type of existing calls.)
+ */
+ if (oldproc->pronargdefaults != 0)
+ {
+ Datum proargdefaults;
+ List *oldDefaults;
+ ListCell *oldlc;
+ ListCell *newlc;
+
+ if (list_length(parameterDefaults) < oldproc->pronargdefaults)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("cannot remove parameter defaults from existing function"),
+ errhint("Use DROP FUNCTION first.")));
+
+ proargdefaults = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup,
+ Anum_pg_proc_proargdefaults,
+ &isnull);
+ Assert(!isnull);
+ oldDefaults = (List *) stringToNode(TextDatumGetCString(proargdefaults));
+ Assert(IsA(oldDefaults, List));
+ Assert(list_length(oldDefaults) == oldproc->pronargdefaults);
+
+ /* new list can have more defaults than old, advance over 'em */
+ newlc = list_head(parameterDefaults);
+ for (i = list_length(parameterDefaults) - oldproc->pronargdefaults;
+ i > 0;
+ i--)
+ newlc = lnext(newlc);
+
+ foreach(oldlc, oldDefaults)
+ {
+ Node *oldDef = (Node *) lfirst(oldlc);
+ Node *newDef = (Node *) lfirst(newlc);
+
+ if (exprType(oldDef) != exprType(newDef))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("cannot change data type of existing parameter default value"),
+ errhint("Use DROP FUNCTION first.")));
+ newlc = lnext(newlc);
+ }
+ }
+
+ /* Can't change aggregate or window-function status, either */
if (oldproc->proisagg != isAgg)
{
if (oldproc->proisagg)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("function \"%s\" is an aggregate",
+ errmsg("function \"%s\" is an aggregate function",
+ procedureName)));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("function \"%s\" is not an aggregate function",
+ procedureName)));
+ }
+ if (oldproc->proiswindow != isWindowFunc)
+ {
+ if (oldproc->proiswindow)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("function \"%s\" is a window function",
procedureName)));
else
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("function \"%s\" is not an aggregate",
+ errmsg("function \"%s\" is not a window function",
procedureName)));
}
- /* do not change existing ownership or permissions, either */
- replaces[Anum_pg_proc_proowner - 1] = ' ';
- replaces[Anum_pg_proc_proacl - 1] = ' ';
+ /*
+ * Do not change existing ownership or permissions, either. Note
+ * dependency-update code below has to agree with this decision.
+ */
+ replaces[Anum_pg_proc_proowner - 1] = false;
+ replaces[Anum_pg_proc_proacl - 1] = false;
/* Okay, do it... */
- tup = heap_modifytuple(oldtup, tupDesc, values, nulls, replaces);
+ tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces);
simple_heap_update(rel, &tup->t_self, tup);
ReleaseSysCache(oldtup);
else
{
/* Creating a new procedure */
- tup = heap_formtuple(tupDesc, values, nulls);
+
+ /* First, get default permissions and set up proacl */
+ proacl = get_user_default_acl(ACL_OBJECT_FUNCTION, proowner,
+ procNamespace);
+ if (proacl != NULL)
+ values[Anum_pg_proc_proacl - 1] = PointerGetDatum(proacl);
+ else
+ nulls[Anum_pg_proc_proacl - 1] = true;
+
+ tup = heap_form_tuple(tupDesc, values, nulls);
simple_heap_insert(rel, tup);
is_update = false;
}
/*
* Create dependencies for the new function. If we are updating an
* existing function, first delete any existing pg_depend entries.
+ * (However, since we are not changing ownership or permissions, the
+ * shared dependencies do *not* need to change, and we leave them alone.
+ * We also don't change any pre-existing extension-membership dependency.)
*/
if (is_update)
- {
- deleteDependencyRecordsFor(ProcedureRelationId, retval);
- deleteSharedDependencyRecordsFor(ProcedureRelationId, retval);
- }
+ deleteDependencyRecordsFor(ProcedureRelationId, retval, true);
myself.classId = ProcedureRelationId;
myself.objectId = retval;
}
/* dependency on owner */
- recordDependencyOnOwner(ProcedureRelationId, retval, GetUserId());
+ if (!is_update)
+ recordDependencyOnOwner(ProcedureRelationId, retval, proowner);
+
+ /* dependency on any roles mentioned in ACL */
+ if (!is_update && proacl != NULL)
+ {
+ int nnewmembers;
+ Oid *newmembers;
+
+ nnewmembers = aclmembers(proacl, &newmembers);
+ updateAclDependencies(ProcedureRelationId, retval, 0,
+ proowner,
+ 0, NULL,
+ nnewmembers, newmembers);
+ }
+
+ /* dependency on extension */
+ if (!is_update)
+ recordDependencyOnCurrentExtension(&myself);
heap_freetuple(tup);
+ /* Post creation hook for new function */
+ InvokeObjectAccessHook(OAT_POST_CREATE, ProcedureRelationId, retval, 0);
+
heap_close(rel, RowExclusiveLock);
/* Verify function body */
if (OidIsValid(languageValidator))
{
+ ArrayType *set_items;
+ int save_nestlevel;
+
/* Advance command counter so new tuple can be seen by validator */
CommandCounterIncrement();
+
+ /* Set per-function configuration parameters */
+ set_items = (ArrayType *) DatumGetPointer(proconfig);
+ if (set_items) /* Need a new GUC nesting level */
+ {
+ save_nestlevel = NewGUCNestLevel();
+ ProcessGUCArray(set_items,
+ (superuser() ? PGC_SUSET : PGC_USERSET),
+ PGC_S_SESSION,
+ GUC_ACTION_SAVE);
+ }
+ else
+ save_nestlevel = 0; /* keep compiler quiet */
+
OidFunctionCall1(languageValidator, ObjectIdGetDatum(retval));
+
+ if (set_items)
+ AtEOXact_GUC(true, save_nestlevel);
}
return retval;
{
Oid funcoid = PG_GETARG_OID(0);
HeapTuple tuple;
- Form_pg_proc proc;
bool isnull;
Datum tmp;
char *prosrc;
* name will be found later if it isn't there now.
*/
- tuple = SearchSysCache(PROCOID,
- ObjectIdGetDatum(funcoid),
- 0, 0, 0);
+ tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for function %u", funcoid);
- proc = (Form_pg_proc) GETSTRUCT(tuple);
tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
if (isnull)
elog(ERROR, "null prosrc");
- prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
+ prosrc = TextDatumGetCString(tmp);
if (fmgr_internal_function(prosrc) == InvalidOid)
ereport(ERROR,
Oid funcoid = PG_GETARG_OID(0);
void *libraryhandle;
HeapTuple tuple;
- Form_pg_proc proc;
bool isnull;
Datum tmp;
char *prosrc;
* and for pg_dump loading it's much better if we *do* check.
*/
- tuple = SearchSysCache(PROCOID,
- ObjectIdGetDatum(funcoid),
- 0, 0, 0);
+ tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for function %u", funcoid);
- proc = (Form_pg_proc) GETSTRUCT(tuple);
tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
if (isnull)
- elog(ERROR, "null prosrc");
- prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
+ elog(ERROR, "null prosrc for C function %u", funcoid);
+ prosrc = TextDatumGetCString(tmp);
tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_probin, &isnull);
if (isnull)
- elog(ERROR, "null probin");
- probin = DatumGetCString(DirectFunctionCall1(textout, tmp));
+ elog(ERROR, "null probin for C function %u", funcoid);
+ probin = TextDatumGetCString(tmp);
(void) load_external_function(probin, prosrc, true, &libraryhandle);
(void) fetch_finfo_record(libraryhandle, prosrc);
Oid funcoid = PG_GETARG_OID(0);
HeapTuple tuple;
Form_pg_proc proc;
+ List *raw_parsetree_list;
List *querytree_list;
+ ListCell *lc;
bool isnull;
Datum tmp;
char *prosrc;
+ parse_error_callback_arg callback_arg;
ErrorContextCallback sqlerrcontext;
bool haspolyarg;
int i;
- tuple = SearchSysCache(PROCOID,
- ObjectIdGetDatum(funcoid),
- 0, 0, 0);
+ tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for function %u", funcoid);
proc = (Form_pg_proc) GETSTRUCT(tuple);
if (isnull)
elog(ERROR, "null prosrc");
- prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
+ prosrc = TextDatumGetCString(tmp);
/*
* Setup error traceback support for ereport().
*/
+ callback_arg.proname = NameStr(proc->proname);
+ callback_arg.prosrc = prosrc;
+
sqlerrcontext.callback = sql_function_parse_error_callback;
- sqlerrcontext.arg = tuple;
+ sqlerrcontext.arg = (void *) &callback_arg;
sqlerrcontext.previous = error_context_stack;
error_context_stack = &sqlerrcontext;
* We can run the text through the raw parser though; this will at
* least catch silly syntactic errors.
*/
+ raw_parsetree_list = pg_parse_query(prosrc);
+
if (!haspolyarg)
{
- querytree_list = pg_parse_and_rewrite(prosrc,
- proc->proargtypes.values,
- proc->pronargs);
+ /*
+ * OK to do full precheck: analyze and rewrite the queries, then
+ * verify the result type.
+ */
+ SQLFunctionParseInfoPtr pinfo;
+
+ /* But first, set up parameter information */
+ pinfo = prepare_sql_fn_parse_info(tuple, NULL, InvalidOid);
+
+ querytree_list = NIL;
+ foreach(lc, raw_parsetree_list)
+ {
+ Node *parsetree = (Node *) lfirst(lc);
+ List *querytree_sublist;
+
+ querytree_sublist = pg_analyze_and_rewrite_params(parsetree,
+ prosrc,
+ (ParserSetupHook) sql_fn_parser_setup,
+ pinfo);
+ querytree_list = list_concat(querytree_list,
+ querytree_sublist);
+ }
+
(void) check_sql_fn_retval(funcoid, proc->prorettype,
- querytree_list, NULL);
+ querytree_list,
+ NULL, NULL);
}
- else
- querytree_list = pg_parse_query(prosrc);
error_context_stack = sqlerrcontext.previous;
}
static void
sql_function_parse_error_callback(void *arg)
{
- HeapTuple tuple = (HeapTuple) arg;
- Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(tuple);
- bool isnull;
- Datum tmp;
- char *prosrc;
+ parse_error_callback_arg *callback_arg = (parse_error_callback_arg *) arg;
/* See if it's a syntax error; if so, transpose to CREATE FUNCTION */
- tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
- if (isnull)
- elog(ERROR, "null prosrc");
- prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
-
- if (!function_parse_error_transpose(prosrc))
+ if (!function_parse_error_transpose(callback_arg->prosrc))
{
/* If it's not a syntax error, push info onto context stack */
- errcontext("SQL function \"%s\"", NameStr(proc->proname));
+ errcontext("SQL function \"%s\"", callback_arg->proname);
}
-
- pfree(prosrc);
}
/*
* Adjust a syntax error occurring inside the function body of a CREATE
- * FUNCTION command. This can be used by any function validator, not only
- * for SQL-language functions. It is assumed that the syntax error position
- * is initially relative to the function body string (as passed in). If
- * possible, we adjust the position to reference the original CREATE command;
- * if we can't manage that, we set up an "internal query" syntax error instead.
+ * FUNCTION or DO command. This can be used by any function validator or
+ * anonymous-block handler, not only for SQL-language functions.
+ * It is assumed that the syntax error position is initially relative to the
+ * function body string (as passed in). If possible, we adjust the position
+ * to reference the original command text; if we can't manage that, we set
+ * up an "internal query" syntax error instead.
*
* Returns true if a syntax error was processed, false if not.
*/
/*
* Try to locate the string literal containing the function body in the
- * given text of the CREATE FUNCTION command. If successful, return the
- * character (not byte) index within the command corresponding to the
+ * given text of the CREATE FUNCTION or DO command. If successful, return
+ * the character (not byte) index within the command corresponding to the
* given character index within the literal. If not successful, return 0.
*/
static int
int cursorpos)
{
/*
- * Rather than fully parsing the CREATE FUNCTION command, we just scan the
+ * Rather than fully parsing the original command, we just scan the
* command looking for $prosrc$ or 'prosrc'. This could be fooled (though
* not in any very probable scenarios), so fail if we find more than one
* match.