diff --git a/.gitignore b/.gitignore index 0813f18..3dd9702 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,5 @@ vendor # GoLand IDE .idea/ -cmd/schemagen/schemagen \ No newline at end of file +cmd/schemagen/schemagen +cmd/schemagen/testdata/go.sum diff --git a/Makefile b/Makefile index 7929994..0465dfb 100644 --- a/Makefile +++ b/Makefile @@ -38,12 +38,21 @@ GOTEST := go test -cpu $(GOTEST_CPU) -count=1 -cover -race -tags all .PHONY: test test: + echo "==> Running tests..." + echo "==> Running tests... in ." @$(GOTEST) . + echo "==> Running tests... in ./qb" @$(GOTEST) ./qb + echo "==> Running tests... in ./table" @$(GOTEST) ./table + echo "==> Running tests... in ./migrate" @$(GOTEST) ./migrate + echo "==> Running tests... in ./dbutil" @$(GOTEST) ./dbutil + echo "==> Running tests... in ./cmd/schemagen" @$(GOTEST) ./cmd/schemagen + echo "==> Running tests... in ./cmd/schemagen/testdata" + @cd ./cmd/schemagen/testdata ; go mod tidy ; $(GOTEST) .; cd ../../.. .PHONY: bench bench: diff --git a/cmd/schemagen/keyspace.tmpl b/cmd/schemagen/keyspace.tmpl index 78091ab..24fe40f 100644 --- a/cmd/schemagen/keyspace.tmpl +++ b/cmd/schemagen/keyspace.tmpl @@ -41,6 +41,7 @@ var ( {{- $type_name := .Name | camelize}} {{- $field_types := .FieldTypes}} type {{$type_name}}UserType struct { + gocqlx.UDT {{- range $index, $element := .FieldNames}} {{. | camelize}} {{(index $field_types $index) | mapScyllaToGoType}} {{- end}} diff --git a/cmd/schemagen/schemagen.go b/cmd/schemagen/schemagen.go index a9b788e..fa2dd4b 100644 --- a/cmd/schemagen/schemagen.go +++ b/cmd/schemagen/schemagen.go @@ -109,12 +109,16 @@ func renderTemplate(md *gocql.KeyspaceMetadata) ([]byte, error) { } imports := make([]string, 0) + if len(md.Types) != 0 { + imports = append(imports, "github.com/scylladb/gocqlx/v2") + } + for _, t := range md.Tables { // Ensure ordered columns are sorted alphabetically sort.Strings(t.OrderedColumns) for _, c := range t.Columns { - if (c.Type == "timestamp" || c.Type == "date" || c.Type == "duration" || c.Type == "time") && !existsInSlice(imports, "time") { + if (c.Type == "timestamp" || c.Type == "date" || c.Type == "time") && !existsInSlice(imports, "time") { imports = append(imports, "time") } if c.Type == "decimal" && !existsInSlice(imports, "gopkg.in/inf.v0") { diff --git a/cmd/schemagen/schemagen_test.go b/cmd/schemagen/schemagen_test.go index 3254bfe..62fbd42 100644 --- a/cmd/schemagen/schemagen_test.go +++ b/cmd/schemagen/schemagen_test.go @@ -28,9 +28,9 @@ func TestSchemagen(t *testing.T) { }, ",") *flagIgnoreIndexes = true - b := runSchemagen(t, "foobar") + b := runSchemagen(t, "schemagentest") - const goldenFile = "testdata/models.go.txt" + const goldenFile = "testdata/models.go" if *flagUpdate { if err := ioutil.WriteFile(goldenFile, b, os.ModePerm); err != nil { t.Fatal(err) @@ -132,6 +132,7 @@ func createTestSchema(t *testing.T) { title text, album text, artist text, + duration duration, tags set, data blob)`) if err != nil { @@ -193,7 +194,9 @@ func runSchemagen(t *testing.T, pkgname string) []byte { t.Fatal(err) } keyspace := "schemagen" + cl := "127.0.1.1" + flagCluster = &cl flagKeyspace = &keyspace flagPkgname = &pkgname flagOutput = &dir diff --git a/cmd/schemagen/testdata/go.mod b/cmd/schemagen/testdata/go.mod new file mode 100644 index 0000000..180b7af --- /dev/null +++ b/cmd/schemagen/testdata/go.mod @@ -0,0 +1,18 @@ +module schemagentest + +go 1.17 + +require ( + github.com/gocql/gocql v0.0.0-20211015133455-b225f9b53fa1 + github.com/google/go-cmp v0.5.4 + github.com/scylladb/gocqlx/v2 v2.8.0 +) + +require ( + github.com/golang/snappy v0.0.4 // indirect + github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect + github.com/scylladb/go-reflectx v1.0.1 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect +) + +replace github.com/gocql/gocql => github.com/scylladb/gocql v1.14.0 diff --git a/cmd/schemagen/testdata/go.sum b/cmd/schemagen/testdata/go.sum new file mode 100644 index 0000000..a0e246e --- /dev/null +++ b/cmd/schemagen/testdata/go.sum @@ -0,0 +1,47 @@ +github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY= +github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= +github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +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/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/psanford/memfs v0.0.0-20210214183328-a001468d78ef/go.mod h1:tcaRap0jS3eifrEEllL6ZMd9dg8IlDpi2S1oARrQ+NI= +github.com/scylladb/go-reflectx v1.0.1 h1:b917wZM7189pZdlND9PbIJ6NQxfDPfBvUaQ7cjj1iZQ= +github.com/scylladb/go-reflectx v1.0.1/go.mod h1:rWnOfDIRWBGN0miMLIcoPt/Dhi2doCMZqwMCJ3KupFc= +github.com/scylladb/gocql v1.14.0 h1:MuQ2sEOHxqTmCWS8zoH34SD5bpm3NbXi7CQbIVxSsZY= +github.com/scylladb/gocql v1.14.0/go.mod h1:ZLEJ0EVE5JhmtxIW2stgHq/v1P4fWap0qyyXSKyV8K0= +github.com/scylladb/gocqlx/v2 v2.8.0 h1:f/oIgoEPjKDKd+RIoeHqexsIQVIbalVmT+axwvUqQUg= +github.com/scylladb/gocqlx/v2 v2.8.0/go.mod h1:4/+cga34PVqjhgSoo5Nr2fX1MQIqZB5eCE5DK4xeDig= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +golang.org/x/net v0.0.0-20220526153639-5463443f8c37 h1:lUkvobShwKsOesNfWWlCS5q7fnbG1MEliIzwu886fn8= +golang.org/x/net v0.0.0-20220526153639-5463443f8c37/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/cmd/schemagen/testdata/models.go.txt b/cmd/schemagen/testdata/models.go similarity index 77% rename from cmd/schemagen/testdata/models.go.txt rename to cmd/schemagen/testdata/models.go index 6714a49..02a81c9 100644 --- a/cmd/schemagen/testdata/models.go.txt +++ b/cmd/schemagen/testdata/models.go @@ -1,8 +1,10 @@ // Code generated by "gocqlx/cmd/schemagen"; DO NOT EDIT. -package foobar +package schemagentest import ( + "github.com/gocql/gocql" + "github.com/scylladb/gocqlx/v2" "github.com/scylladb/gocqlx/v2/table" ) @@ -33,6 +35,7 @@ var ( "album", "artist", "data", + "duration", "id", "tags", "title", @@ -45,6 +48,7 @@ var ( ) type AlbumUserType struct { + gocqlx.UDT Name string Songwriters []string } @@ -57,10 +61,11 @@ type PlaylistsStruct struct { Title string } type SongsStruct struct { - Album string - Artist string - Data []byte - Id [16]byte - Tags []string - Title string + Album string + Artist string + Data []byte + Duration gocql.Duration + Id [16]byte + Tags []string + Title string } diff --git a/cmd/schemagen/testdata/models_test.go b/cmd/schemagen/testdata/models_test.go new file mode 100644 index 0000000..aadd225 --- /dev/null +++ b/cmd/schemagen/testdata/models_test.go @@ -0,0 +1,91 @@ +package schemagentest + +import ( + "flag" + "github.com/google/go-cmp/cmp" + "strings" + "testing" + "time" + + "github.com/gocql/gocql" + "github.com/scylladb/gocqlx/v2" + "github.com/scylladb/gocqlx/v2/qb" +) + +var flagCluster = flag.String("cluster", "127.0.0.1", "a comma-separated list of host:port or host tuples") + +func TestModelLoad(t *testing.T) { + session, err := gocqlx.WrapSession(gocql.NewCluster(strings.Split(*flagCluster, ",")...).CreateSession()) + if err != nil { + t.Fatal("create session:", err.Error()) + } + defer session.Close() + + // Keyspace, types and table are created at `schemaget_test.go` at `createTestSchema` + + song := SongsStruct{ + Id: gocql.TimeUUID(), + Title: "title", + Album: "album", + Artist: "artist", + Duration: gocql.Duration{Nanoseconds: int64(5 * time.Minute)}, + Tags: []string{"tag1", "tag2"}, + Data: []byte("data"), + } + + err = qb.Insert("schemagen.songs"). + Columns("id", "title", "album", "artist", "duration", "tags", "data"). + Query(session). + BindStruct(&song). + Exec() + if err != nil { + t.Fatal("failed to insert song:", err.Error()) + } + + loadedSong := SongsStruct{} + err = qb.Select("schemagen.songs"). + Columns("id", "title", "album", "artist", "duration", "tags", "data"). + Where(qb.Eq("id")). + Query(session). + BindMap(map[string]interface{}{"id": song.Id}). + Get(&loadedSong) + if err != nil { + t.Fatal("failed to select song:", err) + } + if diff := cmp.Diff(song, loadedSong); diff != "" { + t.Error("loaded song is different from inserted song:", diff) + } + + pl := PlaylistsStruct{ + Id: gocql.TimeUUID(), + Title: "title", + Album: AlbumUserType{Name: "album", Songwriters: []string{"songwriter1", "songwriter2"}}, + Artist: "artist", + SongId: gocql.TimeUUID(), + } + + err = qb.Insert("schemagen.playlists"). + Columns("id", "title", "album", "artist", "song_id"). + Query(session). + BindStruct(&pl). + Exec() + if err != nil { + t.Fatal("failed to insert playlist:", err.Error()) + } + + loadedPl := PlaylistsStruct{} + + err = qb.Select("schemagen.playlists"). + Columns("id", "title", "album", "artist", "song_id"). + Where(qb.Eq("id")). + Query(session). + BindMap(map[string]interface{}{"id": pl.Id}). + Get(&loadedPl) + if err != nil { + t.Fatal("failed to select playlist:", err.Error()) + } + + if diff := cmp.Diff(pl, loadedPl); diff != "" { + t.Error("loaded playlist is different from inserted song:", diff) + } +}