server/tests: bigquery scalar type tests

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/5405
Co-authored-by: Gil Mizrahi <8547573+soupi@users.noreply.github.com>
GitOrigin-RevId: fc0a509500068331f9d80d58b411f144e011f094
This commit is contained in:
Abby Sassel 2022-08-11 16:35:58 +01:00 committed by hasura-bot
parent 0922a3bb24
commit 26bbe2be34
20 changed files with 310 additions and 249 deletions

View File

@ -1226,6 +1226,7 @@ test-suite tests-hspec
Test.BackendOnlyPermissionsSpec
Test.BigQuery.ComputedFieldSpec
Test.BigQuery.Metadata.ComputedFieldSpec
Test.BigQuery.Queries.SpatialTypesSpec
Test.BigQuery.TypeInterpretationSpec
Test.CustomRootFieldsSpec
Test.DataConnector.AggregateQuerySpec

View File

@ -170,6 +170,7 @@ scalarType = \case
Schema.TStr -> "STRING"
Schema.TUTCTime -> "DATETIME"
Schema.TBool -> "BOOLEAN"
Schema.TGeography -> "GEOGRAPHY"
Schema.TCustomType txt -> Schema.getBackendScalarType txt bstBigQuery
-- | Create column. BigQuery doesn't support default values. Also,
@ -188,6 +189,7 @@ serialize = \case
VStr s -> "'" <> T.replace "'" "\'" s <> "'"
VUTCTime t -> T.pack $ formatTime defaultTimeLocale "DATETIME '%F %T'" t
VBool b -> tshow b
VGeography (Schema.WKT wkt) -> T.concat ["st_geogfromtext(\'", wkt, "\')"]
VNull -> "NULL"
VCustomValue bsv -> Schema.formatBackendScalarValueType $ Schema.backendScalarValue bsv bsvBigQuery

View File

@ -130,6 +130,7 @@ scalarType = \case
Schema.TStr -> "VARCHAR"
Schema.TUTCTime -> "TIMESTAMP"
Schema.TBool -> "BOOLEAN"
Schema.TGeography -> "GEOGRAPHY"
Schema.TCustomType txt -> Schema.getBackendScalarType txt bstCitus
mkColumn :: Schema.Column -> Text
@ -195,6 +196,7 @@ serialize = \case
VStr s -> "'" <> T.replace "'" "\'" s <> "'"
VUTCTime t -> T.pack $ formatTime defaultTimeLocale "'%F %T'" t
VBool b -> if b then "TRUE" else "FALSE"
VGeography (Schema.WKT wkt) -> T.concat ["st_geogfromtext(\'", wkt, "\')"]
VNull -> "NULL"
VCustomValue bsv -> Schema.formatBackendScalarValueType $ Schema.backendScalarValue bsv bsvCitus

View File

@ -120,6 +120,7 @@ scalarType = \case
Schema.TStr -> "TEXT"
Schema.TUTCTime -> "DATETIME"
Schema.TBool -> "BIT"
Schema.TGeography -> "GEOGRAPHY"
Schema.TCustomType txt -> Schema.getBackendScalarType txt bstMysql
mkColumn :: Schema.Column -> Text
@ -181,6 +182,7 @@ serialize = \case
VStr s -> "'" <> T.replace "'" "\'" s <> "'"
VUTCTime t -> T.pack $ formatTime defaultTimeLocale "'%F %T'" t
VBool b -> tshow @Int $ if b then 1 else 0
VGeography (Schema.WKT wkt) -> T.concat ["st_geogfromtext(\'", wkt, "\')"]
VNull -> "NULL"
VCustomValue bsv -> Schema.formatBackendScalarValueType $ Schema.backendScalarValue bsv bsvMysql

View File

@ -138,6 +138,7 @@ scalarType = \case
Schema.TStr -> "VARCHAR"
Schema.TUTCTime -> "TIMESTAMP"
Schema.TBool -> "BOOLEAN"
Schema.TGeography -> "GEOGRAPHY"
Schema.TCustomType txt -> Schema.getBackendScalarType txt bstPostgres
mkColumn :: Schema.Column -> Text
@ -206,6 +207,7 @@ serialize = \case
VStr s -> "'" <> T.replace "'" "\'" s <> "'"
VUTCTime t -> T.pack $ formatTime defaultTimeLocale "'%F %T'" t
VBool b -> if b then "TRUE" else "FALSE"
VGeography (Schema.WKT wkt) -> T.concat ["st_geogfromtext(\'", wkt, "\')"]
VNull -> "NULL"
VCustomValue bsv -> Schema.formatBackendScalarValueType $ Schema.backendScalarValue bsv bsvPostgres

View File

@ -125,6 +125,7 @@ scalarType = \case
Schema.TStr -> "NVARCHAR(127)"
Schema.TUTCTime -> "DATETIME"
Schema.TBool -> "BIT"
Schema.TGeography -> "GEOGRAPHY"
Schema.TCustomType txt -> Schema.getBackendScalarType txt bstMssql
mkColumn :: Schema.Column -> Text
@ -201,6 +202,7 @@ serialize = \case
VStr s -> "'" <> T.replace "'" "\'" s <> "'"
VUTCTime t -> T.pack $ formatTime defaultTimeLocale "'%F %T'" t
VBool b -> tshow @Int $ if b then 1 else 0
VGeography (Schema.WKT wkt) -> T.concat ["st_geogfromtext(\'", wkt, "\')"]
VNull -> "NULL"
VCustomValue bsv -> Schema.formatBackendScalarValueType $ Schema.backendScalarValue bsv bsvMssql

View File

@ -9,6 +9,7 @@ module Harness.Test.Schema
ScalarType (..),
defaultSerialType,
ScalarValue (..),
WKT (..),
UniqueConstraint (..),
BackendScalarType (..),
BackendScalarValue (..),
@ -215,6 +216,7 @@ data ScalarType
| TStr
| TUTCTime
| TBool
| TGeography
| TCustomType BackendScalarType
deriving (Show, Eq)
@ -225,10 +227,17 @@ data ScalarValue
| VStr Text
| VUTCTime UTCTime
| VBool Bool
| VGeography WKT
| VNull
| VCustomValue BackendScalarValue
deriving (Show, Eq)
-- | Describe Geography values using the WKT representation
-- https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry
-- https://cloud.google.com/bigquery/docs/geospatial-data#loading_wkt_or_wkb_data
newtype WKT = WKT Text
deriving (Eq, Show, IsString)
backendScalarValue :: BackendScalarValue -> (BackendScalarValue -> Maybe BackendScalarValueType) -> BackendScalarValueType
backendScalarValue bsv fn = case fn bsv of
Nothing -> error $ "backendScalarValue: Retrieved value is Nothing, passed " <> show bsv

View File

@ -0,0 +1,290 @@
{-# LANGUAGE QuasiQuotes #-}
-- | Test support for geospatial data types on BigQuery
-- https://cloud.google.com/bigquery/docs/geospatial-data
module Test.BigQuery.Queries.SpatialTypesSpec (spec) where
import Data.Aeson (Value)
import Data.List.NonEmpty qualified as NE
import Harness.Backend.BigQuery qualified as BigQuery
import Harness.GraphqlEngine (postGraphql)
import Harness.Quoter.Graphql (graphql)
import Harness.Quoter.Yaml (interpolateYaml)
import Harness.Test.Fixture qualified as Fixture
import Harness.Test.Schema qualified as Schema
import Harness.TestEnvironment (TestEnvironment (..))
import Harness.Yaml (shouldReturnYaml)
import Hasura.Prelude
import Test.Hspec (SpecWith, describe, it)
spec :: SpecWith TestEnvironment
spec =
Fixture.run
( NE.fromList
[ (Fixture.fixture $ Fixture.Backend Fixture.BigQuery)
{ Fixture.setupTeardown = \(testEnvironment, _) ->
[ BigQuery.setupTablesAction schema testEnvironment
]
}
]
)
tests
--------------------------------------------------------------------------------
-- Schema
schema :: [Schema.Table]
schema =
[ (Schema.table "spatial_types_geography")
{ Schema.tableColumns =
[ Schema.column "point" Schema.TGeography,
Schema.column "linestring" Schema.TGeography,
Schema.column "polygon" Schema.TGeography,
Schema.column "multipoint" Schema.TGeography,
Schema.column "multilinestring" Schema.TGeography,
Schema.column "multipolygon" Schema.TGeography,
Schema.column "geometrycollection" Schema.TGeography
],
Schema.tablePrimaryKey = ["id"],
Schema.tableData =
[ [ Schema.VGeography "POINT(3 4)",
Schema.VGeography "LINESTRING(1 1, 2 3, 4 8, -6 3)",
Schema.VGeography "POLYGON((2 1, 1 2, 1 1, 2 1))",
Schema.VGeography "MULTIPOINT(2 3, 7 8)",
Schema.VGeography "LINESTRING(1 1, 3 3, 5 5, 7 7)",
Schema.VGeography "MULTIPOLYGON(((-120.533 46.566, -118.283 46.1, -122.3 47.45, -120.533 46.566)), ((-2 2, -2 -2, 2 -2, 2 2, -2 2)))",
Schema.VGeography "GEOMETRYCOLLECTION(LINESTRING(1 1, 3 5), POLYGON((-5 -1, -5 -5, -1 -5, -1 -1, -5 -1)))"
]
]
}
]
--------------------------------------------------------------------------------
-- Tests
tests :: Fixture.Options -> SpecWith TestEnvironment
tests opts = do
let shouldBe :: IO Value -> Value -> IO ()
shouldBe = shouldReturnYaml opts
describe "Geography scalar type" do
it "Fetch geospatial data" \testEnvironment -> do
let schemaName = Schema.getSchemaName testEnvironment
let expected :: Value
expected =
[interpolateYaml|
data:
#{schemaName}_spatial_types_geography:
- point: POINT(3 4)
linestring: LINESTRING(1 1, 2 3, 4 8, -6 3)
polygon: POLYGON((2 1, 1 2, 1 1, 2 1))
multipoint: MULTIPOINT(2 3, 7 8)
multilinestring:
LINESTRING(1 1, 3 3, 5 5, 7 7)
multipolygon:
MULTIPOLYGON(((-120.533 46.566, -118.283 46.1, -122.3 47.45, -120.533 46.566)), ((-2 2, -2 -2, 2 -2, 2 2, -2 2)))
geometrycollection:
GEOMETRYCOLLECTION(LINESTRING(1 1, 3 5), POLYGON((-5 -1, -5 -5, -1 -5, -1 -1, -5 -1)))
|]
actual :: IO Value
actual =
postGraphql
testEnvironment
[graphql|
query {
#{schemaName}_spatial_types_geography {
point
linestring
polygon
multipoint
multilinestring
multipolygon
geometrycollection
}
}
|]
actual `shouldBe` expected
it "Fetch geospatial data filtered on _st_equals" \testEnvironment -> do
let schemaName = Schema.getSchemaName testEnvironment
let expected :: Value
expected =
[interpolateYaml|
data:
#{schemaName}_spatial_types_geography:
- geometrycollection:
GEOMETRYCOLLECTION(LINESTRING(1 1, 3 5), POLYGON((-5 -1, -5 -5, -1 -5, -1 -1, -5 -1)))
|]
actual :: IO Value
actual =
postGraphql
testEnvironment
[graphql|
query {
#{schemaName}_spatial_types_geography(
where: {
geometrycollection: {
_st_equals: "GEOMETRYCOLLECTION(LINESTRING(1 1, 3 5), POLYGON((-5 -1, -5 -5, -1 -5, -1 -1, -5 -1)))"
}
}
) {
geometrycollection
}
}
|]
actual `shouldBe` expected
it "Fetch geospatial data filtered on _st_contains" \testEnvironment -> do
let schemaName = Schema.getSchemaName testEnvironment
let expected :: Value
expected =
[interpolateYaml|
data:
#{schemaName}_spatial_types_geography:
- multipolygon:
MULTIPOLYGON(((-120.533 46.566, -118.283 46.1, -122.3 47.45, -120.533 46.566)), ((-2 2, -2 -2, 2 -2, 2 2, -2 2)))
|]
actual :: IO Value
actual =
postGraphql
testEnvironment
[graphql|
query {
#{schemaName}_spatial_types_geography(
where: {
multipolygon: {
_st_contains: "POINT(0.5 0)"
}
}
) {
multipolygon
}
}
|]
actual `shouldBe` expected
it "Fetch geospatial data filtered on _st_intersects" \testEnvironment -> do
let schemaName = Schema.getSchemaName testEnvironment
let expected :: Value
expected =
[interpolateYaml|
data:
#{schemaName}_spatial_types_geography:
- polygon: POLYGON((2 1, 1 2, 1 1, 2 1))
|]
actual :: IO Value
actual =
postGraphql
testEnvironment
[graphql|
query {
#{schemaName}_spatial_types_geography(
where: {
polygon: {
_st_intersects: "LINESTRING(0 0, 2 2)"
}
}
) {
polygon
}
}
|]
actual `shouldBe` expected
it "Fetch geospatial data filtered on _st_within" \testEnvironment -> do
let schemaName = Schema.getSchemaName testEnvironment
let expected :: Value
expected =
[interpolateYaml|
data:
#{schemaName}_spatial_types_geography:
- multipoint: MULTIPOINT(2 3, 7 8)
|]
actual :: IO Value
actual =
postGraphql
testEnvironment
[graphql|
query {
#{schemaName}_spatial_types_geography(
where: {
multipoint: {
_st_within: "POLYGON ((0 0, 10 10, 10 10, 10 0, 0 0))"
}
}
) {
multipoint
}
}
|]
actual `shouldBe` expected
it "Fetch geospatial data filtered on _st_d_within" \testEnvironment -> do
let schemaName = Schema.getSchemaName testEnvironment
let expected :: Value
expected =
[interpolateYaml|
data:
#{schemaName}_spatial_types_geography:
- multipoint: MULTIPOINT(2 3, 7 8)
|]
actual :: IO Value
actual =
postGraphql
testEnvironment
[graphql|
query {
#{schemaName}_spatial_types_geography(
where: {
multipoint: {
_st_d_within: { distance: 1, from: "POLYGON ((0 0, 10 10, 10 10, 10 0, 0 0))"}
}
}
) {
multipoint
}
}
|]
actual `shouldBe` expected
it "Fetch geospatial data filtered on _st_touches" \testEnvironment -> do
let schemaName = Schema.getSchemaName testEnvironment
let expected :: Value
expected =
[interpolateYaml|
data:
#{schemaName}_spatial_types_geography:
- point: POINT(3 4)
|]
actual :: IO Value
actual =
postGraphql
testEnvironment
[graphql|
query {
#{schemaName}_spatial_types_geography(
where: {
point: {
_st_touches: "POLYGON ((3 4, 2 5, 5 5, 5 2, 3 4))"
}
}
) {
point
}
}
|]
actual `shouldBe` expected

View File

@ -1,44 +0,0 @@
type: bulk
args:
- type: bigquery_run_sql
args:
source: bigquery
sql: |
CREATE TABLE `hasura.spatial_types_geog` (
`point` GEOGRAPHY,
`linestring` GEOGRAPHY,
`polygon` GEOGRAPHY,
`multipoint` GEOGRAPHY,
`multilinestring` GEOGRAPHY,
`multipolygon` GEOGRAPHY,
`geometrycollection` GEOGRAPHY
);
INSERT INTO `hasura.spatial_types_geog` (
`point`,
`linestring`,
`polygon`,
`multipoint`,
`multilinestring`,
`multipolygon`,
`geometrycollection`
)
VALUES (
st_geogfromtext('POINT(3 4)'),
st_geogfromtext('LINESTRING(1 1,2 3,4 8, -6 3)'),
st_geogfromtext('POLYGON((1 1, 1 2, 2 1, 1 1))'),
st_geogfromtext('MULTIPOINT((2 3), (7 8))'),
st_geogfromtext(
'MULTILINESTRING((1 1, 3 3, 5 5),(3 3, 5 5, 7 7))'
),
st_geogfromtext(
-- for some odd reason this multipolygon from 3 polygons goes through a very odd conversion in BigQuery
-- st_geogfromtext('MULTIPOLYGON(((-120.533 46.566, -118.283 46.1, -122.3 47.45, -120.533 46.566)),((2 2, 2 -2, -2 -2, -2 2, 2 2)),((1 1, 3 1, 3 3, 1 3, 1 1)))')
-- ->
-- MULTIPOLYGON(((-120.533 46.566, -118.283 46.1, -122.3 47.45, -120.533 46.566)), ((-2 2, -2 -2, 2 -2, 2 1.00015229710421, 3 1, 3 3, 1 3, 1 2.00091355021717, -2 2)))
'MULTIPOLYGON(((-120.533 46.566, -118.283 46.1, -122.3 47.45, -120.533 46.566)),((2 2, 2 -2, -2 -2, -2 2, 2 2)))'
),
st_geogfromtext(
'GEOMETRYCOLLECTION(LINESTRING(1 1, 3 5),POLYGON((-1 -1, -1 -5, -5 -5, -5 -1, -1 -1)))'
)
);

View File

@ -1,8 +0,0 @@
type: bulk
args:
- type: bigquery_run_sql
args:
source: bigquery
cascade: true
sql: |
DROP TABLE IF EXISTS `hasura.spatial_types_geog`;

View File

@ -1,30 +0,0 @@
description: GraphQL query to test different data types of SQL Server
url: /v1/graphql
status: 200
response:
data:
hasura_spatial_types_geog:
- point: POINT(3 4)
linestring: LINESTRING(1 1, 2 3, 4 8, -6 3)
polygon: POLYGON((2 1, 1 2, 1 1, 2 1))
multipoint: MULTIPOINT(2 3, 7 8)
multilinestring:
LINESTRING(1 1, 3 3, 5 5, 7 7)
multipolygon:
MULTIPOLYGON(((-120.533 46.566, -118.283 46.1, -122.3 47.45, -120.533 46.566)), ((-2 2, -2 -2, 2 -2, 2 2, -2 2)))
geometrycollection:
GEOMETRYCOLLECTION(LINESTRING(1 1, 3 5), POLYGON((-5 -1, -5 -5, -1 -5, -1 -1, -5 -1)))
query:
query: |
query {
hasura_spatial_types_geog {
point
linestring
polygon
multipoint
multilinestring
multipolygon
geometrycollection
}
}

View File

@ -1,21 +0,0 @@
description: Query data from spatial_types_geo using _st_contains
url: /v1/graphql
status: 200
response:
data:
hasura_spatial_types_geog:
- multipolygon:
MULTIPOLYGON(((-120.533 46.566, -118.283 46.1, -122.3 47.45, -120.533 46.566)), ((-2 2, -2 -2, 2 -2, 2 2, -2 2)))
query:
query: |
query {
hasura_spatial_types_geog(
where: {
multipolygon: {
_st_contains: "POINT(0.5 0)"
}
}
) {
multipolygon
}
}

View File

@ -1,20 +0,0 @@
description: Query data from spatial_types_geog using _st_d_within
url: /v1/graphql
status: 200
response:
data:
hasura_spatial_types_geog:
- multipoint: MULTIPOINT(2 3, 7 8)
query:
query: |
query {
hasura_spatial_types_geog(
where: {
multipoint: {
_st_d_within: { distance: 1, from: "POLYGON ((0 0, 10 10, 10 10, 10 0, 0 0))"}
}
}
) {
multipoint
}
}

View File

@ -1,24 +0,0 @@
description: Query data from spatial_types_geog using _st_equals
url: /v1/graphql
status: 200
response:
data:
hasura_spatial_types_geog:
- geometrycollection:
GEOMETRYCOLLECTION(LINESTRING(1 1, 3 5), POLYGON((-5 -1, -5 -5, -1 -5, -1 -1, -5 -1)))
query:
# note: using GEOMETRYCOLLECTION(LINESTRING(1 1, 3 5),POLYGON((-1 -1, -1 -5, -5 -5, -5 -1, -1 -1)))
# doesn't work with st_equals in BigQuery even when the docs say that the function should work with
# any point order (probably the problem is in handling GEOGRAPHY params)
query: |
query {
hasura_spatial_types_geog(
where: {
geometrycollection: {
_st_equals: "GEOMETRYCOLLECTION(LINESTRING(1 1, 3 5), POLYGON((-5 -1, -5 -5, -1 -5, -1 -1, -5 -1)))"
}
}
) {
geometrycollection
}
}

View File

@ -1,14 +0,0 @@
description: Query data from spatial_types_geog using _st_intersects
url: /v1/graphql
status: 200
response:
data:
hasura_spatial_types_geog:
- polygon: POLYGON((2 1, 1 2, 1 1, 2 1))
query:
query: |
query {
hasura_spatial_types_geog(where: { polygon: { _st_intersects: "LINESTRING(0 0, 2 2)" } }) {
polygon
}
}

View File

@ -1,20 +0,0 @@
description: Query data from spatial_types_geog using _st_touches
url: /v1/graphql
status: 200
response:
data:
hasura_spatial_types_geog:
- point: POINT(3 4)
query:
query: |
query {
hasura_spatial_types_geog(
where: {
point: {
_st_touches: "POLYGON ((3 4, 2 5, 5 5, 5 2, 3 4))"
}
}
) {
point
}
}

View File

@ -1,20 +0,0 @@
description: Query data from spatial_types_geog using _st_within
url: /v1/graphql
status: 200
response:
data:
hasura_spatial_types_geog:
- multipoint: MULTIPOINT(2 3, 7 8)
query:
query: |
query {
hasura_spatial_types_geog(
where: {
multipoint: {
_st_within: "POLYGON ((0 0, 10 10, 10 10, 10 0, 0 0))"
}
}
) {
multipoint
}
}

View File

@ -1,9 +0,0 @@
type: bulk
args:
- type: bigquery_track_table
args:
source: bigquery
table:
dataset: hasura
name: spatial_types_geog

View File

@ -1,10 +0,0 @@
type: bulk
args:
- type: bigquery_untrack_table
args:
source: bigquery
table:
dataset: hasura
name: spatial_types_geog
cascade: true

View File

@ -1398,32 +1398,3 @@ class TestGraphQLQueryBoolExpSpatialMSSQL:
@classmethod
def dir(cls):
return 'queries/graphql_query/boolexp/spatial'
@pytest.mark.parametrize("transport", ['http', 'websocket'])
@pytest.mark.backend('bigquery')
@usefixtures('per_class_tests_db_state')
class TestGraphQLQueryBoolExpSpatialBigquery:
def test_select_spatial_bigquery_types(self, hge_ctx, transport):
check_query_f(hge_ctx, self.dir() + '/select_query_spatial_types_bigquery.yaml', transport)
def test_select_spatial_bigquery_types_where_st_equals(self, hge_ctx, transport):
check_query_f(hge_ctx, self.dir() + '/select_query_spatial_types_where_st_equals_bigquery.yaml', transport)
def test_select_spatial_bigquery_types_where_st_contains(self, hge_ctx, transport):
check_query_f(hge_ctx, self.dir() + '/select_query_spatial_types_where_st_contains_bigquery.yaml', transport)
def test_select_spatial_bigquery_types_where_st_intersects(self, hge_ctx, transport):
check_query_f(hge_ctx, self.dir() + '/select_query_spatial_types_where_st_intersects_bigquery.yaml', transport)
def test_select_spatial_bigquery_types_where_st_within(self, hge_ctx, transport):
check_query_f(hge_ctx, self.dir() + '/select_query_spatial_types_where_st_within_bigquery.yaml', transport)
def test_select_spatial_bigquery_types_where_st_d_within(self, hge_ctx, transport):
check_query_f(hge_ctx, self.dir() + '/select_query_spatial_types_where_st_d_within_bigquery.yaml', transport)
def test_select_spatial_bigquery_types_where_st_touches(self, hge_ctx, transport):
check_query_f(hge_ctx, self.dir() + '/select_query_spatial_types_where_st_touches_bigquery.yaml', transport)
@classmethod
def dir(cls):
return 'queries/graphql_query/boolexp/spatial'