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 ( import (
"database/sql/driver" "database/sql/driver"
"encoding/json" "encoding/json"
"fmt"
"time" "time"
"github.com/coreos/clair/pkg/pagination" "github.com/coreos/clair/pkg/pagination"
@ -123,10 +124,13 @@ func (l *Layer) GetFeatures() []Feature {
} }
func (l *Layer) GetNamespaces() []Namespace { 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 { for _, ns := range l.Namespaces {
namespaces = append(namespaces, ns.Namespace) namespaces = append(namespaces, ns.Namespace)
} }
for _, f := range l.Features {
namespaces = append(namespaces, f.Feature.PotentialNamespace)
}
return namespaces return namespaces
} }
@ -195,6 +199,10 @@ type NamespacedFeature struct {
Namespace Namespace `json:"namespace"` 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 { func NewNamespacedFeature(namespace *Namespace, feature *Feature) *NamespacedFeature {
// TODO: namespaced feature should use pointer values // TODO: namespaced feature should use pointer values
return &NamespacedFeature{*feature, *namespace} return &NamespacedFeature{*feature, *namespace}

View File

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

View File

@ -182,7 +182,7 @@ func TestFindNamespacedFeatureIDs(t *testing.T) {
expectedIDs = append(expectedIDs, 1) expectedIDs = append(expectedIDs, 1)
namespace := realNamespaces[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) ids, err := tx.findNamespacedFeatureIDs(features)
require.Nil(t, err) require.Nil(t, err)

View File

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

View File

@ -89,6 +89,7 @@ var (
layer_id INT REFERENCES layer ON DELETE CASCADE, layer_id INT REFERENCES layer ON DELETE CASCADE,
feature_id INT REFERENCES feature ON DELETE CASCADE, feature_id INT REFERENCES feature ON DELETE CASCADE,
detector_id INT REFERENCES detector ON DELETE CASCADE, detector_id INT REFERENCES detector ON DELETE CASCADE,
namespace_id INT REFERENCES namespace ON DELETE CASCADE,
UNIQUE (layer_id, feature_id));`, UNIQUE (layer_id, feature_id));`,
`CREATE INDEX ON layer_feature(layer_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_feature_layer_id_feature_id_key",
"layer_id", "layer_id",
"feature_id", "feature_id",
"detector_id") "detector_id",
"namespace_id")
} }
func queryPersistNamespace(count int) string { func queryPersistNamespace(count int) string {