diff --git a/CMakeLists.txt b/CMakeLists.txt index 86a1e2b8da0b..b9bce853b1bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -126,6 +126,9 @@ IF(DEFINED CMAKE_BUILD_TYPE) ENDIF() OPTION(WITH_DEBUG "Use dbug/safemutex" OFF) + +OPTION(WITH_EXPERIMENTAL_UDT "With experimental user defined types" ON) + OPTION(CHECK_ERRMSG_FORMAT "Check printf format for English error messages" OFF) OPTION(DISABLE_ALL_PSI "DISABLE all calls to the PSI interface" OFF) diff --git a/components/udt_example/CMakeLists.txt b/components/udt_example/CMakeLists.txt new file mode 100644 index 000000000000..dba8b1671b2e --- /dev/null +++ b/components/udt_example/CMakeLists.txt @@ -0,0 +1,34 @@ +# Copyright (c) 2016, 2026, Oracle and/or its affiliates. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License, version 2.0, +# as published by the Free Software Foundation. +# +# This program is designed to work with certain software (including +# but not limited to OpenSSL) that is licensed under separate terms, +# as designated in a particular file or component or in included license +# documentation. The authors of MySQL hereby grant you an additional +# permission to link the program and your derivative works with the +# separately licensed software that they have either included with +# the program or referenced in the documentation. +# +# 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, version 2.0, 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-1301 USA + +DISABLE_MISSING_PROFILE_WARNING() + +ADD_DEFINITIONS(-DLOG_COMPONENT_TAG="udt_example") + +MYSQL_ADD_COMPONENT(udt_example + udt_complex.cc + udt_example.cc + udt_log.cc + MODULE_ONLY + TEST_ONLY + ) diff --git a/components/udt_example/udt_complex.cc b/components/udt_example/udt_complex.cc new file mode 100644 index 000000000000..290ed07b62c1 --- /dev/null +++ b/components/udt_example/udt_complex.cc @@ -0,0 +1,82 @@ +/* Copyright (c) 2026, Oracle and/or its affiliates. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License, version 2.0, +as published by the Free Software Foundation. + +This program is designed to work with certain software (including +but not limited to OpenSSL) that is licensed under separate terms, +as designated in a particular file or component or in included license +documentation. The authors of MySQL hereby grant you an additional +permission to link the program and your derivative works with the +separately licensed software that they have either included with +the program or referenced in the documentation. + +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, version 2.0, 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-1301 USA */ + +#include "udt_complex.h" + +#include +#include "my_byteorder.h" + +// #include + +namespace udt_example { + +#ifdef LATER +void Complex::serialize(enum xdr_op op, serialized_complex &buffer) { + { + XDR xdrs; + + xdrmem_create(&xdrs, &buffer.buffer[0], sizeof(buffer.buffer), op); + xdr_double(&xdrs, &m_real); + xdr_double(&xdrs, &m_imaginary); + } + + void Complex::serialize_from(serialized_complex & buffer) { + serialize(XDR_DECODE); + } + + void Complex::serialize_to(serialized_complex & buffer) { + serialize(XDR_ENCODE); + } +#endif + + void Complex::serialize_from(const serialized_complex &buffer) { + assert(sizeof(double) == 8); + const unsigned char *b = &buffer.buffer[0]; + + m_real = float8get(b); + m_imaginary = float8get(b + 8); + } + + void Complex::serialize_to(serialized_complex & buffer) { + assert(sizeof(double) == 8); + unsigned char *b = &buffer.buffer[0]; + + float8store(b, m_real); + float8store(b + 8, m_imaginary); + } + + Complex Complex::add(const Complex &a, const Complex &b) { + Complex result; + result.m_real = a.m_real + b.m_real; + result.m_imaginary = a.m_imaginary + b.m_imaginary; + return result; + } + + Complex Complex::mul(const Complex &a, const Complex &b) { + Complex result; + result.m_real = a.m_real * b.m_real - a.m_imaginary * b.m_imaginary; + result.m_imaginary = a.m_real * b.m_imaginary + b.m_real * a.m_imaginary; + return result; + } + +} // namespace udt_example diff --git a/components/udt_example/udt_complex.h b/components/udt_example/udt_complex.h new file mode 100644 index 000000000000..d4e0ad6d60fb --- /dev/null +++ b/components/udt_example/udt_complex.h @@ -0,0 +1,56 @@ +/* + Copyright (c) 2026, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is designed to work with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have either included with + the program or referenced in the documentation. + + 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, version 2.0, 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-1301 USA +*/ + +#ifndef UDT_COMPLEX_H_INCLUDED +#define UDT_COMPLEX_H_INCLUDED + +namespace udt_example { + +struct serialized_complex { + unsigned char buffer[16]; + + unsigned char *ptr() { return &buffer[0]; } + + unsigned int length() { return sizeof(buffer); } +}; + +class Complex { + public: + Complex() : m_real(0.0), m_imaginary(0.0) {} + Complex(double r, double i) : m_real(r), m_imaginary(i) {} + + void serialize_from(const serialized_complex &buffer); + void serialize_to(serialized_complex &buffer); + + static Complex add(const Complex &a, const Complex &b); + static Complex mul(const Complex &a, const Complex &b); + + double m_real; + double m_imaginary; +}; + +} // namespace udt_example + +#endif /* UDT_EXAMPLE_LOG_H_INCLUDED */ diff --git a/components/udt_example/udt_example.cc b/components/udt_example/udt_example.cc new file mode 100644 index 000000000000..e42baa13c6b7 --- /dev/null +++ b/components/udt_example/udt_example.cc @@ -0,0 +1,235 @@ +/* Copyright (c) 2016, 2026, Oracle and/or its affiliates. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License, version 2.0, +as published by the Free Software Foundation. + +This program is designed to work with certain software (including +but not limited to OpenSSL) that is licensed under separate terms, +as designated in a particular file or component or in included license +documentation. The authors of MySQL hereby grant you an additional +permission to link the program and your derivative works with the +separately licensed software that they have either included with +the program or referenced in the documentation. + +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, version 2.0, 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-1301 USA */ + +#include +#include +#include + +#include + +#include "udt_complex.h" +#include "udt_log.h" + +namespace udt_example { + +REQUIRES_SERVICE_PLACEHOLDER_AS(log_builtins, log_srv); +REQUIRES_SERVICE_PLACEHOLDER_AS(log_builtins_string, log_string_srv); +REQUIRES_SERVICE_PLACEHOLDER_AS(udt_registration, udt_srv); +REQUIRES_SERVICE_PLACEHOLDER_AS(udt_value_null, val_null_srv); +REQUIRES_SERVICE_PLACEHOLDER_AS(udt_value_string, val_string_srv); +REQUIRES_SERVICE_PLACEHOLDER_AS(udt_value_blob, val_blob_srv); + +const char *component_name = "udt_example"; + +// NATIVE TYPE VARCHAR + +struct mysql_type_descriptor_t VARCHAR_TYPE_DESCRIPTOR = { + MYSQL_FIELD_TYPE_VARCHAR, // mysql_type + 0, // type_flags + 0, // length + 0, // decimals + nullptr, // charset + false, // has_explicit_collation + nullptr // type_ident +}; + +// TYPE math.complex_number + +struct mysql_type_ident_t COMPLEX_NUMBER_TYPE_NAME = { + "math", // schema + "complex_number" // object +}; + +struct mysql_type_descriptor_t COMPLEX_NUMBER_TYPE_DESCRIPTOR = { + MYSQL_FIELD_TYPE_BLOB, // mysql_type + 0, // type_flags + 16, // length + 0, // decimals + nullptr, // charset + false, // has_explicit_collation + &COMPLEX_NUMBER_TYPE_NAME // type_ident +}; + +// FUNCTION complex_number_from_string + +struct mysql_type_descriptor_t *FROM_STRING_ARGS[] = {&VARCHAR_TYPE_DESCRIPTOR}; + +struct mysql_function_descriptor_t FROM_STRING = { + "complex_number_from_string", // name + &COMPLEX_NUMBER_TYPE_DESCRIPTOR, // return_type + 1, // arguments + &FROM_STRING_ARGS[0] // argument_type_array +}; + +static int complex_number_from_string(UDT_value *result, size_t argument_count, + UDT_value **argument_value_array) { + fprintf(stderr, "complex_number_from_string()\n"); + + assert(argument_count == 1); + + UDT_value *p1 = argument_value_array[0]; + bool p1_is_null{false}; + val_null_srv->get_null(p1, &p1_is_null); + + if (p1_is_null) { + // complex_number_from_string(NULL) -> NULL + val_null_srv->set_null(result, true); + return 0; + } + + const char *str{nullptr}; + unsigned int len{0}; + + val_string_srv->get_utf8mb4(p1, &str, &len); + + if (len == 0) { + // complex_number_from_string("") -> NULL + val_null_srv->set_null(result, true); + return 0; // FIXME: error ? + } + + double r; + double i; + int n; + + n = sscanf(str, "%lf%lfi", &r, &i); + if (n != 2) { + // complex_number_from_string("unparsable") -> NULL + val_null_srv->set_null(result, true); + return 0; // FIXME: error ? + } + + fprintf(stderr, "complex_number_from_string() found r = %lf, i = %lf\n", r, + i); + + // Build a binary image with (r, i) + Complex c(r, i); + serialized_complex serialized; + c.serialize_to(serialized); + + // complex_number_from_string("valid string") + // -> TYPE complex AS BINARY(16) + val_null_srv->set_null(result, false); + val_blob_srv->set(result, serialized.ptr(), serialized.length()); + + return 0; +} + +// FUNCTION complex_number_to_string + +struct mysql_type_descriptor_t *TO_STRING_ARGS[] = { + &COMPLEX_NUMBER_TYPE_DESCRIPTOR}; + +struct mysql_function_descriptor_t TO_STRING = { + "complex_number_to_string", // name + &VARCHAR_TYPE_DESCRIPTOR, // return_type + 1, // arguments + &TO_STRING_ARGS[0] // argument_type_array +}; + +static int complex_number_to_string(UDT_value *result, size_t argument_count, + UDT_value **argument_value_array) { + return 0; +} + +// FUNCTION complex_number_add + +struct mysql_type_descriptor_t *ADD_ARGS[] = { + &COMPLEX_NUMBER_TYPE_DESCRIPTOR, // p1 + &COMPLEX_NUMBER_TYPE_DESCRIPTOR // p2 +}; + +struct mysql_function_descriptor_t ADD = { + "complex_number_add", // name + &COMPLEX_NUMBER_TYPE_DESCRIPTOR, // return_type + 2, // arguments + &ADD_ARGS[0] // argument_type_array +}; + +static int complex_number_add(UDT_value *result, size_t argument_count, + UDT_value **argument_value_array) { + return 0; +} + +static mysql_service_status_t udt_example_init() { + Log::init(log_srv, log_string_srv); + log_info("%s: Starting ...", component_name); + + udt_srv->register_type(&COMPLEX_NUMBER_TYPE_DESCRIPTOR, nullptr); + udt_srv->register_function(&ADD, complex_number_add); + udt_srv->register_function(&FROM_STRING, complex_number_from_string); + udt_srv->register_function(&TO_STRING, complex_number_to_string); + + log_info("%s: Started.", component_name); + return 0; +} + +static mysql_service_status_t udt_example_deinit() { + log_info("%s: Stopping ...", component_name); + + udt_srv->unregister_function(&ADD); + udt_srv->unregister_function(&FROM_STRING); + udt_srv->unregister_function(&TO_STRING); + udt_srv->unregister_type(&COMPLEX_NUMBER_TYPE_DESCRIPTOR); + + log_info("%s: Stopped.", component_name); + return 0; +} + +// clang-format off +BEGIN_COMPONENT_PROVIDES(udt_example) +END_COMPONENT_PROVIDES(); +// clang-format on + +// clang-format off +BEGIN_COMPONENT_REQUIRES(udt_example) + REQUIRES_SERVICE_AS(log_builtins, log_srv), + REQUIRES_SERVICE_AS(log_builtins_string, log_string_srv), + REQUIRES_SERVICE_AS(udt_registration, udt_srv), + REQUIRES_SERVICE_AS(udt_value_null, val_null_srv), + REQUIRES_SERVICE_AS(udt_value_string, val_string_srv), + REQUIRES_SERVICE_AS(udt_value_blob, val_blob_srv), +END_COMPONENT_REQUIRES(); +// clang-format on + +// clang-format off +BEGIN_COMPONENT_METADATA(udt_example) + METADATA("mysql.author", "Oracle Corporation"), + METADATA("mysql.license", "GPL"), +END_COMPONENT_METADATA(); +// clang-format on + +// clang-format off +DECLARE_COMPONENT(udt_example, "mysql:udt_example") + udt_example_init, + udt_example_deinit +END_DECLARE_COMPONENT(); +// clang-format on + +// clang-format off +DECLARE_LIBRARY_COMPONENTS + &COMPONENT_REF(udt_example) +END_DECLARE_LIBRARY_COMPONENTS +// clang-format on + +} // namespace udt_example diff --git a/components/udt_example/udt_log.cc b/components/udt_example/udt_log.cc new file mode 100644 index 000000000000..6a2ef7c5fbe3 --- /dev/null +++ b/components/udt_example/udt_log.cc @@ -0,0 +1,51 @@ +/* + Copyright (c) 2022, 2026, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is designed to work with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have either included with + the program or referenced in the documentation. + + 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, version 2.0, 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-1301 USA +*/ + +#include "udt_log.h" + +/* + In include/mysql/components/services/log_builtins.h, + the helper macros require these two globals. +*/ +SERVICE_TYPE(log_builtins) * log_bi{nullptr}; +SERVICE_TYPE(log_builtins_string) * log_bs{nullptr}; + +namespace udt_example { + +void Log::init(SERVICE_TYPE(log_builtins) * log_bi_srv, + SERVICE_TYPE(log_builtins_string) * log_bs_srv) { + log_bi = log_bi_srv; + log_bs = log_bs_srv; +} + +void Log::log_message(const char *src_file, int src_line, long long level, + long long code, const char *msg, ...) { + va_list args; + va_start(args, msg); + log_message_va(src_file, src_line, level, code, msg, args); + va_end(args); +} + +} // namespace udt_example diff --git a/components/udt_example/udt_log.h b/components/udt_example/udt_log.h new file mode 100644 index 000000000000..7a4ac4d0c0a1 --- /dev/null +++ b/components/udt_example/udt_log.h @@ -0,0 +1,94 @@ +/* + Copyright (c) 2022, 2026, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is designed to work with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have either included with + the program or referenced in the documentation. + + 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, version 2.0, 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-1301 USA +*/ + +#ifndef UDT_EXAMPLE_LOG_H_INCLUDED +#define UDT_EXAMPLE_LOG_H_INCLUDED + +#include +#include + +namespace udt_example { + +extern const char *component_name; + +class Log { + public: + static void init(SERVICE_TYPE(log_builtins) * log_bi_srv, + SERVICE_TYPE(log_builtins_string) * log_bs_srv); + + static void log_message(const char *src_file, int src_line, long long level, + long long code, const char *msg, ...) + MY_ATTRIBUTE((format(printf, 5, 0))); + + static void log_message_va(const char *src_file, int src_line, + long long level, long long code, const char *msg, + va_list args) + MY_ATTRIBUTE((format(printf, 5, 0))) { + LogEvent() + .no_telemetry() + .prio(level) + .errcode(code) + .subsys(LOG_SUBSYSTEM_TAG) + .source_line(src_line) + .source_file(src_file) + .function(__FUNCTION__) + .component(LOG_COMPONENT_TAG) + .messagev(msg, args); + } + + template + static void log_message_lu(const char *src_file, int src_line, + long long level, long long code, Args... args) { + LogEvent() + .no_telemetry() + .prio(level) + .errcode(code) + .subsys(LOG_SUBSYSTEM_TAG) + .source_line(src_line) + .source_file(src_file) + .function(__FUNCTION__) + .component(LOG_COMPONENT_TAG) + .lookup(code, args...); + } +}; + +} // namespace udt_example + +#define log_info(msg, ...) \ + Log::log_message(__FILE__, __LINE__, INFORMATION_LEVEL, ER_TELEMETRY_INFO, \ + msg, ##__VA_ARGS__) + +#define log_warning(msg, ...) \ + Log::log_message(__FILE__, __LINE__, WARNING_LEVEL, ER_TELEMETRY_WARNING, \ + msg, ##__VA_ARGS__) + +#define log_error(msg, ...) \ + Log::log_message(__FILE__, __LINE__, ERROR_LEVEL, ER_TELEMETRY_ERROR, msg, \ + ##__VA_ARGS__) + +#define log_warn_usage(msgno, ...) \ + Log::log_message_lu(__FILE__, __LINE__, WARNING_LEVEL, msgno, ##__VA_ARGS__) + +#endif /* UDT_EXAMPLE_LOG_H_INCLUDED */ diff --git a/config.h.cmake b/config.h.cmake index 8125df3a8a2b..7d681b7a079f 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -221,6 +221,9 @@ /* Lock Order */ #cmakedefine WITH_LOCK_ORDER 1 +/* User Defined Types*/ +#cmakedefine WITH_EXPERIMENTAL_UDT 1 + /* Character sets and collations */ #cmakedefine DEFAULT_MYSQL_HOME "@DEFAULT_MYSQL_HOME@" #cmakedefine SHAREDIR "@SHAREDIR@" diff --git a/include/my_sqlcommand.h b/include/my_sqlcommand.h index 2b5e7187104c..80fb58531ea5 100644 --- a/include/my_sqlcommand.h +++ b/include/my_sqlcommand.h @@ -212,6 +212,9 @@ enum enum_sql_command { SQLCOM_CREATE_MASKING_POLICY, SQLCOM_DROP_MASKING_POLICY, SQLCOM_SHOW_CREATE_MASKING_POLICY, + + // POC + SQLCOM_CREATE_TYPE, /* This should be the last !!! */ SQLCOM_END }; diff --git a/include/mysql/components/services/bits/mysql_user_defined_type_bits.h b/include/mysql/components/services/bits/mysql_user_defined_type_bits.h new file mode 100644 index 000000000000..e70374637c99 --- /dev/null +++ b/include/mysql/components/services/bits/mysql_user_defined_type_bits.h @@ -0,0 +1,69 @@ +/* Copyright (c) 2026, Oracle and/or its affiliates. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License, version 2.0, +as published by the Free Software Foundation. + +This program is designed to work with certain software (including +but not limited to OpenSSL) that is licensed under separate terms, +as designated in a particular file or component or in included license +documentation. The authors of MySQL hereby grant you an additional +permission to link the program and your derivative works with the +separately licensed software that they have either included with +the program or referenced in the documentation. + +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, version 2.0, 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-1301 USA */ + +#ifndef COMPONENTS_SERVICES_BITS_MYSQL_USER_DEFINED_TYPE_BITS_H +#define COMPONENTS_SERVICES_BITS_MYSQL_USER_DEFINED_TYPE_BITS_H + +#include +#include + +#include "mysql/components/services/bits/mysql_field_types_bits.h" + +struct CHARSET_INFO; +struct UDT_value; + +struct mysql_type_ident_t { + const char *schema; + const char *object; +}; + +struct mysql_type_descriptor_t { + mysql_field_type_t mysql_type{MYSQL_FIELD_TYPE_INVALID}; + uint32_t type_flags{0}; + size_t length{0}; + size_t decimals{0}; + const CHARSET_INFO *charset{nullptr}; + bool has_explicit_collation{false}; + mysql_type_ident_t *type_ident{nullptr}; + // FIXME: m_geo_type + // FIXME: m_internal_list +}; + +struct mysql_function_descriptor_t { + const char *name; + mysql_type_descriptor_t *return_type{nullptr}; + size_t argument_count{0}; + mysql_type_descriptor_t **argument_type_array{nullptr}; +}; + +typedef int (*register_type_t)(mysql_type_descriptor_t *td, void *impl); +typedef int (*unregister_type_t)(mysql_type_descriptor_t *td); + +typedef int (*eval_function_t)(UDT_value *result, size_t argument_count, + UDT_value **argument_value_array); + +typedef int (*register_function_t)(mysql_function_descriptor_t *fd, + eval_function_t impl); +typedef int (*unregister_function_t)(mysql_function_descriptor_t *fd); + +#endif /* COMPONENTS_SERVICES_BITS_MYSQL_USER_DEFINED_TYPE_BITS_H */ diff --git a/include/mysql/components/services/mysql_user_defined_type.h b/include/mysql/components/services/mysql_user_defined_type.h new file mode 100644 index 000000000000..a291f9862693 --- /dev/null +++ b/include/mysql/components/services/mysql_user_defined_type.h @@ -0,0 +1,72 @@ +/* Copyright (c) 2017, 2026, Oracle and/or its affiliates. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License, version 2.0, +as published by the Free Software Foundation. + +This program is designed to work with certain software (including +but not limited to OpenSSL) that is licensed under separate terms, +as designated in a particular file or component or in included license +documentation. The authors of MySQL hereby grant you an additional +permission to link the program and your derivative works with the +separately licensed software that they have either included with +the program or referenced in the documentation. + +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, version 2.0, 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-1301 USA */ + +#ifndef MYSQL_USER_DEFINED_TYPE_SERVICE_H +#define MYSQL_USER_DEFINED_TYPE_SERVICE_H + +#include +#include + +BEGIN_SERVICE_DEFINITION(udt_registration) + +DECLARE_METHOD(int, register_type, (mysql_type_descriptor_t * td, void *impl)); + +DECLARE_METHOD(int, unregister_type, (mysql_type_descriptor_t * td)); + +DECLARE_METHOD(int, register_function, + (mysql_function_descriptor_t * fd, eval_function_t impl)); + +DECLARE_METHOD(int, unregister_function, (mysql_function_descriptor_t * fd)); + +END_SERVICE_DEFINITION(udt_registration) + +//------------------------------------------------------------------- +// UDT_value +//------------------------------------------------------------------- + +BEGIN_SERVICE_DEFINITION(udt_value_null) + +DECLARE_METHOD(void, set_null, (UDT_value * f, bool is_null)); +DECLARE_METHOD(void, get_null, (UDT_value * f, bool *is_null)); + +END_SERVICE_DEFINITION(udt_value_null) + +BEGIN_SERVICE_DEFINITION(udt_value_string) + +DECLARE_METHOD(void, set_utf8mb4, + (UDT_value * f, const char *value, unsigned int length)); +DECLARE_METHOD(void, get_utf8mb4, + (UDT_value * f, const char **str, unsigned int *length)); + +END_SERVICE_DEFINITION(udt_value_string) + +BEGIN_SERVICE_DEFINITION(udt_value_blob) + +DECLARE_METHOD(void, set, + (UDT_value * f, const unsigned char *val, unsigned int len)); +DECLARE_METHOD(void, get, + (UDT_value * f, unsigned char *val, unsigned int *len)); + +END_SERVICE_DEFINITION(udt_value_blob) + +#endif diff --git a/include/mysql/plugin_audit.h.pp b/include/mysql/plugin_audit.h.pp index 490a58bc3762..6b70a81756de 100644 --- a/include/mysql/plugin_audit.h.pp +++ b/include/mysql/plugin_audit.h.pp @@ -349,6 +349,7 @@ SQLCOM_CREATE_MASKING_POLICY, SQLCOM_DROP_MASKING_POLICY, SQLCOM_SHOW_CREATE_MASKING_POLICY, + SQLCOM_CREATE_TYPE, SQLCOM_END }; #include "plugin_audit_message_types.h" diff --git a/mysql-test/suite/udt/r/udt_basic.result b/mysql-test/suite/udt/r/udt_basic.result new file mode 100644 index 000000000000..ea24e8efb3fe --- /dev/null +++ b/mysql-test/suite/udt/r/udt_basic.result @@ -0,0 +1,70 @@ +CREATE TYPE test.usbn13 AS CHAR(13); +Warnings: +Warning 6910 The following code is not implemented: Sql_cmd_create_type::execute() +CREATE TYPE test.complex_number AS BINARY(16); +Warnings: +Warning 6910 The following code is not implemented: Sql_cmd_create_type::execute() +SELECT * FROM INFORMATION_SCHEMA.TYPES; +TYPE_SCHEMA TYPE_NAME +test complex_number +test usbn13 +CREATE PROCEDURE test.demo1() +BEGIN +DECLARE var CHAR(13); +SELECT "Demo" as title; +END$$ +CREATE PROCEDURE test.broken1() +BEGIN +DECLARE var broken.usbn13; +END$$ +ERROR 42Y07: Database 'broken' doesn't exist +CREATE PROCEDURE test.broken2() +BEGIN +DECLARE var test.broken; +END$$ +ERROR HY000: User defined type 'test.broken' doesn't exist +CREATE PROCEDURE test.demo2() +BEGIN +DECLARE var test.usbn13; +SET var = "FIXME"; +END$$ +Warnings: +Warning 6910 The following code is not implemented: resolve_type_descriptor() +SHOW PROCEDURE CODE test.demo1; +Pos Instruction +0 set var@0 NULL +1 stmt "SELECT "Demo" as title" +SHOW PROCEDURE CODE test.demo2; +Pos Instruction +0 set var@0 NULL +1 set var@0 'FIXME' +Warnings: +Warning 6910 The following code is not implemented: resolve_type_descriptor() +CALL test.demo1(); +title +Demo +CALL test.demo2(); +DROP PROCEDURE test.demo1; +DROP PROCEDURE test.demo2; +INSTALL COMPONENT "file://component_udt_example"; +CREATE PROCEDURE test.complex() +BEGIN +DECLARE a test.complex_number; +DECLARE b test.complex_number; +DECLARE c test.complex_number; +SET a = complex_number_from_string("1+2i"); +SET b = complex_number_from_string("3+4i"); +SET c = complex_number_add(a, b); +# SELECT complex_number_to_string(c); +END$$ +Warnings: +Warning 6910 The following code is not implemented: resolve_type_descriptor() +Warning 6910 The following code is not implemented: resolve_type_descriptor() +Warning 6910 The following code is not implemented: resolve_type_descriptor() +call test.complex(); +Warnings: +Warning 6910 The following code is not implemented: resolve_type_descriptor() +Warning 6910 The following code is not implemented: resolve_type_descriptor() +Warning 6910 The following code is not implemented: resolve_type_descriptor() +UNINSTALL COMPONENT "file://component_udt_example"; +DROP PROCEDURE test.complex; diff --git a/mysql-test/suite/udt/t/udt_basic.test b/mysql-test/suite/udt/t/udt_basic.test new file mode 100644 index 000000000000..1a21e95f6eb9 --- /dev/null +++ b/mysql-test/suite/udt/t/udt_basic.test @@ -0,0 +1,67 @@ + +CREATE TYPE test.usbn13 AS CHAR(13); +CREATE TYPE test.complex_number AS BINARY(16); + +SELECT * FROM INFORMATION_SCHEMA.TYPES; + +delimiter $$; + +CREATE PROCEDURE test.demo1() +BEGIN + DECLARE var CHAR(13); + SELECT "Demo" as title; +END$$ + +--error ER_NO_SUCH_DB +CREATE PROCEDURE test.broken1() +BEGIN + DECLARE var broken.usbn13; +END$$ + +--error ER_NO_SUCH_UDT_TYPE +CREATE PROCEDURE test.broken2() +BEGIN + DECLARE var test.broken; +END$$ + + +CREATE PROCEDURE test.demo2() +BEGIN + DECLARE var test.usbn13; + SET var = "FIXME"; +END$$ + +delimiter ;$$ + +SHOW PROCEDURE CODE test.demo1; +SHOW PROCEDURE CODE test.demo2; + +CALL test.demo1(); +CALL test.demo2(); + +DROP PROCEDURE test.demo1; +DROP PROCEDURE test.demo2; + +INSTALL COMPONENT "file://component_udt_example"; + +delimiter $$; + +CREATE PROCEDURE test.complex() +BEGIN + DECLARE a test.complex_number; + DECLARE b test.complex_number; + DECLARE c test.complex_number; + SET a = complex_number_from_string("1+2i"); + SET b = complex_number_from_string("3+4i"); + SET c = complex_number_add(a, b); + # SELECT complex_number_to_string(c); +END$$ + +delimiter ;$$ + +call test.complex(); + +UNINSTALL COMPONENT "file://component_udt_example"; + +DROP PROCEDURE test.complex; + diff --git a/share/messages_to_clients.txt b/share/messages_to_clients.txt index 4afcd3052027..9b64b824979d 100644 --- a/share/messages_to_clients.txt +++ b/share/messages_to_clients.txt @@ -11052,6 +11052,18 @@ ER_CSA_CRST_REQUIREMENT_GTID_ONLY ER_DA_CANNOT_REPLICATE_WITHOUT_BINLOG eng "Cannot replicate from source as it does not have logical log enabled. Check source configuration to enable it." +ER_WARN_CODE_NOT_IMPLEMENTED + eng "The following code is not implemented: %s" + +ER_UDT_TYPE_CREATE_EXISTS + eng "Can't create user defined type '%-.192s.%-.192s'; user defined type exists" + +ER_NO_SUCH_UDT_TYPE + eng "User defined type '%-.192s.%-.192s' doesn't exist" + +ER_UDT_TYPE_DROP_EXISTS + eng "Can't drop user defined type '%-.192s.%-.192s'; user defined type doesn't exist" + # # End of "9.7 cal-ver compatibility lineage (starts from 26.7)" error messages (server-to-client). # diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 295ea784a2fe..2c5bcb79b896 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -128,6 +128,7 @@ SET(DD_SOURCES dd/dd_table.cc dd/dd_tablespace.cc dd/dd_trigger.cc + dd/dd_udt_type.cc dd/dd_view.cc dd/dd_utility.cc dd/properties.cc @@ -205,6 +206,7 @@ SET(DD_SOURCES dd/impl/system_views/table_constraints_extensions.cc dd/impl/system_views/tablespaces_extensions.cc dd/impl/system_views/triggers.cc + dd/impl/system_views/udt_types.cc dd/impl/system_views/view_routine_usage.cc dd/impl/system_views/view_table_usage.cc dd/impl/system_views/views.cc @@ -237,6 +239,7 @@ SET(DD_SOURCES dd/impl/tables/tablespace_files.cc dd/impl/tables/tablespaces.cc dd/impl/tables/triggers.cc + dd/impl/tables/udt_types.cc dd/impl/tables/view_routine_usage.cc dd/impl/tables/view_table_usage.cc @@ -274,6 +277,7 @@ SET(DD_SOURCES dd/impl/types/tablespace_file_impl.cc dd/impl/types/tablespace_impl.cc dd/impl/types/trigger_impl.cc + dd/impl/types/udt_type_impl.cc dd/impl/types/view_impl.cc dd/impl/types/view_routine_impl.cc dd/impl/types/view_table_impl.cc @@ -564,6 +568,7 @@ SET(SQL_SHARED_SOURCES sql_const_folding.cc sql_cmd_ddl.cc sql_cmd_ddl_table.cc + sql_cmd_ddl_type.cc sql_cmd_srs.cc sql_connect.cc sql_constraint.cc @@ -625,8 +630,10 @@ SET(SQL_SHARED_SOURCES sql_trigger.cc sql_truncate.cc sql_udf.cc + sql_udt.cc sql_union.cc sql_update.cc + sql_user_defined_type.cc sql_view.cc ssl_acceptor_context_iterator.cc ssl_acceptor_context_data.cc diff --git a/sql/create_field.cc b/sql/create_field.cc index f543837f2291..fcdbfcfb6236 100644 --- a/sql/create_field.cc +++ b/sql/create_field.cc @@ -586,6 +586,28 @@ bool Create_field::init( return false; /* success */ } +bool Create_field::init_from_type_descriptor(THD *thd, + const char *field_name_arg, + TypeDescriptor *td, + FieldDescriptor *fd) { + bool rc; + + // Should be resolved already. + assert(td->m_type != MYSQL_TYPE_INVALID); + + rc = init(thd, field_name_arg, td->m_type, td->m_length, td->m_dec, + td->m_type_flags, fd->m_default_value, fd->m_on_update_value, + fd->m_comment, fd->m_change, td->m_internal_list, td->m_charset, + td->m_has_explicit_collation, td->m_geo_type, fd->m_gcol_info, + fd->m_default_val_expr, fd->m_fld_masking_policy, fd->m_srid, + fd->m_hidden, fd->m_is_array); + + m_type_is_resolved = true; + m_type_ident = td->m_type_ident; + + return rc; +} + /** Init for a tmp table field. To be extended if need be. */ diff --git a/sql/create_field.h b/sql/create_field.h index 3c4798c85890..7d2043c255c0 100644 --- a/sql/create_field.h +++ b/sql/create_field.h @@ -41,6 +41,34 @@ class Item; class String; class Value_generator; +class Type_ident; + +struct TypeDescriptor { + enum_field_types m_type{MYSQL_TYPE_INVALID}; + ulong m_type_flags{0}; + const char *m_length{nullptr}; + const char *m_dec{nullptr}; + const CHARSET_INFO *m_charset{nullptr}; + bool m_has_explicit_collation{false}; + uint m_geo_type{0}; + List *m_internal_list{nullptr}; + const Type_ident *m_type_ident{nullptr}; +}; + +struct FieldDescriptor { + Item *m_default_value{nullptr}; + Item *m_on_update_value{nullptr}; + const LEX_CSTRING *m_comment{&NULL_CSTR}; + const char *m_change{nullptr}; + Value_generator *m_gcol_info{nullptr}; + Value_generator *m_default_val_expr{nullptr}; + LEX_CSTRING m_fld_masking_policy{NULL_CSTR}; + std::optional m_srid{}; + dd::Column::enum_hidden_type m_hidden{ + dd::Column::enum_hidden_type::HT_VISIBLE}; + bool m_is_array{false}; +}; + /// Create_field is a description a field/column that may or may not exists in /// a table. /// @@ -216,6 +244,9 @@ class Create_field { LEX_CSTRING fld_masking_policy, std::optional srid, dd::Column::enum_hidden_type hidden, bool is_array = false); + bool init_from_type_descriptor(THD *thd, const char *field_name, + TypeDescriptor *td, FieldDescriptor *fd); + ha_storage_media field_storage_type() const { return (ha_storage_media)((flags >> FIELD_FLAGS_STORAGE_MEDIA) & 3); } @@ -249,6 +280,10 @@ class Create_field { /// Whether or not the display width was given explicitly by the user. bool m_explicit_display_width{false}; + + public: + bool m_type_is_resolved{false}; + const Type_ident *m_type_ident{nullptr}; }; /// @returns whether or not this field is a hidden column that represents a diff --git a/sql/dd/cache/object_registry.h b/sql/dd/cache/object_registry.h index b1ebde0d08f0..a038bbe4936c 100644 --- a/sql/dd/cache/object_registry.h +++ b/sql/dd/cache/object_registry.h @@ -39,6 +39,7 @@ #include "sql/dd/types/schema.h" // Schema #include "sql/dd/types/spatial_reference_system.h" // Spatial_reference_system #include "sql/dd/types/tablespace.h" // Tablespace +#include "sql/dd/types/udt_type.h" // UDT_Type namespace dd { namespace cache { @@ -76,6 +77,7 @@ class Object_registry { std::unique_ptr> m_spatial_reference_system_map; std::unique_ptr> m_tablespace_map; + std::unique_ptr> m_udt_type_map; // Not inlined because it is big, and because it takes a lot of time // for the compiler to instantiate. Defined in dd.cc, along the similar @@ -184,6 +186,14 @@ class Object_registry { return m_tablespace_map.get(); } + Local_multi_map *m_map(Type_selector) { + return create_map_if_needed(&m_udt_type_map); + } + + const Local_multi_map *m_map(Type_selector) const { + return m_udt_type_map.get(); + } + /** Template function to get a map instance. @@ -329,6 +339,7 @@ class Object_registry { erase(); erase(); erase(); + erase(); } /** diff --git a/sql/dd/dd_udt_type.cc b/sql/dd/dd_udt_type.cc new file mode 100644 index 000000000000..6fe0f34e6a9a --- /dev/null +++ b/sql/dd/dd_udt_type.cc @@ -0,0 +1,109 @@ +/* Copyright (c) 2015, 2026, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is designed to work with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have either included with + the program or referenced in the documentation. + + 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, version 2.0, 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-1301 USA */ + +#include "sql/dd/dd_udt_type.h" + +#include +#include +#include +#include // unique_ptr +#include + +#include "lex_string.h" +#include "m_string.h" +#include "my_alloc.h" +#include "my_base.h" +#include "my_dbug.h" +#include "my_io.h" +#include "my_sys.h" +#include "mysql/components/services/log_builtins.h" +#include "mysql/my_loglevel.h" +#include "mysql/service_mysql_alloc.h" +#include "mysql/strings/dtoa.h" +#include "mysql/strings/int2str.h" +#include "mysql/strings/m_ctype.h" +#include "mysql/udf_registration_types.h" +#include "mysql_com.h" +#include "mysqld_error.h" +#include "sql/dd/cache/dictionary_client.h" // dd::cache::Dictionary_client +#include "sql/dd/collection.h" // dd::Collection +#include "sql/dd/dd.h" // dd::get_dictionary +#include "sql/dd/dictionary.h" // dd::Dictionary +// TODO: Avoid exposing dd/impl headers in public files. +#include "sql/dd/impl/dictionary_impl.h" // default_catalog_name +#include "sql/dd/impl/system_registry.h" // dd::System_tables +#include "sql/dd/impl/tables/dd_properties.h" // dd::tables:.DD_properties +#include "sql/dd/impl/utils.h" // dd::escape +#include "sql/dd/performance_schema/init.h" // performance_schema:: + // set_PS_version_for_table +#include "sql-common/my_decimal.h" +#include "sql/create_field.h" +#include "sql/dd/dd_version.h" // DD_VERSION +#include "sql/dd/properties.h" // dd::Properties +#include "sql/dd/string_type.h" +#include "sql/dd/types/schema.h" // dd::Schema +#include "sql/dd/types/tablespace.h" // dd::Tablespace +#include "sql/dd/types/udt_type.h" // dd::UDT_Type +#include "sql/debug_sync.h" // DEBUG_SYNC +#include "sql/log.h" +#include "sql/mdl.h" +#include "sql/mem_root_array.h" +#include "sql/mysqld.h" // lower_case_table_names +#include "sql/psi_memory_key.h" // key_memory_frm +#include "sql/sql_class.h" // THD +#include "sql/sql_const.h" +#include "sql/sql_lex.h" +#include "sql/sql_list.h" +#include "sql/sql_parse.h" + +namespace dd { + +bool udt_type_exists(dd::cache::Dictionary_client *client, + const char *schema_name, const char *name, bool *exists) { + DBUG_TRACE; + assert(exists); + + // Tables exist if they can be acquired. + dd::cache::Dictionary_client::Auto_releaser releaser(client); + const dd::UDT_Type *type_obj = nullptr; + if (client->acquire(schema_name, name, &type_obj)) { + // Error is reported by the dictionary subsystem. + return true; + } + *exists = (type_obj != nullptr); + + return false; +} + +bool create_udt_type(THD *thd, const dd::Schema &sch_obj, + const dd::String_type &type_name) { + std::unique_ptr obj(sch_obj.create_udt_type(thd)); + obj->set_name(type_name); + return thd->dd_client()->store(obj.get()); +} + +bool drop_udt_type(THD *thd, const dd::UDT_Type &type_def) { + return thd->dd_client()->drop(&type_def); +} + +} // namespace dd diff --git a/sql/dd/dd_udt_type.h b/sql/dd/dd_udt_type.h new file mode 100644 index 000000000000..e8ca04d58fe4 --- /dev/null +++ b/sql/dd/dd_udt_type.h @@ -0,0 +1,55 @@ +/* Copyright (c) 2015, 2026, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is designed to work with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have either included with + the program or referenced in the documentation. + + 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, version 2.0, 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-1301 USA */ + +#ifndef DD_UDT_TYPE_INCLUDED +#define DD_UDT_TYPE_INCLUDED + +#include +#include // std:unique_ptr +#include + +#include "my_inttypes.h" +#include "sql/dd/string_type.h" + +class THD; +namespace dd { +class Schema; +} // namespace dd + +namespace dd { +class UDT_Type; + +namespace cache { +class Dictionary_client; +} + +bool udt_type_exists(dd::cache::Dictionary_client *client, + const char *schema_name, const char *name, bool *exists); + +bool create_udt_type(THD *thd, const dd::Schema &sch_obj, + const dd::String_type &type_name); + +bool drop_udt_type(THD *thd, const dd::UDT_Type &type_def); + +} // namespace dd +#endif // DD_UDT_TYPE_INCLUDED diff --git a/sql/dd/impl/cache/dictionary_client.cc b/sql/dd/impl/cache/dictionary_client.cc index a58c68af35a6..308e794898e8 100644 --- a/sql/dd/impl/cache/dictionary_client.cc +++ b/sql/dd/impl/cache/dictionary_client.cc @@ -91,6 +91,7 @@ #include "sql/dd/types/table.h" // Table #include "sql/dd/types/table_stat.h" // Table_stat #include "sql/dd/types/tablespace.h" // Tablespace +#include "sql/dd/types/udt_type.h" // UDT_Type #include "sql/dd/types/view.h" // View #include "sql/dd/types/view_routine.h" // View_routine #include "sql/dd/types/view_table.h" // View_table @@ -141,6 +142,7 @@ template constexpr enum_mdl_type READ_LOCK_MDL_TYPE() { return (std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v) ? MDL_INTENTION_EXCLUSIVE : MDL_SHARED; @@ -222,6 +224,19 @@ MDL_key make_mdl_key(THD *, const dd::Tablespace &ts) { return {MDL_key::TABLESPACE, "", ts.name().c_str()}; } +MDL_key make_mdl_key(THD *thd, const dd::UDT_Type &udt_type) { + return with_schema_of(thd->dd_client(), udt_type, [&](const dd::Schema &s) { + MDL_key mdl_key; + char schema_name_buf[NAME_LEN + 1]; + dd::UDT_Type::create_mdl_key( + // FIXME.dt: Temporary dd::String_type from const char* + dd::Object_table_definition_impl::fs_name_case(s.name(), + schema_name_buf), + udt_type.name(), &mdl_key); + return mdl_key; + }); +} + MDL_key make_mdl_key(THD *, const dd::Resource_group &rg) { MDL_key mdl_key; dd::Resource_group::create_mdl_key(rg.name(), &mdl_key); @@ -549,6 +564,7 @@ Dictionary_client::Auto_releaser::~Auto_releaser() { m_client->release(&m_release_registry); m_client->release(&m_release_registry); m_client->release(&m_release_registry); + m_client->release(&m_release_registry); #ifndef NDEBUG // Make sure we still have some meta data lock. This is checked to @@ -2960,6 +2976,25 @@ template bool Dictionary_client::store(Tablespace *); template bool Dictionary_client::update(Tablespace *); template void Dictionary_client::dump() const; +template bool Dictionary_client::acquire_uncached(Object_id, UDT_Type **); +template bool Dictionary_client::acquire_uncached_uncommitted(Object_id, + UDT_Type **); +template bool Dictionary_client::acquire_uncached_uncommitted( + Object_id, std::unique_ptr *); +template bool Dictionary_client::acquire(Object_id, const UDT_Type **); +template bool Dictionary_client::acquire_for_modification(Object_id, + UDT_Type **); +template bool Dictionary_client::acquire(const String_type &, + const String_type &, + const UDT_Type **); +template bool Dictionary_client::acquire_for_modification(const String_type &, + const String_type &, + UDT_Type **); +template void Dictionary_client::remove_uncommitted_objects(bool); +template bool Dictionary_client::drop(const UDT_Type *); +template bool Dictionary_client::store(UDT_Type *); +template bool Dictionary_client::update(UDT_Type *); + template bool Dictionary_client::acquire_uncached(Object_id, View **); template bool Dictionary_client::acquire_uncached_uncommitted(Object_id, View **); diff --git a/sql/dd/impl/cache/local_multi_map.cc b/sql/dd/impl/cache/local_multi_map.cc index 4c5c8b127bd7..9c5da965d3d7 100644 --- a/sql/dd/impl/cache/local_multi_map.cc +++ b/sql/dd/impl/cache/local_multi_map.cc @@ -37,6 +37,7 @@ #include "sql/dd/impl/tables/spatial_reference_systems.h" #include "sql/dd/impl/tables/tables.h" #include "sql/dd/impl/tables/tablespaces.h" +#include "sql/dd/impl/tables/udt_types.h" namespace dd { class Abstract_table; @@ -148,5 +149,6 @@ template class Local_multi_map; template class Local_multi_map; template class Local_multi_map; template class Local_multi_map; +template class Local_multi_map; } // namespace dd::cache diff --git a/sql/dd/impl/cache/multi_map_base.cc b/sql/dd/impl/cache/multi_map_base.cc index 123fc7dc4063..1f4cd903516b 100644 --- a/sql/dd/impl/cache/multi_map_base.cc +++ b/sql/dd/impl/cache/multi_map_base.cc @@ -36,6 +36,7 @@ #include "sql/dd/types/schema.h" // Schema #include "sql/dd/types/spatial_reference_system.h" // Spatial_reference_system #include "sql/dd/types/tablespace.h" // Tablespace +#include "sql/dd/types/udt_type.h" // UDT_Type namespace dd::cache { @@ -78,5 +79,6 @@ template class Multi_map_base; template class Multi_map_base; template class Multi_map_base; template class Multi_map_base; +template class Multi_map_base; } // namespace dd::cache diff --git a/sql/dd/impl/cache/shared_dictionary_cache.cc b/sql/dd/impl/cache/shared_dictionary_cache.cc index 3942301c99d1..886bc4bdd336 100644 --- a/sql/dd/impl/cache/shared_dictionary_cache.cc +++ b/sql/dd/impl/cache/shared_dictionary_cache.cc @@ -67,6 +67,7 @@ void Shared_dictionary_cache::init() { spatial_reference_system_capacity); instance()->m_map()->set_capacity(tablespace_def_size); instance()->m_map()->set_capacity(resource_group_capacity); + instance()->m_map()->set_capacity(udt_type_capacity); } void Shared_dictionary_cache::shutdown() { @@ -84,6 +85,7 @@ void Shared_dictionary_cache::shutdown() { instance()->m_map()->shutdown(); instance()->m_map()->shutdown(); instance()->m_map()->shutdown(); + instance()->m_map()->shutdown(); delete s_cache_instance; s_cache_instance = nullptr; } @@ -330,6 +332,24 @@ Shared_dictionary_cache::get_uncached( template void Shared_dictionary_cache::put( const Tablespace *, Cache_element **); +template bool Shared_dictionary_cache::get( + THD *thd, const UDT_Type::Id_key &, Cache_element **); +template bool Shared_dictionary_cache::get( + THD *thd, const UDT_Type::Name_key &, Cache_element **); +template bool Shared_dictionary_cache::get( + THD *thd, const UDT_Type::Aux_key &, Cache_element **); +template bool Shared_dictionary_cache::get_uncached( + THD *thd, const UDT_Type::Id_key &, enum_tx_isolation, + const UDT_Type **) const; +template bool Shared_dictionary_cache::get_uncached< + UDT_Type::Name_key, UDT_Type>(THD *thd, const UDT_Type::Name_key &, + enum_tx_isolation, const UDT_Type **) const; +template bool Shared_dictionary_cache::get_uncached< + UDT_Type::Aux_key, UDT_Type>(THD *thd, const UDT_Type::Aux_key &, + enum_tx_isolation, const UDT_Type **) const; +template void Shared_dictionary_cache::put( + const UDT_Type *, Cache_element **); + template bool Shared_dictionary_cache::get( THD *thd, const Resource_group::Id_key &, Cache_element **); diff --git a/sql/dd/impl/cache/shared_dictionary_cache.h b/sql/dd/impl/cache/shared_dictionary_cache.h index ba460ffb7d90..efe310ec9b97 100644 --- a/sql/dd/impl/cache/shared_dictionary_cache.h +++ b/sql/dd/impl/cache/shared_dictionary_cache.h @@ -37,6 +37,7 @@ #include "sql/dd/types/spatial_reference_system.h" // Spatial_reference_system #include "sql/dd/types/table.h" // IWYU pragma: keep #include "sql/dd/types/tablespace.h" // Tablespace +#include "sql/dd/types/udt_type.h" // UDT_Type #include "sql/handler.h" // enum_tx_isolation class THD; @@ -78,6 +79,8 @@ class Shared_dictionary_cache { */ static const size_t resource_group_capacity = 32; + static const size_t udt_type_capacity = 256; + Shared_multi_map m_abstract_table_map; Shared_multi_map m_charset_map; Shared_multi_map m_collation_map; @@ -88,6 +91,7 @@ class Shared_dictionary_cache { Shared_multi_map m_schema_map; Shared_multi_map m_spatial_reference_system_map; Shared_multi_map m_tablespace_map; + Shared_multi_map m_udt_type_map; template struct Type_selector {}; // Dummy type to use for @@ -127,6 +131,9 @@ class Shared_dictionary_cache { Shared_multi_map *m_map(Type_selector) { return &m_tablespace_map; } + Shared_multi_map *m_map(Type_selector) { + return &m_udt_type_map; + } const Shared_multi_map *m_map( Type_selector) const { @@ -152,6 +159,9 @@ class Shared_dictionary_cache { const Shared_multi_map *m_map(Type_selector) const { return &m_tablespace_map; } + const Shared_multi_map *m_map(Type_selector) const { + return &m_udt_type_map; + } const Shared_multi_map *m_map( Type_selector) const { return &m_resource_group_map; diff --git a/sql/dd/impl/cache/shared_multi_map.cc b/sql/dd/impl/cache/shared_multi_map.cc index 677850ba1a78..87b6445c0a5d 100644 --- a/sql/dd/impl/cache/shared_multi_map.cc +++ b/sql/dd/impl/cache/shared_multi_map.cc @@ -42,6 +42,7 @@ #include "sql/dd/impl/tables/spatial_reference_systems.h" #include "sql/dd/impl/tables/tables.h" #include "sql/dd/impl/tables/tablespaces.h" +#include "sql/dd/impl/tables/udt_types.h" #include "sql/log.h" // sql_print_warning() #include "sql/mdl.h" // MDL_request #include "sql/sql_class.h" // THD @@ -692,5 +693,23 @@ template void Shared_multi_map::put( template void Shared_multi_map::drop_if_present< Resource_group::Id_key>(const Resource_group::Id_key &); +template class Shared_multi_map; +template bool Shared_multi_map::get( + const UDT_Type *const &, Cache_element **); +template bool Shared_multi_map::get( + const UDT_Type::Id_key &, Cache_element **); +template bool Shared_multi_map::get( + const UDT_Type::Name_key &, Cache_element **); +template bool Shared_multi_map::get( + const UDT_Type::Aux_key &, Cache_element **); +template void Shared_multi_map::put( + const UDT_Type::Id_key *, const UDT_Type *, Cache_element **); +template void Shared_multi_map::put( + const UDT_Type::Name_key *, const UDT_Type *, Cache_element **); +template void Shared_multi_map::put( + const UDT_Type::Aux_key *, const UDT_Type *, Cache_element **); +template void Shared_multi_map::drop_if_present( + const UDT_Type::Id_key &); + } // namespace cache } // namespace dd diff --git a/sql/dd/impl/cache/storage_adapter.cc b/sql/dd/impl/cache/storage_adapter.cc index 0a58a885aff6..eb4029cb0d4e 100644 --- a/sql/dd/impl/cache/storage_adapter.cc +++ b/sql/dd/impl/cache/storage_adapter.cc @@ -52,8 +52,10 @@ #include "sql/dd/impl/tables/table_stats.h" // dd::tables::Table_stats #include "sql/dd/impl/tables/tables.h" // dd::tables::Tables #include "sql/dd/impl/tables/tablespaces.h" // dd::tables::Tablespaces +#include "sql/dd/impl/tables/udt_types.h" // dd::tables::UDT_Types #include "sql/dd/impl/transaction_impl.h" // Transaction_ro #include "sql/dd/impl/types/entity_object_impl.h" +#include "sql/dd/impl/types/udt_type_impl.h" #include "sql/dd/types/abstract_table.h" // Abstract_table #include "sql/dd/types/charset.h" // Charset #include "sql/dd/types/collation.h" // Collation @@ -69,6 +71,7 @@ #include "sql/dd/types/table.h" // Table #include "sql/dd/types/table_stat.h" // Table_stat #include "sql/dd/types/tablespace.h" // Tablespace +#include "sql/dd/types/udt_type.h" // UDT_Type #include "sql/dd/types/view.h" // View #include "sql/debug_sync.h" // DEBUG_SYNC #include "sql/error_handler.h" // Internal_error_handler @@ -600,6 +603,18 @@ template bool Storage_adapter::get( template bool Storage_adapter::drop(THD *, const Tablespace *); template bool Storage_adapter::store(THD *, Tablespace *); +template bool Storage_adapter::get( + THD *, const UDT_Type::Id_key &, enum_tx_isolation, bool, + const UDT_Type **); +template bool Storage_adapter::get( + THD *, const UDT_Type::Name_key &, enum_tx_isolation, bool, + const UDT_Type **); +template bool Storage_adapter::get( + THD *, const UDT_Type::Aux_key &, enum_tx_isolation, bool, + const UDT_Type **); +template bool Storage_adapter::drop(THD *, const UDT_Type *); +template bool Storage_adapter::store(THD *, UDT_Type *); + /* DD objects dd::Table_stat and dd::Index_stat are not cached, because these objects are only updated and never read by DD diff --git a/sql/dd/impl/dd.cc b/sql/dd/impl/dd.cc index 02ec06f908de..a754025898d5 100644 --- a/sql/dd/impl/dd.cc +++ b/sql/dd/impl/dd.cc @@ -52,6 +52,7 @@ #include "sql/dd/impl/types/table_stat_impl.h" #include "sql/dd/impl/types/tablespace_file_impl.h" #include "sql/dd/impl/types/tablespace_impl.h" +#include "sql/dd/impl/types/udt_type_impl.h" #include "sql/dd/impl/types/view_impl.h" namespace dd { @@ -106,6 +107,7 @@ template Table *create_object(); template Table_stat *create_object(); template Tablespace *create_object(); template Tablespace_file *create_object(); +template UDT_Type *create_object(); template View *create_object(); namespace cache { @@ -135,6 +137,8 @@ template void Object_registry::create_map( std::unique_ptr> *map); template void Object_registry::create_map( std::unique_ptr> *map); +template void Object_registry::create_map( + std::unique_ptr> *map); } // namespace cache diff --git a/sql/dd/impl/system_registry.cc b/sql/dd/impl/system_registry.cc index 008cac7d3530..920884046fe0 100644 --- a/sql/dd/impl/system_registry.cc +++ b/sql/dd/impl/system_registry.cc @@ -66,6 +66,7 @@ #include "sql/dd/impl/system_views/table_constraints.h" // Table_constraints #include "sql/dd/impl/system_views/tables.h" // Tables #include "sql/dd/impl/system_views/triggers.h" // Triggers +#include "sql/dd/impl/system_views/udt_types.h" // Types #include "sql/dd/impl/system_views/user_attributes.h" #include "sql/dd/impl/system_views/view_routine_usage.h" // View_routine_usage #include "sql/dd/impl/system_views/view_table_usage.h" // View_table_usage @@ -102,6 +103,7 @@ #include "sql/dd/impl/tables/tablespace_files.h" // Tablespace_files #include "sql/dd/impl/tables/tablespaces.h" // Tablespaces #include "sql/dd/impl/tables/triggers.h" // Triggers +#include "sql/dd/impl/tables/udt_types.h" // Types #include "sql/dd/impl/tables/view_routine_usage.h" // View_routine_usage #include "sql/dd/impl/tables/view_table_usage.h" // View_table_usage #include "sql/table.h" // MYSQL_SYSTEM_SCHEMA @@ -201,6 +203,7 @@ void System_tables::add_remaining_dd_tables() { register_table(core); register_table(core); register_table(core); + register_table(second); register_table(core); register_table(core); @@ -321,6 +324,7 @@ void System_views::init() { register_view(is); register_view(is); register_view(is); + register_view(is); register_view(is); register_view(is); register_view(is); diff --git a/sql/dd/impl/system_views/udt_types.cc b/sql/dd/impl/system_views/udt_types.cc new file mode 100644 index 000000000000..4d26aab50acf --- /dev/null +++ b/sql/dd/impl/system_views/udt_types.cc @@ -0,0 +1,45 @@ +/* Copyright (c) 2017, 2026, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is designed to work with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have either included with + the program or referenced in the documentation. + + 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, version 2.0, 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-1301 USA */ + +#include "sql/dd/impl/system_views/udt_types.h" + +namespace dd::system_views { + +const UDT_Types &UDT_Types::instance() { + static auto *s_instance = new UDT_Types(); + return *s_instance; +} + +UDT_Types::UDT_Types() { + m_target_def.set_view_name(view_name()); + + m_target_def.add_field(FIELD_TYPE_SCHEMA, "TYPE_SCHEMA", + "sch.name" + m_target_def.fs_name_collation()); + m_target_def.add_field(FIELD_TYPE_NAME, "TYPE_NAME", + "typ.name" + m_target_def.fs_name_collation()); + + m_target_def.add_from("mysql.types typ"); + m_target_def.add_from("JOIN mysql.schemata sch ON typ.schema_id=sch.id"); +} + +} // namespace dd::system_views diff --git a/sql/dd/impl/system_views/udt_types.h b/sql/dd/impl/system_views/udt_types.h new file mode 100644 index 000000000000..803030c06ff8 --- /dev/null +++ b/sql/dd/impl/system_views/udt_types.h @@ -0,0 +1,57 @@ +/* Copyright (c) 2017, 2026, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is designed to work with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have either included with + the program or referenced in the documentation. + + 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, version 2.0, 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-1301 USA */ + +#ifndef DD_SYSTEM_VIEWS__TYPES_INCLUDED +#define DD_SYSTEM_VIEWS__TYPES_INCLUDED + +#include "sql/dd/impl/system_views/system_view_definition_impl.h" +#include "sql/dd/impl/system_views/system_view_impl.h" +#include "sql/dd/string_type.h" + +namespace dd { +namespace system_views { + +/* + The class representing INFORMATION_SCHEMA.TYPES + system view definition. +*/ +class UDT_Types : public System_view_impl { + public: + enum enum_fields { FIELD_TYPE_SCHEMA, FIELD_TYPE_NAME }; + + UDT_Types(); + + static const UDT_Types &instance(); + + static const String_type &view_name() { + static String_type s_view_name("TYPES"); + return s_view_name; + } + + const String_type &name() const override { return UDT_Types::view_name(); } +}; + +} // namespace system_views +} // namespace dd + +#endif // DD_SYSTEM_VIEWS__TYPES_INCLUDED diff --git a/sql/dd/impl/tables/udt_types.cc b/sql/dd/impl/tables/udt_types.cc new file mode 100644 index 000000000000..996198fdf32b --- /dev/null +++ b/sql/dd/impl/tables/udt_types.cc @@ -0,0 +1,92 @@ +/* Copyright (c) 2016, 2026, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is designed to work with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have either included with + the program or referenced in the documentation. + + 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, version 2.0, 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-1301 USA */ + +#include "sql/dd/impl/tables/udt_types.h" + +#include +#include + +#include "mysql/strings/m_ctype.h" +#include "sql/dd/impl/raw/object_keys.h" // Parent_id_range_key +#include "sql/dd/impl/raw/raw_record.h" +#include "sql/dd/impl/tables/dd_properties.h" // TARGET_DD_VERSION +#include "sql/dd/impl/types/object_table_definition_impl.h" +#include "sql/dd/impl/types/udt_type_impl.h" // dd::UDT_type_impl + +namespace dd::tables { + +const UDT_Types &UDT_Types::instance() { + static auto *s_instance = new UDT_Types(); + return *s_instance; +} + +/////////////////////////////////////////////////////////////////////////// + +const CHARSET_INFO *UDT_Types::name_collation() { + return &my_charset_utf8mb3_general_ci; +} + +/////////////////////////////////////////////////////////////////////////// + +UDT_Types::UDT_Types() { + m_target_def.set_table_name("types"); + + m_target_def.add_field(FIELD_ID, "FIELD_ID", + "id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT"); + m_target_def.add_field(FIELD_SCHEMA_ID, "FIELD_SCHEMA_ID", + "schema_id BIGINT UNSIGNED NOT NULL"); + m_target_def.add_field(FIELD_NAME, "FIELD_NAME", + "name VARCHAR(64) NOT NULL COLLATE " + + String_type(name_collation()->m_coll_name)); + + m_target_def.add_field(FIELD_CREATED, "FIELD_CREATED", + "created TIMESTAMP NOT NULL"); + m_target_def.add_field(FIELD_LAST_ALTERED, "FIELD_LAST_ALTERED", + "last_altered TIMESTAMP NOT NULL"); + + m_target_def.add_index(INDEX_PK_ID, "INDEX_PK_ID", "PRIMARY KEY (id)"); + m_target_def.add_index(INDEX_UK_SCHEMA_ID_NAME, "INDEX_UK_SCHEMA_ID_NAME", + "UNIQUE KEY (schema_id, name)"); + + m_target_def.add_foreign_key(FK_SCHEMA_ID, "FK_SCHEMA_ID", + "FOREIGN KEY (schema_id) " + "REFERENCES schemata(id)"); +} + +/////////////////////////////////////////////////////////////////////////// + +UDT_Type *UDT_Types::create_entity_object(const Raw_record &) const { + return new (std::nothrow) UDT_Type_impl(); +} + +/////////////////////////////////////////////////////////////////////////// + +bool UDT_Types::update_object_key(Item_name_key *key, Object_id schema_id, + const String_type &name) { + key->update(FIELD_SCHEMA_ID, schema_id, FIELD_NAME, name, name_collation()); + return false; +} + +/////////////////////////////////////////////////////////////////////////// + +} // namespace dd::tables diff --git a/sql/dd/impl/tables/udt_types.h b/sql/dd/impl/tables/udt_types.h new file mode 100644 index 000000000000..e3abc82c1c09 --- /dev/null +++ b/sql/dd/impl/tables/udt_types.h @@ -0,0 +1,81 @@ +/* Copyright (c) 2016, 2026, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is designed to work with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have either included with + the program or referenced in the documentation. + + 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, version 2.0, 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-1301 USA */ + +#ifndef DD_TABLES__TYPES_INCLUDED +#define DD_TABLES__TYPES_INCLUDED + +#include + +#include "sql/dd/impl/types/entity_object_table_impl.h" +#include "sql/dd/object_id.h" +#include "sql/dd/string_type.h" +#include "sql/dd/types/udt_type.h" + +struct CHARSET_INFO; + +namespace dd { + +class Item_name_key; +class Object_key; +class Raw_record; + +namespace tables { + +/////////////////////////////////////////////////////////////////////////// + +class UDT_Types : public Entity_object_table_impl { + public: + static const UDT_Types &instance(); + + static const CHARSET_INFO *name_collation(); + + enum enum_fields { + FIELD_ID, + FIELD_SCHEMA_ID, + FIELD_NAME, + FIELD_LAST_ALTERED, + FIELD_CREATED, + NUMBER_OF_FIELDS // Always keep this entry at the end of the enum + }; + + enum enum_indexes { + INDEX_PK_ID = static_cast(Common_index::PK_ID), + INDEX_UK_SCHEMA_ID_NAME = static_cast(Common_index::UK_NAME), + }; + + enum enum_foreign_keys { FK_SCHEMA_ID }; + + UDT_Types(); + + UDT_Type *create_entity_object(const Raw_record &) const override; + + static bool update_object_key(Item_name_key *key, Object_id catalog_id, + const String_type &name); +}; + +/////////////////////////////////////////////////////////////////////////// + +} // namespace tables +} // namespace dd + +#endif // DD_TABLES__TYPES_INCLUDED diff --git a/sql/dd/impl/types/schema_impl.cc b/sql/dd/impl/types/schema_impl.cc index 08d8766a3776..64f56d05ca6c 100644 --- a/sql/dd/impl/types/schema_impl.cc +++ b/sql/dd/impl/types/schema_impl.cc @@ -52,6 +52,7 @@ #include "sql/dd/types/library.h" // Library #include "sql/dd/types/procedure.h" // Procedure #include "sql/dd/types/table.h" +#include "sql/dd/types/udt_type.h" #include "sql/dd/types/view.h" // View #include "sql/histograms/value_map.h" #include "sql/mdl.h" @@ -331,6 +332,32 @@ View *Schema_impl::create_system_view(THD *thd [[maybe_unused]]) const { /////////////////////////////////////////////////////////////////////////// +UDT_Type *Schema_impl::create_udt_type(THD *thd) const { +// Creating UDT_Type requires an IX meta data lock on the schema name. +#ifndef NDEBUG + char name_buf[NAME_LEN + 1]; + assert(thd->mdl_context.owns_equal_or_stronger_lock( + MDL_key::SCHEMA, + dd::Object_table_definition_impl::fs_name_case(name(), name_buf), "", + MDL_INTENTION_EXCLUSIVE)); +#endif + + std::unique_ptr obj(dd::create_object()); + obj->set_schema_id(this->id()); + + // Get statement start time. + ulonglong ull_curtime = + dd::my_time_t_to_ull_datetime(thd->query_start_in_secs()); + + // Set new table start time. + obj->set_created(ull_curtime); + obj->set_last_altered(ull_curtime); + + return obj.release(); +} + +/////////////////////////////////////////////////////////////////////////// + const Object_table &Schema_impl::object_table() const { return DD_table::instance(); } diff --git a/sql/dd/impl/types/schema_impl.h b/sql/dd/impl/types/schema_impl.h index edf32299059c..60e8c6e79053 100644 --- a/sql/dd/impl/types/schema_impl.h +++ b/sql/dd/impl/types/schema_impl.h @@ -201,6 +201,8 @@ class Schema_impl : public Entity_object_impl, public Schema { View *create_system_view(THD *thd) const override; + UDT_Type *create_udt_type(THD *thd) const override; + public: void debug_print(String_type &outb) const override { char outbuf[1024]; diff --git a/sql/dd/impl/types/udt_type_impl.cc b/sql/dd/impl/types/udt_type_impl.cc new file mode 100644 index 000000000000..b6e57daea174 --- /dev/null +++ b/sql/dd/impl/types/udt_type_impl.cc @@ -0,0 +1,150 @@ +/* Copyright (c) 2016, 2026, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is designed to work with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have either included with + the program or referenced in the documentation. + + 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, version 2.0, 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-1301 USA */ + +#include "sql/dd/impl/types/udt_type_impl.h" + +#include + +#include + +#include "my_rapidjson_size_t.h" // IWYU pragma: keep + +#include +#include + +#include "m_string.h" +#include "sql/dd/dd_utility.h" // normalize_string() +#include "sql/dd/impl/dictionary_impl.h" // Dictionary_impl +#include "sql/dd/impl/raw/raw_record.h" // Raw_record +#include "sql/dd/impl/sdi_impl.h" // sdi read/write functions +#include "sql/dd/impl/tables/schemata.h" // Schemata::name_collation +#include "sql/dd/impl/tables/udt_types.h" // Spatial_reference_sy... +#include "sql/dd/impl/transaction_impl.h" // Open_dictionary_tables_ctx +#include "sql/dd/impl/utils.h" // is_string_in_lowercase +#include "string_with_len.h" + +namespace dd { +class Sdi_rcontext; +class Sdi_wcontext; +} // namespace dd + +using dd::tables::UDT_Types; + +namespace dd { + +/////////////////////////////////////////////////////////////////////////// +// UDT_Type_impl implementation. +/////////////////////////////////////////////////////////////////////////// + +bool UDT_Type_impl::validate() const { return false; } + +/////////////////////////////////////////////////////////////////////////// + +bool UDT_Type_impl::restore_attributes(const Raw_record &r) { + restore_id(r, UDT_Types::FIELD_ID); + restore_name(r, UDT_Types::FIELD_NAME); + + m_schema_id = r.read_ref_id(UDT_Types::FIELD_SCHEMA_ID); + m_last_altered = r.read_int(UDT_Types::FIELD_LAST_ALTERED); + m_created = r.read_int(UDT_Types::FIELD_CREATED); + + return false; +} + +/////////////////////////////////////////////////////////////////////////// + +bool UDT_Type_impl::store_attributes(Raw_record *r) { + return store_id(r, UDT_Types::FIELD_ID) || + store_name(r, UDT_Types::FIELD_NAME) || + r->store_ref_id(UDT_Types::FIELD_SCHEMA_ID, m_schema_id) || + r->store(UDT_Types::FIELD_CREATED, m_created) || + r->store(UDT_Types::FIELD_LAST_ALTERED, m_last_altered); +} + +/////////////////////////////////////////////////////////////////////////// +static_assert(UDT_Types::NUMBER_OF_FIELDS == 5, + "UDT_Types definition has changed, check if " + "serialize() and deserialize() need to be updated!"); +void UDT_Type_impl::serialize(Sdi_wcontext *wctx, Sdi_writer *w) const { + w->StartObject(); + Entity_object_impl::serialize(wctx, w); + write(w, m_last_altered, STRING_WITH_LEN("last_altered")); + write(w, m_created, STRING_WITH_LEN("created")); + w->EndObject(); +} + +/////////////////////////////////////////////////////////////////////////// + +bool UDT_Type_impl::deserialize(Sdi_rcontext *rctx, const RJ_Value &val) { + Entity_object_impl::deserialize(rctx, val); + read(&m_last_altered, val, "last_altered"); + read(&m_created, val, "created"); + + return false; +} + +/////////////////////////////////////////////////////////////////////////// + +bool UDT_Type::update_id_key(Id_key *key, Object_id id) { + key->update(id); + return false; +} + +/////////////////////////////////////////////////////////////////////////// + +bool UDT_Type::update_name_key(Name_key *key, Object_id schema_id, + const String_type &name) { + return UDT_Types::update_object_key(key, schema_id, name); +} + +/////////////////////////////////////////////////////////////////////////// + +const Object_table &UDT_Type_impl::object_table() const { + return DD_table::instance(); +} + +/////////////////////////////////////////////////////////////////////////// + +void UDT_Type_impl::register_tables(Open_dictionary_tables_ctx *otx) { + otx->add_table(); +} + +/////////////////////////////////////////////////////////////////////////// + +void UDT_Type::create_mdl_key(const String_type &schema_name, + const String_type &name, MDL_key *mdl_key) { +#ifndef DEBUG_OFF + // Make sure schema name is lowercased when lower_case_table_names == 2. + if (lower_case_table_names == 2) + assert(is_string_in_lowercase(schema_name, + tables::Schemata::name_collation())); + DBUG_EXECUTE_IF("simulate_lctn_two_case_for_schema_case_compare", { + assert((lower_case_table_names == 2) || + is_string_in_lowercase(schema_name, &my_charset_utf8mb3_tolower_ci)); + }); +#endif + + mdl_key->mdl_key_init(MDL_key::UDT_TYPE, schema_name.c_str(), name.c_str()); +} + +} // namespace dd diff --git a/sql/dd/impl/types/udt_type_impl.h b/sql/dd/impl/types/udt_type_impl.h new file mode 100644 index 000000000000..ff76c754d9f7 --- /dev/null +++ b/sql/dd/impl/types/udt_type_impl.h @@ -0,0 +1,170 @@ +/* Copyright (c) 2016, 2026, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is designed to work with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have either included with + the program or referenced in the documentation. + + 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, version 2.0, 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-1301 USA */ + +#ifndef DD__UDT_TYPE_IMPL_INCLUDED +#define DD__UDT_TYPE_IMPL_INCLUDED + +#include +#include + +#include // std::nullptr_t +#include // std::unique_ptr +#include +#include + +#include "my_inttypes.h" +#include "sql/dd/impl/types/entity_object_impl.h" // dd::Entity_object_impl +#include "sql/dd/impl/types/weak_object_impl.h" +#include "sql/dd/object_id.h" +#include "sql/dd/sdi_fwd.h" +#include "sql/dd/string_type.h" +#include "sql/dd/types/udt_type.h" // dd:UDT_Type +#include "sql/dd/types/weak_object.h" +#include "sql/sql_time.h" // gmt_time_to_local_time + +class THD; + +namespace dd { + +/////////////////////////////////////////////////////////////////////////// + +class Open_dictionary_tables_ctx; +class Raw_record; +class Sdi_rcontext; +class Sdi_wcontext; +class Object_table; + +/////////////////////////////////////////////////////////////////////////// + +class UDT_Type_impl : public Entity_object_impl, public UDT_Type { + public: + UDT_Type_impl() : m_created(0), m_last_altered(0) {} + + private: + UDT_Type_impl(const UDT_Type_impl &other) + : Weak_object(other), + Entity_object_impl(other), + m_created(other.m_created), + m_last_altered(other.m_last_altered), + m_schema_id(other.m_schema_id) {} + + public: + const Object_table &object_table() const override; + + bool validate() const override; + + bool store_attributes(Raw_record *r) override; + + bool restore_attributes(const Raw_record &r) override; + + void serialize(Sdi_wcontext *wctx, Sdi_writer *w) const; + + bool deserialize(Sdi_rcontext *rctx, const RJ_Value &val); + + public: + static void register_tables(Open_dictionary_tables_ctx *otx); + + ///////////////////////////////////////////////////////////////////////// + // schema. + ///////////////////////////////////////////////////////////////////////// + + Object_id schema_id() const override { return m_schema_id; } + + void set_schema_id(Object_id schema_id) override { m_schema_id = schema_id; } + + ///////////////////////////////////////////////////////////////////////// + // created + ///////////////////////////////////////////////////////////////////////// + + ulonglong created(bool convert_time) const override { + return convert_time ? gmt_time_to_local_time(m_created) : m_created; + } + + void set_created(ulonglong created) override { m_created = created; } + + ///////////////////////////////////////////////////////////////////////// + // last_altered + ///////////////////////////////////////////////////////////////////////// + + ulonglong last_altered(bool convert_time) const override { + return convert_time ? gmt_time_to_local_time(m_last_altered) + : m_last_altered; + } + + void set_last_altered(ulonglong last_altered) override { + m_last_altered = last_altered; + } + + // Fix "inherits ... via dominance" warnings + Entity_object_impl *impl() override { return Entity_object_impl::impl(); } + const Entity_object_impl *impl() const override { + return Entity_object_impl::impl(); + } + Object_id id() const override { return Entity_object_impl::id(); } + bool is_persistent() const override { + return Entity_object_impl::is_persistent(); + } + const String_type &name() const override { + return Entity_object_impl::name(); + } + void set_name(const String_type &name) override { + Entity_object_impl::set_name(name); + } + + public: + void debug_print(String_type &outb) const override { + char outbuf[1024]; + sprintf(outbuf, + "UDT_Type OBJECT: id= {OID: %lld}, " + "name= %s, m_created= %llu, m_last_altered= %llu", + id(), name().c_str(), m_created, m_last_altered); + outb = String_type(outbuf); + } + + private: + // Fields + ulonglong m_created; + ulonglong m_last_altered; + + Object_id m_schema_id; + + UDT_Type *clone() const override { return new UDT_Type_impl(*this); } + + UDT_Type *clone_dropped_object_placeholder() const override { + /* + Even though we don't drop SRSes en masse we still create slimmed + down version for consistency sake. + */ + UDT_Type_impl *placeholder = new UDT_Type_impl(); + placeholder->set_id(id()); + placeholder->set_schema_id(schema_id()); + placeholder->set_name(name()); + return placeholder; + } +}; + +/////////////////////////////////////////////////////////////////////////// + +} // namespace dd + +#endif // DD__UDT_TYPE_IMPL_INCLUDED diff --git a/sql/dd/types/schema.h b/sql/dd/types/schema.h index 4dd9c12c849a..72cc422b0362 100644 --- a/sql/dd/types/schema.h +++ b/sql/dd/types/schema.h @@ -40,6 +40,7 @@ class Item_name_key; class Primary_id_key; class Schema_impl; class Table; +class UDT_Type; class View; class Event; class Function; @@ -154,6 +155,8 @@ class Schema : virtual public Entity_object { virtual View *create_system_view(THD *thd) const = 0; + virtual UDT_Type *create_udt_type(THD *thd) const = 0; + /** Allocate a new object and invoke the copy constructor. diff --git a/sql/dd/types/udt_type.h b/sql/dd/types/udt_type.h new file mode 100644 index 000000000000..622663dc62d8 --- /dev/null +++ b/sql/dd/types/udt_type.h @@ -0,0 +1,124 @@ +/* Copyright (c) 2016, 2026, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is designed to work with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have either included with + the program or referenced in the documentation. + + 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, version 2.0, 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-1301 USA */ + +#ifndef DD__TYPE_INCLUDED +#define DD__TYPE_INCLUDED + +#include // std::nullptr_t +#include + +#include "my_inttypes.h" +#include "sql/dd/impl/raw/object_keys.h" // IWYU pragma: keep +#include "sql/dd/types/entity_object.h" // dd::Entity_object + +class THD; +struct MDL_key; + +namespace dd { + +/////////////////////////////////////////////////////////////////////////// + +class Item_name_key; +class Primary_id_key; +class UDT_Type_impl; +class Void_key; + +namespace tables { +class UDT_Types; +} + +/////////////////////////////////////////////////////////////////////////// + +class UDT_Type : virtual public Entity_object { + public: + typedef UDT_Type_impl Impl; + typedef UDT_Type Cache_partition; + typedef tables::UDT_Types DD_table; + typedef Primary_id_key Id_key; + typedef Item_name_key Name_key; + typedef Void_key Aux_key; + + // We need a set of functions to update a preallocated key. + virtual bool update_id_key(Id_key *key) const { + return update_id_key(key, id()); + } + + static bool update_id_key(Id_key *key, Object_id id); + + virtual bool update_name_key(Name_key *key) const { + return update_name_key(key, schema_id(), name()); + } + + static bool update_name_key(Name_key *key, Object_id schema_id, + const String_type &name); + + virtual bool update_aux_key(Aux_key *) const { return true; } + + public: + ~UDT_Type() override = default; + + ///////////////////////////////////////////////////////////////////////// + // schema. + ///////////////////////////////////////////////////////////////////////// + + virtual Object_id schema_id() const = 0; + virtual void set_schema_id(Object_id schema_id) = 0; + + ///////////////////////////////////////////////////////////////////////// + // created + ///////////////////////////////////////////////////////////////////////// + + virtual ulonglong created(bool convert_time) const = 0; + virtual void set_created(ulonglong created) = 0; + + ///////////////////////////////////////////////////////////////////////// + // last_altered + ///////////////////////////////////////////////////////////////////////// + + virtual ulonglong last_altered(bool convert_time) const = 0; + virtual void set_last_altered(ulonglong last_altered) = 0; + + /** + Allocate a new object and invoke the copy constructor + + @return pointer to dynamically allocated copy + */ + virtual UDT_Type *clone() const = 0; + + /** + Allocate a new object which can serve as a placeholder for the original + object in the Dictionary_client's dropped registry. Such object has the + same keys as the original but has no other info and as result occupies + less memory. + */ + virtual UDT_Type *clone_dropped_object_placeholder() const = 0; + + static void create_mdl_key(const String_type &schema_name, + const String_type &name, MDL_key *key); +}; + +/////////////////////////////////////////////////////////////////////////// + +} // namespace dd + +#endif // DD__TYPE_INCLUDED diff --git a/sql/item_func.cc b/sql/item_func.cc index ca9a4ab91acb..e58d7537e16d 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -8565,6 +8565,8 @@ bool Item_func_sp::init_result_field(THD *thd) { m_sp = sp_find_routine(thd, enum_sp_type::FUNCTION, m_name, &thd->sp_func_cache, true); if (m_sp == nullptr) { + fprintf(stderr, "Item_func_sp::init_result_field() function not found\n"); + my_missing_function_error(m_name->m_name, m_name->m_qname.str); return true; } diff --git a/sql/mdl.cc b/sql/mdl.cc index 846b4fd683e2..3e9667d01584 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -133,6 +133,7 @@ PSI_stage_info MDL_key::m_namespace_to_wait_state_name[NAMESPACE_END] = { {0, "Waiting for foreign key metadata lock", 0, PSI_DOCUMENT_ME}, {0, "Waiting for check constraint metadata lock", 0, PSI_DOCUMENT_ME}, {0, "Waiting for library metadata lock", 0, PSI_DOCUMENT_ME}, + {0, "Waiting for user defined type lock", 0, PSI_DOCUMENT_ME}, }; #ifdef HAVE_PSI_INTERFACE diff --git a/sql/mdl.h b/sql/mdl.h index 6e57431fa3cd..475e93e88335 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -419,6 +419,7 @@ struct MDL_key { FOREIGN_KEY, CHECK_CONSTRAINT, LIBRARY, + UDT_TYPE, /* This should be the last ! */ NAMESPACE_END }; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 8e264455c53a..93487ffa1ab4 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -895,6 +895,7 @@ MySQL clients support the protocol: #include "sql/sql_show.h" #include "sql/sql_table.h" // build_table_filename #include "sql/sql_udf.h" +#include "sql/sql_udt.h" #include "sql/ssl_acceptor_context_iterator.h" #include "sql/ssl_acceptor_context_operator.h" #include "sql/ssl_acceptor_context_status.h" @@ -4293,6 +4294,9 @@ SHOW_VAR com_status_vars[] = { {"create_table", (char *)offsetof(System_status_var, com_stat[(uint)SQLCOM_CREATE_TABLE]), SHOW_LONG_STATUS, SHOW_SCOPE_ALL}, + {"create_type", + (char *)offsetof(System_status_var, com_stat[(uint)SQLCOM_CREATE_TYPE]), + SHOW_LONG_STATUS, SHOW_SCOPE_ALL}, {"create_resource_group", (char *)offsetof(System_status_var, com_stat[(uint)SQLCOM_CREATE_RESOURCE_GROUP]), @@ -8470,6 +8474,8 @@ static int init_server_components() { */ udf_init_globals(); + udt_init_globals(); + /* Set tc_log to point to TC_LOG_DUMMY early in order to allow plugin_init() to commit attachable transaction after reading from mysql.plugin table. diff --git a/sql/parse_tree_column_attrs.h b/sql/parse_tree_column_attrs.h index cf3fbf92040b..78fc0dcb29b1 100644 --- a/sql/parse_tree_column_attrs.h +++ b/sql/parse_tree_column_attrs.h @@ -672,6 +672,7 @@ class PT_type : public Parse_tree_node { virtual uint get_uint_geom_type() const { return 0; } virtual List *get_interval_list() const { return nullptr; } virtual bool is_serial_type() const { return false; } + virtual const Type_ident *get_type_ident() const { return nullptr; } }; /** @@ -1013,6 +1014,24 @@ class PT_json_type : public PT_type { const CHARSET_INFO *get_charset() const override { return &my_charset_bin; } }; +class PT_user_defined_type : public PT_type { + typedef PT_type super; + + public: + explicit PT_user_defined_type(const POS &pos, Type_ident *ident) + : PT_type(pos, MYSQL_TYPE_INVALID), type_ident(ident) {} + + const Type_ident *get_type_ident() const override { return type_ident; } + + bool do_contextualize(Parse_context *pc) override { + if (super::do_contextualize(pc)) return true; + return false; + } + + private: + Type_ident *type_ident; +}; + /** Base class for both generated and regular column definitions diff --git a/sql/parse_tree_items.cc b/sql/parse_tree_items.cc index d3d64899d331..fe726f900eac 100644 --- a/sql/parse_tree_items.cc +++ b/sql/parse_tree_items.cc @@ -51,6 +51,7 @@ #include "sql/sql_list.h" #include "sql/sql_show.h" // append_identifier() #include "sql/sql_udf.h" +#include "sql/sql_udt.h" #include "sql/system_variables.h" #include "sql/table.h" #include "sql/trigger_def.h" @@ -285,10 +286,18 @@ bool PTI_function_call_generic_ident_sys::do_itemize(Parse_context *pc, *res = Create_udf_func::s_singleton.create(thd, m_pos, udf, opt_udf_expr_list); } else { - builder = find_qualified_function_builder(thd); - assert(builder); - *res = builder->create_func(thd, m_pos, ident, opt_udf_expr_list); - pc->select->n_stored_func_calls++; + // Try UDT functions + + auto *udt_function = acquire_udt_function(ident.str); + if (udt_function) { + *res = Create_udt_func::create(thd, m_pos, udt_function, + opt_udf_expr_list); + } else { + builder = find_qualified_function_builder(thd); + assert(builder); + *res = builder->create_func(thd, m_pos, ident, opt_udf_expr_list); + pc->select->n_stored_func_calls++; + } } } return *res == nullptr || (*res)->itemize(pc, res); diff --git a/sql/parse_tree_nodes.cc b/sql/parse_tree_nodes.cc index 327208cc60f4..2b87cc46e810 100644 --- a/sql/parse_tree_nodes.cc +++ b/sql/parse_tree_nodes.cc @@ -84,6 +84,7 @@ #include "sql/sql_class.h" #include "sql/sql_cmd.h" #include "sql/sql_cmd_ddl_table.h" +#include "sql/sql_cmd_ddl_type.h" #include "sql/sql_component.h" // Sql_cmd_component #include "sql/sql_const.h" #include "sql/sql_data_change.h" @@ -5868,3 +5869,11 @@ Sql_cmd *PT_install_component::make_cmd(THD *thd) { return new (thd->mem_root) Sql_cmd_install_component(m_urns, m_set_elements); } + +// -- BEGIN POC + +Sql_cmd *PT_create_type_stmt::make_cmd(THD *thd) { + thd->lex->sql_command = SQLCOM_CREATE_TYPE; + + return new (thd->mem_root) Sql_cmd_create_type(m_type_name); +} diff --git a/sql/parse_tree_nodes.h b/sql/parse_tree_nodes.h index f0451a7015ff..631505d7698a 100644 --- a/sql/parse_tree_nodes.h +++ b/sql/parse_tree_nodes.h @@ -6129,4 +6129,22 @@ PT_set_operation *flatten_equal_set_ops(MEM_ROOT *mem_root, const POS &pos, } } +// -- BEGIN POC + +class PT_create_type_stmt : public Parse_tree_root { + Type_ident *m_type_name; + POS m_columns_end_pos; + + public: + PT_create_type_stmt(const POS &pos, Type_ident *type_name, + const POS &columns_end_pos = POS()) + : Parse_tree_root(pos), + m_type_name(type_name), + m_columns_end_pos(columns_end_pos) {} + + Sql_cmd *make_cmd(THD *thd) override; +}; + +// -- END POC + #endif /* PARSE_TREE_NODES_INCLUDED */ diff --git a/sql/parser_yystype.h b/sql/parser_yystype.h index 07774f3fc784..1cd6b3bd3ba1 100644 --- a/sql/parser_yystype.h +++ b/sql/parser_yystype.h @@ -147,6 +147,7 @@ class PT_with_list; class Parse_tree_root; class Query_block; class String; +class Type_ident; class Table_ident; class sp_condition_value; class sp_head; @@ -517,6 +518,7 @@ union MY_SQL_PARSER_STYPE { } lead_lag_info; PT_insert_values_list *values_list; Parse_tree_root *top_level_node; + Type_ident *type_ident; Table_ident *table_ident; Mem_root_array_YY table_ident_list; delete_option_enum opt_delete_option; diff --git a/sql/server_component/mysql_user_defined_type_imp.h b/sql/server_component/mysql_user_defined_type_imp.h new file mode 100644 index 000000000000..6268ebe83756 --- /dev/null +++ b/sql/server_component/mysql_user_defined_type_imp.h @@ -0,0 +1,69 @@ +/* Copyright (c) 2020, 2026, Oracle and/or its affiliates. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License, version 2.0, +as published by the Free Software Foundation. + +This program is designed to work with certain software (including +but not limited to OpenSSL) that is licensed under separate terms, +as designated in a particular file or component or in included license +documentation. The authors of MySQL hereby grant you an additional +permission to link the program and your derivative works with the +separately licensed software that they have either included with +the program or referenced in the documentation. + +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, version 2.0, 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-1301 USA */ + +#ifndef MYSQL_USER_DEFINED_TYPE_IMP_H +#define MYSQL_USER_DEFINED_TYPE_IMP_H + +#include +#include +#include + +class mysql_udt_registration_imp { + public: /* service implementations */ + static DEFINE_METHOD(int, register_type, + (mysql_type_descriptor_t * td, void *impl)); + + static DEFINE_METHOD(int, unregister_type, (mysql_type_descriptor_t * td)); + + static DEFINE_METHOD(int, register_function, + (mysql_function_descriptor_t * fd, + eval_function_t impl)); + + static DEFINE_METHOD(int, unregister_function, + (mysql_function_descriptor_t * fd)); +}; + +class mysql_udt_value_null_imp { + public: /* service implementations */ + static DEFINE_METHOD(void, set_null, (UDT_value * f, bool is_null)); + static DEFINE_METHOD(void, get_null, (UDT_value * f, bool *is_null)); +}; + +class mysql_udt_value_string_imp { + public: /* service implementations */ + static DEFINE_METHOD(void, set_utf8mb4, + (UDT_value * f, const char *value, unsigned int length)); + static DEFINE_METHOD(void, get_utf8mb4, + (UDT_value * f, const char **str, unsigned int *length)); +}; + +class mysql_udt_value_blob_imp { + public: /* service implementations */ + static DEFINE_METHOD(void, set, + (UDT_value * f, const unsigned char *val, + unsigned int len)); + static DEFINE_METHOD(void, get, + (UDT_value * f, unsigned char *val, unsigned int *len)); +}; + +#endif // MYSQL_USER_DEFINED_TYPE_IMP_H diff --git a/sql/server_component/server_component.cc b/sql/server_component/server_component.cc index 3824e0ddde67..d852ebb865ab 100644 --- a/sql/server_component/server_component.cc +++ b/sql/server_component/server_component.cc @@ -57,6 +57,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mysql/components/services/mysql_timestamp.h" #include "mysql/components/services/table_access_service.h" +#include "mysql/components/services/mysql_user_defined_type.h" + // pfs services #include @@ -137,6 +139,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mysql/components/services/log_sink_perfschema.h" #include "table_access_service_impl.h" +#include "mysql_user_defined_type_imp.h" + /* Implementation located in the mysql_server component. */ extern SERVICE_TYPE(mysql_cond_v1) SERVICE_IMPLEMENTATION(mysql_server, mysql_cond_v1); @@ -963,6 +967,36 @@ mysql_component_mysql_lock_free_hash_imp::init, mysql_component_mysql_lock_free_hash_imp::overhead END_SERVICE_IMPLEMENTATION(); +// clang-format off +BEGIN_SERVICE_IMPLEMENTATION(mysql_server, udt_registration) + mysql_udt_registration_imp::register_type, + mysql_udt_registration_imp::unregister_type, + mysql_udt_registration_imp::register_function, + mysql_udt_registration_imp::unregister_function +END_SERVICE_IMPLEMENTATION(); +// clang-format on + +// clang-format off +BEGIN_SERVICE_IMPLEMENTATION(mysql_server, udt_value_null) + mysql_udt_value_null_imp::set_null, + mysql_udt_value_null_imp::get_null +END_SERVICE_IMPLEMENTATION(); +// clang-format on + +// clang-format off +BEGIN_SERVICE_IMPLEMENTATION(mysql_server, udt_value_string) + mysql_udt_value_string_imp::set_utf8mb4, + mysql_udt_value_string_imp::get_utf8mb4 +END_SERVICE_IMPLEMENTATION(); +// clang-format on + +// clang-format off +BEGIN_SERVICE_IMPLEMENTATION(mysql_server, udt_value_blob) + mysql_udt_value_blob_imp::set, + mysql_udt_value_blob_imp::get +END_SERVICE_IMPLEMENTATION(); +// clang-format on + BEGIN_COMPONENT_PROVIDES(mysql_server) PROVIDES_SERVICE(mysql_server_path_filter, dynamic_loader_scheme_file), PROVIDES_SERVICE(mysql_server, persistent_dynamic_loader), @@ -1234,6 +1268,13 @@ PROVIDES_SERVICE(mysql_server_path_filter, dynamic_loader_scheme_file), PROVIDES_SERVICE(mysql_server, mysql_file), PROVIDES_SERVICE(mysql_server, mysql_server_attributes), PROVIDES_SERVICE(mysql_server, mysql_lock_free_hash), + + // Prototype + PROVIDES_SERVICE(mysql_server, udt_registration), + PROVIDES_SERVICE(mysql_server, udt_value_null), + PROVIDES_SERVICE(mysql_server, udt_value_string), + PROVIDES_SERVICE(mysql_server, udt_value_blob), + END_COMPONENT_PROVIDES(); static BEGIN_COMPONENT_REQUIRES(mysql_server) END_COMPONENT_REQUIRES(); diff --git a/sql/sql_cmd_ddl_type.cc b/sql/sql_cmd_ddl_type.cc new file mode 100644 index 000000000000..89b248e0192e --- /dev/null +++ b/sql/sql_cmd_ddl_type.cc @@ -0,0 +1,119 @@ +/* Copyright (c) 2026, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is designed to work with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have either included with + the program or referenced in the documentation. + + 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, version 2.0, 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-1301 USA */ + +#include "sql/sql_cmd_ddl_type.h" +#include "sql/dd/cache/dictionary_client.h" // Dictionary_client +#include "sql/dd/dd_udt_type.h" +#include "sql/mysqld.h" // lower_case_table_names +#include "sql/sql_lex.h" +#include "sql/transaction.h" +#include "sql/warn_not_implemented.h" + +bool Sql_cmd_create_type::execute(THD *thd) { + bool rc; + +#ifdef WITH_EXPERIMENTAL_UDT + WARN_NOT_IMPLEMENTED(thd, "Sql_cmd_create_type::execute()"); + + const char *db_name = m_type_ident->db.str; + const char *type_name = m_type_ident->type.str; + + // MDL LOCK (SCHEMA) + + /* + When creating the schema, we must lock the schema name without case (for + correct MDL locking) when l_c_t_n == 2. + */ + char name_buf[NAME_LEN + 1]; + const char *lock_db_name = db_name; + if (lower_case_table_names == 2) { + my_stpcpy(name_buf, db_name); + my_casedn_str(&my_charset_utf8mb3_tolower_ci, name_buf); + lock_db_name = name_buf; + } + + if (lock_schema_name(thd, lock_db_name)) { + return true; + } + + // MDL LOCK (TYPE) + + MDL_request mdl_request; + MDL_REQUEST_INIT(&mdl_request, MDL_key::UDT_TYPE, db_name, type_name, + MDL_EXCLUSIVE, MDL_TRANSACTION); + + /* + Acquire the lock request created above, and check if + acquisition fails (e.g. timeout or deadlock). + */ + if (thd->mdl_context.acquire_lock(&mdl_request, + thd->variables.lock_wait_timeout)) { + assert(thd->is_system_thread() || thd->killed || thd->is_error()); + return true; + } + + // DD LOOK UP + + const dd::cache::Dictionary_client::Auto_releaser releaser(thd->dd_client()); + + dd::cache::Dictionary_client &dc = *thd->dd_client(); + dd::String_type schema_name{m_type_ident->db.str}; + const dd::Schema *existing_schema = nullptr; + if (dc.acquire(schema_name, &existing_schema)) { + return true; + } + + if (existing_schema == nullptr) { + my_error(ER_NO_SUCH_DB, MYF(0), schema_name.c_str()); + return true; + } + + // CREATE TYPE + + bool exists; + if (dd::udt_type_exists(thd->dd_client(), db_name, type_name, &exists)) { + return true; + } + + if (exists) { + my_error(ER_UDT_TYPE_CREATE_EXISTS, MYF(0), db_name, type_name); + return true; + } + + if (dd::create_udt_type(thd, *existing_schema, type_name)) { + return true; + } + + if (trans_commit_stmt(thd) || trans_commit(thd)) { + return true; + } + + my_ok(thd); + rc = false; +#else + my_error(ER_NOT_SUPPORTED_YET, MYF(0), "CREATE TYPE"); + rc = true; +#endif + + return rc; +} diff --git a/sql/sql_cmd_ddl_type.h b/sql/sql_cmd_ddl_type.h new file mode 100644 index 000000000000..40035f61e502 --- /dev/null +++ b/sql/sql_cmd_ddl_type.h @@ -0,0 +1,55 @@ +/* Copyright (c) 2026, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is designed to work with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have either included with + the program or referenced in the documentation. + + 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, version 2.0, 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-1301 USA */ + +#ifndef SQL_CMD_DDL_TYPE_INCLUDED +#define SQL_CMD_DDL_TYPE_INCLUDED + +#include "lex_string.h" +#include "my_sqlcommand.h" +#include "sql/sql_cmd_ddl.h" + +class THD; +class Type_ident; + +class Sql_cmd_ddl_type : public Sql_cmd_ddl { + public: + Sql_cmd_ddl_type() = default; + ~Sql_cmd_ddl_type() = default; +}; + +class Sql_cmd_create_type final : public Sql_cmd_ddl_type { + public: + Sql_cmd_create_type(Type_ident *type_ident) + : Sql_cmd_ddl_type(), m_type_ident(type_ident) {} + + enum_sql_command sql_command_code() const override { + return SQLCOM_CREATE_TYPE; + } + + bool execute(THD *thd) override; + + private: + Type_ident *m_type_ident; +}; + +#endif /* SQL_CMD_DDL_TYPE_INCLUDED */ diff --git a/sql/sql_lex.h b/sql/sql_lex.h index ae07561d70ca..892bfbcfd949 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -307,6 +307,16 @@ enum class enum_alter_user_attribute { #define TL_OPTION_IGNORE_LEAVES 0x02 #define TL_OPTION_ALIAS 0x04 +class Type_ident { + public: + LEX_CSTRING db; + LEX_CSTRING type; + + Type_ident(const LEX_CSTRING &db_arg, const LEX_CSTRING &type_arg) + : db(db_arg), type(type_arg) {} + Type_ident(const LEX_CSTRING &type_arg) : type(type_arg) { db = NULL_CSTR; } +}; + /* Structure for db & table in sql_yacc */ class Table_function; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 8587a1978067..c09f8295a816 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1198,6 +1198,12 @@ void init_sql_command_flags() { sql_command_flags[SQLCOM_RENAME_USER] |= CF_REQUIRE_ACL_CACHE; sql_command_flags[SQLCOM_SHOW_GRANTS] |= CF_REQUIRE_ACL_CACHE; sql_command_flags[SQLCOM_SET_PASSWORD] |= CF_REQUIRE_ACL_CACHE; + + // Prototyping + sql_command_flags[SQLCOM_CREATE_TYPE] = + CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_DISALLOW_IN_RO_TRANS | + CF_ALLOW_PROTOCOL_PLUGIN | CF_NEEDS_AUTOCOMMIT_OFF | + CF_POTENTIAL_ATOMIC_DDL; } bool sqlcom_can_generate_row_events(enum enum_sql_command command) { @@ -4756,7 +4762,8 @@ int mysql_execute_command(THD *thd, bool first_level) { case SQLCOM_DROP_SRS: case SQLCOM_CREATE_LIBRARY: case SQLCOM_DROP_LIBRARY: - case SQLCOM_ALTER_LIBRARY: { + case SQLCOM_ALTER_LIBRARY: + case SQLCOM_CREATE_TYPE: { assert(lex->m_sql_cmd != nullptr); res = lex->m_sql_cmd->execute(thd); diff --git a/sql/sql_udt.cc b/sql/sql_udt.cc new file mode 100644 index 000000000000..444367133d33 --- /dev/null +++ b/sql/sql_udt.cc @@ -0,0 +1,435 @@ +/* Copyright (c) 2000, 2026, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is designed to work with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have either included with + the program or referenced in the documentation. + + 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, version 2.0, 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-1301 USA */ + +#include "my_macros.h" +#include "my_psi_config.h" + +#include + +#include "map_helpers.h" +#include "my_alloc.h" +#include "my_dbug.h" +#include "sql/mysqld_cs.h" + +#include "mysql/components/services/bits/mysql_rwlock_bits.h" +#include "mysql/components/services/bits/psi_bits.h" +#include "mysql/components/services/bits/psi_memory_bits.h" +#include "mysql/components/services/bits/psi_rwlock_bits.h" +#include "mysql/psi/mysql_memory.h" +#include "mysql/psi/mysql_rwlock.h" +#include "mysql/strings/m_ctype.h" +#include "sql/item_create.h" +#include "sql/sql_class.h" +#include "sql/thr_malloc.h" + +#include "mysql/components/services/mysql_user_defined_type.h" +#include "sql/current_thd.h" +#include "sql/server_component/mysql_user_defined_type_imp.h" +#include "sql/sql_udt.h" +#include "sql/warn_not_implemented.h" + +//------------------------------------------------------------------- +// Parser, create func +//------------------------------------------------------------------- + +Item *Create_udt_func::create(THD *thd, const POS &pos, + udt_function_record *udt_function, + PT_item_list *item_list) { + fprintf(stderr, "Create_udt_func::create_func()\n"); + Item *item = new (thd->mem_root) Item_udt_func(pos, udt_function, item_list); + return item; +} + +//------------------------------------------------------------------- +// Internal hash +//------------------------------------------------------------------- + +struct udt_type_record { + mysql_type_descriptor_t *td; + void *impl; + ulonglong ref_count; +}; + +struct udt_function_record { + mysql_function_descriptor_t *fd; + eval_function_t impl; + ulonglong ref_count; +}; + +static bool initialized = false; +static mysql_rwlock_t THR_LOCK_udt; +static MEM_ROOT MEM_ROOT_udt; +static constexpr const size_t UDT_ALLOC_BLOCK_SIZE{1024}; +static collation_unordered_map *udt_type_hash{ + nullptr}; +static collation_unordered_map + *udt_function_hash{nullptr}; + +static PSI_rwlock_key key_rwlock_THR_LOCK_udt; + +static PSI_memory_key key_memory_udt_mem; + +#ifdef HAVE_PSI_INTERFACE +static PSI_rwlock_info all_udt_rwlocks[] = {{&key_rwlock_THR_LOCK_udt, + "THR_LOCK_udt", PSI_FLAG_SINGLETON, + 0, PSI_DOCUMENT_ME}}; + +static PSI_memory_info all_udt_memory[] = {{&key_memory_udt_mem, "udt_mem", + PSI_FLAG_ONLY_GLOBAL_STAT, 0, + "Shared structure of UDTs."}}; + +static void init_udt_psi_keys(void) { + const char *category = "sql"; + int count; + + count = static_cast(array_elements(all_udt_rwlocks)); + mysql_rwlock_register(category, all_udt_rwlocks, count); + + count = static_cast(array_elements(all_udt_memory)); + mysql_memory_register(category, all_udt_memory, count); +} +#endif + +void udt_init_globals() { + DBUG_TRACE; + if (initialized) return; + +#ifdef HAVE_PSI_INTERFACE + init_udt_psi_keys(); +#endif + + mysql_rwlock_init(key_rwlock_THR_LOCK_udt, &THR_LOCK_udt); + init_sql_alloc(key_memory_udt_mem, &MEM_ROOT_udt, UDT_ALLOC_BLOCK_SIZE); + + udt_type_hash = new collation_unordered_map( + system_charset_info, key_memory_udt_mem); + + udt_function_hash = + new collation_unordered_map( + system_charset_info, key_memory_udt_mem); +} + +void udt_deinit_globals() { + DBUG_TRACE; + + if (udt_function_hash != nullptr) { + delete udt_function_hash; + udt_function_hash = nullptr; + } + + if (udt_type_hash != nullptr) { + delete udt_type_hash; + udt_type_hash = nullptr; + } + + MEM_ROOT_udt.Clear(); + initialized = false; + + mysql_rwlock_destroy(&THR_LOCK_udt); +} + +udt_function_record *acquire_udt_function(const char *name) { + udt_function_record *record = nullptr; + std::string key = name; + + mysql_rwlock_wrlock(&THR_LOCK_udt); + + const auto it = udt_function_hash->find(key); + if (it != udt_function_hash->end()) { + record = it->second; + record->ref_count++; + } + + mysql_rwlock_unlock(&THR_LOCK_udt); + + fprintf(stderr, "acquire_udt_function() name %s record %p\n", key.c_str(), + record); + + return record; +} + +void release_udt_function(udt_function_record *record) { + std::string key = record->fd->name; + + mysql_rwlock_wrlock(&THR_LOCK_udt); + + const auto it = udt_function_hash->find(key); + if (it != udt_function_hash->end()) { + auto hash_record = it->second; + hash_record->ref_count--; + assert(hash_record == record); + } + + mysql_rwlock_unlock(&THR_LOCK_udt); +} + +//------------------------------------------------------------------- +// Service +//------------------------------------------------------------------- + +class UDT_value { + public: + UDT_value(Item *item) : m_item(item) {} + + void set_null(bool is_null); + void get_null(bool *is_null); + + void set_utf8mb4(const char *str, unsigned int length); + void get_utf8mb4(const char **str, unsigned int *length); + + private: + Item *m_item; + String m_string_data; +}; + +void UDT_value::set_null(bool is_null) {} + +void UDT_value::get_null(bool *is_null) { + assert(m_item != nullptr); // readable + + // Defensive, called from 3rd party components. + if (m_item != nullptr) { + *is_null = m_item->is_null(); + } +} + +void UDT_value::get_utf8mb4(const char **str, unsigned int *length) { + if (m_item != nullptr) { + String *data = m_item->val_str(&m_string_data); + *str = data->ptr(); + *length = data->length(); + } +} + +DEFINE_METHOD(int, mysql_udt_registration_imp::register_type, + (mysql_type_descriptor_t * td, void *impl)) { + fprintf(stderr, "mysql_udt_registration_imp::register_type() %p %p\n", td, + impl); + + return 0; +} + +DEFINE_METHOD(int, mysql_udt_registration_imp::unregister_type, + (mysql_type_descriptor_t * td)) { + fprintf(stderr, "mysql_udt_registration_imp::unregister_type() %p\n", td); + return 0; +} + +DEFINE_METHOD(int, mysql_udt_registration_imp::register_function, + (mysql_function_descriptor_t * fd, eval_function_t impl)) { + fprintf(stderr, "mysql_udt_registration_imp::register_function() %p %p\n", fd, + impl); + + int rc = 0; + udt_function_record *record; + + record = + (udt_function_record *)MEM_ROOT_udt.Alloc(sizeof(udt_function_record)); + record->fd = fd; + record->impl = impl; + record->ref_count = 0; + + std::string key = record->fd->name; + + mysql_rwlock_wrlock(&THR_LOCK_udt); + + auto res = udt_function_hash->emplace(key, record); + + if (!res.second) { + rc = 1; // Duplicate + } + + mysql_rwlock_unlock(&THR_LOCK_udt); + + return rc; +} + +DEFINE_METHOD(int, mysql_udt_registration_imp::unregister_function, + (mysql_function_descriptor_t * fd)) { + fprintf(stderr, "mysql_udt_registration_imp::unregister_function() %p\n", fd); + return 0; +} + +DEFINE_METHOD(void, mysql_udt_value_null_imp::set_null, + (UDT_value * f, bool is_null)) { + fprintf(stderr, "mysql_udt_value_null_imp::set_null()\n"); +} + +DEFINE_METHOD(void, mysql_udt_value_null_imp::get_null, + (UDT_value * f, bool *is_null)) { + fprintf(stderr, "mysql_udt_value_null_imp::get_null()\n"); + assert(f != nullptr); + assert(is_null != nullptr); + f->get_null(is_null); +} + +DEFINE_METHOD(void, mysql_udt_value_string_imp::set_utf8mb4, + (UDT_value * f, const char *value, unsigned int length)) { + fprintf(stderr, "mysql_udt_value_string_imp::set_utf8mb4()\n"); +} + +DEFINE_METHOD(void, mysql_udt_value_string_imp::get_utf8mb4, + (UDT_value * f, const char **str, unsigned int *length)) { + fprintf(stderr, "mysql_udt_value_string_imp::get_utf8mb4()\n"); + assert(f != nullptr); + assert(str != nullptr); + assert(length != nullptr); + f->get_utf8mb4(str, length); +} + +DEFINE_METHOD(void, mysql_udt_value_blob_imp::set, + (UDT_value * f, const unsigned char *val, unsigned int len)) { + fprintf(stderr, "mysql_udt_value_blob_imp::set()\n"); +} + +DEFINE_METHOD(void, mysql_udt_value_blob_imp::get, + (UDT_value * f, unsigned char *val, unsigned int *len)) { + fprintf(stderr, "mysql_udt_value_blob_imp::get()\n"); +} + +//------------------------------------------------------------------- +// Runtime, item tree +//------------------------------------------------------------------- + +Item_udt_func::Item_udt_func(const POS &pos, udt_function_record *udt_function, + PT_item_list *opt_list) + : Item_func(pos, opt_list), m_udt_function(udt_function) {} + +bool Item_udt_func::do_itemize(Parse_context *pc, Item **res) { + fprintf(stderr, "Item_udt_func::do_itemize()\n"); + if (super::do_itemize(pc, res)) { + return true; + } + + return false; +} + +bool Item_udt_func::resolve_type_inner(THD *thd) { + fprintf(stderr, "Item_udt_func::resolve_type_inner()\n"); + + const mysql_type_descriptor_t *td = m_udt_function->fd->return_type; + auto td2 = static_cast (td->mysql_type); + + // FIXME: see Item_func_sp::resolve_type() + set_data_type(td2); + return false; +} + +int build_argument_value_array(Item_udt_func *that, + mysql_function_descriptor_t *fd, + size_t *argument_count, + UDT_value ***argument_value_array) { + size_t count = fd->argument_count; + + if (count == 0) { + *argument_count = 0; + *argument_value_array = nullptr; + return 0; + } + + UDT_value **array = new UDT_value *[count]; + Item *item; + + for (size_t i = 0; i < count; i++) { + // FIXME: build proper value + item = that->get_arg(i); + array[i] = new UDT_value(item); + } + + *argument_count = count; + *argument_value_array = array; + return 0; +} + +type_conversion_status Item_udt_func::save_in_field_inner(Field *field, + bool no_conversions) { + fprintf(stderr, "Item_udt_func::save_in_field_inner() field %s\n", + field->field_name); + int rc; + + // 1: Find the field actual type + + const mysql_type_descriptor_t *left_td = nullptr; + + // 2: Check the function return type + + const mysql_type_descriptor_t *right_td = m_udt_function->fd->return_type; + eval_function_t eval = m_udt_function->impl; + + // 3: Build a field value + + UDT_value *result_value = nullptr; + size_t param_count{0}; + UDT_value **param_array{nullptr}; + + rc = build_argument_value_array(this, m_udt_function->fd, ¶m_count, + ¶m_array); + + // 4: Evaluate the function into the value + + fprintf(stderr, "Item_udt_func::save_in_field_inner() field %s before eval\n", + field->field_name); + + rc = (*eval)(result_value, param_count, param_array); + + fprintf(stderr, "Item_udt_func::save_in_field_inner() field %s after eval\n", + field->field_name); + + // 5: Set the value to the field + + return TYPE_ERR_BAD_VALUE; +} + +double Item_udt_func::val_real() { + assert(false); + return 0.0; +} + +longlong Item_udt_func::val_int() { + assert(false); + return 0; +} + +String *Item_udt_func::val_str(String *str) { + assert(false); + return nullptr; +} + +bool Item_udt_func::val_date(Date_val *date, my_time_flags_t flags) { + assert(false); + return false; +} + +bool Item_udt_func::val_time(Time_val *time) { + assert(false); + return false; +} + +bool Item_udt_func::val_datetime(Datetime_val *dt, my_time_flags_t flags) { + assert(false); + return false; +} + +const char *Item_udt_func::func_name() const { + return m_udt_function->fd->name; +} diff --git a/sql/sql_udt.h b/sql/sql_udt.h new file mode 100644 index 000000000000..4e1d607eeb77 --- /dev/null +++ b/sql/sql_udt.h @@ -0,0 +1,71 @@ +/* Copyright (c) 2000, 2026, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is designed to work with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have either included with + the program or referenced in the documentation. + + 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, version 2.0, 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-1301 USA */ + +#ifndef SQL_UDT_INCLUDED +#define SQL_UDT_INCLUDED + +#include "sql/item_func.h" + +struct udt_function_record; + +class Create_udt_func { + public: + static Item *create(THD *thd, const POS &pos, + udt_function_record *udt_function, + PT_item_list *item_list); +}; + +class Item_udt_func : public Item_func { + typedef Item_func super; + + public: + Item_udt_func(const POS &pos, udt_function_record *udt_function, + PT_item_list *opt_list); + + bool do_itemize(Parse_context *pc, Item **res) override; + + bool resolve_type_inner(THD *thd) override; + + double val_real() override; + longlong val_int() override; + String *val_str(String *str) override; + bool val_date(Date_val *date, my_time_flags_t flags) override; + bool val_time(Time_val *time) override; + bool val_datetime(Datetime_val *dt, my_time_flags_t flags) override; + const char *func_name() const override; + + protected: + type_conversion_status save_in_field_inner(Field *field, + bool no_conversions) override; + + private: + udt_function_record *m_udt_function; +}; + +void udt_init_globals(); +void udt_deinit_globals(); + +udt_function_record *acquire_udt_function(const char *name); +void release_udt_function(udt_function_record *record); + +#endif /* SQL_UDT_INCLUDED */ diff --git a/sql/sql_user_defined_type.cc b/sql/sql_user_defined_type.cc new file mode 100644 index 000000000000..1d30695b8b3e --- /dev/null +++ b/sql/sql_user_defined_type.cc @@ -0,0 +1,140 @@ +/* + Copyright (c) 2000, 2026, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is designed to work with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have either included with + the program or referenced in the documentation. + + 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, version 2.0, 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-1301 USA +*/ + +#include "sql/sql_user_defined_type.h" + +#include + +/* HAVE_PSI_*_INTERFACE */ +#include "my_psi_config.h" // IWYU pragma: keep + +#include "dd/object_id.h" +#include "decimal.h" +#include "field_types.h" // enum_field_types +#include "lex_string.h" +#include "sql/dd/cache/dictionary_client.h" // dd::cache::Dictionary_client +#include "sql/dd/dd_udt_type.h" +#include "sql/mysqld.h" // lower_case_table_names +#include "sql/sql_lex.h" // Type_ident + +#include "sql/warn_not_implemented.h" + +bool resolve_type_descriptor(THD *thd, TypeDescriptor *td) { + assert(td != nullptr); + const Type_ident *type_ident = td->m_type_ident; + + if (type_ident == nullptr) { + // Builtin type, nothing to resolve. + return false; + } + + assert(td->m_type == MYSQL_TYPE_INVALID); + + const char *db_name = type_ident->db.str; + const char *type_name = type_ident->type.str; + + // MDL LOCK (SCHEMA) + + /* + When creating the schema, we must lock the schema name without case (for + correct MDL locking) when l_c_t_n == 2. + */ + char name_buf[NAME_LEN + 1]; + const char *lock_db_name = db_name; + if (lower_case_table_names == 2) { + my_stpcpy(name_buf, db_name); + my_casedn_str(&my_charset_utf8mb3_tolower_ci, name_buf); + lock_db_name = name_buf; + } + + if (lock_schema_name(thd, lock_db_name)) { + return true; + } + + // MDL LOCK (TYPE) + + MDL_request mdl_request; + MDL_REQUEST_INIT(&mdl_request, MDL_key::UDT_TYPE, db_name, type_name, + MDL_INTENTION_EXCLUSIVE, MDL_TRANSACTION); + + /* + Acquire the lock request created above, and check if + acquisition fails (e.g. timeout or deadlock). + */ + if (thd->mdl_context.acquire_lock(&mdl_request, + thd->variables.lock_wait_timeout)) { + assert(thd->is_system_thread() || thd->killed || thd->is_error()); + return true; + } + + // DD LOOK UP + + const dd::cache::Dictionary_client::Auto_releaser releaser(thd->dd_client()); + + dd::cache::Dictionary_client &dc = *thd->dd_client(); + dd::String_type schema_name{type_ident->db.str}; + const dd::Schema *existing_schema = nullptr; + if (dc.acquire(schema_name, &existing_schema)) { + return true; + } + + if (existing_schema == nullptr) { + my_error(ER_NO_SUCH_DB, MYF(0), schema_name.c_str()); + return true; + } + + // LOOKUP TYPE + + dd::String_type dd_type_name{type_ident->type.str}; + const dd::UDT_Type *obj = nullptr; + + if (dc.acquire(schema_name, dd_type_name, &obj)) { + return true; + } + + if (obj == nullptr) { + my_error(ER_NO_SUCH_UDT_TYPE, MYF(0), schema_name.c_str(), + dd_type_name.c_str()); + return true; + } + + WARN_NOT_IMPLEMENTED(thd, "resolve_type_descriptor()"); + + fprintf(stderr, "resolve_type_descriptor() use type\n"); + + // FIXME: forged CHAR(13) + td->m_type = MYSQL_TYPE_STRING; + td->m_type_flags = 0; + td->m_length = "13"; + td->m_dec = nullptr; + td->m_charset = &my_charset_utf8mb4_0900_ai_ci; + td->m_has_explicit_collation = false; + td->m_geo_type = 0; + td->m_internal_list = nullptr; + + // FIXME, use type + + return false; +} diff --git a/sql/sql_user_defined_type.h b/sql/sql_user_defined_type.h new file mode 100644 index 000000000000..d9d522fc596a --- /dev/null +++ b/sql/sql_user_defined_type.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2006, 2026, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is designed to work with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have either included with + the program or referenced in the documentation. + + 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, version 2.0, 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-1301 USA */ + +#ifndef SQL_USER_DEFINED_TYPE_INCLUDED +#define SQL_USER_DEFINED_TYPE_INCLUDED + +#include +#include + +#include "sql/create_field.h" + +bool resolve_type_descriptor(THD *thd, TypeDescriptor *td); + +#endif /* SQL_USER_DEFINED_TYPE_INCLUDED */ diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 4e51a99908f7..bfcf56f1b9e9 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -173,6 +173,8 @@ Note: YYTHD is passed as an argument to yyparse(), and subsequently to yylex(). #include "violite.h" #include "sql/tablesample.h" +#include "sql/sql_user_defined_type.h" + /* this is to get the bison compilation windows warnings out */ #ifdef _MSC_VER /* warning C4065: switch statement contains 'default' but no 'case' labels */ @@ -2004,6 +2006,8 @@ CHARSET_INFO *warn_on_deprecated_user_defined_collation( %type text_literal +%type type_ident + %type alter_instance_stmt alter_library_stmt @@ -2019,6 +2023,7 @@ CHARSET_INFO *warn_on_deprecated_user_defined_collation( create_role_stmt create_srs_stmt create_table_stmt + create_type_stmt delete_stmt describe_stmt do_stmt @@ -2222,7 +2227,7 @@ CHARSET_INFO *warn_on_deprecated_user_defined_collation( %type int_type -%type spatial_type type +%type spatial_type broken_type builtin_type user_defined_type %type real_type numeric_type @@ -2520,6 +2525,7 @@ simple_statement: | create_role_stmt | create_srs_stmt | create_table_stmt + | create_type_stmt | deallocate { $$= nullptr; } | delete_stmt | describe_stmt @@ -3337,6 +3343,28 @@ opt_channel: { $$ = to_lex_cstring($3); } ; +type_ident: + IDENT_sys + { + $$= NEW_PTN Type_ident(to_lex_cstring($1)); + if ($$ == nullptr) + MYSQL_YYABORT; + } + | IDENT_sys '.' IDENT_sys + { + $$= NEW_PTN Type_ident(to_lex_cstring($1), to_lex_cstring($3)); + if ($$ == nullptr) + MYSQL_YYABORT; + } + ; + +create_type_stmt: + CREATE TYPE_SYM type_ident AS builtin_type + { + $$= NEW_PTN PT_create_type_stmt(@$, $3); + } + ; + create_table_stmt: CREATE opt_temporary_or_external TABLE_SYM opt_if_not_exists table_ident '(' table_element_list ')' opt_create_table_options_etc @@ -4014,7 +4042,7 @@ sp_fdparams: ; sp_fdparam: - ident type opt_collate + ident broken_type opt_collate { THD *thd= YYTHD; LEX *lex= thd->lex; @@ -4076,7 +4104,7 @@ sp_pdparams: ; sp_pdparam: - sp_opt_inout ident type opt_collate + sp_opt_inout ident broken_type opt_collate { THD *thd= YYTHD; LEX *lex= thd->lex; @@ -4173,7 +4201,7 @@ sp_decls: sp_decl: DECLARE_SYM /*$1*/ sp_decl_idents /*$2*/ - type /*$3*/ + broken_type /*$3*/ opt_collate /*$4*/ sp_opt_default /*$5*/ { /*$6*/ @@ -4232,6 +4260,32 @@ sp_decl: spvar->type= var_type; spvar->default_value= dflt_value_item; + // === + + TypeDescriptor td; + td.m_type = var_type; + td.m_type_flags = $3->get_type_flags(); + td.m_length = $3->get_length(); + td.m_dec = $3->get_dec(); + td.m_charset = cs ? cs : thd->variables.collation_database; + td.m_has_explicit_collation = ($4 != nullptr); + td.m_geo_type = $3->get_uint_geom_type(); + td.m_internal_list = $3->get_interval_list(); + td.m_type_ident = $3->get_type_ident(); + + // FIXME: at parsing time or runtime ? + if (resolve_type_descriptor(thd, &td)) { + MYSQL_YYABORT; + } + + FieldDescriptor fd; + + if (spvar->field_def.init_from_type_descriptor(thd, "", &td, &fd)) + { + MYSQL_YYABORT; + } + +/* if (spvar->field_def.init(thd, "", var_type, $3->get_length(), $3->get_dec(), $3->get_type_flags(), @@ -4244,6 +4298,7 @@ sp_decl: { MYSQL_YYABORT; } +*/ if (prepare_sp_create_field(thd, &spvar->field_def)) MYSQL_YYABORT; @@ -7141,11 +7196,11 @@ constraint_enforcement: ; field_def: - type opt_column_attribute_list + broken_type /* FIXME: opt_collate */ opt_column_attribute_list { $$= NEW_PTN PT_field_def(@$, $1, $2); } - | type opt_collate opt_generated_always + | broken_type opt_collate opt_generated_always AS '(' expr ')' opt_stored_attribute opt_column_attribute_list { @@ -7177,7 +7232,7 @@ opt_stored_attribute: | STORED_SYM { $$= Virtual_or_stored::STORED; } ; -type: +builtin_type: int_type opt_field_length field_options { $$= NEW_PTN PT_numeric_type(@$, YYTHD, $1, $2, $3); @@ -7372,6 +7427,29 @@ type: } ; +user_defined_type: + type_ident + { +#ifdef WITH_EXPERIMENTAL_UDT + $$= NEW_PTN PT_user_defined_type(@$, $1); +#else + my_error(ER_NOT_SUPPORTED_YET, MYF(0), "USER DEFINED TYPE"); + MYSQL_YYABORT; +#endif + } + ; + +broken_type: + builtin_type + { + $$ = $1; + } + | user_defined_type + { + $$ = $1; + } + ; + spatial_type: GEOMETRY_SYM { $$= NEW_PTN PT_spacial_type(@$, Field::GEOM_GEOMETRY); } @@ -7643,6 +7721,7 @@ column_attribute: { $$= NEW_PTN PT_comment_column_attr(@$, to_lex_cstring($2)); } +/* FIXME: */ | COLLATE_SYM collation_name { $$= NEW_PTN PT_collate_column_attr(@$, $2); @@ -12529,7 +12608,7 @@ jt_column: { $$= NEW_PTN PT_json_table_column_for_ordinality(@$, $1); } - | ident type opt_collate jt_column_type PATH_SYM text_literal + | ident broken_type opt_collate jt_column_type PATH_SYM text_literal opt_on_empty_or_error_json_table { auto column = make_unique_destroy_only( @@ -18667,7 +18746,7 @@ sf_tail: Lex->sphead->m_parser_data.set_parameter_end_ptr(@7.cpp.start); } RETURNS_SYM /* $9 */ - type /* $10 */ + broken_type /* $10 */ opt_collate /* $11 */ { /* $12 */ LEX *lex= Lex; diff --git a/sql/warn_not_implemented.h b/sql/warn_not_implemented.h new file mode 100644 index 000000000000..2a68a3bbc371 --- /dev/null +++ b/sql/warn_not_implemented.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2026, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is designed to work with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have either included with + the program or referenced in the documentation. + + 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, version 2.0, 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-1301 USA */ + +#ifndef WARN_NOT_IMPLEMENTED_INCLUDED +#define WARN_NOT_IMPLEMENTED_INCLUDED + +#include "my_config.h" +#include "sql/derror.h" +#include "sql/sql_error.h" + +// MISC HELPER + +#define WARN_NOT_IMPLEMENTED(thd, msg) \ + { \ + push_warning_printf(thd, Sql_condition::SL_WARNING, \ + ER_WARN_CODE_NOT_IMPLEMENTED, \ + ER_THD(thd, ER_WARN_CODE_NOT_IMPLEMENTED), msg); \ + } + +#endif /* WARN_NOT_IMPLEMENTED_INCLUDED */ diff --git a/storage/innobase/include/dict0dd.h b/storage/innobase/include/dict0dd.h index 66746d90e917..f9605c00fbc8 100644 --- a/storage/innobase/include/dict0dd.h +++ b/storage/innobase/include/dict0dd.h @@ -329,6 +329,7 @@ const innodb_dd_table_t innodb_dd_table[] = { INNODB_DD_TABLE("tablespace_files", 2), INNODB_DD_TABLE("tablespaces", 2), INNODB_DD_TABLE("triggers", 7), + INNODB_DD_TABLE("types", 2), INNODB_DD_TABLE("view_routine_usage", 2), INNODB_DD_TABLE("view_table_usage", 2)}; diff --git a/storage/innobase/include/fsp0fsp.ic b/storage/innobase/include/fsp0fsp.ic index d8e8f51ee4bd..fbda335b8b23 100644 --- a/storage/innobase/include/fsp0fsp.ic +++ b/storage/innobase/include/fsp0fsp.ic @@ -290,7 +290,7 @@ inline bool fsp_is_inode_page(page_no_t page) { /* Number of all hard-coded DD table indexes. Please sync it with innodb_dd_table array. */ - static const uint indexes = 102; + static const uint indexes = 104; /* Max page number for index root pages of hard-coded DD tables. */ static const uint max_page_no = diff --git a/storage/perfschema/pfs_column_types.cc b/storage/perfschema/pfs_column_types.cc index 94cae4a6405e..2bba8d1ac4a1 100644 --- a/storage/perfschema/pfs_column_types.cc +++ b/storage/perfschema/pfs_column_types.cc @@ -60,6 +60,7 @@ static s_object_type_map object_type_map[] = { {OBJECT_TYPE_FOREIGN_KEY, {STRING_WITH_LEN("FOREIGN KEY")}}, {OBJECT_TYPE_CHECK_CONSTRAINT, {STRING_WITH_LEN("CHECK CONSTRAINT")}}, {OBJECT_TYPE_LIBRARY, {STRING_WITH_LEN("LIBRARY")}}, + {OBJECT_TYPE_UDT_TYPE, {STRING_WITH_LEN("UDT_TYPE")}}, {NO_OBJECT_TYPE, {STRING_WITH_LEN("")}}}; void object_type_to_string(enum_object_type object_type, const char **string, diff --git a/storage/perfschema/pfs_column_types.h b/storage/perfschema/pfs_column_types.h index 5d84bfa744a0..b75553d90e69 100644 --- a/storage/perfschema/pfs_column_types.h +++ b/storage/perfschema/pfs_column_types.h @@ -251,12 +251,13 @@ enum enum_object_type : char { OBJECT_TYPE_RESOURCE_GROUPS = 17, OBJECT_TYPE_FOREIGN_KEY = 18, OBJECT_TYPE_CHECK_CONSTRAINT = 19, - OBJECT_TYPE_LIBRARY = 20 + OBJECT_TYPE_LIBRARY = 20, + OBJECT_TYPE_UDT_TYPE = 21 }; /** Integer, first value of @sa enum_object_type. */ #define FIRST_OBJECT_TYPE (static_cast(OBJECT_TYPE_EVENT)) /** Integer, last value of @sa enum_object_type. */ -#define LAST_OBJECT_TYPE (static_cast(OBJECT_TYPE_LIBRARY)) +#define LAST_OBJECT_TYPE (static_cast(OBJECT_TYPE_UDT_TYPE)) /** Integer, number of values of @sa enum_object_type. */ #define COUNT_OBJECT_TYPE (LAST_OBJECT_TYPE - FIRST_OBJECT_TYPE + 1) diff --git a/storage/perfschema/table_events_waits.cc b/storage/perfschema/table_events_waits.cc index e668bab8c5b9..8592c902bb3b 100644 --- a/storage/perfschema/table_events_waits.cc +++ b/storage/perfschema/table_events_waits.cc @@ -5,7 +5,7 @@ as published by the Free Software Foundation. This program is designed to work with certain software (including - but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the @@ -383,7 +383,7 @@ int table_events_waits_common::make_metadata_lock_object_columns( if (safe_metadata_lock->get_version() == wait->m_weak_version) { // TODO: remove code duplication with PFS_column_row::make_row() - static_assert(MDL_key::NAMESPACE_END == 19, + static_assert(MDL_key::NAMESPACE_END == 20, "Adjust performance schema when changing enum_mdl_namespace"); const MDL_key *mdl = &safe_metadata_lock->m_mdl_key; @@ -521,6 +521,13 @@ int table_events_waits_common::make_metadata_lock_object_columns( set_schema_name(&m_row.m_object_schema, mdl); m_row.m_object_name_length = mdl->name_length(); break; + case MDL_key::UDT_TYPE: + m_row.m_object_type = "UDT_TYPE"; + m_row.m_object_type_length = 8; + set_schema_name(&m_row.m_object_schema, mdl); + m_row.m_object_name_length = mdl->name_length(); + m_row.m_index_name_length = 0; + break; case MDL_key::NAMESPACE_END: default: m_row.m_object_type_length = 0; diff --git a/storage/perfschema/table_helper.cc b/storage/perfschema/table_helper.cc index 3ec9f85242f6..7420e745db2f 100644 --- a/storage/perfschema/table_helper.cc +++ b/storage/perfschema/table_helper.cc @@ -744,7 +744,7 @@ int PFS_object_row::make_row(PFS_program *pfs) { } int PFS_column_row::make_row(const MDL_key *mdl) { - static_assert(MDL_key::NAMESPACE_END == 19, + static_assert(MDL_key::NAMESPACE_END == 20, "Adjust performance schema when changing enum_mdl_namespace"); bool with_schema = false; @@ -842,6 +842,11 @@ int PFS_column_row::make_row(const MDL_key *mdl) { with_schema = true; with_object = true; break; + case MDL_key::UDT_TYPE: + m_object_type = OBJECT_TYPE_UDT_TYPE; + with_schema = true; + with_object = true; + break; case MDL_key::NAMESPACE_END: default: assert(false); diff --git a/unittest/gunit/mdl-t.cc b/unittest/gunit/mdl-t.cc index 6ba7fc7c1c25..766bd07433da 100644 --- a/unittest/gunit/mdl-t.cc +++ b/unittest/gunit/mdl-t.cc @@ -3871,7 +3871,8 @@ TEST_F(MDLHtonNotifyTest, NotifyNamespaces) { false, // RESOURCE_GROUPS false, // FOREIGN_KEY false, // CHECK_CONSTRAINT - false // LIBRARY + false, // LIBRARY + false // UDT_TYPE }; static_assert( sizeof(notify_or_not) == MDL_key::NAMESPACE_END,