Skip to content

Commit 052e742

Browse files
authored
fix: support enum columns for mysqlsh copy-instance utility (#350)
* test: add enum column to mysqlsh copy-instance test * test: add UUID column * test: uuid and enum * fix: convert enum ordinal to string for duckdb insert
1 parent 0ba0b75 commit 052e742

File tree

2 files changed

+48
-9
lines changed

2 files changed

+48
-9
lines changed

.github/workflows/mysql-copy-tests.yml

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,22 @@ jobs:
4646
4747
- name: Setup test data in source MySQL
4848
run: |
49-
mysqlsh -hlocalhost -P13306 -uroot -proot --sql -e "
49+
mysqlsh -hlocalhost -P13306 -uroot -proot --sql <<'EOF'
5050
CREATE DATABASE testdb;
5151
USE testdb;
52+
-- Normal table, which should be copied to MyDuck via duckdb's csv import
5253
CREATE TABLE users (
5354
id INT AUTO_INCREMENT PRIMARY KEY,
54-
name VARCHAR(100)
55+
name VARCHAR(100),
56+
status ENUM('active', 'inactive', 'pending') DEFAULT 'pending'
5557
);
56-
INSERT INTO users (name) VALUES ('test1'), ('test2'), ('test3');
58+
INSERT INTO users (name, status) VALUES
59+
('test1', 'active'),
60+
('test2', 'inactive'),
61+
('test3', 'pending');
5762
-- Make a gap in the id sequence
58-
INSERT INTO users VALUES (100, 'test100');
59-
INSERT INTO users (name) VALUES ('test101');
63+
INSERT INTO users VALUES (100, 'test100', 'active');
64+
INSERT INTO users (name, status) VALUES ('test101', 'inactive');
6065
6166
-- A table with non-default starting auto_increment value
6267
CREATE TABLE items (
@@ -66,7 +71,22 @@ jobs:
6671
) AUTO_INCREMENT=1000;
6772
6873
INSERT INTO items (v, name) VALUES (1, 'item1'), (2, 'item2'), (3, 'item3');
69-
"
74+
75+
-- Table with UUID primary key
76+
-- For such tables, MySQL Shell generates nontrivial LOAD DATA statements
77+
-- to copy the data to MyDuck: LOAD DATA ... (@id, title, created_at) SET id = FROM_BASE64(@id),
78+
-- which can only be executed by the go-mysql-server framework for now.
79+
CREATE TABLE documents (
80+
id BINARY(16) PRIMARY KEY,
81+
title VARCHAR(200),
82+
status ENUM('draft', 'published', 'archived') DEFAULT 'draft',
83+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
84+
);
85+
86+
INSERT INTO documents (id, title, status) VALUES
87+
(UUID_TO_BIN(UUID()), 'Document 1', 'published'),
88+
(UUID_TO_BIN(UUID()), 'Document 2', 'draft');
89+
EOF
7090
7191
- name: Build and start MyDuck Server
7292
run: |
@@ -85,7 +105,7 @@ jobs:
85105
--users false --ignore-version true
86106
87107
# Verify the data was copied
88-
for table in users items; do
108+
for table in users items documents; do
89109
mysqlsh -hlocalhost -P13306 -uroot -proot --sql -e "
90110
SELECT * FROM testdb.$table ORDER BY id;
91111
" | tee source_data_$table.tsv
@@ -96,4 +116,4 @@ jobs:
96116
diff source_data_$table.tsv copied_data_$table.tsv
97117
done
98118
99-
119+

catalog/inserter.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ type rowInserter struct {
2424
stmt *stdsql.Stmt
2525
err error
2626
flushSQL string
27+
enums []int
2728
}
2829

2930
var _ sql.RowInserter = &rowInserter{}
@@ -75,9 +76,15 @@ func (ri *rowInserter) init(ctx *sql.Context) {
7576
}
7677
insert.WriteString(" INTO ")
7778
insert.WriteString(ConnectIdentifiersANSI(ri.db, ri.table))
78-
insert.WriteString(" SELECT * FROM ")
79+
insert.WriteString(" FROM ")
7980
insert.WriteString(QuoteIdentifierANSI(ri.tmpTable))
8081
ri.flushSQL = insert.String()
82+
83+
for i, col := range ri.schema {
84+
if _, ok := col.Type.(sql.EnumType); ok {
85+
ri.enums = append(ri.enums, i)
86+
}
87+
}
8188
}
8289

8390
func (ri *rowInserter) StatementBegin(ctx *sql.Context) {
@@ -106,6 +113,18 @@ func (ri *rowInserter) Insert(ctx *sql.Context, row sql.Row) error {
106113
if ri.err != nil {
107114
return ri.err
108115
}
116+
117+
// For enum columns, we have to convert the enum ordinal to the enum string.
118+
for _, i := range ri.enums {
119+
if idx, ok := row[i].(uint16); ok {
120+
if s, ok := ri.schema[i].Type.(sql.EnumType).At(int(idx)); ok {
121+
row[i] = s
122+
} else {
123+
return fmt.Errorf("invalid enum value %d for column %s", idx, ri.schema[i].Name)
124+
}
125+
}
126+
}
127+
109128
if _, err := ri.stmt.ExecContext(ctx, row...); err != nil {
110129
ri.err = err
111130
return err

0 commit comments

Comments
 (0)