Index: doc/src/sgml/ref/psql-ref.sgml
===================================================================
RCS file: /projects/cvsroot/pgsql/doc/src/sgml/ref/psql-ref.sgml,v
retrieving revision 1.199
diff -c -B -r1.199 psql-ref.sgml
*** doc/src/sgml/ref/psql-ref.sgml	30 Mar 2008 18:10:20 -0000	1.199
--- doc/src/sgml/ref/psql-ref.sgml	1 Apr 2008 14:05:05 -0000
***************
*** 685,690 ****
--- 685,708 ----
        </varlistentry>
  
        <varlistentry>
+         <term><literal>\alias <replaceable class="parameter">aliasname</replaceable> <replaceable class="parameter">aliascommand</replaceable></literal></term>
+         <listitem>
+         <para>
+         The <literal>\alias</literal> backslash command allows defining your own backslash commands or an alias for an existing built-in command. The alias is allowed to call a SQL command or any other backslash command or aliases already defined. For example:
+ <programlisting>
+ =&gt; <userinput>\alias watch_idle_tx SELECT xact_start, datid, datname, procpid, usesysid FROM pg_stat_activity WHERE current_query LIKE '%IDLE%';</userinput>
+ =&gt; <userinput>\watch_idle_tx</userinput>
+           xact_start           | datid | datname | procpid | usesysid
+ -------------------------------+-------+---------+---------+----------
+  2008-03-07 14:59:25.618827+01 | 24576 | db      |   28864 |       10
+ (1 row)
+ </programlisting>
+         Calling <command>\alias</command> without an <replaceable class="parameter">aliasname</replaceable> just shows a list of all defined aliases, with an existing <replaceable class="parameter">aliasname</replaceable> its assigned <replaceable class="parameter">aliascommand</replaceable> is shown. You can use the <command>\unalias</command> to remove an alias.
+         </para>
+         </listitem>
+       </varlistentry>
+ 
+       <varlistentry>
         <term><literal>\cd [ <replaceable>directory</replaceable> ]</literal></term>
         <listitem>
          <para>
***************
*** 1849,1854 ****
--- 1867,1880 ----
         </listitem>
        </varlistentry>
  
+       <varlistentry>
+         <term><literal>\unalias <replaceable class="parameter">aliascmd</replaceable></literal></term>
+         <listitem>
+         <para>
+         Removes the specified alias from the list.
+         </para>
+         </listitem>
+       </varlistentry>
  
        <varlistentry>
          <term><literal>\w</literal> {<replaceable class="parameter">filename</replaceable> | <replaceable class="parameter">|command</replaceable>}</term>
Index: src/bin/psql/command.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/bin/psql/command.c,v
retrieving revision 1.186
diff -c -B -r1.186 command.c
*** src/bin/psql/command.c	1 Jan 2008 19:45:55 -0000	1.186
--- src/bin/psql/command.c	1 Apr 2008 14:05:06 -0000
***************
*** 199,204 ****
--- 199,251 ----
  	backslashResult status = PSQL_CMD_SKIP_LINE;
  
  	/*
+ 	 * We want to look for aliases first, since we want them to "override" a
+ 	 * built-in command, so our users are able to define their "own" behavior
+ 	 * on their favourite backslash command.
+ 	 */
+ 	const char *aliascmd = GetVariable(pset.aliases, cmd);
+
+ 	if (aliascmd)
+ 	{
+ 		/* Save the current alias substitution string.
+ 		 * We do this to prevent infinite recursion into the aliases
+ 		 * list.
+ 		 */
+ 		const char *substalias = GetVariable(pset.substaliases, cmd);
+ 
+ 		if (substalias != NULL)
+ 		{
+ 			/* already substituted, nothing more to do */
+ 			DeleteVariable(pset.substaliases, cmd);
+ 		}
+ 		else
+ 		{
+ 			char *opt;
+ 			SetVariable(pset.substaliases, cmd, "1");
+ 
+ 			/* save the substituted command to the query buffer but save
+ 			 * additional parameter lists also
+ 			 */
+ 			resetPQExpBuffer(query_buf);
+ 			appendPQExpBuffer(query_buf, aliascmd);
+ 
+ 			if ((opt = psql_scan_slash_option(scan_state,
+ 											  OT_WHOLE_LINE, NULL, false)))
+ 			{
+ 				appendPQExpBuffer(query_buf, " ");
+ 				appendPQExpBuffer(query_buf, opt);
+ 			}
+ 
+ 			success = true;
+ 			return (status = PSQL_CMD_ALIAS);
+ 		}
+ 
+ 	}
+ 
+ 	/* clear recursive alias list */
+ 	ClearVariableSpace(pset.substaliases);
+ 
+ 	/*
  	 * \a -- toggle field alignment This makes little sense but we keep it
  	 * around.
  	 */
***************
*** 1037,1042 ****
--- 1084,1143 ----
  	else if (strcmp(cmd, "?") == 0)
  		slashUsage(pset.popt.topt.pager);
  
+ 	else if (strcmp(cmd, "alias") == 0)
+ 	{
+ 		char *aliasname = psql_scan_slash_option(scan_state,
+ 												 OT_NORMAL, NULL, false);
+ 		char *aliascmd  = psql_scan_slash_option(scan_state,
+ 												 OT_WHOLE_LINE, NULL, false);
+ 
+ 		/* if no aliasname was specified, then print the whole alias list */
+ 		if (!aliasname)
+ 		{
+ 			PrintVariables(pset.aliases);
+ 			success = true;
+ 		}
+ 		else
+ 		{
+ 			/* check wether to assign or to display the alias cmd */
+ 
+ 			if (!aliascmd)
+ 			{
+ 				const char *cmd = GetVariable(pset.aliases, aliasname);
+ 				if (cmd)
+ 				{
+ 					puts(_(cmd));
+ 					success = true;
+ 				}
+ 				else
+ 				{
+ 					psql_error(_("Unknown alias: \"%s\"\n"), aliasname);
+ 					success = false;
+ 				}
+ 			}
+ 			else
+ 			{
+ 				success = SetVariable(pset.aliases, aliasname, aliascmd);
+ 			}
+ 
+ 		}
+ 
+ 	}
+ 	else if (strcmp(cmd, "unalias") == 0)
+ 	{
+ 		char *aliasname = psql_scan_slash_option(scan_state,
+ 												 OT_NORMAL, NULL, false);
+ 		if (aliasname)
+ 		{
+ 			success = DeleteVariable(pset.aliases, aliasname);
+ 		}
+ 		else
+ 		{
+ 			psql_error(_("Unknown alias: \"%s\"\n"), aliasname);
+ 			success = false;
+ 		}
+ 	}
+ 
  #if 0
  
  	/*
Index: src/bin/psql/command.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/bin/psql/command.h,v
retrieving revision 1.30
diff -c -B -r1.30 command.h
*** src/bin/psql/command.h	1 Jan 2008 19:45:55 -0000	1.30
--- src/bin/psql/command.h	1 Apr 2008 14:05:06 -0000
***************
*** 19,26 ****
  	PSQL_CMD_SKIP_LINE,			/* keep building query */
  	PSQL_CMD_TERMINATE,			/* quit program */
  	PSQL_CMD_NEWEDIT,			/* query buffer was changed (e.g., via \e) */
! 	PSQL_CMD_ERROR				/* the execution of the backslash command
  								 * resulted in an error */
  } backslashResult;
  
  
--- 19,27 ----
  	PSQL_CMD_SKIP_LINE,			/* keep building query */
  	PSQL_CMD_TERMINATE,			/* quit program */
  	PSQL_CMD_NEWEDIT,			/* query buffer was changed (e.g., via \e) */
! 	PSQL_CMD_ERROR,				/* the execution of the backslash command
  								 * resulted in an error */
+ 	PSQL_CMD_ALIAS              /* query buffer was changed by command alias */
  } backslashResult;
  
  
Index: src/bin/psql/mainloop.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/bin/psql/mainloop.c,v
retrieving revision 1.87
diff -c -B -r1.87 mainloop.c
*** src/bin/psql/mainloop.c	1 Jan 2008 19:45:56 -0000	1.87
--- src/bin/psql/mainloop.c	1 Apr 2008 14:05:06 -0000
***************
*** 266,273 ****
  
  				success = slashCmdStatus != PSQL_CMD_ERROR;
  
! 				if ((slashCmdStatus == PSQL_CMD_SEND || slashCmdStatus == PSQL_CMD_NEWEDIT) &&
! 					query_buf->len == 0)
  				{
  					/* copy previous buffer to current for handling */
  					appendPQExpBufferStr(query_buf, previous_buf->data);
--- 266,273 ----
  
  				success = slashCmdStatus != PSQL_CMD_ERROR;
  
! 				if ((slashCmdStatus == PSQL_CMD_SEND || slashCmdStatus == PSQL_CMD_NEWEDIT
! 					 || slashCmdStatus == PSQL_CMD_ALIAS) && query_buf->len == 0)
  				{
  					/* copy previous buffer to current for handling */
  					appendPQExpBufferStr(query_buf, previous_buf->data);
***************
*** 284,290 ****
  					/* flush any paren nesting info after forced send */
  					psql_scan_reset(scan_state);
  				}
! 				else if (slashCmdStatus == PSQL_CMD_NEWEDIT)
  				{
  					/* rescan query_buf as new input */
  					psql_scan_finish(scan_state);
--- 284,291 ----
  					/* flush any paren nesting info after forced send */
  					psql_scan_reset(scan_state);
  				}
! 				else if ((slashCmdStatus == PSQL_CMD_NEWEDIT)
! 						 || (slashCmdStatus == PSQL_CMD_ALIAS))
  				{
  					/* rescan query_buf as new input */
  					psql_scan_finish(scan_state);
***************
*** 294,300 ****
  					/* reset parsing state since we are rescanning whole line */
  					psql_scan_reset(scan_state);
  					psql_scan_setup(scan_state, line, strlen(line));
! 					line_saved_in_history = false;
  					prompt_status = PROMPT_READY;
  				}
  				else if (slashCmdStatus == PSQL_CMD_TERMINATE)
--- 295,304 ----
  					/* reset parsing state since we are rescanning whole line */
  					psql_scan_reset(scan_state);
  					psql_scan_setup(scan_state, line, strlen(line));
! 					if (slashCmdStatus == PSQL_CMD_NEWEDIT)
! 						line_saved_in_history = false;
! 					else
! 						line_saved_in_history = true;
  					prompt_status = PROMPT_READY;
  				}
  				else if (slashCmdStatus == PSQL_CMD_TERMINATE)
Index: src/bin/psql/settings.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/bin/psql/settings.h,v
retrieving revision 1.33
diff -c -B -r1.33 settings.h
*** src/bin/psql/settings.h	1 Jan 2008 19:45:56 -0000	1.33
--- src/bin/psql/settings.h	1 Apr 2008 14:05:06 -0000
***************
*** 85,90 ****
--- 85,92 ----
  	FILE	   *logfile;		/* session log file handle */
  
  	VariableSpace vars;			/* "shell variable" repository */
+ 	VariableSpace aliases;      /* "shell aliases" repository */
+ 	VariableSpace substaliases; /* which aliases were substituted already */
  
  	/*
  	 * The remaining fields are set by assign hooks associated with entries in
Index: src/bin/psql/startup.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/bin/psql/startup.c,v
retrieving revision 1.146
diff -c -B -r1.146 startup.c
*** src/bin/psql/startup.c	1 Jan 2008 19:45:56 -0000	1.146
--- src/bin/psql/startup.c	1 Apr 2008 14:05:06 -0000
***************
*** 85,90 ****
--- 85,91 ----
  static void process_psqlrc_file(char *filename);
  static void showVersion(void);
  static void EstablishVariableSpace(void);
+ static void EstablishAliasSpace(void);
  
  #ifdef USE_SSL
  static void printSSLInfo(void);
***************
*** 158,163 ****
--- 159,168 ----
  #endif
  
  	EstablishVariableSpace();
+ 	/* create associative array for alias cmd values
+ 	   we do this here for now, but i'm not sure how this
+ 	   will play with values coming from a .psqlrc */
+ 	EstablishAliasSpace();
  
  	SetVariable(pset.vars, "VERSION", PG_VERSION_STR);
  
***************
*** 868,873 ****
--- 873,884 ----
  		PQsetErrorVerbosity(pset.db, pset.verbosity);
  }
  
+ static void
+ EstablishAliasSpace(void)
+ {
+ 	pset.aliases = CreateVariableSpace();
+ 	pset.substaliases = CreateVariableSpace();
+ }
  
  static void
  EstablishVariableSpace(void)
Index: src/bin/psql/variables.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/bin/psql/variables.c,v
retrieving revision 1.28
diff -c -B -r1.28 variables.c
*** src/bin/psql/variables.c	1 Jan 2008 19:45:56 -0000	1.28
--- src/bin/psql/variables.c	1 Apr 2008 14:05:06 -0000
***************
*** 241,243 ****
--- 241,264 ----
  
  	return true;
  }
+ 
+ int
+ ClearVariableSpace(VariableSpace space)
+ {
+ 	int count = 0;
+ 
+ 	if (!space)
+ 		return -1;
+ 
+ 	/* loop through the list, as long as the root element
+ 	 * contains any remaining pointer to the next element in the
+ 	 * list
+ 	 */
+ 	while(space->next)
+ 	{
+ 		DeleteVariable(space, space->next->name);
+ 		count++;
+ 	}
+ 
+ 	return count;
+ }
Index: src/bin/psql/variables.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/bin/psql/variables.h,v
retrieving revision 1.22
diff -c -B -r1.22 variables.h
*** src/bin/psql/variables.h	1 Jan 2008 19:45:56 -0000	1.22
--- src/bin/psql/variables.h	1 Apr 2008 14:05:06 -0000
***************
*** 56,60 ****
--- 56,61 ----
  bool		SetVariableAssignHook(VariableSpace space, const char *name, VariableAssignHook hook);
  bool		SetVariableBool(VariableSpace space, const char *name);
  bool		DeleteVariable(VariableSpace space, const char *name);
+ int         ClearVariableSpace(VariableSpace space);
  
  #endif   /* VARIABLES_H */
