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'; 20 21import {Controller} from './controller'; 22import {globals} from './globals'; 23 24export interface QueryControllerArgs { 25 queryId: string; 26 engine: Engine; 27} 28 29export class QueryController extends Controller<'init'|'querying'> { 30 constructor(private args: QueryControllerArgs) { 31 super('init'); 32 } 33 34 run() { 35 switch (this.state) { 36 case 'init': 37 const config = assertExists(globals.state.queries[this.args.queryId]); 38 this.runQuery(config.query).then(result => { 39 console.log(`Query ${config.query} took ${result.durationMs} ms`); 40 globals.publish('QueryResult', {id: this.args.queryId, data: result}); 41 globals.dispatch(Actions.deleteQuery({queryId: this.args.queryId})); 42 }); 43 this.setState('querying'); 44 break; 45 46 case 'querying': 47 // Nothing to do here, as soon as the deleteQuery is dispatched this 48 // controller will be destroyed (by the TraceController). 49 break; 50 51 default: 52 throw new Error(`Unexpected state ${this.state}`); 53 } 54 } 55 56 private async runQuery(sqlQuery: string) { 57 const startMs = performance.now(); 58 const rawResult = await this.args.engine.query(sqlQuery); 59 const durationMs = performance.now() - startMs; 60 const columns = rawQueryResultColumns(rawResult); 61 const rows = 62 QueryController.firstN<Row>(10000, rawQueryResultIter(rawResult)); 63 const result: QueryResponse = { 64 id: this.args.queryId, 65 query: sqlQuery, 66 durationMs, 67 error: rawResult.error, 68 totalRowCount: +rawResult.numRecords, 69 columns, 70 rows, 71 }; 72 return result; 73 } 74 75 private static firstN<T>(n: number, iter: IterableIterator<T>): T[] { 76 const list = []; 77 for (let i = 0; i < n; i++) { 78 const {done, value} = iter.next(); 79 if (done) break; 80 list.push(value); 81 } 82 return list; 83 } 84} 85