Hello
I can't to send this mail to pghackers. I don't know reason, it's
unimportant. Next some months I can't to continue in my less work for
postgres. Maybe can be usefull and inspiration for somebody.
Attachments contains patch for debugger (tracer) plpgsql and sample
session. This patch is really hack, but contains all what need, and I hope
will be usefull for next work and discussion. For apply to main tree needs
much better data structures (breakpoints), UI, commands, ...
I plan to add:
quite tracing
better trace info: last n stmts
showing stack
variable value's watchdogs (lt, gt, eq constant,change)
The base of patch is adding two new messages into protoco3. 't' backend
request for user response, 'T' user's response.
Debuger can 's' step, 'p' print, 'c' continue, 'l' list, 'q' quit
commands, please see debug_session file.
Debugger is easy, and has minimal funkcionality, but don't need
bigger changes in plpgsql code (I am not able to do :-().
o it can show all variables, but isn't possible detect current
variable's scope (namespace stack don't exist in runtime)
o isn't possible evaluation of expression (problem with namespace stack
and parsing expression is possible only with compiler time now.
o isn't possible show any system information's now
There was discussion about remote or protocol based dbg<->frontend
comunication
Protocol based
==============
+ is easy, minimal impact on older application
+ every new application can support debugging
(handler traceProcessor)
+ all interfaces based on psql has debug. support
(emacs modes, atd)
+ none problem with debugging in one thread application (psql)
+ none problem with security or minimal
(on production's system debugging can hold resources)
- zero possibility debug older application
Remote debug
============
+ none impact on older application
+ possibility debug older application
+ better for multi threaded app
- needs solving security problem's (more code)
more complicated than sending cancel commnd
security key isn't possible
I hope PostgreSQL will support both variants in future.
I invite any notes, suggestions
Regards
Pavel Stehule
Welcome to psql 8.1devel, the PostgreSQL interactive terminal.
Type: \copyright for distribution terms
\h for help with SQL commands
\? for help with psql commands
\g or terminate with semicolon to execute query
\q to quit
postgres=# set trace_plpgsql to on;
SET
postgres=# select fa(10);
trace: executing function fa
trace: for with integer loopvar on 3: FOR k IN 1..i LOOP
dbg. s
trace: raise on 4: RAISE NOTICE '%', k;
dbg. p
var-. k
trace: var k as int4 has 1
dbg. l
trace: source of function fa
1: DECLARE x integer = 1;
2: BEGIN
3: FOR k IN 1..i LOOP
4: RAISE NOTICE '%', k;
5: x := x + 1;
6: END LOOP;
7: RETURN x;
8: END;
9:
dbg. s
NOTICE: 1
trace: assignment on 5: x := x + 1;
dbg. b
trace: Breakpoint set on
dbg. c
trace: raise on 4: RAISE NOTICE '%', k;
NOTICE: 2
trace: assignment on 5: x := x + 1;
trace: stop on breakpoint
dbg. c
trace: raise on 4: RAISE NOTICE '%', k;
NOTICE: 3
trace: assignment on 5: x := x + 1;
trace: stop on breakpoint
dbg. p
var-. *
trace: var k as int4 has 3
trace: var x as int4 has 3
trace: var found as bool has f
trace: var $1 as int4 has 10
dbg. c
trace: raise on 4: RAISE NOTICE '%', k;
NOTICE: 4
trace: assignment on 5: x := x + 1;
trace: stop on breakpoint
dbg. b
trace: Breakpoint set off
dbg. c
trace: raise on 4: RAISE NOTICE '%', k;
NOTICE: 5
trace: assignment on 5: x := x + 1;
trace: raise on 4: RAISE NOTICE '%', k;
NOTICE: 6
trace: assignment on 5: x := x + 1;
trace: raise on 4: RAISE NOTICE '%', k;
NOTICE: 7
trace: assignment on 5: x := x + 1;
trace: raise on 4: RAISE NOTICE '%', k;
NOTICE: 8
trace: assignment on 5: x := x + 1;
trace: raise on 4: RAISE NOTICE '%', k;
NOTICE: 9
trace: assignment on 5: x := x + 1;
trace: raise on 4: RAISE NOTICE '%', k;
NOTICE: 10
trace: assignment on 5: x := x + 1;
trace: return on 7: RETURN x;
fa
----
11
(1 row)
postgres=# \q
Process SQL finished
diff -c -r pgsql.00/src/backend/utils/misc/guc.c
psql.01/src/backend/utils/misc/guc.c
*** pgsql.00/src/backend/utils/misc/guc.c 2005-06-28 07:09:00.000000000
+0200
--- psql.01/src/backend/utils/misc/guc.c 2005-07-03 16:59:29.000000000
+0200
***************
*** 157,163 ****
char *HbaFileName;
char *IdentFileName;
char *external_pid_file;
!
/*
* These variables are all dummies that don't do anything, except in some
--- 157,163 ----
char *HbaFileName;
char *IdentFileName;
char *external_pid_file;
! bool trace_plpgsql;
/*
* These variables are all dummies that don't do anything, except in some
***************
*** 530,535 ****
--- 530,544 ----
false, NULL, NULL
},
{
+ {"trace_plpgsql", PGC_USERSET, DEVELOPER_OPTIONS,
+ gettext_noop("Set tracing off on plpgsql functions."),
+ NULL,
+ GUC_NOT_IN_SAMPLE
+ },
+ &trace_plpgsql,
+ false, NULL, NULL
+ },
+ {
{"log_duration", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Logs the duration of each completed SQL
statement."),
NULL
diff -c -r pgsql.00/src/bin/psql/command.c psql.01/src/bin/psql/command.c
*** pgsql.00/src/bin/psql/command.c 2005-06-13 08:36:00.000000000 +0200
--- psql.01/src/bin/psql/command.c 2005-07-03 20:13:37.000000000 +0200
***************
*** 1015,1020 ****
--- 1015,1021 ----
}
PQsetNoticeProcessor(pset.db, NoticeProcessor, NULL);
+ PQsetTraceProcessor(pset.db, traceProcessor);
/* Update variables */
SyncVariables();
diff -c -r pgsql.00/src/bin/psql/common.c psql.01/src/bin/psql/common.c
*** pgsql.00/src/bin/psql/common.c 2005-06-22 23:14:00.000000000 +0200
--- psql.01/src/bin/psql/common.c 2005-07-03 20:35:45.000000000 +0200
***************
*** 34,39 ****
--- 34,40 ----
#include "print.h"
#include "mainloop.h"
#include "mb/pg_wchar.h"
+ #include "input.h"
/* Workarounds for Windows */
***************
*** 64,69 ****
--- 65,72 ----
static bool command_no_begin(const char *query);
+ void traceProcessor(char **cmd, char **varname, char **expr);
+
/*
* "Safe" wrapper around strdup()
*/
***************
*** 216,221 ****
--- 219,235 ----
}
+ void traceProcessor(char **cmd, char **varname, char **expr)
+ {
+ *expr = NULL;
+ *varname = NULL;
+
+ *cmd = gets_interactive("dbg. ");
+ if (cmd)
+ if (strcmp(*cmd, "p") == 0)
+ *varname = gets_interactive("var-. ");
+
+ }
/*
* Code to support query cancellation
diff -c -r pgsql.00/src/bin/psql/common.h psql.01/src/bin/psql/common.h
*** pgsql.00/src/bin/psql/common.h 2005-06-13 08:36:00.000000000 +0200
--- psql.01/src/bin/psql/common.h 2005-07-03 20:15:42.000000000 +0200
***************
*** 40,45 ****
--- 40,46 ----
__attribute__((format(printf, 1, 2)));
extern void NoticeProcessor(void *arg, const char *message);
+ extern void traceProcessor(char **cmd, char **varname, char **expr);
extern volatile bool cancel_pressed;
diff -c -r pgsql.00/src/bin/psql/startup.c psql.01/src/bin/psql/startup.c
*** pgsql.00/src/bin/psql/startup.c 2005-06-21 06:02:00.000000000 +0200
--- psql.01/src/bin/psql/startup.c 2005-07-03 20:14:13.000000000 +0200
***************
*** 221,226 ****
--- 221,227 ----
}
PQsetNoticeProcessor(pset.db, NoticeProcessor, NULL);
+ PQsetTraceProcessor(pset.db, traceProcessor);
SyncVariables();
diff -c -r pgsql.00/src/include/utils/guc.h psql.01/src/include/utils/guc.h
*** pgsql.00/src/include/utils/guc.h 2005-06-26 05:04:00.000000000 +0200
--- psql.01/src/include/utils/guc.h 2005-07-03 16:50:04.000000000 +0200
***************
*** 133,139 ****
extern char *HbaFileName;
extern char *IdentFileName;
extern char *external_pid_file;
!
extern void SetConfigOption(const char *name, const char *value,
GucContext context, GucSource source);
--- 133,139 ----
extern char *HbaFileName;
extern char *IdentFileName;
extern char *external_pid_file;
! extern bool trace_plpgsql;
extern void SetConfigOption(const char *name, const char *value,
GucContext context, GucSource source);
diff -c -r pgsql.00/src/interfaces/libpq/fe-connect.c
psql.01/src/interfaces/libpq/fe-connect.c
*** pgsql.00/src/interfaces/libpq/fe-connect.c 2005-06-27 04:04:00.000000000
+0200
--- psql.01/src/interfaces/libpq/fe-connect.c 2005-07-03 20:30:17.000000000
+0200
***************
*** 202,208 ****
}
};
-
static bool connectOptions1(PGconn *conn, const char *conninfo);
static bool connectOptions2(PGconn *conn);
static int connectDBStart(PGconn *conn);
--- 202,207 ----
***************
*** 216,221 ****
--- 215,221 ----
const char *keyword);
static void defaultNoticeReceiver(void *arg, const PGresult *res);
static void defaultNoticeProcessor(void *arg, const char *message);
+ static void defaultTraceProcessor(char **cmd, char **varname, char **expr);
static int parseServiceInfo(PQconninfoOption *options,
PQExpBuffer errorMessage);
static char *pwdfMatchesString(char *buf, char *token);
***************
*** 1825,1830 ****
--- 1825,1831 ----
/* Zero all pointers and booleans */
MemSet(conn, 0, sizeof(PGconn));
+ conn->traceProc = defaultTraceProcessor;
conn->noticeHooks.noticeRec = defaultNoticeReceiver;
conn->noticeHooks.noticeProc = defaultNoticeProcessor;
conn->status = CONNECTION_BAD;
***************
*** 3024,3029 ****
--- 3025,3055 ----
fprintf(stderr, "%s", message);
}
+
+ static void
+ defaultTraceProcessor(char **cmd, char **varname, char **expr)
+ {
+ *cmd = strdup("c");
+ *varname = NULL;
+ *expr = NULL;
+ }
+
+ PQtraceProcessor
+ PQsetTraceProcessor(PGconn *conn, PQtraceProcessor proc)
+ {
+ PQtraceProcessor old;
+
+ if (conn == NULL)
+ return NULL;
+
+ old = conn->traceProc;
+ if (proc)
+ {
+ conn->traceProc = proc;
+ }
+ return old;
+ }
+
/*
* returns a pointer to the next token or NULL if the current
* token doesn't match
diff -c -r pgsql.00/src/interfaces/libpq/fe-protocol3.c
psql.01/src/interfaces/libpq/fe-protocol3.c
*** pgsql.00/src/interfaces/libpq/fe-protocol3.c 2005-06-12
02:00:00.000000000 +0200
--- psql.01/src/interfaces/libpq/fe-protocol3.c 2005-07-03 20:36:44.000000000
+0200
***************
*** 144,149 ****
--- 144,150 ----
*/
if (id == 'A')
{
+
if (getNotify(conn))
return;
}
***************
*** 167,172 ****
--- 168,174 ----
* why it is about to close the connection, so we don't
want
* to just discard it...)
*/
+
if (id == 'E')
{
if (pqGetErrorNotice3(conn, false /* treat as
notice */ ))
***************
*** 193,198 ****
--- 195,221 ----
*/
switch (id)
{
+ case 't':
+ {
+ char *cmd;
+ char *varname;
+ char *expr;
+
+ if (conn->traceProc != NULL)
+ (*conn->traceProc) (&cmd,
&varname, &expr);
+
+ pqPutMsgStart('T', false, conn);
+ pqPutc((cmd)?cmd[0]:'q', conn);
+ pqPuts((varname)?varname:"", conn);
+ pqPuts((expr)?expr:"", conn);
+ pqPutMsgEnd(conn);
+ pqFlush(conn);
+
+ if (cmd) free(cmd);
+ if (varname) free(varname);
+ if (expr) free(expr);
+ }
+ break;
case 'C': /* command complete */
if (pqGets(&conn->workBuffer, conn))
return;
diff -c -r pgsql.00/src/interfaces/libpq/libpq-fe.h
psql.01/src/interfaces/libpq/libpq-fe.h
*** pgsql.00/src/interfaces/libpq/libpq-fe.h 2005-06-13 04:26:00.000000000
+0200
--- psql.01/src/interfaces/libpq/libpq-fe.h 2005-07-03 19:40:41.000000000
+0200
***************
*** 140,145 ****
--- 140,146 ----
/* Function types for notice-handling callbacks */
typedef void (*PQnoticeReceiver) (void *arg, const PGresult *res);
typedef void (*PQnoticeProcessor) (void *arg, const char *message);
+ typedef void (*PQtraceProcessor) (char **cmd, char **varname, char **expr);
/* Print options for PQprint() */
typedef char pqbool;
***************
*** 296,301 ****
--- 297,304 ----
extern PQnoticeProcessor PQsetNoticeProcessor(PGconn *conn,
PQnoticeProcessor proc,
void *arg);
+ extern PQtraceProcessor PQsetTraceProcessor(PGconn *conn,
+ PQtraceProcessor proc);
/*
* Used to set callback that prevents concurrent access to
diff -c -r pgsql.00/src/interfaces/libpq/libpq-int.h
psql.01/src/interfaces/libpq/libpq-int.h
*** pgsql.00/src/interfaces/libpq/libpq-int.h 2005-06-27 04:04:00.000000000
+0200
--- psql.01/src/interfaces/libpq/libpq-int.h 2005-07-03 19:37:42.000000000
+0200
***************
*** 333,338 ****
--- 333,339 ----
/* Status for asynchronous result construction */
PGresult *result; /* result being constructed */
PGresAttValue *curTuple; /* tuple currently being read */
+ PQtraceProcessor traceProc;
#ifdef USE_SSL
bool allow_ssl_try; /* Allowed to try SSL negotiation */
diff -c -r pgsql.00/src/pl/plpgsql/src/gram.y psql.01/src/pl/plpgsql/src/gram.y
*** pgsql.00/src/pl/plpgsql/src/gram.y 2005-06-22 03:35:00.000000000 +0200
--- psql.01/src/pl/plpgsql/src/gram.y 2005-07-03 15:34:05.000000000 +0200
***************
*** 56,61 ****
--- 56,62 ----
PLpgSQL_datum *initial_datum);
static void check_sql_expr(const char *stmt);
static void plpgsql_sql_error_callback(void *arg);
+ char * plpgsql_scanner_src(void);
%}
***************
*** 145,150 ****
--- 146,152 ----
%type <ival> getdiag_kind getdiag_target
%type <ival> lno
+ %type <str> src;
/*
* Keyword tokens
***************
*** 222,228 ****
pl_function : T_FUNCTION comp_optsect pl_block opt_semi
{
! yylval.program =
(PLpgSQL_stmt_block *)$3;
}
| T_TRIGGER comp_optsect pl_block opt_semi
{
--- 224,231 ----
pl_function : T_FUNCTION comp_optsect pl_block opt_semi
{
! yylval.program =
(PLpgSQL_stmt_block *)$3;
!
}
| T_TRIGGER comp_optsect pl_block opt_semi
{
***************
*** 261,266 ****
--- 264,270 ----
new->initvarnos = $1.initvarnos;
new->body = $4;
new->exceptions = $5;
+ new->src = NULL;
plpgsql_ns_pop();
***************
*** 623,628 ****
--- 627,633 ----
new =
palloc0(sizeof(PLpgSQL_stmt_perform));
new->cmd_type =
PLPGSQL_STMT_PERFORM;
new->lineno = $2;
+ new->breakpoint = false;
new->expr = $3;
$$ = (PLpgSQL_stmt *)new;
***************
*** 636,641 ****
--- 641,647 ----
new =
palloc0(sizeof(PLpgSQL_stmt_assign));
new->cmd_type =
PLPGSQL_STMT_ASSIGN;
new->lineno = $2;
+ new->breakpoint = false;
new->varno = $1;
new->expr = $4;
***************
*** 649,654 ****
--- 655,661 ----
new =
palloc0(sizeof(PLpgSQL_stmt_getdiag));
new->cmd_type =
PLPGSQL_STMT_GETDIAG;
+ new->breakpoint = false;
new->lineno = $3;
new->diag_items = $4;
***************
*** 736,741 ****
--- 743,749 ----
new->cond = $3;
new->true_body = $4;
new->false_body = $5;
+ new->breakpoint = false;
$$ = (PLpgSQL_stmt *)new;
}
***************
*** 768,773 ****
--- 776,782 ----
new_if->lineno = $2;
new_if->cond = $3;
new_if->true_body = $4;
+ new_if->breakpoint = false;
new_if->false_body = $5;
/* wrap the if-statement in a
"container" list */
***************
*** 789,794 ****
--- 798,804 ----
new->lineno = $3;
new->label = $1;
new->body = $4;
+ new->breakpoint = false;
plpgsql_ns_pop();
***************
*** 806,811 ****
--- 816,822 ----
new->label = $1;
new->cond = $4;
new->body = $5;
+ new->breakpoint = false;
plpgsql_ns_pop();
***************
*** 866,871 ****
--- 877,883 ----
new =
palloc0(sizeof(PLpgSQL_stmt_dynfors));
new->cmd_type =
PLPGSQL_STMT_DYNFORS;
new->lineno = $1;
+ new->breakpoint = false;
if ($2.rec)
new->rec =
$2.rec;
else if ($2.row)
***************
*** 945,950 ****
--- 957,963 ----
new->reverse =
reverse;
new->lower
= expr1;
new->upper
= expr2;
+ new->breakpoint
= false;
$$ =
(PLpgSQL_stmt *) new;
}
***************
*** 970,975 ****
--- 983,989 ----
new =
palloc0(sizeof(PLpgSQL_stmt_fors));
new->cmd_type =
PLPGSQL_STMT_FORS;
+ new->breakpoint
= false;
new->lineno =
$1;
if ($2.rec)
new->rec = $2.rec;
***************
*** 1047,1052 ****
--- 1061,1067 ----
new->lineno = $2;
new->label = $3;
new->cond = $4;
+ new->breakpoint = false;
$$ = (PLpgSQL_stmt *)new;
}
***************
*** 1071,1076 ****
--- 1086,1092 ----
new->lineno = $2;
new->expr = NULL;
new->retvarno = -1;
+ new->breakpoint = false;
if
(plpgsql_curr_compile->fn_retset)
{
***************
*** 1137,1142 ****
--- 1153,1159 ----
new->lineno = $2;
new->expr = NULL;
new->retvarno = -1;
+ new->breakpoint = false;
if
(plpgsql_curr_compile->out_param_varno >= 0)
{
***************
*** 1182,1187 ****
--- 1199,1205 ----
new->elog_level = $3;
new->message = $4;
new->params = NIL;
+ new->breakpoint = false;
tok = yylex();
***************
*** 1274,1279 ****
--- 1292,1298 ----
new =
palloc(sizeof(PLpgSQL_stmt_dynexecute));
new->cmd_type =
PLPGSQL_STMT_DYNEXECUTE;
new->lineno = $2;
+ new->breakpoint = false;
new->query = expr;
new->rec = NULL;
new->row = NULL;
***************
*** 1324,1329 ****
--- 1343,1349 ----
new =
palloc0(sizeof(PLpgSQL_stmt_open));
new->cmd_type =
PLPGSQL_STMT_OPEN;
+ new->breakpoint = false;
new->lineno = $2;
new->curvar = $3->varno;
***************
*** 1440,1448 ****
--- 1460,1470 ----
new = (PLpgSQL_stmt_fetch
*)make_fetch_stmt();
new->curvar = $3;
+ new->breakpoint = false;
$$ = (PLpgSQL_stmt *)new;
$$->lineno = $2;
+
}
;
***************
*** 1454,1459 ****
--- 1476,1482 ----
new->cmd_type =
PLPGSQL_STMT_CLOSE;
new->lineno = $2;
new->curvar = $3;
+ new->breakpoint = false;
$$ = (PLpgSQL_stmt *)new;
}
***************
*** 1463,1468 ****
--- 1486,1492 ----
{
/* We do not bother building a
node for NULL */
$$ = NULL;
+
}
;
***************
*** 1645,1650 ****
--- 1669,1679 ----
}
;
+ src :
+ {
+ $$ = plpgsql_scanner_src();
+ }
+ ;
%%
diff -c -r pgsql.00/src/pl/plpgsql/src/pl_comp.c
psql.01/src/pl/plpgsql/src/pl_comp.c
*** pgsql.00/src/pl/plpgsql/src/pl_comp.c 2005-06-10 18:23:00.000000000
+0200
--- psql.01/src/pl/plpgsql/src/pl_comp.c 2005-07-03 15:33:39.000000000
+0200
***************
*** 641,647 ****
if (parse_rc != 0)
elog(ERROR, "plpgsql parser returned %d", parse_rc);
function->action = plpgsql_yylval.program;
!
plpgsql_scanner_finish();
pfree(proc_source);
--- 641,647 ----
if (parse_rc != 0)
elog(ERROR, "plpgsql parser returned %d", parse_rc);
function->action = plpgsql_yylval.program;
! function->src = pstrdup(proc_source);
plpgsql_scanner_finish();
pfree(proc_source);
diff -c -r pgsql.00/src/pl/plpgsql/src/pl_exec.c
psql.01/src/pl/plpgsql/src/pl_exec.c
*** pgsql.00/src/pl/plpgsql/src/pl_exec.c 2005-06-27 00:05:00.000000000
+0200
--- psql.01/src/pl/plpgsql/src/pl_exec.c 2005-07-03 20:19:39.000000000
+0200
***************
*** 53,63 ****
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/typcache.h"
!
static const char *const raise_skip_msg = "RAISE";
/*
* All plpgsql function executions within a single transaction share
* the same executor EState for evaluating "simple" expressions. Each
--- 53,67 ----
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/typcache.h"
! #include "libpq/libpq.h"
! #include "libpq/pqformat.h"
! #include "utils/guc.h"
static const char *const raise_skip_msg = "RAISE";
+
+
/*
* All plpgsql function executions within a single transaction share
* the same executor EState for evaluating "simple" expressions. Each
***************
*** 70,75 ****
--- 74,80 ----
*/
static EState *simple_eval_estate = NULL;
static PLpgSQL_expr *active_simple_exprs = NULL;
+ static bool gdb_continue = false;
/************************************************************
* Local function forward declarations
***************
*** 181,187 ****
static bool compatible_tupdesc(TupleDesc td1, TupleDesc td2);
static void exec_set_found(PLpgSQL_execstate *estate, bool state);
static void free_var(PLpgSQL_var *var);
!
/* ----------
* plpgsql_exec_function Called by the call handler for
--- 186,194 ----
static bool compatible_tupdesc(TupleDesc td1, TupleDesc td2);
static void exec_set_found(PLpgSQL_execstate *estate, bool state);
static void free_var(PLpgSQL_var *var);
! static char* exec_getline(int lno, char *src);
! static void exec_sendinfo(const char *fmt, ...);
! static char *pstrndup(char *src, int size);
/* ----------
* plpgsql_exec_function Called by the call handler for
***************
*** 200,206 ****
--- 207,216 ----
* Setup the execution state
*/
plpgsql_estate_setup(&estate, func, (ReturnSetInfo *)
fcinfo->resultinfo);
+ estate.src = func->src;
+ exec_sendinfo("executing function %s", func->fn_name);
+ gdb_continue = false;
/*
* Setup error traceback support for ereport()
*/
***************
*** 284,289 ****
--- 294,302 ----
estate.err_text = NULL;
estate.err_stmt = (PLpgSQL_stmt *) (func->action);
rc = exec_stmt_block(&estate, func->action);
+
+ //estate.src = func->action->src;
+
if (rc != PLPGSQL_RC_RETURN)
{
estate.err_stmt = NULL;
***************
*** 553,558 ****
--- 566,573 ----
* Set the magic variable FOUND to false
*/
exec_set_found(&estate, false);
+
+ estate.src = func->action->src;
/*
* Now call the toplevel block of statements
***************
*** 992,1008 ****
* type specific execution function.
* ----------
*/
static int
exec_stmt(PLpgSQL_execstate *estate, PLpgSQL_stmt *stmt)
{
PLpgSQL_stmt *save_estmt;
int rc = -1;
save_estmt = estate->err_stmt;
estate->err_stmt = stmt;
!
CHECK_FOR_INTERRUPTS();
switch (stmt->cmd_type)
{
case PLPGSQL_STMT_BLOCK:
--- 1007,1231 ----
* type specific execution function.
* ----------
*/
+
+
+ static void
+ exec_sendinfo(const char *fmt, ...)
+ {
+ StringInfoData msgbuf, dtabuf;
+
+ initStringInfo(&dtabuf);
+ dtabuf.maxlen = 30000;
+
+ for (;;)
+ {
+ va_list args;
+ bool success;
+
+ va_start(args, fmt);
+ success = appendStringInfoVA(&dtabuf, fmt, args);
+ va_end(args);
+ if (success)
+ break;
+ enlargeStringInfo(&dtabuf, dtabuf.maxlen);
+ }
+
+ pq_beginmessage(&msgbuf, 'N');
+
+ pq_sendbyte(&msgbuf, PG_DIAG_SEVERITY);
+ pq_sendstring(&msgbuf, "trace");
+
+ pq_sendbyte(&msgbuf, PG_DIAG_MESSAGE_PRIMARY);
+ pq_sendstring(&msgbuf, dtabuf.data);
+
+ pq_sendbyte(&msgbuf, '\0');
+ pq_endmessage(&msgbuf);
+
+ pq_flush();
+
+ pfree(dtabuf.data);
+ }
+
+ static char *
+ exec_getline(int ln, char *src)
+ {
+ int cl = 1;
+ char *c;
+
+ if (*src == '\n')
+ src++;
+
+ while (src)
+ {
+ if ((c = strchr(src,'\n')) == NULL)
+ {
+ if (cl != ln)
+ return NULL;
+ return pstrdup(src);
+ }
+ if (cl++ == ln)
+ return pstrndup(src, c - src);
+ src = c + 1;
+ }
+ return NULL;
+ }
+
+ static char *
+ pstrndup(char *str, int size)
+ {
+ char *res = palloc(size + 1);
+ memcpy(res, str, size);
+ res[size] = '\0';
+ return res;
+ }
+
static int
exec_stmt(PLpgSQL_execstate *estate, PLpgSQL_stmt *stmt)
{
PLpgSQL_stmt *save_estmt;
int rc = -1;
+ bool first_time = true;
save_estmt = estate->err_stmt;
estate->err_stmt = stmt;
!
CHECK_FOR_INTERRUPTS();
+ if (trace_plpgsql)
+ for (;;)
+ {
+ char cmd;
+ char *varname;
+ char *expr;
+ char mtype;
+ StringInfoData buf;
+
+
+ if (first_time)
+ {
+ char *line = exec_getline(stmt->lineno,
estate->src);
+ exec_sendinfo("%s on %d: %s",
plpgsql_stmt_typename(stmt), stmt->lineno,line);
+ if (line) pfree(line);
+ first_time = false;
+ }
+
+ /* noninterctive debug */
+ if (gdb_continue && !stmt->breakpoint)
+ break;
+ if (gdb_continue)
+ exec_sendinfo("stop on breakpoint");
+
+ gdb_continue = false;
+
+ pq_putemptymessage('t');
+ pq_flush();
+
+ mtype = pq_getbyte();
+ if (mtype == EOF)
+ ereport(ERROR,
+ (errcode(ERRCODE_CONNECTION_FAILURE),
+ errmsg("unexpected EOF on client
connection")));
+
+ initStringInfo(&buf);
+ if (pq_getmessage(&buf, 1000))
+ {
+ pfree(buf.data);
+ ereport(ERROR,
+ (errcode(ERRCODE_CONNECTION_FAILURE),
+ errmsg("unexpected EOF on client
connection")));
+ }
+ if (mtype != 'T')
+ {
+ pfree(buf.data);
+ ereport(ERROR,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("unexpected message type 0x%02X
during TRACE op ", mtype)));
+ }
+ cmd = pq_getmsgbyte(&buf);
+ varname = (char *) pq_getmsgstring(&buf);
+ expr = (char *) pq_getmsgstring(&buf);
+ pq_getmsgend(&buf);
+
+ CHECK_FOR_INTERRUPTS();
+
+ if (cmd == 's' || cmd == 'c')
+ {
+ pfree(buf.data);
+ gdb_continue = (cmd == 'c');
+ break;
+ }
+
+ switch (cmd)
+ {
+ case 'q':
+ pfree(buf.data);
+ elog(ERROR, "end of tracing");
+ break;
+ case 'l':
+ {
+ StringInfoData list;
+ int lno = 1;
+ char *c;
+ char *src = estate->src;
+
+ initStringInfo(&list);
+ appendStringInfo(&list, "source of
function %s\n", estate->err_func->fn_name);
+
+ if (*src == '\n')
+ src++;
+ while (src)
+ {
+ if ((c = strchr(src,'\n')) ==
NULL)
+ {
+ appendStringInfo(&list,
"%3d: %s\n", lno, src);
+ break;
+ }
+ else
+ {
+ char *s;
+ s = pstrndup(src, c -
src);
+ appendStringInfo(&list,
"%3d: %s\n", lno++, s);
+ pfree(s);
+ src = c+1;
+ }
+ }
+ exec_sendinfo(list.data);
+ pfree(list.data);
+ }
+ break;
+ case 'p':
+ {
+ char *extval;
+ int i;
+ for (i = estate->ndatums-1;i>=0;i--)
+ {
+ PLpgSQL_var *var = (PLpgSQL_var
*) estate->datums[i];
+ if (strcmp(varname,"*") == 0||
strcmp(varname, var->refname) == 0)
+ {
+ extval =
(var->isnull)?"<NULL>":
+
convert_value_to_string(var->value, var->datatype->typoid);
+
+ exec_sendinfo("var %s
as %s has %s", var->refname, var->datatype->typname, extval);
+ }
+ }
+ }
+ break;
+ case 'b':
+ if (stmt->breakpoint)
+ {
+ stmt->breakpoint = false;
+ exec_sendinfo("Breakpoint set
off");
+ } else
+ {
+ stmt->breakpoint = true;
+ exec_sendinfo("Breakpoint set
on");
+ }
+ break;
+ default:
+ exec_sendinfo("Unknown dbg command");
+ }
+ }
+
switch (stmt->cmd_type)
{
case PLPGSQL_STMT_BLOCK:
***************
*** 2114,2119 ****
--- 2337,2344 ----
estate->err_func = func;
estate->err_stmt = NULL;
estate->err_text = NULL;
+
+ estate->src = NULL;
/*
* Create an EState for evaluation of simple expressions, if there's
diff -c -r pgsql.00/src/pl/plpgsql/src/plpgsql.h
psql.01/src/pl/plpgsql/src/plpgsql.h
*** pgsql.00/src/pl/plpgsql/src/plpgsql.h 2005-06-22 03:35:00.000000000
+0200
--- psql.01/src/pl/plpgsql/src/plpgsql.h 2005-07-03 13:51:59.000000000
+0200
***************
*** 313,318 ****
--- 313,319 ----
{ /* Generic
execution node */
int cmd_type;
int lineno;
+ bool breakpoint;
} PLpgSQL_stmt;
***************
*** 342,347 ****
--- 343,350 ----
{ /* Block of
statements */
int cmd_type;
int lineno;
+ bool breakpoint;
+ char *src;
char *label;
List *body; /* List of statements */
int n_initvars;
***************
*** 354,359 ****
--- 357,363 ----
{ /* Assign
statement */
int cmd_type;
int lineno;
+ bool breakpoint;
int varno;
PLpgSQL_expr *expr;
} PLpgSQL_stmt_assign;
***************
*** 362,367 ****
--- 366,372 ----
{ /* PERFORM
statement */
int cmd_type;
int lineno;
+ bool breakpoint;
PLpgSQL_expr *expr;
} PLpgSQL_stmt_perform;
***************
*** 375,380 ****
--- 380,386 ----
{ /* Get
Diagnostics statement */
int cmd_type;
int lineno;
+ bool breakpoint;
List *diag_items; /* List of PLpgSQL_diag_item */
} PLpgSQL_stmt_getdiag;
***************
*** 383,388 ****
--- 389,395 ----
{ /* IF statement
*/
int cmd_type;
int lineno;
+ bool breakpoint;
PLpgSQL_expr *cond;
List *true_body; /* List of statements */
List *false_body; /* List of statements */
***************
*** 393,398 ****
--- 400,406 ----
{ /*
Unconditional LOOP statement */
int cmd_type;
int lineno;
+ bool breakpoint;
char *label;
List *body; /* List of statements */
} PLpgSQL_stmt_loop;
***************
*** 402,407 ****
--- 410,416 ----
{ /* WHILE cond
LOOP statement */
int cmd_type;
int lineno;
+ bool breakpoint;
char *label;
PLpgSQL_expr *cond;
List *body; /* List of statements */
***************
*** 412,417 ****
--- 421,427 ----
{ /* FOR
statement with integer loopvar */
int cmd_type;
int lineno;
+ bool breakpoint;
char *label;
PLpgSQL_var *var;
PLpgSQL_expr *lower;
***************
*** 425,430 ****
--- 435,441 ----
{ /* FOR
statement running over SELECT */
int cmd_type;
int lineno;
+ bool breakpoint;
char *label;
PLpgSQL_rec *rec;
PLpgSQL_row *row;
***************
*** 437,442 ****
--- 448,454 ----
{ /* FOR
statement running over EXECUTE */
int cmd_type;
int lineno;
+ bool breakpoint;
char *label;
PLpgSQL_rec *rec;
PLpgSQL_row *row;
***************
*** 449,454 ****
--- 461,467 ----
{ /* SELECT ...
INTO statement */
int cmd_type;
int lineno;
+ bool breakpoint;
PLpgSQL_rec *rec;
PLpgSQL_row *row;
PLpgSQL_expr *query;
***************
*** 459,464 ****
--- 472,478 ----
{ /* OPEN a
curvar */
int cmd_type;
int lineno;
+ bool breakpoint;
int curvar;
PLpgSQL_row *returntype;
PLpgSQL_expr *argquery;
***************
*** 471,476 ****
--- 485,491 ----
{ /* FETCH curvar
INTO statement */
int cmd_type;
int lineno;
+ bool breakpoint;
PLpgSQL_rec *rec;
PLpgSQL_row *row;
int curvar;
***************
*** 481,486 ****
--- 496,502 ----
{ /* CLOSE curvar
*/
int cmd_type;
int lineno;
+ bool breakpoint;
int curvar;
} PLpgSQL_stmt_close;
***************
*** 489,494 ****
--- 505,511 ----
{ /* EXIT or
CONTINUE statement */
int cmd_type;
int lineno;
+ bool breakpoint;
bool is_exit; /* Is this an exit or a
continue? */
char *label;
PLpgSQL_expr *cond;
***************
*** 499,504 ****
--- 516,522 ----
{ /* RETURN
statement */
int cmd_type;
int lineno;
+ bool breakpoint;
PLpgSQL_expr *expr;
int retvarno;
} PLpgSQL_stmt_return;
***************
*** 507,512 ****
--- 525,531 ----
{ /* RETURN NEXT
statement */
int cmd_type;
int lineno;
+ bool breakpoint;
PLpgSQL_expr *expr;
int retvarno;
} PLpgSQL_stmt_return_next;
***************
*** 515,520 ****
--- 534,540 ----
{ /* RAISE
statement */
int cmd_type;
int lineno;
+ bool breakpoint;
int elog_level;
char *message;
List *params; /* list of expressions */
***************
*** 525,530 ****
--- 545,551 ----
{ /* Generic SQL
statement to execute */
int cmd_type;
int lineno;
+ bool breakpoint;
PLpgSQL_expr *sqlstmt;
} PLpgSQL_stmt_execsql;
***************
*** 533,538 ****
--- 554,560 ----
{ /* Dynamic SQL
string to execute */
int cmd_type;
int lineno;
+ bool breakpoint;
PLpgSQL_rec *rec; /* INTO record
or row variable */
PLpgSQL_row *row;
PLpgSQL_expr *query;
***************
*** 594,599 ****
--- 616,622 ----
int tg_nargs_varno;
int ndatums;
+ char *src;
PLpgSQL_datum **datums;
PLpgSQL_stmt_block *action;
} PLpgSQL_function;
***************
*** 608,613 ****
--- 631,637 ----
Oid fn_rettype; /* info about declared
function rettype */
bool retistuple;
bool retisset;
+ char *src;
bool readonly_func;
Pouze v psql.01/src/pl/plpgsql/src: pl_scan.c
diff -c -r pgsql.00/src/pl/plpgsql/src/scan.l psql.01/src/pl/plpgsql/src/scan.l
*** pgsql.00/src/pl/plpgsql/src/scan.l 2005-06-26 21:16:00.000000000 +0200
--- psql.01/src/pl/plpgsql/src/scan.l 2005-07-03 10:39:27.000000000 +0200
***************
*** 478,483 ****
--- 478,490 ----
return cur_line_num;
}
+
+ char *
+ plpgsql_scanner_src(void)
+ {
+ return cur_line_start;
+ }
+
/*
* Called before any actual parsing is done
*
---------------------------(end of broadcast)---------------------------
TIP 9: In versions below 8.0, the planner will ignore your desire to
choose an index scan if your joining column's datatypes do not
match