Skip to content

Commit f9607e8

Browse files
support for functional index (#63)
1 parent 6ae27ca commit f9607e8

11 files changed

Lines changed: 251 additions & 39 deletions
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
CREATE TABLE t1 (
2+
id INT PRIMARY KEY,
3+
sig COMPLEX,
4+
INDEX idx_magnitude ((COMPLEX_ABS(sig)))
5+
);
6+
SHOW CREATE TABLE t1;
7+
Table Create Table
8+
t1 CREATE TABLE `t1` (
9+
`id` int NOT NULL,
10+
`sig` vsql_complex.COMPLEX DEFAULT NULL,
11+
PRIMARY KEY (`id`),
12+
KEY `idx_magnitude` ((vsql_complex.complex_abs(`sig`)))
13+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
14+
SELECT TABLE_NAME, INDEX_NAME, COLUMN_NAME, EXPRESSION
15+
FROM INFORMATION_SCHEMA.STATISTICS
16+
WHERE TABLE_NAME = 't1' AND INDEX_NAME = 'idx_magnitude';
17+
TABLE_NAME INDEX_NAME COLUMN_NAME EXPRESSION
18+
t1 idx_magnitude NULL vsql_complex.complex_abs(`sig`)
19+
INSERT INTO t1 (id, sig)
20+
SELECT
21+
row_number() OVER () as id,
22+
COMPLEX_FROM_STRING(CONCAT('(', (RAND() * 20 - 10), ',', (RAND() * 20 - 10), ')'))
23+
FROM
24+
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10) d1,
25+
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10) d2,
26+
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10) d3;
27+
SELECT COUNT(*) FROM t1;
28+
COUNT(*)
29+
1000
30+
INSERT INTO t1 VALUES(100001, '(0,30)');
31+
INSERT INTO t1 VALUES(100002, '(-35,1)');
32+
INSERT INTO t1 VALUES(100003, '(-20,-1)');
33+
ANALYZE TABLE t1;
34+
Table Op Msg_type Msg_text
35+
test.t1 analyze status OK
36+
EXPLAIN SELECT id FROM t1 WHERE COMPLEX_ABS(sig) = CAST(30.0 AS DOUBLE);
37+
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 # # # NULL
39+
Warnings:
40+
Note 1003 /* select#1 */ select `test`.`t1`.`id` AS `id` from `test`.`t1` where (vsql_complex.complex_abs(`sig`) = cast(30.0 as double))
41+
EXPLAIN SELECT id FROM t1 WHERE vsql_complex.COMPLEX_ABS(sig) > CAST(20.0 AS DOUBLE);
42+
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 # # # Using where
44+
Warnings:
45+
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)))
46+
SELECT id FROM t1 WHERE COMPLEX_ABS(sig) = CAST(30.0 AS DOUBLE);
47+
id
48+
100001
49+
SELECT id FROM t1 WHERE vsql_complex.COMPLEX_ABS(sig) > CAST(20.0 AS DOUBLE) ORDER BY id;
50+
id
51+
100001
52+
100002
53+
100003
54+
DROP TABLE t1;
55+
include/rpl/deprecated/show_binlog_events.inc
56+
Log_name Pos Event_type Server_id End_log_pos Info
57+
# # Query # # use `test`; CREATE TABLE `t1` (
58+
`id` int NOT NULL,
59+
`sig` vsql_complex.COMPLEX DEFAULT NULL,
60+
PRIMARY KEY (`id`),
61+
KEY `idx_magnitude` ((vsql_complex.complex_abs(`sig`)))
62+
)
63+
# # Query # # BEGIN
64+
# # Table_map # # table_id: # (test.t1)
65+
# # Write_rows # # table_id: #
66+
# # Write_rows # # table_id: #
67+
# # Write_rows # # table_id: # flags: STMT_END_F
68+
# # Xid # # COMMIT /* XID */
69+
# # Query # # BEGIN
70+
# # Table_map # # table_id: # (test.t1)
71+
# # Write_rows # # table_id: # flags: STMT_END_F
72+
# # Xid # # COMMIT /* XID */
73+
# # Query # # BEGIN
74+
# # Table_map # # table_id: # (test.t1)
75+
# # Write_rows # # table_id: # flags: STMT_END_F
76+
# # Xid # # COMMIT /* XID */
77+
# # Query # # BEGIN
78+
# # Table_map # # table_id: # (test.t1)
79+
# # Write_rows # # table_id: # flags: STMT_END_F
80+
# # Xid # # COMMIT /* XID */
81+
# # Query # # use `test`; ANALYZE TABLE t1
82+
# # Query # # use `test`; DROP TABLE `t1` /* generated by server */
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
CREATE TABLE t1 (
2+
id INT PRIMARY KEY,
3+
sig COMPLEX2,
4+
INDEX idx_real ((COMPLEX_REAL(sig)))
5+
);
6+
ERROR HY000: Cannot initialize function 'complex_real': argument 1 type mismatch (expected vsql_complex.COMPLEX, got vsql_complex.COMPLEX2)
7+
SHOW CREATE TABLE t1;
8+
ERROR 42S02: Table 'test.t1' doesn't exist
9+
CREATE TABLE t2 (
10+
id INT PRIMARY KEY,
11+
sig VARCHAR(100),
12+
INDEX idx_real ((COMPLEX_REAL(sig)))
13+
);
14+
ERROR HY000: Cannot initialize function 'complex_real': argument 1 must be a custom type or string constant
15+
SHOW CREATE TABLE t2;
16+
ERROR 42S02: Table 'test.t2' doesn't exist
17+
CREATE TABLE t3 (
18+
id INT PRIMARY KEY,
19+
sig INT,
20+
INDEX idx_real ((COMPLEX_REAL(sig)))
21+
);
22+
ERROR HY000: Cannot initialize function 'complex_real': argument 1 must be a custom type or string constant
23+
SHOW CREATE TABLE t3;
24+
ERROR 42S02: Table 'test.t3' doesn't exist

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

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,51 @@
11
# Test CREATE TABLE with functional indexes on COMPLEX functions
22
# Tests indexing computed COMPLEX values
33

4-
--source include/villagesql/not_implemented_complex_type.inc
5-
64
--source include/villagesql/install_complex_extension.inc
75
--source include/villagesql/binlog_check_begin.inc
86

97
CREATE TABLE t1 (
108
id INT PRIMARY KEY,
11-
signal COMPLEX,
12-
INDEX idx_real_part ((REAL(signal))),
13-
INDEX idx_magnitude ((ABS(signal))),
14-
INDEX idx_phase ((ARG(signal)))
9+
sig COMPLEX,
10+
INDEX idx_magnitude ((COMPLEX_ABS(sig)))
1511
);
1612

1713
SHOW CREATE TABLE t1;
18-
19-
INSERT INTO t1 VALUES (1, '(3.0,4.0)');
20-
INSERT INTO t1 VALUES (2, '(1.0,1.0)');
21-
INSERT INTO t1 VALUES (3, '(0.0,5.0)');
22-
23-
SELECT * FROM t1;
14+
SELECT TABLE_NAME, INDEX_NAME, COLUMN_NAME, EXPRESSION
15+
FROM INFORMATION_SCHEMA.STATISTICS
16+
WHERE TABLE_NAME = 't1' AND INDEX_NAME = 'idx_magnitude';
17+
18+
# Create a 1000 row dataset to trigger index usage, where we make sure the complex_abs is less than 20
19+
INSERT INTO t1 (id, sig)
20+
SELECT
21+
row_number() OVER () as id,
22+
COMPLEX_FROM_STRING(CONCAT('(', (RAND() * 20 - 10), ',', (RAND() * 20 - 10), ')'))
23+
FROM
24+
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10) d1,
25+
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10) d2,
26+
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10) d3;
27+
28+
SELECT COUNT(*) FROM t1;
29+
30+
# Make sure we 3 values with complex abs > 20, one with exactly 30 for select below
31+
INSERT INTO t1 VALUES(100001, '(0,30)');
32+
INSERT INTO t1 VALUES(100002, '(-35,1)');
33+
INSERT INTO t1 VALUES(100003, '(-20,-1)');
34+
35+
# Make sure statistics is updated for the EXPLAIN
36+
ANALYZE TABLE t1;
2437

2538
# Test index usage with WHERE clauses on functions
26-
EXPLAIN SELECT * FROM t1 WHERE REAL(signal) > 1.0;
27-
EXPLAIN SELECT * FROM t1 WHERE ABS(signal) < 10.0;
28-
29-
SELECT * FROM t1 WHERE REAL(signal) > 1.0;
30-
SELECT * FROM t1 WHERE ABS(signal) BETWEEN 1.0 AND 5.0;
39+
# Triggering usage of functional indexes is hard, the cast to double is needed
40+
# for the optimizer to match and use the index
41+
# rows and filtered are optimizer estimates that vary by platform/run
42+
--replace_column 9 # 10 # 11 #
43+
EXPLAIN SELECT id FROM t1 WHERE COMPLEX_ABS(sig) = CAST(30.0 AS DOUBLE);
44+
--replace_column 9 # 10 # 11 #
45+
EXPLAIN SELECT id FROM t1 WHERE vsql_complex.COMPLEX_ABS(sig) > CAST(20.0 AS DOUBLE);
46+
47+
SELECT id FROM t1 WHERE COMPLEX_ABS(sig) = CAST(30.0 AS DOUBLE);
48+
SELECT id FROM t1 WHERE vsql_complex.COMPLEX_ABS(sig) > CAST(20.0 AS DOUBLE) ORDER BY id;
3149

3250
DROP TABLE t1;
3351

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Test CREATE TABLE functional index errors with COMPLEX type functions
2+
# Verifies that invalid column types in functional indexes are rejected
3+
4+
--source include/villagesql/install_complex_extension.inc
5+
6+
# Test 1: Functional index on a wrong custom type column (COMPLEX2 instead of COMPLEX)
7+
# COMPLEX_REAL expects a COMPLEX argument, not COMPLEX2 - caught at CREATE TABLE time
8+
--error ER_VILLAGESQL_GENERIC_ERROR
9+
CREATE TABLE t1 (
10+
id INT PRIMARY KEY,
11+
sig COMPLEX2,
12+
INDEX idx_real ((COMPLEX_REAL(sig)))
13+
);
14+
15+
--error ER_NO_SUCH_TABLE
16+
SHOW CREATE TABLE t1;
17+
18+
# Test 2: Functional index on a plain VARCHAR column
19+
# COMPLEX_REAL expects a COMPLEX argument, not a plain string column
20+
--error ER_VILLAGESQL_GENERIC_ERROR
21+
CREATE TABLE t2 (
22+
id INT PRIMARY KEY,
23+
sig VARCHAR(100),
24+
INDEX idx_real ((COMPLEX_REAL(sig)))
25+
);
26+
27+
--error ER_NO_SUCH_TABLE
28+
SHOW CREATE TABLE t2;
29+
30+
# Test 3: Functional index on a plain INT column
31+
# COMPLEX_REAL expects a COMPLEX argument, not an integer column
32+
--error ER_VILLAGESQL_GENERIC_ERROR
33+
CREATE TABLE t3 (
34+
id INT PRIMARY KEY,
35+
sig INT,
36+
INDEX idx_real ((COMPLEX_REAL(sig)))
37+
);
38+
39+
--error ER_NO_SUCH_TABLE
40+
SHOW CREATE TABLE t3;
41+
42+
--source include/villagesql/uninstall_complex_extension.inc

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/schema/descriptor/type_descriptor.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ class TypeDescriptor {
156156
// Does not include parameters. Use TypeContext::qualified_name() for the
157157
// full name including parameters (e.g. "vsql_tvector.TVECTOR(3)").
158158
std::string qualified_base_name() const {
159-
return extension_name() + "." + type_name();
159+
return make_qualified_base_name(extension_name(), type_name());
160160
}
161161

162162
// Type implementation details

villagesql/schema/systable/helpers.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ std::string normalize_extension_name(const std::string &name);
4949
// Type names: Always case-insensitive (like SQL type names)
5050
std::string normalize_type_name(const std::string &name);
5151

52+
// Build a qualified base name string "extension_name.type_name".
53+
inline std::string make_qualified_base_name(const std::string &extension_name,
54+
const std::string &type_name) {
55+
return extension_name + "." + type_name;
56+
}
57+
5258
// Helper functions for reading a value from a Field.
5359
void read_string_field(Field *f, std::string &out);
5460
void read_unsigned_field(Field *f, unsigned int &out);

0 commit comments

Comments
 (0)