blob: e342dd430c15b9e1f4ffa339a753203a7a41610d [file] [log] [blame]
// 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 (
"context"
"database/sql"
"fmt"
"github.com/lib/pq"
"golang.org/x/pkgsite/internal"
"golang.org/x/pkgsite/internal/database"
"golang.org/x/pkgsite/internal/derrors"
"golang.org/x/pkgsite/internal/stdlib"
)
// GetPackage returns the a package from the database with the corresponding
// pkgPath, modulePath and version.
//
// If version = internal.LatestVersion, the package corresponding to
// the latest matching module version will be fetched.
//
// If more than one module tie for a given dirPath and version pair, and
// modulePath = internal.UnknownModulePath, the package in the module with the
// longest module path will be fetched.
// For example, if there are
// two rows in the packages table:
// (1) path = "github.com/hashicorp/vault/api"
// module_path = "github.com/hashicorp/vault"
// AND
// (2) path = "github.com/hashicorp/vault/api"
// module_path = "github.com/hashicorp/vault/api"
// The latter will be returned.
//
// The returned error may be checked with
// errors.Is(err, derrors.InvalidArgument) to determine if it was caused by an
// invalid pkgPath, modulePath or version.
//
// The returned error may be checked with
// errors.Is(err, derrors.InvalidArgument) to determine if it was caused by an
// invalid path or version.
func (db *DB) GetPackage(ctx context.Context, pkgPath, modulePath, version string) (_ *internal.LegacyVersionedPackage, err error) {
defer derrors.Wrap(&err, "DB.GetPackage(ctx, %q, %q)", pkgPath, version)
if pkgPath == "" || modulePath == "" || version == "" {
return nil, fmt.Errorf("none of pkgPath, modulePath, or version can be empty: %w", derrors.InvalidArgument)
}
args := []interface{}{pkgPath}
query := `
SELECT
p.path,
p.name,
p.synopsis,
p.v1_path,
p.license_types,
p.license_paths,
p.redistributable,
p.documentation,
p.goos,
p.goarch,
m.version,
m.commit_time,
m.readme_file_path,
m.readme_contents,
m.module_path,
m.version_type,
m.source_info,
m.redistributable,
m.has_go_mod
FROM
modules m
INNER JOIN
packages p
ON
p.module_path = m.module_path
AND m.version = p.version`
if modulePath == internal.UnknownModulePath || modulePath == stdlib.ModulePath {
if version == internal.LatestVersion {
// Only pkgPath is specified, so get the latest version of the
// package found in any module.
query += `
WHERE
p.path = $1
ORDER BY
-- Order the versions by release then prerelease.
-- The default version should be the first release
-- version available, if one exists.
m.version_type = 'release' DESC,
m.sort_version DESC,
m.module_path DESC
LIMIT 1;`
} else {
// pkgPath and version are specified, so get that package version
// from any module. If it exists in multiple modules, return the
// one with the longest path.
query += `
WHERE
p.path = $1
AND p.version = $2
ORDER BY
p.module_path DESC
LIMIT 1;`
args = append(args, version)
}
} else if version == internal.LatestVersion {
// pkgPath and modulePath are specified, so get the latest version of
// the package in the specified module.
query += `
WHERE
p.path = $1
AND p.module_path = $2
ORDER BY
-- Order the versions by release then prerelease.
-- The default version should be the first release
-- version available, if one exists.
m.version_type = 'release' DESC,
m.sort_version DESC
LIMIT 1;`
args = append(args, modulePath)
} else {
// pkgPath, modulePath and version were all specified. Only one
// directory should ever match this query.
query += `
WHERE
p.path = $1
AND p.version = $2
AND p.module_path = $3`
args = append(args, version, modulePath)
}
var (
pkg internal.LegacyVersionedPackage
licenseTypes, licensePaths []string
hasGoMod sql.NullBool
)
row := db.db.QueryRow(ctx, query, args...)
err = row.Scan(&pkg.Path, &pkg.Name, &pkg.Synopsis,
&pkg.V1Path, pq.Array(&licenseTypes), pq.Array(&licensePaths), &pkg.LegacyPackage.IsRedistributable,
database.NullIsEmpty(&pkg.DocumentationHTML), &pkg.GOOS, &pkg.GOARCH, &pkg.Version,
&pkg.CommitTime, database.NullIsEmpty(&pkg.LegacyReadmeFilePath), database.NullIsEmpty(&pkg.LegacyReadmeContents),
&pkg.ModulePath, &pkg.VersionType, jsonbScanner{&pkg.SourceInfo}, &pkg.LegacyModuleInfo.IsRedistributable,
&hasGoMod)
if err != nil {
if err == sql.ErrNoRows {
return nil, fmt.Errorf("package %s@%s: %w", pkgPath, version, derrors.NotFound)
}
return nil, fmt.Errorf("row.Scan(): %v", err)
}
setHasGoMod(&pkg.LegacyModuleInfo, hasGoMod)
lics, err := zipLicenseMetadata(licenseTypes, licensePaths)
if err != nil {
return nil, err
}
pkg.Licenses = lics
return &pkg, nil
}