in internal/postgres/symbol.go [195:325]
func upsertPackageSymbolsReturningIDs(ctx context.Context, db *database.DB,
modulePathID int,
pathToID map[string]int,
nameToID map[string]int,
pathToDocIDToDoc map[string]map[int]*internal.Documentation) (_ map[string]map[packageSymbol]int, err error) {
defer derrors.WrapStack(&err, "upsertPackageSymbolsReturningIDs(ctx, db, %d, pathToID, pathToDocIDToDoc)", modulePathID)
idToPath := map[int]string{}
for path, id := range pathToID {
idToPath[id] = path
}
var names []string
idToSymbolName := map[int]string{}
for name, id := range nameToID {
idToSymbolName[id] = name
names = append(names, name)
}
pathTopkgsymToID := map[string]map[packageSymbol]int{}
collect := func(rows *sql.Rows) error {
var (
id, pathID, symbolID, parentSymbolID int
synopsis string
)
if err := rows.Scan(&id, &pathID, &symbolID, &parentSymbolID, &synopsis); err != nil {
return fmt.Errorf("row.Scan(): %v", err)
}
path := idToPath[pathID]
if _, ok := pathTopkgsymToID[path]; !ok {
pathTopkgsymToID[path] = map[packageSymbol]int{}
}
sym := idToSymbolName[symbolID]
if sym == "" {
return fmt.Errorf("symbol name cannot be empty: %d", symbolID)
}
parentSym, ok := idToSymbolName[parentSymbolID]
if !ok {
// A different variable of this symbol was previously inserted.
// Don't add this to pathTopkgsymToID, since it's not the package
// symbol that we want.
// For example:
// https://dev-pkg.go.dev/github.com/fastly/kingpin@v1.2.6#TokenShort
// and
// https://pkg.go.dev/github.com/fastly/kingpin@v1.3.7#TokenShort
// have the same synopsis, but different parents and sections.
return nil
}
pathTopkgsymToID[path][packageSymbol{
synopsis: synopsis,
name: sym,
parentName: parentSym,
}] = id
return nil
}
// This query fetches more that just the package symbols that we want.
// The relevant package symbols are filtered above.
if err := db.RunQuery(ctx, `
SELECT
ps.id,
ps.package_path_id,
ps.symbol_name_id,
ps.parent_symbol_name_id,
ps.synopsis
FROM package_symbols ps
INNER JOIN symbol_names s ON ps.symbol_name_id = s.id
WHERE module_path_id = $1 AND s.name = ANY($2);`, collect, modulePathID, pq.Array(names)); err != nil {
return nil, err
}
// Sort to prevent deadlocks.
var paths []string
for path := range pathToDocIDToDoc {
paths = append(paths, path)
}
sort.Strings(paths)
var packageSymbols []interface{}
for _, path := range paths {
docs := pathToDocIDToDoc[path]
pathID := pathToID[path]
if pathID == 0 {
return nil, fmt.Errorf("pathID cannot be 0: %q", path)
}
for _, doc := range docs {
// Sort to prevent deadlocks.
sort.Slice(doc.API, func(i, j int) bool {
return doc.API[i].Name < doc.API[j].Name
})
if err := updateSymbols(doc.API, func(sm *internal.SymbolMeta) error {
ps := packageSymbol{synopsis: sm.Synopsis, name: sm.Name, parentName: sm.ParentName}
symID := nameToID[sm.Name]
if symID == 0 {
return fmt.Errorf("symID cannot be 0: %q", sm.Name)
}
if sm.ParentName == "" {
sm.ParentName = sm.Name
}
parentID := nameToID[sm.ParentName]
if parentID == 0 {
return fmt.Errorf("parentSymID cannot be 0: %q", sm.ParentName)
}
if _, ok := pathTopkgsymToID[path][ps]; !ok {
packageSymbols = append(packageSymbols, pathID,
modulePathID, symID, parentID, sm.Section, sm.Kind,
sm.Synopsis)
}
return nil
}); err != nil {
return nil, err
}
}
}
// The order of pkgsymcols must match that of the SELECT query in the
//collect function.
pkgsymcols := []string{"id", "package_path_id", "symbol_name_id", "parent_symbol_name_id", "synopsis"}
if err := db.BulkInsertReturning(ctx, "package_symbols",
[]string{
"package_path_id",
"module_path_id",
"symbol_name_id",
"parent_symbol_name_id",
"section",
"type",
"synopsis",
}, packageSymbols, database.OnConflictDoNothing, pkgsymcols, collect); err != nil {
return nil, err
}
return pathTopkgsymToID, nil
}