// 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 } }