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 (
	
	
	
	

	
	
	
	
)

func ( context.Context,  *database.DB, ,  string,
	 map[string]int,
	 map[string]map[int]*internal.Documentation) ( error) {
	defer derrors.WrapStack(&, "insertSymbols(ctx, db, %q, %q, pathToID, pathToDocs)", , )
	,  := upsertSymbolNamesReturningIDs(, , )
	if  != nil {
		return 
	}
	,  := upsertPackageSymbolsReturningIDs(, , , , , )
	if  != nil {
		return 
	}
	if  := upsertDocumentationSymbols(, , , );  != nil {
		return 
	}
	return upsertSymbolHistory(, , , , ,
		, , )
}

type packageSymbol struct {
	name     string
	synopsis string
parentName is a unique key in packageSymbol because the section can change with the name and synopsis remaining the same. For example: https://pkg.go.dev/go/types@go1.8#Universe is in the Variables section, with parentName Universe. https://pkg.go.dev/go/types@go1.16#Universe is in the Types section, with parentName Scope. https://pkg.go.dev/github.com/89z/page@v1.2.1#Help is in the Types section under type InputMode. https://pkg.go.dev/github.com/89z/page@v1.1.3#Help is in the Types section under ScreenMode.
	parentName string
}

func ( context.Context,  *database.DB,
	 map[string]map[packageSymbol]int,
	 map[string]map[int]*internal.Documentation) ( error) {
	defer derrors.WrapStack(&, "upsertDocumentationSymbols(ctx, db, pathToPkgsymID, pathToDocIDToDoc)")
Create a map of documentation_id TO package_symbol_id set. This will be used to verify that all package_symbols for the unit have been inserted.
	 := map[int]map[int]bool{}
	for ,  := range  {
		for ,  := range  {
			 := updateSymbols(.API, func( *internal.SymbolMeta) error {
				,  := []
				if ! {
					return fmt.Errorf("path could not be found: %q", )
				}
				 := packageSymbol{synopsis: .Synopsis, name: .Name, parentName: .ParentName}
				,  := []
				if ! {
					return fmt.Errorf("package symbol could not be found: %v", )
				}
				_,  = []
				if ! {
					[] = map[int]bool{}
				}
				[][] = true
				return nil
			})
			if  != nil {
				return 
			}
		}
	}
Fetch all existing rows in documentation_symbols for this unit using the documentation IDs. Keep track of which rows already exist in documentation_symbols using gotDocIDToPkgsymIDs.
	var  []interface{}
	for  := range  {
		 = append(, )
	}
	 := map[int]map[int]bool{}
	 := func( *sql.Rows) error {
		var , ,  int
		if  := .Scan(&, &, &);  != nil {
			return fmt.Errorf("row.Scan(): %v", )
		}
The package_symbol_id in the documentation_symbols table does not match the one we want to insert. This can happen if we change the package_symbol_id. In that case, do not add this to the map, so that we can upsert below. See https://go-review.googlesource.com/c/pkgsite/+/315309 and https://go-review.googlesource.com/c/pkgsite/+/315310 where the package_symbol_id was potentially changed.
			return nil
		}
		if ,  := []; ! {
			[] = map[int]bool{}
		}
		[][] = true
		return nil
	}
	if  := .RunQuery(, `
        SELECT
            ds.id,
            ds.documentation_id,
            ds.package_symbol_id
        FROM documentation_symbols ds
        WHERE documentation_id = ANY($1);`, , pq.Array());  != nil {
		return 
	}
Get the difference between the documentation_symbols for this package, and the ones that already exist in the documentation_symbols table. Only insert rows that do not already exist. Sort first to prevent deadlocks.
	var  []int
	for  := range  {
		 = append(, )
	}
	sort.Ints()
	var  []interface{}
	for ,  := range  {
		 := []
		for  := range [] {
			if ![] {
				 = append(, , )
			}
		}
Upsert the rows. Note that the order of pkgsymcols must match that of the SELECT query in the collect function.
	 := []string{"documentation_id", "package_symbol_id"}
	if  := .BulkInsert(, "documentation_symbols", ,
		, `
			ON CONFLICT (documentation_id, package_symbol_id)
			DO UPDATE SET
				documentation_id=excluded.documentation_id,
				package_symbol_id=excluded.package_symbol_id`);  != nil {
		return 
	}
	return nil
}

func ( context.Context,  *database.DB,
	 string,
	 map[string]int,
	 map[string]int,
	 map[string]map[int]*internal.Documentation) ( map[string]map[packageSymbol]int,  error) {
	defer derrors.WrapStack(&, "upsertPackageSymbolsReturningIDs(ctx, db, %q, pathToID, pathToDocIDToDoc)", )

	 := map[int]string{}
	for ,  := range  {
		[] = 
	}
	var  []string
	 := map[int]string{}
	for ,  := range  {
		[] = 
		 = append(, )
	}

	 := []
	if  == 0 {
		return nil, fmt.Errorf("modulePathID cannot be 0: %q", )
	}
	 := map[string]map[packageSymbol]int{}
	 := func( *sql.Rows) error {
		var (
			, , ,  int
			                             string
		)
		if  := .Scan(&, &, &, &, &);  != nil {
			return fmt.Errorf("row.Scan(): %v", )
		}
		 := []
		if ,  := []; ! {
			[] = map[packageSymbol]int{}
		}

		 := []
		if  == "" {
			return fmt.Errorf("symbol name cannot be empty: %d", )
		}
		,  := []
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
		}
		[][packageSymbol{
			synopsis:   ,
			name:       ,
			parentName: ,
		}] = 
		return nil
This query fetches more that just the package symbols that we want. The relevant package symbols are filtered above.
	if  := .RunQuery(, `
        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);`, , , pq.Array());  != nil {
		return nil, 
	}
Sort to prevent deadlocks.
	var  []string
	for  := range  {
		 = append(, )
	}
	sort.Strings()

	var  []interface{}
	for ,  := range  {
		 := []
		 := []
		if  == 0 {
			return nil, fmt.Errorf("pathID cannot be 0: %q", )
		}
Sort to prevent deadlocks.
			sort.Slice(.API, func(,  int) bool {
				return .API[].Name < .API[].Name
			})

			if  := updateSymbols(.API, func( *internal.SymbolMeta) error {
				 := packageSymbol{synopsis: .Synopsis, name: .Name, parentName: .ParentName}
				 := [.Name]
				if  == 0 {
					return fmt.Errorf("symID cannot be 0: %q", .Name)
				}
				if .ParentName == "" {
					.ParentName = .Name
				}
				 := [.ParentName]
				if  == 0 {
					return fmt.Errorf("parentSymID cannot be 0: %q", .ParentName)
				}
				if ,  := [][]; ! {
					 = append(, ,
						, , , .Section, .Kind,
						.Synopsis)
				}
				return nil
			});  != nil {
				return nil, 
			}
		}
The order of pkgsymcols must match that of the SELECT query in the collect function.
	 := []string{"id", "package_path_id", "symbol_name_id", "parent_symbol_name_id", "synopsis"}
	if  := .BulkInsertReturning(, "package_symbols",
		[]string{
			"package_path_id",
			"module_path_id",
			"symbol_name_id",
			"parent_symbol_name_id",
			"section",
			"type",
			"synopsis",
		}, , database.OnConflictDoNothing, , );  != nil {
		return nil, 
	}
	return , nil
}

func ( context.Context,  *database.DB,
	 map[string]map[int]*internal.Documentation) ( map[string]int,  error) {
	defer derrors.WrapStack(&, "upsertSymbolNamesReturningIDs")
	var  []string
	for ,  := range  {
		for ,  := range  {
			if  := updateSymbols(.API, func( *internal.SymbolMeta) error {
				 = append(, .Name)
				return nil
			});  != nil {
				return nil, 
			}
		}
	}
	 := `
        SELECT id, name
        FROM symbol_names
        WHERE name = ANY($1);`
	 := map[string]int{}
	 := func( *sql.Rows) error {
		var (
			   int
			 string
		)
		if  := .Scan(&, &);  != nil {
			return fmt.Errorf("row.Scan(): %v", )
		}
		[] = 
		if  == 0 {
			return fmt.Errorf("id can't be 0: %q", )
		}
		return nil
	}
	if  := .RunQuery(, , , pq.Array());  != nil {
		return nil, 
	}

	sort.Strings()
	var  []interface{}
	for ,  := range  {
		if ,  := []; ! {
			 = append(, )
		}
	}
	if  := .BulkInsertReturning(, "symbol_names", []string{"name"},
		, database.OnConflictDoNothing, []string{"id", "name"}, );  != nil {
		return nil, 
	}
	return , nil
}
getUnitSymbols returns all of the symbols for the given unitID.
func ( context.Context,  *database.DB,  int) ( map[internal.BuildContext][]*internal.Symbol,  error) {
	defer derrors.Wrap(&, "getUnitSymbols(ctx, db, %d)", )
Fetch all symbols for the unit. Order by symbol_type "Type" first, so that when we collect the children the structs for these symbols will already be created.
	 := `
        SELECT
            s1.name AS symbol_name,
            s2.name AS parent_symbol_name,
            ps.section,
            ps.type,
            ps.synopsis,
            d.goos,
            d.goarch
        FROM documentation_symbols ds
        INNER JOIN documentation d ON d.id = ds.documentation_id
        INNER JOIN package_symbols ps ON ds.package_symbol_id = ps.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 d.unit_id = $1
buildToSymbols contains all of the symbols for this unit, grouped by build context.
buildToNameToType 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.
	 := map[internal.BuildContext]map[string]*internal.Symbol{}
	 := func( *sql.Rows) error {
		var (
			    internal.SymbolMeta
			 internal.BuildContext
		)
		if  := .Scan(
			&.Name, &.ParentName,
			&.Section, &.Kind, &.Synopsis,
			&.GOOS, &.GOARCH);  != nil {
			return fmt.Errorf("row.Scan(): %v", )
		}

		 := &internal.Symbol{
			SymbolMeta: ,
			GOOS:       .GOOS,
			GOARCH:     .GOARCH,
		}
For symbols that belong to a type, map that symbol as a children of the parent type.
		case internal.SymbolSectionTypes:
			if .Kind == internal.SymbolKindType {
				,  := []
				if ! {
					[] = map[string]*internal.Symbol{}
				}
				[][.Name] = 
				[] = append([], )
			} else {
				,  := []
				if ! {
					return fmt.Errorf("build context %v for parent type %q could not be found for symbol %q", , .ParentName, .Name)
				}
				,  := [.ParentName]
				if ! {
					return fmt.Errorf("parent type %q could not be found for symbol %q", .ParentName, .Name)
				}
				.Children = append(.Children, &)
			}
		default:
			[] = append([], )
		}
		return nil
	}
	if  := .RunQuery(, , , );  != nil {
		return nil, 
	}
	return , nil
}

func ( []*internal.Symbol,  func( *internal.SymbolMeta) error) error {
	for ,  := range  {
		if  := (&.SymbolMeta);  != nil {
			return 
		}
		for ,  := range .Children {
			if  := ();  != nil {
				return 
			}
		}
	}
	return nil