Skip to content

Commit d69d8ff

Browse files
refactor error messages and use qualified names for matching
1 parent 2754528 commit d69d8ff

7 files changed

Lines changed: 65 additions & 26 deletions

File tree

mysql-test/suite/villagesql/create_table/r/create_table_complex_functional_index.result

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,12 @@ Table Op Msg_type Msg_text
3535
test.t1 analyze status OK
3636
EXPLAIN SELECT id FROM t1 WHERE COMPLEX_ABS(sig) = CAST(30.0 AS DOUBLE);
3737
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
38-
1 SIMPLE t1 NULL ref idx_magnitude idx_magnitude 9 const 1 100.00 NULL
38+
1 SIMPLE t1 NULL ref idx_magnitude idx_magnitude 9 # # # NULL
3939
Warnings:
4040
Note 1003 /* select#1 */ select `test`.`t1`.`id` AS `id` from `test`.`t1` where (vsql_complex.complex_abs(`sig`) = cast(30.0 as double))
4141
EXPLAIN SELECT id FROM t1 WHERE vsql_complex.COMPLEX_ABS(sig) > CAST(20.0 AS DOUBLE);
4242
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
43-
1 SIMPLE t1 NULL range idx_magnitude idx_magnitude 9 NULL 3 100.00 Using where
43+
1 SIMPLE t1 NULL range idx_magnitude idx_magnitude 9 # # # Using where
4444
Warnings:
4545
Note 1003 /* select#1 */ select `test`.`t1`.`id` AS `id` from `test`.`t1` where (vsql_complex.complex_abs(`sig`) > <cache>(cast(20.0 as double)))
4646
SELECT id FROM t1 WHERE COMPLEX_ABS(sig) = CAST(30.0 AS DOUBLE);

mysql-test/suite/villagesql/create_table/t/create_table_complex_functional_index.test

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ ANALYZE TABLE t1;
3737
# Test index usage with WHERE clauses on functions
3838
# Triggering usage of functional indexes is hard, the cast to double is needed
3939
# for the optimizer to match and use the index
40+
# rows and filtered are optimizer estimates that vary by platform/run
41+
--replace_column 9 # 10 # 11 #
4042
EXPLAIN SELECT id FROM t1 WHERE COMPLEX_ABS(sig) = CAST(30.0 AS DOUBLE);
43+
--replace_column 9 # 10 # 11 #
4144
EXPLAIN SELECT id FROM t1 WHERE vsql_complex.COMPLEX_ABS(sig) > CAST(20.0 AS DOUBLE);
4245

4346
SELECT id FROM t1 WHERE COMPLEX_ABS(sig) = CAST(30.0 AS DOUBLE);

mysql-test/suite/villagesql/select/r/select_function_complex.result

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ conjugate_imag
2525
-2.71828
2626
-4
2727
SELECT vsql_complex.complex_real(complex2_col) AS real_part FROM t1 ORDER BY complex2_col;
28-
ERROR HY000: Cannot initialize function 'complex_real': argument 1 type mismatch (expected COMPLEX, got COMPLEX2)
28+
ERROR HY000: Cannot initialize function 'complex_real': argument 1 type mismatch (expected vsql_complex.COMPLEX, got vsql_complex.COMPLEX2)
2929
SELECT vsql_complex.complex_real(str_col) AS real_part FROM t1 ORDER BY str_col;
3030
ERROR HY000: Cannot initialize function 'complex_real': argument 1 must be a custom type or string constant
3131
SELECT vsql_complex.complex_real(int_col) AS real_part FROM t1 ORDER BY int_col;

sql/field.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1968,6 +1968,9 @@ class Create_field_wrapper final : public Field {
19681968
return new (mem_root) Create_field_wrapper(*this);
19691969
}
19701970
bool is_wrapper_field() const final { return true; }
1971+
// VillageSQL: used to check custom_type_context during functional index
1972+
// validation before the table exists.
1973+
const Create_field *get_create_field() const { return m_field; }
19711974
/* purecov: end */
19721975
};
19731976

villagesql/schema/descriptor/type_context.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
namespace villagesql {
3030

3131
void TypeContext::resolve_cached_values() {
32+
// Build qualified_base_name_ once: "ext.type" (no parameters)
33+
qualified_base_name_ = descriptor_->qualified_base_name();
34+
3235
// Build qualified_name_ once: "ext.type" or "ext.type(v1,v2,...)"
3336
// TODO(villagesql): This needs to be updated to support both TYPE(N) and
3437
// TYPE('k1=v1,k2=v2,...') syntax, and to preserve parameter order as defined

villagesql/schema/descriptor/type_context.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,10 @@ class TypeContext {
229229
// Returns "extension_name.type_name" or "extension_name.type_name(v1,v2,...)"
230230
// when parameters are present (e.g. "vsql_tvector.TVECTOR(3)").
231231
const std::string &qualified_name() const { return qualified_name_; }
232+
// Returns "extension_name.type_name" without parameters
233+
const std::string &qualified_base_name() const {
234+
return qualified_base_name_;
235+
}
232236

233237
// Storage characteristics for this type instantiation.
234238
// For fixed-length types, these are copied from the TypeDescriptor.
@@ -250,6 +254,7 @@ class TypeContext {
250254

251255
// Cached values (computed eagerly in resolve_cached_values())
252256
std::string qualified_name_;
257+
std::string qualified_base_name_;
253258
int64_t persisted_length_{0};
254259
int64_t max_decode_buffer_length_{0};
255260
};

villagesql/types/util.cc

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)