#include "plpgsql.h"#include <ctype.h>#include "access/htup_details.h"#include "catalog/namespace.h"#include "catalog/pg_proc.h"#include "catalog/pg_proc_fn.h"#include "catalog/pg_type.h"#include "funcapi.h"#include "nodes/makefuncs.h"#include "parser/parse_type.h"#include "utils/builtins.h"#include "utils/guc.h"#include "utils/lsyscache.h"#include "utils/memutils.h"#include "utils/rel.h"#include "utils/syscache.h"#include "plerrcodes.h"
Go to the source code of this file.
| #define FUNCS_PER_USER 128 |
Definition at line 68 of file pl_comp.c.
Referenced by plpgsql_HashTableInit().
| typedef struct plpgsql_hashent plpgsql_HashEnt |
| static void add_dummy_return | ( | PLpgSQL_function * | function | ) | [static] |
Definition at line 984 of file pl_comp.c.
References PLpgSQL_function::action, PLpgSQL_stmt_block::body, PLpgSQL_stmt_block::exceptions, lappend(), list_make1, llast, NIL, NULL, PLpgSQL_function::out_param_varno, palloc0(), and PLPGSQL_STMT_RETURN.
Referenced by do_compile(), and plpgsql_compile_inline().
{
/*
* If the outer block has an EXCEPTION clause, we need to make a new outer
* block, since the added RETURN shouldn't act like it is inside the
* EXCEPTION clause.
*/
if (function->action->exceptions != NULL)
{
PLpgSQL_stmt_block *new;
new = palloc0(sizeof(PLpgSQL_stmt_block));
new->cmd_type = PLPGSQL_STMT_BLOCK;
new->body = list_make1(function->action);
function->action = new;
}
if (function->action->body == NIL ||
((PLpgSQL_stmt *) llast(function->action->body))->cmd_type != PLPGSQL_STMT_RETURN)
{
PLpgSQL_stmt_return *new;
new = palloc0(sizeof(PLpgSQL_stmt_return));
new->cmd_type = PLPGSQL_STMT_RETURN;
new->expr = NULL;
new->retvarno = function->out_param_varno;
function->action->body = lappend(function->action->body, new);
}
}
| static void add_parameter_name | ( | int | itemtype, | |
| int | itemno, | |||
| const char * | name | |||
| ) | [static] |
Definition at line 959 of file pl_comp.c.
References ereport, errcode(), errmsg(), ERROR, NULL, plpgsql_ns_additem(), plpgsql_ns_lookup(), and plpgsql_ns_top().
Referenced by do_compile().
{
/*
* Before adding the name, check for duplicates. We need this even though
* functioncmds.c has a similar check, because that code explicitly
* doesn't complain about conflicting IN and OUT parameter names. In
* plpgsql, such names are in the same namespace, so there is no way to
* disambiguate.
*/
if (plpgsql_ns_lookup(plpgsql_ns_top(), true,
name, NULL, NULL,
NULL) != NULL)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("parameter name \"%s\" used more than once",
name)));
/* OK, add the name */
plpgsql_ns_additem(itemtype, itemno, name);
}
| static PLpgSQL_type * build_datatype | ( | HeapTuple | typeTup, | |
| int32 | typmod, | |||
| Oid | collation | |||
| ) | [static] |
Definition at line 2151 of file pl_comp.c.
References Assert, PLpgSQL_type::atttypmod, PLpgSQL_type::collation, elog, ereport, errcode(), errmsg(), ERROR, fmgr_info(), GETSTRUCT, getTypeIOParam(), HeapTupleGetOid, NameStr, OidIsValid, palloc(), pstrdup(), RECORDOID, PLpgSQL_type::ttype, PLpgSQL_type::typbyval, PLpgSQL_type::typinput, PLpgSQL_type::typioparam, PLpgSQL_type::typlen, PLpgSQL_type::typname, PLpgSQL_type::typoid, PLpgSQL_type::typrelid, TYPTYPE_BASE, TYPTYPE_COMPOSITE, TYPTYPE_DOMAIN, TYPTYPE_ENUM, TYPTYPE_PSEUDO, and TYPTYPE_RANGE.
Referenced by do_compile(), plpgsql_build_datatype(), plpgsql_parse_cwordtype(), and plpgsql_parse_wordtype().
{
Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
PLpgSQL_type *typ;
if (!typeStruct->typisdefined)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("type \"%s\" is only a shell",
NameStr(typeStruct->typname))));
typ = (PLpgSQL_type *) palloc(sizeof(PLpgSQL_type));
typ->typname = pstrdup(NameStr(typeStruct->typname));
typ->typoid = HeapTupleGetOid(typeTup);
switch (typeStruct->typtype)
{
case TYPTYPE_BASE:
case TYPTYPE_DOMAIN:
case TYPTYPE_ENUM:
case TYPTYPE_RANGE:
typ->ttype = PLPGSQL_TTYPE_SCALAR;
break;
case TYPTYPE_COMPOSITE:
Assert(OidIsValid(typeStruct->typrelid));
typ->ttype = PLPGSQL_TTYPE_ROW;
break;
case TYPTYPE_PSEUDO:
if (typ->typoid == RECORDOID)
typ->ttype = PLPGSQL_TTYPE_REC;
else
typ->ttype = PLPGSQL_TTYPE_PSEUDO;
break;
default:
elog(ERROR, "unrecognized typtype: %d",
(int) typeStruct->typtype);
break;
}
typ->typlen = typeStruct->typlen;
typ->typbyval = typeStruct->typbyval;
typ->typrelid = typeStruct->typrelid;
typ->typioparam = getTypeIOParam(typeTup);
typ->collation = typeStruct->typcollation;
if (OidIsValid(collation) && OidIsValid(typ->collation))
typ->collation = collation;
fmgr_info(typeStruct->typinput, &(typ->typinput));
typ->atttypmod = typmod;
return typ;
}
| static PLpgSQL_row * build_row_from_class | ( | Oid | classOid | ) | [static] |
Definition at line 1972 of file pl_comp.c.
References AccessShareLock, tupleDesc::attrs, CreateTupleDescCopy(), PLpgSQL_row::dtype, ereport, errcode(), errmsg(), ERROR, PLpgSQL_row::fieldnames, i, NAMEDATALEN, NameStr, PLpgSQL_row::nfields, palloc(), palloc0(), plpgsql_build_datatype(), plpgsql_build_variable(), relation_close(), relation_open(), RelationGetDescr, RelationGetForm, RelationGetRelationName, RELKIND_COMPOSITE_TYPE, RELKIND_FOREIGN_TABLE, RELKIND_MATVIEW, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, PLpgSQL_row::rowtupdesc, snprintf(), and PLpgSQL_row::varnos.
Referenced by plpgsql_build_variable().
{
PLpgSQL_row *row;
Relation rel;
Form_pg_class classStruct;
const char *relname;
int i;
/*
* Open the relation to get info.
*/
rel = relation_open(classOid, AccessShareLock);
classStruct = RelationGetForm(rel);
relname = RelationGetRelationName(rel);
/*
* Accept relation, sequence, view, materialized view, composite type, or
* foreign table.
*/
if (classStruct->relkind != RELKIND_RELATION &&
classStruct->relkind != RELKIND_SEQUENCE &&
classStruct->relkind != RELKIND_VIEW &&
classStruct->relkind != RELKIND_MATVIEW &&
classStruct->relkind != RELKIND_COMPOSITE_TYPE &&
classStruct->relkind != RELKIND_FOREIGN_TABLE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("relation \"%s\" is not a table", relname)));
/*
* Create a row datum entry and all the required variables that it will
* point to.
*/
row = palloc0(sizeof(PLpgSQL_row));
row->dtype = PLPGSQL_DTYPE_ROW;
row->rowtupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
row->nfields = classStruct->relnatts;
row->fieldnames = palloc(sizeof(char *) * row->nfields);
row->varnos = palloc(sizeof(int) * row->nfields);
for (i = 0; i < row->nfields; i++)
{
Form_pg_attribute attrStruct;
/*
* Get the attribute and check for dropped column
*/
attrStruct = row->rowtupdesc->attrs[i];
if (!attrStruct->attisdropped)
{
char *attname;
char refname[(NAMEDATALEN * 2) + 100];
PLpgSQL_variable *var;
attname = NameStr(attrStruct->attname);
snprintf(refname, sizeof(refname), "%s.%s", relname, attname);
/*
* Create the internal variable for the field
*
* We know if the table definitions contain a default value or if
* the field is declared in the table as NOT NULL. But it's
* possible to create a table field as NOT NULL without a default
* value and that would lead to problems later when initializing
* the variables due to entering a block at execution time. Thus
* we ignore this information for now.
*/
var = plpgsql_build_variable(refname, 0,
plpgsql_build_datatype(attrStruct->atttypid,
attrStruct->atttypmod,
attrStruct->attcollation),
false);
/* Add the variable to the row */
row->fieldnames[i] = attname;
row->varnos[i] = var->dno;
}
else
{
/* Leave a hole in the row structure for the dropped col */
row->fieldnames[i] = NULL;
row->varnos[i] = -1;
}
}
relation_close(rel, AccessShareLock);
return row;
}
| static PLpgSQL_row * build_row_from_vars | ( | PLpgSQL_variable ** | vars, | |
| int | numvars | |||
| ) | [static] |
Definition at line 2067 of file pl_comp.c.
References CreateTemplateTupleDesc(), PLpgSQL_variable::dno, PLpgSQL_variable::dtype, PLpgSQL_row::dtype, elog, ERROR, PLpgSQL_row::fieldnames, i, PLpgSQL_row::nfields, palloc(), palloc0(), PLPGSQL_DTYPE_REC, PLPGSQL_DTYPE_ROW, PLPGSQL_DTYPE_VAR, PLpgSQL_variable::refname, PLpgSQL_row::rowtupdesc, TupleDescInitEntry(), TupleDescInitEntryCollation(), and PLpgSQL_row::varnos.
Referenced by do_compile().
{
PLpgSQL_row *row;
int i;
row = palloc0(sizeof(PLpgSQL_row));
row->dtype = PLPGSQL_DTYPE_ROW;
row->rowtupdesc = CreateTemplateTupleDesc(numvars, false);
row->nfields = numvars;
row->fieldnames = palloc(numvars * sizeof(char *));
row->varnos = palloc(numvars * sizeof(int));
for (i = 0; i < numvars; i++)
{
PLpgSQL_variable *var = vars[i];
Oid typoid = RECORDOID;
int32 typmod = -1;
Oid typcoll = InvalidOid;
switch (var->dtype)
{
case PLPGSQL_DTYPE_VAR:
typoid = ((PLpgSQL_var *) var)->datatype->typoid;
typmod = ((PLpgSQL_var *) var)->datatype->atttypmod;
typcoll = ((PLpgSQL_var *) var)->datatype->collation;
break;
case PLPGSQL_DTYPE_REC:
break;
case PLPGSQL_DTYPE_ROW:
if (((PLpgSQL_row *) var)->rowtupdesc)
{
typoid = ((PLpgSQL_row *) var)->rowtupdesc->tdtypeid;
typmod = ((PLpgSQL_row *) var)->rowtupdesc->tdtypmod;
/* composite types have no collation */
}
break;
default:
elog(ERROR, "unrecognized dtype: %d", var->dtype);
}
row->fieldnames[i] = var->refname;
row->varnos[i] = var->dno;
TupleDescInitEntry(row->rowtupdesc, i + 1,
var->refname,
typoid, typmod,
0);
TupleDescInitEntryCollation(row->rowtupdesc, i + 1, typcoll);
}
return row;
}
| static void compute_function_hashkey | ( | FunctionCallInfo | fcinfo, | |
| Form_pg_proc | procStruct, | |||
| PLpgSQL_func_hashkey * | hashkey, | |||
| bool | forValidator | |||
| ) | [static] |
Definition at line 2378 of file pl_comp.c.
References PLpgSQL_func_hashkey::argtypes, CALLED_AS_TRIGGER, FunctionCallInfoData::context, FunctionCallInfoData::flinfo, FmgrInfo::fn_expr, FmgrInfo::fn_oid, FunctionCallInfoData::fncollation, PLpgSQL_func_hashkey::funcOid, PLpgSQL_func_hashkey::inputCollation, PLpgSQL_func_hashkey::isTrigger, MemSet, NameStr, NULL, plpgsql_resolve_polymorphic_argtypes(), RelationGetRelid, TriggerData::tg_relation, and PLpgSQL_func_hashkey::trigrelOid.
Referenced by plpgsql_compile().
{
/* Make sure any unused bytes of the struct are zero */
MemSet(hashkey, 0, sizeof(PLpgSQL_func_hashkey));
/* get function OID */
hashkey->funcOid = fcinfo->flinfo->fn_oid;
/* get call context */
hashkey->isTrigger = CALLED_AS_TRIGGER(fcinfo);
/*
* if trigger, get relation OID. In validation mode we do not know what
* relation is intended to be used, so we leave trigrelOid zero; the hash
* entry built in this case will never really be used.
*/
if (hashkey->isTrigger && !forValidator)
{
TriggerData *trigdata = (TriggerData *) fcinfo->context;
hashkey->trigrelOid = RelationGetRelid(trigdata->tg_relation);
}
/* get input collation, if known */
hashkey->inputCollation = fcinfo->fncollation;
if (procStruct->pronargs > 0)
{
/* get the argument types */
memcpy(hashkey->argtypes, procStruct->proargtypes.values,
procStruct->pronargs * sizeof(Oid));
/* resolve any polymorphic argument types */
plpgsql_resolve_polymorphic_argtypes(procStruct->pronargs,
hashkey->argtypes,
NULL,
fcinfo->flinfo->fn_expr,
forValidator,
NameStr(procStruct->proname));
}
}
| static void delete_function | ( | PLpgSQL_function * | func | ) | [static] |
Definition at line 2488 of file pl_comp.c.
References plpgsql_free_function_memory(), plpgsql_HashTableDelete(), and PLpgSQL_function::use_count.
Referenced by plpgsql_compile().
{
/* remove function from hash table (might be done already) */
plpgsql_HashTableDelete(func);
/* release the function's storage if safe and not done already */
if (func->use_count == 0)
plpgsql_free_function_memory(func);
}
| static PLpgSQL_function * do_compile | ( | FunctionCallInfo | fcinfo, | |
| HeapTuple | procTup, | |||
| PLpgSQL_function * | function, | |||
| PLpgSQL_func_hashkey * | hashkey, | |||
| bool | forValidator | |||
| ) | [static] |
Definition at line 260 of file pl_comp.c.
References PLpgSQL_function::action, add_dummy_return(), add_parameter_name(), ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_DEFAULT_MINSIZE, AllocSetContextCreate(), Anum_pg_proc_prosrc, ANYARRAYOID, ANYRANGEOID, ErrorContextCallback::arg, Assert, BOOLOID, buf, build_datatype(), build_row_from_vars(), ErrorContextCallback::callback, CALLED_AS_EVENT_TRIGGER, CALLED_AS_TRIGGER, PLpgSQL_function::datums, datums_alloc, datums_last, PLpgSQL_rec::dno, PLpgSQL_datum::dno, PLpgSQL_variable::dno, PLpgSQL_variable::dtype, elog, ereport, errcode(), errhint(), errmsg(), ERROR, error_context_stack, EVTTRIGGEROID, FunctionCallInfoData::flinfo, fmgr_info(), PLpgSQL_function::fn_argvarnos, PLpgSQL_function::fn_cxt, FmgrInfo::fn_expr, PLpgSQL_function::fn_input_collation, PLpgSQL_function::fn_is_trigger, PLpgSQL_function::fn_nargs, PLpgSQL_function::fn_oid, FmgrInfo::fn_oid, PLpgSQL_function::fn_readonly, PLpgSQL_function::fn_retbyval, PLpgSQL_function::fn_retinput, PLpgSQL_function::fn_retistuple, PLpgSQL_function::fn_retset, PLpgSQL_function::fn_rettype, PLpgSQL_function::fn_rettypioparam, PLpgSQL_function::fn_rettyplen, PLpgSQL_function::fn_signature, PLpgSQL_function::fn_tid, PLpgSQL_function::fn_xmin, FunctionCallInfoData::fncollation, format_procedure(), format_type_be(), PLpgSQL_function::found_varno, get_fn_expr_rettype(), get_func_arg_info(), GETSTRUCT, getTypeIOParam(), HeapTupleHeaderGetXmin, HeapTupleIsValid, i, INT4OID, InvalidOid, IsPolymorphicType, MemoryContextAlloc(), MemoryContextAllocZero(), MemoryContextSwitchTo(), NAMEOID, NameStr, PLpgSQL_function::ndatums, PLpgSQL_function::new_varno, NULL, ObjectIdGetDatum, OidIsValid, OIDOID, PLpgSQL_function::old_varno, PLpgSQL_function::out_param_varno, palloc(), pfree(), plpgsql_adddatum(), plpgsql_build_datatype(), plpgsql_build_record(), plpgsql_build_variable(), plpgsql_check_syntax, PLPGSQL_DML_TRIGGER, PLPGSQL_DTYPE_ROW, PLPGSQL_DTYPE_VAR, plpgsql_DumpExecTree, plpgsql_dumptree(), plpgsql_error_funcname, PLPGSQL_EVENT_TRIGGER, plpgsql_HashTableInsert(), plpgsql_nDatums, PLPGSQL_NOT_TRIGGER, plpgsql_ns_init(), plpgsql_ns_push(), plpgsql_resolve_polymorphic_argtypes(), plpgsql_scanner_finish(), plpgsql_scanner_init(), PLPGSQL_TTYPE_ROW, PLPGSQL_TTYPE_SCALAR, plpgsql_variable_conflict, plpgsql_yyparse(), ErrorContextCallback::previous, PROARGMODE_IN, PROARGMODE_INOUT, PROARGMODE_OUT, PROARGMODE_TABLE, PROARGMODE_VARIADIC, PROCOID, pstrdup(), RECORDOID, ReleaseSysCache(), PLpgSQL_function::resolve_option, SearchSysCache1, snprintf(), SysCacheGetAttr(), HeapTupleData::t_data, HeapTupleData::t_self, TEXTARRAYOID, TextDatumGetCString, TEXTOID, PLpgSQL_function::tg_argv_varno, PLpgSQL_function::tg_event_varno, PLpgSQL_function::tg_level_varno, PLpgSQL_function::tg_name_varno, PLpgSQL_function::tg_nargs_varno, PLpgSQL_function::tg_op_varno, PLpgSQL_function::tg_relid_varno, PLpgSQL_function::tg_relname_varno, PLpgSQL_function::tg_table_name_varno, PLpgSQL_function::tg_table_schema_varno, PLpgSQL_function::tg_tag_varno, PLpgSQL_function::tg_when_varno, TopMemoryContext, TRIGGEROID, PLpgSQL_type::ttype, TYPEOID, TYPTYPE_PSEUDO, and VOIDOID.
Referenced by plpgsql_compile().
{
Form_pg_proc procStruct = (Form_pg_proc) GETSTRUCT(procTup);
bool is_dml_trigger = CALLED_AS_TRIGGER(fcinfo);
bool is_event_trigger = CALLED_AS_EVENT_TRIGGER(fcinfo);
Datum prosrcdatum;
bool isnull;
char *proc_source;
HeapTuple typeTup;
Form_pg_type typeStruct;
PLpgSQL_variable *var;
PLpgSQL_rec *rec;
int i;
ErrorContextCallback plerrcontext;
int parse_rc;
Oid rettypeid;
int numargs;
int num_in_args = 0;
int num_out_args = 0;
Oid *argtypes;
char **argnames;
char *argmodes;
int *in_arg_varnos = NULL;
PLpgSQL_variable **out_arg_variables;
MemoryContext func_cxt;
/*
* Setup the scanner input and error info. We assume that this function
* cannot be invoked recursively, so there's no need to save and restore
* the static variables used here.
*/
prosrcdatum = SysCacheGetAttr(PROCOID, procTup,
Anum_pg_proc_prosrc, &isnull);
if (isnull)
elog(ERROR, "null prosrc");
proc_source = TextDatumGetCString(prosrcdatum);
plpgsql_scanner_init(proc_source);
plpgsql_error_funcname = pstrdup(NameStr(procStruct->proname));
/*
* Setup error traceback support for ereport()
*/
plerrcontext.callback = plpgsql_compile_error_callback;
plerrcontext.arg = forValidator ? proc_source : NULL;
plerrcontext.previous = error_context_stack;
error_context_stack = &plerrcontext;
/*
* Do extra syntax checks when validating the function definition. We skip
* this when actually compiling functions for execution, for performance
* reasons.
*/
plpgsql_check_syntax = forValidator;
/*
* Create the new function struct, if not done already. The function
* structs are never thrown away, so keep them in TopMemoryContext.
*/
if (function == NULL)
{
function = (PLpgSQL_function *)
MemoryContextAllocZero(TopMemoryContext, sizeof(PLpgSQL_function));
}
else
{
/* re-using a previously existing struct, so clear it out */
memset(function, 0, sizeof(PLpgSQL_function));
}
plpgsql_curr_compile = function;
/*
* All the permanent output of compilation (e.g. parse tree) is kept in a
* per-function memory context, so it can be reclaimed easily.
*/
func_cxt = AllocSetContextCreate(TopMemoryContext,
"PL/pgSQL function context",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
compile_tmp_cxt = MemoryContextSwitchTo(func_cxt);
function->fn_signature = format_procedure(fcinfo->flinfo->fn_oid);
function->fn_oid = fcinfo->flinfo->fn_oid;
function->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
function->fn_tid = procTup->t_self;
function->fn_input_collation = fcinfo->fncollation;
function->fn_cxt = func_cxt;
function->out_param_varno = -1; /* set up for no OUT param */
function->resolve_option = plpgsql_variable_conflict;
if (is_dml_trigger)
function->fn_is_trigger = PLPGSQL_DML_TRIGGER;
else if (is_event_trigger)
function->fn_is_trigger = PLPGSQL_EVENT_TRIGGER;
else
function->fn_is_trigger = PLPGSQL_NOT_TRIGGER;
/*
* Initialize the compiler, particularly the namespace stack. The
* outermost namespace contains function parameters and other special
* variables (such as FOUND), and is named after the function itself.
*/
plpgsql_ns_init();
plpgsql_ns_push(NameStr(procStruct->proname));
plpgsql_DumpExecTree = false;
datums_alloc = 128;
plpgsql_nDatums = 0;
/* This is short-lived, so needn't allocate in function's cxt */
plpgsql_Datums = MemoryContextAlloc(compile_tmp_cxt,
sizeof(PLpgSQL_datum *) * datums_alloc);
datums_last = 0;
switch (function->fn_is_trigger)
{
case PLPGSQL_NOT_TRIGGER:
/*
* Fetch info about the procedure's parameters. Allocations aren't
* needed permanently, so make them in tmp cxt.
*
* We also need to resolve any polymorphic input or output
* argument types. In validation mode we won't be able to, so we
* arbitrarily assume we are dealing with integers.
*/
MemoryContextSwitchTo(compile_tmp_cxt);
numargs = get_func_arg_info(procTup,
&argtypes, &argnames, &argmodes);
plpgsql_resolve_polymorphic_argtypes(numargs, argtypes, argmodes,
fcinfo->flinfo->fn_expr,
forValidator,
plpgsql_error_funcname);
in_arg_varnos = (int *) palloc(numargs * sizeof(int));
out_arg_variables = (PLpgSQL_variable **) palloc(numargs * sizeof(PLpgSQL_variable *));
MemoryContextSwitchTo(func_cxt);
/*
* Create the variables for the procedure's parameters.
*/
for (i = 0; i < numargs; i++)
{
char buf[32];
Oid argtypeid = argtypes[i];
char argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
PLpgSQL_type *argdtype;
PLpgSQL_variable *argvariable;
int argitemtype;
/* Create $n name for variable */
snprintf(buf, sizeof(buf), "$%d", i + 1);
/* Create datatype info */
argdtype = plpgsql_build_datatype(argtypeid,
-1,
function->fn_input_collation);
/* Disallow pseudotype argument */
/* (note we already replaced polymorphic types) */
/* (build_variable would do this, but wrong message) */
if (argdtype->ttype != PLPGSQL_TTYPE_SCALAR &&
argdtype->ttype != PLPGSQL_TTYPE_ROW)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("PL/pgSQL functions cannot accept type %s",
format_type_be(argtypeid))));
/* Build variable and add to datum list */
argvariable = plpgsql_build_variable(buf, 0,
argdtype, false);
if (argvariable->dtype == PLPGSQL_DTYPE_VAR)
{
argitemtype = PLPGSQL_NSTYPE_VAR;
}
else
{
Assert(argvariable->dtype == PLPGSQL_DTYPE_ROW);
argitemtype = PLPGSQL_NSTYPE_ROW;
}
/* Remember arguments in appropriate arrays */
if (argmode == PROARGMODE_IN ||
argmode == PROARGMODE_INOUT ||
argmode == PROARGMODE_VARIADIC)
in_arg_varnos[num_in_args++] = argvariable->dno;
if (argmode == PROARGMODE_OUT ||
argmode == PROARGMODE_INOUT ||
argmode == PROARGMODE_TABLE)
out_arg_variables[num_out_args++] = argvariable;
/* Add to namespace under the $n name */
add_parameter_name(argitemtype, argvariable->dno, buf);
/* If there's a name for the argument, make an alias */
if (argnames && argnames[i][0] != '\0')
add_parameter_name(argitemtype, argvariable->dno,
argnames[i]);
}
/*
* If there's just one OUT parameter, out_param_varno points
* directly to it. If there's more than one, build a row that
* holds all of them.
*/
if (num_out_args == 1)
function->out_param_varno = out_arg_variables[0]->dno;
else if (num_out_args > 1)
{
PLpgSQL_row *row = build_row_from_vars(out_arg_variables,
num_out_args);
plpgsql_adddatum((PLpgSQL_datum *) row);
function->out_param_varno = row->dno;
}
/*
* Check for a polymorphic returntype. If found, use the actual
* returntype type from the caller's FuncExpr node, if we have
* one. (In validation mode we arbitrarily assume we are dealing
* with integers.)
*
* Note: errcode is FEATURE_NOT_SUPPORTED because it should always
* work; if it doesn't we're in some context that fails to make
* the info available.
*/
rettypeid = procStruct->prorettype;
if (IsPolymorphicType(rettypeid))
{
if (forValidator)
{
if (rettypeid == ANYARRAYOID)
rettypeid = INT4ARRAYOID;
else if (rettypeid == ANYRANGEOID)
rettypeid = INT4RANGEOID;
else /* ANYELEMENT or ANYNONARRAY */
rettypeid = INT4OID;
/* XXX what could we use for ANYENUM? */
}
else
{
rettypeid = get_fn_expr_rettype(fcinfo->flinfo);
if (!OidIsValid(rettypeid))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("could not determine actual return type "
"for polymorphic function \"%s\"",
plpgsql_error_funcname)));
}
}
/*
* Normal function has a defined returntype
*/
function->fn_rettype = rettypeid;
function->fn_retset = procStruct->proretset;
/*
* Lookup the function's return type
*/
typeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(rettypeid));
if (!HeapTupleIsValid(typeTup))
elog(ERROR, "cache lookup failed for type %u", rettypeid);
typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
/* Disallow pseudotype result, except VOID or RECORD */
/* (note we already replaced polymorphic types) */
if (typeStruct->typtype == TYPTYPE_PSEUDO)
{
if (rettypeid == VOIDOID ||
rettypeid == RECORDOID)
/* okay */ ;
else if (rettypeid == TRIGGEROID || rettypeid == EVTTRIGGEROID)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("trigger functions can only be called as triggers")));
else
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("PL/pgSQL functions cannot return type %s",
format_type_be(rettypeid))));
}
if (typeStruct->typrelid != InvalidOid ||
rettypeid == RECORDOID)
function->fn_retistuple = true;
else
{
function->fn_retbyval = typeStruct->typbyval;
function->fn_rettyplen = typeStruct->typlen;
function->fn_rettypioparam = getTypeIOParam(typeTup);
fmgr_info(typeStruct->typinput, &(function->fn_retinput));
/*
* install $0 reference, but only for polymorphic return
* types, and not when the return is specified through an
* output parameter.
*/
if (IsPolymorphicType(procStruct->prorettype) &&
num_out_args == 0)
{
(void) plpgsql_build_variable("$0", 0,
build_datatype(typeTup,
-1,
function->fn_input_collation),
true);
}
}
ReleaseSysCache(typeTup);
break;
case PLPGSQL_DML_TRIGGER:
/* Trigger procedure's return type is unknown yet */
function->fn_rettype = InvalidOid;
function->fn_retbyval = false;
function->fn_retistuple = true;
function->fn_retset = false;
/* shouldn't be any declared arguments */
if (procStruct->pronargs != 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("trigger functions cannot have declared arguments"),
errhint("The arguments of the trigger can be accessed through TG_NARGS and TG_ARGV instead.")));
/* Add the record for referencing NEW */
rec = plpgsql_build_record("new", 0, true);
function->new_varno = rec->dno;
/* Add the record for referencing OLD */
rec = plpgsql_build_record("old", 0, true);
function->old_varno = rec->dno;
/* Add the variable tg_name */
var = plpgsql_build_variable("tg_name", 0,
plpgsql_build_datatype(NAMEOID,
-1,
InvalidOid),
true);
function->tg_name_varno = var->dno;
/* Add the variable tg_when */
var = plpgsql_build_variable("tg_when", 0,
plpgsql_build_datatype(TEXTOID,
-1,
function->fn_input_collation),
true);
function->tg_when_varno = var->dno;
/* Add the variable tg_level */
var = plpgsql_build_variable("tg_level", 0,
plpgsql_build_datatype(TEXTOID,
-1,
function->fn_input_collation),
true);
function->tg_level_varno = var->dno;
/* Add the variable tg_op */
var = plpgsql_build_variable("tg_op", 0,
plpgsql_build_datatype(TEXTOID,
-1,
function->fn_input_collation),
true);
function->tg_op_varno = var->dno;
/* Add the variable tg_relid */
var = plpgsql_build_variable("tg_relid", 0,
plpgsql_build_datatype(OIDOID,
-1,
InvalidOid),
true);
function->tg_relid_varno = var->dno;
/* Add the variable tg_relname */
var = plpgsql_build_variable("tg_relname", 0,
plpgsql_build_datatype(NAMEOID,
-1,
InvalidOid),
true);
function->tg_relname_varno = var->dno;
/* tg_table_name is now preferred to tg_relname */
var = plpgsql_build_variable("tg_table_name", 0,
plpgsql_build_datatype(NAMEOID,
-1,
InvalidOid),
true);
function->tg_table_name_varno = var->dno;
/* add the variable tg_table_schema */
var = plpgsql_build_variable("tg_table_schema", 0,
plpgsql_build_datatype(NAMEOID,
-1,
InvalidOid),
true);
function->tg_table_schema_varno = var->dno;
/* Add the variable tg_nargs */
var = plpgsql_build_variable("tg_nargs", 0,
plpgsql_build_datatype(INT4OID,
-1,
InvalidOid),
true);
function->tg_nargs_varno = var->dno;
/* Add the variable tg_argv */
var = plpgsql_build_variable("tg_argv", 0,
plpgsql_build_datatype(TEXTARRAYOID,
-1,
function->fn_input_collation),
true);
function->tg_argv_varno = var->dno;
break;
case PLPGSQL_EVENT_TRIGGER:
function->fn_rettype = VOIDOID;
function->fn_retbyval = false;
function->fn_retistuple = true;
function->fn_retset = false;
/* shouldn't be any declared arguments */
if (procStruct->pronargs != 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("event trigger functions cannot have declared arguments")));
/* Add the variable tg_event */
var = plpgsql_build_variable("tg_event", 0,
plpgsql_build_datatype(TEXTOID,
-1,
function->fn_input_collation),
true);
function->tg_event_varno = var->dno;
/* Add the variable tg_tag */
var = plpgsql_build_variable("tg_tag", 0,
plpgsql_build_datatype(TEXTOID,
-1,
function->fn_input_collation),
true);
function->tg_tag_varno = var->dno;
break;
default:
elog(ERROR, "unrecognized function typecode: %d",
(int) function->fn_is_trigger);
break;
}
/* Remember if function is STABLE/IMMUTABLE */
function->fn_readonly = (procStruct->provolatile != PROVOLATILE_VOLATILE);
/*
* Create the magic FOUND variable.
*/
var = plpgsql_build_variable("found", 0,
plpgsql_build_datatype(BOOLOID,
-1,
InvalidOid),
true);
function->found_varno = var->dno;
/*
* Now parse the function's text
*/
parse_rc = plpgsql_yyparse();
if (parse_rc != 0)
elog(ERROR, "plpgsql parser returned %d", parse_rc);
function->action = plpgsql_parse_result;
plpgsql_scanner_finish();
pfree(proc_source);
/*
* If it has OUT parameters or returns VOID or returns a set, we allow
* control to fall off the end without an explicit RETURN statement. The
* easiest way to implement this is to add a RETURN statement to the end
* of the statement list during parsing.
*/
if (num_out_args > 0 || function->fn_rettype == VOIDOID ||
function->fn_retset)
add_dummy_return(function);
/*
* Complete the function's info
*/
function->fn_nargs = procStruct->pronargs;
for (i = 0; i < function->fn_nargs; i++)
function->fn_argvarnos[i] = in_arg_varnos[i];
function->ndatums = plpgsql_nDatums;
function->datums = palloc(sizeof(PLpgSQL_datum *) * plpgsql_nDatums);
for (i = 0; i < plpgsql_nDatums; i++)
function->datums[i] = plpgsql_Datums[i];
/* Debug dump for completed functions */
if (plpgsql_DumpExecTree)
plpgsql_dumptree(function);
/*
* add it to the hash table
*/
plpgsql_HashTableInsert(function, hashkey);
/*
* Pop the error context stack
*/
error_context_stack = plerrcontext.previous;
plpgsql_error_funcname = NULL;
plpgsql_check_syntax = false;
MemoryContextSwitchTo(compile_tmp_cxt);
compile_tmp_cxt = NULL;
return function;
}
| static Node * make_datum_param | ( | PLpgSQL_expr * | expr, | |
| int | dno, | |||
| int | location | |||
| ) | [static] |
Definition at line 1312 of file pl_comp.c.
References Assert, bms_add_member(), PLpgSQL_function::cur_estate, PLpgSQL_execstate::datums, exec_get_datum_type_info(), PLpgSQL_function::fn_cxt, PLpgSQL_expr::func, Param::location, makeNode, MemoryContextSwitchTo(), Param::paramcollid, Param::paramid, Param::paramkind, PLpgSQL_expr::paramnos, Param::paramtype, and Param::paramtypmod.
Referenced by plpgsql_param_ref(), and resolve_column_ref().
{
PLpgSQL_execstate *estate;
PLpgSQL_datum *datum;
Param *param;
MemoryContext oldcontext;
/* see comment in resolve_column_ref */
estate = expr->func->cur_estate;
Assert(dno >= 0 && dno < estate->ndatums);
datum = estate->datums[dno];
/*
* Bitmapset must be allocated in function's permanent memory context
*/
oldcontext = MemoryContextSwitchTo(expr->func->fn_cxt);
expr->paramnos = bms_add_member(expr->paramnos, dno);
MemoryContextSwitchTo(oldcontext);
param = makeNode(Param);
param->paramkind = PARAM_EXTERN;
param->paramid = dno + 1;
exec_get_datum_type_info(estate,
datum,
¶m->paramtype,
¶m->paramtypmod,
¶m->paramcollid);
param->location = location;
return (Node *) param;
}
| int plpgsql_add_initdatums | ( | int ** | varnos | ) |
Definition at line 2326 of file pl_comp.c.
References datums_last, PLpgSQL_datum::dno, i, NULL, palloc(), PLPGSQL_DTYPE_VAR, and plpgsql_nDatums.
{
int i;
int n = 0;
for (i = datums_last; i < plpgsql_nDatums; i++)
{
switch (plpgsql_Datums[i]->dtype)
{
case PLPGSQL_DTYPE_VAR:
n++;
break;
default:
break;
}
}
if (varnos != NULL)
{
if (n > 0)
{
*varnos = (int *) palloc(sizeof(int) * n);
n = 0;
for (i = datums_last; i < plpgsql_nDatums; i++)
{
switch (plpgsql_Datums[i]->dtype)
{
case PLPGSQL_DTYPE_VAR:
(*varnos)[n++] = plpgsql_Datums[i]->dno;
default:
break;
}
}
}
else
*varnos = NULL;
}
datums_last = plpgsql_nDatums;
return n;
}
| void plpgsql_adddatum | ( | PLpgSQL_datum * | new | ) |
Definition at line 2298 of file pl_comp.c.
References datums_alloc, plpgsql_nDatums, and repalloc().
Referenced by do_compile(), plpgsql_build_record(), plpgsql_build_variable(), plpgsql_parse_dblword(), and plpgsql_parse_tripword().
{
if (plpgsql_nDatums == datums_alloc)
{
datums_alloc *= 2;
plpgsql_Datums = repalloc(plpgsql_Datums, sizeof(PLpgSQL_datum *) * datums_alloc);
}
new->dno = plpgsql_nDatums;
plpgsql_Datums[plpgsql_nDatums++] = new;
}
| PLpgSQL_type* plpgsql_build_datatype | ( | Oid | typeOid, | |
| int32 | typmod, | |||
| Oid | collation | |||
| ) |
Definition at line 2131 of file pl_comp.c.
References build_datatype(), elog, ERROR, HeapTupleIsValid, ObjectIdGetDatum, ReleaseSysCache(), SearchSysCache1, and TYPEOID.
Referenced by build_row_from_class(), do_compile(), exec_stmt_case(), plpgsql_compile_inline(), plpgsql_parse_cwordrowtype(), and plpgsql_parse_wordrowtype().
{
HeapTuple typeTup;
PLpgSQL_type *typ;
typeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeOid));
if (!HeapTupleIsValid(typeTup))
elog(ERROR, "cache lookup failed for type %u", typeOid);
typ = build_datatype(typeTup, typmod, collation);
ReleaseSysCache(typeTup);
return typ;
}
| PLpgSQL_rec* plpgsql_build_record | ( | const char * | refname, | |
| int | lineno, | |||
| bool | add2namespace | |||
| ) |
Definition at line 1950 of file pl_comp.c.
References PLpgSQL_datum::dno, PLpgSQL_rec::dtype, PLpgSQL_rec::freetup, PLpgSQL_rec::lineno, palloc0(), plpgsql_adddatum(), plpgsql_ns_additem(), PLPGSQL_NSTYPE_REC, pstrdup(), PLpgSQL_rec::refname, PLpgSQL_rec::tup, and PLpgSQL_rec::tupdesc.
Referenced by do_compile(), and plpgsql_build_variable().
{
PLpgSQL_rec *rec;
rec = palloc0(sizeof(PLpgSQL_rec));
rec->dtype = PLPGSQL_DTYPE_REC;
rec->refname = pstrdup(refname);
rec->lineno = lineno;
rec->tup = NULL;
rec->tupdesc = NULL;
rec->freetup = false;
plpgsql_adddatum((PLpgSQL_datum *) rec);
if (add2namespace)
plpgsql_ns_additem(PLPGSQL_NSTYPE_REC, rec->dno, rec->refname);
return rec;
}
| PLpgSQL_variable* plpgsql_build_variable | ( | const char * | refname, | |
| int | lineno, | |||
| PLpgSQL_type * | dtype, | |||
| bool | add2namespace | |||
| ) |
Definition at line 1870 of file pl_comp.c.
References build_row_from_class(), PLpgSQL_var::datatype, PLpgSQL_datum::dno, PLpgSQL_row::dtype, PLpgSQL_var::dtype, elog, ereport, errcode(), errmsg(), ERROR, format_type_be(), PLpgSQL_var::freeval, PLpgSQL_var::isnull, PLpgSQL_row::lineno, PLpgSQL_var::lineno, palloc0(), plpgsql_adddatum(), plpgsql_build_record(), plpgsql_ns_additem(), PLPGSQL_NSTYPE_ROW, PLPGSQL_NSTYPE_VAR, PLPGSQL_TTYPE_PSEUDO, PLPGSQL_TTYPE_REC, PLPGSQL_TTYPE_ROW, PLPGSQL_TTYPE_SCALAR, pstrdup(), PLpgSQL_row::refname, PLpgSQL_var::refname, PLpgSQL_type::ttype, PLpgSQL_type::typoid, PLpgSQL_type::typrelid, and PLpgSQL_var::value.
Referenced by build_row_from_class(), do_compile(), and plpgsql_compile_inline().
{
PLpgSQL_variable *result;
switch (dtype->ttype)
{
case PLPGSQL_TTYPE_SCALAR:
{
/* Ordinary scalar datatype */
PLpgSQL_var *var;
var = palloc0(sizeof(PLpgSQL_var));
var->dtype = PLPGSQL_DTYPE_VAR;
var->refname = pstrdup(refname);
var->lineno = lineno;
var->datatype = dtype;
/* other fields might be filled by caller */
/* preset to NULL */
var->value = 0;
var->isnull = true;
var->freeval = false;
plpgsql_adddatum((PLpgSQL_datum *) var);
if (add2namespace)
plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR,
var->dno,
refname);
result = (PLpgSQL_variable *) var;
break;
}
case PLPGSQL_TTYPE_ROW:
{
/* Composite type -- build a row variable */
PLpgSQL_row *row;
row = build_row_from_class(dtype->typrelid);
row->dtype = PLPGSQL_DTYPE_ROW;
row->refname = pstrdup(refname);
row->lineno = lineno;
plpgsql_adddatum((PLpgSQL_datum *) row);
if (add2namespace)
plpgsql_ns_additem(PLPGSQL_NSTYPE_ROW,
row->dno,
refname);
result = (PLpgSQL_variable *) row;
break;
}
case PLPGSQL_TTYPE_REC:
{
/* "record" type -- build a record variable */
PLpgSQL_rec *rec;
rec = plpgsql_build_record(refname, lineno, add2namespace);
result = (PLpgSQL_variable *) rec;
break;
}
case PLPGSQL_TTYPE_PSEUDO:
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("variable \"%s\" has pseudo-type %s",
refname, format_type_be(dtype->typoid))));
result = NULL; /* keep compiler quiet */
break;
default:
elog(ERROR, "unrecognized ttype: %d", dtype->ttype);
result = NULL; /* keep compiler quiet */
break;
}
return result;
}
| PLpgSQL_function* plpgsql_compile | ( | FunctionCallInfo | fcinfo, | |
| bool | forValidator | |||
| ) |
Definition at line 132 of file pl_comp.c.
References compute_function_hashkey(), delete_function(), do_compile(), elog, ERROR, FunctionCallInfoData::flinfo, FmgrInfo::fn_extra, FmgrInfo::fn_oid, PLpgSQL_function::fn_tid, PLpgSQL_function::fn_xmin, function, GETSTRUCT, HeapTupleHeaderGetXmin, HeapTupleIsValid, ItemPointerEquals(), ObjectIdGetDatum, plpgsql_HashTableLookup(), PROCOID, ReleaseSysCache(), SearchSysCache1, HeapTupleData::t_data, HeapTupleData::t_self, and PLpgSQL_function::use_count.
Referenced by plpgsql_call_handler(), and plpgsql_validator().
{
Oid funcOid = fcinfo->flinfo->fn_oid;
HeapTuple procTup;
Form_pg_proc procStruct;
PLpgSQL_function *function;
PLpgSQL_func_hashkey hashkey;
bool function_valid = false;
bool hashkey_valid = false;
/*
* Lookup the pg_proc tuple by Oid; we'll need it in any case
*/
procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcOid));
if (!HeapTupleIsValid(procTup))
elog(ERROR, "cache lookup failed for function %u", funcOid);
procStruct = (Form_pg_proc) GETSTRUCT(procTup);
/*
* See if there's already a cache entry for the current FmgrInfo. If not,
* try to find one in the hash table.
*/
function = (PLpgSQL_function *) fcinfo->flinfo->fn_extra;
recheck:
if (!function)
{
/* Compute hashkey using function signature and actual arg types */
compute_function_hashkey(fcinfo, procStruct, &hashkey, forValidator);
hashkey_valid = true;
/* And do the lookup */
function = plpgsql_HashTableLookup(&hashkey);
}
if (function)
{
/* We have a compiled function, but is it still valid? */
if (function->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
ItemPointerEquals(&function->fn_tid, &procTup->t_self))
function_valid = true;
else
{
/*
* Nope, so remove it from hashtable and try to drop associated
* storage (if not done already).
*/
delete_function(function);
/*
* If the function isn't in active use then we can overwrite the
* func struct with new data, allowing any other existing fn_extra
* pointers to make use of the new definition on their next use.
* If it is in use then just leave it alone and make a new one.
* (The active invocations will run to completion using the
* previous definition, and then the cache entry will just be
* leaked; doesn't seem worth adding code to clean it up, given
* what a corner case this is.)
*
* If we found the function struct via fn_extra then it's possible
* a replacement has already been made, so go back and recheck the
* hashtable.
*/
if (function->use_count != 0)
{
function = NULL;
if (!hashkey_valid)
goto recheck;
}
}
}
/*
* If the function wasn't found or was out-of-date, we have to compile it
*/
if (!function_valid)
{
/*
* Calculate hashkey if we didn't already; we'll need it to store the
* completed function.
*/
if (!hashkey_valid)
compute_function_hashkey(fcinfo, procStruct, &hashkey,
forValidator);
/*
* Do the hard part.
*/
function = do_compile(fcinfo, procTup, function,
&hashkey, forValidator);
}
ReleaseSysCache(procTup);
/*
* Save pointer in FmgrInfo to avoid search on subsequent calls
*/
fcinfo->flinfo->fn_extra = (void *) function;
/*
* Finally return the compiled function
*/
return function;
}
| static void plpgsql_compile_error_callback | ( | void * | arg | ) | [static] |
Definition at line 932 of file pl_comp.c.
References errcontext, function_parse_error_transpose(), plpgsql_error_funcname, and plpgsql_latest_lineno().
{
if (arg)
{
/*
* Try to convert syntax error position to reference text of original
* CREATE FUNCTION or DO command.
*/
if (function_parse_error_transpose((const char *) arg))
return;
/*
* Done if a syntax error position was reported; otherwise we have to
* fall back to a "near line N" report.
*/
}
if (plpgsql_error_funcname)
errcontext("compilation of PL/pgSQL function \"%s\" near line %d",
plpgsql_error_funcname, plpgsql_latest_lineno());
}
| PLpgSQL_function* plpgsql_compile_inline | ( | char * | proc_source | ) |
Definition at line 797 of file pl_comp.c.
References PLpgSQL_function::action, add_dummy_return(), ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_DEFAULT_MINSIZE, AllocSetContextCreate(), ErrorContextCallback::arg, BOOLOID, ErrorContextCallback::callback, check_function_bodies, CurrentMemoryContext, PLpgSQL_function::datums, datums_alloc, datums_last, PLpgSQL_variable::dno, elog, ERROR, error_context_stack, fmgr_info(), PLpgSQL_function::fn_cxt, PLpgSQL_function::fn_input_collation, PLpgSQL_function::fn_is_trigger, PLpgSQL_function::fn_nargs, PLpgSQL_function::fn_readonly, PLpgSQL_function::fn_retbyval, PLpgSQL_function::fn_retinput, PLpgSQL_function::fn_retistuple, PLpgSQL_function::fn_retset, PLpgSQL_function::fn_rettype, PLpgSQL_function::fn_rettypioparam, PLpgSQL_function::fn_rettyplen, PLpgSQL_function::fn_signature, PLpgSQL_function::found_varno, function, getTypeInputInfo(), i, InvalidOid, MemoryContextSwitchTo(), PLpgSQL_function::ndatums, NULL, PLpgSQL_function::out_param_varno, palloc(), palloc0(), plpgsql_build_datatype(), plpgsql_build_variable(), plpgsql_check_syntax, plpgsql_DumpExecTree, plpgsql_error_funcname, plpgsql_nDatums, plpgsql_ns_init(), plpgsql_ns_push(), plpgsql_scanner_finish(), plpgsql_scanner_init(), plpgsql_variable_conflict, plpgsql_yyparse(), ErrorContextCallback::previous, pstrdup(), PLpgSQL_function::resolve_option, and VOIDOID.
Referenced by plpgsql_inline_handler().
{
char *func_name = "inline_code_block";
PLpgSQL_function *function;
ErrorContextCallback plerrcontext;
Oid typinput;
PLpgSQL_variable *var;
int parse_rc;
MemoryContext func_cxt;
int i;
/*
* Setup the scanner input and error info. We assume that this function
* cannot be invoked recursively, so there's no need to save and restore
* the static variables used here.
*/
plpgsql_scanner_init(proc_source);
plpgsql_error_funcname = func_name;
/*
* Setup error traceback support for ereport()
*/
plerrcontext.callback = plpgsql_compile_error_callback;
plerrcontext.arg = proc_source;
plerrcontext.previous = error_context_stack;
error_context_stack = &plerrcontext;
/* Do extra syntax checking if check_function_bodies is on */
plpgsql_check_syntax = check_function_bodies;
/* Function struct does not live past current statement */
function = (PLpgSQL_function *) palloc0(sizeof(PLpgSQL_function));
plpgsql_curr_compile = function;
/*
* All the rest of the compile-time storage (e.g. parse tree) is kept in
* its own memory context, so it can be reclaimed easily.
*/
func_cxt = AllocSetContextCreate(CurrentMemoryContext,
"PL/pgSQL function context",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
compile_tmp_cxt = MemoryContextSwitchTo(func_cxt);
function->fn_signature = pstrdup(func_name);
function->fn_is_trigger = PLPGSQL_NOT_TRIGGER;
function->fn_input_collation = InvalidOid;
function->fn_cxt = func_cxt;
function->out_param_varno = -1; /* set up for no OUT param */
function->resolve_option = plpgsql_variable_conflict;
plpgsql_ns_init();
plpgsql_ns_push(func_name);
plpgsql_DumpExecTree = false;
datums_alloc = 128;
plpgsql_nDatums = 0;
plpgsql_Datums = palloc(sizeof(PLpgSQL_datum *) * datums_alloc);
datums_last = 0;
/* Set up as though in a function returning VOID */
function->fn_rettype = VOIDOID;
function->fn_retset = false;
function->fn_retistuple = false;
/* a bit of hardwired knowledge about type VOID here */
function->fn_retbyval = true;
function->fn_rettyplen = sizeof(int32);
getTypeInputInfo(VOIDOID, &typinput, &function->fn_rettypioparam);
fmgr_info(typinput, &(function->fn_retinput));
/*
* Remember if function is STABLE/IMMUTABLE. XXX would it be better to
* set this TRUE inside a read-only transaction? Not clear.
*/
function->fn_readonly = false;
/*
* Create the magic FOUND variable.
*/
var = plpgsql_build_variable("found", 0,
plpgsql_build_datatype(BOOLOID,
-1,
InvalidOid),
true);
function->found_varno = var->dno;
/*
* Now parse the function's text
*/
parse_rc = plpgsql_yyparse();
if (parse_rc != 0)
elog(ERROR, "plpgsql parser returned %d", parse_rc);
function->action = plpgsql_parse_result;
plpgsql_scanner_finish();
/*
* If it returns VOID (always true at the moment), we allow control to
* fall off the end without an explicit RETURN statement.
*/
if (function->fn_rettype == VOIDOID)
add_dummy_return(function);
/*
* Complete the function's info
*/
function->fn_nargs = 0;
function->ndatums = plpgsql_nDatums;
function->datums = palloc(sizeof(PLpgSQL_datum *) * plpgsql_nDatums);
for (i = 0; i < plpgsql_nDatums; i++)
function->datums[i] = plpgsql_Datums[i];
/*
* Pop the error context stack
*/
error_context_stack = plerrcontext.previous;
plpgsql_error_funcname = NULL;
plpgsql_check_syntax = false;
MemoryContextSwitchTo(compile_tmp_cxt);
compile_tmp_cxt = NULL;
return function;
}
| static void plpgsql_HashTableDelete | ( | PLpgSQL_function * | function | ) | [static] |
Definition at line 2552 of file pl_comp.c.
References elog, PLpgSQL_function::fn_hashkey, hash_search(), NULL, and WARNING.
Referenced by delete_function().
{
plpgsql_HashEnt *hentry;
/* do nothing if not in table */
if (function->fn_hashkey == NULL)
return;
hentry = (plpgsql_HashEnt *) hash_search(plpgsql_HashTable,
(void *) function->fn_hashkey,
HASH_REMOVE,
NULL);
if (hentry == NULL)
elog(WARNING, "trying to delete function that does not exist");
/* remove back link, which no longer points to allocated storage */
function->fn_hashkey = NULL;
}
| void plpgsql_HashTableInit | ( | void | ) |
Definition at line 2500 of file pl_comp.c.
References Assert, HASHCTL::entrysize, FUNCS_PER_USER, HASHCTL::hash, hash_create(), HASH_ELEM, HASH_FUNCTION, HASHCTL::keysize, and NULL.
Referenced by _PG_init().
{
HASHCTL ctl;
/* don't allow double-initialization */
Assert(plpgsql_HashTable == NULL);
memset(&ctl, 0, sizeof(ctl));
ctl.keysize = sizeof(PLpgSQL_func_hashkey);
ctl.entrysize = sizeof(plpgsql_HashEnt);
ctl.hash = tag_hash;
plpgsql_HashTable = hash_create("PLpgSQL function cache",
FUNCS_PER_USER,
&ctl,
HASH_ELEM | HASH_FUNCTION);
}
| static void plpgsql_HashTableInsert | ( | PLpgSQL_function * | function, | |
| PLpgSQL_func_hashkey * | func_key | |||
| ) | [static] |
Definition at line 2533 of file pl_comp.c.
References elog, PLpgSQL_function::fn_hashkey, plpgsql_hashent::function, hash_search(), plpgsql_hashent::key, and WARNING.
Referenced by do_compile().
{
plpgsql_HashEnt *hentry;
bool found;
hentry = (plpgsql_HashEnt *) hash_search(plpgsql_HashTable,
(void *) func_key,
HASH_ENTER,
&found);
if (found)
elog(WARNING, "trying to insert a function that already exists");
hentry->function = function;
/* prepare back link from function to hashtable key */
function->fn_hashkey = &hentry->key;
}
| static PLpgSQL_function * plpgsql_HashTableLookup | ( | PLpgSQL_func_hashkey * | func_key | ) | [static] |
Definition at line 2518 of file pl_comp.c.
References plpgsql_hashent::function, and hash_search().
Referenced by plpgsql_compile().
{
plpgsql_HashEnt *hentry;
hentry = (plpgsql_HashEnt *) hash_search(plpgsql_HashTable,
(void *) func_key,
HASH_FIND,
NULL);
if (hentry)
return hentry->function;
else
return NULL;
}
| static Node * plpgsql_param_ref | ( | ParseState * | pstate, | |
| ParamRef * | pref | |||
| ) | [static] |
Definition at line 1096 of file pl_comp.c.
References ParamRef::location, make_datum_param(), PLpgSQL_expr::ns, NULL, ParamRef::number, ParseState::p_ref_hook_state, plpgsql_ns_lookup(), and snprintf().
{
PLpgSQL_expr *expr = (PLpgSQL_expr *) pstate->p_ref_hook_state;
char pname[32];
PLpgSQL_nsitem *nse;
snprintf(pname, sizeof(pname), "$%d", pref->number);
nse = plpgsql_ns_lookup(expr->ns, false,
pname, NULL, NULL,
NULL);
if (nse == NULL)
return NULL; /* name not known to plpgsql */
return make_datum_param(expr, nse->itemno, pref->location);
}
| PLpgSQL_type* plpgsql_parse_cwordrowtype | ( | List * | idents | ) |
Definition at line 1836 of file pl_comp.c.
References get_rel_type_id(), InvalidOid, linitial, list_length(), lsecond, makeRangeVar(), MemoryContextSwitchTo(), NoLock, plpgsql_build_datatype(), RangeVarGetRelid, and strVal.
{
Oid classOid;
RangeVar *relvar;
MemoryContext oldCxt;
if (list_length(idents) != 2)
return NULL;
/* Avoid memory leaks in long-term function context */
oldCxt = MemoryContextSwitchTo(compile_tmp_cxt);
/* Look up relation name. Can't lock it - we might not have privileges. */
relvar = makeRangeVar(strVal(linitial(idents)),
strVal(lsecond(idents)),
-1);
classOid = RangeVarGetRelid(relvar, NoLock, false);
MemoryContextSwitchTo(oldCxt);
/* Build and return the row type struct */
return plpgsql_build_datatype(get_rel_type_id(classOid), -1, InvalidOid);
}
| PLpgSQL_type* plpgsql_parse_cwordtype | ( | List * | idents | ) |
Definition at line 1698 of file pl_comp.c.
References build_datatype(), elog, ERROR, GETSTRUCT, HeapTupleIsValid, PLpgSQL_nsitem::itemno, PLpgSQL_nsitem::itemtype, linitial, list_length(), lsecond, lthird, makeRangeVar(), MemoryContextSwitchTo(), NoLock, NULL, ObjectIdGetDatum, OidIsValid, plpgsql_ns_lookup(), plpgsql_ns_top(), PLPGSQL_NSTYPE_VAR, RangeVarGetRelid, ReleaseSysCache(), RELKIND_COMPOSITE_TYPE, RELKIND_FOREIGN_TABLE, RELKIND_MATVIEW, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RelnameGetRelid(), RELOID, SearchSysCache1, SearchSysCacheAttName(), strVal, and TYPEOID.
{
PLpgSQL_type *dtype = NULL;
PLpgSQL_nsitem *nse;
const char *fldname;
Oid classOid;
HeapTuple classtup = NULL;
HeapTuple attrtup = NULL;
HeapTuple typetup = NULL;
Form_pg_class classStruct;
Form_pg_attribute attrStruct;
MemoryContext oldCxt;
/* Avoid memory leaks in the long-term function context */
oldCxt = MemoryContextSwitchTo(compile_tmp_cxt);
if (list_length(idents) == 2)
{
/*
* Do a lookup in the current namespace stack. We don't need to check
* number of names matched, because we will only consider scalar
* variables.
*/
nse = plpgsql_ns_lookup(plpgsql_ns_top(), false,
strVal(linitial(idents)),
strVal(lsecond(idents)),
NULL,
NULL);
if (nse != NULL && nse->itemtype == PLPGSQL_NSTYPE_VAR)
{
dtype = ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype;
goto done;
}
/*
* First word could also be a table name
*/
classOid = RelnameGetRelid(strVal(linitial(idents)));
if (!OidIsValid(classOid))
goto done;
fldname = strVal(lsecond(idents));
}
else if (list_length(idents) == 3)
{
RangeVar *relvar;
relvar = makeRangeVar(strVal(linitial(idents)),
strVal(lsecond(idents)),
-1);
/* Can't lock relation - we might not have privileges. */
classOid = RangeVarGetRelid(relvar, NoLock, true);
if (!OidIsValid(classOid))
goto done;
fldname = strVal(lthird(idents));
}
else
goto done;
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(classOid));
if (!HeapTupleIsValid(classtup))
goto done;
classStruct = (Form_pg_class) GETSTRUCT(classtup);
/*
* It must be a relation, sequence, view, materialized view, composite
* type, or foreign table
*/
if (classStruct->relkind != RELKIND_RELATION &&
classStruct->relkind != RELKIND_SEQUENCE &&
classStruct->relkind != RELKIND_VIEW &&
classStruct->relkind != RELKIND_MATVIEW &&
classStruct->relkind != RELKIND_COMPOSITE_TYPE &&
classStruct->relkind != RELKIND_FOREIGN_TABLE)
goto done;
/*
* Fetch the named table field and its type
*/
attrtup = SearchSysCacheAttName(classOid, fldname);
if (!HeapTupleIsValid(attrtup))
goto done;
attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup);
typetup = SearchSysCache1(TYPEOID,
ObjectIdGetDatum(attrStruct->atttypid));
if (!HeapTupleIsValid(typetup))
elog(ERROR, "cache lookup failed for type %u", attrStruct->atttypid);
/*
* Found that - build a compiler type struct in the caller's cxt and
* return it
*/
MemoryContextSwitchTo(oldCxt);
dtype = build_datatype(typetup,
attrStruct->atttypmod,
attrStruct->attcollation);
MemoryContextSwitchTo(compile_tmp_cxt);
done:
if (HeapTupleIsValid(classtup))
ReleaseSysCache(classtup);
if (HeapTupleIsValid(attrtup))
ReleaseSysCache(attrtup);
if (HeapTupleIsValid(typetup))
ReleaseSysCache(typetup);
MemoryContextSwitchTo(oldCxt);
return dtype;
}
Definition at line 1418 of file pl_comp.c.
References PLwdatum::datum, PLpgSQL_row::fieldnames, i, PLwdatum::ident, IDENTIFIER_LOOKUP_DECLARE, PLcword::idents, PLwdatum::idents, PLpgSQL_nsitem::itemno, PLpgSQL_nsitem::itemtype, list_make2, makeString(), PLpgSQL_row::nfields, NULL, palloc(), plpgsql_adddatum(), plpgsql_IdentifierLookup, plpgsql_ns_lookup(), plpgsql_ns_top(), PLPGSQL_NSTYPE_REC, PLPGSQL_NSTYPE_ROW, PLPGSQL_NSTYPE_VAR, pstrdup(), PLwdatum::quoted, and PLpgSQL_row::varnos.
Referenced by plpgsql_yylex().
{
PLpgSQL_nsitem *ns;
List *idents;
int nnames;
idents = list_make2(makeString(word1),
makeString(word2));
/*
* We should do nothing in DECLARE sections. In SQL expressions, we
* really only need to make sure that RECFIELD datums are created when
* needed.
*/
if (plpgsql_IdentifierLookup != IDENTIFIER_LOOKUP_DECLARE)
{
/*
* Do a lookup in the current namespace stack
*/
ns = plpgsql_ns_lookup(plpgsql_ns_top(), false,
word1, word2, NULL,
&nnames);
if (ns != NULL)
{
switch (ns->itemtype)
{
case PLPGSQL_NSTYPE_VAR:
/* Block-qualified reference to scalar variable. */
wdatum->datum = plpgsql_Datums[ns->itemno];
wdatum->ident = NULL;
wdatum->quoted = false; /* not used */
wdatum->idents = idents;
return true;
case PLPGSQL_NSTYPE_REC:
if (nnames == 1)
{
/*
* First word is a record name, so second word could
* be a field in this record. We build a RECFIELD
* datum whether it is or not --- any error will be
* detected later.
*/
PLpgSQL_recfield *new;
new = palloc(sizeof(PLpgSQL_recfield));
new->dtype = PLPGSQL_DTYPE_RECFIELD;
new->fieldname = pstrdup(word2);
new->recparentno = ns->itemno;
plpgsql_adddatum((PLpgSQL_datum *) new);
wdatum->datum = (PLpgSQL_datum *) new;
}
else
{
/* Block-qualified reference to record variable. */
wdatum->datum = plpgsql_Datums[ns->itemno];
}
wdatum->ident = NULL;
wdatum->quoted = false; /* not used */
wdatum->idents = idents;
return true;
case PLPGSQL_NSTYPE_ROW:
if (nnames == 1)
{
/*
* First word is a row name, so second word could be a
* field in this row. Again, no error now if it
* isn't.
*/
PLpgSQL_row *row;
int i;
row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]);
for (i = 0; i < row->nfields; i++)
{
if (row->fieldnames[i] &&
strcmp(row->fieldnames[i], word2) == 0)
{
wdatum->datum = plpgsql_Datums[row->varnos[i]];
wdatum->ident = NULL;
wdatum->quoted = false; /* not used */
wdatum->idents = idents;
return true;
}
}
/* fall through to return CWORD */
}
else
{
/* Block-qualified reference to row variable. */
wdatum->datum = plpgsql_Datums[ns->itemno];
wdatum->ident = NULL;
wdatum->quoted = false; /* not used */
wdatum->idents = idents;
return true;
}
break;
default:
break;
}
}
}
/* Nothing found */
cword->idents = idents;
return false;
}
| PLpgSQL_condition* plpgsql_parse_err_condition | ( | char * | condname | ) |
Definition at line 2246 of file pl_comp.c.
References ereport, errcode(), errmsg(), ERROR, i, label, ExceptionLabelMap::label, palloc(), and ExceptionLabelMap::sqlerrstate.
{
int i;
PLpgSQL_condition *new;
PLpgSQL_condition *prev;
/*
* XXX Eventually we will want to look for user-defined exception names
* here.
*/
/*
* OTHERS is represented as code 0 (which would map to '00000', but we
* have no need to represent that as an exception condition).
*/
if (strcmp(condname, "others") == 0)
{
new = palloc(sizeof(PLpgSQL_condition));
new->sqlerrstate = 0;
new->condname = condname;
new->next = NULL;
return new;
}
prev = NULL;
for (i = 0; exception_label_map[i].label != NULL; i++)
{
if (strcmp(condname, exception_label_map[i].label) == 0)
{
new = palloc(sizeof(PLpgSQL_condition));
new->sqlerrstate = exception_label_map[i].sqlerrstate;
new->condname = condname;
new->next = prev;
prev = new;
}
}
if (!prev)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("unrecognized exception condition \"%s\"",
condname)));
return prev;
}
| bool plpgsql_parse_tripword | ( | char * | word1, | |
| char * | word2, | |||
| char * | word3, | |||
| PLwdatum * | wdatum, | |||
| PLcword * | cword | |||
| ) |
Definition at line 1538 of file pl_comp.c.
References PLwdatum::datum, PLpgSQL_row::fieldnames, i, PLwdatum::ident, IDENTIFIER_LOOKUP_DECLARE, PLcword::idents, PLwdatum::idents, PLpgSQL_nsitem::itemno, PLpgSQL_nsitem::itemtype, list_make3, makeString(), PLpgSQL_row::nfields, NULL, palloc(), plpgsql_adddatum(), plpgsql_IdentifierLookup, plpgsql_ns_lookup(), plpgsql_ns_top(), PLPGSQL_NSTYPE_REC, PLPGSQL_NSTYPE_ROW, pstrdup(), PLwdatum::quoted, and PLpgSQL_row::varnos.
Referenced by plpgsql_yylex().
{
PLpgSQL_nsitem *ns;
List *idents;
int nnames;
idents = list_make3(makeString(word1),
makeString(word2),
makeString(word3));
/*
* We should do nothing in DECLARE sections. In SQL expressions, we
* really only need to make sure that RECFIELD datums are created when
* needed.
*/
if (plpgsql_IdentifierLookup != IDENTIFIER_LOOKUP_DECLARE)
{
/*
* Do a lookup in the current namespace stack. Must find a qualified
* reference, else ignore.
*/
ns = plpgsql_ns_lookup(plpgsql_ns_top(), false,
word1, word2, word3,
&nnames);
if (ns != NULL && nnames == 2)
{
switch (ns->itemtype)
{
case PLPGSQL_NSTYPE_REC:
{
/*
* words 1/2 are a record name, so third word could be
* a field in this record.
*/
PLpgSQL_recfield *new;
new = palloc(sizeof(PLpgSQL_recfield));
new->dtype = PLPGSQL_DTYPE_RECFIELD;
new->fieldname = pstrdup(word3);
new->recparentno = ns->itemno;
plpgsql_adddatum((PLpgSQL_datum *) new);
wdatum->datum = (PLpgSQL_datum *) new;
wdatum->ident = NULL;
wdatum->quoted = false; /* not used */
wdatum->idents = idents;
return true;
}
case PLPGSQL_NSTYPE_ROW:
{
/*
* words 1/2 are a row name, so third word could be a
* field in this row.
*/
PLpgSQL_row *row;
int i;
row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]);
for (i = 0; i < row->nfields; i++)
{
if (row->fieldnames[i] &&
strcmp(row->fieldnames[i], word3) == 0)
{
wdatum->datum = plpgsql_Datums[row->varnos[i]];
wdatum->ident = NULL;
wdatum->quoted = false; /* not used */
wdatum->idents = idents;
return true;
}
}
/* fall through to return CWORD */
break;
}
default:
break;
}
}
}
/* Nothing found */
cword->idents = idents;
return false;
}
Definition at line 1362 of file pl_comp.c.
References PLwdatum::datum, elog, ERROR, PLword::ident, PLwdatum::ident, IDENTIFIER_LOOKUP_NORMAL, PLwdatum::idents, PLpgSQL_nsitem::itemno, PLpgSQL_nsitem::itemtype, NULL, plpgsql_IdentifierLookup, plpgsql_ns_lookup(), plpgsql_ns_top(), PLPGSQL_NSTYPE_REC, PLPGSQL_NSTYPE_ROW, PLPGSQL_NSTYPE_VAR, PLword::quoted, and PLwdatum::quoted.
Referenced by plpgsql_yylex().
{
PLpgSQL_nsitem *ns;
/*
* We should do nothing in DECLARE sections. In SQL expressions, there's
* no need to do anything either --- lookup will happen when the
* expression is compiled.
*/
if (plpgsql_IdentifierLookup == IDENTIFIER_LOOKUP_NORMAL)
{
/*
* Do a lookup in the current namespace stack
*/
ns = plpgsql_ns_lookup(plpgsql_ns_top(), false,
word1, NULL, NULL,
NULL);
if (ns != NULL)
{
switch (ns->itemtype)
{
case PLPGSQL_NSTYPE_VAR:
case PLPGSQL_NSTYPE_ROW:
case PLPGSQL_NSTYPE_REC:
wdatum->datum = plpgsql_Datums[ns->itemno];
wdatum->ident = word1;
wdatum->quoted = (yytxt[0] == '"');
wdatum->idents = NIL;
return true;
default:
/* plpgsql_ns_lookup should never return anything else */
elog(ERROR, "unrecognized plpgsql itemtype: %d",
ns->itemtype);
}
}
}
/*
* Nothing found - up to now it's a word without any special meaning for
* us.
*/
word->ident = word1;
word->quoted = (yytxt[0] == '"');
return false;
}
| PLpgSQL_type* plpgsql_parse_wordrowtype | ( | char * | ident | ) |
Definition at line 1815 of file pl_comp.c.
References ereport, errcode(), errmsg(), ERROR, get_rel_type_id(), InvalidOid, OidIsValid, plpgsql_build_datatype(), and RelnameGetRelid().
{
Oid classOid;
/* Lookup the relation */
classOid = RelnameGetRelid(ident);
if (!OidIsValid(classOid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_TABLE),
errmsg("relation \"%s\" does not exist", ident)));
/* Build and return the row type struct */
return plpgsql_build_datatype(get_rel_type_id(classOid), -1, InvalidOid);
}
| PLpgSQL_type* plpgsql_parse_wordtype | ( | char * | ident | ) |
Definition at line 1635 of file pl_comp.c.
References build_datatype(), PLpgSQL_function::fn_input_collation, GETSTRUCT, InvalidOid, PLpgSQL_nsitem::itemno, PLpgSQL_nsitem::itemtype, LookupTypeName(), makeTypeName(), NULL, plpgsql_ns_lookup(), plpgsql_ns_top(), PLPGSQL_NSTYPE_VAR, and ReleaseSysCache().
{
PLpgSQL_type *dtype;
PLpgSQL_nsitem *nse;
HeapTuple typeTup;
/*
* Do a lookup in the current namespace stack
*/
nse = plpgsql_ns_lookup(plpgsql_ns_top(), false,
ident, NULL, NULL,
NULL);
if (nse != NULL)
{
switch (nse->itemtype)
{
case PLPGSQL_NSTYPE_VAR:
return ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype;
/* XXX perhaps allow REC/ROW here? */
default:
return NULL;
}
}
/*
* Word wasn't found in the namespace stack. Try to find a data type with
* that name, but ignore shell types and complex types.
*/
typeTup = LookupTypeName(NULL, makeTypeName(ident), NULL);
if (typeTup)
{
Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
if (!typeStruct->typisdefined ||
typeStruct->typrelid != InvalidOid)
{
ReleaseSysCache(typeTup);
return NULL;
}
dtype = build_datatype(typeTup, -1,
plpgsql_curr_compile->fn_input_collation);
ReleaseSysCache(typeTup);
return dtype;
}
/*
* Nothing found - up to now it's a word without any special meaning for
* us.
*/
return NULL;
}
| void plpgsql_parser_setup | ( | struct ParseState * | pstate, | |
| PLpgSQL_expr * | expr | |||
| ) |
Definition at line 1025 of file pl_comp.c.
References ParseState::p_paramref_hook, ParseState::p_post_columnref_hook, ParseState::p_pre_columnref_hook, and ParseState::p_ref_hook_state.
Referenced by exec_prepare_plan(), and setup_param_list().
{
pstate->p_pre_columnref_hook = plpgsql_pre_column_ref;
pstate->p_post_columnref_hook = plpgsql_post_column_ref;
pstate->p_paramref_hook = plpgsql_param_ref;
/* no need to use p_coerce_param_hook */
pstate->p_ref_hook_state = (void *) expr;
}
| static Node * plpgsql_post_column_ref | ( | ParseState * | pstate, | |
| ColumnRef * | cref, | |||
| Node * | var | |||
| ) | [static] |
Definition at line 1052 of file pl_comp.c.
References ereport, errcode(), errdetail(), errmsg(), ERROR, ColumnRef::fields, PLpgSQL_expr::func, ColumnRef::location, NameListToString(), NULL, ParseState::p_ref_hook_state, parser_errposition(), PLPGSQL_RESOLVE_COLUMN, PLPGSQL_RESOLVE_VARIABLE, resolve_column_ref(), and PLpgSQL_function::resolve_option.
{
PLpgSQL_expr *expr = (PLpgSQL_expr *) pstate->p_ref_hook_state;
Node *myvar;
if (expr->func->resolve_option == PLPGSQL_RESOLVE_VARIABLE)
return NULL; /* we already found there's no match */
if (expr->func->resolve_option == PLPGSQL_RESOLVE_COLUMN && var != NULL)
return NULL; /* there's a table column, prefer that */
/*
* If we find a record/row variable but can't match a field name, throw
* error if there was no core resolution for the ColumnRef either. In
* that situation, the reference is inevitably going to fail, and
* complaining about the record/row variable is likely to be more on-point
* than the core parser's error message. (It's too bad we don't have
* access to transformColumnRef's internal crerr state here, as in case of
* a conflict with a table name this could still be less than the most
* helpful error message possible.)
*/
myvar = resolve_column_ref(pstate, expr, cref, (var == NULL));
if (myvar != NULL && var != NULL)
{
/*
* We could leave it to the core parser to throw this error, but we
* can add a more useful detail message than the core could.
*/
ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_COLUMN),
errmsg("column reference \"%s\" is ambiguous",
NameListToString(cref->fields)),
errdetail("It could refer to either a PL/pgSQL variable or a table column."),
parser_errposition(pstate, cref->location)));
}
return myvar;
}
| static Node * plpgsql_pre_column_ref | ( | ParseState * | pstate, | |
| ColumnRef * | cref | |||
| ) | [static] |
Definition at line 1038 of file pl_comp.c.
References PLpgSQL_expr::func, ParseState::p_ref_hook_state, PLPGSQL_RESOLVE_VARIABLE, resolve_column_ref(), and PLpgSQL_function::resolve_option.
{
PLpgSQL_expr *expr = (PLpgSQL_expr *) pstate->p_ref_hook_state;
if (expr->func->resolve_option == PLPGSQL_RESOLVE_VARIABLE)
return resolve_column_ref(pstate, expr, cref, false);
else
return NULL;
}
| int plpgsql_recognize_err_condition | ( | const char * | condname, | |
| bool | allow_sqlstate | |||
| ) |
Definition at line 2210 of file pl_comp.c.
References ereport, errcode(), errmsg(), ERROR, i, label, ExceptionLabelMap::label, MAKE_SQLSTATE, and ExceptionLabelMap::sqlerrstate.
Referenced by exec_stmt_raise().
{
int i;
if (allow_sqlstate)
{
if (strlen(condname) == 5 &&
strspn(condname, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") == 5)
return MAKE_SQLSTATE(condname[0],
condname[1],
condname[2],
condname[3],
condname[4]);
}
for (i = 0; exception_label_map[i].label != NULL; i++)
{
if (strcmp(condname, exception_label_map[i].label) == 0)
return exception_label_map[i].sqlerrstate;
}
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("unrecognized exception condition \"%s\"",
condname)));
return 0; /* keep compiler quiet */
}
| static void plpgsql_resolve_polymorphic_argtypes | ( | int | numargs, | |
| Oid * | argtypes, | |||
| char * | argmodes, | |||
| Node * | call_expr, | |||
| bool | forValidator, | |||
| const char * | proname | |||
| ) | [static] |
Definition at line 2430 of file pl_comp.c.
References ANYARRAYOID, ANYELEMENTOID, ANYENUMOID, ANYNONARRAYOID, ANYRANGEOID, ereport, errcode(), errmsg(), ERROR, i, and resolve_polymorphic_argtypes().
Referenced by compute_function_hashkey(), and do_compile().
{
int i;
if (!forValidator)
{
/* normal case, pass to standard routine */
if (!resolve_polymorphic_argtypes(numargs, argtypes, argmodes,
call_expr))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("could not determine actual argument "
"type for polymorphic function \"%s\"",
proname)));
}
else
{
/* special validation case */
for (i = 0; i < numargs; i++)
{
switch (argtypes[i])
{
case ANYELEMENTOID:
case ANYNONARRAYOID:
case ANYENUMOID: /* XXX dubious */
argtypes[i] = INT4OID;
break;
case ANYARRAYOID:
argtypes[i] = INT4ARRAYOID;
break;
case ANYRANGEOID:
argtypes[i] = INT4RANGEOID;
break;
default:
break;
}
}
}
}
| static Node * resolve_column_ref | ( | ParseState * | pstate, | |
| PLpgSQL_expr * | expr, | |||
| ColumnRef * | cref, | |||
| bool | error_if_no_field | |||
| ) | [static] |
Definition at line 1123 of file pl_comp.c.
References Assert, PLpgSQL_function::cur_estate, PLpgSQL_execstate::datums, PLpgSQL_recfield::dtype, elog, ereport, errcode(), errmsg(), ERROR, PLpgSQL_recfield::fieldname, PLpgSQL_row::fieldnames, ColumnRef::fields, PLpgSQL_expr::func, i, IsA, PLpgSQL_nsitem::itemno, PLpgSQL_nsitem::itemtype, linitial, list_length(), ColumnRef::location, lsecond, lthird, make_datum_param(), PLpgSQL_execstate::ndatums, PLpgSQL_row::nfields, PLpgSQL_expr::ns, NULL, parser_errposition(), PLPGSQL_DTYPE_RECFIELD, plpgsql_ns_lookup(), PLPGSQL_NSTYPE_REC, PLPGSQL_NSTYPE_ROW, PLPGSQL_NSTYPE_VAR, PLpgSQL_recfield::recparentno, strVal, and PLpgSQL_row::varnos.
Referenced by plpgsql_post_column_ref(), and plpgsql_pre_column_ref().
{
PLpgSQL_execstate *estate;
PLpgSQL_nsitem *nse;
const char *name1;
const char *name2 = NULL;
const char *name3 = NULL;
const char *colname = NULL;
int nnames;
int nnames_scalar = 0;
int nnames_wholerow = 0;
int nnames_field = 0;
/*
* We use the function's current estate to resolve parameter data types.
* This is really pretty bogus because there is no provision for updating
* plans when those types change ...
*/
estate = expr->func->cur_estate;
/*----------
* The allowed syntaxes are:
*
* A Scalar variable reference, or whole-row record reference.
* A.B Qualified scalar or whole-row reference, or field reference.
* A.B.C Qualified record field reference.
* A.* Whole-row record reference.
* A.B.* Qualified whole-row record reference.
*----------
*/
switch (list_length(cref->fields))
{
case 1:
{
Node *field1 = (Node *) linitial(cref->fields);
Assert(IsA(field1, String));
name1 = strVal(field1);
nnames_scalar = 1;
nnames_wholerow = 1;
break;
}
case 2:
{
Node *field1 = (Node *) linitial(cref->fields);
Node *field2 = (Node *) lsecond(cref->fields);
Assert(IsA(field1, String));
name1 = strVal(field1);
/* Whole-row reference? */
if (IsA(field2, A_Star))
{
/* Set name2 to prevent matches to scalar variables */
name2 = "*";
nnames_wholerow = 1;
break;
}
Assert(IsA(field2, String));
name2 = strVal(field2);
colname = name2;
nnames_scalar = 2;
nnames_wholerow = 2;
nnames_field = 1;
break;
}
case 3:
{
Node *field1 = (Node *) linitial(cref->fields);
Node *field2 = (Node *) lsecond(cref->fields);
Node *field3 = (Node *) lthird(cref->fields);
Assert(IsA(field1, String));
name1 = strVal(field1);
Assert(IsA(field2, String));
name2 = strVal(field2);
/* Whole-row reference? */
if (IsA(field3, A_Star))
{
/* Set name3 to prevent matches to scalar variables */
name3 = "*";
nnames_wholerow = 2;
break;
}
Assert(IsA(field3, String));
name3 = strVal(field3);
colname = name3;
nnames_field = 2;
break;
}
default:
/* too many names, ignore */
return NULL;
}
nse = plpgsql_ns_lookup(expr->ns, false,
name1, name2, name3,
&nnames);
if (nse == NULL)
return NULL; /* name not known to plpgsql */
switch (nse->itemtype)
{
case PLPGSQL_NSTYPE_VAR:
if (nnames == nnames_scalar)
return make_datum_param(expr, nse->itemno, cref->location);
break;
case PLPGSQL_NSTYPE_REC:
if (nnames == nnames_wholerow)
return make_datum_param(expr, nse->itemno, cref->location);
if (nnames == nnames_field)
{
/* colname could be a field in this record */
int i;
/* search for a datum referencing this field */
for (i = 0; i < estate->ndatums; i++)
{
PLpgSQL_recfield *fld = (PLpgSQL_recfield *) estate->datums[i];
if (fld->dtype == PLPGSQL_DTYPE_RECFIELD &&
fld->recparentno == nse->itemno &&
strcmp(fld->fieldname, colname) == 0)
{
return make_datum_param(expr, i, cref->location);
}
}
/*
* We should not get here, because a RECFIELD datum should
* have been built at parse time for every possible qualified
* reference to fields of this record. But if we do, handle
* it like field-not-found: throw error or return NULL.
*/
if (error_if_no_field)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("record \"%s\" has no field \"%s\"",
(nnames_field == 1) ? name1 : name2,
colname),
parser_errposition(pstate, cref->location)));
}
break;
case PLPGSQL_NSTYPE_ROW:
if (nnames == nnames_wholerow)
return make_datum_param(expr, nse->itemno, cref->location);
if (nnames == nnames_field)
{
/* colname could be a field in this row */
PLpgSQL_row *row = (PLpgSQL_row *) estate->datums[nse->itemno];
int i;
for (i = 0; i < row->nfields; i++)
{
if (row->fieldnames[i] &&
strcmp(row->fieldnames[i], colname) == 0)
{
return make_datum_param(expr, row->varnos[i],
cref->location);
}
}
/* Not found, so throw error or return NULL */
if (error_if_no_field)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("record \"%s\" has no field \"%s\"",
(nnames_field == 1) ? name1 : name2,
colname),
parser_errposition(pstate, cref->location)));
}
break;
default:
elog(ERROR, "unrecognized plpgsql itemtype: %d", nse->itemtype);
}
/* Name format doesn't match the plpgsql variable type */
return NULL;
}
int datums_alloc [static] |
Definition at line 42 of file pl_comp.c.
Referenced by do_compile(), plpgsql_adddatum(), and plpgsql_compile_inline().
int datums_last = 0 [static] |
Definition at line 45 of file pl_comp.c.
Referenced by do_compile(), plpgsql_add_initdatums(), and plpgsql_compile_inline().
const ExceptionLabelMap exception_label_map[] [static] |
| bool plpgsql_check_syntax = false |
Definition at line 49 of file pl_comp.c.
Referenced by do_compile(), and plpgsql_compile_inline().
| bool plpgsql_DumpExecTree = false |
Definition at line 48 of file pl_comp.c.
Referenced by do_compile(), and plpgsql_compile_inline().
| char* plpgsql_error_funcname |
Definition at line 47 of file pl_comp.c.
Referenced by do_compile(), plpgsql_compile_error_callback(), and plpgsql_compile_inline().
HTAB* plpgsql_HashTable = NULL [static] |
| int plpgsql_nDatums |
Definition at line 43 of file pl_comp.c.
Referenced by do_compile(), plpgsql_add_initdatums(), plpgsql_adddatum(), and plpgsql_compile_inline().
1.7.1