pl_exec.c

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

C
2,523
字号
			/* Commit the inner transaction, return to outer xact context */			ReleaseCurrentSubTransaction();			MemoryContextSwitchTo(oldcontext);			CurrentResourceOwner = oldowner;			/* Revert to outer eval_econtext */			estate->eval_econtext = old_eval_econtext;			estate->eval_estate = old_eval_estate;			estate->eval_estate_simple_id = old_eval_estate_simple_id;			/*			 * 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;			estate->err_text = gettext_noop("during exception cleanup");			/* Save error info */			MemoryContextSwitchTo(oldcontext);			edata = CopyErrorData();			FlushErrorState();			/* Abort the inner transaction */			RollbackAndReleaseCurrentSubTransaction();			MemoryContextSwitchTo(oldcontext);			CurrentResourceOwner = oldowner;			/* Revert to outer eval_econtext */			estate->eval_econtext = old_eval_econtext;			estate->eval_estate = old_eval_estate;			estate->eval_estate_simple_id = old_eval_estate_simple_id;			/*			 * 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;					estate->err_text = NULL;					rc = exec_stmts(estate, exception->action);					free_var(state_var);					state_var->value = (Datum) 0;					free_var(errm_var);					errm_var->value = (Datum) 0;					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		 */		estate->err_text = NULL;		rc = exec_stmts(estate, block->body);	}	estate->err_text = NULL;	/*	 * 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;	}	foreach(s, stmts)	{		PLpgSQL_stmt *stmt = (PLpgSQL_stmt *) lfirst(s);		int			rc = exec_stmt(estate, stmt);		if (rc != PLPGSQL_RC_OK)			return rc;	}	return PLPGSQL_RC_OK;}/* ---------- * exec_stmt			Distribute one statement to the statements *				type specific execution function. * ---------- */static intexec_stmt(PLpgSQL_execstate *estate, PLpgSQL_stmt *stmt){	PLpgSQL_stmt *save_estmt;	int			rc = -1;	save_estmt = estate->err_stmt;	estate->err_stmt = stmt;	/* Let the plugin know that we are about to execute this statement */	if (*plugin_ptr && (*plugin_ptr)->stmt_beg)		((*plugin_ptr)->stmt_beg) (estate, stmt);	CHECK_FOR_INTERRUPTS();	switch (stmt->cmd_type)	{		case PLPGSQL_STMT_BLOCK:			rc = exec_stmt_block(estate, (PLpgSQL_stmt_block *) stmt);			break;		case PLPGSQL_STMT_ASSIGN:			rc = exec_stmt_assign(estate, (PLpgSQL_stmt_assign *) stmt);			break;		case PLPGSQL_STMT_PERFORM:			rc = exec_stmt_perform(estate, (PLpgSQL_stmt_perform *) stmt);			break;		case PLPGSQL_STMT_GETDIAG:			rc = exec_stmt_getdiag(estate, (PLpgSQL_stmt_getdiag *) stmt);			break;		case PLPGSQL_STMT_IF:			rc = exec_stmt_if(estate, (PLpgSQL_stmt_if *) stmt);			break;		case PLPGSQL_STMT_LOOP:			rc = exec_stmt_loop(estate, (PLpgSQL_stmt_loop *) stmt);			break;		case PLPGSQL_STMT_WHILE:			rc = exec_stmt_while(estate, (PLpgSQL_stmt_while *) stmt);			break;		case PLPGSQL_STMT_FORI:			rc = exec_stmt_fori(estate, (PLpgSQL_stmt_fori *) stmt);			break;		case PLPGSQL_STMT_FORS:			rc = exec_stmt_fors(estate, (PLpgSQL_stmt_fors *) stmt);			break;		case PLPGSQL_STMT_EXIT:			rc = exec_stmt_exit(estate, (PLpgSQL_stmt_exit *) stmt);			break;		case PLPGSQL_STMT_RETURN:			rc = exec_stmt_return(estate, (PLpgSQL_stmt_return *) stmt);			break;		case PLPGSQL_STMT_RETURN_NEXT:			rc = exec_stmt_return_next(estate, (PLpgSQL_stmt_return_next *) stmt);			break;		case PLPGSQL_STMT_RETURN_QUERY:			rc = exec_stmt_return_query(estate, (PLpgSQL_stmt_return_query *) stmt);			break;		case PLPGSQL_STMT_RAISE:			rc = exec_stmt_raise(estate, (PLpgSQL_stmt_raise *) stmt);			break;		case PLPGSQL_STMT_EXECSQL:			rc = exec_stmt_execsql(estate, (PLpgSQL_stmt_execsql *) stmt);			break;		case PLPGSQL_STMT_DYNEXECUTE:			rc = exec_stmt_dynexecute(estate, (PLpgSQL_stmt_dynexecute *) stmt);			break;		case PLPGSQL_STMT_DYNFORS:			rc = exec_stmt_dynfors(estate, (PLpgSQL_stmt_dynfors *) stmt);			break;		case PLPGSQL_STMT_OPEN:			rc = exec_stmt_open(estate, (PLpgSQL_stmt_open *) stmt);			break;		case PLPGSQL_STMT_FETCH:			rc = exec_stmt_fetch(estate, (PLpgSQL_stmt_fetch *) stmt);			break;		case PLPGSQL_STMT_CLOSE:			rc = exec_stmt_close(estate, (PLpgSQL_stmt_close *) stmt);			break;		default:			estate->err_stmt = save_estmt;			elog(ERROR, "unrecognized cmdtype: %d", stmt->cmd_type);	}	/* Let the plugin know that we have finished executing this statement */	if (*plugin_ptr && (*plugin_ptr)->stmt_end)		((*plugin_ptr)->stmt_end) (estate, stmt);	estate->err_stmt = save_estmt;	return rc;}/* ---------- * exec_stmt_assign			Evaluate an expression and *					put the result into a variable. * ---------- */static intexec_stmt_assign(PLpgSQL_execstate *estate, PLpgSQL_stmt_assign *stmt){	Assert(stmt->varno >= 0);	exec_assign_expr(estate, estate->datums[stmt->varno], stmt->expr);	return PLPGSQL_RC_OK;}/* ---------- * exec_stmt_perform		Evaluate query and discard result (but set *							FOUND depending on whether at least one row *							was returned). * ---------- */static intexec_stmt_perform(PLpgSQL_execstate *estate, PLpgSQL_stmt_perform *stmt){	PLpgSQL_expr *expr = stmt->expr;	(void) exec_run_select(estate, expr, 0, NULL);	exec_set_found(estate, (estate->eval_processed != 0));	exec_eval_cleanup(estate);	return PLPGSQL_RC_OK;}/* ---------- * exec_stmt_getdiag					Put internal PG information into *										specified variables. * ---------- */static intexec_stmt_getdiag(PLpgSQL_execstate *estate, PLpgSQL_stmt_getdiag *stmt){	ListCell   *lc;	foreach(lc, stmt->diag_items)	{		PLpgSQL_diag_item *diag_item = (PLpgSQL_diag_item *) lfirst(lc);		PLpgSQL_datum *var;		bool		isnull = false;		if (diag_item->target <= 0)			continue;		var = estate->datums[diag_item->target];		if (var == NULL)			continue;		switch (diag_item->kind)		{			case PLPGSQL_GETDIAG_ROW_COUNT:				exec_assign_value(estate, var,								  UInt32GetDatum(estate->eval_processed),								  INT4OID, &isnull);				break;			case PLPGSQL_GETDIAG_RESULT_OID:				exec_assign_value(estate, var,								  ObjectIdGetDatum(estate->eval_lastoid),								  OIDOID, &isnull);				break;			default:				elog(ERROR, "unrecognized attribute request: %d",					 diag_item->kind);		}	}	return PLPGSQL_RC_OK;}/* ---------- * exec_stmt_if				Evaluate a bool expression and *					execute the true or false body *					conditionally. * ---------- */static intexec_stmt_if(PLpgSQL_execstate *estate, PLpgSQL_stmt_if *stmt){	bool		value;	bool		isnull;	value = exec_eval_boolean(estate, stmt->cond, &isnull);	exec_eval_cleanup(estate);	if (!isnull && value)	{		if (stmt->true_body != NIL)			return exec_stmts(estate, stmt->true_body);	}	else	{		if (stmt->false_body != NIL)			return exec_stmts(estate, stmt->false_body);	}	return PLPGSQL_RC_OK;}/* ---------- * exec_stmt_loop			Loop over statements until *					an exit occurs. * ---------- */static intexec_stmt_loop(PLpgSQL_execstate *estate, PLpgSQL_stmt_loop *stmt){	for (;;)	{		int			rc = exec_stmts(estate, stmt->body);		switch (rc)		{			case PLPGSQL_RC_OK:				break;			case PLPGSQL_RC_EXIT:				if (estate->exitlabel == NULL)					return PLPGSQL_RC_OK;				if (stmt->label == NULL)					return PLPGSQL_RC_EXIT;				if (strcmp(stmt->label, estate->exitlabel) != 0)					return PLPGSQL_RC_EXIT;				estate->exitlabel = NULL;				return PLPGSQL_RC_OK;			case PLPGSQL_RC_CONTINUE:				if (estate->exitlabel == NULL)					/* anonymous continue, so re-run the loop */					break;				else if (stmt->label != NULL &&						 strcmp(stmt->label, estate->exitlabel) == 0)					/* label matches named continue, so re-run loop */					estate->exitlabel = NULL;				else					/* label doesn't match named continue, so propagate upward */					return PLPGSQL_RC_CONTINUE;				break;			case PLPGSQL_RC_RETURN:				return PLPGSQL_RC_RETURN;			default:				elog(ERROR, "unrecognized rc: %d", rc);		}	}	return PLPGSQL_RC_OK;}/* ---------- * exec_stmt_while			Loop over statements as long *					as an expression evaluates to *					true or an exit occurs. * ---------- */static intexec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt){	for (;;)	{		int			rc;		bool		value;		bool		isnull;		value = exec_eval_boolean(estate, stmt->cond, &isnull);		exec_eval_cleanup(estate);		if (isnull || !value)			break;		rc = exec_stmts(estate, stmt->body);		switch (rc)		{			case PLPGSQL_RC_OK:				break;			case PLPGSQL_RC_EXIT:				if (estate->exitlabel == NULL)					return PLPGSQL_RC_OK;				if (stmt->label == NULL)					return PLPGSQL_RC_EXIT;				if (strcmp(stmt->label, estate->exitlabel))					return PLPGSQL_RC_EXIT;				estate->exitlabel = NULL;				return PLPGSQL_RC_OK;			case PLPGSQL_RC_CONTINUE:				if (estate->exitlabel == NULL)					/* anonymous continue, so re-run loop */					break;				else if (stmt->label != NULL &&						 strcmp(stmt->label, estate->exitlabel) == 0)					/* label matches named continue, so re-run loop */					estate->exitlabel = NULL;				else					/* label doesn't match named continue, propagate upward */					return PLPGSQL_RC_CONTINUE;				break;			case PLPGSQL_RC_RETURN:				return PLPGSQL_RC_RETURN;			default:				elog(ERROR, "unrecognized rc: %d", rc);

⌨️ 快捷键说明

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