Skip to content

Commit 6066604

Browse files
rebased, refactored and tvector fixes
1 parent c2d4ecb commit 6066604

6 files changed

Lines changed: 164 additions & 51 deletions

File tree

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
INSTALL EXTENSION vsql_tvector;
2+
CREATE TABLE t1 (
3+
id INT PRIMARY KEY,
4+
vec3 TVECTOR(3)
5+
);
6+
INSERT INTO t1 VALUES (1, '[1.0,2.0,3.0]');
7+
INSERT INTO t1 VALUES (2, '[4.5,5.5,6.5]');
8+
INSERT INTO t1 VALUES (3, NULL);
9+
SHOW CREATE TABLE t1;
10+
Table Create Table
11+
t1 CREATE TABLE `t1` (
12+
`id` int NOT NULL,
13+
`vec3` vsql_tvector.TVECTOR DEFAULT NULL,
14+
PRIMARY KEY (`id`)
15+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
16+
SELECT * FROM t1 ORDER BY id;
17+
id vec3
18+
1 [1,2,3]
19+
2 [4.5,5.5,6.5]
20+
3 NULL
21+
ALTER TABLE t1 CHANGE vec3 vec4 TVECTOR(4);
22+
ERROR HY000: Cannot convert between incompatible custom types 'vsql_tvector.TVECTOR(3)' and 'vsql_tvector.TVECTOR(4)'
23+
ALTER TABLE t1 CHANGE vec3 vec3_2 TVECTOR(3);
24+
SHOW CREATE TABLE t1;
25+
Table Create Table
26+
t1 CREATE TABLE `t1` (
27+
`id` int NOT NULL,
28+
`vec3_2` vsql_tvector.TVECTOR DEFAULT NULL,
29+
PRIMARY KEY (`id`)
30+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
31+
SELECT * FROM t1 ORDER BY id;
32+
id vec3_2
33+
1 [1,2,3]
34+
2 [4.5,5.5,6.5]
35+
3 NULL
36+
ALTER TABLE t1 CHANGE vec3_2 vec3_str VARCHAR(100);
37+
SHOW CREATE TABLE t1;
38+
Table Create Table
39+
t1 CREATE TABLE `t1` (
40+
`id` int NOT NULL,
41+
`vec3_str` varchar(100) DEFAULT NULL,
42+
PRIMARY KEY (`id`)
43+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
44+
SELECT * FROM t1 ORDER BY id;
45+
id vec3_str
46+
1 [1,2,3]
47+
2 [4.5,5.5,6.5]
48+
3 NULL
49+
DROP TABLE t1;
50+
CREATE TABLE t2 (
51+
id INT PRIMARY KEY,
52+
vec2 TVECTOR(2),
53+
vec3 TVECTOR(3)
54+
);
55+
INSERT INTO t2 VALUES (1, '[-1.0,2.5]', '[1.0,2.0,3.0]');
56+
INSERT INTO t2 VALUES (2, '[4.0,-5.0]', '[4.5,5.5,6.5]');
57+
INSERT INTO t2 VALUES (3, NULL, NULL);
58+
SELECT * FROM t2 WHERE vec2 = vec3;
59+
ERROR HY000: Cannot compare types vsql_tvector.TVECTOR(2) and vsql_tvector.TVECTOR(3) in =
60+
DROP TABLE t2;
61+
UNINSTALL EXTENSION vsql_tvector;
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Test ALTER TABLE CHANGE on existing TVECTOR column
2+
# Tests copy between TVECTOR(N) columns with same and different prefix sizes,
3+
# and conversion to string.
4+
5+
INSTALL EXTENSION vsql_tvector;
6+
7+
CREATE TABLE t1 (
8+
id INT PRIMARY KEY,
9+
vec3 TVECTOR(3)
10+
);
11+
12+
INSERT INTO t1 VALUES (1, '[1.0,2.0,3.0]');
13+
INSERT INTO t1 VALUES (2, '[4.5,5.5,6.5]');
14+
INSERT INTO t1 VALUES (3, NULL);
15+
16+
SHOW CREATE TABLE t1;
17+
SELECT * FROM t1 ORDER BY id;
18+
19+
--error ER_VILLAGESQL_GENERIC_ERROR
20+
ALTER TABLE t1 CHANGE vec3 vec4 TVECTOR(4);
21+
22+
ALTER TABLE t1 CHANGE vec3 vec3_2 TVECTOR(3);
23+
24+
SHOW CREATE TABLE t1;
25+
SELECT * FROM t1 ORDER BY id;
26+
27+
ALTER TABLE t1 CHANGE vec3_2 vec3_str VARCHAR(100);
28+
29+
SHOW CREATE TABLE t1;
30+
SELECT * FROM t1 ORDER BY id;
31+
32+
DROP TABLE t1;
33+
34+
CREATE TABLE t2 (
35+
id INT PRIMARY KEY,
36+
vec2 TVECTOR(2),
37+
vec3 TVECTOR(3)
38+
);
39+
40+
INSERT INTO t2 VALUES (1, '[-1.0,2.5]', '[1.0,2.0,3.0]');
41+
INSERT INTO t2 VALUES (2, '[4.0,-5.0]', '[4.5,5.5,6.5]');
42+
INSERT INTO t2 VALUES (3, NULL, NULL);
43+
44+
--error ER_VILLAGESQL_GENERIC_ERROR
45+
SELECT * FROM t2 WHERE vec2 = vec3;
46+
47+
DROP TABLE t2;
48+
49+
UNINSTALL EXTENSION vsql_tvector;

sql/field_conv.cc

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -324,17 +324,6 @@ static void do_field_string(Copy_field *, const Field *from_field,
324324
to_field->store(res.ptr(), res.length(), res.charset());
325325
}
326326

327-
// VillageSQL: Copy from a custom type field to custom type field.
328-
static void do_field_custom_to_custom(Copy_field *, const Field *from_field,
329-
Field *to_field) {
330-
villagesql::CopyCustomToCustomField(from_field, to_field);
331-
}
332-
333-
// VillageSQL: Copy from a custom type field to string field.
334-
static void do_field_custom_to_string(Copy_field *, const Field *from_field,
335-
Field *to_field) {
336-
villagesql::CopyCustomToStringField(from_field, to_field);
337-
}
338327

339328
static void do_field_enum(Copy_field *copy, const Field *from_field,
340329
Field *to_field) {
@@ -578,11 +567,7 @@ Copy_field::Copy_func *Copy_field::get_copy_func() {
578567

579568
// VillageSQL: Route to the appropriate custom type copy function.
580569
if (m_from_field->has_type_context()) {
581-
if (m_to_field->has_type_context()) {
582-
return do_field_custom_to_custom;
583-
} else {
584-
return do_field_custom_to_string;
585-
}
570+
return villagesql::GetCopyFunc(m_from_field, m_to_field);
586571
}
587572

588573
const bool compatible_db_low_byte_first =

villagesql/sql/metadata_modifier.cc

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ bool Metadata_modifier::alter_columns(THD *thd [[maybe_unused]],
300300
ColumnEntry new_entry(
301301
ColumnKey(db_name, table_name, alter->m_new_name),
302302
old_entry_ptr->extension_name, old_entry_ptr->extension_version,
303-
old_entry_ptr->type_name);
303+
old_entry_ptr->type_name, old_entry_ptr->type_parameters);
304304

305305
to_rename_.emplace_back(new_entry, old_entry_ptr->key());
306306
}
@@ -361,7 +361,8 @@ bool Metadata_modifier::alter_columns(THD *thd [[maybe_unused]],
361361
to_add_.emplace_back(ColumnKey(db_name, table_name, field.field_name),
362362
field.custom_type_context->extension_name(),
363363
field.custom_type_context->extension_version(),
364-
field.custom_type_context->type_name());
364+
field.custom_type_context->type_name(),
365+
field.custom_type_context->parameters().to_json());
365366
} else if (was_custom_type && is_custom_type) {
366367
// Changing FROM custom TO custom - use delete-then-insert pattern
367368
// Note: Apply removals before additions
@@ -371,14 +372,16 @@ bool Metadata_modifier::alter_columns(THD *thd [[maybe_unused]],
371372
to_add_.emplace_back(ColumnKey(db_name, table_name, field.field_name),
372373
field.custom_type_context->extension_name(),
373374
field.custom_type_context->extension_version(),
374-
field.custom_type_context->type_name());
375+
field.custom_type_context->type_name(),
376+
field.custom_type_context->parameters().to_json());
375377
}
376378
} else if (is_custom_type) {
377379
// This is ADD COLUMN with custom type - insert entry
378380
to_add_.emplace_back(ColumnKey(db_name, table_name, field.field_name),
379381
field.custom_type_context->extension_name(),
380382
field.custom_type_context->extension_version(),
381-
field.custom_type_context->type_name());
383+
field.custom_type_context->type_name(),
384+
field.custom_type_context->parameters().to_json());
382385
}
383386
}
384387
return false;

villagesql/types/util.cc

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,7 @@ bool MaybeInjectCustomType(THD *thd, TABLE_SHARE &share, Field *field) {
132132
// persisted_length. A mismatch here would mean InnoDB allocated a different
133133
// amount of storage than the type expects, which leads to silent data
134134
// corruption.
135-
if (should_assert_if_false(static_cast<int64_t>(field->field_length) ==
136-
tc->persisted_length())) {
135+
if (static_cast<int64_t>(field->field_length) != tc->persisted_length()) {
137136
LogVSQL(ERROR_LEVEL,
138137
"field_length (%u) != persisted_length (%" PRId64
139138
") for column %s in table %s.%s (type %s)",
@@ -604,30 +603,6 @@ bool ValidateAndReportCustomFieldStore(const Item *item, const Field *field) {
604603
return true; // Error
605604
}
606605

607-
bool TryCopyCustomTypeField(const Field *from, Field *to) {
608-
assert(from->has_type_context());
609-
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())) {
614-
StringBuffer<MAX_FIELD_WIDTH> result(from->charset());
615-
result.length(0U);
616-
from->val_external_str(&result);
617-
618-
THD *thd = current_thd;
619-
villagesql_error(
620-
"Type mismatch. Use explicit conversion of %s value '%s' for column "
621-
"'%s' at row %ld",
622-
MYF(0), from->get_type_context()->type_name().c_str(),
623-
result.c_ptr_safe(), to->field_name,
624-
thd->get_stmt_da()->current_row_for_condition());
625-
return false;
626-
}
627-
CopyCustomToCustomField(from, to);
628-
return false;
629-
}
630-
631606
void CopyCustomToCustomField(const Field *from, Field *to) {
632607
// Both fields have the same custom type. Copy binary data directly.
633608
// Handle potential length_bytes differences (VARCHAR(255) vs VARCHAR(65535)).
@@ -654,7 +629,13 @@ void CopyCustomToCustomField(const Field *from, Field *to) {
654629
memcpy(to_ptr + to_length_bytes, from_data, data_len);
655630
}
656631

657-
void CopyCustomToStringField(const Field *from, Field *to) {
632+
static void do_field_custom_to_custom(Copy_field *, const Field *from,
633+
Field *to) {
634+
CopyCustomToCustomField(from, to);
635+
}
636+
637+
static void do_field_custom_to_string(Copy_field *, const Field *from,
638+
Field *to) {
658639
assert(from->has_type_context());
659640
// Custom → non-custom string: decode to string representation.
660641
// NULL is handled outside this function
@@ -665,6 +646,38 @@ void CopyCustomToStringField(const Field *from, Field *to) {
665646
to->store(res.ptr(), res.length(), res.charset());
666647
}
667648

649+
FieldCopyFunc *GetCopyFunc(const Field *from, const Field *to) {
650+
assert(from->has_type_context());
651+
if (to->has_type_context()) {
652+
return do_field_custom_to_custom;
653+
}
654+
return do_field_custom_to_string;
655+
}
656+
657+
bool TryCopyCustomTypeField(const Field *from, Field *to) {
658+
assert(from->has_type_context());
659+
660+
// If target doesn't have a custom type, or custom types do not match,
661+
// this is an incompatible conversion.
662+
if (!to->has_type_context() ||
663+
!AreTypesCompatible(*from->get_type_context(), *to->get_type_context())) {
664+
StringBuffer<MAX_FIELD_WIDTH> result(from->charset());
665+
result.length(0U);
666+
from->val_external_str(&result);
667+
668+
THD *thd = current_thd;
669+
villagesql_error(
670+
"Type mismatch. Use explicit conversion of %s value '%s' for column "
671+
"'%s' at row %ld",
672+
MYF(0), from->get_type_context()->type_name().c_str(),
673+
result.c_ptr_safe(), to->field_name,
674+
thd->get_stmt_da()->current_row_for_condition());
675+
return false;
676+
}
677+
CopyCustomToCustomField(from, to);
678+
return false;
679+
}
680+
668681
type_conversion_status TryEncodeStringFieldToCustom(Field *from_field,
669682
Field *to_field) {
670683
assert(!from_field->has_type_context());

villagesql/types/util.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -287,11 +287,13 @@ extern bool ValidateAndReportCustomFieldStore(const Item *item,
287287
// If to does not have a custom type, generates an error with readable format.
288288
extern bool TryCopyCustomTypeField(const Field *from, Field *to);
289289

290-
// Binary copy from a custom type field to custom type field.
291-
extern void CopyCustomToCustomField(const Field *from, Field *to);
290+
// Function pointer type for field copy operations (matches
291+
// Copy_field::Copy_func).
292+
using FieldCopyFunc = void(Copy_field *, const Field *, Field *);
292293

293-
// Copy from a custom type field to string type field.
294-
extern void CopyCustomToStringField(const Field *from, Field *to);
294+
// Returns the appropriate copy function for copying from a custom type field.
295+
// from must have a custom type context.
296+
extern FieldCopyFunc *GetCopyFunc(const Field *from, const Field *to);
295297

296298
// Encode a string field value and store it in a custom type field.
297299
// This enables CTEs and subqueries with string values to work with custom

0 commit comments

Comments
 (0)