diff --git a/go.mod b/go.mod index 7e92968..222b2b9 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,8 @@ module github.com/mediocregopher/mediocre-go-lib require ( cloud.google.com/go v0.36.0 github.com/boombuler/barcode v1.0.0 // indirect + github.com/go-sql-driver/mysql v1.4.0 + github.com/jmoiron/sqlx v1.2.0 github.com/pquerna/otp v1.1.0 github.com/stretchr/testify v1.3.0 golang.org/x/net v0.0.0-20190213061140-3a22650c66bd diff --git a/go.sum b/go.sum index bb83e25..1fafa4b 100644 --- a/go.sum +++ b/go.sum @@ -23,6 +23,8 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx+opk= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -48,11 +50,17 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= +github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= +github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4= +github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= diff --git a/mdb/msql/sql.go b/mdb/msql/sql.go new file mode 100644 index 0000000..47d3cdb --- /dev/null +++ b/mdb/msql/sql.go @@ -0,0 +1,58 @@ +// Package msql implements connecting to a MySQL/MariaDB instance (and possibly +// others) and simplifies a number of interactions with it. +package msql + +import ( + "context" + "fmt" + + // If something is importing msql it must need mysql, because that's all + // that is implemented at the moment + _ "github.com/go-sql-driver/mysql" + "github.com/jmoiron/sqlx" + "github.com/mediocregopher/mediocre-go-lib/mcfg" + "github.com/mediocregopher/mediocre-go-lib/mctx" + "github.com/mediocregopher/mediocre-go-lib/merr" + "github.com/mediocregopher/mediocre-go-lib/mlog" + "github.com/mediocregopher/mediocre-go-lib/mrun" +) + +// SQL is a wrapper around a sqlx client which provides more functionality. +type SQL struct { + *sqlx.DB + ctx context.Context +} + +// WithMySQL returns a SQL instance which will be initialized and configured +// when the start event is triggered on the returned Context (see mrun.Start). +// The SQL instance will have Close called on it when the stop event is +// triggered on the returned Context (see mrun.Stop). +// +// defaultDB indicates the name of the database in MySQL to use by default, +// though it will be overwritable in the config. +func WithMySQL(parent context.Context, defaultDB string) (context.Context, *SQL) { + ctx := mctx.NewChild(parent, "mysql") + sql := new(SQL) + + ctx, addr := mcfg.WithString(ctx, "addr", "[::1]:3306", "Address where mysql server can be found") + ctx, user := mcfg.WithString(ctx, "user", "root", "User to authenticate to mysql server as") + ctx, pass := mcfg.WithString(ctx, "password", "", "Password to authenticate to mysql server with") + ctx, db := mcfg.WithString(ctx, "database", defaultDB, "mysql database to use") + ctx = mrun.WithStartHook(ctx, func(innerCtx context.Context) error { + sql.ctx = mctx.Annotate(sql.ctx, "addr", *addr, "user", *user) + + dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s", *user, *pass, *addr, *db) + mlog.Debug("constructed dsn", mctx.Annotate(sql.ctx, "dsn", dsn)) + mlog.Info("connecting to mysql server", sql.ctx) + var err error + sql.DB, err = sqlx.ConnectContext(innerCtx, "mysql", dsn) + return merr.Wrap(sql.ctx, err) + }) + ctx = mrun.WithStopHook(ctx, func(innerCtx context.Context) error { + mlog.Info("closing connection to sql server", sql.ctx) + return sql.Close() + }) + + sql.ctx = ctx + return mctx.WithChild(parent, ctx), sql +} diff --git a/mdb/msql/sql_test.go b/mdb/msql/sql_test.go new file mode 100644 index 0000000..c3d6ada --- /dev/null +++ b/mdb/msql/sql_test.go @@ -0,0 +1,18 @@ +package msql + +import ( + . "testing" + + "github.com/mediocregopher/mediocre-go-lib/mtest" +) + +func TestMySQL(t *T) { + ctx := mtest.Context() + ctx, sql := WithMySQL(ctx, "test") + mtest.Run(ctx, t, func() { + _, err := sql.Exec("CREATE TABLE IF NOT EXISTS msql_test (id INT);") + if err != nil { + t.Fatal(err) + } + }) +}