diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 5f27120..ed92912 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -3198,9 +3198,8 @@ static char *
 _complete_from_query(int is_schema_query, const char *text, int state)
 {
 	static int	list_index,
-				string_length;
+				byte_length;
 	static PGresult *result = NULL;
-
 	/*
 	 * If this is the first time for this completion, we fetch a list of our
 	 * "things" from the backend.
@@ -3211,9 +3210,18 @@ _complete_from_query(int is_schema_query, const char *text, int state)
 		char	   *e_text;
 		char	   *e_info_charp;
 		char	   *e_info_charp2;
+		const char *pstr = text;
+		int			string_length = 0;
 
 		list_index = 0;
-		string_length = strlen(text);
+		byte_length = strlen(text);
+
+		/* Count length as number of characters (not bytes), for passing to substring */
+		while (*pstr)
+		{
+			string_length++;
+			pstr += PQmblen(pstr, pset.encoding);
+		}
 
 		/* Free any prior result */
 		PQclear(result);
@@ -3353,7 +3361,7 @@ _complete_from_query(int is_schema_query, const char *text, int state)
 
 		while (list_index < PQntuples(result) &&
 			   (item = PQgetvalue(result, list_index++, 0)))
-			if (pg_strncasecmp(text, item, string_length) == 0)
+			if (pg_strncasecmp(text, item, byte_length) == 0)
 				return pg_strdup(item);
 	}
 
@@ -3372,7 +3380,7 @@ _complete_from_query(int is_schema_query, const char *text, int state)
 static char *
 complete_from_list(const char *text, int state)
 {
-	static int	string_length,
+	static int	byte_length,
 				list_index,
 				matches;
 	static bool casesensitive;
@@ -3385,7 +3393,7 @@ complete_from_list(const char *text, int state)
 	if (state == 0)
 	{
 		list_index = 0;
-		string_length = strlen(text);
+		byte_length = strlen(text);
 		casesensitive = completion_case_sensitive;
 		matches = 0;
 	}
@@ -3393,14 +3401,14 @@ complete_from_list(const char *text, int state)
 	while ((item = completion_charpp[list_index++]))
 	{
 		/* First pass is case sensitive */
-		if (casesensitive && strncmp(text, item, string_length) == 0)
+		if (casesensitive && strncmp(text, item, byte_length) == 0)
 		{
 			matches++;
 			return pg_strdup(item);
 		}
 
 		/* Second pass is case insensitive, don't bother counting matches */
-		if (!casesensitive && pg_strncasecmp(text, item, string_length) == 0)
+		if (!casesensitive && pg_strncasecmp(text, item, byte_length) == 0)
 		{
 			if (completion_case_sensitive)
 				return pg_strdup(item);
@@ -3627,13 +3635,13 @@ pg_strdup_keyword_case(const char *s, const char *ref)
 static char *
 escape_string(const char *text)
 {
-	size_t		text_length;
+	size_t		byte_length;
 	char	   *result;
 
-	text_length = strlen(text);
+	byte_length = strlen(text);
 
-	result = pg_malloc(text_length * 2 + 1);
-	PQescapeStringConn(pset.db, result, text, text_length, NULL);
+	result = pg_malloc(byte_length * 2 + 1);
+	PQescapeStringConn(pset.db, result, text, byte_length, NULL);
 
 	return result;
 }
