diff --git a/.github/workflows/mtr.yml b/.github/workflows/mtr.yml index 35d95eb43734..6054d45b933d 100644 --- a/.github/workflows/mtr.yml +++ b/.github/workflows/mtr.yml @@ -113,7 +113,7 @@ jobs: --report-unstable-tests --retry=3 --retry-failure=2 - --max-test-fail=3 + --max-test-fail=30 "--suite=${MTR_SUITES}" ) ../trusted/scripts/ci/mtr.sh "${args[@]}" diff --git a/CMakeLists.txt b/CMakeLists.txt index 1766cca905b4..551cd846bee5 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..95fef5addd55 --- /dev/null +++ b/components/udt_example/udt_complex.h @@ -0,0 +1,64 @@ +/* + 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 + +#include + +namespace udt_example { + +struct serialized_complex { + unsigned char buffer[16]; + + const unsigned char *ptr() { return &buffer[0]; } + + unsigned int length() { return sizeof(buffer); } + + void set(const unsigned char *ptr, unsigned int len) { + if (len == length()) { + std::memcpy(&buffer[0], ptr, len); + } + } +}; + +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..e13507677f72 --- /dev/null +++ b/components/udt_example/udt_example.cc @@ -0,0 +1,325 @@ +/* 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_out *result, + size_t argument_count, + UDT_value_in **argument_value_array) { + fprintf(stderr, "complex_number_from_string()\n"); + + assert(argument_count == 1); + + UDT_value_in *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_out *result, + size_t argument_count, + UDT_value_in **argument_value_array) { + fprintf(stderr, "complex_number_to_string()\n"); + + assert(argument_count == 1); + + UDT_value_in *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_to_string(NULL) -> NULL + val_null_srv->set_null(result, true); + return 0; + } + + const unsigned char *val = nullptr; + unsigned int len = 0; + val_blob_srv->get(p1, &val, &len); + + serialized_complex serialized; + serialized.set(val, len); + + Complex c; + c.serialize_from(serialized); + + fprintf(stderr, "complex_number_to_string() p1: r = %lf, i = %lf\n", c.m_real, + c.m_imaginary); + + char result_string[1024]; + snprintf(result_string, sizeof(result_string), "%lf%+lfi", c.m_real, + c.m_imaginary); + + fprintf(stderr, "complex_number_to_string() res: %s\n", result_string); + + // complex_number_to_string("valid blob") + // -> TYPE string + val_null_srv->set_null(result, false); + val_string_srv->set_utf8mb4(result, result_string, strlen(result_string)); + + 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_out *result, size_t argument_count, + UDT_value_in **argument_value_array) { + fprintf(stderr, "complex_number_add()\n"); + + assert(argument_count == 2); + + UDT_value_in *p1 = argument_value_array[0]; + UDT_value_in *p2 = argument_value_array[1]; + + bool p1_is_null{false}; + bool p2_is_null{false}; + val_null_srv->get_null(p1, &p1_is_null); + val_null_srv->get_null(p2, &p2_is_null); + + if (p1_is_null || p2_is_null) { + // complex_number_to_string(NULL) -> NULL + val_null_srv->set_null(result, true); + return 0; + } + + const unsigned char *val = nullptr; + unsigned int len = 0; + serialized_complex serialized; + Complex c1; + Complex c2; + + val_blob_srv->get(p1, &val, &len); + serialized.set(val, len); + c1.serialize_from(serialized); + + fprintf(stderr, "complex_number_add() p1: r = %lf, i = %lf\n", c1.m_real, + c1.m_imaginary); + + val_blob_srv->get(p2, &val, &len); + serialized.set(val, len); + c2.serialize_from(serialized); + + fprintf(stderr, "complex_number_add() p2: r = %lf, i = %lf\n", c2.m_real, + c2.m_imaginary); + + Complex c; + c = Complex::add(c1, c2); + c.serialize_to(serialized); + + fprintf(stderr, "complex_number_add() res: r = %lf, i = %lf\n", c.m_real, + c.m_imaginary); + + // complex_number_add_string("valid blob 1", "valid blob 2") + // -> TYPE complex AS BINARY(16) + val_null_srv->set_null(result, false); + val_blob_srv->set(result, serialized.ptr(), serialized.length()); + + 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..23090e0903ed --- /dev/null +++ b/include/mysql/components/services/bits/mysql_user_defined_type_bits.h @@ -0,0 +1,70 @@ +/* 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_in; +struct UDT_value_out; + +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_out *result, size_t argument_count, + UDT_value_in **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..cb04912512cd --- /dev/null +++ b/include/mysql/components/services/mysql_user_defined_type.h @@ -0,0 +1,73 @@ +/* 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_out * f, bool is_null)); +DECLARE_METHOD(void, get_null, (UDT_value_in * f, bool *is_null)); + +END_SERVICE_DEFINITION(udt_value_null) + +BEGIN_SERVICE_DEFINITION(udt_value_string) + +DECLARE_METHOD(void, set_utf8mb4, + (UDT_value_out * f, const char *value, unsigned int length)); +DECLARE_METHOD(void, get_utf8mb4, + (UDT_value_in * 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_out * f, const unsigned char *val, unsigned int len)); +DECLARE_METHOD(void, get, + (UDT_value_in * f, const 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/include/dd_schema_assert_and_fill_table_names.inc b/mysql-test/include/dd_schema_assert_and_fill_table_names.inc index cd75236da514..0208498c0520 100644 --- a/mysql-test/include/dd_schema_assert_and_fill_table_names.inc +++ b/mysql-test/include/dd_schema_assert_and_fill_table_names.inc @@ -9,11 +9,11 @@ SET debug = '+d,skip_dd_table_access_check'; --echo ######################################################################## --echo # The number of hidden DD/DDSE tables must be as expected. --echo ######################################################################## -let $number_of_hidden_dd_tables = 32; +let $number_of_hidden_dd_tables = 33; let $assert_cond = "[SELECT COUNT(*) from mysql.tables WHERE schema_id = 1 AND hidden = \'System\']" = $number_of_hidden_dd_tables; ---let $assert_text = There are 32 hidden DD/DDSE tables. +--let $assert_text = There are 33 hidden DD/DDSE tables. --source include/assert.inc # Fill two help tables with the names of the DDSE and DD tables. @@ -71,6 +71,7 @@ eval INSERT INTO $dd_table_names (name) VALUES ('tablespace_files'), ('tablespaces'), ('triggers'), + ('types'), ('view_routine_usage'), ('view_table_usage'); --enable_result_log @@ -79,7 +80,7 @@ eval INSERT INTO $dd_table_names (name) VALUES let $assert_cond = "[SELECT (SELECT COUNT(*) FROM $dd_table_names) + (SELECT COUNT(*) FROM $ddse_table_names)]" = $number_of_hidden_dd_tables + 2; ---let $assert_text = There are 34 DD/DDSE tables in total. +--let $assert_text = There are 35 DD/DDSE tables in total. --source include/assert.inc --echo ######################################################################## diff --git a/mysql-test/r/dd_is_compatibility_cs.result b/mysql-test/r/dd_is_compatibility_cs.result index 9c324aef19bd..766cb313217b 100644 --- a/mysql-test/r/dd_is_compatibility_cs.result +++ b/mysql-test/r/dd_is_compatibility_cs.result @@ -204,6 +204,7 @@ TABLE_CONSTRAINTS TABLE_CONSTRAINTS_EXTENSIONS TABLE_PRIVILEGES TRIGGERS +TYPES USER_ATTRIBUTES USER_PRIVILEGES VIEWS diff --git a/mysql-test/r/dd_schema_dd_properties_debug.result b/mysql-test/r/dd_schema_dd_properties_debug.result index 2b1e254f17b4..5f33c22fe328 100644 --- a/mysql-test/r/dd_schema_dd_properties_debug.result +++ b/mysql-test/r/dd_schema_dd_properties_debug.result @@ -3315,13 +3315,83 @@ triggers= table_id=32 trx_id=0 space_id=1 -view_routine_usage= +types= col0=table_id=33 col1=table_id=33 col2=table_id=33 col3=table_id=33 col4=table_id=33 col5=table_id=33 + col6=table_id=33 + data=autoinc=0 + version=0 + def= + fields= + elem0=def=created TIMESTAMP NOT NULL + lbl=FIELD_CREATED + pos=4 + elem1=def=id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT + lbl=FIELD_ID + pos=0 + elem2=def=last_altered TIMESTAMP NOT NULL + lbl=FIELD_LAST_ALTERED + pos=3 + elem3=def=name VARCHAR(64) NOT NULL COLLATE utf8mb3_general_ci + lbl=FIELD_NAME + pos=2 + elem4=def=schema_id BIGINT UNSIGNED NOT NULL + lbl=FIELD_SCHEMA_ID + pos=1 + foreign_keys= + elem0=def=FOREIGN KEY (schema_id) REFERENCES schemata(id) + lbl=FK_SCHEMA_ID + pos=0 + indexes= + elem0=def=PRIMARY KEY (id) + lbl=INDEX_PK_ID + pos=0 + elem1=def=UNIQUE KEY (schema_id, name) + lbl=INDEX_UK_SCHEMA_ID_NAME + pos=1 + name=types + options= + elem0=def=DEFAULT CHARSET=utf8mb3 + lbl=CHARSET + pos=1 + elem1=def=COLLATE=utf8mb3_bin + lbl=COLLATION + pos=2 + elem2=def=ENGINE=INNODB + lbl=ENGINE + pos=0 + elem3=def=ROW_FORMAT=DYNAMIC + lbl=ROW_FORMAT + pos=3 + elem4=def=STATS_PERSISTENT=0 + lbl=STATS_PERSISTENT + pos=4 + elem5=def=TABLESPACE=mysql + lbl=TABLESPACE + pos=5 + id=33 + idx0=id=99 + root=104 + space_id=4294967294 + table_id=33 + trx_id=0 + idx1=id=100 + root=105 + space_id=4294967294 + table_id=33 + trx_id=0 + space_id=1 +view_routine_usage= + col0=table_id=34 + col1=table_id=34 + col2=table_id=34 + col3=table_id=34 + col4=table_id=34 + col5=table_id=34 data= def= fields= @@ -3368,25 +3438,25 @@ view_routine_usage= elem5=def=TABLESPACE=mysql lbl=TABLESPACE pos=5 - id=33 - idx0=id=99 - root=104 + id=34 + idx0=id=101 + root=106 space_id=4294967294 - table_id=33 + table_id=34 trx_id=0 - idx1=id=100 - root=105 + idx1=id=102 + root=107 space_id=4294967294 - table_id=33 + table_id=34 trx_id=0 space_id=1 view_table_usage= - col0=table_id=34 - col1=table_id=34 - col2=table_id=34 - col3=table_id=34 - col4=table_id=34 - col5=table_id=34 + col0=table_id=35 + col1=table_id=35 + col2=table_id=35 + col3=table_id=35 + col4=table_id=35 + col5=table_id=35 data= def= fields= @@ -3433,15 +3503,15 @@ view_table_usage= elem5=def=TABLESPACE=mysql lbl=TABLESPACE pos=5 - id=34 - idx0=id=101 - root=106 + id=35 + idx0=id=103 + root=108 space_id=4294967294 - table_id=34 + table_id=35 trx_id=0 - idx1=id=102 - root=107 + idx1=id=104 + root=109 space_id=4294967294 - table_id=34 + table_id=35 trx_id=0 space_id=1 diff --git a/mysql-test/r/dd_schema_definition_debug.result b/mysql-test/r/dd_schema_definition_debug.result index dd9fb8f5a2c1..4b52f7490d2b 100644 --- a/mysql-test/r/dd_schema_definition_debug.result +++ b/mysql-test/r/dd_schema_definition_debug.result @@ -141,8 +141,8 @@ SET debug = '+d,skip_dd_table_access_check'; ######################################################################## # The number of hidden DD/DDSE tables must be as expected. ######################################################################## -include/assert.inc [There are 32 hidden DD/DDSE tables.] -include/assert.inc [There are 34 DD/DDSE tables in total.] +include/assert.inc [There are 33 hidden DD/DDSE tables.] +include/assert.inc [There are 35 DD/DDSE tables in total.] ######################################################################## # No unexpected DD tables must be present. ######################################################################## @@ -165,7 +165,7 @@ SET debug = '+d,skip_dd_table_access_check'; ######################################################################## # The actual DD version stored on disk. ######################################################################## -DD_VERSION=90200 +DD_VERSION=261000 ######################################################################## # List the CREATE TABLE statements for the DD tables. # Mask the AUTO INCREMENT counter, which is not @@ -734,5 +734,5 @@ Warnings: Warning 1681 Integer display width is deprecated and will be removed in a future release. include/assert.inc [The group concat max length is sufficient.] CHECK_STATUS -The schema checksum corresponds to DD version 90500. +The schema checksum corresponds to DD version 260700. include/assert.inc [The schema checksum corresponds to a known DD version.] diff --git a/mysql-test/r/information_schema_cs.result b/mysql-test/r/information_schema_cs.result index 09f91ae9ee77..3c80adac5cf7 100644 --- a/mysql-test/r/information_schema_cs.result +++ b/mysql-test/r/information_schema_cs.result @@ -108,6 +108,7 @@ TABLE_CONSTRAINTS TABLE_CONSTRAINTS_EXTENSIONS TABLE_PRIVILEGES TRIGGERS +TYPES USER_ATTRIBUTES USER_PRIVILEGES VIEWS @@ -656,6 +657,7 @@ TABLE_CONSTRAINTS TABLE_CONSTRAINTS_EXTENSIONS TABLE_PRIVILEGES TRIGGERS +TYPES create database information_schema; ERROR 42000: Access denied for user 'root'@'localhost' to database 'information_schema' use information_schema; @@ -668,6 +670,7 @@ TABLE_CONSTRAINTS SYSTEM VIEW TABLE_CONSTRAINTS_EXTENSIONS SYSTEM VIEW TABLE_PRIVILEGES SYSTEM VIEW TRIGGERS SYSTEM VIEW +TYPES SYSTEM VIEW create table t1(a int); ERROR 42000: Access denied for user 'root'@'localhost' to database 'information_schema' use test; @@ -683,6 +686,7 @@ TABLE_CONSTRAINTS TABLE_CONSTRAINTS_EXTENSIONS TABLE_PRIVILEGES TRIGGERS +TYPES select table_name from tables where table_name='user'; TABLE_NAME user @@ -902,7 +906,7 @@ table_schema IN ('mysql', 'information_schema', 'test', 'mysqltest') AND table_name not like 'ndb%' AND table_name COLLATE utf8mb3_general_ci not like 'innodb_%' GROUP BY TABLE_SCHEMA; TABLE_SCHEMA count(*) -information_schema 53 +information_schema 54 mysql 35 create table t1 (i int, j int); create trigger trg1 before insert on t1 for each row @@ -2543,6 +2547,7 @@ TABLE_CONSTRAINTS CONSTRAINT_SCHEMA TABLE_CONSTRAINTS_EXTENSIONS CONSTRAINT_SCHEMA TABLE_PRIVILEGES TABLE_SCHEMA TRIGGERS TRIGGER_SCHEMA +TYPES TYPE_SCHEMA USER_ATTRIBUTES USER USER_PRIVILEGES GRANTEE VIEWS TABLE_SCHEMA @@ -2615,6 +2620,7 @@ TABLE_CONSTRAINTS CONSTRAINT_SCHEMA TABLE_CONSTRAINTS_EXTENSIONS CONSTRAINT_SCHEMA TABLE_PRIVILEGES TABLE_SCHEMA TRIGGERS TRIGGER_SCHEMA +TYPES TYPE_SCHEMA USER_ATTRIBUTES USER USER_PRIVILEGES GRANTEE VIEWS TABLE_SCHEMA diff --git a/mysql-test/r/mysqld--help-notwin.result b/mysql-test/r/mysqld--help-notwin.result index 4f102873a851..72b12781edca 100644 --- a/mysql-test/r/mysqld--help-notwin.result +++ b/mysql-test/r/mysqld--help-notwin.result @@ -1945,7 +1945,7 @@ performance-schema-max-socket-classes 10 performance-schema-max-socket-instances -1 performance-schema-max-sql-text-length 1024 performance-schema-max-stage-classes 175 -performance-schema-max-statement-classes 234 +performance-schema-max-statement-classes 235 performance-schema-max-statement-stack 10 performance-schema-max-table-handles -1 performance-schema-max-table-instances -1 diff --git a/mysql-test/r/mysqld--help-win.result b/mysql-test/r/mysqld--help-win.result index ce0a7aec72fa..5ccb84ce27fc 100644 --- a/mysql-test/r/mysqld--help-win.result +++ b/mysql-test/r/mysqld--help-win.result @@ -1970,7 +1970,7 @@ performance-schema-max-socket-classes 10 performance-schema-max-socket-instances -1 performance-schema-max-sql-text-length 1024 performance-schema-max-stage-classes 175 -performance-schema-max-statement-classes 234 +performance-schema-max-statement-classes 235 performance-schema-max-statement-stack 10 performance-schema-max-table-handles -1 performance-schema-max-table-instances -1 diff --git a/mysql-test/r/mysqlshow_cs.result b/mysql-test/r/mysqlshow_cs.result index 9618f07f4ebd..1f59707119df 100644 --- a/mysql-test/r/mysqlshow_cs.result +++ b/mysql-test/r/mysqlshow_cs.result @@ -158,6 +158,7 @@ Database: information_schema | TABLE_CONSTRAINTS_EXTENSIONS | | TABLE_PRIVILEGES | | TRIGGERS | +| TYPES | | USER_ATTRIBUTES | | USER_PRIVILEGES | | VIEWS | @@ -247,6 +248,7 @@ Database: INFORMATION_SCHEMA | TABLE_CONSTRAINTS_EXTENSIONS | | TABLE_PRIVILEGES | | TRIGGERS | +| TYPES | | USER_ATTRIBUTES | | USER_PRIVILEGES | | VIEWS | diff --git a/mysql-test/r/show_parse_tree.result b/mysql-test/r/show_parse_tree.result index b318e10fa9b1..c468bd640506 100644 --- a/mysql-test/r/show_parse_tree.result +++ b/mysql-test/r/show_parse_tree.result @@ -2601,8 +2601,8 @@ SHOW PARSE_TREE UPDATE tab SET O = 1; ERROR 42000: This version of MySQL doesn't yet support 'Parse tree display of this statement' SHOW PARSE_TREE CREATE TABLE tab(id INT); ERROR 42000: This version of MySQL doesn't yet support 'Parse tree display of this statement' -SHOW PARSE_TREE CREATE TABLE tab(id INVALID_SYNTAX); -ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INVALID_SYNTAX)' at line 1 +SHOW PARSE_TREE CREATE TABLE tab(id INVALID_SYNTAX INTENDED); +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INTENDED)' at line 1 # # Bug#35964157 mysql 8.1.0/8.2.0, mysqld got signal 11, # when send sql, show parse_tre diff --git a/mysql-test/r/signal.result b/mysql-test/r/signal.result index 1774e33eff29..80b9fca7e0bb 100644 --- a/mysql-test/r/signal.result +++ b/mysql-test/r/signal.result @@ -2321,9 +2321,7 @@ begin DECLARE céèçà foo CONDITION FOR SQLSTATE '12345'; SIGNAL céèçà SET MYSQL_ERRNO = 1000; end $$ -ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'foo CONDITION FOR SQLSTATE '12345'; -SIGNAL céèçà SET MYSQL_ERRNO = 1000; -end' at line 3 +Got one of the listed errors create procedure test_signal() begin DECLARE "céèçà" CONDITION FOR SQLSTATE '12345'; diff --git a/mysql-test/std_data/dd/sdi/innodb_sdi/mysql.json b/mysql-test/std_data/dd/sdi/innodb_sdi/mysql.json index fe67b0a31a96..3333250dd794 100644 --- a/mysql-test/std_data/dd/sdi/innodb_sdi/mysql.json +++ b/mysql-test/std_data/dd/sdi/innodb_sdi/mysql.json @@ -24845,6 +24845,444 @@ } } , +{ + "type": 1, + "id": X, + "object": + { + "mysqld_version_id": X, + "dd_version": X, + "sdi_version": X, + "dd_object_type": "Table", + "dd_object": { + "name": "types", + "mysql_version_id": X, + "created": NNN, + "last_altered": NNN, + "hidden": 2, + "options": "avg_row_length=0;encrypt_type=N;explicit_tablespace=1;key_block_size=0;keys_disabled=0;pack_record=1;row_type=2;stats_auto_recalc=0;stats_persistent=0;stats_sample_pages=0;", + "columns": [ + { + "name": "id", + "type": 9, + "is_nullable": false, + "is_zerofill": false, + "is_unsigned": true, + "is_auto_increment": true, + "is_virtual": false, + "hidden": 1, + "ordinal_position": 1, + "char_length": 20, + "numeric_precision": 20, + "numeric_scale": 0, + "numeric_scale_null": false, + "datetime_precision": 0, + "datetime_precision_null": 1, + "has_no_default": false, + "default_value_null": false, + "srs_id_null": true, + "srs_id": 0, + "default_value": "", + "default_value_utf8_null": true, + "default_value_utf8": "", + "default_option": "", + "update_option": "", + "comment": "", + "generation_expression": "", + "generation_expression_utf8": "", + "options": "interval_count=0;", + "se_private_data": "table_id=X", + "engine_attribute": "", + "secondary_engine_attribute": "", + "column_key": 2, + "column_type_utf8": "bigint unsigned", + "elements": [], + "collation_id": X, + "is_explicit_collation": false + }, + { + "name": "schema_id", + "type": 9, + "is_nullable": false, + "is_zerofill": false, + "is_unsigned": true, + "is_auto_increment": false, + "is_virtual": false, + "hidden": 1, + "ordinal_position": 2, + "char_length": 20, + "numeric_precision": 20, + "numeric_scale": 0, + "numeric_scale_null": false, + "datetime_precision": 0, + "datetime_precision_null": 1, + "has_no_default": true, + "default_value_null": false, + "srs_id_null": true, + "srs_id": 0, + "default_value": "", + "default_value_utf8_null": true, + "default_value_utf8": "", + "default_option": "", + "update_option": "", + "comment": "", + "generation_expression": "", + "generation_expression_utf8": "", + "options": "interval_count=0;", + "se_private_data": "table_id=X", + "engine_attribute": "", + "secondary_engine_attribute": "", + "column_key": 4, + "column_type_utf8": "bigint unsigned", + "elements": [], + "collation_id": X, + "is_explicit_collation": false + }, + { + "name": "name", + "type": 16, + "is_nullable": false, + "is_zerofill": false, + "is_unsigned": false, + "is_auto_increment": false, + "is_virtual": false, + "hidden": 1, + "ordinal_position": 3, + "char_length": 192, + "numeric_precision": 0, + "numeric_scale": 0, + "numeric_scale_null": true, + "datetime_precision": 0, + "datetime_precision_null": 1, + "has_no_default": true, + "default_value_null": false, + "srs_id_null": true, + "srs_id": 0, + "default_value": "", + "default_value_utf8_null": true, + "default_value_utf8": "", + "default_option": "", + "update_option": "", + "comment": "", + "generation_expression": "", + "generation_expression_utf8": "", + "options": "interval_count=0;", + "se_private_data": "table_id=X", + "engine_attribute": "", + "secondary_engine_attribute": "", + "column_key": 1, + "column_type_utf8": "varchar(64)", + "elements": [], + "collation_id": X, + "is_explicit_collation": true + }, + { + "name": "last_altered", + "type": 18, + "is_nullable": false, + "is_zerofill": false, + "is_unsigned": false, + "is_auto_increment": false, + "is_virtual": false, + "hidden": 1, + "ordinal_position": 4, + "char_length": 19, + "numeric_precision": 0, + "numeric_scale": 0, + "numeric_scale_null": true, + "datetime_precision": 0, + "datetime_precision_null": 0, + "has_no_default": true, + "default_value_null": false, + "srs_id_null": true, + "srs_id": 0, + "default_value": "", + "default_value_utf8_null": true, + "default_value_utf8": "", + "default_option": "", + "update_option": "", + "comment": "", + "generation_expression": "", + "generation_expression_utf8": "", + "options": "interval_count=0;", + "se_private_data": "table_id=X", + "engine_attribute": "", + "secondary_engine_attribute": "", + "column_key": 1, + "column_type_utf8": "timestamp", + "elements": [], + "collation_id": X, + "is_explicit_collation": false + }, + { + "name": "created", + "type": 18, + "is_nullable": false, + "is_zerofill": false, + "is_unsigned": false, + "is_auto_increment": false, + "is_virtual": false, + "hidden": 1, + "ordinal_position": 5, + "char_length": 19, + "numeric_precision": 0, + "numeric_scale": 0, + "numeric_scale_null": true, + "datetime_precision": 0, + "datetime_precision_null": 0, + "has_no_default": true, + "default_value_null": false, + "srs_id_null": true, + "srs_id": 0, + "default_value": "", + "default_value_utf8_null": true, + "default_value_utf8": "", + "default_option": "", + "update_option": "", + "comment": "", + "generation_expression": "", + "generation_expression_utf8": "", + "options": "interval_count=0;", + "se_private_data": "table_id=X", + "engine_attribute": "", + "secondary_engine_attribute": "", + "column_key": 1, + "column_type_utf8": "timestamp", + "elements": [], + "collation_id": X, + "is_explicit_collation": false + }, + { + "name": "DB_TRX_ID", + "type": 10, + "is_nullable": false, + "is_zerofill": false, + "is_unsigned": false, + "is_auto_increment": false, + "is_virtual": false, + "hidden": 2, + "ordinal_position": 6, + "char_length": 6, + "numeric_precision": 0, + "numeric_scale": 0, + "numeric_scale_null": true, + "datetime_precision": 0, + "datetime_precision_null": 1, + "has_no_default": false, + "default_value_null": true, + "srs_id_null": true, + "srs_id": 0, + "default_value": "", + "default_value_utf8_null": true, + "default_value_utf8": "", + "default_option": "", + "update_option": "", + "comment": "", + "generation_expression": "", + "generation_expression_utf8": "", + "options": "", + "se_private_data": "table_id=X", + "engine_attribute": "", + "secondary_engine_attribute": "", + "column_key": 1, + "column_type_utf8": "", + "elements": [], + "collation_id": X, + "is_explicit_collation": false + }, + { + "name": "DB_ROLL_PTR", + "type": 9, + "is_nullable": false, + "is_zerofill": false, + "is_unsigned": false, + "is_auto_increment": false, + "is_virtual": false, + "hidden": 2, + "ordinal_position": 7, + "char_length": 7, + "numeric_precision": 0, + "numeric_scale": 0, + "numeric_scale_null": true, + "datetime_precision": 0, + "datetime_precision_null": 1, + "has_no_default": false, + "default_value_null": true, + "srs_id_null": true, + "srs_id": 0, + "default_value": "", + "default_value_utf8_null": true, + "default_value_utf8": "", + "default_option": "", + "update_option": "", + "comment": "", + "generation_expression": "", + "generation_expression_utf8": "", + "options": "", + "se_private_data": "table_id=X", + "engine_attribute": "", + "secondary_engine_attribute": "", + "column_key": 1, + "column_type_utf8": "", + "elements": [], + "collation_id": X, + "is_explicit_collation": false + } + ], + "schema_ref": "mysql", + "se_private_id":NNN, + "engine": "InnoDB", + "last_checked_for_upgrade_version_id": X, + "comment": "", + "se_private_data": "autoinc=0;version=0;", + "engine_attribute": "", + "secondary_engine_attribute": "", + "row_format": 2, + "partition_type": 0, + "partition_expression": "", + "partition_expression_utf8": "", + "default_partitioning": 0, + "subpartition_type": 0, + "subpartition_expression": "", + "subpartition_expression_utf8": "", + "default_subpartitioning": 0, + "indexes": [ + { + "name": "PRIMARY", + "hidden": false, + "is_generated": false, + "ordinal_position": 1, + "comment": "", + "options": "flags=0;", + "se_private_data": "id=A;root=B;space_id=C;table_id=D;trx_id=E", + "type": 1, + "algorithm": 2, + "is_algorithm_explicit": false, + "is_visible": true, + "engine": "InnoDB", + "engine_attribute": "", + "secondary_engine_attribute": "", + "elements": [ + { + "ordinal_position": 1, + "length": 8, + "order": 2, + "hidden": false, + "column_opx": 0 + }, + { + "ordinal_position": 2, + "length": 4294967295, + "order": 2, + "hidden": true, + "column_opx": 5 + }, + { + "ordinal_position": 3, + "length": 4294967295, + "order": 2, + "hidden": true, + "column_opx": 6 + }, + { + "ordinal_position": 4, + "length": 4294967295, + "order": 2, + "hidden": true, + "column_opx": 1 + }, + { + "ordinal_position": 5, + "length": 4294967295, + "order": 2, + "hidden": true, + "column_opx": 2 + }, + { + "ordinal_position": 6, + "length": 4294967295, + "order": 2, + "hidden": true, + "column_opx": 3 + }, + { + "ordinal_position": 7, + "length": 4294967295, + "order": 2, + "hidden": true, + "column_opx": 4 + } + ], + "tablespace_ref": "mysql" + }, + { + "name": "schema_id", + "hidden": false, + "is_generated": false, + "ordinal_position": 2, + "comment": "", + "options": "flags=0;", + "se_private_data": "id=A;root=B;space_id=C;table_id=D;trx_id=E", + "type": 2, + "algorithm": 2, + "is_algorithm_explicit": false, + "is_visible": true, + "engine": "InnoDB", + "engine_attribute": "", + "secondary_engine_attribute": "", + "elements": [ + { + "ordinal_position": 1, + "length": 8, + "order": 2, + "hidden": false, + "column_opx": 1 + }, + { + "ordinal_position": 2, + "length": 192, + "order": 2, + "hidden": false, + "column_opx": 2 + }, + { + "ordinal_position": 3, + "length": 4294967295, + "order": 2, + "hidden": true, + "column_opx": 0 + } + ], + "tablespace_ref": "mysql" + } + ], + "foreign_keys": [ + { + "name": "types_ibfk_1", + "match_option": 1, + "update_rule": 1, + "delete_rule": 1, + "unique_constraint_name": "PRIMARY", + "referenced_table_catalog_name": "def", + "referenced_table_schema_name": "mysql", + "referenced_table_name": "schemata", + "elements": [ + { + "column_opx": 1, + "ordinal_position": 1, + "referenced_column_name": "id" + } + ] + } + ], + "check_constraints": [], + "partitions": [], + "collation_id": X, + "tablespace_ref": "mysql" + } +} +} +, { "type": 1, "id": X, diff --git a/mysql-test/suite/information_schema/include/i_s_schema_assert_and_fill_table_names.inc b/mysql-test/suite/information_schema/include/i_s_schema_assert_and_fill_table_names.inc index 902b5bca4d07..41d3de67fa48 100644 --- a/mysql-test/suite/information_schema/include/i_s_schema_assert_and_fill_table_names.inc +++ b/mysql-test/suite/information_schema/include/i_s_schema_assert_and_fill_table_names.inc @@ -67,6 +67,7 @@ eval INSERT INTO $I_S_view_names (name) VALUES ('TABLES_EXTENSIONS'), ('TABLESPACES_EXTENSIONS'), ('TRIGGERS'), + ('TYPES'), ('USER_ATTRIBUTES'), ('VIEW_ROUTINE_USAGE'), ('VIEW_TABLE_USAGE'), diff --git a/mysql-test/suite/information_schema/include/i_s_schema_dump_table_defs_debug.inc b/mysql-test/suite/information_schema/include/i_s_schema_dump_table_defs_debug.inc index d61020856c94..b00148900ed6 100644 --- a/mysql-test/suite/information_schema/include/i_s_schema_dump_table_defs_debug.inc +++ b/mysql-test/suite/information_schema/include/i_s_schema_dump_table_defs_debug.inc @@ -259,6 +259,12 @@ let $str = `$SELECT_CMD $WHERE_COND`; echo $str; eval INSERT INTO I_S_check_table(t) VALUES ("$str"); +let $WHERE_COND = AND TABLE_NAME='TYPES'; +replace_regex /(cat|sch|tbl).name COLLATE utf8mb3_tolower_ci/\1.name/; +let $str = `$SELECT_CMD $WHERE_COND`; +echo $str; +eval INSERT INTO I_S_check_table(t) VALUES ("$str"); + let $WHERE_COND = AND TABLE_NAME='VIEW_ROUTINE_USAGE'; replace_regex /(cat|sch|vw).name COLLATE utf8mb3_tolower_ci/\1.name/ /vru.routine_(catalog|schema) COLLATE utf8mb3_tolower_ci/vru.routine_\1/; diff --git a/mysql-test/suite/information_schema/r/i_s_schema_definition_debug.result b/mysql-test/suite/information_schema/r/i_s_schema_definition_debug.result index 4ad33cb4653b..e559fae45a8e 100644 --- a/mysql-test/suite/information_schema/r/i_s_schema_definition_debug.result +++ b/mysql-test/suite/information_schema/r/i_s_schema_definition_debug.result @@ -103,8 +103,8 @@ CREATE TABLE test.I_S_view_names (name VARCHAR(64) PRIMARY KEY); ######################################################################## # The number of I_S system views must be as expected. ######################################################################## -include/assert.inc [There are 50 system views.] -include/assert.inc [There are 50 I_S system views in total.] +include/assert.inc [There are 51 system views.] +include/assert.inc [There are 51 I_S system views in total.] ######################################################################## # No unexpected I_S tables must be present. ######################################################################## @@ -118,7 +118,7 @@ include/assert.inc [All expected I_S system views are present.] # I_S system views. ######################################################################## # Print the actual I_S version stored on disk. -Current I_S_VERSION=90500 +Current I_S_VERSION=261000 CREATE TABLE I_S_check_table (id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, t TEXT NOT NULL, row_hash VARCHAR(64) DEFAULT NULL); @@ -615,11 +615,12 @@ CREATE OR REPLACE DEFINER=`mysql.infoschema`@`localhost` VIEW information_schema columns.allow_insert AS ALLOW_INSERT, columns.allow_update AS ALLOW_UPDATE, columns.allow_delete AS ALLOW_DELETE, + columns.allow_check AS ALLOW_CHECK, columns.read_only AS READ_ONLY FROM mysql.tables tbl JOIN mysql.schemata sch ON tbl.schema_id=sch.id JOIN mysql.catalogs cat ON cat.id=sch.catalog_id - JOIN JSON_TABLE(GET_JDV_PROPERTY_KEY_VALUE(sch.name, tbl.name, GET_DD_PROPERTY_KEY_VALUE(tbl.options, 'view_valid'), 'JSON_DUALITY_VIEW_COLUMNS'), '$.entries[*]' COLUMNS ( referenced_table_id INT PATH '$.referenced_table_id', referenced_table_catalog VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_table_catalog', referenced_table_schema VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_table_schema', referenced_table_name VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_table_name', is_root_table TINYINT PATH '$.is_root_table', referenced_column_name VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_column_name', json_key_name VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.json_key_name', allow_insert TINYINT PATH '$.allow_insert', allow_update TINYINT PATH '$.allow_update', allow_delete TINYINT PATH '$.allow_delete', read_only TINYINT PATH '$.read_only' ) ) AS columns WHERE + JOIN JSON_TABLE(GET_JDV_PROPERTY_KEY_VALUE(sch.name, tbl.name, GET_DD_PROPERTY_KEY_VALUE(tbl.options, 'view_valid'), 'JSON_DUALITY_VIEW_COLUMNS'), '$.entries[*]' COLUMNS ( referenced_table_id INT PATH '$.referenced_table_id', referenced_table_catalog VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_table_catalog', referenced_table_schema VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_table_schema', referenced_table_name VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_table_name', is_root_table TINYINT PATH '$.is_root_table', referenced_column_name VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_column_name', json_key_name VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.json_key_name', allow_insert TINYINT PATH '$.allow_insert', allow_update TINYINT PATH '$.allow_update', allow_delete TINYINT PATH '$.allow_delete', allow_check TINYINT PATH '$.allow_check', read_only TINYINT PATH '$.read_only' ) ) AS columns WHERE CAN_ACCESS_VIEW(sch.name, tbl.name, tbl.view_definer, tbl.options) AND CAN_ACCESS_COLUMN(columns.referenced_table_schema, columns.referenced_table_name, columns.referenced_column_name) AND tbl.type = 'VIEW' @@ -639,11 +640,12 @@ INSERT INTO I_S_check_table(t) VALUES ("CREATE OR REPLACE DEFINER=`mysql.infosch columns.allow_insert AS ALLOW_INSERT, columns.allow_update AS ALLOW_UPDATE, columns.allow_delete AS ALLOW_DELETE, + columns.allow_check AS ALLOW_CHECK, columns.read_only AS READ_ONLY FROM mysql.tables tbl JOIN mysql.schemata sch ON tbl.schema_id=sch.id JOIN mysql.catalogs cat ON cat.id=sch.catalog_id - JOIN JSON_TABLE(GET_JDV_PROPERTY_KEY_VALUE(sch.name, tbl.name, GET_DD_PROPERTY_KEY_VALUE(tbl.options, 'view_valid'), 'JSON_DUALITY_VIEW_COLUMNS'), '$.entries[*]' COLUMNS ( referenced_table_id INT PATH '$.referenced_table_id', referenced_table_catalog VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_table_catalog', referenced_table_schema VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_table_schema', referenced_table_name VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_table_name', is_root_table TINYINT PATH '$.is_root_table', referenced_column_name VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_column_name', json_key_name VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.json_key_name', allow_insert TINYINT PATH '$.allow_insert', allow_update TINYINT PATH '$.allow_update', allow_delete TINYINT PATH '$.allow_delete', read_only TINYINT PATH '$.read_only' ) ) AS columns WHERE + JOIN JSON_TABLE(GET_JDV_PROPERTY_KEY_VALUE(sch.name, tbl.name, GET_DD_PROPERTY_KEY_VALUE(tbl.options, 'view_valid'), 'JSON_DUALITY_VIEW_COLUMNS'), '$.entries[*]' COLUMNS ( referenced_table_id INT PATH '$.referenced_table_id', referenced_table_catalog VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_table_catalog', referenced_table_schema VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_table_schema', referenced_table_name VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_table_name', is_root_table TINYINT PATH '$.is_root_table', referenced_column_name VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_column_name', json_key_name VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.json_key_name', allow_insert TINYINT PATH '$.allow_insert', allow_update TINYINT PATH '$.allow_update', allow_delete TINYINT PATH '$.allow_delete', allow_check TINYINT PATH '$.allow_check', read_only TINYINT PATH '$.read_only' ) ) AS columns WHERE CAN_ACCESS_VIEW(sch.name, tbl.name, tbl.view_definer, tbl.options) AND CAN_ACCESS_COLUMN(columns.referenced_table_schema, columns.referenced_table_name, columns.referenced_column_name) AND tbl.type = 'VIEW' @@ -708,6 +710,7 @@ CREATE OR REPLACE DEFINER=`mysql.infoschema`@`localhost` VIEW information_schema tables.allow_insert AS ALLOW_INSERT, tables.allow_update AS ALLOW_UPDATE, tables.allow_delete AS ALLOW_DELETE, + tables.allow_check AS ALLOW_CHECK, tables.read_only AS READ_ONLY, tables.is_root_table AS IS_ROOT_TABLE, tables.referenced_table_id AS REFERENCED_TABLE_ID, @@ -716,7 +719,7 @@ CREATE OR REPLACE DEFINER=`mysql.infoschema`@`localhost` VIEW information_schema mysql.tables tbl JOIN mysql.schemata sch ON tbl.schema_id=sch.id JOIN mysql.catalogs cat ON cat.id=sch.catalog_id - JOIN JSON_TABLE(GET_JDV_PROPERTY_KEY_VALUE(sch.name, tbl.name, GET_DD_PROPERTY_KEY_VALUE(tbl.options, 'view_valid'), 'JSON_DUALITY_VIEW_TABLES'), '$.entries[*]' COLUMNS ( referenced_table_id INT PATH '$.referenced_table_id', referenced_table_parent_id INT PATH '$.referenced_table_parent_id', referenced_table_parent_relationship VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_table_parent_relationship', referenced_table_catalog VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_table_catalog', referenced_table_schema VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_table_schema', referenced_table_name VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_table_name', where_clause VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.where_clause', is_root_table TINYINT PATH '$.is_root_table', allow_insert TINYINT PATH '$.allow_insert', allow_update TINYINT PATH '$.allow_update', allow_delete TINYINT PATH '$.allow_delete', read_only TINYINT PATH '$.read_only' ) ) AS tables WHERE + JOIN JSON_TABLE(GET_JDV_PROPERTY_KEY_VALUE(sch.name, tbl.name, GET_DD_PROPERTY_KEY_VALUE(tbl.options, 'view_valid'), 'JSON_DUALITY_VIEW_TABLES'), '$.entries[*]' COLUMNS ( referenced_table_id INT PATH '$.referenced_table_id', referenced_table_parent_id INT PATH '$.referenced_table_parent_id', referenced_table_parent_relationship VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_table_parent_relationship', referenced_table_catalog VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_table_catalog', referenced_table_schema VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_table_schema', referenced_table_name VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_table_name', where_clause VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.where_clause', is_root_table TINYINT PATH '$.is_root_table', allow_insert TINYINT PATH '$.allow_insert', allow_update TINYINT PATH '$.allow_update', allow_delete TINYINT PATH '$.allow_delete', allow_check TINYINT PATH '$.allow_check', read_only TINYINT PATH '$.read_only' ) ) AS tables WHERE CAN_ACCESS_VIEW(sch.name, tbl.name, tbl.view_definer, tbl.options) AND CAN_ACCESS_TABLE(tables.referenced_table_schema, tables.referenced_table_name) AND tbl.type = 'VIEW' @@ -733,6 +736,7 @@ INSERT INTO I_S_check_table(t) VALUES ("CREATE OR REPLACE DEFINER=`mysql.infosch tables.allow_insert AS ALLOW_INSERT, tables.allow_update AS ALLOW_UPDATE, tables.allow_delete AS ALLOW_DELETE, + tables.allow_check AS ALLOW_CHECK, tables.read_only AS READ_ONLY, tables.is_root_table AS IS_ROOT_TABLE, tables.referenced_table_id AS REFERENCED_TABLE_ID, @@ -741,7 +745,7 @@ INSERT INTO I_S_check_table(t) VALUES ("CREATE OR REPLACE DEFINER=`mysql.infosch mysql.tables tbl JOIN mysql.schemata sch ON tbl.schema_id=sch.id JOIN mysql.catalogs cat ON cat.id=sch.catalog_id - JOIN JSON_TABLE(GET_JDV_PROPERTY_KEY_VALUE(sch.name, tbl.name, GET_DD_PROPERTY_KEY_VALUE(tbl.options, 'view_valid'), 'JSON_DUALITY_VIEW_TABLES'), '$.entries[*]' COLUMNS ( referenced_table_id INT PATH '$.referenced_table_id', referenced_table_parent_id INT PATH '$.referenced_table_parent_id', referenced_table_parent_relationship VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_table_parent_relationship', referenced_table_catalog VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_table_catalog', referenced_table_schema VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_table_schema', referenced_table_name VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_table_name', where_clause VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.where_clause', is_root_table TINYINT PATH '$.is_root_table', allow_insert TINYINT PATH '$.allow_insert', allow_update TINYINT PATH '$.allow_update', allow_delete TINYINT PATH '$.allow_delete', read_only TINYINT PATH '$.read_only' ) ) AS tables WHERE + JOIN JSON_TABLE(GET_JDV_PROPERTY_KEY_VALUE(sch.name, tbl.name, GET_DD_PROPERTY_KEY_VALUE(tbl.options, 'view_valid'), 'JSON_DUALITY_VIEW_TABLES'), '$.entries[*]' COLUMNS ( referenced_table_id INT PATH '$.referenced_table_id', referenced_table_parent_id INT PATH '$.referenced_table_parent_id', referenced_table_parent_relationship VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_table_parent_relationship', referenced_table_catalog VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_table_catalog', referenced_table_schema VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_table_schema', referenced_table_name VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.referenced_table_name', where_clause VARCHAR(64) CHARACTER SET utf8mb4 PATH '$.where_clause', is_root_table TINYINT PATH '$.is_root_table', allow_insert TINYINT PATH '$.allow_insert', allow_update TINYINT PATH '$.allow_update', allow_delete TINYINT PATH '$.allow_delete', allow_check TINYINT PATH '$.allow_check', read_only TINYINT PATH '$.read_only' ) ) AS tables WHERE CAN_ACCESS_VIEW(sch.name, tbl.name, tbl.view_definer, tbl.options) AND CAN_ACCESS_TABLE(tables.referenced_table_schema, tables.referenced_table_name) AND tbl.type = 'VIEW' @@ -1627,6 +1631,20 @@ INSERT INTO I_S_check_table(t) VALUES ("CREATE OR REPLACE DEFINER=`mysql.infosch AND CAN_ACCESS_TRIGGER(sch.name, tbl.name) AND IS_VISIBLE_DD_OBJECT(tbl.hidden) "); +CREATE OR REPLACE DEFINER=`mysql.infoschema`@`localhost` VIEW information_schema.TYPES AS SELECT + sch.name AS TYPE_SCHEMA, + typ.name AS TYPE_NAME FROM + mysql.types typ + JOIN mysql.schemata sch ON typ.schema_id=sch.id WHERE + CAN_ACCESS_DATABASE(sch.name) + +INSERT INTO I_S_check_table(t) VALUES ("CREATE OR REPLACE DEFINER=`mysql.infoschema`@`localhost` VIEW information_schema.TYPES AS SELECT + sch.name AS TYPE_SCHEMA, + typ.name AS TYPE_NAME FROM + mysql.types typ + JOIN mysql.schemata sch ON typ.schema_id=sch.id WHERE + CAN_ACCESS_DATABASE(sch.name) +"); CREATE OR REPLACE DEFINER=`mysql.infoschema`@`localhost` VIEW information_schema.VIEW_ROUTINE_USAGE AS SELECT cat.name AS TABLE_CATALOG, sch.name AS TABLE_SCHEMA, @@ -1915,8 +1933,8 @@ SET debug = '-d,fetch_system_view_definition'; include/assert.inc [Found expected number of system views in DD.] include/assert.inc [Found expected number of system views in I_S_check_table.] include/assert.inc [The group concat max length is sufficient.] -The schema checksum corresponds to I_S version 90500. +The schema checksum corresponds to I_S version 261000. include/assert.inc [The schema checksum corresponds to a known I_S version.] include/assert.inc [The schema checksum corresponds to -IS_VERSION 90500 stored on disk.] +IS_VERSION 261000 stored on disk.] include/assert.inc [The stored I_S version is the latest published I_S version.] diff --git a/mysql-test/suite/information_schema/r/information_schema_db.result b/mysql-test/suite/information_schema/r/information_schema_db.result index 21192cd3884a..039b207c7d0e 100644 --- a/mysql-test/suite/information_schema/r/information_schema_db.result +++ b/mysql-test/suite/information_schema/r/information_schema_db.result @@ -54,6 +54,7 @@ TABLE_CONSTRAINTS TABLE_CONSTRAINTS_EXTENSIONS TABLE_PRIVILEGES TRIGGERS +TYPES USER_ATTRIBUTES USER_PRIVILEGES VIEWS @@ -68,6 +69,7 @@ TABLE_CONSTRAINTS TABLE_CONSTRAINTS_EXTENSIONS TABLE_PRIVILEGES TRIGGERS +TYPES create database `inf%`; create database mbase; use `inf%`; diff --git a/mysql-test/suite/information_schema/t/i_s_schema_definition_debug.test b/mysql-test/suite/information_schema/t/i_s_schema_definition_debug.test index f546145e8ac9..55b9bebf8c44 100644 --- a/mysql-test/suite/information_schema/t/i_s_schema_definition_debug.test +++ b/mysql-test/suite/information_schema/t/i_s_schema_definition_debug.test @@ -98,7 +98,7 @@ SET debug = '+d,skip_dd_table_access_check'; # Total number of system views in MySQL server. -let $expected_system_view_count = 50; +let $expected_system_view_count = 51; --echo ######################################################################## --echo # PART 1 @@ -362,6 +362,12 @@ INSERT INTO I_S_published_schema INSERT INTO I_S_published_schema VALUES ('90500', '90500', 1, '797926c355c5bda0e300ad5fab20371e44d1bcacaf76f10d03636f37570fe732'); +INSERT INTO I_S_published_schema + VALUES ('260700', '260700', 0, + 'ff4aed80171b1b180c0c8e50611bb950b6f2f3760e89098eb48bfbbfcf696c77'); +INSERT INTO I_S_published_schema + VALUES ('261000', '261000', 0, + '2c3103e3abef0a54995936a3e089d84777a3a5005c5f38c84b3312b064e76ae3'); LET $checksum_version = `SELECT IF(ISNULL(mysqld_version), "0", i_s_version) FROM I_S_published_schema i RIGHT OUTER JOIN whole_schema w diff --git a/mysql-test/suite/perfschema/r/memory_key_descriptions.result b/mysql-test/suite/perfschema/r/memory_key_descriptions.result index 4c14be5c7ca0..db178306bd42 100644 --- a/mysql-test/suite/perfschema/r/memory_key_descriptions.result +++ b/mysql-test/suite/perfschema/r/memory_key_descriptions.result @@ -59,6 +59,7 @@ memory/sql/THD::transactions::mem_root Transaction context information per sessi memory/sql/THD::variables Per session copy of global dynamic variables. memory/sql/tz_storage Shared time zone data. memory/sql/udf_mem Shared structure of UDFs. +memory/sql/udt_mem Shared structure of UDTs. memory/sql/user_conn Objects describing user connections. memory/sql/User_level_lock Per session storage of user level locks. memory/sql/XA::recovered_transactions List infrastructure for recovered XA transactions. diff --git a/mysql-test/suite/perfschema/t/memory_key_descriptions.test b/mysql-test/suite/perfschema/t/memory_key_descriptions.test index 03afea03e707..eae8280090bd 100644 --- a/mysql-test/suite/perfschema/t/memory_key_descriptions.test +++ b/mysql-test/suite/perfschema/t/memory_key_descriptions.test @@ -8,6 +8,6 @@ let $query_clause = FROM performance_schema.setup_instruments eval SELECT NAME, DOCUMENTATION $query_clause; eval SET @rows = (SELECT COUNT(NAME) $query_clause); -let $assert_cond = @rows = 60; +let $assert_cond = @rows = 61; let $assert_text = The number of documented P_S memory keys in the SQL category is as expected.; source include/assert.inc; 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..afd4c28b5b92 --- /dev/null +++ b/mysql-test/suite/udt/r/udt_basic.result @@ -0,0 +1,75 @@ +CREATE TYPE test.usbn13 AS CHAR(13); +Warnings: +Warning 6914 The following code is not implemented: Sql_cmd_create_type::execute() +CREATE TYPE test.complex_number AS BINARY(16); +Warnings: +Warning 6914 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 6914 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 6914 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; +DECLARE s VARCHAR(80); +SET a = complex_number_from_string("1+2i"); +SET b = complex_number_from_string("3+4i"); +SET c = complex_number_add(a, b); +SET s = complex_number_to_string(c); +SELECT s as "result"; +# SELECT complex_number_to_string(c) as "result 2"; +END$$ +Warnings: +Warning 6914 The following code is not implemented: resolve_type_descriptor() +Warning 6914 The following code is not implemented: resolve_type_descriptor() +Warning 6914 The following code is not implemented: resolve_type_descriptor() +call test.complex(); +result +4.000000+6.000000i +Warnings: +Warning 6914 The following code is not implemented: resolve_type_descriptor() +Warning 6914 The following code is not implemented: resolve_type_descriptor() +Warning 6914 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..2c9e95243bdd --- /dev/null +++ b/mysql-test/suite/udt/t/udt_basic.test @@ -0,0 +1,71 @@ + +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; + DECLARE s VARCHAR(80); + + SET a = complex_number_from_string("1+2i"); + SET b = complex_number_from_string("3+4i"); + SET c = complex_number_add(a, b); + SET s = complex_number_to_string(c); + SELECT s as "result"; + # SELECT complex_number_to_string(c) as "result 2"; +END$$ + +delimiter ;$$ + +call test.complex(); + +UNINSTALL COMPONENT "file://component_udt_example"; + +DROP PROCEDURE test.complex; + diff --git a/mysql-test/suite/x/r/create_alter_sql.result b/mysql-test/suite/x/r/create_alter_sql.result index 3bbae1a201d2..7b9f6eb4b591 100644 --- a/mysql-test/suite/x/r/create_alter_sql.result +++ b/mysql-test/suite/x/r/create_alter_sql.result @@ -135,9 +135,9 @@ col1 col2 col3 col4 SUBSTR(col5,1,10) col6 RUN CREATE TABLE t1 ( col1 INT , col2 DOUBLE(7,4), col3 DECIMAL(7,4) , col4 VARCHAR(20) , col5 BLOB ) While executing CREATE TABLE t1 ( col1 INT , col2 DOUBLE(7,4), col3 DECIMAL(7,4) , col4 VARCHAR(20) , col5 BLOB ) : Got expected error: Table 't1' already exists (code 1050) -RUN CREATE TABLE t1 ( col1 INVALID_DATA_TYPE , col2 JSON) -While executing CREATE TABLE t1 ( col1 INVALID_DATA_TYPE , col2 JSON) : -Got expected error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INVALID_DATA_TYPE , col2 JSON)' at line 1 (code 1064) +RUN CREATE TABLE t1 ( col1 INVALID_DATA_TYPE INTENDED, col2 JSON) +While executing CREATE TABLE t1 ( col1 INVALID_DATA_TYPE INTENDED, col2 JSON) : +Got expected error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INTENDED, col2 JSON)' at line 1 (code 1064) RUN CREATE TABLE t1_ ( col1 INT NULL PRIMARY KEY , col2 JSON) While executing CREATE TABLE t1_ ( col1 INT NULL PRIMARY KEY , col2 JSON) : Got expected error: All parts of a PRIMARY KEY must be NOT NULL; if you need NULL in a key, use UNIQUE instead (code 1171) diff --git a/mysql-test/suite/x/t/create_alter_sql.test b/mysql-test/suite/x/t/create_alter_sql.test index 8b2d72db6407..306f4d7ed9f5 100644 --- a/mysql-test/suite/x/t/create_alter_sql.test +++ b/mysql-test/suite/x/t/create_alter_sql.test @@ -92,10 +92,11 @@ SELECT col1,col2,col3,col4,SUBSTR(col5,1,10),col6 FROM t11; -->sql CREATE TABLE t1 ( col1 INT , col2 DOUBLE(7,4), col3 DECIMAL(7,4) , col4 VARCHAR(20) , col5 BLOB ) ; #-- "Incorrect data type" +#-- Note that "col1 INVALID_DATA_TYPE" alone can be a user defined type -->endsql -->expecterror 1064 -->sql -CREATE TABLE t1 ( col1 INVALID_DATA_TYPE , col2 JSON) ; +CREATE TABLE t1 ( col1 INVALID_DATA_TYPE INTENDED, col2 JSON) ; #-- "Incorrect column option" -->endsql -->expecterror 1171 diff --git a/mysql-test/t/dd_schema_definition_debug.test b/mysql-test/t/dd_schema_definition_debug.test index 92500bebfa3c..0529c06805d0 100644 --- a/mysql-test/t/dd_schema_definition_debug.test +++ b/mysql-test/t/dd_schema_definition_debug.test @@ -385,6 +385,12 @@ INSERT INTO dd_published_schema INSERT INTO dd_published_schema VALUES('90500', 1, '74da9e2f339801697f9daef2635d24c6d3bf982365d4b06dd67bd8b578e1ac1c'); +INSERT INTO dd_published_schema + VALUES('260700', 0, + '2728f3142fd00d50a653f1e4f9e4f206276994be5caa7e26a2d87813a03297f7'); +INSERT INTO dd_published_schema + VALUES('260700', 1, + '0000000000000000000000000000000000000000000000000000000000000000'); --sorted_result SELECT IFNULL(CONCAT('The schema checksum corresponds to DD version ', version, '.'), diff --git a/mysql-test/t/show_parse_tree.test b/mysql-test/t/show_parse_tree.test index 491cd2c12958..81e78fb9006e 100644 --- a/mysql-test/t/show_parse_tree.test +++ b/mysql-test/t/show_parse_tree.test @@ -56,8 +56,9 @@ SHOW PARSE_TREE SELECT db.func(), char(col1), char(col1 USING utf8mb4), concat(a SHOW PARSE_TREE UPDATE tab SET O = 1; --error ER_NOT_SUPPORTED_YET SHOW PARSE_TREE CREATE TABLE tab(id INT); +# (id INVALID_SYNTAX) can be a user defined type. --error ER_PARSE_ERROR -SHOW PARSE_TREE CREATE TABLE tab(id INVALID_SYNTAX); +SHOW PARSE_TREE CREATE TABLE tab(id INVALID_SYNTAX INTENDED); --echo # --echo # Bug#35964157 mysql 8.1.0/8.2.0, mysqld got signal 11, diff --git a/mysql-test/t/signal.test b/mysql-test/t/signal.test index 5b8ac597d316..a18b17a1a3ad 100644 --- a/mysql-test/t/signal.test +++ b/mysql-test/t/signal.test @@ -2627,7 +2627,13 @@ end $$ call test_signal $$ drop procedure test_signal $$ --- error ER_PARSE_ERROR +# +# Without user defined types: +# DECLARE foo bar ... -> syntax error ER_PARSE_ERROR +# With user defined types: +# DECLARE foo bar ...-> foo of type test.bar, ER_NO_SUCH_UDT_TYPE +# +-- error ER_PARSE_ERROR, ER_NO_SUCH_UDT_TYPE create procedure test_signal() begin DECLARE céèçà foo CONDITION FOR SQLSTATE '12345'; diff --git a/project/udt/slides/MySQL-User_Defined_Types-v2.pdf b/project/udt/slides/MySQL-User_Defined_Types-v2.pdf new file mode 100644 index 000000000000..e7767e59595b Binary files /dev/null and b/project/udt/slides/MySQL-User_Defined_Types-v2.pdf differ diff --git a/share/messages_to_clients.txt b/share/messages_to_clients.txt index 1f014496e5ea..49d2fbe6597c 100644 --- a/share/messages_to_clients.txt +++ b/share/messages_to_clients.txt @@ -11067,6 +11067,18 @@ ER_JDV_COLUMN_TAG_NOT_SUPPORTED_FOR_SUBQUERY ER_JDV_UPDATE_COLUMN_TAG_NOT_SUPPORTED_FOR_PK eng "The UPDATE column tag is not supported for a primary key projection at JSON path '%s'." +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 7dd60d4d2209..368d14708877 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/dd_version.h b/sql/dd/dd_version.h index 7aebcda12536..19abd36e61bc 100644 --- a/sql/dd/dd_version.h +++ b/sql/dd/dd_version.h @@ -230,10 +230,15 @@ - WL#16358: Support for 3rd party JavaScript libraries > Adds a new entry 'LIBRARY' to the TYPE enum in the mysql.routines table, and a new DD type 'Library'. + + 261000: + ---------------------------------------------------------------------------- + Changes: + - new DD table mysql.types */ namespace dd { -static const uint DD_VERSION = 90200; +static const uint DD_VERSION = 261000; static_assert(DD_VERSION <= MYSQL_VERSION_ID, "This release can not use a version number from the future"); 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..7d501b8a6306 --- /dev/null +++ b/sql/dd/impl/system_views/udt_types.cc @@ -0,0 +1,47 @@ +/* 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"); + + m_target_def.add_where("CAN_ACCESS_DATABASE(sch.name)"); +} + +} // 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/info_schema/metadata.h b/sql/dd/info_schema/metadata.h index d461702ac5a8..07166c2c65a3 100644 --- a/sql/dd/info_schema/metadata.h +++ b/sql/dd/info_schema/metadata.h @@ -303,6 +303,7 @@ namespace info_schema { Changes: - WL#17054 Introduce Support for CHECK/NO CHECK at Table and Column Level, and UPDATE/NO UPDATE at Column Level in JSON Duality View + - new view INFORMATION_SCHEMA.TYPES */ static const uint IS_DD_VERSION = 261000; 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 e60484796785..70619006afd2 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" @@ -4294,6 +4295,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]), @@ -8461,6 +8465,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..8539d3f9c559 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, m_type); +} diff --git a/sql/parse_tree_nodes.h b/sql/parse_tree_nodes.h index f0451a7015ff..3db148789bdb 100644 --- a/sql/parse_tree_nodes.h +++ b/sql/parse_tree_nodes.h @@ -6129,4 +6129,24 @@ 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; + PT_type *m_type; + POS m_columns_end_pos; + + public: + PT_create_type_stmt(const POS &pos, Type_ident *type_name, PT_type *type, + const POS &columns_end_pos = POS()) + : Parse_tree_root(pos), + m_type_name(type_name), + m_type(type), + 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..add1ce099441 --- /dev/null +++ b/sql/server_component/mysql_user_defined_type_imp.h @@ -0,0 +1,72 @@ +/* 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_out * f, bool is_null)); + static DEFINE_METHOD(void, get_null, (UDT_value_in * f, bool *is_null)); +}; + +class mysql_udt_value_string_imp { + public: /* service implementations */ + static DEFINE_METHOD(void, set_utf8mb4, + (UDT_value_out * f, const char *value, + unsigned int length)); + static DEFINE_METHOD(void, get_utf8mb4, + (UDT_value_in * f, const char **str, + unsigned int *length)); +}; + +class mysql_udt_value_blob_imp { + public: /* service implementations */ + static DEFINE_METHOD(void, set, + (UDT_value_out * f, const unsigned char *val, + unsigned int len)); + static DEFINE_METHOD(void, get, + (UDT_value_in * f, const 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..9e012a8ee6cb --- /dev/null +++ b/sql/sql_cmd_ddl_type.cc @@ -0,0 +1,130 @@ +/* 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()"); + + if (m_type_ident->db.length == 0) { + m_type_ident->db = thd->db(); + + if (m_type_ident->db.length == 0) { + my_error(ER_NO_DB_ERROR, MYF(0)); + return true; + } + } + + const char *db_name = m_type_ident->db.str; + const char *type_name = m_type_ident->type.str; + + assert(db_name != nullptr); + + // 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..96d641d48985 --- /dev/null +++ b/sql/sql_cmd_ddl_type.h @@ -0,0 +1,57 @@ +/* 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 PT_type; + +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, PT_type *type) + : Sql_cmd_ddl_type(), m_type_ident(type_ident), m_type(type) {} + + enum_sql_command sql_command_code() const override { + return SQLCOM_CREATE_TYPE; + } + + bool execute(THD *thd) override; + + private: + Type_ident *m_type_ident; + PT_type *m_type; +}; + +#endif /* SQL_CMD_DDL_TYPE_INCLUDED */ diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 33d69bcb9597..f40a48b4d8e2 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 dcaed4d29943..5d9221dc5177 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) { @@ -4763,7 +4769,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..353e6fd3e815 --- /dev/null +++ b/sql/sql_udt.cc @@ -0,0 +1,623 @@ +/* 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_in { + public: + UDT_value_in(Item *item) : m_item(item) {} + + void get_null(bool *is_null); + + void get_utf8mb4(const char **str, unsigned int *length); + + void get_blob(const unsigned char **val, unsigned int *length); + + private: + Item *m_item; + String m_string_data; +}; + +class UDT_value_out { + public: + UDT_value_out(Field *field) : m_field(field) {} + + void set_null(bool is_null); + + void set_utf8mb4(const char *str, unsigned int length); + + void set_blob(const unsigned char *val, unsigned int length); + + private: + Field *m_field; +}; + +void UDT_value_in::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_out::set_null(bool is_null) { + // FIXME: ptrdiff_t row_offset ? + if (is_null) { + m_field->set_null(); + } else { + m_field->set_notnull(); + } +} + +void UDT_value_in::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(); + } +} + +void UDT_value_out::set_utf8mb4(const char *str, unsigned int length) { + m_field->store(str, length, &my_charset_utf8mb4_0900_ai_ci); +} + +void UDT_value_in::get_blob(const unsigned char **val, unsigned int *length) { + if (m_item != nullptr) { + String *data = m_item->val_str(&m_string_data); + *val = reinterpret_cast(data->ptr()); + *length = data->length(); + } +} + +void UDT_value_out::set_blob(const unsigned char *val, unsigned int length) { + const char *str = reinterpret_cast(val); + m_field->store(str, length, &my_charset_bin); +} + +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); + + std::string key = fd->name; + + mysql_rwlock_wrlock(&THR_LOCK_udt); + + // FIXME: lifecycle, may be in use + udt_function_hash->erase(key); + + mysql_rwlock_unlock(&THR_LOCK_udt); + + return 0; +} + +DEFINE_METHOD(void, mysql_udt_value_null_imp::set_null, + (UDT_value_out * f, bool is_null)) { + fprintf(stderr, "mysql_udt_value_null_imp::set_null()\n"); + assert(f != nullptr); + f->set_null(is_null); +} + +DEFINE_METHOD(void, mysql_udt_value_null_imp::get_null, + (UDT_value_in * 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_out * f, const char *value, unsigned int length)) { + fprintf(stderr, "mysql_udt_value_string_imp::set_utf8mb4()\n"); + assert(f != nullptr); + f->set_utf8mb4(value, length); +} + +DEFINE_METHOD(void, mysql_udt_value_string_imp::get_utf8mb4, + (UDT_value_in * 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_out * f, const unsigned char *val, unsigned int len)) { + fprintf(stderr, "mysql_udt_value_blob_imp::set()\n"); + assert(f != nullptr); + f->set_blob(val, len); +} + +DEFINE_METHOD(void, mysql_udt_value_blob_imp::get, + (UDT_value_in * f, const unsigned char **val, + unsigned int *len)) { + fprintf(stderr, "mysql_udt_value_blob_imp::get()\n"); + assert(f != nullptr); + assert(val != nullptr); + assert(len != nullptr); + f->get_blob(val, len); +} + +//------------------------------------------------------------------- +// Runtime, item tree +//------------------------------------------------------------------- + +static void convert_type_descriptor(const mysql_type_descriptor_t *from, + TypeDescriptor *to) { + to->m_type = static_cast(from->mysql_type); + to->m_type_flags = from->type_flags; + // to->m_length = from->length; + // to->m_dec = from->decimals; + + // FIXME: how/if to expose charset from component + if (from->mysql_type == MYSQL_FIELD_TYPE_BLOB) { + to->m_charset = &my_charset_bin; + } else if (from->mysql_type == MYSQL_FIELD_TYPE_VARCHAR) { + to->m_charset = &my_charset_utf8mb4_0900_ai_ci; + } else { + to->m_charset = from->charset; + } + + to->m_has_explicit_collation = from->has_explicit_collation; + // to->m_geo_type = from->mysql_type; + // to->m_internal_list = from->mysql_type; + // to->m_type_ident = from->type_ident; +} + +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); + + // FIXME: Create Field for return type + m_return_field = create_result_field(thd); + + if (m_return_field == nullptr) { + return true; + } + + return false; +} + +// See sp_head::create_result_field() +Field *Item_udt_func::create_result_field(THD *thd) { + bool rc; + + // Forge dummy table + m_share.db_low_byte_first = true; + m_table.s = &m_share; + + // Forge field def + mysql_type_descriptor_t *td = m_udt_function->fd->return_type; + TypeDescriptor td2; + convert_type_descriptor(td, &td2); + + FieldDescriptor fd; + const char *field_name = "dummy"; + rc = m_return_field_def.init_from_type_descriptor(thd, field_name, &td2, &fd); + + if (rc) { + return nullptr; + } + + // Add 1 for null byte. + m_table.record[0] = + thd->mem_root->ArrayAlloc(m_return_field_def.pack_length() + 1); + if (m_table.record[0] == nullptr) return nullptr; + + size_t field_length = m_return_field_def.max_display_width_in_bytes(); + + assert(m_return_field_def.auto_flags == Field::NONE); + Field *field = + ::make_field(m_return_field_def, m_table.s, field_name, field_length, + m_table.record[0] + 1, m_table.record[0], 0); + + // Return early, failed to allocate Field on memroot + if (field == nullptr) return nullptr; + + field->gcol_info = m_return_field_def.gcol_info; + field->m_default_val_expr = m_return_field_def.m_default_val_expr; + field->stored_in_db = m_return_field_def.stored_in_db; + field->init(&m_table); + + assert(field->pack_length() == m_return_field_def.pack_length()); + + return field; +} + +/** + * @param [out] argument_count size of @p argument_value_array. + * @param [out] argument_value_array Input parameters to the UDT function. + */ +int build_argument_value_array(Item_udt_func *that, + mysql_function_descriptor_t *fd, + size_t *argument_count, + UDT_value_in ***argument_value_array) { + size_t count = fd->argument_count; + size_t actual = that->arg_count; + + if (count != actual) { + *argument_count = 0; + *argument_value_array = nullptr; + // TODO: Report an error ? + return 1; + } + + if (count == 0) { + *argument_count = 0; + *argument_value_array = nullptr; + return 0; + } + + UDT_value_in **array = new UDT_value_in *[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_in(item); + } + + *argument_count = count; + *argument_value_array = array; + return 0; +} + +void destroy_argument_value_array(size_t count, UDT_value_in **array) { + for (size_t i = 0; i < count; i++) { + delete array[i]; + } + + delete[] array; +} + +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); + + evaluate_to_field(field); + + // FIXME + return TYPE_ERR_BAD_VALUE; +} + +bool Item_udt_func::execute() { + fprintf(stderr, "Item_udt_func::execute()\n"); + assert(m_return_field != nullptr); + return evaluate_to_field(m_return_field); +} + +bool Item_udt_func::evaluate_to_field(Field *field) { + fprintf(stderr, "Item_udt_func::evaluate_to_field() field %s\n", + field->field_name); + int rc; + + eval_function_t eval = m_udt_function->impl; + + // Build input value(s) + + size_t param_count{0}; + UDT_value_in **param_array{nullptr}; + + rc = build_argument_value_array(this, m_udt_function->fd, ¶m_count, + ¶m_array); + + if (rc) { + return true; + } + + // Build output value + + UDT_value_out result_value(field); + + // 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); + + destroy_argument_value_array(param_count, param_array); + + if (rc) { + return true; + } + + // Capture the result null value + + null_value = field->is_null(); + + return false; +} + +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) { + if (execute()) { + return error_str(); + } + + if (null_value) { + return nullptr; + } + + return m_return_field->val_str(str); +} + +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..ad0540c66e77 --- /dev/null +++ b/sql/sql_udt.h @@ -0,0 +1,86 @@ +/* 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/create_field.h" +#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; + + Field *create_result_field(THD *thd); + + protected: + type_conversion_status save_in_field_inner(Field *field, + bool no_conversions) override; + + private: + bool execute(); + bool init_result_field(THD *thd); + bool evaluate_to_field(Field *field); + + udt_function_record *m_udt_function; + + // Fake table to hold the result field. + TABLE m_table; + TABLE_SHARE m_share; + + Create_field m_return_field_def; + + Field *m_return_field{nullptr}; +}; + +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..c5751572fde1 --- /dev/null +++ b/sql/sql_user_defined_type.cc @@ -0,0 +1,155 @@ +/* + 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; + + assert(db_name != nullptr); + assert(type_name != nullptr); + + // 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"); + +#ifdef NEVER + // 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; +#endif + + // FIXME: forged BINARY(16) + td->m_type = MYSQL_TYPE_BLOB; + td->m_type_flags = 0; + td->m_length = "16"; + td->m_dec = nullptr; + td->m_charset = &my_charset_bin; + td->m_has_explicit_collation = false; + td->m_geo_type = 0; + td->m_internal_list = nullptr; + + // FIXME, use type from dd::UDT_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 ce76add84d39..e28b75f657a7 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 */ @@ -2005,6 +2007,8 @@ CHARSET_INFO *warn_on_deprecated_user_defined_collation( %type text_literal +%type type_ident + %type alter_instance_stmt alter_library_stmt @@ -2020,6 +2024,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 @@ -2223,7 +2228,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 @@ -2521,6 +2526,7 @@ simple_statement: | create_role_stmt | create_srs_stmt | create_table_stmt + | create_type_stmt | deallocate { $$= nullptr; } | delete_stmt | describe_stmt @@ -3338,6 +3344,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, $5); + } + ; + create_table_stmt: CREATE opt_temporary_or_external TABLE_SYM opt_if_not_exists table_ident '(' table_element_list ')' opt_create_table_options_etc @@ -4015,7 +4043,7 @@ sp_fdparams: ; sp_fdparam: - ident type opt_collate + ident broken_type opt_collate { THD *thd= YYTHD; LEX *lex= thd->lex; @@ -4077,7 +4105,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; @@ -4174,7 +4202,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*/ @@ -4233,6 +4261,41 @@ 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(); + + if (td.m_type_ident != nullptr) { + // Using stored procedure DB as default. + if (td.m_type_ident->db.length == 0) { + LEX_CSTRING db = to_lex_cstring(sp->m_db); + Type_ident *qualified = new Type_ident(db, td.m_type_ident->type); + td.m_type_ident = qualified; + } + } + + // 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(), @@ -4245,6 +4308,7 @@ sp_decl: { MYSQL_YYABORT; } +*/ if (prepare_sp_create_field(thd, &spvar->field_def)) MYSQL_YYABORT; @@ -7142,11 +7206,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 { @@ -7178,7 +7242,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); @@ -7373,6 +7437,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); } @@ -7644,6 +7731,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); @@ -12565,7 +12653,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( @@ -18703,7 +18791,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,