diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 4f7df367e5a1b486a2ce5840bb0eed4e30af579e..abf9bc7396214bd0b2dbabf194704b2964ba0ea1 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -668,7 +668,7 @@ static char **complete_from_variables(char *text,
 
 static PGresult *exec_query(const char *query);
 
-static char *previous_word(int point, int skip);
+static void get_previous_words(int point, char **previous_words, int nwords);
 
 #ifdef NOT_USED
 static char *quote_file_name(char *text, int match_type, char *quote_pointer);
@@ -710,13 +710,16 @@ psql_completion(char *text, int start, int end)
 	/* This is the variable we'll return. */
 	char	  **matches = NULL;
 
-	/* These are going to contain some scannage of the input line. */
-	char	   *prev_wd,
-			   *prev2_wd,
-			   *prev3_wd,
-			   *prev4_wd,
-			   *prev5_wd,
-			   *prev6_wd;
+	/* This array will contain some scannage of the input line. */
+	char	   *previous_words[6];
+
+	/* For compactness, we use these macros to reference previous_words[]. */
+#define prev_wd   (previous_words[0])
+#define prev2_wd  (previous_words[1])
+#define prev3_wd  (previous_words[2])
+#define prev4_wd  (previous_words[3])
+#define prev5_wd  (previous_words[4])
+#define prev6_wd  (previous_words[5])
 
 	static const char *const sql_commands[] = {
 		"ABORT", "ALTER", "ANALYZE", "BEGIN", "CHECKPOINT", "CLOSE", "CLUSTER",
@@ -755,16 +758,11 @@ psql_completion(char *text, int start, int end)
 	completion_info_charp2 = NULL;
 
 	/*
-	 * Scan the input line before our current position for the last five
+	 * Scan the input line before our current position for the last few
 	 * words. According to those we'll make some smart decisions on what the
-	 * user is probably intending to type. TODO: Use strtokx() to do this.
+	 * user is probably intending to type.
 	 */
-	prev_wd = previous_word(start, 0);
-	prev2_wd = previous_word(start, 1);
-	prev3_wd = previous_word(start, 2);
-	prev4_wd = previous_word(start, 3);
-	prev5_wd = previous_word(start, 4);
-	prev6_wd = previous_word(start, 5);
+	get_previous_words(start, previous_words, lengthof(previous_words));
 
 	/* If a backslash command was started, continue */
 	if (text[0] == '\\')
@@ -782,7 +780,7 @@ psql_completion(char *text, int start, int end)
 	}
 
 	/* If no previous word, suggest one of the basic sql commands */
-	else if (!prev_wd)
+	else if (prev_wd[0] == '\0')
 		COMPLETE_WITH_LIST(sql_commands);
 
 /* CREATE */
@@ -790,10 +788,10 @@ psql_completion(char *text, int start, int end)
 	else if (pg_strcasecmp(prev_wd, "CREATE") == 0)
 		matches = completion_matches(text, create_command_generator);
 
-/* DROP, but watch out for DROP embedded in other commands */
+/* DROP, but not DROP embedded in other commands */
 	/* complete with something you can drop */
 	else if (pg_strcasecmp(prev_wd, "DROP") == 0 &&
-			 pg_strcasecmp(prev2_wd, "DROP") == 0)
+			 prev2_wd[0] == '\0')
 		matches = completion_matches(text, drop_command_generator);
 
 /* ALTER */
@@ -2918,11 +2916,12 @@ psql_completion(char *text, int start, int end)
 	}
 
 	/* free storage */
-	free(prev_wd);
-	free(prev2_wd);
-	free(prev3_wd);
-	free(prev4_wd);
-	free(prev5_wd);
+	{
+		int			i;
+
+		for (i = 0; i < lengthof(previous_words); i++)
+			free(previous_words[i]);
+	}
 
 	/* Return our Grand List O' Matches */
 	return matches;
@@ -3372,77 +3371,88 @@ exec_query(const char *query)
 
 
 /*
- * Return the word (space delimited) before point. Set skip > 0 to
- * skip that many words; e.g. skip=1 finds the word before the
- * previous one. Return value is NULL or a malloc'ed string.
+ * Return the nwords word(s) before point.  Words are returned right to left,
+ * that is, previous_words[0] gets the last word before point.
+ * If we run out of words, remaining array elements are set to empty strings.
+ * Each array element is filled with a malloc'd string.
  */
-static char *
-previous_word(int point, int skip)
+static void
+get_previous_words(int point, char **previous_words, int nwords)
 {
-	int			i,
-				start = 0,
-				end = -1,
-				inquotes = 0;
-	char	   *s;
 	const char *buf = rl_line_buffer;	/* alias */
+	int			i;
 
-	/* first we look for a space or a parenthesis before the current word */
+	/* first we look for a non-word char before the current point */
 	for (i = point - 1; i >= 0; i--)
 		if (strchr(WORD_BREAKS, buf[i]))
 			break;
 	point = i;
 
-	while (skip-- >= 0)
+	while (nwords-- > 0)
 	{
-		int			parentheses = 0;
+		int			start,
+					end;
+		char	   *s;
 
 		/* now find the first non-space which then constitutes the end */
+		end = -1;
 		for (i = point; i >= 0; i--)
-			if (buf[i] != ' ')
+		{
+			if (!isspace((unsigned char) buf[i]))
 			{
 				end = i;
 				break;
 			}
+		}
 
 		/*
-		 * If no end found we return null, because there is no word before the
-		 * point
-		 */
-		if (end == -1)
-			return NULL;
-
-		/*
-		 * Otherwise we now look for the start. The start is either the last
-		 * character before any space going backwards from the end, or it's
-		 * simply character 0. We also handle open quotes and parentheses.
+		 * If no end found we return an empty string, because there is no word
+		 * before the point
 		 */
-		for (start = end; start > 0; start--)
+		if (end < 0)
+		{
+			point = end;
+			s = pg_strdup("");
+		}
+		else
 		{
-			if (buf[start] == '"')
-				inquotes = !inquotes;
-			if (inquotes == 0)
+			/*
+			 * Otherwise we now look for the start. The start is either the
+			 * last character before any word-break character going backwards
+			 * from the end, or it's simply character 0. We also handle open
+			 * quotes and parentheses.
+			 */
+			bool		inquotes = false;
+			int			parentheses = 0;
+
+			for (start = end; start > 0; start--)
 			{
-				if (buf[start] == ')')
-					parentheses++;
-				else if (buf[start] == '(')
+				if (buf[start] == '"')
+					inquotes = !inquotes;
+				else if (!inquotes)
 				{
-					if (--parentheses <= 0)
+					if (buf[start] == ')')
+						parentheses++;
+					else if (buf[start] == '(')
+					{
+						if (--parentheses <= 0)
+							break;
+					}
+					else if (parentheses == 0 &&
+							 strchr(WORD_BREAKS, buf[start - 1]))
 						break;
 				}
-				else if (parentheses == 0 &&
-						 strchr(WORD_BREAKS, buf[start - 1]))
-					break;
 			}
-		}
 
-		point = start - 1;
-	}
+			point = start - 1;
 
-	/* make a copy */
-	s = pg_malloc(end - start + 2);
-	strlcpy(s, &buf[start], end - start + 2);
+			/* make a copy of chars from start to end inclusive */
+			s = pg_malloc(end - start + 2);
+			strlcpy(s, &buf[start], end - start + 2);
+		}
 
-	return s;
+		*previous_words++ = s;
+	}
 }
 
 #ifdef NOT_USED