func (pgSQL *pgSQL) FindLayer(name string, withFeatures, withVulnerabilities bool) (database.Layer, error) { subquery := "all" if withFeatures { subquery += "/features" } else if withVulnerabilities { subquery += "/features+vulnerabilities" } defer observeQueryTime("FindLayer", subquery, time.Now()) // Find the layer var layer database.Layer var parentID zero.Int var parentName zero.String var namespaceID zero.Int var namespaceName sql.NullString t := time.Now() err := pgSQL.QueryRow(searchLayer, name). Scan(&layer.ID, &layer.Name, &layer.EngineVersion, &parentID, &parentName, &namespaceID, &namespaceName) observeQueryTime("FindLayer", "searchLayer", t) if err != nil { return layer, handleError("searchLayer", err) } if !parentID.IsZero() { layer.Parent = &database.Layer{ Model: database.Model{ID: int(parentID.Int64)}, Name: parentName.String, } } if !namespaceID.IsZero() { layer.Namespace = &database.Namespace{ Model: database.Model{ID: int(namespaceID.Int64)}, Name: namespaceName.String, } } // Find its features if withFeatures || withVulnerabilities { // Create a transaction to disable hash/merge joins as our experiments have shown that // PostgreSQL 9.4 makes bad planning decisions about: // - joining the layer tree to feature versions and feature // - joining the feature versions to affected/fixed feature version and vulnerabilities // It would for instance do a merge join between affected feature versions (300 rows, estimated // 3000 rows) and fixed in feature version (100k rows). In this case, it is much more // preferred to use a nested loop. tx, err := pgSQL.Begin() if err != nil { return layer, handleError("FindLayer.Begin()", err) } defer tx.Commit() _, err = tx.Exec(disableHashJoin) if err != nil { log.Warningf("FindLayer: could not disable hash join: %s", err) } _, err = tx.Exec(disableMergeJoin) if err != nil { log.Warningf("FindLayer: could not disable merge join: %s", err) } t = time.Now() featureVersions, err := getLayerFeatureVersions(tx, layer.ID) observeQueryTime("FindLayer", "getLayerFeatureVersions", t) if err != nil { return layer, err } layer.Features = featureVersions if withVulnerabilities { // Load the vulnerabilities that affect the FeatureVersions. t = time.Now() err := loadAffectedBy(tx, layer.Features) observeQueryTime("FindLayer", "loadAffectedBy", t) if err != nil { return layer, err } } } return layer, nil }
func testInsertLayerUpdate(t *testing.T, datastore database.Datastore) { f7 := database.FeatureVersion{ Feature: database.Feature{ Namespace: database.Namespace{Name: "TestInsertLayerNamespace3"}, Name: "TestInsertLayerFeature7", }, Version: types.NewVersionUnsafe("0.01"), } l3, _ := datastore.FindLayer("TestInsertLayer3", true, false) l3u := database.Layer{ Name: l3.Name, Parent: l3.Parent, Namespace: &database.Namespace{Name: "TestInsertLayerNamespaceUpdated1"}, Features: []database.FeatureVersion{f7}, } l4u := database.Layer{ Name: "TestInsertLayer4", Parent: &database.Layer{Name: "TestInsertLayer3"}, Features: []database.FeatureVersion{f7}, EngineVersion: 2, } // Try to re-insert without increasing the EngineVersion. err := datastore.InsertLayer(l3u) assert.Nil(t, err) l3uf, err := datastore.FindLayer(l3u.Name, true, false) if assert.Nil(t, err) { assert.Equal(t, l3.Namespace.Name, l3uf.Namespace.Name) assert.Equal(t, l3.EngineVersion, l3uf.EngineVersion) assert.Len(t, l3uf.Features, len(l3.Features)) } // Update layer l3. // Verify that the Namespace, EngineVersion and FeatureVersions got updated. l3u.EngineVersion = 2 err = datastore.InsertLayer(l3u) assert.Nil(t, err) l3uf, err = datastore.FindLayer(l3u.Name, true, false) if assert.Nil(t, err) { assert.Equal(t, l3u.Namespace.Name, l3uf.Namespace.Name) assert.Equal(t, l3u.EngineVersion, l3uf.EngineVersion) if assert.Len(t, l3uf.Features, 1) { assert.True(t, cmpFV(l3uf.Features[0], f7), "Updated layer should have %#v but actually have %#v", f7, l3uf.Features[0]) } } // Update layer l4. // Verify that the Namespace got updated from its new Parent's, and also verify the // EnginVersion and FeatureVersions. l4u.Parent = &l3uf err = datastore.InsertLayer(l4u) assert.Nil(t, err) l4uf, err := datastore.FindLayer(l3u.Name, true, false) if assert.Nil(t, err) { assert.Equal(t, l3u.Namespace.Name, l4uf.Namespace.Name) assert.Equal(t, l4u.EngineVersion, l4uf.EngineVersion) if assert.Len(t, l4uf.Features, 1) { assert.True(t, cmpFV(l3uf.Features[0], f7), "Updated layer should have %#v but actually have %#v", f7, l4uf.Features[0]) } } }