diff --git a/glean/db/Glean/Query/UserQuery.hs b/glean/db/Glean/Query/UserQuery.hs index f2c01ac1f..8f6b13c7e 100644 --- a/glean/db/Glean/Query/UserQuery.hs +++ b/glean/db/Glean/Query/UserQuery.hs @@ -93,6 +93,7 @@ import Glean.Schema.Util import Glean.Util.Observed as Observed import Glean.Query.Typecheck import Glean.Bytecode.SysCalls (userQuerySysCalls) +import Glean.Types (UserQueryCont) -- NOTE: We keep the public interface monomorphic, at least for now. @@ -599,7 +600,6 @@ userQueryWrites env odb config bounds lookup repo pred q = do minus = Map.unionWith (-) hasFacts pid m = maybe False (> 0) $ Map.lookup pid m - userQueryImpl :: Database.Env -> OpenDB s @@ -619,7 +619,7 @@ userQueryImpl bounds lookup repo - Thrift.UserQuery{..} = do + query@Thrift.UserQuery{..} = do let opts = fromMaybe def userQuery_options case Thrift.userQueryOptions_syntax opts of @@ -628,22 +628,25 @@ userQueryImpl "query syntax not supported: " <> Text.pack (show other) let - schema@DbSchema{..} = odbSchema odb - opts = fromMaybe def userQuery_options + schema = odbSchema odb stored = Thrift.userQueryOptions_store_derived_facts opts debug = Thrift.userQueryOptions_debug opts schemaVersion <- schemaVersionForQuery schema config userQuery_schema_id - trans <- transformationsForQuery schema schemaVersion - (returnType, compileTime, irDiag, cont) <- + compileInfo <- case Thrift.userQueryOptions_continuation opts of Just ucont | Just retTy <- Thrift.userQueryCont_returnType ucont -> do (compileTime, _, returnType) <- timeIt $ compileType schema schemaVersion retTy - return (returnType, compileTime, [], Right ucont) + return CompileInfo { + returnType = returnType, + compileTime = compileTime, + irDiag = [], + cont = Right ucont + } -- This is either a new query or the continuation of a query -- that returns a temporary predicate. @@ -666,10 +669,55 @@ userQueryImpl Just c -> Right c Nothing -> Left query - return (ty, compileTime, irDiag, cont) + return CompileInfo { + returnType = ty, + compileTime = compileTime, + irDiag = irDiag, + cont = cont + } + + if Thrift.userQueryOptions_just_check opts then do + return emptyResult + else do + runQuery env odb config bounds lookup repo compileInfo query + +data CompileInfo = CompileInfo { + returnType :: Type, + compileTime :: Double, + irDiag :: [Text], + cont :: Either CodegenQuery UserQueryCont +} +runQuery + :: Database.Env + -> OpenDB s + -> ServerConfig.Config + -> Boundaries + -> Lookup + -> Thrift.Repo + -> CompileInfo + -> Thrift.UserQuery + -> IO (Results Stats Thrift.Fact) +runQuery + env + odb + config + bounds + lookup + repo + CompileInfo{..} + Thrift.UserQuery{..} = do vlog 2 $ "return type: " <> show (displayDefault returnType) + let + schema@DbSchema{..} = odbSchema odb + opts = fromMaybe def userQuery_options + stored = Thrift.userQueryOptions_store_derived_facts opts + + schemaVersion <- + schemaVersionForQuery schema config userQuery_schema_id + trans <- transformationsForQuery schema schemaVersion + details@PredicateDetails{..} <- case returnType of Angle.PredicateTy (PidRef pid _) -> case IntMap.lookup (fromIntegral (fromPid pid)) predicatesByPid of @@ -745,6 +793,7 @@ userQueryImpl Left query -> do let + debug = Thrift.userQueryOptions_debug opts bytecodeDiag sub = [ "bytecode:\n" <> Text.unlines (disassemble "Query" userQuerySysCalls $ compiledQuerySub sub) @@ -802,8 +851,8 @@ userQueryImpl } return $ if Thrift.userQueryOptions_omit_results opts - then withoutFacts results - else results + then withoutFacts results + else results transformationsForQuery :: DbSchema @@ -943,6 +992,27 @@ mkQueryRuntimeOptions | otherwise -> ResultsOnly } +emptyResult :: Results Stats fact +emptyResult = Results { + resFacts = mempty + , resPredicate = Nothing + , resNestedFacts = mempty + , resCont = Nothing + , resStats = Stats { + statFactCount = 0 + , statResultCount = 0 + , statFullScans = [] + } + , resDiags = [] + , resWriteHandle = Nothing + , resFactsSearched = Nothing + , resType = Nothing + , resBytecodeSize = Nothing + , resCompileTime = Nothing + , resCodegenTime = Nothing + , resExecutionTime = Nothing + } + {- Note [Writing derived facts] diff --git a/glean/hs/Glean/Query/Thrift/Internal.hs b/glean/hs/Glean/Query/Thrift/Internal.hs index 7ef8dd3cf..c17f17dc9 100644 --- a/glean/hs/Glean/Query/Thrift/Internal.hs +++ b/glean/hs/Glean/Query/Thrift/Internal.hs @@ -31,6 +31,7 @@ module Glean.Query.Thrift.Internal , showUserQueryStats , displayQuery , decodeResults + , justCheck ) where import Control.Exception @@ -215,6 +216,12 @@ store (Query q) = Query q' q' = q { userQuery_options = Just (fromMaybe def (userQuery_options q)) { userQueryOptions_store_derived_facts = True } } +justCheck :: Query a -> Query a +justCheck (Query q) = Query q' + where + q' = q { userQuery_options = Just (fromMaybe def (userQuery_options q)) + { userQueryOptions_just_check = True } } + reportUserQueryStats :: Thrift.UserQueryStats -> IO () reportUserQueryStats stats = vlog 1 $ showUserQueryStats stats diff --git a/glean/if/glean.thrift b/glean/if/glean.thrift index f864ddcf1..4c9c45a41 100644 --- a/glean/if/glean.thrift +++ b/glean/if/glean.thrift @@ -510,6 +510,9 @@ struct UserQueryOptions { // A more fine-grained alternative to recursive = true. Only // fields of a predicate in the list will be fetched. 14: list expand_predicates; + + // if true, the query will be compiled, but no facts will be retrieved + 15: bool just_check = false; } struct QueryDebugOptions { diff --git a/glean/test/tests/Angle/MiscTest.hs b/glean/test/tests/Angle/MiscTest.hs index fc90fe222..638008893 100644 --- a/glean/test/tests/Angle/MiscTest.hs +++ b/glean/test/tests/Angle/MiscTest.hs @@ -46,6 +46,7 @@ main = withUnitTest $ testRunner $ TestList , TestLabel "limitBytes" limitTest , TestLabel "fullScans" fullScansTest , TestLabel "newold" $ newOldTest id + , TestLabel "justCheck" justCheckTest ] newOldTest :: (forall a . Query a -> Query a) -> Test @@ -421,6 +422,22 @@ limitTest = dbTestCase $ \env repo -> do Angle.query $ predicate @Glean.Test.Edge wild assertBool "limitBytes" (length results == 2 && truncated) +justCheckTest :: Test +justCheckTest = dbTestCase $ \env repo -> do + results <- runQuery_ env repo $ justCheck $ Angle.query $ + predicate @Cxx.Name wild + assertEqual "just check" 0 (length results) + + results <- try $ runQuery_ env repo $ justCheck $ angleData @Text + [s| + Cxx1.Name X + |] + assertBool "just check - bad query" $ + case results of + Left (BadQuery _) -> True + _ -> False + + fullScansTest :: Test fullScansTest = TestList $ [ TestLabel "no full scans" $ TestCase $ withTestDB [] $ \env repo -> do