[InlineData(1, 20)] // Asking for too many rows at a non-zero start public async Task GetSubsetSuccess(int startRow, int rowCount) { // If: // ... I create a new result set with a valid db data reader // ... And execute the result set DbDataReader mockReader = GetReader(Common.StandardTestDataSet, false, Constants.StandardQuery); var fileStreamFactory = MemoryFileSystem.GetFileStreamFactory(); ResultSet resultSet = new ResultSet(Common.Ordinal, Common.Ordinal, fileStreamFactory); await resultSet.ReadResultToEnd(mockReader, CancellationToken.None); // ... And attempt to get a subset with valid number of rows ResultSetSubset subset = await resultSet.GetSubset(startRow, rowCount); // Then: // ... rows sub-array and RowCount field of the subset should match Assert.Equal(subset.RowCount, subset.Rows.Length); // Then: // ... There should be rows in the subset, either the number of rows or the number of // rows requested or the number of rows in the result set, whichever is lower long availableRowsFromStart = resultSet.RowCount - startRow; Assert.Equal(Math.Min(availableRowsFromStart, rowCount), subset.RowCount); // ... The rows should have the same number of columns as the resultset Assert.Equal(resultSet.Columns.Length, subset.Rows[0].Length); }
public void BatchSubsetValidTest(int rowCount) { // If I have an executed batch Batch b = Common.GetBasicExecutedBatch(); // ... And I ask for a subset with valid arguments ResultSetSubset subset = b.GetSubset(0, 0, rowCount).Result; // Then: // I should get the requested number of rows Assert.Equal(Math.Min(rowCount, Common.StandardTestResultSet.Count()), subset.RowCount); Assert.Equal(Math.Min(rowCount, Common.StandardTestResultSet.Count()), subset.Rows.Length); }
/// <summary> /// Retrieves a subset of rows with the pending updates applied. If more rows than exist /// are requested, only the rows that exist will be returned. /// </summary> /// <param name="startIndex">Index to start returning rows from</param> /// <param name="rowCount">The number of rows to return.</param> /// <returns>An array of rows with pending edits applied</returns> public async Task <EditRow[]> GetRows(long startIndex, int rowCount) { ThrowIfNotInitialized(); // Get the cached rows from the result set ResultSetSubset cachedRows = startIndex < associatedResultSet.RowCount ? await associatedResultSet.GetSubset(startIndex, rowCount) : new ResultSetSubset { RowCount = 0, Rows = new DbCellValue[][] { } }; // Convert the rows into EditRows and apply the changes we have List <EditRow> editRows = new List <EditRow>(); for (int i = 0; i < cachedRows.RowCount; i++) { long rowId = i + startIndex; RowEditBase edr; if (EditCache.TryGetValue(rowId, out edr)) { // Ask the edit object to generate an edit row editRows.Add(edr.GetEditRow(cachedRows.Rows[i])); } else { // Package up the existing row into a clean edit row EditRow er = new EditRow { Id = rowId, Cells = cachedRows.Rows[i].Select(cell => new EditCell(cell, false)).ToArray(), State = EditRow.EditRowState.Clean }; editRows.Add(er); } } // If the requested range of rows was at the end of the original cell set and we have // added new rows, we need to reflect those changes if (rowCount > cachedRows.RowCount) { long endIndex = startIndex + cachedRows.RowCount; var newRows = EditCache.Where(edit => edit.Key >= endIndex).Take(rowCount - cachedRows.RowCount); editRows.AddRange(newRows.Select(newRow => newRow.Value.GetEditRow(null))); } return(editRows.ToArray()); }
public void ResultSetValidTest(int startRow, int rowCount) { // Setup: // ... I have a batch that has been executed Batch b = Common.GetBasicExecutedBatch(); // If: // ... I have a result set and I ask for a subset with valid arguments ResultSet rs = b.ResultSets.First(); ResultSetSubset subset = rs.GetSubset(startRow, rowCount).Result; // Then: // ... I should get the requested number of rows back Assert.Equal(Math.Min(rowCount, Common.StandardTestResultSet.Count()), subset.RowCount); Assert.Equal(Math.Min(rowCount, Common.StandardTestResultSet.Count()), subset.Rows.Length); }
/// <summary> /// Handles a request to get a subset of the results of this query /// </summary> internal async Task HandleResultSubsetRequest(SubsetParams subsetParams, RequestContext <SubsetResult> requestContext) { try { ResultSetSubset subset = await InterServiceResultSubset(subsetParams); var result = new SubsetResult { ResultSubset = subset }; await requestContext.SendResult(result); } catch (Exception e) { // This was unexpected, so send back as error await requestContext.SendError(e.Message); } }
/// <summary> /// Handles a request to get a subset of the results of this query /// </summary> internal async Task HandleResultSubsetRequest(SubsetParams subsetParams, RequestContext <SubsetResult> requestContext) { try { ResultSetSubset subset = await InterServiceResultSubset(subsetParams); var result = new SubsetResult { ResultSubset = subset }; await requestContext.SendResult(result); Logger.Write(TraceEventType.Stop, $"Done Handler for Subset request with for Query:'{subsetParams.OwnerUri}', Batch:'{subsetParams.BatchIndex}', ResultSetIndex:'{subsetParams.ResultSetIndex}', RowsStartIndex'{subsetParams.RowsStartIndex}', Requested RowsCount:'{subsetParams.RowsCount}'\r\n\t\t with subset response of:[ RowCount:'{subset.RowCount}', Rows array of length:'{subset.Rows.Length}']"); } catch (Exception e) { // This was unexpected, so send back as error await requestContext.SendError(e.Message); } }
/// <summary> /// Handles a request to execute a string and return the result /// </summary> internal async Task HandleSimpleExecuteRequest(SimpleExecuteParams executeParams, RequestContext <SimpleExecuteResult> requestContext) { try { string randomUri = Guid.NewGuid().ToString(); ExecuteStringParams executeStringParams = new ExecuteStringParams { Query = executeParams.QueryString, // generate guid as the owner uri to make sure every query is unique OwnerUri = randomUri }; // get connection ConnectionInfo connInfo; if (!ConnectionService.TryFindConnection(executeParams.OwnerUri, out connInfo)) { await requestContext.SendError(SR.QueryServiceQueryInvalidOwnerUri); return; } ConnectParams connectParams = new ConnectParams { OwnerUri = randomUri, Connection = connInfo.ConnectionDetails, Type = ConnectionType.Default }; Task workTask = Task.Run(async() => { await ConnectionService.Connect(connectParams); ConnectionInfo newConn; ConnectionService.TryFindConnection(randomUri, out newConn); Func <string, Task> queryCreateFailureAction = message => requestContext.SendError(message); ResultOnlyContext <SimpleExecuteResult> newContext = new ResultOnlyContext <SimpleExecuteResult>(requestContext); // handle sending event back when the query completes Query.QueryAsyncEventHandler queryComplete = async query => { try { // check to make sure any results were recieved if (query.Batches.Length == 0 || query.Batches[0].ResultSets.Count == 0) { await requestContext.SendError(SR.QueryServiceResultSetHasNoResults); return; } long rowCount = query.Batches[0].ResultSets[0].RowCount; // check to make sure there is a safe amount of rows to load into memory if (rowCount > Int32.MaxValue) { await requestContext.SendError(SR.QueryServiceResultSetTooLarge); return; } SimpleExecuteResult result = new SimpleExecuteResult { RowCount = rowCount, ColumnInfo = query.Batches[0].ResultSets[0].Columns, Rows = new DbCellValue[0][] }; if (rowCount > 0) { SubsetParams subsetRequestParams = new SubsetParams { OwnerUri = randomUri, BatchIndex = 0, ResultSetIndex = 0, RowsStartIndex = 0, RowsCount = Convert.ToInt32(rowCount) }; // get the data to send back ResultSetSubset subset = await InterServiceResultSubset(subsetRequestParams); result.Rows = subset.Rows; } await requestContext.SendResult(result); } finally { Query removedQuery; Task removedTask; // remove the active query since we are done with it ActiveQueries.TryRemove(randomUri, out removedQuery); ActiveSimpleExecuteRequests.TryRemove(randomUri, out removedTask); ConnectionService.Disconnect(new DisconnectParams() { OwnerUri = randomUri, Type = null }); } }; // handle sending error back when query fails Query.QueryAsyncErrorEventHandler queryFail = async(q, e) => { await requestContext.SendError(e); }; await InterServiceExecuteQuery(executeStringParams, newConn, newContext, null, queryCreateFailureAction, queryComplete, queryFail); }); ActiveSimpleExecuteRequests.TryAdd(randomUri, workTask); } catch (Exception ex) { await requestContext.SendError(ex.ToString()); } }
/// <summary> /// Save results as JSON format to the file specified in saveParams /// </summary> /// <param name="saveParams"> Parameters from the request </param> /// <param name="requestContext"> Request context for save results </param> /// <param name="result"> Result query object </param> /// <returns></returns> internal void SaveResultSetAsJson(SaveResultsAsJsonRequestParams saveParams, RequestContext <SaveResultRequestResult> requestContext, Query result) { // Run in a separate thread SaveTask = Task.Run(async() => { try { using (StreamWriter jsonFile = new StreamWriter(File.Open(saveParams.FilePath, FileMode.Create, FileAccess.ReadWrite, FileShare.Read))) using (JsonWriter jsonWriter = new JsonTextWriter(jsonFile)) { int rowCount = 0; int rowStartIndex = 0; int columnStartIndex = 0; int columnEndIndex = 0; jsonWriter.Formatting = Formatting.Indented; jsonWriter.WriteStartArray(); // Get the requested resultSet from query Batch selectedBatch = result.Batches[saveParams.BatchIndex]; ResultSet selectedResultSet = selectedBatch.ResultSets[saveParams.ResultSetIndex]; // Set column, row counts depending on whether save request is for entire result set or a subset if (IsSaveSelection(saveParams)) { rowCount = saveParams.RowEndIndex.Value - saveParams.RowStartIndex.Value + 1; rowStartIndex = saveParams.RowStartIndex.Value; columnStartIndex = saveParams.ColumnStartIndex.Value; columnEndIndex = saveParams.ColumnEndIndex.Value + 1; // include the last column } else { rowCount = (int)selectedResultSet.RowCount; columnEndIndex = selectedResultSet.Columns.Length; } // Split rows into batches for (int count = 0; count < (rowCount / BatchSize) + 1; count++) { int numberOfRows = (count < rowCount / BatchSize) ? BatchSize : (rowCount % BatchSize); if (numberOfRows == 0) { break; } // Retrieve rows and write as json ResultSetSubset resultSubset = await result.GetSubset(saveParams.BatchIndex, saveParams.ResultSetIndex, rowStartIndex + count * BatchSize, numberOfRows); foreach (var row in resultSubset.Rows) { jsonWriter.WriteStartObject(); for (int i = columnStartIndex; i < columnEndIndex; i++) { // Write columnName, value pair DbColumnWrapper col = selectedResultSet.Columns[i]; string val = row[i]?.ToString(); jsonWriter.WritePropertyName(col.ColumnName); if (val == null) { jsonWriter.WriteNull(); } else { jsonWriter.WriteValue(val); } } jsonWriter.WriteEndObject(); } } jsonWriter.WriteEndArray(); } // Successfully wrote file, send success result if (SaveCompleted != null) { await SaveCompleted(null); } } catch (Exception ex) { // Delete file when exception occurs if (FileUtils.SafeFileExists(saveParams.FilePath)) { FileUtils.SafeFileDelete(saveParams.FilePath); } if (SaveFailed != null) { await SaveFailed(ex.Message); } } }); }
/// <summary> /// Handles a request to execute a string and return the result /// </summary> internal Task HandleSimpleExecuteRequest(SimpleExecuteParams executeParams, RequestContext <SimpleExecuteResult> requestContext) { ExecuteStringParams executeStringParams = new ExecuteStringParams { Query = executeParams.QueryString, // generate guid as the owner uri to make sure every query is unique OwnerUri = Guid.NewGuid().ToString() }; // get connection ConnectionInfo connInfo; if (!ConnectionService.TryFindConnection(executeParams.OwnerUri, out connInfo)) { return(requestContext.SendError(SR.QueryServiceQueryInvalidOwnerUri)); } if (connInfo.ConnectionDetails.MultipleActiveResultSets == null || connInfo.ConnectionDetails.MultipleActiveResultSets == false) { // if multipleActive result sets is not allowed, don't specific a connection and make the ownerURI the true owneruri connInfo = null; executeStringParams.OwnerUri = executeParams.OwnerUri; } Func <string, Task> queryCreateFailureAction = message => requestContext.SendError(message); ResultOnlyContext <SimpleExecuteResult> newContext = new ResultOnlyContext <SimpleExecuteResult>(requestContext); // handle sending event back when the query completes Query.QueryAsyncEventHandler queryComplete = async q => { Query removedQuery; // check to make sure any results were recieved if (q.Batches.Length == 0 || q.Batches[0].ResultSets.Count == 0) { await requestContext.SendError(SR.QueryServiceResultSetHasNoResults); ActiveQueries.TryRemove(executeStringParams.OwnerUri, out removedQuery); return; } var rowCount = q.Batches[0].ResultSets[0].RowCount; // check to make sure there is a safe amount of rows to load into memory if (rowCount > Int32.MaxValue) { await requestContext.SendError(SR.QueryServiceResultSetTooLarge); ActiveQueries.TryRemove(executeStringParams.OwnerUri, out removedQuery); return; } SubsetParams subsetRequestParams = new SubsetParams { OwnerUri = executeStringParams.OwnerUri, BatchIndex = 0, ResultSetIndex = 0, RowsStartIndex = 0, RowsCount = Convert.ToInt32(rowCount) }; // get the data to send back ResultSetSubset subset = await InterServiceResultSubset(subsetRequestParams); SimpleExecuteResult result = new SimpleExecuteResult { RowCount = q.Batches[0].ResultSets[0].RowCount, ColumnInfo = q.Batches[0].ResultSets[0].Columns, Rows = subset.Rows }; await requestContext.SendResult(result); // remove the active query since we are done with it ActiveQueries.TryRemove(executeStringParams.OwnerUri, out removedQuery); }; // handle sending error back when query fails Query.QueryAsyncErrorEventHandler queryFail = async(q, e) => { await requestContext.SendError(e); }; return(InterServiceExecuteQuery(executeStringParams, connInfo, newContext, null, queryCreateFailureAction, queryComplete, queryFail)); }