Skip to content

Commit 5b69586

Browse files
simonhollisfacebook-github-bot
authored andcommitted
Merge improvements 2/2: Adding read of schema from JSON
Summary: `glean merge` officially supports supplying both databases to be merged and JSON files + and inventory file to be merged. However, if supplying raw JSONs, there's no `dbSchema` available, and the merge fails. This diff handles the raw JSON case by going off to configerator and reading the schema definitions there. It relies on a `schema_id` field being present in the JSONs to lookup the correct schema. Reviewed By: iamirzhan Differential Revision: D73030289 fbshipit-source-id: 9c6ea68acfaf383d1b7f865342f2cc6f1aff5556
1 parent af6ebf0 commit 5b69586

5 files changed

Lines changed: 131 additions & 10 deletions

File tree

glean.cabal.in

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ common fb-haskell
2525
default-extensions:
2626
BangPatterns
2727
BinaryLiterals
28+
CPP
2829
DataKinds
2930
DeriveDataTypeable
3031
DeriveGeneric
@@ -133,6 +134,10 @@ common folly
133134
build-depends: fb-util < 0.2
134135
pkgconfig-depends: libfolly
135136

137+
-- Facebook-specific features
138+
flag facebook
139+
default: False
140+
136141
common deps
137142
build-depends:
138143
fb-util,
@@ -1124,7 +1129,10 @@ executable glean
11241129
GleanCLI.Restore
11251130
GleanCLI.Write
11261131
ghc-options: -main-is GleanCLI -with-rtsopts=-I0
1132+
default-extensions: CPP
11271133
extra-libraries: stdc++
1134+
if flag(facebook)
1135+
ghc-options: -DGLEAN_FACEBOOK
11281136
build-depends:
11291137
glean:cli-types,
11301138
glean:client-hs,

glean/db/Glean/Database/Config.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ module Glean.Database.Config (
2525
SchemaIndex(..),
2626
schemaForSchemaId,
2727
ProcessedSchema(..),
28+
schemaSourceIndexConfig,
2829
catSchemaFiles,
2930
schemaLocationToSource,
3031
schemaLocationFiles,

glean/db/Glean/Database/Schema.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ module Glean.Database.Schema
1616
, newDbSchema
1717
, newMergedDbSchema
1818
, mkDbSchemaFromSource
19+
, mkDbSchema
20+
, findSchemaInIndex
1921
, lookupPid
2022
, compareSchemaPredicates
2123
, validateNewSchema

glean/db/Glean/Database/Util.hs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{-
2+
Copyright (c) Meta Platforms, Inc. and affiliates.
3+
All rights reserved.
4+
5+
This source code is licensed under the BSD-style license found in the
6+
LICENSE file in the root directory of this source tree.
7+
-}
8+
9+
module Glean.Database.Util
10+
(getDbSchemaFromId
11+
) where
12+
13+
import Control.Exception (throwIO)
14+
import Control.Monad.IO.Class (MonadIO, liftIO)
15+
import Data.Default (def)
16+
17+
import Glean.Database.Config (DebugFlags, SchemaIndex, schemaForSchemaId)
18+
import Glean.Database.Schema (DbSchema, DbContent,
19+
readWriteContent, newDbSchema)
20+
import Glean.Database.Schema.Types (SchemaSelector(..))
21+
import Glean.Types (SchemaId(..))
22+
import qualified Data.Text as Text
23+
24+
25+
-- | Get a DbSchema instance from a SchemaId
26+
dbSchemaFromId :: MonadIO m
27+
=> SchemaIndex -- ^ Schema index to search in
28+
-> SchemaId -- ^ Schema ID to look up
29+
-> DbContent -- ^ Database content information
30+
-> DebugFlags -- ^ Debug flags
31+
-> m DbSchema
32+
dbSchemaFromId index schemaId dbContent debug = liftIO $ do
33+
case schemaForSchemaId index schemaId of
34+
Nothing -> throwIO $ userError $ "Schema ID not found: " ++
35+
Text.unpack (unSchemaId schemaId)
36+
Just _ -> do
37+
-- Create a new DbSchema from the processed schema
38+
newDbSchema Nothing index (SpecificSchemaId schemaId) dbContent debug
39+
40+
-- | Get a DbSchema instance from a SchemaId using default settings
41+
getDbSchemaFromId :: MonadIO m
42+
=> Maybe SchemaIndex -- ^ Schema index to search in
43+
-> SchemaId -- ^ Schema ID to look up
44+
-> m DbSchema
45+
getDbSchemaFromId Nothing _ =
46+
liftIO $ throwIO $ userError "No schema index provided"
47+
getDbSchemaFromId (Just index) schemaId =
48+
dbSchemaFromId index schemaId readWriteContent def

glean/tools/gleancli/GleanCLI/Merge.hs

Lines changed: 72 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
This source code is licensed under the BSD-style license found in the
66
LICENSE file in the root directory of this source tree.
77
-}
8-
9-
{-# LANGUAGE ApplicativeDo #-}
8+
{-# LANGUAGE CPP, ApplicativeDo #-}
109
module GleanCLI.Merge (MergeCommand) where
1110

1211
import Control.Exception
@@ -19,10 +18,11 @@ import System.Directory
1918
import System.FilePath
2019
import System.IO
2120
import System.Process
22-
21+
import Data.Text(unpack)
2322
import Control.Concurrent.Stream
2423
import Util.OptParse
25-
import Util.Log
24+
import Util.Log ( logInfo )
25+
import Glean.Util.ThriftSource (load)
2626
import Thrift.Protocol.Compact
2727

2828
import Glean.LocalOrRemote (loadDbSchema)
@@ -35,13 +35,23 @@ import Glean.RTS.Foreign.Define (DefineFlags(..), defineBatch)
3535
import qualified Glean.RTS.Foreign.Inventory as Inventory
3636
import Glean.RTS.Foreign.Ownership
3737
import Glean.Database.Schema.Types
38+
import Glean.Database.Util (getDbSchemaFromId)
3839

3940
import GleanCLI.Types
4041
import GleanCLI.Common (dbOpts, fileFormatOpt, FileFormat (..))
4142
import Glean.Write (fileToBatches, schemaIdToOpts)
4243
import Glean.Write.JSON (buildJsonBatch)
44+
import Glean.Database.Config (schemaSourceIndexConfig)
45+
import Glean.DefaultConfigs(schemaConfigPath)
4346
import System.Directory.Extra (listFiles)
4447

48+
#if GLEAN_FACEBOOK
49+
import Configerator (
50+
withConfigeratorAPI,
51+
defaultConfigeratorOptions,
52+
)
53+
#endif
54+
4555

4656
data MergeCommand = MergeCommand
4757
{ mergeFiles :: [FilePath]
@@ -88,6 +98,9 @@ instance Plugin MergeCommand where
8898
return (schemaInventory dbSchema, Just dbSchema)
8999
Right mergeInventory -> do
90100
inventory <- Inventory.deserialize <$> B.readFile mergeInventory
101+
-- Get the schema_id from the JSON's schema ID field
102+
--- this needs to be done when the file is considered later on
103+
-- dbSchema <-
91104
return (inventory, Nothing)
92105
createDirectoryIfMissing True mergeOutDir
93106
hSetBuffering stderr LineBuffering
@@ -136,21 +149,71 @@ instance Plugin MergeCommand where
136149
let batch = serializeCompact $
137150
batch0 { batch_owned =
138151
ownershipUnits (unionOwnership ownership) }
139-
hPutStrLn stderr $ "Writing " <> out <>
140-
" (" <> show (B.length batch) <> ")"
152+
logInfo $ "Writing " <> out <>
153+
" (" <> show (B.length batch) <> " bytes)"
141154
B.writeFile out batch
142155

143156
merge fileFormat inventory dbSchema files write = loop 0 0 Nothing files
144157
where
145158
read :: FilePath -> Int -> FactSet -> IO FactOwnership
146159
read file size factSet = do
147160
logInfo $ "Reading " <> file <> " (" <> show size <> " bytes)"
161+
-- Merge can take an existing db or an inventory.
162+
-- A DB has a dbSchema, so we can use that to build the batches
163+
-- to merge
164+
-- An inventory doesn't have a dbSchema, so leads to "Nothing"
165+
-- This means that we need to get the schema from somewhere
166+
--- this will be the first JSON file we see
167+
-- read the 'schema_id' field in that file and create a
168+
-- new dbSchema intance with that using configerator's
169+
-- stored default schema index
170+
148171
batch <- case fileFormat of
149172
JsonFormat -> do
150173
case dbSchema of
151-
Nothing -> throwIO $ ErrorCall $
152-
"No db schema to serialize json format file. "
153-
<> "Please specify the database"
174+
Nothing ->
175+
#if GLEAN_FACEBOOK
176+
withConfigeratorAPI defaultConfigeratorOptions $ \configAPI ->
177+
-- Load the schema index from the schema directory
178+
let schemaIndexResult =
179+
Right $ schemaSourceIndexConfig schemaConfigPath in do
180+
hPutStrLn stderr $
181+
"Reading current schema index from default "
182+
<> "configerator with path " <> unpack schemaConfigPath
183+
184+
-- Get the schema_id from the JSON file
185+
(batches, Just schema_id) <- fileToBatches file
186+
logInfo("Schema_ID from JSON file " <> file <> " is "
187+
<> show schema_id)
188+
189+
-- Try to get the DbSchema from the schema ID string
190+
case schemaIndexResult of
191+
Left err -> do
192+
throwIO $ ErrorCall err
193+
Right schemaIndex -> do
194+
logInfo $ "Looking up schema with ID: " <>
195+
show schema_id
196+
-- Materialise the schema index from its Thrift source
197+
concreteIndex <- load configAPI schemaIndex
198+
dbSchemaResult <-
199+
(Right <$>
200+
getDbSchemaFromId (Just concreteIndex) schema_id
201+
)
202+
`catch` \(e :: SomeException) ->
203+
return $ Left $ "Failed to get schema: " <> show e
204+
205+
case dbSchemaResult of
206+
Left err -> do
207+
throwIO $ ErrorCall("Error getting dbSchemaResult"
208+
<> " - exiting. Error is:\n" <> err)
209+
Right dbSchema -> do
210+
logInfo $ "Successfully loaded schema with ID: "
211+
<> show schema_id
212+
buildJsonBatch dbSchema
213+
(schemaIdToOpts $ Just(schemaId dbSchema)) batches
214+
#else
215+
return $ Left $ "Failed to get schema: "
216+
#endif
154217
Just schema -> do
155218
(batches, schema_id_file) <- fileToBatches file
156219
if Just(schemaId schema) == schema_id_file then
@@ -167,7 +230,6 @@ instance Plugin MergeCommand where
167230
buildJsonBatch schema
168231
(schemaIdToOpts $ getSchemaId schema) batches
169232

170-
171233
BinaryFormat -> do
172234
bytes <- B.readFile file
173235
case deserializeCompact bytes of

0 commit comments

Comments
 (0)