čt 22. 7. 2021 v 14:54 odesílatel Aleksander Alekseev <
aleksan...@timescale.com> napsal:

> Hi Pavel,
>
> >> I am sending an enhanced patch about the regress test for plpgsql's
> debug API.
>
> Thanks for the test! I noticed some little issues with formatting and
> typos. The corrected patch is attached.
>
> > override CPPFLAGS := $(CPPFLAGS) -I$(top_srcdir)/src/pl/plpgsql/src
>
> You probably already noticed, but for the record - AppVeyor doesn't seem
> to be happy still [1]:
>
> ```
> src/test/modules/test_dbgapi/test_dbgapi.c(17): fatal error C1083: Cannot
> open include file: 'plpgsql.h': No such file or directory
> [C:\projects\postgresql\test_dbgapi.vcxproj]
>
```
>
> [1]:
> https://ci.appveyor.com/project/postgresql-cfbot/postgresql/build/1.0.141500
>

I know it. Attached patch try to fix this issue

I merged you patch (thank you)

Regards

Pavel



>
> --
> Best regards,
> Aleksander Alekseev
>
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 14bbe12da5..e0e68a2592 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -4059,6 +4059,8 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
 	{
 		(*plpgsql_plugin_ptr)->error_callback = plpgsql_exec_error_callback;
 		(*plpgsql_plugin_ptr)->assign_expr = exec_assign_expr;
+		(*plpgsql_plugin_ptr)->eval_datum = exec_eval_datum;
+		(*plpgsql_plugin_ptr)->cast_value = do_cast_value;
 
 		if ((*plpgsql_plugin_ptr)->func_setup)
 			((*plpgsql_plugin_ptr)->func_setup) (estate, func);
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index 0f6a5b30b1..abe7d63f78 100644
--- a/src/pl/plpgsql/src/pl_gram.y
+++ b/src/pl/plpgsql/src/pl_gram.y
@@ -415,6 +415,7 @@ pl_block		: decl_sect K_BEGIN proc_sect exception_sect K_END opt_label
 						new->cmd_type	= PLPGSQL_STMT_BLOCK;
 						new->lineno		= plpgsql_location_to_lineno(@2);
 						new->stmtid		= ++plpgsql_curr_compile->nstatements;
+						new->ns			= plpgsql_ns_top();
 						new->label		= $1.label;
 						new->n_initvars = $1.n_initvars;
 						new->initvarnos = $1.initvarnos;
@@ -907,7 +908,8 @@ stmt_perform	: K_PERFORM
 						new = palloc0(sizeof(PLpgSQL_stmt_perform));
 						new->cmd_type = PLPGSQL_STMT_PERFORM;
 						new->lineno   = plpgsql_location_to_lineno(@1);
-						new->stmtid = ++plpgsql_curr_compile->nstatements;
+						new->stmtid   = ++plpgsql_curr_compile->nstatements;
+						new->ns		  = plpgsql_ns_top();
 						plpgsql_push_back_token(K_PERFORM);
 
 						/*
@@ -943,6 +945,7 @@ stmt_call		: K_CALL
 						new->cmd_type = PLPGSQL_STMT_CALL;
 						new->lineno = plpgsql_location_to_lineno(@1);
 						new->stmtid = ++plpgsql_curr_compile->nstatements;
+						new->ns = plpgsql_ns_top();
 						plpgsql_push_back_token(K_CALL);
 						new->expr = read_sql_stmt();
 						new->is_call = true;
@@ -962,6 +965,7 @@ stmt_call		: K_CALL
 						new->cmd_type = PLPGSQL_STMT_CALL;
 						new->lineno = plpgsql_location_to_lineno(@1);
 						new->stmtid = ++plpgsql_curr_compile->nstatements;
+						new->ns = plpgsql_ns_top();
 						plpgsql_push_back_token(K_DO);
 						new->expr = read_sql_stmt();
 						new->is_call = false;
@@ -1000,7 +1004,8 @@ stmt_assign		: T_DATUM
 						new = palloc0(sizeof(PLpgSQL_stmt_assign));
 						new->cmd_type = PLPGSQL_STMT_ASSIGN;
 						new->lineno   = plpgsql_location_to_lineno(@1);
-						new->stmtid = ++plpgsql_curr_compile->nstatements;
+						new->stmtid   = ++plpgsql_curr_compile->nstatements;
+						new->ns		  = plpgsql_ns_top();
 						new->varno = $1.datum->dno;
 						/* Push back the head name to include it in the stmt */
 						plpgsql_push_back_token(T_DATUM);
@@ -1022,6 +1027,7 @@ stmt_getdiag	: K_GET getdiag_area_opt K_DIAGNOSTICS getdiag_list ';'
 						new->cmd_type = PLPGSQL_STMT_GETDIAG;
 						new->lineno   = plpgsql_location_to_lineno(@1);
 						new->stmtid	  = ++plpgsql_curr_compile->nstatements;
+						new->ns		  = plpgsql_ns_top();
 						new->is_stacked = $2;
 						new->diag_items = $4;
 
@@ -1194,6 +1200,7 @@ stmt_if			: K_IF expr_until_then proc_sect stmt_elsifs stmt_else K_END K_IF ';'
 						new->cmd_type	= PLPGSQL_STMT_IF;
 						new->lineno		= plpgsql_location_to_lineno(@1);
 						new->stmtid		= ++plpgsql_curr_compile->nstatements;
+						new->ns			= plpgsql_ns_top();
 						new->cond		= $2;
 						new->then_body	= $3;
 						new->elsif_list = $4;
@@ -1299,6 +1306,7 @@ stmt_loop		: opt_loop_label K_LOOP loop_body
 						new->cmd_type = PLPGSQL_STMT_LOOP;
 						new->lineno   = plpgsql_location_to_lineno(@2);
 						new->stmtid   = ++plpgsql_curr_compile->nstatements;
+						new->ns		  = plpgsql_ns_top();
 						new->label	  = $1;
 						new->body	  = $3.stmts;
 
@@ -1317,6 +1325,7 @@ stmt_while		: opt_loop_label K_WHILE expr_until_loop loop_body
 						new->cmd_type = PLPGSQL_STMT_WHILE;
 						new->lineno   = plpgsql_location_to_lineno(@2);
 						new->stmtid	  = ++plpgsql_curr_compile->nstatements;
+						new->ns		  = plpgsql_ns_top();
 						new->label	  = $1;
 						new->cond	  = $3;
 						new->body	  = $4.stmts;
@@ -1381,6 +1390,7 @@ for_control		: for_variable K_IN
 							new = palloc0(sizeof(PLpgSQL_stmt_dynfors));
 							new->cmd_type = PLPGSQL_STMT_DYNFORS;
 							new->stmtid	  = ++plpgsql_curr_compile->nstatements;
+							new->ns		  = plpgsql_ns_top();
 							if ($1.row)
 							{
 								new->var = (PLpgSQL_variable *) $1.row;
@@ -1427,6 +1437,7 @@ for_control		: for_variable K_IN
 							new = (PLpgSQL_stmt_forc *) palloc0(sizeof(PLpgSQL_stmt_forc));
 							new->cmd_type = PLPGSQL_STMT_FORC;
 							new->stmtid = ++plpgsql_curr_compile->nstatements;
+							new->ns = plpgsql_ns_top();
 							new->curvar = cursor->dno;
 
 							/* Should have had a single variable name */
@@ -1547,6 +1558,7 @@ for_control		: for_variable K_IN
 								new = palloc0(sizeof(PLpgSQL_stmt_fori));
 								new->cmd_type = PLPGSQL_STMT_FORI;
 								new->stmtid	  = ++plpgsql_curr_compile->nstatements;
+								new->ns		  = plpgsql_ns_top();
 								new->var	  = fvar;
 								new->reverse  = reverse;
 								new->lower	  = expr1;
@@ -1575,6 +1587,7 @@ for_control		: for_variable K_IN
 								new = palloc0(sizeof(PLpgSQL_stmt_fors));
 								new->cmd_type = PLPGSQL_STMT_FORS;
 								new->stmtid = ++plpgsql_curr_compile->nstatements;
+								new->ns = plpgsql_ns_top();
 								if ($1.row)
 								{
 									new->var = (PLpgSQL_variable *) $1.row;
@@ -1676,6 +1689,7 @@ stmt_foreach_a	: opt_loop_label K_FOREACH for_variable foreach_slice K_IN K_ARRA
 						new->cmd_type = PLPGSQL_STMT_FOREACH_A;
 						new->lineno = plpgsql_location_to_lineno(@2);
 						new->stmtid = ++plpgsql_curr_compile->nstatements;
+						new->ns = plpgsql_ns_top();
 						new->label = $1;
 						new->slice = $4;
 						new->expr = $7;
@@ -1723,6 +1737,7 @@ stmt_exit		: exit_type opt_label opt_exitcond
 						new = palloc0(sizeof(PLpgSQL_stmt_exit));
 						new->cmd_type = PLPGSQL_STMT_EXIT;
 						new->stmtid	  = ++plpgsql_curr_compile->nstatements;
+						new->ns		  = plpgsql_ns_top();
 						new->is_exit  = $1;
 						new->lineno	  = plpgsql_location_to_lineno(@1);
 						new->label	  = $2;
@@ -1815,6 +1830,7 @@ stmt_raise		: K_RAISE
 						new->cmd_type	= PLPGSQL_STMT_RAISE;
 						new->lineno		= plpgsql_location_to_lineno(@1);
 						new->stmtid		= ++plpgsql_curr_compile->nstatements;
+						new->ns			= plpgsql_ns_top();
 						new->elog_level = ERROR;	/* default */
 						new->condname	= NULL;
 						new->message	= NULL;
@@ -1960,6 +1976,7 @@ stmt_assert		: K_ASSERT
 						new->cmd_type	= PLPGSQL_STMT_ASSERT;
 						new->lineno		= plpgsql_location_to_lineno(@1);
 						new->stmtid		= ++plpgsql_curr_compile->nstatements;
+						new->ns			= plpgsql_ns_top();
 
 						new->cond = read_sql_expression2(',', ';',
 														 ", or ;",
@@ -2040,6 +2057,7 @@ stmt_dynexecute : K_EXECUTE
 						new->cmd_type = PLPGSQL_STMT_DYNEXECUTE;
 						new->lineno = plpgsql_location_to_lineno(@1);
 						new->stmtid = ++plpgsql_curr_compile->nstatements;
+						new->ns = plpgsql_ns_top();
 						new->query = expr;
 						new->into = false;
 						new->strict = false;
@@ -2097,6 +2115,7 @@ stmt_open		: K_OPEN cursor_variable
 						new->cmd_type = PLPGSQL_STMT_OPEN;
 						new->lineno = plpgsql_location_to_lineno(@1);
 						new->stmtid = ++plpgsql_curr_compile->nstatements;
+						new->ns		= plpgsql_ns_top();
 						new->curvar = $2->dno;
 						new->cursor_options = CURSOR_OPT_FAST_PLAN;
 
@@ -2222,6 +2241,7 @@ stmt_close		: K_CLOSE cursor_variable ';'
 						new->cmd_type = PLPGSQL_STMT_CLOSE;
 						new->lineno = plpgsql_location_to_lineno(@1);
 						new->stmtid = ++plpgsql_curr_compile->nstatements;
+						new->ns = plpgsql_ns_top();
 						new->curvar = $2->dno;
 
 						$$ = (PLpgSQL_stmt *)new;
@@ -2243,6 +2263,7 @@ stmt_commit		: K_COMMIT opt_transaction_chain ';'
 						new->cmd_type = PLPGSQL_STMT_COMMIT;
 						new->lineno = plpgsql_location_to_lineno(@1);
 						new->stmtid = ++plpgsql_curr_compile->nstatements;
+						new->ns = plpgsql_ns_top();
 						new->chain = $2;
 
 						$$ = (PLpgSQL_stmt *)new;
@@ -2257,6 +2278,7 @@ stmt_rollback	: K_ROLLBACK opt_transaction_chain ';'
 						new->cmd_type = PLPGSQL_STMT_ROLLBACK;
 						new->lineno = plpgsql_location_to_lineno(@1);
 						new->stmtid = ++plpgsql_curr_compile->nstatements;
+						new->ns = plpgsql_ns_top();
 						new->chain = $2;
 
 						$$ = (PLpgSQL_stmt *)new;
@@ -2269,7 +2291,6 @@ opt_transaction_chain:
 			| /* EMPTY */			{ $$ = false; }
 				;
 
-
 cursor_variable	: T_DATUM
 					{
 						/*
@@ -3047,6 +3068,7 @@ make_execsql_stmt(int firsttoken, int location)
 	execsql->cmd_type = PLPGSQL_STMT_EXECSQL;
 	execsql->lineno  = plpgsql_location_to_lineno(location);
 	execsql->stmtid  = ++plpgsql_curr_compile->nstatements;
+	execsql->ns		 = plpgsql_ns_top();
 	execsql->sqlstmt = expr;
 	execsql->into	 = have_into;
 	execsql->strict	 = have_strict;
@@ -3073,6 +3095,7 @@ read_fetch_direction(void)
 	fetch = (PLpgSQL_stmt_fetch *) palloc0(sizeof(PLpgSQL_stmt_fetch));
 	fetch->cmd_type = PLPGSQL_STMT_FETCH;
 	fetch->stmtid	= ++plpgsql_curr_compile->nstatements;
+	fetch->ns		= plpgsql_ns_top();
 	/* set direction defaults: */
 	fetch->direction = FETCH_FORWARD;
 	fetch->how_many  = 1;
@@ -3226,6 +3249,7 @@ make_return_stmt(int location)
 	new->cmd_type = PLPGSQL_STMT_RETURN;
 	new->lineno   = plpgsql_location_to_lineno(location);
 	new->stmtid	  = ++plpgsql_curr_compile->nstatements;
+	new->ns		  = plpgsql_ns_top();
 	new->expr	  = NULL;
 	new->retvarno = -1;
 
@@ -3314,6 +3338,7 @@ make_return_next_stmt(int location)
 	new->cmd_type	= PLPGSQL_STMT_RETURN_NEXT;
 	new->lineno		= plpgsql_location_to_lineno(location);
 	new->stmtid		= ++plpgsql_curr_compile->nstatements;
+	new->ns			= plpgsql_ns_top();
 	new->expr		= NULL;
 	new->retvarno	= -1;
 
@@ -3378,6 +3403,7 @@ make_return_query_stmt(int location)
 	new->cmd_type = PLPGSQL_STMT_RETURN_QUERY;
 	new->lineno = plpgsql_location_to_lineno(location);
 	new->stmtid = ++plpgsql_curr_compile->nstatements;
+	new->ns = plpgsql_ns_top();
 
 	/* check for RETURN QUERY EXECUTE */
 	if ((tok = yylex()) != K_EXECUTE)
@@ -4042,6 +4068,7 @@ make_case(int location, PLpgSQL_expr *t_expr,
 	new->cmd_type = PLPGSQL_STMT_CASE;
 	new->lineno = plpgsql_location_to_lineno(location);
 	new->stmtid = ++plpgsql_curr_compile->nstatements;
+	new->ns = plpgsql_ns_top();
 	new->t_expr = t_expr;
 	new->t_varno = 0;
 	new->case_when_list = case_when_list;
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index ebd3a5d3c8..d1db206a58 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -460,6 +460,9 @@ typedef struct PLpgSQL_stmt
 	 * per-statement metrics.
 	 */
 	unsigned int stmtid;
+
+	/* namespace chain visible to this statement */
+	PLpgSQL_nsitem *ns;
 } PLpgSQL_stmt;
 
 /*
@@ -500,6 +503,7 @@ typedef struct PLpgSQL_stmt_block
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	char	   *label;
 	List	   *body;			/* List of statements */
 	int			n_initvars;		/* Length of initvarnos[] */
@@ -515,6 +519,7 @@ typedef struct PLpgSQL_stmt_assign
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	int			varno;
 	PLpgSQL_expr *expr;
 } PLpgSQL_stmt_assign;
@@ -527,6 +532,7 @@ typedef struct PLpgSQL_stmt_perform
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	PLpgSQL_expr *expr;
 } PLpgSQL_stmt_perform;
 
@@ -538,6 +544,7 @@ typedef struct PLpgSQL_stmt_call
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	PLpgSQL_expr *expr;
 	bool		is_call;
 	PLpgSQL_variable *target;
@@ -551,6 +558,7 @@ typedef struct PLpgSQL_stmt_commit
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	bool		chain;
 } PLpgSQL_stmt_commit;
 
@@ -562,6 +570,7 @@ typedef struct PLpgSQL_stmt_rollback
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	bool		chain;
 } PLpgSQL_stmt_rollback;
 
@@ -582,6 +591,7 @@ typedef struct PLpgSQL_stmt_getdiag
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	bool		is_stacked;		/* STACKED or CURRENT diagnostics area? */
 	List	   *diag_items;		/* List of PLpgSQL_diag_item */
 } PLpgSQL_stmt_getdiag;
@@ -594,6 +604,7 @@ typedef struct PLpgSQL_stmt_if
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	PLpgSQL_expr *cond;			/* boolean expression for THEN */
 	List	   *then_body;		/* List of statements */
 	List	   *elsif_list;		/* List of PLpgSQL_if_elsif structs */
@@ -618,6 +629,7 @@ typedef struct PLpgSQL_stmt_case
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	PLpgSQL_expr *t_expr;		/* test expression, or NULL if none */
 	int			t_varno;		/* var to store test expression value into */
 	List	   *case_when_list; /* List of PLpgSQL_case_when structs */
@@ -643,6 +655,7 @@ typedef struct PLpgSQL_stmt_loop
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	char	   *label;
 	List	   *body;			/* List of statements */
 } PLpgSQL_stmt_loop;
@@ -655,6 +668,7 @@ typedef struct PLpgSQL_stmt_while
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	char	   *label;
 	PLpgSQL_expr *cond;
 	List	   *body;			/* List of statements */
@@ -668,6 +682,7 @@ typedef struct PLpgSQL_stmt_fori
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	char	   *label;
 	PLpgSQL_var *var;
 	PLpgSQL_expr *lower;
@@ -687,6 +702,7 @@ typedef struct PLpgSQL_stmt_forq
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	char	   *label;
 	PLpgSQL_variable *var;		/* Loop variable (record or row) */
 	List	   *body;			/* List of statements */
@@ -700,6 +716,7 @@ typedef struct PLpgSQL_stmt_fors
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	char	   *label;
 	PLpgSQL_variable *var;		/* Loop variable (record or row) */
 	List	   *body;			/* List of statements */
@@ -715,6 +732,7 @@ typedef struct PLpgSQL_stmt_forc
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	char	   *label;
 	PLpgSQL_variable *var;		/* Loop variable (record or row) */
 	List	   *body;			/* List of statements */
@@ -731,6 +749,7 @@ typedef struct PLpgSQL_stmt_dynfors
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	char	   *label;
 	PLpgSQL_variable *var;		/* Loop variable (record or row) */
 	List	   *body;			/* List of statements */
@@ -747,6 +766,7 @@ typedef struct PLpgSQL_stmt_foreach_a
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	char	   *label;
 	int			varno;			/* loop target variable */
 	int			slice;			/* slice dimension, or 0 */
@@ -762,6 +782,7 @@ typedef struct PLpgSQL_stmt_open
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	int			curvar;
 	int			cursor_options;
 	PLpgSQL_expr *argquery;
@@ -778,6 +799,7 @@ typedef struct PLpgSQL_stmt_fetch
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	PLpgSQL_variable *target;	/* target (record or row) */
 	int			curvar;			/* cursor variable to fetch from */
 	FetchDirection direction;	/* fetch direction */
@@ -795,6 +817,7 @@ typedef struct PLpgSQL_stmt_close
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	int			curvar;
 } PLpgSQL_stmt_close;
 
@@ -806,6 +829,7 @@ typedef struct PLpgSQL_stmt_exit
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	bool		is_exit;		/* Is this an exit or a continue? */
 	char	   *label;			/* NULL if it's an unlabeled EXIT/CONTINUE */
 	PLpgSQL_expr *cond;
@@ -819,6 +843,7 @@ typedef struct PLpgSQL_stmt_return
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	PLpgSQL_expr *expr;
 	int			retvarno;
 } PLpgSQL_stmt_return;
@@ -831,6 +856,7 @@ typedef struct PLpgSQL_stmt_return_next
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	PLpgSQL_expr *expr;
 	int			retvarno;
 } PLpgSQL_stmt_return_next;
@@ -843,6 +869,7 @@ typedef struct PLpgSQL_stmt_return_query
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	PLpgSQL_expr *query;		/* if static query */
 	PLpgSQL_expr *dynquery;		/* if dynamic query (RETURN QUERY EXECUTE) */
 	List	   *params;			/* USING arguments for dynamic query */
@@ -856,6 +883,7 @@ typedef struct PLpgSQL_stmt_raise
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	int			elog_level;
 	char	   *condname;		/* condition name, SQLSTATE, or NULL */
 	char	   *message;		/* old-style message format literal, or NULL */
@@ -880,6 +908,7 @@ typedef struct PLpgSQL_stmt_assert
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	PLpgSQL_expr *cond;
 	PLpgSQL_expr *message;
 } PLpgSQL_stmt_assert;
@@ -892,6 +921,7 @@ typedef struct PLpgSQL_stmt_execsql
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	PLpgSQL_expr *sqlstmt;
 	bool		mod_stmt;		/* is the stmt INSERT/UPDATE/DELETE? */
 	bool		mod_stmt_set;	/* is mod_stmt valid yet? */
@@ -908,6 +938,7 @@ typedef struct PLpgSQL_stmt_dynexecute
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	PLpgSQL_expr *query;		/* string expression */
 	bool		into;			/* INTO supplied? */
 	bool		strict;			/* INTO STRICT flag */
@@ -1120,8 +1151,11 @@ typedef struct PLpgSQL_execstate
  *
  * Also, immediately before any call to func_setup, PL/pgSQL fills in the
  * error_callback and assign_expr fields with pointers to its own
- * plpgsql_exec_error_callback and exec_assign_expr functions.  This is
- * a somewhat ad-hoc expedient to simplify life for debugger plugins.
+ * plpgsql_exec_error_callback and exec_assign_expr functions. eval_datum
+ * is assigned to function exec_eval_datum, and cast_value to function
+ * do_cast_value. With last two functions is easy to get content of
+ * any PLpgSQL_datum in any expected type. This is a somewhat ad-hoc
+ * expedient to simplify life for debugger plugins.
  */
 typedef struct PLpgSQL_plugin
 {
@@ -1136,6 +1170,13 @@ typedef struct PLpgSQL_plugin
 	void		(*error_callback) (void *arg);
 	void		(*assign_expr) (PLpgSQL_execstate *estate, PLpgSQL_datum *target,
 								PLpgSQL_expr *expr);
+	void		(*eval_datum) (PLpgSQL_execstate *estate, PLpgSQL_datum *datum,
+								Oid *typeid, int32 *typetypmod, Datum *value,
+								bool *isnull);
+	Datum		(*cast_value) (PLpgSQL_execstate *estate,
+								Datum value, bool *isnull,
+								Oid valtype, int32 valtypmod,
+								Oid reqtype, int32 reqtypmod);
 } PLpgSQL_plugin;
 
 /*
diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile
index dffc79b2d9..fa4f643599 100644
--- a/src/test/modules/Makefile
+++ b/src/test/modules/Makefile
@@ -15,6 +15,7 @@ SUBDIRS = \
 		  snapshot_too_old \
 		  spgist_name_ops \
 		  test_bloomfilter \
+		  test_dbgapi \
 		  test_ddl_deparse \
 		  test_extensions \
 		  test_ginpostinglist \
diff --git a/src/test/modules/test_dbgapi/.gitignore b/src/test/modules/test_dbgapi/.gitignore
new file mode 100644
index 0000000000..44d119cfcc
--- /dev/null
+++ b/src/test/modules/test_dbgapi/.gitignore
@@ -0,0 +1,3 @@
+# Generated subdirectories
+/log/
+/results/
diff --git a/src/test/modules/test_dbgapi/Makefile b/src/test/modules/test_dbgapi/Makefile
new file mode 100644
index 0000000000..df5e9e9bbd
--- /dev/null
+++ b/src/test/modules/test_dbgapi/Makefile
@@ -0,0 +1,22 @@
+# src/test/modules/test_dbgapi/Makefile
+
+MODULES = test_dbgapi
+
+EXTENSION = test_dbgapi
+DATA = test_dbgapi--1.0.sql
+PGFILEDESC = "test_dbgapi - test of PL/pgSQL debug API"
+
+REGRESS = test_dbgapi
+
+PG_CPPFLAGS = -I$(top_srcdir)/src/pl/plpgsql/src
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/test_dbgapi
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/src/test/modules/test_dbgapi/README b/src/test/modules/test_dbgapi/README
new file mode 100644
index 0000000000..3d58ae7dd8
--- /dev/null
+++ b/src/test/modules/test_dbgapi/README
@@ -0,0 +1,7 @@
+test_dbgapi
+===========
+
+test_dbgapi is an example of usage of PLpgSQL debug API. This extension
+implements some very basic (and simple) code execution tracing. The
+implemented functionality is very limitted, because main purpose is
+allow to run regress test of debug API.
diff --git a/src/test/modules/test_dbgapi/expected/test_dbgapi.out b/src/test/modules/test_dbgapi/expected/test_dbgapi.out
new file mode 100644
index 0000000000..1011d6231e
--- /dev/null
+++ b/src/test/modules/test_dbgapi/expected/test_dbgapi.out
@@ -0,0 +1,90 @@
+CREATE EXTENSION test_dbgapi;
+/*
+ * There is lot of shadowed variables "v". The test should to
+ * choose the variable "v" from namespace that is assigned to
+ * current statement, and show the value before and after an
+ * execution of any statement.
+ */
+CREATE OR REPLACE FUNCTION trace_variable_test_func(v int)
+RETURNS void AS $$
+BEGIN
+  v := v + 1;
+
+<<b1>>
+  DECLARE
+    v int DEFAULT trace_variable_test_func.v + 100;
+  BEGIN
+    v := v + 1;
+    FOR v IN v + 10 .. v + 13
+    LOOP
+      RAISE NOTICE 'v = %', v;
+    END LOOP;
+  END;
+END;
+$$ LANGUAGE plpgsql;
+/*
+ * Prepare simple tracer
+ */
+SELECT trace_plpgsql(true);
+ trace_plpgsql 
+---------------
+ 
+(1 row)
+
+SET test_dbgapi.trace_variable = 'v';
+SET test_dbgapi.trace_forloop_variable = ON;
+SELECT trace_variable_test_func(10);
+NOTICE:  start of function: "trace_variable_test_func(integer)"
+NOTICE:  start of statement: "statement block", no: 6 on line 2
+NOTICE:  value of variable "v" is "10" (before)
+NOTICE:  start of statement: "assignment", no: 1 on line 3
+NOTICE:  value of variable "v" is "10" (before)
+NOTICE:  end of statement: "assignment", no: 1 on line 3
+NOTICE:  value of variable "v" is "11" (after)
+NOTICE:  start of statement: "statement block", no: 5 on line 8
+NOTICE:  value of variable "v" is "11" (before)
+NOTICE:  start of statement: "assignment", no: 2 on line 9
+NOTICE:  value of variable "v" is "111" (before)
+NOTICE:  end of statement: "assignment", no: 2 on line 9
+NOTICE:  value of variable "v" is "112" (after)
+NOTICE:  start of statement: "FOR with integer loop variable", no: 3 on line 10
+NOTICE:  value of variable "v" is "112" (before)
+NOTICE:  start of statement: "RAISE", no: 4 on line 12
+NOTICE:  most inner fori loop control variable "v" is 122
+NOTICE:  value of variable "v" is "122" (before)
+NOTICE:  v = 122
+NOTICE:  end of statement: "RAISE", no: 4 on line 12
+NOTICE:  value of variable "v" is "122" (after)
+NOTICE:  start of statement: "RAISE", no: 4 on line 12
+NOTICE:  most inner fori loop control variable "v" is 123
+NOTICE:  value of variable "v" is "123" (before)
+NOTICE:  v = 123
+NOTICE:  end of statement: "RAISE", no: 4 on line 12
+NOTICE:  value of variable "v" is "123" (after)
+NOTICE:  start of statement: "RAISE", no: 4 on line 12
+NOTICE:  most inner fori loop control variable "v" is 124
+NOTICE:  value of variable "v" is "124" (before)
+NOTICE:  v = 124
+NOTICE:  end of statement: "RAISE", no: 4 on line 12
+NOTICE:  value of variable "v" is "124" (after)
+NOTICE:  start of statement: "RAISE", no: 4 on line 12
+NOTICE:  most inner fori loop control variable "v" is 125
+NOTICE:  value of variable "v" is "125" (before)
+NOTICE:  v = 125
+NOTICE:  end of statement: "RAISE", no: 4 on line 12
+NOTICE:  value of variable "v" is "125" (after)
+NOTICE:  end of statement: "FOR with integer loop variable", no: 3 on line 10
+NOTICE:  value of variable "v" is "125" (after)
+NOTICE:  end of statement: "statement block", no: 5 on line 8
+NOTICE:  value of variable "v" is "112" (after)
+NOTICE:  start of statement: "RETURN", no: 7 on line 0
+NOTICE:  now, there is not any outer fori cycle
+NOTICE:  end of statement: "RETURN", no: 7 on line 0
+NOTICE:  end of statement: "statement block", no: 6 on line 2
+NOTICE:  value of variable "v" is "11" (after)
+NOTICE:  end of function: "trace_variable_test_func(integer)"
+ trace_variable_test_func 
+--------------------------
+ 
+(1 row)
+
diff --git a/src/test/modules/test_dbgapi/sql/test_dbgapi.sql b/src/test/modules/test_dbgapi/sql/test_dbgapi.sql
new file mode 100644
index 0000000000..bcede18a7c
--- /dev/null
+++ b/src/test/modules/test_dbgapi/sql/test_dbgapi.sql
@@ -0,0 +1,36 @@
+CREATE EXTENSION test_dbgapi;
+
+/*
+ * There is lot of shadowed variables "v". The test should to
+ * choose the variable "v" from namespace that is assigned to
+ * current statement, and show the value before and after an
+ * execution of any statement.
+ */
+CREATE OR REPLACE FUNCTION trace_variable_test_func(v int)
+RETURNS void AS $$
+BEGIN
+  v := v + 1;
+
+<<b1>>
+  DECLARE
+    v int DEFAULT trace_variable_test_func.v + 100;
+  BEGIN
+    v := v + 1;
+    FOR v IN v + 10 .. v + 13
+    LOOP
+      RAISE NOTICE 'v = %', v;
+    END LOOP;
+  END;
+END;
+$$ LANGUAGE plpgsql;
+
+/*
+ * Prepare simple tracer
+ */
+SELECT trace_plpgsql(true);
+
+SET test_dbgapi.trace_variable = 'v';
+SET test_dbgapi.trace_forloop_variable = ON;
+
+SELECT trace_variable_test_func(10);
+
diff --git a/src/test/modules/test_dbgapi/test_dbgapi--1.0.sql b/src/test/modules/test_dbgapi/test_dbgapi--1.0.sql
new file mode 100644
index 0000000000..87834e7ea5
--- /dev/null
+++ b/src/test/modules/test_dbgapi/test_dbgapi--1.0.sql
@@ -0,0 +1,7 @@
+/* src/test/modules/test_dbgapi/test_dbgapi--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION test_dbgapi" to load this file. \quit
+
+CREATE FUNCTION trace_plpgsql(boolean) RETURNS void
+  AS 'MODULE_PATHNAME' LANGUAGE C;
diff --git a/src/test/modules/test_dbgapi/test_dbgapi.c b/src/test/modules/test_dbgapi/test_dbgapi.c
new file mode 100644
index 0000000000..9bf6e5180c
--- /dev/null
+++ b/src/test/modules/test_dbgapi/test_dbgapi.c
@@ -0,0 +1,476 @@
+/*-------------------------------------------------------------------------
+ *
+ * test_dbgapi.c
+ *	  Simple PLpgSQL tracer designed to test PLpgSQL's debug API
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *		src/test/modules/test_dbgapi/test_dbgapi.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include "plpgsql.h"
+#include "utils/guc.h"
+#include "utils/builtins.h"
+
+PG_MODULE_MAGIC;
+
+/* Module load/unload functions */
+void		_PG_init(void);
+void		_PG_fini(void);
+
+static void func_beg(PLpgSQL_execstate *estate, PLpgSQL_function *func);
+static void func_end(PLpgSQL_execstate *estate, PLpgSQL_function *func);
+static void stmt_beg(PLpgSQL_execstate *estate, PLpgSQL_stmt *stmt);
+static void stmt_end(PLpgSQL_execstate *estate, PLpgSQL_stmt *stmt);
+
+static bool is_nested_of(PLpgSQL_stmt *stmt, PLpgSQL_stmt *outer_stmt);
+static PLpgSQL_nsitem *outer_ns(PLpgSQL_stmt *stmt);
+static char *get_string_value(PLpgSQL_execstate *estate, PLpgSQL_datum *datum);
+static char *get_string_expr(PLpgSQL_execstate *estate, PLpgSQL_nsitem *ns, char *query);
+
+static PLpgSQL_plugin plugin_funcs = {NULL,
+	func_beg,
+	func_end,
+	stmt_beg,
+	stmt_end,
+	NULL,
+NULL};
+
+typedef const char *(*test_dbgapi_stmt_typename_t) (PLpgSQL_stmt *stmt);
+typedef PLpgSQL_nsitem *(*test_dbgapi_ns_lookup_t) (PLpgSQL_nsitem *ns_cur,
+													bool localmode, const char *name1, const char *name2,
+													const char *name3, int *names_used);
+
+static test_dbgapi_stmt_typename_t stmt_typename_p;
+static test_dbgapi_ns_lookup_t ns_lookup_p;
+
+static PLpgSQL_plugin **test_dbgapi_plugin_var = NULL;
+
+static bool trace_forloop_variable = false;
+static char *trace_variable = NULL;
+
+#define LOAD_EXTERNAL_FUNCTION(file, funcname) \
+	((void *) (load_external_function(file, funcname, true, NULL)))
+
+#define get_eval_mcontext(estate) \
+	((estate)->eval_econtext->ecxt_per_tuple_memory)
+
+PG_FUNCTION_INFO_V1(trace_plpgsql);
+
+typedef struct
+{
+	List	   *fori_stmt_stack;
+}			test_dbgapi_info;
+
+
+static void
+func_beg(PLpgSQL_execstate *estate, PLpgSQL_function *func)
+{
+	MemoryContext oldcxt;
+
+	if (func->fn_signature)
+		ereport(NOTICE,
+				(errmsg("start of function: \"%s\"", func->fn_signature)));
+	else
+		ereport(NOTICE,
+				(errmsg("start of anonymous block")));
+
+	oldcxt = MemoryContextSwitchTo(estate->datum_context);
+
+	estate->plugin_info = palloc0(sizeof(test_dbgapi_info));
+
+	MemoryContextSwitchTo(oldcxt);
+}
+
+static void
+func_end(PLpgSQL_execstate *estate, PLpgSQL_function *func)
+{
+	if (func->fn_signature)
+		ereport(NOTICE,
+				(errmsg("end of function: \"%s\"", func->fn_signature)));
+	else
+		ereport(NOTICE,
+				(errmsg("end of anonymous block")));
+}
+
+static void
+stmt_beg(PLpgSQL_execstate *estate, PLpgSQL_stmt *stmt)
+{
+	test_dbgapi_info *pinfo = estate->plugin_info;
+
+	ereport(NOTICE,
+			(errmsg("start of statement: \"%s\", no: %d on line %d",
+					(char *) stmt_typename_p(stmt),
+					stmt->stmtid, stmt->lineno)));
+
+	/*
+	 * We have to recheck accuracy of saved statement stack, because it can be
+	 * broken by any handled exception. In this case, the *end handlers are
+	 * not executed (so we cannot to maintain this stack by appending in
+	 * stmt_beg and trimming in stmt_end).
+	 *
+	 * This check can be simple - the namespace of current statement should be
+	 * subset of namespace of last fori statement from stack.
+	 */
+	if (pinfo && pinfo->fori_stmt_stack != NIL)
+	{
+		int			i;
+		bool		found = false;
+
+		/*
+		 * try to search top most accurate fori statement. In usual cases the
+		 * most top fori statement is accurate, and this cycle is quickly
+		 * leaved. This is NxM algorithm, but a) this is just test
+		 * application, b) in reality the N and M are low, so it is not
+		 * necessary to implement faster but more complex algorithm.
+		 */
+		for (i = list_length(pinfo->fori_stmt_stack); i > 0; i--)
+		{
+			if (is_nested_of(stmt, (PLpgSQL_stmt *)
+							 list_nth(pinfo->fori_stmt_stack, i - 1)))
+			{
+				if (i < list_length(pinfo->fori_stmt_stack))
+				{
+					/* We need to trim stack of fori statements */
+					pinfo->fori_stmt_stack = list_truncate(pinfo->fori_stmt_stack, i);
+
+					ereport(NOTICE,
+							(errmsg("the stack of fori statements is truncated to %d",
+									i)));
+				}
+
+				found = true;
+				break;
+			}
+		}
+
+		if (!found)
+		{
+			list_free(pinfo->fori_stmt_stack);
+			pinfo->fori_stmt_stack = NIL;
+
+			ereport(NOTICE, (errmsg("now, there is not any outer fori cycle")));
+		}
+	}
+
+	if (pinfo &&
+		pinfo->fori_stmt_stack &&
+		list_length(pinfo->fori_stmt_stack) > 0)
+	{
+		PLpgSQL_stmt_fori *top_fori_stmt;
+		PLpgSQL_var *var;
+
+		top_fori_stmt = (PLpgSQL_stmt_fori *) llast(pinfo->fori_stmt_stack);
+		var = top_fori_stmt->var;
+
+		ereport(NOTICE,
+				(errmsg("most inner fori loop control variable \"%s\" is %s",
+						var->refname,
+						get_string_value(estate, estate->datums[var->dno]))));
+	}
+
+	if (trace_forloop_variable &&
+		stmt->cmd_type == PLPGSQL_STMT_FORI)
+		pinfo->fori_stmt_stack = lappend(pinfo->fori_stmt_stack, stmt);
+
+	/*
+	 * Another case - print content of variable specified by name. The
+	 * variable can be shadowed - and we want to chose variable that is
+	 * visible from current namespace.
+	 *
+	 * Note - there are two ways how to do it. Because this code will be used
+	 * as regress test, then I show (use) both.
+	 */
+	if (trace_variable && *trace_variable != '\0')
+	{
+		PLpgSQL_nsitem *nsi;
+
+		nsi = ns_lookup_p(outer_ns(stmt), false, "v", NULL, NULL, NULL);
+		if (nsi &&
+			(nsi->itemtype == PLPGSQL_NSTYPE_VAR ||
+			 nsi->itemtype == PLPGSQL_NSTYPE_REC))
+		{
+			ereport(NOTICE,
+					(errmsg("value of variable \"%s\" is \"%s\" (before)",
+							nsi->name,
+							get_string_value(estate,
+											 estate->datums[nsi->itemno]))));
+		}
+	}
+}
+
+static void
+stmt_end(PLpgSQL_execstate *estate, PLpgSQL_stmt *stmt)
+{
+	ereport(NOTICE,
+			(errmsg("end of statement: \"%s\", no: %d on line %d",
+					(char *) stmt_typename_p(stmt),
+					stmt->stmtid, stmt->lineno)));
+
+	if (trace_variable && *trace_variable != '\0')
+	{
+		char	   *str = get_string_expr(estate, stmt->ns, trace_variable);
+
+		if (str)
+			ereport(NOTICE,
+					(errmsg("value of variable \"%s\" is \"%s\" (after)",
+							trace_variable, str)));
+	}
+}
+
+/*
+ * Module initialization
+ */
+void
+_PG_init(void)
+{
+	static bool inited = false;
+
+	if (inited)
+		return;
+
+	/*
+	 * Currently these asserts doesn't ensure any safety, but it can be
+	 * changed if referenced functions will be declared in header file.
+	 */
+	AssertVariableIsOfType(stmt_typename_p, test_dbgapi_stmt_typename_t);
+	stmt_typename_p = (test_dbgapi_stmt_typename_t)
+		LOAD_EXTERNAL_FUNCTION("$libdir/plpgsql", "plpgsql_stmt_typename");
+
+	AssertVariableIsOfType(ns_lookup_p, test_dbgapi_ns_lookup_t);
+	ns_lookup_p = (test_dbgapi_ns_lookup_t)
+		LOAD_EXTERNAL_FUNCTION("$libdir/plpgsql", "plpgsql_ns_lookup");
+
+	DefineCustomBoolVariable("test_dbgapi.trace_forloop_variable",
+							 "when it is true, then control variable of for loop cycles will be printed",
+							 NULL,
+							 &trace_forloop_variable,
+							 false,
+							 PGC_USERSET, 0,
+							 NULL, NULL, NULL);
+
+	DefineCustomStringVariable("test_dbgapi.trace_variable",
+							   "the content of specified variable is printed before and after any executed statement",
+							   NULL,
+							   &trace_variable,
+							   "",
+							   PGC_USERSET,
+							   0,
+							   NULL, NULL, NULL);
+
+	EmitWarningsOnPlaceholders("test_dbgapi");
+
+	inited = true;
+}
+
+/*
+ * Module unload callback
+ */
+void
+_PG_fini(void)
+{
+	*test_dbgapi_plugin_var = NULL;
+}
+
+Datum
+trace_plpgsql(PG_FUNCTION_ARGS)
+{
+	bool		enable_tracer = PG_GETARG_BOOL(0);
+
+	if (enable_tracer)
+	{
+		test_dbgapi_plugin_var = (PLpgSQL_plugin **) find_rendezvous_variable("PLpgSQL_plugin");
+		*test_dbgapi_plugin_var = &plugin_funcs;
+	}
+	else
+		*test_dbgapi_plugin_var = NULL;
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * Returns true, if statement stmt is any inner statement of
+ * outer (surely some block statement (loop, case, block).
+ */
+static bool
+is_nested_of(PLpgSQL_stmt *stmt, PLpgSQL_stmt *outer_stmt)
+{
+	PLpgSQL_nsitem *ns = stmt->ns;
+	PLpgSQL_nsitem *outer_stmt_ns = outer_stmt->ns;
+
+	/*
+	 * There are two possibilities how to assign namespace to some block
+	 * statement. First (that was not implemented), is using real outer
+	 * namespace. The advantage of this method is consistency for all plpgsql
+	 * statements. Unfortunately, in this case we lost one level of
+	 * namespaces. Instead we assign an namespace created for block statement
+	 * (this is namespace for all body's statements). There is inconsistency
+	 * in semantic of assigned namespaces between block and non block
+	 * statements, but we can easy detect if some statement is nested inside
+	 * other statement, and we can detect implicit variables.
+	 */
+	while (ns)
+	{
+		if (ns == outer_stmt_ns)
+			return true;
+
+		ns = ns->prev;
+	}
+
+	return false;
+}
+
+/*
+ * Returns outer statement's namespace. For block's statements
+ * we have to go one level up, to skip statement's namespace.
+ */
+static PLpgSQL_nsitem *
+outer_ns(PLpgSQL_stmt *stmt)
+{
+	switch (stmt->cmd_type)
+	{
+		case PLPGSQL_STMT_BLOCK:
+		case PLPGSQL_STMT_LOOP:
+		case PLPGSQL_STMT_WHILE:
+		case PLPGSQL_STMT_FORI:
+		case PLPGSQL_STMT_FORS:
+		case PLPGSQL_STMT_FORC:
+		case PLPGSQL_STMT_FOREACH_A:
+		case PLPGSQL_STMT_DYNFORS:
+			return stmt->ns->prev;
+
+		default:
+			return stmt->ns;
+	}
+}
+
+/*
+ * Don't free returned string, can be constant. Returned string,
+ * when it is not a const string, will be released when eval memory
+ * context will be reset.
+ */
+static char *
+get_string_value(PLpgSQL_execstate *estate, PLpgSQL_datum *datum)
+{
+	Oid			typeid;
+	int32		typmod;
+	Datum		value,
+				text_value;
+	bool		isnull;
+	char	   *result;
+	MemoryContext oldcxt;
+
+	oldcxt = MemoryContextSwitchTo(get_eval_mcontext(estate));
+
+	/* Example of usage debug API eval_datum and cast_value functions */
+	(*test_dbgapi_plugin_var)->eval_datum(estate, datum,
+										  &typeid, &typmod, &value, &isnull);
+
+	/* cast value to text */
+	if (!isnull)
+	{
+		/*
+		 * Generic cast is more usable than just cast to string. There can be
+		 * casts from any to boolean or from numeric to double.
+		 */
+		text_value = (*test_dbgapi_plugin_var)->cast_value(estate,
+														   value, &isnull,
+														   typeid, typmod,
+														   TEXTOID, -1);
+
+		result = TextDatumGetCString(text_value);
+	}
+	else
+		result = "NULL";
+
+	MemoryContextSwitchTo(oldcxt);
+
+	return result;
+}
+
+/*
+ * The value of variable can be taken by evaluation of
+ * expression. This is older technique based on assign_expr
+ * method.
+ */
+static char *
+get_string_expr(PLpgSQL_execstate *estate, PLpgSQL_nsitem *ns, char *query)
+{
+	PLpgSQL_var result;
+	PLpgSQL_type typ;
+	PLpgSQL_expr expr;
+	bool		iserror = false;
+	char	   *result_str = NULL;
+	MemoryContext oldcxt;
+	ResourceOwner oldowner;
+
+	memset(&result, 0, sizeof(result));
+	memset(&typ, 0, sizeof(typ));
+	memset(&expr, 0, sizeof(expr));
+
+	result.dtype = PLPGSQL_DTYPE_VAR;
+	result.dno = -1;
+	result.refname = "*auxstorage*";
+	result.datatype = &typ;
+
+	typ.typoid = TEXTOID;
+	typ.ttype = PLPGSQL_TTYPE_SCALAR;
+	typ.typlen = -1;
+	typ.typbyval = false;
+	typ.typtype = 'b';
+
+	expr.query = query;
+	expr.ns = ns;
+	expr.parseMode = RAW_PARSE_PLPGSQL_EXPR;
+
+	oldowner = CurrentResourceOwner;
+	oldcxt = MemoryContextSwitchTo(get_eval_mcontext(estate));
+
+	BeginInternalSubTransaction(NULL);
+	MemoryContextSwitchTo(get_eval_mcontext(estate));
+
+	PG_TRY();
+	{
+		/* It can fail when the variable doesn't exist */
+		(*test_dbgapi_plugin_var)->assign_expr(estate,
+											   (PLpgSQL_datum *) &result,
+											   &expr);
+
+		ReleaseCurrentSubTransaction();
+		MemoryContextSwitchTo(get_eval_mcontext(estate));
+		CurrentResourceOwner = oldowner;
+
+		SPI_restore_connection();
+	}
+	PG_CATCH();
+	{
+		MemoryContextSwitchTo(get_eval_mcontext(estate));
+		FlushErrorState();
+		RollbackAndReleaseCurrentSubTransaction();
+		MemoryContextSwitchTo(get_eval_mcontext(estate));
+		CurrentResourceOwner = oldowner;
+
+		iserror = true;
+
+		SPI_restore_connection();
+	}
+	PG_END_TRY();
+
+	if (!iserror)
+	{
+		if (!result.isnull)
+			result_str = text_to_cstring(DatumGetTextP(result.value));
+		else
+			result_str = "NULL";
+	}
+
+	MemoryContextSwitchTo(oldcxt);
+
+	return result_str;
+}
diff --git a/src/test/modules/test_dbgapi/test_dbgapi.control b/src/test/modules/test_dbgapi/test_dbgapi.control
new file mode 100644
index 0000000000..977ff193cb
--- /dev/null
+++ b/src/test/modules/test_dbgapi/test_dbgapi.control
@@ -0,0 +1,7 @@
+# test_dbgapi extension
+comment = 'test_dbgapi'
+default_version = '1.0'
+module_pathname = '$libdir/test_dbgapi'
+relocatable = false
+schema = pg_catalog
+trusted = true
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 233ddbf4c2..298d8364bc 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -41,7 +41,10 @@ my @contrib_uselibpq =
 my @contrib_uselibpgport   = ('libpq_pipeline', 'oid2name', 'vacuumlo');
 my @contrib_uselibpgcommon = ('libpq_pipeline', 'oid2name', 'vacuumlo');
 my $contrib_extralibs     = { 'libpq_pipeline' => ['ws2_32.lib'] };
-my $contrib_extraincludes = { 'dblink'         => ['src/backend'] };
+my $contrib_extraincludes = {
+	'dblink'      => ['src/backend'],
+	'test_dbgapi' => ['src/pl/plpgsql/src']
+};
 my $contrib_extrasource   = {
 	'cube' => [ 'contrib/cube/cubescan.l', 'contrib/cube/cubeparse.y' ],
 	'seg'  => [ 'contrib/seg/segscan.l',   'contrib/seg/segparse.y' ],

Reply via email to