internal/postgres: handle multiple documentations for a package
Insert and retrieve documentation for all of a package's build
contexts.
For golang/go#37232
Change-Id: Ib3c7b32be66502c3f343bddd247de700e390c53d
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/288831
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/insert_module.go b/internal/postgres/insert_module.go
index 3ee2374..5384343 100644
--- a/internal/postgres/insert_module.go
+++ b/internal/postgres/insert_module.go
@@ -292,7 +292,7 @@
paths []string
unitValues []interface{}
pathToReadme = map[string]*internal.Readme{}
- pathToDoc = map[string]*internal.Documentation{}
+ pathToDoc = map[string][]*internal.Documentation{}
pathToImports = map[string][]string{}
pathIDToPath = map[int]string{}
)
@@ -331,14 +331,12 @@
if u.Readme != nil {
pathToReadme[u.Path] = u.Readme
}
- if u.Documentation != nil && u.Documentation[0] != nil && u.Documentation[0].Source == nil {
- return fmt.Errorf("insertUnits: unit %q missing source files", u.Path)
+ for _, d := range u.Documentation {
+ if d.Source == nil {
+ return fmt.Errorf("insertUnits: unit %q missing source files for %q, %q", u.Path, d.GOOS, d.GOARCH)
+ }
}
- if u.Documentation == nil {
- pathToDoc[u.Path] = nil
- } else {
- pathToDoc[u.Path] = u.Documentation[0]
- }
+ pathToDoc[u.Path] = u.Documentation
if len(u.Imports) > 0 {
pathToImports[u.Path] = u.Imports
}
@@ -463,20 +461,18 @@
func insertDoc(ctx context.Context, db *database.DB,
paths []string,
pathToUnitID map[string]int,
- pathToDoc map[string]*internal.Documentation) (err error) {
+ pathToDoc map[string][]*internal.Documentation) (err error) {
defer derrors.Wrap(&err, "insertDoc")
var docValues []interface{}
for _, path := range paths {
- doc := pathToDoc[path]
- if doc == nil {
- continue
- }
unitID := pathToUnitID[path]
- if doc.GOOS == "" || doc.GOARCH == "" {
- return errors.New("empty GOOS or GOARCH")
+ for _, doc := range pathToDoc[path] {
+ if doc.GOOS == "" || doc.GOARCH == "" {
+ return errors.New("empty GOOS or GOARCH")
+ }
+ docValues = append(docValues, unitID, doc.GOOS, doc.GOARCH, doc.Synopsis, doc.Source)
}
- docValues = append(docValues, unitID, doc.GOOS, doc.GOARCH, doc.Synopsis, doc.Source)
}
uniqueCols := []string{"unit_id", "goos", "goarch"}
docCols := append(uniqueCols, "synopsis", "source")
diff --git a/internal/postgres/unit.go b/internal/postgres/unit.go
index 97044f5..a18c59d 100644
--- a/internal/postgres/unit.go
+++ b/internal/postgres/unit.go
@@ -345,12 +345,10 @@
defer derrors.Wrap(&err, "getUnitWithAllFields(ctx, %q, %q, %q)", um.Path, um.ModulePath, um.Version)
defer middleware.ElapsedStat(ctx, "getUnitWithAllFields")()
+ // Get README and import counts.
query := `
SELECT
- d.goos,
- d.goarch,
- d.synopsis,
- d.source,
+ u.id,
r.file_path,
r.contents,
COALESCE((
@@ -371,8 +369,6 @@
ON p.id = u.path_id
INNER JOIN modules m
ON u.module_id = m.id
- LEFT JOIN documentation d
- ON d.unit_id = u.id
LEFT JOIN readmes r
ON r.unit_id = u.id
WHERE
@@ -381,15 +377,12 @@
AND m.version = $3;`
var (
- d internal.Documentation
- r internal.Readme
- u internal.Unit
+ unitID int
+ r internal.Readme
+ u internal.Unit
)
err = db.db.QueryRow(ctx, query, um.Path, um.ModulePath, um.Version).Scan(
- database.NullIsEmpty(&d.GOOS),
- database.NullIsEmpty(&d.GOARCH),
- database.NullIsEmpty(&d.Synopsis),
- &d.Source,
+ &unitID,
database.NullIsEmpty(&r.Filepath),
database.NullIsEmpty(&r.Contents),
&u.NumImports,
@@ -399,15 +392,38 @@
case sql.ErrNoRows:
return nil, derrors.NotFound
case nil:
- if d.GOOS != "" {
- u.Documentation = []*internal.Documentation{&d}
- }
if r.Filepath != "" {
u.Readme = &r
}
default:
return nil, err
}
+
+ // Get documentation. There can be multiple rows.
+ query = `
+ SELECT goos, goarch, synopsis, source
+ FROM documentation
+ WHERE unit_id = $1
+ `
+ err = db.db.RunQuery(ctx, query, func(rows *sql.Rows) error {
+ var d internal.Documentation
+ if err := rows.Scan(&d.GOOS, &d.GOARCH, &d.Synopsis, &d.Source); err != nil {
+ return err
+ }
+ u.Documentation = append(u.Documentation, &d)
+ return nil
+ }, unitID)
+ if err != nil {
+ return nil, err
+ }
+ // Sort documentation by GOOS/GOARCH.
+ sort.Slice(u.Documentation, func(i, j int) bool {
+ ci := u.Documentation[i].BuildContext()
+ cj := u.Documentation[j].BuildContext()
+ return internal.CompareBuildContexts(ci, cj) < 0
+ })
+
+ // Get other info.
pkgs, err := db.getPackagesInUnit(ctx, um.Path, um.ModulePath, um.Version)
if err != nil {
return nil, err
diff --git a/internal/postgres/unit_test.go b/internal/postgres/unit_test.go
index 9a2a73c..a3daca9 100644
--- a/internal/postgres/unit_test.go
+++ b/internal/postgres/unit_test.go
@@ -446,6 +446,20 @@
t.Fatal(err)
}
+ // Add a module that has documentation for two Go build contexts.
+ m = sample.Module("a.com/twodoc", "v1.2.3", "p")
+ pkg := m.Packages()[0]
+ doc2 := &internal.Documentation{
+ GOOS: "windows",
+ GOARCH: "amd64",
+ Synopsis: pkg.Documentation[0].Synopsis + " 2",
+ Source: pkg.Documentation[0].Source,
+ }
+ pkg.Documentation = append(pkg.Documentation, doc2)
+ if err := testDB.InsertModule(ctx, m); err != nil {
+ t.Fatal(err)
+ }
+
for _, test := range []struct {
name, path, modulePath, version string
want *internal.Unit
@@ -555,6 +569,19 @@
},
),
},
+ {
+ name: "package with two docs",
+ path: "a.com/twodoc/p",
+ modulePath: "a.com/twodoc",
+ version: "v1.2.3",
+ want: func() *internal.Unit {
+ u := unit("a.com/twodoc/p", "a.com/twodoc", "v1.2.3", "p",
+ nil,
+ []string{"p"})
+ u.Documentation = append(u.Documentation, doc2)
+ return u
+ }(),
+ },
} {
t.Run(test.name, func(t *testing.T) {
um := sample.UnitMeta(