Hi,

here's a review of the \sf and \ef [num] patch from
http://archives.postgresql.org/message-id/162867791003290927y3ca44051p80e697bc6b19d...@mail.gmail.com

== Formatting ==

The patch has some small tabs/spaces and whitespace issues and it applies with some offsets, I ran pgindent and rebased against HEAD, attaching the resulting patch for your convenience.

== Functionality ==

The patch adds the following features:
* \e file.txt num -> starts a editor for the current query buffer and puts the cursor on the [num] line * \ef func num -> starts a editor for a function and puts the cursor on the [num] line
 * \sf func -> shows a full CREATE FUNCTION statement for the function
 * \sf+ func -> the same, but with line numbers
 * \sf[+] func num -> the same, but only from line num onward

It only touches psql, so no performance or backend stability worries.

In my humble opinion, only the \sf[+] is interesting, because it gives you a copy/pasteable version of the function definition without opening up an editor, and I can find that useful (OTOH: you can set PSQL_EDITOR to cat and get the same effect with \ef... ok, just joking). Line numbers are an extra touch, personally it does not thrill me too much, but I've nothing against it.

The number variants of \e and \ef work by simply executing $EDITOR +num file. I tried with some editors that came to my mind, and not all of them support it (most do, though):

 * emacs and emacsclient work
 * vi works
 * nano works
 * pico works
 * mcedit works
 * kwrite does not work
 * kedit does not work

not sure what other people (or for instance Windows people) use. Apart from no universal support from editors, it does not save that many keystrokes - at most a couple. In the end you can usually easily jump to the line you want once you are inside your dream editor.

My recommendation would be to only integrate the \sf[+] part of the patch, which will have the additional benefit of making it much smaller and cleaner (will avoid the grotty splitting of the number from the function name, for instance). But I'm just another user out there, maybe others will find uses for the other cases.

I would personally not add the leading and trailing newlines to \sf output, but that's a question of taste.

Docs could use some small grammar fixes, but other than that they're fine.

== Code ==

In \sf code there just a strncmp, so this works:
\sfblablabla funcname

The error for an empty \sf is not great, it should probably look more like
\sf: missing required argument
following the examples of \pset, \copy or \prompt.

Why is lnptr always being passed as a pointer? Looks like a unnecessary complication and one more variable to care about. Can't we just pass lineno?

== End ==

Cheers,
Jan
*** doc/src/sgml/ref/psql-ref.sgml
--- /tmp/CUVdHd_psql-ref.sgml	2010-07-16 13:31:53.362662393 +0200
***************
*** 1329,1335 ****
  
  
        <varlistentry>
!         <term><literal>\edit</literal> (or <literal>\e</literal>) <literal><optional> <replaceable class="parameter">filename</replaceable> </optional></literal></term>
  
          <listitem>
          <para>
--- 1329,1335 ----
  
  
        <varlistentry>
!         <term><literal>\edit</literal> (or <literal>\e</literal>) <literal><optional> <replaceable class="parameter">filename</replaceable> </optional> <optional> linenumber </optional></literal></term>
  
          <listitem>
          <para>
***************
*** 1359,1370 ****
          systems, <filename>notepad.exe</filename> on Windows systems.
          </para>
          </tip>
          </listitem>
        </varlistentry>
  
  
        <varlistentry>
!         <term><literal>\ef <optional> <replaceable class="parameter">function_description</replaceable> </optional></literal></term>
  
          <listitem>
          <para>
--- 1359,1376 ----
          systems, <filename>notepad.exe</filename> on Windows systems.
          </para>
          </tip>
+ 
+         <para>
+         If <replaceable class="parameter">linenumber</replaceable> is
+         specified, then cursor is moved on this line after start of 
+         editor.
+         </para>
          </listitem>
        </varlistentry>
  
  
        <varlistentry>
!         <term><literal>\ef <optional> <replaceable class="parameter">function_description</replaceable> </optional> <optional> linenumber </optional> </literal></term>
  
          <listitem>
          <para>
***************
*** 1387,1392 ****
--- 1393,1405 ----
           If no function is specified, a blank <command>CREATE FUNCTION</>
           template is presented for editing.
          </para>
+ 
+         <para>
+         If <replaceable class="parameter">linenumber</replaceable> is
+         specified, then cursor is moved on this line after start of 
+         editor. It count lines from start of function body, not from
+         start of text.
+         </para>
          </listitem>
        </varlistentry>
  
***************
*** 2106,2111 ****
--- 2119,2136 ----
  
  
        <varlistentry>
+         <term><literal>\sf[+] <replaceable class="parameter">function_description</replaceable> <optional> linenumber </optional> </literal></term>
+ 
+         <listitem>
+         <para>
+          This command fetches and shows the definition of the named function,
+          in the form of a <command>CREATE OR REPLACE FUNCTION</> command.
+          If the form <literal>\sf+</literal> is used, then lines are numbered.
+         </para>
+         </listitem>
+       </varlistentry>
+ 
+       <varlistentry>
          <term><literal>\t</literal></term>
          <listitem>
          <para>
***************
*** 2113,2118 ****
--- 2138,2149 ----
          footer. This command is equivalent to <literal>\pset
          tuples_only</literal> and is provided for convenience.
          </para>
+ 
+         <para>
+         If <replaceable class="parameter">linenumber</replaceable> is
+         specified, then cursor is moved on this line after start of 
+         editor.
+         </para>
          </listitem>
        </varlistentry>
  
*** src/bin/psql/command.c
--- /tmp/uM1Twe_command.c	2010-07-16 13:31:53.366666075 +0200
***************
*** 57,63 ****
  			 PsqlScanState scan_state,
  			 PQExpBuffer query_buf);
  static bool do_edit(const char *filename_arg, PQExpBuffer query_buf,
! 		bool *edited);
  static bool do_connect(char *dbname, char *user, char *host, char *port);
  static bool do_shell(const char *command);
  static bool lookup_function_oid(PGconn *conn, const char *desc, Oid *foid);
--- 57,63 ----
  			 PsqlScanState scan_state,
  			 PQExpBuffer query_buf);
  static bool do_edit(const char *filename_arg, PQExpBuffer query_buf,
! 		bool *edited, int *lineno);
  static bool do_connect(char *dbname, char *user, char *host, char *port);
  static bool do_shell(const char *command);
  static bool lookup_function_oid(PGconn *conn, const char *desc, Oid *foid);
***************
*** 488,500 ****
  		else
  		{
  			char	   *fname;
  
  			fname = psql_scan_slash_option(scan_state,
  										   OT_NORMAL, NULL, true);
  			expand_tilde(&fname);
  			if (fname)
  				canonicalize_path(fname);
! 			if (do_edit(fname, query_buf, NULL))
  				status = PSQL_CMD_NEWEDIT;
  			else
  				status = PSQL_CMD_ERROR;
--- 488,517 ----
  		else
  		{
  			char	   *fname;
+ 			char	   *lineno;
+ 			int			ln,
+ 					   *lnptr = NULL;
+ 
  
  			fname = psql_scan_slash_option(scan_state,
  										   OT_NORMAL, NULL, true);
+ 
+ 			/* try to get lineno */
+ 			if (fname)
+ 			{
+ 				lineno = psql_scan_slash_option(scan_state,
+ 												OT_NORMAL, NULL, true);
+ 				if (lineno)
+ 				{
+ 					ln = atoi(lineno);
+ 					lnptr = &ln;
+ 				}
+ 			}
+ 
  			expand_tilde(&fname);
  			if (fname)
  				canonicalize_path(fname);
! 			if (do_edit(fname, query_buf, NULL, lnptr))
  				status = PSQL_CMD_NEWEDIT;
  			else
  				status = PSQL_CMD_ERROR;
***************
*** 508,513 ****
--- 525,533 ----
  	 */
  	else if (strcmp(cmd, "ef") == 0)
  	{
+ 		int			lineno;
+ 		int		   *lnptr = NULL;
+ 
  		if (!query_buf)
  		{
  			psql_error("no query buffer\n");
***************
*** 520,525 ****
--- 540,574 ----
  
  			func = psql_scan_slash_option(scan_state,
  										  OT_WHOLE_LINE, NULL, true);
+ 
+ 			/*
+ 			 * parse linenumber from the right
+ 			 */
+ 			if (func && *func)
+ 			{
+ 				char	   *endfunc = func + strlen(func) - 1;
+ 				char	   *c;
+ 
+ 				c = endfunc;
+ 				while (c >= func)
+ 					if (!isdigit(*c))
+ 						break;
+ 					else
+ 						c--;
+ 
+ 				if (c < endfunc && c > func)
+ 				{
+ 					if (*c == ' ' || *c == ')')
+ 					{
+ 						c++;
+ 						/* append rows for SQL decoration */
+ 						lineno = atoi(c) + 3;
+ 						*c = '\0';
+ 						lnptr = &lineno;
+ 					}
+ 				}
+ 			}
+ 
  			if (!func)
  			{
  				/* set up an empty command to fill in */
***************
*** 549,555 ****
  		{
  			bool		edited = false;
  
! 			if (!do_edit(0, query_buf, &edited))
  				status = PSQL_CMD_ERROR;
  			else if (!edited)
  				puts(_("No changes"));
--- 598,604 ----
  		{
  			bool		edited = false;
  
! 			if (!do_edit(0, query_buf, &edited, lnptr))
  				status = PSQL_CMD_ERROR;
  			else if (!edited)
  				puts(_("No changes"));
***************
*** 944,949 ****
--- 993,1103 ----
  		free(fname);
  	}
  
+ 	/*
+ 	 * \sf -- show the named function
+ 	 */
+ 	else if (strncmp(cmd, "sf", 2) == 0)
+ 	{
+ 		int			lineno;
+ 		int		   *lnptr = NULL;
+ 		bool		with_lno = false;
+ 
+ 		if (strcmp(cmd, "sf+") == 0)
+ 			with_lno = true;
+ 
+ 		if (!query_buf)
+ 		{
+ 			psql_error("no query buffer\n");
+ 			status = PSQL_CMD_ERROR;
+ 		}
+ 		else
+ 		{
+ 			char	   *func;
+ 			Oid			foid = InvalidOid;
+ 
+ 			func = psql_scan_slash_option(scan_state,
+ 										  OT_WHOLE_LINE, NULL, true);
+ 
+ 			/*
+ 			 * parse linenumber from the right
+ 			 */
+ 			if (func && *func)
+ 			{
+ 				char	   *endfunc = func + strlen(func) - 1;
+ 				char	   *c;
+ 
+ 				c = endfunc;
+ 				while (c >= func)
+ 					if (!isdigit(*c))
+ 						break;
+ 					else
+ 						c--;
+ 
+ 				if (c < endfunc && c > func)
+ 				{
+ 					if (*c == ' ' || *c == ')')
+ 					{
+ 						c++;
+ 						/* append rows for SQL decoration */
+ 						lineno = atoi(c) + 3;
+ 						*c = '\0';
+ 						lnptr = &lineno;
+ 					}
+ 				}
+ 			}
+ 
+ 			if (!func)
+ 			{
+ 				/* show error for empty command */
+ 				psql_error("missing a function name\n");
+ 				status = PSQL_CMD_ERROR;
+ 			}
+ 			else if (!lookup_function_oid(pset.db, func, &foid))
+ 			{
+ 				/* error already reported */
+ 				status = PSQL_CMD_ERROR;
+ 			}
+ 			else if (!get_create_function_cmd(pset.db, foid, query_buf))
+ 			{
+ 				/* error already reported */
+ 				status = PSQL_CMD_ERROR;
+ 			}
+ 			if (func)
+ 				free(func);
+ 		}
+ 
+ 		if (status != PSQL_CMD_ERROR)
+ 		{
+ 			int			lineno = 0;
+ 			char	   *c = query_buf->data;
+ 			char	   *ptr;
+ 
+ 			printf("\n");
+ 			while (*c)
+ 			{
+ 				for (ptr = c; *c != '\n'; c++);
+ 				*c++ = '\0';
+ 				lineno++;
+ 
+ 				if (lnptr && (*lnptr > lineno))
+ 					continue;
+ 
+ 				if (!with_lno)
+ 					printf("%s\n", ptr);
+ 				else
+ 				{
+ 					/* don't show lineno for first three rows and last row */
+ 					if ((*c == '\0' && lineno != 3) || lineno < 4)
+ 						printf("****  %s\n", ptr);
+ 					else
+ 						printf("%4d  %s\n", lineno - 3, ptr);
+ 				}
+ 			}
+ 			printf("\n");
+ 			fflush(stdout);
+ 		}
+ 	}
+ 
  	/* \set -- generalized set variable/option command */
  	else if (strcmp(cmd, "set") == 0)
  	{
***************
*** 1517,1523 ****
   */
  
  static bool
! editFile(const char *fname)
  {
  	const char *editorName;
  	char	   *sys;
--- 1671,1677 ----
   */
  
  static bool
! editFile(const char *fname, int *lineno)
  {
  	const char *editorName;
  	char	   *sys;
***************
*** 1541,1551 ****
  	 * severe brain damage in their command shell plus the fact that standard
  	 * program paths include spaces.
  	 */
! 	sys = pg_malloc(strlen(editorName) + strlen(fname) + 10 + 1);
  #ifndef WIN32
! 	sprintf(sys, "exec %s '%s'", editorName, fname);
  #else
! 	sprintf(sys, SYSTEMQUOTE "\"%s\" \"%s\"" SYSTEMQUOTE, editorName, fname);
  #endif
  	result = system(sys);
  	if (result == -1)
--- 1695,1714 ----
  	 * severe brain damage in their command shell plus the fact that standard
  	 * program paths include spaces.
  	 */
! 	sys = pg_malloc(strlen(editorName) + strlen(fname) + 20 + 1);
  #ifndef WIN32
! 	if (!lineno)
! 		sprintf(sys, "exec %s '%s'", editorName, fname);
! 	else
! 		sprintf(sys, "exec %s +%d '%s'", editorName, *lineno, fname);
  #else
! 	if (!lineno)
! 		sprintf(sys, SYSTEMQUOTE "\"%s\" \"%s\"" SYSTEMQUOTE, editorName, fname);
! 	else
! 		sprintf(sys, SYSTEMQUOTE "\"%s\" +%d \"%s\"" SYSTEMQUOTE,
! 				editorName,
! 				*lineno,
! 				fname);
  #endif
  	result = system(sys);
  	if (result == -1)
***************
*** 1560,1566 ****
  
  /* call this one */
  static bool
! do_edit(const char *filename_arg, PQExpBuffer query_buf, bool *edited)
  {
  	char		fnametmp[MAXPGPATH];
  	FILE	   *stream = NULL;
--- 1723,1729 ----
  
  /* call this one */
  static bool
! do_edit(const char *filename_arg, PQExpBuffer query_buf, bool *edited, int *lineno)
  {
  	char		fnametmp[MAXPGPATH];
  	FILE	   *stream = NULL;
***************
*** 1652,1658 ****
  
  	/* call editor */
  	if (!error)
! 		error = !editFile(fname);
  
  	if (!error && stat(fname, &after) != 0)
  	{
--- 1815,1821 ----
  
  	/* call editor */
  	if (!error)
! 		error = !editFile(fname, lineno);
  
  	if (!error && stat(fname, &after) != 0)
  	{
*** src/bin/psql/help.c
--- /tmp/g3JMge_help.c	2010-07-16 13:31:53.370666106 +0200
***************
*** 174,186 ****
  	fprintf(output, "\n");
  
  	fprintf(output, _("Query Buffer\n"));
! 	fprintf(output, _("  \\e [FILE]              edit the query buffer (or file) with external editor\n"));
! 	fprintf(output, _("  \\ef [FUNCNAME]         edit function definition with external editor\n"));
  	fprintf(output, _("  \\p                     show the contents of the query buffer\n"));
  	fprintf(output, _("  \\r                     reset (clear) the query buffer\n"));
  #ifdef USE_READLINE
  	fprintf(output, _("  \\s [FILE]              display history or save it to file\n"));
  #endif
  	fprintf(output, _("  \\w FILE                write query buffer to file\n"));
  	fprintf(output, "\n");
  
--- 174,187 ----
  	fprintf(output, "\n");
  
  	fprintf(output, _("Query Buffer\n"));
! 	fprintf(output, _("  \\e [FILE] [lno]        edit the query buffer (or file) with external editor\n"));
! 	fprintf(output, _("  \\ef [FUNCNAME] [lno]   edit function definition with external editor\n"));
  	fprintf(output, _("  \\p                     show the contents of the query buffer\n"));
  	fprintf(output, _("  \\r                     reset (clear) the query buffer\n"));
  #ifdef USE_READLINE
  	fprintf(output, _("  \\s [FILE]              display history or save it to file\n"));
  #endif
+ 	fprintf(output, _("  \\sf[+] FUNCNAME [lno]  show finction definition\n"));
  	fprintf(output, _("  \\w FILE                write query buffer to file\n"));
  	fprintf(output, "\n");
  
*** src/bin/psql/tab-complete.c
--- /tmp/ERVkIe_tab-complete.c	2010-07-16 13:31:53.370666106 +0200
***************
*** 644,650 ****
  		"\\f", "\\g", "\\h", "\\help", "\\H", "\\i", "\\l",
  		"\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
  		"\\o", "\\p", "\\password", "\\prompt", "\\pset", "\\q", "\\qecho", "\\r",
! 		"\\set", "\\t", "\\T",
  		"\\timing", "\\unset", "\\x", "\\w", "\\z", "\\!", NULL
  	};
  
--- 644,650 ----
  		"\\f", "\\g", "\\h", "\\help", "\\H", "\\i", "\\l",
  		"\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
  		"\\o", "\\p", "\\password", "\\prompt", "\\pset", "\\q", "\\qecho", "\\r",
! 		"\\set", "\\sf", "\\t", "\\T",
  		"\\timing", "\\unset", "\\x", "\\w", "\\z", "\\!", NULL
  	};
  
***************
*** 2502,2507 ****
--- 2502,2510 ----
  	else if (strcmp(prev_wd, "\\ef") == 0)
  		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
  
+ 	else if (strncmp(prev_wd, "\\sf", 2) == 0)
+ 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
+ 
  	else if (strcmp(prev_wd, "\\encoding") == 0)
  		COMPLETE_WITH_QUERY(Query_for_list_of_encodings);
  	else if (strcmp(prev_wd, "\\h") == 0 || strcmp(prev_wd, "\\help") == 0)
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to