po 17. 8. 2020 v 8:40 odesílatel Pavel Stehule <pavel.steh...@gmail.com>
napsal:

> Hi
>
> I am working on tracing support to plpgsql_check
>
> https://github.com/okbob/plpgsql_check
>
> I would like to print content of variables - and now, I have to go some
> deeper than I would like. I need to separate between scalar, row, and
> record variables. PLpgSQL has code for it - but it is private.
>
> Now plpgsql debug API has an API for expression evaluation - and it is
> working fine, but there is a need to know the necessary namespace.
> Unfortunately, the plpgsql variables have not assigned any info about
> related namespaces. It increases the necessary work for implementing
> conditional breakpoints or just printing all variables (and maintaining a
> lot of plpgsql code outside plpgsql core).
>
> So my proposals:
>
> 1. enhancing debug api about method
>
> char *get_cstring_valule(PLpgSQL_variable *var, bool *isnull)
>
> 2. enhancing PLpgSQL_var structure about related namespace "struct
> PLpgSQL_nsitem *ns",
> PLpgSQL_stmt *scope statement (statement that limits scope of variable's
> visibility). For usage in debuggers, tracers can be nice to have a info
> about kind of variable (function argument, local variable, automatic custom
> variable (FORC), automatic internal variable (SQLERRM, FOUND, TG_OP, ...).
>
> Comments, notes?
>

There are two patches

The first patch enhances dbg api by two functions - eval_datum and
cast_value - it is an interface for functions exec_eval_datum and
do_cast_value. With this API it is easy to take a value of any PLpgSQL
variable (without the necessity to duplicate a lot of plpgsql's code), and
it easy to transform this value to any expected type - usually it should
provide the cast to the text type.

Second patch injects pointer to related namespace to any plpgsql statement.
Reference to namespace is required for building custom expressions that can
be evaluated by assign_expr function. I would like to use it for
conditional breakpoints or conditional tracing. Without this patch it is
difficult to detect the correct namespace and ensure the correct variable's
visibility.

Regards

Pavel


> Regards
>
> Pavel
>
>
>
commit 47771637cefa8a283b23bb3b1ba692cbd560403b
Author: ok...@github.com <pavel.steh...@gmail.com>
Date:   Tue Aug 18 14:00:58 2020 +0200

    enhancing dbg plpgsql API

diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index d4a3d58daa..294008af88 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -4078,6 +4078,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/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 0c3d30fb13..bec2429555 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -1142,8 +1142,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
 {
@@ -1158,6 +1161,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;
 
 /*
commit 0f82febd5aeddcf5a6e1a916e29d4b9b86bd483f
Author: ok...@github.com <pavel.steh...@gmail.com>
Date:   Tue Aug 18 17:47:12 2020 +0200

    namespace for each statement

diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index 5a7e1a4444..f492c49513 100644
--- a/src/pl/plpgsql/src/pl_gram.y
+++ b/src/pl/plpgsql/src/pl_gram.y
@@ -912,6 +912,7 @@ stmt_perform	: K_PERFORM expr_until_semi
 						new->cmd_type = PLPGSQL_STMT_PERFORM;
 						new->lineno   = plpgsql_location_to_lineno(@1);
 						new->stmtid = ++plpgsql_curr_compile->nstatements;
+						new->ns = plpgsql_ns_top();
 						new->expr  = $2;
 
 						$$ = (PLpgSQL_stmt *)new;
@@ -926,6 +927,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();
 						new->expr = read_sql_stmt("CALL ");
 						new->is_call = true;
 
@@ -941,6 +943,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();
 						new->expr = read_sql_stmt("DO ");
 						new->is_call = false;
 
@@ -957,6 +960,7 @@ stmt_assign		: assign_var assign_operator expr_until_semi
 						new->cmd_type = PLPGSQL_STMT_ASSIGN;
 						new->lineno   = plpgsql_location_to_lineno(@1);
 						new->stmtid = ++plpgsql_curr_compile->nstatements;
+						new->ns = plpgsql_ns_top();
 						new->varno = $1->dno;
 						new->expr  = $3;
 
@@ -1396,6 +1400,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 +1552,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;
@@ -1648,6 +1654,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;
@@ -2010,6 +2017,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;
@@ -2067,6 +2075,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;
 
@@ -2192,6 +2201,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;
@@ -2213,6 +2223,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;
@@ -2227,6 +2238,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;
@@ -2247,7 +2259,7 @@ stmt_set	: K_SET
 						new->cmd_type = PLPGSQL_STMT_SET;
 						new->lineno = plpgsql_location_to_lineno(@1);
 						new->stmtid = ++plpgsql_curr_compile->nstatements;
-
+						new->ns = plpgsql_ns_top();
 						new->expr = read_sql_stmt("SET ");
 
 						$$ = (PLpgSQL_stmt *)new;
@@ -2260,6 +2272,7 @@ stmt_set	: K_SET
 						new->cmd_type = PLPGSQL_STMT_SET;
 						new->lineno = plpgsql_location_to_lineno(@1);
 						new->stmtid = ++plpgsql_curr_compile->nstatements;
+						new->ns = plpgsql_ns_top();
 						new->expr = read_sql_stmt("RESET ");
 
 						$$ = (PLpgSQL_stmt *)new;
@@ -3377,6 +3390,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)
@@ -4051,6 +4065,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 bec2429555..80e4210ebd 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -475,6 +475,9 @@ typedef struct PLpgSQL_stmt
 	 * per-statement metrics.
 	 */
 	unsigned int stmtid;
+
+	/* namespace chain visible to this statement */
+	PLpgSQL_nsitem *ns;
 } PLpgSQL_stmt;
 
 /*
@@ -515,6 +518,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[] */
@@ -530,6 +534,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;
@@ -542,6 +547,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;
 
@@ -553,6 +559,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;
@@ -566,6 +573,7 @@ typedef struct PLpgSQL_stmt_commit
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	bool		chain;
 } PLpgSQL_stmt_commit;
 
@@ -577,6 +585,7 @@ typedef struct PLpgSQL_stmt_rollback
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	bool		chain;
 } PLpgSQL_stmt_rollback;
 
@@ -588,6 +597,7 @@ typedef struct PLpgSQL_stmt_set
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	PLpgSQL_expr *expr;
 } PLpgSQL_stmt_set;
 
@@ -608,6 +618,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;
@@ -620,6 +631,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 */
@@ -644,6 +656,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 */
@@ -669,6 +682,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;
@@ -681,6 +695,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 */
@@ -694,6 +709,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;
@@ -713,6 +729,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 */
@@ -726,6 +743,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 */
@@ -741,6 +759,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 */
@@ -757,6 +776,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 */
@@ -773,6 +793,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 */
@@ -788,6 +809,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;
@@ -804,6 +826,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 */
@@ -821,6 +844,7 @@ typedef struct PLpgSQL_stmt_close
 	PLpgSQL_stmt_type cmd_type;
 	int			lineno;
 	unsigned int stmtid;
+	PLpgSQL_nsitem *ns;
 	int			curvar;
 } PLpgSQL_stmt_close;
 
@@ -832,6 +856,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;
@@ -845,6 +870,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;
@@ -857,6 +883,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;
@@ -869,6 +896,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 */
@@ -882,6 +910,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 */
@@ -906,6 +935,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;
@@ -918,6 +948,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?  Note:
 								 * mod_stmt is set when we plan the query */
@@ -934,6 +965,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 */

Reply via email to