internl/postgres: move delete functions into delete.go
Pure code motion.
Change-Id: I80055dbb95f2341c72d3716b74e08335bdaa6fd4
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/341851
Trust: Jonathan Amsterdam <jba@google.com>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Julie Qiu <julie@golang.org>
diff --git a/internal/postgres/delete.go b/internal/postgres/delete.go
index 2c9e789..4b36663 100644
--- a/internal/postgres/delete.go
+++ b/internal/postgres/delete.go
@@ -8,9 +8,13 @@
"context"
"database/sql"
+ "github.com/Masterminds/squirrel"
"github.com/lib/pq"
+ "golang.org/x/mod/semver"
+ "golang.org/x/pkgsite/internal"
"golang.org/x/pkgsite/internal/database"
"golang.org/x/pkgsite/internal/derrors"
+ "golang.org/x/pkgsite/internal/log"
)
// DeleteModule deletes a Version from the database.
@@ -41,6 +45,85 @@
})
}
+// DeleteOlderVersionFromSearchDocuments deletes from search_documents every package with
+// the given module path whose version is older than the given version.
+// It is used when fetching a module with an alternative path. See internal/worker/fetch.go:fetchAndUpdateState.
+func (db *DB) DeleteOlderVersionFromSearchDocuments(ctx context.Context, modulePath, resolvedVersion string) (err error) {
+ defer derrors.WrapStack(&err, "DeleteOlderVersionFromSearchDocuments(ctx, %q, %q)", modulePath, resolvedVersion)
+
+ return db.db.Transact(ctx, sql.LevelDefault, func(tx *database.DB) error {
+ // Collect all package paths in search_documents with the given module path
+ // and an older version. (package_path is the primary key of search_documents.)
+ var ppaths []string
+ query := `
+ SELECT package_path, version
+ FROM search_documents
+ WHERE module_path = $1
+ `
+ err := tx.RunQuery(ctx, query, func(rows *sql.Rows) error {
+ var ppath, v string
+ if err := rows.Scan(&ppath, &v); err != nil {
+ return err
+ }
+ if semver.Compare(v, resolvedVersion) < 0 {
+ ppaths = append(ppaths, ppath)
+ }
+ return nil
+ }, modulePath)
+ if err != nil {
+ return err
+ }
+ if len(ppaths) == 0 {
+ return nil
+ }
+
+ // Delete all of those paths.
+ return deleteModuleOrPackagesInModuleFromSearchDocuments(ctx, tx, modulePath, ppaths)
+ })
+}
+
+// deleteOtherModulePackagesFromSearchDocuments deletes all packages from search
+// documents with the given module that are not in m.
+func deleteOtherModulePackagesFromSearchDocuments(ctx context.Context, tx *database.DB, m *internal.Module) error {
+ dbPkgs, err := tx.CollectStrings(ctx, `
+ SELECT package_path FROM search_documents WHERE module_path = $1
+ `, m.ModulePath)
+ if err != nil {
+ return err
+ }
+ pkgInModule := map[string]bool{}
+ for _, u := range m.Packages() {
+ pkgInModule[u.Path] = true
+ }
+ var otherPkgs []string
+ for _, p := range dbPkgs {
+ if !pkgInModule[p] {
+ otherPkgs = append(otherPkgs, p)
+ }
+ }
+ return deleteModuleOrPackagesInModuleFromSearchDocuments(ctx, tx, m.ModulePath, otherPkgs)
+}
+
+// deleteModuleOrPackagesInModuleFromSearchDocuments deletes module_path from
+// search_documents. If packages is non-empty, it only deletes those packages.
+func deleteModuleOrPackagesInModuleFromSearchDocuments(ctx context.Context, tx *database.DB, modulePath string, packages []string) error {
+ d := squirrel.Delete("search_documents").
+ Where(squirrel.Eq{"module_path": modulePath})
+ if len(packages) > 0 {
+ d = d.Where("package_path = ANY(?)", pq.Array(packages))
+ }
+ q, args, err := d.PlaceholderFormat(squirrel.Dollar).ToSql()
+ if err != nil {
+ return err
+ }
+ n, err := tx.Exec(ctx, q, args...)
+ if err != nil {
+ return err
+ }
+ log.Infof(ctx, "deleted %d rows of module %s from search_documents", n, modulePath)
+ return nil
+}
+
// DeletePseudoversionsExcept deletes all pseudoversions for the module except
// the provided resolvedVersion.
func (db *DB) DeletePseudoversionsExcept(ctx context.Context, modulePath, resolvedVersion string) (err error) {
diff --git a/internal/postgres/delete_test.go b/internal/postgres/delete_test.go
index f0e5fc7..ae15a73 100644
--- a/internal/postgres/delete_test.go
+++ b/internal/postgres/delete_test.go
@@ -6,12 +6,174 @@
import (
"context"
+ "database/sql"
+ "errors"
+ "sort"
+ "strings"
"testing"
+ "github.com/google/go-cmp/cmp"
+ "golang.org/x/pkgsite/internal/derrors"
"golang.org/x/pkgsite/internal/testing/sample"
"golang.org/x/pkgsite/internal/version"
)
+func TestDeleteModule(t *testing.T) {
+ t.Parallel()
+ testDB, release := acquire(t)
+ defer release()
+ ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
+ defer cancel()
+
+ v := sample.DefaultModule()
+
+ MustInsertModule(ctx, t, testDB, v)
+ if _, err := testDB.GetModuleInfo(ctx, v.ModulePath, v.Version); err != nil {
+ t.Fatal(err)
+ }
+
+ vm := sample.DefaultVersionMap()
+ if err := testDB.UpsertVersionMap(ctx, vm); err != nil {
+ t.Fatal(err)
+ }
+ if _, err := testDB.GetVersionMap(ctx, v.ModulePath, v.Version); err != nil {
+ t.Fatal(err)
+ }
+
+ if err := testDB.DeleteModule(ctx, v.ModulePath, v.Version); err != nil {
+ t.Fatal(err)
+ }
+ if _, err := testDB.GetModuleInfo(ctx, v.ModulePath, v.Version); !errors.Is(err, derrors.NotFound) {
+ t.Errorf("got %v, want NotFound", err)
+ }
+
+ var x int
+ err := testDB.Underlying().QueryRow(ctx, "SELECT 1 FROM imports_unique WHERE from_module_path = $1",
+ v.ModulePath).Scan(&x)
+ if err != sql.ErrNoRows {
+ t.Errorf("imports_unique: got %v, want ErrNoRows", err)
+ }
+ err = testDB.Underlying().QueryRow(
+ ctx,
+ "SELECT 1 FROM version_map WHERE module_path = $1 AND resolved_version = $2",
+ v.ModulePath, v.Version).Scan(&x)
+ if err != sql.ErrNoRows {
+ t.Errorf("version_map: got %v, want ErrNoRows", err)
+ }
+}
+
+func TestDeleteFromSearch(t *testing.T) {
+ t.Parallel()
+ ctx := context.Background()
+
+ const modulePath = "deleteme.com"
+
+ initial := []searchDocumentRow{
+ {modulePath + "/p1", modulePath, "v0.0.9", 0}, // oldest version of same module
+ {modulePath + "/p2", modulePath, "v1.1.0", 0}, // older version of same module
+ {modulePath + "/p4", modulePath, "v1.9.0", 0}, // newer version of same module
+ {"other.org/p2", "other.org", "v1.1.0", 0}, // older version of a different module
+ }
+
+ insertInitial := func(db *DB) {
+ t.Helper()
+ for _, r := range initial {
+ sm := sample.Module(r.ModulePath, r.Version, strings.TrimPrefix(r.PackagePath, r.ModulePath+"/"))
+ MustInsertModule(ctx, t, db, sm)
+ }
+ // Older versions are deleted by InsertModule, so only the newest versions are here.
+ checkSearchDocuments(ctx, t, db, initial[2:])
+ }
+
+ t.Run("DeleteOlderVersionFromSearchDocuments", func(t *testing.T) {
+ testDB, release := acquire(t)
+ defer release()
+ insertInitial(testDB)
+
+ if err := testDB.DeleteOlderVersionFromSearchDocuments(ctx, modulePath, "v1.2.3"); err != nil {
+ t.Fatal(err)
+ }
+
+ checkSearchDocuments(ctx, t, testDB, []searchDocumentRow{
+ {modulePath + "/p4", modulePath, "v1.9.0", 0}, // newer version not deleted
+ {"other.org/p2", "other.org", "v1.1.0", 0}, // other module not deleted
+ })
+ })
+ t.Run("deleteModuleFromSearchDocuments", func(t *testing.T) {
+ // Non-empty list of packages tested above. This tests an empty list.
+ testDB, release := acquire(t)
+ defer release()
+ insertInitial(testDB)
+
+ if err := deleteModuleOrPackagesInModuleFromSearchDocuments(ctx, testDB.db, modulePath, nil); err != nil {
+ t.Fatal(err)
+ }
+ checkSearchDocuments(ctx, t, testDB, []searchDocumentRow{
+ {"other.org/p2", "other.org", "v1.1.0", 0}, // other module not deleted
+ })
+ })
+ t.Run("deleteOtherModulePackagesFromSearchDocuments", func(t *testing.T) {
+ testDB, release := acquire(t)
+ defer release()
+
+ // Insert a module with two packages.
+ m0 := sample.Module(modulePath, "v1.0.0", "p1", "p2")
+ MustInsertModule(ctx, t, testDB, m0)
+ // Set the imported-by count to a non-zero value so we can tell which
+ // rows were deleted.
+ if _, err := testDB.db.Exec(ctx, `UPDATE search_documents SET imported_by_count = 1`); err != nil {
+ t.Fatal(err)
+ }
+ // Later version of module does not have p1.
+ m1 := sample.Module(modulePath, "v1.1.0", "p2", "p3")
+ MustInsertModule(ctx, t, testDB, m1)
+
+ // p1 should be gone, p2 should be there with the same
+ // imported_by_count, and p3 should be there with a zero count.
+ want := []searchDocumentRow{
+ {modulePath + "/p2", modulePath, "v1.1.0", 1},
+ {modulePath + "/p3", modulePath, "v1.1.0", 0},
+ }
+ checkSearchDocuments(ctx, t, testDB, want)
+ })
+}
+
+type searchDocumentRow struct {
+ PackagePath, ModulePath, Version string
+ ImportedByCount int
+}
+
+func readSearchDocuments(ctx context.Context, db *DB) ([]searchDocumentRow, error) {
+ var rows []searchDocumentRow
+ err := db.db.CollectStructs(ctx, &rows, `SELECT package_path, module_path, version, imported_by_count FROM search_documents`)
+ if err != nil {
+ return nil, err
+ }
+ return rows, err
+}
+
+func checkSearchDocuments(ctx context.Context, t *testing.T, db *DB, want []searchDocumentRow) {
+ t.Helper()
+ got, err := readSearchDocuments(ctx, db)
+ if err != nil {
+ t.Fatal(err)
+ }
+ less := func(r1, r2 searchDocumentRow) bool {
+ if r1.PackagePath != r2.PackagePath {
+ return r1.PackagePath < r2.PackagePath
+ }
+ if r1.ModulePath != r2.ModulePath {
+ return r1.ModulePath < r2.ModulePath
+ }
+ return r1.Version < r2.Version
+ }
+ sort.Slice(got, func(i, j int) bool { return less(got[i], got[j]) })
+ sort.Slice(want, func(i, j int) bool { return less(want[i], want[j]) })
+ if diff := cmp.Diff(want, got); diff != "" {
+ t.Errorf("search documents mismatch (-want, +got):\n%s", diff)
+ }
+}
+
func TestDeletePseudoversionsExcept(t *testing.T) {
t.Parallel()
ctx := context.Background()
diff --git a/internal/postgres/insert_module_test.go b/internal/postgres/insert_module_test.go
index 0bac9bb..76a9c6b 100644
--- a/internal/postgres/insert_module_test.go
+++ b/internal/postgres/insert_module_test.go
@@ -445,50 +445,6 @@
}
}
-func TestDeleteModule(t *testing.T) {
- t.Parallel()
- testDB, release := acquire(t)
- defer release()
- ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
- defer cancel()
-
- v := sample.DefaultModule()
-
- MustInsertModule(ctx, t, testDB, v)
- if _, err := testDB.GetModuleInfo(ctx, v.ModulePath, v.Version); err != nil {
- t.Fatal(err)
- }
-
- vm := sample.DefaultVersionMap()
- if err := testDB.UpsertVersionMap(ctx, vm); err != nil {
- t.Fatal(err)
- }
- if _, err := testDB.GetVersionMap(ctx, v.ModulePath, v.Version); err != nil {
- t.Fatal(err)
- }
-
- if err := testDB.DeleteModule(ctx, v.ModulePath, v.Version); err != nil {
- t.Fatal(err)
- }
- if _, err := testDB.GetModuleInfo(ctx, v.ModulePath, v.Version); !errors.Is(err, derrors.NotFound) {
- t.Errorf("got %v, want NotFound", err)
- }
-
- var x int
- err := testDB.Underlying().QueryRow(ctx, "SELECT 1 FROM imports_unique WHERE from_module_path = $1",
- v.ModulePath).Scan(&x)
- if err != sql.ErrNoRows {
- t.Errorf("imports_unique: got %v, want ErrNoRows", err)
- }
- err = testDB.Underlying().QueryRow(
- ctx,
- "SELECT 1 FROM version_map WHERE module_path = $1 AND resolved_version = $2",
- v.ModulePath, v.Version).Scan(&x)
- if err != sql.ErrNoRows {
- t.Errorf("version_map: got %v, want ErrNoRows", err)
- }
-}
-
func TestPostgres_NewerAlternative(t *testing.T) {
t.Parallel()
// Verify that packages are not added to search_documents if the module has a newer
diff --git a/internal/postgres/search.go b/internal/postgres/search.go
index fc788ed..0b70397 100644
--- a/internal/postgres/search.go
+++ b/internal/postgres/search.go
@@ -13,14 +13,12 @@
"strings"
"time"
- "github.com/Masterminds/squirrel"
"github.com/lib/pq"
"go.opencensus.io/plugin/ochttp"
"go.opencensus.io/stats"
"go.opencensus.io/stats/view"
"go.opencensus.io/tag"
"go.opencensus.io/trace"
- "golang.org/x/mod/semver"
"golang.org/x/pkgsite/internal"
"golang.org/x/pkgsite/internal/database"
"golang.org/x/pkgsite/internal/dcensus"
@@ -1055,85 +1053,6 @@
return false
}
-// DeleteOlderVersionFromSearchDocuments deletes from search_documents every package with
-// the given module path whose version is older than the given version.
-// It is used when fetching a module with an alternative path. See internal/worker/fetch.go:fetchAndUpdateState.
-func (db *DB) DeleteOlderVersionFromSearchDocuments(ctx context.Context, modulePath, resolvedVersion string) (err error) {
- defer derrors.WrapStack(&err, "DeleteOlderVersionFromSearchDocuments(ctx, %q, %q)", modulePath, resolvedVersion)
-
- return db.db.Transact(ctx, sql.LevelDefault, func(tx *database.DB) error {
- // Collect all package paths in search_documents with the given module path
- // and an older version. (package_path is the primary key of search_documents.)
- var ppaths []string
- query := `
- SELECT package_path, version
- FROM search_documents
- WHERE module_path = $1
- `
- err := tx.RunQuery(ctx, query, func(rows *sql.Rows) error {
- var ppath, v string
- if err := rows.Scan(&ppath, &v); err != nil {
- return err
- }
- if semver.Compare(v, resolvedVersion) < 0 {
- ppaths = append(ppaths, ppath)
- }
- return nil
- }, modulePath)
- if err != nil {
- return err
- }
- if len(ppaths) == 0 {
- return nil
- }
-
- // Delete all of those paths.
- return deleteModuleOrPackagesInModuleFromSearchDocuments(ctx, tx, modulePath, ppaths)
- })
-}
-
-// deleteOtherModulePackagesFromSearchDocuments deletes all packages from search
-// documents with the given module that are not in m.
-func deleteOtherModulePackagesFromSearchDocuments(ctx context.Context, tx *database.DB, m *internal.Module) error {
- dbPkgs, err := tx.CollectStrings(ctx, `
- SELECT package_path FROM search_documents WHERE module_path = $1
- `, m.ModulePath)
- if err != nil {
- return err
- }
- pkgInModule := map[string]bool{}
- for _, u := range m.Packages() {
- pkgInModule[u.Path] = true
- }
- var otherPkgs []string
- for _, p := range dbPkgs {
- if !pkgInModule[p] {
- otherPkgs = append(otherPkgs, p)
- }
- }
- return deleteModuleOrPackagesInModuleFromSearchDocuments(ctx, tx, m.ModulePath, otherPkgs)
-}
-
-// deleteModuleOrPackagesInModuleFromSearchDocuments deletes module_path from
-// search_documents. If packages is non-empty, it only deletes those packages.
-func deleteModuleOrPackagesInModuleFromSearchDocuments(ctx context.Context, tx *database.DB, modulePath string, packages []string) error {
- d := squirrel.Delete("search_documents").
- Where(squirrel.Eq{"module_path": modulePath})
- if len(packages) > 0 {
- d = d.Where("package_path = ANY(?)", pq.Array(packages))
- }
- q, args, err := d.PlaceholderFormat(squirrel.Dollar).ToSql()
- if err != nil {
- return err
- }
- n, err := tx.Exec(ctx, q, args...)
- if err != nil {
- return err
- }
- log.Infof(ctx, "deleted %d rows of module %s from search_documents", n, modulePath)
- return nil
-}
-
// UpsertSearchDocumentWithImportedByCount is the same as UpsertSearchDocument,
// except it also updates the imported by count. This is only used for testing.
func (db *DB) UpsertSearchDocumentWithImportedByCount(ctx context.Context, args UpsertSearchDocumentArgs, importedByCount int) (err error) {
diff --git a/internal/postgres/search_test.go b/internal/postgres/search_test.go
index 2190465..c1991fb 100644
--- a/internal/postgres/search_test.go
+++ b/internal/postgres/search_test.go
@@ -1334,118 +1334,6 @@
}
}
-func TestDeleteFromSearch(t *testing.T) {
- t.Parallel()
- ctx := context.Background()
-
- const modulePath = "deleteme.com"
-
- initial := []searchDocumentRow{
- {modulePath + "/p1", modulePath, "v0.0.9", 0}, // oldest version of same module
- {modulePath + "/p2", modulePath, "v1.1.0", 0}, // older version of same module
- {modulePath + "/p4", modulePath, "v1.9.0", 0}, // newer version of same module
- {"other.org/p2", "other.org", "v1.1.0", 0}, // older version of a different module
- }
-
- insertInitial := func(db *DB) {
- t.Helper()
- for _, r := range initial {
- sm := sample.Module(r.ModulePath, r.Version, strings.TrimPrefix(r.PackagePath, r.ModulePath+"/"))
- MustInsertModule(ctx, t, db, sm)
- }
- // Older versions are deleted by InsertModule, so only the newest versions are here.
- checkSearchDocuments(ctx, t, db, initial[2:])
- }
-
- t.Run("DeleteOlderVersionFromSearchDocuments", func(t *testing.T) {
- testDB, release := acquire(t)
- defer release()
- insertInitial(testDB)
-
- if err := testDB.DeleteOlderVersionFromSearchDocuments(ctx, modulePath, "v1.2.3"); err != nil {
- t.Fatal(err)
- }
-
- checkSearchDocuments(ctx, t, testDB, []searchDocumentRow{
- {modulePath + "/p4", modulePath, "v1.9.0", 0}, // newer version not deleted
- {"other.org/p2", "other.org", "v1.1.0", 0}, // other module not deleted
- })
- })
- t.Run("deleteModuleFromSearchDocuments", func(t *testing.T) {
- // Non-empty list of packages tested above. This tests an empty list.
- testDB, release := acquire(t)
- defer release()
- insertInitial(testDB)
-
- if err := deleteModuleOrPackagesInModuleFromSearchDocuments(ctx, testDB.db, modulePath, nil); err != nil {
- t.Fatal(err)
- }
- checkSearchDocuments(ctx, t, testDB, []searchDocumentRow{
- {"other.org/p2", "other.org", "v1.1.0", 0}, // other module not deleted
- })
- })
- t.Run("deleteOtherModulePackagesFromSearchDocuments", func(t *testing.T) {
- testDB, release := acquire(t)
- defer release()
-
- // Insert a module with two packages.
- m0 := sample.Module(modulePath, "v1.0.0", "p1", "p2")
- MustInsertModule(ctx, t, testDB, m0)
- // Set the imported-by count to a non-zero value so we can tell which
- // rows were deleted.
- if _, err := testDB.db.Exec(ctx, `UPDATE search_documents SET imported_by_count = 1`); err != nil {
- t.Fatal(err)
- }
- // Later version of module does not have p1.
- m1 := sample.Module(modulePath, "v1.1.0", "p2", "p3")
- MustInsertModule(ctx, t, testDB, m1)
-
- // p1 should be gone, p2 should be there with the same
- // imported_by_count, and p3 should be there with a zero count.
- want := []searchDocumentRow{
- {modulePath + "/p2", modulePath, "v1.1.0", 1},
- {modulePath + "/p3", modulePath, "v1.1.0", 0},
- }
- checkSearchDocuments(ctx, t, testDB, want)
- })
-}
-
-type searchDocumentRow struct {
- PackagePath, ModulePath, Version string
- ImportedByCount int
-}
-
-func readSearchDocuments(ctx context.Context, db *DB) ([]searchDocumentRow, error) {
- var rows []searchDocumentRow
- err := db.db.CollectStructs(ctx, &rows, `SELECT package_path, module_path, version, imported_by_count FROM search_documents`)
- if err != nil {
- return nil, err
- }
- return rows, err
-}
-
-func checkSearchDocuments(ctx context.Context, t *testing.T, db *DB, want []searchDocumentRow) {
- t.Helper()
- got, err := readSearchDocuments(ctx, db)
- if err != nil {
- t.Fatal(err)
- }
- less := func(r1, r2 searchDocumentRow) bool {
- if r1.PackagePath != r2.PackagePath {
- return r1.PackagePath < r2.PackagePath
- }
- if r1.ModulePath != r2.ModulePath {
- return r1.ModulePath < r2.ModulePath
- }
- return r1.Version < r2.Version
- }
- sort.Slice(got, func(i, j int) bool { return less(got[i], got[j]) })
- sort.Slice(want, func(i, j int) bool { return less(want[i], want[j]) })
- if diff := cmp.Diff(want, got); diff != "" {
- t.Errorf("search documents mismatch (-want, +got):\n%s", diff)
- }
-}
-
func TestGroupSearchResults(t *testing.T) {
rs := []*SearchResult{
{PackagePath: "m.com/p", ModulePath: "m.com", Score: 10},