@@ -367,11 +367,9 @@ int CustomMemCompare(const Item *item, const uchar *data1, size_t len1,
367367}
368368
369369bool AreTypesCompatible (const TypeContext &tc1, const TypeContext &tc2) {
370- // Types are compatible if they have identical type name, extension, and
371- // version
372- return tc1.type_name () == tc2.type_name () &&
373- tc1.extension_name () == tc2.extension_name () &&
374- tc1.extension_version () == tc2.extension_version ();
370+ // Types are compatible if they have the same key (type name, extension,
371+ // version, and parameters). This ensures e.g. TVECTOR(3) != TVECTOR(4).
372+ return tc1.key () == tc2.key ();
375373}
376374
377375bool MaybeValidateUnionTypeCompatibility (Item *accumulator, Item *item) {
@@ -536,15 +534,15 @@ bool ValidateAndReportCustomFieldStore(const Item *item, const Field *field) {
536534 assert (field->has_type_context ());
537535
538536 // Check if the item can be stored in the custom field
539- bool can_store = CanStoreInCustomField (item, field);
540- if (can_store) {
537+ if (CanStoreInCustomField (item, field)) {
541538 return false ; // Success
542539 }
543540
544541 // Validation failed - generate appropriate error message
542+ // Use val_external_str() to get a human-readable value: for custom type items
543+ // this decodes the binary representation; for others it behaves like val_str.
545544 String str_value;
546- // Need to cast away const to call val_str (which is not const)
547- String *item_str = const_cast <Item *>(item)->val_str (&str_value);
545+ String *item_str = const_cast <Item *>(item)->val_external_str (&str_value);
548546 const char *value_str = item_str ? item_str->c_ptr_safe () : " <null>" ;
549547 const THD *thd = current_thd;
550548 const Diagnostics_area *da = thd->get_stmt_da ();
@@ -573,6 +571,12 @@ bool ValidateAndReportCustomFieldStore(const Item *item, const Field *field) {
573571 " explicit conversion for column '%s' at row %ld" ,
574572 MYF (0 ), field->get_type_context ()->type_name ().c_str (),
575573 field->field_name , da->current_row_for_condition ());
574+ } else if (item->has_type_context ()) {
575+ villagesql_error (
576+ " Cannot implicitly cast from %s to %s for column '%s' at row %ld" ,
577+ MYF (0 ), item->get_type_context ()->qualified_name ().c_str (),
578+ field->get_type_context ()->qualified_name ().c_str (), field->field_name ,
579+ da->current_row_for_condition ());
576580 } else {
577581 // Generic error for other cases (invalid format, etc.)
578582 villagesql_error (" Incorrect %s value: '%s' for column '%s' at row %ld" ,
@@ -587,9 +591,10 @@ bool ValidateAndReportCustomFieldStore(const Item *item, const Field *field) {
587591bool TryCopyCustomTypeField (const Field *from, Field *to) {
588592 assert (from->has_type_context ());
589593
590- // If target doesn't have a custom type, this is an incompatible conversion.
591- if (!to->has_type_context ()) {
592- // TODO(villagesql-performance): evaluate something more performant
594+ // If target doesn't have a custom type, or custom types do not match,
595+ // this is an incompatible conversion.
596+ if (!to->has_type_context () ||
597+ !AreTypesCompatible (*from->get_type_context (), *to->get_type_context ())) {
593598 StringBuffer<MAX_FIELD_WIDTH> result (from->charset ());
594599 result.length (0U );
595600 from->val_external_str (&result);
@@ -601,12 +606,7 @@ bool TryCopyCustomTypeField(const Field *from, Field *to) {
601606 MYF (0 ), from->get_type_context ()->type_name ().c_str (),
602607 result.c_ptr_safe (), to->field_name ,
603608 thd->get_stmt_da ()->current_row_for_condition ());
604- return false ; // Error generated, don't fall through
605- }
606-
607- // Check if they're the same custom type
608- if (from->get_type_context () != to->get_type_context ()) {
609- return true ;
609+ return false ;
610610 }
611611
612612 // Both fields have the same custom type. Copy binary data directly.
0 commit comments