Skip to content

Commit 6fc16cd

Browse files
catch attempts to implict cast incompatible custom types
1 parent 8fecb96 commit 6fc16cd

3 files changed

Lines changed: 36 additions & 18 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
CREATE TABLE t_mixed (id INT, complex_val COMPLEX, complex2_val COMPLEX2, int_val INT);
2+
INSERT INTO t_mixed VALUES (1, '(1.0,1.0)', '(2.0,2.0)', 100);
3+
UPDATE t_mixed SET complex2_val = complex_val WHERE id = 1;
4+
ERROR HY000: Cannot implicitly cast from vsql_complex.COMPLEX to vsql_complex.COMPLEX2 for column 'complex2_val' at row 1
5+
DROP TABLE t_mixed;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Test INSERT with COMPLEX value into COMPLEX2 field
2+
3+
--source include/villagesql/install_complex_extension.inc
4+
5+
CREATE TABLE t_mixed (id INT, complex_val COMPLEX, complex2_val COMPLEX2, int_val INT);
6+
7+
INSERT INTO t_mixed VALUES (1, '(1.0,1.0)', '(2.0,2.0)', 100);
8+
--error ER_VILLAGESQL_GENERIC_ERROR
9+
UPDATE t_mixed SET complex2_val = complex_val WHERE id = 1;
10+
11+
DROP TABLE t_mixed;
12+
13+
--source include/villagesql/uninstall_complex_extension.inc

villagesql/types/util.cc

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -367,11 +367,9 @@ int CustomMemCompare(const Item *item, const uchar *data1, size_t len1,
367367
}
368368

369369
bool 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

377375
bool 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) {
587591
bool 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

Comments
 (0)