Add psql PROMPT variable showing which line of a statement is being edited.
authorAndres Freund <andres@anarazel.de>
Tue, 2 Sep 2014 11:05:48 +0000 (13:05 +0200)
committerAndres Freund <andres@anarazel.de>
Tue, 2 Sep 2014 11:06:11 +0000 (13:06 +0200)
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
src/bin/psql/copy.c
src/bin/psql/mainloop.c
src/bin/psql/prompt.c
src/bin/psql/settings.h

index 74d46183e51b344bb7a0b5a1aaf04a1bf79fa4cf..db314c326fd104708d3654d983087b050d6ab3e7 100644 (file)
@@ -3315,6 +3315,15 @@ testdb=&gt; <userinput>INSERT INTO my_table VALUES (:'content');</userinput>
         </listitem>
       </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>
index 4b749154adf951c3e02c6d3152f0ecf2d85373fd..90f4a24fa5dc2e8d9e5a45babe3311705ee18a90 100644 (file)
@@ -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++;
+                       }
                }
        }
 
index c3aff208bf18192ea3de698cd53cba03bbea2cab..98211dcb2a7365bc2380d22ab36b803009c94338 100644 (file)
@@ -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;
 
@@ -235,6 +240,22 @@ MainLoop(FILE *source)
                                exit(EXIT_FAILURE);
                        }
 
+                       /*
+                        * Increase statement line number counter for each linebreak added
+                        * to the query buffer by the last psql_scan() call. There only
+                        * will be ones to add when navigating to a statement in
+                        * readline's history containing 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)
index 26fca04756daabbcef52da924ce75133b1741c46..f2db9a97bc53ac7ab3dfe2c724f55984eac27d1c 100644 (file)
@@ -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;
index 453d6c889df12864808f88fe6ea131a57276dcaa..ef24a4ef985dc0f4a3cfdde34c3c9b8e60c53f37 100644 (file)
@@ -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 */