internal/postgres: GetLatestMajorPathForV1Path
GetLatestMajorPathForV1Path returns the path that is the latest major
for the v1path.
Change-Id: Iec02b0771427b535817b5bd3794f6aa1a807280d
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/282617
Trust: Julie Qiu <julie@golang.org>
Run-TryBot: Julie Qiu <julie@golang.org>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
diff --git a/internal/postgres/path.go b/internal/postgres/path.go
new file mode 100644
index 0000000..225c8d3
--- /dev/null
+++ b/internal/postgres/path.go
@@ -0,0 +1,71 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package postgres
+
+import (
+ "context"
+ "database/sql"
+ "fmt"
+ "strconv"
+ "strings"
+
+ "golang.org/x/pkgsite/internal"
+ "golang.org/x/pkgsite/internal/derrors"
+)
+
+// GetLatestMajorPathForV1Path reports the latest unit path in the series for
+// the given v1path.
+func (db *DB) GetLatestMajorPathForV1Path(ctx context.Context, v1path string) (_ string, err error) {
+ defer derrors.Wrap(&err, "DB.GetLatestPathForV1Path(ctx, %q)", v1path)
+ q := `
+ SELECT p.path, m.series_path
+ FROM paths p
+ INNER JOIN units u ON u.path_id = p.id
+ INNER JOIN modules m ON u.module_id = m.id
+ WHERE u.v1path_id = (
+ SELECT p.id
+ FROM paths p
+ INNER JOIN units u ON u.v1path_id = p.id
+ WHERE p.path = $1
+ ORDER BY p.path DESC
+ LIMIT 1
+ );`
+ paths := map[string]string{}
+ err = db.db.RunQuery(ctx, q, func(rows *sql.Rows) error {
+ var p, sp string
+ if err := rows.Scan(&p, &sp); err != nil {
+ return err
+ }
+ paths[p] = sp
+ return nil
+ }, v1path)
+ if err != nil {
+ return "", err
+ }
+
+ var (
+ maj int
+ majPath string
+ )
+ for p, sp := range paths {
+ // Trim the series path and suffix from the unit path.
+ // Keep only the N following vN.
+ suffix := internal.Suffix(v1path, sp)
+ v := strings.TrimSuffix(strings.TrimPrefix(
+ strings.TrimSuffix(strings.TrimPrefix(p, sp), suffix), "/v"), "/")
+ var i int
+ if v != "" {
+ i, err = strconv.Atoi(v)
+ if err != nil {
+ return "", fmt.Errorf("strconv.Atoi(%q): %v", v, err)
+ }
+ }
+ if maj <= i {
+ maj = i
+ majPath = p
+ }
+ }
+ return majPath, nil
+}
diff --git a/internal/postgres/path_test.go b/internal/postgres/path_test.go
new file mode 100644
index 0000000..bdadb27
--- /dev/null
+++ b/internal/postgres/path_test.go
@@ -0,0 +1,85 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package postgres
+
+import (
+ "context"
+ "testing"
+
+ "golang.org/x/pkgsite/internal/testing/sample"
+)
+
+func TestGetLatestMajorPathForV1Path(t *testing.T) {
+ ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
+ defer cancel()
+
+ checkLatest := func(t *testing.T, versions []string, v1path string, wantVersion string) {
+ t.Helper()
+ got, err := testDB.GetLatestMajorPathForV1Path(ctx, v1path)
+ if err != nil {
+ t.Fatal(err)
+ }
+ want := sample.ModulePath
+ if wantVersion != "" {
+ want = want + "/" + wantVersion
+ }
+ if got != want {
+ t.Errorf("GetLatestMajorPathForV1Path(%q) = %q, want %q", v1path, got, want)
+ }
+ }
+
+ for _, test := range []struct {
+ name, want string
+ versions []string
+ }{
+ {
+ "want highest major version",
+ "v11",
+ []string{"", "v2", "v11"},
+ },
+ {
+ "only v1 version",
+ "",
+ []string{""},
+ },
+ {
+ "no v1 version",
+ "v4",
+ []string{"v4"},
+ },
+ } {
+ t.Run(test.name, func(t *testing.T) {
+ ResetTestDB(testDB, t)
+ suffix := "a/b/c"
+
+ for _, v := range test.versions {
+ modpath := sample.ModulePath
+ if v != "" {
+ modpath = modpath + "/" + v
+ }
+ if v == "" {
+ v = sample.VersionString
+ } else {
+ v = v + ".0.0"
+ }
+ m := sample.Module(modpath, v, suffix)
+ if err := testDB.InsertModule(ctx, m); err != nil {
+ t.Fatal(err)
+ }
+ }
+ t.Run("module", func(t *testing.T) {
+ v1path := sample.ModulePath
+ checkLatest(t, test.versions, v1path, test.want)
+ })
+ t.Run("package", func(t *testing.T) {
+ if test.want != "" {
+ test.want += "/"
+ }
+ v1path := sample.ModulePath + "/" + suffix
+ checkLatest(t, test.versions, v1path, test.want+suffix)
+ })
+ })
+ }
+}