blob: ff1bf7e042c9b7f69fbb584ea33eddc0d1f7f1b2 [file] [log] [blame]
// Copyright (C) 2018 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import {Engine} from '../../trace_processor/engine';
import {Row} from '../../trace_processor/query_result';
export interface QueryResponse {
query: string;
error?: string;
totalRowCount: number;
durationMs: number;
columns: string[];
rows: Row[];
statementCount: number;
statementWithOutputCount: number;
lastStatementSql: string;
}
/**
* Runs a query and pulls out all the columns into a list of 'row' objects,
* where each row contains a dictionary of [columnName] -> value.
*
* This method will not throw if the query fails, instead the error is populated
* in the return value.
*
* This function is designed to be used with table viewers, where the structure
* of resulting rows is not known up front. Use engine.query() or
* engine.tryQuery() and use the iterators if the resulting data is to be
* processed.
*
* @param sqlQuery - The query to evaluate.
* @param engine - The engine to use to run the query.
*/
export async function runQueryForQueryTable(
sqlQuery: string,
engine: Engine,
): Promise<QueryResponse> {
const startMs = performance.now();
// TODO(primiano): once the controller thread is gone we should pass down
// the result objects directly to the frontend, iterate over the result
// and deal with pagination there. For now we keep the old behavior and
// truncate to 10k rows.
const maybeResult = await engine.tryQuery(sqlQuery);
if (maybeResult.ok) {
const queryRes = maybeResult.value;
const durationMs = performance.now() - startMs;
const rows: Row[] = [];
const columns = queryRes.columns();
for (const iter = queryRes.iter({}); iter.valid(); iter.next()) {
const row: Row = {};
for (const colName of columns) {
const value = iter.get(colName);
row[colName] = value;
}
rows.push(row);
}
const result: QueryResponse = {
query: sqlQuery,
durationMs,
error: queryRes.error(),
totalRowCount: queryRes.numRows(),
columns,
rows,
statementCount: queryRes.statementCount(),
statementWithOutputCount: queryRes.statementWithOutputCount(),
lastStatementSql: queryRes.lastStatementSql(),
};
return result;
} else {
// In the case of a query error we don't want the exception to bubble up
// as a crash. The |queryRes| object will be populated anyways.
// queryRes.error() is used to tell if the query errored or not. If it
// errored, the frontend will show a graceful message instead.
return {
query: sqlQuery,
durationMs: performance.now() - startMs,
error: maybeResult.error,
totalRowCount: 0,
columns: [],
rows: [],
statementCount: 0,
statementWithOutputCount: 0,
lastStatementSql: '',
};
}
}