📄 pl_exec.c
字号:
/********************************************************************** * pl_exec.c - Executor for the PL/pgSQL * procedural language * * IDENTIFICATION * $Header: /usr/local/cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.12 1999/07/04 01:03:01 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 <stdio.h>#include <stdlib.h>#include <stdarg.h>#include <unistd.h>#include <fcntl.h>#include <string.h>#include <ctype.h>#include <setjmp.h>#include "plpgsql.h"#include "pl.tab.h"#include "executor/spi.h"#include "executor/spi_priv.h"#include "commands/trigger.h"#include "utils/elog.h"#include "utils/builtins.h"#include "fmgr.h"#include "access/heapam.h"#include "tcop/tcopprot.h"#include "utils/syscache.h"#include "catalog/pg_proc.h"#include "catalog/pg_type.h"static PLpgSQL_function *error_info_func = NULL;static PLpgSQL_stmt *error_info_stmt = NULL;static char *error_info_text = NULL;/************************************************************ * Local function forward declarations ************************************************************/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_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_exit(PLpgSQL_execstate * estate, PLpgSQL_stmt_exit * stmt);static int exec_stmt_return(PLpgSQL_execstate * estate, PLpgSQL_stmt_return * 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 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 void exec_eval_clear_fcache(Node *node);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 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);static void exec_move_row(PLpgSQL_execstate * estate, PLpgSQL_rec * rec, PLpgSQL_row * row, HeapTuple tup, TupleDesc tupdesc);static Datum exec_cast_value(Datum value, Oid valtype, Oid reqtype, FmgrInfo *reqinput, int16 reqtypmod, bool *isnull);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, FmgrValues *args, bool *isNull){ PLpgSQL_execstate estate; int i; sigjmp_buf save_restart; PLpgSQL_function *save_efunc; PLpgSQL_stmt *save_estmt; char *save_etext; /* ---------- * Setup debug error info and catch elog() * ---------- */ save_efunc = error_info_func; save_estmt = error_info_stmt; save_etext = error_info_text; error_info_func = func; error_info_stmt = NULL; error_info_text = "while initialization of execution state"; memcpy(&save_restart, &Warn_restart, sizeof(save_restart)); if (sigsetjmp(Warn_restart, 1) != 0) { memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); /* ---------- * If we are the first of cascaded error catchings, * print where this happened * ---------- */ if (error_info_func != NULL) { elog(DEBUG, "Last error occured while executing PL/pgSQL function %s", error_info_func->fn_name); if (error_info_stmt != NULL) { char *stmttype; switch (error_info_stmt->cmd_type) { case PLPGSQL_STMT_BLOCK: stmttype = "blocks variable initialization"; break; case PLPGSQL_STMT_ASSIGN: stmttype = "assignment"; break; case PLPGSQL_STMT_IF: stmttype = "if"; break; case PLPGSQL_STMT_LOOP: stmttype = "loop"; break; case PLPGSQL_STMT_WHILE: stmttype = "while"; break; case PLPGSQL_STMT_FORI: stmttype = "for with integer loopvar"; break; case PLPGSQL_STMT_FORS: stmttype = "for over select rows"; break; case PLPGSQL_STMT_SELECT: stmttype = "select into variables"; break; case PLPGSQL_STMT_EXIT: stmttype = "exit"; break; case PLPGSQL_STMT_RETURN: stmttype = "return"; break; case PLPGSQL_STMT_RAISE: stmttype = "raise"; break; case PLPGSQL_STMT_EXECSQL: stmttype = "SQL statement"; break; default: stmttype = "unknown"; break; } elog(DEBUG, "line %d at %s", error_info_stmt->lineno, stmttype); } else { if (error_info_text != NULL) elog(DEBUG, "%s", error_info_text); else elog(DEBUG, "no more error information available"); } error_info_func = NULL; error_info_stmt = NULL; error_info_text = NULL; } siglongjmp(Warn_restart, 1); } /* ---------- * Setup the execution state * ---------- */ estate.retval = 0; estate.retisnull = false; estate.rettype = InvalidOid; estate.retistuple = func->fn_retistuple; estate.retisset = func->fn_retset; estate.exitlabel = NULL; estate.found_varno = func->found_varno; estate.ndatums = func->ndatums; estate.datums = palloc(sizeof(PLpgSQL_datum *) * estate.ndatums); /* ---------- * Make local execution copies of all the datums * ---------- */ 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: estate.datums[i] = func->datums[i]; break; default: elog(ERROR, "unknown dtype %d in plpgsql_exec_function()", func->datums[i]->dtype); } } /* ---------- * Put the actual call argument values into the variables * ---------- */ error_info_text = "while putting call arguments to 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 = (Datum) (args->data[i]); var->isnull = *isNull; var->shouldfree = false; } break; case PLPGSQL_DTYPE_ROW: { HeapTuple tup; TupleDesc tupdesc; PLpgSQL_row *row = (PLpgSQL_row *) estate.datums[n]; tup = ((TupleTableSlot *) (args->data[i]))->val; tupdesc = ((TupleTableSlot *) (args->data[i]))->ttc_tupleDescriptor; exec_move_row(&estate, NULL, row, tup, tupdesc); } break; default: elog(ERROR, "unknown dtype %d in plpgsql_exec_function()", func->datums[i]->dtype); } } /* ---------- * Initialize the other variables to NULL values for now. * The default values are set when the blocks are entered. * ---------- */ error_info_text = "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->shouldfree = false; } break; case PLPGSQL_DTYPE_ROW: case PLPGSQL_DTYPE_REC: case PLPGSQL_DTYPE_RECFIELD: break; default: elog(ERROR, "unknown dtype %d in plpgsql_exec_function()", func->datums[i]->dtype); } } /* ---------- * Set the magic variable FOUND to false * ---------- */ exec_set_found(&estate, false); /* ---------- * Now call the toplevel block of statements * ---------- */ error_info_text = NULL; error_info_stmt = (PLpgSQL_stmt *) (func->action); if (exec_stmt_block(&estate, func->action) != PLPGSQL_RC_RETURN) { error_info_stmt = NULL; error_info_text = "at END of toplevel PL block"; elog(ERROR, "control reaches end of function without RETURN"); } /* ---------- * We got a return value - process it * ---------- */ error_info_stmt = NULL; error_info_text = "while casting return value to functions return type"; *isNull = estate.retisnull; if (!estate.retistuple) { estate.retval = exec_cast_value(estate.retval, estate.rettype, func->fn_rettype, &(func->fn_retinput), -1, isNull); /* ---------- * If the functions return type isn't by value, * copy the value into upper executor memory context. * ---------- */ if (!*isNull && !func->fn_retbyval) { int len; Datum tmp; if (func->fn_rettyplen < 0) len = VARSIZE(estate.retval); else len = func->fn_rettyplen; tmp = (Datum) SPI_palloc(len); memcpy((void *) tmp, (void *) estate.retval, len); estate.retval = tmp; } } /* ---------- * Restore the previous error info and elog() jump target * ---------- */ error_info_func = save_efunc; error_info_stmt = save_estmt; error_info_text = save_etext; memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); /* ---------- * 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; int i; sigjmp_buf save_restart; PLpgSQL_function *save_efunc; PLpgSQL_stmt *save_estmt; char *save_etext; PLpgSQL_rec *rec_new; PLpgSQL_rec *rec_old; PLpgSQL_var *var; HeapTuple rettup; /* ---------- * Setup debug error info and catch elog() * ---------- */ save_efunc = error_info_func; save_estmt = error_info_stmt; save_etext = error_info_text; error_info_func = func; error_info_stmt = NULL; error_info_text = "while initialization of execution state"; memcpy(&save_restart, &Warn_restart, sizeof(save_restart)); if (sigsetjmp(Warn_restart, 1) != 0) { memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); /* ---------- * If we are the first of cascaded error catchings, * print where this happened * ---------- */ if (error_info_func != NULL) { elog(DEBUG, "Last error occured while executing PL/pgSQL function %s", error_info_func->fn_name); if (error_info_stmt != NULL) { char *stmttype; switch (error_info_stmt->cmd_type) { case PLPGSQL_STMT_BLOCK: stmttype = "blocks variable initialization"; break; case PLPGSQL_STMT_ASSIGN: stmttype = "assignment"; break; case PLPGSQL_STMT_IF: stmttype = "if"; break; case PLPGSQL_STMT_LOOP: stmttype = "loop"; break; case PLPGSQL_STMT_WHILE: stmttype = "while"; break; case PLPGSQL_STMT_FORI: stmttype = "for with integer loopvar"; break; case PLPGSQL_STMT_FORS: stmttype = "for over select rows"; break; case PLPGSQL_STMT_SELECT: stmttype = "select into variables"; break; case PLPGSQL_STMT_EXIT: stmttype = "exit"; break; case PLPGSQL_STMT_RETURN: stmttype = "return"; break; case PLPGSQL_STMT_RAISE: stmttype = "raise"; break; case PLPGSQL_STMT_EXECSQL: stmttype = "SQL statement"; break; default: stmttype = "unknown"; break; } elog(DEBUG, "line %d at %s", error_info_stmt->lineno, stmttype); } else { if (error_info_text != NULL) elog(DEBUG, "%s", error_info_text); else elog(DEBUG, "no more error information available"); } error_info_func = NULL; error_info_stmt = NULL; error_info_text = NULL; } siglongjmp(Warn_restart, 1); } /* ---------- * Setup the execution state * ---------- */ estate.retval = 0; estate.retisnull = false; estate.rettype = InvalidOid; estate.retistuple = func->fn_retistuple; estate.retisset = func->fn_retset; estate.exitlabel = NULL; estate.found_varno = func->found_varno; estate.ndatums = func->ndatums; estate.datums = palloc(sizeof(PLpgSQL_datum *) * estate.ndatums); /* ---------- * Make local execution copies of all the datums * ---------- */ 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_TRIGARG: estate.datums[i] = func->datums[i]; break; default: elog(ERROR, "unknown dtype %d in plpgsql_exec_function()", func->datums[i]->dtype); } } /* ---------- * Put the trig and new tuples into the records * and set the tg_op variable * ---------- */ rec_new = (PLpgSQL_rec *) (estate.datums[func->new_varno]); rec_old = (PLpgSQL_rec *) (estate.datums[func->old_varno]); var = (PLpgSQL_var *) (estate.datums[func->tg_op_varno]); var->isnull = false; 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; var->value = (Datum) textin("INSERT"); } 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; var->value = (Datum) textin("UPDATE"); } 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; var->value = (Datum) textin("DELETE"); } else { rec_new->tup = NULL; rec_new->tupdesc = NULL; var->value = (Datum) textin("UNKNOWN"); } /* ---------- * Fill all the other special tg_ variables * ---------- */ var = (PLpgSQL_var *) (estate.datums[func->tg_name_varno]); var->isnull = false; var->value = (Datum) namein(trigdata->tg_trigger->tgname); var = (PLpgSQL_var *) (estate.datums[func->tg_when_varno]); var->isnull = false; if (TRIGGER_FIRED_BEFORE(trigdata->tg_event)) var->value = (Datum) textin("BEFORE"); else if (TRIGGER_FIRED_AFTER(trigdata->tg_event)) var->value = (Datum) textin("AFTER"); else var->value = (Datum) textin("UNKNOWN"); var = (PLpgSQL_var *) (estate.datums[func->tg_level_varno]); var->isnull = false; if (TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) var->value = (Datum) textin("ROW");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -