Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Field naming starting with reserved keywords breaks updates #3597

Closed
rstpv opened this issue Dec 26, 2019 · 13 comments
Closed

Field naming starting with reserved keywords breaks updates #3597

rstpv opened this issue Dec 26, 2019 · 13 comments
Assignees
Labels
c/server Related to server k/bug Something isn't working

Comments

@rstpv
Copy link

rstpv commented Dec 26, 2019

When in a table there is a column that starts with a reserved keyword, for example nullSomeColumnName or trueSomeColumnName, if you try to create an update with an on_conflict and columns, the update will fail like:

extensions": {
        "path": "$.selectionSet.insert_some_table.args.on_conflict.update_columns[indexOfColumnThatStartsWithNull]",
        "code": "validation-failed"
      },
      "message": "null value found for non-nullable type: some_table_update_column!"

If the field starts with true or false the error is almost the same but the message is related to value must be an enum.

It seems like the engine is trying to parse the column names somehow.

@marionschleifer marionschleifer added the c/server Related to server label Dec 28, 2019
@lexi-lambda lexi-lambda added the k/bug Something isn't working label Jan 3, 2020
@sordina
Copy link
Contributor

sordina commented Feb 13, 2020

Looks very strange! I wonder why the column names might be being parsed? No idea where to start, but my first guess is some kind of derived instance is doing something funky. I'll see if I can figure out how the translation occurs then check out each component of the process.

@sordina
Copy link
Contributor

sordina commented Feb 17, 2020

Hi @rstpv could you provide some more detail for how to reproduce the error? I'm trying from the console with the following steps:

  • Create table called "testTable" with id int PK, nullTestColumn int uniq
  • Run insert into "testTable" ("nullTestColumn") values (1) on conflict do nothing twice

This seems to succeed. Am I missing something about how this should be expected to be triggered? Otherwise, if this does trigger the bug for you, what version of Hasura did you encounter the bug on?

Apologies if I've missed something obvious! I'm just getting to grips with the codebase :)

@rstpv
Copy link
Author

rstpv commented Feb 17, 2020

Hi @sordina, you can reproduce this as:

  1. Create a table with two columns such as:
    image
  2. Try to run a query with on_conflict and update_columns such as:
mutation {
  insert_testTable(objects: [{nullColumntwo: "2"}] on_conflict:{constraint:testTable_pkey, update_columns:[nullColumntwo]}){
    affected_rows
  }
}
  1. It will throw an error such as:
{
  "errors": [
    {
      "extensions": {
        "path": "$.selectionSet.insert_testTable.args.on_conflict.update_columns[0]",
        "code": "validation-failed"
      },
      "message": "null value found for non-nullable type: testTable_update_column!"
    }
  ]
}
  1. Rename the column to anything, for example:
    image

5.Rerun the previous query with the renamed column:

mutation {
  insert_testTable(objects: [{changedColumntwo: "2"}] on_conflict:{constraint:testTable_pkey, update_columns:[changedColumntwo]}){
    affected_rows
  }
}
  1. It succeeds:
{
  "data": {
    "insert_testTable": {
      "affected_rows": 1
    }
  }
}

The version we are using is the v1.0.0 stable.

@sordina
Copy link
Contributor

sordina commented Feb 18, 2020

Thanks @rstpv that makes total sense. I wasn't running a GraphQL mutation, so of course it wasn't triggering the issue. I'll try to reproduce with your steps.

@sordina
Copy link
Contributor

sordina commented Feb 19, 2020

I've replicated the issue with your instructions. I'll dig in deeper now.

@sordina
Copy link
Contributor

sordina commented Feb 20, 2020

Looks like the issue is in the parser. Initial test-case:

propNullWorks :: Property
propNullWorks = property $ either (fail . Protolude.show) (ast ===) astRoundTrip
  where
    -- Protolude.putStrLn (groom parsed) >> print text >> either (print) (print . PP.TB.renderExecutableDoc) parsed
    -- "mutation  { insert_testTable(on_conflict: {update_columns:[nullColumntwo]}) { affected_rows  } }"
    astRoundTrip = parseExecutableDoc printed
    printed      = PP.TB.renderExecutableDoc ast
    ast          = (ExecutableDocument{getExecutableDefinitions =
                        [ExecutableDefinitionOperation
                           (OperationDefinitionTyped
                              (TypedOperationDefinition{_todType = OperationTypeMutation,
                                                        _todName = Nothing,
                                                        _todVariableDefinitions = [],
                                                        _todDirectives = [],
                                                        _todSelectionSet =
                                                          [SelectionField
                                                             (Field{_fAlias = Nothing,
                                                                    _fName = Name{unName = "insert_testTable"},
                                                                    _fArguments =
                                                                      [Argument{_aName = Name{unName = "on_conflict"},
                                                                                _aValue =
                                                                                  VObject
                                                                                    (ObjectValueG{unObjectValue
                                                                                                    =
                                                                                                    [ObjectFieldG{_ofName = Name{unName = "update_columns"},
                                                                                                                  _ofValue
                                                                                                                    =
                                                                                                                    VList
                                                                                                                      (ListValueG{unListValue
                                                                                                                                    =
                                                                                                                                    [ VEnum
                                                                                                                                       (EnumValue{unEnumValue
                                                                                                                                                    =
                                                                                                                                                    Name{unName = "nullColumntwo"}})]})}]})}],
                                                                    _fDirectives = [],
                                                                    _fSelectionSet =
                                                                      [SelectionField
                                                                         (Field{_fAlias = Nothing,
                                                                                _fName =
                                                                                  Name{unName =
                                                                                         "affected_rows"},
                                                                                _fArguments = [],
                                                                                _fDirectives = [],
                                                                                _fSelectionSet =
                                                                                  []})]})]}))]})

@sordina
Copy link
Contributor

sordina commented Feb 20, 2020

Will attempt to reduce this and then fix the parser.

@sordina
Copy link
Contributor

sordina commented Feb 20, 2020

Issue created in parser repo: hasura/graphql-parser-hs#20

@sordina
Copy link
Contributor

sordina commented Feb 24, 2020

@rstpv I've resolved the issue in the parser and am now testing integration with graphql-engine. Should have a candidate fix ready shortly.

@sordina
Copy link
Contributor

sordina commented Feb 24, 2020

image

Works manually. Adding automated tests now.

@sordina
Copy link
Contributor

sordina commented Feb 24, 2020

Pytest added:

pytest --hge-urls http://localhost:8080 --pg-urls=postgres://hasura-test\@localhost:5432/hasura-test2  --accept -vv test_graphql_mutations.py::TestGraphqlInsertNullPrefixedColumnOnConflict
test_graphql_mutations.py::TestGraphqlInsertNullPrefixedColumnOnConflict::test_address_not_null_constraint_err[websocket]
[gw0] [100%] PASSED test_graphql_mutations.py::TestGraphqlInsertNullPrefixedColumnOnConflict::test_address_not_null_constraint_err[websocket]

@sordina
Copy link
Contributor

sordina commented Feb 24, 2020

Created WIP fix: #3927

Still need to finish approval of parser changes

@sordina
Copy link
Contributor

sordina commented Feb 25, 2020

Parser changes merged. Referencing commit in engine:

source-repository-package
  type: git
  location: https://github.com/hasura/graphql-parser-hs.git
  tag: 1380495a7b3269b70a7ab3081d745a5f54171a9c

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c/server Related to server k/bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants