mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-19 13:31:43 +03:00
342391f39d
This upgrades the version of Ormolu required by the HGE repository to v0.5.0.1, and reformats all code accordingly. Ormolu v0.5 reformats code that uses infix operators. This is mostly useful, adding newlines and indentation to make it clear which operators are applied first, but in some cases, it's unpleasant. To make this easier on the eyes, I had to do the following: * Add a few fixity declarations (search for `infix`) * Add parentheses to make precedence clear, allowing Ormolu to keep everything on one line * Rename `relevantEq` to `(==~)` in #6651 and set it to `infix 4` * Add a few _.ormolu_ files (thanks to @hallettj for helping me get started), mostly for Autodocodec operators that don't have explicit fixity declarations In general, I think these changes are quite reasonable. They mostly affect indentation. PR-URL: https://github.com/hasura/graphql-engine-mono/pull/6675 GitOrigin-RevId: cd47d87f1d089fb0bc9dcbbe7798dbceedcd7d83
160 lines
7.7 KiB
Haskell
160 lines
7.7 KiB
Haskell
-- | BigQuery DDL ComputedField
|
|
--
|
|
-- Implementation to build 'ComputedFieldInfo' for a BigQuery table from metadata
|
|
module Hasura.Backends.BigQuery.DDL.ComputedField
|
|
( buildComputedFieldInfo,
|
|
)
|
|
where
|
|
|
|
import Control.Monad.Validate qualified as MV
|
|
import Data.HashMap.Strict qualified as HM
|
|
import Data.HashSet qualified as HS
|
|
import Data.Sequence qualified as Seq
|
|
import Data.Text.Extended
|
|
import Hasura.Backends.BigQuery.DDL.Source
|
|
import Hasura.Backends.BigQuery.Instances.Types ()
|
|
import Hasura.Backends.BigQuery.Meta
|
|
import Hasura.Backends.BigQuery.Types
|
|
import Hasura.Base.Error
|
|
import Hasura.Prelude
|
|
import Hasura.RQL.Types.Common
|
|
import Hasura.RQL.Types.ComputedField
|
|
import Hasura.RQL.Types.Function
|
|
import Hasura.SQL.Backend
|
|
import Hasura.Server.Utils
|
|
import Language.GraphQL.Draft.Syntax qualified as G
|
|
|
|
-- | Errors that occur when validating and building computed fields.
|
|
data ComputedFieldError
|
|
= CFENoArgumentType FunctionArgName
|
|
| CFENotTableValuedFunction
|
|
| CFENoInputArguments
|
|
| CFENoArgumentName
|
|
| CFEInvalidArgumentName FunctionArgName
|
|
| CFEInvalidColumnName TableName ColumnName
|
|
| CFEReturnTableNotTracked TableName
|
|
| CFENeedReturnTableName
|
|
| CFENotRelevantReturnTable TableName
|
|
| CFEReturnTableSchemaError ReturnTableSchemaError
|
|
|
|
-- | Errors that occur when validating returning table schema fields
|
|
data ReturnTableSchemaError
|
|
= RTSENoFieldName
|
|
| RTSENoType Text
|
|
| RTSENotValidGraphQLName Text
|
|
|
|
-- | Generate read-able error message
|
|
showError :: FunctionName -> ComputedFieldError -> Text
|
|
showError functionName = \case
|
|
CFENoArgumentType argName ->
|
|
"argument " <> argName <<> " has no data type defined in the function " <>> functionName
|
|
CFENotTableValuedFunction ->
|
|
prefixFunction <> " is not a TABLE_VALUED_FUNCTION"
|
|
CFENoInputArguments ->
|
|
prefixFunction <> " has no input arguments defined"
|
|
CFENoArgumentName ->
|
|
prefixFunction <> " has at least one argument without name"
|
|
CFEInvalidArgumentName argName ->
|
|
"the argument " <> argName <<> " is not one of function " <> functionName <<> " input arguments"
|
|
CFEInvalidColumnName tableName columnName ->
|
|
"the column " <> columnName <<> " does not exist in table " <>> tableName
|
|
CFEReturnTableNotTracked tableName ->
|
|
prefixFunction <> " returning set of table " <> tableName <<> " is not tracked"
|
|
CFENeedReturnTableName ->
|
|
prefixFunction <> " is not defined with 'RETURNS TABLE'. Expecting return table name."
|
|
CFENotRelevantReturnTable tableName ->
|
|
"return table " <> tableName <<> " is not required as the function " <> functionName <<> " returns arbitrary column fields"
|
|
CFEReturnTableSchemaError returnFieldsError -> showReturnFieldsError returnFieldsError
|
|
where
|
|
showReturnFieldsError :: ReturnTableSchemaError -> Text
|
|
showReturnFieldsError = \case
|
|
RTSENoFieldName -> "at least one field name is absent"
|
|
RTSENoType fieldName -> "fieldName " <> fieldName <<> " has not type information"
|
|
RTSENotValidGraphQLName fieldName -> "fieldName " <> fieldName <<> " is not a valid GraphQL name"
|
|
|
|
prefixFunction :: Text
|
|
prefixFunction = "function " <>> functionName
|
|
|
|
-- | Validate computed field metadata and build field information
|
|
buildComputedFieldInfo ::
|
|
forall m.
|
|
(MonadError QErr m) =>
|
|
HashSet TableName ->
|
|
TableName ->
|
|
HashSet ColumnName ->
|
|
ComputedFieldName ->
|
|
ComputedFieldDefinition ->
|
|
RestRoutine ->
|
|
Comment ->
|
|
m (ComputedFieldInfo 'BigQuery)
|
|
buildComputedFieldInfo trackedTables table tableColumns computedField ComputedFieldDefinition {..} restRoutine comment = do
|
|
either (throw400 NotSupported . showErrors) pure =<< MV.runValidateT mkComputedFieldInfo
|
|
where
|
|
mkComputedFieldInfo ::
|
|
forall n.
|
|
MV.MonadValidate [ComputedFieldError] n =>
|
|
n (ComputedFieldInfo 'BigQuery)
|
|
mkComputedFieldInfo = do
|
|
-- Currently, we only support functions returning set of rows in computed field.
|
|
-- Support for scalar computed fields is being tracked at https://github.com/hasura/graphql-engine/issues/8521.
|
|
unless (routineType restRoutine == TABLE_VALUED_FUNCTION) $ MV.dispute $ pure CFENotTableValuedFunction
|
|
restArguments <- onNothing (arguments restRoutine) $ MV.refute (pure CFENoInputArguments)
|
|
inputArguments <- Seq.fromList <$> for restArguments resolveInputArgument
|
|
for_ (HM.toList _bqcfdArgumentMapping) (validateArgumentMapping inputArguments)
|
|
let fieldFunction = ComputedFieldFunction _bqcfdFunction inputArguments _bqcfdArgumentMapping Nothing
|
|
fieldReturn <- resolveFunctionReturning (returnTableType restRoutine) _bqcfdReturnTable
|
|
pure $ ComputedFieldInfo @'BigQuery () computedField fieldFunction fieldReturn $ commentToMaybeText comment
|
|
where
|
|
resolveInputArgument :: RestArgument -> n FunctionArgument
|
|
resolveInputArgument RestArgument {..} = do
|
|
case _raName of
|
|
Nothing -> MV.refute $ pure CFENoArgumentName
|
|
Just name -> do
|
|
let argName = FunctionArgName name
|
|
restType <- onNothing _raDataType $ MV.refute $ pure $ CFENoArgumentType argName
|
|
pure $ FunctionArgument argName (restTypeToScalarType restType)
|
|
|
|
validateArgumentMapping :: Seq FunctionArgument -> (FunctionArgName, ColumnName) -> n ()
|
|
validateArgumentMapping args (functionArgName, columnName) = do
|
|
-- Check if argument is one of function input arguments
|
|
unless (functionArgName `elem` (_faName <$> args)) $ MV.dispute $ pure $ CFEInvalidArgumentName functionArgName
|
|
-- Check if column name exist in list of table columns
|
|
unless (columnName `HS.member` tableColumns) $ MV.dispute $ pure $ CFEInvalidColumnName table columnName
|
|
|
|
resolveReturnSqlFields :: [RestStandardSqlField] -> n [(ColumnName, G.Name, ScalarType)]
|
|
resolveReturnSqlFields fields =
|
|
forM fields $ \RestStandardSqlField {..} -> do
|
|
fieldName <- onNothing _rssfName $ MV.refute $ pure $ CFEReturnTableSchemaError RTSENoFieldName
|
|
fieldGraphQLName <- onNothing (G.mkName fieldName) $ MV.refute $ pure $ CFEReturnTableSchemaError $ RTSENotValidGraphQLName fieldName
|
|
type' <- onNothing _rssType $ MV.refute $ pure $ CFEReturnTableSchemaError $ RTSENoType fieldName
|
|
pure (ColumnName fieldName, fieldGraphQLName, restTypeToScalarType type')
|
|
|
|
resolveFunctionReturning ::
|
|
Maybe RestStandardSqlTableType ->
|
|
Maybe TableName ->
|
|
n ComputedFieldReturn
|
|
resolveFunctionReturning Nothing Nothing =
|
|
MV.refute $ pure CFENeedReturnTableName
|
|
resolveFunctionReturning Nothing (Just returnTable) =
|
|
-- Function does not return schema of a table. The return table type should be inferred
|
|
-- from function definition. The user provides returning table name through metadata.
|
|
-- Check if returning table is tracked.
|
|
if returnTable `HS.member` trackedTables
|
|
then pure (ReturnExistingTable returnTable)
|
|
else MV.refute $ pure $ CFEReturnTableNotTracked returnTable
|
|
resolveFunctionReturning (Just returnFields) Nothing =
|
|
-- Return table is not specified and the function returns a schema of a table,
|
|
-- specified as a list of column names and their type.
|
|
ReturnTableSchema <$> resolveReturnSqlFields (_rrttColumns returnFields)
|
|
resolveFunctionReturning (Just _) (Just returnTable) =
|
|
MV.refute $ pure $ CFENotRelevantReturnTable returnTable
|
|
|
|
showErrors :: [ComputedFieldError] -> Text
|
|
showErrors allErrors =
|
|
"the computed field "
|
|
<> computedField <<> " cannot be added to table "
|
|
<> table <<> " "
|
|
<> reasonMessage
|
|
where
|
|
reasonMessage = makeReasonMessage allErrors (showError _bqcfdFunction)
|