Browse Source

DB migration mechanism

Vova Tkach 5 years ago
parent
commit
8347010d62

+ 3 - 0
Makefile

@@ -93,3 +93,6 @@ ab:
 	ab -kc 10 -t 120 http://localhost:8080/another/
 	ab -kc 10 -t 120 http://localhost:8080/another/
 	ab -kc 10 -t 120 http://localhost:8080/not-existent-page/
 	ab -kc 10 -t 120 http://localhost:8080/not-existent-page/
 	ab -kc 10 -t 120 http://localhost:8080/blog/
 	ab -kc 10 -t 120 http://localhost:8080/blog/
+
+migrate:
+	./support/migrate.sh

+ 0 - 0
hosts/localhost/config/.installed


+ 7 - 0
main.go

@@ -14,6 +14,7 @@ import (
 	"golang-fave/engine/mysqlpool"
 	"golang-fave/engine/mysqlpool"
 	"golang-fave/logger"
 	"golang-fave/logger"
 	"golang-fave/modules"
 	"golang-fave/modules"
+	"golang-fave/support"
 	"golang-fave/utils"
 	"golang-fave/utils"
 
 
 	"github.com/vladimirok5959/golang-server-bootstrap/bootstrap"
 	"github.com/vladimirok5959/golang-server-bootstrap/bootstrap"
@@ -67,6 +68,12 @@ func main() {
 		return
 		return
 	}
 	}
 
 
+	// Run database migration
+	if err := support.New().Migration(consts.ParamWwwDir); err != nil {
+		fmt.Println(err)
+		return
+	}
+
 	// Init logger
 	// Init logger
 	lg := logger.New()
 	lg := logger.New()
 	defer lg.Close()
 	defer lg.Close()

+ 1 - 0
modules/module_index_act_cypress.go

@@ -39,6 +39,7 @@ func (this *Modules) RegisterAction_IndexCypressReset() *Action {
 				blog_cat_post_rel,
 				blog_cat_post_rel,
 				blog_posts,
 				blog_posts,
 				pages,
 				pages,
+				settings,
 				users
 				users
 			;`,
 			;`,
 		)
 		)

+ 24 - 0
modules/module_index_act_mysql_setup.go

@@ -367,6 +367,30 @@ func (this *Modules) RegisterAction_IndexMysqlSetup() *Action {
 			return
 			return
 		}
 		}
 
 
+		// Table: settings
+		if _, err = tx.Exec(
+			`CREATE TABLE settings (
+				name varchar(255) NOT NULL COMMENT 'Setting name',
+				value text NOT NULL COMMENT 'Setting value'
+			) ENGINE=InnoDB DEFAULT CHARSET=utf8;`,
+		); err != nil {
+			tx.Rollback()
+			wrap.MsgError(err.Error())
+			return
+		}
+		if _, err = tx.Exec(
+			`INSERT INTO settings (name, value) VALUES ('database_version', '000000000');`,
+		); err != nil {
+			tx.Rollback()
+			wrap.MsgError(err.Error())
+			return
+		}
+		if _, err = tx.Exec(`ALTER TABLE settings ADD UNIQUE KEY name (name);`); err != nil {
+			tx.Rollback()
+			wrap.MsgError(err.Error())
+			return
+		}
+
 		// Table: users
 		// Table: users
 		if _, err = tx.Exec(
 		if _, err = tx.Exec(
 			`CREATE TABLE users (
 			`CREATE TABLE users (

+ 1 - 1
package.json

@@ -1,5 +1,5 @@
 {
 {
   "devDependencies": {
   "devDependencies": {
-    "cypress": "^3.2.0"
+    "cypress": "3.3.1"
   }
   }
 }
 }

+ 40 - 0
support/migrate.sh

@@ -0,0 +1,40 @@
+#!/bin/bash
+
+LAST_NUM_STR=`find ./support/migrate/ -maxdepth 1 -name '*.go' | sort -t_ -nk2,2 | tail -n1 | awk -F/ '{print $NF}' | awk -F. '{print $NR}'`
+
+NEXT_NUM_INT=$(($LAST_NUM_STR + 1))
+NEXT_NUM_STR=`printf %09d $NEXT_NUM_INT`
+TARGET_FILE="./support/migrate/${NEXT_NUM_STR}.go"
+
+# Create new migration file
+echo "package migrate" > $TARGET_FILE
+echo "" >> $TARGET_FILE
+echo "import (" >> $TARGET_FILE
+echo "	\"golang-fave/engine/sqlw\"" >> $TARGET_FILE
+echo ")" >> $TARGET_FILE
+echo "" >> $TARGET_FILE
+echo "func Migrate_${NEXT_NUM_STR}(db *sqlw.DB) error {" >> $TARGET_FILE
+echo "	return nil" >> $TARGET_FILE
+echo "}" >> $TARGET_FILE
+
+# Update list
+LIST_FILE="./support/migrate/000000001.go"
+echo "package migrate" > $LIST_FILE
+echo "" >> $LIST_FILE
+echo "import (" >> $LIST_FILE
+echo "	\"golang-fave/engine/sqlw\"" >> $LIST_FILE
+echo ")" >> $LIST_FILE
+echo "" >> $LIST_FILE
+echo "var Migrations = map[string]func(*sqlw.DB) error{" >> $LIST_FILE
+echo "	\"000000000\": nil," >> $LIST_FILE
+echo "	\"000000001\": nil," >> $LIST_FILE
+for i in `ls ./support/migrate/*.go | sort -V`; do
+	IFILE=`echo "$i" | awk -F/ '{print $NF}' | awk -F. '{print $NR}'`
+	if [ $IFILE == "000000000" ] || [ $IFILE == "000000001" ]; then
+		continue
+	fi
+	echo "	\"${IFILE}\": Migrate_${IFILE}," >> $LIST_FILE
+done
+echo "}" >> $LIST_FILE
+
+echo "New migration file created: ${TARGET_FILE}"

+ 31 - 0
support/migrate/000000000.go

@@ -0,0 +1,31 @@
+package migrate
+
+import (
+	"fmt"
+
+	"golang-fave/engine/sqlw"
+	"golang-fave/utils"
+)
+
+func Run(db *sqlw.DB, version int, host string) error {
+	var last string
+	for i, fn := range Migrations {
+		if utils.StrToInt(i) > 1 {
+			if version < utils.StrToInt(i) {
+				last = i
+				if fn != nil {
+					fn(db)
+					fmt.Printf("Migrated %s: %s\n", host, i)
+				}
+			}
+		}
+	}
+
+	if last != "" {
+		if _, err := db.Exec(`UPDATE settings SET value = ? WHERE name = 'database_version';`, last); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}

+ 10 - 0
support/migrate/000000001.go

@@ -0,0 +1,10 @@
+package migrate
+
+import (
+	"golang-fave/engine/sqlw"
+)
+
+var Migrations = map[string]func(*sqlw.DB) error{
+	"000000000": nil,
+	"000000001": nil,
+}

+ 6 - 0
support/schema.sql

@@ -54,6 +54,12 @@ ALTER TABLE `pages` ADD UNIQUE KEY `alias` (`alias`);
 ALTER TABLE `pages` ADD KEY `alias_active` (`alias`,`active`) USING BTREE;
 ALTER TABLE `pages` ADD KEY `alias_active` (`alias`,`active`) USING BTREE;
 ALTER TABLE `pages` ADD KEY `FK_pages_user` (`user`);
 ALTER TABLE `pages` ADD KEY `FK_pages_user` (`user`);
 
 
+CREATE TABLE `settings` (
+	`name` varchar(255) NOT NULL COMMENT 'Setting name',
+	`value` text NOT NULL COMMENT 'Setting value',
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+ALTER TABLE `settings` ADD UNIQUE KEY `name` (`name`);
+
 CREATE TABLE `users` (
 CREATE TABLE `users` (
 	`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'AI',
 	`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'AI',
 	`first_name` varchar(64) NOT NULL DEFAULT '' COMMENT 'User first name',
 	`first_name` varchar(64) NOT NULL DEFAULT '' COMMENT 'User first name',

+ 61 - 0
support/support.go

@@ -0,0 +1,61 @@
+package support
+
+import (
+	"io/ioutil"
+	"os"
+
+	"golang-fave/engine/sqlw"
+	"golang-fave/support/migrate"
+	"golang-fave/utils"
+)
+
+type Support struct {
+}
+
+func New() *Support {
+	sup := Support{}
+	return &sup
+}
+
+func (this *Support) Migration(dir string) error {
+	files, err := ioutil.ReadDir(dir)
+	if err != nil {
+		return err
+	}
+	for _, file := range files {
+		if utils.IsDir(dir + string(os.PathSeparator) + file.Name()) {
+			if err := this.Migrate(dir + string(os.PathSeparator) + file.Name()); err != nil {
+				return err
+			}
+		}
+	}
+	return nil
+}
+
+func (this *Support) Migrate(host string) error {
+	mysql_config_file := host + string(os.PathSeparator) + "config" + string(os.PathSeparator) + "mysql.json"
+	if utils.IsMySqlConfigExists(mysql_config_file) {
+		mc, err := utils.MySqlConfigRead(mysql_config_file)
+		if err != nil {
+			return err
+		}
+		db, err := sqlw.Open("mysql", mc.User+":"+mc.Password+"@tcp("+mc.Host+":"+mc.Port+")/"+mc.Name)
+		if err != nil {
+			return err
+		}
+		if err := db.Ping(); err != nil {
+			return err
+		}
+		defer db.Close()
+		var version string
+		if err := db.QueryRow(`SELECT value FROM settings WHERE name = 'database_version' LIMIT 1;`).Scan(&version); err != nil {
+			return err
+		}
+		return this.Process(db, version, host)
+	}
+	return nil
+}
+
+func (this *Support) Process(db *sqlw.DB, version string, host string) error {
+	return migrate.Run(db, utils.StrToInt(version), host)
+}