|
3 | 3 | using System; |
4 | 4 | using System.Collections.Generic; |
5 | 5 | using System.Data.Common; |
| 6 | +using System.Text; |
6 | 7 | using System.Text.Json; |
7 | 8 | using System.Threading.Tasks; |
| 9 | +using HotChocolate.Resolvers; |
8 | 10 |
|
9 | 11 | namespace Azure.DataGateway.Service.Resolvers |
10 | 12 | { |
@@ -36,64 +38,73 @@ public void RegisterResolver(GraphQLQueryResolver resolver) |
36 | 38 | // no-op |
37 | 39 | } |
38 | 40 |
|
| 41 | + private static async Task<string> GetJsonStringFromDbReader(DbDataReader dbDataReader) |
| 42 | + { |
| 43 | + var jsonString = new StringBuilder(); |
| 44 | + // Even though we only return a single cell, we need this loop for |
| 45 | + // MS SQL. Sadly it splits FOR JSON PATH output across multiple |
| 46 | + // cells if the JSON consists of more than 2033 bytes: |
| 47 | + // Sources: |
| 48 | + // 1. https://docs.microsoft.com/en-us/sql/relational-databases/json/format-query-results-as-json-with-for-json-sql-server?view=sql-server-2017#output-of-the-for-json-clause |
| 49 | + // 2. https://stackoverflow.com/questions/54973536/for-json-path-results-in-ssms-truncated-to-2033-characters/54973676 |
| 50 | + // 3. https://docs.microsoft.com/en-us/sql/relational-databases/json/use-for-json-output-in-sql-server-and-in-client-apps-sql-server?view=sql-server-2017#use-for-json-output-in-a-c-client-app |
| 51 | + if (await dbDataReader.ReadAsync()) |
| 52 | + { |
| 53 | + jsonString.Append(dbDataReader.GetString(0)); |
| 54 | + } |
| 55 | + return jsonString.ToString(); |
| 56 | + } |
| 57 | + |
39 | 58 | // <summary> |
40 | 59 | // ExecuteAsync the given named graphql query on the backend. |
41 | 60 | // </summary> |
42 | | - public async Task<JsonDocument> ExecuteAsync(string graphQLQueryName, IDictionary<string, object> parameters) |
| 61 | + public async Task<JsonDocument> ExecuteAsync(IMiddlewareContext context, IDictionary<string, object> parameters) |
43 | 62 | { |
44 | 63 | // TODO: add support for nesting |
45 | 64 | // TODO: add support for join query against another table |
46 | 65 | // TODO: add support for TOP and Order-by push-down |
47 | 66 |
|
| 67 | + string graphQLQueryName = context.Selection.Field.Name.Value; |
48 | 68 | GraphQLQueryResolver resolver = _metadataStoreProvider.GetQueryResolver(graphQLQueryName); |
49 | | - var jsonDocument = JsonDocument.Parse("{ }"); |
50 | | - |
51 | | - string queryText = _queryBuilder.Build(resolver.ParametrizedQuery, false); |
52 | | - |
| 69 | + SqlQueryStructure structure = new(context, _metadataStoreProvider, _queryBuilder); |
| 70 | + Console.WriteLine(structure.ToString()); |
53 | 71 | // Open connection and execute query using _queryExecutor |
54 | 72 | // |
55 | | - DbDataReader dbDataReader = await _queryExecutor.ExecuteQueryAsync(queryText, parameters); |
| 73 | + DbDataReader dbDataReader = await _queryExecutor.ExecuteQueryAsync(structure.ToString(), parameters); |
56 | 74 |
|
57 | 75 | // Parse Results into Json and return |
58 | | - // |
59 | | - if (await dbDataReader.ReadAsync()) |
60 | | - { |
61 | | - jsonDocument = JsonDocument.Parse(dbDataReader.GetString(0)); |
62 | | - } |
63 | | - else |
| 76 | + if (!dbDataReader.HasRows) |
64 | 77 | { |
65 | | - Console.WriteLine("Did not return enough rows in the JSON result."); |
| 78 | + return null; |
66 | 79 | } |
67 | 80 |
|
68 | | - return jsonDocument; |
| 81 | + return JsonDocument.Parse(await GetJsonStringFromDbReader(dbDataReader)); |
69 | 82 | } |
70 | 83 |
|
71 | 84 | // <summary> |
72 | 85 | // Executes the given named graphql query on the backend and expecting a list of Jsons back. |
73 | 86 | // </summary> |
74 | | - public async Task<IEnumerable<JsonDocument>> ExecuteListAsync(string graphQLQueryName, IDictionary<string, object> parameters) |
| 87 | + public async Task<IEnumerable<JsonDocument>> ExecuteListAsync(IMiddlewareContext context, IDictionary<string, object> parameters) |
75 | 88 | { |
76 | 89 | // TODO: add support for nesting |
77 | 90 | // TODO: add support for join query against another container |
78 | 91 | // TODO: add support for TOP and Order-by push-down |
79 | 92 |
|
| 93 | + string graphQLQueryName = context.Selection.Field.Name.Value; |
80 | 94 | GraphQLQueryResolver resolver = _metadataStoreProvider.GetQueryResolver(graphQLQueryName); |
81 | | - var resultsAsList = new List<JsonDocument>(); |
82 | | - string queryText = _queryBuilder.Build(resolver.ParametrizedQuery, true); |
83 | | - DbDataReader dbDataReader = await _queryExecutor.ExecuteQueryAsync(queryText, parameters); |
84 | 95 |
|
85 | | - // Deserialize results into list of JsonDocuments and return |
| 96 | + SqlQueryStructure structure = new(context, _metadataStoreProvider, _queryBuilder); |
| 97 | + Console.WriteLine(structure.ToString()); |
| 98 | + DbDataReader dbDataReader = await _queryExecutor.ExecuteQueryAsync(structure.ToString(), parameters); |
| 99 | + |
| 100 | + // Parse Results into Json and return |
86 | 101 | // |
87 | | - if (await dbDataReader.ReadAsync()) |
88 | | - { |
89 | | - resultsAsList = JsonSerializer.Deserialize<List<JsonDocument>>(dbDataReader.GetString(0)); |
90 | | - } |
91 | | - else |
| 102 | + if (!dbDataReader.HasRows) |
92 | 103 | { |
93 | | - Console.WriteLine("Did not return enough rows in the JSON result."); |
| 104 | + return new List<JsonDocument>(); |
94 | 105 | } |
95 | 106 |
|
96 | | - return resultsAsList; |
| 107 | + return JsonSerializer.Deserialize<List<JsonDocument>>(await GetJsonStringFromDbReader(dbDataReader)); |
97 | 108 | } |
98 | 109 | } |
99 | 110 | } |
0 commit comments