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