📄 pl_exec.c
字号:
/********************************************************************** * pl_exec.c - Executor for the PL/pgSQL * procedural language * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.93.2.1 2004/02/24 01:44:47 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * * The author hereby grants permission to use, copy, modify, * distribute, and license this software and its documentation * for any purpose, provided that existing copyright notices are * retained in all copies and that this notice is included * verbatim in any distributions. No written agreement, license, * or royalty fee is required for any of the authorized uses. * Modifications to this software may be copyrighted by their * author and need not follow the licensing terms described * here, provided that the new terms are clearly indicated on * the first page of each file where they apply. * * IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR * CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS * SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN * IF THE AUTHOR HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * * THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON * AN "AS IS" BASIS, AND THE AUTHOR AND DISTRIBUTORS HAVE NO * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, * ENHANCEMENTS, OR MODIFICATIONS. * **********************************************************************/#include "plpgsql.h"#include "pl.tab.h"#include <ctype.h>#include <setjmp.h>#include "access/heapam.h"#include "catalog/pg_proc.h"#include "catalog/pg_type.h"#include "executor/spi_priv.h"#include "funcapi.h"#include "optimizer/clauses.h"#include "parser/parse_expr.h"#include "tcop/tcopprot.h"#include "utils/array.h"#include "utils/builtins.h"#include "utils/lsyscache.h"static const char *const raise_skip_msg = "RAISE";/* * All plpgsql function executions within a single transaction share * the same executor EState for evaluating "simple" expressions. Each * function call creates its own "eval_econtext" ExprContext within this * estate. We destroy the estate at transaction shutdown to ensure there * is no permanent leakage of memory (especially for xact abort case). * * If a simple PLpgSQL_expr has been used in the current xact, it is * linked into the active_simple_exprs list. */static EState *simple_eval_estate = NULL;static PLpgSQL_expr *active_simple_exprs = NULL;/************************************************************ * Local function forward declarations ************************************************************/static void plpgsql_exec_error_callback(void *arg);static PLpgSQL_var *copy_var(PLpgSQL_var * var);static PLpgSQL_rec *copy_rec(PLpgSQL_rec * rec);static int exec_stmt_block(PLpgSQL_execstate * estate, PLpgSQL_stmt_block * block);static int exec_stmts(PLpgSQL_execstate * estate, PLpgSQL_stmts * stmts);static int exec_stmt(PLpgSQL_execstate * estate, PLpgSQL_stmt * stmt);static int exec_stmt_assign(PLpgSQL_execstate * estate, PLpgSQL_stmt_assign * stmt);static int exec_stmt_perform(PLpgSQL_execstate * estate, PLpgSQL_stmt_perform * stmt);static int exec_stmt_getdiag(PLpgSQL_execstate * estate, PLpgSQL_stmt_getdiag * stmt);static int exec_stmt_if(PLpgSQL_execstate * estate, PLpgSQL_stmt_if * stmt);static int exec_stmt_loop(PLpgSQL_execstate * estate, PLpgSQL_stmt_loop * stmt);static int exec_stmt_while(PLpgSQL_execstate * estate, PLpgSQL_stmt_while * stmt);static int exec_stmt_fori(PLpgSQL_execstate * estate, PLpgSQL_stmt_fori * stmt);static int exec_stmt_fors(PLpgSQL_execstate * estate, PLpgSQL_stmt_fors * stmt);static int exec_stmt_select(PLpgSQL_execstate * estate, PLpgSQL_stmt_select * stmt);static int exec_stmt_open(PLpgSQL_execstate * estate, PLpgSQL_stmt_open * stmt);static int exec_stmt_fetch(PLpgSQL_execstate * estate, PLpgSQL_stmt_fetch * stmt);static int exec_stmt_close(PLpgSQL_execstate * estate, PLpgSQL_stmt_close * stmt);static int exec_stmt_exit(PLpgSQL_execstate * estate, PLpgSQL_stmt_exit * stmt);static int exec_stmt_return(PLpgSQL_execstate * estate, PLpgSQL_stmt_return * stmt);static int exec_stmt_return_next(PLpgSQL_execstate * estate, PLpgSQL_stmt_return_next * stmt);static int exec_stmt_raise(PLpgSQL_execstate * estate, PLpgSQL_stmt_raise * stmt);static int exec_stmt_execsql(PLpgSQL_execstate * estate, PLpgSQL_stmt_execsql * stmt);static int exec_stmt_dynexecute(PLpgSQL_execstate * estate, PLpgSQL_stmt_dynexecute * stmt);static int exec_stmt_dynfors(PLpgSQL_execstate * estate, PLpgSQL_stmt_dynfors * stmt);static void plpgsql_estate_setup(PLpgSQL_execstate * estate, PLpgSQL_function * func, ReturnSetInfo *rsi);static void exec_eval_cleanup(PLpgSQL_execstate * estate);static void exec_prepare_plan(PLpgSQL_execstate * estate, PLpgSQL_expr * expr);static bool exec_simple_check_node(Node *node);static void exec_simple_check_plan(PLpgSQL_expr * expr);static Datum exec_eval_simple_expr(PLpgSQL_execstate * estate, PLpgSQL_expr * expr, bool *isNull, Oid *rettype);static void exec_assign_expr(PLpgSQL_execstate * estate, PLpgSQL_datum * target, PLpgSQL_expr * expr);static void exec_assign_value(PLpgSQL_execstate * estate, PLpgSQL_datum * target, Datum value, Oid valtype, bool *isNull);static void exec_eval_datum(PLpgSQL_execstate * estate, PLpgSQL_datum * datum, Oid expectedtypeid, Oid *typeid, Datum *value, bool *isnull);static int exec_eval_integer(PLpgSQL_execstate * estate, PLpgSQL_expr * expr, bool *isNull);static bool exec_eval_boolean(PLpgSQL_execstate * estate, PLpgSQL_expr * expr, bool *isNull);static Datum exec_eval_expr(PLpgSQL_execstate * estate, PLpgSQL_expr * expr, bool *isNull, Oid *rettype);static int exec_run_select(PLpgSQL_execstate * estate, PLpgSQL_expr * expr, int maxtuples, Portal *portalP);static void exec_move_row(PLpgSQL_execstate * estate, PLpgSQL_rec * rec, PLpgSQL_row * row, HeapTuple tup, TupleDesc tupdesc);static HeapTuple make_tuple_from_row(PLpgSQL_execstate * estate, PLpgSQL_row * row, TupleDesc tupdesc);static char *convert_value_to_string(Datum value, Oid valtype);static Datum exec_cast_value(Datum value, Oid valtype, Oid reqtype, FmgrInfo *reqinput, Oid reqtypelem, int32 reqtypmod, bool *isnull);static Datum exec_simple_cast_value(Datum value, Oid valtype, Oid reqtype, int32 reqtypmod, bool *isnull);static void exec_init_tuple_store(PLpgSQL_execstate * estate);static bool compatible_tupdesc(TupleDesc td1, TupleDesc td2);static void exec_set_found(PLpgSQL_execstate * estate, bool state);/* ---------- * plpgsql_exec_function Called by the call handler for * function execution. * ---------- */Datumplpgsql_exec_function(PLpgSQL_function * func, FunctionCallInfo fcinfo){ PLpgSQL_execstate estate; ErrorContextCallback plerrcontext; int i; /* * Setup the execution state */ plpgsql_estate_setup(&estate, func, (ReturnSetInfo *) fcinfo->resultinfo); /* * Setup error traceback support for ereport() */ plerrcontext.callback = plpgsql_exec_error_callback; plerrcontext.arg = &estate; plerrcontext.previous = error_context_stack; error_context_stack = &plerrcontext; /* * Make local execution copies of all the datums */ estate.err_text = gettext_noop("during initialization of execution state"); for (i = 0; i < func->ndatums; i++) { switch (func->datums[i]->dtype) { case PLPGSQL_DTYPE_VAR: estate.datums[i] = (PLpgSQL_datum *) copy_var((PLpgSQL_var *) (func->datums[i])); break; case PLPGSQL_DTYPE_REC: estate.datums[i] = (PLpgSQL_datum *) copy_rec((PLpgSQL_rec *) (func->datums[i])); break; case PLPGSQL_DTYPE_ROW: case PLPGSQL_DTYPE_RECFIELD: case PLPGSQL_DTYPE_ARRAYELEM: estate.datums[i] = func->datums[i]; break; default: elog(ERROR, "unrecognized dtype: %d", func->datums[i]->dtype); } } /* * Store the actual call argument values into the variables */ estate.err_text = gettext_noop("while storing call arguments into local variables"); for (i = 0; i < func->fn_nargs; i++) { int n = func->fn_argvarnos[i]; switch (estate.datums[n]->dtype) { case PLPGSQL_DTYPE_VAR: { PLpgSQL_var *var = (PLpgSQL_var *) estate.datums[n]; var->value = fcinfo->arg[i]; var->isnull = fcinfo->argnull[i]; var->freeval = false; } break; case PLPGSQL_DTYPE_ROW: { PLpgSQL_row *row = (PLpgSQL_row *) estate.datums[n]; TupleTableSlot *slot = (TupleTableSlot *) fcinfo->arg[i]; HeapTuple tup; TupleDesc tupdesc; if (!fcinfo->argnull[i]) { Assert(slot != NULL); tup = slot->val; tupdesc = slot->ttc_tupleDescriptor; exec_move_row(&estate, NULL, row, tup, tupdesc); } else { /* If arg is null, treat it as an empty row */ exec_move_row(&estate, NULL, row, NULL, NULL); } } break; default: elog(ERROR, "unrecognized dtype: %d", func->datums[i]->dtype); } } /* * Initialize the other variables to NULL values for now. The default * values are set when the blocks are entered. */ estate.err_text = gettext_noop("while initializing local variables to NULL"); for (i = estate.found_varno; i < estate.ndatums; i++) { switch (estate.datums[i]->dtype) { case PLPGSQL_DTYPE_VAR: { PLpgSQL_var *var = (PLpgSQL_var *) estate.datums[i]; var->value = 0; var->isnull = true; var->freeval = false; } break; case PLPGSQL_DTYPE_ROW: case PLPGSQL_DTYPE_REC: case PLPGSQL_DTYPE_RECFIELD: case PLPGSQL_DTYPE_ARRAYELEM: break; default: elog(ERROR, "unrecognized dtype: %d", func->datums[i]->dtype); } } /* * 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); if (exec_stmt_block(&estate, func->action) != PLPGSQL_RC_RETURN) { estate.err_stmt = NULL; estate.err_text = NULL; ereport(ERROR, (errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT), errmsg("control reached end of function without RETURN"))); } /* * We got a return value - process it */ estate.err_stmt = NULL; estate.err_text = gettext_noop("while casting return value to function's return type"); fcinfo->isnull = estate.retisnull; if (estate.retisset) { ReturnSetInfo *rsi = estate.rsi; /* Check caller can handle a set result */ if (!rsi || !IsA(rsi, ReturnSetInfo) || (rsi->allowedModes & SFRM_Materialize) == 0) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); rsi->returnMode = SFRM_Materialize; /* If we produced any tuples, send back the result */ if (estate.tuple_store) { rsi->setResult = estate.tuple_store; if (estate.rettupdesc) { MemoryContext oldcxt; oldcxt = MemoryContextSwitchTo(estate.tuple_store_cxt); rsi->setDesc = CreateTupleDescCopy(estate.rettupdesc); MemoryContextSwitchTo(oldcxt); } } estate.retval = (Datum) 0; fcinfo->isnull = true; } else if (!estate.retisnull) { if (estate.retistuple) { /* Copy tuple to upper executor memory */ /* Here we need to return a TupleTableSlot not just a tuple */ estate.retval = (Datum) SPI_copytupleintoslot((HeapTuple) (estate.retval), estate.rettupdesc); } else { /* Cast value to proper type */ estate.retval = exec_cast_value(estate.retval, estate.rettype, func->fn_rettype, &(func->fn_retinput), func->fn_rettypelem, -1, &fcinfo->isnull); /* * If the functions return type isn't by value, copy the value * into upper executor memory context. */ if (!fcinfo->isnull && !func->fn_retbyval) { Size len; void *tmp; len = datumGetSize(estate.retval, false, func->fn_rettyplen); tmp = (void *) SPI_palloc(len); memcpy(tmp, DatumGetPointer(estate.retval), len); estate.retval = PointerGetDatum(tmp); } } } /* 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 functions result */ return estate.retval;}/* ---------- * plpgsql_exec_trigger Called by the call handler for * trigger execution. * ---------- */HeapTupleplpgsql_exec_trigger(PLpgSQL_function * func, TriggerData *trigdata){ PLpgSQL_execstate estate; ErrorContextCallback plerrcontext; int i; PLpgSQL_var *var; PLpgSQL_rec *rec_new, *rec_old; HeapTuple rettup; /* * Setup the execution state */ plpgsql_estate_setup(&estate, func, NULL); /* * Setup error traceback support for ereport() */ plerrcontext.callback = plpgsql_exec_error_callback; plerrcontext.arg = &estate; plerrcontext.previous = error_context_stack; error_context_stack = &plerrcontext; /* * Make local execution copies of all the datums */ estate.err_text = gettext_noop("during initialization of execution state"); for (i = 0; i < func->ndatums; i++) { switch (func->datums[i]->dtype) { case PLPGSQL_DTYPE_VAR: estate.datums[i] = (PLpgSQL_datum *) copy_var((PLpgSQL_var *) (func->datums[i])); break; case PLPGSQL_DTYPE_REC: estate.datums[i] = (PLpgSQL_datum *) copy_rec((PLpgSQL_rec *) (func->datums[i])); break; case PLPGSQL_DTYPE_ROW: case PLPGSQL_DTYPE_RECFIELD: case PLPGSQL_DTYPE_ARRAYELEM: case PLPGSQL_DTYPE_TRIGARG: estate.datums[i] = func->datums[i]; break; default: elog(ERROR, "unrecognized dtype: %d", func->datums[i]->dtype); } } /* * Put the OLD and NEW tuples into record variables */ rec_new = (PLpgSQL_rec *) (estate.datums[func->new_varno]); rec_new->freetup = false; rec_new->freetupdesc = false; rec_old = (PLpgSQL_rec *) (estate.datums[func->old_varno]); rec_old->freetup = false; rec_old->freetupdesc = false; if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event)) { /* * Per-statement triggers don't use OLD/NEW variables */ rec_new->tup = NULL; rec_new->tupdesc = NULL; rec_old->tup = NULL; rec_old->tupdesc = NULL; } else if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event)) { 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -