From 4be20187ab0c8dd95a1dd62b67a17ffaf1f33279 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 15 Nov 2001 23:31:09 +0000 Subject: [PATCH] Fix some problems in new plpgsql cursor operations, found while trying to reverse-engineer documentation for them. --- src/pl/plpgsql/src/gram.y | 46 +++++----- src/pl/plpgsql/src/pl_exec.c | 153 +++++----------------------------- src/pl/plpgsql/src/pl_funcs.c | 49 ++++++++++- src/pl/plpgsql/src/plpgsql.h | 5 +- 4 files changed, 98 insertions(+), 155 deletions(-) diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y index a7da1710c1a..abb2ac8e7f3 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.27 2001/10/09 15:59:56 tgl Exp $ + * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.28 2001/11/15 23:31:09 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -351,7 +351,9 @@ decl_statement : decl_varname decl_const decl_datatype decl_notnull decl_defval { plpgsql_ns_rename($2, $4); } - | decl_varname K_CURSOR decl_cursor_args decl_is_from K_SELECT decl_cursor_query + | decl_varname K_CURSOR + { plpgsql_ns_push(NULL); } + decl_cursor_args decl_is_from K_SELECT decl_cursor_query { PLpgSQL_var *new; PLpgSQL_expr *curname_def; @@ -359,6 +361,7 @@ decl_statement : decl_varname decl_const decl_datatype decl_notnull decl_defval char *cp1; char *cp2; + /* pop local namespace for cursor args */ plpgsql_ns_pop(); new = malloc(sizeof(PLpgSQL_var)); @@ -381,22 +384,21 @@ decl_statement : decl_varname decl_const decl_datatype decl_notnull decl_defval *cp2++ = '\\'; *cp2++ = *cp1++; } - *cp2++ = '\''; - *cp2 = '\0'; + strcpy(cp2, "'::refcursor"); curname_def->query = strdup(buf); new->default_val = curname_def; new->datatype = plpgsql_parse_datatype("refcursor"); - new->cursor_explicit_expr = $6; - if ($3 == NULL) + new->cursor_explicit_expr = $7; + if ($4 == NULL) new->cursor_explicit_argrow = -1; else - new->cursor_explicit_argrow = $3->rowno; + new->cursor_explicit_argrow = $4->rowno; plpgsql_adddatum((PLpgSQL_datum *)new); plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, new->varno, - $1.name); + $1.name); } ; @@ -416,15 +418,17 @@ decl_cursor_args : { $$ = NULL; } - | decl_cursor_openparen decl_cursor_arglist ')' + | '(' decl_cursor_arglist ')' { + /* Copy the temp arrays to malloc'd storage */ + int nfields = $2->nfields; char **ftmp; int *vtmp; - ftmp = malloc($2->nfields * sizeof(char *)); - vtmp = malloc($2->nfields * sizeof(int)); - memcpy(ftmp, $2->fieldnames, $2->nfields * sizeof(char *)); - memcpy(vtmp, $2->varnos, $2->nfields * sizeof(int)); + ftmp = malloc(nfields * sizeof(char *)); + vtmp = malloc(nfields * sizeof(int)); + memcpy(ftmp, $2->fieldnames, nfields * sizeof(char *)); + memcpy(vtmp, $2->varnos, nfields * sizeof(int)); pfree((char *)($2->fieldnames)); pfree((char *)($2->varnos)); @@ -449,6 +453,12 @@ decl_cursor_arglist : decl_cursor_arg new->refname = strdup("*internal*"); new->lineno = yylineno; new->rowtypeclass = InvalidOid; + /* + * We make temporary fieldnames/varnos arrays that + * are much bigger than necessary. We will resize + * them to just the needed size in the + * decl_cursor_args production. + */ new->fieldnames = palloc(1024 * sizeof(char *)); new->varnos = palloc(1024 * sizeof(int)); new->nfields = 1; @@ -464,6 +474,8 @@ decl_cursor_arglist : decl_cursor_arg $1->fieldnames[i] = $3->refname; $1->varnos[i] = $3->varno; + + $$ = $1; } ; @@ -484,18 +496,12 @@ decl_cursor_arg : decl_varname decl_datatype plpgsql_adddatum((PLpgSQL_datum *)new); plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, new->varno, - $1.name); + $1.name); $$ = new; } ; -decl_cursor_openparen : '(' - { - plpgsql_ns_push(NULL); - } - ; - decl_is_from : K_IS | /* Oracle */ K_FOR; /* ANSI */ diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 5f2d58f4dd9..d61fac52897 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.51 2001/11/13 02:05:27 tgl Exp $ + * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.52 2001/11/15 23:31:09 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -192,76 +192,12 @@ plpgsql_exec_function(PLpgSQL_function * func, FunctionCallInfo fcinfo) elog(NOTICE, "Error occurred while executing PL/pgSQL function %s", 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; - case PLPGSQL_STMT_GETDIAG: - stmttype = "get diagnostics"; - break; - 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; - case PLPGSQL_STMT_DYNEXECUTE: - stmttype = "execute statement"; - break; - case PLPGSQL_STMT_DYNFORS: - stmttype = "for over execute statement"; - break; - case PLPGSQL_STMT_FETCH: - stmttype = "fetch"; - break; - case PLPGSQL_STMT_CLOSE: - stmttype = "close"; - break; - default: - stmttype = "unknown"; - break; - } elog(NOTICE, "line %d at %s", error_info_stmt->lineno, - stmttype); - } + plpgsql_stmt_typename(error_info_stmt)); + else if (error_info_text != NULL) + elog(NOTICE, "%s", error_info_text); else - { - if (error_info_text != NULL) - elog(NOTICE, "%s", error_info_text); - else - elog(NOTICE, "no more error information available"); - } + elog(NOTICE, "no more error information available"); error_info_func = NULL; error_info_stmt = NULL; @@ -504,70 +440,12 @@ plpgsql_exec_trigger(PLpgSQL_function * func, elog(NOTICE, "Error occurred while executing PL/pgSQL function %s", 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; - case PLPGSQL_STMT_GETDIAG: - stmttype = "get diagnostics"; - break; - 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; - case PLPGSQL_STMT_DYNEXECUTE: - stmttype = "execute statement"; - break; - case PLPGSQL_STMT_DYNFORS: - stmttype = "for over execute statement"; - break; - default: - stmttype = "unknown"; - break; - } elog(NOTICE, "line %d at %s", error_info_stmt->lineno, - stmttype); - } + plpgsql_stmt_typename(error_info_stmt)); + else if (error_info_text != NULL) + elog(NOTICE, "%s", error_info_text); else - { - if (error_info_text != NULL) - elog(NOTICE, "%s", error_info_text); - else - elog(NOTICE, "no more error information available"); - } + elog(NOTICE, "no more error information available"); error_info_func = NULL; error_info_stmt = NULL; @@ -2412,7 +2290,7 @@ exec_stmt_open(PLpgSQL_execstate * estate, PLpgSQL_stmt_open * stmt) HeapTuple typetup; Form_pg_type typeStruct; FmgrInfo finfo_output; - void *curplan = NULL; + void *curplan; /* ---------- * We evaluate the string expression after the @@ -2471,6 +2349,9 @@ exec_stmt_open(PLpgSQL_execstate * estate, PLpgSQL_stmt_open * stmt) { /* ---------- * This is an OPEN cursor + * + * Note: parser should already have checked that statement supplies + * args iff cursor needs them, but we check again to be safe. * ---------- */ if (stmt->argquery != NULL) @@ -2483,6 +2364,9 @@ exec_stmt_open(PLpgSQL_execstate * estate, PLpgSQL_stmt_open * stmt) */ PLpgSQL_stmt_select set_args; + if (curvar->cursor_explicit_argrow < 0) + elog(ERROR, "arguments given for cursor without arguments"); + memset(&set_args, 0, sizeof(set_args)); set_args.cmd_type = PLPGSQL_STMT_SELECT; set_args.lineno = stmt->lineno; @@ -2493,6 +2377,11 @@ exec_stmt_open(PLpgSQL_execstate * estate, PLpgSQL_stmt_open * stmt) if (exec_stmt_select(estate, &set_args) != PLPGSQL_RC_OK) elog(ERROR, "open cursor failed during argument processing"); } + else + { + if (curvar->cursor_explicit_argrow >= 0) + elog(ERROR, "arguments required for cursor"); + } query = curvar->cursor_explicit_expr; if (query->plan == NULL) diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c index 61ab93a6cd4..6a503505c4c 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.16 2001/10/09 15:59:56 tgl Exp $ + * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.17 2001/11/15 23:31:09 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -360,7 +360,54 @@ plpgsql_tolower(char *s) } +/* + * Statement type as a string, for use in error messages etc. + */ +const char * +plpgsql_stmt_typename(PLpgSQL_stmt * stmt) +{ + switch (stmt->cmd_type) + { + case PLPGSQL_STMT_BLOCK: + return "block variables initialization"; + case PLPGSQL_STMT_ASSIGN: + return "assignment"; + case PLPGSQL_STMT_IF: + return "if"; + case PLPGSQL_STMT_LOOP: + return "loop"; + case PLPGSQL_STMT_WHILE: + return "while"; + case PLPGSQL_STMT_FORI: + return "for with integer loopvar"; + case PLPGSQL_STMT_FORS: + return "for over select rows"; + case PLPGSQL_STMT_SELECT: + return "select into variables"; + case PLPGSQL_STMT_EXIT: + return "exit"; + case PLPGSQL_STMT_RETURN: + return "return"; + case PLPGSQL_STMT_RAISE: + return "raise"; + case PLPGSQL_STMT_EXECSQL: + return "SQL statement"; + case PLPGSQL_STMT_DYNEXECUTE: + return "execute statement"; + case PLPGSQL_STMT_DYNFORS: + return "for over execute statement"; + case PLPGSQL_STMT_GETDIAG: + return "get diagnostics"; + case PLPGSQL_STMT_OPEN: + return "open"; + case PLPGSQL_STMT_FETCH: + return "fetch"; + case PLPGSQL_STMT_CLOSE: + return "close"; + } + return "unknown"; +} /********************************************************************** diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index 8065764ed57..7856a0dbb5f 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.22 2001/11/05 17:46:39 momjian Exp $ + * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.23 2001/11/15 23:31:09 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -598,8 +598,9 @@ extern void plpgsql_ns_rename(char *oldname, char *newname); * Other functions in pl_funcs.c * ---------- */ -extern void plpgsql_dumptree(PLpgSQL_function * func); extern char *plpgsql_tolower(char *s); +extern const char *plpgsql_stmt_typename(PLpgSQL_stmt * stmt); +extern void plpgsql_dumptree(PLpgSQL_function * func); /* ---------- * Externs in gram.y and scan.l