1998-08-22 14:38:39 +02:00
|
|
|
/**********************************************************************
|
|
|
|
* pl_exec.c - Executor for the PL/pgSQL
|
|
|
|
* procedural language
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2001-11-05 20:41:56 +01:00
|
|
|
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.49 2001/11/05 19:41:56 tgl Exp $
|
1998-08-22 14:38:39 +02:00
|
|
|
*
|
1998-09-01 06:40:42 +02:00
|
|
|
* This software is copyrighted by Jan Wieck - Hamburg.
|
1998-08-22 14:38:39 +02:00
|
|
|
*
|
1998-09-01 06:40:42 +02:00
|
|
|
* 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.
|
1998-08-22 14:38:39 +02:00
|
|
|
*
|
1998-09-01 06:40:42 +02:00
|
|
|
* 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.
|
1998-08-22 14:38:39 +02:00
|
|
|
*
|
1998-09-01 06:40:42 +02:00
|
|
|
* 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.
|
1998-08-22 14:38:39 +02:00
|
|
|
*
|
|
|
|
**********************************************************************/
|
|
|
|
|
|
|
|
#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"
|
|
|
|
|
2000-07-12 04:37:39 +02:00
|
|
|
#include "access/heapam.h"
|
|
|
|
#include "catalog/pg_proc.h"
|
|
|
|
#include "catalog/pg_type.h"
|
|
|
|
#include "commands/trigger.h"
|
1998-08-22 14:38:39 +02:00
|
|
|
#include "executor/spi.h"
|
1999-01-27 17:15:22 +01:00
|
|
|
#include "executor/spi_priv.h"
|
1998-08-22 14:38:39 +02:00
|
|
|
#include "fmgr.h"
|
2000-12-01 21:43:59 +01:00
|
|
|
#include "optimizer/clauses.h"
|
2000-08-13 04:50:35 +02:00
|
|
|
#include "parser/parse_expr.h"
|
1999-04-20 04:19:59 +02:00
|
|
|
#include "tcop/tcopprot.h"
|
2000-07-12 04:37:39 +02:00
|
|
|
#include "utils/builtins.h"
|
|
|
|
#include "utils/lsyscache.h"
|
1998-08-22 14:38:39 +02:00
|
|
|
#include "utils/syscache.h"
|
|
|
|
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
static PLpgSQL_function *error_info_func = NULL;
|
|
|
|
static PLpgSQL_stmt *error_info_stmt = NULL;
|
|
|
|
static char *error_info_text = NULL;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
|
|
|
|
|
|
|
/************************************************************
|
|
|
|
* Local function forward declarations
|
|
|
|
************************************************************/
|
1998-09-01 06:40:42 +02:00
|
|
|
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);
|
2000-09-05 11:02:18 +02:00
|
|
|
static int exec_stmt_getdiag(PLpgSQL_execstate * estate,
|
2001-03-22 05:01:46 +01:00
|
|
|
PLpgSQL_stmt_getdiag * stmt);
|
1998-09-01 06:40:42 +02:00
|
|
|
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);
|
2001-05-21 16:22:19 +02:00
|
|
|
static int exec_stmt_open(PLpgSQL_execstate * estate,
|
2001-10-25 07:50:21 +02:00
|
|
|
PLpgSQL_stmt_open * stmt);
|
2001-05-21 16:22:19 +02:00
|
|
|
static int exec_stmt_fetch(PLpgSQL_execstate * estate,
|
2001-10-25 07:50:21 +02:00
|
|
|
PLpgSQL_stmt_fetch * stmt);
|
2001-05-21 16:22:19 +02:00
|
|
|
static int exec_stmt_close(PLpgSQL_execstate * estate,
|
2001-10-25 07:50:21 +02:00
|
|
|
PLpgSQL_stmt_close * stmt);
|
1998-09-01 06:40:42 +02:00
|
|
|
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);
|
2000-08-31 15:26:16 +02:00
|
|
|
static int exec_stmt_dynexecute(PLpgSQL_execstate * estate,
|
2001-03-22 05:01:46 +01:00
|
|
|
PLpgSQL_stmt_dynexecute * stmt);
|
2000-08-31 15:26:16 +02:00
|
|
|
static int exec_stmt_dynfors(PLpgSQL_execstate * estate,
|
2001-03-22 05:01:46 +01:00
|
|
|
PLpgSQL_stmt_dynfors * stmt);
|
1998-09-01 06:40:42 +02:00
|
|
|
|
2001-08-02 23:31:23 +02:00
|
|
|
static void plpgsql_estate_setup(PLpgSQL_execstate * estate,
|
2001-10-25 07:50:21 +02:00
|
|
|
PLpgSQL_function * func);
|
2001-08-02 23:31:23 +02:00
|
|
|
static void exec_eval_cleanup(PLpgSQL_execstate * estate);
|
|
|
|
|
1999-01-27 17:15:22 +01:00
|
|
|
static void exec_prepare_plan(PLpgSQL_execstate * estate,
|
1999-05-25 18:15:34 +02:00
|
|
|
PLpgSQL_expr * expr);
|
|
|
|
static bool exec_simple_check_node(Node *node);
|
1999-01-27 17:15:22 +01:00
|
|
|
static void exec_simple_check_plan(PLpgSQL_expr * expr);
|
1999-05-25 18:15:34 +02:00
|
|
|
static Datum exec_eval_simple_expr(PLpgSQL_execstate * estate,
|
|
|
|
PLpgSQL_expr * expr,
|
|
|
|
bool *isNull,
|
|
|
|
Oid *rettype);
|
1999-01-27 17:15:22 +01:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
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,
|
2001-05-21 16:22:19 +02:00
|
|
|
PLpgSQL_expr * expr, int maxtuples, Portal *portalP);
|
1998-09-01 06:40:42 +02:00
|
|
|
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,
|
1998-08-22 14:38:39 +02:00
|
|
|
FmgrInfo *reqinput,
|
2000-01-15 23:43:25 +01:00
|
|
|
Oid reqtypelem,
|
|
|
|
int32 reqtypmod,
|
1998-08-22 14:38:39 +02:00
|
|
|
bool *isnull);
|
1998-09-01 06:40:42 +02:00
|
|
|
static void exec_set_found(PLpgSQL_execstate * estate, bool state);
|
1998-08-22 14:38:39 +02:00
|
|
|
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* plpgsql_exec_function Called by the call handler for
|
|
|
|
* function execution.
|
|
|
|
* ----------
|
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
Datum
|
2000-05-28 19:56:29 +02:00
|
|
|
plpgsql_exec_function(PLpgSQL_function * func, FunctionCallInfo fcinfo)
|
1998-08-22 14:38:39 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
PLpgSQL_execstate estate;
|
|
|
|
int i;
|
|
|
|
sigjmp_buf save_restart;
|
|
|
|
PLpgSQL_function *save_efunc;
|
|
|
|
PLpgSQL_stmt *save_estmt;
|
|
|
|
char *save_etext;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Setup debug error info and catch elog()
|
1998-08-22 14:38:39 +02:00
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
save_efunc = error_info_func;
|
|
|
|
save_estmt = error_info_stmt;
|
|
|
|
save_etext = error_info_text;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
error_info_func = func;
|
|
|
|
error_info_stmt = NULL;
|
|
|
|
error_info_text = "while initialization of execution state";
|
1998-08-22 14:38:39 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
memcpy(&save_restart, &Warn_restart, sizeof(save_restart));
|
|
|
|
if (sigsetjmp(Warn_restart, 1) != 0)
|
|
|
|
{
|
|
|
|
memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* If we are the first of cascaded error catchings, print where
|
|
|
|
* this happened
|
1998-09-01 06:40:42 +02:00
|
|
|
*/
|
|
|
|
if (error_info_func != NULL)
|
1998-08-22 14:38:39 +02:00
|
|
|
{
|
2001-05-28 21:33:24 +02:00
|
|
|
elog(NOTICE, "Error occurred while executing PL/pgSQL function %s",
|
1998-09-01 06:40:42 +02:00
|
|
|
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;
|
2000-09-05 11:02:18 +02:00
|
|
|
case PLPGSQL_STMT_GETDIAG:
|
|
|
|
stmttype = "get diagnostics";
|
|
|
|
break;
|
1998-09-01 06:40:42 +02:00
|
|
|
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;
|
2000-08-31 15:26:16 +02:00
|
|
|
case PLPGSQL_STMT_DYNEXECUTE:
|
|
|
|
stmttype = "execute statement";
|
|
|
|
break;
|
|
|
|
case PLPGSQL_STMT_DYNFORS:
|
|
|
|
stmttype = "for over execute statement";
|
|
|
|
break;
|
2001-05-21 16:22:19 +02:00
|
|
|
case PLPGSQL_STMT_FETCH:
|
|
|
|
stmttype = "fetch";
|
|
|
|
break;
|
|
|
|
case PLPGSQL_STMT_CLOSE:
|
|
|
|
stmttype = "close";
|
|
|
|
break;
|
1998-09-01 06:40:42 +02:00
|
|
|
default:
|
|
|
|
stmttype = "unknown";
|
|
|
|
break;
|
|
|
|
}
|
2001-05-28 21:33:24 +02:00
|
|
|
elog(NOTICE, "line %d at %s", error_info_stmt->lineno,
|
1998-09-01 06:40:42 +02:00
|
|
|
stmttype);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (error_info_text != NULL)
|
2001-05-28 21:33:24 +02:00
|
|
|
elog(NOTICE, "%s", error_info_text);
|
1998-09-01 06:40:42 +02:00
|
|
|
else
|
2001-05-28 21:33:24 +02:00
|
|
|
elog(NOTICE, "no more error information available");
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
error_info_func = NULL;
|
|
|
|
error_info_stmt = NULL;
|
|
|
|
error_info_text = NULL;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
siglongjmp(Warn_restart, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Setup the execution state
|
|
|
|
*/
|
2001-08-02 23:31:23 +02:00
|
|
|
plpgsql_estate_setup(&estate, func);
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* 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);
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* 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];
|
|
|
|
|
2000-05-28 19:56:29 +02:00
|
|
|
var->value = fcinfo->arg[i];
|
|
|
|
var->isnull = fcinfo->argnull[i];
|
2001-05-21 16:22:19 +02:00
|
|
|
var->freeval = false;
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PLPGSQL_DTYPE_ROW:
|
|
|
|
{
|
2000-05-28 19:56:29 +02:00
|
|
|
PLpgSQL_row *row = (PLpgSQL_row *) estate.datums[n];
|
|
|
|
TupleTableSlot *slot = (TupleTableSlot *) fcinfo->arg[i];
|
1998-09-01 06:40:42 +02:00
|
|
|
HeapTuple tup;
|
|
|
|
TupleDesc tupdesc;
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
Assert(slot != NULL && !fcinfo->argnull[i]);
|
2000-05-28 19:56:29 +02:00
|
|
|
tup = slot->val;
|
|
|
|
tupdesc = slot->ttc_tupleDescriptor;
|
1998-09-01 06:40:42 +02:00
|
|
|
exec_move_row(&estate, NULL, row, tup, tupdesc);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
elog(ERROR, "unknown dtype %d in plpgsql_exec_function()",
|
|
|
|
func->datums[i]->dtype);
|
|
|
|
}
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* Initialize the other variables to NULL values for now. The default
|
|
|
|
* values are set when the blocks are entered.
|
1998-09-01 06:40:42 +02:00
|
|
|
*/
|
|
|
|
error_info_text = "while initializing local variables to NULL";
|
|
|
|
for (i = estate.found_varno; i < estate.ndatums; i++)
|
|
|
|
{
|
|
|
|
switch (estate.datums[i]->dtype)
|
1998-08-22 14:38:39 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
case PLPGSQL_DTYPE_VAR:
|
|
|
|
{
|
|
|
|
PLpgSQL_var *var = (PLpgSQL_var *) estate.datums[i];
|
|
|
|
|
|
|
|
var->value = 0;
|
|
|
|
var->isnull = true;
|
2001-05-21 16:22:19 +02:00
|
|
|
var->freeval = false;
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
|
|
|
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);
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Set the magic variable FOUND to false
|
|
|
|
*/
|
|
|
|
exec_set_found(&estate, false);
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* 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");
|
|
|
|
}
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* We got a return value - process it
|
|
|
|
*/
|
1998-08-22 14:38:39 +02:00
|
|
|
error_info_stmt = NULL;
|
1998-09-01 06:40:42 +02:00
|
|
|
error_info_text = "while casting return value to functions return type";
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2000-05-28 19:56:29 +02:00
|
|
|
fcinfo->isnull = estate.retisnull;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-11-05 20:41:56 +01:00
|
|
|
if (!estate.retisnull)
|
1998-09-01 06:40:42 +02:00
|
|
|
{
|
2001-11-05 20:41:56 +01:00
|
|
|
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
|
1998-09-01 06:40:42 +02:00
|
|
|
{
|
2001-11-05 20:41:56 +01:00
|
|
|
/* 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)
|
|
|
|
{
|
|
|
|
int len;
|
|
|
|
Datum tmp;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-11-05 20:41:56 +01:00
|
|
|
if (func->fn_rettyplen < 0)
|
|
|
|
len = VARSIZE(estate.retval);
|
|
|
|
else
|
|
|
|
len = func->fn_rettyplen;
|
1998-09-01 06:40:42 +02:00
|
|
|
|
2001-11-05 20:41:56 +01:00
|
|
|
tmp = (Datum) SPI_palloc(len);
|
|
|
|
memcpy((void *) tmp, (void *) estate.retval, len);
|
|
|
|
estate.retval = tmp;
|
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
|
|
|
}
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-08-02 23:31:23 +02:00
|
|
|
/* Clean up any leftover temporary memory */
|
|
|
|
exec_eval_cleanup(&estate);
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Restore the previous error info and elog() jump target
|
1998-08-22 14:38:39 +02:00
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
error_info_func = save_efunc;
|
|
|
|
error_info_stmt = save_estmt;
|
|
|
|
error_info_text = save_etext;
|
|
|
|
memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Return the functions result
|
|
|
|
*/
|
|
|
|
return estate.retval;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* plpgsql_exec_trigger Called by the call handler for
|
|
|
|
* trigger execution.
|
|
|
|
* ----------
|
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
HeapTuple
|
|
|
|
plpgsql_exec_trigger(PLpgSQL_function * func,
|
|
|
|
TriggerData *trigdata)
|
1998-08-22 14:38:39 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
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;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Setup debug error info and catch elog()
|
1998-08-22 14:38:39 +02:00
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
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));
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* If we are the first of cascaded error catchings, print where
|
|
|
|
* this happened
|
1998-09-01 06:40:42 +02:00
|
|
|
*/
|
|
|
|
if (error_info_func != NULL)
|
|
|
|
{
|
2001-05-28 21:33:24 +02:00
|
|
|
elog(NOTICE, "Error occurred while executing PL/pgSQL function %s",
|
1998-09-01 06:40:42 +02:00
|
|
|
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;
|
2001-02-19 20:49:53 +01:00
|
|
|
case PLPGSQL_STMT_GETDIAG:
|
|
|
|
stmttype = "get diagnostics";
|
|
|
|
break;
|
1998-09-01 06:40:42 +02:00
|
|
|
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;
|
2000-08-31 15:26:16 +02:00
|
|
|
case PLPGSQL_STMT_DYNEXECUTE:
|
|
|
|
stmttype = "execute statement";
|
|
|
|
break;
|
|
|
|
case PLPGSQL_STMT_DYNFORS:
|
|
|
|
stmttype = "for over execute statement";
|
|
|
|
break;
|
1998-09-01 06:40:42 +02:00
|
|
|
default:
|
|
|
|
stmttype = "unknown";
|
|
|
|
break;
|
|
|
|
}
|
2001-05-28 21:33:24 +02:00
|
|
|
elog(NOTICE, "line %d at %s", error_info_stmt->lineno,
|
1998-09-01 06:40:42 +02:00
|
|
|
stmttype);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (error_info_text != NULL)
|
2001-05-28 21:33:24 +02:00
|
|
|
elog(NOTICE, "%s", error_info_text);
|
1998-09-01 06:40:42 +02:00
|
|
|
else
|
2001-05-28 21:33:24 +02:00
|
|
|
elog(NOTICE, "no more error information available");
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
error_info_func = NULL;
|
|
|
|
error_info_stmt = NULL;
|
|
|
|
error_info_text = NULL;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
siglongjmp(Warn_restart, 1);
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Setup the execution state
|
|
|
|
*/
|
2001-08-02 23:31:23 +02:00
|
|
|
plpgsql_estate_setup(&estate, func);
|
1998-09-01 06:40:42 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* Put the trig and new tuples into the records and set the tg_op
|
|
|
|
* variable
|
1998-09-01 06:40:42 +02:00
|
|
|
*/
|
|
|
|
rec_new = (PLpgSQL_rec *) (estate.datums[func->new_varno]);
|
2001-08-02 23:31:23 +02:00
|
|
|
rec_new->freetup = false;
|
|
|
|
rec_new->freetupdesc = false;
|
1998-09-01 06:40:42 +02:00
|
|
|
rec_old = (PLpgSQL_rec *) (estate.datums[func->old_varno]);
|
2001-08-02 23:31:23 +02:00
|
|
|
rec_old->freetup = false;
|
|
|
|
rec_old->freetupdesc = false;
|
1998-09-01 06:40:42 +02:00
|
|
|
var = (PLpgSQL_var *) (estate.datums[func->tg_op_varno]);
|
|
|
|
|
|
|
|
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;
|
2000-07-06 01:12:09 +02:00
|
|
|
var->value = DirectFunctionCall1(textin, CStringGetDatum("INSERT"));
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
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;
|
2000-07-06 01:12:09 +02:00
|
|
|
var->value = DirectFunctionCall1(textin, CStringGetDatum("UPDATE"));
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
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;
|
2000-07-06 01:12:09 +02:00
|
|
|
var->value = DirectFunctionCall1(textin, CStringGetDatum("DELETE"));
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rec_new->tup = NULL;
|
|
|
|
rec_new->tupdesc = NULL;
|
2001-08-02 23:31:23 +02:00
|
|
|
rec_old->tup = NULL;
|
|
|
|
rec_old->tupdesc = NULL;
|
2000-07-06 01:12:09 +02:00
|
|
|
var->value = DirectFunctionCall1(textin, CStringGetDatum("UNKNOWN"));
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
2001-08-02 23:31:23 +02:00
|
|
|
var->isnull = false;
|
|
|
|
var->freeval = true;
|
1998-09-01 06:40:42 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Fill all the other special tg_ variables
|
|
|
|
*/
|
|
|
|
var = (PLpgSQL_var *) (estate.datums[func->tg_name_varno]);
|
|
|
|
var->isnull = false;
|
2001-08-02 23:31:23 +02:00
|
|
|
var->freeval = true;
|
2000-08-03 18:35:08 +02:00
|
|
|
var->value = DirectFunctionCall1(namein,
|
2001-03-22 05:01:46 +01:00
|
|
|
CStringGetDatum(trigdata->tg_trigger->tgname));
|
1998-09-01 06:40:42 +02:00
|
|
|
|
|
|
|
var = (PLpgSQL_var *) (estate.datums[func->tg_when_varno]);
|
|
|
|
var->isnull = false;
|
2001-08-02 23:31:23 +02:00
|
|
|
var->freeval = true;
|
1998-09-01 06:40:42 +02:00
|
|
|
if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
|
2000-07-06 01:12:09 +02:00
|
|
|
var->value = DirectFunctionCall1(textin, CStringGetDatum("BEFORE"));
|
1998-09-01 06:40:42 +02:00
|
|
|
else if (TRIGGER_FIRED_AFTER(trigdata->tg_event))
|
2000-07-06 01:12:09 +02:00
|
|
|
var->value = DirectFunctionCall1(textin, CStringGetDatum("AFTER"));
|
1998-09-01 06:40:42 +02:00
|
|
|
else
|
2000-07-06 01:12:09 +02:00
|
|
|
var->value = DirectFunctionCall1(textin, CStringGetDatum("UNKNOWN"));
|
1998-09-01 06:40:42 +02:00
|
|
|
|
|
|
|
var = (PLpgSQL_var *) (estate.datums[func->tg_level_varno]);
|
|
|
|
var->isnull = false;
|
2001-08-02 23:31:23 +02:00
|
|
|
var->freeval = true;
|
1998-09-01 06:40:42 +02:00
|
|
|
if (TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
|
2000-07-06 01:12:09 +02:00
|
|
|
var->value = DirectFunctionCall1(textin, CStringGetDatum("ROW"));
|
1998-09-01 06:40:42 +02:00
|
|
|
else if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
|
2000-07-06 01:12:09 +02:00
|
|
|
var->value = DirectFunctionCall1(textin, CStringGetDatum("STATEMENT"));
|
1998-09-01 06:40:42 +02:00
|
|
|
else
|
2000-07-06 01:12:09 +02:00
|
|
|
var->value = DirectFunctionCall1(textin, CStringGetDatum("UNKNOWN"));
|
1998-09-01 06:40:42 +02:00
|
|
|
|
|
|
|
var = (PLpgSQL_var *) (estate.datums[func->tg_relid_varno]);
|
|
|
|
var->isnull = false;
|
2001-05-21 16:22:19 +02:00
|
|
|
var->freeval = false;
|
2001-08-02 23:31:23 +02:00
|
|
|
var->value = ObjectIdGetDatum(trigdata->tg_relation->rd_id);
|
1998-09-01 06:40:42 +02:00
|
|
|
|
|
|
|
var = (PLpgSQL_var *) (estate.datums[func->tg_relname_varno]);
|
|
|
|
var->isnull = false;
|
2001-08-02 23:31:23 +02:00
|
|
|
var->freeval = true;
|
2000-08-03 18:35:08 +02:00
|
|
|
var->value = DirectFunctionCall1(namein,
|
2001-03-22 05:01:46 +01:00
|
|
|
CStringGetDatum(RelationGetRelationName(trigdata->tg_relation)));
|
1998-09-01 06:40:42 +02:00
|
|
|
|
|
|
|
var = (PLpgSQL_var *) (estate.datums[func->tg_nargs_varno]);
|
|
|
|
var->isnull = false;
|
2001-05-21 16:22:19 +02:00
|
|
|
var->freeval = false;
|
2001-08-02 23:31:23 +02:00
|
|
|
var->value = Int16GetDatum(trigdata->tg_trigger->tgnargs);
|
1998-09-01 06:40:42 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* Put the actual call argument values into the special execution
|
|
|
|
* state variables
|
1998-09-01 06:40:42 +02:00
|
|
|
*/
|
|
|
|
error_info_text = "while putting call arguments to 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++)
|
2000-07-06 01:12:09 +02:00
|
|
|
estate.trig_argv[i] = DirectFunctionCall1(textin,
|
2001-03-22 05:01:46 +01:00
|
|
|
CStringGetDatum(trigdata->tg_trigger->tgargs[i]));
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* Initialize the other variables to NULL values for now. The default
|
|
|
|
* values are set when the blocks are entered.
|
1998-09-01 06:40:42 +02:00
|
|
|
*/
|
|
|
|
error_info_text = "while initializing local variables to NULL";
|
|
|
|
for (i = estate.found_varno; i < estate.ndatums; i++)
|
|
|
|
{
|
|
|
|
switch (estate.datums[i]->dtype)
|
1998-08-22 14:38:39 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
case PLPGSQL_DTYPE_VAR:
|
|
|
|
{
|
|
|
|
PLpgSQL_var *var = (PLpgSQL_var *) estate.datums[i];
|
|
|
|
|
|
|
|
var->value = 0;
|
|
|
|
var->isnull = true;
|
2001-05-21 16:22:19 +02:00
|
|
|
var->freeval = false;
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PLPGSQL_DTYPE_ROW:
|
|
|
|
case PLPGSQL_DTYPE_REC:
|
|
|
|
case PLPGSQL_DTYPE_RECFIELD:
|
|
|
|
case PLPGSQL_DTYPE_TRIGARG:
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
elog(ERROR, "unknown dtype %d in plpgsql_exec_trigger()",
|
|
|
|
func->datums[i]->dtype);
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Set the magic variable FOUND to false
|
|
|
|
*/
|
|
|
|
exec_set_found(&estate, false);
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* 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 trigger procedure without RETURN");
|
|
|
|
}
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Check that the returned tuple structure has the same attributes,
|
|
|
|
* the relation that fired the trigger has.
|
|
|
|
*
|
2001-03-22 07:16:21 +01:00
|
|
|
* 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
|
1998-09-01 06:40:42 +02:00
|
|
|
*/
|
|
|
|
if (estate.retisnull)
|
|
|
|
rettup = NULL;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TupleDesc td1 = trigdata->tg_relation->rd_att;
|
|
|
|
TupleDesc td2 = estate.rettupdesc;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (td1->natts != td2->natts)
|
|
|
|
elog(ERROR, "returned tuple structure doesn't match table of trigger event");
|
|
|
|
for (i = 1; i <= td1->natts; i++)
|
|
|
|
{
|
|
|
|
if (SPI_gettypeid(td1, i) != SPI_gettypeid(td2, i))
|
|
|
|
elog(ERROR, "returned tuple structure doesn't match table of trigger event");
|
|
|
|
}
|
|
|
|
|
2001-08-02 23:31:23 +02:00
|
|
|
/* Copy tuple to upper executor memory */
|
1998-09-01 06:40:42 +02:00
|
|
|
rettup = SPI_copytuple((HeapTuple) (estate.retval));
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
2001-08-02 23:31:23 +02:00
|
|
|
/* Clean up any leftover temporary memory */
|
|
|
|
exec_eval_cleanup(&estate);
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* 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));
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Return the triggers result
|
|
|
|
*/
|
|
|
|
return rettup;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* Support functions for copying local execution variables
|
|
|
|
* ----------
|
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
static PLpgSQL_var *
|
|
|
|
copy_var(PLpgSQL_var * var)
|
1998-08-22 14:38:39 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
PLpgSQL_var *new = palloc(sizeof(PLpgSQL_var));
|
|
|
|
|
|
|
|
memcpy(new, var, sizeof(PLpgSQL_var));
|
2001-05-21 16:22:19 +02:00
|
|
|
new->freeval = false;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
return new;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
static PLpgSQL_rec *
|
|
|
|
copy_rec(PLpgSQL_rec * rec)
|
1998-08-22 14:38:39 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
PLpgSQL_rec *new = palloc(sizeof(PLpgSQL_rec));
|
1998-08-22 14:38:39 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
memcpy(new, rec, sizeof(PLpgSQL_rec));
|
2001-05-21 16:22:19 +02:00
|
|
|
new->tup = NULL;
|
|
|
|
new->tupdesc = NULL;
|
|
|
|
new->freetup = false;
|
|
|
|
new->freetupdesc = false;
|
1998-09-01 06:40:42 +02:00
|
|
|
|
|
|
|
return new;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* exec_stmt_block Execute a block of statements
|
|
|
|
* ----------
|
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
static int
|
|
|
|
exec_stmt_block(PLpgSQL_execstate * estate, PLpgSQL_stmt_block * block)
|
1998-08-22 14:38:39 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
int rc;
|
|
|
|
int i;
|
|
|
|
int n;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* First initialize all variables declared in this block
|
|
|
|
*/
|
|
|
|
for (i = 0; i < block->n_initvars; i++)
|
|
|
|
{
|
|
|
|
n = block->initvarnos[i];
|
1998-08-22 14:38:39 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
switch (estate->datums[n]->dtype)
|
|
|
|
{
|
|
|
|
case PLPGSQL_DTYPE_VAR:
|
|
|
|
{
|
|
|
|
PLpgSQL_var *var = (PLpgSQL_var *) (estate->datums[n]);
|
|
|
|
|
2001-05-21 16:22:19 +02:00
|
|
|
if (var->freeval)
|
|
|
|
{
|
2001-10-25 07:50:21 +02:00
|
|
|
pfree((void *) (var->value));
|
2001-05-21 16:22:19 +02:00
|
|
|
var->freeval = false;
|
|
|
|
}
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
if (!var->isconst || var->isnull)
|
|
|
|
{
|
|
|
|
if (var->default_val == NULL)
|
|
|
|
{
|
|
|
|
var->value = (Datum) 0;
|
|
|
|
var->isnull = true;
|
|
|
|
if (var->notnull)
|
|
|
|
elog(ERROR, "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]);
|
|
|
|
|
2001-05-21 16:22:19 +02:00
|
|
|
if (rec->freetup)
|
|
|
|
{
|
|
|
|
heap_freetuple(rec->tup);
|
|
|
|
FreeTupleDesc(rec->tupdesc);
|
|
|
|
rec->freetup = false;
|
|
|
|
}
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
rec->tup = NULL;
|
|
|
|
rec->tupdesc = NULL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PLPGSQL_DTYPE_RECFIELD:
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
elog(ERROR, "unknown dtype %d in exec_stmt_block()", estate->datums[n]->dtype);
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Execute the statements in the block's body
|
|
|
|
*/
|
|
|
|
rc = exec_stmts(estate, block->body);
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Handle the return code.
|
|
|
|
*/
|
|
|
|
switch (rc)
|
|
|
|
{
|
|
|
|
case PLPGSQL_RC_OK:
|
|
|
|
return PLPGSQL_RC_OK;
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
case PLPGSQL_RC_RETURN:
|
|
|
|
return PLPGSQL_RC_RETURN;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
default:
|
|
|
|
elog(ERROR, "unknown rc %d from exec_stmt()", rc);
|
|
|
|
}
|
1998-08-22 14:38:39 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
return PLPGSQL_RC_OK;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* exec_stmts Iterate over a list of statements
|
|
|
|
* as long as their return code is OK
|
|
|
|
* ----------
|
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
static int
|
|
|
|
exec_stmts(PLpgSQL_execstate * estate, PLpgSQL_stmts * stmts)
|
1998-08-22 14:38:39 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
int rc;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < stmts->stmts_used; i++)
|
|
|
|
{
|
|
|
|
rc = exec_stmt(estate, (PLpgSQL_stmt *) (stmts->stmts[i]));
|
|
|
|
if (rc != PLPGSQL_RC_OK)
|
|
|
|
return rc;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
return PLPGSQL_RC_OK;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* exec_stmt Distribute one statement to the statements
|
|
|
|
* type specific execution function.
|
|
|
|
* ----------
|
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
static int
|
|
|
|
exec_stmt(PLpgSQL_execstate * estate, PLpgSQL_stmt * stmt)
|
1998-08-22 14:38:39 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
PLpgSQL_stmt *save_estmt;
|
|
|
|
int rc = -1;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
save_estmt = error_info_stmt;
|
|
|
|
error_info_stmt = stmt;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
switch (stmt->cmd_type)
|
|
|
|
{
|
|
|
|
case PLPGSQL_STMT_BLOCK:
|
|
|
|
rc = exec_stmt_block(estate, (PLpgSQL_stmt_block *) stmt);
|
|
|
|
break;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
case PLPGSQL_STMT_ASSIGN:
|
|
|
|
rc = exec_stmt_assign(estate, (PLpgSQL_stmt_assign *) stmt);
|
|
|
|
break;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2000-09-05 11:02:18 +02:00
|
|
|
case PLPGSQL_STMT_GETDIAG:
|
|
|
|
rc = exec_stmt_getdiag(estate, (PLpgSQL_stmt_getdiag *) stmt);
|
|
|
|
break;
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
case PLPGSQL_STMT_IF:
|
|
|
|
rc = exec_stmt_if(estate, (PLpgSQL_stmt_if *) stmt);
|
|
|
|
break;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
case PLPGSQL_STMT_LOOP:
|
|
|
|
rc = exec_stmt_loop(estate, (PLpgSQL_stmt_loop *) stmt);
|
|
|
|
break;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
case PLPGSQL_STMT_WHILE:
|
|
|
|
rc = exec_stmt_while(estate, (PLpgSQL_stmt_while *) stmt);
|
|
|
|
break;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
case PLPGSQL_STMT_FORI:
|
|
|
|
rc = exec_stmt_fori(estate, (PLpgSQL_stmt_fori *) stmt);
|
|
|
|
break;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
case PLPGSQL_STMT_FORS:
|
|
|
|
rc = exec_stmt_fors(estate, (PLpgSQL_stmt_fors *) stmt);
|
|
|
|
break;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
case PLPGSQL_STMT_SELECT:
|
|
|
|
rc = exec_stmt_select(estate, (PLpgSQL_stmt_select *) stmt);
|
|
|
|
break;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
case PLPGSQL_STMT_EXIT:
|
|
|
|
rc = exec_stmt_exit(estate, (PLpgSQL_stmt_exit *) stmt);
|
|
|
|
break;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
case PLPGSQL_STMT_RETURN:
|
|
|
|
rc = exec_stmt_return(estate, (PLpgSQL_stmt_return *) stmt);
|
|
|
|
break;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
case PLPGSQL_STMT_RAISE:
|
|
|
|
rc = exec_stmt_raise(estate, (PLpgSQL_stmt_raise *) stmt);
|
|
|
|
break;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
case PLPGSQL_STMT_EXECSQL:
|
|
|
|
rc = exec_stmt_execsql(estate, (PLpgSQL_stmt_execsql *) stmt);
|
|
|
|
break;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2000-08-31 15:26:16 +02:00
|
|
|
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;
|
|
|
|
|
2001-05-21 16:22:19 +02:00
|
|
|
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;
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
default:
|
|
|
|
error_info_stmt = save_estmt;
|
|
|
|
elog(ERROR, "unknown cmdtype %d in exec_stmt",
|
|
|
|
stmt->cmd_type);
|
|
|
|
}
|
1998-08-22 14:38:39 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
error_info_stmt = save_estmt;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
return rc;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* exec_stmt_assign Evaluate an expression and
|
|
|
|
* put the result into a variable.
|
2001-05-28 21:33:24 +02:00
|
|
|
*
|
|
|
|
* For no very good reason, this is also used for PERFORM statements.
|
1998-08-22 14:38:39 +02:00
|
|
|
* ----------
|
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
static int
|
|
|
|
exec_stmt_assign(PLpgSQL_execstate * estate, PLpgSQL_stmt_assign * stmt)
|
1998-08-22 14:38:39 +02:00
|
|
|
{
|
2001-10-25 07:50:21 +02:00
|
|
|
PLpgSQL_expr *expr = stmt->expr;
|
2001-05-28 21:33:24 +02:00
|
|
|
|
|
|
|
if (stmt->varno >= 0)
|
|
|
|
exec_assign_expr(estate, estate->datums[stmt->varno], expr);
|
1998-09-01 06:40:42 +02:00
|
|
|
else
|
2001-05-28 21:33:24 +02:00
|
|
|
{
|
|
|
|
/*
|
2001-10-25 07:50:21 +02:00
|
|
|
* PERFORM: evaluate query and discard result. This cannot share
|
|
|
|
* code with the assignment case since we do not wish to
|
|
|
|
* constraint the discarded result to be only one row/column.
|
2001-05-28 21:33:24 +02:00
|
|
|
*/
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If not already done create a plan for this expression
|
|
|
|
*/
|
|
|
|
if (expr->plan == NULL)
|
|
|
|
exec_prepare_plan(estate, expr);
|
|
|
|
|
|
|
|
rc = exec_run_select(estate, expr, 0, NULL);
|
|
|
|
if (rc != SPI_OK_SELECT)
|
|
|
|
elog(ERROR, "query \"%s\" didn't return data", expr->query);
|
|
|
|
|
2001-08-02 23:31:23 +02:00
|
|
|
exec_eval_cleanup(estate);
|
2001-05-28 21:33:24 +02:00
|
|
|
}
|
1998-08-22 14:38:39 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
return PLPGSQL_RC_OK;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
2000-09-05 11:02:18 +02:00
|
|
|
/* ----------
|
2001-03-22 05:01:46 +01:00
|
|
|
* exec_stmt_getdiag Put internal PG information into
|
|
|
|
* specified variables.
|
2000-09-05 11:02:18 +02:00
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
exec_stmt_getdiag(PLpgSQL_execstate * estate, PLpgSQL_stmt_getdiag * stmt)
|
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
int i;
|
|
|
|
PLpgSQL_datum *var;
|
|
|
|
bool isnull = false;
|
2000-09-05 11:02:18 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
for (i = 0; i < stmt->ndtitems; i++)
|
2000-09-05 11:02:18 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
PLpgSQL_diag_item *dtitem = &stmt->dtitems[i];
|
2001-02-19 20:49:53 +01:00
|
|
|
|
|
|
|
if (dtitem->target <= 0)
|
|
|
|
continue;
|
2001-03-22 05:01:46 +01:00
|
|
|
|
2001-02-19 20:49:53 +01:00
|
|
|
var = (estate->datums[dtitem->target]);
|
2000-09-05 11:02:18 +02:00
|
|
|
|
|
|
|
if (var == NULL)
|
2001-02-19 20:49:53 +01:00
|
|
|
continue;
|
2000-09-05 11:02:18 +02:00
|
|
|
|
2001-02-19 20:49:53 +01:00
|
|
|
switch (dtitem->item)
|
2000-09-05 11:02:18 +02:00
|
|
|
{
|
2001-02-19 20:49:53 +01:00
|
|
|
case PLPGSQL_GETDIAG_ROW_COUNT:
|
2000-09-05 11:02:18 +02:00
|
|
|
|
2001-08-02 23:31:23 +02:00
|
|
|
exec_assign_value(estate, var,
|
|
|
|
UInt32GetDatum(estate->eval_processed),
|
2001-02-19 20:49:53 +01:00
|
|
|
INT4OID, &isnull);
|
|
|
|
break;
|
2000-09-05 11:02:18 +02:00
|
|
|
|
2001-02-19 20:49:53 +01:00
|
|
|
case PLPGSQL_GETDIAG_RESULT_OID:
|
2000-09-05 11:02:18 +02:00
|
|
|
|
2001-08-02 23:31:23 +02:00
|
|
|
exec_assign_value(estate, var,
|
|
|
|
ObjectIdGetDatum(estate->eval_lastoid),
|
2001-02-19 20:49:53 +01:00
|
|
|
OIDOID, &isnull);
|
|
|
|
break;
|
2000-09-05 11:02:18 +02:00
|
|
|
|
|
|
|
default:
|
2001-03-22 05:01:46 +01:00
|
|
|
|
2001-02-19 20:49:53 +01:00
|
|
|
elog(ERROR, "unknown attribute request %d in get_diagnostic",
|
|
|
|
dtitem->item);
|
|
|
|
}
|
|
|
|
}
|
2001-03-22 05:01:46 +01:00
|
|
|
|
2001-02-19 20:49:53 +01:00
|
|
|
return PLPGSQL_RC_OK;
|
2000-09-05 11:02:18 +02:00
|
|
|
}
|
1998-08-22 14:38:39 +02:00
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* exec_stmt_if Evaluate a bool expression and
|
|
|
|
* execute the true or false body
|
|
|
|
* conditionally.
|
|
|
|
* ----------
|
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
static int
|
|
|
|
exec_stmt_if(PLpgSQL_execstate * estate, PLpgSQL_stmt_if * stmt)
|
1998-08-22 14:38:39 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
Datum value;
|
|
|
|
Oid valtype;
|
|
|
|
bool isnull = false;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
value = exec_eval_expr(estate, stmt->cond, &isnull, &valtype);
|
2001-08-02 23:31:23 +02:00
|
|
|
exec_eval_cleanup(estate);
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-08-02 23:31:23 +02:00
|
|
|
if (!isnull && DatumGetBool(value))
|
1998-09-01 06:40:42 +02:00
|
|
|
{
|
|
|
|
if (stmt->true_body != NULL)
|
|
|
|
return exec_stmts(estate, stmt->true_body);
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if (stmt->false_body != NULL)
|
|
|
|
return exec_stmts(estate, stmt->false_body);
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
return PLPGSQL_RC_OK;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* exec_stmt_loop Loop over statements until
|
|
|
|
* an exit occurs.
|
|
|
|
* ----------
|
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
static int
|
|
|
|
exec_stmt_loop(PLpgSQL_execstate * estate, PLpgSQL_stmt_loop * stmt)
|
1998-08-22 14:38:39 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
int rc;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
rc = exec_stmts(estate, stmt->body);
|
1998-08-22 14:38:39 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
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_RETURN:
|
|
|
|
return PLPGSQL_RC_RETURN;
|
|
|
|
|
|
|
|
default:
|
|
|
|
elog(ERROR, "unknown rc %d from exec_stmts()", rc);
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
return PLPGSQL_RC_OK;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* exec_stmt_while Loop over statements as long
|
|
|
|
* as an expression evaluates to
|
|
|
|
* true or an exit occurs.
|
|
|
|
* ----------
|
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
static int
|
|
|
|
exec_stmt_while(PLpgSQL_execstate * estate, PLpgSQL_stmt_while * stmt)
|
1998-08-22 14:38:39 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
Datum value;
|
|
|
|
Oid valtype;
|
|
|
|
bool isnull = false;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
value = exec_eval_expr(estate, stmt->cond, &isnull, &valtype);
|
2001-08-02 23:31:23 +02:00
|
|
|
exec_eval_cleanup(estate);
|
|
|
|
if (isnull || !DatumGetBool(value))
|
1998-09-01 06:40:42 +02:00
|
|
|
break;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
rc = exec_stmts(estate, stmt->body);
|
1998-08-22 14:38:39 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
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_RETURN:
|
|
|
|
return PLPGSQL_RC_RETURN;
|
|
|
|
|
|
|
|
default:
|
|
|
|
elog(ERROR, "unknown rc %d from exec_stmts()", rc);
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
return PLPGSQL_RC_OK;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* exec_stmt_fori Iterate an integer variable
|
|
|
|
* from a lower to an upper value.
|
|
|
|
* Loop can be left with exit.
|
|
|
|
* ----------
|
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
static int
|
|
|
|
exec_stmt_fori(PLpgSQL_execstate * estate, PLpgSQL_stmt_fori * stmt)
|
1998-08-22 14:38:39 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
PLpgSQL_var *var;
|
|
|
|
Datum value;
|
|
|
|
Oid valtype;
|
|
|
|
bool isnull = false;
|
|
|
|
int rc;
|
|
|
|
|
2001-08-02 23:31:23 +02:00
|
|
|
var = (PLpgSQL_var *) (estate->datums[stmt->var->varno]);
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Get the value of the lower bound into the loop var
|
1998-08-22 14:38:39 +02:00
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
value = exec_eval_expr(estate, stmt->lower, &isnull, &valtype);
|
|
|
|
value = exec_cast_value(value, valtype, var->datatype->typoid,
|
|
|
|
&(var->datatype->typinput),
|
2000-01-15 23:43:25 +01:00
|
|
|
var->datatype->typelem,
|
1998-09-01 06:40:42 +02:00
|
|
|
var->datatype->atttypmod, &isnull);
|
|
|
|
if (isnull)
|
|
|
|
elog(ERROR, "lower bound of FOR loop cannot be NULL");
|
|
|
|
var->value = value;
|
|
|
|
var->isnull = false;
|
2001-08-02 23:31:23 +02:00
|
|
|
exec_eval_cleanup(estate);
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Get the value of the upper bound
|
1998-08-22 14:38:39 +02:00
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
value = exec_eval_expr(estate, stmt->upper, &isnull, &valtype);
|
|
|
|
value = exec_cast_value(value, valtype, var->datatype->typoid,
|
|
|
|
&(var->datatype->typinput),
|
2000-01-15 23:43:25 +01:00
|
|
|
var->datatype->typelem,
|
1998-09-01 06:40:42 +02:00
|
|
|
var->datatype->atttypmod, &isnull);
|
|
|
|
if (isnull)
|
|
|
|
elog(ERROR, "upper bound of FOR loop cannot be NULL");
|
2001-08-02 23:31:23 +02:00
|
|
|
exec_eval_cleanup(estate);
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Now do the loop
|
1998-08-22 14:38:39 +02:00
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
exec_set_found(estate, false);
|
|
|
|
for (;;)
|
|
|
|
{
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Check bounds
|
|
|
|
*/
|
|
|
|
if (stmt->reverse)
|
|
|
|
{
|
|
|
|
if ((int4) (var->value) < (int4) value)
|
|
|
|
break;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if ((int4) (var->value) > (int4) value)
|
|
|
|
break;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
exec_set_found(estate, true);
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Execute the statements
|
|
|
|
*/
|
|
|
|
rc = exec_stmts(estate, stmt->body);
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Check returncode
|
|
|
|
*/
|
|
|
|
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_RETURN:
|
|
|
|
return PLPGSQL_RC_RETURN;
|
|
|
|
|
|
|
|
default:
|
|
|
|
elog(ERROR, "unknown rc %d from exec_stmts()", rc);
|
|
|
|
}
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Increase/decrease loop var
|
|
|
|
*/
|
|
|
|
if (stmt->reverse)
|
1999-01-17 22:53:32 +01:00
|
|
|
var->value--;
|
1998-09-01 06:40:42 +02:00
|
|
|
else
|
1999-01-17 22:53:32 +01:00
|
|
|
var->value++;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
return PLPGSQL_RC_OK;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* exec_stmt_fors Execute a query, assign each
|
|
|
|
* tuple to a record or row and
|
|
|
|
* execute a group of statements
|
|
|
|
* for it.
|
|
|
|
* ----------
|
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
static int
|
|
|
|
exec_stmt_fors(PLpgSQL_execstate * estate, PLpgSQL_stmt_fors * stmt)
|
1998-08-22 14:38:39 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
PLpgSQL_rec *rec = NULL;
|
|
|
|
PLpgSQL_row *row = NULL;
|
|
|
|
SPITupleTable *tuptab;
|
2001-05-21 16:22:19 +02:00
|
|
|
Portal portal;
|
1998-09-01 06:40:42 +02:00
|
|
|
int rc;
|
|
|
|
int i;
|
|
|
|
int n;
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Initialize the global found variable to false
|
1998-08-22 14:38:39 +02:00
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
exec_set_found(estate, false);
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Determine if we assign to a record or a row
|
1998-08-22 14:38:39 +02:00
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
if (stmt->rec != NULL)
|
|
|
|
rec = (PLpgSQL_rec *) (estate->datums[stmt->rec->recno]);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (stmt->row != NULL)
|
|
|
|
row = (PLpgSQL_row *) (estate->datums[stmt->row->rowno]);
|
|
|
|
else
|
|
|
|
elog(ERROR, "unsupported target in exec_stmt_fors()");
|
|
|
|
}
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2001-10-25 07:50:21 +02:00
|
|
|
* Open the implicit cursor for the statement and fetch the initial 10
|
|
|
|
* rows.
|
1998-08-22 14:38:39 +02:00
|
|
|
*/
|
2001-05-21 16:22:19 +02:00
|
|
|
exec_run_select(estate, stmt->query, 0, &portal);
|
2001-08-02 23:31:23 +02:00
|
|
|
|
2001-05-21 16:22:19 +02:00
|
|
|
SPI_cursor_fetch(portal, true, 10);
|
1998-09-01 06:40:42 +02:00
|
|
|
n = SPI_processed;
|
2001-08-02 23:31:23 +02:00
|
|
|
tuptab = SPI_tuptable;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* If the query didn't return any row, set the target to NULL and
|
|
|
|
* return.
|
1998-09-01 06:40:42 +02:00
|
|
|
*/
|
|
|
|
if (n == 0)
|
|
|
|
{
|
|
|
|
exec_move_row(estate, rec, row, NULL, NULL);
|
2001-05-21 16:22:19 +02:00
|
|
|
SPI_cursor_close(portal);
|
1998-08-22 14:38:39 +02:00
|
|
|
return PLPGSQL_RC_OK;
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* There are tuples, so set found to true
|
|
|
|
*/
|
|
|
|
exec_set_found(estate, true);
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Now do the loop
|
|
|
|
*/
|
2001-05-21 16:22:19 +02:00
|
|
|
for (;;)
|
1998-09-01 06:40:42 +02:00
|
|
|
{
|
2001-05-21 16:22:19 +02:00
|
|
|
for (i = 0; i < n; i++)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Assign the tuple to the target
|
|
|
|
*/
|
|
|
|
exec_move_row(estate, rec, row, tuptab->vals[i], tuptab->tupdesc);
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-05-21 16:22:19 +02:00
|
|
|
/*
|
|
|
|
* Execute the statements
|
|
|
|
*/
|
|
|
|
rc = exec_stmts(estate, stmt->body);
|
1998-09-01 06:40:42 +02:00
|
|
|
|
2001-05-21 16:22:19 +02:00
|
|
|
/*
|
|
|
|
* Check returncode
|
|
|
|
*/
|
|
|
|
switch (rc)
|
|
|
|
{
|
|
|
|
case PLPGSQL_RC_OK:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PLPGSQL_RC_EXIT:
|
|
|
|
SPI_freetuptable(tuptab);
|
|
|
|
SPI_cursor_close(portal);
|
|
|
|
|
|
|
|
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;
|
1998-09-01 06:40:42 +02:00
|
|
|
return PLPGSQL_RC_OK;
|
|
|
|
|
2001-05-21 16:22:19 +02:00
|
|
|
case PLPGSQL_RC_RETURN:
|
|
|
|
SPI_freetuptable(tuptab);
|
|
|
|
SPI_cursor_close(portal);
|
1998-09-01 06:40:42 +02:00
|
|
|
|
2001-05-21 16:22:19 +02:00
|
|
|
return PLPGSQL_RC_RETURN;
|
|
|
|
|
|
|
|
default:
|
|
|
|
elog(ERROR, "unknown rc %d from exec_stmts()", rc);
|
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
2001-05-21 16:22:19 +02:00
|
|
|
|
|
|
|
SPI_freetuptable(tuptab);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fetch the next 50 tuples
|
|
|
|
*/
|
|
|
|
SPI_cursor_fetch(portal, true, 50);
|
2001-08-02 23:31:23 +02:00
|
|
|
n = SPI_processed;
|
|
|
|
tuptab = SPI_tuptable;
|
|
|
|
|
|
|
|
if (n == 0)
|
2001-05-21 16:22:19 +02:00
|
|
|
break;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
2001-05-21 16:22:19 +02:00
|
|
|
/*
|
|
|
|
* Close the implicit cursor
|
|
|
|
*/
|
|
|
|
SPI_cursor_close(portal);
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
return PLPGSQL_RC_OK;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* exec_stmt_select Run a query and assign the first
|
|
|
|
* row to a record or rowtype.
|
|
|
|
* ----------
|
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
static int
|
|
|
|
exec_stmt_select(PLpgSQL_execstate * estate, PLpgSQL_stmt_select * stmt)
|
1998-08-22 14:38:39 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
PLpgSQL_rec *rec = NULL;
|
|
|
|
PLpgSQL_row *row = NULL;
|
|
|
|
SPITupleTable *tuptab;
|
2001-08-02 23:31:23 +02:00
|
|
|
uint32 n;
|
1998-09-01 06:40:42 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Initialize the global found variable to false
|
|
|
|
*/
|
|
|
|
exec_set_found(estate, false);
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Determine if we assign to a record or a row
|
|
|
|
*/
|
|
|
|
if (stmt->rec != NULL)
|
|
|
|
rec = (PLpgSQL_rec *) (estate->datums[stmt->rec->recno]);
|
2001-08-02 23:31:23 +02:00
|
|
|
else if (stmt->row != NULL)
|
|
|
|
row = (PLpgSQL_row *) (estate->datums[stmt->row->rowno]);
|
1998-09-01 06:40:42 +02:00
|
|
|
else
|
2001-08-02 23:31:23 +02:00
|
|
|
elog(ERROR, "unsupported target in exec_stmt_select()");
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Run the query
|
|
|
|
*/
|
2001-05-21 16:22:19 +02:00
|
|
|
exec_run_select(estate, stmt->query, 1, NULL);
|
2001-08-02 23:31:23 +02:00
|
|
|
n = estate->eval_processed;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* If the query didn't return any row, set the target to NULL and
|
|
|
|
* return.
|
1998-09-01 06:40:42 +02:00
|
|
|
*/
|
|
|
|
if (n == 0)
|
|
|
|
{
|
|
|
|
exec_move_row(estate, rec, row, NULL, NULL);
|
2001-08-02 23:31:23 +02:00
|
|
|
exec_eval_cleanup(estate);
|
1998-09-01 06:40:42 +02:00
|
|
|
return PLPGSQL_RC_OK;
|
|
|
|
}
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Put the result into the target and set found to true
|
|
|
|
*/
|
2001-08-02 23:31:23 +02:00
|
|
|
tuptab = estate->eval_tuptable;
|
1998-09-01 06:40:42 +02:00
|
|
|
exec_move_row(estate, rec, row, tuptab->vals[0], tuptab->tupdesc);
|
|
|
|
exec_set_found(estate, true);
|
2001-08-02 23:31:23 +02:00
|
|
|
|
|
|
|
exec_eval_cleanup(estate);
|
1998-09-01 06:40:42 +02:00
|
|
|
|
|
|
|
return PLPGSQL_RC_OK;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* exec_stmt_exit Start exiting loop(s) or blocks
|
|
|
|
* ----------
|
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
static int
|
|
|
|
exec_stmt_exit(PLpgSQL_execstate * estate, PLpgSQL_stmt_exit * stmt)
|
1998-08-22 14:38:39 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
Datum value;
|
|
|
|
Oid valtype;
|
|
|
|
bool isnull = false;
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* If the exit has a condition, check that it's true
|
|
|
|
*/
|
|
|
|
if (stmt->cond != NULL)
|
|
|
|
{
|
|
|
|
value = exec_eval_expr(estate, stmt->cond, &isnull, &valtype);
|
2001-08-02 23:31:23 +02:00
|
|
|
exec_eval_cleanup(estate);
|
|
|
|
if (isnull || !DatumGetBool(value))
|
1998-09-01 06:40:42 +02:00
|
|
|
return PLPGSQL_RC_OK;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
estate->exitlabel = stmt->label;
|
|
|
|
return PLPGSQL_RC_EXIT;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* exec_stmt_return Evaluate an expression and start
|
|
|
|
* returning from the function.
|
|
|
|
* ----------
|
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
static int
|
|
|
|
exec_stmt_return(PLpgSQL_execstate * estate, PLpgSQL_stmt_return * stmt)
|
1998-08-22 14:38:39 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
if (estate->retistuple)
|
|
|
|
{
|
2001-08-02 23:31:23 +02:00
|
|
|
/* initialize for null result tuple */
|
|
|
|
estate->retval = (Datum) 0;
|
|
|
|
estate->rettupdesc = NULL;
|
|
|
|
estate->retisnull = true;
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
if (stmt->retrecno >= 0)
|
|
|
|
{
|
|
|
|
PLpgSQL_rec *rec = (PLpgSQL_rec *) (estate->datums[stmt->retrecno]);
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-08-02 23:31:23 +02:00
|
|
|
if (HeapTupleIsValid(rec->tup))
|
|
|
|
{
|
2001-11-05 20:41:56 +01:00
|
|
|
estate->retval = (Datum) rec->tup;
|
|
|
|
estate->rettupdesc = rec->tupdesc;
|
2001-08-02 23:31:23 +02:00
|
|
|
estate->retisnull = false;
|
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
return PLPGSQL_RC_RETURN;
|
|
|
|
}
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-08-02 23:31:23 +02:00
|
|
|
if (stmt->expr != NULL)
|
1998-09-01 06:40:42 +02:00
|
|
|
{
|
2001-05-21 16:22:19 +02:00
|
|
|
exec_run_select(estate, stmt->expr, 1, NULL);
|
2001-08-02 23:31:23 +02:00
|
|
|
if (estate->eval_processed > 0)
|
|
|
|
{
|
2001-11-05 20:41:56 +01:00
|
|
|
estate->retval = (Datum) estate->eval_tuptable->vals[0];
|
|
|
|
estate->rettupdesc = estate->eval_tuptable->tupdesc;
|
2001-08-02 23:31:23 +02:00
|
|
|
estate->retisnull = false;
|
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
|
|
|
return PLPGSQL_RC_RETURN;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
estate->retval = exec_eval_expr(estate, stmt->expr,
|
|
|
|
&(estate->retisnull),
|
|
|
|
&(estate->rettype));
|
1998-08-22 14:38:39 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
return PLPGSQL_RC_RETURN;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* exec_stmt_raise Build a message and throw it with
|
|
|
|
* elog()
|
|
|
|
* ----------
|
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
static int
|
|
|
|
exec_stmt_raise(PLpgSQL_execstate * estate, PLpgSQL_stmt_raise * stmt)
|
1998-08-22 14:38:39 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
HeapTuple typetup;
|
|
|
|
Form_pg_type typeStruct;
|
|
|
|
FmgrInfo finfo_output;
|
|
|
|
char *extval;
|
|
|
|
int pidx = 0;
|
|
|
|
char c[2] = {0, 0};
|
|
|
|
char *cp;
|
|
|
|
PLpgSQL_dstring ds;
|
|
|
|
PLpgSQL_var *var;
|
|
|
|
PLpgSQL_rec *rec;
|
|
|
|
PLpgSQL_recfield *recfield;
|
|
|
|
int fno;
|
|
|
|
|
|
|
|
plpgsql_dstring_init(&ds);
|
|
|
|
|
|
|
|
for (cp = stmt->message; *cp; cp++)
|
|
|
|
{
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* Occurences of a single % are replaced by the next arguments
|
|
|
|
* external representation. Double %'s are left as is so elog()
|
|
|
|
* will also don't touch them.
|
1998-09-01 06:40:42 +02:00
|
|
|
*/
|
|
|
|
if ((c[0] = *cp) == '%')
|
|
|
|
{
|
|
|
|
cp++;
|
|
|
|
if (*cp == '%')
|
|
|
|
{
|
|
|
|
plpgsql_dstring_append(&ds, c);
|
|
|
|
plpgsql_dstring_append(&ds, c);
|
|
|
|
continue;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
cp--;
|
|
|
|
if (pidx >= stmt->nparams)
|
|
|
|
{
|
|
|
|
plpgsql_dstring_append(&ds, c);
|
|
|
|
plpgsql_dstring_append(&ds, c);
|
|
|
|
continue;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
switch (estate->datums[stmt->params[pidx]]->dtype)
|
|
|
|
{
|
|
|
|
case PLPGSQL_DTYPE_VAR:
|
|
|
|
var = (PLpgSQL_var *)
|
|
|
|
(estate->datums[stmt->params[pidx]]);
|
|
|
|
if (var->isnull)
|
|
|
|
extval = "<NULL>";
|
|
|
|
else
|
|
|
|
{
|
2000-11-16 23:30:52 +01:00
|
|
|
typetup = SearchSysCache(TYPEOID,
|
2001-03-22 05:01:46 +01:00
|
|
|
ObjectIdGetDatum(var->datatype->typoid),
|
2000-11-16 23:30:52 +01:00
|
|
|
0, 0, 0);
|
1998-09-01 06:40:42 +02:00
|
|
|
if (!HeapTupleIsValid(typetup))
|
2001-08-02 23:31:23 +02:00
|
|
|
elog(ERROR, "cache lookup for type %u failed",
|
2000-11-16 23:30:52 +01:00
|
|
|
var->datatype->typoid);
|
1998-09-01 06:40:42 +02:00
|
|
|
typeStruct = (Form_pg_type) GETSTRUCT(typetup);
|
|
|
|
|
|
|
|
fmgr_info(typeStruct->typoutput, &finfo_output);
|
2000-05-30 06:25:00 +02:00
|
|
|
extval = DatumGetCString(FunctionCall3(&finfo_output,
|
2001-03-22 05:01:46 +01:00
|
|
|
var->value,
|
|
|
|
ObjectIdGetDatum(typeStruct->typelem),
|
|
|
|
Int32GetDatum(var->datatype->atttypmod)));
|
2000-11-16 23:30:52 +01:00
|
|
|
ReleaseSysCache(typetup);
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
|
|
|
plpgsql_dstring_append(&ds, extval);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PLPGSQL_DTYPE_RECFIELD:
|
|
|
|
recfield = (PLpgSQL_recfield *)
|
|
|
|
(estate->datums[stmt->params[pidx]]);
|
|
|
|
rec = (PLpgSQL_rec *)
|
|
|
|
(estate->datums[recfield->recno]);
|
|
|
|
if (!HeapTupleIsValid(rec->tup))
|
|
|
|
extval = "<NULL>";
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fno = SPI_fnumber(rec->tupdesc, recfield->fieldname);
|
|
|
|
if (fno == SPI_ERROR_NOATTRIBUTE)
|
|
|
|
elog(ERROR, "record %s has no field %s", rec->refname, recfield->fieldname);
|
|
|
|
extval = SPI_getvalue(rec->tup, rec->tupdesc, fno);
|
2000-04-28 02:12:44 +02:00
|
|
|
if (extval == NULL)
|
|
|
|
extval = "<NULL>";
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
|
|
|
plpgsql_dstring_append(&ds, extval);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PLPGSQL_DTYPE_TRIGARG:
|
|
|
|
{
|
|
|
|
PLpgSQL_trigarg *trigarg;
|
|
|
|
int value;
|
|
|
|
Oid valtype;
|
|
|
|
bool valisnull = false;
|
|
|
|
|
|
|
|
trigarg = (PLpgSQL_trigarg *)
|
|
|
|
(estate->datums[stmt->params[pidx]]);
|
|
|
|
value = (int) exec_eval_expr(estate, trigarg->argnum,
|
|
|
|
&valisnull, &valtype);
|
2001-08-02 23:31:23 +02:00
|
|
|
exec_eval_cleanup(estate);
|
1998-09-01 06:40:42 +02:00
|
|
|
if (valisnull)
|
|
|
|
extval = "<INDEX_IS_NULL>";
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (value < 0 || value >= estate->trig_nargs)
|
|
|
|
extval = "<OUT_OF_RANGE>";
|
|
|
|
else
|
2000-07-06 01:12:09 +02:00
|
|
|
extval = DatumGetCString(DirectFunctionCall1(textout,
|
2001-03-22 05:01:46 +01:00
|
|
|
estate->trig_argv[value]));
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
|
|
|
plpgsql_dstring_append(&ds, extval);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
c[0] = '?';
|
|
|
|
plpgsql_dstring_append(&ds, c);
|
|
|
|
break;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
pidx++;
|
|
|
|
continue;
|
|
|
|
}
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2001-08-02 23:31:23 +02:00
|
|
|
* Occurrences of single ' are removed. double ' are reduced to
|
2001-03-22 07:16:21 +01:00
|
|
|
* single ones.
|
1998-09-01 06:40:42 +02:00
|
|
|
*/
|
|
|
|
if (*cp == '\'')
|
|
|
|
{
|
|
|
|
cp++;
|
|
|
|
if (*cp == '\'')
|
|
|
|
plpgsql_dstring_append(&ds, c);
|
|
|
|
else
|
|
|
|
cp--;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
plpgsql_dstring_append(&ds, c);
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Now suppress debug info and throw the elog()
|
1998-08-22 14:38:39 +02:00
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
if (stmt->elog_level == ERROR)
|
|
|
|
{
|
|
|
|
error_info_func = NULL;
|
|
|
|
error_info_stmt = NULL;
|
|
|
|
error_info_text = NULL;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
elog(stmt->elog_level, "%s", plpgsql_dstring_get(&ds));
|
|
|
|
plpgsql_dstring_free(&ds);
|
1998-08-22 14:38:39 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
return PLPGSQL_RC_OK;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-08-02 23:31:23 +02:00
|
|
|
/* ----------
|
|
|
|
* Initialize an empty estate
|
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
plpgsql_estate_setup(PLpgSQL_execstate * estate,
|
|
|
|
PLpgSQL_function * func)
|
|
|
|
{
|
|
|
|
estate->retval = (Datum) 0;
|
|
|
|
estate->retisnull = true;
|
|
|
|
estate->rettype = InvalidOid;
|
|
|
|
estate->retistuple = func->fn_retistuple;
|
|
|
|
estate->rettupdesc = NULL;
|
|
|
|
estate->retisset = func->fn_retset;
|
|
|
|
estate->exitlabel = NULL;
|
|
|
|
|
|
|
|
estate->trig_nargs = 0;
|
|
|
|
estate->trig_argv = NULL;
|
|
|
|
|
|
|
|
estate->found_varno = func->found_varno;
|
|
|
|
estate->ndatums = func->ndatums;
|
|
|
|
estate->datums = palloc(sizeof(PLpgSQL_datum *) * estate->ndatums);
|
|
|
|
/* caller is expected to fill the datums array */
|
|
|
|
|
|
|
|
estate->eval_tuptable = NULL;
|
|
|
|
estate->eval_processed = 0;
|
|
|
|
estate->eval_lastoid = InvalidOid;
|
|
|
|
estate->eval_econtext = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* Release temporary memory used by expression/subselect evaluation
|
|
|
|
*
|
|
|
|
* NB: the result of the evaluation is no longer valid after this is done,
|
|
|
|
* unless it is a pass-by-value datatype.
|
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
exec_eval_cleanup(PLpgSQL_execstate * estate)
|
|
|
|
{
|
|
|
|
/* Clear result of a full SPI_exec */
|
|
|
|
if (estate->eval_tuptable != NULL)
|
|
|
|
SPI_freetuptable(estate->eval_tuptable);
|
|
|
|
estate->eval_tuptable = NULL;
|
|
|
|
|
|
|
|
/* Clear result of exec_eval_simple_expr */
|
|
|
|
if (estate->eval_econtext != NULL)
|
|
|
|
FreeExprContext(estate->eval_econtext);
|
|
|
|
estate->eval_econtext = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-01-27 17:15:22 +01:00
|
|
|
/* ----------
|
|
|
|
* Generate a prepared plan
|
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
exec_prepare_plan(PLpgSQL_execstate * estate,
|
1999-05-25 18:15:34 +02:00
|
|
|
PLpgSQL_expr * expr)
|
1999-01-27 17:15:22 +01:00
|
|
|
{
|
|
|
|
PLpgSQL_var *var;
|
|
|
|
PLpgSQL_rec *rec;
|
|
|
|
PLpgSQL_recfield *recfield;
|
|
|
|
int i;
|
|
|
|
int fno;
|
|
|
|
void *plan;
|
|
|
|
Oid *argtypes;
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2001-10-25 07:50:21 +02:00
|
|
|
* We need a temporary argtypes array to load with data. (The finished
|
|
|
|
* plan structure will contain a copy of it.)
|
2001-08-02 23:31:23 +02:00
|
|
|
*
|
|
|
|
* +1 is just to avoid palloc(0) error.
|
1999-01-27 17:15:22 +01:00
|
|
|
*/
|
2001-08-02 23:31:23 +02:00
|
|
|
argtypes = palloc(sizeof(Oid *) * (expr->nparams + 1));
|
1999-01-27 17:15:22 +01:00
|
|
|
|
|
|
|
for (i = 0; i < expr->nparams; i++)
|
|
|
|
{
|
|
|
|
switch (estate->datums[expr->params[i]]->dtype)
|
|
|
|
{
|
|
|
|
case PLPGSQL_DTYPE_VAR:
|
|
|
|
var = (PLpgSQL_var *) (estate->datums[expr->params[i]]);
|
|
|
|
argtypes[i] = var->datatype->typoid;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PLPGSQL_DTYPE_RECFIELD:
|
|
|
|
recfield = (PLpgSQL_recfield *) (estate->datums[expr->params[i]]);
|
|
|
|
rec = (PLpgSQL_rec *) (estate->datums[recfield->recno]);
|
|
|
|
|
|
|
|
if (!HeapTupleIsValid(rec->tup))
|
|
|
|
elog(ERROR, "record %s is unassigned yet", rec->refname);
|
|
|
|
fno = SPI_fnumber(rec->tupdesc, recfield->fieldname);
|
|
|
|
if (fno == SPI_ERROR_NOATTRIBUTE)
|
|
|
|
elog(ERROR, "record %s has no field %s", rec->refname, recfield->fieldname);
|
|
|
|
argtypes[i] = SPI_gettypeid(rec->tupdesc, fno);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PLPGSQL_DTYPE_TRIGARG:
|
|
|
|
argtypes[i] = (Oid) TEXTOID;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2000-01-15 23:43:25 +01:00
|
|
|
elog(ERROR, "unknown parameter dtype %d in exec_run_select()",
|
|
|
|
estate->datums[expr->params[i]]->dtype);
|
1999-01-27 17:15:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1999-01-27 17:15:22 +01:00
|
|
|
* Generate and save the plan
|
|
|
|
*/
|
|
|
|
plan = SPI_prepare(expr->query, expr->nparams, argtypes);
|
|
|
|
if (plan == NULL)
|
|
|
|
elog(ERROR, "SPI_prepare() failed on \"%s\"", expr->query);
|
|
|
|
expr->plan = SPI_saveplan(plan);
|
2001-08-02 23:31:23 +02:00
|
|
|
expr->plan_argtypes = ((_SPI_plan *) expr->plan)->argtypes;
|
1999-01-27 17:15:22 +01:00
|
|
|
expr->plan_simple_expr = NULL;
|
|
|
|
exec_simple_check_plan(expr);
|
2001-08-02 23:31:23 +02:00
|
|
|
|
|
|
|
pfree(argtypes);
|
1999-01-27 17:15:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
1998-08-22 14:38:39 +02:00
|
|
|
/* ----------
|
|
|
|
* exec_stmt_execsql Execute an SQL statement not
|
|
|
|
* returning any data.
|
|
|
|
* ----------
|
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
static int
|
|
|
|
exec_stmt_execsql(PLpgSQL_execstate * estate,
|
|
|
|
PLpgSQL_stmt_execsql * stmt)
|
1998-08-22 14:38:39 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
PLpgSQL_var *var;
|
|
|
|
PLpgSQL_rec *rec;
|
|
|
|
PLpgSQL_recfield *recfield;
|
|
|
|
PLpgSQL_trigarg *trigarg;
|
|
|
|
int tgargno;
|
|
|
|
Oid tgargoid;
|
|
|
|
int fno;
|
|
|
|
int i;
|
|
|
|
Datum *values;
|
|
|
|
char *nulls;
|
|
|
|
int rc;
|
|
|
|
PLpgSQL_expr *expr = stmt->sqlstmt;
|
|
|
|
bool isnull;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* On the first call for this expression generate the plan
|
|
|
|
*/
|
|
|
|
if (expr->plan == NULL)
|
1999-01-27 17:15:22 +01:00
|
|
|
exec_prepare_plan(estate, expr);
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Now build up the values and nulls arguments for SPI_execp()
|
|
|
|
*/
|
|
|
|
values = palloc(sizeof(Datum) * (expr->nparams + 1));
|
|
|
|
nulls = palloc(expr->nparams + 1);
|
|
|
|
|
|
|
|
for (i = 0; i < expr->nparams; i++)
|
|
|
|
{
|
|
|
|
switch (estate->datums[expr->params[i]]->dtype)
|
|
|
|
{
|
|
|
|
case PLPGSQL_DTYPE_VAR:
|
|
|
|
var = (PLpgSQL_var *) (estate->datums[expr->params[i]]);
|
|
|
|
values[i] = var->value;
|
|
|
|
if (var->isnull)
|
|
|
|
nulls[i] = 'n';
|
|
|
|
else
|
|
|
|
nulls[i] = ' ';
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PLPGSQL_DTYPE_RECFIELD:
|
|
|
|
recfield = (PLpgSQL_recfield *) (estate->datums[expr->params[i]]);
|
|
|
|
rec = (PLpgSQL_rec *) (estate->datums[recfield->recno]);
|
|
|
|
|
|
|
|
if (!HeapTupleIsValid(rec->tup))
|
|
|
|
elog(ERROR, "record %s is unassigned yet", rec->refname);
|
|
|
|
fno = SPI_fnumber(rec->tupdesc, recfield->fieldname);
|
|
|
|
if (fno == SPI_ERROR_NOATTRIBUTE)
|
|
|
|
elog(ERROR, "record %s has no field %s", rec->refname, recfield->fieldname);
|
|
|
|
|
|
|
|
if (expr->plan_argtypes[i] != SPI_gettypeid(rec->tupdesc, fno))
|
|
|
|
elog(ERROR, "type of %s.%s doesn't match that when preparing the plan", rec->refname, recfield->fieldname);
|
|
|
|
|
|
|
|
values[i] = SPI_getbinval(rec->tup, rec->tupdesc, fno, &isnull);
|
|
|
|
if (isnull)
|
|
|
|
nulls[i] = 'n';
|
|
|
|
else
|
|
|
|
nulls[i] = ' ';
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PLPGSQL_DTYPE_TRIGARG:
|
|
|
|
trigarg = (PLpgSQL_trigarg *) (estate->datums[expr->params[i]]);
|
|
|
|
tgargno = (int) exec_eval_expr(estate, trigarg->argnum,
|
|
|
|
&isnull, &tgargoid);
|
2001-08-02 23:31:23 +02:00
|
|
|
exec_eval_cleanup(estate);
|
1998-09-01 06:40:42 +02:00
|
|
|
if (isnull || tgargno < 0 || tgargno >= estate->trig_nargs)
|
|
|
|
{
|
|
|
|
values[i] = 0;
|
|
|
|
nulls[i] = 'n';
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
values[i] = estate->trig_argv[tgargno];
|
|
|
|
nulls[i] = ' ';
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
elog(ERROR, "unknown parameter dtype %d in exec_stmt_execsql()", estate->datums[expr->params[i]]->dtype);
|
|
|
|
}
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
nulls[i] = '\0';
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Execute the plan
|
|
|
|
*/
|
|
|
|
rc = SPI_execp(expr->plan, values, nulls, 0);
|
|
|
|
switch (rc)
|
|
|
|
{
|
|
|
|
case SPI_OK_UTILITY:
|
|
|
|
case SPI_OK_SELINTO:
|
|
|
|
case SPI_OK_INSERT:
|
|
|
|
case SPI_OK_DELETE:
|
|
|
|
case SPI_OK_UPDATE:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SPI_OK_SELECT:
|
|
|
|
elog(ERROR, "unexpected SELECT query in exec_stmt_execsql()");
|
|
|
|
|
|
|
|
default:
|
|
|
|
elog(ERROR, "error executing query \"%s\"",
|
|
|
|
expr->query);
|
|
|
|
}
|
2001-08-02 23:31:23 +02:00
|
|
|
|
2001-10-25 07:50:21 +02:00
|
|
|
/*
|
|
|
|
* Release any result tuples from SPI_execp (probably shouldn't be
|
|
|
|
* any)
|
|
|
|
*/
|
2001-08-02 23:31:23 +02:00
|
|
|
SPI_freetuptable(SPI_tuptable);
|
|
|
|
|
|
|
|
/* Save result info for GET DIAGNOSTICS */
|
|
|
|
estate->eval_processed = SPI_processed;
|
|
|
|
estate->eval_lastoid = SPI_lastoid;
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
pfree(values);
|
|
|
|
pfree(nulls);
|
|
|
|
|
|
|
|
return PLPGSQL_RC_OK;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-08-31 15:26:16 +02:00
|
|
|
/* ----------
|
|
|
|
* exec_stmt_dynexecute Execute a dynamic SQL query not
|
|
|
|
* returning any data.
|
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
exec_stmt_dynexecute(PLpgSQL_execstate * estate,
|
2001-03-22 05:01:46 +01:00
|
|
|
PLpgSQL_stmt_dynexecute * stmt)
|
2000-08-31 15:26:16 +02:00
|
|
|
{
|
|
|
|
Datum query;
|
|
|
|
bool isnull = false;
|
|
|
|
Oid restype;
|
|
|
|
char *querystr;
|
|
|
|
HeapTuple typetup;
|
|
|
|
Form_pg_type typeStruct;
|
|
|
|
FmgrInfo finfo_output;
|
2001-01-04 03:38:02 +01:00
|
|
|
int exec_res;
|
2000-08-31 15:26:16 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* First we evaluate the string expression after the EXECUTE keyword.
|
|
|
|
* It's result is the querystring we have to execute.
|
2000-08-31 15:26:16 +02:00
|
|
|
*/
|
|
|
|
query = exec_eval_expr(estate, stmt->query, &isnull, &restype);
|
|
|
|
if (isnull)
|
2001-01-04 03:38:02 +01:00
|
|
|
elog(ERROR, "cannot EXECUTE NULL query");
|
2000-08-31 15:26:16 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2000-08-31 15:26:16 +02:00
|
|
|
* Get the C-String representation.
|
|
|
|
*/
|
2000-11-16 23:30:52 +01:00
|
|
|
typetup = SearchSysCache(TYPEOID,
|
|
|
|
ObjectIdGetDatum(restype),
|
|
|
|
0, 0, 0);
|
2000-08-31 15:26:16 +02:00
|
|
|
if (!HeapTupleIsValid(typetup))
|
2001-08-02 23:31:23 +02:00
|
|
|
elog(ERROR, "cache lookup for type %u failed", restype);
|
2000-08-31 15:26:16 +02:00
|
|
|
typeStruct = (Form_pg_type) GETSTRUCT(typetup);
|
|
|
|
|
|
|
|
fmgr_info(typeStruct->typoutput, &finfo_output);
|
|
|
|
querystr = DatumGetCString(FunctionCall3(&finfo_output,
|
2000-11-16 23:30:52 +01:00
|
|
|
query,
|
2001-03-22 05:01:46 +01:00
|
|
|
ObjectIdGetDatum(typeStruct->typelem),
|
2000-11-16 23:30:52 +01:00
|
|
|
Int32GetDatum(-1)));
|
2000-08-31 15:26:16 +02:00
|
|
|
|
2000-11-16 23:30:52 +01:00
|
|
|
ReleaseSysCache(typetup);
|
2001-08-02 23:31:23 +02:00
|
|
|
exec_eval_cleanup(estate);
|
2000-11-16 23:30:52 +01:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* Call SPI_exec() without preparing a saved plan. The returncode can
|
|
|
|
* be any standard OK. Note that while a SELECT is allowed, its
|
|
|
|
* results will be discarded.
|
2000-08-31 15:26:16 +02:00
|
|
|
*/
|
2001-01-04 03:38:02 +01:00
|
|
|
exec_res = SPI_exec(querystr, 0);
|
|
|
|
switch (exec_res)
|
2000-08-31 15:26:16 +02:00
|
|
|
{
|
2001-01-04 03:38:02 +01:00
|
|
|
case SPI_OK_SELECT:
|
2000-08-31 15:26:16 +02:00
|
|
|
case SPI_OK_INSERT:
|
|
|
|
case SPI_OK_UPDATE:
|
|
|
|
case SPI_OK_DELETE:
|
2001-01-04 03:38:02 +01:00
|
|
|
case SPI_OK_UTILITY:
|
2000-08-31 15:26:16 +02:00
|
|
|
break;
|
|
|
|
|
2001-01-04 03:38:02 +01:00
|
|
|
case 0:
|
2001-03-22 05:01:46 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Also allow a zero return, which implies the querystring
|
2001-01-04 03:38:02 +01:00
|
|
|
* contained no commands.
|
|
|
|
*/
|
2000-08-31 15:26:16 +02:00
|
|
|
break;
|
|
|
|
|
2001-02-09 01:14:26 +01:00
|
|
|
case SPI_OK_SELINTO:
|
2001-03-22 05:01:46 +01:00
|
|
|
|
2001-02-09 01:14:26 +01:00
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* Disallow this for now, because its behavior is not
|
2001-03-22 07:16:21 +01:00
|
|
|
* consistent with SELECT INTO in a normal plpgsql context. We
|
|
|
|
* need to reimplement EXECUTE to parse the string as a
|
2001-03-22 05:01:46 +01:00
|
|
|
* plpgsql command, not just feed it to SPI_exec.
|
2001-02-09 01:14:26 +01:00
|
|
|
*/
|
|
|
|
elog(ERROR, "EXECUTE of SELECT ... INTO is not implemented yet");
|
|
|
|
break;
|
|
|
|
|
2000-08-31 15:26:16 +02:00
|
|
|
default:
|
2001-01-04 03:38:02 +01:00
|
|
|
elog(ERROR, "unexpected error %d in EXECUTE of query '%s'",
|
|
|
|
exec_res, querystr);
|
2000-08-31 15:26:16 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2001-08-02 23:31:23 +02:00
|
|
|
/* Release any result from SPI_exec, as well as the querystring */
|
|
|
|
SPI_freetuptable(SPI_tuptable);
|
2000-08-31 15:26:16 +02:00
|
|
|
pfree(querystr);
|
2001-08-02 23:31:23 +02:00
|
|
|
|
|
|
|
/* Save result info for GET DIAGNOSTICS */
|
|
|
|
estate->eval_processed = SPI_processed;
|
|
|
|
estate->eval_lastoid = SPI_lastoid;
|
|
|
|
|
2000-08-31 15:26:16 +02:00
|
|
|
return PLPGSQL_RC_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* exec_stmt_dynfors Execute a dynamic query, assign each
|
|
|
|
* tuple to a record or row and
|
|
|
|
* execute a group of statements
|
|
|
|
* for it.
|
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
exec_stmt_dynfors(PLpgSQL_execstate * estate, PLpgSQL_stmt_dynfors * stmt)
|
|
|
|
{
|
|
|
|
Datum query;
|
|
|
|
bool isnull = false;
|
|
|
|
Oid restype;
|
|
|
|
char *querystr;
|
|
|
|
PLpgSQL_rec *rec = NULL;
|
|
|
|
PLpgSQL_row *row = NULL;
|
|
|
|
SPITupleTable *tuptab;
|
|
|
|
int rc;
|
|
|
|
int i;
|
|
|
|
int n;
|
|
|
|
HeapTuple typetup;
|
|
|
|
Form_pg_type typeStruct;
|
|
|
|
FmgrInfo finfo_output;
|
2001-10-25 07:50:21 +02:00
|
|
|
void *plan;
|
2001-05-21 16:22:19 +02:00
|
|
|
Portal portal;
|
2000-08-31 15:26:16 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2000-08-31 15:26:16 +02:00
|
|
|
* Initialize the global found variable to false
|
|
|
|
*/
|
|
|
|
exec_set_found(estate, false);
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2000-08-31 15:26:16 +02:00
|
|
|
* Determine if we assign to a record or a row
|
|
|
|
*/
|
|
|
|
if (stmt->rec != NULL)
|
|
|
|
rec = (PLpgSQL_rec *) (estate->datums[stmt->rec->recno]);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (stmt->row != NULL)
|
|
|
|
row = (PLpgSQL_row *) (estate->datums[stmt->row->rowno]);
|
|
|
|
else
|
|
|
|
elog(ERROR, "unsupported target in exec_stmt_fors()");
|
|
|
|
}
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* Evaluate the string expression after the EXECUTE keyword. It's
|
|
|
|
* result is the querystring we have to execute.
|
2000-08-31 15:26:16 +02:00
|
|
|
*/
|
|
|
|
query = exec_eval_expr(estate, stmt->query, &isnull, &restype);
|
|
|
|
if (isnull)
|
|
|
|
elog(ERROR, "cannot EXECUTE NULL-query");
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2000-08-31 15:26:16 +02:00
|
|
|
* Get the C-String representation.
|
|
|
|
*/
|
2000-11-16 23:30:52 +01:00
|
|
|
typetup = SearchSysCache(TYPEOID,
|
|
|
|
ObjectIdGetDatum(restype),
|
|
|
|
0, 0, 0);
|
2000-08-31 15:26:16 +02:00
|
|
|
if (!HeapTupleIsValid(typetup))
|
2001-08-02 23:31:23 +02:00
|
|
|
elog(ERROR, "cache lookup for type %u failed", restype);
|
2000-08-31 15:26:16 +02:00
|
|
|
typeStruct = (Form_pg_type) GETSTRUCT(typetup);
|
|
|
|
|
|
|
|
fmgr_info(typeStruct->typoutput, &finfo_output);
|
|
|
|
querystr = DatumGetCString(FunctionCall3(&finfo_output,
|
2001-03-22 05:01:46 +01:00
|
|
|
query,
|
|
|
|
ObjectIdGetDatum(typeStruct->typelem),
|
|
|
|
Int32GetDatum(-1)));
|
2000-08-31 15:26:16 +02:00
|
|
|
|
2000-11-16 23:30:52 +01:00
|
|
|
ReleaseSysCache(typetup);
|
2001-08-02 23:31:23 +02:00
|
|
|
exec_eval_cleanup(estate);
|
2000-11-16 23:30:52 +01:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2001-05-21 16:22:19 +02:00
|
|
|
* Prepare a plan and open an implicit cursor for the query
|
2000-08-31 15:26:16 +02:00
|
|
|
*/
|
2001-05-21 16:22:19 +02:00
|
|
|
plan = SPI_prepare(querystr, 0, NULL);
|
|
|
|
if (plan == NULL)
|
|
|
|
elog(ERROR, "SPI_prepare() failed for dynamic query \"%s\"", querystr);
|
|
|
|
portal = SPI_cursor_open(NULL, plan, NULL, NULL);
|
|
|
|
if (portal == NULL)
|
|
|
|
elog(ERROR, "failed to open implicit cursor for dynamic query \"%s\"",
|
2001-10-25 07:50:21 +02:00
|
|
|
querystr);
|
2000-08-31 15:26:16 +02:00
|
|
|
pfree(querystr);
|
2001-05-21 16:22:19 +02:00
|
|
|
SPI_freeplan(plan);
|
2000-08-31 15:26:16 +02:00
|
|
|
|
2001-05-21 16:22:19 +02:00
|
|
|
/*
|
|
|
|
* Fetch the initial 10 tuples
|
|
|
|
*/
|
|
|
|
SPI_cursor_fetch(portal, true, 10);
|
2000-08-31 15:26:16 +02:00
|
|
|
n = SPI_processed;
|
2001-08-02 23:31:23 +02:00
|
|
|
tuptab = SPI_tuptable;
|
2000-08-31 15:26:16 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* If the query didn't return any row, set the target to NULL and
|
|
|
|
* return.
|
2000-08-31 15:26:16 +02:00
|
|
|
*/
|
|
|
|
if (n == 0)
|
|
|
|
{
|
|
|
|
exec_move_row(estate, rec, row, NULL, NULL);
|
2001-05-21 16:22:19 +02:00
|
|
|
SPI_cursor_close(portal);
|
2000-08-31 15:26:16 +02:00
|
|
|
return PLPGSQL_RC_OK;
|
|
|
|
}
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2000-08-31 15:26:16 +02:00
|
|
|
* There are tuples, so set found to true
|
|
|
|
*/
|
|
|
|
exec_set_found(estate, true);
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2000-08-31 15:26:16 +02:00
|
|
|
* Now do the loop
|
|
|
|
*/
|
2001-05-21 16:22:19 +02:00
|
|
|
for (;;)
|
2000-08-31 15:26:16 +02:00
|
|
|
{
|
2001-05-21 16:22:19 +02:00
|
|
|
for (i = 0; i < n; i++)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Assign the tuple to the target
|
|
|
|
*/
|
|
|
|
exec_move_row(estate, rec, row, tuptab->vals[i], tuptab->tupdesc);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Execute the statements
|
|
|
|
*/
|
|
|
|
rc = exec_stmts(estate, stmt->body);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check returncode
|
|
|
|
*/
|
|
|
|
switch (rc)
|
|
|
|
{
|
|
|
|
case PLPGSQL_RC_OK:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PLPGSQL_RC_EXIT:
|
|
|
|
SPI_freetuptable(tuptab);
|
|
|
|
SPI_cursor_close(portal);
|
|
|
|
|
|
|
|
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_RETURN:
|
|
|
|
SPI_freetuptable(tuptab);
|
|
|
|
SPI_cursor_close(portal);
|
|
|
|
|
|
|
|
return PLPGSQL_RC_RETURN;
|
|
|
|
|
|
|
|
default:
|
|
|
|
elog(ERROR, "unknown rc %d from exec_stmts()", rc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SPI_freetuptable(tuptab);
|
2001-03-22 07:16:21 +01:00
|
|
|
|
|
|
|
/*
|
2001-05-21 16:22:19 +02:00
|
|
|
* Fetch the next 50 tuples
|
2000-08-31 15:26:16 +02:00
|
|
|
*/
|
2001-05-21 16:22:19 +02:00
|
|
|
SPI_cursor_fetch(portal, true, 50);
|
2001-08-02 23:31:23 +02:00
|
|
|
n = SPI_processed;
|
|
|
|
tuptab = SPI_tuptable;
|
|
|
|
|
|
|
|
if (n == 0)
|
2001-05-21 16:22:19 +02:00
|
|
|
break;
|
|
|
|
}
|
2000-08-31 15:26:16 +02:00
|
|
|
|
2001-05-21 16:22:19 +02:00
|
|
|
/*
|
|
|
|
* Close the cursor
|
|
|
|
*/
|
|
|
|
SPI_cursor_close(portal);
|
|
|
|
|
|
|
|
return PLPGSQL_RC_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* exec_stmt_open Execute an OPEN cursor statement
|
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
exec_stmt_open(PLpgSQL_execstate * estate, PLpgSQL_stmt_open * stmt)
|
|
|
|
{
|
|
|
|
PLpgSQL_var *curvar = NULL;
|
2001-10-25 07:50:21 +02:00
|
|
|
char *curname = NULL;
|
2001-05-21 16:22:19 +02:00
|
|
|
PLpgSQL_expr *query = NULL;
|
|
|
|
Portal portal;
|
|
|
|
|
|
|
|
PLpgSQL_var *var;
|
|
|
|
PLpgSQL_rec *rec;
|
|
|
|
PLpgSQL_recfield *recfield;
|
|
|
|
PLpgSQL_trigarg *trigarg;
|
|
|
|
int tgargno;
|
|
|
|
Oid tgargoid;
|
|
|
|
int i;
|
|
|
|
Datum *values;
|
|
|
|
char *nulls;
|
|
|
|
int fno;
|
|
|
|
bool isnull;
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* Get the cursor variable and if it has an assigned name, check
|
|
|
|
* that it's not in use currently.
|
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
curvar = (PLpgSQL_var *) (estate->datums[stmt->curvar]);
|
|
|
|
if (!curvar->isnull)
|
|
|
|
{
|
|
|
|
curname = DatumGetCString(DirectFunctionCall1(textout, curvar->value));
|
|
|
|
if (SPI_cursor_find(curname) != NULL)
|
|
|
|
elog(ERROR, "cursor \"%s\" already in use", curname);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* Process the OPEN according to it's type.
|
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
if (stmt->query != NULL)
|
|
|
|
{
|
|
|
|
/* ----------
|
|
|
|
* This is an OPEN refcursor FOR SELECT ...
|
|
|
|
*
|
|
|
|
* We just make sure the query is planned. The real work is
|
|
|
|
* done downstairs.
|
|
|
|
* ----------
|
2000-08-31 15:26:16 +02:00
|
|
|
*/
|
2001-10-25 07:50:21 +02:00
|
|
|
query = stmt->query;
|
2001-05-21 16:22:19 +02:00
|
|
|
if (query->plan == NULL)
|
|
|
|
exec_prepare_plan(estate, query);
|
|
|
|
}
|
|
|
|
else if (stmt->dynquery != NULL)
|
|
|
|
{
|
|
|
|
/* ----------
|
|
|
|
* This is an OPEN refcursor FOR EXECUTE ...
|
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
Datum queryD;
|
|
|
|
Oid restype;
|
|
|
|
char *querystr;
|
|
|
|
HeapTuple typetup;
|
|
|
|
Form_pg_type typeStruct;
|
|
|
|
FmgrInfo finfo_output;
|
2001-10-25 07:50:21 +02:00
|
|
|
void *curplan = NULL;
|
2001-05-21 16:22:19 +02:00
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* We evaluate the string expression after the
|
|
|
|
* EXECUTE keyword. It's result is the querystring we have
|
|
|
|
* to execute.
|
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
queryD = exec_eval_expr(estate, stmt->dynquery, &isnull, &restype);
|
|
|
|
if (isnull)
|
|
|
|
elog(ERROR, "cannot EXECUTE NULL query");
|
2000-08-31 15:26:16 +02:00
|
|
|
|
2001-05-21 16:22:19 +02:00
|
|
|
/* ----------
|
|
|
|
* Get the C-String representation.
|
|
|
|
* ----------
|
2000-08-31 15:26:16 +02:00
|
|
|
*/
|
2001-05-21 16:22:19 +02:00
|
|
|
typetup = SearchSysCache(TYPEOID,
|
|
|
|
ObjectIdGetDatum(restype),
|
|
|
|
0, 0, 0);
|
|
|
|
if (!HeapTupleIsValid(typetup))
|
2001-08-02 23:31:23 +02:00
|
|
|
elog(ERROR, "cache lookup for type %u failed", restype);
|
2001-05-21 16:22:19 +02:00
|
|
|
typeStruct = (Form_pg_type) GETSTRUCT(typetup);
|
|
|
|
|
|
|
|
fmgr_info(typeStruct->typoutput, &finfo_output);
|
|
|
|
querystr = DatumGetCString(FunctionCall3(&finfo_output,
|
|
|
|
queryD,
|
2001-10-25 07:50:21 +02:00
|
|
|
ObjectIdGetDatum(typeStruct->typelem),
|
2001-05-21 16:22:19 +02:00
|
|
|
Int32GetDatum(-1)));
|
|
|
|
|
|
|
|
ReleaseSysCache(typetup);
|
2001-08-02 23:31:23 +02:00
|
|
|
exec_eval_cleanup(estate);
|
2001-05-21 16:22:19 +02:00
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* Now we prepare a query plan for it and open a cursor
|
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
curplan = SPI_prepare(querystr, 0, NULL);
|
|
|
|
portal = SPI_cursor_open(curname, curplan, NULL, NULL);
|
|
|
|
if (portal == NULL)
|
|
|
|
elog(ERROR, "Failed to open cursor");
|
|
|
|
pfree(querystr);
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* Store the eventually assigned cursor name in the cursor variable
|
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
if (curvar->freeval)
|
2001-10-25 07:50:21 +02:00
|
|
|
pfree((void *) (curvar->value));
|
2001-05-21 16:22:19 +02:00
|
|
|
|
|
|
|
curvar->value = DirectFunctionCall1(textin, CStringGetDatum(portal->name));
|
|
|
|
curvar->isnull = false;
|
|
|
|
curvar->freeval = true;
|
|
|
|
|
|
|
|
return PLPGSQL_RC_OK;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* ----------
|
|
|
|
* This is an OPEN cursor
|
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
if (stmt->argquery != NULL)
|
2000-08-31 15:26:16 +02:00
|
|
|
{
|
2001-05-21 16:22:19 +02:00
|
|
|
/* ----------
|
|
|
|
* Er - OPEN CURSOR (args). We fake a SELECT ... INTO ...
|
|
|
|
* statement to evaluate the args and put 'em into the
|
|
|
|
* internal row.
|
|
|
|
* ----------
|
|
|
|
*/
|
2001-10-25 07:50:21 +02:00
|
|
|
PLpgSQL_stmt_select set_args;
|
2001-05-21 16:22:19 +02:00
|
|
|
|
|
|
|
memset(&set_args, 0, sizeof(set_args));
|
2001-10-25 07:50:21 +02:00
|
|
|
set_args.cmd_type = PLPGSQL_STMT_SELECT;
|
|
|
|
set_args.lineno = stmt->lineno;
|
|
|
|
set_args.row = (PLpgSQL_row *)
|
|
|
|
(estate->datums[curvar->cursor_explicit_argrow]);
|
|
|
|
set_args.query = stmt->argquery;
|
2001-05-21 16:22:19 +02:00
|
|
|
|
|
|
|
if (exec_stmt_select(estate, &set_args) != PLPGSQL_RC_OK)
|
|
|
|
elog(ERROR, "open cursor failed during argument processing");
|
|
|
|
}
|
|
|
|
|
2001-10-25 07:50:21 +02:00
|
|
|
query = curvar->cursor_explicit_expr;
|
2001-05-21 16:22:19 +02:00
|
|
|
if (query->plan == NULL)
|
|
|
|
exec_prepare_plan(estate, query);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* Here we go if we have a saved plan where we have to put
|
|
|
|
* values into, either from an explicit cursor or from a
|
|
|
|
* refcursor opened with OPEN ... FOR SELECT ...;
|
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
values = palloc(sizeof(Datum) * (query->nparams + 1));
|
|
|
|
nulls = palloc(query->nparams + 1);
|
|
|
|
|
|
|
|
for (i = 0; i < query->nparams; i++)
|
|
|
|
{
|
|
|
|
switch (estate->datums[query->params[i]]->dtype)
|
|
|
|
{
|
|
|
|
case PLPGSQL_DTYPE_VAR:
|
|
|
|
var = (PLpgSQL_var *) (estate->datums[query->params[i]]);
|
|
|
|
values[i] = var->value;
|
|
|
|
if (var->isnull)
|
|
|
|
nulls[i] = 'n';
|
|
|
|
else
|
|
|
|
nulls[i] = ' ';
|
2000-08-31 15:26:16 +02:00
|
|
|
break;
|
|
|
|
|
2001-05-21 16:22:19 +02:00
|
|
|
case PLPGSQL_DTYPE_RECFIELD:
|
|
|
|
recfield = (PLpgSQL_recfield *) (estate->datums[query->params[i]]);
|
|
|
|
rec = (PLpgSQL_rec *) (estate->datums[recfield->recno]);
|
2000-08-31 15:26:16 +02:00
|
|
|
|
2001-05-21 16:22:19 +02:00
|
|
|
if (!HeapTupleIsValid(rec->tup))
|
|
|
|
elog(ERROR, "record %s is unassigned yet", rec->refname);
|
|
|
|
fno = SPI_fnumber(rec->tupdesc, recfield->fieldname);
|
|
|
|
if (fno == SPI_ERROR_NOATTRIBUTE)
|
|
|
|
elog(ERROR, "record %s has no field %s", rec->refname, recfield->fieldname);
|
|
|
|
|
|
|
|
if (query->plan_argtypes[i] != SPI_gettypeid(rec->tupdesc, fno))
|
|
|
|
elog(ERROR, "type of %s.%s doesn't match that when preparing the plan", rec->refname, recfield->fieldname);
|
|
|
|
|
|
|
|
values[i] = SPI_getbinval(rec->tup, rec->tupdesc, fno, &isnull);
|
|
|
|
if (isnull)
|
|
|
|
nulls[i] = 'n';
|
|
|
|
else
|
|
|
|
nulls[i] = ' ';
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PLPGSQL_DTYPE_TRIGARG:
|
|
|
|
trigarg = (PLpgSQL_trigarg *) (estate->datums[query->params[i]]);
|
|
|
|
tgargno = (int) exec_eval_expr(estate, trigarg->argnum,
|
|
|
|
&isnull, &tgargoid);
|
2001-08-02 23:31:23 +02:00
|
|
|
exec_eval_cleanup(estate);
|
2001-05-21 16:22:19 +02:00
|
|
|
if (isnull || tgargno < 0 || tgargno >= estate->trig_nargs)
|
|
|
|
{
|
|
|
|
values[i] = 0;
|
|
|
|
nulls[i] = 'n';
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
values[i] = estate->trig_argv[tgargno];
|
|
|
|
nulls[i] = ' ';
|
|
|
|
}
|
|
|
|
break;
|
2000-08-31 15:26:16 +02:00
|
|
|
|
|
|
|
default:
|
2001-05-21 16:22:19 +02:00
|
|
|
elog(ERROR, "unknown parameter dtype %d in exec_stmt_open()",
|
|
|
|
estate->datums[query->params[i]]->dtype);
|
2000-08-31 15:26:16 +02:00
|
|
|
}
|
|
|
|
}
|
2001-05-21 16:22:19 +02:00
|
|
|
nulls[i] = '\0';
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* Open the cursor
|
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
portal = SPI_cursor_open(curname, query->plan, values, nulls);
|
|
|
|
if (portal == NULL)
|
|
|
|
elog(ERROR, "Failed to open cursor");
|
|
|
|
|
|
|
|
pfree(values);
|
|
|
|
pfree(nulls);
|
2001-08-02 23:31:23 +02:00
|
|
|
if (curname)
|
|
|
|
pfree(curname);
|
2001-05-21 16:22:19 +02:00
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* Store the eventually assigned portal name in the cursor variable
|
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
if (curvar->freeval)
|
2001-10-25 07:50:21 +02:00
|
|
|
pfree((void *) (curvar->value));
|
2001-05-21 16:22:19 +02:00
|
|
|
|
|
|
|
curvar->value = DirectFunctionCall1(textin, CStringGetDatum(portal->name));
|
|
|
|
curvar->isnull = false;
|
|
|
|
curvar->freeval = true;
|
|
|
|
|
|
|
|
return PLPGSQL_RC_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* exec_stmt_fetch Fetch from a cursor into a target
|
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
exec_stmt_fetch(PLpgSQL_execstate * estate, PLpgSQL_stmt_fetch * stmt)
|
|
|
|
{
|
|
|
|
PLpgSQL_var *curvar = NULL;
|
|
|
|
PLpgSQL_rec *rec = NULL;
|
|
|
|
PLpgSQL_row *row = NULL;
|
2001-08-02 23:31:23 +02:00
|
|
|
SPITupleTable *tuptab;
|
2001-05-21 16:22:19 +02:00
|
|
|
Portal portal;
|
2001-10-25 07:50:21 +02:00
|
|
|
char *curname;
|
2001-05-21 16:22:19 +02:00
|
|
|
int n;
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* Get the portal of the cursor by name
|
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
curvar = (PLpgSQL_var *) (estate->datums[stmt->curvar]);
|
|
|
|
if (curvar->isnull)
|
|
|
|
elog(ERROR, "cursor variable \"%s\" is NULL", curvar->refname);
|
|
|
|
curname = DatumGetCString(DirectFunctionCall1(textout, curvar->value));
|
|
|
|
|
|
|
|
portal = SPI_cursor_find(curname);
|
|
|
|
if (portal == NULL)
|
|
|
|
elog(ERROR, "cursor \"%s\" is invalid", curname);
|
|
|
|
pfree(curname);
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* Initialize the global found variable to false
|
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
exec_set_found(estate, false);
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* Determine if we fetch into a record or a row
|
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
if (stmt->rec != NULL)
|
|
|
|
rec = (PLpgSQL_rec *) (estate->datums[stmt->rec->recno]);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (stmt->row != NULL)
|
|
|
|
row = (PLpgSQL_row *) (estate->datums[stmt->row->rowno]);
|
|
|
|
else
|
|
|
|
elog(ERROR, "unsupported target in exec_stmt_select()");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* Fetch 1 tuple from the cursor
|
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
SPI_cursor_fetch(portal, true, 1);
|
|
|
|
n = SPI_processed;
|
2001-08-02 23:31:23 +02:00
|
|
|
tuptab = SPI_tuptable;
|
2001-05-21 16:22:19 +02:00
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* If the FETCH didn't return a row, set the target
|
|
|
|
* to NULL and return with FOUND = false.
|
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
if (n == 0)
|
|
|
|
{
|
|
|
|
exec_move_row(estate, rec, row, NULL, NULL);
|
|
|
|
return PLPGSQL_RC_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* Put the result into the target and set found to true
|
|
|
|
* ----------
|
|
|
|
*/
|
2001-08-02 23:31:23 +02:00
|
|
|
exec_move_row(estate, rec, row, tuptab->vals[0], tuptab->tupdesc);
|
2001-05-21 16:22:19 +02:00
|
|
|
exec_set_found(estate, true);
|
|
|
|
|
2001-08-02 23:31:23 +02:00
|
|
|
SPI_freetuptable(tuptab);
|
2001-05-21 16:22:19 +02:00
|
|
|
|
|
|
|
return PLPGSQL_RC_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* exec_stmt_close Close a cursor
|
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
exec_stmt_close(PLpgSQL_execstate * estate, PLpgSQL_stmt_close * stmt)
|
|
|
|
{
|
|
|
|
PLpgSQL_var *curvar = NULL;
|
|
|
|
Portal portal;
|
2001-10-25 07:50:21 +02:00
|
|
|
char *curname;
|
2001-05-21 16:22:19 +02:00
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* Get the portal of the cursor by name
|
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
curvar = (PLpgSQL_var *) (estate->datums[stmt->curvar]);
|
|
|
|
if (curvar->isnull)
|
|
|
|
elog(ERROR, "cursor variable \"%s\" is NULL", curvar->refname);
|
|
|
|
curname = DatumGetCString(DirectFunctionCall1(textout, curvar->value));
|
|
|
|
|
|
|
|
portal = SPI_cursor_find(curname);
|
|
|
|
if (portal == NULL)
|
|
|
|
elog(ERROR, "cursor \"%s\" is invalid", curname);
|
|
|
|
pfree(curname);
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* And close it.
|
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
SPI_cursor_close(portal);
|
2000-08-31 15:26:16 +02:00
|
|
|
|
|
|
|
return PLPGSQL_RC_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1998-08-22 14:38:39 +02:00
|
|
|
/* ----------
|
|
|
|
* exec_assign_expr Put an expressions result into
|
|
|
|
* a variable.
|
|
|
|
* ----------
|
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
static void
|
|
|
|
exec_assign_expr(PLpgSQL_execstate * estate, PLpgSQL_datum * target,
|
|
|
|
PLpgSQL_expr * expr)
|
1998-08-22 14:38:39 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
Datum value;
|
|
|
|
Oid valtype;
|
|
|
|
bool isnull = false;
|
|
|
|
|
|
|
|
value = exec_eval_expr(estate, expr, &isnull, &valtype);
|
2001-05-28 21:33:24 +02:00
|
|
|
exec_assign_value(estate, target, value, valtype, &isnull);
|
2001-08-02 23:31:23 +02:00
|
|
|
exec_eval_cleanup(estate);
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* exec_assign_value Put a value into a target field
|
|
|
|
* ----------
|
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
static void
|
|
|
|
exec_assign_value(PLpgSQL_execstate * estate,
|
|
|
|
PLpgSQL_datum * target,
|
|
|
|
Datum value, Oid valtype, bool *isNull)
|
1998-08-22 14:38:39 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
PLpgSQL_var *var;
|
|
|
|
PLpgSQL_rec *rec;
|
|
|
|
PLpgSQL_recfield *recfield;
|
|
|
|
int fno;
|
|
|
|
int i;
|
|
|
|
int natts;
|
|
|
|
Datum *values;
|
|
|
|
char *nulls;
|
2001-08-02 23:31:23 +02:00
|
|
|
void *mustfree;
|
2001-01-06 02:43:01 +01:00
|
|
|
Datum newvalue;
|
1998-09-01 06:40:42 +02:00
|
|
|
bool attisnull;
|
|
|
|
Oid atttype;
|
2000-01-15 23:43:25 +01:00
|
|
|
int32 atttypmod;
|
1998-09-01 06:40:42 +02:00
|
|
|
HeapTuple typetup;
|
2001-05-21 16:22:19 +02:00
|
|
|
HeapTuple newtup;
|
1998-09-01 06:40:42 +02:00
|
|
|
Form_pg_type typeStruct;
|
|
|
|
FmgrInfo finfo_input;
|
|
|
|
|
|
|
|
switch (target->dtype)
|
|
|
|
{
|
|
|
|
case PLPGSQL_DTYPE_VAR:
|
2001-03-22 07:16:21 +01:00
|
|
|
|
|
|
|
/*
|
2001-05-21 16:22:19 +02:00
|
|
|
* Target field is a variable
|
1998-09-01 06:40:42 +02:00
|
|
|
*/
|
|
|
|
var = (PLpgSQL_var *) target;
|
2001-05-21 16:22:19 +02:00
|
|
|
|
|
|
|
if (var->freeval)
|
|
|
|
{
|
2001-10-25 07:50:21 +02:00
|
|
|
pfree((void *) (var->value));
|
2001-05-21 16:22:19 +02:00
|
|
|
var->freeval = false;
|
|
|
|
}
|
|
|
|
|
2001-01-06 02:43:01 +01:00
|
|
|
newvalue = exec_cast_value(value, valtype, var->datatype->typoid,
|
|
|
|
&(var->datatype->typinput),
|
|
|
|
var->datatype->typelem,
|
|
|
|
var->datatype->atttypmod,
|
|
|
|
isNull);
|
1998-09-01 06:40:42 +02:00
|
|
|
|
2001-08-02 23:31:23 +02:00
|
|
|
if (*isNull && var->notnull)
|
|
|
|
elog(ERROR, "NULL assignment to variable '%s' declared NOT NULL", var->refname);
|
|
|
|
|
|
|
|
/*
|
2001-10-25 07:50:21 +02:00
|
|
|
* If type is by-reference, make sure we have a freshly
|
|
|
|
* palloc'd copy; the originally passed value may not live as
|
|
|
|
* long as the variable! But we don't need to re-copy if
|
|
|
|
* exec_cast_value performed a conversion; its output must
|
|
|
|
* already be palloc'd.
|
2001-08-02 23:31:23 +02:00
|
|
|
*/
|
|
|
|
if (!var->datatype->typbyval && !*isNull)
|
2001-05-21 16:22:19 +02:00
|
|
|
{
|
2001-08-02 23:31:23 +02:00
|
|
|
if (newvalue == value)
|
|
|
|
{
|
2001-10-25 07:50:21 +02:00
|
|
|
int len;
|
2001-08-02 23:31:23 +02:00
|
|
|
|
|
|
|
if (var->datatype->typlen < 0)
|
|
|
|
len = VARSIZE(newvalue);
|
|
|
|
else
|
|
|
|
len = var->datatype->typlen;
|
|
|
|
var->value = (Datum) palloc(len);
|
2001-10-25 07:50:21 +02:00
|
|
|
memcpy((void *) (var->value), (void *) newvalue, len);
|
2001-08-02 23:31:23 +02:00
|
|
|
}
|
2001-05-21 16:22:19 +02:00
|
|
|
else
|
2001-08-02 23:31:23 +02:00
|
|
|
var->value = newvalue;
|
2001-05-21 16:22:19 +02:00
|
|
|
var->freeval = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
var->value = newvalue;
|
1998-09-01 06:40:42 +02:00
|
|
|
var->isnull = *isNull;
|
|
|
|
break;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
case PLPGSQL_DTYPE_RECFIELD:
|
2001-03-22 07:16:21 +01:00
|
|
|
|
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Target field is a record
|
|
|
|
*/
|
|
|
|
recfield = (PLpgSQL_recfield *) target;
|
|
|
|
rec = (PLpgSQL_rec *) (estate->datums[recfield->recno]);
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* Check that there is already a tuple in the record. We need
|
|
|
|
* that because records don't have any predefined field
|
|
|
|
* structure.
|
1998-09-01 06:40:42 +02:00
|
|
|
*/
|
|
|
|
if (!HeapTupleIsValid(rec->tup))
|
2000-01-05 19:23:54 +01:00
|
|
|
elog(ERROR, "record %s is unassigned yet - don't know its tuple structure", rec->refname);
|
1998-09-01 06:40:42 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Get the number of the records field to change and the
|
|
|
|
* number of attributes in the tuple.
|
|
|
|
*/
|
|
|
|
fno = SPI_fnumber(rec->tupdesc, recfield->fieldname);
|
|
|
|
if (fno == SPI_ERROR_NOATTRIBUTE)
|
|
|
|
elog(ERROR, "record %s has no field %s", rec->refname, recfield->fieldname);
|
|
|
|
fno--;
|
|
|
|
natts = rec->tupdesc->natts;
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2001-10-25 07:50:21 +02:00
|
|
|
* Set up values/datums arrays for heap_formtuple. For all
|
|
|
|
* the attributes except the one we want to replace, use the
|
|
|
|
* value that's in the old tuple.
|
1998-09-01 06:40:42 +02:00
|
|
|
*/
|
|
|
|
values = palloc(sizeof(Datum) * natts);
|
2001-08-02 23:31:23 +02:00
|
|
|
nulls = palloc(natts);
|
1998-09-01 06:40:42 +02:00
|
|
|
|
|
|
|
for (i = 0; i < natts; i++)
|
|
|
|
{
|
2001-08-02 23:31:23 +02:00
|
|
|
if (i == fno)
|
1998-09-01 06:40:42 +02:00
|
|
|
continue;
|
2001-08-02 23:31:23 +02:00
|
|
|
values[i] = SPI_getbinval(rec->tup, rec->tupdesc,
|
|
|
|
i + 1, &attisnull);
|
1998-09-01 06:40:42 +02:00
|
|
|
if (attisnull)
|
|
|
|
nulls[i] = 'n';
|
|
|
|
else
|
|
|
|
nulls[i] = ' ';
|
|
|
|
}
|
|
|
|
|
2001-08-02 23:31:23 +02:00
|
|
|
/*
|
|
|
|
* Now insert the new value, being careful to cast it to the
|
|
|
|
* right type.
|
|
|
|
*/
|
|
|
|
atttype = SPI_gettypeid(rec->tupdesc, fno + 1);
|
|
|
|
atttypmod = rec->tupdesc->attrs[fno]->atttypmod;
|
|
|
|
typetup = SearchSysCache(TYPEOID,
|
|
|
|
ObjectIdGetDatum(atttype),
|
|
|
|
0, 0, 0);
|
|
|
|
if (!HeapTupleIsValid(typetup))
|
|
|
|
elog(ERROR, "cache lookup for type %u failed", atttype);
|
|
|
|
typeStruct = (Form_pg_type) GETSTRUCT(typetup);
|
|
|
|
fmgr_info(typeStruct->typinput, &finfo_input);
|
|
|
|
|
|
|
|
attisnull = *isNull;
|
|
|
|
values[fno] = exec_cast_value(value, valtype,
|
|
|
|
atttype, &finfo_input,
|
|
|
|
typeStruct->typelem,
|
|
|
|
atttypmod, &attisnull);
|
|
|
|
if (attisnull)
|
|
|
|
nulls[fno] = 'n';
|
|
|
|
else
|
|
|
|
nulls[fno] = ' ';
|
|
|
|
|
|
|
|
/*
|
2001-10-25 07:50:21 +02:00
|
|
|
* Avoid leaking the result of exec_cast_value, if it
|
|
|
|
* performed a conversion to a pass-by-ref type.
|
2001-08-02 23:31:23 +02:00
|
|
|
*/
|
|
|
|
if (!typeStruct->typbyval && !attisnull && values[fno] != value)
|
|
|
|
mustfree = DatumGetPointer(values[fno]);
|
|
|
|
else
|
|
|
|
mustfree = NULL;
|
|
|
|
|
|
|
|
ReleaseSysCache(typetup);
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* Now call heap_formtuple() to create a new tuple that
|
|
|
|
* replaces the old one in the record.
|
1998-09-01 06:40:42 +02:00
|
|
|
*/
|
2001-05-21 16:22:19 +02:00
|
|
|
newtup = heap_formtuple(rec->tupdesc, values, nulls);
|
|
|
|
|
|
|
|
if (rec->freetup)
|
|
|
|
heap_freetuple(rec->tup);
|
|
|
|
|
|
|
|
rec->tup = newtup;
|
|
|
|
rec->freetup = true;
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
pfree(values);
|
|
|
|
pfree(nulls);
|
2001-08-02 23:31:23 +02:00
|
|
|
if (mustfree)
|
|
|
|
pfree(mustfree);
|
1998-09-01 06:40:42 +02:00
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
elog(ERROR, "unknown dtype %d in exec_assign_value()",
|
|
|
|
target->dtype);
|
|
|
|
}
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* exec_eval_expr Evaluate an expression and return
|
|
|
|
* the result Datum.
|
2001-08-02 23:31:23 +02:00
|
|
|
*
|
|
|
|
* NOTE: caller must do exec_eval_cleanup when done with the Datum.
|
1998-08-22 14:38:39 +02:00
|
|
|
* ----------
|
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
static Datum
|
|
|
|
exec_eval_expr(PLpgSQL_execstate * estate,
|
|
|
|
PLpgSQL_expr * expr,
|
|
|
|
bool *isNull,
|
|
|
|
Oid *rettype)
|
1998-08-22 14:38:39 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
int rc;
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1999-01-27 17:15:22 +01:00
|
|
|
* If not already done create a plan for this expression
|
|
|
|
*/
|
|
|
|
if (expr->plan == NULL)
|
|
|
|
exec_prepare_plan(estate, expr);
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* If this is a simple expression, bypass SPI and use the executor
|
|
|
|
* directly
|
1999-01-27 17:15:22 +01:00
|
|
|
*/
|
|
|
|
if (expr->plan_simple_expr != NULL)
|
|
|
|
return exec_eval_simple_expr(estate, expr, isNull, rettype);
|
|
|
|
|
2001-05-21 16:22:19 +02:00
|
|
|
rc = exec_run_select(estate, expr, 2, NULL);
|
1998-09-01 06:40:42 +02:00
|
|
|
if (rc != SPI_OK_SELECT)
|
|
|
|
elog(ERROR, "query \"%s\" didn't return data", expr->query);
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* If there are no rows selected, the result is NULL.
|
|
|
|
*/
|
2001-08-02 23:31:23 +02:00
|
|
|
if (estate->eval_processed == 0)
|
1998-09-01 06:40:42 +02:00
|
|
|
{
|
|
|
|
*isNull = true;
|
|
|
|
return (Datum) 0;
|
|
|
|
}
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Check that the expression returned one single Datum
|
|
|
|
*/
|
2001-08-02 23:31:23 +02:00
|
|
|
if (estate->eval_processed > 1)
|
2001-05-08 03:00:53 +02:00
|
|
|
elog(ERROR, "query \"%s\" returned more than one row", expr->query);
|
2001-08-02 23:31:23 +02:00
|
|
|
if (estate->eval_tuptable->tupdesc->natts != 1)
|
2001-05-21 16:22:19 +02:00
|
|
|
elog(ERROR, "query \"%s\" returned %d columns", expr->query,
|
2001-10-25 07:50:21 +02:00
|
|
|
estate->eval_tuptable->tupdesc->natts);
|
1998-09-01 06:40:42 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2000-01-05 19:23:54 +01:00
|
|
|
* Return the result and its type
|
1998-09-01 06:40:42 +02:00
|
|
|
*/
|
2001-08-02 23:31:23 +02:00
|
|
|
*rettype = SPI_gettypeid(estate->eval_tuptable->tupdesc, 1);
|
|
|
|
return SPI_getbinval(estate->eval_tuptable->vals[0],
|
|
|
|
estate->eval_tuptable->tupdesc, 1, isNull);
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* exec_run_select Execute a select query
|
|
|
|
* ----------
|
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
static int
|
|
|
|
exec_run_select(PLpgSQL_execstate * estate,
|
2001-05-21 16:22:19 +02:00
|
|
|
PLpgSQL_expr * expr, int maxtuples, Portal *portalP)
|
1998-08-22 14:38:39 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
PLpgSQL_var *var;
|
|
|
|
PLpgSQL_rec *rec;
|
|
|
|
PLpgSQL_recfield *recfield;
|
|
|
|
PLpgSQL_trigarg *trigarg;
|
|
|
|
int tgargno;
|
|
|
|
Oid tgargoid;
|
|
|
|
int i;
|
|
|
|
Datum *values;
|
|
|
|
char *nulls;
|
|
|
|
int rc;
|
|
|
|
int fno;
|
|
|
|
bool isnull;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* On the first call for this expression generate the plan
|
1998-08-22 14:38:39 +02:00
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
if (expr->plan == NULL)
|
1999-01-27 17:15:22 +01:00
|
|
|
exec_prepare_plan(estate, expr);
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1998-09-01 06:40:42 +02:00
|
|
|
* Now build up the values and nulls arguments for SPI_execp()
|
1998-08-22 14:38:39 +02:00
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
values = palloc(sizeof(Datum) * (expr->nparams + 1));
|
|
|
|
nulls = palloc(expr->nparams + 1);
|
|
|
|
|
|
|
|
for (i = 0; i < expr->nparams; i++)
|
|
|
|
{
|
|
|
|
switch (estate->datums[expr->params[i]]->dtype)
|
|
|
|
{
|
|
|
|
case PLPGSQL_DTYPE_VAR:
|
|
|
|
var = (PLpgSQL_var *) (estate->datums[expr->params[i]]);
|
|
|
|
values[i] = var->value;
|
|
|
|
if (var->isnull)
|
|
|
|
nulls[i] = 'n';
|
|
|
|
else
|
|
|
|
nulls[i] = ' ';
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PLPGSQL_DTYPE_RECFIELD:
|
|
|
|
recfield = (PLpgSQL_recfield *) (estate->datums[expr->params[i]]);
|
|
|
|
rec = (PLpgSQL_rec *) (estate->datums[recfield->recno]);
|
|
|
|
|
|
|
|
if (!HeapTupleIsValid(rec->tup))
|
|
|
|
elog(ERROR, "record %s is unassigned yet", rec->refname);
|
|
|
|
fno = SPI_fnumber(rec->tupdesc, recfield->fieldname);
|
|
|
|
if (fno == SPI_ERROR_NOATTRIBUTE)
|
|
|
|
elog(ERROR, "record %s has no field %s", rec->refname, recfield->fieldname);
|
|
|
|
|
|
|
|
if (expr->plan_argtypes[i] != SPI_gettypeid(rec->tupdesc, fno))
|
|
|
|
elog(ERROR, "type of %s.%s doesn't match that when preparing the plan", rec->refname, recfield->fieldname);
|
|
|
|
|
|
|
|
values[i] = SPI_getbinval(rec->tup, rec->tupdesc, fno, &isnull);
|
|
|
|
if (isnull)
|
|
|
|
nulls[i] = 'n';
|
|
|
|
else
|
|
|
|
nulls[i] = ' ';
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PLPGSQL_DTYPE_TRIGARG:
|
|
|
|
trigarg = (PLpgSQL_trigarg *) (estate->datums[expr->params[i]]);
|
|
|
|
tgargno = (int) exec_eval_expr(estate, trigarg->argnum,
|
|
|
|
&isnull, &tgargoid);
|
2001-08-02 23:31:23 +02:00
|
|
|
exec_eval_cleanup(estate);
|
1998-09-01 06:40:42 +02:00
|
|
|
if (isnull || tgargno < 0 || tgargno >= estate->trig_nargs)
|
|
|
|
{
|
|
|
|
values[i] = 0;
|
|
|
|
nulls[i] = 'n';
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
values[i] = estate->trig_argv[tgargno];
|
|
|
|
nulls[i] = ' ';
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2000-01-15 23:43:25 +01:00
|
|
|
elog(ERROR, "unknown parameter dtype %d in exec_eval_expr()",
|
|
|
|
estate->datums[expr->params[i]]->dtype);
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
nulls[i] = '\0';
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2001-08-02 23:31:23 +02:00
|
|
|
* If a portal was requested, put the query into the portal
|
1998-09-01 06:40:42 +02:00
|
|
|
*/
|
2001-05-21 16:22:19 +02:00
|
|
|
if (portalP != NULL)
|
|
|
|
{
|
|
|
|
*portalP = SPI_cursor_open(NULL, expr->plan, values, nulls);
|
|
|
|
if (*portalP == NULL)
|
|
|
|
elog(ERROR, "failed to open implicit cursor for \"%s\"",
|
2001-10-25 07:50:21 +02:00
|
|
|
expr->query);
|
2001-05-21 16:22:19 +02:00
|
|
|
pfree(values);
|
|
|
|
pfree(nulls);
|
|
|
|
return SPI_OK_CURSOR;
|
|
|
|
}
|
2001-08-02 23:31:23 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Execute the query
|
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
rc = SPI_execp(expr->plan, values, nulls, maxtuples);
|
|
|
|
if (rc != SPI_OK_SELECT)
|
|
|
|
elog(ERROR, "query \"%s\" isn't a SELECT", expr->query);
|
2001-08-02 23:31:23 +02:00
|
|
|
|
|
|
|
/* Save query results for eventual cleanup */
|
|
|
|
Assert(estate->eval_tuptable == NULL);
|
|
|
|
estate->eval_tuptable = SPI_tuptable;
|
|
|
|
estate->eval_processed = SPI_processed;
|
|
|
|
estate->eval_lastoid = SPI_lastoid;
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
pfree(values);
|
|
|
|
pfree(nulls);
|
|
|
|
|
|
|
|
return rc;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-01-27 17:15:22 +01:00
|
|
|
/* ----------
|
|
|
|
* exec_eval_simple_expr - Evaluate a simple expression returning
|
|
|
|
* a Datum by directly calling ExecEvalExpr().
|
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
static Datum
|
1999-05-25 18:15:34 +02:00
|
|
|
exec_eval_simple_expr(PLpgSQL_execstate * estate,
|
|
|
|
PLpgSQL_expr * expr,
|
|
|
|
bool *isNull,
|
|
|
|
Oid *rettype)
|
1999-01-27 17:15:22 +01:00
|
|
|
{
|
2001-08-02 23:31:23 +02:00
|
|
|
_SPI_plan *spi_plan = (_SPI_plan *) expr->plan;
|
1999-01-27 17:15:22 +01:00
|
|
|
Datum retval;
|
|
|
|
PLpgSQL_var *var;
|
|
|
|
PLpgSQL_rec *rec;
|
|
|
|
PLpgSQL_recfield *recfield;
|
|
|
|
PLpgSQL_trigarg *trigarg;
|
|
|
|
int tgargno;
|
|
|
|
Oid tgargoid;
|
|
|
|
int fno;
|
|
|
|
int i;
|
|
|
|
bool isnull;
|
1999-05-25 18:15:34 +02:00
|
|
|
ExprContext *econtext;
|
1999-01-27 17:15:22 +01:00
|
|
|
ParamListInfo paramLI;
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2001-01-22 01:50:07 +01:00
|
|
|
* Create a simple expression context to hold the arguments.
|
|
|
|
*
|
2001-08-02 23:31:23 +02:00
|
|
|
* NOTE: we pass the SPI plan's context as the query-lifetime context for
|
|
|
|
* function cache nodes and suchlike allocations. This is appropriate
|
2001-10-25 07:50:21 +02:00
|
|
|
* because that's where the expression tree itself is, and the
|
|
|
|
* function cache nodes must live as long as it does.
|
2001-08-02 23:31:23 +02:00
|
|
|
*/
|
|
|
|
econtext = MakeExprContext(NULL, spi_plan->plancxt);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Param list can live in econtext's temporary memory context.
|
|
|
|
*/
|
2001-10-25 07:50:21 +02:00
|
|
|
paramLI = (ParamListInfo)
|
2001-08-02 23:31:23 +02:00
|
|
|
MemoryContextAlloc(econtext->ecxt_per_tuple_memory,
|
2001-10-25 07:50:21 +02:00
|
|
|
(expr->nparams + 1) * sizeof(ParamListInfoData));
|
1999-01-27 17:15:22 +01:00
|
|
|
econtext->ecxt_param_list_info = paramLI;
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* Put the parameter values into the parameter list info of the
|
|
|
|
* expression context.
|
1999-01-27 17:15:22 +01:00
|
|
|
*/
|
|
|
|
for (i = 0; i < expr->nparams; i++, paramLI++)
|
|
|
|
{
|
|
|
|
paramLI->kind = PARAM_NUM;
|
1999-05-25 18:15:34 +02:00
|
|
|
paramLI->id = i + 1;
|
1999-01-27 17:15:22 +01:00
|
|
|
|
|
|
|
switch (estate->datums[expr->params[i]]->dtype)
|
|
|
|
{
|
|
|
|
case PLPGSQL_DTYPE_VAR:
|
|
|
|
var = (PLpgSQL_var *) (estate->datums[expr->params[i]]);
|
|
|
|
paramLI->isnull = var->isnull;
|
|
|
|
paramLI->value = var->value;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PLPGSQL_DTYPE_RECFIELD:
|
|
|
|
recfield = (PLpgSQL_recfield *) (estate->datums[expr->params[i]]);
|
|
|
|
rec = (PLpgSQL_rec *) (estate->datums[recfield->recno]);
|
|
|
|
|
|
|
|
if (!HeapTupleIsValid(rec->tup))
|
|
|
|
elog(ERROR, "record %s is unassigned yet", rec->refname);
|
|
|
|
fno = SPI_fnumber(rec->tupdesc, recfield->fieldname);
|
|
|
|
if (fno == SPI_ERROR_NOATTRIBUTE)
|
|
|
|
elog(ERROR, "record %s has no field %s", rec->refname, recfield->fieldname);
|
|
|
|
|
|
|
|
if (expr->plan_argtypes[i] != SPI_gettypeid(rec->tupdesc, fno))
|
|
|
|
elog(ERROR, "type of %s.%s doesn't match that when preparing the plan", rec->refname, recfield->fieldname);
|
|
|
|
|
|
|
|
paramLI->value = SPI_getbinval(rec->tup, rec->tupdesc, fno, &isnull);
|
|
|
|
paramLI->isnull = isnull;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PLPGSQL_DTYPE_TRIGARG:
|
|
|
|
trigarg = (PLpgSQL_trigarg *) (estate->datums[expr->params[i]]);
|
|
|
|
tgargno = (int) exec_eval_expr(estate, trigarg->argnum,
|
|
|
|
&isnull, &tgargoid);
|
2001-08-02 23:31:23 +02:00
|
|
|
exec_eval_cleanup(estate);
|
1999-01-27 17:15:22 +01:00
|
|
|
if (isnull || tgargno < 0 || tgargno >= estate->trig_nargs)
|
|
|
|
{
|
|
|
|
paramLI->value = 0;
|
|
|
|
paramLI->isnull = TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
paramLI->value = estate->trig_argv[tgargno];
|
|
|
|
paramLI->isnull = FALSE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
elog(ERROR, "unknown parameter dtype %d in exec_eval_simple_expr()", estate->datums[expr->params[i]]->dtype);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
paramLI->kind = PARAM_INVALID;
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1999-01-27 17:15:22 +01:00
|
|
|
* Initialize things
|
|
|
|
*/
|
|
|
|
*rettype = expr->plan_simple_type;
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1999-01-27 17:15:22 +01:00
|
|
|
* Now call the executor to evaluate the expression
|
|
|
|
*/
|
|
|
|
SPI_push();
|
2000-07-12 04:37:39 +02:00
|
|
|
retval = ExecEvalExprSwitchContext(expr->plan_simple_expr,
|
|
|
|
econtext,
|
|
|
|
isNull,
|
2000-08-24 05:29:15 +02:00
|
|
|
NULL);
|
1999-01-27 17:15:22 +01:00
|
|
|
SPI_pop();
|
|
|
|
|
2000-07-12 04:37:39 +02:00
|
|
|
/*
|
2001-10-25 07:50:21 +02:00
|
|
|
* Note: if pass-by-reference, the result is in the econtext's
|
|
|
|
* temporary memory context. It will be freed when exec_eval_cleanup
|
|
|
|
* is done.
|
2000-07-12 04:37:39 +02:00
|
|
|
*/
|
2001-08-02 23:31:23 +02:00
|
|
|
Assert(estate->eval_econtext == NULL);
|
|
|
|
estate->eval_econtext = econtext;
|
2000-07-12 04:37:39 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1999-01-27 17:15:22 +01:00
|
|
|
* That's it.
|
|
|
|
*/
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1998-08-22 14:38:39 +02:00
|
|
|
/* ----------
|
2001-04-30 22:05:40 +02:00
|
|
|
* exec_move_row Move one tuple's values into a record or row
|
1998-08-22 14:38:39 +02:00
|
|
|
* ----------
|
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
static void
|
|
|
|
exec_move_row(PLpgSQL_execstate * estate,
|
|
|
|
PLpgSQL_rec * rec,
|
|
|
|
PLpgSQL_row * row,
|
|
|
|
HeapTuple tup, TupleDesc tupdesc)
|
1998-08-22 14:38:39 +02:00
|
|
|
{
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* Record is simple - just put the tuple and its descriptor into the
|
|
|
|
* record
|
1998-09-01 06:40:42 +02:00
|
|
|
*/
|
|
|
|
if (rec != NULL)
|
|
|
|
{
|
2001-05-21 16:22:19 +02:00
|
|
|
if (rec->freetup)
|
|
|
|
{
|
|
|
|
heap_freetuple(rec->tup);
|
|
|
|
rec->freetup = false;
|
|
|
|
}
|
|
|
|
if (rec->freetupdesc)
|
|
|
|
{
|
|
|
|
FreeTupleDesc(rec->tupdesc);
|
|
|
|
rec->freetupdesc = false;
|
|
|
|
}
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
if (HeapTupleIsValid(tup))
|
|
|
|
{
|
2001-05-21 16:22:19 +02:00
|
|
|
rec->tup = heap_copytuple(tup);
|
|
|
|
rec->tupdesc = CreateTupleDescCopy(tupdesc);
|
|
|
|
rec->freetup = true;
|
|
|
|
rec->freetupdesc = true;
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rec->tup = NULL;
|
|
|
|
rec->tupdesc = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2001-04-30 22:05:40 +02:00
|
|
|
* Row is a bit more complicated in that we assign the individual
|
|
|
|
* attributes of the tuple to the variables the row points to.
|
|
|
|
*
|
|
|
|
* NOTE: this code used to demand row->nfields == tup->t_data->t_natts,
|
2001-10-25 07:50:21 +02:00
|
|
|
* but that's wrong. The tuple might have more fields than we
|
|
|
|
* expected if it's from an inheritance-child table of the current
|
|
|
|
* table, or it might have fewer if the table has had columns added by
|
|
|
|
* ALTER TABLE. Ignore extra columns and assume NULL for missing
|
|
|
|
* columns, the same as heap_getattr would do.
|
1998-09-01 06:40:42 +02:00
|
|
|
*/
|
|
|
|
if (row != NULL)
|
|
|
|
{
|
2001-04-30 22:05:40 +02:00
|
|
|
int t_natts;
|
|
|
|
int i;
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
if (HeapTupleIsValid(tup))
|
2001-04-30 22:05:40 +02:00
|
|
|
t_natts = tup->t_data->t_natts;
|
|
|
|
else
|
|
|
|
t_natts = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < row->nfields; i++)
|
1998-09-01 06:40:42 +02:00
|
|
|
{
|
2001-04-30 22:05:40 +02:00
|
|
|
PLpgSQL_var *var;
|
|
|
|
Datum value;
|
|
|
|
bool isnull;
|
|
|
|
Oid valtype;
|
1998-09-01 06:40:42 +02:00
|
|
|
|
2001-04-30 22:05:40 +02:00
|
|
|
var = (PLpgSQL_var *) (estate->datums[row->varnos[i]]);
|
|
|
|
if (i < t_natts)
|
1998-09-01 06:40:42 +02:00
|
|
|
{
|
|
|
|
value = SPI_getbinval(tup, tupdesc, i + 1, &isnull);
|
2001-05-08 03:00:53 +02:00
|
|
|
valtype = SPI_gettypeid(tupdesc, i + 1);
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
2001-04-30 22:05:40 +02:00
|
|
|
else
|
1998-09-01 06:40:42 +02:00
|
|
|
{
|
2001-04-30 22:05:40 +02:00
|
|
|
value = (Datum) 0;
|
|
|
|
isnull = true;
|
2001-05-08 03:00:53 +02:00
|
|
|
valtype = InvalidOid;
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
2001-04-30 22:05:40 +02:00
|
|
|
|
|
|
|
exec_assign_value(estate, estate->datums[row->varnos[i]],
|
|
|
|
value, valtype, &isnull);
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
elog(ERROR, "unsupported target in exec_move_row()");
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* exec_cast_value Cast a value if required
|
|
|
|
* ----------
|
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
static Datum
|
|
|
|
exec_cast_value(Datum value, Oid valtype,
|
|
|
|
Oid reqtype,
|
1998-08-22 14:38:39 +02:00
|
|
|
FmgrInfo *reqinput,
|
2000-01-15 23:43:25 +01:00
|
|
|
Oid reqtypelem,
|
|
|
|
int32 reqtypmod,
|
1998-08-22 14:38:39 +02:00
|
|
|
bool *isnull)
|
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
if (!*isnull)
|
|
|
|
{
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* If the type of the queries return value isn't that of the
|
|
|
|
* variable, convert it.
|
1998-09-01 06:40:42 +02:00
|
|
|
*/
|
2000-01-15 23:43:25 +01:00
|
|
|
if (valtype != reqtype || reqtypmod != -1)
|
1998-09-01 06:40:42 +02:00
|
|
|
{
|
|
|
|
HeapTuple typetup;
|
|
|
|
Form_pg_type typeStruct;
|
|
|
|
FmgrInfo finfo_output;
|
|
|
|
char *extval;
|
|
|
|
|
2000-11-16 23:30:52 +01:00
|
|
|
typetup = SearchSysCache(TYPEOID,
|
|
|
|
ObjectIdGetDatum(valtype),
|
|
|
|
0, 0, 0);
|
1998-09-01 06:40:42 +02:00
|
|
|
if (!HeapTupleIsValid(typetup))
|
1999-05-10 02:46:32 +02:00
|
|
|
elog(ERROR, "cache lookup for type %u failed", valtype);
|
1998-09-01 06:40:42 +02:00
|
|
|
typeStruct = (Form_pg_type) GETSTRUCT(typetup);
|
|
|
|
|
|
|
|
fmgr_info(typeStruct->typoutput, &finfo_output);
|
2000-05-30 06:25:00 +02:00
|
|
|
extval = DatumGetCString(FunctionCall3(&finfo_output,
|
2001-03-22 05:01:46 +01:00
|
|
|
value,
|
|
|
|
ObjectIdGetDatum(typeStruct->typelem),
|
|
|
|
Int32GetDatum(-1)));
|
2000-05-30 06:25:00 +02:00
|
|
|
value = FunctionCall3(reqinput,
|
|
|
|
CStringGetDatum(extval),
|
|
|
|
ObjectIdGetDatum(reqtypelem),
|
|
|
|
Int32GetDatum(reqtypmod));
|
2000-01-15 23:43:25 +01:00
|
|
|
pfree(extval);
|
2000-11-16 23:30:52 +01:00
|
|
|
ReleaseSysCache(typetup);
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
return value;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-01-27 17:15:22 +01:00
|
|
|
/* ----------
|
|
|
|
* exec_simple_check_node - Recursively check if an expression
|
|
|
|
* is made only of simple things we can
|
|
|
|
* hand out directly to ExecEvalExpr()
|
|
|
|
* instead of calling SPI.
|
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
static bool
|
1999-05-25 18:15:34 +02:00
|
|
|
exec_simple_check_node(Node *node)
|
1999-01-27 17:15:22 +01:00
|
|
|
{
|
|
|
|
switch (nodeTag(node))
|
|
|
|
{
|
2001-10-25 07:50:21 +02:00
|
|
|
case T_Expr:
|
1999-05-25 18:15:34 +02:00
|
|
|
{
|
|
|
|
Expr *expr = (Expr *) node;
|
|
|
|
List *l;
|
|
|
|
|
|
|
|
switch (expr->opType)
|
|
|
|
{
|
|
|
|
case OP_EXPR:
|
|
|
|
case FUNC_EXPR:
|
|
|
|
case OR_EXPR:
|
|
|
|
case AND_EXPR:
|
|
|
|
case NOT_EXPR:
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return FALSE;
|
|
|
|
}
|
1999-01-27 17:15:22 +01:00
|
|
|
|
1999-05-25 18:15:34 +02:00
|
|
|
foreach(l, expr->args)
|
|
|
|
{
|
|
|
|
if (!exec_simple_check_node(lfirst(l)))
|
|
|
|
return FALSE;
|
|
|
|
}
|
1999-01-27 17:15:22 +01:00
|
|
|
|
1999-05-25 18:15:34 +02:00
|
|
|
return TRUE;
|
|
|
|
}
|
1999-01-27 17:15:22 +01:00
|
|
|
|
1999-05-25 18:15:34 +02:00
|
|
|
case T_Param:
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
case T_Const:
|
|
|
|
return TRUE;
|
|
|
|
|
2000-08-13 04:50:35 +02:00
|
|
|
case T_RelabelType:
|
|
|
|
return exec_simple_check_node(((RelabelType *) node)->arg);
|
|
|
|
|
1999-05-25 18:15:34 +02:00
|
|
|
default:
|
|
|
|
return FALSE;
|
1999-01-27 17:15:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* exec_simple_check_plan - Check if a plan is simple enough to
|
|
|
|
* be evaluated by ExecEvalExpr() instead
|
|
|
|
* of SPI.
|
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
exec_simple_check_plan(PLpgSQL_expr * expr)
|
|
|
|
{
|
1999-05-25 18:15:34 +02:00
|
|
|
_SPI_plan *spi_plan = (_SPI_plan *) expr->plan;
|
|
|
|
Plan *plan;
|
|
|
|
TargetEntry *tle;
|
1999-01-27 17:15:22 +01:00
|
|
|
|
|
|
|
expr->plan_simple_expr = NULL;
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1999-01-27 17:15:22 +01:00
|
|
|
* 1. We can only evaluate queries that resulted in one single
|
2001-03-22 07:16:21 +01:00
|
|
|
* execution plan
|
1999-01-27 17:15:22 +01:00
|
|
|
*/
|
1999-07-04 03:03:01 +02:00
|
|
|
if (length(spi_plan->ptlist) != 1)
|
1999-01-27 17:15:22 +01:00
|
|
|
return;
|
|
|
|
|
1999-05-25 18:15:34 +02:00
|
|
|
plan = (Plan *) lfirst(spi_plan->ptlist);
|
1999-01-27 17:15:22 +01:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1999-01-27 17:15:22 +01:00
|
|
|
* 2. It must be a RESULT plan --> no scan's required
|
|
|
|
*/
|
1999-07-04 03:03:01 +02:00
|
|
|
if (plan == NULL) /* utility statement produces this */
|
|
|
|
return;
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
if (!IsA(plan, Result))
|
1999-01-27 17:15:22 +01:00
|
|
|
return;
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2000-03-11 07:19:00 +01:00
|
|
|
* 3. Can't have any subplan or qual clause, either
|
1999-01-27 17:15:22 +01:00
|
|
|
*/
|
2000-03-11 07:19:00 +01:00
|
|
|
if (plan->lefttree != NULL ||
|
|
|
|
plan->righttree != NULL ||
|
|
|
|
plan->initPlan != NULL ||
|
|
|
|
plan->subPlan != NULL ||
|
|
|
|
plan->qual != NULL ||
|
|
|
|
((Result *) plan)->resconstantqual != NULL)
|
1999-01-27 17:15:22 +01:00
|
|
|
return;
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2000-03-11 07:19:00 +01:00
|
|
|
* 4. The plan must have a single attribute as result
|
1999-01-27 17:15:22 +01:00
|
|
|
*/
|
2000-03-11 07:19:00 +01:00
|
|
|
if (length(plan->targetlist) != 1)
|
1999-01-27 17:15:22 +01:00
|
|
|
return;
|
|
|
|
|
2000-03-11 07:19:00 +01:00
|
|
|
tle = (TargetEntry *) lfirst(plan->targetlist);
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* 5. Check that all the nodes in the expression are one of Expr,
|
|
|
|
* Param or Const.
|
1999-01-27 17:15:22 +01:00
|
|
|
*/
|
|
|
|
if (!exec_simple_check_node(tle->expr))
|
|
|
|
return;
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* Yes - this is a simple expression. Remember the expression and the
|
|
|
|
* return type
|
1999-01-27 17:15:22 +01:00
|
|
|
*/
|
|
|
|
expr->plan_simple_expr = tle->expr;
|
2000-08-13 04:50:35 +02:00
|
|
|
expr->plan_simple_type = exprType(tle->expr);
|
1999-01-27 17:15:22 +01:00
|
|
|
}
|
|
|
|
|
1998-08-22 14:38:39 +02:00
|
|
|
/* ----------
|
|
|
|
* exec_set_found Set the global found variable
|
|
|
|
* to true/false
|
|
|
|
* ----------
|
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
static void
|
|
|
|
exec_set_found(PLpgSQL_execstate * estate, bool state)
|
1998-08-22 14:38:39 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
PLpgSQL_var *var;
|
1998-08-22 14:38:39 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
var = (PLpgSQL_var *) (estate->datums[estate->found_varno]);
|
|
|
|
var->value = (Datum) state;
|
|
|
|
var->isnull = false;
|
|
|
|
}
|