diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index b803ef9..85adf55 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1338,7 +1338,7 @@ testdb=&gt;
 
 
       <varlistentry>
-        <term><literal>\edit</literal> (or <literal>\e</literal>) <literal><optional> <replaceable class="parameter">filename</replaceable> </optional></literal></term>
+        <term><literal>\edit</literal> (or <literal>\e</literal>) <literal><optional> <replaceable class="parameter">filename</replaceable> </optional> <optional> linenumber </optional></literal></term>
 
         <listitem>
         <para>
@@ -1368,12 +1368,19 @@ testdb=&gt;
         systems, <filename>notepad.exe</filename> on Windows systems.
         </para>
         </tip>
+
+        <para>
+        If a line number is specified, <application>psql</application> will
+        attempt to position the cursor on the specified line of the file.
+        An error will occur if <varname>EDITOR_LINENUMBER_SWITCH</varname> 
+        is not set.
+        </para>
         </listitem>
       </varlistentry>
 
 
       <varlistentry>
-        <term><literal>\ef <optional> <replaceable class="parameter">function_description</replaceable> </optional></literal></term>
+        <term><literal>\ef <optional> <replaceable class="parameter">function_description</replaceable> </optional> <optional> linenumber </optional> </literal></term>
 
         <listitem>
         <para>
@@ -1396,6 +1403,14 @@ testdb=&gt;
          If no function is specified, a blank <command>CREATE FUNCTION</>
          template is presented for editing.
         </para>
+
+        <para>
+        If a line number is specified, <application>psql</application> will
+        attempt to position the cursor on the specified line of the function
+        body (note that the function body typically does not begin on the
+        first line of the file).  An error will occur if
+        <varname>EDITOR_LINENUMBER_SWITCH</varname> is not set.
+        </para>
         </listitem>
       </varlistentry>
 
@@ -2458,6 +2473,25 @@ bar
       </varlistentry>
 
       <varlistentry>
+        <term><varname>EDITOR_LINENUMBER_SWITCH</varname></term>
+        <listitem>
+        <para>
+        When <command>\edit</command> or <command>\ef</command> is used with
+        a line number argument, this variable specifies the switch used to
+        pass the line number to the user's editor.  For editors such as
+        <productname>emacs</> or <productname>vi</>, you can simply set this
+        variable to a single plus sign.  In some cases, it might be necessary
+        to include a trailing space in the value assigned to this variable.
+        For example:
+
+<programlisting>
+\set EDIT_LINENUMBER_SWITCH '--line '
+</programlisting>
+        </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
         <term><varname>ENCODING</varname></term>
         <listitem>
         <para>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index a3e55c5..697a906 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -57,7 +57,7 @@ static backslashResult exec_command(const char *cmd,
 			 PsqlScanState scan_state,
 			 PQExpBuffer query_buf);
 static bool do_edit(const char *filename_arg, PQExpBuffer query_buf,
-		bool *edited);
+		int lineno, 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);
@@ -66,6 +66,9 @@ static void minimal_error_message(PGresult *res);
 
 static void printSSLInfo(void);
 
+static int strip_lineno_from_funcdesc(char *func);
+static char *get_functiondef_dollarquote_tag(char *line);
+
 #ifdef WIN32
 static void checkWin32Codepage(void);
 #endif
@@ -510,17 +513,39 @@ exec_command(const char *cmd,
 		else
 		{
 			char	   *fname;
+			char	   *ln;
+			int			lineno = -1;
 
 			fname = psql_scan_slash_option(scan_state,
 										   OT_NORMAL, NULL, true);
-			expand_tilde(&fname);
+
+			/* try to get lineno */
 			if (fname)
-				canonicalize_path(fname);
-			if (do_edit(fname, query_buf, NULL))
-				status = PSQL_CMD_NEWEDIT;
-			else
-				status = PSQL_CMD_ERROR;
-			free(fname);
+			{
+				ln = psql_scan_slash_option(scan_state,
+									   OT_NORMAL, NULL, true);
+				if (ln)
+				{
+					lineno = atoi(ln);
+					if (lineno < 1)
+					{
+						psql_error("invalid line number\n");
+						status = PSQL_CMD_ERROR;
+					}
+				}
+			}
+			if (status != PSQL_CMD_ERROR)
+			{
+				expand_tilde(&fname);
+				if (fname)
+					canonicalize_path(fname);
+				if (do_edit(fname, query_buf, lineno, NULL))
+					status = PSQL_CMD_NEWEDIT;
+				else
+					status = PSQL_CMD_ERROR;
+			}
+			if (fname)
+				free(fname);
 		}
 	}
 
@@ -530,6 +555,8 @@ exec_command(const char *cmd,
 	 */
 	else if (strcmp(cmd, "ef") == 0)
 	{
+		int	lineno = -1;		/* keep compiler quiet */
+
 		if (!query_buf)
 		{
 			psql_error("no query buffer\n");
@@ -542,7 +569,13 @@ exec_command(const char *cmd,
 
 			func = psql_scan_slash_option(scan_state,
 										  OT_WHOLE_LINE, NULL, true);
-			if (!func)
+			lineno = strip_lineno_from_funcdesc(func);
+			if (lineno == 0)
+			{
+				/* error already reported */
+				status = PSQL_CMD_ERROR;
+			}
+			else if (!func)
 			{
 				/* set up an empty command to fill in */
 				printfPQExpBuffer(query_buf,
@@ -571,7 +604,7 @@ exec_command(const char *cmd,
 		{
 			bool		edited = false;
 
-			if (!do_edit(0, query_buf, &edited))
+			if (!do_edit(0, query_buf, lineno, &edited))
 				status = PSQL_CMD_ERROR;
 			else if (!edited)
 				puts(_("No changes"));
@@ -1543,11 +1576,11 @@ UnsyncVariables(void)
  * If you do not specify a filename, the current query buffer will be copied
  * into a temporary one.
  */
-
 static bool
-editFile(const char *fname)
+editFile(const char *fname, int lineno)
 {
 	const char *editorName;
+	const char *editor_lineno_switch = NULL;	/* be compiler quiet */
 	char	   *sys;
 	int			result;
 
@@ -1562,6 +1595,25 @@ editFile(const char *fname)
 	if (!editorName)
 		editorName = DEFAULT_EDITOR;
 
+	/* Get line number switch, if we need it. */
+	if (lineno > 0)
+	{
+		editor_lineno_switch = GetVariable(pset.vars,
+										   "EDITOR_LINENUMBER_SWITCH");
+		if (editor_lineno_switch == NULL)
+		{
+			psql_error("EDITOR_LINENUMBER_SWITCH variable is not set\n");
+			return false;
+		}
+	}
+
+	/* Allocate sufficient memory for command line. */
+	if (lineno > 1)
+		sys = pg_malloc(strlen(editorName) + strlen(editor_lineno_switch) + 10
+						+ 1 + strlen(fname) + 10 + 1);
+	else
+		sys = pg_malloc(strlen(editorName) + strlen(fname) + 10 + 1);
+
 	/*
 	 * On Unix the EDITOR value should *not* be quoted, since it might include
 	 * switches, eg, EDITOR="pico -t"; it's up to the user to put quotes in it
@@ -1569,11 +1621,20 @@ editFile(const char *fname)
 	 * 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);
+	if (lineno > 0)
+		sprintf(sys, "exec %s %s%d '%s'", editorName, editor_lineno_switch, lineno, fname);
+	else
+		sprintf(sys, "exec %s '%s'", editorName, fname);
 #else
-	sprintf(sys, SYSTEMQUOTE "\"%s\" \"%s\"" SYSTEMQUOTE, editorName, fname);
+	if (lineno > 0)
+		sprintf(sys, SYSTEMQUOTE "\"%s\" %s%d \"%s\"" SYSTEMQUOTE, 
+									    editorName, 
+									    editor_lineno_switch,
+									    lineno, 
+									    fname);
+	else
+		sprintf(sys, SYSTEMQUOTE "\"%s\" \"%s\"" SYSTEMQUOTE, editorName, fname);
 #endif
 	result = system(sys);
 	if (result == -1)
@@ -1588,7 +1649,7 @@ editFile(const char *fname)
 
 /* call this one */
 static bool
-do_edit(const char *filename_arg, PQExpBuffer query_buf, bool *edited)
+do_edit(const char *filename_arg, PQExpBuffer query_buf, int lineno, bool *edited)
 {
 	char		fnametmp[MAXPGPATH];
 	FILE	   *stream = NULL;
@@ -1678,9 +1739,34 @@ do_edit(const char *filename_arg, PQExpBuffer query_buf, bool *edited)
 		error = true;
 	}
 
+	/* adjust line number based on start of actual function body */
+	if (!error && lineno != -1)
+	{
+		char   *lines = query_buf->data;
+		char	*dqtag;
+
+		/* skip header lines */
+		while (*lines != '\0')
+		{
+			dqtag = get_functiondef_dollarquote_tag(lines);
+			if (dqtag)
+			{
+				free(dqtag);
+				break;
+			}
+			lineno++;
+
+			/* find start of next line */
+			lines = strchr(lines, '\n');
+			if (!lines)
+				break;
+			lines++;
+		}
+	}
+
 	/* call editor */
 	if (!error)
-		error = !editFile(fname);
+		error = !editFile(fname, lineno);
 
 	if (!error && stat(fname, &after) != 0)
 	{
@@ -2236,3 +2322,75 @@ minimal_error_message(PGresult *res)
 
 	destroyPQExpBuffer(msg);
 }
+
+
+/*
+ * Strips an optional trailing line number off of a backslash command.
+ *
+ * Retuns -1 if no line number is present, 0 on error, or a positive value
+ * otherwise.
+ */
+static int
+strip_lineno_from_funcdesc(char *func)
+{
+	char	   *endfunc;
+	char	   *c;
+	int			lineno;
+
+	if (!func)
+		return -1;
+	endfunc = func + strlen(func) - 1;
+	c = endfunc;
+
+	/* skip trailing whitespace */
+	while (c >= func && isblank(*c))
+		c--;
+	if (c == func || !isdigit(*c))
+		return -1;
+
+	/* find start of digit string */
+	while (c >= func && isdigit(*c))
+		c--;
+
+	/* digits must be separated from identifier by blank or closing paren */
+	if (c == func || (!isblank(*c) && *c != ')'))
+		return -1;
+
+	/* parse digit string */
+	lineno = atoi(c + 1);
+	if (lineno < 1)
+	{
+		psql_error("invalid line number\n");
+		return 0;
+	}
+	c[1] = '\0';
+	return lineno;
+}
+
+/*
+ * Returns tag of dollar quoted string used as function body.  We assume
+ * that the function body is returned by pg_get_functiondef() and therefore
+ * must be begin on a line that starts with "AS $function$".  If the line is
+ * not of that format, we return NULL.
+ */
+static char *
+get_functiondef_dollarquote_tag(char *line)
+{
+	char	   *starttag;
+	char	   *endtag;
+	int			len;
+	char	   *result;
+
+	if (strncmp(line, "AS $function", 12) != 0)
+		return NULL;
+	starttag = line + 3;
+	endtag = strchr(line + 12, '$');
+	psql_assert(endtag != NULL);
+
+	len = endtag - starttag + 1;
+	result = pg_malloc(len + 1);
+	memcpy(result, starttag, len);
+	result[len] = '\0';
+
+	return result;
+}
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 19c807d..6a00a1f 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -162,7 +162,7 @@ slashUsage(unsigned short int pager)
 {
 	FILE	   *output;
 
-	output = PageOutput(87, pager);
+	output = PageOutput(89, pager);
 
 	/* if you add/remove a line here, change the row count above */
 
@@ -174,8 +174,8 @@ slashUsage(unsigned short int pager)
 	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, _("  \\e [FILE] [LINE]       edit the query buffer (or file) with external editor\n"));
+	fprintf(output, _("  \\ef [FUNCNAME] [LINE]  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
