Skip site navigation (1) Skip section navigation (2)

Peripheral Links

Header And Logo

PostgreSQL
| The world's most advanced open source database.

Site Navigation

Search archives
  Advanced Search

plpgsql debugger (experimental) (fwd)



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
   *


Home | Main Index | Thread Index

Privacy Policy | PostgreSQL Archives hosted by Command Prompt, Inc. | Designed by tinysofa
Copyright © 1996 – 2008 PostgreSQL Global Development Group