@@ -383,11 +383,9 @@ int CustomMemCompare(const Item *item, const uchar *data1, size_t len1,
383383}
384384
385385bool AreTypesCompatible (const TypeContext &tc1, const TypeContext &tc2) {
386- // Types are compatible if they have identical type name, extension, and
387- // version
388- return tc1.type_name () == tc2.type_name () &&
389- tc1.extension_name () == tc2.extension_name () &&
390- tc1.extension_version () == tc2.extension_version ();
386+ // Types are compatible if they have the same key (type name, extension,
387+ // version, and parameters). This ensures e.g. TVECTOR(3) != TVECTOR(4).
388+ return tc1.key () == tc2.key ();
391389}
392390
393391bool MaybeValidateUnionTypeCompatibility (Item *accumulator, Item *item) {
@@ -552,15 +550,15 @@ bool ValidateAndReportCustomFieldStore(const Item *item, const Field *field) {
552550 assert (field->has_type_context ());
553551
554552 // Check if the item can be stored in the custom field
555- bool can_store = CanStoreInCustomField (item, field);
556- if (can_store) {
553+ if (CanStoreInCustomField (item, field)) {
557554 return false ; // Success
558555 }
559556
560557 // Validation failed - generate appropriate error message
558+ // Use val_external_str() to get a human-readable value: for custom type items
559+ // this decodes the binary representation; for others it behaves like val_str.
561560 String str_value;
562- // Need to cast away const to call val_str (which is not const)
563- String *item_str = const_cast <Item *>(item)->val_str (&str_value);
561+ String *item_str = const_cast <Item *>(item)->val_external_str (&str_value);
564562 const char *value_str = item_str ? item_str->c_ptr_safe () : " <null>" ;
565563 const THD *thd = current_thd;
566564 const Diagnostics_area *da = thd->get_stmt_da ();
@@ -589,6 +587,12 @@ bool ValidateAndReportCustomFieldStore(const Item *item, const Field *field) {
589587 " explicit conversion for column '%s' at row %ld" ,
590588 MYF (0 ), field->get_type_context ()->type_name ().c_str (),
591589 field->field_name , da->current_row_for_condition ());
590+ } else if (item->has_type_context ()) {
591+ villagesql_error (
592+ " Cannot implicitly cast from %s to %s for column '%s' at row %ld" ,
593+ MYF (0 ), item->get_type_context ()->qualified_name ().c_str (),
594+ field->get_type_context ()->qualified_name ().c_str (), field->field_name ,
595+ da->current_row_for_condition ());
592596 } else {
593597 // Generic error for other cases (invalid format, etc.)
594598 villagesql_error (" Incorrect %s value: '%s' for column '%s' at row %ld" ,
@@ -603,9 +607,10 @@ bool ValidateAndReportCustomFieldStore(const Item *item, const Field *field) {
603607bool TryCopyCustomTypeField (const Field *from, Field *to) {
604608 assert (from->has_type_context ());
605609
606- // If target doesn't have a custom type, this is an incompatible conversion.
607- if (!to->has_type_context ()) {
608- // TODO(villagesql-performance): evaluate something more performant
610+ // If target doesn't have a custom type, or custom types do not match,
611+ // this is an incompatible conversion.
612+ if (!to->has_type_context () ||
613+ !AreTypesCompatible (*from->get_type_context (), *to->get_type_context ())) {
609614 StringBuffer<MAX_FIELD_WIDTH> result (from->charset ());
610615 result.length (0U );
611616 from->val_external_str (&result);
@@ -617,12 +622,7 @@ bool TryCopyCustomTypeField(const Field *from, Field *to) {
617622 MYF (0 ), from->get_type_context ()->type_name ().c_str (),
618623 result.c_ptr_safe (), to->field_name ,
619624 thd->get_stmt_da ()->current_row_for_condition ());
620- return false ; // Error generated, don't fall through
621- }
622-
623- // Check if they're the same custom type
624- if (from->get_type_context () != to->get_type_context ()) {
625- return true ;
625+ return false ;
626626 }
627627
628628 // Both fields have the same custom type. Copy binary data directly.
0 commit comments