pl_exec.c

来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 2,523 行 · 第 1/5 页

C
2,523
字号
	{		rec_new->tup = trigdata->tg_trigtuple;		rec_new->tupdesc = trigdata->tg_relation->rd_att;		rec_old->tup = NULL;		rec_old->tupdesc = NULL;	}	else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))	{		rec_new->tup = trigdata->tg_newtuple;		rec_new->tupdesc = trigdata->tg_relation->rd_att;		rec_old->tup = trigdata->tg_trigtuple;		rec_old->tupdesc = trigdata->tg_relation->rd_att;	}	else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))	{		rec_new->tup = NULL;		rec_new->tupdesc = NULL;		rec_old->tup = trigdata->tg_trigtuple;		rec_old->tupdesc = trigdata->tg_relation->rd_att;	}	else		elog(ERROR, "unrecognized trigger action: not INSERT, DELETE, or UPDATE");	/*	 * Assign the special tg_ variables	 */	var = (PLpgSQL_var *) (estate.datums[func->tg_op_varno]);	if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))		var->value = DirectFunctionCall1(textin, CStringGetDatum("INSERT"));	else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))		var->value = DirectFunctionCall1(textin, CStringGetDatum("UPDATE"));	else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))		var->value = DirectFunctionCall1(textin, CStringGetDatum("DELETE"));	else		elog(ERROR, "unrecognized trigger action: not INSERT, DELETE, or UPDATE");	var->isnull = false;	var->freeval = true;	var = (PLpgSQL_var *) (estate.datums[func->tg_name_varno]);	var->value = DirectFunctionCall1(namein,							  CStringGetDatum(trigdata->tg_trigger->tgname));	var->isnull = false;	var->freeval = true;	var = (PLpgSQL_var *) (estate.datums[func->tg_when_varno]);	if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))		var->value = DirectFunctionCall1(textin, CStringGetDatum("BEFORE"));	else if (TRIGGER_FIRED_AFTER(trigdata->tg_event))		var->value = DirectFunctionCall1(textin, CStringGetDatum("AFTER"));	else		elog(ERROR, "unrecognized trigger execution time: not BEFORE or AFTER");	var->isnull = false;	var->freeval = true;	var = (PLpgSQL_var *) (estate.datums[func->tg_level_varno]);	if (TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))		var->value = DirectFunctionCall1(textin, CStringGetDatum("ROW"));	else if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))		var->value = DirectFunctionCall1(textin, CStringGetDatum("STATEMENT"));	else		elog(ERROR, "unrecognized trigger event type: not ROW or STATEMENT");	var->isnull = false;	var->freeval = true;	var = (PLpgSQL_var *) (estate.datums[func->tg_relid_varno]);	var->value = ObjectIdGetDatum(trigdata->tg_relation->rd_id);	var->isnull = false;	var->freeval = false;	var = (PLpgSQL_var *) (estate.datums[func->tg_relname_varno]);	var->value = DirectFunctionCall1(namein,			CStringGetDatum(RelationGetRelationName(trigdata->tg_relation)));	var->isnull = false;	var->freeval = true;	var = (PLpgSQL_var *) (estate.datums[func->tg_table_name_varno]);	var->value = DirectFunctionCall1(namein,			CStringGetDatum(RelationGetRelationName(trigdata->tg_relation)));	var->isnull = false;	var->freeval = true;	var = (PLpgSQL_var *) (estate.datums[func->tg_table_schema_varno]);	var->value = DirectFunctionCall1(namein,									 CStringGetDatum(													 get_namespace_name(														RelationGetNamespace(												   trigdata->tg_relation))));	var->isnull = false;	var->freeval = true;	var = (PLpgSQL_var *) (estate.datums[func->tg_nargs_varno]);	var->value = Int16GetDatum(trigdata->tg_trigger->tgnargs);	var->isnull = false;	var->freeval = false;	/*	 * Store the trigger argument values into the special execution state	 * variables	 */	estate.err_text = gettext_noop("while storing call arguments into local variables");	estate.trig_nargs = trigdata->tg_trigger->tgnargs;	if (estate.trig_nargs == 0)		estate.trig_argv = NULL;	else	{		estate.trig_argv = palloc(sizeof(Datum) * estate.trig_nargs);		for (i = 0; i < trigdata->tg_trigger->tgnargs; i++)			estate.trig_argv[i] = DirectFunctionCall1(textin,						   CStringGetDatum(trigdata->tg_trigger->tgargs[i]));	}	estate.err_text = gettext_noop("during function entry");	/*	 * Set the magic variable FOUND to false	 */	exec_set_found(&estate, false);	/*	 * Let the instrumentation plugin peek at this function	 */	if (*plugin_ptr && (*plugin_ptr)->func_beg)		((*plugin_ptr)->func_beg) (&estate, func);	/*	 * Now call the toplevel block of statements	 */	estate.err_text = NULL;	estate.err_stmt = (PLpgSQL_stmt *) (func->action);	rc = exec_stmt_block(&estate, func->action);	if (rc != PLPGSQL_RC_RETURN)	{		estate.err_stmt = NULL;		estate.err_text = NULL;		/*		 * Provide a more helpful message if a CONTINUE has been used outside		 * a loop.		 */		if (rc == PLPGSQL_RC_CONTINUE)			ereport(ERROR,					(errcode(ERRCODE_SYNTAX_ERROR),					 errmsg("CONTINUE cannot be used outside a loop")));		else			ereport(ERROR,			   (errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT),				errmsg("control reached end of trigger procedure without RETURN")));	}	estate.err_stmt = NULL;	estate.err_text = gettext_noop("during function exit");	if (estate.retisset)		ereport(ERROR,				(errcode(ERRCODE_DATATYPE_MISMATCH),				 errmsg("trigger procedure cannot return a set")));	/*	 * Check that the returned tuple structure has the same attributes, the	 * relation that fired the trigger has. A per-statement trigger always	 * needs to return NULL, so we ignore any return value the function itself	 * produces (XXX: is this a good idea?)	 *	 * XXX This way it is possible, that the trigger returns a tuple where	 * attributes don't have the correct atttypmod's length. It's up to the	 * trigger's programmer to ensure that this doesn't happen. Jan	 */	if (estate.retisnull || TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))		rettup = NULL;	else	{		if (!compatible_tupdesc(estate.rettupdesc,								trigdata->tg_relation->rd_att))			ereport(ERROR,					(errcode(ERRCODE_DATATYPE_MISMATCH),					 errmsg("returned tuple structure does not match table of trigger event")));		/* Copy tuple to upper executor memory */		rettup = SPI_copytuple((HeapTuple) (estate.retval));	}	/*	 * Let the instrumentation plugin peek at this function	 */	if (*plugin_ptr && (*plugin_ptr)->func_end)		((*plugin_ptr)->func_end) (&estate, func);	/* Clean up any leftover temporary memory */	FreeExprContext(estate.eval_econtext);	estate.eval_econtext = NULL;	exec_eval_cleanup(&estate);	/*	 * Pop the error context stack	 */	error_context_stack = plerrcontext.previous;	/*	 * Return the trigger's result	 */	return rettup;}/* * error context callback to let us supply a call-stack traceback */static voidplpgsql_exec_error_callback(void *arg){	PLpgSQL_execstate *estate = (PLpgSQL_execstate *) arg;	/* safety check, shouldn't happen */	if (estate->err_func == NULL)		return;	/* if we are doing RAISE, don't report its location */	if (estate->err_text == raise_skip_msg)		return;	if (estate->err_text != NULL)	{		/*		 * We don't expend the cycles to run gettext() on err_text unless we		 * actually need it.  Therefore, places that set up err_text should		 * use gettext_noop() to ensure the strings get recorded in the		 * message dictionary.		 *		 * If both err_text and err_stmt are set, use the err_text as		 * description, but report the err_stmt's line number.  When err_stmt		 * is not set, we're in function entry/exit, or some such place not		 * attached to a specific line number.		 */		if (estate->err_stmt != NULL)		{			/*			 * translator: last %s is a phrase such as "during statement block			 * local variable initialization"			 */			errcontext("PL/pgSQL function \"%s\" line %d %s",					   estate->err_func->fn_name,					   estate->err_stmt->lineno,					   gettext(estate->err_text));		}		else		{			/*			 * translator: last %s is a phrase such as "while storing call			 * arguments into local variables"			 */			errcontext("PL/pgSQL function \"%s\" %s",					   estate->err_func->fn_name,					   gettext(estate->err_text));		}	}	else if (estate->err_stmt != NULL)	{		/* translator: last %s is a plpgsql statement type name */		errcontext("PL/pgSQL function \"%s\" line %d at %s",				   estate->err_func->fn_name,				   estate->err_stmt->lineno,				   plpgsql_stmt_typename(estate->err_stmt));	}	else		errcontext("PL/pgSQL function \"%s\"",				   estate->err_func->fn_name);}/* ---------- * Support function for initializing local execution variables * ---------- */static PLpgSQL_datum *copy_plpgsql_datum(PLpgSQL_datum *datum){	PLpgSQL_datum *result;	switch (datum->dtype)	{		case PLPGSQL_DTYPE_VAR:			{				PLpgSQL_var *new = palloc(sizeof(PLpgSQL_var));				memcpy(new, datum, sizeof(PLpgSQL_var));				/* Ensure the value is null (possibly not needed?) */				new->value = 0;				new->isnull = true;				new->freeval = false;				result = (PLpgSQL_datum *) new;			}			break;		case PLPGSQL_DTYPE_REC:			{				PLpgSQL_rec *new = palloc(sizeof(PLpgSQL_rec));				memcpy(new, datum, sizeof(PLpgSQL_rec));				/* Ensure the value is null (possibly not needed?) */				new->tup = NULL;				new->tupdesc = NULL;				new->freetup = false;				new->freetupdesc = false;				result = (PLpgSQL_datum *) new;			}			break;		case PLPGSQL_DTYPE_ROW:		case PLPGSQL_DTYPE_RECFIELD:		case PLPGSQL_DTYPE_ARRAYELEM:		case PLPGSQL_DTYPE_TRIGARG:			/*			 * These datum records are read-only at runtime, so no need to			 * copy them			 */			result = datum;			break;		default:			elog(ERROR, "unrecognized dtype: %d", datum->dtype);			result = NULL;		/* keep compiler quiet */			break;	}	return result;}static boolexception_matches_conditions(ErrorData *edata, PLpgSQL_condition *cond){	for (; cond != NULL; cond = cond->next)	{		int			sqlerrstate = cond->sqlerrstate;		/*		 * OTHERS matches everything *except* query-canceled; if you're		 * foolish enough, you can match that explicitly.		 */		if (sqlerrstate == 0)		{			if (edata->sqlerrcode != ERRCODE_QUERY_CANCELED)				return true;		}		/* Exact match? */		else if (edata->sqlerrcode == sqlerrstate)			return true;		/* Category match? */		else if (ERRCODE_IS_CATEGORY(sqlerrstate) &&				 ERRCODE_TO_CATEGORY(edata->sqlerrcode) == sqlerrstate)			return true;	}	return false;}/* ---------- * exec_stmt_block			Execute a block of statements * ---------- */static intexec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block){	volatile int rc = -1;	int			i;	int			n;	/*	 * First initialize all variables declared in this block	 */	estate->err_text = gettext_noop("during statement block local variable initialization");	for (i = 0; i < block->n_initvars; i++)	{		n = block->initvarnos[i];		switch (estate->datums[n]->dtype)		{			case PLPGSQL_DTYPE_VAR:				{					PLpgSQL_var *var = (PLpgSQL_var *) (estate->datums[n]);					/* free any old value, in case re-entering block */					free_var(var);					/* Initially it contains a NULL */					var->value = (Datum) 0;					var->isnull = true;					if (var->default_val == NULL)					{						/*						 * If needed, give the datatype a chance to reject						 * NULLs, by assigning a NULL to the variable. We						 * claim the value is of type UNKNOWN, not the var's						 * datatype, else coercion will be skipped. (Do this						 * before the notnull check to be consistent with						 * exec_assign_value.)						 */						if (!var->datatype->typinput.fn_strict)						{							bool		valIsNull = true;							exec_assign_value(estate,											  (PLpgSQL_datum *) var,											  (Datum) 0,											  UNKNOWNOID,											  &valIsNull);						}						if (var->notnull)							ereport(ERROR,									(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),									 errmsg("variable \"%s\" declared NOT NULL cannot default to NULL",											var->refname)));					}					else					{						exec_assign_expr(estate, (PLpgSQL_datum *) var,										 var->default_val);					}				}				break;			case PLPGSQL_DTYPE_REC:				{					PLpgSQL_rec *rec = (PLpgSQL_rec *) (estate->datums[n]);					if (rec->freetup)					{						heap_freetuple(rec->tup);						FreeTupleDesc(rec->tupdesc);						rec->freetup = false;					}					rec->tup = NULL;					rec->tupdesc = NULL;				}				break;			case PLPGSQL_DTYPE_RECFIELD:			case PLPGSQL_DTYPE_ARRAYELEM:				break;			default:				elog(ERROR, "unrecognized dtype: %d",					 estate->datums[n]->dtype);		}	}	if (block->exceptions)	{		/*		 * Execute the statements in the block's body inside a sub-transaction		 */		MemoryContext oldcontext = CurrentMemoryContext;		ResourceOwner oldowner = CurrentResourceOwner;		ExprContext *old_eval_econtext = estate->eval_econtext;		EState	   *old_eval_estate = estate->eval_estate;		long int	old_eval_estate_simple_id = estate->eval_estate_simple_id;		estate->err_text = gettext_noop("during statement block entry");		BeginInternalSubTransaction(NULL);		/* Want to run statements inside function's memory context */		MemoryContextSwitchTo(oldcontext);		PG_TRY();		{			/*			 * We need to run the block's statements with a new eval_econtext			 * that belongs to the current subtransaction; if we try to use			 * the outer econtext then ExprContext shutdown callbacks will be			 * called at the wrong times.			 */			plpgsql_create_econtext(estate);			estate->err_text = NULL;			/* Run the block's statements */			rc = exec_stmts(estate, block->body);			estate->err_text = gettext_noop("during statement block exit");			/*			 * If the block ended with RETURN, we may need to copy the return			 * value out of the subtransaction eval_context.  This is			 * currently only needed for scalar result types --- rowtype			 * values will always exist in the function's own memory context.			 */			if (rc == PLPGSQL_RC_RETURN &&				!estate->retisset &&				!estate->retisnull &&				estate->rettupdesc == NULL)			{				int16		resTypLen;				bool		resTypByVal;				get_typlenbyval(estate->rettype, &resTypLen, &resTypByVal);				estate->retval = datumCopy(estate->retval,										   resTypByVal, resTypLen);			}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?