]> granicus.if.org Git - postgresql/commitdiff
Speedup of PL/pgSQL by calling ExecEvalExpr() directly
authorJan Wieck <JanWieck@Yahoo.com>
Wed, 27 Jan 1999 16:15:22 +0000 (16:15 +0000)
committerJan Wieck <JanWieck@Yahoo.com>
Wed, 27 Jan 1999 16:15:22 +0000 (16:15 +0000)
instead of SPI_execp() for simple expressions.

Jan

src/backend/executor/spi.c
src/include/executor/spi.h
src/include/executor/spi_priv.h [new file with mode: 0644]
src/pl/plpgsql/src/pl_exec.c
src/pl/plpgsql/src/plpgsql.h

index a7358425b47582587b5411624e6b1305ff5e4379..7ca2a6c21e507972d953bbe821171e7d5c9dbfe6 100644 (file)
@@ -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)
 {
index 72b6fe0f1fa1c4cbebc80c097c138e39ad0a336f..cc0ac6c4b7d0c43b6aca3db385401c48928a87c5 100644 (file)
@@ -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 (file)
index 0000000..c0e4492
--- /dev/null
@@ -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 */
index 89470729e8ba61414d33ff506e3e6b63712b51e7..30155f7b082d0205233e7540b43afa6d47cb28d2 100644 (file)
@@ -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);
@@ -1655,6 +1666,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()
@@ -2172,6 +2174,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
@@ -2296,6 +2422,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
index 281cdc64a1c8efd0e601cdcc4771ee41695f001f..2d0a57cd2d04324d6f7c7e35339a651b21775d8c 100644 (file)
@@ -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];