xref: /aosp_15_r20/external/perfetto/ui/src/plugins/dev.perfetto.SqlModules/sql_modules_impl.ts (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1// Copyright (C) 2024 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 {z} from 'zod';
16import {
17  SqlModules,
18  SqlColumn,
19  SqlFunction,
20  SqlArgument,
21  SqlMacro,
22  SqlModule,
23  SqlPackage,
24  SqlTable,
25  SqlTableFunction,
26} from './sql_modules';
27import {SqlTableDescription} from '../../components/widgets/sql/table/table_description';
28import {
29  TableColumn,
30  TableColumnSet,
31} from '../../components/widgets/sql/table/column';
32import {
33  ArgSetColumnSet,
34  DurationColumn,
35  ProcessColumn,
36  ProcessIdColumn,
37  SchedIdColumn,
38  SliceIdColumn,
39  StandardColumn,
40  ThreadColumn,
41  ThreadIdColumn,
42  ThreadStateIdColumn,
43  TimestampColumn,
44} from '../../components/widgets/sql/table/well_known_columns';
45
46export class SqlModulesImpl implements SqlModules {
47  readonly packages: SqlPackage[];
48
49  constructor(docs: SqlModulesDocsSchema) {
50    this.packages = docs.map((json) => new StdlibPackageImpl(json));
51  }
52
53  listTables(): string[] {
54    return this.packages.flatMap((p) => p.listTables());
55  }
56
57  getModuleForTable(tableName: string): SqlModule | undefined {
58    for (const stdlibPackage of this.packages) {
59      const maybeTable = stdlibPackage.getModuleForTable(tableName);
60      if (maybeTable) {
61        return maybeTable;
62      }
63    }
64    return undefined;
65  }
66}
67
68export class StdlibPackageImpl implements SqlPackage {
69  readonly name: string;
70  readonly modules: SqlModule[];
71
72  constructor(docs: DocsPackageSchemaType) {
73    this.name = docs.name;
74    this.modules = [];
75    for (const moduleJson of docs.modules) {
76      this.modules.push(new StdlibModuleImpl(moduleJson));
77    }
78  }
79
80  getModuleForTable(tableName: string): SqlModule | undefined {
81    for (const module of this.modules) {
82      for (const dataObj of module.dataObjects) {
83        if (dataObj.name == tableName) {
84          return module;
85        }
86      }
87    }
88    return undefined;
89  }
90
91  listTables(): string[] {
92    return this.modules.flatMap((module) =>
93      module.dataObjects.map((dataObj) => dataObj.name),
94    );
95  }
96
97  getSqlTableDescription(tableName: string): SqlTableDescription | undefined {
98    for (const module of this.modules) {
99      for (const dataObj of module.dataObjects) {
100        if (dataObj.name == tableName) {
101          return module.getSqlTableDescription(tableName);
102        }
103      }
104    }
105    return undefined;
106  }
107}
108
109export class StdlibModuleImpl implements SqlModule {
110  readonly includeKey: string;
111  readonly dataObjects: SqlTable[];
112  readonly functions: SqlFunction[];
113  readonly tableFunctions: SqlTableFunction[];
114  readonly macros: SqlMacro[];
115
116  constructor(docs: DocsModuleSchemaType) {
117    this.includeKey = docs.module_name;
118    this.dataObjects = docs.data_objects.map(
119      (json) => new StdlibDataObjectImpl(json),
120    );
121    this.functions = docs.functions.map((json) => new StdlibFunctionImpl(json));
122    this.tableFunctions = docs.table_functions.map(
123      (json) => new StdlibTableFunctionImpl(json),
124    );
125    this.macros = docs.macros.map((json) => new StdlibMacroImpl(json));
126  }
127  getTable(tableName: string): SqlTable | undefined {
128    for (const obj of this.dataObjects) {
129      if (obj.name == tableName) {
130        return obj;
131      }
132    }
133    return undefined;
134  }
135
136  getSqlTableDescription(tableName: string): SqlTableDescription | undefined {
137    const sqlTable = this.getTable(tableName);
138    if (sqlTable === undefined) {
139      return undefined;
140    }
141    return {
142      imports: [this.includeKey],
143      name: sqlTable.name,
144      columns: sqlTable.getTableColumns(),
145    };
146  }
147}
148
149class StdlibMacroImpl implements SqlMacro {
150  readonly name: string;
151  readonly summaryDesc: string;
152  readonly description: string;
153  readonly args: SqlArgument[];
154  readonly returnType: string;
155
156  constructor(docs: DocsMacroSchemaType) {
157    this.name = docs.name;
158    this.summaryDesc = docs.summary_desc;
159    this.description = docs.desc;
160    this.returnType = docs.return_type;
161    this.args = [];
162    this.args = docs.args.map((json) => new StdlibFunctionArgImpl(json));
163  }
164}
165
166class StdlibTableFunctionImpl implements SqlTableFunction {
167  readonly name: string;
168  readonly summaryDesc: string;
169  readonly description: string;
170  readonly args: SqlArgument[];
171  readonly returnCols: SqlColumn[];
172
173  constructor(docs: DocsTableFunctionSchemaType) {
174    this.name = docs.name;
175    this.summaryDesc = docs.summary_desc;
176    this.description = docs.desc;
177    this.args = docs.args.map((json) => new StdlibFunctionArgImpl(json));
178    this.returnCols = docs.cols.map((json) => new StdlibColumnImpl(json));
179  }
180}
181
182class StdlibFunctionImpl implements SqlFunction {
183  readonly name: string;
184  readonly summaryDesc: string;
185  readonly description: string;
186  readonly args: SqlArgument[];
187  readonly returnType: string;
188  readonly returnDesc: string;
189
190  constructor(docs: DocsFunctionSchemaType) {
191    this.name = docs.name;
192    this.summaryDesc = docs.summary_desc;
193    this.description = docs.desc;
194    this.returnType = docs.return_type;
195    this.returnDesc = docs.return_desc;
196    this.args = docs.args.map((json) => new StdlibFunctionArgImpl(json));
197  }
198}
199
200class StdlibDataObjectImpl implements SqlTable {
201  name: string;
202  description: string;
203  type: string;
204  columns: SqlColumn[];
205
206  constructor(docs: DocsDataObjectSchemaType) {
207    this.name = docs.name;
208    this.description = docs.desc;
209    this.type = docs.type;
210    this.columns = docs.cols.map((json) => new StdlibColumnImpl(json));
211  }
212
213  getTableColumns(): (TableColumn | TableColumnSet)[] {
214    return this.columns.map((col) => col.asTableColumn(this.name));
215  }
216}
217
218class StdlibColumnImpl implements SqlColumn {
219  name: string;
220  type: string;
221  description: string;
222
223  constructor(docs: DocsArgOrColSchemaType) {
224    this.type = docs.type;
225    this.description = docs.desc;
226    this.name = docs.name;
227  }
228
229  asTableColumn(tableName: string): TableColumn | TableColumnSet {
230    if (this.type === 'TIMESTAMP') {
231      return new TimestampColumn(this.name);
232    }
233    if (this.type === 'DURATION') {
234      return new DurationColumn(this.name);
235    }
236    if (this.type === 'ARGSETID') {
237      return new ArgSetColumnSet(this.name);
238    }
239
240    if (this.name === 'ID') {
241      if (tableName === 'slice') {
242        return new SliceIdColumn(this.name);
243      }
244      if (tableName === 'thread') {
245        return new ThreadIdColumn(this.name);
246      }
247      if (tableName === 'process') {
248        return new ProcessIdColumn(this.name);
249      }
250      if (tableName === 'thread_state') {
251        return new ThreadStateIdColumn(this.name);
252      }
253      if (tableName === 'sched') {
254        return new SchedIdColumn(this.name);
255      }
256      return new StandardColumn(this.name);
257    }
258
259    if (this.type === 'JOINID(slice.id)') {
260      return new SliceIdColumn(this.name);
261    }
262    if (this.type === 'JOINID(thread.id)') {
263      return new ThreadColumn(this.name);
264    }
265    if (this.type === 'JOINID(process.id)') {
266      return new ProcessColumn(this.name);
267    }
268    if (this.type === 'JOINID(thread_state.id)') {
269      return new ThreadStateIdColumn(this.name);
270    }
271    if (this.type === 'JOINID(sched.id)') {
272      return new SchedIdColumn(this.name);
273    }
274    return new StandardColumn(this.name);
275  }
276}
277
278class StdlibFunctionArgImpl implements SqlArgument {
279  name: string;
280  description: string;
281  type: string;
282
283  constructor(docs: DocsArgOrColSchemaType) {
284    this.type = docs.type;
285    this.description = docs.desc;
286    this.name = docs.name;
287  }
288}
289
290const ARG_OR_COL_SCHEMA = z.object({
291  name: z.string(),
292  type: z.string(),
293  desc: z.string(),
294});
295type DocsArgOrColSchemaType = z.infer<typeof ARG_OR_COL_SCHEMA>;
296
297const DATA_OBJECT_SCHEMA = z.object({
298  name: z.string(),
299  desc: z.string(),
300  summary_desc: z.string(),
301  type: z.string(),
302  cols: z.array(ARG_OR_COL_SCHEMA),
303});
304type DocsDataObjectSchemaType = z.infer<typeof DATA_OBJECT_SCHEMA>;
305
306const FUNCTION_SCHEMA = z.object({
307  name: z.string(),
308  desc: z.string(),
309  summary_desc: z.string(),
310  args: z.array(ARG_OR_COL_SCHEMA),
311  return_type: z.string(),
312  return_desc: z.string(),
313});
314type DocsFunctionSchemaType = z.infer<typeof FUNCTION_SCHEMA>;
315
316const TABLE_FUNCTION_SCHEMA = z.object({
317  name: z.string(),
318  desc: z.string(),
319  summary_desc: z.string(),
320  args: z.array(ARG_OR_COL_SCHEMA),
321  cols: z.array(ARG_OR_COL_SCHEMA),
322});
323type DocsTableFunctionSchemaType = z.infer<typeof TABLE_FUNCTION_SCHEMA>;
324
325const MACRO_SCHEMA = z.object({
326  name: z.string(),
327  desc: z.string(),
328  summary_desc: z.string(),
329  return_desc: z.string(),
330  return_type: z.string(),
331  args: z.array(ARG_OR_COL_SCHEMA),
332});
333type DocsMacroSchemaType = z.infer<typeof MACRO_SCHEMA>;
334
335const MODULE_SCHEMA = z.object({
336  module_name: z.string(),
337  data_objects: z.array(DATA_OBJECT_SCHEMA),
338  functions: z.array(FUNCTION_SCHEMA),
339  table_functions: z.array(TABLE_FUNCTION_SCHEMA),
340  macros: z.array(MACRO_SCHEMA),
341});
342type DocsModuleSchemaType = z.infer<typeof MODULE_SCHEMA>;
343
344const PACKAGE_SCHEMA = z.object({
345  name: z.string(),
346  modules: z.array(MODULE_SCHEMA),
347});
348type DocsPackageSchemaType = z.infer<typeof PACKAGE_SCHEMA>;
349
350export const SQL_MODULES_DOCS_SCHEMA = z.array(PACKAGE_SCHEMA);
351export type SqlModulesDocsSchema = z.infer<typeof SQL_MODULES_DOCS_SCHEMA>;
352