commit 4da75d8ed70a7814a60b66e985bc42068ff90f50
Author: Alexander Barkov <bar@mariadb.com>
Date:   Tue Feb 25 16:09:42 2020 +0400

    MDEV-17832 Protocol: extensions for Pluggable types and JSON, GEOMETRY

diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt
index dfa805fc7e8..0f63f58e5da 100644
--- a/client/CMakeLists.txt
+++ b/client/CMakeLists.txt
@@ -44,7 +44,9 @@ IF(UNIX)
   SET_TARGET_PROPERTIES(mysql PROPERTIES ENABLE_EXPORTS TRUE)
 ENDIF(UNIX)
 
-MYSQL_ADD_EXECUTABLE(mysqltest mysqltest.cc COMPONENT Test)
+MYSQL_ADD_EXECUTABLE(mysqltest mysqltest.cc
+                     ${CMAKE_SOURCE_DIR}/sql/sql_string.cc
+                     COMPONENT Test)
 SET_SOURCE_FILES_PROPERTIES(mysqltest.cc PROPERTIES COMPILE_FLAGS "-DTHREADS ${PCRE2_DEBIAN_HACK}")
 TARGET_LINK_LIBRARIES(mysqltest  ${CLIENT_LIB} pcre2-posix pcre2-8)
 SET_TARGET_PROPERTIES(mysqltest PROPERTIES ENABLE_EXPORTS TRUE)
diff --git a/client/client_metadata.h b/client/client_metadata.h
new file mode 100644
index 00000000000..84c17e4f3d8
--- /dev/null
+++ b/client/client_metadata.h
@@ -0,0 +1,52 @@
+#ifndef SQL_CLIENT_METADATA_INCLUDED
+#define SQL_CLIENT_METADATA_INCLUDED
+/*
+   Copyright (c) 2020, MariaDB Corporation.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA */
+
+#include "sql_string.h"
+
+
+class Client_field_metadata
+{
+  MARIADB_CONST_STRING m_type;
+  MARIADB_CONST_STRING m_format;
+public:
+  Client_field_metadata(MYSQL_FIELD *field)
+   :m_type(mariadb_field_metadata_attr(field,
+                                       MARIADB_FIELD_METADATA_DATA_TYPE_NAME)),
+    m_format(mariadb_field_metadata_attr(field,
+                                         MARIADB_FIELD_METADATA_FORMAT_NAME))
+  { }
+  void append_to(Binary_string *to) const
+  {
+    uint orig_to_length= to->length();
+    if (m_type.length)
+    {
+      to->append(C_STRING_WITH_LEN("type="));
+      to->append(m_type.str, m_type.length);
+    }
+    if (m_format.length)
+    {
+      if (to->length() != orig_to_length)
+        to->append(" ", 1);
+      to->append(C_STRING_WITH_LEN("format="));
+      to->append(m_format.str, m_format.length);
+    }
+  }
+};
+
+
+#endif // SQL_CLIENT_METADATA_INCLUDED
diff --git a/client/mysql.cc b/client/mysql.cc
index 2c7f0a500b9..e7ac0979e4f 100644
--- a/client/mysql.cc
+++ b/client/mysql.cc
@@ -58,6 +58,7 @@ static char *server_version= NULL;
 #define MAX_SERVER_ARGS               64
 
 #include "sql_string.h"
+#include "client_metadata.h"
 
 extern "C" {
 #if defined(HAVE_CURSES_H) && defined(HAVE_TERM_H)
@@ -3513,12 +3514,15 @@ print_field_types(MYSQL_RES *result)
 
   while ((field = mysql_fetch_field(result)))
   {
+    Client_field_metadata metadata(field);
+    BinaryStringBuffer<128> metadata_str;
+    metadata.append_to(&metadata_str);
     tee_fprintf(PAGER, "Field %3u:  `%s`\n"
                        "Catalog:    `%s`\n"
                        "Database:   `%s`\n"
                        "Table:      `%s`\n"
                        "Org_table:  `%s`\n"
-                       "Type:       %s\n"
+                       "Type:       %s%s%.*s%s\n"
                        "Collation:  %s (%u)\n"
                        "Length:     %lu\n"
                        "Max_length: %lu\n"
@@ -3527,6 +3531,9 @@ print_field_types(MYSQL_RES *result)
                 ++i,
                 field->name, field->catalog, field->db, field->table,
                 field->org_table, fieldtype2str(field->type),
+                metadata_str.length() ? " (" : "",
+                metadata_str.length(), metadata_str.ptr(),
+                metadata_str.length() ? ")" : "",
                 get_charset_name(field->charsetnr), field->charsetnr,
                 field->length, field->max_length, field->decimals,
                 fieldflags2str(field->flags));
diff --git a/client/mysqltest.cc b/client/mysqltest.cc
index 148a492a648..a43ad8a3cc9 100644
--- a/client/mysqltest.cc
+++ b/client/mysqltest.cc
@@ -40,6 +40,7 @@
 #include <mysqld_error.h>
 #include <sql_common.h>
 #include <m_ctype.h>
+#include "client_metadata.h"
 #include <my_dir.h>
 #include <hash.h>
 #include <stdarg.h>
@@ -7709,6 +7710,16 @@ void append_metadata(DYNAMIC_STRING *ds,
     dynstr_append_mem(ds, field->name, field->name_length);
     dynstr_append_mem(ds, "\t", 1);
     replace_dynstr_append_uint(ds, field->type);
+
+    Client_field_metadata metadata(field);
+    BinaryStringBuffer<128> metadata_str;
+    metadata.append_to(&metadata_str);
+    if (metadata_str.length())
+    {
+      dynstr_append_mem(ds, " (", 2);
+      dynstr_append_mem(ds, metadata_str.ptr(), metadata_str.length());
+      dynstr_append_mem(ds, ")", 1);
+    }
     dynstr_append_mem(ds, "\t", 1);
     replace_dynstr_append_uint(ds, field->length);
     dynstr_append_mem(ds, "\t", 1);
diff --git a/include/mysql.h b/include/mysql.h
index ec49ca0482a..9b0759ce3e6 100644
--- a/include/mysql.h
+++ b/include/mysql.h
@@ -97,6 +97,9 @@ extern char *mysql_unix_port;
 #define IS_LONGDATA(t) ((t) >= MYSQL_TYPE_TINY_BLOB && (t) <= MYSQL_TYPE_STRING)
 
 
+typedef struct st_mysql_const_lex_string MARIADB_CONST_STRING;
+
+
 typedef struct st_mysql_field {
   char *name;                 /* Name of column */
   char *org_name;             /* Original column name, if an alias */
@@ -411,6 +414,14 @@ MYSQL_FIELD * STDCALL mysql_fetch_fields(MYSQL_RES *res);
 MYSQL_ROW_OFFSET STDCALL mysql_row_tell(MYSQL_RES *res);
 MYSQL_FIELD_OFFSET STDCALL mysql_field_tell(MYSQL_RES *res);
 
+
+
+
+MARIADB_CONST_STRING
+  mariadb_field_metadata_attr(const MYSQL_FIELD *field,
+                              enum mariadb_field_metadata_attr_t type);
+
+
 unsigned int STDCALL mysql_field_count(MYSQL *mysql);
 my_ulonglong STDCALL mysql_affected_rows(MYSQL *mysql);
 my_ulonglong STDCALL mysql_insert_id(MYSQL *mysql);
diff --git a/include/mysql.h.pp b/include/mysql.h.pp
index 4fcfab4aea4..e4936a79549 100644
--- a/include/mysql.h.pp
+++ b/include/mysql.h.pp
@@ -28,6 +28,11 @@ enum enum_indicator_type
   STMT_INDICATOR_DEFAULT,
   STMT_INDICATOR_IGNORE
 };
+enum mariadb_field_metadata_attr_t
+{
+  MARIADB_FIELD_METADATA_DATA_TYPE_NAME= 1,
+  MARIADB_FIELD_METADATA_FORMAT_NAME= 2
+};
 struct st_vio;
 typedef struct st_vio Vio;
 typedef struct st_net {
@@ -207,6 +212,7 @@ extern int list_walk(LIST *,list_walk_action action,unsigned char * argument);
 extern unsigned int mariadb_deinitialize_ssl;
 extern unsigned int mysql_port;
 extern char *mysql_unix_port;
+typedef struct st_mysql_const_lex_string MARIADB_CONST_STRING;
 typedef struct st_mysql_field {
   char *name;
   char *org_name;
@@ -436,6 +442,9 @@ MYSQL_FIELD * mysql_fetch_field_direct(MYSQL_RES *res,
 MYSQL_FIELD * mysql_fetch_fields(MYSQL_RES *res);
 MYSQL_ROW_OFFSET mysql_row_tell(MYSQL_RES *res);
 MYSQL_FIELD_OFFSET mysql_field_tell(MYSQL_RES *res);
+MARIADB_CONST_STRING
+  mariadb_field_metadata_attr(const MYSQL_FIELD *field,
+                              enum mariadb_field_metadata_attr_t type);
 unsigned int mysql_field_count(MYSQL *mysql);
 my_ulonglong mysql_affected_rows(MYSQL *mysql);
 my_ulonglong mysql_insert_id(MYSQL *mysql);
diff --git a/include/mysql_com.h b/include/mysql_com.h
index 4eafe148743..5b3a83582ba 100644
--- a/include/mysql_com.h
+++ b/include/mysql_com.h
@@ -296,6 +296,8 @@ enum enum_indicator_type
 #define MARIADB_CLIENT_COM_MULTI (1ULL << 33)
 /* support of array binding */
 #define MARIADB_CLIENT_STMT_BULK_OPERATIONS (1ULL << 34)
+/* support of extended data type/format information */
+#define MARIADB_CLIENT_EXTENDED_TYPE_INFO (1ULL << 35)
 
 #ifdef HAVE_COMPRESS
 #define CAN_CLIENT_COMPRESS CLIENT_COMPRESS
@@ -337,6 +339,7 @@ enum enum_indicator_type
                            CLIENT_CONNECT_ATTRS |\
                            MARIADB_CLIENT_COM_MULTI |\
                            MARIADB_CLIENT_STMT_BULK_OPERATIONS |\
+                           MARIADB_CLIENT_EXTENDED_TYPE_INFO|\
                            CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS)
 
 /*
@@ -348,6 +351,15 @@ enum enum_indicator_type
                                                & ~CLIENT_COMPRESS) \
                                                & ~CLIENT_SSL_VERIFY_SERVER_CERT)
 
+enum mariadb_field_metadata_attr_t
+{
+  MARIADB_FIELD_METADATA_DATA_TYPE_NAME= 1,
+  MARIADB_FIELD_METADATA_FORMAT_NAME= 2
+};
+
+#define MARIADB_FIELD_METADATA_LAST MARIADB_FIELD_METADATA_FORMAT_NAME
+
+
 /**
   Is raised when a multi-statement transaction
   has been started, either explicitly, by means
diff --git a/libmysqld/emb_qcache.cc b/libmysqld/emb_qcache.cc
index 603542e820e..fb623f91458 100644
--- a/libmysqld/emb_qcache.cc
+++ b/libmysqld/emb_qcache.cc
@@ -445,6 +445,7 @@ int emb_load_querycache_result(THD *thd, Querycache_stream *src)
         !(field->catalog= src->load_str(f_alloc, &field->catalog_length))    ||
         src->load_safe_str(f_alloc, &field->def, &field->def_length))
       goto err;
+    field->extension= NULL;
   }
   
   data->rows= rows;
diff --git a/libmysqld/embedded_priv.h b/libmysqld/embedded_priv.h
index 77955ea499e..37078a22a4f 100644
--- a/libmysqld/embedded_priv.h
+++ b/libmysqld/embedded_priv.h
@@ -38,4 +38,11 @@ typedef struct embedded_query_result
   char sqlstate[SQLSTATE_LENGTH+1];
 } EQR;
 
+
+typedef struct st_mariadb_field_extension
+{
+  MARIADB_CONST_STRING metadata[MARIADB_FIELD_METADATA_LAST+1]; /* 10.5 */
+} MARIADB_FIELD_EXTENSION;
+
+
 C_MODE_END
diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc
index 8b25515405c..665d60b7b1e 100644
--- a/libmysqld/lib_sql.cc
+++ b/libmysqld/lib_sql.cc
@@ -1032,6 +1032,51 @@ void Protocol_text::remove_last_row()
 }
 
 
+
+static MARIADB_CONST_STRING ma_const_string_copy_root(MEM_ROOT *memroot,
+                                                      const char *str,
+                                                      size_t length)
+{
+  MARIADB_CONST_STRING res;
+  if (!str || !(res.str= strmake_root(memroot, str, length)))
+    return null_clex_str;
+  res.length= length;
+  return res;
+}
+
+static void ma_field_extension_init_type_info(MEM_ROOT *memroot,
+                                              MARIADB_FIELD_EXTENSION *ext,
+                                              const char *ptr, size_t length)
+{
+  const char *end= ptr + length;
+  for ( ;  ptr < end; )
+  {
+    uint itype= (uchar) *ptr++;
+    uint len= (uchar) *ptr++;
+    if (ptr + len > end  || len > 127)
+      break; /*Badly encoded data*/
+    if (itype <= 127 && itype <= MARIADB_FIELD_METADATA_LAST)
+      ext->metadata[itype]= ma_const_string_copy_root(memroot, ptr, len);
+    ptr+= len;
+  }
+}
+
+
+class Client_field_extension: public Sql_alloc,
+                              public MARIADB_FIELD_EXTENSION
+{
+public:
+  Client_field_extension()
+  {
+    memset(this, 0, sizeof(*this));
+  }
+  void init_type_info(MEM_ROOT *memroot, const char *ptr, size_t length)
+  {
+    ma_field_extension_init_type_info(memroot, this, ptr, length);
+  }
+};
+
+
 bool Protocol_text::store_field_metadata(const THD * thd,
                                          const Send_field &server_field,
                                          CHARSET_INFO *charset_for_protocol,
@@ -1080,6 +1125,18 @@ bool Protocol_text::store_field_metadata(const THD * thd,
   client_field->catalog= dup_str_aux(field_alloc, "def", 3, cs, thd_cs);
   client_field->catalog_length= 3;
 
+  LEX_CSTRING type_info= server_field.extended_type_info.lex_cstring();
+  if (type_info.length)
+  {
+    Client_field_extension *ext= new (field_alloc) Client_field_extension();
+    if ((client_field->extension= ext))
+      ext->init_type_info(field_alloc, type_info.str, type_info.length);
+  }
+  else
+  {
+    client_field->extension= NULL;
+  }
+
   if (IS_NUM(client_field->type))
     client_field->flags|= NUM_FLAG;
 
diff --git a/libmysqld/libmysql.c b/libmysqld/libmysql.c
index 2be94882303..7dca3136a22 100644
--- a/libmysqld/libmysql.c
+++ b/libmysqld/libmysql.c
@@ -64,6 +64,7 @@
 
 #include <sql_common.h>
 #include "client_settings.h"
+#include "embedded_priv.h"
 
 #undef net_buffer_length
 #undef max_allowed_packet
@@ -732,6 +733,23 @@ mysql_fetch_field(MYSQL_RES *result)
 }
 
 
+/**************************************************************************
+** Return mysql field metadata
+**************************************************************************/
+MARIADB_CONST_STRING
+mariadb_field_metadata_attr(const MYSQL_FIELD *field,
+                            enum mariadb_field_metadata_attr_t type)
+{
+  MARIADB_FIELD_EXTENSION *ext= (MARIADB_FIELD_EXTENSION*) field->extension;
+  if (!ext || type > MARIADB_FIELD_METADATA_LAST)
+  {
+    static MARIADB_CONST_STRING null_str= {0,0};
+    return null_str;
+  }
+  return ext->metadata[type];
+}
+
+
 /**************************************************************************
   Move to a specific row and column
 **************************************************************************/
diff --git a/mysql-test/main/gis.result b/mysql-test/main/gis.result
index 936924ffe87..fa7ded103b2 100644
--- a/mysql-test/main/gis.result
+++ b/mysql-test/main/gis.result
@@ -5095,5 +5095,211 @@ ERROR HY000: Operator does not exists: 'CAST(expr AS multilinestring)'
 SELECT CONVERT(1, MULTIPOLYGON);
 ERROR HY000: Operator does not exists: 'CAST(expr AS multipolygon)'
 #
+# MDEV-17832 Protocol: extensions for Pluggable types and JSON, GEOMETRY
+#
+SET NAMES utf8;
+CREATE TABLE t1 (
+p    POINT,
+ls   LINESTRING,
+pl   POLYGON,
+mp   MULTIPOINT,
+mls  MULTILINESTRING,
+mpl  MULTIPOLYGON,
+gc   GEOMETRYCOLLECTION,
+g    GEOMETRY
+) CHARACTER SET utf8;
+SELECT * FROM t1;
+Catalog	Database	Table	Table_alias	Column	Column_alias	Type	Length	Max length	Is_null	Flags	Decimals	Charsetnr
+def	test	t1	t1	p	p	255 (type=point)	4294967295	0	Y	144	0	63
+def	test	t1	t1	ls	ls	255 (type=linestring)	4294967295	0	Y	144	0	63
+def	test	t1	t1	pl	pl	255 (type=polygon)	4294967295	0	Y	144	0	63
+def	test	t1	t1	mp	mp	255 (type=multipoint)	4294967295	0	Y	144	0	63
+def	test	t1	t1	mls	mls	255 (type=multilinestring)	4294967295	0	Y	144	0	63
+def	test	t1	t1	mpl	mpl	255 (type=multipolygon)	4294967295	0	Y	144	0	63
+def	test	t1	t1	gc	gc	255 (type=geometrycollection)	4294967295	0	Y	144	0	63
+def	test	t1	t1	g	g	255	4294967295	0	Y	144	0	63
+p	ls	pl	mp	mls	mpl	gc	g
+SELECT
+COALESCE(p) AS p,
+COALESCE(ls) AS ls,
+COALESCE(pl) AS pl,
+COALESCE(mp) AS mp,
+COALESCE(mls) AS mls,
+COALESCE(mpl) AS mpl,
+COALESCE(gc) AS gc,
+COALESCE(g) AS g
+FROM t1;
+Catalog	Database	Table	Table_alias	Column	Column_alias	Type	Length	Max length	Is_null	Flags	Decimals	Charsetnr
+def					p	255 (type=point)	4294967295	0	Y	128	0	63
+def					ls	255 (type=linestring)	4294967295	0	Y	128	0	63
+def					pl	255 (type=polygon)	4294967295	0	Y	128	0	63
+def					mp	255 (type=multipoint)	4294967295	0	Y	128	0	63
+def					mls	255 (type=multilinestring)	4294967295	0	Y	128	0	63
+def					mpl	255 (type=multipolygon)	4294967295	0	Y	128	0	63
+def					gc	255 (type=geometrycollection)	4294967295	0	Y	128	0	63
+def					g	255	4294967295	0	Y	128	0	63
+p	ls	pl	mp	mls	mpl	gc	g
+SELECT
+COALESCE(p,p),
+COALESCE(p,ls),
+COALESCE(p,pl),
+COALESCE(p,mp),
+COALESCE(p,mls),
+COALESCE(p,mpl),
+COALESCE(p,g),
+COALESCE(p,gc)
+FROM t1;
+Catalog	Database	Table	Table_alias	Column	Column_alias	Type	Length	Max length	Is_null	Flags	Decimals	Charsetnr
+def					COALESCE(p,p)	255 (type=point)	4294967295	0	Y	128	0	63
+def					COALESCE(p,ls)	255	4294967295	0	Y	128	0	63
+def					COALESCE(p,pl)	255	4294967295	0	Y	128	0	63
+def					COALESCE(p,mp)	255	4294967295	0	Y	128	0	63
+def					COALESCE(p,mls)	255	4294967295	0	Y	128	0	63
+def					COALESCE(p,mpl)	255	4294967295	0	Y	128	0	63
+def					COALESCE(p,g)	255	4294967295	0	Y	128	0	63
+def					COALESCE(p,gc)	255	4294967295	0	Y	128	0	63
+COALESCE(p,p)	COALESCE(p,ls)	COALESCE(p,pl)	COALESCE(p,mp)	COALESCE(p,mls)	COALESCE(p,mpl)	COALESCE(p,g)	COALESCE(p,gc)
+SELECT
+COALESCE(ls,p),
+COALESCE(ls,ls),
+COALESCE(ls,pl),
+COALESCE(ls,mp),
+COALESCE(ls,mls),
+COALESCE(ls,mpl),
+COALESCE(ls,g),
+COALESCE(ls,gc)
+FROM t1;
+Catalog	Database	Table	Table_alias	Column	Column_alias	Type	Length	Max length	Is_null	Flags	Decimals	Charsetnr
+def					COALESCE(ls,p)	255	4294967295	0	Y	128	0	63
+def					COALESCE(ls,ls)	255 (type=linestring)	4294967295	0	Y	128	0	63
+def					COALESCE(ls,pl)	255	4294967295	0	Y	128	0	63
+def					COALESCE(ls,mp)	255	4294967295	0	Y	128	0	63
+def					COALESCE(ls,mls)	255	4294967295	0	Y	128	0	63
+def					COALESCE(ls,mpl)	255	4294967295	0	Y	128	0	63
+def					COALESCE(ls,g)	255	4294967295	0	Y	128	0	63
+def					COALESCE(ls,gc)	255	4294967295	0	Y	128	0	63
+COALESCE(ls,p)	COALESCE(ls,ls)	COALESCE(ls,pl)	COALESCE(ls,mp)	COALESCE(ls,mls)	COALESCE(ls,mpl)	COALESCE(ls,g)	COALESCE(ls,gc)
+SELECT
+COALESCE(pl,p),
+COALESCE(pl,ls),
+COALESCE(pl,pl),
+COALESCE(pl,mp),
+COALESCE(pl,mls),
+COALESCE(pl,mpl),
+COALESCE(pl,g),
+COALESCE(pl,gc)
+FROM t1;
+Catalog	Database	Table	Table_alias	Column	Column_alias	Type	Length	Max length	Is_null	Flags	Decimals	Charsetnr
+def					COALESCE(pl,p)	255	4294967295	0	Y	128	0	63
+def					COALESCE(pl,ls)	255	4294967295	0	Y	128	0	63
+def					COALESCE(pl,pl)	255 (type=polygon)	4294967295	0	Y	128	0	63
+def					COALESCE(pl,mp)	255	4294967295	0	Y	128	0	63
+def					COALESCE(pl,mls)	255	4294967295	0	Y	128	0	63
+def					COALESCE(pl,mpl)	255	4294967295	0	Y	128	0	63
+def					COALESCE(pl,g)	255	4294967295	0	Y	128	0	63
+def					COALESCE(pl,gc)	255	4294967295	0	Y	128	0	63
+COALESCE(pl,p)	COALESCE(pl,ls)	COALESCE(pl,pl)	COALESCE(pl,mp)	COALESCE(pl,mls)	COALESCE(pl,mpl)	COALESCE(pl,g)	COALESCE(pl,gc)
+SELECT
+COALESCE(mp,p),
+COALESCE(mp,ls),
+COALESCE(mp,pl),
+COALESCE(mp,mp),
+COALESCE(mp,mls),
+COALESCE(mp,mpl),
+COALESCE(mp,g),
+COALESCE(mp,gc)
+FROM t1;
+Catalog	Database	Table	Table_alias	Column	Column_alias	Type	Length	Max length	Is_null	Flags	Decimals	Charsetnr
+def					COALESCE(mp,p)	255	4294967295	0	Y	128	0	63
+def					COALESCE(mp,ls)	255	4294967295	0	Y	128	0	63
+def					COALESCE(mp,pl)	255	4294967295	0	Y	128	0	63
+def					COALESCE(mp,mp)	255 (type=multipoint)	4294967295	0	Y	128	0	63
+def					COALESCE(mp,mls)	255	4294967295	0	Y	128	0	63
+def					COALESCE(mp,mpl)	255	4294967295	0	Y	128	0	63
+def					COALESCE(mp,g)	255	4294967295	0	Y	128	0	63
+def					COALESCE(mp,gc)	255	4294967295	0	Y	128	0	63
+COALESCE(mp,p)	COALESCE(mp,ls)	COALESCE(mp,pl)	COALESCE(mp,mp)	COALESCE(mp,mls)	COALESCE(mp,mpl)	COALESCE(mp,g)	COALESCE(mp,gc)
+SELECT
+COALESCE(mls,p),
+COALESCE(mls,ls),
+COALESCE(mls,pl),
+COALESCE(mls,mp),
+COALESCE(mls,mls),
+COALESCE(mls,mpl),
+COALESCE(mls,g),
+COALESCE(mls,gc)
+FROM t1;
+Catalog	Database	Table	Table_alias	Column	Column_alias	Type	Length	Max length	Is_null	Flags	Decimals	Charsetnr
+def					COALESCE(mls,p)	255	4294967295	0	Y	128	0	63
+def					COALESCE(mls,ls)	255	4294967295	0	Y	128	0	63
+def					COALESCE(mls,pl)	255	4294967295	0	Y	128	0	63
+def					COALESCE(mls,mp)	255	4294967295	0	Y	128	0	63
+def					COALESCE(mls,mls)	255 (type=multilinestring)	4294967295	0	Y	128	0	63
+def					COALESCE(mls,mpl)	255	4294967295	0	Y	128	0	63
+def					COALESCE(mls,g)	255	4294967295	0	Y	128	0	63
+def					COALESCE(mls,gc)	255	4294967295	0	Y	128	0	63
+COALESCE(mls,p)	COALESCE(mls,ls)	COALESCE(mls,pl)	COALESCE(mls,mp)	COALESCE(mls,mls)	COALESCE(mls,mpl)	COALESCE(mls,g)	COALESCE(mls,gc)
+SELECT
+COALESCE(mpl,p),
+COALESCE(mpl,ls),
+COALESCE(mpl,pl),
+COALESCE(mpl,mp),
+COALESCE(mpl,mls),
+COALESCE(mpl,mpl),
+COALESCE(mpl,g),
+COALESCE(mpl,gc)
+FROM t1;
+Catalog	Database	Table	Table_alias	Column	Column_alias	Type	Length	Max length	Is_null	Flags	Decimals	Charsetnr
+def					COALESCE(mpl,p)	255	4294967295	0	Y	128	0	63
+def					COALESCE(mpl,ls)	255	4294967295	0	Y	128	0	63
+def					COALESCE(mpl,pl)	255	4294967295	0	Y	128	0	63
+def					COALESCE(mpl,mp)	255	4294967295	0	Y	128	0	63
+def					COALESCE(mpl,mls)	255	4294967295	0	Y	128	0	63
+def					COALESCE(mpl,mpl)	255 (type=multipolygon)	4294967295	0	Y	128	0	63
+def					COALESCE(mpl,g)	255	4294967295	0	Y	128	0	63
+def					COALESCE(mpl,gc)	255	4294967295	0	Y	128	0	63
+COALESCE(mpl,p)	COALESCE(mpl,ls)	COALESCE(mpl,pl)	COALESCE(mpl,mp)	COALESCE(mpl,mls)	COALESCE(mpl,mpl)	COALESCE(mpl,g)	COALESCE(mpl,gc)
+SELECT
+COALESCE(g,p),
+COALESCE(g,ls),
+COALESCE(g,pl),
+COALESCE(g,mp),
+COALESCE(g,mls),
+COALESCE(g,mpl),
+COALESCE(g,g),
+COALESCE(g,gc)
+FROM t1;
+Catalog	Database	Table	Table_alias	Column	Column_alias	Type	Length	Max length	Is_null	Flags	Decimals	Charsetnr
+def					COALESCE(g,p)	255	4294967295	0	Y	128	0	63
+def					COALESCE(g,ls)	255	4294967295	0	Y	128	0	63
+def					COALESCE(g,pl)	255	4294967295	0	Y	128	0	63
+def					COALESCE(g,mp)	255	4294967295	0	Y	128	0	63
+def					COALESCE(g,mls)	255	4294967295	0	Y	128	0	63
+def					COALESCE(g,mpl)	255	4294967295	0	Y	128	0	63
+def					COALESCE(g,g)	255	4294967295	0	Y	128	0	63
+def					COALESCE(g,gc)	255	4294967295	0	Y	128	0	63
+COALESCE(g,p)	COALESCE(g,ls)	COALESCE(g,pl)	COALESCE(g,mp)	COALESCE(g,mls)	COALESCE(g,mpl)	COALESCE(g,g)	COALESCE(g,gc)
+SELECT
+COALESCE(gc,p),
+COALESCE(gc,ls),
+COALESCE(gc,pl),
+COALESCE(gc,mp),
+COALESCE(gc,mls),
+COALESCE(gc,mpl),
+COALESCE(gc,g),
+COALESCE(gc,gc)
+FROM t1;
+Catalog	Database	Table	Table_alias	Column	Column_alias	Type	Length	Max length	Is_null	Flags	Decimals	Charsetnr
+def					COALESCE(gc,p)	255	4294967295	0	Y	128	0	63
+def					COALESCE(gc,ls)	255	4294967295	0	Y	128	0	63
+def					COALESCE(gc,pl)	255	4294967295	0	Y	128	0	63
+def					COALESCE(gc,mp)	255	4294967295	0	Y	128	0	63
+def					COALESCE(gc,mls)	255	4294967295	0	Y	128	0	63
+def					COALESCE(gc,mpl)	255	4294967295	0	Y	128	0	63
+def					COALESCE(gc,g)	255	4294967295	0	Y	128	0	63
+def					COALESCE(gc,gc)	255 (type=geometrycollection)	4294967295	0	Y	128	0	63
+COALESCE(gc,p)	COALESCE(gc,ls)	COALESCE(gc,pl)	COALESCE(gc,mp)	COALESCE(gc,mls)	COALESCE(gc,mpl)	COALESCE(gc,g)	COALESCE(gc,gc)
+DROP TABLE t1;
+#
 # End of 10.5 tests
 #
diff --git a/mysql-test/main/gis.test b/mysql-test/main/gis.test
index 48f2803b27d..3249b2579d3 100644
--- a/mysql-test/main/gis.test
+++ b/mysql-test/main/gis.test
@@ -3180,6 +3180,127 @@ SELECT CONVERT(1, MULTILINESTRING);
 SELECT CONVERT(1, MULTIPOLYGON);
 
 
+--echo #
+--echo # MDEV-17832 Protocol: extensions for Pluggable types and JSON, GEOMETRY
+--echo #
+
+SET NAMES utf8;
+CREATE TABLE t1 (
+  p    POINT,
+  ls   LINESTRING,
+  pl   POLYGON,
+  mp   MULTIPOINT,
+  mls  MULTILINESTRING,
+  mpl  MULTIPOLYGON,
+  gc   GEOMETRYCOLLECTION,
+  g    GEOMETRY
+) CHARACTER SET utf8;
+
+--enable_metadata
+SELECT * FROM t1;
+
+SELECT
+  COALESCE(p) AS p,
+  COALESCE(ls) AS ls,
+  COALESCE(pl) AS pl,
+  COALESCE(mp) AS mp,
+  COALESCE(mls) AS mls,
+  COALESCE(mpl) AS mpl,
+  COALESCE(gc) AS gc,
+  COALESCE(g) AS g
+FROM t1;
+
+SELECT
+  COALESCE(p,p),
+  COALESCE(p,ls),
+  COALESCE(p,pl),
+  COALESCE(p,mp),
+  COALESCE(p,mls),
+  COALESCE(p,mpl),
+  COALESCE(p,g),
+  COALESCE(p,gc)
+FROM t1;
+
+SELECT
+  COALESCE(ls,p),
+  COALESCE(ls,ls),
+  COALESCE(ls,pl),
+  COALESCE(ls,mp),
+  COALESCE(ls,mls),
+  COALESCE(ls,mpl),
+  COALESCE(ls,g),
+  COALESCE(ls,gc)
+FROM t1;
+
+SELECT
+  COALESCE(pl,p),
+  COALESCE(pl,ls),
+  COALESCE(pl,pl),
+  COALESCE(pl,mp),
+  COALESCE(pl,mls),
+  COALESCE(pl,mpl),
+  COALESCE(pl,g),
+  COALESCE(pl,gc)
+FROM t1;
+
+SELECT
+  COALESCE(mp,p),
+  COALESCE(mp,ls),
+  COALESCE(mp,pl),
+  COALESCE(mp,mp),
+  COALESCE(mp,mls),
+  COALESCE(mp,mpl),
+  COALESCE(mp,g),
+  COALESCE(mp,gc)
+FROM t1;
+
+SELECT
+  COALESCE(mls,p),
+  COALESCE(mls,ls),
+  COALESCE(mls,pl),
+  COALESCE(mls,mp),
+  COALESCE(mls,mls),
+  COALESCE(mls,mpl),
+  COALESCE(mls,g),
+  COALESCE(mls,gc)
+FROM t1;
+
+SELECT
+  COALESCE(mpl,p),
+  COALESCE(mpl,ls),
+  COALESCE(mpl,pl),
+  COALESCE(mpl,mp),
+  COALESCE(mpl,mls),
+  COALESCE(mpl,mpl),
+  COALESCE(mpl,g),
+  COALESCE(mpl,gc)
+FROM t1;
+
+SELECT
+  COALESCE(g,p),
+  COALESCE(g,ls),
+  COALESCE(g,pl),
+  COALESCE(g,mp),
+  COALESCE(g,mls),
+  COALESCE(g,mpl),
+  COALESCE(g,g),
+  COALESCE(g,gc)
+FROM t1;
+
+SELECT
+  COALESCE(gc,p),
+  COALESCE(gc,ls),
+  COALESCE(gc,pl),
+  COALESCE(gc,mp),
+  COALESCE(gc,mls),
+  COALESCE(gc,mpl),
+  COALESCE(gc,g),
+  COALESCE(gc,gc)
+FROM t1;
+
+--disable_metadata
+DROP TABLE t1;
+
 --echo #
 --echo # End of 10.5 tests
 --echo #
diff --git a/mysql-test/main/mysql-metadata.result b/mysql-test/main/mysql-metadata.result
new file mode 100644
index 00000000000..ce6c77ed7fa
--- /dev/null
+++ b/mysql-test/main/mysql-metadata.result
@@ -0,0 +1,87 @@
+#
+# MDEV-17832 Protocol: extensions for Pluggable types and JSON, GEOMETRY
+#
+SET NAMES utf8;
+CREATE TABLE t1 (
+js0 JSON,
+js1 TEXT CHECK (JSON_VALID(js1)),
+js2 TEXT CHECK (LENGTH(js2) > 0 AND JSON_VALID(js2)),
+js3 TEXT CHECK (LENGTH(js2) > 0 OR JSON_VALID(js2))
+) CHARACTER SET utf8;
+--------------
+SELECT * FROM t1
+--------------
+
+Field   1:  `js0`
+Catalog:    `def`
+Database:   `test`
+Table:      `t1`
+Org_table:  `t1`
+Type:       BLOB (format=json)
+Collation:  latin1_swedish_ci (8)
+Length:     4294967295
+Max_length: 0
+Decimals:   0
+Flags:      BLOB BINARY 
+
+Field   2:  `js1`
+Catalog:    `def`
+Database:   `test`
+Table:      `t1`
+Org_table:  `t1`
+Type:       BLOB (format=json)
+Collation:  latin1_swedish_ci (8)
+Length:     65535
+Max_length: 0
+Decimals:   0
+Flags:      BLOB 
+
+Field   3:  `js2`
+Catalog:    `def`
+Database:   `test`
+Table:      `t1`
+Org_table:  `t1`
+Type:       BLOB (format=json)
+Collation:  latin1_swedish_ci (8)
+Length:     65535
+Max_length: 0
+Decimals:   0
+Flags:      BLOB 
+
+Field   4:  `js3`
+Catalog:    `def`
+Database:   `test`
+Table:      `t1`
+Org_table:  `t1`
+Type:       BLOB
+Collation:  latin1_swedish_ci (8)
+Length:     65535
+Max_length: 0
+Decimals:   0
+Flags:      BLOB 
+
+
+0 rows in set (TIME)
+
+Bye
+--------------
+SELECT JSON_COMPACT(js0) FROM t1
+--------------
+
+Field   1:  `JSON_COMPACT(js0)`
+Catalog:    `def`
+Database:   ``
+Table:      ``
+Org_table:  ``
+Type:       LONG_BLOB (format=json)
+Collation:  binary (63)
+Length:     4294967295
+Max_length: 0
+Decimals:   0
+Flags:      BINARY 
+
+
+0 rows in set (TIME)
+
+Bye
+DROP TABLE t1;
diff --git a/mysql-test/main/mysql-metadata.test b/mysql-test/main/mysql-metadata.test
new file mode 100644
index 00000000000..bab44496f78
--- /dev/null
+++ b/mysql-test/main/mysql-metadata.test
@@ -0,0 +1,22 @@
+-- source include/have_working_dns.inc
+-- source include/not_embedded.inc
+
+--echo #
+--echo # MDEV-17832 Protocol: extensions for Pluggable types and JSON, GEOMETRY
+--echo #
+
+SET NAMES utf8;
+CREATE TABLE t1 (
+  js0 JSON,
+  js1 TEXT CHECK (JSON_VALID(js1)),
+  js2 TEXT CHECK (LENGTH(js2) > 0 AND JSON_VALID(js2)),
+  js3 TEXT CHECK (LENGTH(js2) > 0 OR JSON_VALID(js2))
+) CHARACTER SET utf8;
+
+--replace_regex /0 rows in set [(].*[)]/0 rows in set (TIME)/
+--exec $MYSQL -vvv --column-type-info --database=test -e "SELECT * FROM t1;"
+
+--replace_regex /0 rows in set [(].*[)]/0 rows in set (TIME)/
+--exec $MYSQL -vvv --column-type-info --database=test -e "SELECT JSON_COMPACT(js0) FROM t1;"
+
+DROP TABLE t1;
diff --git a/mysql-test/main/type_json.result b/mysql-test/main/type_json.result
index 96e96cca404..2c4f7b7d42b 100644
--- a/mysql-test/main/type_json.result
+++ b/mysql-test/main/type_json.result
@@ -98,3 +98,33 @@ select cast('{a:1}' as text);
 ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'text)' at line 1
 select cast('{a:1}' as json);
 ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'json)' at line 1
+#
+# Start of 10.5 tests
+#
+#
+# MDEV-17832 Protocol: extensions for Pluggable types and JSON, GEOMETRY
+#
+SET NAMES utf8;
+CREATE TABLE t1 (
+js0 JSON,
+js1 TEXT CHECK (JSON_VALID(js1)),
+js2 TEXT CHECK (LENGTH(js2) > 0 AND JSON_VALID(js2)),
+js3 TEXT CHECK (LENGTH(js2) > 0 OR JSON_VALID(js2))
+) CHARACTER SET utf8;
+SELECT * FROM t1;
+Catalog	Database	Table	Table_alias	Column	Column_alias	Type	Length	Max length	Is_null	Flags	Decimals	Charsetnr
+def	test	t1	t1	js0	js0	252 (format=json)	4294967295	0	Y	144	0	33
+def	test	t1	t1	js1	js1	252 (format=json)	196605	0	Y	16	0	33
+def	test	t1	t1	js2	js2	252 (format=json)	196605	0	Y	16	0	33
+def	test	t1	t1	js3	js3	252	196605	0	Y	16	0	33
+js0	js1	js2	js3
+SELECT js0, JSON_COMPACT(js0), JSON_COMPACT('{}') FROM t1;
+Catalog	Database	Table	Table_alias	Column	Column_alias	Type	Length	Max length	Is_null	Flags	Decimals	Charsetnr
+def	test	t1	t1	js0	js0	252 (format=json)	4294967295	0	Y	144	0	33
+def					JSON_COMPACT(js0)	251 (format=json)	4294967295	0	Y	128	0	63
+def					JSON_COMPACT('{}')	253 (format=json)	6	0	Y	128	0	63
+js0	JSON_COMPACT(js0)	JSON_COMPACT('{}')
+DROP TABLE t1;
+#
+# End of 10.5 tests
+#
diff --git a/mysql-test/main/type_json.test b/mysql-test/main/type_json.test
index bd13dc1fcf4..7ab0af20e03 100644
--- a/mysql-test/main/type_json.test
+++ b/mysql-test/main/type_json.test
@@ -64,3 +64,30 @@ drop table t1;
 select cast('{a:1}' as text);
 --error ER_PARSE_ERROR
 select cast('{a:1}' as json);
+
+--echo #
+--echo # Start of 10.5 tests
+--echo #
+
+--echo #
+--echo # MDEV-17832 Protocol: extensions for Pluggable types and JSON, GEOMETRY
+--echo #
+
+SET NAMES utf8;
+CREATE TABLE t1 (
+  js0 JSON,
+  js1 TEXT CHECK (JSON_VALID(js1)),
+  js2 TEXT CHECK (LENGTH(js2) > 0 AND JSON_VALID(js2)),
+  js3 TEXT CHECK (LENGTH(js2) > 0 OR JSON_VALID(js2))
+) CHARACTER SET utf8;
+--disable_ps_protocol
+--enable_metadata
+SELECT * FROM t1;
+SELECT js0, JSON_COMPACT(js0), JSON_COMPACT('{}') FROM t1;
+--disable_metadata
+--enable_ps_protocol
+DROP TABLE t1;
+
+--echo #
+--echo # End of 10.5 tests
+--echo #
diff --git a/plugin/type_inet/mysql-test/type_inet/type_inet6.result b/plugin/type_inet/mysql-test/type_inet/type_inet6.result
index 3dbbbccb100..15c8dbf8ca9 100644
--- a/plugin/type_inet/mysql-test/type_inet/type_inet6.result
+++ b/plugin/type_inet/mysql-test/type_inet/type_inet6.result
@@ -40,12 +40,12 @@ CREATE TABLE t1 (a INET6);
 INSERT INTO t1 VALUES ('::1');
 SELECT * FROM t1;
 Catalog	Database	Table	Table_alias	Column	Column_alias	Type	Length	Max length	Is_null	Flags	Decimals	Charsetnr
-def	test	t1	t1	a	a	254	39	3	Y	160	0	8
+def	test	t1	t1	a	a	254 (type=inet6)	39	3	Y	160	0	8
 a
 ::1
 SELECT CAST('::' AS INET6) AS a;
 Catalog	Database	Table	Table_alias	Column	Column_alias	Type	Length	Max length	Is_null	Flags	Decimals	Charsetnr
-def					a	254	39	2	N	33	0	8
+def					a	254 (type=inet6)	39	2	N	33	0	8
 a
 ::
 DROP TABLE t1;
diff --git a/plugin/type_inet/mysql-test/type_inet/type_inet6_mysql.result b/plugin/type_inet/mysql-test/type_inet/type_inet6_mysql.result
index 468e9fea41e..868b9902f5c 100644
--- a/plugin/type_inet/mysql-test/type_inet/type_inet6_mysql.result
+++ b/plugin/type_inet/mysql-test/type_inet/type_inet6_mysql.result
@@ -4,12 +4,36 @@ Catalog:    `def`
 Database:   `test`
 Table:      `t1`
 Org_table:  `t1`
-Type:       STRING
+Type:       STRING (type=inet6)
 Collation:  latin1_swedish_ci (8)
 Length:     39
 Max_length: 0
 Decimals:   0
 Flags:      UNSIGNED BINARY 
 
+Field   2:  `b`
+Catalog:    `def`
+Database:   ``
+Table:      ``
+Org_table:  ``
+Type:       STRING (type=inet6)
+Collation:  latin1_swedish_ci (8)
+Length:     39
+Max_length: 0
+Decimals:   0
+Flags:      NOT_NULL UNSIGNED 
+
+Field   3:  `c`
+Catalog:    `def`
+Database:   ``
+Table:      ``
+Org_table:  ``
+Type:       STRING (type=inet6)
+Collation:  latin1_swedish_ci (8)
+Length:     39
+Max_length: 0
+Decimals:   0
+Flags:      UNSIGNED 
+
 
 DROP TABLE t1;
diff --git a/plugin/type_inet/mysql-test/type_inet/type_inet6_mysql.test b/plugin/type_inet/mysql-test/type_inet/type_inet6_mysql.test
index 5e6ac6f3804..dfb300816d9 100644
--- a/plugin/type_inet/mysql-test/type_inet/type_inet6_mysql.test
+++ b/plugin/type_inet/mysql-test/type_inet/type_inet6_mysql.test
@@ -2,5 +2,5 @@
 -- source include/not_embedded.inc
 
 CREATE TABLE t1 (a INET6);
---exec $MYSQL -t test --column-type-info -e "SELECT * FROM t1" 2>&1 
+--exec $MYSQL -t test --column-type-info -e "SELECT a, CAST('::' AS INET6) AS b, COALESCE(a) AS c FROM t1" 2>&1 
 DROP TABLE t1;
diff --git a/plugin/type_inet/sql_type_inet.cc b/plugin/type_inet/sql_type_inet.cc
index 6803bdba434..79f85cd1f19 100644
--- a/plugin/type_inet/sql_type_inet.cc
+++ b/plugin/type_inet/sql_type_inet.cc
@@ -705,6 +705,11 @@ class Field_inet6: public Field
     str.set_ascii(name.ptr(), name.length());
   }
 
+  bool extended_type_info_append_to(Extended_type_info *to) const override
+  {
+    return type_handler_inet6.extended_type_info_append_to(to);
+  }
+
   bool validate_value_in_record(THD *thd, const uchar *record) const override
   {
     return false;
diff --git a/plugin/type_inet/sql_type_inet.h b/plugin/type_inet/sql_type_inet.h
index b483ff94e8d..5a06999cd71 100644
--- a/plugin/type_inet/sql_type_inet.h
+++ b/plugin/type_inet/sql_type_inet.h
@@ -328,6 +328,15 @@ class Type_handler_inet6: public Type_handler
   {
     return PROTOCOL_SEND_STRING;
   }
+  bool extended_type_info_append_to(Extended_type_info *to) const
+  {
+    return to->append_type(name().lex_cstring());
+  }
+  bool Item_append_extended_type_info(Extended_type_info *to,
+                                      const Item *item) const override
+  {
+    return extended_type_info_append_to(to);
+  }
 
   enum_field_types field_type() const override
   {
diff --git a/sql/field.cc b/sql/field.cc
index 1ce49b0bdfa..ab05adb37a1 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -2039,6 +2039,7 @@ void Field::make_send_field(Send_field *field)
   field->set_handler(type_handler());
   field->flags=table->maybe_null ? (flags & ~NOT_NULL_FLAG) : flags;
   field->decimals= 0;
+  extended_type_info_append_to(&field->extended_type_info);
 }
 
 
@@ -7030,6 +7031,28 @@ Field_longstr::report_if_important_data(const char *pstr, const char *end,
 }
 
 
+/*
+  This is JSON specific.
+  We should eventually add Field_json_varchar and Field_json_blob
+  and move make_send_field() to the new classes.
+*/
+void Field_longstr::make_send_field(Send_field *field)
+{
+  Field_str::make_send_field(field);
+  if (check_constraint)
+  {
+    /*
+      Append the format that is implicitly implied by the CHECK CONSTRAINT.
+      For example:
+        CREATE TABLE t1 (js longtext DEFAULT NULL CHECK (json_valid(a)));
+        SELECT j FROM t1;
+      will add "format=json" to the extended type info metadata for t1.js.
+    */
+    check_constraint->expr->
+      append_format_by_check_constraint(&field->extended_type_info);
+  }
+}
+
 	/* Copy a string and fill with space */
 
 int Field_string::store(const char *from, size_t length,CHARSET_INFO *cs)
diff --git a/sql/field.h b/sql/field.h
index 4a8eec35b05..7391d0566a0 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -1299,6 +1299,10 @@ class Field: public Value_source
   */
   virtual void sql_type(String &str) const =0;
   virtual void sql_rpl_type(String *str) const { sql_type(*str); }
+  virtual bool extended_type_info_append_to(Extended_type_info *to) const
+  {
+    return false;
+  }
   virtual uint size_of() const =0;		// For new field
   inline bool is_null(my_ptrdiff_t row_offset= 0) const
   {
@@ -2123,6 +2127,7 @@ class Field_longstr :public Field_str
                                     const Conv_param &param) const;
   int store_decimal(const my_decimal *d);
   uint32 max_data_length() const;
+  void make_send_field(Send_field *) override;
 
   bool is_varchar_and_in_write_set() const
   {
@@ -5467,6 +5472,7 @@ class Send_field :public Sql_alloc,
   LEX_CSTRING db_name;
   LEX_CSTRING table_name, org_table_name;
   LEX_CSTRING col_name, org_col_name;
+  Extended_type_info extended_type_info;
   ulong length;
   uint flags, decimals;
   Send_field(Field *field)
diff --git a/sql/item.cc b/sql/item.cc
index 6b585325439..a55ce723f6c 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -6197,6 +6197,8 @@ void Item::init_make_send_field(Send_field *tmp_field,
   tmp_field->decimals=decimals;
   if (unsigned_flag)
     tmp_field->flags |= UNSIGNED_FLAG;
+  tmp_field->extended_type_info.init();
+  h->Item_append_extended_type_info(&tmp_field->extended_type_info, this);
 }
 
 void Item::make_send_field(THD *thd, Send_field *tmp_field)
diff --git a/sql/item.h b/sql/item.h
index 6a9d401b101..3f3d72b7dfc 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -1814,6 +1814,14 @@ class Item: public Value_source,
   /* This is to handle printing of default values */
   virtual bool need_parentheses_in_default() { return false; }
   virtual void save_in_result_field(bool no_conversions) {}
+  /*
+    Data type format implied by the CHECK CONSTRAINT,
+    to be sent to the client in the result set metadata.
+  */
+  virtual bool append_format_by_check_constraint(Extended_type_info *) const
+  {
+    return false;
+  }
   /*
     set value of aggregate function in case of no rows for grouping were found
   */
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 6df2b5dbd3a..7d6ab23de14 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -6481,6 +6481,20 @@ Item *Item_cond_and::neg_transformer(THD *thd)	/* NOT(a AND b AND ...)  -> */
 }
 
 
+bool
+Item_cond_and::append_format_by_check_constraint(Extended_type_info *to) const
+{
+  List_iterator_fast<Item> li(const_cast<List<Item>&>(list));
+  Item *item;
+  while ((item= li++))
+  {
+    if (item->append_format_by_check_constraint(to))
+      return true;
+  }
+  return false;
+}
+
+
 Item *Item_cond_or::neg_transformer(THD *thd)	/* NOT(a OR b OR ...)  -> */
 					/* NOT a AND NOT b AND ... */
 {
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 9b20fa50214..7f769f08e42 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -3349,6 +3349,7 @@ class Item_cond_and :public Item_cond
   COND *build_equal_items(THD *thd, COND_EQUAL *inherited,
                           bool link_item_fields,
                           COND_EQUAL **cond_equal_ref);
+  bool append_format_by_check_constraint(Extended_type_info *to) const;
   void add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level,
                       table_map usable_tables, SARGABLE_PARAM **sargables);
   SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr);
diff --git a/sql/item_jsonfunc.h b/sql/item_jsonfunc.h
index e61d0875056..fe0d434d79b 100644
--- a/sql/item_jsonfunc.h
+++ b/sql/item_jsonfunc.h
@@ -84,6 +84,11 @@ class Item_func_json_valid: public Item_bool_func
     maybe_null= 1;
     return FALSE;
   }
+  bool append_format_by_check_constraint(Extended_type_info *to) const
+  {
+    static const Lex_cstring fmt(STRING_WITH_LEN("json"));
+    return to->append_format(fmt);
+  }
   Item *get_copy(THD *thd)
   { return get_item_copy<Item_func_json_valid>(thd, this); }
 };
@@ -118,6 +123,12 @@ class Item_json_func: public Item_str_func
   Item_json_func(THD *thd, List<Item> &list)
    :Item_str_func(thd, list) { }
   bool is_json_type() { return true; }
+  void make_send_field(THD *thd, Send_field *tmp_field)
+  {
+    Item_str_func::make_send_field(thd, tmp_field);
+    static const Lex_cstring fmt(STRING_WITH_LEN("json"));
+    tmp_field->extended_type_info.append_format(fmt);
+  }
 };
 
 
diff --git a/sql/protocol.cc b/sql/protocol.cc
index 30e4cc6c214..af1cf5631e0 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -816,8 +816,19 @@ bool Protocol_text::store_field_metadata(const THD * thd,
         store_str(field.table_name, cs, thd_charset) ||
         store_str(field.org_table_name, cs, thd_charset) ||
         store_str(field.col_name, cs, thd_charset) ||
-        store_str(field.org_col_name, cs, thd_charset) ||
-        packet->realloc(packet->length() + 12))
+        store_str(field.org_col_name, cs, thd_charset))
+      return true;
+    if (thd->client_capabilities & MARIADB_CLIENT_EXTENDED_TYPE_INFO)
+    {
+      /*
+        Don't apply character set conversion:
+        extended metadata is a binary encoded data.
+      */
+      if (store_str(field.extended_type_info.lex_cstring(),
+                    cs, &my_charset_bin))
+        return true;
+    }
+    if (packet->realloc(packet->length() + 12))
       return true;
     /* Store fixed length fields */
     pos= (char*) packet->end();
diff --git a/sql/sql_type.h b/sql/sql_type.h
index ce87c8e9d93..c053107de2e 100644
--- a/sql/sql_type.h
+++ b/sql/sql_type.h
@@ -23,6 +23,7 @@
 
 
 #include "mysqld.h"
+#include "lex_string.h"
 #include "sql_array.h"
 #include "sql_const.h"
 #include "sql_time.h"
@@ -137,6 +138,40 @@ enum column_definition_type_t
 };
 
 
+class Extended_type_info: public Binary_string
+{
+public:
+  void init() { length(0); }
+  bool append_chunk(mariadb_field_metadata_attr_t type,
+                    const LEX_CSTRING &value)
+  {
+    /*
+      If we eventually support many metadata chunk types and long metadata
+      values, we'll need to encode type and length using net_store_length()
+      and do corresponding changes to the unpacking code in libmariadb.
+      For now let's just assert that type and length fit into one byte.
+    */
+    DBUG_ASSERT(net_length_size(type) == 1);
+    DBUG_ASSERT(net_length_size(value.length) == 1);
+    size_t nbytes= 1/*type*/ + 1/*length*/ + value.length;
+    if (reserve(nbytes))
+      return true;
+    qs_append((char) (uchar) type);
+    qs_append((char) (uchar) value.length);
+    qs_append(&value);
+    return false;
+  }
+  bool append_format(const LEX_CSTRING &str)
+  {
+    return append_chunk(MARIADB_FIELD_METADATA_FORMAT_NAME, str);
+  }
+  bool append_type(const LEX_CSTRING &str)
+  {
+    return append_chunk(MARIADB_FIELD_METADATA_DATA_TYPE_NAME, str);
+  }
+};
+
+
 class Data_type_statistics
 {
 public:
@@ -3426,6 +3461,11 @@ class Type_handler
     return field_type();
   }
   virtual protocol_send_type_t protocol_send_type() const= 0;
+  virtual bool Item_append_extended_type_info(Extended_type_info *to,
+                                              const Item *item) const
+  {
+    return false;
+  }
   virtual Item_result result_type() const= 0;
   virtual Item_result cmp_type() const= 0;
   virtual enum_dynamic_column_type
diff --git a/sql/sql_type_geom.cc b/sql/sql_type_geom.cc
index 3ca0eaabc3f..41c5c6741d3 100644
--- a/sql/sql_type_geom.cc
+++ b/sql/sql_type_geom.cc
@@ -34,6 +34,16 @@ Named_type_handler<Type_handler_geometrycollection> type_handler_geometrycollect
 
 Type_collection_geometry        type_collection_geometry;
 
+
+bool
+Type_handler_geometry::extended_type_info_append_to(Extended_type_info *to)
+                                                    const
+{
+  return geometry_type() == GEOM_GEOMETRY ? false :
+         to->append_type(name().lex_cstring());
+}
+
+
 const Type_handler_geometry *
 Type_handler_geometry::type_handler_geom_by_type(uint type)
 {
diff --git a/sql/sql_type_geom.h b/sql/sql_type_geom.h
index a2baa5ae299..cb201271469 100644
--- a/sql/sql_type_geom.h
+++ b/sql/sql_type_geom.h
@@ -39,9 +39,15 @@ class Type_handler_geometry: public Type_handler_string_result
                                          Item * const *args,
                                          uint start, uint end);
   static const Type_handler_geometry *type_handler_geom_by_type(uint type);
+  bool extended_type_info_append_to(Extended_type_info *to) const;
 public:
   virtual ~Type_handler_geometry() {}
   enum_field_types field_type() const override { return MYSQL_TYPE_GEOMETRY; }
+  bool Item_append_extended_type_info(Extended_type_info *to,
+                                      const Item *item) const override
+  {
+    return extended_type_info_append_to(to);
+  }
   bool is_param_long_data_type() const override { return true; }
   uint32 max_display_length_for_field(const Conv_source &src) const override;
   uint32 calc_pack_length(uint32 length) const override;
@@ -366,6 +372,10 @@ class Field_geom :public Field_blob
                                   const Item *item,
                                   bool is_eq_func) const override;
   void sql_type(String &str) const override;
+  bool extended_type_info_append_to(Extended_type_info *to) const override
+  {
+    return m_type_handler->extended_type_info_append_to(to);
+  }
   Copy_func *get_copy_func(const Field *from) const override
   {
     const Type_handler_geometry *fth=
