|
1 | 1 | using Azure.DataGateway.Service.Models; |
2 | 2 | using Azure.DataGateway.Services; |
| 3 | +using HotChocolate.Resolvers; |
3 | 4 | using System; |
4 | 5 | using System.Collections.Generic; |
5 | 6 | using System.Data.Common; |
| 7 | +using System.Text; |
6 | 8 | using System.Text.Json; |
7 | 9 | using System.Threading.Tasks; |
8 | 10 |
|
@@ -36,64 +38,69 @@ 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 | + |
| 56 | + return jsonString.ToString(); |
| 57 | + } |
| 58 | + |
39 | 59 | // <summary> |
40 | 60 | // ExecuteAsync the given named graphql query on the backend. |
41 | 61 | // </summary> |
42 | | - public async Task<JsonDocument> ExecuteAsync(string graphQLQueryName, IDictionary<string, object> parameters) |
| 62 | + public async Task<JsonDocument> ExecuteAsync(IMiddlewareContext context, IDictionary<string, object> parameters) |
43 | 63 | { |
44 | 64 | // TODO: add support for nesting |
45 | 65 | // TODO: add support for join query against another table |
46 | 66 | // TODO: add support for TOP and Order-by push-down |
47 | 67 |
|
48 | | - GraphQLQueryResolver resolver = _metadataStoreProvider.GetQueryResolver(graphQLQueryName); |
49 | | - var jsonDocument = JsonDocument.Parse("{ }"); |
50 | | - |
51 | | - string queryText = _queryBuilder.Build(resolver.ParametrizedQuery, false); |
52 | | - |
| 68 | + SqlQueryStructure structure = new(context, _metadataStoreProvider, _queryBuilder); |
| 69 | + Console.WriteLine(structure.ToString()); |
53 | 70 | // Open connection and execute query using _queryExecutor |
54 | 71 | // |
55 | | - DbDataReader dbDataReader = await _queryExecutor.ExecuteQueryAsync(queryText, parameters); |
| 72 | + DbDataReader dbDataReader = await _queryExecutor.ExecuteQueryAsync(structure.ToString(), parameters); |
56 | 73 |
|
57 | 74 | // Parse Results into Json and return |
58 | | - // |
59 | | - if (await dbDataReader.ReadAsync()) |
60 | | - { |
61 | | - jsonDocument = JsonDocument.Parse(dbDataReader.GetString(0)); |
62 | | - } |
63 | | - else |
| 75 | + if (!dbDataReader.HasRows) |
64 | 76 | { |
65 | | - Console.WriteLine("Did not return enough rows in the JSON result."); |
| 77 | + return null; |
66 | 78 | } |
67 | 79 |
|
68 | | - return jsonDocument; |
| 80 | + return JsonDocument.Parse(await GetJsonStringFromDbReader(dbDataReader)); |
69 | 81 | } |
70 | 82 |
|
71 | 83 | // <summary> |
72 | 84 | // Executes the given named graphql query on the backend and expecting a list of Jsons back. |
73 | 85 | // </summary> |
74 | | - public async Task<IEnumerable<JsonDocument>> ExecuteListAsync(string graphQLQueryName, IDictionary<string, object> parameters) |
| 86 | + public async Task<IEnumerable<JsonDocument>> ExecuteListAsync(IMiddlewareContext context, IDictionary<string, object> parameters) |
75 | 87 | { |
76 | 88 | // TODO: add support for nesting |
77 | 89 | // TODO: add support for join query against another container |
78 | 90 | // TODO: add support for TOP and Order-by push-down |
79 | 91 |
|
80 | | - 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); |
| 92 | + SqlQueryStructure structure = new(context, _metadataStoreProvider, _queryBuilder); |
| 93 | + Console.WriteLine(structure.ToString()); |
| 94 | + DbDataReader dbDataReader = await _queryExecutor.ExecuteQueryAsync(structure.ToString(), parameters); |
84 | 95 |
|
85 | | - // Deserialize results into list of JsonDocuments and return |
| 96 | + // Parse Results into Json and return |
86 | 97 | // |
87 | | - if (await dbDataReader.ReadAsync()) |
88 | | - { |
89 | | - resultsAsList = JsonSerializer.Deserialize<List<JsonDocument>>(dbDataReader.GetString(0)); |
90 | | - } |
91 | | - else |
| 98 | + if (!dbDataReader.HasRows) |
92 | 99 | { |
93 | | - Console.WriteLine("Did not return enough rows in the JSON result."); |
| 100 | + return new List<JsonDocument>(); |
94 | 101 | } |
95 | 102 |
|
96 | | - return resultsAsList; |
| 103 | + return JsonSerializer.Deserialize<List<JsonDocument>>(await GetJsonStringFromDbReader(dbDataReader)); |
97 | 104 | } |
98 | 105 | } |
99 | 106 | } |
0 commit comments