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=&gt; <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

Reply via email to