Added EXECUTE command to PL/pgSQL for execution of
authorJan Wieck <JanWieck@Yahoo.com>
Thu, 31 Aug 2000 13:26:16 +0000 (13:26 +0000)
committerJan Wieck <JanWieck@Yahoo.com>
Thu, 31 Aug 2000 13:26:16 +0000 (13:26 +0000)
dynamic SQL and utility statements.

Jan

src/pl/plpgsql/src/gram.y
src/pl/plpgsql/src/pl_comp.c
src/pl/plpgsql/src/pl_exec.c
src/pl/plpgsql/src/pl_funcs.c
src/pl/plpgsql/src/plpgsql.h
src/pl/plpgsql/src/scan.l

index e76ddecb3c8f1b2bc1d52575e7ff8ddd97d31866..99f7a1b0f6e820f615264bdbd039c15152498e05 100644 (file)
@@ -4,7 +4,7 @@
  *           procedural language
  *
  * IDENTIFICATION
- *    $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.10 2000/06/05 07:29:14 tgl Exp $
+ *    $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.11 2000/08/31 13:26:15 wieck Exp $
  *
  *    This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -113,6 +113,7 @@ static  PLpgSQL_expr    *make_tupret_expr(PLpgSQL_row *row);
 %type <stmt>   stmt_assign, stmt_if, stmt_loop, stmt_while, stmt_exit
 %type <stmt>   stmt_return, stmt_raise, stmt_execsql, stmt_fori
 %type <stmt>   stmt_fors, stmt_select, stmt_perform
+%type <stmt>   stmt_dynexecute, stmt_dynfors
 
 %type <dtlist> raise_params
 %type <ival>   raise_level, raise_param
@@ -134,6 +135,7 @@ static  PLpgSQL_expr    *make_tupret_expr(PLpgSQL_row *row);
 %token K_ELSE
 %token K_END
 %token K_EXCEPTION
+%token K_EXECUTE
 %token K_EXIT
 %token K_FOR
 %token K_FROM
@@ -568,6 +570,10 @@ proc_stmt  : pl_block
            { $$ = $1; }
        | stmt_execsql
            { $$ = $1; }
+       | stmt_dynexecute
+           { $$ = $1; }
+       | stmt_dynfors
+           { $$ = $1; }
        | stmt_perform
            { $$ = $1; }
        ;
@@ -844,6 +850,35 @@ stmt_fors  : opt_label K_FOR lno fors_target K_IN K_SELECT expr_until_loop loop_b
            $$ = (PLpgSQL_stmt *)new;
            }
 
+stmt_dynfors : opt_label K_FOR lno fors_target K_IN K_EXECUTE expr_until_loop loop_body
+           {
+           PLpgSQL_stmt_dynfors    *new;
+
+           new = malloc(sizeof(PLpgSQL_stmt_dynfors));
+           memset(new, 0, sizeof(PLpgSQL_stmt_dynfors));
+
+           new->cmd_type = PLPGSQL_STMT_DYNFORS;
+           new->lineno   = $3;
+           new->label    = $1;
+           switch ($4->dtype) {
+               case PLPGSQL_DTYPE_REC:
+                   new->rec = $4;
+               break;
+               case PLPGSQL_DTYPE_ROW:
+                   new->row = (PLpgSQL_row *)$4;
+               break;
+               default:
+               plpgsql_comperrinfo();
+                   elog(ERROR, "unknown dtype %d in stmt_dynfors", $4->dtype);
+           }
+           new->query = $7;
+           new->body  = $8;
+
+           plpgsql_ns_pop();
+
+           $$ = (PLpgSQL_stmt *)new;
+           }
+
 fors_target    : T_RECORD
            {
                $$ = yylval.rec;
@@ -1028,6 +1063,19 @@ stmt_execsql : execsql_start lno
            }
        ;
 
+stmt_dynexecute    : K_EXECUTE lno expr_until_semi
+           {
+               PLpgSQL_stmt_dynexecute *new;
+
+           new = malloc(sizeof(PLpgSQL_stmt_dynexecute));
+           new->cmd_type = PLPGSQL_STMT_DYNEXECUTE;
+           new->lineno   = $2;
+           new->query    = $3;
+
+           $$ = (PLpgSQL_stmt *)new;
+           }
+       ;
+
 execsql_start  : T_WORD
            { $$ = strdup(yytext); }
        | T_ERROR
index cc4196fb1e81061cc4f20e83f33c920bbc3d0849..bed95890968efb4f57af1dadf440a6ed827d4937 100644 (file)
@@ -3,7 +3,7 @@
  *           procedural language
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.22 2000/08/03 16:34:57 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.23 2000/08/31 13:26:16 wieck Exp $
  *
  *   This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -159,8 +159,8 @@ plpgsql_compile(Oid fn_oid, int functype)
 
    function->fn_functype = functype;
    function->fn_oid = fn_oid;
-   function->fn_name = DatumGetCString(DirectFunctionCall1(nameout,
-                       NameGetDatum(&(procStruct->proname))));
+   function->fn_name = strdup(DatumGetCString(DirectFunctionCall1(nameout,
+                       NameGetDatum(&(procStruct->proname)))));
 
    switch (functype)
    {
index 6c761c50f0c843e93cc8d4914c2051090b2a3408..7740c2790ee71d542fecaf49601c5fb618af488c 100644 (file)
@@ -3,7 +3,7 @@
  *           procedural language
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.28 2000/08/24 03:29:15 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.29 2000/08/31 13:26:16 wieck Exp $
  *
  *   This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -100,6 +100,10 @@ static int exec_stmt_raise(PLpgSQL_execstate * estate,
                PLpgSQL_stmt_raise * stmt);
 static int exec_stmt_execsql(PLpgSQL_execstate * estate,
                  PLpgSQL_stmt_execsql * stmt);
+static int exec_stmt_dynexecute(PLpgSQL_execstate * estate,
+                 PLpgSQL_stmt_dynexecute * stmt);
+static int exec_stmt_dynfors(PLpgSQL_execstate * estate,
+              PLpgSQL_stmt_dynfors * stmt);
 
 static void exec_prepare_plan(PLpgSQL_execstate * estate,
                  PLpgSQL_expr * expr);
@@ -219,6 +223,12 @@ plpgsql_exec_function(PLpgSQL_function * func, FunctionCallInfo fcinfo)
                    case PLPGSQL_STMT_EXECSQL:
                        stmttype = "SQL statement";
                        break;
+                   case PLPGSQL_STMT_DYNEXECUTE:
+                       stmttype = "execute statement";
+                       break;
+                   case PLPGSQL_STMT_DYNFORS:
+                       stmttype = "for over execute statement";
+                       break;
                    default:
                        stmttype = "unknown";
                        break;
@@ -522,6 +532,12 @@ plpgsql_exec_trigger(PLpgSQL_function * func,
                    case PLPGSQL_STMT_EXECSQL:
                        stmttype = "SQL statement";
                        break;
+                   case PLPGSQL_STMT_DYNEXECUTE:
+                       stmttype = "execute statement";
+                       break;
+                   case PLPGSQL_STMT_DYNFORS:
+                       stmttype = "for over execute statement";
+                       break;
                    default:
                        stmttype = "unknown";
                        break;
@@ -995,6 +1011,14 @@ exec_stmt(PLpgSQL_execstate * estate, PLpgSQL_stmt * stmt)
            rc = exec_stmt_execsql(estate, (PLpgSQL_stmt_execsql *) stmt);
            break;
 
+       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;
+
        default:
            error_info_stmt = save_estmt;
            elog(ERROR, "unknown cmdtype %d in exec_stmt",
@@ -1852,6 +1876,234 @@ exec_stmt_execsql(PLpgSQL_execstate * estate,
 }
 
 
+/* ----------
+ * exec_stmt_dynexecute            Execute a dynamic SQL query not
+ *                 returning any data.
+ * ----------
+ */
+static int
+exec_stmt_dynexecute(PLpgSQL_execstate * estate,
+                 PLpgSQL_stmt_dynexecute * stmt)
+{
+   Datum       query;
+   bool        isnull = false;
+   Oid         restype;
+   char       *querystr;
+   HeapTuple   typetup;
+   Form_pg_type typeStruct;
+   FmgrInfo    finfo_output;
+
+   /* ----------
+    * First we evaluate the string expression after the
+    * EXECUTE keyword. It's result is the querystring we have
+    * to execute.
+    * ----------
+    */
+   query = exec_eval_expr(estate, stmt->query, &isnull, &restype);
+   if (isnull)
+       elog(ERROR, "cannot EXECUTE NULL-query");
+
+   /* ----------
+    * Get the C-String representation.
+    * ----------
+    */
+   typetup = SearchSysCacheTuple(TYPEOID,
+       ObjectIdGetDatum(restype), 0, 0, 0);
+   if (!HeapTupleIsValid(typetup))
+       elog(ERROR, "cache lookup for type %u failed (1)", restype);
+   typeStruct = (Form_pg_type) GETSTRUCT(typetup);
+
+   fmgr_info(typeStruct->typoutput, &finfo_output);
+   querystr = DatumGetCString(FunctionCall3(&finfo_output,
+               query,
+               ObjectIdGetDatum(typeStruct->typelem),
+               Int32GetDatum(-1)));
+
+   if(!typeStruct->typbyval)
+       pfree((void *)query);
+
+   /* ----------
+    * Call SPI_exec() without preparing a saved plan. 
+    * The returncode can be any OK except for OK_SELECT.
+    * ----------
+    */
+   switch(SPI_exec(querystr, 0))
+   {
+       case SPI_OK_UTILITY:
+       case SPI_OK_SELINTO:
+       case SPI_OK_INSERT:
+       case SPI_OK_UPDATE:
+       case SPI_OK_DELETE:
+           break;
+
+       case SPI_OK_SELECT:
+           elog(ERROR, "unexpected SELECT operation in EXECUTE of query '%s'",
+                       querystr);
+           break;
+
+       default:
+           elog(ERROR, "unexpected error in EXECUTE for query '%s'",
+                       querystr);
+           break;
+   }
+
+   pfree(querystr);
+   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;
+
+   /* ----------
+    * Initialize the global found variable to false
+    * ----------
+    */
+   exec_set_found(estate, false);
+
+   /* ----------
+    * 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()");
+   }
+
+   /* ----------
+    * Evaluate the string expression after the
+    * EXECUTE keyword. It's result is the querystring we have
+    * to execute.
+    * ----------
+    */
+   query = exec_eval_expr(estate, stmt->query, &isnull, &restype);
+   if (isnull)
+       elog(ERROR, "cannot EXECUTE NULL-query");
+
+   /* ----------
+    * Get the C-String representation.
+    * ----------
+    */
+   typetup = SearchSysCacheTuple(TYPEOID,
+       ObjectIdGetDatum(restype), 0, 0, 0);
+   if (!HeapTupleIsValid(typetup))
+       elog(ERROR, "cache lookup for type %u failed (1)", restype);
+   typeStruct = (Form_pg_type) GETSTRUCT(typetup);
+
+   fmgr_info(typeStruct->typoutput, &finfo_output);
+   querystr = DatumGetCString(FunctionCall3(&finfo_output,
+               query,
+               ObjectIdGetDatum(typeStruct->typelem),
+               Int32GetDatum(-1)));
+
+   if(!typeStruct->typbyval)
+       pfree((void *)query);
+
+   /* ----------
+    * Run the query
+    * ----------
+    */
+   if (SPI_exec(querystr, 0) != SPI_OK_SELECT)
+       elog(ERROR, "FOR ... EXECUTE query '%s' was no SELECT", querystr);
+   pfree(querystr);
+
+   n = SPI_processed;
+
+   /* ----------
+    * If the query didn't return any row, set the target
+    * to NULL and return.
+    * ----------
+    */
+   if (n == 0)
+   {
+       exec_move_row(estate, rec, row, NULL, NULL);
+       return PLPGSQL_RC_OK;
+   }
+
+   /* ----------
+    * There are tuples, so set found to true
+    * ----------
+    */
+   exec_set_found(estate, true);
+
+   /* ----------
+    * Now do the loop
+    * ----------
+    */
+   tuptab = SPI_tuptable;
+   SPI_tuptable = NULL;
+
+   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:
+               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);
+       }
+   }
+
+   return PLPGSQL_RC_OK;
+}
+
+
 /* ----------
  * exec_assign_expr            Put an expressions result into
  *                 a variable.
index 0b191ff603b28dfdee14b2dc5496d167b0a88023..dc1a2f423d6f9831dd2fecb614aac8617a41a144 100644 (file)
@@ -3,7 +3,7 @@
  *           procedural language
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.6 2000/06/14 18:18:00 petere Exp $
+ *   $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.7 2000/08/31 13:26:16 wieck Exp $
  *
  *   This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -388,6 +388,8 @@ static void dump_exit(PLpgSQL_stmt_exit * stmt);
 static void dump_return(PLpgSQL_stmt_return * stmt);
 static void dump_raise(PLpgSQL_stmt_raise * stmt);
 static void dump_execsql(PLpgSQL_stmt_execsql * stmt);
+static void dump_dynexecute(PLpgSQL_stmt_dynexecute * stmt);
+static void dump_dynfors(PLpgSQL_stmt_dynfors * stmt);
 static void dump_expr(PLpgSQL_expr * expr);
 
 
@@ -442,6 +444,12 @@ dump_stmt(PLpgSQL_stmt * stmt)
        case PLPGSQL_STMT_EXECSQL:
            dump_execsql((PLpgSQL_stmt_execsql *) stmt);
            break;
+       case PLPGSQL_STMT_DYNEXECUTE:
+           dump_dynexecute((PLpgSQL_stmt_dynexecute *) stmt);
+           break;
+       case PLPGSQL_STMT_DYNFORS:
+           dump_dynfors((PLpgSQL_stmt_dynfors *) stmt);
+           break;
        default:
            elog(ERROR, "plpgsql_dump: unknown cmd_type %d\n", stmt->cmd_type);
            break;
@@ -662,6 +670,34 @@ dump_execsql(PLpgSQL_stmt_execsql * stmt)
    printf("\n");
 }
 
+static void
+dump_dynexecute(PLpgSQL_stmt_dynexecute * stmt)
+{
+   dump_ind();
+   printf("EXECUTE ");
+   dump_expr(stmt->query);
+   printf("\n");
+}
+
+static void
+dump_dynfors(PLpgSQL_stmt_dynfors * stmt)
+{
+   int         i;
+
+   dump_ind();
+   printf("FORS %s EXECUTE ", (stmt->rec != NULL) ? stmt->rec->refname : stmt->row->refname);
+   dump_expr(stmt->query);
+   printf("\n");
+
+   dump_indent += 2;
+   for (i = 0; i < stmt->body->stmts_used; i++)
+       dump_stmt((PLpgSQL_stmt *) (stmt->body->stmts[i]));
+   dump_indent -= 2;
+
+   dump_ind();
+   printf("    ENDFORS\n");
+}
+
 static void
 dump_expr(PLpgSQL_expr * expr)
 {
index f4246980fca540b44a42def4444f7df6c27560b2..e48a56ce89303736cafe5f73f3d272532d5a4e90 100644 (file)
@@ -3,7 +3,7 @@
  *           procedural language
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.9 2000/05/28 17:56:28 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.10 2000/08/31 13:26:16 wieck Exp $
  *
  *   This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -91,7 +91,9 @@ enum
    PLPGSQL_STMT_EXIT,
    PLPGSQL_STMT_RETURN,
    PLPGSQL_STMT_RAISE,
-   PLPGSQL_STMT_EXECSQL
+   PLPGSQL_STMT_EXECSQL,
+   PLPGSQL_STMT_DYNEXECUTE,
+   PLPGSQL_STMT_DYNFORS
 };
 
 
@@ -318,6 +320,18 @@ typedef struct
 }          PLpgSQL_stmt_fors;
 
 
+typedef struct
+{                              /* FOR statement running over EXECUTE   */
+   int         cmd_type;
+   int         lineno;
+   char       *label;
+   PLpgSQL_rec *rec;
+   PLpgSQL_row *row;
+   PLpgSQL_expr *query;
+   PLpgSQL_stmts *body;
+}          PLpgSQL_stmt_dynfors;
+
+
 typedef struct
 {                              /* SELECT ... INTO statement        */
    int         cmd_type;
@@ -366,6 +380,14 @@ typedef struct
 }          PLpgSQL_stmt_execsql;
 
 
+typedef struct
+{                              /* Dynamic SQL string to execute */
+   int         cmd_type;
+   int         lineno;
+   PLpgSQL_expr *query;
+}          PLpgSQL_stmt_dynexecute;
+
+
 typedef struct PLpgSQL_function
 {                              /* Complete compiled function     */
    Oid         fn_oid;
index a3eae579205268cbba543b9adfdc446c40c50e68..73b605032f126f82da082154657375a2a69eba3b 100644 (file)
@@ -4,7 +4,7 @@
  *           procedural language
  *
  * IDENTIFICATION
- *    $Header: /cvsroot/pgsql/src/pl/plpgsql/src/Attic/scan.l,v 1.5 2000/08/22 14:59:28 tgl Exp $
+ *    $Header: /cvsroot/pgsql/src/pl/plpgsql/src/Attic/scan.l,v 1.6 2000/08/31 13:26:16 wieck Exp $
  *
  *    This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -100,6 +100,7 @@ default         { return K_DEFAULT;         }
 else           { return K_ELSE;            }
 end                { return K_END;             }
 exception      { return K_EXCEPTION;       }
+execute            { return K_EXECUTE;         }
 exit           { return K_EXIT;            }
 for                { return K_FOR;             }
 from           { return K_FROM;            }