2017-07-26 23:23:54 +00:00
|
|
|
package pgsql
|
|
|
|
|
|
|
|
import (
|
|
|
|
"database/sql"
|
|
|
|
"errors"
|
|
|
|
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
|
|
|
|
"github.com/coreos/clair/database"
|
|
|
|
"github.com/coreos/clair/pkg/commonerr"
|
|
|
|
)
|
|
|
|
|
2018-09-05 15:34:49 +00:00
|
|
|
func (tx *pgSession) UpsertAncestry(ancestry database.AncestryWithContent) error {
|
2017-07-26 23:23:54 +00:00
|
|
|
if ancestry.Name == "" {
|
2018-09-05 15:34:49 +00:00
|
|
|
log.Error("Empty ancestry name is not allowed")
|
2017-07-26 23:23:54 +00:00
|
|
|
return commonerr.NewBadRequestError("could not insert an ancestry with empty name")
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(ancestry.Layers) == 0 {
|
2018-09-05 15:34:49 +00:00
|
|
|
log.Error("Empty ancestry is not allowed")
|
2017-07-26 23:23:54 +00:00
|
|
|
return commonerr.NewBadRequestError("could not insert an ancestry with 0 layers")
|
|
|
|
}
|
|
|
|
|
2018-09-05 15:34:49 +00:00
|
|
|
if err := tx.deleteAncestry(ancestry.Name); err != nil {
|
2017-07-26 23:23:54 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var ancestryID int64
|
2018-09-05 15:34:49 +00:00
|
|
|
if err := tx.QueryRow(insertAncestry, ancestry.Name).Scan(&ancestryID); err != nil {
|
2017-07-26 23:23:54 +00:00
|
|
|
if isErrUniqueViolation(err) {
|
2018-09-05 15:34:49 +00:00
|
|
|
return handleError("insertAncestry", errors.New("other Go-routine is processing this ancestry (skip)"))
|
2017-07-26 23:23:54 +00:00
|
|
|
}
|
|
|
|
return handleError("insertAncestry", err)
|
|
|
|
}
|
|
|
|
|
2018-09-05 15:34:49 +00:00
|
|
|
if err := tx.insertAncestryLayers(ancestryID, ancestry.Layers); err != nil {
|
2017-07-26 23:23:54 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return tx.persistProcessors(persistAncestryLister,
|
|
|
|
"persistAncestryLister",
|
|
|
|
persistAncestryDetector,
|
|
|
|
"persistAncestryDetector",
|
2018-09-05 15:34:49 +00:00
|
|
|
ancestryID, ancestry.ProcessedBy)
|
2017-07-26 23:23:54 +00:00
|
|
|
}
|
|
|
|
|
2018-09-05 15:34:49 +00:00
|
|
|
func (tx *pgSession) FindAncestry(name string) (database.Ancestry, bool, error) {
|
|
|
|
var (
|
|
|
|
ancestryID int64
|
|
|
|
ancestry = database.Ancestry{Name: name}
|
|
|
|
err error
|
|
|
|
)
|
2017-07-26 23:23:54 +00:00
|
|
|
|
2018-09-05 15:34:49 +00:00
|
|
|
if err = tx.QueryRow(searchAncestry, name).Scan(&ancestryID); err != nil {
|
2017-07-26 23:23:54 +00:00
|
|
|
if err == sql.ErrNoRows {
|
2018-09-05 15:34:49 +00:00
|
|
|
return ancestry, false, nil
|
2017-07-26 23:23:54 +00:00
|
|
|
}
|
2018-09-05 15:34:49 +00:00
|
|
|
return ancestry, false, handleError("searchAncestry", err)
|
2017-07-26 23:23:54 +00:00
|
|
|
}
|
|
|
|
|
2018-09-05 15:34:49 +00:00
|
|
|
if ancestry.Layers, err = tx.findAncestryLayers(ancestryID); err != nil {
|
|
|
|
return ancestry, false, err
|
2017-07-26 23:23:54 +00:00
|
|
|
}
|
|
|
|
|
2018-09-05 15:34:49 +00:00
|
|
|
if ancestry.ProcessedBy.Detectors, err = tx.findProcessors(searchAncestryDetectors, "searchAncestryDetectors", "detector", ancestryID); err != nil {
|
|
|
|
return ancestry, false, err
|
2017-07-26 23:23:54 +00:00
|
|
|
}
|
|
|
|
|
2018-09-05 15:34:49 +00:00
|
|
|
if ancestry.ProcessedBy.Listers, err = tx.findProcessors(searchAncestryListers, "searchAncestryListers", "lister", ancestryID); err != nil {
|
|
|
|
return ancestry, false, err
|
2017-07-26 23:23:54 +00:00
|
|
|
}
|
|
|
|
|
2018-09-05 15:34:49 +00:00
|
|
|
return ancestry, true, nil
|
2017-07-26 23:23:54 +00:00
|
|
|
}
|
|
|
|
|
2018-09-05 15:34:49 +00:00
|
|
|
func (tx *pgSession) FindAncestryWithContent(name string) (database.AncestryWithContent, bool, error) {
|
2017-07-26 23:23:54 +00:00
|
|
|
var (
|
2018-09-05 15:34:49 +00:00
|
|
|
ancestryContent database.AncestryWithContent
|
|
|
|
isValid bool
|
|
|
|
err error
|
2017-07-26 23:23:54 +00:00
|
|
|
)
|
|
|
|
|
2018-09-05 15:34:49 +00:00
|
|
|
if ancestryContent.Ancestry, isValid, err = tx.FindAncestry(name); err != nil || !isValid {
|
|
|
|
return ancestryContent, isValid, err
|
2017-07-26 23:23:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
rows, err := tx.Query(searchAncestryFeatures, name)
|
|
|
|
if err != nil {
|
2018-09-05 15:34:49 +00:00
|
|
|
return ancestryContent, false, handleError("searchAncestryFeatures", err)
|
2017-07-26 23:23:54 +00:00
|
|
|
}
|
|
|
|
|
2018-09-05 15:34:49 +00:00
|
|
|
features := map[int][]database.NamespacedFeature{}
|
2017-07-26 23:23:54 +00:00
|
|
|
for rows.Next() {
|
2018-09-05 15:34:49 +00:00
|
|
|
var (
|
|
|
|
feature database.NamespacedFeature
|
|
|
|
// layerIndex is used to determine which layer the namespaced feature belongs to.
|
|
|
|
layerIndex sql.NullInt64
|
|
|
|
)
|
|
|
|
|
|
|
|
if err := rows.Scan(&feature.Namespace.Name,
|
|
|
|
&feature.Namespace.VersionFormat,
|
|
|
|
&feature.Feature.Name, &feature.Feature.Version,
|
|
|
|
&layerIndex); err != nil {
|
|
|
|
return ancestryContent, false, handleError("searchAncestryFeatures", err)
|
2017-07-26 23:23:54 +00:00
|
|
|
}
|
2018-09-05 15:34:49 +00:00
|
|
|
|
|
|
|
feature.Feature.VersionFormat = feature.Namespace.VersionFormat // This looks strange.
|
|
|
|
features[int(layerIndex.Int64)] = append(features[int(layerIndex.Int64)], feature)
|
|
|
|
}
|
|
|
|
|
|
|
|
// By the assumption of Ancestry Layer Index, we have the ancestry's layer
|
|
|
|
// index corresponding to the index in the array.
|
|
|
|
for index, layer := range ancestryContent.Ancestry.Layers {
|
|
|
|
ancestryLayer := database.AncestryLayer{Layer: layer}
|
|
|
|
ancestryLayer.DetectedFeatures, _ = features[index]
|
|
|
|
ancestryContent.Layers = append(ancestryContent.Layers, ancestryLayer)
|
2017-07-26 23:23:54 +00:00
|
|
|
}
|
|
|
|
|
2018-09-05 15:34:49 +00:00
|
|
|
return ancestryContent, true, nil
|
2017-07-26 23:23:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (tx *pgSession) deleteAncestry(name string) error {
|
|
|
|
result, err := tx.Exec(removeAncestry, name)
|
|
|
|
if err != nil {
|
|
|
|
return handleError("removeAncestry", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = result.RowsAffected()
|
|
|
|
if err != nil {
|
|
|
|
return handleError("removeAncestry", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (tx *pgSession) findProcessors(query, queryName, processorType string, id int64) ([]string, error) {
|
|
|
|
rows, err := tx.Query(query, id)
|
|
|
|
if err != nil {
|
|
|
|
if err == sql.ErrNoRows {
|
|
|
|
log.Warning("No " + processorType + " are used")
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
return nil, handleError(queryName, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
processors []string
|
|
|
|
processor string
|
|
|
|
)
|
|
|
|
|
|
|
|
for rows.Next() {
|
|
|
|
err := rows.Scan(&processor)
|
|
|
|
if err != nil {
|
|
|
|
return nil, handleError(queryName, err)
|
|
|
|
}
|
|
|
|
processors = append(processors, processor)
|
|
|
|
}
|
|
|
|
|
|
|
|
return processors, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (tx *pgSession) findAncestryLayers(ancestryID int64) ([]database.Layer, error) {
|
|
|
|
rows, err := tx.Query(searchAncestryLayer, ancestryID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, handleError("searchAncestryLayer", err)
|
|
|
|
}
|
2018-09-05 15:34:49 +00:00
|
|
|
|
2017-07-26 23:23:54 +00:00
|
|
|
layers := []database.Layer{}
|
|
|
|
for rows.Next() {
|
|
|
|
var layer database.Layer
|
2018-09-05 15:34:49 +00:00
|
|
|
if err := rows.Scan(&layer.Hash); err != nil {
|
2017-07-26 23:23:54 +00:00
|
|
|
return nil, handleError("searchAncestryLayer", err)
|
|
|
|
}
|
2018-09-05 15:34:49 +00:00
|
|
|
|
2017-07-26 23:23:54 +00:00
|
|
|
layers = append(layers, layer)
|
|
|
|
}
|
2018-09-05 15:34:49 +00:00
|
|
|
|
2017-07-26 23:23:54 +00:00
|
|
|
return layers, nil
|
|
|
|
}
|
|
|
|
|
2018-09-05 15:34:49 +00:00
|
|
|
// insertAncestryLayers inserts the ancestry layers along with its content into
|
|
|
|
// the database. The layers are 0 based indexed in the original order.
|
|
|
|
func (tx *pgSession) insertAncestryLayers(ancestryID int64, layers []database.AncestryLayer) error {
|
2017-07-26 23:23:54 +00:00
|
|
|
//TODO(Sida): use bulk insert.
|
|
|
|
stmt, err := tx.Prepare(insertAncestryLayer)
|
|
|
|
if err != nil {
|
|
|
|
return handleError("insertAncestryLayer", err)
|
|
|
|
}
|
|
|
|
|
2018-09-05 15:34:49 +00:00
|
|
|
ancestryLayerIDs := []sql.NullInt64{}
|
2017-07-26 23:23:54 +00:00
|
|
|
for index, layer := range layers {
|
2018-09-05 15:34:49 +00:00
|
|
|
var ancestryLayerID sql.NullInt64
|
|
|
|
if err := stmt.QueryRow(ancestryID, index, layer.Hash).Scan(&ancestryLayerID); err != nil {
|
2017-07-26 23:23:54 +00:00
|
|
|
return handleError("insertAncestryLayer", commonerr.CombineErrors(err, stmt.Close()))
|
|
|
|
}
|
|
|
|
|
2018-09-05 15:34:49 +00:00
|
|
|
ancestryLayerIDs = append(ancestryLayerIDs, ancestryLayerID)
|
2017-07-26 23:23:54 +00:00
|
|
|
}
|
|
|
|
|
2018-09-05 15:34:49 +00:00
|
|
|
if err := stmt.Close(); err != nil {
|
|
|
|
return handleError("Failed to close insertAncestryLayer statement", err)
|
2017-07-26 23:23:54 +00:00
|
|
|
}
|
|
|
|
|
2018-09-05 15:34:49 +00:00
|
|
|
stmt, err = tx.Prepare(insertAncestryLayerFeature)
|
|
|
|
defer stmt.Close()
|
2017-07-26 23:23:54 +00:00
|
|
|
|
2018-09-05 15:34:49 +00:00
|
|
|
for i, layer := range layers {
|
|
|
|
var (
|
|
|
|
nsFeatureIDs []sql.NullInt64
|
|
|
|
layerID = ancestryLayerIDs[i]
|
|
|
|
)
|
|
|
|
|
|
|
|
if nsFeatureIDs, err = tx.findNamespacedFeatureIDs(layer.DetectedFeatures); err != nil {
|
|
|
|
return err
|
2017-07-26 23:23:54 +00:00
|
|
|
}
|
|
|
|
|
2018-09-05 15:34:49 +00:00
|
|
|
for _, id := range nsFeatureIDs {
|
|
|
|
if _, err := stmt.Exec(layerID, id); err != nil {
|
|
|
|
return handleError("insertAncestryLayerFeature", commonerr.CombineErrors(err, stmt.Close()))
|
|
|
|
}
|
2017-07-26 23:23:54 +00:00
|
|
|
}
|
2018-09-05 15:34:49 +00:00
|
|
|
|
2017-07-26 23:23:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|