Store PotentialNamespace in database

PotentialNamespace is part of layer_feature table and it is also stored
in namespace table.
This commit is contained in:
Ales Raszka 2019-02-28 11:02:39 +01:00
parent 34c2d96b36
commit 44c4a6f3ce
6 changed files with 49 additions and 22 deletions

View File

@ -17,6 +17,7 @@ package database
import (
"database/sql/driver"
"encoding/json"
"fmt"
"time"
"github.com/coreos/clair/pkg/pagination"
@ -123,10 +124,13 @@ func (l *Layer) GetFeatures() []Feature {
}
func (l *Layer) GetNamespaces() []Namespace {
namespaces := make([]Namespace, 0, len(l.Namespaces))
namespaces := make([]Namespace, 0, len(l.Namespaces)+len(l.Features))
for _, ns := range l.Namespaces {
namespaces = append(namespaces, ns.Namespace)
}
for _, f := range l.Features {
namespaces = append(namespaces, f.Feature.PotentialNamespace)
}
return namespaces
}
@ -195,6 +199,10 @@ type NamespacedFeature struct {
Namespace Namespace `json:"namespace"`
}
func (nf *NamespacedFeature) Key() string {
return fmt.Sprintf("%s-%s-%s-%s-%s-%s", nf.Name, nf.Version, nf.VersionFormat, nf.Type, nf.Namespace.Name, nf.Namespace.VersionFormat)
}
func NewNamespacedFeature(namespace *Namespace, feature *Feature) *NamespacedFeature {
// TODO: namespaced feature should use pointer values
return &NamespacedFeature{*feature, *namespace}

View File

@ -17,6 +17,7 @@ package pgsql
import (
"database/sql"
"sort"
"strconv"
"github.com/lib/pq"
log "github.com/sirupsen/logrus"
@ -311,7 +312,7 @@ func (tx *pgSession) findNamespacedFeatureIDs(nfs []database.NamespacedFeature)
return nil, nil
}
nfsMap := map[database.NamespacedFeature]int64{}
nfsMap := map[string]int64{}
keys := make([]interface{}, 0, len(nfs)*5)
for _, nf := range nfs {
keys = append(keys, nf.Name, nf.Version, nf.VersionFormat, nf.Type, nf.Namespace.Name)
@ -334,12 +335,12 @@ func (tx *pgSession) findNamespacedFeatureIDs(nfs []database.NamespacedFeature)
if err != nil {
return nil, handleError("searchNamespacedFeature", err)
}
nfsMap[nf] = id
nfsMap[nf.Key()] = id
}
ids := make([]sql.NullInt64, len(nfs))
for i, nf := range nfs {
if id, ok := nfsMap[nf]; ok {
if id, ok := nfsMap[nf.Key()]; ok {
ids[i] = sql.NullInt64{id, true}
} else {
ids[i] = sql.NullInt64{}
@ -359,13 +360,14 @@ func (tx *pgSession) findFeatureIDs(fs []database.Feature) ([]sql.NullInt64, err
return nil, err
}
fMap := map[database.Feature]sql.NullInt64{}
fMap := map[string]sql.NullInt64{}
keys := make([]interface{}, 0, len(fs)*4)
for _, f := range fs {
typeID := types.byName[f.Type]
keys = append(keys, f.Name, f.Version, f.VersionFormat, typeID)
fMap[f] = sql.NullInt64{}
mapKey := f.Name + f.Version + f.VersionFormat + strconv.Itoa(typeID)
fMap[mapKey] = sql.NullInt64{}
}
rows, err := tx.Query(querySearchFeatureID(len(fs)), keys...)
@ -386,12 +388,15 @@ func (tx *pgSession) findFeatureIDs(fs []database.Feature) ([]sql.NullInt64, err
}
f.Type = types.byID[typeID]
fMap[f] = id
mapKey := f.Name + f.Version + f.VersionFormat + strconv.Itoa(typeID)
fMap[mapKey] = id
}
ids := make([]sql.NullInt64, len(fs))
for i, f := range fs {
ids[i] = fMap[f]
typeID := types.byName[f.Type]
mapKey := f.Name + f.Version + f.VersionFormat + strconv.Itoa(typeID)
ids[i] = fMap[mapKey]
}
return ids, nil

View File

@ -182,7 +182,7 @@ func TestFindNamespacedFeatureIDs(t *testing.T) {
expectedIDs = append(expectedIDs, 1)
namespace := realNamespaces[1]
features = append(features, *database.NewNamespacedFeature(&namespace, database.NewBinaryPackage("not-found", "1.0", "dpkg"))) // test not found feature
features = append(features, *database.NewNamespacedFeature(&namespace, database.NewBinaryPackage("not-found", "1.0", "dpkg", database.Namespace{}))) // test not found feature
ids, err := tx.findNamespacedFeatureIDs(features)
require.Nil(t, err)

View File

@ -37,10 +37,11 @@ const (
SELECT id FROM layer WHERE hash = $1`
findLayerFeatures = `
SELECT f.name, f.version, f.version_format, t.name, lf.detector_id
FROM layer_feature AS lf, feature AS f, feature_type AS t
SELECT f.name, f.version, f.version_format, t.name, lf.detector_id, ns.name, ns.version_format
FROM layer_feature AS lf, feature AS f, feature_type AS t, namespace AS ns
WHERE lf.feature_id = f.id
AND t.id = f.type
AND lf.namespace_id = ns.id
AND lf.layer_id = $1`
findLayerNamespaces = `
@ -61,9 +62,10 @@ type dbLayerNamespace struct {
// dbLayerFeature represents the layer_feature table
type dbLayerFeature struct {
layerID int64
featureID int64
detectorID int64
layerID int64
featureID int64
detectorID int64
namespaceID int64
}
func (tx *pgSession) FindLayer(hash string) (database.Layer, bool, error) {
@ -199,10 +201,16 @@ func (tx *pgSession) persistAllLayerFeatures(layerID int64, features []database.
if err != nil {
return err
}
var namespaces []database.Namespace
for _, feature := range features {
namespaces = append(namespaces, feature.PotentialNamespace)
}
nameSpaceIDs, _ := tx.findNamespaceIDs(namespaces)
featureNamespaceMap := map[database.Namespace]sql.NullInt64{}
rawFeatures := make([]database.Feature, 0, len(features))
for _, f := range features {
for i, f := range features {
rawFeatures = append(rawFeatures, f.Feature)
featureNamespaceMap[f.PotentialNamespace] = nameSpaceIDs[i]
}
featureIDs, err := tx.findFeatureIDs(rawFeatures)
@ -213,12 +221,16 @@ func (tx *pgSession) persistAllLayerFeatures(layerID int64, features []database.
dbFeatures := make([]dbLayerFeature, 0, len(features))
for i, f := range features {
detectorID := detectorMap.byValue[f.By]
featureID := featureIDs[i].Int64
if !featureIDs[i].Valid {
return database.ErrMissingEntities
}
featureID := featureIDs[i].Int64
if !featureNamespaceMap[f.PotentialNamespace].Valid {
return database.ErrMissingEntities
}
namespaceID := featureNamespaceMap[f.PotentialNamespace].Int64
dbFeatures = append(dbFeatures, dbLayerFeature{layerID, featureID, detectorID})
dbFeatures = append(dbFeatures, dbLayerFeature{layerID, featureID, detectorID, namespaceID})
}
if err := tx.persistLayerFeatures(dbFeatures); err != nil {
@ -236,9 +248,9 @@ func (tx *pgSession) persistLayerFeatures(features []dbLayerFeature) error {
sort.Slice(features, func(i, j int) bool {
return features[i].featureID < features[j].featureID
})
keys := make([]interface{}, 0, len(features)*3)
keys := make([]interface{}, 0, len(features)*4)
for _, f := range features {
keys = append(keys, f.layerID, f.featureID, f.detectorID)
keys = append(keys, f.layerID, f.featureID, f.detectorID, f.namespaceID)
}
_, err := tx.Exec(queryPersistLayerFeature(len(features)), keys...)
@ -308,7 +320,7 @@ func (tx *pgSession) findLayerFeatures(layerID int64, detectors detectorMap) ([]
detectorID int64
feature database.LayerFeature
)
if err := rows.Scan(&feature.Name, &feature.Version, &feature.VersionFormat, &feature.Type, &detectorID); err != nil {
if err := rows.Scan(&feature.Name, &feature.Version, &feature.VersionFormat, &feature.Type, &detectorID, &feature.PotentialNamespace.Name, &feature.PotentialNamespace.VersionFormat); err != nil {
return nil, handleError("findLayerFeatures", err)
}

View File

@ -89,6 +89,7 @@ var (
layer_id INT REFERENCES layer ON DELETE CASCADE,
feature_id INT REFERENCES feature ON DELETE CASCADE,
detector_id INT REFERENCES detector ON DELETE CASCADE,
namespace_id INT REFERENCES namespace ON DELETE CASCADE,
UNIQUE (layer_id, feature_id));`,
`CREATE INDEX ON layer_feature(layer_id);`,

View File

@ -124,7 +124,8 @@ func queryPersistLayerFeature(count int) string {
"layer_feature_layer_id_feature_id_key",
"layer_id",
"feature_id",
"detector_id")
"detector_id",
"namespace_id")
}
func queryPersistNamespace(count int) string {