aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Wieck <JanWieck@Yahoo.com>1999-01-27 16:15:22 +0000
committerJan Wieck <JanWieck@Yahoo.com>1999-01-27 16:15:22 +0000
commit28d8b42ca5de929fc6099dff2ac5ef7129a1d722 (patch)
treeb41eee1daeb751cb5e4930db392bb77a13b04e6f
parentd611ccb874b908b38a709f1e21e9fdb6c69019ff (diff)
downloadpostgresql-28d8b42ca5de929fc6099dff2ac5ef7129a1d722.tar.gz
postgresql-28d8b42ca5de929fc6099dff2ac5ef7129a1d722.zip
Speedup of PL/pgSQL by calling ExecEvalExpr() directly
instead of SPI_execp() for simple expressions. Jan
-rw-r--r--src/backend/executor/spi.c37
-rw-r--r--src/include/executor/spi.h2
-rw-r--r--src/include/executor/spi_priv.h38
-rw-r--r--src/pl/plpgsql/src/pl_exec.c475
-rw-r--r--src/pl/plpgsql/src/plpgsql.h4
5 files changed, 439 insertions, 117 deletions
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index a7358425b47..7ca2a6c21e5 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -3,25 +3,16 @@
* spi.c--
* Server Programming Interface
*
- * $Id: spi.c,v 1.31 1999/01/27 00:36:21 tgl Exp $
+ * $Id: spi.c,v 1.32 1999/01/27 16:15:20 wieck Exp $
*
*-------------------------------------------------------------------------
*/
#include "executor/spi.h"
+#include "executor/spi_priv.h"
#include "catalog/pg_type.h"
#include "access/printtup.h"
#include "fmgr.h"
-typedef struct
-{
- QueryTreeList *qtlist; /* malloced */
- uint32 processed; /* by Executor */
- SPITupleTable *tuptable;
- Portal portal; /* portal per procedure */
- MemoryContext savedcxt;
- CommandId savedId;
-} _SPI_connection;
-
static Portal _SPI_portal = (Portal) NULL;
static _SPI_connection *_SPI_stack = NULL;
static _SPI_connection *_SPI_current = NULL;
@@ -32,24 +23,12 @@ uint32 SPI_processed = 0;
SPITupleTable *SPI_tuptable;
int SPI_result;
-typedef struct
-{
- QueryTreeList *qtlist;
- List *ptlist;
- int nargs;
- Oid *argtypes;
-} _SPI_plan;
-
static int _SPI_execute(char *src, int tcount, _SPI_plan *plan);
static int _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount);
static int _SPI_execute_plan(_SPI_plan *plan,
Datum *Values, char *Nulls, int tcount);
-#define _SPI_CPLAN_CURCXT 0
-#define _SPI_CPLAN_PROCXT 1
-#define _SPI_CPLAN_TOPCXT 2
-
static _SPI_plan *_SPI_copy_plan(_SPI_plan *plan, int location);
static int _SPI_begin_call(bool execmem);
@@ -178,6 +157,18 @@ SPI_finish()
}
+void
+SPI_push(void)
+{
+ _SPI_curid++;
+}
+
+void
+SPI_pop(void)
+{
+ _SPI_curid--;
+}
+
int
SPI_exec(char *src, int tcount)
{
diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h
index 72b6fe0f1fa..cc0ac6c4b7d 100644
--- a/src/include/executor/spi.h
+++ b/src/include/executor/spi.h
@@ -72,6 +72,8 @@ extern int SPI_result;
extern int SPI_connect(void);
extern int SPI_finish(void);
+extern void SPI_push(void);
+extern void SPI_pop(void);
extern int SPI_exec(char *src, int tcount);
extern int SPI_execp(void *plan, Datum *values, char *Nulls, int tcount);
extern void *SPI_prepare(char *src, int nargs, Oid *argtypes);
diff --git a/src/include/executor/spi_priv.h b/src/include/executor/spi_priv.h
new file mode 100644
index 00000000000..c0e44921f1c
--- /dev/null
+++ b/src/include/executor/spi_priv.h
@@ -0,0 +1,38 @@
+/*-------------------------------------------------------------------------
+ *
+ * spi.c--
+ * Server Programming Interface private declarations
+ *
+ * $Header: /cvsroot/pgsql/src/include/executor/spi_priv.h,v 1.1 1999/01/27 16:15:21 wieck Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SPI_PRIV_H
+#define SPI_PRIV_H
+
+#include "catalog/pg_type.h"
+#include "access/printtup.h"
+
+typedef struct
+{
+ QueryTreeList *qtlist; /* malloced */
+ uint32 processed; /* by Executor */
+ SPITupleTable *tuptable;
+ Portal portal; /* portal per procedure */
+ MemoryContext savedcxt;
+ CommandId savedId;
+} _SPI_connection;
+
+typedef struct
+{
+ QueryTreeList *qtlist;
+ List *ptlist;
+ int nargs;
+ Oid *argtypes;
+} _SPI_plan;
+
+#define _SPI_CPLAN_CURCXT 0
+#define _SPI_CPLAN_PROCXT 1
+#define _SPI_CPLAN_TOPCXT 2
+
+#endif /* SPI_PRIV_H */
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 89470729e8b..30155f7b082 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.5 1999/01/17 21:53:32 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.6 1999/01/27 16:15:22 wieck Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -48,6 +48,7 @@
#include "pl.tab.h"
#include "executor/spi.h"
+#include "executor/spi_priv.h"
#include "commands/trigger.h"
#include "utils/elog.h"
#include "utils/builtins.h"
@@ -116,6 +117,16 @@ static int exec_stmt_raise(PLpgSQL_execstate * estate,
static int exec_stmt_execsql(PLpgSQL_execstate * estate,
PLpgSQL_stmt_execsql * stmt);
+static void exec_prepare_plan(PLpgSQL_execstate * estate,
+ PLpgSQL_expr * expr);
+static bool exec_simple_check_node(Node * node);
+static void exec_simple_check_plan(PLpgSQL_expr * expr);
+static void exec_eval_clear_fcache(Node *node);
+static Datum exec_eval_simple_expr(PLpgSQL_execstate * estate,
+ PLpgSQL_expr * expr,
+ bool *isNull,
+ Oid *rettype);
+
static void exec_assign_expr(PLpgSQL_execstate * estate,
PLpgSQL_datum * target,
PLpgSQL_expr * expr);
@@ -1656,6 +1667,72 @@ exec_stmt_raise(PLpgSQL_execstate * estate, PLpgSQL_stmt_raise * stmt)
/* ----------
+ * Generate a prepared plan
+ * ----------
+ */
+static void
+exec_prepare_plan(PLpgSQL_execstate * estate,
+ PLpgSQL_expr * expr)
+{
+ PLpgSQL_var *var;
+ PLpgSQL_rec *rec;
+ PLpgSQL_recfield *recfield;
+ int i;
+ int fno;
+ void *plan;
+ Oid *argtypes;
+
+ /* ----------
+ * Setup the argtypes array
+ * ----------
+ */
+ argtypes = malloc(sizeof(Oid *) * (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]]);
+ 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:
+ elog(ERROR, "unknown parameter dtype %d in exec_run_select()", estate->datums[expr->params[i]]);
+ }
+ }
+
+ /* ----------
+ * 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);
+ expr->plan_argtypes = argtypes;
+ expr->plan_simple_expr = NULL;
+ exec_simple_check_plan(expr);
+}
+
+
+/* ----------
* exec_stmt_execsql Execute an SQL statement not
* returning any data.
* ----------
@@ -1683,48 +1760,7 @@ exec_stmt_execsql(PLpgSQL_execstate * estate,
* ----------
*/
if (expr->plan == NULL)
- {
- void *plan;
- Oid *argtypes;
-
- argtypes = malloc(sizeof(Oid *) * (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]]);
- 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:
- elog(ERROR, "unknown parameter dtype %d in exec_stmt_execsql()", estate->datums[expr->params[i]]->dtype);
- }
- }
-
- 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);
- expr->plan_argtypes = argtypes;
- }
+ exec_prepare_plan(estate, expr);
/* ----------
* Now build up the values and nulls arguments for SPI_execp()
@@ -1987,6 +2023,21 @@ exec_eval_expr(PLpgSQL_execstate * estate,
{
int rc;
+ /* ----------
+ * If not already done create a plan for this expression
+ * ----------
+ */
+ if (expr->plan == NULL)
+ exec_prepare_plan(estate, expr);
+
+ /* ----------
+ * If this is a simple expression, bypass SPI and use the
+ * executor directly
+ * ----------
+ */
+ if (expr->plan_simple_expr != NULL)
+ return exec_eval_simple_expr(estate, expr, isNull, rettype);
+
rc = exec_run_select(estate, expr, 2);
if (rc != SPI_OK_SELECT)
elog(ERROR, "query \"%s\" didn't return data", expr->query);
@@ -2045,56 +2096,7 @@ exec_run_select(PLpgSQL_execstate * estate,
* ----------
*/
if (expr->plan == NULL)
- {
- void *plan;
- Oid *argtypes;
-
- /* ----------
- * Setup the argtypes array
- * ----------
- */
- argtypes = malloc(sizeof(Oid *) * (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]]);
- 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:
- elog(ERROR, "unknown parameter dtype %d in exec_run_select()", estate->datums[expr->params[i]]);
- }
- }
-
- /* ----------
- * 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);
- expr->plan_argtypes = argtypes;
- }
+ exec_prepare_plan(estate, expr);
/* ----------
* Now build up the values and nulls arguments for SPI_execp()
@@ -2173,6 +2175,130 @@ exec_run_select(PLpgSQL_execstate * estate,
/* ----------
+ * exec_eval_simple_expr - Evaluate a simple expression returning
+ * a Datum by directly calling ExecEvalExpr().
+ * ----------
+ */
+static Datum
+exec_eval_simple_expr(PLpgSQL_execstate * estate,
+ PLpgSQL_expr * expr,
+ bool *isNull,
+ Oid *rettype)
+{
+ Datum retval;
+ PLpgSQL_var *var;
+ PLpgSQL_rec *rec;
+ PLpgSQL_recfield *recfield;
+ PLpgSQL_trigarg *trigarg;
+ int tgargno;
+ Oid tgargoid;
+ int fno;
+ int i;
+ bool isnull;
+ bool isdone;
+ ExprContext *econtext;
+ ParamListInfo paramLI;
+
+ /* ----------
+ * Create a simple expression context to hold the arguments
+ * ----------
+ */
+ econtext = makeNode(ExprContext);
+ paramLI = (ParamListInfo) palloc((expr->nparams + 1) *
+ sizeof(ParamListInfoData));
+ econtext->ecxt_param_list_info = paramLI;
+
+ /* ----------
+ * Put the parameter values into the parameter list info of
+ * the expression context.
+ * ----------
+ */
+ for (i = 0; i < expr->nparams; i++, paramLI++)
+ {
+ paramLI->kind = PARAM_NUM;
+ paramLI->id = i + 1;
+
+ 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);
+ 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;
+
+ /* ----------
+ * Initialize things
+ * ----------
+ */
+ *isNull = FALSE;
+ *rettype = expr->plan_simple_type;
+ isdone = FALSE;
+
+ /* ----------
+ * Clear the function cache
+ * ----------
+ */
+ exec_eval_clear_fcache(expr->plan_simple_expr);
+
+ /* ----------
+ * Now call the executor to evaluate the expression
+ * ----------
+ */
+ SPI_push();
+ retval = ExecEvalExpr(expr->plan_simple_expr,
+ econtext,
+ isNull,
+ &isdone);
+ SPI_pop();
+
+ /* ----------
+ * That's it.
+ * ----------
+ */
+ return retval;
+}
+
+
+/* ----------
* exec_move_row Move one tuples values into a
* record or row
* ----------
@@ -2297,6 +2423,169 @@ exec_cast_value(Datum value, Oid valtype,
/* ----------
+ * 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
+exec_simple_check_node(Node * node)
+{
+ switch (nodeTag(node))
+ {
+ case T_Expr: {
+ 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;
+ }
+
+ foreach (l, expr->args)
+ {
+ if (!exec_simple_check_node(lfirst(l)))
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ case T_Param: return TRUE;
+
+ case T_Const: return TRUE;
+
+ default: return FALSE;
+ }
+}
+
+
+/* ----------
+ * 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)
+{
+ _SPI_plan *spi_plan = (_SPI_plan *)expr->plan;
+ Plan *plan;
+ TargetEntry *tle;
+
+ expr->plan_simple_expr = NULL;
+
+ /* ----------
+ * 1. We can only evaluate queries that resulted in one single
+ * execution plan
+ * ----------
+ */
+ if (spi_plan->ptlist == NULL || length(spi_plan->ptlist) != 1)
+ return;
+
+ plan = (Plan *)lfirst(spi_plan->ptlist);
+
+ /* ----------
+ * 2. It must be a RESULT plan --> no scan's required
+ * ----------
+ */
+ if (nodeTag(plan) != T_Result)
+ return;
+
+ /* ----------
+ * 3. The plan must have a single attribute as result
+ * ----------
+ */
+ if (length(plan->targetlist) != 1)
+ return;
+
+ /* ----------
+ * 4. Don't know if all these can break us, so let SPI handle
+ * those plans
+ * ----------
+ */
+ if (plan->qual != NULL || plan->lefttree != NULL || plan->righttree != NULL)
+ return;
+
+ /* ----------
+ * 5. Check that all the nodes in the expression are one of
+ * Expr, Param or Const.
+ * ----------
+ */
+ tle = (TargetEntry *)lfirst(plan->targetlist);
+ if (!exec_simple_check_node(tle->expr))
+ return;
+
+ /* ----------
+ * Yes - this is a simple expression. Remember the expression
+ * and the return type
+ * ----------
+ */
+ expr->plan_simple_expr = tle->expr;
+
+ switch (nodeTag(tle->expr))
+ {
+ case T_Expr: expr->plan_simple_type =
+ ((Expr *)(tle->expr))->typeOid;
+ break;
+
+ case T_Param: expr->plan_simple_type =
+ ((Param *)(tle->expr))->paramtype;
+ break;
+
+ case T_Const: expr->plan_simple_type =
+ ((Const *)(tle->expr))->consttype;
+ break;
+
+ default: expr->plan_simple_type = InvalidOid;
+ }
+
+ return;
+}
+
+
+/* ----------
+ * exec_eval_clear_fcache - The function cache is palloc()'d by
+ * the executor, and contains call specific
+ * data based on the arguments. This has
+ * to be recalculated.
+ * ----------
+ */
+static void
+exec_eval_clear_fcache(Node *node)
+{
+ Expr *expr;
+ List *l;
+
+ if (nodeTag(node) != T_Expr)
+ return;
+
+ expr = (Expr *)node;
+
+ switch(expr->opType)
+ {
+ case OP_EXPR: ((Oper *)(expr->oper))->op_fcache = NULL;
+ break;
+
+ case FUNC_EXPR: ((Func *)(expr->oper))->func_fcache = NULL;
+ break;
+
+ default: break;
+ }
+
+ foreach (l, expr->args)
+ exec_eval_clear_fcache(lfirst(l));
+}
+
+
+/* ----------
* exec_set_found Set the global found variable
* to true/false
* ----------
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 281cdc64a1c..2d0a57cd2d0 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.2 1998/09/01 04:40:27 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.3 1999/01/27 16:15:22 wieck Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -143,6 +143,8 @@ typedef struct
int exprno;
char *query;
void *plan;
+ Node *plan_simple_expr;
+ Oid plan_simple_type;
Oid *plan_argtypes;
int nparams;
int params[1];