diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index fca3aa3ffe4842a19b78cfee89d04c789451e0ca..a90d7eb72cfb4f03f11cc2bd69e8215ea7db0ba6 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2006, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.149 2006/03/05 15:58:52 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.150 2006/04/02 09:02:41 alvherre Exp $
  */
 
 /*----------------------------------------------------------------------
@@ -465,6 +465,7 @@ static const pgsql_thing_t words_after_create[] = {
 /* Forward declaration of functions */
 static char **psql_completion(char *text, int start, int end);
 static char *create_command_generator(const char *text, int state);
+static char *drop_command_generator(const char *text, int state);
 static char *complete_from_query(const char *text, int state);
 static char *complete_from_schema_query(const char *text, int state);
 static char *_complete_from_query(int is_schema_query,
@@ -521,11 +522,13 @@ psql_completion(char *text, int start, int end)
 			   *prev5_wd;
 
 	static const char *const sql_commands[] = {
-		"ABORT", "ALTER", "ANALYZE", "BEGIN", "CHECKPOINT", "CLOSE", "CLUSTER", "COMMENT",
-		"COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE", "DELETE FROM", "DROP", "END", "EXECUTE",
-		"EXPLAIN", "FETCH", "GRANT", "INSERT", "LISTEN", "LOAD", "LOCK", "MOVE", "NOTIFY",
-		"PREPARE", "REINDEX", "RELEASE", "RESET", "REVOKE", "ROLLBACK", "SAVEPOINT",
-		"SELECT", "SET", "SHOW", "START", "TRUNCATE", "UNLISTEN", "UPDATE", "VACUUM", NULL
+		"ABORT", "ALTER", "ANALYZE", "BEGIN", "CHECKPOINT", "CLOSE", "CLUSTER",
+		"COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE",
+		"DELETE FROM", "DROP", "END", "EXECUTE", "EXPLAIN", "FETCH", "GRANT",
+		"INSERT", "LISTEN", "LOAD", "LOCK", "MOVE", "NOTIFY", "PREPARE",
+		"REASSIGN", "REINDEX", "RELEASE", "RESET", "REVOKE", "ROLLBACK",
+		"SAVEPOINT", "SELECT", "SET", "SHOW", "START", "TRUNCATE", "UNLISTEN",
+		"UPDATE", "VACUUM", NULL
 	};
 
 	static const char *const backslash_commands[] = {
@@ -536,7 +539,8 @@ psql_completion(char *text, int start, int end)
 		"\\e", "\\echo", "\\encoding",
 		"\\f", "\\g", "\\h", "\\help", "\\H", "\\i", "\\l",
 		"\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
-		"\\o", "\\p", "\\password", "\\pset", "\\q", "\\qecho", "\\r", "\\set", "\\t", "\\T",
+		"\\o", "\\p", "\\password", "\\pset", "\\q", "\\qecho", "\\r",
+		"\\set", "\\t", "\\T",
 		"\\timing", "\\unset", "\\x", "\\w", "\\z", "\\!", NULL
 	};
 
@@ -570,15 +574,19 @@ psql_completion(char *text, int start, int end)
 	else if (!prev_wd)
 		COMPLETE_WITH_LIST(sql_commands);
 
-/* CREATE or DROP but not ALTER (TABLE|DOMAIN|GROUP) sth DROP */
-	/* complete with something you can create or drop */
-	else if (pg_strcasecmp(prev_wd, "CREATE") == 0 ||
-			 (pg_strcasecmp(prev_wd, "DROP") == 0 &&
-			  pg_strcasecmp(prev3_wd, "TABLE") != 0 &&
-			  pg_strcasecmp(prev3_wd, "DOMAIN") != 0 &&
-			  pg_strcasecmp(prev3_wd, "GROUP") != 0))
+/* CREATE */
+	/* complete with something you can create */
+	else if (pg_strcasecmp(prev_wd, "CREATE") == 0)
 		matches = completion_matches(text, create_command_generator);
 
+/* DROP, except ALTER (TABLE|DOMAIN|GROUP) sth DROP */
+	/* complete with something you can drop */
+	else if (pg_strcasecmp(prev_wd, "DROP") == 0 &&
+			 pg_strcasecmp(prev3_wd, "TABLE") != 0 &&
+			 pg_strcasecmp(prev3_wd, "DOMAIN") != 0 &&
+			 pg_strcasecmp(prev3_wd, "GROUP") != 0)
+		matches = completion_matches(text, drop_command_generator);
+
 /* ALTER */
 
 	/*
@@ -1248,23 +1256,22 @@ psql_completion(char *text, int start, int end)
 			  pg_strcasecmp(prev3_wd, "AGGREGATE") == 0 &&
 			  prev_wd[strlen(prev_wd) - 1] == ')'))
 	{
-		
 		if ((pg_strcasecmp(prev3_wd, "DROP") == 0) && (pg_strcasecmp(prev2_wd, "FUNCTION") == 0))
-                {
-                        if (find_open_parenthesis(end))
+		{
+			if (find_open_parenthesis(end))
 			{
 				static const char func_args_query[] = "select pg_catalog.oidvectortypes(proargtypes)||')' from pg_proc where proname='%s'";
-                        	char *tmp_buf = malloc(strlen(func_args_query) + strlen(prev_wd));
-                        	sprintf(tmp_buf, func_args_query, prev_wd);
-                        	COMPLETE_WITH_QUERY(tmp_buf);
-                        	free(tmp_buf);
+				char *tmp_buf = malloc(strlen(func_args_query) + strlen(prev_wd));
+				sprintf(tmp_buf, func_args_query, prev_wd);
+				COMPLETE_WITH_QUERY(tmp_buf);
+				free(tmp_buf);
 			}
 			else
 			{
-                        	COMPLETE_WITH_CONST("(");
+				COMPLETE_WITH_CONST("(");
 			}
-                }
-                else
+		}
+		else
 		{
 			static const char *const list_DROPCR[] =
 			{"CASCADE", "RESTRICT", NULL};
@@ -1274,7 +1281,7 @@ psql_completion(char *text, int start, int end)
 	}
 	else if (pg_strcasecmp(prev4_wd, "DROP") == 0 &&
 			pg_strcasecmp(prev3_wd, "FUNCTION") == 0 &&
-			pg_strcasecmp(prev_wd, "(") == 0 )
+			pg_strcasecmp(prev_wd, "(") == 0)
 	{
 		static const char func_args_query[] = "select pg_catalog.oidvectortypes(proargtypes)||')' from pg_proc where proname='%s'";
 		char *tmp_buf = malloc(strlen(func_args_query) + strlen(prev2_wd));
@@ -1282,6 +1289,14 @@ psql_completion(char *text, int start, int end)
 		COMPLETE_WITH_QUERY(tmp_buf);
 		free(tmp_buf);
 	}
+	/* DROP OWNED BY */
+	else if (pg_strcasecmp(prev2_wd, "DROP") == 0 &&
+			 pg_strcasecmp(prev_wd, "OWNED") == 0)
+		COMPLETE_WITH_CONST("BY");
+	else if (pg_strcasecmp(prev3_wd, "DROP") == 0 &&
+			 pg_strcasecmp(prev2_wd, "OWNED") == 0 &&
+			 pg_strcasecmp(prev_wd, "BY") == 0)
+		COMPLETE_WITH_QUERY(Query_for_list_of_roles);
 
 
 
@@ -1502,7 +1517,7 @@ psql_completion(char *text, int start, int end)
 	else if (pg_strcasecmp(prev_wd, "NOTIFY") == 0)
 		COMPLETE_WITH_QUERY("SELECT pg_catalog.quote_ident(relname) FROM pg_catalog.pg_listener WHERE substring(pg_catalog.quote_ident(relname),1,%d)='%s'");
 
-/* OWNER TO  - complete with available roles*/
+/* OWNER TO  - complete with available roles */
 	else if (pg_strcasecmp(prev2_wd, "OWNER") == 0 &&
 			 pg_strcasecmp(prev_wd, "TO") == 0)
 		COMPLETE_WITH_QUERY(Query_for_list_of_roles);
@@ -1526,6 +1541,25 @@ psql_completion(char *text, int start, int end)
 		COMPLETE_WITH_LIST(list_PREPARE);
 	}
 
+/* REASSIGN OWNED BY xxx TO yyy */
+	else if (pg_strcasecmp(prev_wd, "REASSIGN") == 0)
+		COMPLETE_WITH_CONST("OWNED");
+	else if (pg_strcasecmp(prev_wd, "OWNED") == 0 &&
+			 pg_strcasecmp(prev2_wd, "REASSIGN") == 0)
+		COMPLETE_WITH_CONST("BY");
+	else if (pg_strcasecmp(prev_wd, "BY") == 0 &&
+			 pg_strcasecmp(prev2_wd, "OWNED") == 0 &&
+			 pg_strcasecmp(prev3_wd, "REASSIGN") == 0)
+		COMPLETE_WITH_QUERY(Query_for_list_of_roles);
+	else if (pg_strcasecmp(prev2_wd, "BY") == 0 &&
+			 pg_strcasecmp(prev3_wd, "OWNED") == 0 &&
+			 pg_strcasecmp(prev4_wd, "REASSIGN") == 0)
+		COMPLETE_WITH_CONST("TO");
+	else if (pg_strcasecmp(prev_wd, "TO") == 0 &&
+			 pg_strcasecmp(prev3_wd, "BY") == 0 &&
+			 pg_strcasecmp(prev4_wd, "OWNED") == 0 &&
+			 pg_strcasecmp(prev5_wd, "REASSIGN") == 0)
+		COMPLETE_WITH_QUERY(Query_for_list_of_roles);
 
 /* REINDEX */
 	else if (pg_strcasecmp(prev_wd, "REINDEX") == 0)
@@ -1909,7 +1943,7 @@ psql_completion(char *text, int start, int end)
    something of that sort.
 */
 
-/* This one gives you one from a list of things you can put after CREATE or DROP
+/* This one gives you one from a list of things you can put after CREATE
    as defined above.
 */
 static char *
@@ -1935,6 +1969,51 @@ create_command_generator(const char *text, int state)
 	return NULL;
 }
 
+/*
+ * This function gives you a list of things you can put after a DROP command.
+ * Very similar to create_command_generator, but has an additional entry for
+ * OWNED BY.  (We do it this way in order not to duplicate the
+ * words_after_create list.)
+ */
+static char *
+drop_command_generator(const char *text, int state)
+{
+	static int	list_index,
+				string_length;
+	const char *name;
+
+	if (state == 0)
+	{
+		/* If this is the first time for this completion, init some values */
+		list_index = 0;
+		string_length = strlen(text);
+
+		/*
+		 * DROP can be followed by "OWNED BY", which is not found in the list
+		 * for CREATE matches, so make it the first state. (We do not make it
+		 * the last state because it would be more difficult to detect when we
+		 * have to return NULL instead.)
+		 *
+		 * Make sure we advance to the next state.
+		 */
+		list_index++;
+		if (pg_strncasecmp("OWNED", text, string_length) == 0)
+			return pg_strdup("OWNED");
+	}
+
+	/*
+	 * In subsequent attempts, try to complete with the same items we use for
+	 * CREATE
+	 */
+	while ((name = words_after_create[list_index++ - 1].name))
+	{
+		if (pg_strncasecmp(name, text, string_length) == 0)
+			return pg_strdup(name);
+	}
+
+	/* if nothing matches, return NULL */
+	return NULL;
+}
 
 /* The following two functions are wrappers for _complete_from_query */