Skip to content

Commit 2d2fe93

Browse files
plcplchasura-bot
authored andcommitted
fix: MSSQL remote relationships handles large results
PR-URL: hasura/graphql-engine-mono#10327 GitOrigin-RevId: c7e478f52f7aa7b44a3597886588b0c0f1e7341c
1 parent 5e2f5d4 commit 2d2fe93

File tree

3 files changed

+224
-1
lines changed

3 files changed

+224
-1
lines changed

server/lib/api-tests/api-tests.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ library
229229
Test.Regression.RemoteRelationshipStringifyNum8387Spec
230230
Test.Regression.RootExistElementAliasing9457Spec
231231
Test.Regression.SqlServerIdentifierQuotingSpec
232+
Test.Regression.SqlServerRemoteRelationshipRhs8712Spec
232233
Test.Regression.StreamConflictSpec
233234
Test.Regression.UsingTheSameFunctionForRootFieldAndComputedField8643Spec
234235
Test.ScheduledEvents.ScheduledEventsInvalidEnvVarSpec
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
{-# LANGUAGE DuplicateRecordFields #-}
2+
{-# LANGUAGE QuasiQuotes #-}
3+
4+
-- | Tests that SqlServer is capable of returning many rows when invoked as the
5+
-- rhs of a remote object relationship.
6+
--
7+
-- 'FOR JSON' selects at the top-level split up large results across multiple result
8+
-- set rows, and the function that invokes the query needs to be aware of that.
9+
module Test.Regression.SqlServerRemoteRelationshipRhs8712Spec (spec) where
10+
11+
import Data.Aeson (Value)
12+
import Data.List.NonEmpty qualified as NE
13+
import Data.Text qualified as Text
14+
import Harness.Backend.Postgres qualified as Postgres
15+
import Harness.Backend.Sqlserver qualified as SQLServer
16+
import Harness.GraphqlEngine qualified as GraphqlEngine
17+
import Harness.Quoter.Graphql (graphql)
18+
import Harness.Quoter.Yaml (yaml)
19+
import Harness.Schema (Table (..))
20+
import Harness.Schema qualified as Schema
21+
import Harness.Test.Fixture qualified as Fixture
22+
import Harness.Test.SetupAction as SetupAction
23+
import Harness.Test.TestResource (Managed)
24+
import Harness.TestEnvironment
25+
( GlobalTestEnvironment,
26+
Server,
27+
TestEnvironment,
28+
focusFixtureLeft,
29+
focusFixtureRight,
30+
)
31+
import Harness.Yaml (shouldAtLeastBe)
32+
import Hasura.Prelude
33+
import Test.Hspec hiding (context)
34+
35+
--------------------------------------------------------------------------------
36+
-- Preamble
37+
38+
spec :: SpecWith GlobalTestEnvironment
39+
spec = Fixture.runWithLocalTestEnvironment contexts tests
40+
where
41+
contexts = NE.singleton $ Fixture.combineFixtures [] lhsPostgres rhsSQLServer
42+
43+
--------------------------------------------------------------------------------
44+
45+
--------------------------------------------------------------------------------
46+
47+
-- | LHS context.
48+
--
49+
-- Each LHS context is responsible for setting up the remote relationship, and
50+
-- for tearing it down. Each lhs context is given the JSON representation for
51+
-- the table name on the RHS.
52+
type LHSFixture = Value -> Fixture.Fixture (Maybe Server)
53+
54+
lhsPostgres :: LHSFixture
55+
lhsPostgres tableName =
56+
(Fixture.fixture $ Fixture.Backend Postgres.backendTypeMetadata)
57+
{ Fixture.mkLocalTestEnvironment = lhsPostgresMkLocalTestEnvironment,
58+
Fixture.setupTeardown = \testEnv ->
59+
[ SetupAction.noTeardown (lhsPostgresSetup tableName testEnv)
60+
]
61+
}
62+
63+
--------------------------------------------------------------------------------
64+
65+
-- | RHS context
66+
--
67+
-- Each RHS context is responsible for setting up the target table, and for
68+
-- returning the JSON representation of said table.
69+
type RHSFixture = (Value, Fixture.Fixture ())
70+
71+
rhsSQLServer :: RHSFixture
72+
rhsSQLServer =
73+
let table =
74+
[yaml|
75+
schema: hasura
76+
name: album
77+
|]
78+
context =
79+
(Fixture.fixture $ Fixture.Backend SQLServer.backendTypeMetadata)
80+
{ Fixture.setupTeardown = \testEnv ->
81+
[ SetupAction.noTeardown (rhsSQLServerSetup testEnv)
82+
]
83+
}
84+
in (table, context)
85+
86+
--------------------------------------------------------------------------------
87+
-- Schema
88+
89+
-- | LHS
90+
track :: Schema.Table
91+
track =
92+
(Schema.table "track")
93+
{ tableColumns =
94+
[ Schema.column "id" Schema.TInt,
95+
Schema.column "title" Schema.TStr,
96+
Schema.columnNull "album_id" Schema.TInt
97+
],
98+
tablePrimaryKey = ["id"],
99+
tableData =
100+
[ [Schema.VInt i, Schema.VStr "sometrack", Schema.VInt i] | i <- [0 .. 19]
101+
]
102+
}
103+
104+
-- | RHS
105+
album :: Schema.Table
106+
album =
107+
(Schema.table "album")
108+
{ tableColumns =
109+
[ Schema.column "id" Schema.TInt,
110+
Schema.column "title" Schema.TStr,
111+
Schema.columnNull "artist_id" Schema.TInt
112+
],
113+
tablePrimaryKey = ["id"],
114+
tableData =
115+
[ [Schema.VInt i, Schema.VStr (Text.concat $ replicate 10 "somealbum"), Schema.VInt 1]
116+
| i <- [0 .. 19 :: Int]
117+
]
118+
}
119+
120+
--------------------------------------------------------------------------------
121+
-- LHS Postgres
122+
123+
lhsPostgresMkLocalTestEnvironment :: TestEnvironment -> Managed (Maybe Server)
124+
lhsPostgresMkLocalTestEnvironment _ = pure Nothing
125+
126+
lhsPostgresSetup :: (HasCallStack) => Value -> (TestEnvironment, Maybe Server) -> IO ()
127+
lhsPostgresSetup rhsTableName (wholeTestEnvironment, _) = do
128+
let testEnvironment = focusFixtureLeft wholeTestEnvironment
129+
sourceName = "source"
130+
sourceConfig = Postgres.defaultSourceConfiguration testEnvironment
131+
schemaName = Schema.getSchemaName testEnvironment
132+
-- Add remote source
133+
GraphqlEngine.postMetadata_
134+
testEnvironment
135+
[yaml|
136+
type: pg_add_source
137+
args:
138+
name: *sourceName
139+
configuration: *sourceConfig
140+
|]
141+
-- setup tables only
142+
Postgres.createTable testEnvironment track
143+
Postgres.insertTable testEnvironment track
144+
Schema.trackTable sourceName track testEnvironment
145+
GraphqlEngine.postMetadata_
146+
testEnvironment
147+
[yaml|
148+
type: bulk
149+
args:
150+
- type: pg_create_remote_relationship
151+
args:
152+
source: *sourceName
153+
table:
154+
schema: *schemaName
155+
name: track
156+
name: album
157+
definition:
158+
to_source:
159+
source: target
160+
table: *rhsTableName
161+
relationship_type: object
162+
field_mapping:
163+
album_id: id
164+
|]
165+
166+
--------------------------------------------------------------------------------
167+
-- RHS SQLServer
168+
169+
rhsSQLServerSetup :: (TestEnvironment, ()) -> IO ()
170+
rhsSQLServerSetup (wholeTestEnvironment, _) = do
171+
let testEnvironment = focusFixtureRight wholeTestEnvironment
172+
sourceName = "target"
173+
sourceConfig = SQLServer.defaultSourceConfiguration testEnvironment
174+
175+
-- Add remote source
176+
GraphqlEngine.postMetadata_
177+
testEnvironment
178+
[yaml|
179+
type: mssql_add_source
180+
args:
181+
name: *sourceName
182+
configuration: *sourceConfig
183+
|]
184+
SQLServer.createTable testEnvironment album
185+
SQLServer.insertTable testEnvironment album
186+
Schema.trackTable sourceName album testEnvironment
187+
188+
--------------------------------------------------------------------------------
189+
-- Tests
190+
191+
tests :: SpecWith (TestEnvironment, Maybe Server)
192+
tests = describe "object-relationship" $ do
193+
executionTests
194+
195+
-- | Basic queries using *-to-DB joins
196+
executionTests :: SpecWith (TestEnvironment, Maybe Server)
197+
executionTests = describe "execution" $ do
198+
-- fetches the relationship data
199+
it "related-data" $ \(testEnvironment, _) -> do
200+
let lhsSchema = Schema.getSchemaName $ focusFixtureLeft testEnvironment
201+
let query =
202+
[graphql|
203+
query {
204+
track: #{lhsSchema}_track {
205+
id
206+
title
207+
album {
208+
id
209+
title
210+
}
211+
}
212+
}
213+
|]
214+
expected =
215+
[yaml|
216+
data:
217+
track: []
218+
|]
219+
220+
actual <- (GraphqlEngine.postGraphql testEnvironment query)
221+
222+
actual `shouldAtLeastBe` expected

server/src-lib/Hasura/Backends/MSSQL/Instances/Execute.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -518,5 +518,5 @@ msDBRemoteRelationshipPlan userInfo sourceName sourceConfig lhs lhsSchema argume
518518
pure $ DBStepInfo @'MSSQL sourceName sourceConfig (Just queryString) odbcQuery ()
519519
where
520520
runSelectQuery queryPrinter = OnBaseMonad do
521-
let queryTx = encJFromText <$> Tx.singleRowQueryE defaultMSSQLTxErrorHandler (toQueryFlat queryPrinter)
521+
let queryTx = encJFromText <$> Tx.forJsonQueryE defaultMSSQLTxErrorHandler (toQueryFlat queryPrinter)
522522
mssqlRunReadOnly (_mscExecCtx sourceConfig) (fmap withNoStatistics queryTx)

0 commit comments

Comments
 (0)