On 2014-08-31 12:06:31 +0900, Sawada Masahiko wrote: > Thank you for review comment and improving the patch! > I tested it. > Your patch always increment line number even if there is no input line > as follows. > > postgres[1]=# > postgres[2]=# select > postgres[3]-# , > postgres[4]-# from > postgres[5]-# hoge; > ERROR: syntax error at or near "," at character 8 > STATEMENT: select > , > from > hoge; > ERROR: syntax error at or near "," > LINE 2: , > ^ > Actually error syntax is in line 2 as postgres reported. > But it is inconsistent.
Hm. Right. That's clearly wrong. > Attached patch is resolve above behavior based on your version patch. I've looked a bit further and found two more broken things. 1) postgres[1]=# SELECT 1; SELECT 2 postgres[1]=# Note the 1 in the second line. Obviously wrong. The fix for this is easy: Don't count a newline if there isn't one. But check for PSCAN_EOL. That also gets rid of inconsistent pset.stmt_lineno initializations (sometimes to 0, sometimes to 1). 2) postgres[1]=# SELECT 1, postgres[2]-# 2, postgres[3]-# 3; ┌──────────┬──────────┬──────────┐ │ ?column? │ ?column? │ ?column? │ ├──────────┼──────────┼──────────┤ │ 1 │ 2 │ 3 │ └──────────┴──────────┴──────────┘ (1 row) postgres[1]=# SELECT 1, 2, 3; ┌──────────┬──────────┬──────────┐ │ ?column? │ ?column? │ ?column? │ ├──────────┼──────────┼──────────┤ │ 1 │ 2 │ 3 │ └──────────┴──────────┴──────────┘ (1 row) postgres[3]=# Obviously the three in the last line is wrong. The fix is slightly nontrivial. It's wrong to look at 'line' when determining the number of lines to add - it may already be executed. The, it seems to me, correct thing is to look at the data that's appended to the query buffer. Alternatively we could always count all lines in the query buffer, but that'd be O(lines^2)... I've done both in the appended patch. I've now used up a perfectly good glass of wine for this, so this is it for today ;) Greetings, Andres Freund -- Andres Freund http://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Training & Services
>From a4823d67181c8b5109f6d01a7a41f8dfbfdc86c5 Mon Sep 17 00:00:00 2001 From: Andres Freund <and...@anarazel.de> Date: Tue, 2 Sep 2014 04:10:30 +0200 Subject: [PATCH] Add psql PROMPT variable showing which line of a statement is being edited. The new %l substitution shows the line number inside a (potentially multi-line) statement starting from one. Author: Sawada Masahiko, heavily editorialized by me. Reviewed-By: Jeevan Chalke, Alvaro Herrera --- doc/src/sgml/ref/psql-ref.sgml | 9 +++++++++ src/bin/psql/copy.c | 15 +++++++++------ src/bin/psql/mainloop.c | 23 +++++++++++++++++++++++ src/bin/psql/prompt.c | 5 +++++ src/bin/psql/settings.h | 1 + 5 files changed, 47 insertions(+), 6 deletions(-) diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index 74d4618..db314c3 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -3316,6 +3316,15 @@ testdb=> <userinput>INSERT INTO my_table VALUES (:'content');</userinput> </varlistentry> <varlistentry> + <term><literal>%l</literal></term> + <listitem> + <para> + The line number inside the current statement, starting from <literal>1</>. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><literal>%</literal><replaceable class="parameter">digits</replaceable></term> <listitem> <para> diff --git a/src/bin/psql/copy.c b/src/bin/psql/copy.c index 4b74915..90f4a24 100644 --- a/src/bin/psql/copy.c +++ b/src/bin/psql/copy.c @@ -517,8 +517,8 @@ bool handleCopyIn(PGconn *conn, FILE *copystream, bool isbinary, PGresult **res) { bool OK; - const char *prompt; char buf[COPYBUFSIZ]; + bool showprompt = false; /* * Establish longjmp destination for exiting from wait-for-input. (This is @@ -540,21 +540,20 @@ handleCopyIn(PGconn *conn, FILE *copystream, bool isbinary, PGresult **res) /* Prompt if interactive input */ if (isatty(fileno(copystream))) { + showprompt = true; if (!pset.quiet) puts(_("Enter data to be copied followed by a newline.\n" "End with a backslash and a period on a line by itself.")); - prompt = get_prompt(PROMPT_COPY); } - else - prompt = NULL; OK = true; if (isbinary) { /* interactive input probably silly, but give one prompt anyway */ - if (prompt) + if (showprompt) { + const char *prompt = get_prompt(PROMPT_COPY); fputs(prompt, stdout); fflush(stdout); } @@ -589,8 +588,9 @@ handleCopyIn(PGconn *conn, FILE *copystream, bool isbinary, PGresult **res) bool firstload; bool linedone; - if (prompt) + if (showprompt) { + const char *prompt = get_prompt(PROMPT_COPY); fputs(prompt, stdout); fflush(stdout); } @@ -650,7 +650,10 @@ handleCopyIn(PGconn *conn, FILE *copystream, bool isbinary, PGresult **res) } if (copystream == pset.cur_cmd_source) + { pset.lineno++; + pset.stmt_lineno++; + } } } diff --git a/src/bin/psql/mainloop.c b/src/bin/psql/mainloop.c index c3aff20..f07801c 100644 --- a/src/bin/psql/mainloop.c +++ b/src/bin/psql/mainloop.c @@ -58,6 +58,7 @@ MainLoop(FILE *source) pset.cur_cmd_source = source; pset.cur_cmd_interactive = ((source == stdin) && !pset.notty); pset.lineno = 0; + pset.stmt_lineno = 1; /* Create working state */ scan_state = psql_scan_create(); @@ -110,6 +111,7 @@ MainLoop(FILE *source) count_eof = 0; slashCmdStatus = PSQL_CMD_UNKNOWN; prompt_status = PROMPT_READY; + pset.stmt_lineno = 1; cancel_pressed = false; if (pset.cur_cmd_interactive) @@ -225,7 +227,10 @@ MainLoop(FILE *source) { PsqlScanResult scan_result; promptStatus_t prompt_tmp = prompt_status; + size_t pos_in_query; + char *tmp_line; + pos_in_query = query_buf->len; scan_result = psql_scan(scan_state, query_buf, &prompt_tmp); prompt_status = prompt_tmp; @@ -236,6 +241,22 @@ MainLoop(FILE *source) } /* + * Increase statement line number counter for each linebreak added + * to the query buffer by the last psql_scan() call. That just + * will be the case when navigating to a statement in readline's + * history that contains newlines. + */ + tmp_line = query_buf->data + pos_in_query; + while (*tmp_line != '\0') + { + if (*(tmp_line++) == '\n') + pset.stmt_lineno++; + } + + if (scan_result == PSCAN_EOL) + pset.stmt_lineno++; + + /* * Send command if semicolon found, or if end of line and we're in * single-line mode. */ @@ -256,6 +277,7 @@ MainLoop(FILE *source) /* execute query */ success = SendQuery(query_buf->data); slashCmdStatus = success ? PSQL_CMD_SEND : PSQL_CMD_ERROR; + pset.stmt_lineno = 1; /* transfer query to previous_buf by pointer-swapping */ { @@ -303,6 +325,7 @@ MainLoop(FILE *source) query_buf : previous_buf); success = slashCmdStatus != PSQL_CMD_ERROR; + pset.stmt_lineno = 1; if ((slashCmdStatus == PSQL_CMD_SEND || slashCmdStatus == PSQL_CMD_NEWEDIT) && query_buf->len == 0) diff --git a/src/bin/psql/prompt.c b/src/bin/psql/prompt.c index 26fca04..f2db9a9 100644 --- a/src/bin/psql/prompt.c +++ b/src/bin/psql/prompt.c @@ -44,6 +44,7 @@ * in prompt2 -, *, ', or "; * in prompt3 nothing * %x - transaction status: empty, *, !, ? (unknown or no connection) + * %l - The line number inside the current statement, starting from 1. * %? - the error code of the last query (not yet implemented) * %% - a percent sign * @@ -229,6 +230,10 @@ get_prompt(promptStatus_t status) } break; + case 'l': + snprintf(buf, sizeof(buf), UINT64_FORMAT, pset.stmt_lineno); + break; + case '?': /* not here yet */ break; diff --git a/src/bin/psql/settings.h b/src/bin/psql/settings.h index 453d6c8..ef24a4e 100644 --- a/src/bin/psql/settings.h +++ b/src/bin/psql/settings.h @@ -88,6 +88,7 @@ typedef struct _psqlSettings const char *progname; /* in case you renamed psql */ char *inputfile; /* file being currently processed, if any */ uint64 lineno; /* also for error reporting */ + uint64 stmt_lineno; /* line number inside the current statement */ bool timing; /* enable timing of all queries */ -- 2.0.0.rc2.4.g1dc51c6.dirty
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers