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