@@ -1029,18 +1029,25 @@ bool ValidateAndConvertVDFArguments(THD *thd, const char *func_name,
10291029 continue ;
10301030 }
10311031
1032+ // Build the expected qualified base name ("extension.TYPE") once for use
1033+ // in Cases 1 and 3. This ensures a TYPE from extension A is not accepted
1034+ // by a VDF from extension B that also declares the same TYPE.
1035+ // expected_type.custom_type is always non-null for VEF_TYPE_CUSTOM params
1036+ // (enforced by the SDK).
1037+ assert (expected_type.custom_type != nullptr );
1038+ const std::string expected_qbn =
1039+ std::string (extension_name.str , extension_name.length ) + " ." +
1040+ expected_type.custom_type ;
1041+
1042+ // Case 1: Argument already has custom type - validate compatibility.
10321043 auto *tc = args[i]->get_type_context ();
1033-
1034- // Case 1: Argument already has custom type - validate compatibility
10351044 if (tc != nullptr ) {
1036- const char *expected_type_name = expected_type.custom_type ;
1037- if (expected_type_name != nullptr &&
1038- tc->type_name () != expected_type_name) {
1045+ if (tc->qualified_base_name () != expected_qbn) {
10391046 villagesql_error (
10401047 " Cannot initialize function '%s': argument %u type mismatch "
10411048 " (expected %s, got %s)" ,
1042- MYF (0 ), func_name, i + 1 , expected_type_name ,
1043- tc->type_name ().c_str ());
1049+ MYF (0 ), func_name, i + 1 , expected_qbn. c_str () ,
1050+ tc->qualified_base_name ().c_str ());
10441051 return true ;
10451052 }
10461053 continue ;
@@ -1049,19 +1056,10 @@ bool ValidateAndConvertVDFArguments(THD *thd, const char *func_name,
10491056 // Case 2: Argument is a constant string - try implicit conversion
10501057 if (args[i]->type () == Item::STRING_ITEM &&
10511058 args[i]->const_for_execution ()) {
1052- const char *custom_type_name = expected_type.custom_type ;
1053- if (custom_type_name == nullptr ) {
1054- villagesql_error (
1055- " Cannot initialize function '%s': invalid function signature for "
1056- " argument %u" ,
1057- MYF (0 ), func_name, i + 1 );
1058- return true ;
1059- }
1060-
10611059 // Resolve the custom type by extension and type name
10621060 LEX_STRING lex_type_name;
1063- lex_type_name.str = const_cast <char *>(custom_type_name );
1064- lex_type_name.length = strlen (custom_type_name );
1061+ lex_type_name.str = const_cast <char *>(expected_type. custom_type );
1062+ lex_type_name.length = strlen (expected_type. custom_type );
10651063
10661064 const TypeContext *type_ctx = nullptr ;
10671065 // TODO(villagesql-beta): pass real TypeParameters for parameterized types
@@ -1075,7 +1073,7 @@ bool ValidateAndConvertVDFArguments(THD *thd, const char *func_name,
10751073 villagesql_error (
10761074 " Cannot initialize function '%s': custom type '%s' not found for "
10771075 " argument %u" ,
1078- MYF (0 ), func_name, custom_type_name , i + 1 );
1076+ MYF (0 ), func_name, expected_type. custom_type , i + 1 );
10791077 return true ;
10801078 }
10811079
@@ -1088,11 +1086,38 @@ bool ValidateAndConvertVDFArguments(THD *thd, const char *func_name,
10881086
10891087 // Case 3: Argument is a column reference (Item_field) without type context
10901088 // yet. This happens during functional index creation: the table is being
1091- // created, so the field's custom type context hasn't been injected yet.
1092- // Allow it through; at execution time the field will carry its type context
1093- // and marshal_args() will pass the binary data correctly.
1089+ // created, so MaybeInjectCustomType hasn't run yet. The field's underlying
1090+ // Create_field_wrapper (set by replace_field_processor) lets us check
1091+ // whether the column will actually be a custom type.
1092+ // We match on qualified_base_name() (extension.type, no parameters),
1093+ // consistent with Case 1, because VDF signatures cannot express type
1094+ // parameterization - a VDF declared with param("TVECTOR") must accept any
1095+ // TVECTOR(N) column.
10941096 if (args[i]->type () == Item::FIELD_ITEM) {
1095- continue ;
1097+ auto *item_field = down_cast<Item_field *>(args[i]);
1098+ if (item_field->field != nullptr &&
1099+ item_field->field ->is_wrapper_field ()) {
1100+ auto *wrapper =
1101+ down_cast<const Create_field_wrapper *>(item_field->field );
1102+ const Create_field *cf = wrapper->get_create_field ();
1103+ if (cf->custom_type_context != nullptr ) {
1104+ if (cf->custom_type_context ->qualified_base_name () != expected_qbn) {
1105+ villagesql_error (
1106+ " Cannot initialize function '%s': argument %u type mismatch "
1107+ " (expected %s, got %s)" ,
1108+ MYF (0 ), func_name, i + 1 , expected_qbn.c_str (),
1109+ cf->custom_type_context ->qualified_base_name ().c_str ());
1110+ return true ;
1111+ }
1112+ continue ;
1113+ }
1114+ }
1115+ // Column has no custom type context - reject it
1116+ villagesql_error (
1117+ " Cannot initialize function '%s': argument %u must be a custom type "
1118+ " or string constant" ,
1119+ MYF (0 ), func_name, i + 1 );
1120+ return true ;
10961121 }
10971122
10981123 // Case 4: Argument is not a custom type and not a constant string
0 commit comments