add line number as prompt option to psql

Started by Sawada Masahikoover 11 years ago25 messages
#1Sawada Masahiko
sawada.mshk@gmail.com
1 attachment(s)

Hi all,

The attached IWP patch is one prompt option for psql, which shows
current line number.
If the user made syntax error with too long SQL then psql outputs
message as following.

ERROR: syntax error at or near "a"
LINE 250: hoge
^
psql teaches me where syntax error is occurred, but it is not kind
when SQL is too long.
We can use write SQL with ¥e(editor) command(e.g., emacs) , and we can
know line number.
but it would make terminal log dirty . It will make analyzing of log
difficult after error is occurred.
(I think that we usually use copy & paste)

After attached this patch, we will be able to use %l option as prompting option.

e.g.,
$ cat ~/.psqlrc
\set PROMPT2 '%/[%l]%R%# '
\set PROMPT1 '%/[%l]%R%# '
$ psql -d postgres
postgres[1]=# select
postgres[2]-# *
postgres[3]-# from
postgres[4]-# hoge;

The past discussion is following.
</messages/by-id/CAFj8pRC1ruPk6+chA1jpxPh3uS_zipaBDOvmcEex4wPbp2kZMQ@mail.gmail.com&gt;

Please give me feedback.

Regards,

-------
Sawada Masahiko

Attachments:

psql-line-number_v1.patchapplication/octet-stream; name=psql-line-number_v1.patchDownload
*** a/doc/src/sgml/ref/psql-ref.sgml
--- b/doc/src/sgml/ref/psql-ref.sgml
***************
*** 3298,3303 **** testdb=&gt; <userinput>INSERT INTO my_table VALUES (:'content');</userinput>
--- 3298,3308 ----
        </varlistentry>
  
        <varlistentry>
+         <term><literal>%l</literal></term>
+         <listitem><para>The current line number</para></listitem>
+       </varlistentry>
+ 
+       <varlistentry>
          <term><literal>%</literal><replaceable class="parameter">digits</replaceable></term>
          <listitem>
          <para>
*** a/src/bin/psql/prompt.c
--- b/src/bin/psql/prompt.c
***************
*** 17,22 ****
--- 17,24 ----
  #include <netdb.h>
  #endif
  
+ #include <limits.h>
+ 
  #include "common.h"
  #include "input.h"
  #include "prompt.h"
***************
*** 44,49 ****
--- 46,52 ----
   *		in prompt2 -, *, ', or ";
   *		in prompt3 nothing
   * %x - transaction status: empty, *, !, ? (unknown or no connection)
+  * %l - the line number
   * %? - the error code of the last query (not yet implemented)
   * %% - a percent sign
   *
***************
*** 72,82 **** get_prompt(promptStatus_t status)
--- 75,92 ----
  	bool		esc = false;
  	const char *p;
  	const char *prompt_string = "? ";
+ 	static int cur_line = 1;
+ 
+ 	if (++cur_line >= INT_MAX)
+ 	{
+ 		cur_line = 1;
+ 	}
  
  	switch (status)
  	{
  		case PROMPT_READY:
  			prompt_string = pset.prompt1;
+ 			cur_line = 1;
  			break;
  
  		case PROMPT_CONTINUE:
***************
*** 229,234 **** get_prompt(promptStatus_t status)
--- 239,247 ----
  						}
  					break;
  
+ 				case 'l':
+ 					sprintf(buf, "%d", cur_line);
+ 					break;
  				case '?':
  					/* not here yet */
  					break;
#2Jeevan Chalke
jeevan.chalke@enterprisedb.com
In reply to: Sawada Masahiko (#1)
Re: add line number as prompt option to psql

Hi Sawada Masahiko,

I liked this feature. So I have reviewed it.

Changes are straight forward and looks perfect.
No issues found with make/make install/initdb/regression.

However I would suggest removing un-necessary braces at if, as we have only
one statement into it.

if (++cur_line >= INT_MAX)
{
cur_line = 1;
}

Also following looks wrong:

postgres[1]=# select
postgres[2]-# *
postgres[3]-# from
postgres[4]-# tab;
a
---
(0 rows)

postgres[1]=# select
*
from
tab
postgres[2]-# where t > 10;
ERROR: column "t" does not exist
LINE 5: where t > 10;
^

Line number in ERROR is 5 which is correct.
But line number in psql prompt is wrong.

To get first 4 lines I have simply used up arrow followed by an enter for
which I was expecting 5 in psql prompt.
But NO it was 2 which is certainly wrong.

Need to handle above carefully.

Thanks

On Thu, Jun 12, 2014 at 10:46 PM, Sawada Masahiko <sawada.mshk@gmail.com>
wrote:

Hi all,

The attached IWP patch is one prompt option for psql, which shows
current line number.
If the user made syntax error with too long SQL then psql outputs
message as following.

ERROR: syntax error at or near "a"
LINE 250: hoge
^
psql teaches me where syntax error is occurred, but it is not kind
when SQL is too long.
We can use write SQL with ¥e(editor) command(e.g., emacs) , and we can
know line number.
but it would make terminal log dirty . It will make analyzing of log
difficult after error is occurred.
(I think that we usually use copy & paste)

After attached this patch, we will be able to use %l option as prompting
option.

e.g.,
$ cat ~/.psqlrc
\set PROMPT2 '%/[%l]%R%# '
\set PROMPT1 '%/[%l]%R%# '
$ psql -d postgres
postgres[1]=# select
postgres[2]-# *
postgres[3]-# from
postgres[4]-# hoge;

The past discussion is following.
<
/messages/by-id/CAFj8pRC1ruPk6+chA1jpxPh3uS_zipaBDOvmcEex4wPbp2kZMQ@mail.gmail.com

Please give me feedback.

Regards,

-------
Sawada Masahiko

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--
Jeevan B Chalke
Principal Software Engineer, Product Development
EnterpriseDB Corporation
The Enterprise PostgreSQL Company

Phone: +91 20 30589500

Website: www.enterprisedb.com
EnterpriseDB Blog: http://blogs.enterprisedb.com/
Follow us on Twitter: http://www.twitter.com/enterprisedb

This e-mail message (and any attachment) is intended for the use of the
individual or entity to whom it is addressed. This message contains
information from EnterpriseDB Corporation that may be privileged,
confidential, or exempt from disclosure under applicable law. If you are
not the intended recipient or authorized to receive this for the intended
recipient, any use, dissemination, distribution, retention, archiving, or
copying of this communication is strictly prohibited. If you have received
this e-mail in error, please notify the sender immediately by reply e-mail
and delete this message.

#3Sawada Masahiko
sawada.mshk@gmail.com
In reply to: Jeevan Chalke (#2)
1 attachment(s)
Re: add line number as prompt option to psql

On Fri, Jun 20, 2014 at 7:17 PM, Jeevan Chalke
<jeevan.chalke@enterprisedb.com> wrote:

Hi Sawada Masahiko,

I liked this feature. So I have reviewed it.

Changes are straight forward and looks perfect.
No issues found with make/make install/initdb/regression.

However I would suggest removing un-necessary braces at if, as we have only
one statement into it.

if (++cur_line >= INT_MAX)
{
cur_line = 1;
}

Also following looks wrong:

postgres[1]=# select
postgres[2]-# *
postgres[3]-# from
postgres[4]-# tab;
a
---
(0 rows)

postgres[1]=# select
*
from
tab
postgres[2]-# where t > 10;
ERROR: column "t" does not exist
LINE 5: where t > 10;
^

Line number in ERROR is 5 which is correct.
But line number in psql prompt is wrong.

To get first 4 lines I have simply used up arrow followed by an enter for
which I was expecting 5 in psql prompt.
But NO it was 2 which is certainly wrong.

Need to handle above carefully.

Thanks

On Thu, Jun 12, 2014 at 10:46 PM, Sawada Masahiko <sawada.mshk@gmail.com>
wrote:

Hi all,

The attached IWP patch is one prompt option for psql, which shows
current line number.
If the user made syntax error with too long SQL then psql outputs
message as following.

ERROR: syntax error at or near "a"
LINE 250: hoge
^
psql teaches me where syntax error is occurred, but it is not kind
when SQL is too long.
We can use write SQL with ¥e(editor) command(e.g., emacs) , and we can
know line number.
but it would make terminal log dirty . It will make analyzing of log
difficult after error is occurred.
(I think that we usually use copy & paste)

After attached this patch, we will be able to use %l option as prompting
option.

e.g.,
$ cat ~/.psqlrc
\set PROMPT2 '%/[%l]%R%# '
\set PROMPT1 '%/[%l]%R%# '
$ psql -d postgres
postgres[1]=# select
postgres[2]-# *
postgres[3]-# from
postgres[4]-# hoge;

The past discussion is following.

</messages/by-id/CAFj8pRC1ruPk6+chA1jpxPh3uS_zipaBDOvmcEex4wPbp2kZMQ@mail.gmail.com&gt;

Please give me feedback.

Regards,

-------
Sawada Masahiko

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--
Jeevan B Chalke
Principal Software Engineer, Product Development
EnterpriseDB Corporation
The Enterprise PostgreSQL Company

Phone: +91 20 30589500

Website: www.enterprisedb.com
EnterpriseDB Blog: http://blogs.enterprisedb.com/
Follow us on Twitter: http://www.twitter.com/enterprisedb

This e-mail message (and any attachment) is intended for the use of the
individual or entity to whom it is addressed. This message contains
information from EnterpriseDB Corporation that may be privileged,
confidential, or exempt from disclosure under applicable law. If you are not
the intended recipient or authorized to receive this for the intended
recipient, any use, dissemination, distribution, retention, archiving, or
copying of this communication is strictly prohibited. If you have received
this e-mail in error, please notify the sender immediately by reply e-mail
and delete this message.

Thank you for reviewing patch, and sorry for late response.

I have updated this patch, and attached it.

postgres[1]=# select
*
from
tab
postgres[2]-# where t > 10;
ERROR: column "t" does not exist
LINE 5: where t > 10;

Attached patch can handle this case.

Please give me feedback.

Regards,

-------
Sawada Masahiko

Attachments:

psql-line-number_v2.patchapplication/octet-stream; name=psql-line-number_v2.patchDownload
*** a/doc/src/sgml/ref/psql-ref.sgml
--- b/doc/src/sgml/ref/psql-ref.sgml
***************
*** 3298,3303 **** testdb=&gt; <userinput>INSERT INTO my_table VALUES (:'content');</userinput>
--- 3298,3308 ----
        </varlistentry>
  
        <varlistentry>
+         <term><literal>%l</literal></term>
+         <listitem><para>The current line number</para></listitem>
+       </varlistentry>
+ 
+       <varlistentry>
          <term><literal>%</literal><replaceable class="parameter">digits</replaceable></term>
          <listitem>
          <para>
*** a/src/bin/psql/mainloop.c
--- b/src/bin/psql/mainloop.c
***************
*** 58,63 **** MainLoop(FILE *source)
--- 58,64 ----
  	pset.cur_cmd_source = source;
  	pset.cur_cmd_interactive = ((source == stdin) && !pset.notty);
  	pset.lineno = 0;
+ 	cur_line = 1;
  
  	/* Create working state */
  	scan_state = psql_scan_create();
***************
*** 180,185 **** MainLoop(FILE *source)
--- 181,188 ----
  			continue;
  		}
  
+ 		cur_line++;
+ 
  		/* A request for help? Be friendly and give them some guidance */
  		if (pset.cur_cmd_interactive && query_buf->len == 0 &&
  			pg_strncasecmp(line, "help", 4) == 0 &&
***************
*** 225,230 **** MainLoop(FILE *source)
--- 228,234 ----
  		{
  			PsqlScanResult scan_result;
  			promptStatus_t prompt_tmp = prompt_status;
+ 			char *tmp = line;
  
  			scan_result = psql_scan(scan_state, query_buf, &prompt_tmp);
  			prompt_status = prompt_tmp;
***************
*** 235,240 **** MainLoop(FILE *source)
--- 239,251 ----
  				exit(EXIT_FAILURE);
  			}
  
+ 			/* Calculate the line number */
+ 			while (*tmp != '\0' && scan_result != PSCAN_INCOMPLETE)
+ 			{
+ 				if (*(tmp++) == '\n')
+ 					cur_line++;
+ 			}
+ 
  			/*
  			 * Send command if semicolon found, or if end of line and we're in
  			 * single-line mode.
***************
*** 256,261 **** MainLoop(FILE *source)
--- 267,273 ----
  				/* execute query */
  				success = SendQuery(query_buf->data);
  				slashCmdStatus = success ? PSQL_CMD_SEND : PSQL_CMD_ERROR;
+ 				cur_line = 1;
  
  				/* transfer query to previous_buf by pointer-swapping */
  				{
***************
*** 303,308 **** MainLoop(FILE *source)
--- 315,321 ----
  												 query_buf : previous_buf);
  
  				success = slashCmdStatus != PSQL_CMD_ERROR;
+ 				cur_line = 1;
  
  				if ((slashCmdStatus == PSQL_CMD_SEND || slashCmdStatus == PSQL_CMD_NEWEDIT) &&
  					query_buf->len == 0)
*** a/src/bin/psql/prompt.c
--- b/src/bin/psql/prompt.c
***************
*** 44,49 ****
--- 44,50 ----
   *		in prompt2 -, *, ', or ";
   *		in prompt3 nothing
   * %x - transaction status: empty, *, !, ? (unknown or no connection)
+  * %l - the line number
   * %? - the error code of the last query (not yet implemented)
   * %% - a percent sign
   *
***************
*** 229,234 **** get_prompt(promptStatus_t status)
--- 230,238 ----
  						}
  					break;
  
+ 				case 'l':
+ 					sprintf(buf, "%d", cur_line);
+ 					break;
  				case '?':
  					/* not here yet */
  					break;
*** a/src/bin/psql/prompt.h
--- b/src/bin/psql/prompt.h
***************
*** 22,25 **** typedef enum _promptStatus
--- 22,28 ----
  
  char	   *get_prompt(promptStatus_t status);
  
+ /* Current line number */
+ int        cur_line;
+ 
  #endif   /* PROMPT_H */
#4Jeevan Chalke
jeevan.chalke@enterprisedb.com
In reply to: Sawada Masahiko (#3)
Re: add line number as prompt option to psql

Hi,

Found two (A and B) issues with latest patch:

A:
-- Set prompts
postgres=# \set PROMPT1 '%/[%l]%R%# '
postgres[1]=# \set PROMPT2 '%/[%l]%R%# '

postgres[1]=#
postgres[1]=# select
postgres[2]-# *
postgres[3]-# from
postgres[4]-# abc;
ERROR: relation "abc" does not exist
LINE 4: abc;
^

Now I used \e to edit the source. Deleted last line i.e. "abc;" and
returned to prompt, landed at line 4, typed "abc;"

postgres[1]=# \e
postgres[4]-# abc;
ERROR: relation "abc" does not exist
LINE 5: abc;
^

In above steps, error message says "LINE 5", where as on prompt "abc" is at
line 4.

postgres[1]=# select
*
from
abc;
ERROR: relation "abc" does not exist
LINE 4: abc;
^

Here I again see error at line 4. Something fishy. Please investigate.
Looks like bug in LINE numbering in error message, not sure though.
But with prompt line number feature, it should be sync to each other.

B:
However, I see that you have removed the code changes related to INT_MAX.
Why?
I have set cur_line to INT_MAX - 2 and then observed that after 2 lines I
start getting negative numbers.

Thanks

On Sun, Jul 6, 2014 at 10:48 PM, Sawada Masahiko <sawada.mshk@gmail.com>
wrote:

On Fri, Jun 20, 2014 at 7:17 PM, Jeevan Chalke
<jeevan.chalke@enterprisedb.com> wrote:

Hi Sawada Masahiko,

I liked this feature. So I have reviewed it.

Changes are straight forward and looks perfect.
No issues found with make/make install/initdb/regression.

However I would suggest removing un-necessary braces at if, as we have

only

one statement into it.

if (++cur_line >= INT_MAX)
{
cur_line = 1;
}

Also following looks wrong:

postgres[1]=# select
postgres[2]-# *
postgres[3]-# from
postgres[4]-# tab;
a
---
(0 rows)

postgres[1]=# select
*
from
tab
postgres[2]-# where t > 10;
ERROR: column "t" does not exist
LINE 5: where t > 10;
^

Line number in ERROR is 5 which is correct.
But line number in psql prompt is wrong.

To get first 4 lines I have simply used up arrow followed by an enter for
which I was expecting 5 in psql prompt.
But NO it was 2 which is certainly wrong.

Need to handle above carefully.

Thanks

On Thu, Jun 12, 2014 at 10:46 PM, Sawada Masahiko <sawada.mshk@gmail.com

wrote:

Hi all,

The attached IWP patch is one prompt option for psql, which shows
current line number.
If the user made syntax error with too long SQL then psql outputs
message as following.

ERROR: syntax error at or near "a"
LINE 250: hoge
^
psql teaches me where syntax error is occurred, but it is not kind
when SQL is too long.
We can use write SQL with ¥e(editor) command(e.g., emacs) , and we can
know line number.
but it would make terminal log dirty . It will make analyzing of log
difficult after error is occurred.
(I think that we usually use copy & paste)

After attached this patch, we will be able to use %l option as prompting
option.

e.g.,
$ cat ~/.psqlrc
\set PROMPT2 '%/[%l]%R%# '
\set PROMPT1 '%/[%l]%R%# '
$ psql -d postgres
postgres[1]=# select
postgres[2]-# *
postgres[3]-# from
postgres[4]-# hoge;

The past discussion is following.

<

/messages/by-id/CAFj8pRC1ruPk6+chA1jpxPh3uS_zipaBDOvmcEex4wPbp2kZMQ@mail.gmail.com

Please give me feedback.

Regards,

-------
Sawada Masahiko

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--
Jeevan B Chalke
Principal Software Engineer, Product Development
EnterpriseDB Corporation
The Enterprise PostgreSQL Company

Phone: +91 20 30589500

Website: www.enterprisedb.com
EnterpriseDB Blog: http://blogs.enterprisedb.com/
Follow us on Twitter: http://www.twitter.com/enterprisedb

This e-mail message (and any attachment) is intended for the use of the
individual or entity to whom it is addressed. This message contains
information from EnterpriseDB Corporation that may be privileged,
confidential, or exempt from disclosure under applicable law. If you are

not

the intended recipient or authorized to receive this for the intended
recipient, any use, dissemination, distribution, retention, archiving, or
copying of this communication is strictly prohibited. If you have

received

this e-mail in error, please notify the sender immediately by reply

e-mail

and delete this message.

Thank you for reviewing patch, and sorry for late response.

I have updated this patch, and attached it.

postgres[1]=# select
*
from
tab
postgres[2]-# where t > 10;
ERROR: column "t" does not exist
LINE 5: where t > 10;

Attached patch can handle this case.

Please give me feedback.

Regards,

-------
Sawada Masahiko

--
Jeevan B Chalke
Principal Software Engineer, Product Development
EnterpriseDB Corporation
The Enterprise PostgreSQL Company

Phone: +91 20 30589500

Website: www.enterprisedb.com
EnterpriseDB Blog: http://blogs.enterprisedb.com/
Follow us on Twitter: http://www.twitter.com/enterprisedb

This e-mail message (and any attachment) is intended for the use of the
individual or entity to whom it is addressed. This message contains
information from EnterpriseDB Corporation that may be privileged,
confidential, or exempt from disclosure under applicable law. If you are
not the intended recipient or authorized to receive this for the intended
recipient, any use, dissemination, distribution, retention, archiving, or
copying of this communication is strictly prohibited. If you have received
this e-mail in error, please notify the sender immediately by reply e-mail
and delete this message.

#5Sawada Masahiko
sawada.mshk@gmail.com
In reply to: Jeevan Chalke (#4)
1 attachment(s)
Re: add line number as prompt option to psql

On Mon, Jul 7, 2014 at 8:33 PM, Jeevan Chalke
<jeevan.chalke@enterprisedb.com> wrote:

Hi,

Found two (A and B) issues with latest patch:

A:
-- Set prompts
postgres=# \set PROMPT1 '%/[%l]%R%# '
postgres[1]=# \set PROMPT2 '%/[%l]%R%# '

postgres[1]=#
postgres[1]=# select
postgres[2]-# *
postgres[3]-# from
postgres[4]-# abc;
ERROR: relation "abc" does not exist
LINE 4: abc;
^

Now I used \e to edit the source. Deleted last line i.e. "abc;" and
returned to prompt, landed at line 4, typed "abc;"

postgres[1]=# \e
postgres[4]-# abc;
ERROR: relation "abc" does not exist
LINE 5: abc;
^

In above steps, error message says "LINE 5", where as on prompt "abc" is at
line 4.

postgres[1]=# select
*
from
abc;
ERROR: relation "abc" does not exist
LINE 4: abc;
^

Here I again see error at line 4. Something fishy. Please investigate.
Looks like bug in LINE numbering in error message, not sure though.
But with prompt line number feature, it should be sync to each other.

B:
However, I see that you have removed the code changes related to INT_MAX.
Why?
I have set cur_line to INT_MAX - 2 and then observed that after 2 lines I
start getting negative numbers.

Thank you for reviewing the patch.
I have revised the patch, and attached.

A:
But with prompt line number feature, it should be sync to each other.

This patch can handle this case.

B:
However, I see that you have removed the code changes related to INT_MAX.
Why?

I had mistake to remove them. I added them to latest patch.

Regards,

-------
Sawada Masahiko

Attachments:

psql-line-number_v3.patchapplication/octet-stream; name=psql-line-number_v3.patchDownload
*** a/doc/src/sgml/ref/psql-ref.sgml
--- b/doc/src/sgml/ref/psql-ref.sgml
***************
*** 3298,3303 **** testdb=&gt; <userinput>INSERT INTO my_table VALUES (:'content');</userinput>
--- 3298,3308 ----
        </varlistentry>
  
        <varlistentry>
+         <term><literal>%l</literal></term>
+         <listitem><para>The current line number</para></listitem>
+       </varlistentry>
+ 
+       <varlistentry>
          <term><literal>%</literal><replaceable class="parameter">digits</replaceable></term>
          <listitem>
          <para>
*** a/src/bin/psql/mainloop.c
--- b/src/bin/psql/mainloop.c
***************
*** 8,13 ****
--- 8,14 ----
  #include "postgres_fe.h"
  #include "mainloop.h"
  
+ #include <limits.h>
  
  #include "command.h"
  #include "common.h"
***************
*** 58,63 **** MainLoop(FILE *source)
--- 59,65 ----
  	pset.cur_cmd_source = source;
  	pset.cur_cmd_interactive = ((source == stdin) && !pset.notty);
  	pset.lineno = 0;
+ 	cur_line = 1;
  
  	/* Create working state */
  	scan_state = psql_scan_create();
***************
*** 204,213 **** MainLoop(FILE *source)
  
  		/* insert newlines into query buffer between source lines */
  		if (query_buf->len > 0)
- 		{
- 			appendPQExpBufferChar(query_buf, '\n');
  			added_nl_pos = query_buf->len;
- 		}
  		else
  			added_nl_pos = -1;	/* flag we didn't add one */
  
--- 206,212 ----
***************
*** 225,230 **** MainLoop(FILE *source)
--- 224,231 ----
  		{
  			PsqlScanResult scan_result;
  			promptStatus_t prompt_tmp = prompt_status;
+ 			char *tmp = line;
+ 			int newline = 0;
  
  			scan_result = psql_scan(scan_state, query_buf, &prompt_tmp);
  			prompt_status = prompt_tmp;
***************
*** 235,240 **** MainLoop(FILE *source)
--- 236,261 ----
  				exit(EXIT_FAILURE);
  			}
  
+ 			/* Count the number of new line for calculate of line number */
+ 			while (*tmp != '\0' && scan_result != PSCAN_INCOMPLETE)
+ 			{
+ 				if (*(tmp++) == '\n')
+ 					newline++;
+ 			}
+ 			
+ 			/* Calculate the line number */
+ 			if (scan_result != PSCAN_INCOMPLETE)
+ 			{
+ 				newline = (newline != 0) ? newline : 1;
+ 				cur_line += newline;
+ 			}
+ 
+ 			/* Avoid exceed the limit of integer */
+ 			if (cur_line >= INT_MAX)
+ 			{
+ 				cur_line=1;
+ 			}
+ 
  			/*
  			 * Send command if semicolon found, or if end of line and we're in
  			 * single-line mode.
***************
*** 256,261 **** MainLoop(FILE *source)
--- 277,283 ----
  				/* execute query */
  				success = SendQuery(query_buf->data);
  				slashCmdStatus = success ? PSQL_CMD_SEND : PSQL_CMD_ERROR;
+ 				cur_line = 1;
  
  				/* transfer query to previous_buf by pointer-swapping */
  				{
***************
*** 284,290 **** MainLoop(FILE *source)
  				 */
  				if (query_buf->len == added_nl_pos)
  				{
! 					query_buf->data[--query_buf->len] = '\0';
  					pg_send_history(history_buf);
  				}
  				added_nl_pos = -1;
--- 306,312 ----
  				 */
  				if (query_buf->len == added_nl_pos)
  				{
! 					appendPQExpBufferChar(query_buf, '\0');
  					pg_send_history(history_buf);
  				}
  				added_nl_pos = -1;
***************
*** 303,308 **** MainLoop(FILE *source)
--- 325,331 ----
  												 query_buf : previous_buf);
  
  				success = slashCmdStatus != PSQL_CMD_ERROR;
+ 				cur_line = 1;
  
  				if ((slashCmdStatus == PSQL_CMD_SEND || slashCmdStatus == PSQL_CMD_NEWEDIT) &&
  					query_buf->len == 0)
*** a/src/bin/psql/prompt.c
--- b/src/bin/psql/prompt.c
***************
*** 44,49 ****
--- 44,50 ----
   *		in prompt2 -, *, ', or ";
   *		in prompt3 nothing
   * %x - transaction status: empty, *, !, ? (unknown or no connection)
+  * %l - the line number
   * %? - the error code of the last query (not yet implemented)
   * %% - a percent sign
   *
***************
*** 229,234 **** get_prompt(promptStatus_t status)
--- 230,238 ----
  						}
  					break;
  
+ 				case 'l':
+ 					sprintf(buf, "%d", cur_line);
+ 					break;
  				case '?':
  					/* not here yet */
  					break;
*** a/src/bin/psql/prompt.h
--- b/src/bin/psql/prompt.h
***************
*** 22,25 **** typedef enum _promptStatus
--- 22,28 ----
  
  char	   *get_prompt(promptStatus_t status);
  
+ /* Current line number */
+ int        cur_line;
+ 
  #endif   /* PROMPT_H */
#6Jeevan Chalke
jeevan.chalke@enterprisedb.com
In reply to: Sawada Masahiko (#5)
Re: add line number as prompt option to psql

Hi,

Found few more bugs in new code:

A:
This got bad:

jeevan@ubuntu:~/pg_master$ ./install/bin/psql postgres
psql (9.5devel)
Type "help" for help.

postgres=# \set PROMPT1 '%/[%l]%R%# '
postgres[1]=# \set PROMPT2 '%/[%l]%R%# '
postgres[1]=# select
postgres[2]-# *
postgres[3]-# from
postgres[4]-# abc;
ERROR: syntax error at or near "fromabc"
LINE 1: select*fromabc;
^
postgres[1]=#
postgres[1]=#
postgres[1]=# \e
ERROR: syntax error at or near "fromabc"
LINE 1: select*fromabc;
^
postgres[1]=# select*fromabc;
ERROR: syntax error at or near "fromabc"
LINE 1: select*fromabc;
^
postgres[1]=#

See query text in LINE 1:. This is because, you have removed addition of
newline character. Related added_nl_pos. Need more investigation here.
However I don't think these changes are relevant to what you wanted in this
feature.
Will you please explain the idea behind these changes ?

Moreover, if you don't want to add newline character, then I guess entire
logic related to added_nl_pos is NO more required. You may remove this
variable and its logic altogether, not sure though. Also make sure you
update the relevant comments while doing so. Here you have removed the code
which is adding the newline but the comment there still reads:
/* insert newlines into query buffer between source lines */

Need more thoughts on this.

B:
postgres=# \set PROMPT1 '%/[%l]%R%# '
postgres[1]=# \set PROMPT2 '%/[%l]%R%# '
postgres[1]=# \e
postgres[-2147483645]-# limit 1;
relname
--------------
pg_statistic
(1 row)

postgres[1]=#
postgres[1]=# select
relname
from
pg_class
limit 1;

Logic related to wrapping around the cur_line counter is wrong. Actually
issue is with newline variable. If number of lines in \e editor goes beyond
INT_MAX (NOT sure about the practical use), then newline will be -ve which
then enforces cur_line to be negative. To mimic this I have initialized
newline = INT_MAX - 1.

Thanks

--
Jeevan B Chalke
Principal Software Engineer, Product Development
EnterpriseDB Corporation
The Enterprise PostgreSQL Company

#7Sawada Masahiko
sawada.mshk@gmail.com
In reply to: Jeevan Chalke (#6)
1 attachment(s)
Re: add line number as prompt option to psql

On Thu, Jul 10, 2014 at 8:35 PM, Jeevan Chalke
<jeevan.chalke@enterprisedb.com> wrote:

Hi,

Found few more bugs in new code:

A:
This got bad:

jeevan@ubuntu:~/pg_master$ ./install/bin/psql postgres
psql (9.5devel)
Type "help" for help.

postgres=# \set PROMPT1 '%/[%l]%R%# '
postgres[1]=# \set PROMPT2 '%/[%l]%R%# '
postgres[1]=# select
postgres[2]-# *
postgres[3]-# from
postgres[4]-# abc;
ERROR: syntax error at or near "fromabc"
LINE 1: select*fromabc;

^
postgres[1]=#
postgres[1]=#
postgres[1]=# \e
ERROR: syntax error at or near "fromabc"
LINE 1: select*fromabc;
^
postgres[1]=# select*fromabc;
ERROR: syntax error at or near "fromabc"
LINE 1: select*fromabc;
^
postgres[1]=#

See query text in LINE 1:. This is because, you have removed addition of
newline character. Related added_nl_pos. Need more investigation here.
However I don't think these changes are relevant to what you wanted in this
feature.
Will you please explain the idea behind these changes ?

Moreover, if you don't want to add newline character, then I guess entire
logic related to added_nl_pos is NO more required. You may remove this
variable and its logic altogether, not sure though. Also make sure you
update the relevant comments while doing so. Here you have removed the code
which is adding the newline but the comment there still reads:
/* insert newlines into query buffer between source lines */

Need more thoughts on this.

B:

postgres=# \set PROMPT1 '%/[%l]%R%# '
postgres[1]=# \set PROMPT2 '%/[%l]%R%# '
postgres[1]=# \e
postgres[-2147483645]-# limit 1;
relname
--------------
pg_statistic
(1 row)

postgres[1]=#
postgres[1]=# select
relname
from
pg_class
limit 1;

Logic related to wrapping around the cur_line counter is wrong. Actually
issue is with newline variable. If number of lines in \e editor goes beyond
INT_MAX (NOT sure about the practical use), then newline will be -ve which
then enforces cur_line to be negative. To mimic this I have initialized
newline = INT_MAX - 1.

Thank you for reviewing the patch with variable cases.
I have revised the patch, and attached latest patch.

A:
Will you please explain the idea behind these changes ?

I thought wrong about adding new to tail of query_buf.
The latest patch does not change related to them.

B:

I added the condition of cur_line < 0.

Regards,

-------
Sawada Masahiko

Attachments:

psql-line-number_v4.patchapplication/octet-stream; name=psql-line-number_v4.patchDownload
*** a/doc/src/sgml/ref/psql-ref.sgml
--- b/doc/src/sgml/ref/psql-ref.sgml
***************
*** 3298,3303 **** testdb=&gt; <userinput>INSERT INTO my_table VALUES (:'content');</userinput>
--- 3298,3308 ----
        </varlistentry>
  
        <varlistentry>
+         <term><literal>%l</literal></term>
+         <listitem><para>The current line number</para></listitem>
+       </varlistentry>
+ 
+       <varlistentry>
          <term><literal>%</literal><replaceable class="parameter">digits</replaceable></term>
          <listitem>
          <para>
*** a/src/bin/psql/mainloop.c
--- b/src/bin/psql/mainloop.c
***************
*** 8,13 ****
--- 8,14 ----
  #include "postgres_fe.h"
  #include "mainloop.h"
  
+ #include <limits.h>
  
  #include "command.h"
  #include "common.h"
***************
*** 58,63 **** MainLoop(FILE *source)
--- 59,65 ----
  	pset.cur_cmd_source = source;
  	pset.cur_cmd_interactive = ((source == stdin) && !pset.notty);
  	pset.lineno = 0;
+ 	cur_line = 1;
  
  	/* Create working state */
  	scan_state = psql_scan_create();
***************
*** 225,230 **** MainLoop(FILE *source)
--- 227,234 ----
  		{
  			PsqlScanResult scan_result;
  			promptStatus_t prompt_tmp = prompt_status;
+ 			char *tmp = line;
+ 			int newline = 0;
  
  			scan_result = psql_scan(scan_state, query_buf, &prompt_tmp);
  			prompt_status = prompt_tmp;
***************
*** 235,240 **** MainLoop(FILE *source)
--- 239,265 ----
  				exit(EXIT_FAILURE);
  			}
  
+ 			/* Count the number of new line for calculate ofline number */
+ 			while (*tmp != '\0' && scan_result != PSCAN_INCOMPLETE)
+ 			{
+ 				if (*(tmp++) == '\n')
+ 					newline++;
+ 			}
+ 
+ 			/* Calculate the line number */
+ 			if (scan_result != PSCAN_INCOMPLETE)
+ 			{
+ 				/* The one new line is always added to tail of query_buf  */
+ 				newline = (newline != 0) ? (newline + 1) : 1;
+ 				cur_line += newline;
+ 			}
+ 
+ 			/* Avoid cur_line and newline exceeds the INT_MAX */
+ 			if (cur_line >= INT_MAX || cur_line < 0)
+ 			{
+ 				cur_line = INT_MAX - 1;
+ 			}
+ 
  			/*
  			 * Send command if semicolon found, or if end of line and we're in
  			 * single-line mode.
***************
*** 256,261 **** MainLoop(FILE *source)
--- 281,287 ----
  				/* execute query */
  				success = SendQuery(query_buf->data);
  				slashCmdStatus = success ? PSQL_CMD_SEND : PSQL_CMD_ERROR;
+ 				cur_line = 1;
  
  				/* transfer query to previous_buf by pointer-swapping */
  				{
***************
*** 303,308 **** MainLoop(FILE *source)
--- 329,335 ----
  												 query_buf : previous_buf);
  
  				success = slashCmdStatus != PSQL_CMD_ERROR;
+ 				cur_line = 1;
  
  				if ((slashCmdStatus == PSQL_CMD_SEND || slashCmdStatus == PSQL_CMD_NEWEDIT) &&
  					query_buf->len == 0)
*** a/src/bin/psql/prompt.c
--- b/src/bin/psql/prompt.c
***************
*** 44,49 ****
--- 44,50 ----
   *		in prompt2 -, *, ', or ";
   *		in prompt3 nothing
   * %x - transaction status: empty, *, !, ? (unknown or no connection)
+  * %l - the line number
   * %? - the error code of the last query (not yet implemented)
   * %% - a percent sign
   *
***************
*** 229,234 **** get_prompt(promptStatus_t status)
--- 230,238 ----
  						}
  					break;
  
+ 				case 'l':
+ 					sprintf(buf, "%d", cur_line);
+ 					break;
  				case '?':
  					/* not here yet */
  					break;
*** a/src/bin/psql/prompt.h
--- b/src/bin/psql/prompt.h
***************
*** 22,25 **** typedef enum _promptStatus
--- 22,28 ----
  
  char	   *get_prompt(promptStatus_t status);
  
+ /* Current line number */
+ int        cur_line;
+ 
  #endif   /* PROMPT_H */
#8Jeevan Chalke
jeevan.chalke@enterprisedb.com
In reply to: Sawada Masahiko (#7)
Re: add line number as prompt option to psql

Hi,

Found new issues with latest patch:

Thank you for reviewing the patch with variable cases.
I have revised the patch, and attached latest patch.

A:
Will you please explain the idea behind these changes ?

I thought wrong about adding new to tail of query_buf.
The latest patch does not change related to them.

Thanks.

B:

I added the condition of cur_line < 0.

A.

However, this introduced new bug. As I told, when editor number of lines
reaches INT_MAX it starts giving negative number. You tried overcoming this
issue by adding "< 0" check. But I guess you again fumbled in setting that
correctly. You are setting it to INT_MAX - 1. This enforces each new line
to show line number as INT_MAX - 1 which is incorrect.

postgres=# \set PROMPT1 '%/[%l]%R%# '
postgres[1]=# \set PROMPT2 '%/[%l]%R%# '
postgres[1]=# \e
postgres[2147483646]-# limit
postgres[2147483646]-# 1;
relname
--------------
pg_statistic
(1 row)

postgres[1]=# \e
postgres[2147483646]-# =
postgres[2147483646]-# '
postgres[2147483646]'# abc
postgres[2147483646]'# '
postgres[2147483646]-# ;
relname
---------
(0 rows)

postgres[1]=# select
relname
from
pg_class
where
relname
=
'
abc
'
;

Again to mimic that, I have simply intialized newline to INT_MAX - 2.
Please don't take me wrong, but it seems that your unit testing is not
enough. Above issue I discovered by doing exactly same steps I did in
reviewing previous patch. If you had tested your new patch with those steps
I guess you have caught that yourself.

B.

+             /* Calculate the line number */
+             if (scan_result != PSCAN_INCOMPLETE)
+             {
+                 /* The one new line is always added to tail of query_buf
*/
+                 newline = (newline != 0) ? (newline + 1) : 1;
+                 cur_line += newline;
+             }

Here in above code changes, in any case you are adding 1 to newline. i.e.
when it is 0 you are setting it 1 (+1) and when > 0 you are setting nl + 1
(again +1).
So why can't you simply use"
if (scan_result != PSCAN_INCOMPLETE)
cur_line += (newline + 1);

Or better, why can't you initialize newline with 1 itself and then directly
assign cur_line with newline. That will eliminate above entire code block,
isn't it?
Or much better, simply get rid of newline, and use cur_line itself, will
this work well for you?

C. Typos:
1.
/* Count the number of new line for calculate ofline number */

Missing space between 'of' and 'line'.
However better improve that to something like (just suggestion):
"Count the number of new lines to correctly adjust current line number"

2.
/* Avoid cur_line and newline exceeds the INT_MAX */

You are saying avoid cur_line AND newline, but there is no adjustment for
newline in the code following the comment.

Thanks
--
Jeevan B Chalke
Principal Software Engineer, Product Development
EnterpriseDB Corporation
The Enterprise PostgreSQL Company

#9Sawada Masahiko
sawada.mshk@gmail.com
In reply to: Jeevan Chalke (#8)
Re: add line number as prompt option to psql

On Fri, Jul 11, 2014 at 4:23 PM, Jeevan Chalke
<jeevan.chalke@enterprisedb.com> wrote:

Hi,

A.

However, this introduced new bug. As I told, when editor number of lines
reaches INT_MAX it starts giving negative number. You tried overcoming this
issue by adding "< 0" check. But I guess you again fumbled in setting that
correctly. You are setting it to INT_MAX - 1. This enforces each new line to
show line number as INT_MAX - 1 which is incorrect.

postgres=# \set PROMPT1 '%/[%l]%R%# '
postgres[1]=# \set PROMPT2 '%/[%l]%R%# '
postgres[1]=# \e
postgres[2147483646]-# limit
postgres[2147483646]-# 1;

relname
--------------
pg_statistic
(1 row)

postgres[1]=# \e
postgres[2147483646]-# =
postgres[2147483646]-# '
postgres[2147483646]'# abc
postgres[2147483646]'# '
postgres[2147483646]-# ;
relname
---------
(0 rows)

postgres[1]=# select
relname
from
pg_class
where
relname
=
'
abc
'
;

Again to mimic that, I have simply intialized newline to INT_MAX - 2.
Please don't take me wrong, but it seems that your unit testing is not
enough. Above issue I discovered by doing exactly same steps I did in
reviewing previous patch. If you had tested your new patch with those steps
I guess you have caught that yourself.

To my understating cleanly, you means that line number is not changed
when newline has reached to INT_MAX, is incorrect?
And the line number should be switched to 1 when line number has
reached to INT_MAX?

B.

+             /* Calculate the line number */
+             if (scan_result != PSCAN_INCOMPLETE)
+             {
+                 /* The one new line is always added to tail of query_buf
*/
+                 newline = (newline != 0) ? (newline + 1) : 1;
+                 cur_line += newline;
+             }

Here in above code changes, in any case you are adding 1 to newline. i.e.
when it is 0 you are setting it 1 (+1) and when > 0 you are setting nl + 1
(again +1).
So why can't you simply use"
if (scan_result != PSCAN_INCOMPLETE)
cur_line += (newline + 1);

Or better, why can't you initialize newline with 1 itself and then directly
assign cur_line with newline. That will eliminate above entire code block,
isn't it?
Or much better, simply get rid of newline, and use cur_line itself, will
this work well for you?

this is better. I will change code to this.

C. Typos:
1.
/* Count the number of new line for calculate ofline number */

Missing space between 'of' and 'line'.
However better improve that to something like (just suggestion):
"Count the number of new lines to correctly adjust current line number"

2.
/* Avoid cur_line and newline exceeds the INT_MAX */

You are saying avoid cur_line AND newline, but there is no adjustment for
newline in the code following the comment.

Thanks
--
Jeevan B Chalke
Principal Software Engineer, Product Development
EnterpriseDB Corporation
The Enterprise PostgreSQL Company

Thanks.
I will fix it.

--
Regards,

-------
Sawada Masahiko

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#10Jeevan Chalke
jeevan.chalke@enterprisedb.com
In reply to: Sawada Masahiko (#9)
1 attachment(s)
Re: add line number as prompt option to psql

Hi,

On Fri, Jul 11, 2014 at 3:13 PM, Sawada Masahiko <sawada.mshk@gmail.com>
wrote:

To my understating cleanly, you means that line number is not changed
when newline has reached to INT_MAX, is incorrect?

As per my thinking yes.

And the line number should be switched to 1 when line number has
reached to INT_MAX?

Yes, when it goes beyond INT_MAX, wrap around to 1.

BTW, I wonder, can't we simply use unsigned int instead?

Also, what is the behaviour on LINE n, in error message in case of such
wrap-around?

Or much better, simply get rid of newline, and use cur_line itself, will
this work well for you?

this is better. I will change code to this.
Thanks.
I will fix it.

Meanwhile I have tried this. Attached patch to have your suggestion on
that.

Thanks

--
Jeevan B Chalke
Principal Software Engineer, Product Development
EnterpriseDB Corporation
The Enterprise PostgreSQL Company

Attachments:

psql-line-number_v5_jeevan.patchtext/x-diff; charset=US-ASCII; name=psql-line-number_v5_jeevan.patchDownload
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 255e8ca..030f4d0 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -3298,6 +3298,11 @@ testdb=&gt; <userinput>INSERT INTO my_table VALUES (:'content');</userinput>
       </varlistentry>
 
       <varlistentry>
+        <term><literal>%l</literal></term>
+        <listitem><para>The current line number</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
         <term><literal>%</literal><replaceable class="parameter">digits</replaceable></term>
         <listitem>
         <para>
diff --git a/src/bin/psql/mainloop.c b/src/bin/psql/mainloop.c
index c3aff20..675b550 100644
--- a/src/bin/psql/mainloop.c
+++ b/src/bin/psql/mainloop.c
@@ -8,6 +8,7 @@
 #include "postgres_fe.h"
 #include "mainloop.h"
 
+#include <limits.h>
 
 #include "command.h"
 #include "common.h"
@@ -58,6 +59,7 @@ MainLoop(FILE *source)
 	pset.cur_cmd_source = source;
 	pset.cur_cmd_interactive = ((source == stdin) && !pset.notty);
 	pset.lineno = 0;
+	cur_line = 1;
 
 	/* Create working state */
 	scan_state = psql_scan_create();
@@ -225,6 +227,7 @@ MainLoop(FILE *source)
 		{
 			PsqlScanResult scan_result;
 			promptStatus_t prompt_tmp = prompt_status;
+			char *tmp = line;
 
 			scan_result = psql_scan(scan_state, query_buf, &prompt_tmp);
 			prompt_status = prompt_tmp;
@@ -235,6 +238,30 @@ MainLoop(FILE *source)
 				exit(EXIT_FAILURE);
 			}
 
+			/* 
+			 * Increase current line number counter with the new lines present
+			 * in the line buffer
+			 */
+			while (*tmp != '\0' && scan_result != PSCAN_INCOMPLETE)
+			{
+				if (*(tmp++) == '\n')
+					cur_line++;
+			}
+
+			/* The one new line is always added to tail of query_buf */
+			if (scan_result != PSCAN_INCOMPLETE)
+				cur_line++;
+
+			/*
+			 * If we overflow, then we start at INT_MIN and move towards 0.  So
+			 * to get +ve wrap-around line number we have to add INT_MAX + 2 to
+			 * this number.  We add 2 due to the fact that we have difference
+			 * of 1 in absolute value of INT_MIN and INT_MAX and another 1 as
+			 * line number starts at one and not at zero.
+			 */
+			if (cur_line < 0)
+				cur_line += INT_MAX + 2;
+
 			/*
 			 * Send command if semicolon found, or if end of line and we're in
 			 * single-line mode.
@@ -256,6 +283,7 @@ MainLoop(FILE *source)
 				/* execute query */
 				success = SendQuery(query_buf->data);
 				slashCmdStatus = success ? PSQL_CMD_SEND : PSQL_CMD_ERROR;
+				cur_line = 1;
 
 				/* transfer query to previous_buf by pointer-swapping */
 				{
@@ -303,6 +331,7 @@ MainLoop(FILE *source)
 												 query_buf : previous_buf);
 
 				success = slashCmdStatus != PSQL_CMD_ERROR;
+				cur_line = 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..6a62e5f 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
  * %? - the error code of the last query (not yet implemented)
  * %% - a percent sign
  *
@@ -229,6 +230,9 @@ get_prompt(promptStatus_t status)
 						}
 					break;
 
+				case 'l':
+					sprintf(buf, "%d", cur_line);
+					break;
 				case '?':
 					/* not here yet */
 					break;
diff --git a/src/bin/psql/prompt.h b/src/bin/psql/prompt.h
index 4d2f7e3..f1f80d2 100644
--- a/src/bin/psql/prompt.h
+++ b/src/bin/psql/prompt.h
@@ -22,4 +22,7 @@ typedef enum _promptStatus
 
 char	   *get_prompt(promptStatus_t status);
 
+/* Current line number */
+int        cur_line;
+
 #endif   /* PROMPT_H */
#11Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Jeevan Chalke (#10)
Re: add line number as prompt option to psql

Jeevan Chalke wrote:

On Fri, Jul 11, 2014 at 3:13 PM, Sawada Masahiko <sawada.mshk@gmail.com>
wrote:

And the line number should be switched to 1 when line number has
reached to INT_MAX?

Yes, when it goes beyond INT_MAX, wrap around to 1.

BTW, I wonder, can't we simply use unsigned int instead?

That was my thought also: let the variable be unsigned, and have it wrap
around normally. So once you reach UINT_MAX, the next line number is
zero (instead of getting stuck at UINT_MAX, which would be rather
strange). Anyway I don't think anyone is going to reach the UINT_MAX
limit ... I mean that would be one hell of a query, wouldn't it. If
your query is upwards of a million lines, surely you are in deep trouble
already.

Does your text editor handle files longer than 4 billion lines?

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#12Sawada Masahiko
sawada.mshk@gmail.com
In reply to: Alvaro Herrera (#11)
1 attachment(s)
Re: add line number as prompt option to psql

On Fri, Jul 11, 2014 at 11:01 PM, Alvaro Herrera
<alvherre@2ndquadrant.com> wrote:

Jeevan Chalke wrote:

On Fri, Jul 11, 2014 at 3:13 PM, Sawada Masahiko <sawada.mshk@gmail.com>
wrote:

And the line number should be switched to 1 when line number has
reached to INT_MAX?

Yes, when it goes beyond INT_MAX, wrap around to 1.

BTW, I wonder, can't we simply use unsigned int instead?

That was my thought also: let the variable be unsigned, and have it wrap
around normally. So once you reach UINT_MAX, the next line number is
zero (instead of getting stuck at UINT_MAX, which would be rather
strange). Anyway I don't think anyone is going to reach the UINT_MAX
limit ... I mean that would be one hell of a query, wouldn't it. If
your query is upwards of a million lines, surely you are in deep trouble
already.

Does your text editor handle files longer than 4 billion lines?

As you said, if line number reached UINT_MAX then I think that this
case is too strange.
I think INT_MAX is enough for line number.

The v5 patch which Jeevan is created seems to good.
But one point, I got hunk when patch is applied to HEAD. (doc file)
So I have revised it and attached.

Regards,

-------
Sawada Masahiko

Attachments:

psql-line-number_v5.patchapplication/octet-stream; name=psql-line-number_v5.patchDownload
*** a/doc/src/sgml/ref/psql-ref.sgml
--- b/doc/src/sgml/ref/psql-ref.sgml
***************
*** 3312,3317 **** testdb=&gt; <userinput>INSERT INTO my_table VALUES (:'content');</userinput>
--- 3312,3322 ----
        </varlistentry>
  
        <varlistentry>
+         <term><literal>%l</literal></term>
+         <listitem><para>The current line number</para></listitem>
+       </varlistentry>
+ 
+       <varlistentry>
          <term><literal>%</literal><replaceable class="parameter">digits</replaceable></term>
          <listitem>
          <para>
*** a/src/bin/psql/mainloop.c
--- b/src/bin/psql/mainloop.c
***************
*** 8,13 ****
--- 8,14 ----
  #include "postgres_fe.h"
  #include "mainloop.h"
  
+ #include <limits.h>
  
  #include "command.h"
  #include "common.h"
***************
*** 58,63 **** MainLoop(FILE *source)
--- 59,65 ----
  	pset.cur_cmd_source = source;
  	pset.cur_cmd_interactive = ((source == stdin) && !pset.notty);
  	pset.lineno = 0;
+ 	cur_line = 1;
  
  	/* Create working state */
  	scan_state = psql_scan_create();
***************
*** 225,230 **** MainLoop(FILE *source)
--- 227,233 ----
  		{
  			PsqlScanResult scan_result;
  			promptStatus_t prompt_tmp = prompt_status;
+ 			char *tmp = line;
  
  			scan_result = psql_scan(scan_state, query_buf, &prompt_tmp);
  			prompt_status = prompt_tmp;
***************
*** 235,240 **** MainLoop(FILE *source)
--- 238,267 ----
  				exit(EXIT_FAILURE);
  			}
  
+ 			/* 
+ 			 * Increase current line number counter with the new lines present
+ 			 * in the line buffer
+ 			 */
+ 			while (*tmp != '\0' && scan_result != PSCAN_INCOMPLETE)
+ 			{
+ 				if (*(tmp++) == '\n')
+ 					cur_line++;
+ 			}
+ 
+ 			/* The one new line is always added to tail of query_buf */
+ 			if (scan_result != PSCAN_INCOMPLETE)
+ 				cur_line++;
+ 
+ 			/*
+ 			 * If we overflow, then we start at INT_MIN and move towards 0.  So
+ 			 * to get +ve wrap-around line number we have to add INT_MAX + 2 to
+ 			 * this number.  We add 2 due to the fact that we have difference
+ 			 * of 1 in absolute value of INT_MIN and INT_MAX and another 1 as
+ 			 * line number starts at one and not at zero.
+ 			 */
+ 			if (cur_line < 0)
+ 				cur_line += INT_MAX + 2;
+ 
  			/*
  			 * Send command if semicolon found, or if end of line and we're in
  			 * single-line mode.
***************
*** 256,261 **** MainLoop(FILE *source)
--- 283,289 ----
  				/* execute query */
  				success = SendQuery(query_buf->data);
  				slashCmdStatus = success ? PSQL_CMD_SEND : PSQL_CMD_ERROR;
+ 				cur_line = 1;
  
  				/* transfer query to previous_buf by pointer-swapping */
  				{
***************
*** 303,308 **** MainLoop(FILE *source)
--- 331,337 ----
  												 query_buf : previous_buf);
  
  				success = slashCmdStatus != PSQL_CMD_ERROR;
+ 				cur_line = 1;
  
  				if ((slashCmdStatus == PSQL_CMD_SEND || slashCmdStatus == PSQL_CMD_NEWEDIT) &&
  					query_buf->len == 0)
*** a/src/bin/psql/prompt.c
--- b/src/bin/psql/prompt.c
***************
*** 44,49 ****
--- 44,50 ----
   *		in prompt2 -, *, ', or ";
   *		in prompt3 nothing
   * %x - transaction status: empty, *, !, ? (unknown or no connection)
+  * %l - the line number
   * %? - the error code of the last query (not yet implemented)
   * %% - a percent sign
   *
***************
*** 229,234 **** get_prompt(promptStatus_t status)
--- 230,238 ----
  						}
  					break;
  
+ 				case 'l':
+ 					sprintf(buf, "%d", cur_line);
+ 					break;
  				case '?':
  					/* not here yet */
  					break;
*** a/src/bin/psql/prompt.h
--- b/src/bin/psql/prompt.h
***************
*** 22,25 **** typedef enum _promptStatus
--- 22,28 ----
  
  char	   *get_prompt(promptStatus_t status);
  
+ /* Current line number */
+ int        cur_line;
+ 
  #endif   /* PROMPT_H */
#13Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Sawada Masahiko (#12)
Re: add line number as prompt option to psql

Sawada Masahiko wrote:

As you said, if line number reached UINT_MAX then I think that this
case is too strange.
I think INT_MAX is enough for line number.

My point is not whether 2 billion is a better number than 4 billion as a
maximum value. My point is that wraparound of signed int is, I think,
not even defined in C, whereas wraparound of unsigned int is well
defined. cur_line should be declared as unsigned int. I don't trust
that INT_MAX+2 arithmetic.

Please don't use cur_line as a name for a global variable. Something
like PSQLLineNumber seems more appropriate if it's going to be exposed
through prompt.h. However, note that MainLoop() keeps state in local
variables and notes that it is reentrant; what happens to your cur_line
when a file is read by \i and similar? I wonder if it should be part of
PsqlScanStateData instead ...

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#14Sawada Masahiko
sawada.mshk@gmail.com
In reply to: Alvaro Herrera (#13)
1 attachment(s)
Re: add line number as prompt option to psql

On Sat, Jul 12, 2014 at 2:19 AM, Alvaro Herrera
<alvherre@2ndquadrant.com> wrote:

Sawada Masahiko wrote:

As you said, if line number reached UINT_MAX then I think that this
case is too strange.
I think INT_MAX is enough for line number.

My point is not whether 2 billion is a better number than 4 billion as a
maximum value. My point is that wraparound of signed int is, I think,
not even defined in C, whereas wraparound of unsigned int is well
defined. cur_line should be declared as unsigned int. I don't trust
that INT_MAX+2 arithmetic.

Please don't use cur_line as a name for a global variable. Something
like PSQLLineNumber seems more appropriate if it's going to be exposed
through prompt.h. However, note that MainLoop() keeps state in local
variables and notes that it is reentrant; what happens to your cur_line
when a file is read by \i and similar? I wonder if it should be part of
PsqlScanStateData instead ...

Thank you for comment.
I restarted to make this patch again.

Attached patch is new version patch, and rebased.
pset structure has cur_lineno variable which shows current line number
as unsigned int64.

Regards,

-------
Sawada Masahiko

Attachments:

psql-line-number_v6.patchapplication/octet-stream; name=psql-line-number_v6.patchDownload
*** a/doc/src/sgml/ref/psql-ref.sgml
--- b/doc/src/sgml/ref/psql-ref.sgml
***************
*** 3316,3321 **** testdb=&gt; <userinput>INSERT INTO my_table VALUES (:'content');</userinput>
--- 3316,3326 ----
        </varlistentry>
  
        <varlistentry>
+         <term><literal>%l</literal></term>
+         <listitem><para>The current line number</para></listitem>
+       </varlistentry>
+ 
+       <varlistentry>
          <term><literal>%</literal><replaceable class="parameter">digits</replaceable></term>
          <listitem>
          <para>
*** a/src/bin/psql/mainloop.c
--- b/src/bin/psql/mainloop.c
***************
*** 8,13 ****
--- 8,14 ----
  #include "postgres_fe.h"
  #include "mainloop.h"
  
+ #include <limits.h>
  
  #include "command.h"
  #include "common.h"
***************
*** 58,63 **** MainLoop(FILE *source)
--- 59,65 ----
  	pset.cur_cmd_source = source;
  	pset.cur_cmd_interactive = ((source == stdin) && !pset.notty);
  	pset.lineno = 0;
+ 	pset.cur_lineno = 1;
  
  	/* Create working state */
  	scan_state = psql_scan_create();
***************
*** 225,230 **** MainLoop(FILE *source)
--- 227,233 ----
  		{
  			PsqlScanResult scan_result;
  			promptStatus_t prompt_tmp = prompt_status;
+ 			char *tmp = line;
  
  			scan_result = psql_scan(scan_state, query_buf, &prompt_tmp);
  			prompt_status = prompt_tmp;
***************
*** 235,240 **** MainLoop(FILE *source)
--- 238,257 ----
  				exit(EXIT_FAILURE);
  			}
  
+ 			/* 
+ 			 * Increase current line number counter with the new lines present
+ 			 * in the line buffer
+ 			 */
+ 			while (*tmp != '\0' && scan_result != PSCAN_INCOMPLETE)
+ 			{
+ 				if (*(tmp++) == '\n')
+ 					pset.cur_lineno++;
+ 			}
+ 
+ 			/* The one new line is always added to tail of query_buf */
+ 			if (scan_result != PSCAN_INCOMPLETE)
+ 				pset.cur_lineno++;
+ 
  			/*
  			 * Send command if semicolon found, or if end of line and we're in
  			 * single-line mode.
***************
*** 256,261 **** MainLoop(FILE *source)
--- 273,279 ----
  				/* execute query */
  				success = SendQuery(query_buf->data);
  				slashCmdStatus = success ? PSQL_CMD_SEND : PSQL_CMD_ERROR;
+ 				pset.cur_lineno = 1;
  
  				/* transfer query to previous_buf by pointer-swapping */
  				{
***************
*** 303,308 **** MainLoop(FILE *source)
--- 321,327 ----
  												 query_buf : previous_buf);
  
  				success = slashCmdStatus != PSQL_CMD_ERROR;
+ 				pset.cur_lineno = 1;
  
  				if ((slashCmdStatus == PSQL_CMD_SEND || slashCmdStatus == PSQL_CMD_NEWEDIT) &&
  					query_buf->len == 0)
*** a/src/bin/psql/prompt.c
--- b/src/bin/psql/prompt.c
***************
*** 44,49 ****
--- 44,50 ----
   *		in prompt2 -, *, ', or ";
   *		in prompt3 nothing
   * %x - transaction status: empty, *, !, ? (unknown or no connection)
+  * %l - the line number
   * %? - the error code of the last query (not yet implemented)
   * %% - a percent sign
   *
***************
*** 229,234 **** get_prompt(promptStatus_t status)
--- 230,238 ----
  						}
  					break;
  
+ 				case 'l':
+ 					sprintf(buf, "%u", pset.cur_lineno);
+ 					break;
  				case '?':
  					/* not here yet */
  					break;
*** a/src/bin/psql/settings.h
--- b/src/bin/psql/settings.h
***************
*** 88,93 **** typedef struct _psqlSettings
--- 88,94 ----
  	const char *progname;		/* in case you renamed psql */
  	char	   *inputfile;		/* file being currently processed, if any */
  	uint64		lineno;			/* also for error reporting */
+ 	uint64		cur_lineno;		/* current line number */
  
  	bool		timing;			/* enable timing of all queries */
  
#15Jeevan Chalke
jeevan.chalke@enterprisedb.com
In reply to: Sawada Masahiko (#14)
Re: add line number as prompt option to psql

Hi,

I have reviewed this:

I have initialize cur_lineno to UINTMAX - 2. And then observed following
behaviour to check wrap-around.

postgres=# \set PROMPT1 '%/[%l]%R%# '
postgres[18446744073709551613]=# \set PROMPT2 '%/[%l]%R%# '
postgres[18446744073709551613]=# select
postgres[18446744073709551614]-# a
postgres[18446744073709551615]-# ,
postgres[0]-# b
postgres[1]-# from
postgres[2]-# dual;

It is wrapping to 0, where as line number always start with 1. Any issues?

I would like to ignore this as UINTMAX lines are too much for a input
buffer to hold. It is almost NIL chances to hit this.

However, I think you need to use UINT64_FORMAT while printing uint64
number. Currently it is %u which wrap-around at UINT_MAX.
See how pset.lineno is displayed.

Apart from this, I didn't see any issues in my testing.

Patch applies cleanly. make/make install/initdb/make check all are well.

Thanks

--
Jeevan B Chalke
Principal Software Engineer, Product Development
EnterpriseDB Corporation
The Enterprise PostgreSQL Company

#16Sawada Masahiko
sawada.mshk@gmail.com
In reply to: Jeevan Chalke (#15)
1 attachment(s)
Re: add line number as prompt option to psql

On Wed, Aug 20, 2014 at 9:00 PM, Jeevan Chalke
<jeevan.chalke@enterprisedb.com> wrote:

Hi,

I have reviewed this:

I have initialize cur_lineno to UINTMAX - 2. And then observed following
behaviour to check wrap-around.

postgres=# \set PROMPT1 '%/[%l]%R%# '
postgres[18446744073709551613]=# \set PROMPT2 '%/[%l]%R%# '
postgres[18446744073709551613]=# select
postgres[18446744073709551614]-# a
postgres[18446744073709551615]-# ,
postgres[0]-# b
postgres[1]-# from
postgres[2]-# dual;

It is wrapping to 0, where as line number always start with 1. Any issues?

I would like to ignore this as UINTMAX lines are too much for a input
buffer to hold. It is almost NIL chances to hit this.

However, I think you need to use UINT64_FORMAT while printing uint64
number. Currently it is %u which wrap-around at UINT_MAX.
See how pset.lineno is displayed.

Apart from this, I didn't see any issues in my testing.

Patch applies cleanly. make/make install/initdb/make check all are well.

Thank you for reviewing the patch!
Attached patch is latest version patch.
I modified the output format of cur_lineno.

Regards,

-------
Sawada Masahiko

Attachments:

psql-line-number_v7.patchapplication/octet-stream; name=psql-line-number_v7.patchDownload
*** a/doc/src/sgml/ref/psql-ref.sgml
--- b/doc/src/sgml/ref/psql-ref.sgml
***************
*** 3316,3321 **** testdb=&gt; <userinput>INSERT INTO my_table VALUES (:'content');</userinput>
--- 3316,3326 ----
        </varlistentry>
  
        <varlistentry>
+         <term><literal>%l</literal></term>
+         <listitem><para>The current line number</para></listitem>
+       </varlistentry>
+ 
+       <varlistentry>
          <term><literal>%</literal><replaceable class="parameter">digits</replaceable></term>
          <listitem>
          <para>
*** a/src/bin/psql/mainloop.c
--- b/src/bin/psql/mainloop.c
***************
*** 8,13 ****
--- 8,14 ----
  #include "postgres_fe.h"
  #include "mainloop.h"
  
+ #include <limits.h>
  
  #include "command.h"
  #include "common.h"
***************
*** 58,63 **** MainLoop(FILE *source)
--- 59,65 ----
  	pset.cur_cmd_source = source;
  	pset.cur_cmd_interactive = ((source == stdin) && !pset.notty);
  	pset.lineno = 0;
+ 	pset.cur_lineno = 1;
  
  	/* Create working state */
  	scan_state = psql_scan_create();
***************
*** 225,230 **** MainLoop(FILE *source)
--- 227,233 ----
  		{
  			PsqlScanResult scan_result;
  			promptStatus_t prompt_tmp = prompt_status;
+ 			char *tmp = line;
  
  			scan_result = psql_scan(scan_state, query_buf, &prompt_tmp);
  			prompt_status = prompt_tmp;
***************
*** 235,240 **** MainLoop(FILE *source)
--- 238,257 ----
  				exit(EXIT_FAILURE);
  			}
  
+ 			/* 
+ 			 * Increase current line number counter with the new lines present
+ 			 * in the line buffer
+ 			 */
+ 			while (*tmp != '\0' && scan_result != PSCAN_INCOMPLETE)
+ 			{
+ 				if (*(tmp++) == '\n')
+ 					pset.cur_lineno++;
+ 			}
+ 
+ 			/* The one new line is always added to tail of query_buf */
+ 			if (scan_result != PSCAN_INCOMPLETE)
+ 				pset.cur_lineno++;
+ 
  			/*
  			 * Send command if semicolon found, or if end of line and we're in
  			 * single-line mode.
***************
*** 256,261 **** MainLoop(FILE *source)
--- 273,279 ----
  				/* execute query */
  				success = SendQuery(query_buf->data);
  				slashCmdStatus = success ? PSQL_CMD_SEND : PSQL_CMD_ERROR;
+ 				pset.cur_lineno = 1;
  
  				/* transfer query to previous_buf by pointer-swapping */
  				{
***************
*** 303,308 **** MainLoop(FILE *source)
--- 321,327 ----
  												 query_buf : previous_buf);
  
  				success = slashCmdStatus != PSQL_CMD_ERROR;
+ 				pset.cur_lineno = 1;
  
  				if ((slashCmdStatus == PSQL_CMD_SEND || slashCmdStatus == PSQL_CMD_NEWEDIT) &&
  					query_buf->len == 0)
*** a/src/bin/psql/prompt.c
--- b/src/bin/psql/prompt.c
***************
*** 44,49 ****
--- 44,50 ----
   *		in prompt2 -, *, ', or ";
   *		in prompt3 nothing
   * %x - transaction status: empty, *, !, ? (unknown or no connection)
+  * %l - the line number
   * %? - the error code of the last query (not yet implemented)
   * %% - a percent sign
   *
***************
*** 229,234 **** get_prompt(promptStatus_t status)
--- 230,239 ----
  						}
  					break;
  
+ 				case 'l':
+ 					snprintf(buf, sizeof(buf), UINT64_FORMAT, pset.cur_lineno);
+ 					break;
+ 
  				case '?':
  					/* not here yet */
  					break;
*** a/src/bin/psql/settings.h
--- b/src/bin/psql/settings.h
***************
*** 88,93 **** typedef struct _psqlSettings
--- 88,94 ----
  	const char *progname;		/* in case you renamed psql */
  	char	   *inputfile;		/* file being currently processed, if any */
  	uint64		lineno;			/* also for error reporting */
+ 	uint64		cur_lineno;		/* current line number */
  
  	bool		timing;			/* enable timing of all queries */
  
#17Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Jeevan Chalke (#15)
Re: add line number as prompt option to psql

Jeevan Chalke wrote:

I have initialize cur_lineno to UINTMAX - 2. And then observed following
behaviour to check wrap-around.

postgres=# \set PROMPT1 '%/[%l]%R%# '
postgres[18446744073709551613]=# \set PROMPT2 '%/[%l]%R%# '
postgres[18446744073709551613]=# select
postgres[18446744073709551614]-# a
postgres[18446744073709551615]-# ,
postgres[0]-# b
postgres[1]-# from
postgres[2]-# dual;

It is wrapping to 0, where as line number always start with 1. Any issues?

I would like to ignore this as UINTMAX lines are too much for a input
buffer to hold. It is almost NIL chances to hit this.

Yeah, most likely you will run out of memory before reaching that point,
or out of patience.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#18Jeevan Chalke
jeevan.chalke@enterprisedb.com
In reply to: Alvaro Herrera (#17)
Re: add line number as prompt option to psql

I would like to ignore this as UINTMAX lines are too much for a input

buffer to hold. It is almost NIL chances to hit this.

Yeah, most likely you will run out of memory before reaching that point,
or out of patience.

Yep.

BTW, I have marked this as "waiting for committer".

Thanks
--
Jeevan B Chalke
Principal Software Engineer, Product Development
EnterpriseDB Corporation
The Enterprise PostgreSQL Company

#19Andres Freund
andres@2ndquadrant.com
In reply to: Sawada Masahiko (#16)
1 attachment(s)
Re: add line number as prompt option to psql

On 2014-08-21 11:43:48 +0900, Sawada Masahiko wrote:

On Wed, Aug 20, 2014 at 9:00 PM, Jeevan Chalke
<jeevan.chalke@enterprisedb.com> wrote:

Hi,

I have reviewed this:

I have initialize cur_lineno to UINTMAX - 2. And then observed following
behaviour to check wrap-around.

postgres=# \set PROMPT1 '%/[%l]%R%# '
postgres[18446744073709551613]=# \set PROMPT2 '%/[%l]%R%# '
postgres[18446744073709551613]=# select
postgres[18446744073709551614]-# a
postgres[18446744073709551615]-# ,
postgres[0]-# b
postgres[1]-# from
postgres[2]-# dual;

It is wrapping to 0, where as line number always start with 1. Any issues?

I would like to ignore this as UINTMAX lines are too much for a input
buffer to hold. It is almost NIL chances to hit this.

However, I think you need to use UINT64_FORMAT while printing uint64
number. Currently it is %u which wrap-around at UINT_MAX.
See how pset.lineno is displayed.

Apart from this, I didn't see any issues in my testing.

Patch applies cleanly. make/make install/initdb/make check all are well.

Thank you for reviewing the patch!
Attached patch is latest version patch.
I modified the output format of cur_lineno.

I like the feature - and I wanted to commit it, but enough stuff turned
up that I needed to fix that it warrants some new testing.

Stuff I've changed:
* removed include of limits.h - that probably was a rememnant from a
previous version
* removed a trailing whitespace
* expanded the documentation about %l. "The current line number" isn't
very clear. Of a file? Of all lines ever entered in psql? It's now
"The line number inside the current statement, starting from
<literal>1</>."
* Correspondingly I've changed the variable's name to stmt_lineno.
* COPY FROM ... STDIN/PROMPT3 was broken because a) the promp was only generated
once b) the lineno wasn't incremented.
* CTRL-C didn't reset the line number.
* Unfortunately I've notice here that the prompting is broken in some
common cases:

postgres[1]=# SELECT 1,
postgres[2]-# '2
postgres[2]'# 2b
postgres[2]'# 2c
postgres[2]'# 2d',
postgres[3]-# 3;
┌──────────┬──────────┬──────────┐
│ ?column? │ ?column? │ ?column? │
├──────────┼──────────┼──────────┤
│ 1 │ 2 ↵│ 3 │
│ │ 2b ↵│ │
│ │ 2c ↵│ │
│ │ 2d │ │
└──────────┴──────────┴──────────┘
(1 row)

postgres[1]=# SELECT 1,
'2
2b
2c
2d',
3
postgres[7]-#

That's rather inconsistent...

I've attached my version of the patch. Note that I've got rid of all the
PSCAN_INCOMPLETE checks... Please test!

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Attachments:

0001-Add-psql-PROMPT-variable-showing-which-line-of-a-sta.patchtext/x-patch; charset=us-asciiDownload
>From 476d799d74f2ea8eefc3480f176b3726c35cf425 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Tue, 26 Aug 2014 13:35:49 +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, 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        | 17 +++++++++++++++++
 src/bin/psql/prompt.c          |  5 +++++
 src/bin/psql/settings.h        |  1 +
 5 files changed, 41 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 c759abf..6908742 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..9751009 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)
@@ -168,6 +170,7 @@ MainLoop(FILE *source)
 		count_eof = 0;
 
 		pset.lineno++;
+		pset.stmt_lineno++;
 
 		/* ignore UTF-8 Unicode byte-order mark */
 		if (pset.lineno == 1 && pset.encoding == PG_UTF8 && strncmp(line, "\xef\xbb\xbf", 3) == 0)
@@ -225,6 +228,18 @@ MainLoop(FILE *source)
 		{
 			PsqlScanResult scan_result;
 			promptStatus_t prompt_tmp = prompt_status;
+			char *tmp_line = line;
+
+			/*
+			 * Increase statement line number counter for each complete lines
+			 * already present in the line buffer. That's normally just the
+			 * case when navigating to a statement in the readline history.
+			 */
+			while (*tmp_line != '\0')
+			{
+				if (*(tmp_line++) == '\n')
+					pset.stmt_lineno++;
+			}
 
 			scan_result = psql_scan(scan_state, query_buf, &prompt_tmp);
 			prompt_status = prompt_tmp;
@@ -256,6 +271,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 +319,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

#20Sawada Masahiko
sawada.mshk@gmail.com
In reply to: Andres Freund (#19)
1 attachment(s)
Re: add line number as prompt option to psql

On Tue, Aug 26, 2014 at 10:23 PM, Andres Freund <andres@2ndquadrant.com> wrote:

On 2014-08-21 11:43:48 +0900, Sawada Masahiko wrote:

On Wed, Aug 20, 2014 at 9:00 PM, Jeevan Chalke
<jeevan.chalke@enterprisedb.com> wrote:

Hi,

I have reviewed this:

I have initialize cur_lineno to UINTMAX - 2. And then observed following
behaviour to check wrap-around.

postgres=# \set PROMPT1 '%/[%l]%R%# '
postgres[18446744073709551613]=# \set PROMPT2 '%/[%l]%R%# '
postgres[18446744073709551613]=# select
postgres[18446744073709551614]-# a
postgres[18446744073709551615]-# ,
postgres[0]-# b
postgres[1]-# from
postgres[2]-# dual;

It is wrapping to 0, where as line number always start with 1. Any issues?

I would like to ignore this as UINTMAX lines are too much for a input
buffer to hold. It is almost NIL chances to hit this.

However, I think you need to use UINT64_FORMAT while printing uint64
number. Currently it is %u which wrap-around at UINT_MAX.
See how pset.lineno is displayed.

Apart from this, I didn't see any issues in my testing.

Patch applies cleanly. make/make install/initdb/make check all are well.

Thank you for reviewing the patch!
Attached patch is latest version patch.
I modified the output format of cur_lineno.

I like the feature - and I wanted to commit it, but enough stuff turned
up that I needed to fix that it warrants some new testing.

Stuff I've changed:
* removed include of limits.h - that probably was a rememnant from a
previous version
* removed a trailing whitespace
* expanded the documentation about %l. "The current line number" isn't
very clear. Of a file? Of all lines ever entered in psql? It's now
"The line number inside the current statement, starting from
<literal>1</>."
* Correspondingly I've changed the variable's name to stmt_lineno.
* COPY FROM ... STDIN/PROMPT3 was broken because a) the promp was only generated
once b) the lineno wasn't incremented.
* CTRL-C didn't reset the line number.
* Unfortunately I've notice here that the prompting is broken in some
common cases:

postgres[1]=# SELECT 1,
postgres[2]-# '2
postgres[2]'# 2b
postgres[2]'# 2c
postgres[2]'# 2d',
postgres[3]-# 3;
┌──────────┬──────────┬──────────┐
│ ?column? │ ?column? │ ?column? │
├──────────┼──────────┼──────────┤
│ 1 │ 2 ↵│ 3 │
│ │ 2b ↵│ │
│ │ 2c ↵│ │
│ │ 2d │ │
└──────────┴──────────┴──────────┘
(1 row)

postgres[1]=# SELECT 1,
'2
2b
2c
2d',
3
postgres[7]-#

That's rather inconsistent...

I've attached my version of the patch. Note that I've got rid of all the
PSCAN_INCOMPLETE checks... Please test!

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.
Attached patch is resolve above behavior based on your version patch.

Regards,

-------
Sawada Masahiko

Attachments:

psql-line-number_v7.patchapplication/octet-stream; name=psql-line-number_v7.patchDownload
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..45dd34c 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,10 +227,25 @@ MainLoop(FILE *source)
 		{
 			PsqlScanResult scan_result;
 			promptStatus_t prompt_tmp = prompt_status;
+			char *tmp_line = line;
 
 			scan_result = psql_scan(scan_state, query_buf, &prompt_tmp);
 			prompt_status = prompt_tmp;
 
+			/*
+			 * Increase statement line number counter for each complete lines
+			 * already present in the line buffer. That's normally just the
+			 * case when navigating to a statement in the readline history.
+			 */
+			while (*tmp_line != '\0')
+			{
+				if (*(tmp_line++) == '\n')
+					pset.stmt_lineno++;
+			}
+
+			/* The one new line is always added to tail of query_buf */
+			pset.stmt_lineno++;
+
 			if (PQExpBufferBroken(query_buf))
 			{
 				psql_error("out of memory\n");
@@ -256,6 +273,7 @@ MainLoop(FILE *source)
 				/* execute query */
 				success = SendQuery(query_buf->data);
 				slashCmdStatus = success ? PSQL_CMD_SEND : PSQL_CMD_ERROR;
+				pset.stmt_lineno = 0;
 
 				/* transfer query to previous_buf by pointer-swapping */
 				{
@@ -303,6 +321,7 @@ MainLoop(FILE *source)
 												 query_buf : previous_buf);
 
 				success = slashCmdStatus != PSQL_CMD_ERROR;
+				pset.stmt_lineno = 0;
 
 				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 */
 
#21Andres Freund
andres@2ndquadrant.com
In reply to: Sawada Masahiko (#20)
1 attachment(s)
Re: add line number as prompt option to psql

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

Attachments:

0001-Add-psql-PROMPT-variable-showing-which-line-of-a-sta.patchtext/x-patch; charset=us-asciiDownload
>From a4823d67181c8b5109f6d01a7a41f8dfbfdc86c5 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@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

#22Michael Paquier
michael.paquier@gmail.com
In reply to: Andres Freund (#21)
Re: add line number as prompt option to psql

On Tue, Sep 2, 2014 at 11:12 AM, Andres Freund <andres@2ndquadrant.com> wrote:

I've now used up a perfectly good glass of wine for this.

Red or white? From where? Useful tips for hacking in this area are
always useful.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#23Andres Freund
andres@2ndquadrant.com
In reply to: Michael Paquier (#22)
Re: add line number as prompt option to psql

On 2014-09-02 11:19:09 +0900, Michael Paquier wrote:

On Tue, Sep 2, 2014 at 11:12 AM, Andres Freund <andres@2ndquadrant.com> wrote:

I've now used up a perfectly good glass of wine for this.

Red or white? From where? Useful tips for hacking in this area are
always useful.

Hah ;). Nothing special, but I rather like it: Bordeaux Superieur Cuvee,
Chateau Couronneau 2011.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#24Sawada Masahiko
sawada.mshk@gmail.com
In reply to: Andres Freund (#21)
Re: add line number as prompt option to psql

On Tue, Sep 2, 2014 at 11:12 AM, Andres Freund <andres@2ndquadrant.com> wrote:

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 ;)

Thank you for updating the patch!
I tested it.
These fix looks good to me :)

Regards,

-------
Sawada Masahiko

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#25Andres Freund
andres@2ndquadrant.com
In reply to: Sawada Masahiko (#24)
Re: add line number as prompt option to psql

On 2014-09-02 12:34:12 +0900, Sawada Masahiko wrote:

On Tue, Sep 2, 2014 at 11:12 AM, Andres Freund <andres@2ndquadrant.com> wrote:

I've now used up a perfectly good glass of wine for this, so this is it
for today ;)

Thank you for updating the patch!
I tested it.
These fix looks good to me :)

Committed. Thanks for the patch!

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers