Copyright 2019 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 (
	
	
	
	
	

	
	
	
	
	
	
	
	
	
)
GetUnitMeta returns information about the "best" entity (module, path or directory) with the given path. The module and version arguments provide additional constraints. If the module is unknown, pass internal.UnknownModulePath; if the version is unknown, pass internal.LatestVersion. The rules for picking the best are: 1. If the version is known but the module path is not, choose the longest module path at that version that contains fullPath. 2. Otherwise, find the latest "good" version (in the modules table) that contains fullPath. a. First, follow the algorithm of the go command: prefer longer module paths, and find the latest unretracted version, using semver but preferring release to pre-release. b. If no modules have latest-version information, find the latest by sorting the versions we do have: again first by module path length, then by version.
func ( *DB) ( context.Context, , ,  string) ( *internal.UnitMeta,  error) {
	defer derrors.WrapStack(&, "DB.GetUnitMeta(ctx, %q, %q, %q)", , , )
	defer middleware.ElapsedStat(, "DB.GetUnitMeta")()

	 := 
	 := 
	var  *internal.LatestModuleVersions
	if  == internal.LatestVersion {
		, , ,  = .getLatestUnitVersion(, , )
		if  != nil {
			return nil, 
		}
	}
	return .getUnitMetaWithKnownLatestVersion(, , , , )
}

func ( *DB) ( context.Context, , ,  string,  *internal.LatestModuleVersions) ( *internal.UnitMeta,  error) {
	defer derrors.WrapStack(&, "getUnitMetaKnownVersion")
	defer middleware.ElapsedStat(, "getUnitMetaKnownVersion")()

	 := squirrel.Select(
		"m.module_path",
		"m.version",
		"m.commit_time",
		"m.source_info",
		"m.has_go_mod",
		"m.redistributable",
		"u.name",
		"u.redistributable",
		"u.license_types",
		"u.license_paths").
		From("modules m").
		Join("units u on u.module_id = m.id").
		Join("paths p ON p.id = u.path_id").Where(squirrel.Eq{"p.path": }).
		PlaceholderFormat(squirrel.Dollar)

	if internal.DefaultBranches[] {
		 = .
			Join("version_map vm ON m.id = vm.module_id").
			Where("vm.requested_version = ?", )
	} else {
		 = .Where(squirrel.Eq{"version": })
	}
If we don't know the module, look for the one with the longest series path.
		 = .OrderBy("m.series_path DESC").Limit(1)
	} else {
		 = .Where(squirrel.Eq{"m.module_path": })
	}

	, ,  := .ToSql()
	if  != nil {
		return nil, 
	}
	var (
		 []string
		 []string
		           = internal.UnitMeta{Path: }
	)
	 = .db.QueryRow(, , ...).Scan(
		&.ModulePath,
		&.Version,
		&.CommitTime,
		jsonbScanner{&.SourceInfo},
		&.HasGoMod,
		&.ModuleInfo.IsRedistributable,
		&.Name,
		&.IsRedistributable,
		pq.Array(&),
		pq.Array(&))
	if  == sql.ErrNoRows {
		return nil, derrors.NotFound
	}
	if  != nil {
		return nil, 
	}

	,  := zipLicenseMetadata(, )
	if  != nil {
		return nil, 
	}

	if .bypassLicenseCheck {
		.IsRedistributable = true
	}
	.Licenses = 
If we don't have the latest version information, try to get it. We can be here if there is really no info (in which case we are repeating some work, but it's fast), or if we are ignoring the info (for instance, if all versions were retracted).
	if  == nil {
		,  = .GetLatestModuleVersions(, .ModulePath)
		if  != nil {
			return nil, 
		}
	}
	if  != nil {
		.PopulateModuleInfo(&.ModuleInfo)
	}
	return &, nil
}
getLatestUnitVersion gets the latest version of requestedModulePath that contains fullPath. See GetUnitMeta for more details.
func ( *DB) ( context.Context, ,  string) (
	,  string,  *internal.LatestModuleVersions,  error) {

	defer derrors.WrapStack(&, "getLatestUnitVersion(%q, %q)", , )
	defer middleware.ElapsedStat(, "getLatestUnitVersion")()

If we don't know the module path, try each possible module path from longest to shortest.
	if  == internal.UnknownModulePath {
		 = internal.CandidateModulePaths()
Get latest-version information for all possible modules, from longest to shortest path.
	,  := .getMultiLatestModuleVersions(, )
	if  != nil {
		return "", "", nil, 
	}
Collect all the versions of this module that contain fullPath.
		 := squirrel.Select("m.version").
			From("modules m").
			Join("units u on u.module_id = m.id").
			Join("paths p ON p.id = u.path_id").
			Where(squirrel.Eq{"m.module_path": .ModulePath}).
			Where(squirrel.Eq{"p.path": })
		, ,  := .PlaceholderFormat(squirrel.Dollar).ToSql()
		if  != nil {
			return "", "", nil, 
		}
		,  := collectStrings(, .db, , ...)
		if  != nil {
			return "", "", nil, 
Remove retracted versions.
If there are no unretracted versions, move on. If we fall out of the loop we will pick the latest retracted version.
		if len() == 0 {
			continue
Choose the latest version. If the cooked latest version is compatible, then by the logic of internal/version.Latest (which matches the go command), either incompatible versions should be ignored or there were no incompatible versions. In either case, remove them.
		if !version.IsIncompatible(.CookedVersion) {
			 = version.RemoveIf(, version.IsIncompatible)
The good version should never be later than the cooked version. https://golang.org/issue/43265 documents how that could happen: 1. A pseudo-version is created off of a tagged version. In this case, golang.zx2c4.com/wireguard@v0.0.20201119-0.20210128142622-6a128dde71d9. 2. All tagged versions are retracted. 3. A new pseudo-version is published. In this case, v0.0.0-20210506092213-60a26371f42f. Technically, the first pseudo-version is higher than the second, so it is the latest good version. But the proxy's latest endpoint returns the second pseudo-version, so that is the cooked version (and what the go command will download).
		if .CookedVersion != "" {
			 = version.RemoveIf(, func( string) bool {
				return version.Later(, .CookedVersion)
			})
		}
		 = version.LatestOf()
		break
	}
	if  != "" {
		return .ModulePath, , , nil
If we don't have latest-version info for any path (or there are no unretracted versions for paths where we do), fall back to finding the latest good version from the longest path. We can't determine deprecations or retractions, and the "go get" command won't download the module unless a specific version is supplied. But we can still show the latest version we have.
	 := squirrel.Select("m.module_path", "m.version").
		From("modules m").
		Join("units u on u.module_id = m.id").
		Join("paths p ON p.id = u.path_id").
Like the go command, order first by path length, then by release version, then prerelease. Without latest-version information, we ignore all adjustments for incompatible and retracted versions.
		OrderBy("m.series_path DESC", "m.version_type = 'release' DESC", "m.sort_version DESC").
		Limit(1)
	, ,  := .PlaceholderFormat(squirrel.Dollar).ToSql()
	if  != nil {
		return "", "", nil, 
	}
	 = .db.QueryRow(, , ...).Scan(&, &)
	if  == sql.ErrNoRows {
		return "", "", nil, derrors.NotFound
	}
	if  != nil {
		return "", "", nil, 
	}
	return , , nil, nil
}
GetUnit returns a unit from the database, along with all of the data associated with that unit. If bc is not nil, get only the Documentation that matches it (or nil if none do).
func ( *DB) ( context.Context,  *internal.UnitMeta,  internal.FieldSet,  internal.BuildContext) ( *internal.Unit,  error) {
	defer derrors.WrapStack(&, "GetUnit(ctx, %q, %q, %q, %v)", .Path, .ModulePath, .Version, )

	 := &internal.Unit{UnitMeta: *}
	if &internal.WithMain != 0 {
		,  = .getUnitWithAllFields(, , )
		if  != nil {
			return nil, 
		}
	}
	if &internal.WithImports == 0 &&
		&internal.WithLicenses == 0 {
		return , nil
	}

	defer middleware.ElapsedStat(, "GetUnit")()
	,  := .getUnitID(, .Path, .ModulePath, .Version)
	if  != nil {
		return nil, 
	}

	if &internal.WithImports != 0 {
		,  := .getImports(, )
		if  != nil {
			return nil, 
		}
		if len() > 0 {
			.Imports = 
			.NumImports = len()
		}
	}
	if &internal.WithLicenses != 0 {
		,  := .getLicenses(, .Path, .ModulePath, )
		if  != nil {
			return nil, 
		}
		.LicenseContents = 
	}
	if .bypassLicenseCheck {
		.IsRedistributable = true
	} else {
		.RemoveNonRedistributableData()
	}
	return , nil
}

func ( *DB) ( context.Context, , ,  string) ( int,  error) {
	defer derrors.WrapStack(&, "getUnitID(ctx, %q, %q, %q)", , , )
	defer middleware.ElapsedStat(, "getUnitID")()
	var  int
	 := `
		SELECT u.id
		FROM units u
		INNER JOIN paths p ON (p.id = u.path_id)
		INNER JOIN modules m ON (u.module_id = m.id)
		WHERE
			p.path = $1
			AND m.module_path = $2
			AND m.version = $3;`
	 = .db.QueryRow(, , , , ).Scan(&)
	switch  {
	case sql.ErrNoRows:
		return 0, derrors.NotFound
	case nil:
		return , nil
	default:
		return 0, 
	}
}
getImports returns the imports corresponding to unitID.
func ( *DB) ( context.Context,  int) ( []string,  error) {
	defer derrors.WrapStack(&, "getImports(ctx, %d)", )
	defer middleware.ElapsedStat(, "getImports")()
	return collectStrings(, .db, `
		SELECT to_path
		FROM package_imports
		WHERE unit_id = $1`, )
}
getPackagesInUnit returns all of the packages in a unit from a module_id, including the package that lives at fullPath, if present.
func ( *DB) ( context.Context,  string,  int) ( []*internal.PackageMeta,  error) {
	return getPackagesInUnit(, .db, , "", "", , .bypassLicenseCheck)
}

func ( context.Context,  *database.DB, , ,  string,  int,  bool) ( []*internal.PackageMeta,  error) {
	defer derrors.WrapStack(&, "getPackagesInUnit(ctx, %q, %q, %q, %d)", , , , )
	defer middleware.ElapsedStat(, "getPackagesInUnit")()

	 := squirrel.Select(
		"p.path",
		"u.name",
		"u.redistributable",
		"d.synopsis",
		"d.GOOS",
		"d.GOARCH",
		"u.license_types",
		"u.license_paths",
	).From("units u")

	if  != -1 {
		 = .Join("paths p ON p.id = u.path_id").
			LeftJoin("documentation d ON d.unit_id = u.id").
			Where(squirrel.Eq{"u.module_id": })
	} else {
		 = .
			Join("modules m ON u.module_id = m.id").
			Join("paths p ON p.id = u.path_id").
			LeftJoin("documentation d ON d.unit_id = u.id").
			Where(squirrel.Eq{"m.module_path": }).
			Where(squirrel.Eq{"m.version": })
	}

	 = .Where(squirrel.NotEq{"u.name": ""})

	, ,  := .PlaceholderFormat(squirrel.Dollar).ToSql()
	if  != nil {
		return nil, 
	}
If a package has more than build context (GOOS/GOARCH pair), it will have more than one row in documentation, and this query will produce multiple rows for that package. If we could sort the build contexts in SQL we could deal with that in the query, but we must sort in code, so we read all the rows and pick the right one afterwards.
	type  struct {
		 *internal.PackageMeta
		 internal.BuildContext
	}
	 := map[string][]{}
	 := func( *sql.Rows) error {
		var (
			          internal.PackageMeta
			 []string
			 []string
			           internal.BuildContext
		)
		if  := .Scan(
			&.Path,
			&.Name,
			&.IsRedistributable,
			database.NullIsEmpty(&.Synopsis),
			database.NullIsEmpty(&.GOOS),
			database.NullIsEmpty(&.GOARCH),
			pq.Array(&),
			pq.Array(&),
		);  != nil {
			return fmt.Errorf("row.Scan(): %v", )
		}
		if  == stdlib.ModulePath || .Path ==  || strings.HasPrefix(.Path, +"/") {
			,  := zipLicenseMetadata(, )
			if  != nil {
				return 
			}
			.Licenses = 
			[.Path] = append([.Path], {&, })
		}
		return nil
	}
	if  := .RunQuery(, , , ...);  != nil {
		return nil, 
	}

	var  []*internal.PackageMeta
	for ,  := range  {
		sort.Slice(, func(,  int) bool { return internal.CompareBuildContexts([]., [].) < 0 })
		 = append(, [0].)
	}
	sort.Slice(, func(,  int) bool { return [].Path < [].Path })
	for ,  := range  {
		if  {
			.IsRedistributable = true
		} else {
			.RemoveNonRedistributableData()
		}
	}
	return , nil
}

func ( *DB) ( context.Context,  *internal.UnitMeta,  internal.BuildContext) ( *internal.Unit,  error) {
	defer derrors.WrapStack(&, "getUnitWithAllFields(ctx, %q, %q, %q)", .Path, .ModulePath, .Version)
	defer middleware.ElapsedStat(, "getUnitWithAllFields")()
Get build contexts and unit ID.
	var , ,  int
	var  []internal.BuildContext
	 = .db.RunQuery(, `
		SELECT d.goos, d.goarch, u.id, p.id, u.module_id
		FROM units u
		INNER JOIN paths p ON p.id = u.path_id
		INNER JOIN modules m ON m.id = u.module_id
		LEFT JOIN documentation d ON d.unit_id = u.id
		WHERE
			p.path = $1
			AND m.module_path = $2
			AND m.version = $3
	`, func( *sql.Rows) error {
GOOS and GOARCH will be NULL if there are no documentation rows for the unit, but we still want the unit ID.

		if  := .Scan(database.NullIsEmpty(&.GOOS), database.NullIsEmpty(&.GOARCH), &, &, &);  != nil {
			return 
		}
		if .GOOS != "" && .GOARCH != "" {
			 = append(, )
		}
		return nil
	}, .Path, .ModulePath, .Version)
	if  != nil {
		return nil, 
	}
	sort.Slice(, func(,  int) bool { return internal.CompareBuildContexts([], []) < 0 })
	var  internal.BuildContext
	for ,  := range  {
		if .Match() {
			 = 
			break
		}
Get README, documentation and import counts.
	 := `
        SELECT
			r.file_path,
			r.contents,
			d.synopsis,
			d.source,
			COALESCE((
				SELECT COUNT(unit_id)
				FROM package_imports
				WHERE unit_id = u.id
				GROUP BY unit_id
				), 0) AS num_imports,
			COALESCE((
				SELECT imported_by_count
				FROM search_documents
				-- Only package_path is needed b/c it is the PK for
				-- search_documents.
				WHERE package_path = $1
				), 0) AS num_imported_by
		FROM units u
		LEFT JOIN readmes r
		ON r.unit_id = u.id

		LEFT JOIN (
			SELECT synopsis, source, goos, goarch, unit_id
			FROM documentation d
			WHERE d.GOOS = $3 AND d.GOARCH = $4
        ) d
		ON d.unit_id = u.id
		WHERE u.id = $2
	`
	var (
		 internal.Readme
		 internal.Unit
	)
	.BuildContexts = 
	var ,  interface{}
	if .GOOS != "" {
		 = .GOOS
		 = .GOARCH
	}
	 := &internal.Documentation{GOOS: .GOOS, GOARCH: .GOARCH}
	 := middleware.ElapsedStat(, "getUnitWithAllFields-readme-and-imports")
	 = .db.QueryRow(, , .Path, , , ).Scan(
		database.NullIsEmpty(&.Filepath),
		database.NullIsEmpty(&.Contents),
		database.NullIsEmpty(&.Synopsis),
		&.Source,
		&.NumImports,
		&.NumImportedBy,
	)
	switch  {
Neither a README nor documentation; that's OK, continue.
	case nil:
		if .Filepath != "" && .ModulePath != stdlib.ModulePath {
			.Readme = &
		}
		if .GOOS != "" {
			.Documentation = []*internal.Documentation{}
		}
	default:
		return nil, 
	}
Get other info.
	,  := .getPackagesInUnit(, .Path, )
	if  != nil {
		return nil, 
	}
	.Subdirectories = 
	.UnitMeta = *

	if .IsPackage() && .Source != nil {
		if .ModulePath == stdlib.ModulePath {
			.SymbolHistory,  = GetSymbolHistoryForBuildContext(, .db, , .ModulePath, )
			if  != nil {
				return nil, 
			}
		}

		if !experiment.IsActive(, internal.ExperimentSymbolHistoryMainPage) {
			return &, nil
		}
		if experiment.IsActive(, internal.ExperimentReadSymbolHistory) {
			.SymbolHistory,  = GetSymbolHistoryForBuildContext(, .db, , .ModulePath, )
			if  != nil {
				return nil, 
			}
			return &, nil
		}
		,  := GetSymbolHistoryWithPackageSymbols(, .db, .Path,
			.ModulePath)
		if  != nil {
			return nil, 
		}
		 := map[string]string{}
		for ,  := range .Versions() {
			 := .SymbolsAtVersion()
			for  := range  {
				[] = 
			}
		}
		.SymbolHistory = 
	}
	return &, nil
}

type dbPath struct {
	id              int64
	path            string
	moduleID        int64
	name            string
	licenseTypes    []string
	licensePaths    []string
	redistributable bool
}

func ( *DB) ( context.Context, ,  string) ( []*dbPath,  error) {
	defer derrors.WrapStack(&, "DB.getPathsInModule(ctx, %q, %q)", , )
	 := `
	SELECT
		u.id,
		p.path,
		u.module_id,
		u.name,
		u.license_types,
		u.license_paths,
		u.redistributable
	FROM
		units u
	INNER JOIN
		paths p
	ON
		p.id = u.path_id
	INNER JOIN
		modules m
	ON
		u.module_id = m.id
	WHERE
		m.module_path = $1
		AND m.version = $2
	ORDER BY path;`

	var  []*dbPath
	 := func( *sql.Rows) error {
		var  dbPath
		if  := .Scan(&.id, &.path, &.moduleID, &.name, pq.Array(&.licenseTypes),
			pq.Array(&.licensePaths), &.redistributable);  != nil {
			return fmt.Errorf("row.Scan(): %v", )
		}
		 = append(, &)
		return nil
	}
	if  := .db.RunQuery(, , , , );  != nil {
		return nil, 
	}
	return , nil
}
GetModuleReadme returns the README corresponding to the modulePath and version.
func ( *DB) ( context.Context, ,  string) ( *internal.Readme,  error) {
	return getModuleReadme(, .db, , )
}

func ( context.Context,  *database.DB, ,  string) ( *internal.Readme,  error) {
	defer derrors.WrapStack(&, "getModuleReadme(ctx, %q, %q)", , )
	var  internal.Readme
	 = .QueryRow(, `
		SELECT file_path, contents
		FROM modules m
		INNER JOIN units u
		ON u.module_id = m.id
		INNER JOIN paths p
		ON u.path_id = p.id
		INNER JOIN readmes r
		ON u.id = r.unit_id
		WHERE
		    m.module_path=$1
			AND m.version=$2
			AND m.module_path=p.path`, , ).Scan(&.Filepath, &.Contents)
	switch  {
	case sql.ErrNoRows:
		return nil, derrors.NotFound
	case nil:
		return &, nil
	default:
		return nil, 
	}