Run migrate Database schema ด้วย Go Migrate
Tags: go, migration
Published: 2020-06-05 14:23:43

Go migrate เป็นโปรแกรมช่วยเราจัดการการเปลี่ยนแปลงของ Datbase schema และจัดการ versioning ทำให้เราสามารถเปลี่ยนหรือย้อนกลับจากสิ่งที่เปลี่ยนไปแล้วกลับมาเหมือนเดิมได้ง่ายขึ้น 

{% github golang-migrate/migrate %} 

ตัว Go migrate เขียนด้วย Go นอกจากจะทำเป็นโปรแกรม CLI ให้เราเรียกใช้แล้ว ยังมีเป็น package library ให้เราเอาไปเรียกใช้ผ่าน Go ที่เราเขียนเองได้อีกด้วย

ตัวอย่างการใช้งานเช่นผมมี database ชื่อ example ซึ่งผมใช้ผ่าน [PostgreSQL](http://postgresql.org) ผมต้องการสร้าง migration script สำหรับสร้าง table ชื่อ users ผมสามารถสั่งคำสั่ง migrate ได้ดังนี้

```shell
migrate create -ext sql -dir db/migrations -seq create_users_table
```

ส่วน option ที่ส่งไปให้กับ `migrate create` ได้แค่ `-ext sql` คือบอกว่าให้นามสกุลของไฟล์ที่สร้างเป็น sql แล้วก็ `-dir db/migrations` คือให้สร้างไฟล์ที่ directory `db/migrations` แล้วก็ -seq บอกว่าให้สร้าง prefix ของไฟล์เป็น running number เป็นตัวเลขเรียงตามลำดับ เริ่มจาก 1 และสุดท้าย `create_user_table` เป็นชื่อไฟล์หลังจาก prefix ของตัวเลขด้านหน้า

หลังจากสั่งคำสั่งนี้ไปแล้วเราจะเห็นไฟล์ 2 ไฟล์ใน `db/migrations` ได้แก่

- 000001_create_users_table.up.sql
- 000001_create_users_table.down.sql

ซึ่งไฟล์ up ก็เอาไว้เขียน script ตอนที่เราสั่งให้ migrate มา version นี้ส่วน down ก็คือ script ตอนที่เราสั่งให้ถอยกลับคือเอา version นี้ออก

ตัวอย่างของไฟล์​ `000001_create_users_table.up.sql` เช่น

```sql
CREATE TABLE IF NOT EXISTS users(
    user_id serial PRIMARY KEY,
    username CHARACTER VARYING UNIQUE NOT NULL,
    password CHARACTER VARYING NOT NULL,
    email CHARACTER VARYING UNIQUE NOT NULL
);
```

ตัวอย่างของไฟล์ `000001_create_users_table.down.sql`

```sql
DROP TABLE IF EXISTS users;
```

หลังจากนั่นเมื่อเราต้องการให้รัน script up ของ version 1 เพื่อสร้าง table users เราก็สั่งผ่าน migrate command แบบนี้

```shell
migrate -database 'postgres://username:password@localhost:5432/example?sslmode=disable' -path db/migrations up
```

โดยที่ `-database 'postgres://username:password@localhost:5432/example?sslmode=disable'` คือให้เราใส่ url connection ของ PostgreSQL ถ้าเป็นโปรแกรมฐานข้อมูลอื่นๆก็ใส่ url connection ต่างกันไปต้องไปดูคู่มือของ driver แต่ละแบบเองอีกที

ส่วน `-path db/migrations` ก็ใส่ path ของ script SQL ที่เราเขียน

ส่วนท้ายคือ up เพื่อสั่งให้ script up ทำงาน

หลังจากนั้นถ้าเราลิสต์ table ออกมาดูจะเห็นแบบนี้

```
               List of relations
 Schema |       Name        | Type  |  Owner   
--------+-------------------+-------+----------
 public | schema_migrations | table | weerasak
 public | users             | table | weerasak
(2 rows)
```

จะเห็นว่ามี 2 tables สร้างขึ้นมา ลองดูเฉพาะ schema ของ users จะเป็น 

```
                                     Table "public.users"
  Column  |       Type        | Collation | Nullable |                Default                 
----------+-------------------+-----------+----------+----------------------------------------
 user_id  | integer           |           | not null | nextval('users_user_id_seq'::regclass)
 username | character varying |           | not null | 
 password | character varying |           | not null | 
 email    | character varying |           | not null | 
Indexes:
    "users_pkey" PRIMARY KEY, btree (user_id)
    "users_email_key" UNIQUE CONSTRAINT, btree (email)
    "users_username_key" UNIQUE CONSTRAINT, btree (username)
```

ส่วน `schema_migrations` ถ้าเราลอง `select * from schema_migrations` ดูจะพบข้อมูลแบบนี้อยู่ 

```
 version | dirty 
---------+-------
       1 | f
```

ซึ่ง table นี้เป็น table ที่ Go migrate สร้างขึ้นมาเพื่อ track version ของ schema ถ้าเราสร้าง script เพิ่มเราก็จะได้ script ที่มี `2_` ข้างหน้าแล้วพอเราสั่ง up อีกรอบ script ของ version 2 ก็จะถูก run และ เลข version ในนี้ก็จะเปลี่ยนเป็น 2 

ถ้าเราต้องการลบสิ่งที่เปลี่ยนแปลงไปจาก version ล่าสุดที่เราเพิ่งสั่ง up ก็ให้เราสั่ง down ได้ดังนี้

```shell
migrate -database 'postgres://username:password@localhost:5432/example?sslmode=disable'` -path db/migrations down 1
```

แต่ถ้าต้องการ ถอยกลับที่หมดที่ up มาก็เอาเลข 1 ออก สั่งแค่ down แบบนี้

```shell
migrate -database 'postgres://username:password@localhost:5432/example?sslmode=disable'` -path db/migrations down
```

นอกจากพื้นฐานตรงนี้แล้วก็ยังมี sub command อื่นๆอีก ลองสั่ง `migrate -help` ดูได้เลย