blob: 99e98e9746e83ad1aea88e7a17a9a77a3e1a404f [file] [log] [blame]
// 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"
"golang.org/x/pkgsite/internal"
"golang.org/x/pkgsite/internal/derrors"
)
// GetPackageSymbols returns all of the symbols for a given package path and module path.
func (db *DB) GetPackageSymbols(ctx context.Context, packagePath, modulePath string,
) (_ map[string]map[string]*internal.UnitSymbol, err error) {
defer derrors.Wrap(&err, "GetPackageSymbols(ctx, db, %q, %q)", packagePath, modulePath)
query := `
SELECT
s1.name AS symbol_name,
s2.name AS parent_symbol_name,
ps.section,
ps.type,
ps.synopsis,
m.version,
d.goos,
d.goarch
FROM modules m
INNER JOIN units u ON u.module_id = m.id
INNER JOIN new_documentation d ON d.unit_id = u.id
INNER JOIN documentation_symbols ds ON ds.documentation_id = d.id
INNER JOIN package_symbols ps ON ps.id = ds.package_symbol_id
INNER JOIN paths p1 ON u.path_id = p1.id
INNER JOIN symbol_names s1 ON ps.symbol_name_id = s1.id
INNER JOIN symbol_names s2 ON ps.parent_symbol_name_id = s2.id
WHERE p1.path = $1 AND m.module_path = $2
ORDER BY
CASE WHEN ps.type='Type' THEN 0 ELSE 1 END,
symbol_name;`
// versionToNameToUnitSymbol contains all of the types for this unit,
// grouped by name and build context. This is used to keep track of the
// parent types, so that we can map the children to those symbols.
versionToNameToUnitSymbol := map[string]map[string]*internal.UnitSymbol{}
collect := func(rows *sql.Rows) error {
var (
newUS internal.UnitSymbol
build internal.BuildContext
)
if err := rows.Scan(
&newUS.Name,
&newUS.ParentName,
&newUS.Section,
&newUS.Kind,
&newUS.Synopsis,
&newUS.Version,
&build.GOOS,
&build.GOARCH,
); err != nil {
return fmt.Errorf("row.Scan(): %v", err)
}
if newUS.Section == internal.SymbolSectionTypes && newUS.Kind != internal.SymbolKindType {
if err := validateChildSymbol(&newUS, build, versionToNameToUnitSymbol); err != nil {
return err
}
}
nts, ok := versionToNameToUnitSymbol[newUS.Version]
if !ok {
nts = map[string]*internal.UnitSymbol{}
versionToNameToUnitSymbol[newUS.Version] = nts
}
us, ok := nts[newUS.Name]
if !ok {
us = &newUS
nts[newUS.Name] = us
}
us.AddBuildContext(build)
return nil
}
if err := db.db.RunQuery(ctx, query, collect, packagePath, modulePath); err != nil {
return nil, err
}
return versionToNameToUnitSymbol, nil
}
func validateChildSymbol(us *internal.UnitSymbol, build internal.BuildContext,
versionToNameToUnitSymbol map[string]map[string]*internal.UnitSymbol) error {
nameToUnitSymbol, ok := versionToNameToUnitSymbol[us.Version]
if !ok {
return fmt.Errorf("version %q could not be found: %q", us.Version, us.Name)
}
parent, ok := nameToUnitSymbol[us.ParentName]
if !ok {
return fmt.Errorf("parent %q could not be found at version %q: %q",
us.ParentName, us.Version, us.Name)
}
if !parent.SupportsBuild(build) {
return fmt.Errorf("parent %q does not have build %v at version %q: %q",
us.ParentName, build, us.Version, us.Name)
}
return nil
}