diff --git a/mysql-test/suite/villagesql/extension/r/type_method_name_validation.result b/mysql-test/suite/villagesql/extension/r/type_method_name_validation.result deleted file mode 100644 index 9d045fc9270..00000000000 --- a/mysql-test/suite/villagesql/extension/r/type_method_name_validation.result +++ /dev/null @@ -1,21 +0,0 @@ -call mtr.add_suppression("Orphaned expansion directory found but not removed"); -call mtr.add_suppression("uses '::' but prefix"); -call mtr.add_suppression("is not a valid type method"); -call mtr.add_suppression("Failed to install extension"); -# restart: --veb-dir=MYSQLTEST_VARDIR/veb -# Test 1: bad prefix — WRONGTYPE::encode for type MYTYPE -Creating extension bad_colon_prefix using SDK... -Created bad_colon_prefix.veb -INSTALL EXTENSION bad_colon_prefix; -ERROR HY000: Failed to install extension 'bad_colon_prefix': type 'MYTYPE' failed validation -# Verify extension was NOT installed -SELECT * FROM INFORMATION_SCHEMA.EXTENSIONS; -EXTENSION_NAME EXTENSION_VERSION -# Test 2: bad suffix — MYTYPE::transform for type MYTYPE -Creating extension bad_colon_suffix using SDK... -Created bad_colon_suffix.veb -INSTALL EXTENSION bad_colon_suffix; -ERROR HY000: Failed to install extension 'bad_colon_suffix': type 'MYTYPE' failed validation -# Verify extension was NOT installed -SELECT * FROM INFORMATION_SCHEMA.EXTENSIONS; -EXTENSION_NAME EXTENSION_VERSION diff --git a/mysql-test/suite/villagesql/extension/t/type_method_name_validation.test b/mysql-test/suite/villagesql/extension/t/type_method_name_validation.test deleted file mode 100644 index 6a8951e0733..00000000000 --- a/mysql-test/suite/villagesql/extension/t/type_method_name_validation.test +++ /dev/null @@ -1,38 +0,0 @@ -# Test install-time validation of :: names in type method VDFs. -# Verifies that INSTALL EXTENSION fails when a type's VDF name uses :: but -# the prefix doesn't match the type name or the suffix is not a valid method. - ---let $veb_dir = $MYSQLTEST_VARDIR/veb ---exec mkdir -p $veb_dir - -call mtr.add_suppression("Orphaned expansion directory found but not removed"); -call mtr.add_suppression("uses '::' but prefix"); -call mtr.add_suppression("is not a valid type method"); -call mtr.add_suppression("Failed to install extension"); ---replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR ---let $restart_parameters = restart: --veb-dir=$veb_dir ---source include/restart_mysqld.inc - ---echo # Test 1: bad prefix — WRONGTYPE::encode for type MYTYPE ---let $extension_name = bad_colon_prefix ---let $extension_version = 0.0.1-devtest ---let $extension_source = $MYSQL_TEST_DIR/suite/villagesql/std_data/bad_colon_prefix.cc ---source include/villagesql/create_extension_sdk.inc - ---error ER_VILLAGESQL_GENERIC_ERROR -INSTALL EXTENSION bad_colon_prefix; - ---echo # Verify extension was NOT installed -SELECT * FROM INFORMATION_SCHEMA.EXTENSIONS; - ---echo # Test 2: bad suffix — MYTYPE::transform for type MYTYPE ---let $extension_name = bad_colon_suffix ---let $extension_version = 0.0.1-devtest ---let $extension_source = $MYSQL_TEST_DIR/suite/villagesql/std_data/bad_colon_suffix.cc ---source include/villagesql/create_extension_sdk.inc - ---error ER_VILLAGESQL_GENERIC_ERROR -INSTALL EXTENSION bad_colon_suffix; - ---echo # Verify extension was NOT installed -SELECT * FROM INFORMATION_SCHEMA.EXTENSIONS; diff --git a/mysql-test/suite/villagesql/std_data/bad_colon_prefix.cc b/mysql-test/suite/villagesql/std_data/bad_colon_prefix.cc deleted file mode 100644 index 63cb8bfe631..00000000000 --- a/mysql-test/suite/villagesql/std_data/bad_colon_prefix.cc +++ /dev/null @@ -1,72 +0,0 @@ -/* Copyright (c) 2026 VillageSQL Contributors - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -// Bad extension: registers type MYTYPE but names its encode VDF -// "WRONGTYPE::from_string". The :: prefix does not match the type name, so -// INSTALL EXTENSION must fail. - -#include - -#include - -bool mytype_encode(std::string_view from, vsql::Span buf, - size_t *length) { - if (buf.size() < 4) return true; - size_t to_copy = from.size() < buf.size() ? from.size() : buf.size(); - memcpy(buf.data(), from.data(), to_copy); - *length = to_copy; - return false; -} - -bool mytype_decode(vsql::Span data, - vsql::Span out, size_t *out_len) { - if (data.size() == 0) return true; - size_t to_copy = data.size() < out.size() ? data.size() : out.size(); - memcpy(out.data(), data.data(), to_copy); - *out_len = to_copy; - return false; -} - -int mytype_compare(vsql::Span a, - vsql::Span b) { - size_t min_len = a.size() < b.size() ? a.size() : b.size(); - int r = memcmp(a.data(), b.data(), min_len); - if (r != 0) return r; - if (a.size() < b.size()) return -1; - if (a.size() > b.size()) return 1; - return 0; -} - -using villagesql::vsql::make_extension; -using namespace villagesql::func_builder; -using namespace villagesql::type_builder; - -// MYTYPE is the type name, but the encode VDF is named -// "WRONGTYPE::from_string". The prefix "WRONGTYPE" does not match "MYTYPE", so -// installation must fail. -VEF_GENERATE_ENTRY_POINTS( - make_extension() - .type(make_type("MYTYPE") - .persisted_length(16) - .max_decode_buffer_length(64) - .encode("WRONGTYPE::from_string") - .decode("MYTYPE::decode") - .compare("MYTYPE::compare") - .build()) - .func(make_type_encode<&mytype_encode>("WRONGTYPE::from_string", - "MYTYPE")) - .func(make_type_decode<&mytype_decode>("MYTYPE::decode", "MYTYPE")) - .func(make_type_compare<&mytype_compare>("MYTYPE::compare", "MYTYPE"))) diff --git a/mysql-test/suite/villagesql/std_data/bad_colon_suffix.cc b/mysql-test/suite/villagesql/std_data/bad_colon_suffix.cc deleted file mode 100644 index bfacf18e158..00000000000 --- a/mysql-test/suite/villagesql/std_data/bad_colon_suffix.cc +++ /dev/null @@ -1,71 +0,0 @@ -/* Copyright (c) 2026 VillageSQL Contributors - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -// Bad extension: registers type MYTYPE but names its encode VDF -// "MYTYPE::transform". The :: suffix "transform" is not a valid type method -// name, so INSTALL EXTENSION must fail. - -#include - -#include - -bool mytype_encode(std::string_view from, vsql::Span buf, - size_t *length) { - if (buf.size() < 4) return true; - size_t to_copy = from.size() < buf.size() ? from.size() : buf.size(); - memcpy(buf.data(), from.data(), to_copy); - *length = to_copy; - return false; -} - -bool mytype_decode(vsql::Span data, - vsql::Span out, size_t *out_len) { - if (data.size() == 0) return true; - size_t to_copy = data.size() < out.size() ? data.size() : out.size(); - memcpy(out.data(), data.data(), to_copy); - *out_len = to_copy; - return false; -} - -int mytype_compare(vsql::Span a, - vsql::Span b) { - size_t min_len = a.size() < b.size() ? a.size() : b.size(); - int r = memcmp(a.data(), b.data(), min_len); - if (r != 0) return r; - if (a.size() < b.size()) return -1; - if (a.size() > b.size()) return 1; - return 0; -} - -using villagesql::vsql::make_extension; -using namespace villagesql::func_builder; -using namespace villagesql::type_builder; - -// MYTYPE is the type name, but the encode VDF is named "MYTYPE::transform". -// The suffix "transform" is not a valid type method (must be encode, decode, -// compare, or hash), so installation must fail. -VEF_GENERATE_ENTRY_POINTS( - make_extension() - .type(make_type("MYTYPE") - .persisted_length(16) - .max_decode_buffer_length(64) - .encode("MYTYPE::transform") - .decode("MYTYPE::decode") - .compare("MYTYPE::compare") - .build()) - .func(make_type_encode<&mytype_encode>("MYTYPE::transform", "MYTYPE")) - .func(make_type_decode<&mytype_decode>("MYTYPE::decode", "MYTYPE")) - .func(make_type_compare<&mytype_compare>("MYTYPE::compare", "MYTYPE"))) diff --git a/unittest/gunit/villagesql/validate-t.cc b/unittest/gunit/villagesql/validate-t.cc index cf2ccdb86ca..8966ace52a7 100644 --- a/unittest/gunit/villagesql/validate-t.cc +++ b/unittest/gunit/villagesql/validate-t.cc @@ -287,6 +287,108 @@ TEST_F(ValidateExtensionRegistrationTest, EXPECT_EQ(result->funcs.size(), 1u); } +// A v2 type whose encode_vdf_name is malformed fails validation. +TEST_F(ValidateExtensionRegistrationTest, V2TypeBadVdfName) { + struct TestCase { + const char *bad_name; + const char *expected_error; + }; + + const TestCase cases[] = { + {"MYTYPE::transform", + "type 'MYTYPE' failed validation"}, // unrecognised method suffix + {"WRONGTYPE::from_string", + "type 'MYTYPE' failed validation"}, // prefix does not match type name + {"::MYTPE::transform", "type 'MYTYPE' failed validation"}, + {"MYTPE::::transform", "type 'MYTYPE' failed validation"}, + }; + + for (const auto &tc : cases) { + SCOPED_TRACE(std::string("bad_name=") + tc.bad_name); + + vef_type_desc_t td = {}; + td.protocol = VEF_PROTOCOL_2; + td.name = "MYTYPE"; + td.persisted_length = 16; + td.max_decode_buffer_length = 256; + td.encode_vdf_name = tc.bad_name; + + vef_type_desc_t *types[] = {&td}; + vef_registration_t reg = {}; + reg.protocol = VEF_PROTOCOL_2; + reg.deprecated_extension_name = "my_ext"; + reg.type_count = 1; + reg.types = types; + + std::string error; + auto result = villagesql::veb::validate_extension_registration( + make_ext_reg(®, VEF_PROTOCOL_2), "my_ext", "1.0.0", error); + + EXPECT_FALSE(result.has_value()); + EXPECT_EQ(error, tc.expected_error); + } +} + +// A v2 type registration using valid VDF names succeeds end-to-end. +TEST_F(ValidateExtensionRegistrationTest, V2TypeValidVdfNames) { + vef_type_desc_t td = {}; + td.protocol = VEF_PROTOCOL_2; + td.name = "MYTYPE"; + td.persisted_length = 16; + td.max_decode_buffer_length = 256; + td.encode_vdf_name = "MYTYPE::from_string"; + td.decode_vdf_name = "MYTYPE::to_string"; + td.compare_vdf_name = "MYTYPE::compare"; + + vef_type_t str_type = {VEF_TYPE_STRING, nullptr}; + vef_type_t int_type = {VEF_TYPE_INT, nullptr}; + vef_type_t custom_mytype = {VEF_TYPE_CUSTOM, "MYTYPE"}; + + vef_type_t encode_params[] = {str_type}; + vef_signature_t encode_sig = {1, encode_params, custom_mytype}; + vef_func_desc_t encode_fd = {}; + encode_fd.protocol = VEF_PROTOCOL_2; + encode_fd.name = "MYTYPE::from_string"; + encode_fd.signature = &encode_sig; + encode_fd.vdf = stub_vdf; + + vef_type_t decode_params[] = {custom_mytype}; + vef_signature_t decode_sig = {1, decode_params, str_type}; + vef_func_desc_t decode_fd = {}; + decode_fd.protocol = VEF_PROTOCOL_2; + decode_fd.name = "MYTYPE::to_string"; + decode_fd.signature = &decode_sig; + decode_fd.vdf = stub_vdf; + + vef_type_t compare_params[] = {custom_mytype, custom_mytype}; + vef_signature_t compare_sig = {2, compare_params, int_type}; + vef_func_desc_t compare_fd = {}; + compare_fd.protocol = VEF_PROTOCOL_2; + compare_fd.name = "MYTYPE::compare"; + compare_fd.signature = &compare_sig; + compare_fd.vdf = stub_vdf; + + vef_type_desc_t *types[] = {&td}; + vef_func_desc_t *funcs[] = {&encode_fd, &decode_fd, &compare_fd}; + + vef_registration_t reg = {}; + reg.protocol = VEF_PROTOCOL_2; + reg.deprecated_extension_name = "my_ext"; + reg.type_count = 1; + reg.types = types; + reg.func_count = 3; + reg.funcs = funcs; + + std::string error; + auto result = villagesql::veb::validate_extension_registration( + make_ext_reg(®, VEF_PROTOCOL_2), "my_ext", "1.0.0", error); + + ASSERT_TRUE(result.has_value()); + EXPECT_TRUE(error.empty()); + ASSERT_EQ(result->types.size(), 1u); + EXPECT_EQ(result->types[0].type_name(), "MYTYPE"); +} + // Multiple types and funcs are all validated and returned. TEST_F(ValidateExtensionRegistrationTest, MultipleTypesAndFuncs) { vef_type_desc_t td1 = make_v1_type("TYPE_A");