function->nstatements = 0;
function->requires_procedure_resowner = false;
+ function->has_exception_block = false;
/*
* Initialize the compiler, particularly the namespace stack. The
plpgsql_finish_datums(function);
+ if (function->has_exception_block)
+ plpgsql_mark_local_assignment_targets(function);
+
/* Debug dump for completed functions */
if (plpgsql_DumpExecTree)
plpgsql_dumptree(function);
function->nstatements = 0;
function->requires_procedure_resowner = false;
+ function->has_exception_block = false;
plpgsql_ns_init();
plpgsql_ns_push(func_name, PLPGSQL_LABEL_BLOCK);
plpgsql_finish_datums(function);
+ if (function->has_exception_block)
+ plpgsql_mark_local_assignment_targets(function);
+
+ /* Debug dump for completed functions */
+ if (plpgsql_DumpExecTree)
+ plpgsql_dumptree(function);
+
/*
* Pop the error context stack
*/
}
+/**********************************************************************
+ * Mark assignment source expressions that have local target variables,
+ * that is, the target variable is declared within the exception block
+ * most closely containing the assignment itself. (Such target variables
+ * need not be preserved if the assignment's source expression raises an
+ * error, since the variable will no longer be accessible afterwards.
+ * Detecting this allows better optimization.)
+ *
+ * This code need not be called if the plpgsql function contains no exception
+ * blocks, because mark_expr_as_assignment_source will have set all the flags
+ * to true already. Also, we need not reconsider default-value expressions
+ * for variables, because variable declarations are necessarily within the
+ * nearest exception block. (In DECLARE ... BEGIN ... EXCEPTION ... END, the
+ * variable initializations are done before entering the exception scope.)
+ *
+ * Within the recursion, local_dnos is a Bitmapset of dnos of variables
+ * known to be declared within the current exception level.
+ **********************************************************************/
+static void mark_stmt(PLpgSQL_stmt *stmt, Bitmapset *local_dnos);
+static void mark_expr(PLpgSQL_expr *expr, Bitmapset *local_dnos);
+
+static void
+mark_stmt(PLpgSQL_stmt *stmt, Bitmapset *local_dnos)
+{
+ if (stmt == NULL)
+ return;
+ if (stmt->cmd_type == PLPGSQL_STMT_BLOCK)
+ {
+ PLpgSQL_stmt_block *block = (PLpgSQL_stmt_block *) stmt;
+
+ if (block->exceptions)
+ {
+ /*
+ * The block creates a new exception scope, so variables declared
+ * at outer levels are nonlocal. For that matter, so are any
+ * variables declared in the block's DECLARE section. Hence, we
+ * must pass down empty local_dnos.
+ */
+ plpgsql_statement_tree_walker(stmt, mark_stmt, mark_expr, NULL);
+ }
+ else
+ {
+ /*
+ * Otherwise, the block does not create a new exception scope, and
+ * any variables it declares can also be considered local within
+ * it. Note that only initializable datum types (VAR, REC) are
+ * included in initvarnos; but that's sufficient for our purposes.
+ */
+ local_dnos = bms_copy(local_dnos);
+ for (int i = 0; i < block->n_initvars; i++)
+ local_dnos = bms_add_member(local_dnos, block->initvarnos[i]);
+ plpgsql_statement_tree_walker(stmt, mark_stmt, mark_expr,
+ local_dnos);
+ bms_free(local_dnos);
+ }
+ }
+ else
+ plpgsql_statement_tree_walker(stmt, mark_stmt, mark_expr, local_dnos);
+}
+
+static void
+mark_expr(PLpgSQL_expr *expr, Bitmapset *local_dnos)
+{
+ /*
+ * If this expression has an assignment target, check whether the target
+ * is local, and mark the expression accordingly.
+ */
+ if (expr && expr->target_param >= 0)
+ expr->target_is_local = bms_is_member(expr->target_param, local_dnos);
+}
+
+void
+plpgsql_mark_local_assignment_targets(PLpgSQL_function *func)
+{
+ Bitmapset *local_dnos;
+
+ /* Function parameters can be treated as local targets at outer level */
+ local_dnos = NULL;
+ for (int i = 0; i < func->fn_nargs; i++)
+ local_dnos = bms_add_member(local_dnos, func->fn_argvarnos[i]);
+ mark_stmt((PLpgSQL_stmt *) func->action, local_dnos);
+ bms_free(local_dnos);
+}
+
+
/**********************************************************************
* Release memory when a PL/pgSQL function is no longer needed
*
dump_expr(PLpgSQL_expr *expr)
{
printf("'%s'", expr->query);
+ if (expr->target_param >= 0)
+ printf(" target %d%s", expr->target_param,
+ expr->target_is_local ? " (local)" : "");
}
void
PLpgSQL_exception_block *new = palloc(sizeof(PLpgSQL_exception_block));
PLpgSQL_variable *var;
+ plpgsql_curr_compile->has_exception_block = true;
+
var = plpgsql_build_variable("sqlstate", lineno,
plpgsql_build_datatype(TEXTOID,
-1,
expr->ns = plpgsql_ns_top();
/* might get changed later during parsing: */
expr->target_param = -1;
+ expr->target_is_local = false;
/* other fields are left as zeroes until first execution */
return expr;
}
* other DTYPEs, so no need to mark in other cases.
*/
if (target->dtype == PLPGSQL_DTYPE_VAR)
+ {
expr->target_param = target->dno;
+
+ /*
+ * For now, assume the target is local to the nearest enclosing
+ * exception block. That's correct if the function contains no
+ * exception blocks; otherwise we'll update this later.
+ */
+ expr->target_is_local = true;
+ }
else
+ {
expr->target_param = -1; /* should be that already */
+ expr->target_is_local = false; /* ditto */
+ }
}
/* Convenience routine to read an expression with one possible terminator */
/*
* These fields are used to help optimize assignments to expanded-datum
* variables. If this expression is the source of an assignment to a
- * simple variable, target_param holds that variable's dno (else it's -1).
+ * simple variable, target_param holds that variable's dno (else it's -1),
+ * and target_is_local indicates whether the target is declared inside the
+ * closest exception block containing the assignment.
*/
int target_param; /* dno of assign target, or -1 if none */
+ bool target_is_local; /* is it within nearest exception block? */
/*
* Fields above are set during plpgsql parsing. Remaining fields are left
/* data derived while parsing body */
unsigned int nstatements; /* counter for assigning stmtids */
bool requires_procedure_resowner; /* contains CALL or DO? */
+ bool has_exception_block; /* contains BEGIN...EXCEPTION? */
/* these fields change when the function is used */
struct PLpgSQL_execstate *cur_estate;
*/
extern PGDLLEXPORT const char *plpgsql_stmt_typename(PLpgSQL_stmt *stmt);
extern const char *plpgsql_getdiag_kindname(PLpgSQL_getdiag_kind kind);
+extern void plpgsql_mark_local_assignment_targets(PLpgSQL_function *func);
extern void plpgsql_free_function_memory(PLpgSQL_function *func);
extern void plpgsql_dumptree(PLpgSQL_function *func);