internal/postgres: use series path in sort by latest

Using module path when sorting by the latest version can give the
wrong answer for some paths. Consider M/v9 and M/v10.  The latter is
obviously later, but sorting by module path descending before version
returns M/v9. But sometimes we do want to sort by the module path
first, as when we prefer an older version in a nested module to a
newer version outside. (See
TestGetUnitMeta/prefer_pubsublite_nested_module.)

If we sort by series path instead of module path handles both cases.
When looking for the latest major version of a given module, we select
for the series path, so sorting by series path is a no-op.  But the
series path correctly distinguishes nested modules from their parent
modules.

Fixes golang/go#43902

Change-Id: Icadab4ce2326f91ab3d3c81f46d17e482a2b9654
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/286452
Trust: Jonathan Amsterdam <jba@google.com>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
Reviewed-by: Julie Qiu <julie@golang.org>
diff --git a/internal/postgres/unit.go b/internal/postgres/unit.go
index 45241b5..72ef3c9 100644
--- a/internal/postgres/unit.go
+++ b/internal/postgres/unit.go
@@ -143,7 +143,7 @@
 			WHEN m.version_type = 'prerelease' THEN 4
 			ELSE 5
 		END`,
-		"m.module_path DESC",
+		"m.series_path DESC",
 		"m.sort_version DESC",
 	).PlaceholderFormat(squirrel.Dollar)
 }
@@ -157,8 +157,8 @@
 					WHEN m.version_type = 'prerelease' THEN 4
 					ELSE 5
 				END,
-				m.sort_version DESC,
-				m.module_path DESC`
+				m.series_path DESC,
+				m.sort_version DESC`
 
 // GetUnit returns a unit from the database, along with all of the data
 // associated with that unit.
diff --git a/internal/postgres/version_test.go b/internal/postgres/version_test.go
index b3adc90..8233f9d 100644
--- a/internal/postgres/version_test.go
+++ b/internal/postgres/version_test.go
@@ -240,6 +240,8 @@
 		sample.Module("a.com/M/v2", "v2.0.5", "all", "most"),
 		sample.Module("a.com/M/v3", "v3.0.1", "all", "some"),
 		sample.Module("a.com/M/D", "v1.3.0", "other"),
+		sample.Module("b.com/M/v9", "v9.0.0", ""),
+		sample.Module("b.com/M/v10", "v10.0.0", ""),
 	} {
 		if err := testDB.InsertModule(ctx, m); err != nil {
 			t.Fatal(err)
@@ -247,12 +249,13 @@
 	}
 
 	for _, test := range []struct {
-		unit string
-		want internal.LatestInfo
+		unit, module string
+		want         internal.LatestInfo
 	}{
 		{
 			// A unit that is the module.
-			"a.com/M",
+			"a.com/M", "a.com/M",
+
 			internal.LatestInfo{
 				MinorVersion:      "v1.2.0",
 				MinorModulePath:   "a.com/M",
@@ -263,7 +266,8 @@
 		},
 		{
 			// A unit that exists in all versions of the module.
-			"a.com/M/all",
+			"a.com/M/all", "a.com/M",
+
 			internal.LatestInfo{
 				MinorVersion:      "v1.2.0",
 				MinorModulePath:   "a.com/M",
@@ -274,7 +278,8 @@
 		},
 		{
 			// A unit that exists in most versions, but not the latest major.
-			"a.com/M/most",
+			"a.com/M/most", "a.com/M",
+
 			internal.LatestInfo{
 				MinorVersion:      "v1.2.0",
 				MinorModulePath:   "a.com/M",
@@ -285,7 +290,8 @@
 		},
 		{
 			// A unit that does not exist at the latest minor version, but does at the latest major.
-			"a.com/M/some",
+			"a.com/M/some", "a.com/M",
+
 			internal.LatestInfo{
 				MinorVersion:      "v1.1.1",
 				MinorModulePath:   "a.com/M",
@@ -296,7 +302,8 @@
 		},
 		{
 			// A unit that does not exist at the latest minor or major versions.
-			"a.com/M/one",
+			"a.com/M/one", "a.com/M",
+
 			internal.LatestInfo{
 				MinorVersion:      "v1.1.1",
 				MinorModulePath:   "a.com/M",
@@ -307,7 +314,8 @@
 		},
 		{
 			// A unit whose latest minor version is in a different module.
-			"a.com/M/D/other",
+			"a.com/M/D/other", "a.com/M",
+
 			internal.LatestInfo{
 				MinorVersion:      "v1.3.0",
 				MinorModulePath:   "a.com/M/D",
@@ -316,9 +324,20 @@
 				MajorUnitPath:     "a.com/M/v3",
 			},
 		},
+		{
+			// A module with v9 and v10 versions.
+			"b.com/M/v9", "b.com/M/v9",
+			internal.LatestInfo{
+				MinorVersion:      "v9.0.0",
+				MinorModulePath:   "b.com/M/v9",
+				UnitExistsAtMinor: true,
+				MajorModulePath:   "b.com/M/v10",
+				MajorUnitPath:     "b.com/M/v10",
+			},
+		},
 	} {
 		t.Run(test.unit, func(t *testing.T) {
-			got, err := testDB.GetLatestInfo(ctx, test.unit, "a.com/M")
+			got, err := testDB.GetLatestInfo(ctx, test.unit, test.module)
 			if err != nil {
 				t.Fatal(err)
 			}