From d4266620e1c92d0b5d76d8c583f2fbfcf5bcd7fe Mon Sep 17 00:00:00 2001 From: Jan Wieck <JanWieck@Yahoo.com> Date: Thu, 31 Aug 2000 13:26:16 +0000 Subject: [PATCH] Added EXECUTE command to PL/pgSQL for execution of dynamic SQL and utility statements. Jan --- src/pl/plpgsql/src/gram.y | 50 ++++++- src/pl/plpgsql/src/pl_comp.c | 6 +- src/pl/plpgsql/src/pl_exec.c | 254 +++++++++++++++++++++++++++++++++- src/pl/plpgsql/src/pl_funcs.c | 38 ++++- src/pl/plpgsql/src/plpgsql.h | 26 +++- src/pl/plpgsql/src/scan.l | 3 +- 6 files changed, 368 insertions(+), 9 deletions(-) diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y index e76ddecb3c8..99f7a1b0f6e 100644 --- a/src/pl/plpgsql/src/gram.y +++ b/src/pl/plpgsql/src/gram.y @@ -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 diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index cc4196fb1e8..bed95890968 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -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) { diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 6c761c50f0c..7740c2790ee 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.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. diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c index 0b191ff603b..dc1a2f423d6 100644 --- a/src/pl/plpgsql/src/pl_funcs.c +++ b/src/pl/plpgsql/src/pl_funcs.c @@ -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) { diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index f4246980fca..e48a56ce893 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.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; diff --git a/src/pl/plpgsql/src/scan.l b/src/pl/plpgsql/src/scan.l index a3eae579205..73b605032f1 100644 --- a/src/pl/plpgsql/src/scan.l +++ b/src/pl/plpgsql/src/scan.l @@ -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; } -- GitLab