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

Reply via email to