⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pl_exec.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 5 页
字号:
		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_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]));	}	/*	 * Set the magic variable FOUND to false	 */	exec_set_found(&estate, false);	/*	 * 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")));	}	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));	}	/* Clean up any leftover temporary memory */	if (estate.eval_econtext != NULL)		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_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 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.		 */		/*		 * 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		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	 */	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_var(var);					if (!var->isconst || var->isnull)					{						if (var->default_val == NULL)						{							var->value = (Datum) 0;							var->isnull = true;							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;		BeginInternalSubTransaction(NULL);		/* Want to run statements inside function's memory context */		MemoryContextSwitchTo(oldcontext);		PG_TRY();		{			rc = exec_stmts(estate, block->body);			/* Commit the inner transaction, return to outer xact context */			ReleaseCurrentSubTransaction();			MemoryContextSwitchTo(oldcontext);			CurrentResourceOwner = oldowner;			/*			 * AtEOSubXact_SPI() should not have popped any SPI context, but			 * just in case it did, make sure we remain connected.			 */			SPI_restore_connection();		}		PG_CATCH();		{			ErrorData  *edata;			ListCell   *e;			/* Save error info */			MemoryContextSwitchTo(oldcontext);			edata = CopyErrorData();			FlushErrorState();			/* Abort the inner transaction */			RollbackAndReleaseCurrentSubTransaction();			MemoryContextSwitchTo(oldcontext);			CurrentResourceOwner = oldowner;			/*			 * If AtEOSubXact_SPI() popped any SPI context of the subxact, it			 * will have left us in a disconnected state.  We need this hack			 * to return to connected state.			 */			SPI_restore_connection();			/* Look for a matching exception handler */			foreach(e, block->exceptions->exc_list)			{				PLpgSQL_exception *exception = (PLpgSQL_exception *) lfirst(e);				if (exception_matches_conditions(edata, exception->conditions))				{					/*					 * Initialize the magic SQLSTATE and SQLERRM variables for					 * the exception block. We needn't do this until we have					 * found a matching exception.					 */					PLpgSQL_var *state_var;					PLpgSQL_var *errm_var;					state_var = (PLpgSQL_var *)						estate->datums[block->exceptions->sqlstate_varno];					state_var->value = DirectFunctionCall1(textin,					   CStringGetDatum(unpack_sql_state(edata->sqlerrcode)));					state_var->freeval = true;					state_var->isnull = false;					errm_var = (PLpgSQL_var *)						estate->datums[block->exceptions->sqlerrm_varno];					errm_var->value = DirectFunctionCall1(textin,											CStringGetDatum(edata->message));					errm_var->freeval = true;					errm_var->isnull = false;					rc = exec_stmts(estate, exception->action);					free_var(state_var);					free_var(errm_var);					break;				}			}			/* If no match found, re-throw the error */			if (e == NULL)				ReThrowError(edata);			else				FreeErrorData(edata);		}		PG_END_TRY();	}	else	{		/*		 * Just execute the statements in the block's body		 */		rc = exec_stmts(estate, block->body);	}	/*	 * Handle the return code.	 */	switch (rc)	{		case PLPGSQL_RC_OK:		case PLPGSQL_RC_CONTINUE:		case PLPGSQL_RC_RETURN:			return rc;		case PLPGSQL_RC_EXIT:			if (estate->exitlabel == NULL)				return PLPGSQL_RC_OK;			if (block->label == NULL)				return PLPGSQL_RC_EXIT;			if (strcmp(block->label, estate->exitlabel))				return PLPGSQL_RC_EXIT;			estate->exitlabel = NULL;			return PLPGSQL_RC_OK;		default:			elog(ERROR, "unrecognized rc: %d", rc);	}	return PLPGSQL_RC_OK;}/* ---------- * exec_stmts			Iterate over a list of statements *				as long as their return code is OK * ---------- */static intexec_stmts(PLpgSQL_execstate *estate, List *stmts){	ListCell   *s;	if (stmts == NIL)	{		/*		 * Ensure we do a CHECK_FOR_INTERRUPTS() even though there is no		 * statement.  This prevents hangup in a tight loop if, for instance,		 * there is a LOOP construct with an empty body.		 */		CHECK_FOR_INTERRUPTS();		return PLPGSQL_RC_OK;	}

⌨️ 快捷键说明

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