|
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