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

	
	
	
	
	
	
	
	
	
	
)
GetVersionsForPath returns a list of tagged versions sorted in descending semver order if any exist. If none, it returns the 10 most recent from a list of pseudo-versions sorted in descending semver order.
func ( *DB) ( context.Context,  string) ( []*internal.ModuleInfo,  error) {
	defer derrors.WrapStack(&, "GetVersionsForPath(ctx, %q)", )
	defer middleware.ElapsedStat(, "GetVersionsForPath")()

	,  := getPathVersions(, , , version.TypeRelease, version.TypePrerelease)
	if  != nil {
		return nil, 
	}
	if len() != 0 {
		return , nil
	}
	,  = getPathVersions(, , , version.TypePseudo)
	if  != nil {
		return nil, 
	}
	return , nil
}
getPathVersions returns a list of versions sorted in descending semver order. The version types included in the list are specified by a list of VersionTypes.
func ( context.Context,  *DB,  string,  ...version.Type) ( []*internal.ModuleInfo,  error) {
	defer derrors.WrapStack(&, "getPathVersions(ctx, db, %q, %v)", , )

	 := `
	SELECT
		m.module_path,
		m.version,
		m.commit_time,
		m.redistributable,
		m.has_go_mod,
		m.source_info
	FROM modules m
	INNER JOIN units u
		ON u.module_id = m.id
	WHERE
		u.v1path_id = (
			SELECT u2.v1path_id
			FROM units as u2
			INNER JOIN paths p
			ON p.id = u2.path_id
			WHERE p.path = $1
			LIMIT 1
		)
		AND version_type in (%s)
	ORDER BY
		m.incompatible,
		m.module_path DESC,
		m.sort_version DESC %s`

	 := `;`
	if len() == 0 {
		return nil, fmt.Errorf("error: must specify at least one version type")
	} else if len() == 1 && [0] == version.TypePseudo {
		 = `LIMIT 10;`
	}
	 := fmt.Sprintf(, versionTypeExpr(), )
	var  []*internal.ModuleInfo
	 := func( *sql.Rows) error {
		,  := scanModuleInfo(.Scan)
		if  != nil {
			return fmt.Errorf("row.Scan(): %v", )
		}
		 = append(, )
		return nil
	}
	if  := .db.RunQuery(, , , );  != nil {
		return nil, 
	}
	if  := populateLatestInfos(, , );  != nil {
		return nil, 
	}
	return , nil
}
versionTypeExpr returns a comma-separated list of version types, for use in a clause like "WHERE version_type IN (%s)"
func ( []version.Type) string {
	var  []string
	for ,  := range  {
		 = append(, fmt.Sprintf("'%s'", .String()))
	}
	return strings.Join(, ", ")
}

func ( context.Context,  *DB,  *internal.ModuleInfo) ( error) {
	defer derrors.WrapStack(&, "populateLatestInfo(%q)", .ModulePath)
Get information about retractions an deprecations, and apply it.
	,  := .GetLatestModuleVersions(, .ModulePath)
	if  != nil {
		return 
	}
	if  != nil {
		.PopulateModuleInfo()
	}
	return nil
}

func ( context.Context,  *DB,  []*internal.ModuleInfo) ( error) {
	defer derrors.WrapStack(&, "populateLatestInfos(%d ModuleInfos)", len())
Collect the LatestModuleVersions for all modules in the list.
	 := map[string]*internal.LatestModuleVersions{}
	for ,  := range  {
		if ,  := [.ModulePath]; ! {
			,  := .GetLatestModuleVersions(, .ModulePath)
			if  != nil {
				return 
			}
			[.ModulePath] = 
		}
Use the collected LatestModuleVersions to populate the ModuleInfos.
	for ,  := range  {
		 := [.ModulePath]
		if  != nil {
			.PopulateModuleInfo()
		}
	}
	return nil
}
GetLatestInfo returns the latest information about the unit in the module. See internal.LatestInfo for documentation about the returned values. If latestUnitMeta is non-nil, it is the result of GetUnitMeta(unitPath, internal.UnknownModulePath, internal.LatestVersion). That can save a redundant call to GetUnitMeta here.
func ( *DB) ( context.Context, ,  string,  *internal.UnitMeta) ( internal.LatestInfo,  error) {
	defer derrors.WrapStack(&, "DB.GetLatestInfo(ctx, %q, %q)", , )
	defer middleware.ElapsedStat(, "DB.GetLatestInfo")()

	,  := errgroup.WithContext()

	if  != nil {
		.MinorVersion = .Version
		.MinorModulePath = .ModulePath
	} else {
		.Go(func() error {
			,  := .GetUnitMeta(, , internal.UnknownModulePath, internal.LatestVersion)
			if  != nil {
				return 
			}
			.MinorVersion = .Version
			.MinorModulePath = .ModulePath
			return nil
		})
	}
	.Go(func() ( error) {
		.MajorModulePath, .MajorUnitPath,  = .getLatestMajorVersion(, , )
		return 
	})
	.Go(func() ( error) {
		.UnitExistsAtMinor,  = .unitExistsAtLatest(, , )
		return 
	})

	if  := .Wait();  != nil {
		return internal.LatestInfo{}, 
	}
	return , nil
}
getLatestMajorVersion returns the latest module path and the full package path of the latest version found, given the fullPath and the modulePath. For example, in the module path "github.com/casbin/casbin", there is another module path with a greater major version "github.com/casbin/casbin/v3". This function will return "github.com/casbin/casbin/v3" or the input module path if no later module path was found. It also returns the full package path at the latest module version if it exists. If not, it returns the module path. getLatestMajorVersion only considers tagged (non-pseudo) versions. If there are none, it returns empty strings.
func ( *DB) ( context.Context, ,  string) (,  string,  error) {
	defer derrors.WrapStack(&, "DB.getLatestMajorVersion2(%q)", )
	defer middleware.ElapsedStat(, "DB.getLatestMajorVersion")()
Collect all the non-deprecated module paths for the series that have at least one good version, along with that good version. A good version is both servable (in the modules table) and not retracted.
	 := internal.SeriesPathForModule()
	, ,  := squirrel.Select("p.path", "l.good_version").
		From("latest_module_versions l").
		Join("paths p ON p.id = l.module_path_id").
		Where(squirrel.Eq{"l.series_path": }).
		Where("NOT l.deprecated").
		Where(squirrel.NotEq{"l.good_version": ""}).
		PlaceholderFormat(squirrel.Dollar).
		ToSql()
	if  != nil {
		return "", "", 
	}

	type  struct {
		,  string
	}

	var  []
	 = .db.RunQuery(, , func( *sql.Rows) error {
		var  
		if  := .Scan(&., &.);  != nil {
			return 
		}
		 = append(, )
		return nil
	}, ...)
	if  != nil {
		return "", "", 
	}
Find the highest tagged version from among the (module path, good version) pairs.
	var  
	for ,  := range  {
		if version.IsPseudo(.) {
			continue
Use semver.Compare, not version.Later, because we don't want to prefer release to prerelease: we want v2.0.0-pre over v1.0.0.
		if . == "" || semver.Compare(., .) > 0 {
			 = 
		}
	}
No highest tagged version: return empty strings.
	if . == "" {
		return "", "", nil
	}
Find the unit path at the max-version module path.
	 := internal.V1Path(, )
	 := .db.QueryRow(, `
		SELECT p.path
		FROM units u
		INNER JOIN modules m ON m.id = u.module_id
		INNER JOIN paths p ON p.id = u.path_id
		INNER JOIN paths p2 ON p2.id = u.v1path_id
		WHERE p2.path = $1 AND m.module_path = $2 AND m.version = $3`,
		, ., .)
	var  string
	switch  := .Scan(&);  {
	case nil:
		return ., , nil
	case sql.ErrNoRows:
		return ., ., nil
	default:
		return "", "", 
	}
}
unitExistsAtLatest reports whether unitPath exists at the latest version of modulePath.
func ( *DB) ( context.Context, ,  string) ( bool,  error) {
	defer derrors.WrapStack(&, "DB.unitExistsAtLatest(ctx, %q, %q)", , )
	defer middleware.ElapsedStat(, "DB.unitExistsAtLatest")()
Find the latest version of the module path in the modules table.
	var  string
	,  := .GetLatestModuleVersions(, )
	if  != nil {
		return false, 
	}
If we have latest-version info, use it.
		 = .GoodVersion
Otherwise, query the modules table, ignoring all adjustments for incompatible and retracted versions.
		 := .db.QueryRow(, `
			SELECT version
			FROM modules
			WHERE module_path = $1
			ORDER BY
				version_type = 'release' DESC,
				sort_version DESC
			LIMIT 1
		`, ).Scan(&)
		if  != nil {
			return false, 
		}
	}
	if  == "" {
		return true, nil
See if the unit path exists at that version.
	var  int
	 = .db.QueryRow(, `
		SELECT 1
		FROM units u
		INNER JOIN paths p ON p.id = u.path_id
		INNER JOIN modules m ON m.id = u.module_id
		WHERE p.path = $1 AND m.module_path = $2 AND m.version = $3
	`, , , ).Scan(&)
	switch  {
	case nil:
		return true, nil
	case sql.ErrNoRows:
		return false, nil
	default:
		return false, 
	}
}

func ( *DB) ( context.Context,  []string) ( []*internal.LatestModuleVersions,  error) {
	defer derrors.WrapStack(&, "getMultiLatestModuleVersions(%v)", )
	defer middleware.ElapsedStat(, "getMultiLatestModuleVersions")()

	 := func( *sql.Rows) error {
		var (
			, , ,  string
			                    []byte
		)
		if  := .Scan(&, &, &, &, &);  != nil {
			return 
		}
		,  := internal.NewLatestModuleVersions(, , , , )
		if  != nil {
			return 
		}
		 = append(, )
		return nil
	}

	 = .db.RunQuery(, `
		SELECT p.path, r.raw_version, r.cooked_version, r.good_version, r.raw_go_mod_bytes
		FROM latest_module_versions r
		INNER JOIN paths p ON p.id = r.module_path_id
		WHERE p.path = ANY($1)
		AND r.status = 200
		ORDER BY p.path DESC
	`, , pq.Array())
	if  != nil {
		return nil, 
	}
	return , nil
}
getLatestGoodVersion returns the latest version of a module in the modules table, respecting the retractions and other information in the given LatestModuleVersions. If lmv is nil, it finds the latest version, favoring release over pre-release, including incompatible versions, and ignoring retractions.
func ( context.Context,  *database.DB,  string,  *internal.LatestModuleVersions) ( string,  error) {
	defer derrors.WrapStack(&, "getLatestGoodVersion(%q)", )
Read the versions from the modules table. If the cooked latest version is incompatible, then include incompatible versions. If it isn't, then either there are no incompatible versions, or there are but the latest compatible version has a go.mod file. Either way, ignore incompatible versions.
	 := squirrel.Select("version").
		From("modules").
		Where(squirrel.Eq{"module_path": }).
		PlaceholderFormat(squirrel.Dollar)
	if  != nil && !version.IsIncompatible(.CookedVersion) {
		 = .Where("NOT incompatible")
	}
	, ,  := .ToSql()
	if  != nil {
		return "", 
	}
	,  := collectStrings(, , , ...)
	if  != nil {
		return "", 
Choose the latest good version from a filtered list of versions.
Remove retracted versions.
The good version should never be later than the cooked version.
		if .CookedVersion != "" {
			 = version.RemoveIf(, func( string) bool {
				return version.Later(, .CookedVersion)
			})
		}
	}

	return version.LatestOf(), nil
}
GetLatestModuleVersions returns the row of the latest_module_versions table for modulePath. If the module path is not found, it returns nil, nil.
func ( *DB) ( context.Context,  string) ( *internal.LatestModuleVersions,  error) {
	, ,  := getLatestModuleVersions(, .db, )
	return , 
}

func ( context.Context,  *database.DB,  string) ( *internal.LatestModuleVersions,  int,  error) {
	derrors.WrapStack(&, "getLatestModuleVersions(%q)", )

	var (
		, ,  string
		        []byte
		            int
	)
	 = .QueryRow(, `
		SELECT
			r.module_path_id, r.raw_version, r.cooked_version, r.good_version, r.raw_go_mod_bytes, r.status
		FROM latest_module_versions r
		INNER JOIN paths p ON p.id = r.module_path_id
		WHERE p.path = $1`,
		).Scan(&, &, &, &, &, &)
	if  != nil {
		if  == sql.ErrNoRows {
			return nil, 0, nil
		}
		return nil, 0, 
	}
No information for this module path, but the ID is still useful.
		return nil, , nil
	}
	,  := internal.NewLatestModuleVersions(, , , , )
	if  != nil {
		return nil, 0, 
	}
	return , , nil
}
rawIsMoreRecent reports whether raw version v1 is more recent than v2. v1 is more recent if it is later according to the go command (higher semver, preferring release to prerelease). However, the raw latest version can go backwards if it was an incompatible version, but then a compatible version with a go.mod file is published. For example, the module starts with a v2.0.0+incompatible, but then the author adds a v1.0.0 with a go.mod file, making v1.0.0 the new latest.
func (,  string) bool {
	return version.Later(, ) || (version.IsIncompatible() && !version.IsIncompatible())
}
UpdateLatestModuleVersions upserts its argument into the latest_module_versions table if the row doesn't exist, or the new version is later. It returns the version that is in the DB when it completes.
func ( *DB) ( context.Context,  *internal.LatestModuleVersions) ( *internal.LatestModuleVersions,  error) {
	defer derrors.WrapStack(&, "UpdateLatestModuleVersions(%q)", .ModulePath)

We need RepeatableRead here because the INSERT...ON CONFLICT does a read.
	 = .db.Transact(, sql.LevelRepeatableRead, func( *database.DB) error {
		, ,  := getLatestModuleVersions(, , .ModulePath)
		if  != nil {
			return 
Is vNew the most recent information, or does the DB already have something more up to date?
If new versions are added, cooked can change even if raw is the same.
			(.RawVersion == .RawVersion && .CookedVersion != .CookedVersion)
		if ! {
			log.Debugf(, "%s: not updating latest module versions", .ModulePath)
			 = 
			return nil
		}
		if  == nil {
			log.Debugf(, "%s: inserting latest_module_versions raw=%q, cooked=%q",
				.ModulePath, .RawVersion, .CookedVersion)
		} else {
			log.Debugf(, "%s: updating latest_module_versions raw=%q, cooked=%q to raw=%q, cooked=%q",
				.ModulePath, .RawVersion, .CookedVersion,
If the latest good version is now retracted, recompute it.
			if .GoodVersion != "" && .IsRetracted(.GoodVersion) {
				,  := getLatestGoodVersion(, , .ModulePath, )
				if  != nil {
					return 
				}
				.GoodVersion = 
				log.Debugf(, "%s: updating latest_module_versions good=%q", .ModulePath, .GoodVersion)
			} else {
				.GoodVersion = .GoodVersion
			}
		}
		 = 
		return upsertLatestModuleVersions(, , .ModulePath, , , 200)
	})
	if  != nil {
		return nil, 
	}
	return , nil
}
UpdateLatestModuleVersionsStatus updates or inserts a failure status into the latest_module_versions table. It only updates the table if it doesn't have valid information for the module path.
func ( *DB) ( context.Context,  string,  int) ( error) {
	defer derrors.WrapStack(&, "UpdateLatestModuleVersionsStatus(%q, %d)", , )
We need RepeatableRead here because the INSERT...ON CONFLICT does a read.
	return .db.Transact(, sql.LevelRepeatableRead, func( *database.DB) error {
		var ,  int
		 := .QueryRow(, `
				SELECT r.module_path_id, r.status
				FROM latest_module_versions r
				INNER JOIN paths p ON p.id = r.module_path_id
				WHERE p.path = $1`,
			).Scan(&, &)
		if  != nil &&  != sql.ErrNoRows {
			return 
		}
		if  == 200 {
			return nil
		}
		log.Debugf(, "%s: updating latest_module_versions status to %d", , )
		return upsertLatestModuleVersions(, , , , nil, )
	})
}

func ( context.Context,  *database.DB,  string,  int,  *internal.LatestModuleVersions,  int) ( error) {
	defer derrors.WrapStack(&, "upsertLatestModuleVersions(%s, %d)", , )
If the row doesn't exist, get a path ID for the module path.
	if  == 0 {
		,  = upsertPath(, , )
		if  != nil {
			return 
		}
	}
	var (
		, ,  string
		        = []byte{} // not nil, a zero-length slice
		        bool
	)
	if  != nil {
		 = .RawVersion
		 = .CookedVersion
		 = .GoodVersion
Convert the go.mod file into bytes.
		,  = .GoModFile.Format()
		if  != nil {
			return 
		}
	}
	_,  = .Exec(, `
		INSERT INTO latest_module_versions (
			module_path_id,
			series_path,
			raw_version,
			cooked_version,
			good_version,
			deprecated,
			raw_go_mod_bytes,
			status
		) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
		ON CONFLICT (module_path_id)
		DO UPDATE SET
			series_path=excluded.series_path,
			raw_version=excluded.raw_version,
			cooked_version=excluded.cooked_version,
			good_version=excluded.good_version,
			deprecated=excluded.deprecated,
			raw_go_mod_bytes=excluded.raw_go_mod_bytes,
			status=excluded.status
		`,
		, internal.SeriesPathForModule(), , , , , , )
	return 
}
updateLatestGoodVersion updates latest_module_versions.good_version for modulePath to version.
func ( context.Context,  *database.DB, ,  string) ( error) {
	defer derrors.WrapStack(&, "updateLatestGoodVersion(%q, %q)", , )

	,  := .Exec(, `
		UPDATE latest_module_versions
		SET good_version = $2
		WHERE module_path_id = (
			SELECT id FROM paths
			WHERE path = $1
		)`, , )
	if  != nil {
		return 
	}
	switch  {
	case 0:
		log.Debugf(, "updateLatestGoodVersion(%q, %q): no change", , )
	case 1:
		log.Debugf(, "updateLatestGoodVersion(%q, %q): updated", , )
	default:
		return errors.New("more than one row affected")
	}
	return nil