go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/mql/mql.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package mql 5 6 import ( 7 "errors" 8 9 "github.com/rs/zerolog/log" 10 "go.mondoo.com/cnquery" 11 "go.mondoo.com/cnquery/llx" 12 "go.mondoo.com/cnquery/mql/internal" 13 "go.mondoo.com/cnquery/mqlc" 14 ) 15 16 // New creates a new MQL executor instance. It allows you to easily run multiple queries against the 17 // same runtime 18 func New(runtime llx.Runtime, features cnquery.Features) *Executor { 19 return &Executor{ 20 runtime: runtime, 21 features: features, 22 } 23 } 24 25 type Executor struct { 26 runtime llx.Runtime 27 features cnquery.Features 28 } 29 30 // Exec runs a query with properties against the runtime 31 func (e *Executor) Exec(query string, props map[string]*llx.Primitive) (*llx.RawData, error) { 32 return Exec(query, e.runtime, e.features, props) 33 } 34 35 func Exec(query string, runtime llx.Runtime, features cnquery.Features, props map[string]*llx.Primitive) (*llx.RawData, error) { 36 bundle, err := mqlc.Compile(query, props, mqlc.NewConfig(runtime.Schema(), features)) 37 if err != nil { 38 return nil, errors.New("failed to compile: " + err.Error()) 39 } 40 41 var results []*llx.RawResult 42 43 if len(bundle.CodeV2.Entrypoints()) == 0 { 44 return llx.NilData, nil 45 } 46 47 if !bundle.CodeV2.Blocks[0].SingleValue { 48 log.Warn().Str("query", query).Msg("mql> Code must only return one value, but it has many configured. Only returning last result.") 49 } 50 51 raw, err := ExecuteCode(runtime, bundle, props, features) 52 if err != nil { 53 return nil, err 54 } 55 56 results = llx.ReturnValuesV2(bundle, func(checksum string) (*llx.RawResult, bool) { 57 res, ok := raw[checksum] 58 return res, ok 59 }) 60 61 if len(results) > 1 { 62 return nil, errors.New("too many results received") 63 } 64 65 rawres := results[0] 66 res, err := rawres.Data.Dereference(rawres.CodeID, bundle) 67 if err != nil { 68 return nil, err 69 } 70 71 return res, nil 72 } 73 74 func ExecuteCode(runtime llx.Runtime, codeBundle *llx.CodeBundle, props map[string]*llx.Primitive, features cnquery.Features) (map[string]*llx.RawResult, error) { 75 builder := internal.NewBuilder() 76 builder.WithFeatureBoolAssertions(features.IsActive(cnquery.BoolAssertions)) 77 78 builder.AddQuery(codeBundle, nil, props) 79 for _, checksum := range internal.CodepointChecksums(codeBundle) { 80 builder.CollectDatapoint(checksum) 81 } 82 83 resultMap := map[string]*llx.RawResult{} 84 collector := &internal.FuncCollector{ 85 SinkDataFunc: func(results []*llx.RawResult) { 86 for _, d := range results { 87 resultMap[d.CodeID] = d 88 } 89 }, 90 } 91 builder.AddDatapointCollector(collector) 92 93 ge, err := builder.Build(runtime.Schema(), runtime, "") 94 if err != nil { 95 return nil, err 96 } 97 98 if err := ge.Execute(); err != nil { 99 return nil, err 100 } 101 102 return resultMap, nil 103 }