maria-developers team mailing list archive
-
maria-developers team
-
Mailing list archive
-
Message #12111
Re: Please review MDEV-17832 Protocol: extensions for Pluggable types and JSON, GEOMETRY
Hi Sergei,
I addressed most of your suggestions.
Except these ones:
On 3/4/20 4:25 PM, Sergei Golubchik wrote:
--- 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);
+ }
+
I don't like that it needs to be done _per plugin_
We should add Field_fixed_binary and move a number of Field_inet6
methods there, including this method. I propose I do it separately.
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 6df2b5dbd3a..949e389698a 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::format_by_check_constraint_append_to(Extended_type_info *to) const
+{
+ List_iterator_fast<Item> li(const_cast<List<Item>&>(list));
Why the cast?
Same here. It's a separate problem.
It needs reorganization in List_iterator_fast.
I don't like the idea of adding a new List_iterator_fast constructor to
with a cast inside.
Also, please discuss with Georg the exact API of the new library functions.
Thanks!
commit 4da75d8ed70a7814a60b66e985bc42068ff90f50
Author: Alexander Barkov <bar@xxxxxxxxxxx>
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 ¶m) 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=
diff --git a/include/ma_common.h b/include/ma_common.h
index c598136..a583f10 100644
--- a/include/ma_common.h
+++ b/include/ma_common.h
@@ -113,3 +113,9 @@ struct st_mariadb_extension {
(a)->options.extension->key : 0
#endif
+
+
+typedef struct st_mariadb_field_extension
+{
+ MARIADB_CONST_STRING metadata[MARIADB_FIELD_METADATA_LAST+1]; /* 10.5 */
+} MA_FIELD_EXTENSION;
diff --git a/include/ma_priv.h b/include/ma_priv.h
index 7d30094..5ee083c 100644
--- a/include/ma_priv.h
+++ b/include/ma_priv.h
@@ -24,8 +24,27 @@
void free_rows(MYSQL_DATA *cur);
int ma_multi_command(MYSQL *mysql, enum enum_multi_status status);
-MYSQL_FIELD * unpack_fields(MYSQL_DATA *data,
+MYSQL_FIELD * unpack_fields(const MYSQL *mysql, MYSQL_DATA *data,
MA_MEM_ROOT *alloc,uint fields,
my_bool default_value);
+static inline my_bool ma_has_extended_type_info(const MYSQL *mysql)
+{
+ return ((mysql->extension->mariadb_server_capabilities << 32) &
+ MARIADB_CLIENT_EXTENDED_TYPE_INFO) != 0;
+}
+
+static inline uint ma_extended_type_info_rows(const MYSQL *mysql)
+{
+ return ma_has_extended_type_info(mysql) ? 1 : 0;
+}
+
+static inline uint ma_result_set_rows(const MYSQL *mysql)
+{
+ return ma_has_extended_type_info(mysql) ? 9 : 8;
+}
+
+MA_FIELD_EXTENSION *ma_field_extension_deep_dup(MA_MEM_ROOT *memroot,
+ const MA_FIELD_EXTENSION *from);
+
#endif
diff --git a/include/mariadb_com.h b/include/mariadb_com.h
index 9a5da28..aafa2a5 100644
--- a/include/mariadb_com.h
+++ b/include/mariadb_com.h
@@ -170,13 +170,16 @@ enum enum_server_command
#define MARIADB_CLIENT_PROGRESS (1ULL << 32)
#define MARIADB_CLIENT_COM_MULTI (1ULL << 33)
#define MARIADB_CLIENT_STMT_BULK_OPERATIONS (1ULL << 34)
+/* support of extended data type/format information, since 10.5.0 */
+#define MARIADB_CLIENT_EXTENDED_TYPE_INFO (1ULL << 35)
#define IS_MARIADB_EXTENDED_SERVER(mysql)\
!(mysql->server_capabilities & CLIENT_MYSQL)
#define MARIADB_CLIENT_SUPPORTED_FLAGS (MARIADB_CLIENT_PROGRESS |\
MARIADB_CLIENT_COM_MULTI |\
- MARIADB_CLIENT_STMT_BULK_OPERATIONS)
+ MARIADB_CLIENT_STMT_BULK_OPERATIONS|\
+ MARIADB_CLIENT_EXTENDED_TYPE_INFO)
#define CLIENT_SUPPORTED_FLAGS (CLIENT_MYSQL |\
CLIENT_FOUND_ROWS |\
diff --git a/include/mysql.h b/include/mysql.h
index dcaf316..476f5c1 100644
--- a/include/mysql.h
+++ b/include/mysql.h
@@ -61,6 +61,14 @@ typedef int my_socket;
#include "ma_list.h"
#include "mariadb_ctype.h"
+
+typedef struct st_ma_const_string
+{
+ const char *str;
+ size_t length;
+} MARIADB_CONST_STRING;
+
+
#ifndef ST_MA_USED_MEM_DEFINED
#define ST_MA_USED_MEM_DEFINED
typedef struct st_ma_used_mem { /* struct for once_alloc */
@@ -383,6 +391,20 @@ typedef struct
void *extension;
} MYSQL_PARAMETERS;
+
+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
+
+
+MARIADB_CONST_STRING
+ mariadb_field_metadata_attr(const MYSQL_FIELD *field,
+ enum mariadb_field_metadata_attr_t type);
+
#ifndef _mysql_time_h_
enum enum_mysql_timestamp_type
{
diff --git a/libmariadb/mariadb_lib.c b/libmariadb/mariadb_lib.c
index 1063f14..4c5536e 100644
--- a/libmariadb/mariadb_lib.c
+++ b/libmariadb/mariadb_lib.c
@@ -751,6 +751,79 @@ my_bool _mariadb_set_conf_option(MYSQL *mysql, const char *config_option, const
return 1;
}
+
+static MARIADB_CONST_STRING null_const_string= {0,0};
+
+/***************************************************************************
+** Allocate a string copy on memroot
+***************************************************************************/
+static MARIADB_CONST_STRING ma_const_string_copy_root(MA_MEM_ROOT *memroot,
+ const char *str,
+ size_t length)
+{
+ MARIADB_CONST_STRING res;
+ if (!str || !(res.str= ma_memdup_root(memroot, str, length)))
+ return null_const_string;
+ res.length= length;
+ return res;
+}
+
+
+/***************************************************************************
+** Allocate and initialize MA_FIELD_EXTENSION
+***************************************************************************/
+MA_FIELD_EXTENSION *new_ma_field_extension(MA_MEM_ROOT *memroot)
+{
+ MA_FIELD_EXTENSION *ext= ma_alloc_root(memroot, sizeof(MA_FIELD_EXTENSION));
+ if (ext)
+ memset((void *) ext, 0, sizeof(*ext));
+ return ext;
+}
+
+
+/***************************************************************************
+** Populate field extension from a type info packet
+***************************************************************************/
+
+static void ma_field_extension_init_type_info(MA_MEM_ROOT *memroot,
+ MA_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;
+ }
+}
+
+
+/***************************************************************************
+** Allocate a field extension deep copy
+***************************************************************************/
+
+MA_FIELD_EXTENSION *ma_field_extension_deep_dup(MA_MEM_ROOT *memroot,
+ const MA_FIELD_EXTENSION *from)
+{
+ MA_FIELD_EXTENSION *ext= new_ma_field_extension(memroot);
+ uint i;
+ if (!ext)
+ return NULL;
+ for (i= 0; i < MARIADB_FIELD_METADATA_LAST; i++)
+ {
+ if (from->metadata[i].str)
+ ext->metadata[i]= ma_const_string_copy_root(memroot,
+ from->metadata[i].str,
+ from->metadata[i].length);
+ }
+ return ext;
+}
+
/***************************************************************************
** Change field rows to field structs
***************************************************************************/
@@ -771,7 +844,8 @@ static size_t rset_field_offsets[]= {
};
MYSQL_FIELD *
-unpack_fields(MYSQL_DATA *data,MA_MEM_ROOT *alloc,uint fields,
+unpack_fields(const MYSQL *mysql,
+ MYSQL_DATA *data, MA_MEM_ROOT *alloc, uint fields,
my_bool default_value)
{
MYSQL_ROWS *row;
@@ -804,7 +878,20 @@ unpack_fields(MYSQL_DATA *data,MA_MEM_ROOT *alloc,uint fields,
}
}
- p= (char *)row->data[6];
+ field->extension= NULL;
+ if (ma_has_extended_type_info(mysql))
+ {
+ if (row->data[i+1] - row->data[i] > 1)
+ {
+ size_t len= row->data[i+1] - row->data[i] - 1;
+ MA_FIELD_EXTENSION *ext= new_ma_field_extension(alloc);
+ if ((field->extension= ext))
+ ma_field_extension_init_type_info(alloc, ext, row->data[i], len);
+ }
+ i++;
+ }
+
+ p= (char *)row->data[i];
/* filler */
field->charsetnr= uint2korr(p);
p+= 2;
@@ -823,10 +910,11 @@ unpack_fields(MYSQL_DATA *data,MA_MEM_ROOT *alloc,uint fields,
if (INTERNAL_NUM_FIELD(field))
field->flags|= NUM_FLAG;
+ i++;
/* This is used by deprecated function mysql_list_fields only,
however the reported length is not correct, so we always zero it */
- if (default_value && row->data[7])
- field->def=ma_strdup_root(alloc,(char*) row->data[7]);
+ if (default_value && row->data[i])
+ field->def=ma_strdup_root(alloc,(char*) row->data[i]);
else
field->def=0;
field->def_length= 0;
@@ -2193,9 +2281,10 @@ get_info:
mysql->server_status|= SERVER_STATUS_IN_TRANS;
mysql->extra_info= net_field_length_ll(&pos); /* Maybe number of rec */
- if (!(fields=mysql->methods->db_read_rows(mysql,(MYSQL_FIELD*) 0,8)))
+ if (!(fields=mysql->methods->db_read_rows(mysql,(MYSQL_FIELD*) 0,
+ ma_result_set_rows(mysql))))
return(-1);
- if (!(mysql->fields=unpack_fields(fields,&mysql->field_alloc,
+ if (!(mysql->fields=unpack_fields(mysql, fields, &mysql->field_alloc,
(uint) field_count, 1)))
return(-1);
mysql->status=MYSQL_STATUS_GET_RESULT;
@@ -2345,6 +2434,21 @@ mysql_fetch_field(MYSQL_RES *result)
return &result->fields[result->current_field++];
}
+
+/**************************************************************************
+** Return mysql field metadata
+**************************************************************************/
+MARIADB_CONST_STRING
+mariadb_field_metadata_attr(const MYSQL_FIELD *field,
+ enum mariadb_field_metadata_attr_t type)
+{
+ MA_FIELD_EXTENSION *ext= (MA_FIELD_EXTENSION*) field->extension;
+ if (!ext || type > MARIADB_FIELD_METADATA_LAST)
+ return null_const_string;
+ return ext->metadata[type];
+}
+
+
/**************************************************************************
** Return next row of the query results
**************************************************************************/
@@ -2513,7 +2617,8 @@ mysql_list_fields(MYSQL *mysql, const char *table, const char *wild)
length= snprintf(buff, 128, "%s%c%s", table, '\0', wild ? wild : "");
if (ma_simple_command(mysql, COM_FIELD_LIST,buff,length,1,0) ||
- !(query = mysql->methods->db_read_rows(mysql,(MYSQL_FIELD*) 0,8)))
+ !(query = mysql->methods->db_read_rows(mysql,(MYSQL_FIELD*) 0,
+ ma_result_set_rows(mysql))))
return(NULL);
free_old_query(mysql);
@@ -2526,7 +2631,7 @@ mysql_list_fields(MYSQL *mysql, const char *table, const char *wild)
mysql->fields=0;
result->eof=1;
result->field_count = (uint) query->rows;
- result->fields= unpack_fields(query,&result->field_alloc,
+ result->fields= unpack_fields(mysql, query, &result->field_alloc,
result->field_count, 1);
if (result->fields)
return(result);
@@ -2552,8 +2657,8 @@ mysql_list_processes(MYSQL *mysql)
field_count=(uint) net_field_length(&pos);
if (!(fields = mysql->methods->db_read_rows(mysql,(MYSQL_FIELD*) 0,5)))
return(NULL);
- if (!(mysql->fields=unpack_fields(fields, &mysql->field_alloc,
- field_count, 0)))
+ if (!(mysql->fields= unpack_fields(mysql, fields, &mysql->field_alloc,
+ field_count, 0)))
return(NULL);
mysql->status=MYSQL_STATUS_GET_RESULT;
mysql->field_count=field_count;
diff --git a/libmariadb/mariadb_stmt.c b/libmariadb/mariadb_stmt.c
index 86e39ca..433e495 100644
--- a/libmariadb/mariadb_stmt.c
+++ b/libmariadb/mariadb_stmt.c
@@ -1581,7 +1581,8 @@ my_bool mthd_stmt_get_param_metadata(MYSQL_STMT *stmt)
{
MYSQL_DATA *result;
- if (!(result= stmt->mysql->methods->db_read_rows(stmt->mysql, (MYSQL_FIELD *)0, 7)))
+ if (!(result= stmt->mysql->methods->db_read_rows(stmt->mysql, (MYSQL_FIELD *)0,
+ 7 + ma_extended_type_info_rows(stmt->mysql))))
return(1);
free_rows(result);
@@ -1593,9 +1594,10 @@ my_bool mthd_stmt_get_result_metadata(MYSQL_STMT *stmt)
MYSQL_DATA *result;
MA_MEM_ROOT *fields_ma_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root;
- if (!(result= stmt->mysql->methods->db_read_rows(stmt->mysql, (MYSQL_FIELD *)0, 7)))
+ if (!(result= stmt->mysql->methods->db_read_rows(stmt->mysql, (MYSQL_FIELD *)0,
+ 7 + ma_extended_type_info_rows(stmt->mysql))))
return(1);
- if (!(stmt->fields= unpack_fields(result,fields_ma_alloc_root,
+ if (!(stmt->fields= unpack_fields(stmt->mysql, result, fields_ma_alloc_root,
stmt->field_count, 0)))
return(1);
return(0);
@@ -1836,6 +1838,11 @@ static int madb_alloc_stmt_fields(MYSQL_STMT *stmt)
stmt->fields[i].decimals= stmt->mysql->fields[i].decimals;
stmt->fields[i].charsetnr= stmt->mysql->fields[i].charsetnr;
stmt->fields[i].max_length= stmt->mysql->fields[i].max_length;
+ stmt->fields[i].extension=
+ stmt->mysql->fields[i].extension ?
+ ma_field_extension_deep_dup(fields_ma_alloc_root,
+ stmt->mysql->fields[i].extension) :
+ NULL;
}
if (!(stmt->bind= (MYSQL_BIND *)ma_alloc_root(fields_ma_alloc_root, stmt->field_count * sizeof(MYSQL_BIND))))
{
@@ -1912,7 +1919,6 @@ int stmt_read_execute_response(MYSQL_STMT *stmt)
/* since all pointers will be incorrect if another statement will
be executed, so we need to allocate memory and copy the
information */
- stmt->fields[i].extension= 0; /* not in use yet */
if (mysql->fields[i].db)
stmt->fields[i].db= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].db);
if (mysql->fields[i].table)
@@ -1927,6 +1933,11 @@ int stmt_read_execute_response(MYSQL_STMT *stmt)
stmt->fields[i].catalog= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].catalog);
if (mysql->fields[i].def)
stmt->fields[i].def= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].def);
+ stmt->fields[i].extension=
+ mysql->fields[i].extension ?
+ ma_field_extension_deep_dup(fields_ma_alloc_root,
+ mysql->fields[i].extension) :
+ NULL;
}
}
References