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 */