diff --git a/mysql-test/r/func_digest.result b/mysql-test/r/func_digest.result
index bd0c43f4cfe..0070fdc6933 100644
--- a/mysql-test/r/func_digest.result
+++ b/mysql-test/r/func_digest.result
@@ -2008,3 +2008,18 @@ SHOW variables LIKE 'character_set_client';
 Variable_name	Value
 character_set_client	latin1
 SET NAMES DEFAULT;
+#
+# Bug STATEMENT_DIGEST() adds trailing semicolon to digest
+#
+SELECT statement_digest('select 1;');
+statement_digest('select 1;')
+d1b44b0c19af710b5a679907e284acd2ddc285201794bc69a2389d77baedddae
+SELECT statement_digest('select 1');
+statement_digest('select 1')
+d1b44b0c19af710b5a679907e284acd2ddc285201794bc69a2389d77baedddae
+SELECT statement_digest_text('select 1;');
+statement_digest_text('select 1;')
+SELECT ?
+SELECT statement_digest_text('select 1');
+statement_digest_text('select 1')
+SELECT ?
diff --git a/mysql-test/t/func_digest.test b/mysql-test/t/func_digest.test
index 5fef2810b12..6ab50acbcde 100644
--- a/mysql-test/t/func_digest.test
+++ b/mysql-test/t/func_digest.test
@@ -786,3 +786,11 @@ SELECT substr(hex(statement_digest_text(CONVERT(@v USING latin1))), -6, 4);
 SHOW variables LIKE 'character_set_client';
 
 SET NAMES DEFAULT;
+
+--echo #
+--echo # Bug STATEMENT_DIGEST() adds trailing semicolon to digest
+--echo #
+SELECT statement_digest('select 1;');
+SELECT statement_digest('select 1');
+SELECT statement_digest_text('select 1;');
+SELECT statement_digest_text('select 1');
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index 84a3d6edd0b..eb92aae978e 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -993,12 +993,27 @@ bool parse(THD *thd, Item *statement_expr, String *statement_string) {
   thd->variables.character_set_client = cs;
   thd->update_charset();
 
-  Parser_state ps;
-
   // The lexer needs null-terminated strings, despite boasting the below
   // interface. Hence the use of c_ptr_safe().
-  if (ps.init(thd, statement_string->c_ptr_safe(), statement_string->length()))
-    return true;
+  char *query = statement_string->c_ptr_safe();
+  size_t length = statement_string->length();
+
+  /* Remove garbage at start and end of query. See also alloc_query(). */
+  while (length > 0 && my_isspace(cs, query[0])) {
+    query++;
+    length--;
+  }
+  const char *pos = query + length;  // Point at end null
+  while (length > 0 &&
+         (pos[-1] == ';' || my_isspace(cs, pos[-1]))) {
+    pos--;
+    length--;
+  }
+  query[length] = '\0';
+
+  Parser_state ps;
+
+  if (ps.init(thd, query, length)) return true;
 
   ps.m_lip.multi_statements = false;
   ps.m_lip.m_digest = thd->m_digest;