-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
384 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
package benchmark | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/mstgnz/sqlmapper/mysql" | ||
"github.com/mstgnz/sqlmapper/postgres" | ||
"github.com/mstgnz/sqlmapper/sqlite" | ||
) | ||
|
||
var complexMySQLSchema = ` | ||
CREATE TABLE users ( | ||
id INT AUTO_INCREMENT PRIMARY KEY, | ||
name VARCHAR(100) NOT NULL, | ||
email VARCHAR(255) UNIQUE, | ||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, | ||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | ||
); | ||
CREATE TABLE categories ( | ||
id INT AUTO_INCREMENT PRIMARY KEY, | ||
name VARCHAR(100) NOT NULL, | ||
description TEXT, | ||
parent_id INT, | ||
FOREIGN KEY (parent_id) REFERENCES categories(id) | ||
); | ||
CREATE TABLE products ( | ||
id INT AUTO_INCREMENT PRIMARY KEY, | ||
category_id INT NOT NULL, | ||
name VARCHAR(200) NOT NULL, | ||
description TEXT, | ||
price DECIMAL(10,2) NOT NULL, | ||
stock INT DEFAULT 0, | ||
status ENUM('active', 'inactive', 'deleted') DEFAULT 'active', | ||
metadata JSON, | ||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, | ||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, | ||
FOREIGN KEY (category_id) REFERENCES categories(id) | ||
); | ||
CREATE TABLE orders ( | ||
id INT AUTO_INCREMENT PRIMARY KEY, | ||
user_id INT NOT NULL, | ||
total_amount DECIMAL(12,2) NOT NULL, | ||
status VARCHAR(50) DEFAULT 'pending', | ||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, | ||
FOREIGN KEY (user_id) REFERENCES users(id) | ||
); | ||
CREATE TABLE order_items ( | ||
id INT AUTO_INCREMENT PRIMARY KEY, | ||
order_id INT NOT NULL, | ||
product_id INT NOT NULL, | ||
quantity INT NOT NULL, | ||
price DECIMAL(10,2) NOT NULL, | ||
FOREIGN KEY (order_id) REFERENCES orders(id), | ||
FOREIGN KEY (product_id) REFERENCES products(id) | ||
); | ||
CREATE INDEX idx_user_email ON users(email); | ||
CREATE INDEX idx_category_parent ON categories(parent_id); | ||
CREATE INDEX idx_product_category ON products(category_id); | ||
CREATE INDEX idx_product_status ON products(status); | ||
CREATE FULLTEXT INDEX idx_product_search ON products(name, description); | ||
CREATE VIEW active_products AS | ||
SELECT p.*, c.name as category_name | ||
FROM products p | ||
JOIN categories c ON p.category_id = c.id | ||
WHERE p.status = 'active'; | ||
DELIMITER // | ||
CREATE TRIGGER update_product_timestamp | ||
BEFORE UPDATE ON products | ||
FOR EACH ROW | ||
BEGIN | ||
SET NEW.updated_at = CURRENT_TIMESTAMP; | ||
END// | ||
DELIMITER ; | ||
` | ||
|
||
func BenchmarkMySQLToPostgreSQL(b *testing.B) { | ||
mysqlParser := mysql.NewMySQL() | ||
pgParser := postgres.NewPostgreSQL() | ||
|
||
b.ResetTimer() | ||
for i := 0; i < b.N; i++ { | ||
schema, err := mysqlParser.Parse(complexMySQLSchema) | ||
if err != nil { | ||
b.Fatal(err) | ||
} | ||
|
||
_, err = pgParser.Generate(schema) | ||
if err != nil { | ||
b.Fatal(err) | ||
} | ||
} | ||
} | ||
|
||
func BenchmarkMySQLToSQLite(b *testing.B) { | ||
mysqlParser := mysql.NewMySQL() | ||
sqliteParser := sqlite.NewSQLite() | ||
|
||
b.ResetTimer() | ||
for i := 0; i < b.N; i++ { | ||
schema, err := mysqlParser.Parse(complexMySQLSchema) | ||
if err != nil { | ||
b.Fatal(err) | ||
} | ||
|
||
_, err = sqliteParser.Generate(schema) | ||
if err != nil { | ||
b.Fatal(err) | ||
} | ||
} | ||
} | ||
|
||
func BenchmarkPostgreSQLToMySQL(b *testing.B) { | ||
pgParser := postgres.NewPostgreSQL() | ||
mysqlParser := mysql.NewMySQL() | ||
|
||
// Convert MySQL schema to PostgreSQL first | ||
schema, err := mysql.NewMySQL().Parse(complexMySQLSchema) | ||
if err != nil { | ||
b.Fatal(err) | ||
} | ||
|
||
pgSQL, err := pgParser.Generate(schema) | ||
if err != nil { | ||
b.Fatal(err) | ||
} | ||
|
||
b.ResetTimer() | ||
for i := 0; i < b.N; i++ { | ||
schema, err := pgParser.Parse(pgSQL) | ||
if err != nil { | ||
b.Fatal(err) | ||
} | ||
|
||
_, err = mysqlParser.Generate(schema) | ||
if err != nil { | ||
b.Fatal(err) | ||
} | ||
} | ||
} | ||
|
||
func BenchmarkSQLiteToMySQL(b *testing.B) { | ||
sqliteParser := sqlite.NewSQLite() | ||
mysqlParser := mysql.NewMySQL() | ||
|
||
// Use a simpler schema for SQLite | ||
simpleSQLiteSchema := ` | ||
CREATE TABLE users ( | ||
id INTEGER PRIMARY KEY AUTOINCREMENT, | ||
name TEXT NOT NULL, | ||
email TEXT UNIQUE, | ||
created_at TEXT DEFAULT CURRENT_TIMESTAMP | ||
); | ||
CREATE TABLE products ( | ||
id INTEGER PRIMARY KEY AUTOINCREMENT, | ||
name TEXT NOT NULL, | ||
price REAL NOT NULL, | ||
stock INTEGER DEFAULT 0 | ||
); | ||
CREATE VIEW active_products AS | ||
SELECT * FROM products WHERE stock > 0; | ||
` | ||
|
||
b.ResetTimer() | ||
for i := 0; i < b.N; i++ { | ||
schema, err := sqliteParser.Parse(simpleSQLiteSchema) | ||
if err != nil { | ||
b.Fatal(err) | ||
} | ||
|
||
_, err = mysqlParser.Generate(schema) | ||
if err != nil { | ||
b.Fatal(err) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
package integration | ||
|
||
import ( | ||
"bytes" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/mstgnz/sqlmapper" | ||
"github.com/mstgnz/sqlmapper/mysql" | ||
"github.com/mstgnz/sqlmapper/postgres" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestMySQLStreamParser(t *testing.T) { | ||
parser := mysql.NewMySQLStreamParser() | ||
|
||
// Test input | ||
input := ` | ||
CREATE TABLE users ( | ||
id INT AUTO_INCREMENT PRIMARY KEY, | ||
name VARCHAR(255) NOT NULL, | ||
email VARCHAR(255) UNIQUE, | ||
status ENUM('active', 'inactive') DEFAULT 'active', | ||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP | ||
); | ||
CREATE VIEW active_users AS | ||
SELECT * FROM users WHERE status = 'active'; | ||
CREATE FUNCTION get_user_status(user_id INT) | ||
RETURNS VARCHAR(50) | ||
BEGIN | ||
DECLARE status VARCHAR(50); | ||
SELECT status INTO status FROM users WHERE id = user_id; | ||
RETURN status; | ||
END; | ||
CREATE TRIGGER before_user_update | ||
BEFORE UPDATE ON users | ||
FOR EACH ROW | ||
BEGIN | ||
SET NEW.updated_at = CURRENT_TIMESTAMP; | ||
END; | ||
` | ||
|
||
// Test parsing | ||
var objects []sqlmapper.SchemaObject | ||
err := parser.ParseStream(strings.NewReader(input), func(obj sqlmapper.SchemaObject) error { | ||
objects = append(objects, obj) | ||
return nil | ||
}) | ||
|
||
assert.NoError(t, err) | ||
assert.Equal(t, 4, len(objects)) | ||
|
||
// Test table object | ||
assert.Equal(t, sqlmapper.TableObject, objects[0].Type) | ||
table := objects[0].Data.(*sqlmapper.Table) | ||
assert.Equal(t, "users", table.Name) | ||
assert.Equal(t, 5, len(table.Columns)) | ||
|
||
// Test view object | ||
assert.Equal(t, sqlmapper.ViewObject, objects[1].Type) | ||
view := objects[1].Data.(*sqlmapper.View) | ||
assert.Equal(t, "active_users", view.Name) | ||
|
||
// Test function object | ||
assert.Equal(t, sqlmapper.FunctionObject, objects[2].Type) | ||
function := objects[2].Data.(*sqlmapper.Function) | ||
assert.Equal(t, "get_user_status", function.Name) | ||
|
||
// Test trigger object | ||
assert.Equal(t, sqlmapper.TriggerObject, objects[3].Type) | ||
trigger := objects[3].Data.(*sqlmapper.Trigger) | ||
assert.Equal(t, "before_user_update", trigger.Name) | ||
|
||
// Test generation | ||
var output bytes.Buffer | ||
schema := &sqlmapper.Schema{ | ||
Tables: []sqlmapper.Table{*table}, | ||
Views: []sqlmapper.View{*view}, | ||
Functions: []sqlmapper.Function{*function}, | ||
Triggers: []sqlmapper.Trigger{*trigger}, | ||
} | ||
|
||
err = parser.GenerateStream(schema, &output) | ||
assert.NoError(t, err) | ||
assert.Contains(t, output.String(), "CREATE TABLE users") | ||
assert.Contains(t, output.String(), "CREATE VIEW active_users") | ||
assert.Contains(t, output.String(), "CREATE FUNCTION get_user_status") | ||
assert.Contains(t, output.String(), "CREATE TRIGGER before_user_update") | ||
} | ||
|
||
func TestPostgreSQLStreamParser(t *testing.T) { | ||
parser := postgres.NewPostgreSQLStreamParser() | ||
|
||
// Test input | ||
input := ` | ||
CREATE TYPE user_status AS ENUM ('active', 'inactive'); | ||
CREATE TABLE users ( | ||
id SERIAL PRIMARY KEY, | ||
name VARCHAR(255) NOT NULL, | ||
email VARCHAR(255) UNIQUE, | ||
status user_status DEFAULT 'active', | ||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP | ||
); | ||
CREATE INDEX idx_users_email ON users USING btree (email); | ||
CREATE MATERIALIZED VIEW active_users AS | ||
SELECT * FROM users WHERE status = 'active' | ||
WITH DATA; | ||
CREATE FUNCTION get_user_status(user_id INTEGER) | ||
RETURNS user_status | ||
LANGUAGE plpgsql | ||
AS $$ | ||
DECLARE | ||
status user_status; | ||
BEGIN | ||
SELECT status INTO status FROM users WHERE id = user_id; | ||
RETURN status; | ||
END; | ||
$$; | ||
CREATE TRIGGER before_user_update | ||
BEFORE UPDATE ON users | ||
FOR EACH ROW | ||
EXECUTE FUNCTION update_timestamp(); | ||
GRANT SELECT, INSERT ON users TO app_user; | ||
` | ||
|
||
// Test parsing | ||
var objects []sqlmapper.SchemaObject | ||
err := parser.ParseStream(strings.NewReader(input), func(obj sqlmapper.SchemaObject) error { | ||
objects = append(objects, obj) | ||
return nil | ||
}) | ||
|
||
assert.NoError(t, err) | ||
assert.Equal(t, 7, len(objects)) | ||
|
||
// Test type object | ||
assert.Equal(t, sqlmapper.TypeObject, objects[0].Type) | ||
typ := objects[0].Data.(*sqlmapper.Type) | ||
assert.Equal(t, "user_status", typ.Name) | ||
|
||
// Test table object | ||
assert.Equal(t, sqlmapper.TableObject, objects[1].Type) | ||
table := objects[1].Data.(*sqlmapper.Table) | ||
assert.Equal(t, "users", table.Name) | ||
assert.Equal(t, 5, len(table.Columns)) | ||
|
||
// Test index object | ||
assert.Equal(t, sqlmapper.IndexObject, objects[2].Type) | ||
index := objects[2].Data.(*sqlmapper.Index) | ||
assert.Equal(t, "idx_users_email", index.Name) | ||
|
||
// Test view object | ||
assert.Equal(t, sqlmapper.ViewObject, objects[3].Type) | ||
view := objects[3].Data.(*sqlmapper.View) | ||
assert.Equal(t, "active_users", view.Name) | ||
assert.True(t, view.IsMaterialized) | ||
|
||
// Test function object | ||
assert.Equal(t, sqlmapper.FunctionObject, objects[4].Type) | ||
function := objects[4].Data.(*sqlmapper.Function) | ||
assert.Equal(t, "get_user_status", function.Name) | ||
|
||
// Test trigger object | ||
assert.Equal(t, sqlmapper.TriggerObject, objects[5].Type) | ||
trigger := objects[5].Data.(*sqlmapper.Trigger) | ||
assert.Equal(t, "before_user_update", trigger.Name) | ||
|
||
// Test permission object | ||
assert.Equal(t, sqlmapper.PermissionObject, objects[6].Type) | ||
permission := objects[6].Data.(*sqlmapper.Permission) | ||
assert.Equal(t, "GRANT", permission.Type) | ||
|
||
// Test generation | ||
var output bytes.Buffer | ||
schema := &sqlmapper.Schema{ | ||
Types: []sqlmapper.Type{*typ}, | ||
Tables: []sqlmapper.Table{*table}, | ||
Views: []sqlmapper.View{*view}, | ||
Functions: []sqlmapper.Function{*function}, | ||
Triggers: []sqlmapper.Trigger{*trigger}, | ||
Permissions: []sqlmapper.Permission{*permission}, | ||
} | ||
|
||
err = parser.GenerateStream(schema, &output) | ||
assert.NoError(t, err) | ||
assert.Contains(t, output.String(), "CREATE TYPE user_status") | ||
assert.Contains(t, output.String(), "CREATE TABLE users") | ||
assert.Contains(t, output.String(), "CREATE MATERIALIZED VIEW active_users") | ||
assert.Contains(t, output.String(), "CREATE FUNCTION get_user_status") | ||
assert.Contains(t, output.String(), "CREATE TRIGGER before_user_update") | ||
} |