|
4 | 4 |
|
5 | 5 | English | [中文](README_zh.md) |
6 | 6 |
|
7 | | -SQLize is a powerful SQL toolkit for Golang, offering parsing, building, and migration capabilities. |
8 | | - |
9 | | -## Features |
10 | | - |
11 | | -- SQL parsing and building for multiple databases: |
12 | | - - MySQL |
13 | | - - PostgreSQL |
14 | | - - SQLite |
15 | | - |
16 | | -- SQL migration generation: |
17 | | - - Create migrations from Golang models and current SQL schema |
18 | | - - Generate migration versions compatible with `golang-migrate/migrate` |
19 | | - - Export ERD (MermaidJs) |
20 | | - - Export Arvo Schema |
21 | | - |
22 | | -- Advanced functionalities: |
23 | | - - Support for embedded structs |
24 | | - - Avro schema generation (MySQL only) |
25 | | - - Compatibility with `gorm` tags (default tag is `sql`) |
| 7 | +SQLize is a powerful migration generation tool that detects differences between two SQL state sources. It simplifies migration creation by comparing an existing SQL schema with Go models, ensuring seamless database updates. |
| 8 | +Designed for flexibility, SQLize supports `MySQL`, `PostgreSQL`, and `SQLite` and integrates well with popular Go ORM and migration tools like `gorm` (gorm tag), `golang-migrate/migrate` (migration version), and more. |
| 9 | +Additionally, SQLize offers advanced features, including `Avro Schema` export (MySQL only) and `ERD` diagram generation (`MermaidJS`). |
26 | 10 |
|
27 | 11 | ## Conventions |
28 | 12 |
|
@@ -72,176 +56,95 @@ SQLize is a powerful SQL toolkit for Golang, offering parsing, building, and mig |
72 | 56 | BIGINT => bigint(20) |
73 | 57 | ``` |
74 | 58 |
|
75 | | -### Important Notes |
76 | | - |
77 | | -- Pointer values must be declared in the struct |
78 | | - |
79 | | -### Examples |
80 | | - |
81 | | -1. Using pointer values: |
| 59 | +- Pointer values must be declared in the struct or predefined data types. |
82 | 60 |
|
83 | 61 | ```golang |
84 | | -type sample struct { |
85 | | - ID int32 `sql:"primary_key"` |
| 62 | +// your struct |
| 63 | +type Record struct { |
| 64 | + ID int |
86 | 65 | DeletedAt *time.Time |
87 | 66 | } |
88 | 67 |
|
| 68 | +// => |
| 69 | +// the struct is declared with a value |
89 | 70 | now := time.Now() |
90 | | -newMigration.FromObjects(sample{DeletedAt: &now}) |
91 | | -``` |
92 | | - |
93 | | -2. Embedded struct: |
94 | | - |
95 | | -```golang |
96 | | -type Base struct { |
97 | | - ID int32 `sql:"primary_key"` |
98 | | - CreatedAt time.Time |
99 | | -} |
100 | | -type sample struct { |
101 | | - Base `sql:"embedded"` |
102 | | - User string |
103 | | -} |
104 | | - |
105 | | -newMigration.FromObjects(sample{}) |
106 | | - |
107 | | -/* |
108 | | -CREATE TABLE sample ( |
109 | | - id int(11) PRIMARY KEY, |
110 | | - user text, |
111 | | - created_at datetime |
112 | | -); |
113 | | -*/ |
114 | | -``` |
115 | | - |
116 | | -3. Comparing SQL schema with Go struct: |
117 | | - |
118 | | -```go |
119 | | -package main |
120 | | - |
121 | | -import ( |
122 | | - "time" |
123 | | - |
124 | | - "github.com/sunary/sqlize" |
125 | | -) |
| 71 | +Record{DeletedAt: &now} |
126 | 72 |
|
127 | | -type user struct { |
128 | | - ID int32 `sql:"primary_key;auto_increment"` |
129 | | - Alias string `sql:"type:VARCHAR(64)"` |
130 | | - Name string `sql:"type:VARCHAR(64);unique;index_columns:name,age"` |
131 | | - Age int |
132 | | - Bio string |
133 | | - IgnoreMe string `sql:"-"` |
134 | | - AcceptTncAt *time.Time `sql:"index:idx_accept_tnc_at"` |
135 | | - CreatedAt time.Time `sql:"default:CURRENT_TIMESTAMP"` |
136 | | - UpdatedAt time.Time `sql:"default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;index:idx_updated_at"` |
| 73 | +// or predefined data type |
| 74 | +type Record struct { |
| 75 | + ID int |
| 76 | + DeletedAt *time.Time `sql:"type:DATETIME"` |
137 | 77 | } |
138 | 78 |
|
139 | | -func (user) TableName() string { |
140 | | - return "user" |
141 | | -} |
142 | | - |
143 | | -var createStm = ` |
144 | | -CREATE TABLE user ( |
145 | | - id INT AUTO_INCREMENT PRIMARY KEY, |
146 | | - name VARCHAR(64), |
147 | | - age INT, |
148 | | - bio TEXT, |
149 | | - gender BOOL, |
150 | | - accept_tnc_at DATETIME NULL, |
151 | | - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, |
152 | | - updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
153 | | -); |
154 | | -CREATE UNIQUE INDEX idx_name_age ON user(name, age); |
155 | | -CREATE INDEX idx_updated_at ON user(updated_at);` |
156 | | - |
157 | | -func main() { |
158 | | - n := time.Now() |
159 | | - newMigration := sqlize.NewSqlize(sqlize.WithSqlTag("sql"), sqlize.WithMigrationFolder("")) |
160 | | - _ = newMigration.FromObjects(user{AcceptTncAt: &n}) |
161 | | - |
162 | | - println(newMigration.StringUp()) |
163 | | - //CREATE TABLE `user` ( |
164 | | - // `id` int(11) AUTO_INCREMENT PRIMARY KEY, |
165 | | - // `alias` varchar(64), |
166 | | - // `name` varchar(64), |
167 | | - // `age` int(11), |
168 | | - // `bio` text, |
169 | | - // `accept_tnc_at` datetime NULL, |
170 | | - // `created_at` datetime DEFAULT CURRENT_TIMESTAMP(), |
171 | | - // `updated_at` datetime DEFAULT CURRENT_TIMESTAMP() ON UPDATE CURRENT_TIMESTAMP() |
172 | | - //); |
173 | | - //CREATE UNIQUE INDEX `idx_name_age` ON `user`(`name`, `age`); |
174 | | - //CREATE INDEX `idx_accept_tnc_at` ON `user`(`accept_tnc_at`); |
175 | | - //CREATE INDEX `idx_updated_at` ON `user`(`updated_at`); |
176 | | - |
177 | | - println(newMigration.StringDown()) |
178 | | - //DROP TABLE IF EXISTS `user`; |
179 | | - |
180 | | - oldMigration := sqlize.NewSqlize(sqlize.WithMigrationFolder("")) |
181 | | - //_ = oldMigration.FromMigrationFolder() |
182 | | - _ = oldMigration.FromString(createStm) |
183 | | - |
184 | | - newMigration.Diff(*oldMigration) |
185 | | - |
186 | | - println(newMigration.StringUp()) |
187 | | - //ALTER TABLE `user` ADD COLUMN `alias` varchar(64) AFTER `id`; |
188 | | - //ALTER TABLE `user` DROP COLUMN `gender`; |
189 | | - //CREATE INDEX `idx_accept_tnc_at` ON `user`(`accept_tnc_at`); |
190 | | - |
191 | | - println(newMigration.StringDown()) |
192 | | - //ALTER TABLE `user` DROP COLUMN `alias`; |
193 | | - //ALTER TABLE `user` ADD COLUMN `gender` tinyint(1) AFTER `age`; |
194 | | - //DROP INDEX `idx_accept_tnc_at` ON `user`; |
195 | | - |
196 | | - println(newMigration.MermaidJsLive()) |
197 | | - println(newMigration.ArvoSchema()) |
198 | | - //... |
199 | | - |
200 | | - _ = newMigration.WriteFiles("demo migration") |
| 79 | +// or using struct supported by "database/sql" |
| 80 | +type Record struct { |
| 81 | + ID int |
| 82 | + DeletedAt sql.NullTime |
201 | 83 | } |
202 | 84 | ``` |
203 | 85 |
|
204 | | -4. Comparing Two SQL Schemas: |
| 86 | +## Usage |
205 | 87 |
|
206 | | -```go |
| 88 | +- Add the following code to your project as a command. |
| 89 | +- Implement `YourModels()` to return the Go models affected by the migration. |
| 90 | +- Run the command whenever you need to generate a migration. |
| 91 | + |
| 92 | +```golang |
207 | 93 | package main |
208 | 94 |
|
209 | 95 | import ( |
| 96 | + "fmt" |
| 97 | + "log" |
| 98 | + "os" |
| 99 | + |
210 | 100 | "github.com/sunary/sqlize" |
211 | 101 | ) |
212 | 102 |
|
213 | 103 | func main() { |
214 | | - sql1 := sqlize.NewSqlize() |
215 | | - sql1.FromString(` |
216 | | -CREATE TABLE user ( |
217 | | - id INT AUTO_INCREMENT PRIMARY KEY, |
218 | | - name VARCHAR(64), |
219 | | - age INT, |
220 | | - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, |
221 | | - updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
222 | | -); |
223 | | -CREATE UNIQUE INDEX idx_name_age ON user(name, age); |
224 | | - `) |
225 | | - |
226 | | - sql2 := sqlize.NewSqlize() |
227 | | - sql2.FromString(` |
228 | | -CREATE TABLE user ( |
229 | | - id INT, |
230 | | - name VARCHAR(64), |
231 | | - age INT, |
232 | | - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, |
233 | | - updated_at DATETIME |
234 | | -);`) |
235 | | - |
236 | | - sql1.Diff(*sql2) |
237 | | - println(sql1.StringUp()) |
238 | | - //ALTER TABLE `user` MODIFY COLUMN `id` int(11) AUTO_INCREMENT PRIMARY KEY; |
239 | | - //ALTER TABLE `user` MODIFY COLUMN `updated_at` datetime DEFAULT CURRENT_TIMESTAMP() ON UPDATE CURRENT_TIMESTAMP(); |
240 | | - //CREATE UNIQUE INDEX `idx_name_age` ON `user`(`name`, `age`); |
241 | | - |
242 | | - println(sql1.StringDown()) |
243 | | - //ALTER TABLE `user` MODIFY COLUMN `id` int(11); |
244 | | - //ALTER TABLE `user` MODIFY COLUMN `updated_at` datetime; |
245 | | - //DROP INDEX `idx_name_age` ON `user`; |
| 104 | + migrationFolder := "migrations/" |
| 105 | + sqlLatest := sqlize.NewSqlize(sqlize.WithSqlTag("sql"), |
| 106 | + sqlize.WithMigrationFolder(migrationFolder), |
| 107 | + sqlize.WithCommentGenerate()) |
| 108 | + |
| 109 | + ms := YourModels() // TODO: implement YourModels() function |
| 110 | + err := sqlLatest.FromObjects(ms...) |
| 111 | + if err != nil { |
| 112 | + log.Fatal("sqlize FromObjects", err) |
| 113 | + } |
| 114 | + sqlVersion := sqlLatest.HashValue() |
| 115 | + |
| 116 | + sqlMigrated := sqlize.NewSqlize(sqlize.WithMigrationFolder(migrationFolder)) |
| 117 | + err = sqlMigrated.FromMigrationFolder() |
| 118 | + if err != nil { |
| 119 | + log.Fatal("sqlize FromMigrationFolder", err) |
| 120 | + } |
| 121 | + |
| 122 | + sqlLatest.Diff(*sqlMigrated) |
| 123 | + |
| 124 | + fmt.Println("sql version", sqlVersion) |
| 125 | + |
| 126 | + fmt.Println("\n\n### migration up") |
| 127 | + migrationUp := sqlLatest.StringUp() |
| 128 | + fmt.Println(migrationUp) |
| 129 | + |
| 130 | + fmt.Println("\n\n### migration down") |
| 131 | + fmt.Println(sqlLatest.StringDown()) |
| 132 | + |
| 133 | + initVersion := false |
| 134 | + if initVersion { |
| 135 | + log.Println("write to init version") |
| 136 | + err = sqlLatest.WriteFilesVersion("new version", 0, false) |
| 137 | + if err != nil { |
| 138 | + log.Fatal("sqlize WriteFilesVersion", err) |
| 139 | + } |
| 140 | + } |
| 141 | + |
| 142 | + if len(os.Args) > 1 { |
| 143 | + log.Println("write to file", os.Args[1]) |
| 144 | + err = sqlLatest.WriteFilesWithVersion(os.Args[1], sqlVersion, false) |
| 145 | + if err != nil { |
| 146 | + log.Fatal("sqlize WriteFilesWithVersion", err) |
| 147 | + } |
| 148 | + } |
246 | 149 | } |
247 | 150 | ``` |
0 commit comments