[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);
        }
Example #2
0
        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);
        }
Example #3
0
        /// <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());
        }
Example #4
0
        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);
        }
Example #5
0
        /// <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);
            }
        }
Example #6
0
        /// <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);
            }
        }
Example #7
0
        /// <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());
            }
        }
Example #8
0
        /// <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);
                    }
                }
            });
        }
Example #9
0
        /// <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));
        }