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 (
	
	
	

	
	
	
	
	
	
	
)
getPackageSymbols returns all of the symbols for a given package path and module path.
func ( context.Context,  *database.DB, ,  string,
) ( *internal.SymbolHistory,  error) {
	defer derrors.Wrap(&, "getPackageSymbols(ctx, ddb, %q, %q)", , )
	defer middleware.ElapsedStat(, "getPackageSymbols")()

	 := 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"), , ).
		OrderBy("CASE WHEN ps.type='Type' THEN 0 ELSE 1 END").
		OrderBy("s1.name")
	, ,  := .PlaceholderFormat(squirrel.Dollar).ToSql()
	if  != nil {
		return nil, 
	}
	,  := collectSymbolHistory(func( *internal.SymbolHistory,  internal.SymbolMeta,  string,  internal.BuildContext) error {
		if .Section == internal.SymbolSectionTypes && .Kind != internal.SymbolKindType {
			,  := .GetSymbol(.ParentName, , )
			if  != nil {
				return fmt.Errorf("could not find parent for %q: %v", .Name, )
			}
			return nil
		}
		return nil
	})
	if  := .RunQuery(, , , ...);  != nil {
		return nil, 
	}
	return , nil
}

func ( squirrel.SelectBuilder, ,  string) squirrel.SelectBuilder {
	return .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": }).
		Where(squirrel.Eq{"m.module_path": }).
		Where("NOT m.incompatible").
		Where(squirrel.Eq{"m.version_type": "release"})
}

func ( func( *internal.SymbolHistory,  internal.SymbolMeta,  string,  internal.BuildContext) error) (*internal.SymbolHistory, func( *sql.Rows) error) {
	 := internal.NewSymbolHistory()
	return , func( *sql.Rows) ( error) {
		defer derrors.Wrap(&, "collectSymbolHistory")
		var (
			    internal.SymbolMeta
			 internal.BuildContext
			     string
		)
		if  := .Scan(
			&.Name,
			&.ParentName,
			&.Section,
			&.Kind,
			&.Synopsis,
			&,
			&.GOOS,
			&.GOARCH,
		);  != nil {
			return fmt.Errorf("row.Scan(): %v", )
		}
		if  := (, , , );  != nil {
			return fmt.Errorf("check(): %v", )
		}
		.AddSymbol(, , )
		return nil
	}
}

func ( context.Context,  *database.DB,
	, ,  string) ( error) {
	defer derrors.Wrap(&, "upsertSearchDocumentSymbols(ctx, ddb, %q, %q, %q)", , , )
	defer middleware.ElapsedStat(, "upsertSearchDocumentSymbols")()

	if !experiment.IsActive(, internal.ExperimentInsertSymbolSearchDocuments) {
		return nil
	}
If a user is looking for the symbol "DB.Begin", from package database/sql, we want them to be able to find this by searching for "DB.Begin" and "sql.DB.Begin". Searching for "sql.DB", "DB", "Begin" or "sql.DB" will not return "DB.Begin".
	 := packageSymbolQueryJoin(squirrel.Select(
		"p1.id AS package_path_id",
Group the build contexts as an array, with the format "<goos>/<goarch>". We only care about the build contexts when the default goos/goarch for the package page does not contain the matching symbol. TODO(https://golang/issue/44142): We could probably get away with storing just the GOOS value, since we don't really need the GOARCH to link to a symbol page. If we do that we should also change the column type to []goos. Store in order of the build context list at internal.BuildContexts.
		`ARRAY_AGG(FORMAT('%s/%s', d.goos, d.goarch)
			ORDER BY
				CASE WHEN d.goos='linux' THEN 0
				WHEN d.goos='windows' THEN 1
				WHEN d.goos='darwin' THEN 2
If a user is looking for the symbol "DB.Begin", from package database/sql, we want them to be able to find this by searching for "DB.Begin", "Begin", and "sql.DB.Begin". Searching for "sql.DB" or "DB" will not return "DB.Begin". Index <package>.<identifier> (i.e. "sql.DB.Begin")
		`SETWEIGHT(
			TO_TSVECTOR('simple', concat(s1.name, ' ', concat(u.name, '.', s1.name))),
Index <identifier>, including the parent name (i.e. DB.Begin).
			`SETWEIGHT(
				TO_TSVECTOR('simple', s1.name),
Index <identifier> without parent name (i.e. "Begin"). This is weighted less, so that if other symbols are just named "Begin" they will rank higher in a search for "Begin".
			`SETWEIGHT(
				TO_TSVECTOR('simple', split_part(s1.name, '.', 2)),
				'B') AS tokens`,
	), , ).
		Where(squirrel.Eq{"m.version": }).
		GroupBy("p1.id, s1.id", "tokens").
		OrderBy("s1.name")

	, ,  := .PlaceholderFormat(squirrel.Dollar).ToSql()
	if  != nil {
		return 
	}

	var  []interface{}
	 := func( *sql.Rows) ( error) {
		var (
			 int
			  int
			        string
			 []string
		)
		if  := .Scan(
			&,
			&,
			pq.Array(&),
			&,
		);  != nil {
			return fmt.Errorf("row.Scan(): %v", )
		}
		 = append(, , , pq.Array(), )
		return nil
	}
	if  := .RunQuery(, , , ...);  != nil {
		return 
	}

	 := []string{"package_path_id", "symbol_name_id", "build_contexts", "tsv_symbol_tokens"}
	return .BulkInsert(, "symbol_search_documents", , ,
		`ON CONFLICT (package_path_id, symbol_name_id)
				DO UPDATE
				SET
					build_contexts=excluded.build_contexts,
					tsv_symbol_tokens=excluded.tsv_symbol_tokens`)