1// Copyright (C) 2018 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15import {assertExists} from '../base/logging'; 16import {Actions} from '../common/actions'; 17import {Engine} from '../common/engine'; 18import {rawQueryResultColumns, rawQueryResultIter, Row} from '../common/protos'; 19import {QueryResponse} from '../common/queries'; 20import {slowlyCountRows} from '../common/query_iterator'; 21 22import {Controller} from './controller'; 23import {globals} from './globals'; 24 25export interface QueryControllerArgs { 26 queryId: string; 27 engine: Engine; 28} 29 30export class QueryController extends Controller<'init'|'querying'> { 31 constructor(private args: QueryControllerArgs) { 32 super('init'); 33 } 34 35 run() { 36 switch (this.state) { 37 case 'init': 38 const config = assertExists(globals.state.queries[this.args.queryId]); 39 this.runQuery(config.query).then(result => { 40 console.log(`Query ${config.query} took ${result.durationMs} ms`); 41 globals.publish('QueryResult', {id: this.args.queryId, data: result}); 42 globals.dispatch(Actions.deleteQuery({queryId: this.args.queryId})); 43 }); 44 this.setState('querying'); 45 break; 46 47 case 'querying': 48 // Nothing to do here, as soon as the deleteQuery is dispatched this 49 // controller will be destroyed (by the TraceController). 50 break; 51 52 default: 53 throw new Error(`Unexpected state ${this.state}`); 54 } 55 } 56 57 private async runQuery(sqlQuery: string) { 58 const startMs = performance.now(); 59 const rawResult = await this.args.engine.uncheckedQuery(sqlQuery); 60 const durationMs = performance.now() - startMs; 61 const columns = rawQueryResultColumns(rawResult); 62 const rows = 63 QueryController.firstN<Row>(10000, rawQueryResultIter(rawResult)); 64 const result: QueryResponse = { 65 id: this.args.queryId, 66 query: sqlQuery, 67 durationMs, 68 error: rawResult.error, 69 totalRowCount: slowlyCountRows(rawResult), 70 columns, 71 rows, 72 }; 73 return result; 74 } 75 76 private static firstN<T>(n: number, iter: IterableIterator<T>): T[] { 77 const list = []; 78 for (let i = 0; i < n; i++) { 79 const {done, value} = iter.next(); 80 if (done) break; 81 list.push(value); 82 } 83 return list; 84 } 85} 86