这是indexloc提供的服务,不要输入任何密码
Skip to content

Conversation

@abooij
Copy link
Contributor

@abooij abooij commented Apr 30, 2020

Description

This is a really dirty way to fix #3773 and fix #4577.

Changelog

  • CHANGELOG.md is updated with user-facing content relevant to this PR.

Affected components

  • Server

Solution and Design

The origin of #3773 is that the "validation" stage not just validates correctness of input data, but also gives back updated values. In particular, the "validation" combines the GraphQL query specification with the argument values, so that any default values are overwritten. As a consequence, the default value was not accessible during the query planning stage.

As a dirty hack to this, this code adds a field to AnnInpVal that specifies a default value (if any). This can then be updated at a later stage, so that the default value is available during the query planning stage.

The real solution here would be:

  1. to parse, rather than validate, the query,
  2. to separate the parsing (or validation) of the GraphQL query from the parsing (or validation) of argument values, so that we do not risk overwriting default values, and
  3. to model the query planning in a more type-safe way, so that we avoid the temptation of overwriting one value with another.

Steps to test and verify

See #3773 and #4577.

Limitations, known bugs & workarounds

This PR does NOT contain a regression test, as I do not know of an easy way to test this. So when #4111 is merged, we should pay attention that this bug does not re-appear.

Server checklist

Catalog upgrade

Does this PR change Hasura Catalog version?

  • No

Metadata

Does this PR add a new Metadata feature?

  • No

GraphQL

  • No new GraphQL schema is generated

Breaking changes

  • No Breaking changes

@abooij abooij added the c/server Related to server label Apr 30, 2020
@abooij abooij changed the title 3773 v2 cache default values server: cache default values (fix #3773) Apr 30, 2020
@hasura-bot
Copy link
Contributor

Review app for commit 1a8dcb3 deployed to Heroku: https://hge-ci-pull-4620.herokuapp.com
Docker image for server: hasura/graphql-engine:pull4620-1a8dcb36

@abooij abooij force-pushed the 3773-v2-cache-default-values branch from 1a8dcb3 to 080cbc1 Compare April 30, 2020 11:10
@hasura-bot
Copy link
Contributor

Review app for commit 080cbc1 deployed to Heroku: https://hge-ci-pull-4620.herokuapp.com
Docker image for server: hasura/graphql-engine:pull4620-080cbc18

@abooij abooij force-pushed the 3773-v2-cache-default-values branch from 080cbc1 to 73d81a7 Compare May 4, 2020 07:31
@abooij abooij marked this pull request as ready for review May 4, 2020 07:37
@abooij abooij requested a review from a team as a code owner May 4, 2020 07:37
@hasura-bot
Copy link
Contributor

Review app for commit ec713c6 deployed to Heroku: https://hge-ci-pull-4620.herokuapp.com
Docker image for server: hasura/graphql-engine:pull4620-ec713c6f

@abooij abooij force-pushed the 3773-v2-cache-default-values branch from 8e9071e to c7bcc68 Compare May 5, 2020 13:06
@hasura-bot
Copy link
Contributor

Review app for commit c7bcc68 deployed to Heroku: https://hge-ci-pull-4620.herokuapp.com
Docker image for server: hasura/graphql-engine:pull4620-c7bcc68a

data ReusableVariableInfo
= ReusableVariableInfo RQL.PGColumnType (ReusableDefaultValue PGScalarValue)
deriving (Show, Eq)
$(J.deriveToJSON J.defaultOptions{J.constructorTagModifier = drop 8} ''ReusableVariableInfo)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need the extra ReusableDefaultValue type? Isn't this equally readable without losing any type safety?

data ReusableVariableInfo
  = ReusableVariableInfo 
  { _rviType :: RQL.PGColumnType,
  , _rviDefault :: Maybe PGScalarValue
  } deriving (Show, Eq)

Copy link
Contributor Author

@abooij abooij May 6, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because Maybe is subject a sort of Boolean blindness. In the current situation, we don't clearly separate AnnInpVals originating from the query, and those originating from user-provided arguments. So we have to string the default values through our code, and this is error-prone, as this issue shows. The reason that we incorrectly cached these kinds of queries in the first place is that the provided value was simply <|>'d with the default value (in validateVariables), thus forgetting the default value. ResuableDefaultValue forces us to think explicitly about what to do with default vs user-provided values, at least in some places.

Put differently: suppose you get a Maybe PGScalarValue somewhere, what kind of value is this? Is this a user-provided argument? If so, has the default value been applied? If not, where would you get that from? ReusableDefaultValue goes one step towards clarifying the answers to these questions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I see what you mean. In that case wouldn't the standard pattern be a newtype wrapper on PGScalarValue like this?

newtype ReusableDefaultValue = ReusableDefaultValue PGScalarValue
data ReusableVariableInfo
  = ReusableVariableInfo 
  { _rviType :: RQL.PGColumnType,
  , _rviDefault :: Maybe ReusableDefaultValue
  } deriving (Show, Eq)

i.e, why parameterize ReusableDefaultValue and why encompass Maybe too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the newtype variant you now have to unpack two constructors to get to the value, as in:

f : Maybe ReusableDefaultValue -> a
f (Just (ReusableDefaultValue pgScalarValue)) = _
f Nothing = _

So since data types are really cheap in Haskell, we might as well re-define Maybe to get a more convenient API:

f : ReusableDefaultValue -> a
f (ReusableDefault pgScalarValue) = _
f ReusableNoDefault = _

Note that the two patterns directly give a lot of information about why we are doing pattern matching, in contrast with the newtype variant, where it's not quite clear, visually, what kind of value we are deconstructing. Indeed, in the second variant, the type signature is pretty much unnecessary to understand the code, whereas in the first variant one would need to be quite familiar with the codebase in order to be able to do without.

Anyway, this is not a critical point, so I'm happy to make this into a newtype if that allows this PR to get merged without further debate.

@hasura-bot
Copy link
Contributor

Review app for commit 4035613 deployed to Heroku: https://hge-ci-pull-4620.herokuapp.com
Docker image for server: hasura/graphql-engine:pull4620-40356138

@hasura-bot
Copy link
Contributor

Review app for commit 846be42 deployed to Heroku: https://hge-ci-pull-4620.herokuapp.com
Docker image for server: hasura/graphql-engine:pull4620-846be429

@hasura-bot
Copy link
Contributor

Review app for commit aedded7 deployed to Heroku: https://hge-ci-pull-4620.herokuapp.com
Docker image for server: hasura/graphql-engine:pull4620-aedded74

@abooij abooij force-pushed the 3773-v2-cache-default-values branch from aedded7 to 83e62bd Compare May 14, 2020 16:00
@hasura-bot
Copy link
Contributor

Review app for commit 83e62bd deployed to Heroku: https://hge-ci-pull-4620.herokuapp.com
Docker image for server: hasura/graphql-engine:pull4620-83e62bdb

@abooij
Copy link
Contributor Author

abooij commented May 14, 2020

This now has a regression test, conform the discussion in #4661.

data AnnInpVal
= AnnInpVal
{ _aivType :: !G.GType
, _aivVariable :: !(Maybe G.Variable)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's change G.Variable to VariableInfo where VariableInfo represents (G.Variable, Maybe AnnGValue).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, we seem to be adding this value here, after this change we don't need to do that anymore, validateVariables itself can build this VariableInfo and add it to the AnnInpVal.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your link doesn't work for me, unfortunately, so I am not sure what you are suggesting.

@0x777
Copy link
Member

0x777 commented May 15, 2020

Left a few comments otherwise the overall idea looks good. I see a lot of friction because of how AnnInpVal is modelled currently, ideally it should've been paramaterized with the source information - default, variables, query but given that we are getting rid of it soon, this seems like a reasonable compromise.

@hasura-bot
Copy link
Contributor

Review app for commit 21dba0e deployed to Heroku: https://hge-ci-pull-4620.herokuapp.com
Docker image for server: hasura/graphql-engine:pull4620-21dba0e0

@abooij abooij requested a review from tirumaraiselvan May 15, 2020 11:59
Copy link
Contributor

@tirumaraiselvan tirumaraiselvan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changelog

@0x777
Copy link
Member

0x777 commented Aug 31, 2020

Not relevant any more.

@0x777 0x777 closed this Aug 31, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c/server Related to server

Projects

None yet

4 participants