-- | This module (along with the various @Hasura.RQL.DDL.Schema.*@ modules) provides operations to
-- load and modify the Hasura catalog and schema cache.
--
-- * The /catalog/ refers to the set of PostgreSQL tables and views that store all schema information
--    known by Hasura. This includes any tracked Postgres tables, views, and functions, all remote
--    schemas, and any additionaly Hasura-specific information such as permissions and relationships.
--
--    Primitive functions for loading and modifying the catalog are defined in
--    "Hasura.RQL.DDL.Schema.Catalog", but most uses are wrapped by other functions to synchronize
--    catalog information with the information in the schema cache.
--
-- * The /schema cache/ is a process-global value of type 'SchemaCache' that stores an in-memory
--    representation of the data stored in the catalog. The in-memory representation is not identical
--    to the data in the catalog, since it has some post-processing applied to it in order to make it
--    easier to consume for other parts of the system, such as GraphQL schema generation. For example,
--    although column information is represented by 'RawColumnInfo', the schema cache contains
--    “processed” 'ColumnInfo' values, instead.
--
--    Ultimately, the catalog is the source of truth for all information contained in the schema
--    cache, but to avoid rebuilding the entire schema cache on every change to the catalog, various
--    functions incrementally update the cache when they modify the catalog.
module Hasura.RQL.DDL.Schema
  ( module M,
    RunSQLRes (..),
  )
where

import Data.Aeson
import Data.Text.Encoding qualified as TE
import Database.PG.Query qualified as PG
import Database.PostgreSQL.LibPQ qualified as PQ
import Hasura.Prelude
import Hasura.RQL.DDL.Schema.Cache as M
import Hasura.RQL.DDL.Schema.Catalog as M
import Hasura.RQL.DDL.Schema.Rename as M
import Hasura.Table.API as M

data RunSQLRes = RunSQLRes
  { RunSQLRes -> Text
rrResultType :: Text,
    RunSQLRes -> Value
rrResult :: Value
  }
  deriving (Int -> RunSQLRes -> ShowS
[RunSQLRes] -> ShowS
RunSQLRes -> String
(Int -> RunSQLRes -> ShowS)
-> (RunSQLRes -> String)
-> ([RunSQLRes] -> ShowS)
-> Show RunSQLRes
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> RunSQLRes -> ShowS
showsPrec :: Int -> RunSQLRes -> ShowS
$cshow :: RunSQLRes -> String
show :: RunSQLRes -> String
$cshowList :: [RunSQLRes] -> ShowS
showList :: [RunSQLRes] -> ShowS
Show, (forall x. RunSQLRes -> Rep RunSQLRes x)
-> (forall x. Rep RunSQLRes x -> RunSQLRes) -> Generic RunSQLRes
forall x. Rep RunSQLRes x -> RunSQLRes
forall x. RunSQLRes -> Rep RunSQLRes x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. RunSQLRes -> Rep RunSQLRes x
from :: forall x. RunSQLRes -> Rep RunSQLRes x
$cto :: forall x. Rep RunSQLRes x -> RunSQLRes
to :: forall x. Rep RunSQLRes x -> RunSQLRes
Generic, RunSQLRes -> RunSQLRes -> Bool
(RunSQLRes -> RunSQLRes -> Bool)
-> (RunSQLRes -> RunSQLRes -> Bool) -> Eq RunSQLRes
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: RunSQLRes -> RunSQLRes -> Bool
== :: RunSQLRes -> RunSQLRes -> Bool
$c/= :: RunSQLRes -> RunSQLRes -> Bool
/= :: RunSQLRes -> RunSQLRes -> Bool
Eq)

instance FromJSON RunSQLRes where
  parseJSON :: Value -> Parser RunSQLRes
parseJSON = Options -> Value -> Parser RunSQLRes
forall a.
(Generic a, GFromJSON Zero (Rep a)) =>
Options -> Value -> Parser a
genericParseJSON Options
hasuraJSON

instance ToJSON RunSQLRes where
  toJSON :: RunSQLRes -> Value
toJSON = Options -> RunSQLRes -> Value
forall a.
(Generic a, GToJSON' Value Zero (Rep a)) =>
Options -> a -> Value
genericToJSON Options
hasuraJSON
  toEncoding :: RunSQLRes -> Encoding
toEncoding = Options -> RunSQLRes -> Encoding
forall a.
(Generic a, GToJSON' Encoding Zero (Rep a)) =>
Options -> a -> Encoding
genericToEncoding Options
hasuraJSON

instance PG.FromRes RunSQLRes where
  fromRes :: ResultOk -> ExceptT Text IO RunSQLRes
fromRes (PG.ResultOkEmpty Result
_) =
    RunSQLRes -> ExceptT Text IO RunSQLRes
forall a. a -> ExceptT Text IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (RunSQLRes -> ExceptT Text IO RunSQLRes)
-> RunSQLRes -> ExceptT Text IO RunSQLRes
forall a b. (a -> b) -> a -> b
$ Text -> Value -> RunSQLRes
RunSQLRes Text
"CommandOk" Value
Null
  fromRes (PG.ResultOkData Result
res) = do
    [[Text]]
csvRows <- Result -> ExceptT Text IO [[Text]]
resToCSV Result
res
    RunSQLRes -> ExceptT Text IO RunSQLRes
forall a. a -> ExceptT Text IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (RunSQLRes -> ExceptT Text IO RunSQLRes)
-> RunSQLRes -> ExceptT Text IO RunSQLRes
forall a b. (a -> b) -> a -> b
$ Text -> Value -> RunSQLRes
RunSQLRes Text
"TuplesOk" (Value -> RunSQLRes) -> Value -> RunSQLRes
forall a b. (a -> b) -> a -> b
$ [[Text]] -> Value
forall a. ToJSON a => a -> Value
toJSON [[Text]]
csvRows
    where
      resToCSV :: PQ.Result -> ExceptT Text IO [[Text]]
      resToCSV :: Result -> ExceptT Text IO [[Text]]
resToCSV Result
r = do
        Row
nr <- IO Row -> ExceptT Text IO Row
forall a. IO a -> ExceptT Text IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Row -> ExceptT Text IO Row) -> IO Row -> ExceptT Text IO Row
forall a b. (a -> b) -> a -> b
$ Result -> IO Row
PQ.ntuples Result
r
        Column
nc <- IO Column -> ExceptT Text IO Column
forall a. IO a -> ExceptT Text IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Column -> ExceptT Text IO Column)
-> IO Column -> ExceptT Text IO Column
forall a b. (a -> b) -> a -> b
$ Result -> IO Column
PQ.nfields Result
r

        [Text]
hdr <- [Column]
-> (Column -> ExceptT Text IO Text) -> ExceptT Text IO [Text]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
t a -> (a -> m b) -> m (t b)
forM [Column
0 .. Column -> Column
forall a. Enum a => a -> a
pred Column
nc] ((Column -> ExceptT Text IO Text) -> ExceptT Text IO [Text])
-> (Column -> ExceptT Text IO Text) -> ExceptT Text IO [Text]
forall a b. (a -> b) -> a -> b
$ \Column
ic -> do
          Maybe ByteString
colNameBS <- IO (Maybe ByteString) -> ExceptT Text IO (Maybe ByteString)
forall a. IO a -> ExceptT Text IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Maybe ByteString) -> ExceptT Text IO (Maybe ByteString))
-> IO (Maybe ByteString) -> ExceptT Text IO (Maybe ByteString)
forall a b. (a -> b) -> a -> b
$ Result -> Column -> IO (Maybe ByteString)
PQ.fname Result
r Column
ic
          ExceptT Text IO Text
-> (ByteString -> ExceptT Text IO Text)
-> Maybe ByteString
-> ExceptT Text IO Text
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (Text -> ExceptT Text IO Text
forall a. a -> ExceptT Text IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Text
"unknown") ByteString -> ExceptT Text IO Text
decodeBS Maybe ByteString
colNameBS

        [[Text]]
rows <- [Row]
-> (Row -> ExceptT Text IO [Text]) -> ExceptT Text IO [[Text]]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
t a -> (a -> m b) -> m (t b)
forM [Row
0 .. Row -> Row
forall a. Enum a => a -> a
pred Row
nr] ((Row -> ExceptT Text IO [Text]) -> ExceptT Text IO [[Text]])
-> (Row -> ExceptT Text IO [Text]) -> ExceptT Text IO [[Text]]
forall a b. (a -> b) -> a -> b
$ \Row
ir ->
          [Column]
-> (Column -> ExceptT Text IO Text) -> ExceptT Text IO [Text]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
t a -> (a -> m b) -> m (t b)
forM [Column
0 .. Column -> Column
forall a. Enum a => a -> a
pred Column
nc] ((Column -> ExceptT Text IO Text) -> ExceptT Text IO [Text])
-> (Column -> ExceptT Text IO Text) -> ExceptT Text IO [Text]
forall a b. (a -> b) -> a -> b
$ \Column
ic -> do
            Maybe ByteString
cellValBS <- IO (Maybe ByteString) -> ExceptT Text IO (Maybe ByteString)
forall a. IO a -> ExceptT Text IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Maybe ByteString) -> ExceptT Text IO (Maybe ByteString))
-> IO (Maybe ByteString) -> ExceptT Text IO (Maybe ByteString)
forall a b. (a -> b) -> a -> b
$ Result -> Row -> Column -> IO (Maybe ByteString)
PQ.getvalue Result
r Row
ir Column
ic
            ExceptT Text IO Text
-> (ByteString -> ExceptT Text IO Text)
-> Maybe ByteString
-> ExceptT Text IO Text
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (Text -> ExceptT Text IO Text
forall a. a -> ExceptT Text IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Text
"NULL") ByteString -> ExceptT Text IO Text
decodeBS Maybe ByteString
cellValBS

        [[Text]] -> ExceptT Text IO [[Text]]
forall a. a -> ExceptT Text IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ([[Text]] -> ExceptT Text IO [[Text]])
-> [[Text]] -> ExceptT Text IO [[Text]]
forall a b. (a -> b) -> a -> b
$ [Text]
hdr [Text] -> [[Text]] -> [[Text]]
forall a. a -> [a] -> [a]
: [[Text]]
rows

      decodeBS :: ByteString -> ExceptT Text IO Text
decodeBS = (UnicodeException -> ExceptT Text IO Text)
-> (Text -> ExceptT Text IO Text)
-> Either UnicodeException Text
-> ExceptT Text IO Text
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (Text -> ExceptT Text IO Text
forall a. Text -> ExceptT Text IO a
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError (Text -> ExceptT Text IO Text)
-> (UnicodeException -> Text)
-> UnicodeException
-> ExceptT Text IO Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. UnicodeException -> Text
forall a. Show a => a -> Text
tshow) Text -> ExceptT Text IO Text
forall a. a -> ExceptT Text IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either UnicodeException Text -> ExceptT Text IO Text)
-> (ByteString -> Either UnicodeException Text)
-> ByteString
-> ExceptT Text IO Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Either UnicodeException Text
TE.decodeUtf8'