chore(server): allow non-nullable NQ -> NQ object relationships

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/9353
GitOrigin-RevId: 9da95675d224f3045509592c2515b7afa2500d00
This commit is contained in:
Daniel Harvey 2023-05-31 11:32:57 +01:00 committed by hasura-bot
parent b19eff22e9
commit 7d011644ac
12 changed files with 75 additions and 44 deletions

View File

@ -204,6 +204,7 @@ fromRemoteRelationFieldsG existingJoins joinColumns (IR.FieldName name, field) =
IR.AnnRelationSelectG
(IR.RelName $ mkNonEmptyTextUnsafe name)
joinColumns
IR.Nullable
annotatedRelationship
-- | Top/root-level 'Select'. All descendent/sub-translations are collected to produce a root TSQL.Select.

View File

@ -31,6 +31,7 @@ import Hasura.Backends.Postgres.Translate.Select.Internal.Helpers
import Hasura.Backends.Postgres.Translate.Types
import Hasura.Prelude
import Hasura.RQL.IR.Select (ConnectionSlice (SliceFirst, SliceLast))
import Hasura.RQL.Types.Relationships.Local (Nullable (..))
generateSQLSelect ::
-- | Pre join condition for lateral joins
@ -86,9 +87,9 @@ generateSQLSelect joinCondition selectSource selectNode =
S.WhereFrag $ S.simplifyBoolExp $ S.BEBin S.AndOp joinCond whereCond
-- function to create a joined from item from two from items
leftOuterJoin current new =
leftOuterJoin current (new, joinType) =
S.FIJoin
$ S.JoinExpr current S.LeftOuter new
$ S.JoinExpr current joinType new
$ S.JoinOn
$ S.BELit True
@ -102,33 +103,40 @@ generateSQLSelect joinCondition selectSource selectNode =
<> map computedFieldToFromItem (HashMap.toList computedFields)
objectRelationToFromItem ::
(ObjectRelationSource, SelectNode) -> S.FromItem
(ObjectRelationSource, SelectNode) -> (S.FromItem, S.JoinType)
objectRelationToFromItem (objectRelationSource, node) =
let ObjectRelationSource _ colMapping objectSelectSource = objectRelationSource
let ObjectRelationSource
{ _orsRelationMapping = colMapping,
_orsSelectSource = objectSelectSource,
_orsNullable = nullable
} = objectRelationSource
alias = S.toTableAlias $ _ossPrefix objectSelectSource
source = objectSelectSourceToSelectSource objectSelectSource
select = generateSQLSelect (mkJoinCond baseSelectIdentifier colMapping) source node
in S.mkLateralFromItem select alias
joinType = case nullable of
Nullable -> S.LeftOuter
NotNullable -> S.Inner
in (S.mkLateralFromItem select alias, joinType)
arrayRelationToFromItem ::
(ArrayRelationSource, MultiRowSelectNode) -> S.FromItem
(ArrayRelationSource, MultiRowSelectNode) -> (S.FromItem, S.JoinType)
arrayRelationToFromItem (arrayRelationSource, arraySelectNode) =
let ArrayRelationSource _ colMapping source = arrayRelationSource
alias = S.toTableAlias $ _ssPrefix source
select =
generateSQLSelectFromArrayNode source arraySelectNode
$ mkJoinCond baseSelectIdentifier colMapping
in S.mkLateralFromItem select alias
in (S.mkLateralFromItem select alias, S.LeftOuter)
arrayConnectionToFromItem ::
(ArrayConnectionSource, MultiRowSelectNode) -> S.FromItem
(ArrayConnectionSource, MultiRowSelectNode) -> (S.FromItem, S.JoinType)
arrayConnectionToFromItem (arrayConnectionSource, arraySelectNode) =
let selectWith = connectionToSelectWith baseSelectAlias arrayConnectionSource arraySelectNode
alias = S.toTableAlias $ _ssPrefix $ _acsSource arrayConnectionSource
in S.FISelectWith (S.Lateral True) selectWith alias
in (S.FISelectWith (S.Lateral True) selectWith alias, S.LeftOuter)
computedFieldToFromItem ::
(ComputedFieldTableSetSource, MultiRowSelectNode) -> S.FromItem
(ComputedFieldTableSetSource, MultiRowSelectNode) -> (S.FromItem, S.JoinType)
computedFieldToFromItem (computedFieldTableSource, node) =
let ComputedFieldTableSetSource _ source = computedFieldTableSource
internalSelect = generateSQLSelect (S.BELit True) source $ _mrsnSelectNode node
@ -138,7 +146,7 @@ generateSQLSelect joinCondition selectSource selectNode =
{ S.selExtr = _mrsnTopExtractors node,
S.selFrom = Just $ S.FromExp [S.mkSelFromItem internalSelect alias]
}
in S.mkLateralFromItem select alias
in (S.mkLateralFromItem select alias, S.LeftOuter)
-- | Create a select query
generateSQLSelectFromArrayNode ::

View File

@ -130,7 +130,7 @@ processOrderByItems sourcePrefix' fieldAlias' similarArrayFields distOnCols = \c
(tableIdentifierToIdentifier relSourcePrefix)
(S.FISimple relTable Nothing)
(toSQLBoolExp (S.QualTable relTable) relFilter)
relSource = ObjectRelationSource relName colMapping selectSource
relSource = ObjectRelationSource relName colMapping selectSource Nullable
pure
( relSource,
HashMap.singleton relOrderByAlias relOrdByExp,

View File

@ -291,7 +291,7 @@ processAnnFields sourcePrefix fieldAlias similarArrFields annFields tCase = do
AFNodeId _ sn tn pKeys -> pure $ mkNodeId sn tn pKeys
AFColumn c -> toSQLCol c
AFObjectRelation objSel -> withWriteObjectRelation $ do
let AnnRelationSelectG relName relMapping annObjSel = objSel
let AnnRelationSelectG relName relMapping nullable annObjSel = objSel
AnnObjectSelectG objAnnFields target targetFilter = annObjSel
objRelSourcePrefix = mkObjectRelationTableAlias sourcePrefix relName
sourcePrefixes = mkSourcePrefixes objRelSourcePrefix
@ -314,7 +314,7 @@ processAnnFields sourcePrefix fieldAlias similarArrFields annFields tCase = do
(_pfThis sourcePrefixes)
(S.FIIdentifier nativeQueryIdentifier)
(toSQLBoolExp (S.QualifiedIdentifier nativeQueryIdentifier Nothing) targetFilter)
objRelSource = ObjectRelationSource relName relMapping selectSource
objRelSource = ObjectRelationSource relName relMapping selectSource nullable
pure
( objRelSource,
HashMap.fromList [annFieldsExtr],
@ -326,7 +326,7 @@ processAnnFields sourcePrefix fieldAlias similarArrFields annFields tCase = do
(_pfThis sourcePrefixes)
(S.FISimple tableFrom Nothing)
(toSQLBoolExp (S.QualTable tableFrom) targetFilter)
objRelSource = ObjectRelationSource relName relMapping selectSource
objRelSource = ObjectRelationSource relName relMapping selectSource Nullable
pure
( objRelSource,
HashMap.fromList [annFieldsExtr],
@ -496,7 +496,7 @@ processArrayRelation ::
processArrayRelation sourcePrefixes fieldAlias relAlias arrSel _tCase =
case arrSel of
ASSimple annArrRel -> withWriteArrayRelation $ do
let AnnRelationSelectG _ colMapping sel = annArrRel
let AnnRelationSelectG _ colMapping _ sel = annArrRel
permLimitSubQuery =
maybe PLSQNotRequired PLSQRequired $ _tpLimit $ _asnPerm sel
(source, nodeExtractors) <-
@ -514,7 +514,7 @@ processArrayRelation sourcePrefixes fieldAlias relAlias arrSel _tCase =
()
)
ASAggregate aggSel -> withWriteArrayRelation $ do
let AnnRelationSelectG _ colMapping sel = aggSel
let AnnRelationSelectG _ colMapping _ sel = aggSel
(source, nodeExtractors, topExtr) <-
processAnnAggregateSelect sourcePrefixes fieldAlias sel
pure
@ -524,7 +524,7 @@ processArrayRelation sourcePrefixes fieldAlias relAlias arrSel _tCase =
()
)
ASConnection connSel -> withWriteArrayConnection $ do
let AnnRelationSelectG _ colMapping sel = connSel
let AnnRelationSelectG _ colMapping _ sel = connSel
(source, topExtractor, nodeExtractors) <-
processConnectionSelect sourcePrefixes fieldAlias relAlias colMapping sel
pure

View File

@ -12,7 +12,7 @@ module Hasura.Backends.Postgres.Translate.Types
DistinctAndOrderByExpr (ASorting),
JoinTree (..),
MultiRowSelectNode (..),
ObjectRelationSource (ObjectRelationSource),
ObjectRelationSource (..),
ObjectSelectSource (ObjectSelectSource, _ossPrefix),
PermissionLimitSubQuery (..),
SelectNode (SelectNode),
@ -38,6 +38,7 @@ import Hasura.NativeQuery.Metadata (InterpolatedQuery)
import Hasura.Prelude
import Hasura.RQL.IR.Select
import Hasura.RQL.Types.Common
import Hasura.RQL.Types.Relationships.Local (Nullable)
data SourcePrefixes = SourcePrefixes
{ -- | Current source prefix
@ -169,7 +170,8 @@ objectSelectSourceToSelectSource ObjectSelectSource {..} =
data ObjectRelationSource = ObjectRelationSource
{ _orsRelationshipName :: RelName,
_orsRelationMapping :: HashMap.HashMap Postgres.PGCol Postgres.PGCol,
_orsSelectSource :: ObjectSelectSource
_orsSelectSource :: ObjectSelectSource,
_orsNullable :: Nullable
}
deriving (Generic, Show)

View File

@ -33,6 +33,7 @@ import Hasura.RQL.Types.Backend
import Hasura.RQL.Types.BackendType
import Hasura.RQL.Types.Column (ColumnType, fromCol)
import Hasura.RQL.Types.Common
import Hasura.RQL.Types.Relationships.Local (Nullable (..))
import Hasura.RQL.Types.ResultCustomization
import Hasura.RQL.Types.Schema.Options qualified as Options
import Hasura.RemoteSchema.SchemaCache
@ -197,11 +198,11 @@ convertRemoteSourceRelationship
relationshipField = case relationship of
SourceRelationshipObject s ->
AFObjectRelation $ AnnRelationSelectG relName columnMapping s
AFObjectRelation $ AnnRelationSelectG relName columnMapping Nullable s
SourceRelationshipArray s ->
AFArrayRelation $ ASSimple $ AnnRelationSelectG relName columnMapping s
AFArrayRelation $ ASSimple $ AnnRelationSelectG relName columnMapping Nullable s
SourceRelationshipArrayAggregate s ->
AFArrayRelation $ ASAggregate $ AnnRelationSelectG relName columnMapping s
AFArrayRelation $ ASAggregate $ AnnRelationSelectG relName columnMapping Nullable s
argumentIdField =
( fromCol @b argumentIdColumn,

View File

@ -1448,7 +1448,7 @@ relationshipField table ri = runMaybeT do
$ P.subselection_ relFieldName desc selectionSetParser
<&> \fields ->
IR.AFObjectRelation
$ IR.AnnRelationSelectG (riName ri) (riMapping ri)
$ IR.AnnRelationSelectG (riName ri) (riMapping ri) Nullable
$ IR.AnnObjectSelectG fields (IR.FromTable otherTableName)
$ deduplicatePermissions
$ IR._tpFilter
@ -1460,7 +1460,7 @@ relationshipField table ri = runMaybeT do
otherTableParser <&> \selectExp ->
IR.AFArrayRelation
$ IR.ASSimple
$ IR.AnnRelationSelectG (riName ri) (riMapping ri)
$ IR.AnnRelationSelectG (riName ri) (riMapping ri) Nullable
$ deduplicatePermissions' selectExp
relAggFieldName = applyFieldNameCaseCust tCase $ relFieldName <> Name.__aggregate
relAggDesc = Just $ G.Description "An aggregate relationship"
@ -1479,8 +1479,8 @@ relationshipField table ri = runMaybeT do
pure
$ catMaybes
[ Just arrayRelField,
fmap (IR.AFArrayRelation . IR.ASAggregate . IR.AnnRelationSelectG (riName ri) (riMapping ri)) <$> remoteAggField,
fmap (IR.AFArrayRelation . IR.ASConnection . IR.AnnRelationSelectG (riName ri) (riMapping ri)) <$> remoteConnectionField
fmap (IR.AFArrayRelation . IR.ASAggregate . IR.AnnRelationSelectG (riName ri) (riMapping ri) Nullable) <$> remoteAggField,
fmap (IR.AFArrayRelation . IR.ASConnection . IR.AnnRelationSelectG (riName ri) (riMapping ri) Nullable) <$> remoteConnectionField
]
tablePermissionsInfo :: (Backend b) => SelPermInfo b -> TablePerms b

View File

@ -200,11 +200,11 @@ parseLogicalModelField relationshipInfo column logimoField = do
( LogicalModelField
{ lmfType =
LogicalModelTypeReference
(LogicalModelTypeReferenceC {lmtrReference})
(LogicalModelTypeReferenceC {lmtrNullable, lmtrReference})
}
) -> do
-- we currently ignore nullability and assume the field is nullable
relName <- hoistMaybe $ columnToRelName @b column
-- lookup the reference in the data source
relationship <-
InsOrdHashMap.lookup relName relationshipInfo
@ -215,14 +215,14 @@ parseLogicalModelField relationshipInfo column logimoField = do
<> commaSeparated (map relNameToTxt (InsOrdHashMap.keys relationshipInfo))
<> "]."
)
logicalModelObjectRelationshipField @b @r @m @n lmtrReference relationship
logicalModelObjectRelationshipField @b @r @m @n lmtrReference (nullableFromBool lmtrNullable) relationship
( LogicalModelField
{ lmfType =
LogicalModelTypeArray
( LogicalModelTypeArrayC
{ lmtaArray =
LogicalModelTypeReference (LogicalModelTypeReferenceC {lmtrReference}),
lmtaNullable
LogicalModelTypeReference (LogicalModelTypeReferenceC {lmtrReference, lmtrNullable = innerNullability}),
lmtaNullable = arrayNullability
}
)
}
@ -232,9 +232,12 @@ parseLogicalModelField relationshipInfo column logimoField = do
relName <- hoistMaybe $ columnToRelName @b column
-- lookup the reference in the data source
relationship <- hoistMaybe $ InsOrdHashMap.lookup relName relationshipInfo
let nullability = if lmtaNullable then Nullable else NotNullable
logicalModelArrayRelationshipField @b @r @m @n lmtrReference nullability relationship
logicalModelArrayRelationshipField @b @r @m @n
lmtrReference
(nullableFromBool arrayNullability)
(nullableFromBool innerNullability)
relationship
( LogicalModelField
{ lmfType =
LogicalModelTypeArray
@ -250,6 +253,10 @@ parseLogicalModelField relationshipInfo column logimoField = do
) ->
throw500 "Nested arrays are not currently implemented"
nullableFromBool :: Bool -> Nullable
nullableFromBool True = Nullable
nullableFromBool False = NotNullable
defaultLogicalModelSelectionSet ::
forall b r m n.
( MonadBuildSchema b r m n,
@ -421,9 +428,10 @@ logicalModelObjectRelationshipField ::
MonadBuildSchema b r m n
) =>
LogicalModelName ->
Nullable ->
RelInfo b ->
MaybeT (SchemaT r m) (FieldParser n (AnnotatedField b))
logicalModelObjectRelationshipField logicalModelName ri | riType ri == ObjRel =
logicalModelObjectRelationshipField logicalModelName nullability ri | riType ri == ObjRel =
case riTarget ri of
RelTargetNativeQuery nativeQueryName -> do
nativeQueryInfo <- lift $ askNativeQueryInfo nativeQueryName
@ -443,16 +451,22 @@ logicalModelObjectRelationshipField logicalModelName ri | riType ri == ObjRel =
relFieldName <- lift $ textToName $ relNameToTxt $ riName ri
let objectRelDesc = Just $ G.Description "An object relationship"
nativeQueryParser <- MaybeT $ selectNativeQueryObject nativeQueryInfo relFieldName objectRelDesc
-- this only affects the generated GraphQL type
let nullabilityModifier =
case nullability of
Nullable -> id
NotNullable -> IP.nonNullableField
pure
$ nullabilityModifier
$ nativeQueryParser
<&> \selectExp ->
IR.AFObjectRelation (IR.AnnRelationSelectG (riName ri) (riMapping ri) selectExp)
IR.AFObjectRelation (IR.AnnRelationSelectG (riName ri) (riMapping ri) nullability selectExp)
RelTargetTable _otherTableName -> do
throw500 "Object relationships from logical models to tables are not implemented"
logicalModelObjectRelationshipField _ _ =
logicalModelObjectRelationshipField _ _ _ =
hoistMaybe Nothing -- the target logical model expected an object relationship, but this was an array
-- | Field parsers for a logical model relationship
@ -463,9 +477,10 @@ logicalModelArrayRelationshipField ::
) =>
LogicalModelName ->
Nullable ->
Nullable ->
RelInfo b ->
MaybeT (SchemaT r m) (FieldParser n (AnnotatedField b))
logicalModelArrayRelationshipField logicalModelName nullability ri | riType ri == ArrRel =
logicalModelArrayRelationshipField logicalModelName arrayNullability innerNullability ri | riType ri == ArrRel =
case riTarget ri of
RelTargetNativeQuery nativeQueryName -> do
nativeQueryInfo <- lift $ askNativeQueryInfo nativeQueryName
@ -485,15 +500,15 @@ logicalModelArrayRelationshipField logicalModelName nullability ri | riType ri =
let objectRelDesc = Just $ G.Description "An array relationship"
nativeQueryParser <- MaybeT $ selectNativeQuery nativeQueryInfo relFieldName nullability objectRelDesc
nativeQueryParser <- MaybeT $ selectNativeQuery nativeQueryInfo relFieldName arrayNullability objectRelDesc
pure
$ nativeQueryParser
<&> \selectExp ->
IR.AFArrayRelation
$ IR.ASSimple
$ IR.AnnRelationSelectG (riName ri) (riMapping ri) selectExp
$ IR.AnnRelationSelectG (riName ri) (riMapping ri) innerNullability selectExp
RelTargetTable _otherTableName -> do
throw500 "Array relationships from logical models to tables are not implemented"
logicalModelArrayRelationshipField _ _ _ =
logicalModelArrayRelationshipField _ _ _ _ =
hoistMaybe Nothing -- the target logical model expected an array relationship, but this was an object

View File

@ -292,7 +292,7 @@ convExtRel sqlGen fieldInfoMap relName mAlias selQ sessVarBldr prepValBldr = do
when misused $ throw400 UnexpectedPayload objRelMisuseMsg
pure
$ Left
$ AnnRelationSelectG (fromMaybe relName mAlias) colMapping
$ AnnRelationSelectG (fromMaybe relName mAlias) colMapping Nullable
$ AnnObjectSelectG (_asnFields annSel) (FromTable relTableName)
$ _tpFilter
$ _asnPerm annSel
@ -303,6 +303,7 @@ convExtRel sqlGen fieldInfoMap relName mAlias selQ sessVarBldr prepValBldr = do
$ AnnRelationSelectG
(fromMaybe relName mAlias)
colMapping
Nullable
annSel
where
pgWhenRelErr = "only relationships can be expanded"

View File

@ -18,6 +18,7 @@ module Hasura.RQL.IR.Select.Lenses
aarAnnSelect,
aarColumnMapping,
aarRelationshipName,
aarNullable,
anosSupportsNestedObjects,
anosColumn,
anosFields,

View File

@ -12,12 +12,14 @@ import Hasura.Prelude
import Hasura.RQL.Types.Backend
import Hasura.RQL.Types.BackendType
import Hasura.RQL.Types.Common
import Hasura.RQL.Types.Relationships.Local (Nullable)
-- Local relationship
data AnnRelationSelectG (b :: BackendType) a = AnnRelationSelectG
{ _aarRelationshipName :: RelName, -- Relationship name
_aarColumnMapping :: HashMap (Column b) (Column b), -- Column of left table to join with
_aarNullable :: Nullable, -- is the target object allowed to be missing?
_aarAnnSelect :: a -- Current table. Almost ~ to SQL Select
}
deriving stock (Functor, Foldable, Traversable)