1// Copyright (C) 2023 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 {generateSqlWithInternalLayout} from '../sql_utils/layout'; 16import {NAMED_ROW, NamedRow, NamedSliceTrack} from './named_slice_track'; 17import {createView} from '../../trace_processor/sql_utils'; 18import {Slice} from '../../public/track'; 19import {AsyncDisposableStack} from '../../base/disposable_stack'; 20import {sqlNameSafe} from '../../base/string_utils'; 21import {Trace} from '../../public/trace'; 22 23export interface CustomSqlImportConfig { 24 modules: string[]; 25} 26 27export interface CustomSqlTableDefConfig { 28 // Table name 29 sqlTableName: string; 30 // Table columns 31 columns?: string[]; 32 whereClause?: string; 33 disposable?: AsyncDisposable; 34} 35 36export abstract class CustomSqlTableSliceTrack extends NamedSliceTrack< 37 Slice, 38 NamedRow 39> { 40 protected readonly tableName; 41 42 constructor(trace: Trace, uri: string) { 43 super(trace, uri); 44 this.tableName = `customsqltableslicetrack_${sqlNameSafe(uri)}`; 45 } 46 47 getRowSpec(): NamedRow { 48 return NAMED_ROW; 49 } 50 51 rowToSlice(row: NamedRow): Slice { 52 return this.rowToSliceBase(row); 53 } 54 55 abstract getSqlDataSource(): 56 | CustomSqlTableDefConfig 57 | Promise<CustomSqlTableDefConfig>; 58 59 getSqlImports(): CustomSqlImportConfig { 60 return { 61 modules: [] as string[], 62 }; 63 } 64 65 async onInit() { 66 await this.loadImports(); 67 const config = await Promise.resolve(this.getSqlDataSource()); 68 let columns = ['*']; 69 if (config.columns !== undefined) { 70 columns = config.columns; 71 } 72 const trash = new AsyncDisposableStack(); 73 config.disposable && trash.use(config.disposable); 74 trash.use( 75 await createView( 76 this.engine, 77 this.tableName, 78 generateSqlWithInternalLayout({ 79 columns: columns, 80 sourceTable: config.sqlTableName, 81 ts: 'ts', 82 dur: 'dur', 83 whereClause: config.whereClause, 84 }), 85 ), 86 ); 87 return trash; 88 } 89 90 getSqlSource(): string { 91 return `SELECT * FROM ${this.tableName}`; 92 } 93 94 async loadImports() { 95 for (const importModule of this.getSqlImports().modules) { 96 await this.engine.query(`INCLUDE PERFETTO MODULE ${importModule};`); 97 } 98 } 99} 100