I have a Go project using goose for Mysql migrations. I would like to bind the migrations to the package executable so that the executable can be deployed and used independently from any system, similar to JAR files in JAVA projects.
Is there an equivalent in Go to accomplish that?
How to get a single file which can migrate database and work
Install
go get -u github.com/pressly/goose/cmd/goose
Make app. I base it on examplemain.go and add run option. Suppose your project is located at github.com/user/project:
package main
import (
"database/sql"
"flag"
"log"
"os"
"github.com/pressly/goose"
// Init DB drivers. -- here I recommend remove unnecessary - but it's up to you
_ "github.com/go-sql-driver/mysql"
_ "github.com/lib/pq"
_ "github.com/mattn/go-sqlite3"
_ "github.com/ziutek/mymysql/godrv"
// here our migrations will live -- use your path
_ "github.com/user/project/migrations"
)
var (
flags = flag.NewFlagSet("goose", flag.ExitOnError)
dir = flags.String("dir", ".", "directory with migration files")
)
func main() {
flags.Usage = usage
flags.Parse(os.Args[1:])
args := flags.Args()
//////
if len(args) > 1 && args[0] == "run" {
log.Printf("PROGRAM RUN\n") //
.....
os.Exit(0)
}
if len(args) > 1 && args[0] == "create" {
if err := goose.Run("create", nil, *dir, args[1:]...); err != nil {
log.Fatalf("goose run: %v", err)
}
return
}
if len(args) < 3 {
flags.Usage()
return
}
if args[0] == "-h" || args[0] == "--help" {
flags.Usage()
return
}
driver, dbstring, command := args[0], args[1], args[2]
switch driver {
case "postgres", "mysql", "sqlite3", "redshift":
if err := goose.SetDialect(driver); err != nil {
log.Fatal(err)
}
default:
log.Fatalf("%q driver not supported\n", driver)
}
switch dbstring {
case "":
log.Fatalf("-dbstring=%q not supported\n", dbstring)
default:
}
if driver == "redshift" {
driver = "postgres"
}
db, err := sql.Open(driver, dbstring)
if err != nil {
log.Fatalf("-dbstring=%q: %v\n", dbstring, err)
}
arguments := []string{}
if len(args) > 3 {
arguments = append(arguments, args[3:]...)
}
if err := goose.Run(command, db, *dir, arguments...); err != nil {
log.Fatalf("goose run: %v", err)
}
}
func usage() {
log.Print(usagePrefix)
flags.PrintDefaults()
log.Print(usageCommands)
}
var (
usagePrefix = `Usage: goose [OPTIONS] DRIVER DBSTRING COMMAND
Drivers:
postgres
mysql
sqlite3
redshift
Examples:
goose sqlite3 ./foo.db status
goose sqlite3 ./foo.db create init sql
goose sqlite3 ./foo.db create add_some_column sql
goose sqlite3 ./foo.db create fetch_user_data go
goose sqlite3 ./foo.db up
goose postgres "user=postgres dbname=postgres sslmode=disable" status
goose mysql "user:password#/dbname?parseTime=true" status
goose redshift "postgres://user:password#qwerty.us-east-1.redshift.amazonaws.com:5439/db"
status
Options:
`
usageCommands = `
Commands:
up Migrate the DB to the most recent version available
up-to VERSION Migrate the DB to a specific VERSION
down Roll back the version by 1
down-to VERSION Roll back to a specific VERSION
redo Re-run the latest migration
status Dump the migration status for the current DB
version Print the current version of the database
create NAME [sql|go] Creates new migration file with next version
`
)
Create folder for migrations:
mkdir migrations && cd migrations
Create first migrations. We will use go-style migrations:
goose mysql "user:password#/dbname?parseTime=true" create init go
You'll get a file 00001_init.go with Go code. Migrations are baked in it as SQL-commands. Just edit them as you need.
Then go to the main folder and build the application:
cd ..
go build -v -o myapp *.go
You'll get a file myapp with all the migrations baked in it. To check move it to some other place, for example to /tmp folder, and run from there:
./myapp mysql "user:password#/dbname?parseTime=true" status
Run your app:
./myapp run
Result
You have single file which can be used as a migration tool so as a working application itself. All the migration are buil-it. In source code they are stored in a subpackage migrations - so it's easy to edit.
If you use Docker you may put folder to the image.
If not projects like https://github.com/rakyll/statik or https://github.com/jteeuwen/go-bindata can help.
If you're already using Goose, one option would be to write the migrations in Go instead of SQL. Based on the Go migrations example in the Goose repo, when you build the goose binary here, it will bundle all the *.go ones into the binary.
This was the output after I built the example and removed all files except the binary itself. The Go-based migration was embedded:
2017/10/31 11:22:31 Applied At Migration
2017/10/31 11:22:31 =======================================
2017/10/31 11:22:31 Mon Jun 19 21:56:00 2017 -- 00002_rename_root.go
If you're looking to use SQL-based migrations but don't want to take on additional dependencies, you could embed your SQL-based migrations into *.go files as string constants, then, starting from the go-migrations example, add an init phase to main.go that writes them out to the current directory before proceeding.
Related
As the title says, I have an error when importing go-mysql-driver package. I have installed the go-my-sql driver in my machine but the error still persists. I use XAMPP for local hosting and here’s the block of program.
package model
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
type Table interface {
Name() string
Field() ([]string, []interface{})
}
func Connect(username string, password string, host string, database string) (*sql.DB, error) {
conn := fmt.Sprintf("%s:%s#tcp(%s:3306)/%s", username, password, host, database)
db, err := sql.Open("mysql", conn)
return db, err
}
func CreateDB(db *sql.DB, name string) error {
query := fmt.Sprintf("CREATE DATABASE %v", name)
_, err := db.Exec(query)
return err
}
func CreateTable(db *sql.DB, query string) error {
_, err := db.Exec(query)
return err
}
func DropDB(db *sql.DB, name string) error {
query := fmt.Sprintf("DROP DATABASE %v", name)
_, err := db.Exec(query)
return err
}
could not import github.com/go-sql-driver/mysql (no required modules provides package "github.com/go-sql-driver/mysql")
screenshot of what's happening
It seems that you read the tutorial for an older go version.
Go 1.17 requires dependencies must be explicitly in go.mod.
Maybe you could try go module first (https://go.dev/blog/using-go-modules)
Your IDE is not showing you the whole picture. By running go run main.go (or whatever main file you have) on the command line, you can see the same error as you're seeing on your IDE with some extra:
$ go run main.go
main.go:7:5: no required module provides package github.com/go-sql-driver/mysql; to add it:
go get github.com/go-sql-driver/mysql
By issuing the suggested command go get github.com/go-sql-driver/mysql, you'll get the dependency added to your go.mod file and the contents of the package will be downloaded to your machine.
Your next execution will work:
$ go run main.go
Hello world
I've made some small modifications to your code to work, I'll add them here for completeness:
I've used the same source, but changed the package name to main.
I've added a main function to the bottom of the file:
func main() {
fmt.Println("Hello world")
_, err := Connect("username", "password", "localhost", "db")
if err != nil {
panic(err)
}
}
I've saved to a file named main.go
I've initialized the go.mod file by running go mod init test and go mod tidy, then I took the steps described on the beginning of the answer.
I installed the libs and can run/debug the libvirt related codes in idea IDE, but after running below build command on MacBook
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o zagent.exe cmd/test/main.go
I got
cmd/test/main.go:11:22: undefined: libvirt.NewConnect
Thank you for your attention, below is the codes:
package main
import (
_logUtils "github.com/easysoft/zagent/internal/pkg/lib/log"
"github.com/libvirt/libvirt-go"
)
func main() {
connStr := "***"
LibvirtConn, err := libvirt.NewConnect(connStr)
if err != nil {
_logUtils.Errorf(err.Error())
return
}
active, err := LibvirtConn.IsAlive()
if err != nil {
_logUtils.Errorf(err.Error())
return
}
if !active {
_logUtils.Errorf("not active")
}
}
The libvirt-go package is a CGo based API to the underlying libvirt.so library. You cannot set CGO_ENABLED=0 and expect it to still work. AFAI, Go toolchain doesn't allow cross-compiling with CGo either.
I am beginner in LUA. I have written the following code in lua file that mysql proxy run with it.
function read_query(packet)
if string.byte(packet) == proxy.COM_QUERY then
local command = string.lower(packet)
if string.find(command, "select") ~= nil and string.find(string.lower(packet), "from") ~= nil then
local socket = require('socket')
local conn, err = socket.connect('localhost', 5050)
print(conn, err)
proxy.response.type = proxy.MYSQLD_PACKET_OK
//proxy.response.resultset get json from web service (url)
proxy.response.resultset = {
fields = {
{ type = proxy.MYSQL_TYPE_INT, name = "id", },
},
rows = {
{ 9001 }
}
}
return proxy.PROXY_SEND_RESULT
end
end
end
I want to connect to the web service on Port 5050 that return the JSON file and save the json that it returns in the proxy.response.resultset.
Another question, How can i add socket module. I paste files like following image
socket module files
but give an error : can not find /socket/core.lua.
local socket = require('socket')
You are using luasocket and it comes as a mix of lua (socket.lua) and binary (socket/core.so) files. You need to set (if it's not set already) to point to .so files; something like this may work: package.cpath=package.cpath..';./?.so'
Hi i am trying to execute a script to fill data into a database using Golang
func executeTestScript(){
cmd := exec.Command("/usr/local/mysql/bin/mysql", "-h127.0.0.1", "-P3333", "-uusr", "-pPassxxx", "-Ddtb_test", "< /Users/XXX/Documents/test/scripts/olds/SCRIPT_XXX.sql")
var out, stderr bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
fmt.Println(fmt.Sprintf("Error executing query. Command Output: %+v\n: %+v, %v", out.String(), stderr.String(), err))
log.Fatalf("Error executing query. Command Output: %+v\n: %+v, %v", out.String(), stderr.String(), err)
}
}
The problem is that i am getting the error:
ERROR 1049 (42000): Unknown database '< /Users/XXX/Documents/test/scripts/olds/SCRIPT_XXX.sql'
i think the problem is the last param (the sql script path) that the exec thinks is the dbname
The following command in the terminal is working:
/usr/local/mysql/bin/mysql --host=127.0.0.1 --port=3333 --user=usr --password=Passxxx --database=dtb_test < /Users/XXX/Documents/roseula/scripts/olds/SCRIPT_XXX.sql
but i try to replicate in Go to automatize the execution of the script.
The script have drop tables, create tables, inserts, and PK with FK relationships its a very complete one so i cant execute line by line, because of that i decided to execute de mysql program to insert the data in the database.
Any suggestions?
+1 to answer from #MatteoRagni for showing how to do stdin redirection in Golang.
But here's a simple alternative that I use:
cmd := exec.Command("/usr/local/mysql/bin/mysql", "-h127.0.0.1", "-P3333",
"-uusr", "-pPassxxx", "-Ddtb_test",
"-e", "source /Users/XXX/Documents/test/scripts/olds/SCRIPT_XXX.sql")
You don't have to make the mysql client read the script using stdin redirection. Instead, you can make the mysql client execute a specific command, which is source <scriptname>.
P.S.: I also would not put the host, port, user, and password in your code. That means you have to recompile your program any time you change those connection parameters. Also it's not secure to use passwords in plaintext on the command-line. Instead, I'd put all the connection parameters into a defaults file and use mysql --defaults-file=FILENAME.
This is a little example that runs something like:
cat < test.txt
that is what I think you are missing in your code:
package main
import (
"fmt"
"os/exec"
"os"
)
func main() {
cmd := exec.Command("cat")
file, _ := os.Open("test.txt")
cmd.Stdin = file
out, _ := cmd.Output()
fmt.Printf("%s\n", out)
}
That prints in the console the content of test.txt, as read by cat. You will need to adapt it to your problem.
Something like:
func executeTestScript(){
cmd := exec.Command("/usr/local/mysql/bin/mysql", "-h127.0.0.1", "-P3333", "-uusr", "-pPassxxx", "-Ddtb_test")
dump, dump_err = os.Open("/Users/XXX/Documents/test/scripts/olds/SCRIPT_XXX.sql")
if dump_err != nil {
/* Handle the error if file not opened */
}
cmd.Stdin = dump
var out, stderr bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
fmt.Println(fmt.Sprintf("Error executing query. Command Output: %+v\n: %+v, %v", out.String(), stderr.String(), err))
log.Fatalf("Error executing query. Command Output: %+v\n: %+v, %v", out.String(), stderr.String(), err)
}
}
if I'm not wrong...
I can connect to the RDS instance using mysql -h ... command so I know it's not a security group problem.
I've tried to use:
sql.Open("mysql", "id:password#tcp(your-amazonaws-uri.com:3306)/dbname")
in the readme file of go-sql-driver(https://github.com/go-sql-driver/mysql), but it doesn't seem to work.
I'm using my username under the RDS instance instead of id here though.
Edit:
The error returned is: panic runtime error: invalid memory address or nil pointer deference [signal 0xb code=0x1 addr=0x20 pc=0x5b551e]
goroutine 16 [running]
runtime.panic(0x7d4fc0, 0xa6ca73)...database/sql.(*Rows).Next...
It works fine with my local DB.
The connection string for sql.Open() is in DSN format.
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
db, err := sql.Open("mysql", "<username>:<password>#tcp(<AWSConnectionEndpoint >:<port>)/<dbname>")
if err != nil {
fmt.Print(err.Error())
}
defer db.Close()
Make sure the actual error isn't related to an import issue (as in issues 266)
Check (to be sure you are using the latest versions, as in this issue):
your Go-MySQL-Driver version (or git SHA)
your Go version (run go version in your console)
If the error isn't directly in the Open step, but when accessing the Rows, check this comment out:
Use either a for loop (for rows.Next() { ... }) or something like this:
if rows.Next() {
// whatever
} else {
// catch error with rows.Err()
}
rows.Close() // <- don't forget this if you are not iterating over ALL results