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 (
	
	
	
	
	
	
	
	

	
	
	
	
	
	
)

func ( *DB) ( context.Context, ,  string,  int) ( []*licenses.License,  error) {
	defer derrors.WrapStack(&, "getLicenses(ctx, %d)", )
	defer middleware.ElapsedStat(, "getLicenses")()

	 := `
		SELECT
			l.types,
			l.file_path,
			l.contents,
			l.coverage
		FROM
			licenses l
		INNER JOIN
			units u
		ON
			u.module_id=l.module_id
		INNER JOIN
			modules m
		ON
			u.module_id=m.id
		WHERE
			u.id = $1;`

	,  := .db.Query(, , )
	if  != nil {
		return nil, 
	}
	defer .Close()

	,  := collectLicenses(, .bypassLicenseCheck)
	if  != nil {
		return nil, 
	}
The `query` returns all licenses for the module version. We need to filter the licenses that applies to the specified fullPath, i.e. A license in the current or any parent directory of the specified fullPath applies to it.
	var  []*licenses.License
	for ,  := range  {
		if  == stdlib.ModulePath {
			 = append(, )
		} else {
			 := path.Join(, path.Dir(.FilePath))
			if strings.HasPrefix(, ) {
				 = append(, )
			}
		}
	}
	if !.bypassLicenseCheck {
		for ,  := range  {
			.RemoveNonRedistributableData()
		}
	}
	return , nil
}
getModuleLicenses returns all licenses associated with the given module path and version. These are the top-level licenses in the module zip file. It returns an InvalidArgument error if the module path or version is invalid.
func ( *DB) ( context.Context,  int) ( []*licenses.License,  error) {
	defer derrors.WrapStack(&, "getModuleLicenses(ctx, %d)", )

	 := `
	SELECT
		types, file_path, contents, coverage
	FROM
		licenses
	WHERE
		module_id = $1 AND position('/' in file_path) = 0
    `
	,  := .db.Query(, , )
	if  != nil {
		return nil, 
	}
	defer .Close()
	return collectLicenses(, .bypassLicenseCheck)
}
collectLicenses converts the sql rows to a list of licenses. The columns must be types, file_path and contents, in that order.
func ( *sql.Rows,  bool) ([]*licenses.License, error) {
	mustHaveColumns(, "types", "file_path", "contents", "coverage")
	var  []*licenses.License
	for .Next() {
		var (
			          = &licenses.License{Metadata: &licenses.Metadata{}}
			 []string
			     []byte
		)
		if  := .Scan(pq.Array(&), &.FilePath, &.Contents, &);  != nil {
			return nil, fmt.Errorf("row.Scan(): %v", )
The coverage column is JSON for either the new or old licensecheck.Coverage struct. The new Match type has an ID field which is always populated, but the old one doesn't. First try unmarshalling the new one, then if that doesn't populate the ID field, try the old.
		if  := json.Unmarshal(, &.Coverage);  != nil {
			return nil, 
		}
		if len(.Coverage.Match) == 0 || .Coverage.Match[0].ID == "" {
			.Coverage = licensecheck.Coverage{}
			if  := json.Unmarshal(, &.OldCoverage);  != nil {
				return nil, 
			}
		}
		.Types = 
		if ! {
			.RemoveNonRedistributableData()
		}
		 = append(, )
	}
	sort.Slice(, func(,  int) bool {
		return compareLicenses([].Metadata, [].Metadata)
	})
	if  := .Err();  != nil {
		return nil, 
	}
	return , nil
}
mustHaveColumns panics if the columns of rows does not match wantColumns.
func ( *sql.Rows,  ...string) {
	,  := .Columns()
	if  != nil {
		panic()
	}
	if !reflect.DeepEqual(, ) {
		panic(fmt.Sprintf("got columns %v, want $%v", , ))
	}
}
zipLicenseMetadata constructs licenses.Metadata from the given license types and paths, by zipping and then sorting.
func ( []string,  []string) ( []*licenses.Metadata,  error) {
	defer derrors.WrapStack(&, "zipLicenseMetadata(%v, %v)", , )

	if len() != len() {
		return nil, fmt.Errorf("BUG: got %d license types and %d license paths", len(), len())
	}
	 := make(map[string]*licenses.Metadata)
	var  []*licenses.Metadata
	for ,  := range  {
		,  := []
		if ! {
			 = &licenses.Metadata{FilePath: }
			 = append(, )
By convention, we insert a license path with empty corresponding license type if we are unable to detect *any* licenses in the file. This ensures that we mark this package as non-redistributable.
		if [] != "" {
			.Types = append(.Types, [])
		}
	}
	sort.Slice(, func(,  int) bool {
		return compareLicenses([], [])
	})
	return , nil
}
compareLicenses reports whether i < j according to our license sorting semantics.
func (,  *licenses.Metadata) bool {
	if len(strings.Split(.FilePath, "/")) > len(strings.Split(.FilePath, "/")) {
		return true
	}
	return .FilePath < .FilePath