internal/postgres: don't insert into search if a prefix
In ReconcileSearch, don't insert a module into search_documents if
there is already a module path that is a suffix of the current one.
This avoids cases like the following:
1. github.com/araalinetworks/api, which has a golang package, is
fetched and added to search_documents.
2. github.com/araalinetworks/api/golang is fetched and added to
search_documents, correctly replacing github.com/araalinetworks/api
because it is a longer module path.
3. Another version of github.com/araalinetworks/api is fetched. It is
bad, so it isn't inserted in the usual way, but ReconcileSearch
re-inserts the latest good version, which overwrites the longer
module path inserted in step 2.
Change-Id: If8ae59acfde7838c0ea48882d6beb0a457e125db
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/342469
Trust: Jonathan Amsterdam <jba@google.com>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Jamal Carvalho <jamal@golang.org>
Reviewed-by: Julie Qiu <julie@golang.org>
diff --git a/internal/postgres/insert_module.go b/internal/postgres/insert_module.go
index 073ab41..d977141 100644
--- a/internal/postgres/insert_module.go
+++ b/internal/postgres/insert_module.go
@@ -639,8 +639,9 @@
// ReconcileSearch reconciles the search data for modulePath. If the module is
// alternative or has no good versions, it removes search data. Otherwise, if
-// the latest good version doesn't match the version in search_documents, and it
-// inserts the latest good version into search_documents and imports_unique.
+// the latest good version doesn't match the version in search_documents,
+// and the module path is not a prefix of one already in search_documents,
+// it inserts the latest good version into search_documents and imports_unique.
// The version and status arguments should come from the module currently being
// fetched. They are used to determine if the module is alternative.
func (db *DB) ReconcileSearch(ctx context.Context, modulePath, version string, status int) (err error) {
@@ -685,18 +686,18 @@
log.Debugf(ctx, "ReconcileSearch(%q): alternative or no good version; removed from search_documents and imports_unique", modulePath)
return nil
}
- // Is the latest good version in search_documents?
+ // Is the latest good version in search_documents, or is there a longer module path?
var x int
switch err := tx.QueryRow(ctx, `
SELECT 1
FROM search_documents
- WHERE module_path = $1
- AND version = $2
+ WHERE (module_path = $1 AND version = $2)
+ OR module_path LIKE $1 || '/%'
`, modulePath, lmv.GoodVersion).Scan(&x); err {
case sql.ErrNoRows:
break
case nil:
- log.Debugf(ctx, "ReconcileSearch(%q): good version %s found in search_documents; doing nothing",
+ log.Debugf(ctx, "ReconcileSearch(%q): good version %s or suffix module path found in search_documents; doing nothing",
modulePath, lmv.GoodVersion)
return nil
default:
diff --git a/internal/postgres/insert_module_test.go b/internal/postgres/insert_module_test.go
index 42510ee..0e1ca99 100644
--- a/internal/postgres/insert_module_test.go
+++ b/internal/postgres/insert_module_test.go
@@ -620,8 +620,8 @@
defer release()
ctx := context.Background()
- insert := func(modulePath, version string, status int, imports []string, modfile string) {
- m := sample.Module(modulePath, version, "pkg")
+ insert := func(modulePath, version, suffix string, status int, imports []string, modfile string) {
+ m := sample.Module(modulePath, version, suffix)
pkg := m.Packages()[0]
pkg.Documentation[0].Synopsis = version
pkg.Imports = imports
@@ -645,15 +645,19 @@
}
}
- check := func(modPath, wantVersion string, wantImports []string) {
+ check := func(modPath, wantVersion, suffix string, wantImports []string) {
t.Helper()
+ pkgPath := modPath
+ if suffix != "" {
+ pkgPath += "/" + suffix
+ }
var gotVersion, gotSynopsis string
err := testDB.db.QueryRow(ctx, `
SELECT version, synopsis
FROM search_documents
WHERE module_path = $1
- AND package_path = $1 || '/pkg'
- `, modPath).Scan(&gotVersion, &gotSynopsis)
+ AND package_path = $2
+ `, modPath, pkgPath).Scan(&gotVersion, &gotSynopsis)
if errors.Is(err, sql.ErrNoRows) {
gotVersion = ""
gotSynopsis = ""
@@ -667,9 +671,9 @@
gotImports, err := testDB.db.CollectStrings(ctx, `
SELECT to_path
FROM imports_unique
- WHERE from_path = $1 || '/pkg'
- AND from_module_path = $1
- `, modPath)
+ WHERE from_path = $1
+ AND from_module_path = $2
+ `, pkgPath, modPath)
if err != nil {
t.Fatal(err)
}
@@ -683,31 +687,40 @@
imports1 := []string{"fmt", "log"}
const modPath1 = "m.com/a"
// Insert a good module. It should be in search_documents and imports_unique.
- insert(modPath1, "v1.1.0", 200, imports1, "")
- check(modPath1, "v1.1.0", imports1)
+ insert(modPath1, "v1.1.0", "pkg", 200, imports1, "")
+ check(modPath1, "v1.1.0", "pkg", imports1)
// Insert a higher, good version. It should replace the first.
imports2 := []string{"log", "strings"}
- insert(modPath1, "v1.2.0", 200, imports2, "")
- check(modPath1, "v1.2.0", imports2)
+ insert(modPath1, "v1.2.0", "pkg", 200, imports2, "")
+ check(modPath1, "v1.2.0", "pkg", imports2)
// Now an even higher, bad version comes along that retracts v1.2.0.
// The search_documents and imports_unique tables should go back to v1.1.0.
- insert(modPath1, "v1.3.0", 400, nil, "retract v1.2.0")
- check(modPath1, "v1.1.0", imports1)
+ insert(modPath1, "v1.3.0", "pkg", 400, nil, "retract v1.2.0")
+ check(modPath1, "v1.1.0", "pkg", imports1)
// Now a still higher version comes along that retracts everything. The
// module should no longer be in search_documents or imports_unique.
- insert(modPath1, "v1.4.0", 200, nil, "retract [v1.0.0, v1.4.0]")
- check(modPath1, "", nil)
+ insert(modPath1, "v1.4.0", "pkg", 200, nil, "retract [v1.0.0, v1.4.0]")
+ check(modPath1, "", "pkg", nil)
// Insert another good module.
const modPath2 = "m.com/b"
- insert(modPath2, "v1.1.0", 200, imports1, "")
- check(modPath2, "v1.1.0", imports1)
+ insert(modPath2, "v1.1.0", "pkg", 200, imports1, "")
+ check(modPath2, "v1.1.0", "pkg", imports1)
// A later version makes this an alternative module.
// The module should be removed from search.
- insert(modPath2, "v1.2.0", 491, imports1, "")
- check(modPath2, "", nil)
+ insert(modPath2, "v1.2.0", "pkg", 491, imports1, "")
+ check(modPath2, "", "pkg", nil)
+
+ // Insert a module that is its own package.
+ const modPath3 = "m.com/c/pkg"
+ insert(modPath3, "v1.0.0", "", 200, imports1, "")
+ check(modPath3, "v1.0.0", "", imports1)
+
+ // Insert a module that is a prefix. It shouldn't change anything.
+ insert("m.com/c", "v1.0.0", "pkg", 200, imports1, "")
+ check(modPath3, "v1.0.0", "", imports1)
}