github.com/quay/claircore@v1.5.28/datastore/postgres/packagesbylayer.go (about)

     1  package postgres
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"strconv"
     8  	"time"
     9  
    10  	"github.com/jackc/pgtype"
    11  	"github.com/jackc/pgx/v4"
    12  	"github.com/prometheus/client_golang/prometheus"
    13  	"github.com/prometheus/client_golang/prometheus/promauto"
    14  
    15  	"github.com/quay/claircore"
    16  	"github.com/quay/claircore/indexer"
    17  )
    18  
    19  var (
    20  	packagesByLayerCounter = promauto.NewCounterVec(
    21  		prometheus.CounterOpts{
    22  			Namespace: "claircore",
    23  			Subsystem: "indexer",
    24  			Name:      "packagesbylayer_total",
    25  			Help:      "Total number of database queries issued in the PackagesByLayer method.",
    26  		},
    27  		[]string{"query"},
    28  	)
    29  
    30  	packagesByLayerDuration = promauto.NewHistogramVec(
    31  		prometheus.HistogramOpts{
    32  			Namespace: "claircore",
    33  			Subsystem: "indexer",
    34  			Name:      "packagesbylayer_duration_seconds",
    35  			Help:      "The duration of all queries issued in the PackagesByLayer method",
    36  		},
    37  		[]string{"query"},
    38  	)
    39  )
    40  
    41  func (s *IndexerStore) PackagesByLayer(ctx context.Context, hash claircore.Digest, scnrs indexer.VersionedScanners) ([]*claircore.Package, error) {
    42  	const (
    43  		selectScanner = `
    44  SELECT
    45  	id
    46  FROM
    47  	scanner
    48  WHERE
    49  	name = $1 AND version = $2 AND kind = $3;
    50  `
    51  		query = `
    52  SELECT
    53  	package.id,
    54  	package.name,
    55  	package.kind,
    56  	package.version,
    57  	package.norm_kind,
    58  	package.norm_version,
    59  	package.module,
    60  	package.arch,
    61  	source_package.id,
    62  	source_package.name,
    63  	source_package.kind,
    64  	source_package.version,
    65  	source_package.module,
    66  	source_package.arch,
    67  	package_scanartifact.package_db,
    68  	package_scanartifact.repository_hint,
    69  	package_scanartifact.filepath
    70  FROM
    71  	package_scanartifact
    72  	LEFT JOIN package ON
    73  			package_scanartifact.package_id = package.id
    74  	LEFT JOIN package AS source_package ON
    75  			package_scanartifact.source_id
    76  			= source_package.id
    77  	JOIN layer ON layer.hash = $1
    78  WHERE
    79  	package_scanartifact.layer_id = layer.id
    80  	AND package_scanartifact.scanner_id = ANY ($2);
    81  `
    82  	)
    83  
    84  	if len(scnrs) == 0 {
    85  		return []*claircore.Package{}, nil
    86  	}
    87  	// get scanner ids
    88  	scannerIDs := make([]int64, len(scnrs))
    89  	for i, scnr := range scnrs {
    90  		start := time.Now()
    91  		err := s.pool.QueryRow(ctx, selectScanner, scnr.Name(), scnr.Version(), scnr.Kind()).
    92  			Scan(&scannerIDs[i])
    93  		if err != nil {
    94  			return nil, fmt.Errorf("failed to retrieve scanner ids: %w", err)
    95  		}
    96  		packagesByLayerCounter.WithLabelValues("selectScanner").Add(1)
    97  		packagesByLayerDuration.WithLabelValues("selectScanner").Observe(time.Since(start).Seconds())
    98  	}
    99  
   100  	start := time.Now()
   101  	rows, err := s.pool.Query(ctx, query, hash, scannerIDs)
   102  	switch {
   103  	case errors.Is(err, nil):
   104  	case errors.Is(err, pgx.ErrNoRows):
   105  		return nil, fmt.Errorf("store:packagesByLayer no packages found for hash %v and scanners %v", hash, scnrs)
   106  	default:
   107  		return nil, fmt.Errorf("store:packagesByLayer failed to retrieve package rows for hash %v and scanners %v: %w", hash, scnrs, err)
   108  	}
   109  	packagesByLayerCounter.WithLabelValues("query").Add(1)
   110  	packagesByLayerDuration.WithLabelValues("query").Observe(time.Since(start).Seconds())
   111  	defer rows.Close()
   112  
   113  	res := []*claircore.Package{}
   114  	for rows.Next() {
   115  		var pkg claircore.Package
   116  		var spkg claircore.Package
   117  
   118  		var id, srcID int64
   119  		var nKind, fPath *string
   120  		var nVer pgtype.Int4Array
   121  		err := rows.Scan(
   122  			&id,
   123  			&pkg.Name,
   124  			&pkg.Kind,
   125  			&pkg.Version,
   126  			&nKind,
   127  			&nVer,
   128  			&pkg.Module,
   129  			&pkg.Arch,
   130  
   131  			&srcID,
   132  			&spkg.Name,
   133  			&spkg.Kind,
   134  			&spkg.Version,
   135  			&spkg.Module,
   136  			&spkg.Arch,
   137  
   138  			&pkg.PackageDB,
   139  			&pkg.RepositoryHint,
   140  			&fPath,
   141  		)
   142  		pkg.ID = strconv.FormatInt(id, 10)
   143  		spkg.ID = strconv.FormatInt(srcID, 10)
   144  		if err != nil {
   145  			return nil, fmt.Errorf("failed to scan packages: %w", err)
   146  		}
   147  		if nKind != nil {
   148  			pkg.NormalizedVersion.Kind = *nKind
   149  			for i, n := range nVer.Elements {
   150  				pkg.NormalizedVersion.V[i] = n.Int
   151  			}
   152  		}
   153  		if fPath != nil {
   154  			pkg.Filepath = *fPath
   155  		}
   156  		// nest source package
   157  		pkg.Source = &spkg
   158  
   159  		res = append(res, &pkg)
   160  	}
   161  	if err := rows.Err(); err != nil {
   162  		return nil, err
   163  	}
   164  
   165  	return res, nil
   166  }