Skip to content

Commit 3de0b3f

Browse files
committed
benchmark
1 parent 34176ee commit 3de0b3f

File tree

2 files changed

+246
-1
lines changed

2 files changed

+246
-1
lines changed

README.md

+62-1
Original file line numberDiff line numberDiff line change
@@ -144,4 +144,65 @@ This project is licensed under the Apache License - see the [LICENSE](LICENSE) f
144144

145145
## Support
146146

147-
For support and questions, please open an issue in the GitHub repository.
147+
For support and questions, please open an issue in the GitHub repository.
148+
149+
## Benchmark Results
150+
151+
The following benchmark results show the performance of SQLMapper for different database conversion scenarios. Tests were performed on Apple M1 processor.
152+
153+
```
154+
goos: darwin
155+
goarch: arm64
156+
cpu: Apple M1
157+
158+
BenchmarkMySQLToPostgreSQL-8 2130 2500152 ns/op 6943466 B/op 16046 allocs/op
159+
BenchmarkMySQLToSQLite-8 2101 2135750 ns/op 1656745 B/op 4274 allocs/op
160+
BenchmarkPostgreSQLToMySQL-8 1783 6188440 ns/op 8975810 B/op 138055 allocs/op
161+
BenchmarkSQLiteToMySQL-8 185398 6009 ns/op 9131 B/op 115 allocs/op
162+
```
163+
164+
### Interpretation of Results
165+
166+
1. **MySQL to PostgreSQL**
167+
- Operations per second: ~2,130
168+
- Average time per operation: ~2.50ms
169+
- Memory allocation: ~6.9MB per operation
170+
- Number of allocations: 16,046 per operation
171+
172+
2. **MySQL to SQLite**
173+
- Operations per second: ~2,101
174+
- Average time per operation: ~2.14ms
175+
- Memory allocation: ~1.7MB per operation
176+
- Number of allocations: 4,274 per operation
177+
178+
3. **PostgreSQL to MySQL**
179+
- Operations per second: ~1,783
180+
- Average time per operation: ~6.19ms
181+
- Memory allocation: ~9.0MB per operation
182+
- Number of allocations: 138,055 per operation
183+
184+
4. **SQLite to MySQL** (with simplified schema)
185+
- Operations per second: ~185,398
186+
- Average time per operation: ~0.006ms
187+
- Memory allocation: ~9KB per operation
188+
- Number of allocations: 115 per operation
189+
190+
### Test Scenarios
191+
192+
1. **Complex Schema Tests** (MySQL to PostgreSQL/SQLite, PostgreSQL to MySQL):
193+
- Multiple tables with various column types
194+
- Foreign key constraints
195+
- Indexes (including fulltext)
196+
- Views
197+
- Triggers
198+
- JSON data type support
199+
- ENUM types
200+
- Timestamp auto-update features
201+
202+
2. **Simple Schema Test** (SQLite to MySQL):
203+
- Basic tables with common data types
204+
- Simple constraints
205+
- Basic view
206+
- No triggers or complex features
207+
208+
Note: The SQLite to MySQL test uses a simplified schema due to SQLite's limited support for complex database features.

benchmark/benchmark_test.go

+184
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
package benchmark
2+
3+
import (
4+
"testing"
5+
6+
"github.com/mstgnz/sqlmapper/mysql"
7+
"github.com/mstgnz/sqlmapper/postgres"
8+
"github.com/mstgnz/sqlmapper/sqlite"
9+
)
10+
11+
var complexMySQLSchema = `
12+
CREATE TABLE users (
13+
id INT AUTO_INCREMENT PRIMARY KEY,
14+
name VARCHAR(100) NOT NULL,
15+
email VARCHAR(255) UNIQUE,
16+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
17+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
18+
);
19+
20+
CREATE TABLE categories (
21+
id INT AUTO_INCREMENT PRIMARY KEY,
22+
name VARCHAR(100) NOT NULL,
23+
description TEXT,
24+
parent_id INT,
25+
FOREIGN KEY (parent_id) REFERENCES categories(id)
26+
);
27+
28+
CREATE TABLE products (
29+
id INT AUTO_INCREMENT PRIMARY KEY,
30+
category_id INT NOT NULL,
31+
name VARCHAR(200) NOT NULL,
32+
description TEXT,
33+
price DECIMAL(10,2) NOT NULL,
34+
stock INT DEFAULT 0,
35+
status ENUM('active', 'inactive', 'deleted') DEFAULT 'active',
36+
metadata JSON,
37+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
38+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
39+
FOREIGN KEY (category_id) REFERENCES categories(id)
40+
);
41+
42+
CREATE TABLE orders (
43+
id INT AUTO_INCREMENT PRIMARY KEY,
44+
user_id INT NOT NULL,
45+
total_amount DECIMAL(12,2) NOT NULL,
46+
status VARCHAR(50) DEFAULT 'pending',
47+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
48+
FOREIGN KEY (user_id) REFERENCES users(id)
49+
);
50+
51+
CREATE TABLE order_items (
52+
id INT AUTO_INCREMENT PRIMARY KEY,
53+
order_id INT NOT NULL,
54+
product_id INT NOT NULL,
55+
quantity INT NOT NULL,
56+
price DECIMAL(10,2) NOT NULL,
57+
FOREIGN KEY (order_id) REFERENCES orders(id),
58+
FOREIGN KEY (product_id) REFERENCES products(id)
59+
);
60+
61+
CREATE INDEX idx_user_email ON users(email);
62+
CREATE INDEX idx_category_parent ON categories(parent_id);
63+
CREATE INDEX idx_product_category ON products(category_id);
64+
CREATE INDEX idx_product_status ON products(status);
65+
CREATE FULLTEXT INDEX idx_product_search ON products(name, description);
66+
67+
CREATE VIEW active_products AS
68+
SELECT p.*, c.name as category_name
69+
FROM products p
70+
JOIN categories c ON p.category_id = c.id
71+
WHERE p.status = 'active';
72+
73+
DELIMITER //
74+
CREATE TRIGGER update_product_timestamp
75+
BEFORE UPDATE ON products
76+
FOR EACH ROW
77+
BEGIN
78+
SET NEW.updated_at = CURRENT_TIMESTAMP;
79+
END//
80+
DELIMITER ;
81+
`
82+
83+
func BenchmarkMySQLToPostgreSQL(b *testing.B) {
84+
mysqlParser := mysql.NewMySQL()
85+
pgParser := postgres.NewPostgreSQL()
86+
87+
b.ResetTimer()
88+
for i := 0; i < b.N; i++ {
89+
schema, err := mysqlParser.Parse(complexMySQLSchema)
90+
if err != nil {
91+
b.Fatal(err)
92+
}
93+
94+
_, err = pgParser.Generate(schema)
95+
if err != nil {
96+
b.Fatal(err)
97+
}
98+
}
99+
}
100+
101+
func BenchmarkMySQLToSQLite(b *testing.B) {
102+
mysqlParser := mysql.NewMySQL()
103+
sqliteParser := sqlite.NewSQLite()
104+
105+
b.ResetTimer()
106+
for i := 0; i < b.N; i++ {
107+
schema, err := mysqlParser.Parse(complexMySQLSchema)
108+
if err != nil {
109+
b.Fatal(err)
110+
}
111+
112+
_, err = sqliteParser.Generate(schema)
113+
if err != nil {
114+
b.Fatal(err)
115+
}
116+
}
117+
}
118+
119+
func BenchmarkPostgreSQLToMySQL(b *testing.B) {
120+
pgParser := postgres.NewPostgreSQL()
121+
mysqlParser := mysql.NewMySQL()
122+
123+
// Convert MySQL schema to PostgreSQL first
124+
schema, err := mysql.NewMySQL().Parse(complexMySQLSchema)
125+
if err != nil {
126+
b.Fatal(err)
127+
}
128+
129+
pgSQL, err := pgParser.Generate(schema)
130+
if err != nil {
131+
b.Fatal(err)
132+
}
133+
134+
b.ResetTimer()
135+
for i := 0; i < b.N; i++ {
136+
schema, err := pgParser.Parse(pgSQL)
137+
if err != nil {
138+
b.Fatal(err)
139+
}
140+
141+
_, err = mysqlParser.Generate(schema)
142+
if err != nil {
143+
b.Fatal(err)
144+
}
145+
}
146+
}
147+
148+
func BenchmarkSQLiteToMySQL(b *testing.B) {
149+
sqliteParser := sqlite.NewSQLite()
150+
mysqlParser := mysql.NewMySQL()
151+
152+
// Use a simpler schema for SQLite
153+
simpleSQLiteSchema := `
154+
CREATE TABLE users (
155+
id INTEGER PRIMARY KEY AUTOINCREMENT,
156+
name TEXT NOT NULL,
157+
email TEXT UNIQUE,
158+
created_at TEXT DEFAULT CURRENT_TIMESTAMP
159+
);
160+
161+
CREATE TABLE products (
162+
id INTEGER PRIMARY KEY AUTOINCREMENT,
163+
name TEXT NOT NULL,
164+
price REAL NOT NULL,
165+
stock INTEGER DEFAULT 0
166+
);
167+
168+
CREATE VIEW active_products AS
169+
SELECT * FROM products WHERE stock > 0;
170+
`
171+
172+
b.ResetTimer()
173+
for i := 0; i < b.N; i++ {
174+
schema, err := sqliteParser.Parse(simpleSQLiteSchema)
175+
if err != nil {
176+
b.Fatal(err)
177+
}
178+
179+
_, err = mysqlParser.Generate(schema)
180+
if err != nil {
181+
b.Fatal(err)
182+
}
183+
}
184+
}

0 commit comments

Comments
 (0)