From e48f3cbef1c7165ec880167669053aec8a5f79e6 Mon Sep 17 00:00:00 2001 From: hasura-bot Date: Wed, 1 Jun 2022 09:40:48 -0700 Subject: [PATCH] Add _cast support for other postgres datatypes fix #6050 and fix #5426 GITHUB_PR_NUMBER: 8524 GITHUB_PR_URL: https://github.com/hasura/graphql-engine/pull/8524 PR-URL: https://github.com/hasura/graphql-engine-mono/pull/4551 Co-authored-by: Abdullah Saleem <5978905+abdullah2993@users.noreply.github.com> Co-authored-by: Abby Sassel <3883855+sassela@users.noreply.github.com> GitOrigin-RevId: f85742318167d1e51f463c45fcd00f26269c2555 --- CHANGELOG.md | 1 + .../Hasura/Backends/Postgres/DDL/BoolExp.hs | 15 ++ .../Backends/Postgres/Instances/Schema.hs | 15 ++ ..._cast_test_where_cast_string_postgres.yaml | 250 ++++++++++++++++++ .../graphql_query/boolexp/basic/setup.yaml | 62 +++++ .../graphql_query/boolexp/basic/teardown.yaml | 1 + server/tests-py/test_graphql_queries.py | 3 + 7 files changed, 347 insertions(+) create mode 100644 server/tests-py/queries/graphql_query/boolexp/basic/select_cast_test_where_cast_string_postgres.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index f144dcf942e..a63e51de9c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -147,6 +147,7 @@ Response 2: - server: fixes url/query date variable bug in REST endpoints - server: makes url/query variables in REST endpoints assume string if other types not applicable - server: fix inserting empty objects with default values to postgres, citus, and sql server (fix #8475) +- server: allow casting most postgres scalar types to strings in comparison expressions (fix #8524) - console: add remote database relationships for views - console: bug fixes for RS-to-RS relationships - console: allow users to remove prefix / suffix / root field namespace from a remote schema diff --git a/server/src-lib/Hasura/Backends/Postgres/DDL/BoolExp.hs b/server/src-lib/Hasura/Backends/Postgres/DDL/BoolExp.hs index c6ad69c9785..4472acdab8f 100644 --- a/server/src-lib/Hasura/Backends/Postgres/DDL/BoolExp.hs +++ b/server/src-lib/Hasura/Backends/Postgres/DDL/BoolExp.hs @@ -214,6 +214,21 @@ parseBoolExpOperations rhsParser rootTable fim columnRef value = do (ColumnScalar PGGeometry, PGGeography) -> return () (ColumnScalar PGGeography, PGGeometry) -> return () (ColumnScalar PGJSONB, PGText) -> return () + (ColumnScalar PGSmallInt, PGText) -> return () + (ColumnScalar PGInteger, PGText) -> return () + (ColumnScalar PGBigInt, PGText) -> return () + (ColumnScalar PGFloat, PGText) -> return () + (ColumnScalar PGDouble, PGText) -> return () + (ColumnScalar PGNumeric, PGText) -> return () + (ColumnScalar PGMoney, PGText) -> return () + (ColumnScalar PGBoolean, PGText) -> return () + (ColumnScalar PGChar, PGText) -> return () + (ColumnScalar PGDate, PGText) -> return () + (ColumnScalar PGTimeStamp, PGText) -> return () + (ColumnScalar PGTimeStampTZ, PGText) -> return () + (ColumnScalar PGTimeTZ, PGText) -> return () + (ColumnScalar PGJSON, PGText) -> return () + (ColumnScalar PGUUID, PGText) -> return () _ -> throw400 UnexpectedPayload $ "cannot cast column of type " <> colTy <<> " to type " <>> targetType diff --git a/server/src-lib/Hasura/Backends/Postgres/Instances/Schema.hs b/server/src-lib/Hasura/Backends/Postgres/Instances/Schema.hs index 5520c1781fc..3dd8be92ce1 100644 --- a/server/src-lib/Hasura/Backends/Postgres/Instances/Schema.hs +++ b/server/src-lib/Hasura/Backends/Postgres/Instances/Schema.hs @@ -669,6 +669,21 @@ comparisonExps = memoize 'comparisonExps \columnType -> do ColumnScalar PGGeography -> Just (PGGeography, PGGeometry) ColumnScalar PGGeometry -> Just (PGGeometry, PGGeography) ColumnScalar PGJSONB -> Just (PGJSONB, PGText) + ColumnScalar PGSmallInt -> Just (PGSmallInt, PGText) + ColumnScalar PGInteger -> Just (PGInteger, PGText) + ColumnScalar PGBigInt -> Just (PGBigInt, PGText) + ColumnScalar PGFloat -> Just (PGFloat, PGText) + ColumnScalar PGDouble -> Just (PGDouble, PGText) + ColumnScalar PGNumeric -> Just (PGNumeric, PGText) + ColumnScalar PGMoney -> Just (PGMoney, PGText) + ColumnScalar PGBoolean -> Just (PGBoolean, PGText) + ColumnScalar PGChar -> Just (PGChar, PGText) + ColumnScalar PGDate -> Just (PGDate, PGText) + ColumnScalar PGTimeStamp -> Just (PGTimeStamp, PGText) + ColumnScalar PGTimeStampTZ -> Just (PGTimeStampTZ, PGText) + ColumnScalar PGTimeTZ -> Just (PGTimeTZ, PGText) + ColumnScalar PGJSON -> Just (PGJSON, PGText) + ColumnScalar PGUUID -> Just (PGUUID, PGText) _ -> Nothing forM maybeScalars $ \(sourceScalar, targetScalar) -> do diff --git a/server/tests-py/queries/graphql_query/boolexp/basic/select_cast_test_where_cast_string_postgres.yaml b/server/tests-py/queries/graphql_query/boolexp/basic/select_cast_test_where_cast_string_postgres.yaml new file mode 100644 index 00000000000..2ff21802d59 --- /dev/null +++ b/server/tests-py/queries/graphql_query/boolexp/basic/select_cast_test_where_cast_string_postgres.yaml @@ -0,0 +1,250 @@ +- description: Fetch data from cast_test table for smallint column after casting to string + url: /v1/graphql + status: 200 + response: + data: + cast_test: + - id: 1 + smallint_col: 123 + query: + query: |- + query { + cast_test (where: {smallint_col: {_cast: {String: {_ilike: "123"}}}}){ + id + smallint_col + } + } + +- description: Fetch data from cast_test table for integer column after casting to string + url: /v1/graphql + status: 200 + response: + data: + cast_test: + - id: 2 + integer_col: 123 + query: + query: |- + query { + cast_test (where: {integer_col: {_cast: {String: {_ilike: "123"}}}}){ + id + integer_col + } + } + +- description: Fetch data from cast_test table for bigint column after casting to string + url: /v1/graphql + status: 200 + response: + data: + cast_test: + - id: 3 + bigint_col: '123' + query: + query: |- + query { + cast_test (where: {bigint_col: {_cast: {String: {_ilike: "123"}}}}){ + id + bigint_col + } + } + +- description: Fetch data from cast_test table for float column after casting to string + url: /v1/graphql + status: 200 + response: + data: + cast_test: + - id: 4 + float_col: '1.23' + query: + query: |- + query { + cast_test (where: {float_col: {_cast: {String: {_ilike: "1.23"}}}}){ + id + float_col + } + } + +- description: Fetch data from cast_test table for double column after casting to string + url: /v1/graphql + status: 200 + response: + data: + cast_test: + - id: 5 + double_col: '1.23' + query: + query: |- + query { + cast_test (where: {double_col: {_cast: {String: {_ilike: "1.23"}}}}){ + id + double_col + } + } + +- description: Fetch data from cast_test table for numeric column after casting to string + url: /v1/graphql + status: 200 + response: + data: + cast_test: + - id: 6 + numeric_col: '123' + query: + query: |- + query { + cast_test (where: {numeric_col: {_cast: {String: {_ilike: "123"}}}}){ + id + numeric_col + } + } +- description: Fetch data from cast_test table for money column after casting to string + url: /v1/graphql + status: 200 + response: + data: + cast_test: + - id: 7 + money_col: '$1.23' + query: + query: |- + query { + cast_test (where: {money_col: {_cast: {String: {_ilike: "%1%"}}}}){ + id + money_col + } + } +- description: Fetch data from cast_test table for boolean column after casting to string + url: /v1/graphql + status: 200 + response: + data: + cast_test: + - id: 8 + boolean_col: true + query: + query: |- + query { + cast_test (where: {boolean_col: {_cast: {String: {_ilike: "true"}}}}){ + id + boolean_col + } + } +- description: Fetch data from cast_test table for char column after casting to string + url: /v1/graphql + status: 200 + response: + data: + cast_test: + - id: 9 + char_col: 'a' + query: + query: |- + query { + cast_test (where: {char_col: {_cast: {String: {_ilike: "a"}}}}){ + id + char_col + } + } + +- description: Fetch data from cast_test table for date column after casting to string + url: /v1/graphql + status: 200 + response: + data: + cast_test: + - id: 10 + date_col: '2018-05-12' + query: + query: |- + query { + cast_test (where: {date_col: {_cast: {String: {_ilike: "2018-%"}}}}){ + id + date_col + } + } +- description: Fetch data from cast_test table for timestamp column after casting to string + url: /v1/graphql + status: 200 + response: + data: + cast_test: + - id: 11 + timestamp_col: '2018-09-21T09:50:44' + query: + query: |- + query { + cast_test (where: {timestamp_col: {_cast: {String: {_ilike: "2018-%"}}}}){ + id + timestamp_col + } + } +- description: Fetch data from cast_test table for timestamptz column after casting to string + url: /v1/graphql + status: 200 + response: + data: + cast_test: + - id: 12 + timestamptz_col: '2018-09-21T09:50:44+00:00' + query: + query: |- + query { + cast_test (where: {timestamptz_col: {_cast: {String: {_ilike: "2018-%"}}}}){ + id + timestamptz_col + } + } + +- description: Fetch data from cast_test table for timetz column after casting to string + url: /v1/graphql + status: 200 + response: + data: + cast_test: + - id: 13 + timetz_col: '09:50:44+00' + query: + query: |- + query { + cast_test (where: {timetz_col: {_cast: {String: {_ilike: "09%"}}}}){ + id + timetz_col + } + } + +- description: Fetch data from cast_test table for json column after casting to string + url: /v1/graphql + status: 200 + response: + data: + cast_test: + - id: 14 + json_col: + - foo + query: + query: |- + query { + cast_test (where: {json_col: {_cast: {String: {_ilike: "%f%"}}}}){ + id + json_col + } + } + +- description: Fetch data from cast_test table for uuid column after casting to string + url: /v1/graphql + status: 200 + response: + data: + cast_test: + - id: 15 + uuid_col: '28d6d683-1317-49f7-b1cf-7d195242e4e5' + query: + query: |- + query { + cast_test (where: {uuid_col: {_cast: {String: {_ilike: "%7d195242e4e5"}}}}){ + id + uuid_col + } + } diff --git a/server/tests-py/queries/graphql_query/boolexp/basic/setup.yaml b/server/tests-py/queries/graphql_query/boolexp/basic/setup.yaml index 3bab22b0bc7..15d29082da3 100644 --- a/server/tests-py/queries/graphql_query/boolexp/basic/setup.yaml +++ b/server/tests-py/queries/graphql_query/boolexp/basic/setup.yaml @@ -143,6 +143,25 @@ args: , sql_id information_schema.sql_identifier ); INSERT INTO table_with_sql_identifier (sql_id) VALUES ('one'), ('one'), ('two'), ('three'), ('four'), ('one'), ('two'); + + CREATE TABLE "cast_test" ( + id serial primary key, + smallint_col smallint, + integer_col integer, + bigint_col bigint, + float_col float, + double_col double precision, + numeric_col numeric, + money_col money, + boolean_col boolean, + char_col char, + date_col date, + timestamp_col timestamp, + timestamptz_col timestamptz, + timetz_col timetz, + json_col json, + uuid_col uuid + ); - type: track_table args: @@ -284,3 +303,46 @@ args: - type: track_table args: table_with_sql_identifier + +#Table to test _cast to string +- type: track_table + args: + name: cast_test + schema: public + +- type: insert + args: + table: cast_test + objects: + - smallint_col: 123 + - integer_col: 123 + - bigint_col: 123 + - float_col: 1.23 + - double_col: 1.23 + - numeric_col: 123 + - money_col: 1.23 + - boolean_col: true + - char_col: 'a' + - date_col: '2018-05-12' + - timestamp_col: '2018-09-21T09:50:44' + - timestamptz_col: '2018-09-21T09:50:44' + - timetz_col: '09:50:44' + - json_col: + - foo + - uuid_col: 28d6d683-1317-49f7-b1cf-7d195242e4e5 + - smallint_col: 789 + - integer_col: 789 + - bigint_col: 789 + - float_col: 7.89 + - double_col: 7.89 + - numeric_col: 789 + - money_col: 7.89 + - boolean_col: false + - char_col: 'z' + - date_col: '2019-01-11' + - timestamp_col: '2019-01-21T01:10:11' + - timestamptz_col: '2019-01-21T01:10:11' + - timetz_col: '01:51:11' + - json_col: + - bar + - uuid_col: 28d6d683-1317-49f7-b1cf-7d195242e4e6 \ No newline at end of file diff --git a/server/tests-py/queries/graphql_query/boolexp/basic/teardown.yaml b/server/tests-py/queries/graphql_query/boolexp/basic/teardown.yaml index 2f1ea845ad8..84bd59efb36 100644 --- a/server/tests-py/queries/graphql_query/boolexp/basic/teardown.yaml +++ b/server/tests-py/queries/graphql_query/boolexp/basic/teardown.yaml @@ -12,4 +12,5 @@ args: DROP TABLE uuid_test; DROP TABLE "user"; DROP TABLE account; + DROP TABLE cast_test; cascade: true diff --git a/server/tests-py/test_graphql_queries.py b/server/tests-py/test_graphql_queries.py index 55343dc41a3..fccb31e1a0e 100644 --- a/server/tests-py/test_graphql_queries.py +++ b/server/tests-py/test_graphql_queries.py @@ -651,6 +651,9 @@ class TestGraphQLQueryBoolExpBasicPostgres: def test_in_sql_identifier_array(self, hge_ctx, transport): check_query_f(hge_ctx, self.dir() + '/in_sql_identifier_array.yaml', transport) + def test_select_cast_test_where_cast_string(self, hge_ctx, transport): + check_query_f(hge_ctx, self.dir() + '/select_cast_test_where_cast_string_postgres.yaml', transport) + @classmethod def dir(cls): return 'queries/graphql_query/boolexp/basic'