blob: aaa282678f34af0ded1edec775bbcc69d5112f8a [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"
"github.com/Masterminds/squirrel"
"golang.org/x/pkgsite/internal"
"golang.org/x/pkgsite/internal/database"
"golang.org/x/pkgsite/internal/derrors"
"golang.org/x/pkgsite/internal/middleware/stats"
)
// getPackageSymbols returns all of the symbols for a given package path and module path.
func getPackageSymbols(ctx context.Context, ddb *database.DB, packagePath, modulePath string,
) (_ *internal.SymbolHistory, err error) {
defer derrors.Wrap(&err, "getPackageSymbols(ctx, ddb, %q, %q)", packagePath, modulePath)
defer stats.Elapsed(ctx, "getPackageSymbols")()
query := packageSymbolQueryJoin(
squirrel.Select(
"s1.name AS symbol_name",
"s2.name AS parent_symbol_name",
"ps.section",
"ps.type",
"ps.synopsis",
"m.version",
"d.goos",
"d.goarch"), packagePath, modulePath).
OrderBy("CASE WHEN ps.type='Type' THEN 0 ELSE 1 END").
OrderBy("s1.name")
q, args, err := query.PlaceholderFormat(squirrel.Dollar).ToSql()
if err != nil {
return nil, err
}
sh, collect := collectSymbolHistory(func(sh *internal.SymbolHistory, sm internal.SymbolMeta, v string, build internal.BuildContext) error {
if sm.Section == internal.SymbolSectionTypes && sm.Kind != internal.SymbolKindType {
_, err := sh.GetSymbol(sm.ParentName, v, build)
if err != nil {
return fmt.Errorf("could not find parent for %q: %v", sm.Name, err)
}
return nil
}
return nil
})
if err := ddb.RunQuery(ctx, q, collect, args...); err != nil {
return nil, err
}
return sh, nil
}
func packageSymbolQueryJoin(query squirrel.SelectBuilder, pkgPath, modulePath string) squirrel.SelectBuilder {
return query.From("modules m").
Join("units u on u.module_id = m.id").
Join("documentation d ON d.unit_id = u.id").
Join("documentation_symbols ds ON ds.documentation_id = d.id").
Join("package_symbols ps ON ps.id = ds.package_symbol_id").
Join("paths p1 ON u.path_id = p1.id").
Join("symbol_names s1 ON ps.symbol_name_id = s1.id").
Join("symbol_names s2 ON ps.parent_symbol_name_id = s2.id").
Where(squirrel.Eq{"p1.path": pkgPath}).
Where(squirrel.Eq{"m.module_path": modulePath}).
Where("NOT m.incompatible").
Where(squirrel.Eq{"m.version_type": "release"})
}
func collectSymbolHistory(check func(sh *internal.SymbolHistory, sm internal.SymbolMeta, v string, build internal.BuildContext) error) (*internal.SymbolHistory, func(rows *sql.Rows) error) {
sh := internal.NewSymbolHistory()
return sh, func(rows *sql.Rows) (err error) {
defer derrors.Wrap(&err, "collectSymbolHistory")
var (
sm internal.SymbolMeta
build internal.BuildContext
v string
)
if err := rows.Scan(
&sm.Name,
&sm.ParentName,
&sm.Section,
&sm.Kind,
&sm.Synopsis,
&v,
&build.GOOS,
&build.GOARCH,
); err != nil {
return fmt.Errorf("row.Scan(): %v", err)
}
if err := check(sh, sm, v, build); err != nil {
return fmt.Errorf("check(): %v", err)
}
sh.AddSymbol(sm, v, build)
return nil
}
}